Skip to content

Commit

Permalink
Merge pull request #3169 from digitalfabrik/feature/tinymce-insert-co…
Browse files Browse the repository at this point in the history
…ntact-use-linkcheck

[2024-11-07] TinyMCE: Insert contact, but track using linkcheck
  • Loading branch information
charludo authored Dec 18, 2024
2 parents d6eb968 + 0944716 commit 2e01833
Show file tree
Hide file tree
Showing 28 changed files with 961 additions and 81 deletions.
130 changes: 129 additions & 1 deletion integreat_cms/cms/models/contact/contact.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
from __future__ import annotations

from typing import Generator, TYPE_CHECKING

from django.conf import settings
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
from django.db import models
from django.db.models import Q
from django.db.utils import DataError
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _
from linkcheck.models import Link

from ..abstract_base_model import AbstractBaseModel
from ..events.event_translation import EventTranslation
from ..fields.truncating_char_field import TruncatingCharField
from ..pages.page_translation import PageTranslation
from ..pois.poi import POI
from ..pois.poi_translation import POITranslation
from ..regions.region import Region

if TYPE_CHECKING:
from django.db.models.query import QuerySet

from ..abstract_content_translation import AbstractContentTranslation


class Contact(AbstractBaseModel):
"""
Expand Down Expand Up @@ -59,6 +73,28 @@ def region(self) -> Region:
"""
return self.location.region

@classmethod
def search(cls, region: Region, query: str) -> QuerySet:
"""
Searches for all contacts which match the given `query` in their comment.
:param region: The current region
:param query: The query string used for filtering the contacts
:return: A query for all matching objects
"""
vector = SearchVector(
"name",
"email",
"phone_number",
"website",
"point_of_contact_for",
)
query = SearchQuery(query)
return (
Contact.objects.filter(location__region=region, archived=False)
.annotate(rank=SearchRank(vector, query))
.order_by("-rank")
)

def __str__(self) -> str:
"""
This overwrites the default Django :meth:`~django.db.models.Model.__str__` method which would return ``Contact object (id)``.
Expand Down Expand Up @@ -116,6 +152,89 @@ def get_repr(self) -> str:
"""
return f"<Contact (id: {self.id}, point of contact for: {self.point_of_contact_for}, name: {self.name}, region: {self.region.slug})>"

@cached_property
def get_repr_short(self) -> str:
"""
Returns a short representation only contaiing the relevant data, no field names.
:return: The short representation of the contact
"""
point_of_contact_for = (
f"{self.point_of_contact_for}: " if self.point_of_contact_for else ""
)
name = f"{self.name} " if self.name else ""
details = [
detail for detail in [self.email, self.phone_number, self.website] if detail
]
details_repr = f"({', '.join(details)})" if details else ""

return f"{point_of_contact_for}{name}{details_repr}".strip()

@cached_property
def referring_page_translations(self) -> QuerySet[PageTranslation]:
"""
Returns a queryset containing all :class:`~integreat_cms.cms.models.pages.page_translation.PageTranslation` objects which reference this contact
:return: all PageTranslation objects referencing this contact
"""
from ...linklists import PageTranslationLinklist

return PageTranslation.objects.filter(
id__in=(
Link.objects.filter(
url__url=self.full_url,
content_type=PageTranslationLinklist.content_type(),
).values("object_id")
),
)

@cached_property
def referring_poi_translations(self) -> QuerySet[POITranslation]:
"""
Returns a queryset containing all :class:`~integreat_cms.cms.models.pois.poi_translation.POITranslation` objects which reference this contact
:return: all POITranslation objects referencing this contact
"""
from ...linklists import POITranslationLinklist

return POITranslation.objects.filter(
id__in=(
Link.objects.filter(
url__url=self.full_url,
content_type=POITranslationLinklist.content_type(),
).values("object_id")
),
)

@cached_property
def referring_event_translations(self) -> QuerySet[EventTranslation]:
"""
Returns a queryset containing all :class:`~integreat_cms.cms.models.events.event_translation.EventTranslation` objects which reference this contact
:return: all EventTranslation objects referencing this contact
"""
from ...linklists import EventTranslationLinklist

return EventTranslation.objects.filter(
id__in=(
Link.objects.filter(
url__url=self.full_url,
content_type=EventTranslationLinklist.content_type(),
).values("object_id")
),
)

@cached_property
def referring_objects(self) -> Generator[AbstractContentTranslation]:
"""
Returns a list of all objects linking to this contact.
:return: all objects referring to this contact
"""
return (
link.content_object for link in Link.objects.filter(url__url=self.full_url)
)

def archive(self) -> None:
"""
Archives the contact
Expand All @@ -138,6 +257,15 @@ def copy(self) -> None:
self.point_of_contact_for = self.point_of_contact_for + " " + _("(Copy)")
self.save()

@cached_property
def full_url(self) -> str:
"""
This property returns the full url of this contact
:return: The full url
"""
return f"{settings.BASE_URL}/{self.location.region.slug}/contact/{self.id}/"

class Meta:
verbose_name = _("contact")
default_related_name = "contact"
Expand Down
19 changes: 19 additions & 0 deletions integreat_cms/cms/models/pois/poi.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.utils.functional import cached_property

if TYPE_CHECKING:
from typing import Any
Expand Down Expand Up @@ -200,6 +201,24 @@ def is_used(self) -> bool:
"""
return self.events.exists() or self.contacts.exists()

@cached_property
def short_address(self) -> str:
"""
:return: one-line representation of this POI's address
"""
return f"{self.address}, {self.postcode} {self.city}"

@cached_property
def map_url(self) -> str:
"""
:return: the link to the POI of the default (public) translation
"""
return (
self.default_public_translation.map_url
if self.default_public_translation
else self.default_translation.map_url
)

class Meta:
#: The verbose name of the model
verbose_name = _("location")
Expand Down
11 changes: 11 additions & 0 deletions integreat_cms/cms/models/pois/poi_translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from typing import TYPE_CHECKING

from django.conf import settings
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from django.db.models import Q
Expand All @@ -17,6 +18,7 @@

from .. import POI, Region

from ...constants import status
from ...utils.translation_utils import gettext_many_lazy as __
from ..abstract_content_translation import AbstractContentTranslation
from ..decorators import modify_fields
Expand Down Expand Up @@ -98,6 +100,15 @@ def backend_edit_link(self) -> str:
},
)

@cached_property
def map_url(self) -> str:
"""
:return: the link to the POI on the Integreat map (if it exists), to google maps otherwise
"""
if not self.poi.location_on_map and not self.status == status.DRAFT:
return f"{settings.WEBAPP_URL}{self.get_absolute_url()}"
return f"https://www.google.com/maps/search/?api=1&query={self.poi.address},{self.poi.city},{self.poi.country}"

@staticmethod
def default_icon() -> str | None:
"""
Expand Down
104 changes: 54 additions & 50 deletions integreat_cms/cms/templates/_related_contents_table.html
Original file line number Diff line number Diff line change
@@ -1,65 +1,69 @@
{% 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 %}
{% get_current_language as LANGUAGE_CODE %}
{% get_language LANGUAGE_CODE as backend_language %}
{% with request.region.default_language as region_default_language %}
<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 " %} {{ backend_language.translated_name }}
{% translate "Name in " %} {{ region_default_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 %}
<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="{{ backendlang_content_translation.backend_edit_link }}"
<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" %}">
{{ backendlang_content_translation.title }}
{{ 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>
{% 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>
{% endwith %}
7 changes: 7 additions & 0 deletions integreat_cms/cms/templates/_tinymce_config.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,16 @@
data-group-icon-text='{% translate "Group" %}'
data-group-icon-src="{% get_base_url %}{% static 'svg/group.svg' %}"
data-group-icon-alt="{% translate "A group of people" %}"
data-contact-dialog-search-text='{% translate "Search for contact" %}'
data-contact-dialog-title-text='{% translate "Add Contact" %}'
data-contact-change-text='{% translate "Change Contact" %}'
data-contact-remove-text='{% translate "Remove Contact" %}'
data-contact-icon-text='{% translate "Contact Person" %}'
data-contact-icon-src="{% get_base_url %}{% static 'svg/contact.svg' %}"
data-contact-icon-alt="{% translate "Contact Person" %}"
data-contact-ajax-url="{% url 'search_contact_ajax' region_slug=request.region.slug %}"
data-contact-menu-text='{% translate "Contact..." %}'
data-contact-no-results-text='{% translate "no results" %}'
data-speech-icon-text='{% translate "Spoken Languages" %}'
data-speech-icon-src="{% get_base_url %}{% static 'svg/speech.svg' %}"
data-speech-icon-alt="{% translate "Spoken Languages" %}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
{{ poi.country }}
{% endif %}
</div>
<a href="https://www.google.com/maps/search/?api=1&query={% if poi %}{{ poi.address }},{{ poi.city }},{{ poi.country }}{% endif %}"
<a href="{{ poi.map_url }}"
class="text-blue-500 hover:underline"
target="_blank"
rel="noopener noreferrer">
{% translate "Open on Google Maps" %}
{% translate "Open on Maps" %}
</a>
<label class="secondary">
{% translate "Contact details" %}
Expand Down
Loading

0 comments on commit 2e01833

Please sign in to comment.