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.2 #3018

Merged
merged 37 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b2c872c
add new menu item
theresantonie Aug 7, 2024
486c6a7
Add contact model
JoeyStk Aug 6, 2024
4c34720
Fix automatic slug update on sbs form
lunars97 Jul 9, 2024
ed2f7a1
Merge pull request #2842 from digitalfabrik/bugfix/automatic-slug-upd…
JoeyStk Aug 9, 2024
7a239e1
Do not return internal Zammad messages to customers, fix #2843
svenseeberg Jun 12, 2024
149a846
Merge pull request #2970 from digitalfabrik/feature/add-contact-model
JoeyStk Aug 9, 2024
45cc6af
change order of menu items
theresantonie Aug 10, 2024
6c18e76
Change permission check
david-venhoff Aug 11, 2024
934a33e
Merge pull request #2981 from digitalfabrik/feature/add_contacts_navi…
JoeyStk Aug 11, 2024
8e18bed
Include links of imprints into link replacement
MizukiTemma Aug 9, 2024
bff00d6
Retry sending failed push notifications that were intended to be sent…
deen13 Aug 12, 2024
0725bf2
Merge pull request #2983 from digitalfabrik/bug/include_imprint_links…
MizukiTemma Aug 12, 2024
dc5cddd
Hide links to images with empty alt attributes
lunars97 Aug 8, 2024
41d4d8e
Fix machine translations being considered as not up-to-date
charludo Aug 12, 2024
745bd15
Merge pull request #2984 from digitalfabrik/fix/machine-translations-…
charludo Aug 13, 2024
267ff75
Fix some typos and wrong type hints (#2986)
david-venhoff Aug 14, 2024
735dfd0
Disable auto-skip on y-axis ticks to ensure all translation report la…
deen13 Aug 14, 2024
5ba5e46
ignore soft hyphens when detecting links
PeterNerlich Aug 14, 2024
2ee4946
Merge pull request #2901 from digitalfabrik/feature/hide-links-to-ima…
PeterNerlich Aug 14, 2024
897ffbe
Merge pull request #2999 from digitalfabrik/bugfix/autolink-ignore-so…
PeterNerlich Aug 15, 2024
ed09e2e
Correctly represent the last week in rrules (#2992)
david-venhoff Aug 16, 2024
b8fbdbf
fix link auto update checkbox always checked
PeterNerlich Aug 15, 2024
24113e9
Mask api token in MatomoException (#3000)
deen13 Aug 19, 2024
768891a
Merge pull request #2844 from digitalfabrik/feature/integreat-chat-in…
charludo Aug 19, 2024
30b5fc5
Add translation status reset bulk action
jarlhengstmengel May 2, 2024
2bfe96b
Merge pull request #2865 from digitalfabrik/feature/add_bulk_action_t…
jarlhengstmengel Aug 21, 2024
0fd320e
Fix uncaught TypeError
MizukiTemma Aug 19, 2024
a007a9e
Merge pull request #3011 from digitalfabrik/bug/fix_uncaught_typeerro…
MizukiTemma Aug 21, 2024
9331362
Merge pull request #3002 from digitalfabrik/bugfix/link-initial-autou…
PeterNerlich Aug 22, 2024
0747301
Add back to POI and events list link
JoeyStk Aug 16, 2024
b13b4b0
Merge pull request #3006 from digitalfabrik/feature/add-back-to-POI-a…
JoeyStk Aug 23, 2024
f6fca4a
Fix translation for back-to-pages-button
JoeyStk Aug 23, 2024
059dda2
Merge pull request #3019 from digitalfabrik/bugfix/fix-back-to-pages
JoeyStk Aug 23, 2024
bf6a2c3
Store delivered push notification count per region and language (#2920)
deen13 Aug 28, 2024
709ceee
Fix migration
MizukiTemma Aug 28, 2024
5da1c1a
Merge pull request #3027 from digitalfabrik/fix_migrations
MizukiTemma Aug 28, 2024
5c4eb16
release notes: use issue number if possible
PeterNerlich Aug 29, 2024
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
9 changes: 6 additions & 3 deletions integreat_cms/api/v3/chat/zammad_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,17 @@ def _transform_attachment(
# pylint: disable=method-hidden
def get_messages(self, chat: UserChat) -> dict[str, dict | list[dict]]:
"""
Get all messages for a given ticket
Get all non-internal messages for a given ticket

:param chat: UserChat instance for the relevant Zammad ticket
"""
raw_response = self._attempt_call(self.client.ticket.articles, chat.zammad_id)
if not isinstance(raw_response, list):
return self._parse_response(raw_response) # type: ignore[return-value]

response = self._parse_response(
self._attempt_call(self.client.ticket.articles, chat.zammad_id)
[article for article in raw_response if not article.get("internal")]
)

for message in response:
if "attachments" in message:
message["attachments"] = [
Expand Down
3 changes: 3 additions & 0 deletions integreat_cms/cms/constants/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@
"delete_user",
"grant_page_permissions",
"manage_translations",
"change_contact",
"delete_contact",
"view_contact",
]

#: The permissions of the cms team
Expand Down
9 changes: 9 additions & 0 deletions integreat_cms/cms/constants/weeks.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,12 @@
(FOURTH, _("Fourth week")),
(LAST, _("Last week")),
]

#: A mapping from our week constants to the expected rrule values
WEEK_TO_RRULE_WEEK = {
FIRST: 1,
SECOND: 2,
THIRD: 3,
FOURTH: 4,
LAST: -1,
}
30 changes: 30 additions & 0 deletions integreat_cms/cms/fixtures/test_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -5779,5 +5779,35 @@
"poi": 4,
"meta_description": "Meta description of the test location"
}
},
{
"model": "cms.contact",
"pk": 1,
"fields": {
"title": "Integrationsbeauftragte",
"name": "Martina Musterfrau",
"poi": 6,
"email": "[email protected]",
"phone_number": "0123456789",
"website": "",
"archived": false,
"created_date": "2024-08-06T13:23:45.256Z",
"last_updated": "2024-08-06T13:23:45.256Z"
}
},
{
"model": "cms.contact",
"pk": 2,
"fields": {
"title": "Integrationsberaterin",
"name": "Martina Musterfrau",
"poi": 6,
"email": "[email protected]",
"phone_number": "0987654321",
"website": "www.random-page.com",
"archived": true,
"created_date": "2024-08-06T13:23:45.256Z",
"last_updated": "2024-08-06T13:23:45.256Z"
}
}
]
112 changes: 112 additions & 0 deletions integreat_cms/cms/migrations/0098_add_contact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import django.db.models.deletion
import django.utils.timezone
from django.apps.registry import Apps
from django.core.management.sql import emit_post_migrate_signal
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor

from integreat_cms.cms.constants import roles


# pylint: disable=unused-argument
def update_roles(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None:
"""
Add permissions for managing external calendars
:param apps: The configuration of installed applications
:param schema_editor: The database abstraction layer that creates actual SQL code
"""
Group = apps.get_model("auth", "Group")
Permission = apps.get_model("auth", "Permission")

# Emit post-migrate signal to make sure the Permission objects are created before they can be assigned
emit_post_migrate_signal(2, False, "default")

# Clear and update permissions according to new constants
for role_name in dict(roles.CHOICES):
group, _ = Group.objects.get_or_create(name=role_name)
# Clear permissions
group.permissions.clear()
# Set permissions
group.permissions.add(
*Permission.objects.filter(codename__in=roles.PERMISSIONS[role_name])
)


class Migration(migrations.Migration):
"""
Initial migration file for contact
"""

dependencies = [
("cms", "0097_alter_pushnotificationtranslation_text"),
]

operations = [
migrations.CreateModel(
name="Contact",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("title", models.CharField(max_length=200, verbose_name="title")),
("name", models.CharField(max_length=200, verbose_name="name")),
(
"email",
models.EmailField(
blank=True, max_length=254, verbose_name="email address"
),
),
(
"phone_number",
models.CharField(
blank=True, max_length=250, verbose_name="phone number"
),
),
(
"website",
models.URLField(blank=True, max_length=250, verbose_name="website"),
),
(
"archived",
models.BooleanField(
default=False,
help_text="Whether or not the location is read-only and hidden in the API.",
verbose_name="archived",
),
),
(
"last_updated",
models.DateTimeField(
auto_now=True, verbose_name="modification date"
),
),
(
"created_date",
models.DateTimeField(
default=django.utils.timezone.now, verbose_name="creation date"
),
),
(
"poi",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to="cms.poi",
verbose_name="POI",
),
),
],
options={
"verbose_name": "contact",
"verbose_name_plural": "contacts",
"ordering": ["name"],
"default_permissions": ("change", "delete", "view"),
"default_related_name": "contact",
},
),
]
35 changes: 35 additions & 0 deletions integreat_cms/cms/migrations/0099_firebasestatistic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from django.db import migrations, models


class Migration(migrations.Migration):
"""
Create FirebaseStatistic model
"""

dependencies = [
("cms", "0098_add_contact"),
]

operations = [
migrations.CreateModel(
name="FirebaseStatistic",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("date", models.DateField()),
("region", models.CharField(max_length=100)),
("language_slug", models.CharField(max_length=2)),
("count", models.IntegerField()),
],
options={
"abstract": False,
},
),
]
3 changes: 3 additions & 0 deletions integreat_cms/cms/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@

from __future__ import annotations

from integreat_cms.cms.models.firebase.firebase_statistic import FirebaseStatistic

from .chat.attachment_map import AttachmentMap
from .chat.chat_message import ChatMessage
from .chat.user_chat import ABTester, UserChat
from .contact.contact import Contact
from .events.event import Event
from .events.event_translation import EventTranslation
from .events.recurrence_rule import RecurrenceRule
Expand Down
7 changes: 5 additions & 2 deletions integreat_cms/cms/models/abstract_content_translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,11 +441,14 @@ def is_outdated(self) -> bool:
def is_up_to_date(self) -> bool:
"""
This property checks whether a translation is up to date.
A translation is considered up to date when it is not outdated and not being translated at the moment.
A translation is considered up to date when it is either explicitly set to up-to-date, or has been machine-translated.

:return: Flag which indicates whether a translation is up to date
"""
return self.translation_state == translation_status.UP_TO_DATE
return self.translation_state in [
translation_status.UP_TO_DATE,
translation_status.MACHINE_TRANSLATED,
]

@cached_property
def translation_state(self) -> str:
Expand Down
14 changes: 5 additions & 9 deletions integreat_cms/cms/models/abstract_tree_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from treebeard.ns_tree import NS_Node, NS_NodeManager

if TYPE_CHECKING:
from typing import Any
from typing import Any, Self

from treebeard.ns_tree import NS_NodeQuerySet

Expand Down Expand Up @@ -193,9 +193,7 @@ def next_region_sibling(self) -> AbstractTreeNode | None:
return siblings[idx + 1]
return None

def get_cached_ancestors(
self, include_self: bool = False
) -> list[AbstractTreeNode]:
def get_cached_ancestors(self, include_self: bool = False) -> list[Self]:
"""
Get the cached ancestors of a specific node

Expand All @@ -210,7 +208,7 @@ def get_cached_ancestors(
return self._cached_ancestors

@cached_property
def cached_parent(self) -> AbstractTreeNode | None:
def cached_parent(self) -> Self | None:
"""
Get the parent node of the current node object.
Caches the result in the object itself to help in loops.
Expand All @@ -221,9 +219,7 @@ def cached_parent(self) -> AbstractTreeNode | None:
return None
return self.get_cached_ancestors()[-1]

def get_cached_descendants(
self, include_self: bool = False
) -> list[AbstractTreeNode]:
def get_cached_descendants(self, include_self: bool = False) -> list[Self]:
"""
Get the cached descendants of a specific node

Expand All @@ -238,7 +234,7 @@ def get_cached_descendants(
return self._cached_descendants

@cached_property
def cached_children(self) -> list[AbstractTreeNode]:
def cached_children(self) -> list[Self]:
"""
Get all cached children

Expand Down
72 changes: 72 additions & 0 deletions integreat_cms/cms/models/contact/contact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from django.db import models
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _

from ..abstract_base_model import AbstractBaseModel
from ..pois.poi import POI
from ..regions.region import Region


class Contact(AbstractBaseModel):
"""
Data model representing a contact
"""

title = models.CharField(max_length=200, verbose_name=_("title"))
name = models.CharField(max_length=200, verbose_name=_("name"))
poi = models.ForeignKey(POI, on_delete=models.PROTECT, verbose_name=_("POI"))
email = models.EmailField(
blank=True,
verbose_name=_("email address"),
)
phone_number = models.CharField(
max_length=250, blank=True, verbose_name=_("phone number")
)
website = models.URLField(blank=True, max_length=250, verbose_name=_("website"))
archived = models.BooleanField(
default=False,
verbose_name=_("archived"),
help_text=_("Whether or not the location is read-only and hidden in the API."),
)
last_updated = models.DateTimeField(
auto_now=True,
verbose_name=_("modification date"),
)
created_date = models.DateTimeField(
default=timezone.now, verbose_name=_("creation date")
)

@cached_property
def region(self) -> Region:
"""
Returns the region this contact belongs to

:return: Region this contact belongs to
"""
return self.poi.region

def __str__(self) -> str:
"""
This overwrites the default Django :meth:`~django.db.models.Model.__str__` method which would return ``Contact object (id)``.
It is used in the Django admin backend and as label for ModelChoiceFields.

:return: A readable string representation of the contact
"""
return f"{self.title} {self.name}"

def get_repr(self) -> str:
"""
This overwrites the default Django ``__repr__()`` method which would return ``<Contact: Contact object (id)>``.
It is used for logging.

:return: The canonical string representation of the contact
"""
return f"<Contact (id: {self.id}, title: {self.title}, name: {self.name}, region: {self.region.slug})>"

class Meta:
verbose_name = _("contact")
default_related_name = "contact"
verbose_name_plural = _("contacts")
default_permissions = ("change", "delete", "view")
ordering = ["name"]
Loading
Loading