From 2cc58226147448882b0d42ad233f07de1dedd790 Mon Sep 17 00:00:00 2001 From: Thibaut Born Date: Tue, 23 Mar 2021 17:25:19 +0100 Subject: [PATCH 1/4] Problem: customizing tabular view is hard Solution: use views for table cells such that they can be overridden --- news/customize-tabular.feature | 1 + plone/app/contenttypes/browser/configure.zcml | 32 +++++++ plone/app/contenttypes/browser/folder.py | 57 +++++++++++- plone/app/contenttypes/browser/tabular.py | 81 +++++++++++++++++ .../browser/templates/creatorcell.pt | 8 ++ .../browser/templates/listing_tabular.pt | 89 ++----------------- .../browser/templates/titlecell.pt | 10 +++ 7 files changed, 192 insertions(+), 86 deletions(-) create mode 100644 news/customize-tabular.feature create mode 100644 plone/app/contenttypes/browser/tabular.py create mode 100644 plone/app/contenttypes/browser/templates/creatorcell.pt create mode 100644 plone/app/contenttypes/browser/templates/titlecell.pt diff --git a/news/customize-tabular.feature b/news/customize-tabular.feature new file mode 100644 index 000000000..451196464 --- /dev/null +++ b/news/customize-tabular.feature @@ -0,0 +1 @@ +Enable customization of tabular_view via views for fields of contentlisting items. diff --git a/plone/app/contenttypes/browser/configure.zcml b/plone/app/contenttypes/browser/configure.zcml index ae3464175..e1cd6ad2b 100644 --- a/plone/app/contenttypes/browser/configure.zcml +++ b/plone/app/contenttypes/browser/configure.zcml @@ -223,4 +223,36 @@ name="plone.app.contenttypes.migration.changed_base_classes" /> + + + + + + + + diff --git a/plone/app/contenttypes/browser/folder.py b/plone/app/contenttypes/browser/folder.py index 9bdac03b4..592e9a223 100644 --- a/plone/app/contenttypes/browser/folder.py +++ b/plone/app/contenttypes/browser/folder.py @@ -12,10 +12,14 @@ from plone.event.interfaces import IEvent from plone.memoize.view import memoize from plone.registry.interfaces import IRegistry +from Products.CMFPlone import PloneMessageFactory from Products.Five import BrowserView from zope.component import getMultiAdapter from zope.component import getUtility +from zope.component import queryMultiAdapter from zope.contentprovider.interfaces import IContentProvider +from zope.i18n import translate +from zope.i18nmessageid import Message import random @@ -25,6 +29,7 @@ class FolderView(BrowserView): _plone_view = None _portal_state = None _pas_member = None + _image_scale = None @property def plone_view(self): @@ -42,6 +47,15 @@ def portal_state(self): ) return self._portal_state + @property + def image_scale(self): + if not self._image_scale: + portal = self.portal_state.portal() + self._image_scale = getMultiAdapter( + (portal, self.request), name="image_scale" + ) + return self._image_scale + @property def pas_member(self): if not self._pas_member: @@ -142,7 +156,6 @@ def text(self): ) return text - @property def tabular_fields(self): ret = [] ret.append("Title") @@ -157,7 +170,10 @@ def tabular_field_label(self, field): """Return the internationalized label (Message object) corresponding to the field. """ - return get_field_label(field) + label = get_field_label(field) + if not isinstance(label, Message): + return PloneMessageFactory(label) + return label def tabular_fielddata(self, item, fieldname): value = getattr(item, fieldname, "") @@ -184,6 +200,35 @@ def tabular_fielddata(self, item, fieldname): "value": value } + def render_cells(self, item): + result = [] + icon_cell_view = queryMultiAdapter( + (item, self.request), name="tabular-cell-icon" + ) + if icon_cell_view is not None: + icon_cell_view.table_view = self + result.append(icon_cell_view()) + for field in self.tabular_fields(): + if field == "getIcon": + continue + cell_view = queryMultiAdapter( + (item, self.request), name=f"tabular-cell-{field}" + ) + if cell_view is not None: + cell_view.table_view = self + result.append(cell_view()) + else: + field_data = self.tabular_fielddata(item, field) + value = translate(field_data["value"], context=self.request) + result.append('%s' % value) + image_cell_view = queryMultiAdapter( + (item, self.request), name="tabular-cell-image" + ) + if image_cell_view is not None: + image_cell_view.table_view = self + result.append(image_cell_view()) + return "".join(result) + def is_event(self, obj): if getattr(obj, "getObject", False): obj = obj.getObject() @@ -248,6 +293,10 @@ def get_thumb_scale_table(self): return None return settings.thumb_scale_table + @property + def img_class(self): + return "thumb-%s pull-end" % self.get_thumb_scale_table() + @memoize def get_thumb_scale_list(self): if getattr(self.context, "suppress_thumbs", False): @@ -276,3 +325,7 @@ def get_thumb_scale_summary(self): def show_icons(self): return not getattr(self.context, "suppress_icons", False) + + @property + def iconresolver(self): + return self.context.restrictedTraverse("@@iconresolver") diff --git a/plone/app/contenttypes/browser/tabular.py b/plone/app/contenttypes/browser/tabular.py new file mode 100644 index 000000000..64b4ccd21 --- /dev/null +++ b/plone/app/contenttypes/browser/tabular.py @@ -0,0 +1,81 @@ +from Products.Five import BrowserView + +# BEWARE: the cell views are registered for ContentListingObject +# which are not acquisition aware. +# That precludes using Products.Five.ViewPageTemplateFile +# and imposes to use zope.browserpage.viewpagetemplatefile. +from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile + + +# BEWARE + + +class TitleCell(BrowserView): + __call__ = ViewPageTemplateFile("templates/titlecell.pt") + + @property + def title(self): + return self.context.Title() or self.context.getId() + + @property + def link(self): + suffix = ( + "/view" + if self.context.portal_type in self.table_view.use_view_action + else "" + ) + return self.context.getURL() + suffix + + @property + def type_class(self): + return ( + "contenttype/" + self.table_view.normalizeString(self.context.portal_type) + if self.table_view.show_icons + else "" + ) + + @property + def wf_state_class(self): + return "state-" + self.table_view.normalizeString(self.context.review_state()) + + +class CreatorCell(BrowserView): + __call__ = ViewPageTemplateFile("templates/creatorcell.pt") + + @property + def author(self): + return self.table_view.pas_member.info(self.context.Creator) + + @property + def author_name(self): + return self.author["fullname"] or self.author["username"] + + +class IconCell(BrowserView): + def __call__(self): + item = self.context + item_type = item.portal_type + if item_type == "File": + icon_type = "mimetype-" + item.mime_type + elif self.table_view.show_icons: + icon_type = "contenttype/" + self.table_view.normalizeString(item_type) + else: + icon_type = "" + icon = self.table_view.iconresolver.tag(icon_type).decode("utf8") + return "" + icon + "" + + +class ImageCell(BrowserView): + def render_image(self): + thumb_scale_table = self.table_view.get_thumb_scale_table() + if thumb_scale_table and self.context.getIcon: + img_class = self.table_view.img_class + return self.table_view.image_scale.tag( + self.context, "image", scale=thumb_scale_table, css_class=img_class + ) + else: + return "" + + def __call__(self): + image = self.render_image() + return "" + image + "" diff --git a/plone/app/contenttypes/browser/templates/creatorcell.pt b/plone/app/contenttypes/browser/templates/creatorcell.pt new file mode 100644 index 000000000..1b4017cb6 --- /dev/null +++ b/plone/app/contenttypes/browser/templates/creatorcell.pt @@ -0,0 +1,8 @@ + + Jos Henken + diff --git a/plone/app/contenttypes/browser/templates/listing_tabular.pt b/plone/app/contenttypes/browser/templates/listing_tabular.pt index 29b8cdc39..82fd3eea3 100644 --- a/plone/app/contenttypes/browser/templates/listing_tabular.pt +++ b/plone/app/contenttypes/browser/templates/listing_tabular.pt @@ -43,7 +43,6 @@ tal:define=" tabular_fields view/tabular_fields; thumb_scale_table python:view.get_thumb_scale_table(); - img_class python:'thumb-%s float-end' % thumb_scale_table; showicons python:view.show_icons(); " i18n:attributes="summary summary_content_listing;" @@ -62,93 +61,15 @@ >Image - - - - - - - - - - - - - - - - - - - - Item Title - - - - - - ${name} - - - - - - - - - - - - - - + + + diff --git a/plone/app/contenttypes/browser/templates/titlecell.pt b/plone/app/contenttypes/browser/templates/titlecell.pt new file mode 100644 index 000000000..5d6be4ecf --- /dev/null +++ b/plone/app/contenttypes/browser/templates/titlecell.pt @@ -0,0 +1,10 @@ + + Item Title + + From 262f62c6cb78b7d92ab6404736a598794dadb544 Mon Sep 17 00:00:00 2001 From: Thibaut Born Date: Fri, 25 Aug 2023 16:05:14 +0200 Subject: [PATCH 2/4] no brackets for displaying lists or tuples --- plone/app/contenttypes/browser/folder.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plone/app/contenttypes/browser/folder.py b/plone/app/contenttypes/browser/folder.py index 592e9a223..5e0b65ea2 100644 --- a/plone/app/contenttypes/browser/folder.py +++ b/plone/app/contenttypes/browser/folder.py @@ -195,6 +195,9 @@ def tabular_fielddata(self, item, fieldname): ]: value = self.toLocalizedTime(value, long_format=1) + if isinstance(value, (list, tuple)): + value = ", ".join([entry for entry in value]) + return { # 'title': _(fieldname, default=fieldname), "value": value From a5068799738ff84ae9a96424d1aca82ba299eba8 Mon Sep 17 00:00:00 2001 From: Godefroid Chapelle Date: Fri, 15 Sep 2023 18:37:13 +0200 Subject: [PATCH 3/4] nix develop setup --- MANIFEST.in | 1 + flake.nix | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 flake.nix diff --git a/MANIFEST.in b/MANIFEST.in index 74225c91d..0dfaaf3c6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -8,3 +8,4 @@ global-exclude *.pyc recursive-exclude news * exclude news +include flake.nix diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..e477ab172 --- /dev/null +++ b/flake.nix @@ -0,0 +1,26 @@ +{ + description = "A very basic flake"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/release-23.05"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem + (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + + my-python-packages = python-packages: with python-packages; [ + virtualenv + tox + ]; + python = pkgs.python311.withPackages my-python-packages; + in + { + devShells.default = pkgs.mkShell { + buildInputs = [ python pkgs.geckodriver pkgs.chromedriver]; + }; + } + ); +} From bc6bfd1b6f790d69b2d2a3dc616a975c965dc2c0 Mon Sep 17 00:00:00 2001 From: Godefroid Chapelle Date: Thu, 5 Oct 2023 16:05:30 +0200 Subject: [PATCH 4/4] add missing dependency --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 5ddf6af5c..32204eefd 100644 --- a/setup.py +++ b/setup.py @@ -48,6 +48,7 @@ def read(*rnames): install_requires=[ "setuptools", "plone.app.contentmenu", + "plone.app.contentlisting", "plone.app.dexterity >= 2.0.7", # has a fix for INameFromFilename "plone.app.linkintegrity", "plone.app.querystring >= 1.2.2", # custom_query support