Skip to content

Commit

Permalink
Move metatags to Python utils
Browse files Browse the repository at this point in the history
  • Loading branch information
rafalp committed Jun 23, 2024
1 parent c3bd8f5 commit 3443a07
Show file tree
Hide file tree
Showing 13 changed files with 390 additions and 64 deletions.
7 changes: 7 additions & 0 deletions misago/context_processors/metatags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.http import HttpRequest

from ..metatags.metatags import get_default_metatags


def default_metatags(request: HttpRequest) -> dict:
return {"default_metatags": get_default_metatags(request)}
Empty file added misago/metatags/__init__.py
Empty file.
7 changes: 7 additions & 0 deletions misago/metatags/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.apps import AppConfig


class MisagoMetatagsConfig(AppConfig):
name = "misago.metatags"
label = "misago_metatags"
verbose_name = "Misago Metatags"
2 changes: 2 additions & 0 deletions misago/metatags/hooks/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .get_default_metatags import get_default_metatags_hook
from .get_forum_index_metatags import get_forum_index_metatags_hook
95 changes: 95 additions & 0 deletions misago/metatags/hooks/get_default_metatags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
from typing import Protocol

from django.http import HttpRequest

from ...plugins.hooks import FilterHook
from ..metatag import MetaTag


class GetDefaultMetatagsHookAction(Protocol):
"""
A standard Misago function used to get default metatags for all pages.
# Arguments
## `request: HttpRequest`
The request object.
# Return value
A Python `dict` with metatags to include in the response HTML.
"""

def __call__(self, request: HttpRequest) -> dict[str, MetaTag]: ...


class GetDefaultMetatagsHookFilter(Protocol):
"""
A function implemented by a plugin that can be registered in this hook.
# Arguments
## `action: GetDefaultMetatagsHookAction`
A standard Misago function used to get default metatags for all pages.
See the [action](#action) section for details.
## `request: HttpRequest`
The request object.
# Return value
A Python `dict` with metatags to include in the response HTML.
"""

def __call__(
self,
action: GetDefaultMetatagsHookAction,
request: HttpRequest,
) -> dict[str, MetaTag]: ...


class GetDefaultMetatagsHook(
FilterHook[GetDefaultMetatagsHookAction, GetDefaultMetatagsHookFilter]
):
"""
This hook wraps the standard function that Misago uses to get default
metatags for all pages.
# Example
The code below implements a custom filter function that adds a custom
metatag to all pages:
```python
from django.http import HttpRequest
from misago.metatags.hooks import get_default_metatags_hook
@get_default_metatags_hook.append_filter
def include_custom_metatag(action, request: HttpRequest) -> dict[str, MetaTag]:
metatags = action(request)
metatags["custom"] = MetaTag(
name="og:custom",
property="twitter:custom",
itemprop="custom",
content="custom content",
)
return metatags
```
"""

__slots__ = FilterHook.__slots__

def __call__(
self,
action: GetDefaultMetatagsHookAction,
request: HttpRequest,
) -> dict[str, MetaTag]:
return super().__call__(action, request)


get_default_metatags_hook = GetDefaultMetatagsHook()
96 changes: 96 additions & 0 deletions misago/metatags/hooks/get_forum_index_metatags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from typing import Protocol

from django.http import HttpRequest

from ...plugins.hooks import FilterHook
from ..metatag import MetaTag


class GetForumIndexMetatagsHookAction(Protocol):
"""
A standard Misago function used to get metatags for the forum index page.
# Arguments
## `request: HttpRequest`
The request object.
# Return value
A Python `dict` with metatags to include in the response HTML.
"""

def __call__(self, request: HttpRequest) -> dict[str, MetaTag]: ...


class GetForumIndexMetatagsHookFilter(Protocol):
"""
A function implemented by a plugin that can be registered in this hook.
# Arguments
## `action: GetForumIndexMetatagsHookAction`
A standard Misago function used to get metatags for the forum index page.
See the [action](#action) section for details.
## `request: HttpRequest`
The request object.
# Return value
A Python `dict` with metatags to include in the response HTML.
"""

def __call__(
self,
action: GetForumIndexMetatagsHookAction,
request: HttpRequest,
) -> dict[str, MetaTag]: ...


class GetForumIndexMetatagsHook(
FilterHook[GetForumIndexMetatagsHookAction, GetForumIndexMetatagsHookFilter]
):
"""
This hook wraps the standard function that Misago uses to get metatags for
the forum index page.
# Example
The code below implements a custom filter function that adds a custom
metatag to the forum index page:
```python
from django.http import HttpRequest
from misago.metatags.hooks import get_forum_index_metatags_hook
from misago.metatags.metatag import MetaTag
@get_forum_index_metatags_hook.append_filter
def include_custom_metatag(action, request: HttpRequest) -> dict[str, MetaTag]:
metatags = action(request)
metatags["custom"] = MetaTag(
name="og:custom",
property="twitter:custom",
itemprop="custom",
content="custom content",
)
return metatags
```
"""

__slots__ = FilterHook.__slots__

def __call__(
self,
action: GetForumIndexMetatagsHookAction,
request: HttpRequest,
) -> dict[str, MetaTag]:
return super().__call__(action, request)


get_forum_index_metatags_hook = GetForumIndexMetatagsHook()
23 changes: 23 additions & 0 deletions misago/metatags/metatag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import html
from dataclasses import dataclass


@dataclass(frozen=True)
class MetaTag:
content: str | int
name: str | None = None
property: str | None = None
itemprop: str | None = None

def as_html(self):
attrs: dict[str, str] = {}
if self.name:
attrs["name"] = str(self.name)
if self.property:
attrs["property"] = str(self.property)
if self.itemprop:
attrs["itemprop"] = str(self.itemprop)
attrs["content"] = str(self.content)

attrs_html = [f'{name}="{html.escape(value)}"' for name, value in attrs.items()]
return f"<meta {' '.join(attrs_html)}>"
95 changes: 95 additions & 0 deletions misago/metatags/metatags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
from django.conf import settings as dj_settings
from django.http import HttpRequest
from django.templatetags.static import static

from .hooks import get_default_metatags_hook, get_forum_index_metatags_hook
from .metatag import MetaTag


def get_forum_index_metatags(request: HttpRequest) -> dict[str, MetaTag]:
return get_forum_index_metatags_hook(_get_forum_index_metatags_action, request)


def _get_forum_index_metatags_action(request: HttpRequest) -> dict[str, MetaTag]:
metatags = get_default_metatags(request)

if request.settings.index_title:
metatags["title"] = MetaTag(
property="og:title",
name="twitter:title",
content=request.settings.index_title,
)

if request.settings.index_meta_description:
metatags["description"] = MetaTag(
property="og:description",
name="twitter:description",
content=request.settings.index_meta_description,
)

if request.settings.forum_address:
metatags["url"] = (
MetaTag(
property="og:url",
name="twitter:url",
content=request.settings.forum_address,
),
)

return metatags


def get_default_metatags(request: HttpRequest) -> dict[str, MetaTag]:
return get_default_metatags_hook(_get_default_metatags_action, request)


def _get_default_metatags_action(request: HttpRequest) -> dict[str, MetaTag]:
settings = request.settings

metatags = {
"og:site_name": MetaTag(property="og:site_name", content=settings.forum_name),
"og:type": MetaTag(property="og:type", content="website"),
"twitter:card": MetaTag(name="twitter:card", content="summary"),
}

og_image = request.settings.get("og_image")
if og_image["value"]:
metatags.update(
{
"image": MetaTag(
property="og:image",
name="twitter:image",
content=request.build_absolute_uri(og_image["value"]),
),
"image:width": MetaTag(
property="og:image:width", content=og_image["width"]
),
"image:height": MetaTag(
property="og:image:height", content=og_image["height"]
),
}
)
else:
og_image_url = request.build_absolute_uri(
static(dj_settings.MISAGO_DEFAULT_OG_IMAGE)
)

metatags.update(
{
"image": MetaTag(
property="og:image",
name="twitter:image",
content=og_image_url,
),
"image:width": MetaTag(
property="og:image:width",
content=dj_settings.MISAGO_DEFAULT_OG_IMAGE_WIDTH,
),
"image:height": MetaTag(
property="og:image:height",
content=dj_settings.MISAGO_DEFAULT_OG_IMAGE_HEIGHT,
),
}
)

return metatags
Empty file.
17 changes: 13 additions & 4 deletions misago/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
__all__ = [
"INSTALLED_APPS",
"INSTALLED_PLUGINS",
"MISAGO_DEFAULT_OG_IMAGE",
"MISAGO_DEFAULT_OG_IMAGE_WIDTH",
"MISAGO_DEFAULT_OG_IMAGE_HEIGHT",
"MISAGO_EMAIL_CHANGE_TOKEN_EXPIRES",
"MISAGO_FORUM_ADDRESS_HISTORY",
"MISAGO_MIDDLEWARE",
Expand Down Expand Up @@ -50,6 +53,7 @@
"misago.themes",
"misago.markup",
"misago.menus",
"misago.metatags",
"misago.middleware",
"misago.notifications",
"misago.oauth2",
Expand Down Expand Up @@ -82,6 +86,7 @@
"misago.context_processors.categories.categories",
"misago.context_processors.forumindex.main_menu",
"misago.context_processors.htmx.is_request_htmx",
"misago.context_processors.metatags.default_metatags",
"misago.context_processors.permissions.user_permissions",
"misago.acl.context_processors.user_acl",
"misago.conf.context_processors.conf",
Expand Down Expand Up @@ -132,12 +137,16 @@
"misago.threads.middleware.UnreadThreadsCountMiddleware",
]

MISAGO_DEFAULT_OG_IMAGE = "misago/img/og-image.jpg"
MISAGO_DEFAULT_OG_IMAGE_WIDTH = 1200
MISAGO_DEFAULT_OG_IMAGE_HEIGHT = 630

MISAGO_EMAIL_CHANGE_TOKEN_EXPIRES = 48 # Hours

MISAGO_FORUM_ADDRESS_HISTORY = []

MISAGO_NOTIFICATIONS_RETRY_DELAY = 5 # Seconds

MISAGO_PARSER_MAX_ATTACHMENTS = 30
MISAGO_PARSER_MAX_POSTS = 20
MISAGO_PARSER_MAX_USERS = 25

MISAGO_FORUM_ADDRESS_HISTORY = []

MISAGO_EMAIL_CHANGE_TOKEN_EXPIRES = 48 # Hours
Loading

0 comments on commit 3443a07

Please sign in to comment.