Skip to content

Commit

Permalink
global: add support for community theming
Browse files Browse the repository at this point in the history
* adds new data field called `theme`
* adds specific template loader that handles themed templates per community
* enables feature only for system user at the moment programmtically
* disables indexing of community theme information

Co-authored-by: Karolina Przerwa <[email protected]>
  • Loading branch information
2 people authored and slint committed Jan 16, 2024
1 parent b2990d2 commit 714e625
Show file tree
Hide file tree
Showing 24 changed files with 471 additions and 57 deletions.
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ include babel.ini
include docs/requirements.txt
include LICENSE
include pytest.ini
include invenio_communities/templates/semantic-ui/invenio_communities/community_theme_template.css
recursive-include docs *.bat
recursive-include docs *.py
recursive-include docs *.rst
Expand All @@ -30,6 +31,7 @@ recursive-include invenio_communities *.png
recursive-include invenio_communities *.po *.pot *.mo
recursive-include invenio_communities *.py
recursive-include invenio_communities *.tpl
recursive-include tests *.html
recursive-include tests *.json
recursive-include tests *.py
include .git-blame-ignore-revs
26 changes: 26 additions & 0 deletions invenio_communities/communities/dumpers/community_theme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
#
# This file is part of Invenio.
# Copyright (C) 2024 CERN.
#
# Invenio is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.

"""Community theme dumper.
Dumper used to remove the theme field on dump of a community from a search body.
"""

from invenio_records.dumpers import SearchDumperExt


class CommunityThemeDumperExt(SearchDumperExt):
"""Dumper to remove community theme field from indexing."""

def __init__(self, key="theme"):
"""Initialize the dumper."""
self.key = key

def dump(self, record, data):
"""Remove theme information from indexing."""
data.pop("theme", None)
2 changes: 2 additions & 0 deletions invenio_communities/communities/records/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
IsVerifiedField,
)

from ..dumpers.community_theme import CommunityThemeDumperExt
from ..dumpers.featured import FeaturedDumperExt
from . import models
from .systemfields.access import CommunityAccessField
Expand Down Expand Up @@ -58,6 +59,7 @@ class Community(Record):

dumper = SearchDumper(
extensions=[
CommunityThemeDumperExt("theme"),
FeaturedDumperExt("featured"),
RelationDumperExt("relations"),
CalculatedFieldDumperExt("is_verified"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,21 @@
}
}
},
"theme": {
"type": "object",
"description": "Community theme settings.",
"additionalProperties": false,
"properties": {
"config": {
"description": "Theme config.",
"type": "object"
},
"brand": {
"description": "Theme brand name.",
"type": "string"
}
}
},
"tombstone": {
"type": "object",
"description": "Tombstone information for the community.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
},
"ids": {
"type": "array",
"items": {"type": "string"},
"items": {
"type": "string"
},
"uniqueItems": true
}
}
Expand All @@ -27,7 +29,9 @@
},
"affiliations": {
"type": "array",
"items": {"$ref": "#/affiliation"},
"items": {
"$ref": "#/affiliation"
},
"uniqueItems": true
},
"agent": {
Expand Down Expand Up @@ -455,7 +459,9 @@
},
"subjects": {
"type": "array",
"items": {"$ref": "#/subject"},
"items": {
"$ref": "#/subject"
},
"uniqueItems": true
},
"title_type": {
Expand All @@ -474,7 +480,10 @@
"additionalProperties": false,
"properties": {
"user": {
"type": ["integer", "string"]
"type": [
"integer",
"string"
]
}
}
},
Expand Down
18 changes: 13 additions & 5 deletions invenio_communities/communities/resources/ui_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from marshmallow import Schema, fields, post_dump
from marshmallow_utils.fields import FormatEDTF as FormatEDTF_

from invenio_communities.communities.schema import CommunityThemeSchema
from invenio_communities.proxies import current_communities


Expand Down Expand Up @@ -91,6 +92,8 @@ class UICommunitySchema(BaseObjectSchema):

tombstone = fields.Nested(TombstoneSchema, attribute="tombstone")

theme = fields.Nested(CommunityThemeSchema, dump_only=True, load_default={})

# Custom fields
custom_fields = fields.Nested(
partial(CustomFieldsSchemaUI, fields_var="COMMUNITIES_CUSTOM_FIELDS")
Expand All @@ -109,12 +112,17 @@ def get_permissions(self, obj):
return {"can_include_directly": can_include_directly, "can_update": can_update}

@post_dump
def hide_tombstone(self, obj, **kwargs):
"""Hide the tombstone information if it's not visible."""
if not obj.get("tombstone", {}).get("is_visible", False):
obj.pop("tombstone", None)
def post_dump(self, data, many, **kwargs):
"""Pop theme field if None."""
is_deleted = (data.get("deletion_status") or {}).get("is_deleted", False)
tombstone_visible = (data.get("tombstone") or {}).get("is_visible", True)

if not is_deleted or not tombstone_visible:
data.pop("tombstone", None)

return obj
if data.get("theme") is None:
data.pop("theme", None)
return data


class TypesSchema(Schema):
Expand Down
15 changes: 14 additions & 1 deletion invenio_communities/communities/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,13 @@ class DeletionStatusSchema(Schema):
status = fields.String(dump_only=True)


class CommunityThemeSchema(Schema):
"""Community theme schema."""

config = fields.Dict()
brand = fields.Str()


class CommunitySchema(BaseRecordSchema, FieldPermissionsMixin):
"""Schema for the community metadata."""

Expand Down Expand Up @@ -196,7 +203,10 @@ class Meta:

is_verified = fields.Boolean(dump_only=True)

theme = fields.Nested(CommunityThemeSchema, allow_none=True)

tombstone = fields.Nested(TombstoneSchema, dump_only=True)

deletion_status = fields.Nested(DeletionStatusSchema, dump_only=True)

@post_dump
Expand All @@ -206,7 +216,10 @@ def post_dump(self, data, many, **kwargs):
tombstone_visible = (data.get("tombstone") or {}).get("is_visible", True)

if data.get("custom_fields") is None:
data.pop("custom_fields")
data.pop("custom_fields", None)

if data.get("theme") is None:
data.pop("theme", None)

if not is_deleted or not tombstone_visible:
data.pop("tombstone", None)
Expand Down
27 changes: 27 additions & 0 deletions invenio_communities/communities/services/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,35 @@ def unmark(self, identity, data=None, record=None, **kwargs):
record.tombstone = record.tombstone


class CommunityThemeComponent(ServiceComponent):
"""Service component for Community theme."""

def update(self, identity, data=None, record=None, errors=None, **kwargs):
"""Inject parsed theme to the record."""
stored_record_theme = record.get("theme")
if "theme" in data:
# if theme set to None then it is a delete operation
if data["theme"] is None:
if stored_record_theme is not None:
self.service.require_permission(
identity, "delete_theme", record=record
)
# delete theme from record and data
record.pop("theme", None)
# We always pop the {theme: None} from the data so we don't store None
# value in the record
data.pop("theme", None)
elif data["theme"] != stored_record_theme:
# check update permissions for theme only if incoming theme is
# different from stored. Check is needed, so we don't apply the theme
# permission check when other community information is updated
self.service.require_permission(identity, "set_theme", record=record)
record["theme"] = data["theme"]


DefaultCommunityComponents = [
MetadataComponent,
CommunityThemeComponent,
CustomFieldsComponent,
PIDComponent,
RelationsComponent,
Expand Down
7 changes: 6 additions & 1 deletion invenio_communities/communities/services/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@
)

from ...permissions import CommunityPermissionPolicy, can_perform_action
from ..schema import CommunityFeaturedSchema, CommunitySchema, TombstoneSchema
from ..schema import (
CommunityFeaturedSchema,
CommunitySchema,
CommunityThemeSchema,
TombstoneSchema,
)
from .components import DefaultCommunityComponents
from .links import CommunityLink
from .search_params import IncludeDeletedCommunitiesParam, StatusParam
Expand Down
4 changes: 4 additions & 0 deletions invenio_communities/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ class CommunityPermissionPolicy(BasePermissionPolicy):
# correct permissions based on which the field will be exposed only to moderators
can_moderate = [Disable()]

# Permissions to crud community theming
can_set_theme = [SystemProcess()]
can_delete_theme = can_set_theme


def can_perform_action(community, context):
"""Check if the given action is available on the request."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,10 @@
#}

{% extends config.BASE_TEMPLATE %}

{%- block css %}
{{ super() }}
{% if community %}
<link rel="stylesheet" type="text/css" href="/communities/{{community.slug}}/community-theme-{{ community.revision_id }}.css">
{% endif %}
{%- endblock css %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
.page-subheader-outer {
background-color: {{ theme.mainHeaderBackgroundColor }} !important;
}

.theme-secondary, .ui.secondary.button {
background-color: {{ theme.secondaryColor }} !important;
color: {{ theme.secondaryTextColor }} !important;
}

.invenio-page-body .ui.search.button {
background-color: {{ theme.secondaryColor }} !important;
color: {{ theme.secondaryTextColor }} !important;
}

.theme-primary a, .theme-primary h1, .theme-primary h2 {
color: {{ theme.primaryTextColor }} !important;
}

.theme-primary.pointing.menu .item.active {
border-color: {{ theme.secondaryColor }} !important;
}

.theme-primary-menu .item.active {
background-color: {{ theme.primaryColor }} !important;
}

.theme-primary .item, .theme-primary-menu, .page-subheader-outer{
font-family: {{ theme.font.family }} !important;
font-weight: {{ theme.font.weight }} !important;
font-size: {{ theme.font.size }};
}

.theme-primary {
background-color: {{ theme.primaryColor }} !important;
}

.theme-tertiary, .ui.tertiary.button {
background-color: {{ theme.tertiaryColor }} !important;
color: {{ theme.tertiaryTextColor }};
}

.invenio-accordion-field .title, .ui.primary.button{
background-color: {{ theme.primaryColor }} !important;
color: {{ theme.primaryTextColor }} !important;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
{% extends "invenio_communities/base.html" %}

{%- block page_body %}
{% set community_menu_active = True %}
{% include "invenio_communities/details/header.html" %}
{%- endblock page_body %}
Loading

0 comments on commit 714e625

Please sign in to comment.