Skip to content

Commit

Permalink
feat: [GAPS-26] - djangocms-moderation integration (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
Aiky30 authored Sep 22, 2021
1 parent c2335b7 commit 091140b
Show file tree
Hide file tree
Showing 23 changed files with 371 additions and 110 deletions.
26 changes: 6 additions & 20 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ Run::
to perform the application's database migrations.


=====
Usage
=====

Configuration
=============

The default Content Expiry changelist is filtered by a date range which is 30 days by default. This can be changed by setting a value in days as an integer in settings.py::

CMS_CONTENT_EXPIRY_DEFAULT_RANGEFILTER_DELTA=60


Testing
Expand All @@ -41,20 +44,3 @@ To run all the tests the only thing you need to do is run

pip install -r tests/requirements.txt
python setup.py test


Documentation
=============

We maintain documentation under the ``docs`` folder using rst format.

To generate the HTML documentation you will need to install ``sphinx`` (``pip install sphinx``) and ``graphviz`` (as per
your operating system's package management system). You can then generate the docs using the following command:

Run::

cd docs/
make html

This should generate all html files from rst documents under `docs/_build` folder, which can be browsed.

40 changes: 21 additions & 19 deletions djangocms_content_expiry/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,41 @@
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _

import datetime
from rangefilter.filters import DateRangeFilter

from .filters import AuthorFilter, ContentTypeFilter, VersionStateFilter
from .filters import (
AuthorFilter,
ContentExpiryDateRangeFilter,
ContentTypeFilter,
VersionStateFilter,
)
from .forms import ContentExpiryForm
from .helpers import get_rangefilter_expires_default
from .models import ContentExpiry


@admin.register(ContentExpiry)
class ContentExpiryAdmin(admin.ModelAdmin):
list_display = ['title', 'content_type', 'expires', 'version_state', 'version_author']
# Disable automatically linking to the Expiry record
list_display_links = None
list_filter = (ContentTypeFilter, ('expires', DateRangeFilter), VersionStateFilter, AuthorFilter)
list_filter = (ContentTypeFilter, ('expires', ContentExpiryDateRangeFilter), VersionStateFilter, AuthorFilter)
form = ContentExpiryForm

class Media:
css = {
'all': ('css/date_filter.css',)
'all': ('djangocms_content_expiry/css/date_filter.css',)
}

def get_queryset(self, request):
queryset = super().get_queryset(request)
def has_add_permission(self, *args, **kwargs):
# Entries are added automatically
return False

if 'expires__range' not in request.path:
default_gte, default_lte = self.get_rangefilter_expires_default(request)
queryset = queryset.filter(expires__range=(default_gte, default_lte))
return queryset
def has_delete_permission(self, *args, **kwargs):
# Deletion should never be possible, the only way that a
# content expiry record could be deleted is via versioning.
return False

def get_rangefilter_expires_default(self, request):
start_date = datetime.datetime.now() - datetime.timedelta(30)
end_date = datetime.datetime.now()
return start_date, end_date
def get_rangefilter_expires_default(self, *args, **kwargs):
return get_rangefilter_expires_default()

def get_rangefilter_expires_title(self, request, field_path):
def get_rangefilter_expires_title(self, *args, **kwargs):
return _("By Expiry Date Range")

def title(self, obj):
Expand Down
1 change: 1 addition & 0 deletions djangocms_content_expiry/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ class DjangocmsContentExpiryConfig(AppConfig):
def ready(self):
from .handlers import create_content_expiry
from .monkeypatch import admin as monkeypatch_admin # noqa: F401

signals.post_version_operation.connect(create_content_expiry)
59 changes: 59 additions & 0 deletions djangocms_content_expiry/cms_config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,68 @@
from django.conf import settings
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.html import format_html

from cms.app_base import CMSAppConfig

from .constants import CONTENT_EXPIRY_EXPIRE_FIELD_LABEL


def get_moderation_content_expiry_link(obj):
"""
Return a user friendly button for viewing content expiry in the
actions section of the Moderation Request Admin Changelist
in djangocms-moderation.
:param obj: A Moderation Request object supplied from the admin view table row
:return: A link to the expiry record if one exists
"""
version = obj.moderation_request.version

# If a content expiry record exists we can go to it
if hasattr(version, "contentexpiry"):
view_endpoint = format_html(
"{}?collection__id__exact={}&_popup=1",
reverse("admin:djangocms_content_expiry_contentexpiry_change", args=[version.contentexpiry.pk]),
obj.pk,
)
return render_to_string(
"djangocms_content_expiry/calendar_icon.html", {"url": view_endpoint, "field_id": f"contentexpiry_{obj.pk}"}
)
return ""


def get_expiry_date(obj):
"""
A custom field to show the expiry date in the
Moderation Request Admin Changelist in djangocms-moderation.
:param obj: A Moderation Request object supplied from the admin view table row
:return: The expiry date from the matching moderation request object
"""
version = obj.moderation_request.version

if hasattr(version, "contentexpiry"):
return version.contentexpiry.expires


get_expiry_date.short_description = CONTENT_EXPIRY_EXPIRE_FIELD_LABEL


class ContentExpiryAppConfig(CMSAppConfig):
# Enable moderation to be able to "configure it"
djangocms_moderation_enabled = True
moderated_models = []
moderation_request_changelist_actions = [
get_moderation_content_expiry_link,
]
moderation_request_changelist_fields = [
get_expiry_date,
]
# Enable versioning because moderation is versioning dependant
djangocms_versioning_enabled = True
versioning = []

djangocms_content_expiry_enabled = getattr(
settings, "DJANGOCMS_CONTENT_EXPIRY_ENABLED", True
)
6 changes: 6 additions & 0 deletions djangocms_content_expiry/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.conf import settings


DEFAULT_RANGEFILTER_DELTA = getattr(
settings, "CMS_CONTENT_EXPIRY_DEFAULT_RANGEFILTER_DELTA", 30
)
4 changes: 4 additions & 0 deletions djangocms_content_expiry/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from django.utils.translation import ugettext_lazy as _


CONTENT_EXPIRY_EXPIRE_FIELD_LABEL = _("expiry date")
18 changes: 16 additions & 2 deletions djangocms_content_expiry/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

from djangocms_versioning.constants import PUBLISHED, VERSION_STATES
from djangocms_versioning.versionables import _cms_extension
from rangefilter.filters import DateRangeFilter

from . import helpers
from .helpers import get_authors, get_rangefilter_expires_default


class SimpleListMultiselectFilter(admin.SimpleListFilter):
Expand Down Expand Up @@ -158,7 +159,7 @@ class AuthorFilter(admin.SimpleListFilter):
def lookups(self, request, model_admin):
from django.utils.encoding import force_text
options = []
for user in helpers.get_authors():
for user in get_authors():
options.append(
(force_text(user.pk), user.get_full_name() or user.get_username())
)
Expand All @@ -168,3 +169,16 @@ def queryset(self, request, queryset):
if self.value():
return queryset.filter(created_by=self.value()).distinct()
return queryset


class ContentExpiryDateRangeFilter(DateRangeFilter):
def queryset(self, request, queryset):
queryset = super().queryset(request, queryset)

# By default the widget should default to show a default duration and not all content
# expiry records
if not any('expires__range' in seed for seed in request.GET):
default_gte, default_lte = get_rangefilter_expires_default()
queryset = queryset.filter(expires__range=(default_gte, default_lte))

return queryset
24 changes: 24 additions & 0 deletions djangocms_content_expiry/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from django import forms
from django.contrib.admin import widgets
from django.contrib.admin.sites import site

from .models import ContentExpiry


class ForeignKeyReadOnlyWidget(widgets.ForeignKeyRawIdWidget):
"""
A Widget for displaying ForeignKeys in a read only interface rather than
in a <select> box.
"""
template_name = 'admin/widgets/foreign_key_read_only.html'


class ContentExpiryForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

version_field = ContentExpiry._meta.get_field('version').remote_field
self.fields['version'].widget = ForeignKeyReadOnlyWidget(version_field, site)

user_field = ContentExpiry._meta.get_field('created_by').remote_field
self.fields['created_by'].widget = ForeignKeyReadOnlyWidget(user_field, site)
14 changes: 14 additions & 0 deletions djangocms_content_expiry/helpers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
from datetime import datetime, timedelta

from django.contrib.auth import get_user_model

from djangocms_content_expiry.conf import DEFAULT_RANGEFILTER_DELTA


def get_authors():
"""
Helper to return all authors created by content expiry
"""
User = get_user_model()
return User.objects.filter(contentexpiry__created_by__isnull=False).distinct()


def get_rangefilter_expires_default():
"""
Sets a default date range to help filter
Content Expiry records
"""
start_date = datetime.now() - timedelta(DEFAULT_RANGEFILTER_DELTA)
end_date = datetime.now()
return start_date, end_date
4 changes: 3 additions & 1 deletion djangocms_content_expiry/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

from djangocms_versioning.models import Version

from .constants import CONTENT_EXPIRY_EXPIRE_FIELD_LABEL


class ContentExpiry(models.Model):
created = models.DateTimeField(auto_now_add=True)
Expand All @@ -17,7 +19,7 @@ class ContentExpiry(models.Model):
on_delete=models.CASCADE,
verbose_name=_('version')
)
expires = models.DateTimeField(_("expiry date"))
expires = models.DateTimeField(CONTENT_EXPIRY_EXPIRE_FIELD_LABEL)

class Meta:
verbose_name = _("Content Expiry")
Expand Down
10 changes: 4 additions & 6 deletions djangocms_content_expiry/monkeypatch/admin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from django.utils.translation import ugettext_lazy as _

from djangocms_versioning import admin

from djangocms_content_expiry.constants import CONTENT_EXPIRY_EXPIRE_FIELD_LABEL
from djangocms_content_expiry.models import ContentExpiry


Expand All @@ -12,19 +11,18 @@ def expires(self, obj):
return ""


expires.short_description = _('expire date')
expires.short_description = CONTENT_EXPIRY_EXPIRE_FIELD_LABEL
admin.VersionAdmin.expire = expires


def get_list_display(func):
"""
Register the locked field with the Versioning Admin
Register the expires field with the Versioning Admin
"""

def inner(self, request):
list_display = func(self, request)
created_by_index = list_display.index('created_by')
return list_display[:created_by_index] + (_('expire'),) + list_display[created_by_index:]
return list_display[:created_by_index] + ('expire',) + list_display[created_by_index:]

return inner

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<input type="hidden" name="{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value|stringformat:'s' }}"{% endif %}>
{{ link_label }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{% load static i18n %}
<a class="btn cms-moderation-action-btn js-moderation-action related-widget-wrapper-link" id="change_content_expiry_id_{{ field_id }}" href="{{ url }}" title="{% trans 'View Expiry' %}"><span class="svg-juxtaposed-font"><img src="{% static 'djangocms_content_expiry/svg/calendar.svg' %}" /></span></a>
3 changes: 2 additions & 1 deletion djangocms_content_expiry/test_utils/polls/cms_config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from collections import OrderedDict

from cms.app_base import CMSAppConfig
from cms.utils.i18n import get_language_tuple

from collections import OrderedDict
from djangocms_versioning.datastructures import VersionableItem, default_copy

from .models import PollContent
Expand Down
5 changes: 3 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ combine_as_imports = true
include_trailing_comma = true
balanced_wrapping = true
skip = manage.py, migrations, .tox, node_modules
known_standard_library = mock
extra_standard_library = mock
known_django = django
known_cms = cms, menus
sections = FUTURE, STDLIB, DJANGO, CMS, THIRDPARTY, FIRSTPARTY, LIB, LOCALFOLDER
known_first_party = djangocms_content_expiry
sections = FUTURE, STDLIB, DJANGO, CMS, THIRDPARTY, FIRSTPARTY, LOCALFOLDER

[coverage:run]
branch = True
Expand Down
2 changes: 2 additions & 0 deletions test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
"djangocms_content_expiry",
"djangocms_content_expiry.test_utils.polls",
"djangocms_versioning",
"djangocms_moderation",
"rangefilter"
],
"MIGRATION_MODULES": {
"auth": None,
"cms": None,
"menus": None,
"djangocms_versioning": None,
"djangocms_moderation": None,
"djangocms_content_expiry": None,
},
"CMS_PERMISSION": True,
Expand Down
11 changes: 6 additions & 5 deletions tests/requirements/requirements_base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ django-treebeard>=4.3
argparse
dj-database-url
djangocms-admin-style>=1.5
django-admin-rangefilter
django-sekizai>=0.7
django-classy-tags>=0.7.2
iptools
Expand All @@ -16,10 +17,10 @@ django-app-helper
mock>=2.0.0
django-formtools
factory-boy
# Custom date range filter
django-admin-rangefilter
freezegun

# Unreleased django-cms 4.0 compatible packages
https://github.com/divio/django-cms/tarball/66c70394c9e144281a0b47d93e3784d06318acf9#egg=django-cms
https://github.com/divio/djangocms-text-ckeditor/archive/support/4.0.x.zip
https://github.com/divio/djangocms-versioning/tarball/492d01c974e40b007b71e2a7e961a9019a0d5ac4#egg=djangocms-versioning
https://github.com/divio/django-cms/tarball/release/4.0.x#egg=django-cms
https://github.com/divio/djangocms-text-ckeditor/tarball/support/4.0.x#egg=djangocms-text-ckeditor
https://github.com/divio/djangocms-versioning/tarball/master#egg=djangocms-versioning
https://github.com/django-cms/djangocms-moderation/tarball/release/1.0.x#egg=djangocms-moderation
Loading

0 comments on commit 091140b

Please sign in to comment.