Пример #1
0
def send_autoupdate_notification(mod):
    followers = [u.email for u in mod.followers]
    changelog = mod.default_version().changelog
    if changelog:
        changelog = '\n'.join(['    ' + l for l in changelog.split('\n')])

    targets = list()
    for follower in followers:
        targets.append(follower)
    if len(targets) == 0:
        return
    with open("emails/mod-autoupdated") as f:
        message = html.parser.HTMLParser().unescape(pystache.render(f.read(),
            {
                'mod': mod,
                'domain': _cfg("domain"),
                'site-name': _cfg('site-name'),
                'latest': mod.default_version(),
                'url': '/mod/' + str(mod.id) + '/' + secure_filename(mod.name)[:64],
                'changelog': changelog
            }))
	# We (or rather just me) probably want that this is not dependent on KSP, since I know some people
	# who run forks of SpaceDock for non-KSP purposes.
	# TODO(Thomas): Consider in putting the game name into a config.
    subject = mod.name + " is compatible with Game " + mod.versions[0].gameversion.friendly_version + "!"
    send_mail.delay(_cfg('support-mail'), targets, subject, message)
Пример #2
0
def is_oauth_provider_configured(provider):
    if provider == 'github':
        return bool(_cfg('gh-oauth-id')) and bool(_cfg('gh-oauth-secret'))
    if provider == 'google':
        return (bool(_cfg('google-oauth-id'))
                and bool(_cfg('google-oauth-secret')))
    return False
Пример #3
0
def update_user_background(username):
    if current_user == None:
        return {'error': True, 'reason': 'You are not logged in.'}, 401
    user = User.query.filter(User.username == username).first()
    if not current_user.admin and current_user.username != user.username:
        return {
            'error': True,
            'reason': 'You are not authorized to edit this user\'s background'
        }, 403
    f = request.files['image']
    filetype = os.path.splitext(os.path.basename(f.filename))[1]
    if not filetype in ['.png', '.jpg']:
        return {
            'error': True,
            'reason': 'This file type is not acceptable.'
        }, 400
    filename = secure_filename(user.username) + filetype
    base_path = os.path.join(
        secure_filename(user.username) + '-' + str(time.time()) + '_' +
        str(user.id))
    full_path = os.path.join(_cfg('storage'), base_path)
    if not os.path.exists(full_path):
        os.makedirs(full_path)
    path = os.path.join(full_path, filename)
    try:
        os.remove(os.path.join(_cfg('storage'), user.backgroundMedia))
    except:
        pass  # who cares
    f.save(path)
    user.backgroundMedia = os.path.join(base_path, filename)
    return {'path': '/content/' + user.backgroundMedia}
Пример #4
0
def is_oauth_provider_configured(provider):
    if provider == 'github':
        return bool(_cfg('gh-oauth-id')) and bool(_cfg('gh-oauth-secret'))
    if provider == 'google':
        return (bool(_cfg('google-oauth-id')) and
                bool(_cfg('google-oauth-secret')))
    return False
Пример #5
0
def send_mail(sender, recipients, subject, message, important=False):
    if _cfg("smtp-host") == "":
        return
    smtp = smtplib.SMTP(host=_cfg("smtp-host"), port=_cfgi("smtp-port"))
    if _cfgb("smtp-tls"):
        smtp.starttls()
    if _cfg("smtp-user") != "":
        smtp.login(_cfg("smtp-user"), _cfg("smtp-password"))
    message = MIMEText(message)
    if important:
        message['X-MC-Important'] = "true"
    message['X-MC-PreserveRecipients'] = "false"
    message['Subject'] = subject
    message['From'] = sender
    if len(recipients) > 1:
        message['Precedence'] = 'bulk'
    for group in chunks(recipients, 100):
        if len(group) > 1:
            message['To'] = "undisclosed-recipients:;"
        else:
            message['To'] = ";".join(group)
        print("Sending email from {} to {} recipients".format(
            sender, len(group)))
        smtp.sendmail(sender, group, message.as_string())
    smtp.quit()
Пример #6
0
def update_mod_background(mod_id):
    if current_user == None:
        return { 'error': True, 'reason': 'You are not logged in.' }, 401
    mod = Mod.query.filter(Mod.id == mod_id).first()
    if not mod:
        return { 'error': True, 'reason': 'Mod not found.' }, 404
    editable = False
    if current_user:
        if current_user.admin:
            editable = True
        if current_user.id == mod.user_id:
            editable = True
        if any([u.accepted and u.user == current_user for u in mod.shared_authors]):
            editable = True
    if not editable:
        return { 'error': True, 'reason': 'Not enought rights.' }, 401
    f = request.files['image']
    filetype = os.path.splitext(os.path.basename(f.filename))[1]
    if not filetype in ['.png', '.jpg']:
        return { 'error': True, 'reason': 'This file type is not acceptable.' }, 400
    filename = secure_filename(mod.name) + '-' + str(time.time()) + filetype
    base_path = os.path.join(secure_filename(mod.user.username) + '_' + str(mod.user.id), secure_filename(mod.name))
    full_path = os.path.join(_cfg('storage'), base_path)
    if not os.path.exists(full_path):
        os.makedirs(full_path)
    path = os.path.join(full_path, filename)
    try:
        os.remove(os.path.join(_cfg('storage'), mod.background))
    except:
        pass # who cares
    f.save(path)
    mod.background = os.path.join(base_path, filename)
    return { 'path': '/content/' + mod.background }
Пример #7
0
def send_update_notification(mod, version, user):
    followers = [u.email for u in mod.followers]
    changelog = version.changelog
    if changelog:
        changelog = '\n'.join(['    ' + l for l in changelog.split('\n')])

    targets = list()
    for follower in followers:
        targets.append(follower)
    if len(targets) == 0:
        return
    with open("emails/mod-updated") as f:
        message = html.parser.HTMLParser().unescape(
            pystache.render(
                f.read(), {
                    'mod':
                    mod,
                    'user':
                    user,
                    'site-name':
                    _cfg('site-name'),
                    'domain':
                    _cfg("domain"),
                    'latest':
                    version,
                    'url':
                    '/mod/' + str(mod.id) + '/' +
                    secure_filename(mod.name)[:64],
                    'changelog':
                    changelog
                }))
    subject = user.username + " has just updated " + mod.name + "!"
    send_mail.delay(_cfg('support-mail'), targets, subject, message)
Пример #8
0
def send_reset(user):
    with open("emails/password-reset") as f:
        message = html.parser.HTMLParser().unescape(\
                pystache.render(f.read(), { 'user': user, 'site-name': _cfg('site-name'), "domain": _cfg("domain"), 'confirmation': user.passwordReset }))
    send_mail.delay(_cfg('support-mail'), [user.email],
                    "Reset your password on " + _cfg('site-name'),
                    message,
                    important=True)
Пример #9
0
def send_confirmation(user, followMod=None):
    with open("emails/confirm-account") as f:
        if followMod != None:
            message = pystache.render(f.read(), { 'user': user, 'site-name': _cfg('site-name'), "domain": _cfg("domain"),\
                    'confirmation': user.confirmation + "?f=" + followMod })
        else:
            message = html.parser.HTMLParser().unescape(\
                    pystache.render(f.read(), { 'user': user, 'site-name': _cfg('site-name'), "domain": _cfg("domain"), 'confirmation': user.confirmation }))
    send_mail.delay(_cfg('support-mail'), [ user.email ], "Welcome to " + _cfg('site-name') + "!", message, important=True)
Пример #10
0
def update_patreon():
    donation_cache.set('patreon_update_time', time.time())
    if _cfg('patreon_user_id') != '' and _cfg('patreon_campaign') != '':
        r = requests.get("https://api.patreon.com/user/" + _cfg('patreon_user_id'))
        if r.status_code == 200:
            patreon = json.loads(r.text)
            for linked_data in patreon['linked']:
                if 'creation_name' in linked_data and 'pledge_sum' in linked_data:
                    if linked_data['creation_name'] == _cfg('patreon_campaign'):
                        donation_cache.set('patreon_donation_amount', linked_data['pledge_sum'])
Пример #11
0
def send_grant_notice(mod, user):
    with open("emails/grant-notice") as f:
        message = html.parser.HTMLParser().unescape(\
                pystache.render(f.read(), { 'user': user, 'site-name': _cfg('site-name'), "domain": _cfg("domain"),\
                'mod': mod, 'url': url_for('mods.mod', id=mod.id, mod_name=mod.name) }))
    send_mail.delay(_cfg('support-mail'), [user.email],
                    "You've been asked to co-author a mod on " +
                    _cfg('site-name'),
                    message,
                    important=True)
Пример #12
0
def connect_with_oauth():
    provider = request.form.get('provider')

    if not is_oauth_provider_configured(provider):
        return 'This install is not configured for login with %s' % provider

    oauth = get_oauth_provider(provider)
    callback = "{}://{}{}".format(_cfg("protocol"), _cfg("domain"),
            url_for('.connect_with_oauth_authorized_' + provider))
    return oauth.authorize(callback=callback)
Пример #13
0
def connect_with_oauth():
    provider = request.form.get('provider')

    if not is_oauth_provider_configured(provider):
        return 'This install is not configured for login with %s' % provider

    oauth = get_oauth_provider(provider)
    callback = "{}://{}{}".format(
        _cfg("protocol"), _cfg("domain"),
        url_for('.connect_with_oauth_authorized_' + provider))
    return oauth.authorize(callback=callback)
Пример #14
0
def send_to_ckan(mod):
    if not _cfg("netkan_repo_path"):
        return
    if not mod.ckan:
        return
    json_blob = {
        'spec_version': 'v1.4',
        'identifier': re.sub(r'\W+', '', mod.name),
        '$kref': '#/ckan/spacedock/' + str(mod.id),
        'license': mod.license,
        'x_via': 'Automated ' + _cfg('site-name') + ' CKAN submission'
    }
    wd = _cfg("netkan_repo_path")
    path = os.path.join(wd, 'NetKAN', json_blob['identifier'] + '.netkan')

    if os.path.exists(path):
        # If the file is already there, then chances are this mod has already been indexed
        return

    with open(path, 'w') as f:
        f.write(json.dumps(json_blob, indent=4))
    subprocess.call(['git', 'fetch', 'upstream'], cwd=wd)
    subprocess.call([
        'git', 'checkout', '-b', 'add-' + json_blob['identifier'],
        'upstream/master'
    ],
                    cwd=wd)
    subprocess.call(['git', 'add', '-A'], cwd=wd)
    subprocess.call(['git', 'commit', '-m', 'Add {0} from '.format(mod.name) + _cfg('site-name') + '\n\nThis is an automated commit on behalf of {1}'\
            .format(mod.name, mod.user.username), '--author={0} <{1}>'.format(mod.user.username, mod.user.email)], cwd=wd)
    subprocess.call(
        ['git', 'push', '-u', 'origin', 'add-' + json_blob['identifier']],
        cwd=wd)
    g = Github(_cfg('github_user'), _cfg('github_pass'))
    r = g.get_repo("KSP-CKAN/NetKAN")
    r.create_pull(title="Add {0} from ".format(mod.name) + _cfg('site-name'), base=r.default_branch, head=_cfg('github_user') + ":add-" + json_blob['identifier'], body=\
"""\
This pull request was automatically generated by """ + _cfg('site-name') + """ on behalf of {0}, to add [{1}]({4}{2}) to CKAN.

Mod details:
    name = {2}
    author = {0}
    abstract = {6}
    license = {7}
    Homepage = {8}
    description = {5}

Please direct questions about this pull request to [{0}]({4}{3}).
""".format(mod.user.username, mod.name,\
    url_for('mods.mod', mod_name=mod.name, id=mod.id),\
    url_for("profile.view_profile", username=mod.user.username),\
    _cfg("protocol") + "://" + _cfg("domain"),\
    mod.description, mod.short_description,\
    mod.license, mod.external_link))
Пример #15
0
 def background_thumb(self):
     if (_cfg('thumbnail_size') == ''):
         return self.background
     thumbnailSizesStr = _cfg('thumbnail_size').split('x')
     thumbnailSize = (int(thumbnailSizesStr[0]), int(thumbnailSizesStr[1]))
     split = os.path.split(self.background)
     thumbPath = os.path.join(split[0], 'thumb_' + split[1])
     fullThumbPath = os.path.join(os.path.join(_cfg('storage'), thumbPath.replace('/content/', '')))
     fullImagePath = os.path.join(_cfg('storage'), self.background.replace('/content/', ''))
     if not os.path.exists(fullThumbPath):
         thumbnail.create(fullImagePath, fullThumbPath, thumbnailSize)
     return thumbPath
Пример #16
0
def send_confirmation(user, followMod=None):
    with open("emails/confirm-account") as f:
        if followMod != None:
            message = pystache.render(f.read(), { 'user': user, 'site-name': _cfg('site-name'), "domain": _cfg("domain"),\
                    'confirmation': user.confirmation + "?f=" + followMod })
        else:
            message = html.parser.HTMLParser().unescape(\
                    pystache.render(f.read(), { 'user': user, 'site-name': _cfg('site-name'), "domain": _cfg("domain"), 'confirmation': user.confirmation }))
    send_mail.delay(_cfg('support-mail'), [user.email],
                    "Welcome to " + _cfg('site-name') + "!",
                    message,
                    important=True)
Пример #17
0
def update_patreon():
    donation_cache.set('patreon_update_time', time.time())
    if _cfg('patreon_user_id') != '' and _cfg('patreon_campaign') != '':
        r = requests.get("https://api.patreon.com/user/" +
                         _cfg('patreon_user_id'))
        if r.status_code == 200:
            patreon = json.loads(r.text)
            for linked_data in patreon['linked']:
                if 'creation_name' in linked_data and 'pledge_sum' in linked_data:
                    if linked_data['creation_name'] == _cfg(
                            'patreon_campaign'):
                        donation_cache.set('patreon_donation_amount',
                                           linked_data['pledge_sum'])
Пример #18
0
def send_to_ckan(mod):
    if not _cfg("netkan_repo_path"):
        return
    if not mod.ckan:
        return
    json_blob = {
        'spec_version': 'v1.4',
        'identifier': re.sub(r'\W+', '', mod.name),
        '$kref': '#/ckan/spacedock/' + str(mod.id),
        'license': mod.license,
        'x_via': 'Automated ' + _cfg('site-name') + ' CKAN submission'
    }
    wd = _cfg("netkan_repo_path")
    path = os.path.join(wd, 'NetKAN', json_blob['identifier'] + '.netkan')

    if os.path.exists(path):
        # If the file is already there, then chances are this mod has already been indexed
        return

    with open(path, 'w') as f:
        f.write(json.dumps(json_blob, indent=4))
    subprocess.call(['git', 'fetch', 'upstream'], cwd=wd)
    subprocess.call(['git', 'checkout', '-b', 'add-' + json_blob['identifier'], 'upstream/master'], cwd=wd)
    subprocess.call(['git', 'add', '-A'], cwd=wd)
    subprocess.call(['git', 'commit', '-m', 'Add {0} from '.format(mod.name) + _cfg('site-name') + '\n\nThis is an automated commit on behalf of {1}'\
            .format(mod.name, mod.user.username), '--author={0} <{1}>'.format(mod.user.username, mod.user.email)], cwd=wd)
    subprocess.call(['git', 'push', '-u', 'origin', 'add-' + json_blob['identifier']], cwd=wd)
    g = Github(_cfg('github_user'), _cfg('github_pass'))
    r = g.get_repo("KSP-CKAN/NetKAN")
    r.create_pull(title="Add {0} from ".format(mod.name) + _cfg('site-name'), base=r.default_branch, head=_cfg('github_user') + ":add-" + json_blob['identifier'], body=\
"""\
This pull request was automatically generated by """ + _cfg('site-name') + """ on behalf of {0}, to add [{1}]({4}{2}) to CKAN.

Mod details:
    name = {2}
    author = {0}
    abstract = {6}
    license = {7}
    Homepage = {8}
    description = {5}

Please direct questions about this pull request to [{0}]({4}{3}).
""".format(mod.user.username, mod.name,\
    url_for('mods.mod', mod_name=mod.name, id=mod.id),\
    url_for("profile.view_profile", username=mod.user.username),\
    _cfg("protocol") + "://" + _cfg("domain"),\
    mod.description, mod.short_description,\
    mod.license, mod.external_link))
Пример #19
0
def get_oauth_provider(provider):
    oauth = OAuth(current_app)
    if provider == 'github':
        github = oauth.remote_app(
            'github',
            consumer_key=_cfg('gh-oauth-id'),
            consumer_secret=_cfg('gh-oauth-secret'),
            request_token_params={'scope': 'user:email'},
            base_url='https://api.github.com/',
            request_token_url=None,
            access_token_method='POST',
            access_token_url='https://github.com/login/oauth/access_token',
            authorize_url='https://github.com/login/oauth/authorize'
        )

        @github.tokengetter
        def get_github_oauth_token():
            return session.get('github_token')

        return github

    if provider == 'google':
        google = oauth.remote_app(
            'google',
            consumer_key=_cfg('google-oauth-id'),
            consumer_secret=_cfg('google-oauth-secret'),
            request_token_params={'scope': 'email'},
            base_url='https://www.googleapis.com/oauth2/v1/',
            request_token_url=None,
            access_token_method='POST',
            access_token_url='https://accounts.google.com/o/oauth2/token',
            authorize_url='https://accounts.google.com/o/oauth2/auth',
        )

        @google.tokengetter
        def get_google_oauth_token():
            return session.get('google_token')

        return google

    raise Exception('This OAuth provider was not implemented: ' + provider)
Пример #20
0
def update_mod_background(mod_id):
    if current_user == None:
        return {'error': True, 'reason': 'You are not logged in.'}, 401
    mod = Mod.query.filter(Mod.id == mod_id).first()
    if not mod:
        return {'error': True, 'reason': 'Mod not found.'}, 404
    editable = False
    if current_user:
        if current_user.admin:
            editable = True
        if current_user.id == mod.user_id:
            editable = True
        if any([
                u.accepted and u.user == current_user
                for u in mod.shared_authors
        ]):
            editable = True
    if not editable:
        return {'error': True, 'reason': 'Not enought rights.'}, 401
    f = request.files['image']
    filetype = os.path.splitext(os.path.basename(f.filename))[1]
    if not filetype in ['.png', '.jpg']:
        return {
            'error': True,
            'reason': 'This file type is not acceptable.'
        }, 400
    filename = secure_filename(mod.name) + '-' + str(time.time()) + filetype
    base_path = os.path.join(
        secure_filename(mod.user.username) + '_' + str(mod.user.id),
        secure_filename(mod.name))
    full_path = os.path.join(_cfg('storage'), base_path)
    if not os.path.exists(full_path):
        os.makedirs(full_path)
    path = os.path.join(full_path, filename)
    try:
        os.remove(os.path.join(_cfg('storage'), mod.background))
    except:
        pass  # who cares
    f.save(path)
    mod.background = os.path.join(base_path, filename)
    return {'path': '/content/' + mod.background}
Пример #21
0
def get_oauth_provider(provider):
    oauth = OAuth(current_app)
    if provider == 'github':
        github = oauth.remote_app(
            'github',
            consumer_key=_cfg('gh-oauth-id'),
            consumer_secret=_cfg('gh-oauth-secret'),
            request_token_params={'scope': 'user:email'},
            base_url='https://api.github.com/',
            request_token_url=None,
            access_token_method='POST',
            access_token_url='https://github.com/login/oauth/access_token',
            authorize_url='https://github.com/login/oauth/authorize')

        @github.tokengetter
        def get_github_oauth_token():
            return session.get('github_token')

        return github

    if provider == 'google':
        google = oauth.remote_app(
            'google',
            consumer_key=_cfg('google-oauth-id'),
            consumer_secret=_cfg('google-oauth-secret'),
            request_token_params={'scope': 'email'},
            base_url='https://www.googleapis.com/oauth2/v1/',
            request_token_url=None,
            access_token_method='POST',
            access_token_url='https://accounts.google.com/o/oauth2/token',
            authorize_url='https://accounts.google.com/o/oauth2/auth',
        )

        @google.tokengetter
        def get_google_oauth_token():
            return session.get('google_token')

        return google

    raise Exception('This OAuth provider was not implemented: ' + provider)
Пример #22
0
def update_user_background(username):
    if current_user == None:
        return { 'error': True, 'reason': 'You are not logged in.' }, 401
    user = User.query.filter(User.username == username).first()
    if not current_user.admin and current_user.username != user.username:
        return { 'error': True, 'reason': 'You are not authorized to edit this user\'s background' }, 403
    f = request.files['image']
    filetype = os.path.splitext(os.path.basename(f.filename))[1]
    if not filetype in ['.png', '.jpg']:
        return { 'error': True, 'reason': 'This file type is not acceptable.' }, 400
    filename = secure_filename(user.username) + filetype
    base_path = os.path.join(secure_filename(user.username) + '-' + str(time.time()) + '_' + str(user.id))
    full_path = os.path.join(_cfg('storage'), base_path)
    if not os.path.exists(full_path):
        os.makedirs(full_path)
    path = os.path.join(full_path, filename)
    try:
        os.remove(os.path.join(_cfg('storage'), user.backgroundMedia))
    except:
        pass # who cares
    f.save(path)
    user.backgroundMedia = os.path.join(base_path, filename)
    return { 'path': '/content/' + user.backgroundMedia }
Пример #23
0
def send_autoupdate_notification(mod):
    followers = [u.email for u in mod.followers]
    changelog = mod.default_version().changelog
    if changelog:
        changelog = '\n'.join(['    ' + l for l in changelog.split('\n')])

    targets = list()
    for follower in followers:
        targets.append(follower)
    if len(targets) == 0:
        return
    with open("emails/mod-autoupdated") as f:
        message = html.parser.HTMLParser().unescape(
            pystache.render(
                f.read(), {
                    'mod':
                    mod,
                    'domain':
                    _cfg("domain"),
                    'site-name':
                    _cfg('site-name'),
                    'latest':
                    mod.default_version(),
                    'url':
                    '/mod/' + str(mod.id) + '/' +
                    secure_filename(mod.name)[:64],
                    'changelog':
                    changelog
                }))

# We (or rather just me) probably want that this is not dependent on KSP, since I know some people
# who run forks of SpaceDock for non-KSP purposes.
# TODO(Thomas): Consider in putting the game name into a config.
    subject = mod.name + " is compatible with Game " + mod.versions[
        0].gameversion.friendly_version + "!"
    send_mail.delay(_cfg('support-mail'), targets, subject, message)
Пример #24
0
def send_mail(sender, recipients, subject, message, important=False):
    if _cfg("smtp-host") == "":
        return
    smtp = smtplib.SMTP(host=_cfg("smtp-host"), port=_cfgi("smtp-port"))
    if _cfgb("smtp-tls"):
        smtp.starttls()
    if _cfg("smtp-user") != "":
        smtp.login(_cfg("smtp-user"), _cfg("smtp-password"))
    message = MIMEText(message)
    if important:
        message['X-MC-Important'] = "true"
    message['X-MC-PreserveRecipients'] = "false"
    message['Subject'] = subject
    message['From'] = sender
    if len(recipients) > 1:
        message['Precedence'] = 'bulk'
    for group in chunks(recipients, 100):
        if len(group) > 1:
            message['To'] = "undisclosed-recipients:;"
        else:
            message['To'] = ";".join(group)
        print("Sending email from {} to {} recipients".format(sender, len(group)))
        smtp.sendmail(sender, group, message.as_string())
    smtp.quit()
Пример #25
0
def send_update_notification(mod, version, user):
    followers = [u.email for u in mod.followers]
    changelog = version.changelog
    if changelog:
        changelog = '\n'.join(['    ' + l for l in changelog.split('\n')])

    targets = list()
    for follower in followers:
        targets.append(follower)
    if len(targets) == 0:
        return
    with open("emails/mod-updated") as f:
        message = html.parser.HTMLParser().unescape(pystache.render(f.read(),
            {
                'mod': mod,
                'user': user,
                'site-name': _cfg('site-name'),
                'domain': _cfg("domain"),
                'latest': version,
                'url': '/mod/' + str(mod.id) + '/' + secure_filename(mod.name)[:64],
                'changelog': changelog
            }))
    subject = user.username + " has just updated " + mod.name + "!"
    send_mail.delay(_cfg('support-mail'), targets, subject, message)
Пример #26
0
def delete(mod_id):
    mod = Mod.query.filter(Mod.id == mod_id).first()
    if not mod:
        abort(404)
    game = Game.query.filter(Game.id == mod.game_id).first()
    session['game'] = game.id;
    session['gamename'] = game.name;
    session['gameshort'] = game.short;
    session['gameid'] = game.id;
    if not mod or not game:
        ga = Game.query.filter(Game.short == 'kerbal-space-program').order_by(desc(Game.id)).first()
        session['game'] = ga.id;
        session['gamename'] = ga.name;
        session['gameshort'] = ga.short;
        session['gameid'] = ga.id;
        abort(404)
    else:
        session['game'] = game.id;
        session['gamename'] = game.name;
        session['gameshort'] = game.short;
        session['gameid'] = game.id;
    editable = False
    if current_user:
        if current_user.admin:
            editable = True
        if current_user.id == mod.user_id:
            editable = True
    if not editable:
        abort(401)
    db.delete(mod)
    for feature in Featured.query.filter(Featured.mod_id == mod.id).all():
        db.delete(feature)
    for media in Media.query.filter(Media.mod_id == mod.id).all():
        db.delete(media)
    for version in ModVersion.query.filter(ModVersion.mod_id == mod.id).all():
        db.delete(version)
    base_path = os.path.join(secure_filename(mod.user.username) + '_' + str(mod.user.id), secure_filename(mod.name))
    full_path = os.path.join(_cfg('storage'), base_path)
    db.commit()
    notify_ckan.delay(mod_id, 'delete')
    rmtree(full_path)
    return redirect("/profile/" + current_user.username)
Пример #27
0
def send_reset(user):
    with open("emails/password-reset") as f:
        message = html.parser.HTMLParser().unescape(\
                pystache.render(f.read(), { 'user': user, 'site-name': _cfg('site-name'), "domain": _cfg("domain"), 'confirmation': user.passwordReset }))
    send_mail.delay(_cfg('support-mail'), [ user.email ], "Reset your password on " + _cfg('site-name'), message, important=True)
Пример #28
0
def update_mod(mod_id):
    if current_user == None:
        return {'error': True, 'reason': 'You are not logged in.'}, 401
    mod = Mod.query.filter(Mod.id == mod_id).first()
    if not mod:
        return {'error': True, 'reason': 'Mod not found.'}, 404
    editable = False
    if current_user:
        if current_user.admin:
            editable = True
        if current_user.id == mod.user_id:
            editable = True
        if any([
                u.accepted and u.user == current_user
                for u in mod.shared_authors
        ]):
            editable = True
    if not editable:
        return {'error': True, 'reason': 'Not enought rights.'}, 401
    version = request.form.get('version')
    changelog = request.form.get('changelog')
    game_version = request.form.get('game-version')
    notify = request.form.get('notify-followers')
    zipball = request.files.get('zipball')
    if not version \
        or not game_version \
        or not zipball:
        # Client side validation means that they're just being pricks if they
        # get here, so we don't need to show them a pretty error reason
        # SMILIE: this doesn't account for "external" API use --> return a json error
        return {'error': True, 'reason': 'All fields are required.'}, 400
    test_gameversion = GameVersion.query.filter(
        GameVersion.game_id == Mod.game_id).filter(
            GameVersion.friendly_version == game_version).first()
    if not test_gameversion:
        return {'error': True, 'reason': 'Game version does not exist.'}, 400
    game_version_id = test_gameversion.id
    if notify == None:
        notify = False
    else:
        notify = (notify.lower() == "true" or notify.lower() == "yes")
    filename = secure_filename(
        mod.name) + '-' + secure_filename(version) + '.zip'
    base_path = os.path.join(
        secure_filename(current_user.username) + '_' + str(current_user.id),
        secure_filename(mod.name))
    full_path = os.path.join(_cfg('storage'), base_path)
    if not os.path.exists(full_path):
        os.makedirs(full_path)
    path = os.path.join(full_path, filename)
    for v in mod.versions:
        if v.friendly_version == secure_filename(version):
            return {
                'error':
                True,
                'reason':
                'We already have this version. Did you mistype the version number?'
            }, 400
    if os.path.isfile(path):
        os.remove(path)
    zipball.save(path)
    if not zipfile.is_zipfile(path):
        os.remove(path)
        return {'error': True, 'reason': 'This is not a valid zip file.'}, 400
    version = ModVersion(secure_filename(version), game_version_id,
                         os.path.join(base_path, filename))
    version.changelog = changelog
    # Assign a sort index
    if len(mod.versions) == 0:
        version.sort_index = 0
    else:
        version.sort_index = max([v.sort_index for v in mod.versions]) + 1
    mod.versions.append(version)
    mod.updated = datetime.now()
    if notify:
        send_update_notification(mod, version, current_user)
    db.add(version)
    db.commit()
    mod.default_version_id = version.id
    db.commit()
    notify_ckan.delay(mod_id, 'update')
    return {
        'url': url_for("mods.mod", id=mod.id, mod_name=mod.name),
        "id": version.id
    }
Пример #29
0
def create_mod():
    if not current_user:
        return {'error': True, 'reason': 'You are not logged in.'}, 401
    if not current_user.public:
        return {
            'error': True,
            'reason': 'Only users with public profiles may create mods.'
        }, 403
    name = request.form.get('name')
    game = request.form.get('game')
    short_description = request.form.get('short-description')
    version = request.form.get('version')
    game_version = request.form.get('game-version')
    license = request.form.get('license')
    ckan = request.form.get('ckan')
    zipball = request.files.get('zipball')
    # Validate
    if not name \
        or not short_description \
        or not version \
        or not game \
        or not game_version \
        or not license \
        or not zipball:
        return {'error': True, 'reason': 'All fields are required.'}, 400
    # Validation, continued
    if len(name) > 100 \
        or len(short_description) > 1000 \
        or len(license) > 128:
        return {
            'error': True,
            'reason': 'Fields exceed maximum permissible length.'
        }, 400
    if ckan == None:
        ckan = False
    else:
        ckan = (ckan.lower() == "true" or ckan.lower() == "yes"
                or ckan.lower() == "on")
    test_game = Game.query.filter(Game.id == game).first()
    if not test_game:
        return {'error': True, 'reason': 'Game does not exist.'}, 400
    test_gameversion = GameVersion.query.filter(
        GameVersion.game_id == test_game.id).filter(
            GameVersion.friendly_version == game_version).first()
    if not test_gameversion:
        return {'error': True, 'reason': 'Game version does not exist.'}, 400
    game_version_id = test_gameversion.id
    mod = Mod()
    mod.user = current_user
    mod.name = name
    mod.game_id = game
    mod.short_description = short_description
    mod.description = default_description
    mod.ckan = ckan
    mod.license = license
    # Save zipball
    filename = secure_filename(name) + '-' + secure_filename(version) + '.zip'
    base_path = os.path.join(
        secure_filename(current_user.username) + '_' + str(current_user.id),
        secure_filename(name))
    full_path = os.path.join(_cfg('storage'), base_path)
    if not os.path.exists(full_path):
        os.makedirs(full_path)
    path = os.path.join(full_path, filename)
    if os.path.isfile(path):
        # We already have this version
        # We'll remove it because the only reason it could be here on creation is an error
        os.remove(path)
    zipball.save(path)
    if not zipfile.is_zipfile(path):
        os.remove(path)
        return {'error': True, 'reason': 'This is not a valid zip file.'}, 400
    version = ModVersion(secure_filename(version), game_version_id,
                         os.path.join(base_path, filename))
    mod.versions.append(version)
    db.add(version)
    # Save database entry
    db.add(mod)
    db.commit()
    mod.default_version_id = version.id
    db.commit()
    ga = Game.query.filter(Game.id == game).first()
    session['game'] = ga.id
    session['gamename'] = ga.name
    session['gameshort'] = ga.short
    session['gameid'] = ga.id
    notify_ckan.delay(mod.id, 'create')
    return {
        'url': url_for("mods.mod", id=mod.id, mod_name=mod.name),
        "id": mod.id,
        "name": mod.name
    }
Пример #30
0
def create_mod():
    if not current_user:
        return { 'error': True, 'reason': 'You are not logged in.' }, 401
    if not current_user.public:
        return { 'error': True, 'reason': 'Only users with public profiles may create mods.' }, 403
    name = request.form.get('name')
    game = request.form.get('game')
    short_description = request.form.get('short-description')
    version = request.form.get('version')
    game_version = request.form.get('game-version')
    license = request.form.get('license')
    ckan = request.form.get('ckan')
    zipball = request.files.get('zipball')
    # Validate
    if not name \
        or not short_description \
        or not version \
        or not game \
        or not game_version \
        or not license \
        or not zipball:
        return { 'error': True, 'reason': 'All fields are required.' }, 400
    # Validation, continued
    if len(name) > 100 \
        or len(short_description) > 1000 \
        or len(license) > 128:
        return { 'error': True, 'reason': 'Fields exceed maximum permissible length.' }, 400
    if ckan == None:
        ckan = False
    else:
        ckan = (ckan.lower() == "true" or ckan.lower() == "yes" or ckan.lower() == "on")
    test_game = Game.query.filter(Game.id == game).first()
    if not test_game:
        return { 'error': True, 'reason': 'Game does not exist.' }, 400
    test_gameversion = GameVersion.query.filter(GameVersion.game_id == test_game.id).filter(GameVersion.friendly_version == game_version).first()
    if not test_gameversion:
        return { 'error': True, 'reason': 'Game version does not exist.' }, 400
    game_version_id = test_gameversion.id
    mod = Mod()
    mod.user = current_user
    mod.name = name
    mod.game_id = game
    mod.short_description = short_description
    mod.description = default_description
    mod.ckan = ckan
    mod.license = license
    # Save zipball
    filename = secure_filename(name) + '-' + secure_filename(version) + '.zip'
    base_path = os.path.join(secure_filename(current_user.username) + '_' + str(current_user.id), secure_filename(name))
    full_path = os.path.join(_cfg('storage'), base_path)
    if not os.path.exists(full_path):
        os.makedirs(full_path)
    path = os.path.join(full_path, filename)
    if os.path.isfile(path):
        # We already have this version
        # We'll remove it because the only reason it could be here on creation is an error
        os.remove(path)
    zipball.save(path)
    if not zipfile.is_zipfile(path):
        os.remove(path)
        return { 'error': True, 'reason': 'This is not a valid zip file.' }, 400
    version = ModVersion(secure_filename(version), game_version_id, os.path.join(base_path, filename))
    mod.versions.append(version)
    db.add(version)
    # Save database entry
    db.add(mod)
    db.commit()
    mod.default_version_id = version.id
    db.commit()
    ga = Game.query.filter(Game.id == game).first()
    session['game'] = ga.id;
    session['gamename'] = ga.name;
    session['gameshort'] = ga.short;
    session['gameid'] = ga.id;
    notify_ckan.delay(mod.id, 'create')
    return { 'url': url_for("mods.mod", id=mod.id, mod_name=mod.name), "id": mod.id, "name": mod.name }
Пример #31
0
def download(mod_id, mod_name, version):
    mod = Mod.query.filter(Mod.id == mod_id).first()
    if not mod:
        abort(404)
    game = Game.query.filter(Game.id == mod.game_id).first()
    session['game'] = game.id;
    session['gamename'] = game.name;
    session['gameshort'] = game.short;
    session['gameid'] = game.id;
    if not mod or not game:
        ga = Game.query.filter(Game.short == 'kerbal-space-program').order_by(desc(Game.id)).first()
        session['game'] = ga.id;
        session['gamename'] = ga.name;
        session['gameshort'] = ga.short;
        session['gameid'] = ga.id;
        abort(404)
    else:
        session['game'] = game.id;
        session['gamename'] = game.name;
        session['gameshort'] = game.short;
        session['gameid'] = game.id;
    if not mod.published and (not current_user or current_user.id != mod.user_id):
        abort(401)
    version = ModVersion.query.filter(ModVersion.mod_id == mod_id, \
            ModVersion.friendly_version == version).first()
    if not version:
        abort(404)
    download = DownloadEvent.query\
            .filter(DownloadEvent.mod_id == mod.id and DownloadEvent.version_id == version.id)\
            .order_by(desc(DownloadEvent.created))\
            .first()
    if not os.path.isfile(os.path.join(_cfg('storage'), version.download_path)):
        abort(404)
    
    if not 'Range' in request.headers:
        # Events are aggregated hourly
        if not download or ((datetime.now() - download.created).seconds / 60 / 60) >= 1:
            download = DownloadEvent()
            download.mod = mod
            download.version = version
            download.downloads = 1
            db.add(download)
            db.flush()
            db.commit()
            mod.downloads.append(download)
        else:
            download.downloads += 1
        mod.download_count += 1
    
    if _cfg("cdn-domain"):
        return redirect("http://" + _cfg("cdn-domain") + '/' + version.download_path, code=302)
    
    response = None
    if _cfg("use-x-accel") == 'nginx':
        response = make_response("")
        response.headers['Content-Type'] = 'application/zip'
        response.headers['Content-Disposition'] = 'attachment; filename=' + os.path.basename(version.download_path)
        response.headers['X-Accel-Redirect'] = '/internal/' + version.download_path
    if _cfg("use-x-accel") == 'apache':
        response = make_response("")
        response.headers['Content-Type'] = 'application/zip'
        response.headers['Content-Disposition'] = 'attachment; filename=' + os.path.basename(version.download_path)
        response.headers['X-Sendfile'] = os.path.join(_cfg('storage'), version.download_path)
    if response is None:
        response = make_response(send_file(os.path.join(_cfg('storage'), version.download_path), as_attachment = True))
    return response
Пример #32
0
def notify_ckan(mod_id, event_type):
    if _cfg("notify-url") == "":
        return
    send_data = { 'mod_id': mod_id, 'event_type': event_type }
    requests.post(_cfg("notify-url"), send_data)
Пример #33
0
import requests
import smtplib
from celery import Celery
from email.mime.text import MIMEText
from SpaceDock.config import _cfg, _cfgi, _cfgb
import redis
import requests
import time
import json

app = Celery("tasks", broker=_cfg("redis-connection"))
donation_cache = redis.Redis(host=_cfg('patreon-host'), port=_cfg('patreon-port'), db=_cfg('patreon-db'))

def chunks(l, n):
    """ Yield successive n-sized chunks from l.
    """
    for i in range(0, len(l), n):
        yield l[i:i+n]

@app.task
def send_mail(sender, recipients, subject, message, important=False):
    if _cfg("smtp-host") == "":
        return
    smtp = smtplib.SMTP(host=_cfg("smtp-host"), port=_cfgi("smtp-port"))
    if _cfgb("smtp-tls"):
        smtp.starttls()
    if _cfg("smtp-user") != "":
        smtp.login(_cfg("smtp-user"), _cfg("smtp-password"))
    message = MIMEText(message)
    if important:
        message['X-MC-Important'] = "true"
Пример #34
0
from SpaceDock.config import _cfg
from SpaceDock.celery import update_patreon
import celery
import redis
import time
import json

donation_cache = redis.Redis(host=_cfg('patreon-host'), port=_cfg('patreon-port'), db=_cfg('patreon-db'))

def GetDonationAmount():
    last_update_time = donation_cache.get('patreon_update_time')
    donation_amount = donation_cache.get('patreon_donation_amount')
    request_update = False

    if donation_amount is None:
        donation_amount = 0
        donation_cache.set('patreon_donation_amount', 0)
    else:
        donation_amount = int(donation_amount.decode('utf-8'))
    
    if last_update_time is None:
        request_update = True
    else:
        last_update_time = float(last_update_time.decode('utf-8'))
        time_delta = time.time() - last_update_time
        #Update every 10 minutes
        if time_delta > 600:
            request_update = True
    
    if request_update:
        update_patreon.delay()
Пример #35
0
def notify_ckan(mod_id, event_type):
    if _cfg("notify-url") == "":
        return
    send_data = {'mod_id': mod_id, 'event_type': event_type}
    requests.post(_cfg("notify-url"), send_data)
Пример #36
0
import requests
import smtplib
from celery import Celery
from email.mime.text import MIMEText
from SpaceDock.config import _cfg, _cfgi, _cfgb
import redis
import requests
import time
import json

app = Celery("tasks", broker=_cfg("redis-connection"))
donation_cache = redis.Redis(host=_cfg('patreon-host'),
                             port=_cfg('patreon-port'),
                             db=_cfg('patreon-db'))


def chunks(l, n):
    """ Yield successive n-sized chunks from l.
    """
    for i in range(0, len(l), n):
        yield l[i:i + n]


@app.task
def send_mail(sender, recipients, subject, message, important=False):
    if _cfg("smtp-host") == "":
        return
    smtp = smtplib.SMTP(host=_cfg("smtp-host"), port=_cfgi("smtp-port"))
    if _cfgb("smtp-tls"):
        smtp.starttls()
    if _cfg("smtp-user") != "":
Пример #37
0
def send_grant_notice(mod, user):
    with open("emails/grant-notice") as f:
        message = html.parser.HTMLParser().unescape(\
                pystache.render(f.read(), { 'user': user, 'site-name': _cfg('site-name'), "domain": _cfg("domain"),\
                'mod': mod, 'url': url_for('mods.mod', id=mod.id, mod_name=mod.name) }))
    send_mail.delay(_cfg('support-mail'), [ user.email ], "You've been asked to co-author a mod on " + _cfg('site-name'), message, important=True)
Пример #38
0
def update_mod(mod_id):
    if current_user == None:
        return { 'error': True, 'reason': 'You are not logged in.' }, 401
    mod = Mod.query.filter(Mod.id == mod_id).first()
    if not mod:
        return { 'error': True, 'reason': 'Mod not found.' }, 404
    editable = False
    if current_user:
        if current_user.admin:
            editable = True
        if current_user.id == mod.user_id:
            editable = True
        if any([u.accepted and u.user == current_user for u in mod.shared_authors]):
            editable = True
    if not editable:
        return { 'error': True, 'reason': 'Not enought rights.' }, 401
    version = request.form.get('version')
    changelog = request.form.get('changelog')
    game_version = request.form.get('game-version')
    notify = request.form.get('notify-followers')
    zipball = request.files.get('zipball')
    if not version \
        or not game_version \
        or not zipball:
        # Client side validation means that they're just being pricks if they
        # get here, so we don't need to show them a pretty error reason
        # SMILIE: this doesn't account for "external" API use --> return a json error
        return { 'error': True, 'reason': 'All fields are required.' }, 400
    test_gameversion = GameVersion.query.filter(GameVersion.game_id == Mod.game_id).filter(GameVersion.friendly_version == game_version).first()
    if not test_gameversion:
        return { 'error': True, 'reason': 'Game version does not exist.' }, 400
    game_version_id = test_gameversion.id
    if notify == None:
        notify = False
    else:
        notify = (notify.lower() == "true" or notify.lower() == "yes")
    filename = secure_filename(mod.name) + '-' + secure_filename(version) + '.zip'
    base_path = os.path.join(secure_filename(current_user.username) + '_' + str(current_user.id), secure_filename(mod.name))
    full_path = os.path.join(_cfg('storage'), base_path)
    if not os.path.exists(full_path):
        os.makedirs(full_path)
    path = os.path.join(full_path, filename)
    for v in mod.versions:
        if v.friendly_version == secure_filename(version):
            return { 'error': True, 'reason': 'We already have this version. Did you mistype the version number?' }, 400
    if os.path.isfile(path):
        os.remove(path)        
    zipball.save(path)
    if not zipfile.is_zipfile(path):
        os.remove(path)
        return { 'error': True, 'reason': 'This is not a valid zip file.' }, 400
    version = ModVersion(secure_filename(version), game_version_id, os.path.join(base_path, filename))
    version.changelog = changelog
    # Assign a sort index
    if len(mod.versions) == 0:
        version.sort_index = 0
    else:
        version.sort_index = max([v.sort_index for v in mod.versions]) + 1
    mod.versions.append(version)
    mod.updated = datetime.now()
    if notify:
        send_update_notification(mod, version, current_user)
    db.add(version)
    db.commit()
    mod.default_version_id = version.id
    db.commit()
    notify_ckan.delay(mod_id, 'update')
    return { 'url': url_for("mods.mod", id=mod.id, mod_name=mod.name), "id": version.id  }
Пример #39
0
def mod(id, mod_name):
    mod = Mod.query.filter(Mod.id == id).first()
    ga = mod.game
    session['game'] = ga.id;
    session['gamename'] = ga.name;
    session['gameshort'] = ga.short;
    session['gameid'] = ga.id;
    if not mod or not ga:
        abort(404)
    editable = False
    if current_user:
        if current_user.admin:
            editable = True
        if current_user.id == mod.user_id:
            editable = True
    if not mod.published and not editable:
        abort(401)
    latest = mod.default_version()
    referral = request.referrer
    if referral:
        host = urllib.parse.urlparse(referral).hostname
        event = ReferralEvent.query\
                .filter(ReferralEvent.mod_id == mod.id)\
                .filter(ReferralEvent.host == host)\
                .first()
        if not event:
            event = ReferralEvent()
            event.mod = mod
            event.events = 1
            event.host = host
            db.add(event)
            db.flush()
            db.commit()
            mod.referrals.append(event)
        else:
            event.events += 1
    download_stats = None
    follower_stats = None
    referrals = None
    json_versions = None
    thirty_days_ago = datetime.now() - timedelta(days=30)
    referrals = list()
    for r in ReferralEvent.query\
        .filter(ReferralEvent.mod_id == mod.id)\
        .order_by(desc(ReferralEvent.events)):
        referrals.append( { 'host': r.host, 'count': r.events } )
    download_stats = list()
    for d in DownloadEvent.query\
        .filter(DownloadEvent.mod_id == mod.id)\
        .filter(DownloadEvent.created > thirty_days_ago)\
        .order_by(DownloadEvent.created):
        download_stats.append(dumb_object(d))
    follower_stats = list()
    for f in FollowEvent.query\
        .filter(FollowEvent.mod_id == mod.id)\
        .filter(FollowEvent.created > thirty_days_ago)\
        .order_by(FollowEvent.created):
        follower_stats.append(dumb_object(f))
    json_versions = list()
    for v in mod.versions:
        json_versions.append({ 'name': v.friendly_version, 'id': v.id })
    if request.args.get('noedit') != None:
        editable = False
    forumThread = False
    if mod.external_link != None:
        try:
            u = urlparse(mod.external_link)
            if u.netloc == 'forum.kerbalspaceprogram.com':
                forumThread = True
        except e:
            print(e)
            pass
    total_authors = 1
    pending_invite = False
    owner = editable
    for a in mod.shared_authors:
        if a.accepted:
            total_authors += 1
        if current_user:
            if current_user.id == a.user_id and not a.accepted:
                pending_invite = True
            if current_user.id == a.user_id and a.accepted:
                editable = True
    games = Game.query.filter(Game.active == True).order_by(desc(Game.id)).all()

    game_versions = GameVersion.query.filter(GameVersion.game_id == mod.game_id).order_by(desc(GameVersion.id)).all()

    outdated = False
    if latest:
        outdated = latest.gameversion.id != game_versions[0].id and latest.gameversion.friendly_version != '1.0.5'
    return render_template("detail.html",ptype='mod',stype='view',
        **{
            'mod': mod,
            'latest': latest,
            'safe_name': secure_filename(mod.name)[:64],
            'featured': any(Featured.query.filter(Featured.mod_id == mod.id).all()),
            'editable': editable,
            'owner': owner,
            'pending_invite': pending_invite,
            'download_stats': download_stats,
            'follower_stats': follower_stats,
            'referrals': referrals,
            'json_versions': json_versions,
            'thirty_days_ago': thirty_days_ago,
            'share_link': urllib.parse.quote_plus(_cfg("protocol") + "://" + _cfg("domain") + "/mod/" + str(mod.id)),
            'game_versions': game_versions,
            'games':  games,
            'outdated': outdated,
            'forum_thread': forumThread,
            'new': request.args.get('new') != None,
            'stupid_user': request.args.get('stupid_user') != None,
            'total_authors': total_authors,
			"site_name": _cfg('site-name'), 
			"support_mail": _cfg('support-mail'),
            'ga': ga
        })
Пример #40
0
def send_bulk_email(users, subject, body):
    targets = list()
    for u in users:
        targets.append(u)
    send_mail.delay(_cfg('support-mail'), targets, subject, body)
Пример #41
0
def send_bulk_email(users, subject, body):
    targets = list()
    for u in users:
        targets.append(u)
    send_mail.delay(_cfg('support-mail'), targets, subject, body)