From 204858d011dfddd0cadb256e124b126186b05d4d Mon Sep 17 00:00:00 2001 From: Jeremy Zhang Date: Thu, 7 Dec 2017 06:49:32 +0000 Subject: [PATCH] Add user cards and badges, Closes #25 --- ...97040e_added_badges_column_to_cosmetics.py | 28 ++++++++ webapp/run.py | 3 + webapp/titanembeds/blueprints/admin/admin.py | 14 ++++ webapp/titanembeds/blueprints/api/api.py | 7 +- webapp/titanembeds/blueprints/user/user.py | 4 +- webapp/titanembeds/database/__init__.py | 2 +- webapp/titanembeds/database/cosmetics.py | 35 ++++++++- webapp/titanembeds/static/css/embedstyle.css | 67 +++++++++++++++++ .../titanembeds/static/js/admin_cosmetics.js | 23 +++++- webapp/titanembeds/static/js/embed.js | 72 ++++++++++++++++++- .../templates/admin_cosmetics.html.j2 | 24 +++++++ webapp/titanembeds/templates/embed.html.j2 | 24 +++++++ 12 files changed, 293 insertions(+), 10 deletions(-) create mode 100644 webapp/alembic/versions/66971a97040e_added_badges_column_to_cosmetics.py diff --git a/webapp/alembic/versions/66971a97040e_added_badges_column_to_cosmetics.py b/webapp/alembic/versions/66971a97040e_added_badges_column_to_cosmetics.py new file mode 100644 index 0000000..629bd35 --- /dev/null +++ b/webapp/alembic/versions/66971a97040e_added_badges_column_to_cosmetics.py @@ -0,0 +1,28 @@ +"""Added badges column to cosmetics + +Revision ID: 66971a97040e +Revises: 16b4fdbbe155 +Create Date: 2017-12-07 04:30:25.639794 + +""" + +# revision identifiers, used by Alembic. +revision = '66971a97040e' +down_revision = '16b4fdbbe155' +branch_labels = None +depends_on = None + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('cosmetics', sa.Column('badges', sa.String(length=255), server_default='[]', nullable=False)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('cosmetics', 'badges') + # ### end Alembic commands ### diff --git a/webapp/run.py b/webapp/run.py index 7f2f6f3..b067fc5 100644 --- a/webapp/run.py +++ b/webapp/run.py @@ -7,6 +7,9 @@ def init_debug(): from flask import jsonify, request os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' # Testing oauthlib + + app.jinja_env.auto_reload = True + app.config['TEMPLATES_AUTO_RELOAD'] = True # Session viewer https://gist.github.com/babldev/502364a3f7c9bafaa6db def decode_flask_cookie(secret_key, cookie_str): diff --git a/webapp/titanembeds/blueprints/admin/admin.py b/webapp/titanembeds/blueprints/admin/admin.py index a640de1..f2d7c45 100644 --- a/webapp/titanembeds/blueprints/admin/admin.py +++ b/webapp/titanembeds/blueprints/admin/admin.py @@ -4,6 +4,7 @@ from functools import wraps from titanembeds.database import db, get_administrators_list, Cosmetics, Guilds, UnauthenticatedUsers, UnauthenticatedBans, TitanTokens, TokenTransactions, get_titan_token, set_titan_token from titanembeds.oauth import generate_guild_icon_url import datetime +import json admin = Blueprint("admin", __name__) @@ -39,6 +40,7 @@ def cosmetics_post(): css = request.form.get("css", None) css_limit = int(request.form.get("css_limit", 0)) guest_icon = request.form.get("guest_icon", None) + badges = request.form.get("badges", None) entry = db.session.query(Cosmetics).filter(Cosmetics.user_id == user_id).first() if entry: abort(409) @@ -51,6 +53,11 @@ def cosmetics_post(): if guest_icon is not None: guest_icon = guest_icon.lower() == "true" user.guest_icon = guest_icon + if badges is not None: + badges = badges.split(",") + if badges == [""]: + badges = [] + user.badges = json.dumps(badges) db.session.add(user) db.session.commit() return ('', 204) @@ -77,6 +84,7 @@ def cosmetics_patch(): css = request.form.get("css", None) css_limit = request.form.get("css_limit", None) guest_icon = request.form.get("guest_icon", None) + badges = request.form.get("badges", None) entry = db.session.query(Cosmetics).filter(Cosmetics.user_id == user_id).first() if not entry: abort(409) @@ -88,8 +96,14 @@ def cosmetics_patch(): if guest_icon: guest_icon = guest_icon.lower() == "true" entry.guest_icon = guest_icon + if badges is not None: + badges = badges.split(",") + if badges == [""]: + badges = [] + entry.badges = json.dumps(badges) db.session.commit() return ('', 204) + def prepare_guild_members_list(members, bans): all_users = [] ip_pool = [] diff --git a/webapp/titanembeds/blueprints/api/api.py b/webapp/titanembeds/blueprints/api/api.py index ab4d209..fd8a946 100644 --- a/webapp/titanembeds/blueprints/api/api.py +++ b/webapp/titanembeds/blueprints/api/api.py @@ -1,4 +1,4 @@ -from titanembeds.database import db, Guilds, UnauthenticatedUsers, UnauthenticatedBans, AuthenticatedUsers, KeyValueProperties, GuildMembers, Messages, get_channel_messages, list_all_guild_members, get_guild_member, get_administrators_list +from titanembeds.database import db, Guilds, UnauthenticatedUsers, UnauthenticatedBans, AuthenticatedUsers, KeyValueProperties, GuildMembers, Messages, get_channel_messages, list_all_guild_members, get_guild_member, get_administrators_list, get_badges from titanembeds.decorators import valid_session_required, discord_users_only from titanembeds.utils import check_guild_existance, guild_accepts_visitors, guild_query_unauth_users_bool, get_client_ipaddr, discord_api, rate_limiter, channel_ratelimit_key, guild_ratelimit_key, user_unauthenticated, checkUserRevoke, checkUserBanned, update_user_status, check_user_in_guild, get_guild_channels, guild_webhooks_enabled, guild_unauthcaptcha_enabled from titanembeds.oauth import user_has_permission, generate_avatar_url, check_user_can_administrate_guild @@ -429,6 +429,10 @@ def create_authenticated_user(): response.status_code = 403 return response +@api.route("/badges/") +def badges(user_id): + return jsonify(get_badges(user_id)) + def canCleanupDB(): canclean = False if request.form.get("secret", None) == config['app-secret']: @@ -437,6 +441,7 @@ def canCleanupDB(): if session['user_id'] in get_administrators_list(): canclean = True return canclean + @api.route("/cleanup-db", methods=["DELETE"]) def cleanup_keyval_db(): if canCleanupDB(): diff --git a/webapp/titanembeds/blueprints/user/user.py b/webapp/titanembeds/blueprints/user/user.py index 37b18d6..89df165 100644 --- a/webapp/titanembeds/blueprints/user/user.py +++ b/webapp/titanembeds/blueprints/user/user.py @@ -3,7 +3,7 @@ from flask import current_app as app from flask_socketio import emit from config import config from titanembeds.decorators import discord_users_only -from titanembeds.database import db, Guilds, UnauthenticatedUsers, UnauthenticatedBans, Cosmetics, UserCSS, Patreon, set_titan_token, get_titan_token +from titanembeds.database import db, Guilds, UnauthenticatedUsers, UnauthenticatedBans, Cosmetics, UserCSS, Patreon, set_titan_token, get_titan_token, add_badge from titanembeds.oauth import authorize_url, token_url, make_authenticated_session, get_current_authenticated_user, get_user_managed_servers, check_user_can_administrate_guild, check_user_permission, generate_avatar_url, generate_guild_icon_url, generate_bot_invite_url import time import datetime @@ -431,6 +431,7 @@ def donate_confirm(): action = "PAYPAL {}".format(trans_id) set_titan_token(session["user_id"], tokens, action) session["tokens"] = get_titan_token(session["user_id"]) + add_badge(session["user_id"], "supporter") return redirect(url_for('user.donate_thanks', transaction=trans_id)) else: return redirect(url_for('index')) @@ -555,6 +556,7 @@ def patreon_sync_post(): db.session.add(dbpatreon) db.session.commit() set_titan_token(session["user_id"], usr["titan"]["eligible_tokens"], "PATREON {} [{}]".format(usr["attributes"]["full_name"], usr["id"])) + add_badge(session["user_id"], "supporter") session["tokens"] = get_titan_token(session["user_id"]) return ('', 204) diff --git a/webapp/titanembeds/database/__init__.py b/webapp/titanembeds/database/__init__.py index 01880af..c0833c5 100644 --- a/webapp/titanembeds/database/__init__.py +++ b/webapp/titanembeds/database/__init__.py @@ -9,7 +9,7 @@ from .authenticated_users import AuthenticatedUsers from .guild_members import GuildMembers, list_all_guild_members, get_guild_member from .keyvalue_properties import KeyValueProperties, set_keyvalproperty, get_keyvalproperty, getexpir_keyvalproperty, setexpir_keyvalproperty, ifexists_keyvalproperty, delete_keyvalproperty from .messages import Messages, get_channel_messages -from .cosmetics import Cosmetics +from .cosmetics import Cosmetics, set_badges, get_badges, add_badge, remove_badge from .user_css import UserCSS from .administrators import Administrators, get_administrators_list from .titan_tokens import TitanTokens, get_titan_token diff --git a/webapp/titanembeds/database/cosmetics.py b/webapp/titanembeds/database/cosmetics.py index 7b1acd8..4a615c1 100644 --- a/webapp/titanembeds/database/cosmetics.py +++ b/webapp/titanembeds/database/cosmetics.py @@ -1,4 +1,5 @@ from titanembeds.database import db +import json class Cosmetics(db.Model): __tablename__ = "cosmetics" @@ -7,6 +8,7 @@ class Cosmetics(db.Model): css = db.Column(db.Boolean(), nullable=False) # If they can create/edit custom CSS css_limit = db.Column(db.Integer, nullable=False, server_default="0") # Custom CSS Limit guest_icon = db.Column(db.Boolean(), nullable=False, server_default=db.false()) # If they can set the guest icon for all guilds + badges = db.Column(db.String(255), nullable=False, server_default="[]") # JSON list of all the badges the user has def __init__(self, user_id, **kwargs): self.user_id = user_id @@ -24,4 +26,35 @@ class Cosmetics(db.Model): if "guest_icon" in kwargs: self.guest_icon = kwargs["guest_icon"] else: - self.guest_icon = False \ No newline at end of file + self.guest_icon = False + + if "badges" in kwargs: + self.badges = json.dumps(kwargs["badges"]) + else: + self.badges = "[]" + +def set_badges(user_id, badges): + usr = db.session.query(Cosmetics).filter(Cosmetics.user_id == user_id).first() + if not usr: + usr = Cosmetics(user_id) + usr.badges = json.dumps(badges) + db.session.add(usr) + db.session.commit() + +def get_badges(user_id): + usr = db.session.query(Cosmetics).filter(Cosmetics.user_id == user_id).first() + if usr: + return json.loads(usr.badges) + return [] + +def add_badge(user_id, name): + bgs = get_badges(user_id) + if name not in bgs: + bgs.append(name) + set_badges(user_id, bgs) + +def remove_badge(user_id, name): + bgs = get_badges(user_id) + if name in bgs: + bgs.remove(name) + set_badges(user_id, bgs) \ No newline at end of file diff --git a/webapp/titanembeds/static/css/embedstyle.css b/webapp/titanembeds/static/css/embedstyle.css index 3e24d64..e8cede4 100644 --- a/webapp/titanembeds/static/css/embedstyle.css +++ b/webapp/titanembeds/static/css/embedstyle.css @@ -679,6 +679,73 @@ p.mentioned span.chatmessage { top: -5px; } +#usercard .identity { + display: inline; + vertical-align: sub; +} + +#usercard .identity .hash, #usercard .identity .discriminator { + font-size: 1.30rem; +} + +#usercard .bottag { + background-color: #5DADE2; + padding: 5px; + border-radius: 10px; + font-weight: bold; + margin-left: 7px; + font-size: 10pt; +} + +#usercard .role .bubble { + color: #cacbce; + border: 1px solid; + border-color: inherit; + border-radius: 10px; +} + +#usercard .role .text, #usercard .role .color { + margin-left: 5px; + margin-right: 5px; +} + +#usercard .role .color { + display: inline-block; + width: 10px; + height: 10px; + background-color: #cacbce; + margin-right: 0; + border-radius: 10px; +} + +#usercard .role .text { + color: lightgray; +} + +#usercard .game { + font-weight: bold; +} + +#usercard .game .text { + font-weight: normal; +} + +#usercard .badges .star { + color: yellow; +} + +#usercard .badges .supporter { + color: limegreen; +} + +#usercard .badges .administrator { + color: hotpink; +} + +#usercard .badges .partner { + color: orange; +} + #google-recaptcha { margin: 0 auto; width: 302px; diff --git a/webapp/titanembeds/static/js/admin_cosmetics.js b/webapp/titanembeds/static/js/admin_cosmetics.js index 52a9ec2..926711c 100644 --- a/webapp/titanembeds/static/js/admin_cosmetics.js +++ b/webapp/titanembeds/static/js/admin_cosmetics.js @@ -1,13 +1,13 @@ /* global $, Materialize, location */ -function postForm(user_id, css, css_limit, guest_icon) { +function postForm(user_id, css, css_limit, guest_icon, badges) { if (css_limit == "") { css_limit = 0; } var funct = $.ajax({ dataType: "json", method: "POST", - data: {"user_id": user_id, "css": css, "css_limit": css_limit, "guest_icon": guest_icon} + data: {"user_id": user_id, "css": css, "css_limit": css_limit, "guest_icon": guest_icon, "badges": badges} }); return funct.promise(); } @@ -32,6 +32,7 @@ function patchForm(user_id, param) { } $(function() { + $('select').material_select(); $("#new_submit").click(function () { var user_id = $("#new_user_id").val(); if (user_id.length < 1) { @@ -41,7 +42,8 @@ $(function() { var css_checked = $("#new_css_switch").is(':checked'); var css_limit = $("#new_css_limit").val(); var guest_icon_checked = $("#new_guest_icon_switch").is(':checked'); - var formPost = postForm(user_id, css_checked, css_limit, guest_icon_checked); + var badges = $("#new_badges > option:selected").map(function(){ return this.value }).get().join(); + var formPost = postForm(user_id, css_checked, css_limit, guest_icon_checked, badges); formPost.done(function (data) { location.reload(); }); @@ -114,4 +116,19 @@ function update_guest_icon_switch(user_id, element) { Materialize.toast('Oh no! Something has failed changing the guest icon toggle!', 10000); } }); +} + +function update_badges(user_id, element) { + var badges = $(element).val().join(); + var formPatch = patchForm(user_id, {"badges": badges}); + formPatch.done(function (data) { + Materialize.toast('Badges updated!', 10000); + }); + formPatch.fail(function (data) { + if (data.status == 409) { + Materialize.toast('This user id does not exists!', 10000); + } else { + Materialize.toast('Oh no! Something has failed changing the badges!', 10000); + } + }); } \ No newline at end of file diff --git a/webapp/titanembeds/static/js/embed.js b/webapp/titanembeds/static/js/embed.js index d433724..c4b10a9 100644 --- a/webapp/titanembeds/static/js/embed.js +++ b/webapp/titanembeds/static/js/embed.js @@ -19,6 +19,7 @@ (function () { const theme_options = ["DiscordDark", "BetterTitan"]; // All the avaliable theming names + const badges_options = ["administrator", "partner", "supporter", "star"]; // All badges avaliable var user_def_css; // Saves the user defined css var has_already_been_initially_resized = false; // keep track if the embed initially been resized @@ -134,6 +135,14 @@ return funct.promise(); } + function api_badges(user_id) { + var funct = $.ajax({ + dataType: "json", + url: "/api/badges/" + user_id, + }); + return funct.promise(); + } + $(function() { if ($("#user-defined-css").length > 0) { user_def_css = $("#user-defined-css").text(); @@ -165,6 +174,9 @@ inDuration: 400, outDuration: 400, }); + $("#usercard").modal({ + opacity: .5, + }); $("#nameplate").click(function () { $("#userembedmodal").modal("open"); @@ -607,7 +619,7 @@ var rendered_user = Mustache.render(template_user, {"id": member.id.toString() + "d", "username": member_name, "avatar": member.avatar_url}); $("#discord-members").append(rendered_user); $( "#discorduser-" + member.id.toString() + "d").click({"member_id": member.id.toString()}, function(event) { - mention_member(event.data.member_id); + openUserCard(event.data.member_id); }); if (member.color) { $( "#discorduser-" + member.id.toString() + "d").css("color", "#" + member.color); @@ -631,7 +643,7 @@ var rendered = Mustache.render(template, {"id": member.id.toString() + "a", "username": username, "avatar": member.avatar_url}); $("#embed-discord-members").append(rendered); $( "#discorduser-" + member.id.toString() + "a").click({"member_id": member.id.toString()}, function(event) { - mention_member(event.data.member_id); + openUserCard(event.data.member_id); }); } authenticated_users_list = users; @@ -679,6 +691,60 @@ }, 5000); } + function openUserCard(user_id) { + var bgs = api_badges(user_id); + bgs.done(function (data) { + for (var i = 0; i < badges_options.length; i++) { + var badge = badges_options[i]; + if (data.indexOf(badge) != -1) { + $(`#usercard .badges .${badge}`).show(); + } else { + $(`#usercard .badges .${badge}`).hide(); + } + } + }); + for (var i = 0; i < discord_users_list.length; i++) { + var usr = discord_users_list[i]; + if (usr.id == user_id) { + $("#usercard .avatar").attr("src", usr.avatar_url); + $("#usercard .identity .username").text(usr.username); + $("#usercard .identity .discriminator").text(usr.discriminator); + if (usr.bot) { + $("#usercard .bottag").show(); + } else { + $("#usercard .bottag").hide(); + } + if (usr.status == "offline") { + $("#usercard .offline-text").show(); + } else { + $("#usercard .offline-text").hide(); + } + if (usr["hoist-role"]) { + $("#usercard .role").show(); + $("#usercard .role .text").text(usr["hoist-role"].name); + if (usr.color) { + $("#usercard .role .bubble").css("color", "#" + usr.color); + $("#usercard .role .color").css("background-color", "#" + usr.color); + } + } else { + $("#usercard .role").hide(); + } + if (usr.game) { + $("#usercard .game").show(); + $("#usercard .game .text").text(usr.game.name); + } else { + $("#usercard .game").hide(); + } + $("#usercard-mention-btn").off("click"); + $("#usercard-mention-btn").click(function () { + mention_member(user_id); + $("#usercard").modal('close'); + }); + } + } + $("#usercard").modal('open'); + } + function flashElement(element) { var opacity = element.css("opacity"); for (var i = 0; i < 3; i++) { @@ -966,7 +1032,7 @@ $("#chatcontent .chatusername").last().click(function () { var discordid = $(this).parent().attr("discord_userid"); if (discordid) { - mention_member(discordid); + openUserCard(discordid); } }); $("#chatcontent p:last-child").find(".channellink").click(function () { diff --git a/webapp/titanembeds/templates/admin_cosmetics.html.j2 b/webapp/titanembeds/templates/admin_cosmetics.html.j2 index 70db705..c96683d 100644 --- a/webapp/titanembeds/templates/admin_cosmetics.html.j2 +++ b/webapp/titanembeds/templates/admin_cosmetics.html.j2 @@ -15,6 +15,7 @@ CSS CSS Limit Guest Icon + Badges Submit @@ -50,6 +51,17 @@ + +
+ +
+ Submit @@ -69,6 +81,7 @@ CSS CSS Limit Guest Icon + Badges @@ -101,6 +114,17 @@ + +
+ +
+ {% endfor %} diff --git a/webapp/titanembeds/templates/embed.html.j2 b/webapp/titanembeds/templates/embed.html.j2 index d3e3163..c5512c2 100644 --- a/webapp/titanembeds/templates/embed.html.j2 +++ b/webapp/titanembeds/templates/embed.html.j2 @@ -175,6 +175,30 @@ + +