Skip to content

Commit

Permalink
Translatable ModelAdmin (#550)
Browse files Browse the repository at this point in the history
  • Loading branch information
dinoperovic authored May 3, 2022
1 parent 63a9c4d commit 51d98a2
Show file tree
Hide file tree
Showing 25 changed files with 1,438 additions and 47 deletions.
34 changes: 34 additions & 0 deletions docs/how-to/modeladmin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Translatable ModelAdmin

Wagtail Localize supports translation of custom Wagtail's [ModelAdmin](https://docs.wagtail.org/en/stable/reference/contrib/modeladmin/index.html) registered models.

## Installation

Add `wagtail_localize.modeladmin` to your `INSTALLED_APPS`:

```python
INSTALLED_APPS = [
# ...
"wagtail_localize.modeladmin",
]
```

## How to use

When registering your custom models you can use the supplied `TranslatableModelAdmin` in place of Wagtail's `ModelAdmin` class.

```python
from wagtail.contrib.modeladmin.options import modeladmin_register
from wagtail_localize.modeladmin.options import TranslatableModelAdmin

from .models import MyTranslatableModel


class MyTranslatableModelAdmin(TranslatableModelAdmin):
model = MyTranslatableModel


modeladmin_register(MyTranslatableModelAdmin)
```

That's it! You can translate your custom ModelAdmin models in the admin dashboard the same way you would Wagtail snippets.
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ nav:
- How To Guides:
- Installation guide: how-to/installation.md
- Configuring translatable fields: how-to/field-configuration.md
- Translatable ModelAdmin: how-to/modeladmin.md
- Integrations:
- Pontoon: how-to/integrations/pontoon.md
- Machine Translation: how-to/integrations/machine-translation.md
Expand Down
10 changes: 10 additions & 0 deletions wagtail_localize/modeladmin/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django import VERSION as DJANGO_VERSION


if DJANGO_VERSION >= (3, 2):
# The declaration is only needed for older Django versions
pass
else:
default_app_config = (
"wagtail_localize.modeladmin.apps.WagtailLocalizeModelAdminAppConfig"
)
7 changes: 7 additions & 0 deletions wagtail_localize/modeladmin/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.apps import AppConfig


class WagtailLocalizeModelAdminAppConfig(AppConfig):
name = "wagtail_localize.modeladmin"
label = "wagtail_localize_modeladmin"
verbose_name = "Wagtail localize modeladmin"
80 changes: 80 additions & 0 deletions wagtail_localize/modeladmin/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from urllib.parse import urlencode

from django.contrib.admin.utils import quote
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from wagtail.contrib.modeladmin.helpers import ButtonHelper, PageButtonHelper
from wagtail.contrib.modeladmin.views import InspectView
from wagtail.core.models import Locale, Page, TranslatableMixin

from wagtail_localize.models import TranslationSource


class TranslatableButtonHelper(ButtonHelper):
def get_buttons_for_obj(self, obj, **kwargs):
btns = super().get_buttons_for_obj(obj, **kwargs)
user = self.request.user
next_url = self.request.get_full_path()
if isinstance(self.view, InspectView):
classname = "button button-secondary"
else:
classname = "button button-secondary button-small"
btns += list(get_translation_buttons(obj, user, next_url, classname))
return btns


class TranslatablePageButtonHelper(TranslatableButtonHelper, PageButtonHelper):
pass


def get_translation_buttons(obj, user, next_url=None, classname=""):
"""
Generate listing buttons for translating/syncing objects in modeladmin.
"""
model = type(obj)

if issubclass(model, TranslatableMixin) and user.has_perm(
"wagtail_localize.submit_translation"
):

# If there's at least one locale that we haven't translated into yet, show "Translate" button
if isinstance(obj, Page):
has_locale_to_translate_to = Locale.objects.exclude(
id__in=obj.get_translations(inclusive=True)
.exclude(alias_of__isnull=False)
.values_list("locale_id", flat=True)
).exists()
else:
has_locale_to_translate_to = Locale.objects.exclude(
id__in=obj.get_translations(inclusive=True).values_list(
"locale_id", flat=True
)
).exists()

if has_locale_to_translate_to:
url = reverse(
"wagtail_localize_modeladmin:submit_translation",
args=[model._meta.app_label, model._meta.model_name, quote(obj.pk)],
)
yield {
"url": url,
"label": _("Translate"),
"classname": classname,
"title": _("Translate"),
}

# If the object is the source for translations, show "Sync translated" button
source = TranslationSource.objects.get_for_instance_or_none(obj)
if source is not None and source.translations.filter(enabled=True).exists():
url = reverse("wagtail_localize:update_translations", args=[source.id])
if next_url is not None:
url += "?" + urlencode({"next": next_url})

yield {
"url": url,
"label": _("Sync translated %(model_name)s")
% {"model_name": obj._meta.verbose_name_plural},
"classname": classname,
"title": _("Sync translated %(model_name)s")
% {"model_name": obj._meta.verbose_name_plural},
}
57 changes: 57 additions & 0 deletions wagtail_localize/modeladmin/options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from wagtail.contrib.modeladmin.options import ModelAdmin
from wagtail.core.models import TranslatableMixin

from .helpers import TranslatableButtonHelper, TranslatablePageButtonHelper
from .views import (
TranslatableChooseParentView,
TranslatableCreateView,
TranslatableDeleteView,
TranslatableEditView,
TranslatableHistoryView,
TranslatableIndexView,
TranslatableInspectView,
)


class TranslatableModelAdmin(ModelAdmin):
index_view_class = TranslatableIndexView
create_view_class = TranslatableCreateView
edit_view_class = TranslatableEditView
inspect_view_class = TranslatableInspectView
delete_view_class = TranslatableDeleteView
history_view_class = TranslatableHistoryView
choose_parent_view_class = TranslatableChooseParentView

def __init__(self, parent=None):
super().__init__(parent)

if "wagtail_localize.modeladmin" not in settings.INSTALLED_APPS:
raise ImproperlyConfigured(
'To use the TranslatableModelAdmin class "wagtail_localize.modeladmin" '
"must be added to your INSTALLED_APPS setting."
)

if not issubclass(self.model, TranslatableMixin):
raise ImproperlyConfigured(
f"Model `{self.model}` used in translatable admin `{self.__class__}` "
f"must subclass the `{TranslatableMixin}` class."
)

def get_button_helper_class(self):
if self.button_helper_class:
return self.button_helper_class
if self.is_pagemodel:
return TranslatablePageButtonHelper
return TranslatableButtonHelper

def get_templates(self, action="index"):
app_label = self.opts.app_label.lower()
model_name = self.opts.model_name.lower()
return [
"wagtail_localize/modeladmin/%s/%s/translatable_%s.html"
% (app_label, model_name, action),
"wagtail_localize/modeladmin/%s/translatable_%s.html" % (app_label, action),
"wagtail_localize/modeladmin/translatable_%s.html" % (action,),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<a{% if button.url %} href="{{ button.url }}{% if locale %}?locale={{ locale.language_code }}{% endif %}"{% endif %} class="{{ button.classname }}" title="{{ button.title }}"{% if button.target %} target="{{ button.target }}"{% endif %}{% if button.rel %} rel="{{ button.rel }}"{% endif %}>{{ button.label }}</a>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% extends "wagtailadmin/shared/header_with_locale_selector.html" %}

{% block breadcrumb %}
{% include "modeladmin/includes/breadcrumb.html" %}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% extends "modeladmin/choose_parent.html" %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% extends "modeladmin/create.html" %}

{% block header %}
{% include "wagtailadmin/shared/header_with_locale_selector.html" with title=view.get_page_title subtitle=view.get_page_subtitle icon=view.header_icon tabbed=1 merged=1 %}
{% endblock %}

{% block form_action %}{{ view.create_url }}?locale={{ locale.language_code }}{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% extends "modeladmin/delete.html" %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{% extends "modeladmin/edit.html" %}
{% load i18n wagtailadmin_tags %}

{% block header %}
{% if wagtail_version >= "2.15" %}
{% include "modeladmin/includes/header_with_history.html" with title=view.get_page_title subtitle=view.get_page_subtitle icon=view.header_icon tabbed=1 merged=1 latest_log_entry=latest_log_entry history_url=history_url %}
{% else %}
{% include "wagtailadmin/shared/header_with_locale_selector.html" with title=view.get_page_title subtitle=view.get_page_subtitle icon=view.header_icon tabbed=1 merged=1 %}
{% endif %}
{% endblock %}

{% block form_actions %}
<div class="dropdown dropup dropdown-button match-width">
<button type="submit" class="button action-save button-longrunning" data-clicked-text="{% trans 'Saving…' %}">
{% icon name="spinner" %}<em>{% trans "Save" %}</em>
</button>

{% if user_can_delete %}
<div class="dropdown-toggle">{% icon name="arrow-up" %}</div>
<ul>
{% if translation %}
<li>
<form method="POST">
<input type="hidden" name="localize-restart-translation">
<button class="button">{% trans "Start Synced translation" %}</button>
</form>
</li>
{% endif %}
<li><a href="{{ view.delete_url }}" class="shortcut">{% trans "Delete" %}</a></li>
</ul>
{% endif %}

</div>
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% extends "modeladmin/history.html" %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{% extends "modeladmin/index.html" %}
{% load i18n modeladmin_tags wagtailadmin_tags %}

{% block header_extra %}
<div class="right header-right">
<div class="col">
{% include 'wagtailadmin/shared/locale_selector.html' with class='c-dropdown--large' %}
</div>
{% if user_can_create %}
<div class="actionbutton col">
{% include 'wagtail_localize/modeladmin/includes/button.html' with button=view.button_helper.add_button %}
</div>
{% endif %}
{% if view.list_export %}
<div class="dropdown dropdown-button match-width col">
<a href="?export=xlsx&{{ request.GET.urlencode }}" class="button bicolor button--icon">{% icon name="download" wrapped=1 %}{% trans 'Download XLSX' %}</a>
<div class="dropdown-toggle">{% icon name="arrow-down" %}</div>
<ul>
<li><a class="button bicolor button--icon" href="?export=csv&{{ request.GET.urlencode }}">{% icon name="download" wrapped=1 %}{% trans 'Download CSV' %}</a></li>
</ul>
</div>
{% endif %}
</div>
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% extends "modeladmin/inspect.html" %}

{% block header %}
{% include "wagtail_localize/modeladmin/includes/header_with_breadcrumb.html" with title=view.get_page_title subtitle=view.get_page_subtitle icon=view.header_icon tabbed=True %}
{% endblock %}
Loading

0 comments on commit 51d98a2

Please sign in to comment.