From 9874727eea5a22b789e7553e6eaf9c86ef0c14a4 Mon Sep 17 00:00:00 2001 From: rimu <3310831+rimu@users.noreply.github.com> Date: Wed, 7 Feb 2024 17:31:12 +1300 Subject: [PATCH] theme engine and high contrast theme fixes #27 fixes #19 --- app/admin/forms.py | 3 + app/admin/routes.py | 2 + app/models.py | 2 + app/static/themes | 1 + app/templates/admin/activities.html | 6 +- app/templates/admin/activity_json.html | 6 +- .../admin/approve_registrations.html | 6 +- app/templates/admin/communities.html | 6 +- app/templates/admin/edit_community.html | 6 +- app/templates/admin/edit_topic.html | 6 +- app/templates/admin/edit_user.html | 6 +- app/templates/admin/federation.html | 6 +- app/templates/admin/home.html | 6 +- app/templates/admin/misc.html | 6 +- app/templates/admin/reports.html | 6 +- app/templates/admin/site.html | 6 +- app/templates/admin/topics.html | 6 +- app/templates/admin/users.html | 6 +- app/templates/auth/register.html | 6 +- app/templates/auth/reset_password.html | 6 +- .../auth/reset_password_request.html | 6 +- app/templates/base.html | 3 + app/templates/community/add_local.html | 6 +- app/templates/community/add_post.html | 6 +- app/templates/community/add_remote.html | 6 +- app/templates/community/community.html | 6 +- app/templates/community/community_delete.html | 6 +- app/templates/domain/domain.html | 6 +- app/templates/domain/domains.html | 6 +- app/templates/donate.html | 6 +- app/templates/errors/404.html | 6 +- app/templates/errors/500.html | 6 +- app/templates/index.html | 6 +- app/templates/keyboard_shortcuts.html | 6 +- app/templates/list_communities.html | 6 +- app/templates/list_topics.html | 6 +- app/templates/post/add_reply.html | 6 +- app/templates/post/continue_discussion.html | 6 +- app/templates/post/post.html | 6 +- app/templates/post/post_edit.html | 6 +- app/templates/post/post_mea_culpa.html | 6 +- app/templates/post/post_options.html | 6 +- app/templates/post/post_reply_edit.html | 6 +- app/templates/post/post_reply_options.html | 6 +- app/templates/post/post_reply_report.html | 6 +- app/templates/post/post_report.html | 6 +- app/templates/privacy.html | 6 +- .../themes/high_contrast/high_contrast.json | 3 + app/templates/themes/high_contrast/styles.css | 86 +++++++++++++++++++ app/templates/topic/show_topic.html | 6 +- app/templates/user/delete_account.html | 6 +- app/templates/user/edit_filters.html | 6 +- app/templates/user/edit_profile.html | 6 +- app/templates/user/edit_settings.html | 7 +- app/templates/user/filters.html | 6 +- app/templates/user/notifications.html | 6 +- app/templates/user/people.html | 6 +- app/templates/user/show_profile.html | 6 +- app/templates/user/user_report.html | 6 +- app/user/forms.py | 3 + app/user/routes.py | 2 + app/utils.py | 14 ++- migrations/versions/f19d761c216d_themes.py | 38 ++++++++ pyfedi.py | 4 +- 64 files changed, 419 insertions(+), 55 deletions(-) create mode 120000 app/static/themes create mode 100644 app/templates/themes/high_contrast/high_contrast.json create mode 100644 app/templates/themes/high_contrast/styles.css create mode 100644 migrations/versions/f19d761c216d_themes.py diff --git a/app/admin/forms.py b/app/admin/forms.py index 9a9dbd47..ed9a9c8f 100644 --- a/app/admin/forms.py +++ b/app/admin/forms.py @@ -29,6 +29,9 @@ class SiteMiscForm(FlaskForm): registration_mode = SelectField(_l('Registration mode'), choices=types, default=1, coerce=str) application_question = TextAreaField(_l('Question to ask people applying for an account')) log_activitypub_json = BooleanField(_l('Log ActivityPub JSON for debugging')) + themes = [('', _l('PieFed')), + ('high_contrast', _l('High contrast'))] + default_theme = SelectField(_l('Default theme'), choices=themes, coerce=str) submit = SubmitField(_l('Save')) diff --git a/app/admin/routes.py b/app/admin/routes.py index 0e2ca625..4d8c19f3 100644 --- a/app/admin/routes.py +++ b/app/admin/routes.py @@ -80,6 +80,7 @@ def admin_misc(): site.application_question = form.application_question.data site.log_activitypub_json = form.log_activitypub_json.data site.updated = utcnow() + site.default_theme = form.default_theme.data if site.id is None: db.session.add(site) db.session.commit() @@ -95,6 +96,7 @@ def admin_misc(): form.registration_mode.data = site.registration_mode form.application_question.data = site.application_question form.log_activitypub_json.data = site.log_activitypub_json + form.default_theme.data = site.default_theme if site.default_theme is not None else '' return render_template('admin/misc.html', title=_('Misc settings'), form=form, moderating_communities=moderating_communities(current_user.get_id()), joined_communities=joined_communities(current_user.get_id()), diff --git a/app/models.py b/app/models.py index 903b6dbe..821edd90 100644 --- a/app/models.py +++ b/app/models.py @@ -352,6 +352,7 @@ class User(UserMixin, db.Model): instance_id = db.Column(db.Integer, db.ForeignKey('instance.id'), index=True) reports = db.Column(db.Integer, default=0) # how many times this user has been reported. default_sort = db.Column(db.String(25), default='hot') + theme = db.Column(db.String(20), default='') avatar = db.relationship('File', lazy='joined', foreign_keys=[avatar_id], single_parent=True, cascade="all, delete-orphan") cover = db.relationship('File', lazy='joined', foreign_keys=[cover_id], single_parent=True, cascade="all, delete-orphan") @@ -1021,6 +1022,7 @@ class Site(db.Model): updated = db.Column(db.DateTime, default=utcnow) last_active = db.Column(db.DateTime, default=utcnow) log_activitypub_json = db.Column(db.Boolean, default=False) + default_theme = db.Column(db.String(20), default='') @staticmethod def admins() -> List[User]: diff --git a/app/static/themes b/app/static/themes new file mode 120000 index 00000000..3112f93b --- /dev/null +++ b/app/static/themes @@ -0,0 +1 @@ +../templates/themes \ No newline at end of file diff --git a/app/templates/admin/activities.html b/app/templates/admin/activities.html index 8f1c1372..a8f86d4b 100644 --- a/app/templates/admin/activities.html +++ b/app/templates/admin/activities.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/admin/activity_json.html b/app/templates/admin/activity_json.html index 5a2f8b7e..8b300a19 100644 --- a/app/templates/admin/activity_json.html +++ b/app/templates/admin/activity_json.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/admin/approve_registrations.html b/app/templates/admin/approve_registrations.html index 0b86f2eb..454b99f6 100644 --- a/app/templates/admin/approve_registrations.html +++ b/app/templates/admin/approve_registrations.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/admin/communities.html b/app/templates/admin/communities.html index 411659f2..548ed408 100644 --- a/app/templates/admin/communities.html +++ b/app/templates/admin/communities.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/admin/edit_community.html b/app/templates/admin/edit_community.html index c800ba89..2c5ca385 100644 --- a/app/templates/admin/edit_community.html +++ b/app/templates/admin/edit_community.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_field %} {% block app_content %} diff --git a/app/templates/admin/edit_topic.html b/app/templates/admin/edit_topic.html index db53cdc7..0a65d554 100644 --- a/app/templates/admin/edit_topic.html +++ b/app/templates/admin/edit_topic.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/admin/edit_user.html b/app/templates/admin/edit_user.html index 17fd7cff..f71b478b 100644 --- a/app/templates/admin/edit_user.html +++ b/app/templates/admin/edit_user.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_field %} {% block app_content %} diff --git a/app/templates/admin/federation.html b/app/templates/admin/federation.html index b2a82362..96bd0ca2 100644 --- a/app/templates/admin/federation.html +++ b/app/templates/admin/federation.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/admin/home.html b/app/templates/admin/home.html index 4c47482f..db0363db 100644 --- a/app/templates/admin/home.html +++ b/app/templates/admin/home.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/admin/misc.html b/app/templates/admin/misc.html index 0fe862c2..73db60b2 100644 --- a/app/templates/admin/misc.html +++ b/app/templates/admin/misc.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/admin/reports.html b/app/templates/admin/reports.html index 97827f29..67af4e06 100644 --- a/app/templates/admin/reports.html +++ b/app/templates/admin/reports.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/admin/site.html b/app/templates/admin/site.html index c12dcd50..53478d98 100644 --- a/app/templates/admin/site.html +++ b/app/templates/admin/site.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_field %} {% block app_content %} diff --git a/app/templates/admin/topics.html b/app/templates/admin/topics.html index bede4c1e..7bba2707 100644 --- a/app/templates/admin/topics.html +++ b/app/templates/admin/topics.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/admin/users.html b/app/templates/admin/users.html index 881816f7..c73eee35 100644 --- a/app/templates/admin/users.html +++ b/app/templates/admin/users.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/auth/register.html b/app/templates/auth/register.html index 4e99578d..891643ee 100644 --- a/app/templates/auth/register.html +++ b/app/templates/auth/register.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block scripts %} diff --git a/app/templates/auth/reset_password.html b/app/templates/auth/reset_password.html index 2b83b4b8..1af5d3b3 100644 --- a/app/templates/auth/reset_password.html +++ b/app/templates/auth/reset_password.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/auth/reset_password_request.html b/app/templates/auth/reset_password_request.html index 2b83b4b8..1af5d3b3 100644 --- a/app/templates/auth/reset_password_request.html +++ b/app/templates/auth/reset_password_request.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/base.html b/app/templates/base.html index ece3cbdd..5498a990 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -43,6 +43,9 @@ {% if markdown_editor %} {% endif %} + {% if theme() %} + + {% endif %} {% endblock %} {% if title %}{{ title }}{% else %}{{ _('PieFed') }}{% endif %} diff --git a/app/templates/community/add_local.html b/app/templates/community/add_local.html index eafbc744..34ad9a31 100644 --- a/app/templates/community/add_local.html +++ b/app/templates/community/add_local.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_field %} {% block app_content %} diff --git a/app/templates/community/add_post.html b/app/templates/community/add_post.html index f74cddc5..2a037418 100644 --- a/app/templates/community/add_post.html +++ b/app/templates/community/add_post.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_field %} {% block app_content %} diff --git a/app/templates/community/add_remote.html b/app/templates/community/add_remote.html index 443bafc4..368ca106 100644 --- a/app/templates/community/add_remote.html +++ b/app/templates/community/add_remote.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/community/community.html b/app/templates/community/community.html index 5b623ec1..fa79dc53 100644 --- a/app/templates/community/community.html +++ b/app/templates/community/community.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/community/community_delete.html b/app/templates/community/community_delete.html index 15a39f5b..28e1bf91 100644 --- a/app/templates/community/community_delete.html +++ b/app/templates/community/community_delete.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/domain/domain.html b/app/templates/domain/domain.html index aced33eb..bfca8ab1 100644 --- a/app/templates/domain/domain.html +++ b/app/templates/domain/domain.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/domain/domains.html b/app/templates/domain/domains.html index 705975c0..a7345f1d 100644 --- a/app/templates/domain/domains.html +++ b/app/templates/domain/domains.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/donate.html b/app/templates/donate.html index ddda0dff..947e53c8 100644 --- a/app/templates/donate.html +++ b/app/templates/donate.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% block app_content %}
diff --git a/app/templates/errors/404.html b/app/templates/errors/404.html index cda52e29..e652ca43 100644 --- a/app/templates/errors/404.html +++ b/app/templates/errors/404.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% block app_content %}
diff --git a/app/templates/errors/500.html b/app/templates/errors/500.html index e957bf7e..36c848c1 100644 --- a/app/templates/errors/500.html +++ b/app/templates/errors/500.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% block app_content %}
diff --git a/app/templates/index.html b/app/templates/index.html index d4d861ea..fab064a0 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} {% from 'bootstrap5/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/keyboard_shortcuts.html b/app/templates/keyboard_shortcuts.html index d4b29bf7..91b04df0 100644 --- a/app/templates/keyboard_shortcuts.html +++ b/app/templates/keyboard_shortcuts.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% block app_content %}
diff --git a/app/templates/list_communities.html b/app/templates/list_communities.html index 7659a089..f524a562 100644 --- a/app/templates/list_communities.html +++ b/app/templates/list_communities.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap5/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/list_topics.html b/app/templates/list_topics.html index 7c760c86..80174337 100644 --- a/app/templates/list_topics.html +++ b/app/templates/list_topics.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap5/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/post/add_reply.html b/app/templates/post/add_reply.html index f6bf7698..99908fa7 100644 --- a/app/templates/post/add_reply.html +++ b/app/templates/post/add_reply.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/post/continue_discussion.html b/app/templates/post/continue_discussion.html index 638016b6..e07e9c7a 100644 --- a/app/templates/post/continue_discussion.html +++ b/app/templates/post/continue_discussion.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/post/post.html b/app/templates/post/post.html index 41639c3f..652834fc 100644 --- a/app/templates/post/post.html +++ b/app/templates/post/post.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/post/post_edit.html b/app/templates/post/post_edit.html index a6f05676..adfdac1b 100644 --- a/app/templates/post/post_edit.html +++ b/app/templates/post/post_edit.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form, render_field %} {% block app_content %} diff --git a/app/templates/post/post_mea_culpa.html b/app/templates/post/post_mea_culpa.html index 3a630fe9..9b19caae 100644 --- a/app/templates/post/post_mea_culpa.html +++ b/app/templates/post/post_mea_culpa.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/post/post_options.html b/app/templates/post/post_options.html index 69a48521..1981a931 100644 --- a/app/templates/post/post_options.html +++ b/app/templates/post/post_options.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/post/post_reply_edit.html b/app/templates/post/post_reply_edit.html index fc0670b5..74a2af66 100644 --- a/app/templates/post/post_reply_edit.html +++ b/app/templates/post/post_reply_edit.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/post/post_reply_options.html b/app/templates/post/post_reply_options.html index 79418d65..9c77eed4 100644 --- a/app/templates/post/post_reply_options.html +++ b/app/templates/post/post_reply_options.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/post/post_reply_report.html b/app/templates/post/post_reply_report.html index ea8dcc5f..4708d851 100644 --- a/app/templates/post/post_reply_report.html +++ b/app/templates/post/post_reply_report.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/post/post_report.html b/app/templates/post/post_report.html index c706129b..3eaf0148 100644 --- a/app/templates/post/post_report.html +++ b/app/templates/post/post_report.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/privacy.html b/app/templates/privacy.html index 69ac936e..9623f1e3 100644 --- a/app/templates/privacy.html +++ b/app/templates/privacy.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% block app_content %}
diff --git a/app/templates/themes/high_contrast/high_contrast.json b/app/templates/themes/high_contrast/high_contrast.json new file mode 100644 index 00000000..a6294223 --- /dev/null +++ b/app/templates/themes/high_contrast/high_contrast.json @@ -0,0 +1,3 @@ +{ + "name": "High Contrast" +} \ No newline at end of file diff --git a/app/templates/themes/high_contrast/styles.css b/app/templates/themes/high_contrast/styles.css new file mode 100644 index 00000000..3f3c553a --- /dev/null +++ b/app/templates/themes/high_contrast/styles.css @@ -0,0 +1,86 @@ +:root { + --bs-link-color: black; + --bs-link-color-rgb: black; + --bs-link-hover-color-rgb: #333; +} + +[data-bs-theme="dark"] { + --bs-body-bg: black; + --bs-link-color: white; + --bs-link-color-rgb: white; + --bs-link-hover-color-rgb: #eee; + --bs-heading-color: white; + --bs-body-color: white; + --bs-body-color-rgb: white; +} + +.btn-primary { + --bs-btn-bg: black; + --bs-btn-border-color: black; + --bs-btn-hover-bg: #333; +} +[data-bs-theme="dark"] .btn-primary { + --bs-btn-bg: white; + --bs-btn-border-color: white; + --bs-btn-hover-bg: white; + --bs-btn-color: black; +} +[data-bs-theme="dark"] .btn-primary:hover { + --bs-btn-hover-color: black; +} + +.btn-outline-secondary { + --bs-btn-color: black; +} +[data-bs-theme="dark"] .btn-outline-secondary { + --bs-btn-color: white; +} + +.post_list .post_teaser { + border-bottom: solid 1px black; +} +[data-bs-theme="dark"] .post_list .post_teaser { + border-bottom: solid 1px white; +} + +.domain_link, .domain_link a { + color: black; +} +[data-bs-theme="dark"] .domain_link, [data-bs-theme="dark"] .domain_link a { + color: ghostwhite; +} + +.main_pane, .voting_buttons div { + border: solid 1px #333; +} +[data-bs-theme="dark"] .main_pane, [data-bs-theme="dark"] .voting_buttons div { + border: solid 1px #eee; +} + +div.navbar { + border-bottom: solid 1px #333; +} +[data-bs-theme="dark"] div.navbar { + border-bottom: solid 1px #fff; +} + +.nav-link { + color: black; +} +[data-bs-theme="dark"] .nav-link { + color: white; +} + +.card { + --bs-card-border-color: #333; +} +[data-bs-theme="dark"] .card { + --bs-card-border-color: #fff; +} + +.coolfieldset.collapsed legend, .coolfieldset legend, .coolfieldset.expanded legend { + background-color: white; +} +[data-bs-theme="dark"] .coolfieldset.collapsed legend, [data-bs-theme="dark"] .coolfieldset legend, [data-bs-theme="dark"] .coolfieldset.expanded legend { + background-color: black; +} \ No newline at end of file diff --git a/app/templates/topic/show_topic.html b/app/templates/topic/show_topic.html index 52e3db7e..49a09092 100644 --- a/app/templates/topic/show_topic.html +++ b/app/templates/topic/show_topic.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/user/delete_account.html b/app/templates/user/delete_account.html index 75a1197e..3e9d19a8 100644 --- a/app/templates/user/delete_account.html +++ b/app/templates/user/delete_account.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/user/edit_filters.html b/app/templates/user/edit_filters.html index 70189b82..accbb512 100644 --- a/app/templates/user/edit_filters.html +++ b/app/templates/user/edit_filters.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_field %} {% block app_content %} diff --git a/app/templates/user/edit_profile.html b/app/templates/user/edit_profile.html index 34c04570..a3f1ec87 100644 --- a/app/templates/user/edit_profile.html +++ b/app/templates/user/edit_profile.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_field %} {% block app_content %} diff --git a/app/templates/user/edit_settings.html b/app/templates/user/edit_settings.html index c88a0b94..7af59896 100644 --- a/app/templates/user/edit_settings.html +++ b/app/templates/user/edit_settings.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_field %} {% block app_content %} @@ -22,6 +26,7 @@ {{ render_field(form.indexable) }} {{ render_field(form.default_sort) }} {{ render_field(form.import_file) }} + {{ render_field(form.theme) }} {{ render_field(form.submit) }}   {{ _('Manage content filters') }} diff --git a/app/templates/user/filters.html b/app/templates/user/filters.html index 760ba2d6..8c38b6f6 100644 --- a/app/templates/user/filters.html +++ b/app/templates/user/filters.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_field %} {% block app_content %} diff --git a/app/templates/user/notifications.html b/app/templates/user/notifications.html index 93519566..188d153d 100644 --- a/app/templates/user/notifications.html +++ b/app/templates/user/notifications.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/user/people.html b/app/templates/user/people.html index 84201e81..64085d27 100644 --- a/app/templates/user/people.html +++ b/app/templates/user/people.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/user/show_profile.html b/app/templates/user/show_profile.html index e2a0be64..5a7bdf87 100644 --- a/app/templates/user/show_profile.html +++ b/app/templates/user/show_profile.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/templates/user/user_report.html b/app/templates/user/user_report.html index 8ec62028..6c85b404 100644 --- a/app/templates/user/user_report.html +++ b/app/templates/user/user_report.html @@ -1,4 +1,8 @@ -{% extends "base.html" %} +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} {% from 'bootstrap/form.html' import render_form %} {% block app_content %} diff --git a/app/user/forms.py b/app/user/forms.py index a5e08d37..15849655 100644 --- a/app/user/forms.py +++ b/app/user/forms.py @@ -45,6 +45,9 @@ class SettingsForm(FlaskForm): ('active', _l('Active')), ] default_sort = SelectField(_l('By default, sort posts by'), choices=sorts, validators=[DataRequired()], coerce=str) + themes = [('', _l('PieFed')), + ('high_contrast', _l('High contrast'))] + theme = SelectField(_l('Theme'), choices=themes, coerce=str) submit = SubmitField(_l('Save settings')) diff --git a/app/user/routes.py b/app/user/routes.py index a342d2df..6efe3441 100644 --- a/app/user/routes.py +++ b/app/user/routes.py @@ -160,6 +160,7 @@ def change_settings(): current_user.searchable = form.searchable.data current_user.indexable = form.indexable.data current_user.default_sort = form.default_sort.data + current_user.theme = form.theme.data import_file = request.files['import_file'] if import_file and import_file.filename != '': file_ext = os.path.splitext(import_file.filename)[1] @@ -190,6 +191,7 @@ def change_settings(): form.searchable.data = current_user.searchable form.indexable.data = current_user.indexable form.default_sort.data = current_user.default_sort + form.theme.data = current_user.theme return render_template('user/edit_settings.html', title=_('Edit profile'), form=form, user=current_user, moderating_communities=moderating_communities(current_user.get_id()), diff --git a/app/utils.py b/app/utils.py index c7470795..4e3eb6d6 100644 --- a/app/utils.py +++ b/app/utils.py @@ -29,8 +29,8 @@ from app.models import Settings, Domain, Instance, BannedInstances, User, Commun # Flask's render_template function, with support for themes added def render_template(template_name: str, **context) -> Response: - theme = get_setting('theme', '') - if theme != '': + theme = current_theme() + if theme != '' and os.path.exists(f'app/templates/themes/{theme}/{template_name}'): content = flask.render_template(f'themes/{theme}/{template_name}', **context) else: content = flask.render_template(template_name, **context) @@ -672,3 +672,13 @@ def parse_page(page_url, tags_to_search = KNOWN_OPENGRAPH_TAGS, fallback_tags = found_tags[og_tag] = soup.find(fallback_tags[og_tag]).text return found_tags + + +def current_theme(): + if current_user.is_authenticated: + if current_user.theme is not None and current_user.theme != '': + return current_user.theme + else: + return g.site.default_theme if g.site.default_theme is not None else '' + else: + return g.site.default_theme if g.site.default_theme is not None else '' diff --git a/migrations/versions/f19d761c216d_themes.py b/migrations/versions/f19d761c216d_themes.py new file mode 100644 index 00000000..ded679b7 --- /dev/null +++ b/migrations/versions/f19d761c216d_themes.py @@ -0,0 +1,38 @@ +"""themes + +Revision ID: f19d761c216d +Revises: 3fb833e34c75 +Create Date: 2024-02-07 16:13:47.846365 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'f19d761c216d' +down_revision = '3fb833e34c75' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('site', schema=None) as batch_op: + batch_op.add_column(sa.Column('default_theme', sa.String(length=20), nullable=True)) + + with op.batch_alter_table('user', schema=None) as batch_op: + batch_op.add_column(sa.Column('theme', sa.String(length=20), nullable=True)) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('user', schema=None) as batch_op: + batch_op.drop_column('theme') + + with op.batch_alter_table('site', schema=None) as batch_op: + batch_op.drop_column('default_theme') + + # ### end Alembic commands ### diff --git a/pyfedi.py b/pyfedi.py index 52d5c565..9f2988f3 100644 --- a/pyfedi.py +++ b/pyfedi.py @@ -8,7 +8,7 @@ from flask import session, g, json, request from app.constants import POST_TYPE_LINK, POST_TYPE_IMAGE, POST_TYPE_ARTICLE from app.models import Site from app.utils import getmtime, gibberish, shorten_string, shorten_url, digits, user_access, community_membership, \ - can_create, can_upvote, can_downvote, shorten_number, ap_datetime + can_create, can_upvote, can_downvote, shorten_number, ap_datetime, current_theme app = create_app() cli.register(app) @@ -39,6 +39,8 @@ with app.app_context(): app.jinja_env.globals['can_create'] = can_create app.jinja_env.globals['can_upvote'] = can_upvote app.jinja_env.globals['can_downvote'] = can_downvote + app.jinja_env.globals['theme'] = current_theme + app.jinja_env.globals['file_exists'] = os.path.exists app.jinja_env.filters['shorten'] = shorten_string app.jinja_env.filters['shorten_url'] = shorten_url