We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
sorting in control panel
The items of the control panel are completely unsorted (should be sorted in alphabetical order (depending on the current language in Plone).
![screenshot-dev1 veit-schiele de 8080 2015-07-18 13-35-44](https://cloud.githubusercontent.com/assets/594239/8761438/07d76bf4-2d52-11e5-8540-82fd53932690.png)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `Products/CMFPlone/PloneControlPanel.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 from AccessControl import ClassSecurityInfo
3 from AccessControl.class_init import InitializeClass
4 from App.special_dtml import DTMLFile
5 from OFS.Folder import Folder
6 from OFS.PropertyManager import PropertyManager
7 from Products.CMFCore.ActionInformation import ActionInformation
8 from Products.CMFCore.ActionProviderBase import ActionProviderBase
9 from Products.CMFCore.Expression import Expression, createExprContext
10 from Products.CMFCore.permissions import ManagePortal, View
11 from Products.CMFCore.utils import _checkPermission
12 from Products.CMFCore.utils import getToolByName
13 from Products.CMFCore.utils import registerToolInterface
14 from Products.CMFCore.utils import UniqueObject
15 from Products.CMFPlone import PloneMessageFactory as _
16 from Products.CMFPlone.interfaces import IControlPanel
17 from Products.CMFPlone.PloneBaseTool import PloneBaseTool
18 from zope.component.hooks import getSite
19 from zope.i18n import translate
20 from zope.i18nmessageid import Message
21 from zope.interface import implementer
22 
23 import six
24 
25 
26 class PloneConfiglet(ActionInformation):
27 
28     def __init__(self, appId, **kwargs):
29         self.appId = appId
30         ActionInformation.__init__(self, **kwargs)
31 
32     def getAppId(self):
33         return self.appId
34 
35     def getDescription(self):
36         return self.description
37 
38     def clone(self):
39         return self.__class__(**self.__dict__)
40 
41     def getAction(self, ec):
42         res = ActionInformation.getAction(self, ec)
43         res['description'] = self.getDescription()
44         return res
45 
46 
47 @implementer(IControlPanel)
48 class PloneControlPanel(PloneBaseTool, UniqueObject,
49                         Folder, ActionProviderBase, PropertyManager):
50     """Weave together the various sources of "actions" which
51     are apropos to the current user and context.
52     """
53 
54     security = ClassSecurityInfo()
55 
56     id = 'portal_controlpanel'
57     title = 'Control Panel'
58     toolicon = 'skins/plone_images/site_icon.png'
59     meta_type = 'Plone Control Panel Tool'
60     _actions_form = DTMLFile('www/editPloneConfiglets', globals())
61 
62     manage_options = (ActionProviderBase.manage_options +
63                       PropertyManager.manage_options)
64 
65     group = dict(
66         member=[
67             ('Member', _(u'My Preferences')),
68         ],
69         site=[
70             ('plone-general', _(u'General')),
71             ('plone-content', _(u'Content')),
72             ('plone-users', _(u'Users')),
73             ('plone-security', _(u'Security')),
74             ('plone-advanced', _(u'Advanced')),
75             ('Plone', _(u'Plone Configuration')),
76             ('Products', _(u'Add-on Configuration')),
77         ]
78     )
79 
80     def __init__(self, **kw):
81         if kw:
82             self.__dict__.update(**kw)
83 
84     security.declareProtected(ManagePortal, 'registerConfiglets')
85 
86     def registerConfiglets(self, configlets):
87         for conf in configlets:
88             self.registerConfiglet(**conf)
89 
90     security.declareProtected(ManagePortal, 'getGroupIds')
91 
92     def getGroupIds(self, category='site'):
93         groups = self.group.get(category, [])
94         return [g[0] for g in groups if g]
95 
96     security.declareProtected(View, 'getGroups')
97 
98     def getGroups(self, category='site'):
99         groups = self.group.get(category, [])
100         return [{'id': g[0], 'title': g[1]} for g in groups if g]
101 
102     security.declarePrivate('listActions')
103 
104     def listActions(self, info=None, object=None):
105         # This exists here to shut up a deprecation warning about old-style
106         # actions in CMFCore's ActionProviderBase.  It was decided not to
107         # move configlets to be based on action tool categories for Plone 4
108         # (see PLIP #8804), but that (or an alternative) will have to happen
109         # before CMF 2.4 when support for old-style actions is removed.
110         return self._actions or ()
111 
112     security.declarePublic('maySeeSomeConfiglets')
113 
114     def maySeeSomeConfiglets(self):
115         groups = self.getGroups('site')
116 
117         all = []
118         for group in groups:
119             all.extend(self.enumConfiglets(group=group['id']))
120         all = [item for item in all if item['visible']]
121         return len(all) != 0
122 
123     security.declarePublic('enumConfiglets')
124 
125     def enumConfiglets(self, group=None):
126         portal = getToolByName(self, 'portal_url').getPortalObject()
127         context = createExprContext(self, portal, self)
128         res = []
129         for a in self.listActions():
130             verified = 0
131             for permission in a.permissions:
132                 if _checkPermission(permission, portal):
133                     verified = 1
134             if verified and a.category == group and a.testCondition(context) \
135                     and a.visible:
136                 res.append(a.getAction(context))
137         # Translate the title for sorting
138         if getattr(self, 'REQUEST', None) is not None:
139             for a in res:
140                 title = a['title']
141                 if not isinstance(title, Message):
142                     title = Message(title, domain='plone')
143                 a['title'] = translate(title,
144                                        context=self.REQUEST)
145 
146         def _id(v):
147             return v['id']
148         res.sort(key=_id)
149         return res
150 
151     security.declareProtected(ManagePortal, 'unregisterConfiglet')
152 
153     def unregisterConfiglet(self, id):
154         actids = [o.id for o in self.listActions()]
155         selection = [actids.index(a) for a in actids if a == id]
156         if not selection:
157             return
158         self.deleteActions(selection)
159 
160     security.declareProtected(ManagePortal, 'unregisterApplication')
161 
162     def unregisterApplication(self, appId):
163         acts = list(self.listActions())
164         selection = [acts.index(a) for a in acts if a.appId == appId]
165         if not selection:
166             return
167         self.deleteActions(selection)
168 
169     def _extractAction(self, properties, index):
170         # Extract an ActionInformation from the funky form properties.
171         id = str(properties.get('id_%d' % index, ''))
172         name = str(properties.get('name_%d' % index, ''))
173         action = str(properties.get('action_%d' % index, ''))
174         condition = str(properties.get('condition_%d' % index, ''))
175         category = str(properties.get('category_%d' % index, ''))
176         visible = properties.get('visible_%d' % index, 0)
177         permissions = properties.get('permission_%d' % index, ())
178         appId = properties.get('appId_%d' % index, '')
179         description = properties.get('description_%d' % index, '')
180         icon_expr = properties.get('icon_expr_%d' % index, '')
181 
182         if not name:
183             raise ValueError('A name is required.')
184 
185         if action != '':
186             action = Expression(text=action)
187 
188         if condition != '':
189             condition = Expression(text=condition)
190 
191         if category == '':
192             category = 'object'
193 
194         if not isinstance(visible, int):
195             try:
196                 visible = int(visible)
197             except ValueError:
198                 visible = 0
199 
200         if isinstance(permissions, six.string_types):
201             permissions = (permissions, )
202 
203         return PloneConfiglet(id=id,
204                               title=name,
205                               action=action,
206                               condition=condition,
207                               permissions=permissions,
208                               category=category,
209                               visible=visible,
210                               appId=appId,
211                               description=description,
212                               icon_expr=icon_expr,
213                               )
214 
215     security.declareProtected(ManagePortal, 'addAction')
216 
217     def addAction(self,
218                   id,
219                   name,
220                   action,
221                   condition='',
222                   permission='',
223                   category='Plone',
224                   visible=1,
225                   appId=None,
226                   icon_expr='',
227                   description='',
228                   REQUEST=None,
229                   ):
230         # Add an action to our list.
231         if not name:
232             raise ValueError('A name is required.')
233 
234         a_expr = action and Expression(text=str(action)) or ''
235         c_expr = condition and Expression(text=str(condition)) or ''
236 
237         if not isinstance(permission, tuple):
238             permission = permission and (str(permission), ) or ()
239 
240         new_actions = self._cloneActions()
241 
242         new_action = PloneConfiglet(id=str(id),
243                                     title=name,
244                                     action=a_expr,
245                                     condition=c_expr,
246                                     permissions=permission,
247                                     category=str(category),
248                                     visible=int(visible),
249                                     appId=appId,
250                                     description=description,
251                                     icon_expr=icon_expr,
252                                     )
253 
254         new_actions.append(new_action)
255         self._actions = tuple(new_actions)
256 
257         if REQUEST is not None:
258             return self.manage_editActionsForm(
259                 REQUEST, manage_tabs_message='Added.')
260 
261     security.declareProtected(ManagePortal, 'registerConfiglet')
262     registerConfiglet = addAction
263 
264     security.declareProtected(ManagePortal, 'manage_editActionsForm')
265 
266     def manage_editActionsForm(self, REQUEST, manage_tabs_message=None):
267         """ Show the 'Actions' management tab.
268         """
269         actions = []
270 
271         for a in self.listActions():
272 
273             a1 = {}
274             a1['id'] = a.getId()
275             a1['name'] = a.Title()
276             p = a.getPermissions()
277             if p:
278                 a1['permission'] = p[0]
279             else:
280                 a1['permission'] = ''
281             a1['category'] = a.getCategory() or 'object'
282             a1['visible'] = a.getVisibility()
283             a1['action'] = a.getActionExpression()
284             a1['condition'] = a.getCondition()
285             a1['appId'] = a.getAppId()
286             a1['description'] = a.getDescription()
287             a1['icon_expr'] = a.getIconExpression()
288             actions.append(a1)
289 
290         # possible_permissions is in OFS.role.RoleManager.
291         pp = self.possible_permissions()
292         return self._actions_form(
293             self,
294             REQUEST,
295             actions=actions,
296             possible_permissions=pp,
297             management_view='Actions',
298             manage_tabs_message=manage_tabs_message,
299         )
300 
301     @property
302     def site_url(self):
303         """Return the absolute URL to the current site, which is likely not
304         necessarily the portal root.
305         Used by ``portlet_prefs`` to construct the URL to
306         ``@@overview-controlpanel``.
307         """
308         return getSite().absolute_url()
309 
310 
311 InitializeClass(PloneControlPanel)
312 registerToolInterface('portal_controlpanel', IControlPanel)
313
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/Products/CMFPlone/PloneControlPanel.py b/Products/CMFPlone/PloneControlPanel.py
--- a/Products/CMFPlone/PloneControlPanel.py
+++ b/Products/CMFPlone/PloneControlPanel.py
@@ -143,9 +143,10 @@
                 a['title'] = translate(title,
                                        context=self.REQUEST)
 
-        def _id(v):
-            return v['id']
-        res.sort(key=_id)
+        def _title(v):
+            return v['title']
+
+        res.sort(key=_title)
         return res
 
     security.declareProtected(ManagePortal, 'unregisterConfiglet')

{"golden_diff": "diff --git a/Products/CMFPlone/PloneControlPanel.py b/Products/CMFPlone/PloneControlPanel.py\n--- a/Products/CMFPlone/PloneControlPanel.py\n+++ b/Products/CMFPlone/PloneControlPanel.py\n@@ -143,9 +143,10 @@\n                 a['title'] = translate(title,\n                                        context=self.REQUEST)\n \n-        def _id(v):\n-            return v['id']\n-        res.sort(key=_id)\n+        def _title(v):\n+            return v['title']\n+\n+        res.sort(key=_title)\n         return res\n \n     security.declareProtected(ManagePortal, 'unregisterConfiglet')\n", "issue": "sorting in control panel\nThe items of the control panel are completely unsorted (should be sorted in alphabetical order (depending on the current language in Plone).\n![screenshot-dev1 veit-schiele de 8080 2015-07-18 13-35-44](https://cloud.githubusercontent.com/assets/594239/8761438/07d76bf4-2d52-11e5-8540-82fd53932690.png)\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\nfrom AccessControl import ClassSecurityInfo\nfrom AccessControl.class_init import InitializeClass\nfrom App.special_dtml import DTMLFile\nfrom OFS.Folder import Folder\nfrom OFS.PropertyManager import PropertyManager\nfrom Products.CMFCore.ActionInformation import ActionInformation\nfrom Products.CMFCore.ActionProviderBase import ActionProviderBase\nfrom Products.CMFCore.Expression import Expression, createExprContext\nfrom Products.CMFCore.permissions import ManagePortal, View\nfrom Products.CMFCore.utils import _checkPermission\nfrom Products.CMFCore.utils import getToolByName\nfrom Products.CMFCore.utils import registerToolInterface\nfrom Products.CMFCore.utils import UniqueObject\nfrom Products.CMFPlone import PloneMessageFactory as _\nfrom Products.CMFPlone.interfaces import IControlPanel\nfrom Products.CMFPlone.PloneBaseTool import PloneBaseTool\nfrom zope.component.hooks import getSite\nfrom zope.i18n import translate\nfrom zope.i18nmessageid import Message\nfrom zope.interface import implementer\n\nimport six\n\n\nclass PloneConfiglet(ActionInformation):\n\n    def __init__(self, appId, **kwargs):\n        self.appId = appId\n        ActionInformation.__init__(self, **kwargs)\n\n    def getAppId(self):\n        return self.appId\n\n    def getDescription(self):\n        return self.description\n\n    def clone(self):\n        return self.__class__(**self.__dict__)\n\n    def getAction(self, ec):\n        res = ActionInformation.getAction(self, ec)\n        res['description'] = self.getDescription()\n        return res\n\n\n@implementer(IControlPanel)\nclass PloneControlPanel(PloneBaseTool, UniqueObject,\n                        Folder, ActionProviderBase, PropertyManager):\n    \"\"\"Weave together the various sources of \"actions\" which\n    are apropos to the current user and context.\n    \"\"\"\n\n    security = ClassSecurityInfo()\n\n    id = 'portal_controlpanel'\n    title = 'Control Panel'\n    toolicon = 'skins/plone_images/site_icon.png'\n    meta_type = 'Plone Control Panel Tool'\n    _actions_form = DTMLFile('www/editPloneConfiglets', globals())\n\n    manage_options = (ActionProviderBase.manage_options +\n                      PropertyManager.manage_options)\n\n    group = dict(\n        member=[\n            ('Member', _(u'My Preferences')),\n        ],\n        site=[\n            ('plone-general', _(u'General')),\n            ('plone-content', _(u'Content')),\n            ('plone-users', _(u'Users')),\n            ('plone-security', _(u'Security')),\n            ('plone-advanced', _(u'Advanced')),\n            ('Plone', _(u'Plone Configuration')),\n            ('Products', _(u'Add-on Configuration')),\n        ]\n    )\n\n    def __init__(self, **kw):\n        if kw:\n            self.__dict__.update(**kw)\n\n    security.declareProtected(ManagePortal, 'registerConfiglets')\n\n    def registerConfiglets(self, configlets):\n        for conf in configlets:\n            self.registerConfiglet(**conf)\n\n    security.declareProtected(ManagePortal, 'getGroupIds')\n\n    def getGroupIds(self, category='site'):\n        groups = self.group.get(category, [])\n        return [g[0] for g in groups if g]\n\n    security.declareProtected(View, 'getGroups')\n\n    def getGroups(self, category='site'):\n        groups = self.group.get(category, [])\n        return [{'id': g[0], 'title': g[1]} for g in groups if g]\n\n    security.declarePrivate('listActions')\n\n    def listActions(self, info=None, object=None):\n        # This exists here to shut up a deprecation warning about old-style\n        # actions in CMFCore's ActionProviderBase.  It was decided not to\n        # move configlets to be based on action tool categories for Plone 4\n        # (see PLIP #8804), but that (or an alternative) will have to happen\n        # before CMF 2.4 when support for old-style actions is removed.\n        return self._actions or ()\n\n    security.declarePublic('maySeeSomeConfiglets')\n\n    def maySeeSomeConfiglets(self):\n        groups = self.getGroups('site')\n\n        all = []\n        for group in groups:\n            all.extend(self.enumConfiglets(group=group['id']))\n        all = [item for item in all if item['visible']]\n        return len(all) != 0\n\n    security.declarePublic('enumConfiglets')\n\n    def enumConfiglets(self, group=None):\n        portal = getToolByName(self, 'portal_url').getPortalObject()\n        context = createExprContext(self, portal, self)\n        res = []\n        for a in self.listActions():\n            verified = 0\n            for permission in a.permissions:\n                if _checkPermission(permission, portal):\n                    verified = 1\n            if verified and a.category == group and a.testCondition(context) \\\n                    and a.visible:\n                res.append(a.getAction(context))\n        # Translate the title for sorting\n        if getattr(self, 'REQUEST', None) is not None:\n            for a in res:\n                title = a['title']\n                if not isinstance(title, Message):\n                    title = Message(title, domain='plone')\n                a['title'] = translate(title,\n                                       context=self.REQUEST)\n\n        def _id(v):\n            return v['id']\n        res.sort(key=_id)\n        return res\n\n    security.declareProtected(ManagePortal, 'unregisterConfiglet')\n\n    def unregisterConfiglet(self, id):\n        actids = [o.id for o in self.listActions()]\n        selection = [actids.index(a) for a in actids if a == id]\n        if not selection:\n            return\n        self.deleteActions(selection)\n\n    security.declareProtected(ManagePortal, 'unregisterApplication')\n\n    def unregisterApplication(self, appId):\n        acts = list(self.listActions())\n        selection = [acts.index(a) for a in acts if a.appId == appId]\n        if not selection:\n            return\n        self.deleteActions(selection)\n\n    def _extractAction(self, properties, index):\n        # Extract an ActionInformation from the funky form properties.\n        id = str(properties.get('id_%d' % index, ''))\n        name = str(properties.get('name_%d' % index, ''))\n        action = str(properties.get('action_%d' % index, ''))\n        condition = str(properties.get('condition_%d' % index, ''))\n        category = str(properties.get('category_%d' % index, ''))\n        visible = properties.get('visible_%d' % index, 0)\n        permissions = properties.get('permission_%d' % index, ())\n        appId = properties.get('appId_%d' % index, '')\n        description = properties.get('description_%d' % index, '')\n        icon_expr = properties.get('icon_expr_%d' % index, '')\n\n        if not name:\n            raise ValueError('A name is required.')\n\n        if action != '':\n            action = Expression(text=action)\n\n        if condition != '':\n            condition = Expression(text=condition)\n\n        if category == '':\n            category = 'object'\n\n        if not isinstance(visible, int):\n            try:\n                visible = int(visible)\n            except ValueError:\n                visible = 0\n\n        if isinstance(permissions, six.string_types):\n            permissions = (permissions, )\n\n        return PloneConfiglet(id=id,\n                              title=name,\n                              action=action,\n                              condition=condition,\n                              permissions=permissions,\n                              category=category,\n                              visible=visible,\n                              appId=appId,\n                              description=description,\n                              icon_expr=icon_expr,\n                              )\n\n    security.declareProtected(ManagePortal, 'addAction')\n\n    def addAction(self,\n                  id,\n                  name,\n                  action,\n                  condition='',\n                  permission='',\n                  category='Plone',\n                  visible=1,\n                  appId=None,\n                  icon_expr='',\n                  description='',\n                  REQUEST=None,\n                  ):\n        # Add an action to our list.\n        if not name:\n            raise ValueError('A name is required.')\n\n        a_expr = action and Expression(text=str(action)) or ''\n        c_expr = condition and Expression(text=str(condition)) or ''\n\n        if not isinstance(permission, tuple):\n            permission = permission and (str(permission), ) or ()\n\n        new_actions = self._cloneActions()\n\n        new_action = PloneConfiglet(id=str(id),\n                                    title=name,\n                                    action=a_expr,\n                                    condition=c_expr,\n                                    permissions=permission,\n                                    category=str(category),\n                                    visible=int(visible),\n                                    appId=appId,\n                                    description=description,\n                                    icon_expr=icon_expr,\n                                    )\n\n        new_actions.append(new_action)\n        self._actions = tuple(new_actions)\n\n        if REQUEST is not None:\n            return self.manage_editActionsForm(\n                REQUEST, manage_tabs_message='Added.')\n\n    security.declareProtected(ManagePortal, 'registerConfiglet')\n    registerConfiglet = addAction\n\n    security.declareProtected(ManagePortal, 'manage_editActionsForm')\n\n    def manage_editActionsForm(self, REQUEST, manage_tabs_message=None):\n        \"\"\" Show the 'Actions' management tab.\n        \"\"\"\n        actions = []\n\n        for a in self.listActions():\n\n            a1 = {}\n            a1['id'] = a.getId()\n            a1['name'] = a.Title()\n            p = a.getPermissions()\n            if p:\n                a1['permission'] = p[0]\n            else:\n                a1['permission'] = ''\n            a1['category'] = a.getCategory() or 'object'\n            a1['visible'] = a.getVisibility()\n            a1['action'] = a.getActionExpression()\n            a1['condition'] = a.getCondition()\n            a1['appId'] = a.getAppId()\n            a1['description'] = a.getDescription()\n            a1['icon_expr'] = a.getIconExpression()\n            actions.append(a1)\n\n        # possible_permissions is in OFS.role.RoleManager.\n        pp = self.possible_permissions()\n        return self._actions_form(\n            self,\n            REQUEST,\n            actions=actions,\n            possible_permissions=pp,\n            management_view='Actions',\n            manage_tabs_message=manage_tabs_message,\n        )\n\n    @property\n    def site_url(self):\n        \"\"\"Return the absolute URL to the current site, which is likely not\n        necessarily the portal root.\n        Used by ``portlet_prefs`` to construct the URL to\n        ``@@overview-controlpanel``.\n        \"\"\"\n        return getSite().absolute_url()\n\n\nInitializeClass(PloneControlPanel)\nregisterToolInterface('portal_controlpanel', IControlPanel)\n", "path": "Products/CMFPlone/PloneControlPanel.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\nfrom AccessControl import ClassSecurityInfo\nfrom AccessControl.class_init import InitializeClass\nfrom App.special_dtml import DTMLFile\nfrom OFS.Folder import Folder\nfrom OFS.PropertyManager import PropertyManager\nfrom Products.CMFCore.ActionInformation import ActionInformation\nfrom Products.CMFCore.ActionProviderBase import ActionProviderBase\nfrom Products.CMFCore.Expression import Expression, createExprContext\nfrom Products.CMFCore.permissions import ManagePortal, View\nfrom Products.CMFCore.utils import _checkPermission\nfrom Products.CMFCore.utils import getToolByName\nfrom Products.CMFCore.utils import registerToolInterface\nfrom Products.CMFCore.utils import UniqueObject\nfrom Products.CMFPlone import PloneMessageFactory as _\nfrom Products.CMFPlone.interfaces import IControlPanel\nfrom Products.CMFPlone.PloneBaseTool import PloneBaseTool\nfrom zope.component.hooks import getSite\nfrom zope.i18n import translate\nfrom zope.i18nmessageid import Message\nfrom zope.interface import implementer\n\nimport six\n\n\nclass PloneConfiglet(ActionInformation):\n\n    def __init__(self, appId, **kwargs):\n        self.appId = appId\n        ActionInformation.__init__(self, **kwargs)\n\n    def getAppId(self):\n        return self.appId\n\n    def getDescription(self):\n        return self.description\n\n    def clone(self):\n        return self.__class__(**self.__dict__)\n\n    def getAction(self, ec):\n        res = ActionInformation.getAction(self, ec)\n        res['description'] = self.getDescription()\n        return res\n\n\n@implementer(IControlPanel)\nclass PloneControlPanel(PloneBaseTool, UniqueObject,\n                        Folder, ActionProviderBase, PropertyManager):\n    \"\"\"Weave together the various sources of \"actions\" which\n    are apropos to the current user and context.\n    \"\"\"\n\n    security = ClassSecurityInfo()\n\n    id = 'portal_controlpanel'\n    title = 'Control Panel'\n    toolicon = 'skins/plone_images/site_icon.png'\n    meta_type = 'Plone Control Panel Tool'\n    _actions_form = DTMLFile('www/editPloneConfiglets', globals())\n\n    manage_options = (ActionProviderBase.manage_options +\n                      PropertyManager.manage_options)\n\n    group = dict(\n        member=[\n            ('Member', _(u'My Preferences')),\n        ],\n        site=[\n            ('plone-general', _(u'General')),\n            ('plone-content', _(u'Content')),\n            ('plone-users', _(u'Users')),\n            ('plone-security', _(u'Security')),\n            ('plone-advanced', _(u'Advanced')),\n            ('Plone', _(u'Plone Configuration')),\n            ('Products', _(u'Add-on Configuration')),\n        ]\n    )\n\n    def __init__(self, **kw):\n        if kw:\n            self.__dict__.update(**kw)\n\n    security.declareProtected(ManagePortal, 'registerConfiglets')\n\n    def registerConfiglets(self, configlets):\n        for conf in configlets:\n            self.registerConfiglet(**conf)\n\n    security.declareProtected(ManagePortal, 'getGroupIds')\n\n    def getGroupIds(self, category='site'):\n        groups = self.group.get(category, [])\n        return [g[0] for g in groups if g]\n\n    security.declareProtected(View, 'getGroups')\n\n    def getGroups(self, category='site'):\n        groups = self.group.get(category, [])\n        return [{'id': g[0], 'title': g[1]} for g in groups if g]\n\n    security.declarePrivate('listActions')\n\n    def listActions(self, info=None, object=None):\n        # This exists here to shut up a deprecation warning about old-style\n        # actions in CMFCore's ActionProviderBase.  It was decided not to\n        # move configlets to be based on action tool categories for Plone 4\n        # (see PLIP #8804), but that (or an alternative) will have to happen\n        # before CMF 2.4 when support for old-style actions is removed.\n        return self._actions or ()\n\n    security.declarePublic('maySeeSomeConfiglets')\n\n    def maySeeSomeConfiglets(self):\n        groups = self.getGroups('site')\n\n        all = []\n        for group in groups:\n            all.extend(self.enumConfiglets(group=group['id']))\n        all = [item for item in all if item['visible']]\n        return len(all) != 0\n\n    security.declarePublic('enumConfiglets')\n\n    def enumConfiglets(self, group=None):\n        portal = getToolByName(self, 'portal_url').getPortalObject()\n        context = createExprContext(self, portal, self)\n        res = []\n        for a in self.listActions():\n            verified = 0\n            for permission in a.permissions:\n                if _checkPermission(permission, portal):\n                    verified = 1\n            if verified and a.category == group and a.testCondition(context) \\\n                    and a.visible:\n                res.append(a.getAction(context))\n        # Translate the title for sorting\n        if getattr(self, 'REQUEST', None) is not None:\n            for a in res:\n                title = a['title']\n                if not isinstance(title, Message):\n                    title = Message(title, domain='plone')\n                a['title'] = translate(title,\n                                       context=self.REQUEST)\n\n        def _title(v):\n            return v['title']\n\n        res.sort(key=_title)\n        return res\n\n    security.declareProtected(ManagePortal, 'unregisterConfiglet')\n\n    def unregisterConfiglet(self, id):\n        actids = [o.id for o in self.listActions()]\n        selection = [actids.index(a) for a in actids if a == id]\n        if not selection:\n            return\n        self.deleteActions(selection)\n\n    security.declareProtected(ManagePortal, 'unregisterApplication')\n\n    def unregisterApplication(self, appId):\n        acts = list(self.listActions())\n        selection = [acts.index(a) for a in acts if a.appId == appId]\n        if not selection:\n            return\n        self.deleteActions(selection)\n\n    def _extractAction(self, properties, index):\n        # Extract an ActionInformation from the funky form properties.\n        id = str(properties.get('id_%d' % index, ''))\n        name = str(properties.get('name_%d' % index, ''))\n        action = str(properties.get('action_%d' % index, ''))\n        condition = str(properties.get('condition_%d' % index, ''))\n        category = str(properties.get('category_%d' % index, ''))\n        visible = properties.get('visible_%d' % index, 0)\n        permissions = properties.get('permission_%d' % index, ())\n        appId = properties.get('appId_%d' % index, '')\n        description = properties.get('description_%d' % index, '')\n        icon_expr = properties.get('icon_expr_%d' % index, '')\n\n        if not name:\n            raise ValueError('A name is required.')\n\n        if action != '':\n            action = Expression(text=action)\n\n        if condition != '':\n            condition = Expression(text=condition)\n\n        if category == '':\n            category = 'object'\n\n        if not isinstance(visible, int):\n            try:\n                visible = int(visible)\n            except ValueError:\n                visible = 0\n\n        if isinstance(permissions, six.string_types):\n            permissions = (permissions, )\n\n        return PloneConfiglet(id=id,\n                              title=name,\n                              action=action,\n                              condition=condition,\n                              permissions=permissions,\n                              category=category,\n                              visible=visible,\n                              appId=appId,\n                              description=description,\n                              icon_expr=icon_expr,\n                              )\n\n    security.declareProtected(ManagePortal, 'addAction')\n\n    def addAction(self,\n                  id,\n                  name,\n                  action,\n                  condition='',\n                  permission='',\n                  category='Plone',\n                  visible=1,\n                  appId=None,\n                  icon_expr='',\n                  description='',\n                  REQUEST=None,\n                  ):\n        # Add an action to our list.\n        if not name:\n            raise ValueError('A name is required.')\n\n        a_expr = action and Expression(text=str(action)) or ''\n        c_expr = condition and Expression(text=str(condition)) or ''\n\n        if not isinstance(permission, tuple):\n            permission = permission and (str(permission), ) or ()\n\n        new_actions = self._cloneActions()\n\n        new_action = PloneConfiglet(id=str(id),\n                                    title=name,\n                                    action=a_expr,\n                                    condition=c_expr,\n                                    permissions=permission,\n                                    category=str(category),\n                                    visible=int(visible),\n                                    appId=appId,\n                                    description=description,\n                                    icon_expr=icon_expr,\n                                    )\n\n        new_actions.append(new_action)\n        self._actions = tuple(new_actions)\n\n        if REQUEST is not None:\n            return self.manage_editActionsForm(\n                REQUEST, manage_tabs_message='Added.')\n\n    security.declareProtected(ManagePortal, 'registerConfiglet')\n    registerConfiglet = addAction\n\n    security.declareProtected(ManagePortal, 'manage_editActionsForm')\n\n    def manage_editActionsForm(self, REQUEST, manage_tabs_message=None):\n        \"\"\" Show the 'Actions' management tab.\n        \"\"\"\n        actions = []\n\n        for a in self.listActions():\n\n            a1 = {}\n            a1['id'] = a.getId()\n            a1['name'] = a.Title()\n            p = a.getPermissions()\n            if p:\n                a1['permission'] = p[0]\n            else:\n                a1['permission'] = ''\n            a1['category'] = a.getCategory() or 'object'\n            a1['visible'] = a.getVisibility()\n            a1['action'] = a.getActionExpression()\n            a1['condition'] = a.getCondition()\n            a1['appId'] = a.getAppId()\n            a1['description'] = a.getDescription()\n            a1['icon_expr'] = a.getIconExpression()\n            actions.append(a1)\n\n        # possible_permissions is in OFS.role.RoleManager.\n        pp = self.possible_permissions()\n        return self._actions_form(\n            self,\n            REQUEST,\n            actions=actions,\n            possible_permissions=pp,\n            management_view='Actions',\n            manage_tabs_message=manage_tabs_message,\n        )\n\n    @property\n    def site_url(self):\n        \"\"\"Return the absolute URL to the current site, which is likely not\n        necessarily the portal root.\n        Used by ``portlet_prefs`` to construct the URL to\n        ``@@overview-controlpanel``.\n        \"\"\"\n        return getSite().absolute_url()\n\n\nInitializeClass(PloneControlPanel)\nregisterToolInterface('portal_controlpanel', IControlPanel)\n", "path": "Products/CMFPlone/PloneControlPanel.py"}]}