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

Added burger menu for action items in list views #124

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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 CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog

Unreleased
==========
* feat: Added code for burger menu in actions column of changelist view.

1.0.7 (2022-04-01)
==================
Expand Down
11 changes: 8 additions & 3 deletions djangocms_navigation/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,13 @@ class MenuContentAdmin(admin.ModelAdmin):
list_filter = (LanguageFilter, )

class Media:
js = ("admin/js/jquery.init.js", "djangocms_versioning/js/actions.js",)
css = {"all": ("djangocms_versioning/css/actions.css", "djangocms_version_locking/css/version-locking.css",)}
js = ("admin/js/jquery.init.js", "djangocms_versioning/js/actions.js")
css = {
"all": (
"djangocms_versioning/css/actions.css",
"djangocms_version_locking/css/version-locking.css",
)
}

def get_version(self, obj):
"""
Expand Down Expand Up @@ -325,11 +330,11 @@ class MenuItemAdmin(TreeAdmin):
actions = None
change_form_template = "admin/djangocms_navigation/menuitem/change_form.html"
change_list_template = "admin/djangocms_navigation/menuitem/change_list.html"
list_display = ["__str__", "get_object_url", "soft_root", 'hide_node']
sortable_by = ["pk"]
list_per_page = TREE_MAX_RESULT_PER_PAGE_COUNT

class Media:
js = ("admin/js/jquery.init.js", "djangocms_versioning/js/actions.js")
css = {
"all": (
"djangocms_versioning/css/actions.css",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{% extends "admin/change_list.html" %}
{% load static %}

{% block extrahead %}
{{ block.super }}

{# INFO: versioning_static_url_prefix variable is used in versioning actions.js #}
<script>
let versioning_static_url_prefix = "{% static "djangocms_versioning/" %}";
</script>
{% endblock extrahead %}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
{% extends "admin/tree_change_list.html" %}
{% load static admin_list admin_urls navigation_admin_tree i18n djangocms_versioning %}

{% block extrahead %}
{{ block.super }}
{# INFO: versioning_static_url_prefix variable is used to inject static_url into actions.js #}
<script>
let versioning_static_url_prefix = "{% static "djangocms_versioning/" %}";
</script>
{% endblock extrahead %}

{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static 'djangocms_navigation/css/navigation_admin_overrides.css' %}"/>
Expand Down
68 changes: 66 additions & 2 deletions djangocms_navigation/templatetags/navigation_admin_tree.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
# -*- coding: utf-8 -*-
from django import template

import datetime

from django.contrib.admin.templatetags.admin_list import (
result_headers,
result_hidden_fields,
)
from django.contrib.admin.utils import (
display_for_field,
display_for_value,
lookup_field,
)
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.template import Library
from django.templatetags.static import static
from django.utils.encoding import force_str
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
Expand All @@ -13,17 +24,70 @@
from treebeard.templatetags.admin_tree import check_empty_dict, results


register = template.Library()
register = Library()

"""
This module is simply for overwriting some of treebeard's admin_tree templatetag functions
with djangocms_navigation specific values.

CAVEAT: Treebeard encapulates markup in some of it's template tags, so we are needing
to keep the same approach in order to overwrite markup, ie. with get_space and get_collapse below.

INFO: get_result_and_row_class is an almost exact copy, with the exception of a field_name
function to allow proper str representation for callables included as part of list_display
on changelist views.
"""


def get_field_name(field_name):
if callable(field_name):
return field_name.__name__
return field_name


def get_result_and_row_class(cl, field_name, result):
empty_value_display = cl.model_admin.get_empty_value_display()
row_classes = ['field-%s' % get_field_name(field_name)]
try:
f, attr, value = lookup_field(field_name, result, cl.model_admin)
except ObjectDoesNotExist:
result_repr = empty_value_display
else:
empty_value_display = getattr(attr, 'empty_value_display', empty_value_display)
if f is None:
if field_name == 'action_checkbox':
row_classes = ['action-checkbox']
allow_tags = getattr(attr, 'allow_tags', False)
boolean = getattr(attr, 'boolean', False)
result_repr = display_for_value(value, empty_value_display, boolean)
# Strip HTML tags in the resulting text, except if the
# function has an "allow_tags" attribute set to True.
# WARNING: this will be deprecated in Django 2.0
if allow_tags:
result_repr = mark_safe(result_repr)
if isinstance(value, (datetime.date, datetime.time)):
row_classes.append('nowrap')
else:
if isinstance(getattr(f, 'remote_field'), models.ManyToOneRel):
field_val = getattr(result, f.name)
if field_val is None:
result_repr = empty_value_display
else:
result_repr = field_val
else:
result_repr = display_for_field(value, f, empty_value_display)
if isinstance(f, (models.DateField, models.TimeField,
models.ForeignKey)):
row_classes.append('nowrap')
if force_str(result_repr) == '':
result_repr = mark_safe('&nbsp;')
row_class = mark_safe(' class="%s"' % ' '.join(row_classes))
return result_repr, row_class


admin_tree.get_result_and_row_class = get_result_and_row_class


def get_spacer(first, result):
if first:
spacer = f'<span class="spacer" style="--s-width:{(result.get_depth() - 1)}">&nbsp;</span>'
Expand Down
32 changes: 32 additions & 0 deletions tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import html
import importlib
import json
import re
import sys
from unittest.mock import patch

Expand Down Expand Up @@ -241,6 +242,20 @@ def test_list_display_without_versioning(self):
menu_content_admin.get_list_display(request), ["title", "get_menuitem_link", "get_preview_link"]
)

def test_menucontent_changelist_verify_burger_menu_available(self):
"""
The actions column should display a burger menu for any actions in addition
to edit or preview. Dependent on djangocms_versioning, verify media is
present.
"""
factories.MenuContentWithVersionFactory()
list_url = reverse("admin:djangocms_navigation_menucontent_changelist")

response = self.client.get(list_url)
soup = BeautifulSoup(str(response.content), features="lxml")

self.assertTrue(soup.find("script", src=re.compile("djangocms_versioning/js/actions.js")))


class MenuItemModelAdminTestCase(TestCase):
def setUp(self):
Expand Down Expand Up @@ -974,6 +989,23 @@ def test_menuitem_changelist_check_for_expand_all(self):
self.assertEqual(_('Toggle expand/collapse all'), link['title'])
self.assertIn('+', link.string)

def test_menuitem_changelist_verify_burger_menu_available(self):
"""
The actions burger menu should be available for MenuItem. Dependent on
djangocms_versioning, verify css class is present and js is loaded.
"""
menu_content = factories.MenuContentWithVersionFactory()

list_url = reverse(
"admin:djangocms_navigation_menuitem_list", args=(menu_content.id,)
)
response = self.client.get(list_url)

soup = BeautifulSoup(str(response.content), features="lxml")

self.assertTrue(soup.find_all("a", class_="cms-versioning-action-btn"))
self.assertTrue(soup.find("script", src=re.compile("djangocms_versioning/js/actions.js")))


@override_settings(
CMS_PERMISSION=True,
Expand Down