| @@ -3,7 +3,7 @@ | |||||
| from pathlib import Path | from pathlib import Path | ||||
| from flask import Flask, flash, g, redirect, request, url_for | |||||
| from flask import Flask, abort, flash, g, redirect, request, url_for | |||||
| from flask_mako import MakoTemplates, render_template | from flask_mako import MakoTemplates, render_template | ||||
| import calefaction | import calefaction | ||||
| @@ -76,12 +76,13 @@ def logout(): | |||||
| flash(Messages.LOGGED_OUT, "success") | flash(Messages.LOGGED_OUT, "success") | ||||
| return redirect(url_for("index"), 303) | return redirect(url_for("index"), 303) | ||||
| @app.route("/test") | |||||
| @app.route("/settings/style/<style>", methods=["POST"]) | |||||
| @catch_exceptions | @catch_exceptions | ||||
| @route_restricted | @route_restricted | ||||
| def test(): | |||||
| ... | |||||
| return "Success! You are authenticated!" | |||||
| def set_style(style): | |||||
| if not auth.set_character_style(style): | |||||
| abort(404) | |||||
| return "", 204 | |||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||
| app.run(debug=True, port=8080) | app.run(debug=True, port=8080) | ||||
| @@ -196,6 +196,23 @@ class AuthManager: | |||||
| g._character_props = g.db.read_character(cid) | g._character_props = g.db.read_character(cid) | ||||
| return g._character_props.get(prop) | return g._character_props.get(prop) | ||||
| def set_character_style(self, style): | |||||
| """Update the current user's style and return whether successful.""" | |||||
| cid = self.get_character_id() | |||||
| if not cid: | |||||
| return False | |||||
| style = style.strip().lower() | |||||
| if style not in self._config.get("style.enabled"): | |||||
| return False | |||||
| self._debug("Setting style to %s for char id=%d", style, cid) | |||||
| g.db.set_character_style(cid, style) | |||||
| if hasattr(g, "_character_props"): | |||||
| delattr(g, "_character_props") | |||||
| return True | |||||
| def is_authenticated(self): | def is_authenticated(self): | ||||
| """Return whether the user has permission to access this site. | """Return whether the user has permission to access this site. | ||||
| @@ -148,6 +148,12 @@ class Database: | |||||
| res = self._conn.execute(query, (cid,)).fetchall() | res = self._conn.execute(query, (cid,)).fetchall() | ||||
| return {"name": res[0][0], "style": res[0][1]} if res else {} | return {"name": res[0][0], "style": res[0][1]} if res else {} | ||||
| def set_character_style(self, cid, style): | |||||
| """Update a character's style setting.""" | |||||
| with self._conn as conn: | |||||
| conn.execute("""UPDATE character SET character_style = ? | |||||
| WHERE character_id = ?""", (style, cid)) | |||||
| def set_auth(self, cid, token, expires, refresh): | def set_auth(self, cid, token, expires, refresh): | ||||
| """Set the authentication info for the given character.""" | """Set the authentication info for the given character.""" | ||||
| with self._conn as conn: | with self._conn as conn: | ||||
| @@ -33,9 +33,14 @@ auth: | |||||
| # SSO client secret: | # SSO client secret: | ||||
| client_secret: XXAPGc0LM6wdOJAwSNQmliZ2QhQpoBuUutQY6Rlc | client_secret: XXAPGc0LM6wdOJAwSNQmliZ2QhQpoBuUutQY6Rlc | ||||
| # Default stylesheet from static/styles/*.css; | |||||
| # one of "amarr", "caldari", "gallente", "minmatar", or create your own: | |||||
| style: null | |||||
| style: | |||||
| # Default stylesheet from static/styles/*.css: | |||||
| default: null | |||||
| enabled: | |||||
| - amarr | |||||
| - caldari | |||||
| - gallente | |||||
| - minmatar | |||||
| welcome: |- | welcome: |- | ||||
| (If you are seeing this message on the public internet, someone forgot to | (If you are seeing this message on the public internet, someone forgot to | ||||
| @@ -37,19 +37,21 @@ h1 { | |||||
| width: 100%; | width: 100%; | ||||
| } | } | ||||
| .styled-border { | |||||
| border-color: #4A4A4A; | |||||
| border-style: solid; | |||||
| } | |||||
| main, header, footer { | main, header, footer { | ||||
| background-color: rgba(0, 0, 0, 0.8); | background-color: rgba(0, 0, 0, 0.8); | ||||
| border-color: #4A4A4A; | |||||
| } | } | ||||
| main { | main { | ||||
| border-width: 1px; | border-width: 1px; | ||||
| border-style: solid; | |||||
| } | } | ||||
| header { | header { | ||||
| border-bottom-width: 1px; | |||||
| border-bottom-style: solid; | |||||
| border-width: 0 0 1px 0; | |||||
| } | } | ||||
| footer { | footer { | ||||
| @@ -57,8 +59,7 @@ footer { | |||||
| font-size: 85%; | font-size: 85%; | ||||
| text-align: center; | text-align: center; | ||||
| color: #BABABA; | color: #BABABA; | ||||
| border-top-width: 1px; | |||||
| border-top-style: solid; | |||||
| border-width: 1px 0 0; | |||||
| } | } | ||||
| header > div, footer > div { | header > div, footer > div { | ||||
| @@ -164,18 +165,6 @@ footer ul li:not(:last-child):after { | |||||
| text-decoration: none; | text-decoration: none; | ||||
| } | } | ||||
| #character-portrait { | |||||
| height: 32px; | |||||
| margin-right: 0.25em; | |||||
| box-sizing: border-box; | |||||
| border-width: 1px; | |||||
| border-style: solid; | |||||
| } | |||||
| #character-summary { | |||||
| font-size: 90%; | |||||
| } | |||||
| #flashes { | #flashes { | ||||
| margin-top: 0.5em; | margin-top: 0.5em; | ||||
| } | } | ||||
| @@ -196,6 +185,74 @@ footer ul li:not(:last-child):after { | |||||
| background-color: rgba(255, 60, 30, 0.2); | background-color: rgba(255, 60, 30, 0.2); | ||||
| } | } | ||||
| #character-portrait { | |||||
| height: 32px; | |||||
| margin-right: 0.25em; | |||||
| box-sizing: border-box; | |||||
| border-width: 1px; | |||||
| } | |||||
| #character-summary { | |||||
| font-size: 90%; | |||||
| } | |||||
| #character-options { | |||||
| position: absolute; | |||||
| margin-top: 1em; | |||||
| margin-left: -10px; | |||||
| padding: 0.5em; | |||||
| background-color: rgba(0, 0, 0, 0.8); | |||||
| border-width: 1px; | |||||
| } | |||||
| #character-options:after, #character-options:before { | |||||
| bottom: 100%; | |||||
| left: 20px; | |||||
| border: solid transparent; | |||||
| content: " "; | |||||
| height: 0; | |||||
| width: 0; | |||||
| position: absolute; | |||||
| pointer-events: none; | |||||
| } | |||||
| #character-options:after { | |||||
| border-bottom-color: black; | |||||
| border-width: 10px; | |||||
| margin-left: -10px; | |||||
| } | |||||
| #character-options:before { | |||||
| border-bottom-color: #4A4A4A; | |||||
| border-width: 12px; | |||||
| margin-left: -12px; | |||||
| } | |||||
| #style-options { | |||||
| line-height: 0; | |||||
| } | |||||
| #style-options form { | |||||
| display: inline-block; | |||||
| } | |||||
| #style-options form:not(:first-child) { | |||||
| margin-left: 0.25em; | |||||
| } | |||||
| #style-options input[type=submit] { | |||||
| display: inline-block; | |||||
| height: 24px; | |||||
| width: 24px; | |||||
| background-color: transparent; | |||||
| background-size: contain; | |||||
| background-position: center; | |||||
| background-repeat: no-repeat; | |||||
| font-size: 0; | |||||
| border: 0; | |||||
| cursor: pointer; | |||||
| } | |||||
| #error pre { | #error pre { | ||||
| white-space: pre-wrap; | white-space: pre-wrap; | ||||
| } | } | ||||
| @@ -1,3 +1,7 @@ | |||||
| var overlaps = function(needle, haystack) { | |||||
| return haystack.is(needle) || haystack.has(needle).length > 0; | |||||
| }; | |||||
| $(function() { | $(function() { | ||||
| // Install logout auto-POST form: | // Install logout auto-POST form: | ||||
| $("#logout").click(function() { | $("#logout").click(function() { | ||||
| @@ -7,4 +11,23 @@ $(function() { | |||||
| }).appendTo($("body")).submit(); | }).appendTo($("body")).submit(); | ||||
| return false; | return false; | ||||
| }); | }); | ||||
| // Toggle character options on click: | |||||
| var charopts = $("#character-options"); | |||||
| charopts.hide(); | |||||
| $("#character-portrait").click(function() { | |||||
| if (charopts.is(":visible")) { | |||||
| charopts.hide(); | |||||
| $(document).off("mouseup.charopts"); | |||||
| } else { | |||||
| charopts.show(); | |||||
| $(document).on("mouseup.charopts", function(e) { | |||||
| if (!overlaps(e.target, charopts) && | |||||
| !overlaps(e.target, $("#character-portrait"))) { | |||||
| charopts.hide(); | |||||
| $(document).off("mouseup.charopts"); | |||||
| } | |||||
| }); | |||||
| } | |||||
| }).css("cursor", "pointer"); | |||||
| }); | }); | ||||
| @@ -1,11 +1,15 @@ | |||||
| body { | body { | ||||
| background-image: url("/static/images/amarr.jpg?v=1"); | |||||
| background-image: url("/static/images/bg/amarr.jpg?v=1"); | |||||
| } | } | ||||
| main, header, footer, #character-portrait { | |||||
| .styled-border { | |||||
| border-color: #5F4A26; | border-color: #5F4A26; | ||||
| } | } | ||||
| #character-options:before { | |||||
| border-bottom-color: #5F4A26; | |||||
| } | |||||
| a { | a { | ||||
| color: #FFDE78; | color: #FFDE78; | ||||
| } | } | ||||
| @@ -1,11 +1,15 @@ | |||||
| body { | body { | ||||
| background-image: url("/static/images/caldari.jpg?v=1"); | |||||
| background-image: url("/static/images/bg/caldari.jpg?v=1"); | |||||
| } | } | ||||
| main, header, footer, #character-portrait { | |||||
| .styled-border { | |||||
| border-color: #364A5F; | border-color: #364A5F; | ||||
| } | } | ||||
| #character-options:before { | |||||
| border-bottom-color: #364A5F; | |||||
| } | |||||
| a { | a { | ||||
| color: #99CCEE; | color: #99CCEE; | ||||
| } | } | ||||
| @@ -1,11 +1,15 @@ | |||||
| body { | body { | ||||
| background-image: url("/static/images/gallente.jpg?v=1"); | |||||
| background-image: url("/static/images/bg/gallente.jpg?v=1"); | |||||
| } | } | ||||
| main, header, footer, #character-portrait { | |||||
| .styled-border { | |||||
| border-color: #365F4A; | border-color: #365F4A; | ||||
| } | } | ||||
| #character-options:before { | |||||
| border-bottom-color: #365F4A; | |||||
| } | |||||
| a { | a { | ||||
| color: #66BB99; | color: #66BB99; | ||||
| } | } | ||||
| @@ -1,11 +1,15 @@ | |||||
| body { | body { | ||||
| background-image: url("/static/images/minmatar.jpg?v=1"); | |||||
| background-image: url("/static/images/bg/minmatar.jpg?v=1"); | |||||
| } | } | ||||
| main, header, footer, #character-portrait { | |||||
| .styled-border { | |||||
| border-color: #5F3C42; | border-color: #5F3C42; | ||||
| } | } | ||||
| #character-options:before { | |||||
| border-bottom-color: #5F3C42; | |||||
| } | |||||
| a { | a { | ||||
| color: #D89988; | color: #D89988; | ||||
| } | } | ||||
| @@ -8,12 +8,12 @@ | |||||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
| <link rel="canonical" href="${g.config.scheme}://${g.config.get('site.canonical')}${request.script_root}${request.path}"> | <link rel="canonical" href="${g.config.scheme}://${g.config.get('site.canonical')}${request.script_root}${request.path}"> | ||||
| <link rel="stylesheet" type="text/css" href="${url_for('staticv', filename='main.css')}"/> | <link rel="stylesheet" type="text/css" href="${url_for('staticv', filename='main.css')}"/> | ||||
| % if g.config.get("style"): | |||||
| <% | |||||
| charstyle = g.auth.get_character_prop("style") | |||||
| style = charstyle if charstyle else g.config.get("style") | |||||
| stylesheet = "styles/{}.css".format(style) | |||||
| %> | |||||
| <% | |||||
| charstyle = g.auth.get_character_prop("style") | |||||
| style = charstyle if charstyle else g.config.get("style.default") | |||||
| %> | |||||
| % if style: | |||||
| <% stylesheet = "styles/{}.css".format(style) %> | |||||
| <link rel="stylesheet" type="text/css" href="${url_for('staticv', filename=stylesheet)}"/> | <link rel="stylesheet" type="text/css" href="${url_for('staticv', filename=stylesheet)}"/> | ||||
| % endif | % endif | ||||
| % for size in g.eve.image.corp_widths: | % for size in g.eve.image.corp_widths: | ||||
| @@ -24,7 +24,7 @@ | |||||
| </head> | </head> | ||||
| <body> | <body> | ||||
| <%block name="header"> | <%block name="header"> | ||||
| <header> | |||||
| <header class="styled-border"> | |||||
| <div> | <div> | ||||
| <div class="left"> | <div class="left"> | ||||
| <%block name="lefthead"> | <%block name="lefthead"> | ||||
| @@ -45,7 +45,7 @@ | |||||
| <%block name="container"> | <%block name="container"> | ||||
| <div id="container"> | <div id="container"> | ||||
| <div> | <div> | ||||
| <main> | |||||
| <main class="styled-border"> | |||||
| <%block name="flashes"> | <%block name="flashes"> | ||||
| <% messages = get_flashed_messages(with_categories=True) %> | <% messages = get_flashed_messages(with_categories=True) %> | ||||
| % if messages: | % if messages: | ||||
| @@ -62,7 +62,7 @@ | |||||
| </div> | </div> | ||||
| </%block> | </%block> | ||||
| <%block name="footer"> | <%block name="footer"> | ||||
| <footer> | |||||
| <footer class="styled-border"> | |||||
| <div> | <div> | ||||
| <ul> | <ul> | ||||
| <li>YC ${g.eve.clock.now()}</li> | <li>YC ${g.eve.clock.now()}</li> | ||||
| @@ -6,7 +6,16 @@ | |||||
| </nav> | </nav> | ||||
| </%block> | </%block> | ||||
| <%block name="righthead"> | <%block name="righthead"> | ||||
| <img id="character-portrait" class="aligned" title="${g.auth.get_character_prop('name')}" alt="" src="${g.eve.image.character(g.auth.get_character_id(), 256)}"/> | |||||
| <img id="character-portrait" class="styled-border aligned" alt="" src="${g.eve.image.character(g.auth.get_character_id(), 256)}"/> | |||||
| <div id="character-options" class="styled-border"> | |||||
| <div id="style-options"> | |||||
| % for style in g.config.get("style.enabled"): | |||||
| <form action="${url_for('set_style', style=style)}" method="post"> | |||||
| <input type="submit" title="${style.title()}" value="${style}" style="background-image: url('${url_for('staticv', filename='images/style/{}.png'.format(style))}')"> | |||||
| </form> | |||||
| % endfor | |||||
| </div> | |||||
| </div> | |||||
| <span id="character-summary" class="aligned"> | <span id="character-summary" class="aligned"> | ||||
| ${g.auth.get_character_prop("name")} | ${g.auth.get_character_prop("name")} | ||||
| <span class="sep">[</span><a id="logout" title="Log out" href="${url_for('logout')}">log out</a><span class="sep">]</span> | <span class="sep">[</span><a id="logout" title="Log out" href="${url_for('logout')}">log out</a><span class="sep">]</span> | ||||