diff --git a/news/1981.feature b/news/1981.feature new file mode 100644 index 000000000..7a81cadfd --- /dev/null +++ b/news/1981.feature @@ -0,0 +1 @@ +The `@controlpanel` service now includes `searchable_text` for each control panel. @Manik-Khajuria-5 diff --git a/src/plone/restapi/controlpanels/__init__.py b/src/plone/restapi/controlpanels/__init__.py index a19875be2..52c8c0e29 100644 --- a/src/plone/restapi/controlpanels/__init__.py +++ b/src/plone/restapi/controlpanels/__init__.py @@ -3,6 +3,8 @@ from zope.interface import implementer from zope.publisher.interfaces import NotFound +import zope.schema + @implementer(IControlpanel) class RegistryConfigletPanel: @@ -38,6 +40,23 @@ def __init__(self, context, request): self.title = self.configlet["title"] self.group = self._get_group_title() + def get_searchable_text(self): + + text_parts = [] + + if self.title: + text_parts.append(self.title) + + if self.group: + text_parts.append(self.group) + + if self.schema is not None: + for name, field in zope.schema.getFields(self.schema).items(): + if field.title: + text_parts.append(field.title) + + return [text for text in text_parts if text] + def add(self, names): raise NotFound(self.context, names, self.request) diff --git a/src/plone/restapi/controlpanels/interfaces.py b/src/plone/restapi/controlpanels/interfaces.py index 61ba1a641..e35b9096a 100644 --- a/src/plone/restapi/controlpanels/interfaces.py +++ b/src/plone/restapi/controlpanels/interfaces.py @@ -25,6 +25,13 @@ def update(names): def delete(names): """Remove controlpanel children by names""" + def get_searchable_text(): + """Return searchable text for this control panel. + + Schema-based control panels return text from field titles and descriptions. + Other control panels can return custom text. + """ + class IDexterityTypesControlpanel(IControlpanel): """Dexterity Types Control panel""" diff --git a/src/plone/restapi/controlpanels/rules.py b/src/plone/restapi/controlpanels/rules.py index f39fc2b7d..ccb4e4a67 100644 --- a/src/plone/restapi/controlpanels/rules.py +++ b/src/plone/restapi/controlpanels/rules.py @@ -25,6 +25,21 @@ class ContentRulesControlpanel(RegistryConfigletPanel): def publishTraverse(self, request, name): return self.context.restrictedTraverse("++rule++" + name) + def get_searchable_text(self): + text_parts = super().get_searchable_text() + + cpanel = queryMultiAdapter( + (self.context, self.request), name="rules-controlpanel" + ) + if cpanel: + registered_rules = cpanel.registeredRules() + for rule in registered_rules: + if isinstance(rule, dict): + if rule.get("title"): + text_parts.append(rule["title"]) + + return text_parts + def add(self, names): data = json_body(self.request) rules = queryMultiAdapter((self.context, self.request), name="+rule") diff --git a/src/plone/restapi/controlpanels/types.py b/src/plone/restapi/controlpanels/types.py index 677398072..ecdf6ec2f 100644 --- a/src/plone/restapi/controlpanels/types.py +++ b/src/plone/restapi/controlpanels/types.py @@ -1,3 +1,4 @@ +from plone.dexterity.interfaces import IDexterityFTI from plone.i18n.normalizer import idnormalizer from plone.restapi.controlpanels import RegistryConfigletPanel from plone.restapi.controlpanels.interfaces import IDexterityTypesControlpanel @@ -7,6 +8,7 @@ from plone.restapi.interfaces import ISerializeToJson from zExceptions import BadRequest from zope.component import adapter +from zope.component import getAllUtilitiesRegisteredFor from zope.component import queryMultiAdapter from zope.interface import alsoProvides from zope.interface import implementer @@ -22,6 +24,17 @@ class DexterityTypesControlpanel(RegistryConfigletPanel): configlet_id = "dexterity-types" configlet_category_id = "plone-content" + def get_searchable_text(self): + + text_parts = super().get_searchable_text() + + ftis = getAllUtilitiesRegisteredFor(IDexterityFTI) + for fti in ftis: + if fti.Title(): + text_parts.append(fti.Title()) + + return text_parts + def add(self, names): data = json_body(self.request) diff --git a/src/plone/restapi/serializer/controlpanels/__init__.py b/src/plone/restapi/serializer/controlpanels/__init__.py index 86d377380..52d32de5c 100644 --- a/src/plone/restapi/serializer/controlpanels/__init__.py +++ b/src/plone/restapi/serializer/controlpanels/__init__.py @@ -26,7 +26,7 @@ def __init__(self, controlpanel): self.controlpanel = controlpanel def __call__(self): - return { + result = { "@id": "{}/{}/{}".format( self.controlpanel.context.absolute_url(), SERVICE_ID, @@ -36,6 +36,17 @@ def __call__(self): "group": self.controlpanel.group, } + if hasattr(self.controlpanel, "get_searchable_text"): + result["searchable_text"] = self.controlpanel.get_searchable_text() + else: + if self.controlpanel.title: + result["searchable_text"] = [ + self.controlpanel.title, + self.controlpanel.description, + ] + + return result + def get_jsonschema_for_controlpanel(controlpanel, context, request, form=None): """Build a complete JSON schema for the given controlpanel.""" diff --git a/src/plone/restapi/tests/http-examples/controlpanels_get.resp b/src/plone/restapi/tests/http-examples/controlpanels_get.resp index f8b7144db..14124b2a4 100644 --- a/src/plone/restapi/tests/http-examples/controlpanels_get.resp +++ b/src/plone/restapi/tests/http-examples/controlpanels_get.resp @@ -5,76 +5,251 @@ Content-Type: application/json { "@id": "http://localhost:55001/plone/@controlpanels/date-and-time", "group": "General", + "searchable_text": [ + "Date and Time", + "General", + "Portal default timezone", + "Available timezones", + "First weekday" + ], "title": "Date and Time" }, { "@id": "http://localhost:55001/plone/@controlpanels/language", "group": "General", + "searchable_text": [ + "Language", + "General", + "Site language", + "Available languages", + "Show country-specific language variants", + "Show language flags", + "Always show language selector", + "Use the language of the content item", + "Use language codes in URL path for manual override", + "Use cookie for manual override", + "Authenticated users only", + "Set the language cookie always", + "Use subdomain", + "Use top-level domain", + "Use browser language request negotiation" + ], "title": "Language" }, { "@id": "http://localhost:55001/plone/@controlpanels/mail", "group": "General", + "searchable_text": [ + "Mail", + "General", + "SMTP server", + "SMTP port", + "ESMTP username", + "ESMTP password", + "Site 'From' name", + "Site 'From' address", + "E-mail characterset" + ], "title": "Mail" }, { "@id": "http://localhost:55001/plone/@controlpanels/navigation", "group": "General", + "searchable_text": [ + "Navigation", + "General", + "Navigation depth", + "Automatically generate tabs", + "Generate tabs for items other than folders.", + "Sort tabs on", + "Reversed sort order for tabs.", + "Displayed content types", + "Filter on workflow state", + "Show items normally excluded from navigation if viewing their children.", + "Root", + "Sitemap depth", + "Hide children of these types" + ], "title": "Navigation" }, { "@id": "http://localhost:55001/plone/@controlpanels/search", "group": "General", + "searchable_text": [ + "Search", + "General", + "Enable LiveSearch", + "Select content types which should be excluded from search results", + "Crop the item description in search result listings after a number of characters.", + "Sort on", + "Show images in results", + "Image scale for results" + ], "title": "Search" }, { "@id": "http://localhost:55001/plone/@controlpanels/site", "group": "General", + "searchable_text": [ + "Site", + "General", + "Site title", + "Site Logo", + "MIME type of the site favicon", + "Site Favicon", + "Expose Dublin Core metadata", + "Expose sitemap.xml.gz", + "JavaScript integrations included in head section", + "JavaScript integrations included after the footer", + "Display publication date", + "Icon visibility", + "Thumb visibility", + "No Thumbs in portlets", + "No thumbs in list views", + "No thumbs in summary views", + "No thumbs in table views", + "Thumb scale for portlets", + "Thumb scale for listings", + "Thumb scale for tables", + "Thumb scale for summary view", + "Toolbar position", + "Relative URL for the toolbar logo", + "robots.txt", + "Default page IDs", + "Roles that can add keywords" + ], "title": "Site" }, { "@id": "http://localhost:55001/plone/@controlpanels/socialmedia", "group": "General", + "searchable_text": [ + "Social Media", + "General", + "Share social data", + "Twitter username", + "Facebook App ID", + "Facebook username" + ], "title": "Social Media" }, { "@id": "http://localhost:55001/plone/@controlpanels/content-rules", "group": "Content", + "searchable_text": [ + "Content Rules", + "Content" + ], "title": "Content Rules" }, { "@id": "http://localhost:55001/plone/@controlpanels/dexterity-types", "group": "Content", + "searchable_text": [ + "Content Types", + "Content", + "Plone Site", + "Collection", + "Page", + "Folder", + "Link", + "File", + "Image", + "News Item", + "Event", + "DX Test Document" + ], "title": "Content Types" }, { "@id": "http://localhost:55001/plone/@controlpanels/discussion", "group": "Content", + "searchable_text": [ + "Discussion", + "Content", + "Globally enable comments", + "Enable anonymous comments", + "Enable anonymous email field", + "Enable comment moderation", + "Enable editing of comments", + "Enable deleting own comments", + "Comment text transform", + "Captcha", + "Show commenter image", + "Enable moderator email notification", + "Moderator Email Address", + "Enable user email notification" + ], "title": "Discussion" }, { "@id": "http://localhost:55001/plone/@controlpanels/editing", "group": "Content", + "searchable_text": [ + "Editing", + "Content", + "Available editors", + "Default editor", + "Enable External Editor feature", + "Enable link integrity checks", + "Enable locking for through-the-web edits", + "Limit tags/keywords to the current navigation root" + ], "title": "Editing" }, { "@id": "http://localhost:55001/plone/@controlpanels/imaging", "group": "Content", + "searchable_text": [ + "Image Handling", + "Content", + "Allowed image sizes", + "Scaled image quality", + "High pixel density mode", + "Image quality at 2x", + "Image quality at 3x", + "Picture variants", + "Enable image captioning" + ], "title": "Image Handling" }, { "@id": "http://localhost:55001/plone/@controlpanels/markup", "group": "Content", + "searchable_text": [ + "Markup", + "Content", + "Default format", + "Alternative formats", + "Enabled markdown extensions" + ], "title": "Markup" }, { "@id": "http://localhost:55001/plone/@controlpanels/usergroup", "group": "Users", + "searchable_text": [ + "User and Group Settings", + "Users", + "Many groups?", + "Many users?" + ], "title": "User and Group Settings" }, { "@id": "http://localhost:55001/plone/@controlpanels/security", "group": "Security", + "searchable_text": [ + "Security", + "Security", + "Enable self-registration", + "Let users select their own passwords", + "Enable User Folders", + "Allow anyone to view 'about' information", + "Use email address as login name", + "Use UUID user ids", + "Login user after password reset" + ], "title": "Security" } ]