Skip to content

Commit f829714

Browse files
Enable global Control Panel search via plone.restapi searchable text - 1981 (#1982)
* Fix-for-controlpannel-Searchabletext * Added changelog * control panel classes (in types.py and rules.py) and adding safety * Added suggested doc changes * Apply suggestion from @davisagli Co-authored-by: David Glick <david@glicksoftware.com> * Update src/plone/restapi/controlpanels/interfaces.py * Update control panel example response with searchable text (generated by running the tests on Plone 6.2 + Python 3.13) * Update news/1981.bugfix * Rename 1981.bugfix to 1981.feature --------- Co-authored-by: David Glick <david@glicksoftware.com>
1 parent 0dc0aaf commit f829714

File tree

7 files changed

+242
-1
lines changed

7 files changed

+242
-1
lines changed

news/1981.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The `@controlpanel` service now includes `searchable_text` for each control panel. @Manik-Khajuria-5

src/plone/restapi/controlpanels/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from zope.interface import implementer
44
from zope.publisher.interfaces import NotFound
55

6+
import zope.schema
7+
68

79
@implementer(IControlpanel)
810
class RegistryConfigletPanel:
@@ -38,6 +40,23 @@ def __init__(self, context, request):
3840
self.title = self.configlet["title"]
3941
self.group = self._get_group_title()
4042

43+
def get_searchable_text(self):
44+
45+
text_parts = []
46+
47+
if self.title:
48+
text_parts.append(self.title)
49+
50+
if self.group:
51+
text_parts.append(self.group)
52+
53+
if self.schema is not None:
54+
for name, field in zope.schema.getFields(self.schema).items():
55+
if field.title:
56+
text_parts.append(field.title)
57+
58+
return [text for text in text_parts if text]
59+
4160
def add(self, names):
4261
raise NotFound(self.context, names, self.request)
4362

src/plone/restapi/controlpanels/interfaces.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ def update(names):
2525
def delete(names):
2626
"""Remove controlpanel children by names"""
2727

28+
def get_searchable_text():
29+
"""Return searchable text for this control panel.
30+
31+
Schema-based control panels return text from field titles and descriptions.
32+
Other control panels can return custom text.
33+
"""
34+
2835

2936
class IDexterityTypesControlpanel(IControlpanel):
3037
"""Dexterity Types Control panel"""

src/plone/restapi/controlpanels/rules.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,21 @@ class ContentRulesControlpanel(RegistryConfigletPanel):
2525
def publishTraverse(self, request, name):
2626
return self.context.restrictedTraverse("++rule++" + name)
2727

28+
def get_searchable_text(self):
29+
text_parts = super().get_searchable_text()
30+
31+
cpanel = queryMultiAdapter(
32+
(self.context, self.request), name="rules-controlpanel"
33+
)
34+
if cpanel:
35+
registered_rules = cpanel.registeredRules()
36+
for rule in registered_rules:
37+
if isinstance(rule, dict):
38+
if rule.get("title"):
39+
text_parts.append(rule["title"])
40+
41+
return text_parts
42+
2843
def add(self, names):
2944
data = json_body(self.request)
3045
rules = queryMultiAdapter((self.context, self.request), name="+rule")

src/plone/restapi/controlpanels/types.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from plone.dexterity.interfaces import IDexterityFTI
12
from plone.i18n.normalizer import idnormalizer
23
from plone.restapi.controlpanels import RegistryConfigletPanel
34
from plone.restapi.controlpanels.interfaces import IDexterityTypesControlpanel
@@ -7,6 +8,7 @@
78
from plone.restapi.interfaces import ISerializeToJson
89
from zExceptions import BadRequest
910
from zope.component import adapter
11+
from zope.component import getAllUtilitiesRegisteredFor
1012
from zope.component import queryMultiAdapter
1113
from zope.interface import alsoProvides
1214
from zope.interface import implementer
@@ -22,6 +24,17 @@ class DexterityTypesControlpanel(RegistryConfigletPanel):
2224
configlet_id = "dexterity-types"
2325
configlet_category_id = "plone-content"
2426

27+
def get_searchable_text(self):
28+
29+
text_parts = super().get_searchable_text()
30+
31+
ftis = getAllUtilitiesRegisteredFor(IDexterityFTI)
32+
for fti in ftis:
33+
if fti.Title():
34+
text_parts.append(fti.Title())
35+
36+
return text_parts
37+
2538
def add(self, names):
2639
data = json_body(self.request)
2740

src/plone/restapi/serializer/controlpanels/__init__.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def __init__(self, controlpanel):
2626
self.controlpanel = controlpanel
2727

2828
def __call__(self):
29-
return {
29+
result = {
3030
"@id": "{}/{}/{}".format(
3131
self.controlpanel.context.absolute_url(),
3232
SERVICE_ID,
@@ -36,6 +36,17 @@ def __call__(self):
3636
"group": self.controlpanel.group,
3737
}
3838

39+
if hasattr(self.controlpanel, "get_searchable_text"):
40+
result["searchable_text"] = self.controlpanel.get_searchable_text()
41+
else:
42+
if self.controlpanel.title:
43+
result["searchable_text"] = [
44+
self.controlpanel.title,
45+
self.controlpanel.description,
46+
]
47+
48+
return result
49+
3950

4051
def get_jsonschema_for_controlpanel(controlpanel, context, request, form=None):
4152
"""Build a complete JSON schema for the given controlpanel."""

src/plone/restapi/tests/http-examples/controlpanels_get.resp

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,76 +5,251 @@ Content-Type: application/json
55
{
66
"@id": "http://localhost:55001/plone/@controlpanels/date-and-time",
77
"group": "General",
8+
"searchable_text": [
9+
"Date and Time",
10+
"General",
11+
"Portal default timezone",
12+
"Available timezones",
13+
"First weekday"
14+
],
815
"title": "Date and Time"
916
},
1017
{
1118
"@id": "http://localhost:55001/plone/@controlpanels/language",
1219
"group": "General",
20+
"searchable_text": [
21+
"Language",
22+
"General",
23+
"Site language",
24+
"Available languages",
25+
"Show country-specific language variants",
26+
"Show language flags",
27+
"Always show language selector",
28+
"Use the language of the content item",
29+
"Use language codes in URL path for manual override",
30+
"Use cookie for manual override",
31+
"Authenticated users only",
32+
"Set the language cookie always",
33+
"Use subdomain",
34+
"Use top-level domain",
35+
"Use browser language request negotiation"
36+
],
1337
"title": "Language"
1438
},
1539
{
1640
"@id": "http://localhost:55001/plone/@controlpanels/mail",
1741
"group": "General",
42+
"searchable_text": [
43+
"Mail",
44+
"General",
45+
"SMTP server",
46+
"SMTP port",
47+
"ESMTP username",
48+
"ESMTP password",
49+
"Site 'From' name",
50+
"Site 'From' address",
51+
"E-mail characterset"
52+
],
1853
"title": "Mail"
1954
},
2055
{
2156
"@id": "http://localhost:55001/plone/@controlpanels/navigation",
2257
"group": "General",
58+
"searchable_text": [
59+
"Navigation",
60+
"General",
61+
"Navigation depth",
62+
"Automatically generate tabs",
63+
"Generate tabs for items other than folders.",
64+
"Sort tabs on",
65+
"Reversed sort order for tabs.",
66+
"Displayed content types",
67+
"Filter on workflow state",
68+
"Show items normally excluded from navigation if viewing their children.",
69+
"Root",
70+
"Sitemap depth",
71+
"Hide children of these types"
72+
],
2373
"title": "Navigation"
2474
},
2575
{
2676
"@id": "http://localhost:55001/plone/@controlpanels/search",
2777
"group": "General",
78+
"searchable_text": [
79+
"Search",
80+
"General",
81+
"Enable LiveSearch",
82+
"Select content types which should be excluded from search results",
83+
"Crop the item description in search result listings after a number of characters.",
84+
"Sort on",
85+
"Show images in results",
86+
"Image scale for results"
87+
],
2888
"title": "Search"
2989
},
3090
{
3191
"@id": "http://localhost:55001/plone/@controlpanels/site",
3292
"group": "General",
93+
"searchable_text": [
94+
"Site",
95+
"General",
96+
"Site title",
97+
"Site Logo",
98+
"MIME type of the site favicon",
99+
"Site Favicon",
100+
"Expose Dublin Core metadata",
101+
"Expose sitemap.xml.gz",
102+
"JavaScript integrations included in head section",
103+
"JavaScript integrations included after the footer",
104+
"Display publication date",
105+
"Icon visibility",
106+
"Thumb visibility",
107+
"No Thumbs in portlets",
108+
"No thumbs in list views",
109+
"No thumbs in summary views",
110+
"No thumbs in table views",
111+
"Thumb scale for portlets",
112+
"Thumb scale for listings",
113+
"Thumb scale for tables",
114+
"Thumb scale for summary view",
115+
"Toolbar position",
116+
"Relative URL for the toolbar logo",
117+
"robots.txt",
118+
"Default page IDs",
119+
"Roles that can add keywords"
120+
],
33121
"title": "Site"
34122
},
35123
{
36124
"@id": "http://localhost:55001/plone/@controlpanels/socialmedia",
37125
"group": "General",
126+
"searchable_text": [
127+
"Social Media",
128+
"General",
129+
"Share social data",
130+
"Twitter username",
131+
"Facebook App ID",
132+
"Facebook username"
133+
],
38134
"title": "Social Media"
39135
},
40136
{
41137
"@id": "http://localhost:55001/plone/@controlpanels/content-rules",
42138
"group": "Content",
139+
"searchable_text": [
140+
"Content Rules",
141+
"Content"
142+
],
43143
"title": "Content Rules"
44144
},
45145
{
46146
"@id": "http://localhost:55001/plone/@controlpanels/dexterity-types",
47147
"group": "Content",
148+
"searchable_text": [
149+
"Content Types",
150+
"Content",
151+
"Plone Site",
152+
"Collection",
153+
"Page",
154+
"Folder",
155+
"Link",
156+
"File",
157+
"Image",
158+
"News Item",
159+
"Event",
160+
"DX Test Document"
161+
],
48162
"title": "Content Types"
49163
},
50164
{
51165
"@id": "http://localhost:55001/plone/@controlpanels/discussion",
52166
"group": "Content",
167+
"searchable_text": [
168+
"Discussion",
169+
"Content",
170+
"Globally enable comments",
171+
"Enable anonymous comments",
172+
"Enable anonymous email field",
173+
"Enable comment moderation",
174+
"Enable editing of comments",
175+
"Enable deleting own comments",
176+
"Comment text transform",
177+
"Captcha",
178+
"Show commenter image",
179+
"Enable moderator email notification",
180+
"Moderator Email Address",
181+
"Enable user email notification"
182+
],
53183
"title": "Discussion"
54184
},
55185
{
56186
"@id": "http://localhost:55001/plone/@controlpanels/editing",
57187
"group": "Content",
188+
"searchable_text": [
189+
"Editing",
190+
"Content",
191+
"Available editors",
192+
"Default editor",
193+
"Enable External Editor feature",
194+
"Enable link integrity checks",
195+
"Enable locking for through-the-web edits",
196+
"Limit tags/keywords to the current navigation root"
197+
],
58198
"title": "Editing"
59199
},
60200
{
61201
"@id": "http://localhost:55001/plone/@controlpanels/imaging",
62202
"group": "Content",
203+
"searchable_text": [
204+
"Image Handling",
205+
"Content",
206+
"Allowed image sizes",
207+
"Scaled image quality",
208+
"High pixel density mode",
209+
"Image quality at 2x",
210+
"Image quality at 3x",
211+
"Picture variants",
212+
"Enable image captioning"
213+
],
63214
"title": "Image Handling"
64215
},
65216
{
66217
"@id": "http://localhost:55001/plone/@controlpanels/markup",
67218
"group": "Content",
219+
"searchable_text": [
220+
"Markup",
221+
"Content",
222+
"Default format",
223+
"Alternative formats",
224+
"Enabled markdown extensions"
225+
],
68226
"title": "Markup"
69227
},
70228
{
71229
"@id": "http://localhost:55001/plone/@controlpanels/usergroup",
72230
"group": "Users",
231+
"searchable_text": [
232+
"User and Group Settings",
233+
"Users",
234+
"Many groups?",
235+
"Many users?"
236+
],
73237
"title": "User and Group Settings"
74238
},
75239
{
76240
"@id": "http://localhost:55001/plone/@controlpanels/security",
77241
"group": "Security",
242+
"searchable_text": [
243+
"Security",
244+
"Security",
245+
"Enable self-registration",
246+
"Let users select their own passwords",
247+
"Enable User Folders",
248+
"Allow anyone to view 'about' information",
249+
"Use email address as login name",
250+
"Use UUID user ids",
251+
"Login user after password reset"
252+
],
78253
"title": "Security"
79254
}
80255
]

0 commit comments

Comments
 (0)