We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
addPloneSite swallows exceptions while importing extension profiles
If there's an exception while Products.CMFPlone.factory.addPloneSite is installing an extension profile, it is logged but does not abort execution. This causes problems if addPloneSite is used in a script and something is relying on the exit status of the script to determine whether the site was created successfully (like in https://github.com/plone/plone-backend/blob/5.2.x/skeleton/scripts/create_site.py). It also leads to confusion since it can hide the fact that the site was not fully configured.

Even worse, the exception is obscured in the log because Zope hits a different exception while trying to render a page showing the original exception. This happens because the not-yet-fully-configured Plone site is still set as the active zope.component site.

--- 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/factory.py`
Content:
```
1 from logging import getLogger
2 from plone.registry.interfaces import IRegistry
3 from plone.uuid.handlers import addAttributeUUID
4 from Products.CMFCore.interfaces import ISiteRoot
5 from Products.CMFPlone import PloneMessageFactory as _
6 from Products.CMFPlone.events import SiteManagerCreatedEvent
7 from plone.base.interfaces import INonInstallable
8 from Products.CMFPlone.Portal import PloneSite
9 from Products.GenericSetup.tool import SetupTool
10 from Products.statusmessages.interfaces import IStatusMessage
11 from zope.component import queryUtility
12 from zope.component.hooks import setSite
13 from zope.event import notify
14 from zope.interface import implementer
15 from zope.lifecycleevent import ObjectCreatedEvent
16 
17 _TOOL_ID = 'portal_setup'
18 _DEFAULT_PROFILE = 'Products.CMFPlone:plone'
19 _TYPES_PROFILE = 'plone.app.contenttypes:default'
20 _CONTENT_PROFILE = 'plone.app.contenttypes:plone-content'
21 
22 # A little hint for PloneTestCase (pre-Plone 6.0)
23 _IMREALLYPLONE5 = True
24 
25 # Marker hints for code that needs to know the major Plone version
26 # Works the same way than zcml condition hints so it contains the current and the
27 # last ones
28 PLONE52MARKER = True
29 PLONE60MARKER = True
30 
31 logger = getLogger('Plone')
32 
33 
34 @implementer(INonInstallable)
35 class NonInstallable:
36 
37     def getNonInstallableProducts(self):
38         return [
39             'CMFDefault', 'Products.CMFDefault',
40             'CMFPlone', 'Products.CMFPlone', 'Products.CMFPlone.migrations',
41             'CMFTopic', 'Products.CMFTopic',
42             'CMFUid', 'Products.CMFUid',
43             'DCWorkflow', 'Products.DCWorkflow',
44             'PasswordResetTool', 'Products.PasswordResetTool',
45             'PlonePAS', 'Products.PlonePAS',
46             'PloneLanguageTool', 'Products.PloneLanguageTool',
47             'MimetypesRegistry', 'Products.MimetypesRegistry',
48             'PortalTransforms', 'Products.PortalTransforms',
49             'CMFDiffTool', 'Products.CMFDiffTool',
50             'CMFEditions', 'Products.CMFEditions',
51             'Products.NuPlone',
52             'borg.localrole',
53             'plone.app.caching',
54             'plone.app.dexterity',
55             'plone.app.discussion',
56             'plone.app.event',
57             'plone.app.intid',
58             'plone.app.linkintegrity',
59             'plone.app.querystring',
60             'plone.app.registry',
61             'plone.app.referenceablebehavior',
62             'plone.app.relationfield',
63             'plone.app.theming',
64             'plone.app.users',
65             'plone.app.widgets',
66             'plone.app.z3cform',
67             'plone.formwidget.recurrence',
68             'plone.keyring',
69             'plone.outputfilters',
70             'plone.portlet.static',
71             'plone.portlet.collection',
72             'plone.protect',
73             'plone.resource',
74             'plonetheme.barceloneta',
75         ]
76 
77     def getNonInstallableProfiles(self):
78         return [_DEFAULT_PROFILE,
79                 _CONTENT_PROFILE,
80                 'Products.CMFDiffTool:CMFDiffTool',
81                 'Products.CMFEditions:CMFEditions',
82                 'Products.CMFPlone:dependencies',
83                 'Products.CMFPlone:testfixture',
84                 'Products.NuPlone:uninstall',
85                 'Products.MimetypesRegistry:MimetypesRegistry',
86                 'Products.PasswordResetTool:PasswordResetTool',
87                 'Products.PortalTransforms:PortalTransforms',
88                 'Products.PloneLanguageTool:PloneLanguageTool',
89                 'Products.PlonePAS:PlonePAS',
90                 'borg.localrole:default',
91                 'plone.browserlayer:default',
92                 'plone.keyring:default',
93                 'plone.outputfilters:default',
94                 'plone.portlet.static:default',
95                 'plone.portlet.collection:default',
96                 'plone.protect:default',
97                 'plone.app.contenttypes:default',
98                 'plone.app.dexterity:default',
99                 'plone.app.discussion:default',
100                 'plone.app.event:default',
101                 'plone.app.linkintegrity:default',
102                 'plone.app.registry:default',
103                 'plone.app.relationfield:default',
104                 'plone.app.theming:default',
105                 'plone.app.users:default',
106                 'plone.app.versioningbehavior:default',
107                 'plone.app.z3cform:default',
108                 'plone.formwidget.recurrence:default',
109                 'plone.resource:default',
110                 ]
111 
112 
113 def zmi_constructor(context):
114     """This is a dummy constructor for the ZMI."""
115     url = context.DestinationURL()
116     request = context.REQUEST
117     return request.response.redirect(url + '/@@plone-addsite?site_id=Plone')
118 
119 
120 def addPloneSite(context, site_id, title='Plone site', description='',
121                  profile_id=_DEFAULT_PROFILE,
122                  content_profile_id=_CONTENT_PROFILE, snapshot=False,
123                  extension_ids=(), setup_content=True,
124                  default_language='en', portal_timezone='UTC'):
125     """Add a PloneSite to the context."""
126 
127     site = PloneSite(site_id)
128     notify(ObjectCreatedEvent(site))
129     context[site_id] = site
130 
131     site = context[site_id]
132     site.setLanguage(default_language)
133     # Set the accepted language for the rest of the request.  This makes sure
134     # the front-page text gets the correct translation also when your browser
135     # prefers non-English and you choose English as language for the Plone
136     # Site.
137     request = context.REQUEST
138     request['HTTP_ACCEPT_LANGUAGE'] = default_language
139 
140     site[_TOOL_ID] = SetupTool(_TOOL_ID)
141     setup_tool = site[_TOOL_ID]
142 
143     notify(SiteManagerCreatedEvent(site))
144     setSite(site)
145 
146     setup_tool.setBaselineContext('profile-%s' % profile_id)
147     setup_tool.runAllImportStepsFromProfile('profile-%s' % profile_id)
148 
149     reg = queryUtility(IRegistry, context=site)
150     reg['plone.portal_timezone'] = portal_timezone
151     reg['plone.available_timezones'] = [portal_timezone]
152     reg['plone.default_language'] = default_language
153     reg['plone.available_languages'] = [default_language]
154 
155     # Install default content types profile if user do not select "example content"
156     # during site creation.
157     content_types_profile = content_profile_id if setup_content else _TYPES_PROFILE
158 
159     setup_tool.runAllImportStepsFromProfile(f'profile-{content_types_profile}')
160 
161     props = dict(
162         title=title,
163         description=description,
164     )
165     # Do this before applying extension profiles, so the settings from a
166     # properties.xml file are applied and not overwritten by this
167     site.manage_changeProperties(**props)
168 
169     for extension_id in extension_ids:
170         try:
171             setup_tool.runAllImportStepsFromProfile(
172                 'profile-%s' % extension_id)
173         except Exception as msg:
174             IStatusMessage(request).add(_(
175                 'Could not install ${profile_id}: ${error_msg}! '
176                 'Please try to install it manually using the "Addons" '
177                 'controlpanel and report any issues to the '
178                 'addon maintainers.',
179                 mapping={
180                     'profile_id': extension_id,
181                     'error_msg': msg.args,
182                 }),
183                 type='error')
184             logger.exception(
185                 'Error while installing addon {}. '
186                 'See traceback below for details.'.format(extension_id))
187 
188     if snapshot is True:
189         setup_tool.createSnapshot('initial_configuration')
190 
191     return site
192
```
--- 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/factory.py b/Products/CMFPlone/factory.py
--- a/Products/CMFPlone/factory.py
+++ b/Products/CMFPlone/factory.py
@@ -1,13 +1,10 @@
 from logging import getLogger
 from plone.registry.interfaces import IRegistry
-from plone.uuid.handlers import addAttributeUUID
-from Products.CMFCore.interfaces import ISiteRoot
 from Products.CMFPlone import PloneMessageFactory as _
 from Products.CMFPlone.events import SiteManagerCreatedEvent
 from plone.base.interfaces import INonInstallable
 from Products.CMFPlone.Portal import PloneSite
 from Products.GenericSetup.tool import SetupTool
-from Products.statusmessages.interfaces import IStatusMessage
 from zope.component import queryUtility
 from zope.component.hooks import setSite
 from zope.event import notify
@@ -143,49 +140,41 @@
     notify(SiteManagerCreatedEvent(site))
     setSite(site)
 
-    setup_tool.setBaselineContext('profile-%s' % profile_id)
-    setup_tool.runAllImportStepsFromProfile('profile-%s' % profile_id)
-
-    reg = queryUtility(IRegistry, context=site)
-    reg['plone.portal_timezone'] = portal_timezone
-    reg['plone.available_timezones'] = [portal_timezone]
-    reg['plone.default_language'] = default_language
-    reg['plone.available_languages'] = [default_language]
-
-    # Install default content types profile if user do not select "example content"
-    # during site creation.
-    content_types_profile = content_profile_id if setup_content else _TYPES_PROFILE
-
-    setup_tool.runAllImportStepsFromProfile(f'profile-{content_types_profile}')
-
-    props = dict(
-        title=title,
-        description=description,
-    )
-    # Do this before applying extension profiles, so the settings from a
-    # properties.xml file are applied and not overwritten by this
-    site.manage_changeProperties(**props)
-
-    for extension_id in extension_ids:
-        try:
-            setup_tool.runAllImportStepsFromProfile(
-                'profile-%s' % extension_id)
-        except Exception as msg:
-            IStatusMessage(request).add(_(
-                'Could not install ${profile_id}: ${error_msg}! '
-                'Please try to install it manually using the "Addons" '
-                'controlpanel and report any issues to the '
-                'addon maintainers.',
-                mapping={
-                    'profile_id': extension_id,
-                    'error_msg': msg.args,
-                }),
-                type='error')
-            logger.exception(
-                'Error while installing addon {}. '
-                'See traceback below for details.'.format(extension_id))
-
-    if snapshot is True:
-        setup_tool.createSnapshot('initial_configuration')
-
-    return site
+    try:
+        setup_tool.setBaselineContext('profile-%s' % profile_id)
+        setup_tool.runAllImportStepsFromProfile('profile-%s' % profile_id)
+
+        reg = queryUtility(IRegistry, context=site)
+        reg['plone.portal_timezone'] = portal_timezone
+        reg['plone.available_timezones'] = [portal_timezone]
+        reg['plone.default_language'] = default_language
+        reg['plone.available_languages'] = [default_language]
+
+        # Install default content types profile if user do not select "example content"
+        # during site creation.
+        content_types_profile = content_profile_id if setup_content else _TYPES_PROFILE
+
+        setup_tool.runAllImportStepsFromProfile(f'profile-{content_types_profile}')
+
+        props = dict(
+            title=title,
+            description=description,
+        )
+        # Do this before applying extension profiles, so the settings from a
+        # properties.xml file are applied and not overwritten by this
+        site.manage_changeProperties(**props)
+
+        for extension_id in extension_ids:
+            try:
+                setup_tool.runAllImportStepsFromProfile(f"profile-{extension_id}")
+            except Exception:
+                logger.error(f"Error while installing profile {extension_id}:")
+                raise
+
+        if snapshot is True:
+            setup_tool.createSnapshot('initial_configuration')
+
+        return site
+    except Exception:
+        setSite(None)
+        raise

{"golden_diff": "diff --git a/Products/CMFPlone/factory.py b/Products/CMFPlone/factory.py\n--- a/Products/CMFPlone/factory.py\n+++ b/Products/CMFPlone/factory.py\n@@ -1,13 +1,10 @@\n from logging import getLogger\n from plone.registry.interfaces import IRegistry\n-from plone.uuid.handlers import addAttributeUUID\n-from Products.CMFCore.interfaces import ISiteRoot\n from Products.CMFPlone import PloneMessageFactory as _\n from Products.CMFPlone.events import SiteManagerCreatedEvent\n from plone.base.interfaces import INonInstallable\n from Products.CMFPlone.Portal import PloneSite\n from Products.GenericSetup.tool import SetupTool\n-from Products.statusmessages.interfaces import IStatusMessage\n from zope.component import queryUtility\n from zope.component.hooks import setSite\n from zope.event import notify\n@@ -143,49 +140,41 @@\n     notify(SiteManagerCreatedEvent(site))\n     setSite(site)\n \n-    setup_tool.setBaselineContext('profile-%s' % profile_id)\n-    setup_tool.runAllImportStepsFromProfile('profile-%s' % profile_id)\n-\n-    reg = queryUtility(IRegistry, context=site)\n-    reg['plone.portal_timezone'] = portal_timezone\n-    reg['plone.available_timezones'] = [portal_timezone]\n-    reg['plone.default_language'] = default_language\n-    reg['plone.available_languages'] = [default_language]\n-\n-    # Install default content types profile if user do not select \"example content\"\n-    # during site creation.\n-    content_types_profile = content_profile_id if setup_content else _TYPES_PROFILE\n-\n-    setup_tool.runAllImportStepsFromProfile(f'profile-{content_types_profile}')\n-\n-    props = dict(\n-        title=title,\n-        description=description,\n-    )\n-    # Do this before applying extension profiles, so the settings from a\n-    # properties.xml file are applied and not overwritten by this\n-    site.manage_changeProperties(**props)\n-\n-    for extension_id in extension_ids:\n-        try:\n-            setup_tool.runAllImportStepsFromProfile(\n-                'profile-%s' % extension_id)\n-        except Exception as msg:\n-            IStatusMessage(request).add(_(\n-                'Could not install ${profile_id}: ${error_msg}! '\n-                'Please try to install it manually using the \"Addons\" '\n-                'controlpanel and report any issues to the '\n-                'addon maintainers.',\n-                mapping={\n-                    'profile_id': extension_id,\n-                    'error_msg': msg.args,\n-                }),\n-                type='error')\n-            logger.exception(\n-                'Error while installing addon {}. '\n-                'See traceback below for details.'.format(extension_id))\n-\n-    if snapshot is True:\n-        setup_tool.createSnapshot('initial_configuration')\n-\n-    return site\n+    try:\n+        setup_tool.setBaselineContext('profile-%s' % profile_id)\n+        setup_tool.runAllImportStepsFromProfile('profile-%s' % profile_id)\n+\n+        reg = queryUtility(IRegistry, context=site)\n+        reg['plone.portal_timezone'] = portal_timezone\n+        reg['plone.available_timezones'] = [portal_timezone]\n+        reg['plone.default_language'] = default_language\n+        reg['plone.available_languages'] = [default_language]\n+\n+        # Install default content types profile if user do not select \"example content\"\n+        # during site creation.\n+        content_types_profile = content_profile_id if setup_content else _TYPES_PROFILE\n+\n+        setup_tool.runAllImportStepsFromProfile(f'profile-{content_types_profile}')\n+\n+        props = dict(\n+            title=title,\n+            description=description,\n+        )\n+        # Do this before applying extension profiles, so the settings from a\n+        # properties.xml file are applied and not overwritten by this\n+        site.manage_changeProperties(**props)\n+\n+        for extension_id in extension_ids:\n+            try:\n+                setup_tool.runAllImportStepsFromProfile(f\"profile-{extension_id}\")\n+            except Exception:\n+                logger.error(f\"Error while installing profile {extension_id}:\")\n+                raise\n+\n+        if snapshot is True:\n+            setup_tool.createSnapshot('initial_configuration')\n+\n+        return site\n+    except Exception:\n+        setSite(None)\n+        raise\n", "issue": "addPloneSite swallows exceptions while importing extension profiles\nIf there's an exception while Products.CMFPlone.factory.addPloneSite is installing an extension profile, it is logged but does not abort execution. This causes problems if addPloneSite is used in a script and something is relying on the exit status of the script to determine whether the site was created successfully (like in https://github.com/plone/plone-backend/blob/5.2.x/skeleton/scripts/create_site.py). It also leads to confusion since it can hide the fact that the site was not fully configured.\r\n\r\nEven worse, the exception is obscured in the log because Zope hits a different exception while trying to render a page showing the original exception. This happens because the not-yet-fully-configured Plone site is still set as the active zope.component site.\n", "before_files": [{"content": "from logging import getLogger\nfrom plone.registry.interfaces import IRegistry\nfrom plone.uuid.handlers import addAttributeUUID\nfrom Products.CMFCore.interfaces import ISiteRoot\nfrom Products.CMFPlone import PloneMessageFactory as _\nfrom Products.CMFPlone.events import SiteManagerCreatedEvent\nfrom plone.base.interfaces import INonInstallable\nfrom Products.CMFPlone.Portal import PloneSite\nfrom Products.GenericSetup.tool import SetupTool\nfrom Products.statusmessages.interfaces import IStatusMessage\nfrom zope.component import queryUtility\nfrom zope.component.hooks import setSite\nfrom zope.event import notify\nfrom zope.interface import implementer\nfrom zope.lifecycleevent import ObjectCreatedEvent\n\n_TOOL_ID = 'portal_setup'\n_DEFAULT_PROFILE = 'Products.CMFPlone:plone'\n_TYPES_PROFILE = 'plone.app.contenttypes:default'\n_CONTENT_PROFILE = 'plone.app.contenttypes:plone-content'\n\n# A little hint for PloneTestCase (pre-Plone 6.0)\n_IMREALLYPLONE5 = True\n\n# Marker hints for code that needs to know the major Plone version\n# Works the same way than zcml condition hints so it contains the current and the\n# last ones\nPLONE52MARKER = True\nPLONE60MARKER = True\n\nlogger = getLogger('Plone')\n\n\n@implementer(INonInstallable)\nclass NonInstallable:\n\n    def getNonInstallableProducts(self):\n        return [\n            'CMFDefault', 'Products.CMFDefault',\n            'CMFPlone', 'Products.CMFPlone', 'Products.CMFPlone.migrations',\n            'CMFTopic', 'Products.CMFTopic',\n            'CMFUid', 'Products.CMFUid',\n            'DCWorkflow', 'Products.DCWorkflow',\n            'PasswordResetTool', 'Products.PasswordResetTool',\n            'PlonePAS', 'Products.PlonePAS',\n            'PloneLanguageTool', 'Products.PloneLanguageTool',\n            'MimetypesRegistry', 'Products.MimetypesRegistry',\n            'PortalTransforms', 'Products.PortalTransforms',\n            'CMFDiffTool', 'Products.CMFDiffTool',\n            'CMFEditions', 'Products.CMFEditions',\n            'Products.NuPlone',\n            'borg.localrole',\n            'plone.app.caching',\n            'plone.app.dexterity',\n            'plone.app.discussion',\n            'plone.app.event',\n            'plone.app.intid',\n            'plone.app.linkintegrity',\n            'plone.app.querystring',\n            'plone.app.registry',\n            'plone.app.referenceablebehavior',\n            'plone.app.relationfield',\n            'plone.app.theming',\n            'plone.app.users',\n            'plone.app.widgets',\n            'plone.app.z3cform',\n            'plone.formwidget.recurrence',\n            'plone.keyring',\n            'plone.outputfilters',\n            'plone.portlet.static',\n            'plone.portlet.collection',\n            'plone.protect',\n            'plone.resource',\n            'plonetheme.barceloneta',\n        ]\n\n    def getNonInstallableProfiles(self):\n        return [_DEFAULT_PROFILE,\n                _CONTENT_PROFILE,\n                'Products.CMFDiffTool:CMFDiffTool',\n                'Products.CMFEditions:CMFEditions',\n                'Products.CMFPlone:dependencies',\n                'Products.CMFPlone:testfixture',\n                'Products.NuPlone:uninstall',\n                'Products.MimetypesRegistry:MimetypesRegistry',\n                'Products.PasswordResetTool:PasswordResetTool',\n                'Products.PortalTransforms:PortalTransforms',\n                'Products.PloneLanguageTool:PloneLanguageTool',\n                'Products.PlonePAS:PlonePAS',\n                'borg.localrole:default',\n                'plone.browserlayer:default',\n                'plone.keyring:default',\n                'plone.outputfilters:default',\n                'plone.portlet.static:default',\n                'plone.portlet.collection:default',\n                'plone.protect:default',\n                'plone.app.contenttypes:default',\n                'plone.app.dexterity:default',\n                'plone.app.discussion:default',\n                'plone.app.event:default',\n                'plone.app.linkintegrity:default',\n                'plone.app.registry:default',\n                'plone.app.relationfield:default',\n                'plone.app.theming:default',\n                'plone.app.users:default',\n                'plone.app.versioningbehavior:default',\n                'plone.app.z3cform:default',\n                'plone.formwidget.recurrence:default',\n                'plone.resource:default',\n                ]\n\n\ndef zmi_constructor(context):\n    \"\"\"This is a dummy constructor for the ZMI.\"\"\"\n    url = context.DestinationURL()\n    request = context.REQUEST\n    return request.response.redirect(url + '/@@plone-addsite?site_id=Plone')\n\n\ndef addPloneSite(context, site_id, title='Plone site', description='',\n                 profile_id=_DEFAULT_PROFILE,\n                 content_profile_id=_CONTENT_PROFILE, snapshot=False,\n                 extension_ids=(), setup_content=True,\n                 default_language='en', portal_timezone='UTC'):\n    \"\"\"Add a PloneSite to the context.\"\"\"\n\n    site = PloneSite(site_id)\n    notify(ObjectCreatedEvent(site))\n    context[site_id] = site\n\n    site = context[site_id]\n    site.setLanguage(default_language)\n    # Set the accepted language for the rest of the request.  This makes sure\n    # the front-page text gets the correct translation also when your browser\n    # prefers non-English and you choose English as language for the Plone\n    # Site.\n    request = context.REQUEST\n    request['HTTP_ACCEPT_LANGUAGE'] = default_language\n\n    site[_TOOL_ID] = SetupTool(_TOOL_ID)\n    setup_tool = site[_TOOL_ID]\n\n    notify(SiteManagerCreatedEvent(site))\n    setSite(site)\n\n    setup_tool.setBaselineContext('profile-%s' % profile_id)\n    setup_tool.runAllImportStepsFromProfile('profile-%s' % profile_id)\n\n    reg = queryUtility(IRegistry, context=site)\n    reg['plone.portal_timezone'] = portal_timezone\n    reg['plone.available_timezones'] = [portal_timezone]\n    reg['plone.default_language'] = default_language\n    reg['plone.available_languages'] = [default_language]\n\n    # Install default content types profile if user do not select \"example content\"\n    # during site creation.\n    content_types_profile = content_profile_id if setup_content else _TYPES_PROFILE\n\n    setup_tool.runAllImportStepsFromProfile(f'profile-{content_types_profile}')\n\n    props = dict(\n        title=title,\n        description=description,\n    )\n    # Do this before applying extension profiles, so the settings from a\n    # properties.xml file are applied and not overwritten by this\n    site.manage_changeProperties(**props)\n\n    for extension_id in extension_ids:\n        try:\n            setup_tool.runAllImportStepsFromProfile(\n                'profile-%s' % extension_id)\n        except Exception as msg:\n            IStatusMessage(request).add(_(\n                'Could not install ${profile_id}: ${error_msg}! '\n                'Please try to install it manually using the \"Addons\" '\n                'controlpanel and report any issues to the '\n                'addon maintainers.',\n                mapping={\n                    'profile_id': extension_id,\n                    'error_msg': msg.args,\n                }),\n                type='error')\n            logger.exception(\n                'Error while installing addon {}. '\n                'See traceback below for details.'.format(extension_id))\n\n    if snapshot is True:\n        setup_tool.createSnapshot('initial_configuration')\n\n    return site\n", "path": "Products/CMFPlone/factory.py"}], "after_files": [{"content": "from logging import getLogger\nfrom plone.registry.interfaces import IRegistry\nfrom Products.CMFPlone import PloneMessageFactory as _\nfrom Products.CMFPlone.events import SiteManagerCreatedEvent\nfrom plone.base.interfaces import INonInstallable\nfrom Products.CMFPlone.Portal import PloneSite\nfrom Products.GenericSetup.tool import SetupTool\nfrom zope.component import queryUtility\nfrom zope.component.hooks import setSite\nfrom zope.event import notify\nfrom zope.interface import implementer\nfrom zope.lifecycleevent import ObjectCreatedEvent\n\n_TOOL_ID = 'portal_setup'\n_DEFAULT_PROFILE = 'Products.CMFPlone:plone'\n_TYPES_PROFILE = 'plone.app.contenttypes:default'\n_CONTENT_PROFILE = 'plone.app.contenttypes:plone-content'\n\n# A little hint for PloneTestCase (pre-Plone 6.0)\n_IMREALLYPLONE5 = True\n\n# Marker hints for code that needs to know the major Plone version\n# Works the same way than zcml condition hints so it contains the current and the\n# last ones\nPLONE52MARKER = True\nPLONE60MARKER = True\n\nlogger = getLogger('Plone')\n\n\n@implementer(INonInstallable)\nclass NonInstallable:\n\n    def getNonInstallableProducts(self):\n        return [\n            'CMFDefault', 'Products.CMFDefault',\n            'CMFPlone', 'Products.CMFPlone', 'Products.CMFPlone.migrations',\n            'CMFTopic', 'Products.CMFTopic',\n            'CMFUid', 'Products.CMFUid',\n            'DCWorkflow', 'Products.DCWorkflow',\n            'PasswordResetTool', 'Products.PasswordResetTool',\n            'PlonePAS', 'Products.PlonePAS',\n            'PloneLanguageTool', 'Products.PloneLanguageTool',\n            'MimetypesRegistry', 'Products.MimetypesRegistry',\n            'PortalTransforms', 'Products.PortalTransforms',\n            'CMFDiffTool', 'Products.CMFDiffTool',\n            'CMFEditions', 'Products.CMFEditions',\n            'Products.NuPlone',\n            'borg.localrole',\n            'plone.app.caching',\n            'plone.app.dexterity',\n            'plone.app.discussion',\n            'plone.app.event',\n            'plone.app.intid',\n            'plone.app.linkintegrity',\n            'plone.app.querystring',\n            'plone.app.registry',\n            'plone.app.referenceablebehavior',\n            'plone.app.relationfield',\n            'plone.app.theming',\n            'plone.app.users',\n            'plone.app.widgets',\n            'plone.app.z3cform',\n            'plone.formwidget.recurrence',\n            'plone.keyring',\n            'plone.outputfilters',\n            'plone.portlet.static',\n            'plone.portlet.collection',\n            'plone.protect',\n            'plone.resource',\n            'plonetheme.barceloneta',\n        ]\n\n    def getNonInstallableProfiles(self):\n        return [_DEFAULT_PROFILE,\n                _CONTENT_PROFILE,\n                'Products.CMFDiffTool:CMFDiffTool',\n                'Products.CMFEditions:CMFEditions',\n                'Products.CMFPlone:dependencies',\n                'Products.CMFPlone:testfixture',\n                'Products.NuPlone:uninstall',\n                'Products.MimetypesRegistry:MimetypesRegistry',\n                'Products.PasswordResetTool:PasswordResetTool',\n                'Products.PortalTransforms:PortalTransforms',\n                'Products.PloneLanguageTool:PloneLanguageTool',\n                'Products.PlonePAS:PlonePAS',\n                'borg.localrole:default',\n                'plone.browserlayer:default',\n                'plone.keyring:default',\n                'plone.outputfilters:default',\n                'plone.portlet.static:default',\n                'plone.portlet.collection:default',\n                'plone.protect:default',\n                'plone.app.contenttypes:default',\n                'plone.app.dexterity:default',\n                'plone.app.discussion:default',\n                'plone.app.event:default',\n                'plone.app.linkintegrity:default',\n                'plone.app.registry:default',\n                'plone.app.relationfield:default',\n                'plone.app.theming:default',\n                'plone.app.users:default',\n                'plone.app.versioningbehavior:default',\n                'plone.app.z3cform:default',\n                'plone.formwidget.recurrence:default',\n                'plone.resource:default',\n                ]\n\n\ndef zmi_constructor(context):\n    \"\"\"This is a dummy constructor for the ZMI.\"\"\"\n    url = context.DestinationURL()\n    request = context.REQUEST\n    return request.response.redirect(url + '/@@plone-addsite?site_id=Plone')\n\n\ndef addPloneSite(context, site_id, title='Plone site', description='',\n                 profile_id=_DEFAULT_PROFILE,\n                 content_profile_id=_CONTENT_PROFILE, snapshot=False,\n                 extension_ids=(), setup_content=True,\n                 default_language='en', portal_timezone='UTC'):\n    \"\"\"Add a PloneSite to the context.\"\"\"\n\n    site = PloneSite(site_id)\n    notify(ObjectCreatedEvent(site))\n    context[site_id] = site\n\n    site = context[site_id]\n    site.setLanguage(default_language)\n    # Set the accepted language for the rest of the request.  This makes sure\n    # the front-page text gets the correct translation also when your browser\n    # prefers non-English and you choose English as language for the Plone\n    # Site.\n    request = context.REQUEST\n    request['HTTP_ACCEPT_LANGUAGE'] = default_language\n\n    site[_TOOL_ID] = SetupTool(_TOOL_ID)\n    setup_tool = site[_TOOL_ID]\n\n    notify(SiteManagerCreatedEvent(site))\n    setSite(site)\n\n    try:\n        setup_tool.setBaselineContext('profile-%s' % profile_id)\n        setup_tool.runAllImportStepsFromProfile('profile-%s' % profile_id)\n\n        reg = queryUtility(IRegistry, context=site)\n        reg['plone.portal_timezone'] = portal_timezone\n        reg['plone.available_timezones'] = [portal_timezone]\n        reg['plone.default_language'] = default_language\n        reg['plone.available_languages'] = [default_language]\n\n        # Install default content types profile if user do not select \"example content\"\n        # during site creation.\n        content_types_profile = content_profile_id if setup_content else _TYPES_PROFILE\n\n        setup_tool.runAllImportStepsFromProfile(f'profile-{content_types_profile}')\n\n        props = dict(\n            title=title,\n            description=description,\n        )\n        # Do this before applying extension profiles, so the settings from a\n        # properties.xml file are applied and not overwritten by this\n        site.manage_changeProperties(**props)\n\n        for extension_id in extension_ids:\n            try:\n                setup_tool.runAllImportStepsFromProfile(f\"profile-{extension_id}\")\n            except Exception:\n                logger.error(f\"Error while installing profile {extension_id}:\")\n                raise\n\n        if snapshot is True:\n            setup_tool.createSnapshot('initial_configuration')\n\n        return site\n    except Exception:\n        setSite(None)\n        raise\n", "path": "Products/CMFPlone/factory.py"}]}