def handle_login(form): def show_safe_err(err): if "@" in username: flash("Incorrect email or password", "danger") else: flash(err, "danger") username = form.username.data.strip() user = User.query.filter( or_(User.username == username, User.email == username)).first() if user is None: return show_safe_err("User {} does not exist".format(username)) if not check_password_hash(user.password, form.password.data): return show_safe_err("Incorrect password. Did you set one?") if not user.is_active: flash("You need to confirm the registration email", "danger") return addAuditLog(AuditSeverity.USER, user, "Logged in using password", url_for("users.profile", username=user.username)) db.session.commit() if not login_user(user, remember=form.remember_me.data): flash("Login failed", "danger") return return post_login(user, request.args.get("next"))
def handle_set_password(form): one = form.password.data two = form.password2.data if one != two: flash("Passwords do not much", "danger") return addAuditLog(AuditSeverity.USER, current_user, "Changed their password", url_for("users.profile", username=current_user.username)) current_user.password = make_flask_login_password(form.password.data) if hasattr(form, "email"): newEmail = nonEmptyOrNone(form.email.data) if newEmail and newEmail != current_user.email: if EmailSubscription.query.filter_by(email=form.email.data, blacklisted=True).count() > 0: flash( "That email address has been unsubscribed/blacklisted, and cannot be used", "danger") return token = randomString(32) ver = UserEmailVerification() ver.user = current_user ver.token = token ver.email = newEmail db.session.add(ver) db.session.commit() flash("Your password has been changed successfully.", "success") return redirect(url_for("homepage.home"))
def handle_profile_edit(form, user, username): severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION addAuditLog(severity, current_user, "Edited {}'s profile".format(user.display_name), url_for("users.profile", username=username)) if user.checkPerm(current_user, Permission.CHANGE_DISPLAY_NAME) and \ user.display_name != form.display_name.data: if User.query.filter( User.id != user.id, or_(User.username == form.display_name.data, User.display_name.ilike( form.display_name.data))).count() > 0: flash("A user already has that name", "danger") return None user.display_name = form.display_name.data severity = AuditSeverity.USER if current_user == user else AuditSeverity.MODERATION addAuditLog( severity, current_user, "Changed display name of {} to {}".format(user.username, user.display_name), url_for("users.profile", username=username)) if user.checkPerm(current_user, Permission.CHANGE_PROFILE_URLS): user.website_url = form["website_url"].data user.donate_url = form["donate_url"].data db.session.commit() return redirect(url_for("users.profile", username=username))
def forgot_password(): form = ForgotPasswordForm(request.form) if form.validate_on_submit(): email = form.email.data user = User.query.filter_by(email=email).first() if user: token = randomString(32) addAuditLog(AuditSeverity.USER, user, "(Anonymous) requested a password reset", url_for("users.profile", username=user.username), None) ver = UserEmailVerification() ver.user = user ver.token = token ver.email = email ver.is_password_reset = True db.session.add(ver) db.session.commit() send_verify_email.delay(form.email.data, token) else: send_anon_email.delay(email, "Unable to find account", """ <p> We were unable to perform the password reset as we could not find an account associated with this email. </p> <p> If you weren't expecting to receive this email, then you can safely ignore it. </p> """) flash("Check your email address to continue the reset", "success") return redirect(url_for("homepage.home")) return render_template("users/forgot_password.html", form=form)
def do_create_vcs_release(user: User, package: Package, title: str, ref: str, min_v: MinetestRelease = None, max_v: MinetestRelease = None, reason: str = None): check_can_create_release(user, package) rel = PackageRelease() rel.package = package rel.title = title rel.url = "" rel.task_id = uuid() rel.min_rel = min_v rel.max_rel = max_v db.session.add(rel) if reason is None: msg = "Created release {}".format(rel.title) else: msg = "Created release {} ({})".format(rel.title, reason) addAuditLog(AuditSeverity.NORMAL, user, msg, package.getURL("packages.view"), package) db.session.commit() makeVCSRelease.apply_async((rel.id, nonEmptyOrNone(ref)), task_id=rel.task_id) return rel
def delete_reply(id): thread = Thread.query.get(id) if thread is None: abort(404) reply_id = request.args.get("reply") if reply_id is None: abort(404) reply = ThreadReply.query.get(reply_id) if reply is None or reply.thread != thread: abort(404) if thread.replies[0] == reply: flash("Cannot delete thread opening post!", "danger") return redirect(thread.getViewURL()) if not reply.checkPerm(current_user, Permission.DELETE_REPLY): abort(403) if request.method == "GET": return render_template("threads/delete_reply.html", thread=thread, reply=reply) msg = "Deleted reply by {}".format(reply.author.display_name) addAuditLog(AuditSeverity.MODERATION, current_user, msg, thread.getViewURL(), thread.package, reply.comment) db.session.delete(reply) db.session.commit() return redirect(thread.getViewURL())
def delete(username): user: User = User.query.filter_by(username=username).first() if not user: abort(404) if request.method == "GET": return render_template("users/delete.html", user=user, can_delete=user.can_delete()) if user.can_delete(): msg = "Deleted user {}".format(user.username) flash(msg, "success") addAuditLog(AuditSeverity.MODERATION, current_user, msg, None) db.session.delete(user) else: user.replies.delete() for thread in user.threads.all(): db.session.delete(thread) user.email = None user.rank = UserRank.NOT_JOINED msg = "Deactivated user {}".format(user.username) flash(msg, "success") addAuditLog(AuditSeverity.MODERATION, current_user, msg, None) db.session.commit() if user == current_user: logout_user() return redirect(url_for("homepage.home"))
def handle_register(form): user_by_name = User.query.filter( or_(User.username == form.username.data, User.username == form.display_name.data, User.display_name == form.display_name.data, User.forums_username == form.username.data, User.github_username == form.username.data)).first() if user_by_name: if user_by_name.rank == UserRank.NOT_JOINED and user_by_name.forums_username: flash( "An account already exists for that username but hasn't been claimed yet.", "danger") return redirect( url_for("users.claim_forums", username=user_by_name.forums_username)) else: flash( "That username/display name is already in use, please choose another.", "danger") return user_by_email = User.query.filter_by(email=form.email.data).first() if user_by_email: send_anon_email.delay( form.email.data, "Email already in use", "We were unable to create the account as the email is already in use by {}. Try a different email address." .format(user_by_email.display_name)) flash("Check your email address to verify your account", "success") return redirect(url_for("homepage.home")) elif EmailSubscription.query.filter_by(email=form.email.data, blacklisted=True).count() > 0: flash( "That email address has been unsubscribed/blacklisted, and cannot be used", "danger") return user = User(form.username.data, False, form.email.data, make_flask_login_password(form.password.data)) user.notification_preferences = UserNotificationPreferences(user) if form.display_name.data: user.display_name = form.display_name.data db.session.add(user) addAuditLog(AuditSeverity.USER, user, "Registered with email, display name=" + user.display_name, url_for("users.profile", username=user.username)) token = randomString(32) ver = UserEmailVerification() ver.user = user ver.token = token ver.email = form.email.data db.session.add(ver) db.session.commit() send_verify_email.delay(form.email.data, token) flash("Check your email address to verify your account", "success") return redirect(url_for("homepage.home"))
def do_create_screenshot(user: User, package: Package, title: str, file, reason: str = None): thirty_minutes_ago = datetime.datetime.now() - datetime.timedelta(minutes=30) count = package.screenshots.filter(PackageScreenshot.created_at > thirty_minutes_ago).count() if count >= 20: raise LogicError(429, "Too many requests, please wait before trying again") uploaded_url, uploaded_path = upload_file(file, "image", "a PNG or JPG image file") counter = 1 for screenshot in package.screenshots.all(): screenshot.order = counter counter += 1 ss = PackageScreenshot() ss.package = package ss.title = title or "Untitled" ss.url = uploaded_url ss.approved = package.checkPerm(user, Permission.APPROVE_SCREENSHOT) ss.order = counter db.session.add(ss) if reason is None: msg = "Created screenshot {}".format(ss.title) else: msg = "Created screenshot {} ({})".format(ss.title, reason) addNotification(package.maintainers, user, NotificationType.PACKAGE_EDIT, msg, package.getURL("packages.view"), package) addAuditLog(AuditSeverity.NORMAL, user, msg, package.getURL("packages.view"), package) db.session.commit() return ss
def set_lock(id): thread = Thread.query.get(id) if thread is None or not thread.checkPerm(current_user, Permission.LOCK_THREAD): abort(404) thread.locked = isYes(request.args.get("lock")) if thread.locked is None: abort(400) msg = None if thread.locked: msg = "Locked thread '{}'".format(thread.title) flash("Locked thread", "success") else: msg = "Unlocked thread '{}'".format(thread.title) flash("Unlocked thread", "success") addNotification(thread.watchers, current_user, NotificationType.OTHER, msg, thread.getViewURL(), thread.package) addAuditLog(AuditSeverity.MODERATION, current_user, NotificationType.OTHER, msg, thread.getViewURL(), thread.package) db.session.commit() return redirect(thread.getViewURL())
def account(username): user: User = User.query.filter_by(username=username).first() if not user: abort(404) if not user.can_see_edit_profile(current_user): flash("Permission denied", "danger") return redirect(url_for("users.profile", username=username)) can_edit_account_settings = user.checkPerm(current_user, Permission.CHANGE_USERNAMES) or \ user.checkPerm(current_user, Permission.CHANGE_RANK) form = UserAccountForm(obj=user) if can_edit_account_settings else None if form and form.validate_on_submit(): severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION addAuditLog(severity, current_user, "Edited {}'s profile".format(user.display_name), url_for("users.profile", username=username)) # Copy form fields to user_profile fields if user.checkPerm(current_user, Permission.CHANGE_USERNAMES): if user.username != form.username.data: for package in user.packages: alias = PackageAlias(user.username, package.name) package.aliases.append(alias) db.session.add(alias) user.username = form.username.data user.display_name = form.display_name.data user.forums_username = nonEmptyOrNone(form.forums_username.data) user.github_username = nonEmptyOrNone(form.github_username.data) if user.checkPerm(current_user, Permission.CHANGE_RANK): newRank = form["rank"].data if current_user.rank.atLeast(newRank): if newRank != user.rank: user.rank = form["rank"].data msg = "Set rank of {} to {}".format( user.display_name, user.rank.getTitle()) addAuditLog(AuditSeverity.MODERATION, current_user, msg, url_for("users.profile", username=username)) else: flash("Can't promote a user to a rank higher than yourself!", "danger") db.session.commit() return redirect(url_for("users.account", username=username)) return render_template("users/account.html", user=user, form=form, tabs=get_setting_tabs(user), current_tab="account")
def send_bulk_email(): form = SendEmailForm(request.form) if form.validate_on_submit(): addAuditLog(AuditSeverity.MODERATION, current_user, "Sent bulk email", None, None, form.text.data) text = form.text.data html = render_markdown(text) for user in User.query.filter(User.email != None).all(): send_user_email.delay(user.email, form.subject.data, text, html) return redirect(url_for("admin.admin_page")) return render_template("admin/send_bulk_email.html", form=form)
def send_bulk_notification(): form = SendNotificationForm(request.form) if form.validate_on_submit(): addAuditLog(AuditSeverity.MODERATION, current_user, "Sent bulk notification", None, None, form.title.data) users = User.query.filter(User.rank >= UserRank.NEW_MEMBER).all() addNotification(users, current_user, NotificationType.OTHER, form.title.data, form.url.data, None) db.session.commit() return redirect(url_for("admin.admin_page")) return render_template("admin/send_bulk_notification.html", form=form)
def verify_email(): token = request.args.get("token") ver: UserEmailVerification = UserEmailVerification.query.filter_by( token=token).first() if ver is None: flash("Unknown verification token!", "danger") return redirect(url_for("homepage.home")) user = ver.user addAuditLog(AuditSeverity.USER, user, "Confirmed their email", url_for("users.profile", username=user.username)) was_activating = not user.is_active if ver.email and user.email != ver.email: if User.query.filter_by(email=ver.email).count() > 0: flash("Another user is already using that email", "danger") return redirect(url_for("homepage.home")) flash("Confirmed email change", "success") if user.email: send_user_email.delay( user.email, "Email address changed", "Your email address has changed. If you didn't request this, please contact an administrator." ) user.is_active = True user.email = ver.email db.session.delete(ver) db.session.commit() if ver.is_password_reset: login_user(user) user.password = None db.session.commit() return redirect(url_for("users.set_password")) if current_user.is_authenticated: return redirect( url_for("users.profile", username=current_user.username)) elif was_activating: flash("You may now log in", "success") return redirect(url_for("users.login")) else: return redirect(url_for("homepage.home"))
def apply_all_updates(username): user: User = User.query.filter_by(username=username).first() if not user: abort(404) if current_user != user and not current_user.rank.atLeast(UserRank.EDITOR): abort(403) outdated_packages = user.maintained_packages \ .filter(Package.state != PackageState.DELETED, Package.update_config.has(PackageUpdateConfig.outdated_at.isnot(None))) \ .order_by(db.asc(Package.title)).all() for package in outdated_packages: if not package.checkPerm(current_user, Permission.MAKE_RELEASE): continue if package.releases.filter( or_( PackageRelease.task_id.isnot(None), PackageRelease.commit_hash == package.update_config.last_commit)).count() > 0: continue title = package.update_config.get_title() ref = package.update_config.get_ref() rel = PackageRelease() rel.package = package rel.title = title rel.url = "" rel.task_id = uuid() db.session.add(rel) db.session.commit() makeVCSRelease.apply_async((rel.id, ref), task_id=rel.task_id) msg = "Created release {} (Applied all Git Update Detection)".format( rel.title) addNotification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg, rel.getEditURL(), package) addAuditLog(AuditSeverity.NORMAL, current_user, msg, package.getDetailsURL(), package) db.session.commit() return redirect(url_for("todo.view_user", username=username))
def callback(oauth_token): next_url = request.args.get("next") if oauth_token is None: flash("Authorization failed [err=gh-oauth-login-failed]", "danger") return redirect(url_for("users.login")) # Get Github username url = "https://api.github.com/user" r = requests.get(url, headers={"Authorization": "token " + oauth_token}) username = r.json()["login"] # Get user by github username userByGithub = User.query.filter( func.lower(User.github_username) == func.lower(username)).first() # If logged in, connect if current_user and current_user.is_authenticated: if userByGithub is None: current_user.github_username = username db.session.commit() flash("Linked github to account", "success") return redirect(url_for("homepage.home")) else: flash("Github account is already associated with another user", "danger") return redirect(url_for("homepage.home")) # If not logged in, log in else: if userByGithub is None: flash("Unable to find an account for that Github user", "danger") return redirect(url_for("users.claim_forums")) elif login_user_set_active(userByGithub, remember=True): addAuditLog( AuditSeverity.USER, userByGithub, "Logged in using GitHub OAuth", url_for("users.profile", username=userByGithub.username)) db.session.commit() if not current_user.password: return redirect( next_url or url_for("users.set_password", optional=True)) else: return redirect(next_url or url_for("homepage.home")) else: flash("Authorization failed [err=gh-login-failed]", "danger") return redirect(url_for("users.login"))
def handle_email_notifications(user, prefs: UserNotificationPreferences, is_new, form): for notificationType in NotificationType: field_email = getattr(form, "pref_" + notificationType.toName()).data field_digest = getattr(form, "pref_" + notificationType.toName() + "_digest").data or field_email prefs.set_can_email(notificationType, field_email) prefs.set_can_digest(notificationType, field_digest) if is_new: db.session.add(prefs) if user.checkPerm(current_user, Permission.CHANGE_EMAIL): newEmail = form.email.data if newEmail and newEmail != user.email and newEmail.strip() != "": if EmailSubscription.query.filter_by(email=form.email.data, blacklisted=True).count() > 0: flash( "That email address has been unsubscribed/blacklisted, and cannot be used", "danger") return token = randomString(32) severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION msg = "Changed email of {}".format(user.display_name) addAuditLog(severity, current_user, msg, url_for("users.profile", username=user.username)) ver = UserEmailVerification() ver.user = user ver.token = token ver.email = newEmail db.session.add(ver) db.session.commit() flash("Check your email to confirm it", "success") send_verify_email.delay(newEmail, token) return redirect( url_for("users.email_notifications", username=user.username)) db.session.commit() return redirect( url_for("users.email_notifications", username=user.username))
def profile_edit(username): user : User = User.query.filter_by(username=username).first() if not user: abort(404) if not user.can_see_edit_profile(current_user): flash("Permission denied", "danger") return redirect(url_for("users.profile", username=username)) form = UserProfileForm(formdata=request.form, obj=user) # Process valid POST if request.method=="POST" and form.validate(): severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION addAuditLog(severity, current_user, "Edited {}'s profile".format(user.display_name), url_for("users.profile", username=username)) # Copy form fields to user_profile fields if user.checkPerm(current_user, Permission.CHANGE_USERNAMES): user.display_name = form.display_name.data user.forums_username = nonEmptyOrNone(form.forums_username.data) user.github_username = nonEmptyOrNone(form.github_username.data) if user.checkPerm(current_user, Permission.CHANGE_PROFILE_URLS): user.website_url = form["website_url"].data user.donate_url = form["donate_url"].data if user.checkPerm(current_user, Permission.CHANGE_RANK): newRank = form["rank"].data if current_user.rank.atLeast(newRank): if newRank != user.rank: user.rank = form["rank"].data msg = "Set rank of {} to {}".format(user.display_name, user.rank.getTitle()) addAuditLog(AuditSeverity.MODERATION, current_user, msg, url_for("users.profile", username=username)) else: flash("Can't promote a user to a rank higher than yourself!", "danger") # Save user_profile db.session.commit() return redirect(url_for("users.profile", username=username)) # Process GET or invalid POST return render_template("users/profile_edit.html", user=user, form=form, tabs=get_setting_tabs(user), current_tab="edit_profile")
def do_create_zip_release(user: User, package: Package, title: str, file, min_v: MinetestRelease = None, max_v: MinetestRelease = None, reason: str = None, commit_hash: str = None): check_can_create_release(user, package) if commit_hash: commit_hash = commit_hash.lower() if not (len(commit_hash) == 40 and re.match(r"^[0-9a-f]+$", commit_hash)): raise LogicError( 400, "Invalid commit hash; it must be a 40 character long base16 string" ) uploaded_url, uploaded_path = upload_file(file, "zip", "a zip file") rel = PackageRelease() rel.package = package rel.title = title rel.url = uploaded_url rel.task_id = uuid() rel.commit_hash = commit_hash rel.min_rel = min_v rel.max_rel = max_v db.session.add(rel) if reason is None: msg = "Created release {}".format(rel.title) else: msg = "Created release {} ({})".format(rel.title, reason) addAuditLog(AuditSeverity.NORMAL, user, msg, package.getURL("packages.view"), package) db.session.commit() checkZipRelease.apply_async((rel.id, uploaded_path), task_id=rel.task_id) return rel
def send_email(username): user = User.query.filter_by(username=username).first() if user is None: abort(404) next_url = url_for("users.profile", username=user.username) if user.email is None: flash("User has no email address!", "danger") return redirect(next_url) form = SendEmailForm(request.form) if form.validate_on_submit(): addAuditLog(AuditSeverity.MODERATION, current_user, "Sent email to {}".format(user.display_name), url_for("users.profile", username=username)) text = form.text.data html = render_markdown(text) task = sendEmailRaw.delay([user.email], form.subject.data, text, html) return redirect(url_for("tasks.check", id=task.id, r=next_url)) return render_template("users/send_email.html", form=form)
def edit_reply(id): thread = Thread.query.get(id) if thread is None: abort(404) reply_id = request.args.get("reply") if reply_id is None: abort(404) reply = ThreadReply.query.get(reply_id) if reply is None or reply.thread != thread: abort(404) if not reply.checkPerm(current_user, Permission.EDIT_REPLY): abort(403) form = CommentForm(formdata=request.form, obj=reply) if form.validate_on_submit(): comment = form.comment.data msg = "Edited reply by {}".format(reply.author.display_name) severity = AuditSeverity.NORMAL if current_user == reply.author else AuditSeverity.MODERATION addNotification(reply.author, current_user, NotificationType.OTHER, msg, thread.getViewURL(), thread.package) addAuditLog(severity, current_user, msg, thread.getViewURL(), thread.package, reply.comment) reply.comment = comment db.session.commit() return redirect(thread.getViewURL()) return render_template("threads/edit_reply.html", thread=thread, reply=reply, form=form)
def delete_thread(id): thread = Thread.query.get(id) if thread is None or not thread.checkPerm(current_user, Permission.DELETE_THREAD): abort(404) if request.method == "GET": return render_template("threads/delete_thread.html", thread=thread) summary = "\n\n".join([("<{}> {}".format(reply.author.display_name, reply.comment)) for reply in thread.replies]) msg = "Deleted thread {} by {}".format(thread.title, thread.author.display_name) db.session.delete(thread) addAuditLog(AuditSeverity.MODERATION, current_user, msg, None, thread.package, summary) db.session.commit() return redirect(url_for("homepage.home"))
def profile(username): user = User.query.filter_by(username=username).first() if not user: abort(404) form = None if user.checkPerm(current_user, Permission.CHANGE_USERNAMES) or \ user.checkPerm(current_user, Permission.CHANGE_EMAIL) or \ user.checkPerm(current_user, Permission.CHANGE_RANK): # Initialize form form = UserProfileForm(formdata=request.form, obj=user) # Process valid POST if request.method == "POST" and form.validate(): severity = AuditSeverity.NORMAL if current_user == user else AuditSeverity.MODERATION addAuditLog(severity, current_user, "Edited {}'s profile".format(user.display_name), url_for("users.profile", username=username)) # Copy form fields to user_profile fields if user.checkPerm(current_user, Permission.CHANGE_USERNAMES): user.display_name = form.display_name.data user.forums_username = nonEmptyOrNone( form.forums_username.data) user.github_username = nonEmptyOrNone( form.github_username.data) if user.checkPerm(current_user, Permission.CHANGE_PROFILE_URLS): user.website_url = form["website_url"].data user.donate_url = form["donate_url"].data if user.checkPerm(current_user, Permission.CHANGE_RANK): newRank = form["rank"].data if current_user.rank.atLeast(newRank): if newRank != user.rank: user.rank = form["rank"].data msg = "Set rank of {} to {}".format( user.display_name, user.rank.getTitle()) addAuditLog( AuditSeverity.MODERATION, current_user, msg, url_for("users.profile", username=username)) else: flash( "Can't promote a user to a rank higher than yourself!", "danger") if user.checkPerm(current_user, Permission.CHANGE_EMAIL): newEmail = form["email"].data if newEmail != user.email and newEmail.strip() != "": token = randomString(32) msg = "Changed email of {}".format(user.display_name) addAuditLog(severity, current_user, msg, url_for("users.profile", username=username)) ver = UserEmailVerification() ver.user = user ver.token = token ver.email = newEmail db.session.add(ver) db.session.commit() task = sendVerifyEmail.delay(newEmail, token) return redirect( url_for("tasks.check", id=task.id, r=url_for("users.profile", username=username))) # Save user_profile db.session.commit() # Redirect to home page return redirect(url_for("users.profile", username=username)) packages = user.packages.filter(Package.state != PackageState.DELETED) if not current_user.is_authenticated or ( user != current_user and not current_user.canAccessTodoList()): packages = packages.filter_by(state=PackageState.APPROVED) packages = packages.order_by(db.asc(Package.title)) topics_to_add = None if current_user == user or user.checkPerm(current_user, Permission.CHANGE_AUTHOR): topics_to_add = ForumTopic.query \ .filter_by(author_id=user.id) \ .filter(~ db.exists().where(Package.forums==ForumTopic.topic_id)) \ .order_by(db.asc(ForumTopic.name), db.asc(ForumTopic.title)) \ .all() # Process GET or invalid POST return render_template("users/profile.html", user=user, form=form, packages=packages, topics_to_add=topics_to_add)
def do_edit_package(user: User, package: Package, was_new: bool, data: dict, reason: str = None): if not package.checkPerm(user, Permission.EDIT_PACKAGE): raise LogicError(403, "You do not have permission to edit this package") if "name" in data and package.name != data["name"] and \ not package.checkPerm(user, Permission.CHANGE_NAME): raise LogicError( 403, "You do not have permission to change the package name") for alias, to in ALIASES.items(): if alias in data: data[to] = data[alias] validate(data) if "type" in data: data["type"] = PackageType.coerce(data["type"]) if "license" in data: data["license"] = get_license(data["license"]) if "media_license" in data: data["media_license"] = get_license(data["media_license"]) for key in [ "name", "title", "short_desc", "desc", "type", "license", "media_license", "repo", "website", "issueTracker", "forums" ]: if key in data: setattr(package, key, data[key]) if package.type == PackageType.TXP: package.license = package.media_license if was_new and package.type == PackageType.MOD: m = MetaPackage.GetOrCreate(package.name, {}) package.provides.append(m) if "tags" in data: package.tags.clear() for tag_id in data["tags"]: if is_int(tag_id): package.tags.append(Tag.query.get(tag_id)) else: tag = Tag.query.filter_by(name=tag_id).first() if tag is None: raise LogicError(400, "Unknown tag: " + tag_id) package.tags.append(tag) if "content_warnings" in data: package.content_warnings.clear() for warning_id in data["content_warnings"]: if is_int(warning_id): package.content_warnings.append( ContentWarning.query.get(warning_id)) else: warning = ContentWarning.query.filter_by( name=warning_id).first() if warning is None: raise LogicError(400, "Unknown warning: " + warning_id) package.content_warnings.append(warning) if not was_new: if reason is None: msg = "Edited {}".format(package.title) else: msg = "Edited {} ({})".format(package.title, reason) severity = AuditSeverity.NORMAL if user in package.maintainers else AuditSeverity.EDITOR addAuditLog(severity, user, msg, package.getDetailsURL(), package) db.session.commit() return package