diff --git a/app/models/post.py b/app/models/post.py index cac722d..85d2dea 100644 --- a/app/models/post.py +++ b/app/models/post.py @@ -10,7 +10,7 @@ class Post(db.Model): # Unique Post ID for the whole site id = db.Column(db.Integer, primary_key=True) # Post type (polymorphic discriminator) - type = db.Column(db.String(20)) + type = db.Column(db.String(20), index=True) # Creation and edition date date_created = db.Column(db.DateTime) diff --git a/app/models/user.py b/app/models/user.py index 6e31c62..325040d 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -3,11 +3,17 @@ from flask_login import UserMixin from sqlalchemy import func as SQLfunc from os.path import isfile from PIL import Image + from app import app, db from app.models.priv import SpecialPrivilege, Group, GroupMember, \ GroupPrivilege from app.models.trophy import Trophy, TrophyMember, Title from app.models.notification import Notification +from app.models.post import Post +from app.models.comment import Comment +from app.models.topic import Topic +from app.models.program import Program + import app.utils.unicode_names as unicode_names import app.utils.ldap as ldap from app.utils.unicode_names import normalize @@ -113,9 +119,15 @@ class Member(User): # Relations trophies = db.relationship('Trophy', secondary=TrophyMember, back_populates='owners') - topics = db.relationship('Topic') - programs = db.relationship('Program') - comments = db.relationship('Comment') + + # Access to polymorphic posts + # TODO: Check that the query uses the double index on Post.{author_id,type} + def comments(self): + return db.session.query(Comment).filter(Post.author_id==self.id).all() + def topics(self): + return db.session.query(Topic).filter(Post.author_id==self.id).all() + def programs(self): + return db.session.query(Program).filter(Post.author_id==self.id).all() # Other fields populated automatically through relations: # List of unseen notifications (of type Notification) @@ -129,7 +141,7 @@ class Member(User): self.email_confirmed = not V5Config.ENABLE_EMAIL_CONFIRMATION if not V5Config.USE_LDAP: self.set_password(password) - # Workflow with LDAP enabled is User → Postgresql → LDAP → set password + # Workflow with LDAP enabled is User → PostgreSQL → LDAP → set password self.xp = 0 self.theme = 'default_theme' @@ -149,23 +161,23 @@ class Member(User): Transfers all the posts to another user. This is generally used to transfer ownership to a newly-created Guest before deleting an account. """ - for t in self.topics: + for t in self.topics(): t.author = other db.session.add(t) - for p in self.programs: + for p in self.programs(): p.author = other db.session.add(p) - for c in self.comments: + for c in self.comments(): c.author = other db.session.add(c) def delete_posts(self): """Deletes the user's posts.""" - for t in self.topics: + for t in self.topics(): t.delete() - for p in self.programs: + for p in self.programs(): p.delete() - for c in self.comments: + for c in self.comments(): c.delete() def delete(self): @@ -450,7 +462,7 @@ class Member(User): progress(levels, post_count) if context in ["new-program", None]: - program_count = len(self.programs) + program_count = len(self.programs()) levels = { 5: "Programmeur du dimanche", diff --git a/app/routes/admin/account.py b/app/routes/admin/account.py index 3cd065f..3cf49d5 100644 --- a/app/routes/admin/account.py +++ b/app/routes/admin/account.py @@ -128,9 +128,9 @@ def adm_delete_account(user_id): # TODO: Number of comments by *other* members which will be deleted stats = { - 'comments': len(user.comments), - 'topics': len(user.topics), - 'programs': len(user.programs), + 'comments': len(user.comments()), + 'topics': len(user.topics()), + 'programs': len(user.programs()), 'groups': len(user.groups), 'privs': len(user.special_privileges()), } diff --git a/app/static/css/global.css b/app/static/css/global.css index ef8577e..e96db7f 100644 --- a/app/static/css/global.css +++ b/app/static/css/global.css @@ -45,6 +45,9 @@ a:focus { text-decoration: underline; outline: none; } +img.pixelated { + image-rendering: pixelated; +} section p { line-height: 20px; word-wrap: anywhere; @@ -117,6 +120,24 @@ input[type="submit"]:focus { .bg-warn:active { background: var(--warn-active); } +.align-left { + text-align: left; +} +.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} +.align-right { + display: block; + margin-left: auto; +} +.float-left { + float: left; +} +.float-right { + float: right; +} .skip-to-content-link { height: 30px; left: 50%; diff --git a/app/static/css/themes/FK_dark_theme.css b/app/static/css/themes/FK_dark_theme.css index 2769142..1b56d85 100644 --- a/app/static/css/themes/FK_dark_theme.css +++ b/app/static/css/themes/FK_dark_theme.css @@ -135,3 +135,11 @@ div.editor-toolbar, div.CodeMirror { --separator: #404040; --text-disabled: #262c2f; } + +.dl-button { + --link: #149641; + --link-text: #ffffff; + --link-active: #0f7331; + --meta: rgba(255, 255, 255, .15); + --meta-text: #ffffff; +} diff --git a/app/static/css/themes/default_theme.css b/app/static/css/themes/default_theme.css index 1c04ac1..c287756 100644 --- a/app/static/css/themes/default_theme.css +++ b/app/static/css/themes/default_theme.css @@ -125,6 +125,14 @@ div.editor-toolbar, div.CodeMirror { --text-disabled: #c0c0c0; } +.dl-button { + --link: #149641; + --link-text: #ffffff; + --link-active: #0f7331; + --meta: rgba(0, 0, 0, .15); + --meta-text: #000000; +} + /* Extra style on top of the Pygments style */ table.codehilitetable td.linenos { color: #808080; diff --git a/app/static/css/widgets.css b/app/static/css/widgets.css index eab0e4e..316ac9d 100644 --- a/app/static/css/widgets.css +++ b/app/static/css/widgets.css @@ -86,6 +86,9 @@ height: 64px; } } +hr.signature { + opacity: 0.2; +} .trophies { display: flex; flex-wrap: wrap; @@ -125,6 +128,7 @@ .trophy span { font-size: 80%; } + hr.signature { opacity: 0.2; } @@ -173,4 +177,34 @@ hr.signature { } .gallery-spot { margin: 10px auto; -} \ No newline at end of file +.dl-button { + display: inline-flex; + flex-direction: row; + align-items: stretch; + border-radius: 5px; + overflow: hidden; + margin: 3px 5px; + vertical-align: middle; +} +.dl-button a { + display: flex; + align-items: center; + padding: 5px 15px; + font-size: 110%; + background: var(--link); + color: var(--link-text); +} +.dl-button a:hover, +.dl-button a:focus, +.dl-button a:active { + background: var(--link-active); + text-decoration: none; +} +.dl-button span { + display: flex; + align-items: center; + padding: 5px 8px; + background: var(--meta); + color: var(--meta-text); + font-size: 90%; +} diff --git a/app/static/less/global.less b/app/static/less/global.less index 7459f26..cfbf990 100644 --- a/app/static/less/global.less +++ b/app/static/less/global.less @@ -40,6 +40,10 @@ a { } } +img.pixelated { + image-rendering: pixelated; +} + section { p { line-height: 20px; @@ -113,6 +117,25 @@ section { } } +.align-left { + text-align: left; +} +.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} +.align-right { + display: block; + margin-left: auto; +} +.float-left { + float: left; +} +.float-right { + float: right; +} + .skip-to-content-link { height: 30px; diff --git a/app/static/less/widgets.less b/app/static/less/widgets.less index e9703fa..d2f6315 100644 --- a/app/static/less/widgets.less +++ b/app/static/less/widgets.less @@ -109,6 +109,11 @@ } } +hr.signature { + opacity: 0.2; +} + + /* Trophies */ .trophies { display: flex; @@ -157,8 +162,33 @@ } } -hr.signature { - opacity: 0.2; + +/* Download button */ +.dl-button { + display: inline-flex; flex-direction: row; align-items: stretch; + + border-radius: 5px; overflow: hidden; + margin: 3px 5px; vertical-align: middle; + + a { + display:flex; align-items:center; + padding: 5px 15px; + + font-size: 110%; + background: var(--link); color: var(--link-text); + + &:hover, &:focus, &:active { + background: var(--link-active); + text-decoration: none; + } + } + + span { + display: flex; align-items:center; + padding: 5px 8px; + background: var(--meta); color: var(--meta-text); + font-size: 90%; + } } /* Gallery without Javascript */ diff --git a/app/static/scripts/trigger_menu.js b/app/static/scripts/trigger_menu.js index ed00cf5..7c3fcfa 100644 --- a/app/static/scripts/trigger_menu.js +++ b/app/static/scripts/trigger_menu.js @@ -69,5 +69,5 @@ var keyboard_trigger = function(event) { } } -document.onclick = mouse_trigger; -document.onkeypress = keyboard_trigger; +document.addEventListener("click", mouse_trigger); +document.addEventListener("keydown", keyboard_trigger); diff --git a/app/templates/account/user.html b/app/templates/account/user.html index 1df1a06..4db27fc 100644 --- a/app/templates/account/user.html +++ b/app/templates/account/user.html @@ -68,7 +68,7 @@ Forum Création - {% for t in member.topics %} + {% for t in member.topics() %} {{ t.title }} {{ t.forum.name }} diff --git a/app/templates/admin/delete_account.html b/app/templates/admin/delete_account.html index a22b3be..4bd6fa1 100644 --- a/app/templates/admin/delete_account.html +++ b/app/templates/admin/delete_account.html @@ -11,7 +11,7 @@

Les propriétés suivantes seront supprimées :