We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
I cannot migrate a Plone 5.2 ZoDB to 6
I took a Plone 5.2.3 Database with some history and I tried to migrate it to Plone 6 (buildout coredev, dx site root plip cfg).

When I browse to the site root http://localhost:8080, I have this:
```
...
AttributeError: 'MountedObject' object has no attribute 'getId'

 - Expression: "view/sites"
 - Filename:   ... ne/Products/CMFPlone/browser/templates/plone-overview.pt
 ...
```

The reason is that we have the `temp_folder` which is already meant to be removed in Plone 6 but unluckily this is broken and so the meta_type attribute can be found:
```
((Pdb++)) pp root.values()                                                     
[<BrowserIdManager at /browser_id_manager>,
 <SessionDataManager at /session_data_manager>,
 <SiteErrorLog at /error_log>,
 <persistent broken Products.ZODBMountPoint.MountedObject.MountedObject instance b'\x00\x00\x00\x00\x00\x00\x00\t'>,
 <Products.SiteAccess.VirtualHostMonster.VirtualHostMonster object at 0x7f304b3984a0 oid 0xa in <Connection at 7f3051c6a490>>,
 <ZopePageTemplate at /index_html>,
 <PloneSite at /Plone>,
 <PluggableAuthService at /acl_users>]
```


--- 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/browser/admin.py`
Content:
```
1 from AccessControl import getSecurityManager
2 from AccessControl.Permissions import view as View
3 from OFS.interfaces import IApplication
4 from Products.CMFCore.permissions import ManagePortal
5 from Products.CMFPlone.factory import _DEFAULT_PROFILE
6 from Products.CMFPlone.factory import addPloneSite
7 from Products.CMFPlone.interfaces import INonInstallable
8 from Products.CMFPlone.interfaces import IPloneSiteRoot
9 from Products.CMFPlone.utils import get_installer
10 from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
11 from Products.GenericSetup import BASE, EXTENSION
12 from Products.GenericSetup import profile_registry
13 from Products.GenericSetup.upgrade import normalize_version
14 from ZPublisher.BaseRequest import DefaultPublishTraverse
15 from collections import OrderedDict
16 from plone.i18n.locales.interfaces import IContentLanguageAvailability
17 from plone.keyring.interfaces import IKeyManager
18 from plone.protect.authenticator import check as checkCSRF
19 from plone.protect.interfaces import IDisableCSRFProtection
20 from urllib import parse
21 from zope.component import adapts
22 from zope.component import getAllUtilitiesRegisteredFor
23 from zope.component import getUtility
24 from zope.component import queryMultiAdapter
25 from zope.component import queryUtility
26 from zope.i18n.interfaces import IUserPreferredLanguages
27 from zope.i18n.locales import locales, LoadLocaleError
28 from zope.interface import Interface
29 from zope.interface import alsoProvides
30 from zope.publisher.browser import BrowserView
31 from zope.publisher.interfaces import IRequest
32 from zope.schema.interfaces import IVocabularyFactory
33 
34 import logging
35 LOGGER = logging.getLogger('Products.CMFPlone')
36 
37 
38 class AppTraverser(DefaultPublishTraverse):
39     adapts(IApplication, IRequest)
40 
41     def publishTraverse(self, request, name):
42         if name == 'index_html':
43             view = queryMultiAdapter(
44                 (self.context, request), Interface, 'plone-overview')
45             if view is not None:
46                 return view
47         return DefaultPublishTraverse.publishTraverse(self, request, name)
48 
49 
50 class Overview(BrowserView):
51 
52     def sites(self, root=None):
53         if root is None:
54             root = self.context
55 
56         result = []
57         secman = getSecurityManager()
58         for obj in root.values():
59             if obj.meta_type == 'Folder':
60                 result = result + self.sites(obj)
61             elif IPloneSiteRoot.providedBy(obj):
62                 if secman.checkPermission(View, obj):
63                     result.append(obj)
64             elif obj.getId() in getattr(root, '_mount_points', {}):
65                 result.extend(self.sites(root=obj))
66         return result
67 
68     def outdated(self, obj):
69         mig = obj.get('portal_migration', None)
70         if mig is not None:
71             return mig.needUpgrading()
72         return False
73 
74     def can_manage(self):
75         secman = getSecurityManager()
76         return secman.checkPermission(ManagePortal, self.context)
77 
78     def upgrade_url(self, site, can_manage=None):
79         if can_manage is None:
80             can_manage = self.can_manage()
81         if can_manage:
82             return site.absolute_url() + '/@@plone-upgrade'
83         else:
84             return self.context.absolute_url() + '/@@plone-root-login'
85 
86 
87 class RootLoginRedirect(BrowserView):
88     """ @@plone-root-login
89 
90     This view of the Zope root forces authentication via the root
91     acl_users and then redirects elsewhere.
92     """
93 
94     def __call__(self, came_from=None):
95         if came_from is not None:
96             # see if this is a relative url or an absolute
97             if len(parse.urlparse(came_from)[1]) == 0:
98                 # No host specified, so url is relative.  Get an absolute url.
99                 # Note: '\\domain.org' is not recognised as host,
100                 # which is good.
101                 came_from = parse.urljoin(
102                     self.context.absolute_url() + '/', came_from,
103                 )
104             elif not came_from.startswith(self.context.absolute_url()):
105                 # Note: we cannot use portal_url.isURLInPortal here, because we
106                 # are not in a Plone portal, but in the Zope root.
107                 came_from = None
108         if came_from is None:
109             came_from = self.context.absolute_url()
110         self.request.response.redirect(came_from)
111 
112 
113 class RootLogout(BrowserView):
114     """ @@plone-root-logout """
115 
116     logout = ViewPageTemplateFile('templates/plone-admin-logged-out.pt')
117 
118     def __call__(self):
119         response = self.request.response
120         realm = response.realm
121         response.setStatus(401)
122         response.setHeader('WWW-Authenticate', 'basic realm="%s"' % realm, 1)
123         response.setBody(self.logout())
124         return
125 
126 
127 class FrontPage(BrowserView):
128 
129     index = ViewPageTemplateFile('templates/plone-frontpage.pt')
130 
131 
132 class AddPloneSite(BrowserView):
133 
134     # Profiles that are installed by default,
135     # but can be removed later.
136     default_extension_profiles = (
137         'plone.app.caching:default',
138         'plonetheme.barceloneta:default',
139     )
140 
141     def profiles(self):
142         base_profiles = []
143         extension_profiles = []
144 
145         # profiles available for install/uninstall, but hidden at the time
146         # the Plone site is created
147         not_installable = [
148             'Products.CMFPlacefulWorkflow:CMFPlacefulWorkflow',
149         ]
150         utils = getAllUtilitiesRegisteredFor(INonInstallable)
151         for util in utils:
152             not_installable.extend(util.getNonInstallableProfiles())
153 
154         for info in profile_registry.listProfileInfo():
155             if info.get('type') == EXTENSION and \
156                info.get('for') in (IPloneSiteRoot, None):
157                 profile_id = info.get('id')
158                 if profile_id not in not_installable:
159                     if profile_id in self.default_extension_profiles:
160                         info['selected'] = 'selected'
161                     extension_profiles.append(info)
162 
163         def _key(v):
164             # Make sure implicitly selected items come first
165             selected = v.get('selected') and 'automatic' or 'manual'
166             return '{}-{}'.format(selected, v.get('title', ''))
167         extension_profiles.sort(key=_key)
168 
169         for info in profile_registry.listProfileInfo():
170             if info.get('type') == BASE and \
171                info.get('for') in (IPloneSiteRoot, None):
172                 base_profiles.append(info)
173 
174         return dict(
175             base=tuple(base_profiles),
176             default=_DEFAULT_PROFILE,
177             extensions=tuple(extension_profiles),
178         )
179 
180     def browser_language(self):
181         language = 'en'
182         pl = IUserPreferredLanguages(self.request)
183         if pl is not None:
184             languages = pl.getPreferredLanguages()
185             for httplang in languages:
186                 parts = (httplang.split('-') + [None, None])[:3]
187                 if parts[0] == parts[1]:
188                     # Avoid creating a country code for simple languages codes
189                     parts = [parts[0], None, None]
190                 try:
191                     locale = locales.getLocale(*parts)
192                     language = locale.getLocaleID().replace('_', '-').lower()
193                     break
194                 except LoadLocaleError:
195                     # Just try the next combination
196                     pass
197         return language
198 
199     def grouped_languages(self, default='en'):
200         util = queryUtility(IContentLanguageAvailability)
201         available = util.getLanguages(combined=True)
202         languages = dict(util.getLanguageListing())
203 
204         # Group country specific versions by language
205         grouped = OrderedDict()
206         for langcode, data in available.items():
207             lang = langcode.split('-')[0]
208             language = languages.get(lang, lang)  # Label
209 
210             struct = grouped.get(lang, {'label': language, 'languages': []})
211 
212             langs = struct['languages']
213             langs.append({
214                 'langcode': langcode,
215                 'label': data.get('native', data.get('name')),
216             })
217 
218             grouped[lang] = struct
219 
220         # Sort list by language, next by country
221         data = sorted(grouped.values(), key=lambda k: k['label'])
222         for item in data:
223             item['languages'] = sorted(
224                 item['languages'], key=lambda k: k['label'].lower())
225         return data
226 
227     def timezones(self):
228         tz_vocab = getUtility(
229             IVocabularyFactory,
230             'plone.app.vocabularies.CommonTimezones'
231         )(self.context)
232 
233         grouped = OrderedDict()
234         tz_values = [it.value for it in tz_vocab]
235         for value in tz_values:
236             splitted = value.split('/')
237             group = splitted.pop(0)
238             label = '/'.join(splitted)
239 
240             entries = grouped.get(group, [])
241             entries.append({'label': label or group, 'value': value})
242             grouped[group] = entries
243 
244         return grouped
245 
246     def __call__(self):
247         context = self.context
248         form = self.request.form
249         submitted = form.get('form.submitted', False)
250         if submitted:
251             site_id = form.get('site_id', 'Plone')
252 
253             # CSRF protect. DO NOT use auto CSRF protection for adding a site
254             alsoProvides(self.request, IDisableCSRFProtection)
255 
256             # check if keyring is installed on root, disable CSRF protection
257             # if it is because it is not installed until a plone site
258             # is created
259             if queryUtility(IKeyManager) is None:
260                 LOGGER.info('CSRF protection disabled on initial site '
261                             'creation')
262             else:
263                 # we have a keymanager, check csrf protection manually now
264                 checkCSRF(self.request)
265             site = addPloneSite(
266                 context, site_id,
267                 title=form.get('title', ''),
268                 profile_id=form.get('profile_id', _DEFAULT_PROFILE),
269                 extension_ids=form.get('extension_ids', ()),
270                 setup_content=form.get('setup_content', False),
271                 default_language=form.get('default_language', 'en'),
272                 portal_timezone=form.get('portal_timezone', 'UTC')
273             )
274             self.request.response.redirect(site.absolute_url())
275             return ''
276 
277         return self.index()
278 
279 
280 class Upgrade(BrowserView):
281 
282     def upgrades(self):
283         pm = getattr(self.context, 'portal_migration')
284         return pm.listUpgrades()
285 
286     def versions(self):
287         pm = getattr(self.context, 'portal_migration')
288         result = {}
289         result['instance'] = pm.getInstanceVersion()
290         result['fs'] = pm.getFileSystemVersion()
291         result['equal'] = result['instance'] == result['fs']
292         instance_version = normalize_version(result['instance'])
293         fs_version = normalize_version(result['fs'])
294         result['instance_gt'] = instance_version > fs_version
295         result['instance_lt'] = instance_version < fs_version
296         result['corelist'] = pm.coreVersions()
297         return result
298 
299     def __call__(self):
300         form = self.request.form
301         submitted = form.get('form.submitted', False)
302         if submitted:
303             # CSRF protect. DO NOT use auto CSRF protection for upgrading sites
304             alsoProvides(self.request, IDisableCSRFProtection)
305 
306             pm = getattr(self.context, 'portal_migration')
307             report = pm.upgrade(
308                 REQUEST=self.request,
309                 dry_run=form.get('dry_run', False),
310             )
311             return self.index(
312                 report=report,
313             )
314 
315         return self.index()
316
```
--- 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/browser/admin.py b/Products/CMFPlone/browser/admin.py
--- a/Products/CMFPlone/browser/admin.py
+++ b/Products/CMFPlone/browser/admin.py
@@ -18,6 +18,7 @@
 from plone.protect.authenticator import check as checkCSRF
 from plone.protect.interfaces import IDisableCSRFProtection
 from urllib import parse
+from ZODB.broken import Broken
 from zope.component import adapts
 from zope.component import getAllUtilitiesRegisteredFor
 from zope.component import getUtility
@@ -55,7 +56,10 @@
 
         result = []
         secman = getSecurityManager()
-        for obj in root.values():
+        candidates = (
+            obj for obj in root.values() if not isinstance(obj, Broken)
+        )
+        for obj in candidates:
             if obj.meta_type == 'Folder':
                 result = result + self.sites(obj)
             elif IPloneSiteRoot.providedBy(obj):

{"golden_diff": "diff --git a/Products/CMFPlone/browser/admin.py b/Products/CMFPlone/browser/admin.py\n--- a/Products/CMFPlone/browser/admin.py\n+++ b/Products/CMFPlone/browser/admin.py\n@@ -18,6 +18,7 @@\n from plone.protect.authenticator import check as checkCSRF\n from plone.protect.interfaces import IDisableCSRFProtection\n from urllib import parse\n+from ZODB.broken import Broken\n from zope.component import adapts\n from zope.component import getAllUtilitiesRegisteredFor\n from zope.component import getUtility\n@@ -55,7 +56,10 @@\n \n         result = []\n         secman = getSecurityManager()\n-        for obj in root.values():\n+        candidates = (\n+            obj for obj in root.values() if not isinstance(obj, Broken)\n+        )\n+        for obj in candidates:\n             if obj.meta_type == 'Folder':\n                 result = result + self.sites(obj)\n             elif IPloneSiteRoot.providedBy(obj):\n", "issue": "I cannot migrate a Plone 5.2 ZoDB to 6\nI took a Plone 5.2.3 Database with some history and I tried to migrate it to Plone 6 (buildout coredev, dx site root plip cfg).\r\n\r\nWhen I browse to the site root http://localhost:8080, I have this:\r\n```\r\n...\r\nAttributeError: 'MountedObject' object has no attribute 'getId'\r\n\r\n - Expression: \"view/sites\"\r\n - Filename:   ... ne/Products/CMFPlone/browser/templates/plone-overview.pt\r\n ...\r\n```\r\n\r\nThe reason is that we have the `temp_folder` which is already meant to be removed in Plone 6 but unluckily this is broken and so the meta_type attribute can be found:\r\n```\r\n((Pdb++)) pp root.values()                                                     \r\n[<BrowserIdManager at /browser_id_manager>,\r\n <SessionDataManager at /session_data_manager>,\r\n <SiteErrorLog at /error_log>,\r\n <persistent broken Products.ZODBMountPoint.MountedObject.MountedObject instance b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\t'>,\r\n <Products.SiteAccess.VirtualHostMonster.VirtualHostMonster object at 0x7f304b3984a0 oid 0xa in <Connection at 7f3051c6a490>>,\r\n <ZopePageTemplate at /index_html>,\r\n <PloneSite at /Plone>,\r\n <PluggableAuthService at /acl_users>]\r\n```\r\n\n", "before_files": [{"content": "from AccessControl import getSecurityManager\nfrom AccessControl.Permissions import view as View\nfrom OFS.interfaces import IApplication\nfrom Products.CMFCore.permissions import ManagePortal\nfrom Products.CMFPlone.factory import _DEFAULT_PROFILE\nfrom Products.CMFPlone.factory import addPloneSite\nfrom Products.CMFPlone.interfaces import INonInstallable\nfrom Products.CMFPlone.interfaces import IPloneSiteRoot\nfrom Products.CMFPlone.utils import get_installer\nfrom Products.Five.browser.pagetemplatefile import ViewPageTemplateFile\nfrom Products.GenericSetup import BASE, EXTENSION\nfrom Products.GenericSetup import profile_registry\nfrom Products.GenericSetup.upgrade import normalize_version\nfrom ZPublisher.BaseRequest import DefaultPublishTraverse\nfrom collections import OrderedDict\nfrom plone.i18n.locales.interfaces import IContentLanguageAvailability\nfrom plone.keyring.interfaces import IKeyManager\nfrom plone.protect.authenticator import check as checkCSRF\nfrom plone.protect.interfaces import IDisableCSRFProtection\nfrom urllib import parse\nfrom zope.component import adapts\nfrom zope.component import getAllUtilitiesRegisteredFor\nfrom zope.component import getUtility\nfrom zope.component import queryMultiAdapter\nfrom zope.component import queryUtility\nfrom zope.i18n.interfaces import IUserPreferredLanguages\nfrom zope.i18n.locales import locales, LoadLocaleError\nfrom zope.interface import Interface\nfrom zope.interface import alsoProvides\nfrom zope.publisher.browser import BrowserView\nfrom zope.publisher.interfaces import IRequest\nfrom zope.schema.interfaces import IVocabularyFactory\n\nimport logging\nLOGGER = logging.getLogger('Products.CMFPlone')\n\n\nclass AppTraverser(DefaultPublishTraverse):\n    adapts(IApplication, IRequest)\n\n    def publishTraverse(self, request, name):\n        if name == 'index_html':\n            view = queryMultiAdapter(\n                (self.context, request), Interface, 'plone-overview')\n            if view is not None:\n                return view\n        return DefaultPublishTraverse.publishTraverse(self, request, name)\n\n\nclass Overview(BrowserView):\n\n    def sites(self, root=None):\n        if root is None:\n            root = self.context\n\n        result = []\n        secman = getSecurityManager()\n        for obj in root.values():\n            if obj.meta_type == 'Folder':\n                result = result + self.sites(obj)\n            elif IPloneSiteRoot.providedBy(obj):\n                if secman.checkPermission(View, obj):\n                    result.append(obj)\n            elif obj.getId() in getattr(root, '_mount_points', {}):\n                result.extend(self.sites(root=obj))\n        return result\n\n    def outdated(self, obj):\n        mig = obj.get('portal_migration', None)\n        if mig is not None:\n            return mig.needUpgrading()\n        return False\n\n    def can_manage(self):\n        secman = getSecurityManager()\n        return secman.checkPermission(ManagePortal, self.context)\n\n    def upgrade_url(self, site, can_manage=None):\n        if can_manage is None:\n            can_manage = self.can_manage()\n        if can_manage:\n            return site.absolute_url() + '/@@plone-upgrade'\n        else:\n            return self.context.absolute_url() + '/@@plone-root-login'\n\n\nclass RootLoginRedirect(BrowserView):\n    \"\"\" @@plone-root-login\n\n    This view of the Zope root forces authentication via the root\n    acl_users and then redirects elsewhere.\n    \"\"\"\n\n    def __call__(self, came_from=None):\n        if came_from is not None:\n            # see if this is a relative url or an absolute\n            if len(parse.urlparse(came_from)[1]) == 0:\n                # No host specified, so url is relative.  Get an absolute url.\n                # Note: '\\\\domain.org' is not recognised as host,\n                # which is good.\n                came_from = parse.urljoin(\n                    self.context.absolute_url() + '/', came_from,\n                )\n            elif not came_from.startswith(self.context.absolute_url()):\n                # Note: we cannot use portal_url.isURLInPortal here, because we\n                # are not in a Plone portal, but in the Zope root.\n                came_from = None\n        if came_from is None:\n            came_from = self.context.absolute_url()\n        self.request.response.redirect(came_from)\n\n\nclass RootLogout(BrowserView):\n    \"\"\" @@plone-root-logout \"\"\"\n\n    logout = ViewPageTemplateFile('templates/plone-admin-logged-out.pt')\n\n    def __call__(self):\n        response = self.request.response\n        realm = response.realm\n        response.setStatus(401)\n        response.setHeader('WWW-Authenticate', 'basic realm=\"%s\"' % realm, 1)\n        response.setBody(self.logout())\n        return\n\n\nclass FrontPage(BrowserView):\n\n    index = ViewPageTemplateFile('templates/plone-frontpage.pt')\n\n\nclass AddPloneSite(BrowserView):\n\n    # Profiles that are installed by default,\n    # but can be removed later.\n    default_extension_profiles = (\n        'plone.app.caching:default',\n        'plonetheme.barceloneta:default',\n    )\n\n    def profiles(self):\n        base_profiles = []\n        extension_profiles = []\n\n        # profiles available for install/uninstall, but hidden at the time\n        # the Plone site is created\n        not_installable = [\n            'Products.CMFPlacefulWorkflow:CMFPlacefulWorkflow',\n        ]\n        utils = getAllUtilitiesRegisteredFor(INonInstallable)\n        for util in utils:\n            not_installable.extend(util.getNonInstallableProfiles())\n\n        for info in profile_registry.listProfileInfo():\n            if info.get('type') == EXTENSION and \\\n               info.get('for') in (IPloneSiteRoot, None):\n                profile_id = info.get('id')\n                if profile_id not in not_installable:\n                    if profile_id in self.default_extension_profiles:\n                        info['selected'] = 'selected'\n                    extension_profiles.append(info)\n\n        def _key(v):\n            # Make sure implicitly selected items come first\n            selected = v.get('selected') and 'automatic' or 'manual'\n            return '{}-{}'.format(selected, v.get('title', ''))\n        extension_profiles.sort(key=_key)\n\n        for info in profile_registry.listProfileInfo():\n            if info.get('type') == BASE and \\\n               info.get('for') in (IPloneSiteRoot, None):\n                base_profiles.append(info)\n\n        return dict(\n            base=tuple(base_profiles),\n            default=_DEFAULT_PROFILE,\n            extensions=tuple(extension_profiles),\n        )\n\n    def browser_language(self):\n        language = 'en'\n        pl = IUserPreferredLanguages(self.request)\n        if pl is not None:\n            languages = pl.getPreferredLanguages()\n            for httplang in languages:\n                parts = (httplang.split('-') + [None, None])[:3]\n                if parts[0] == parts[1]:\n                    # Avoid creating a country code for simple languages codes\n                    parts = [parts[0], None, None]\n                try:\n                    locale = locales.getLocale(*parts)\n                    language = locale.getLocaleID().replace('_', '-').lower()\n                    break\n                except LoadLocaleError:\n                    # Just try the next combination\n                    pass\n        return language\n\n    def grouped_languages(self, default='en'):\n        util = queryUtility(IContentLanguageAvailability)\n        available = util.getLanguages(combined=True)\n        languages = dict(util.getLanguageListing())\n\n        # Group country specific versions by language\n        grouped = OrderedDict()\n        for langcode, data in available.items():\n            lang = langcode.split('-')[0]\n            language = languages.get(lang, lang)  # Label\n\n            struct = grouped.get(lang, {'label': language, 'languages': []})\n\n            langs = struct['languages']\n            langs.append({\n                'langcode': langcode,\n                'label': data.get('native', data.get('name')),\n            })\n\n            grouped[lang] = struct\n\n        # Sort list by language, next by country\n        data = sorted(grouped.values(), key=lambda k: k['label'])\n        for item in data:\n            item['languages'] = sorted(\n                item['languages'], key=lambda k: k['label'].lower())\n        return data\n\n    def timezones(self):\n        tz_vocab = getUtility(\n            IVocabularyFactory,\n            'plone.app.vocabularies.CommonTimezones'\n        )(self.context)\n\n        grouped = OrderedDict()\n        tz_values = [it.value for it in tz_vocab]\n        for value in tz_values:\n            splitted = value.split('/')\n            group = splitted.pop(0)\n            label = '/'.join(splitted)\n\n            entries = grouped.get(group, [])\n            entries.append({'label': label or group, 'value': value})\n            grouped[group] = entries\n\n        return grouped\n\n    def __call__(self):\n        context = self.context\n        form = self.request.form\n        submitted = form.get('form.submitted', False)\n        if submitted:\n            site_id = form.get('site_id', 'Plone')\n\n            # CSRF protect. DO NOT use auto CSRF protection for adding a site\n            alsoProvides(self.request, IDisableCSRFProtection)\n\n            # check if keyring is installed on root, disable CSRF protection\n            # if it is because it is not installed until a plone site\n            # is created\n            if queryUtility(IKeyManager) is None:\n                LOGGER.info('CSRF protection disabled on initial site '\n                            'creation')\n            else:\n                # we have a keymanager, check csrf protection manually now\n                checkCSRF(self.request)\n            site = addPloneSite(\n                context, site_id,\n                title=form.get('title', ''),\n                profile_id=form.get('profile_id', _DEFAULT_PROFILE),\n                extension_ids=form.get('extension_ids', ()),\n                setup_content=form.get('setup_content', False),\n                default_language=form.get('default_language', 'en'),\n                portal_timezone=form.get('portal_timezone', 'UTC')\n            )\n            self.request.response.redirect(site.absolute_url())\n            return ''\n\n        return self.index()\n\n\nclass Upgrade(BrowserView):\n\n    def upgrades(self):\n        pm = getattr(self.context, 'portal_migration')\n        return pm.listUpgrades()\n\n    def versions(self):\n        pm = getattr(self.context, 'portal_migration')\n        result = {}\n        result['instance'] = pm.getInstanceVersion()\n        result['fs'] = pm.getFileSystemVersion()\n        result['equal'] = result['instance'] == result['fs']\n        instance_version = normalize_version(result['instance'])\n        fs_version = normalize_version(result['fs'])\n        result['instance_gt'] = instance_version > fs_version\n        result['instance_lt'] = instance_version < fs_version\n        result['corelist'] = pm.coreVersions()\n        return result\n\n    def __call__(self):\n        form = self.request.form\n        submitted = form.get('form.submitted', False)\n        if submitted:\n            # CSRF protect. DO NOT use auto CSRF protection for upgrading sites\n            alsoProvides(self.request, IDisableCSRFProtection)\n\n            pm = getattr(self.context, 'portal_migration')\n            report = pm.upgrade(\n                REQUEST=self.request,\n                dry_run=form.get('dry_run', False),\n            )\n            return self.index(\n                report=report,\n            )\n\n        return self.index()\n", "path": "Products/CMFPlone/browser/admin.py"}], "after_files": [{"content": "from AccessControl import getSecurityManager\nfrom AccessControl.Permissions import view as View\nfrom OFS.interfaces import IApplication\nfrom Products.CMFCore.permissions import ManagePortal\nfrom Products.CMFPlone.factory import _DEFAULT_PROFILE\nfrom Products.CMFPlone.factory import addPloneSite\nfrom Products.CMFPlone.interfaces import INonInstallable\nfrom Products.CMFPlone.interfaces import IPloneSiteRoot\nfrom Products.CMFPlone.utils import get_installer\nfrom Products.Five.browser.pagetemplatefile import ViewPageTemplateFile\nfrom Products.GenericSetup import BASE, EXTENSION\nfrom Products.GenericSetup import profile_registry\nfrom Products.GenericSetup.upgrade import normalize_version\nfrom ZPublisher.BaseRequest import DefaultPublishTraverse\nfrom collections import OrderedDict\nfrom plone.i18n.locales.interfaces import IContentLanguageAvailability\nfrom plone.keyring.interfaces import IKeyManager\nfrom plone.protect.authenticator import check as checkCSRF\nfrom plone.protect.interfaces import IDisableCSRFProtection\nfrom urllib import parse\nfrom ZODB.broken import Broken\nfrom zope.component import adapts\nfrom zope.component import getAllUtilitiesRegisteredFor\nfrom zope.component import getUtility\nfrom zope.component import queryMultiAdapter\nfrom zope.component import queryUtility\nfrom zope.i18n.interfaces import IUserPreferredLanguages\nfrom zope.i18n.locales import locales, LoadLocaleError\nfrom zope.interface import Interface\nfrom zope.interface import alsoProvides\nfrom zope.publisher.browser import BrowserView\nfrom zope.publisher.interfaces import IRequest\nfrom zope.schema.interfaces import IVocabularyFactory\n\nimport logging\nLOGGER = logging.getLogger('Products.CMFPlone')\n\n\nclass AppTraverser(DefaultPublishTraverse):\n    adapts(IApplication, IRequest)\n\n    def publishTraverse(self, request, name):\n        if name == 'index_html':\n            view = queryMultiAdapter(\n                (self.context, request), Interface, 'plone-overview')\n            if view is not None:\n                return view\n        return DefaultPublishTraverse.publishTraverse(self, request, name)\n\n\nclass Overview(BrowserView):\n\n    def sites(self, root=None):\n        if root is None:\n            root = self.context\n\n        result = []\n        secman = getSecurityManager()\n        candidates = (\n            obj for obj in root.values() if not isinstance(obj, Broken)\n        )\n        for obj in candidates:\n            if obj.meta_type == 'Folder':\n                result = result + self.sites(obj)\n            elif IPloneSiteRoot.providedBy(obj):\n                if secman.checkPermission(View, obj):\n                    result.append(obj)\n            elif obj.getId() in getattr(root, '_mount_points', {}):\n                result.extend(self.sites(root=obj))\n        return result\n\n    def outdated(self, obj):\n        mig = obj.get('portal_migration', None)\n        if mig is not None:\n            return mig.needUpgrading()\n        return False\n\n    def can_manage(self):\n        secman = getSecurityManager()\n        return secman.checkPermission(ManagePortal, self.context)\n\n    def upgrade_url(self, site, can_manage=None):\n        if can_manage is None:\n            can_manage = self.can_manage()\n        if can_manage:\n            return site.absolute_url() + '/@@plone-upgrade'\n        else:\n            return self.context.absolute_url() + '/@@plone-root-login'\n\n\nclass RootLoginRedirect(BrowserView):\n    \"\"\" @@plone-root-login\n\n    This view of the Zope root forces authentication via the root\n    acl_users and then redirects elsewhere.\n    \"\"\"\n\n    def __call__(self, came_from=None):\n        if came_from is not None:\n            # see if this is a relative url or an absolute\n            if len(parse.urlparse(came_from)[1]) == 0:\n                # No host specified, so url is relative.  Get an absolute url.\n                # Note: '\\\\domain.org' is not recognised as host,\n                # which is good.\n                came_from = parse.urljoin(\n                    self.context.absolute_url() + '/', came_from,\n                )\n            elif not came_from.startswith(self.context.absolute_url()):\n                # Note: we cannot use portal_url.isURLInPortal here, because we\n                # are not in a Plone portal, but in the Zope root.\n                came_from = None\n        if came_from is None:\n            came_from = self.context.absolute_url()\n        self.request.response.redirect(came_from)\n\n\nclass RootLogout(BrowserView):\n    \"\"\" @@plone-root-logout \"\"\"\n\n    logout = ViewPageTemplateFile('templates/plone-admin-logged-out.pt')\n\n    def __call__(self):\n        response = self.request.response\n        realm = response.realm\n        response.setStatus(401)\n        response.setHeader('WWW-Authenticate', 'basic realm=\"%s\"' % realm, 1)\n        response.setBody(self.logout())\n        return\n\n\nclass FrontPage(BrowserView):\n\n    index = ViewPageTemplateFile('templates/plone-frontpage.pt')\n\n\nclass AddPloneSite(BrowserView):\n\n    # Profiles that are installed by default,\n    # but can be removed later.\n    default_extension_profiles = (\n        'plone.app.caching:default',\n        'plonetheme.barceloneta:default',\n    )\n\n    def profiles(self):\n        base_profiles = []\n        extension_profiles = []\n\n        # profiles available for install/uninstall, but hidden at the time\n        # the Plone site is created\n        not_installable = [\n            'Products.CMFPlacefulWorkflow:CMFPlacefulWorkflow',\n        ]\n        utils = getAllUtilitiesRegisteredFor(INonInstallable)\n        for util in utils:\n            not_installable.extend(util.getNonInstallableProfiles())\n\n        for info in profile_registry.listProfileInfo():\n            if info.get('type') == EXTENSION and \\\n               info.get('for') in (IPloneSiteRoot, None):\n                profile_id = info.get('id')\n                if profile_id not in not_installable:\n                    if profile_id in self.default_extension_profiles:\n                        info['selected'] = 'selected'\n                    extension_profiles.append(info)\n\n        def _key(v):\n            # Make sure implicitly selected items come first\n            selected = v.get('selected') and 'automatic' or 'manual'\n            return '{}-{}'.format(selected, v.get('title', ''))\n        extension_profiles.sort(key=_key)\n\n        for info in profile_registry.listProfileInfo():\n            if info.get('type') == BASE and \\\n               info.get('for') in (IPloneSiteRoot, None):\n                base_profiles.append(info)\n\n        return dict(\n            base=tuple(base_profiles),\n            default=_DEFAULT_PROFILE,\n            extensions=tuple(extension_profiles),\n        )\n\n    def browser_language(self):\n        language = 'en'\n        pl = IUserPreferredLanguages(self.request)\n        if pl is not None:\n            languages = pl.getPreferredLanguages()\n            for httplang in languages:\n                parts = (httplang.split('-') + [None, None])[:3]\n                if parts[0] == parts[1]:\n                    # Avoid creating a country code for simple languages codes\n                    parts = [parts[0], None, None]\n                try:\n                    locale = locales.getLocale(*parts)\n                    language = locale.getLocaleID().replace('_', '-').lower()\n                    break\n                except LoadLocaleError:\n                    # Just try the next combination\n                    pass\n        return language\n\n    def grouped_languages(self, default='en'):\n        util = queryUtility(IContentLanguageAvailability)\n        available = util.getLanguages(combined=True)\n        languages = dict(util.getLanguageListing())\n\n        # Group country specific versions by language\n        grouped = OrderedDict()\n        for langcode, data in available.items():\n            lang = langcode.split('-')[0]\n            language = languages.get(lang, lang)  # Label\n\n            struct = grouped.get(lang, {'label': language, 'languages': []})\n\n            langs = struct['languages']\n            langs.append({\n                'langcode': langcode,\n                'label': data.get('native', data.get('name')),\n            })\n\n            grouped[lang] = struct\n\n        # Sort list by language, next by country\n        data = sorted(grouped.values(), key=lambda k: k['label'])\n        for item in data:\n            item['languages'] = sorted(\n                item['languages'], key=lambda k: k['label'].lower())\n        return data\n\n    def timezones(self):\n        tz_vocab = getUtility(\n            IVocabularyFactory,\n            'plone.app.vocabularies.CommonTimezones'\n        )(self.context)\n\n        grouped = OrderedDict()\n        tz_values = [it.value for it in tz_vocab]\n        for value in tz_values:\n            splitted = value.split('/')\n            group = splitted.pop(0)\n            label = '/'.join(splitted)\n\n            entries = grouped.get(group, [])\n            entries.append({'label': label or group, 'value': value})\n            grouped[group] = entries\n\n        return grouped\n\n    def __call__(self):\n        context = self.context\n        form = self.request.form\n        submitted = form.get('form.submitted', False)\n        if submitted:\n            site_id = form.get('site_id', 'Plone')\n\n            # CSRF protect. DO NOT use auto CSRF protection for adding a site\n            alsoProvides(self.request, IDisableCSRFProtection)\n\n            # check if keyring is installed on root, disable CSRF protection\n            # if it is because it is not installed until a plone site\n            # is created\n            if queryUtility(IKeyManager) is None:\n                LOGGER.info('CSRF protection disabled on initial site '\n                            'creation')\n            else:\n                # we have a keymanager, check csrf protection manually now\n                checkCSRF(self.request)\n            site = addPloneSite(\n                context, site_id,\n                title=form.get('title', ''),\n                profile_id=form.get('profile_id', _DEFAULT_PROFILE),\n                extension_ids=form.get('extension_ids', ()),\n                setup_content=form.get('setup_content', False),\n                default_language=form.get('default_language', 'en'),\n                portal_timezone=form.get('portal_timezone', 'UTC')\n            )\n            self.request.response.redirect(site.absolute_url())\n            return ''\n\n        return self.index()\n\n\nclass Upgrade(BrowserView):\n\n    def upgrades(self):\n        pm = getattr(self.context, 'portal_migration')\n        return pm.listUpgrades()\n\n    def versions(self):\n        pm = getattr(self.context, 'portal_migration')\n        result = {}\n        result['instance'] = pm.getInstanceVersion()\n        result['fs'] = pm.getFileSystemVersion()\n        result['equal'] = result['instance'] == result['fs']\n        instance_version = normalize_version(result['instance'])\n        fs_version = normalize_version(result['fs'])\n        result['instance_gt'] = instance_version > fs_version\n        result['instance_lt'] = instance_version < fs_version\n        result['corelist'] = pm.coreVersions()\n        return result\n\n    def __call__(self):\n        form = self.request.form\n        submitted = form.get('form.submitted', False)\n        if submitted:\n            # CSRF protect. DO NOT use auto CSRF protection for upgrading sites\n            alsoProvides(self.request, IDisableCSRFProtection)\n\n            pm = getattr(self.context, 'portal_migration')\n            report = pm.upgrade(\n                REQUEST=self.request,\n                dry_run=form.get('dry_run', False),\n            )\n            return self.index(\n                report=report,\n            )\n\n        return self.index()\n", "path": "Products/CMFPlone/browser/admin.py"}]}