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];
+ };
+ }
+ );
+}
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..5e0b65ea2 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, "")
@@ -179,11 +195,43 @@ 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
}
+ 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 +296,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 +328,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
+
+ |
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