Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 2024.8.0 #2965

Merged
merged 8 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/src/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@
("py:class", "linkcheck.models.Link"),
("py:class", "linkcheck.models.Url"),
("py:class", "NoneType"),
("py:class", "lxml.html.Element"),
("py:class", "polymorphic.query.PolymorphicQuerySet"),
("py:class", "PolymorphicQuerySet"),
("py:class", "pytest_django.fixtures.SettingsWrapper"),
Expand Down
14 changes: 13 additions & 1 deletion integreat_cms/cms/forms/custom_content_model_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from django.utils.translation import gettext_lazy as _

from ..constants import status
from ..utils.content_translation_utils import update_links_to
from ..utils.content_utils import clean_content
from ..utils.slug_utils import generate_unique_slug_helper
from .custom_model_form import CustomModelForm
Expand Down Expand Up @@ -140,7 +141,18 @@ def save(self, commit: bool = True, foreign_form_changed: bool = False) -> Any:
self.instance.pk = None

# Save CustomModelForm
return super().save(commit=commit)
result = super().save(commit=commit)

# Update links to this content translation
# Also update if the status got changed, since title or slug might have changed in a previous draft version
if (
commit
and self.instance.status == status.PUBLIC
and not {"title", "slug", "status"}.isdisjoint(self.changed_data)
):
update_links_to(self.instance, self.instance.creator)

return result

def add_success_message(self, request: HttpRequest) -> None:
"""
Expand Down
16 changes: 16 additions & 0 deletions integreat_cms/cms/forms/events/event_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
from django.conf import settings
from django.utils.translation import gettext_lazy as _

from ...constants import status
from ...models import Event
from ...utils.content_translation_utils import update_links_to
from ..custom_model_form import CustomModelForm
from ..icon_widget import IconWidget

Expand Down Expand Up @@ -207,3 +209,17 @@ def clean(self) -> dict[str, Any]:
self.data["end"] = cleaned_data["end"]
logger.debug("EventForm validated [2] with cleaned data %r", cleaned_data)
return cleaned_data

def save(self, commit: bool = True) -> Any:
result = super().save(commit)

# Update links to instances of this event, since the autogenerated data might contain a link
if commit and "icon" in self.changed_data:
logger.debug("Updating links to %r, since its icon changed", self.instance)
for (
translation
) in self.instance.prefetched_public_translations_by_language_slug.values():
if translation.status == status.PUBLIC:
update_links_to(translation, translation.creator)

return result
18 changes: 16 additions & 2 deletions integreat_cms/cms/forms/pages/page_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@

from django.utils.translation import gettext_lazy as _

from ...constants import mirrored_page_first, position
from ...constants import mirrored_page_first, position, status
from ...models import Page, Region
from ...utils.content_translation_utils import update_links_to
from ..custom_model_form import CustomModelForm
from ..custom_tree_node_form import CustomTreeNodeForm
from ..icon_widget import IconWidget
Expand Down Expand Up @@ -170,7 +171,6 @@ def __init__(self, **kwargs: Any) -> None:
for page in mirrored_page_queryset.cache_tree(archived=False)
]

# Set choices of organizations manually to filter only organizations of the region of the page
self.fields["organization"].queryset = self.instance.region.organizations.all()

# Set choices of parent and _ref_node_id fields manually to make use of cache_tree()
Expand Down Expand Up @@ -255,3 +255,17 @@ def get_editor_queryset(self) -> QuerySet:
self.instance.editors.all()
)
return users_without_permissions

def save(self, commit: bool = True) -> Any:
result = super().save(commit)

# Update links to instances of this page, since the autogenerated data might contain a link
if commit and "icon" in self.changed_data:
logger.debug("Updating links to %r, since its icon changed", self.instance)
for (
translation
) in self.instance.prefetched_public_translations_by_language_slug.values():
if translation.status == status.PUBLIC:
update_links_to(translation, translation.creator)

return result
27 changes: 26 additions & 1 deletion integreat_cms/cms/forms/pois/poi_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
from jsonschema.exceptions import ValidationError

from ....nominatim_api.nominatim_api_client import NominatimApiClient
from ...constants import opening_hours
from ...constants import opening_hours, status
from ...models import POI
from ...utils.content_translation_utils import update_links_to
from ...utils.translation_utils import gettext_many_lazy as __
from ..custom_model_form import CustomModelForm
from ..icon_widget import IconWidget
Expand Down Expand Up @@ -65,6 +66,16 @@ class Meta:
"icon": IconWidget(),
}

def __init__(self, **kwargs: Any) -> None:
r"""
Initialize page form

:param \**kwargs: The supplied keyword arguments
"""
super().__init__(**kwargs)

self.fields["organization"].queryset = self.instance.region.organizations.all()

# pylint: disable=too-many-return-statements
def clean_opening_hours(self) -> list[dict[str, Any]]:
"""
Expand Down Expand Up @@ -210,3 +221,17 @@ def clean(self) -> dict[str, Any]:
)

return cleaned_data

def save(self, commit: bool = True) -> Any:
result = super().save(commit)

# Update links to instances of this poi, since the autogenerated data might contain a link
if commit and "icon" in self.changed_data:
logger.debug("Updating links to %r, since its icon changed", self.instance)
for (
translation
) in self.instance.prefetched_public_translations_by_language_slug.values():
if translation.status == status.PUBLIC:
update_links_to(translation, translation.creator)

return result
41 changes: 40 additions & 1 deletion integreat_cms/cms/models/abstract_content_translation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import logging
from html import escape
from typing import TYPE_CHECKING

from django.conf import settings
Expand All @@ -11,10 +12,14 @@
from django.utils.translation import gettext_lazy as _
from linkcheck.listeners import disable_listeners

from ..utils.tinymce_icon_utils import get_icon_html, make_icon

if TYPE_CHECKING:
from collections.abc import Iterable
from typing import Any, Literal

from django.db.models.query import QuerySet
from lxml.html import Element

from .abstract_content_model import AbstractContentModel
from .regions.region import Region
Expand Down Expand Up @@ -537,13 +542,47 @@ def hix_sufficient_for_mt(self) -> bool:
Whether this translation has a sufficient HIX value for machine translations.
If it is ``None``, machine translations are allowed by default.

:return: Wether the HIX value is sufficient for MT
:return: Whether the HIX value is sufficient for MT
"""
return (
self.hix_score is None
or self.rounded_hix_score >= settings.HIX_REQUIRED_FOR_MT
)

@staticmethod
def default_icon() -> str | None:
"""
Returns the default icon that should be used for this content translation type, or None for no icon
"""
return None

@cached_property
def link_title(self) -> Element | str:
"""
This property returns the html that should be used as a title for a link to this translation

:return: The link content
"""
foreign_object = self.foreign_object
if icon := getattr(foreign_object, "icon", None):
if url := icon.thumbnail_url:
img = make_icon(url)
img.tail = self.title
return img

if icon_name := self.default_icon():
img = get_icon_html(icon_name)
img.tail = self.title
return img

return escape(str(self))

def get_all_used_slugs(self) -> Iterable[str]:
"""
:return: All slugs that have been used by at least on version of this translation
"""
return self.all_versions.values_list("slug", flat=True)

def __str__(self) -> str:
"""
This overwrites the default Django :meth:`~django.db.models.Model.__str__` method.
Expand Down
7 changes: 7 additions & 0 deletions integreat_cms/cms/models/events/event_translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ def backend_edit_link(self) -> str:
},
)

@staticmethod
def default_icon() -> str | None:
"""
:return: The default icon that should be used for this content translation type, or ``None`` for no icon
"""
return "clock"

@classmethod
def search(cls, region: Region, language_slug: str, query: str) -> QuerySet:
"""
Expand Down
7 changes: 7 additions & 0 deletions integreat_cms/cms/models/pages/imprint_page_translation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import logging
from typing import Iterable

from django.conf import settings
from django.contrib.contenttypes.fields import GenericRelation
Expand Down Expand Up @@ -78,6 +79,12 @@ def slug(self) -> str:
"""
return settings.IMPRINT_SLUG

def get_all_used_slugs(self) -> Iterable[str]:
"""
:return: All slugs that have been used by at least on version of this translation
"""
return [settings.IMPRINT_SLUG]

class Meta:
#: The verbose name of the model
verbose_name = _("imprint translation")
Expand Down
4 changes: 2 additions & 2 deletions integreat_cms/cms/models/pages/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ def cache_tree(
else:
# Keep track of all skipped pages
skipped_pages.append(page)
logger.debug("Cached result: %r", result)
logger.debug("Skipped pages: %r", skipped_pages)
logger.debug("Cached pages: %r", len(result))
logger.debug("Skipped pages: %r", len(skipped_pages))
return list(result.values())


Expand Down
7 changes: 7 additions & 0 deletions integreat_cms/cms/models/pois/poi_translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ def backend_edit_link(self) -> str:
},
)

@staticmethod
def default_icon() -> str | None:
"""
:return: The default icon that should be used for this content translation type, or ``None`` for no icon
"""
return "pin"

@classmethod
def search(cls, region: Region, language_slug: str, query: str) -> QuerySet:
"""
Expand Down
4 changes: 2 additions & 2 deletions integreat_cms/cms/models/users/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ def get_repr(self) -> str:
return f"<Organization (id: {self.id}, slug: {self.slug}, region: {self.region.slug})>"

@property
def num_pages(self) -> int:
def num_contents(self) -> int:
"""

:return: the current number of maintained pages of an organization object
"""
return self.pages.count()
return self.pages.count() + self.pois.count()

@property
def num_members(self) -> int:
Expand Down
65 changes: 65 additions & 0 deletions integreat_cms/cms/templates/_related_contents_table.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{% load i18n %}
{% load content_filters %}
<div class="table-listing w-full px-4 pb-4">
<div>
<label>
{{ table_title }}
</label>
</div>
<table class="w-full mt-4 rounded border border-solid border-gray-200 shadow bg-white">
<thead>
<tr class="border-b border-solid border-gray-200">
<th class="text-sm text-left uppercase py-3 pl-4 pr-2">
{% translate "Name in " %} {{ region_default_language.translated_name }}
</th>
{% if region_default_language != backend_language %}
<th class="text-sm text-left uppercase py-3 pl-4 pr-2">
{% translate "Name in " %} {{ backend_language.translated_name }}
</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for content in contents %}
{% get_translation content region_default_language.slug as content_translation %}
{% get_translation content backend_language.slug as backendlang_content_translation %}
<tr class="border-b border-solid border-gray-200 hover:bg-gray-100">
{% if content_translation %}
<td class="text-sm text-left py-3 pl-4 pr-2">
<a href="{{ content_translation.backend_edit_link }}"
class="block py-1.5 px-2 overflow-hidden max-w-xs whitespace-nowrap text-ellipsis text-gray-800"
title="{% translate "View content" %}">
{{ content_translation.title }}
</a>
</td>
{% else %}
<td class="text-sm text-left py-3 pl-4 pr-2">
<i>{% translate "Translation not available" %}</i>
</td>
{% endif %}
{% if region_default_language != backend_language %}
{% if backendlang_content_translation %}
<td class="text-sm text-left py-3 pl-4 pr-2">
<a href="{{ backendlang_content_translation.backend_edit_link }}"
class="block py-1.5 px-2 overflow-hidden max-w-xs whitespace-nowrap text-ellipsis text-gray-800"
title="{% translate "View content" %}">
{{ backendlang_content_translation.title }}
</a>
</td>
{% else %}
<td class="text-sm text-left py-3 pl-4 pr-2">
<i>{% translate "Translation not available" %}</i>
</td>
{% endif %}
{% endif %}
</tr>
{% empty %}
<tr>
<td colspan="2" class="px-4 py-3">
{% trans no_content_message %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
1 change: 1 addition & 0 deletions integreat_cms/cms/templates/_tinymce_config.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
data-link-dialog-url-text='{% translate "URL" %}'
data-link-dialog-text-text='{% translate "Text to display" %}'
data-link-dialog-internal_link-text='{% translate "Or link to existing content" %}'
data-link-dialog-autoupdate-text='{% translate "Automatically update the link text when the linked content changes" %}'
data-custom-plugins="{% get_base_url %}{{ editor_content_js_files.0.url }}"
data-content-css="{% get_base_url %}{{ editor_content_css_files.0.url }}"
data-content-style="{{ content_style }}"
Expand Down
1 change: 1 addition & 0 deletions integreat_cms/cms/templates/content_versions.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ <h1 class="heading">
</div>
<form method="post" action="{{ versions_url }}">
{% csrf_token %}
<input type="hidden" name="revision" value="" />
<div class="w-full m-auto mb-28 flex items-center relative">
<div id="button-prev" class="flex-shrink-0 px-4 version-history-control">
<i icon-name="chevron-left" class="w-5"></i>
Expand Down
Loading
Loading