def setUp(self): # Enable debug mode always to ensure cache is disabled by default Globals.DevelopmentMode = True self.settings = getUtility(IRegistry).forInterface(IThemeSettings) self.settings.enabled = True createThemeFromTemplate(title="linked", description="Generated from test") test_theme_name = createThemeFromTemplate( title="rapidotest", description="Generated from test") theme = getTheme(test_theme_name) applyTheme(theme) import transaction transaction.commit() self.portal = self.layer['portal'] package_dir_path = os.path.join(test_dir_path, 'other') dir = FilesystemResourceDirectory(package_dir_path) provideUtility(dir, provides=IResourceDirectory, name=u'++theme++other') self.browser = Browser(self.layer['app']) self.browser.handleErrors = False
def test_createThemeFromTemplate_ja_unicode_title(self): from plone.app.theming.utils import createThemeFromTemplate title = u"copy of test theme by 日本語" description = u"test theme by 日本語" try: createThemeFromTemplate(title, description, baseOn="another-theme") except UnicodeEncodeError: self.fail(msg=u"Unicode Encode Error")
def test_createThemeFromTemplate_ja_unicode_title(self): from plone.app.theming.utils import createThemeFromTemplate title = u"copy of test theme by 日本語" description = u"test theme by 日本語" try: createThemeFromTemplate(title, description, baseOn="another-theme") except UnicodeEncodeError: self.fail(msg=u"Unicode Encode Error")
def activateTheme(): theme = getTheme('custom') if theme is None: createThemeFromTemplate('custom', '') theme = getTheme('custom') directory = queryResourceDirectory(THEME_RESOURCE_NAME, 'custom') try: directory['jbot'] except: directory.makeDirectory('jbot') fi = open(os.path.join(TEST_FILES_DIR, 'footer2.pt')) directory.writeFile('jbot/' + CUSTOMIZED_RESOURCE, fi) fi.close() if getCurrentTheme() != 'custom': applyTheme(theme)
def setUp(self): self.portal = self.layer['portal'] self.request = self.layer['request'] login(self.portal, TEST_USER_NAME) setRoles(self.portal, TEST_USER_ID, ('Member', 'Manager')) # part of setup is to always copy the theme to new one createThemeFromTemplate('foobar', '', 'castle.theme') themeData = getTheme('foobar') applyTheme(themeData) registry = getUtility(IRegistry) theme_settings = registry.forInterface(IThemeSettings, False) theme_settings.enabled = True directory = queryResourceDirectory('theme', 'foobar') directory.makeDirectory('tiles') self.tiles_directory = directory['tiles'] # clear cache for key in list(self.request.environ.keys())[:]: if key.startswith(CACHE_KEY): del self.request.environ[key]
def setUp(self): # Enable debug mode always to ensure cache is disabled by default Globals.DevelopmentMode = True self.settings = getUtility(IRegistry).forInterface(IThemeSettings) self.settings.enabled = True createThemeFromTemplate(title="linked", description="Generated from test") test_theme_name = createThemeFromTemplate(title="rapidotest", description="Generated from test") theme = getTheme(test_theme_name) applyTheme(theme) import transaction transaction.commit() self.portal = self.layer['portal'] package_dir_path = os.path.join(test_dir_path, 'other') dir = FilesystemResourceDirectory(package_dir_path) provideUtility( dir, provides=IResourceDirectory, name=u'++theme++other') self.browser = Browser(self.layer['app']) self.browser.handleErrors = False
def test_createThemeFromTemplate_rel_path(self): from plone.app.theming.utils import createThemeFromTemplate from plone.app.theming.utils import getAvailableThemes from plone.app.theming.utils import getTheme from plone.app.theming.interfaces import THEME_RESOURCE_NAME from plone.app.theming.interfaces import RULE_FILENAME title = "copy of test theme with custom prefix" description = "test theme creation" themeName = createThemeFromTemplate(title, description, baseOn="another-theme") titles = [theme.title for theme in getAvailableThemes()] self.assertTrue(title in titles) theme = getTheme(themeName) expected_prefix = u"/++%s++%s" % (THEME_RESOURCE_NAME, title.replace(" ", "-")) self.assertEqual(theme.absolutePrefix, expected_prefix) expected_rules = u"/++%s++%s/%s" % ( THEME_RESOURCE_NAME, title.replace(" ", "-"), RULE_FILENAME) self.assertEqual(theme.rules, expected_rules) self.assertEqual(theme.enabled_bundles, ['plone']) self.assertEqual(theme.disabled_bundles, ['foobar']) expected_dev_css = u"++%s++%s/less/barceloneta.plone.less" % ( THEME_RESOURCE_NAME, title.replace(" ", "-")) expected_prod_css = u"++%s++%s/less/barceloneta-compiled.css" % ( THEME_RESOURCE_NAME, title.replace(" ", "-")) expected_tinymce = u"++%s++%s/less/barceloneta-compiled.css" % ( THEME_RESOURCE_NAME, title.replace(" ", "-")) self.assertEqual(theme.development_css, expected_dev_css) self.assertEqual(theme.production_css, expected_prod_css) self.assertEqual(theme.tinymce_content_css, expected_tinymce) expected_dev_js = u"++%s++%s/script.js" % (THEME_RESOURCE_NAME, title.replace(" ", "-")) expected_prod_js = u"++%s++%s/script.min.js" % ( THEME_RESOURCE_NAME, title.replace(" ", "-")) self.assertEqual(theme.development_js, expected_dev_js) self.assertEqual(theme.production_js, expected_prod_js)
def test_createThemeFromTemplate_custom_prefix(self): from plone.app.theming.utils import createThemeFromTemplate from plone.app.theming.utils import getAvailableThemes from plone.app.theming.utils import getTheme from plone.app.theming.interfaces import THEME_RESOURCE_NAME from plone.app.theming.interfaces import RULE_FILENAME title = "copy of test theme with custom prefix" description = "test theme creation" themeName = createThemeFromTemplate(title, description, baseOn="secondary-theme") titles = [theme.title for theme in getAvailableThemes()] self.assertTrue(title in titles) theme = getTheme(themeName) expected_prefix = u"/++%s++%s" % (THEME_RESOURCE_NAME, title.replace(" ", "-")) self.assertEqual(theme.absolutePrefix, expected_prefix) expected_rules = u"/++%s++%s/%s" % (THEME_RESOURCE_NAME, title.replace(" ", "-"), RULE_FILENAME) self.assertEqual(theme.rules, expected_rules) self.assertEqual(theme.enabled_bundles, ['plone']) self.assertEqual(theme.disabled_bundles, ['foobar']) expected_dev_css = u"/++%s++%s/less/barceloneta.plone.less" % ( THEME_RESOURCE_NAME, title.replace(" ", "-")) expected_prod_css = u"/++%s++%s/less/barceloneta-compiled.css" % ( THEME_RESOURCE_NAME, title.replace(" ", "-")) expected_tinymce = u"/++%s++%s/less/barceloneta-compiled.css" % ( THEME_RESOURCE_NAME, title.replace(" ", "-")) self.assertEqual(theme.development_css, expected_dev_css) self.assertEqual(theme.production_css, expected_prod_css) self.assertEqual(theme.tinymce_content_css, expected_tinymce) expected_dev_js = u"/++%s++%s/script.js" % ( THEME_RESOURCE_NAME, title.replace(" ", "-")) expected_prod_js = u"/++%s++%s/script.min.js" % ( THEME_RESOURCE_NAME, title.replace(" ", "-")) self.assertEqual(theme.development_js, expected_dev_js) self.assertEqual(theme.production_js, expected_prod_js)
def test_createThemeFromTemplate(self): from plone.app.theming.utils import createThemeFromTemplate from plone.app.theming.utils import getAvailableThemes from plone.app.theming.utils import getTheme from plone.app.theming.interfaces import THEME_RESOURCE_NAME from plone.app.theming.interfaces import RULE_FILENAME title = "copy of test theme" description = "test theme creation" themeName = createThemeFromTemplate(title, description, baseOn="plone.app.theming.tests") titles = [theme.title for theme in getAvailableThemes()] self.assertTrue(title in titles) theme = getTheme(themeName) expected_prefix = u"/++%s++%s" % (THEME_RESOURCE_NAME, title.replace(" ", "-")) self.assertEqual(theme.absolutePrefix, expected_prefix) expected_rules = u"/++%s++%s/%s" % ( THEME_RESOURCE_NAME, title.replace(" ", "-"), RULE_FILENAME) self.assertEqual(theme.rules, expected_rules)
def test_createThemeFromTemplate(self): from plone.app.theming.utils import createThemeFromTemplate from plone.app.theming.utils import getAvailableThemes from plone.app.theming.utils import getTheme from plone.app.theming.interfaces import THEME_RESOURCE_NAME from plone.app.theming.interfaces import RULE_FILENAME title = "copy of test theme" description = "test theme creation" themeName = createThemeFromTemplate(title, description, baseOn="plone.app.theming.tests") titles = [theme.title for theme in getAvailableThemes()] self.assertTrue(title in titles) theme = getTheme(themeName) expected_prefix = u"/++%s++%s" % (THEME_RESOURCE_NAME, title.replace(" ", "-")) self.assertEqual(theme.absolutePrefix, expected_prefix) expected_rules = u"/++%s++%s/%s" % (THEME_RESOURCE_NAME, title.replace(" ", "-"), RULE_FILENAME) self.assertEqual(theme.rules, expected_rules)
def update(self): # XXX: complexity too high: refactoring needed processInputs(self.request) self._setup() self.errors = {} form = self.request.form if 'form.button.Cancel' in form: IStatusMessage(self.request).add(_(u"Changes cancelled")) self.redirect("{0}/@@overview-controlpanel".format(self.site_url)) return False if 'form.button.Enable' in form: self.authorize() themeSelection = form.get('themeName', None) if themeSelection: themeData = self.getThemeData( self.availableThemes, themeSelection ) applyTheme(themeData) self.theme_settings.enabled = True IStatusMessage( self.request ).add( _( u"Theme enabled. Note that this control panel page is " u"never themed." ) ) self._setup() return True if 'form.button.InvalidateCache' in form: self.authorize() policy = theming_policy() policy.invalidateCache() return True if 'form.button.Disable' in form: self.authorize() applyTheme(None) self.theme_settings.enabled = False IStatusMessage(self.request).add(_(u"Theme disabled.")) self._setup() return True if 'form.button.AdvancedSave' in form: self.authorize() self.theme_settings.readNetwork = form.get('readNetwork', False) themeEnabled = form.get('themeEnabled', False) rules = form.get('rules', None) prefix = form.get('absolutePrefix', None) doctype = str(form.get('doctype', "")) hostnameBlacklist = form.get('hostnameBlacklist', []) parameterExpressions = {} parameterExpressionsList = form.get('parameterExpressions', []) for line in parameterExpressionsList: try: name, expression = line.split('=', 1) name = str(name.strip()) expression = str(expression.strip()) parameterExpressions[name] = expression except ValueError: message = _( 'error_invalid_parameter_expressions', default=u"Please ensure you enter one expression per " u"line, in the format <name> = <expression>." ) self.errors['parameterExpressions'] = message themeBase = form.get('themeBase', None) markSpecialLinks = form.get('markSpecialLinks', None) extLinksOpenInNewWindow = form.get('extLinksOpenInNewWindow', None) if not self.errors: # Trigger onDisabled() on plugins if theme was active # previously and rules were changed if self.theme_settings.rules != rules: applyTheme(None) self.theme_settings.enabled = themeEnabled self.theme_settings.rules = rules self.theme_settings.absolutePrefix = prefix self.theme_settings.parameterExpressions = parameterExpressions self.theme_settings.hostnameBlacklist = hostnameBlacklist self.theme_settings.doctype = doctype # Theme base settings if themeBase is not None: if six.PY2: themeBase = themeBase.encode('utf-8') self.pskin.default_skin = themeBase if markSpecialLinks is not None: self.mark_special_links = markSpecialLinks if extLinksOpenInNewWindow is not None: self.ext_links_open_new_window = extLinksOpenInNewWindow IStatusMessage(self.request).add(_(u"Changes saved")) self._setup() return True else: IStatusMessage(self.request).add( _(u"There were errors"), 'error' ) self.redirectToFieldset('advanced') return False if 'form.button.Import' in form: self.authorize() enableNewTheme = form.get('enableNewTheme', False) replaceExisting = form.get('replaceExisting', False) themeArchive = form.get('themeArchive', None) themeZip = None performImport = False try: themeZip = zipfile.ZipFile(themeArchive) except (zipfile.BadZipfile, zipfile.LargeZipFile): logger.exception("Could not read zip file") self.errors['themeArchive'] = _( 'error_invalid_zip', default=u"The uploaded file is not a valid Zip archive" ) if themeZip: try: themeData = extractThemeInfo(themeZip, checkRules=False) except (ValueError, KeyError) as e: logger.warn(str(e)) self.errors['themeArchive'] = _( 'error_no_rules_file', u"The uploaded file does not contain a valid theme " u"archive." ) else: themeContainer = getOrCreatePersistentResourceDirectory() themeExists = themeData.__name__ in themeContainer if themeExists: if not replaceExisting: self.errors['themeArchive'] = _( 'error_already_installed', u"This theme is already installed. Select " u"'Replace existing theme' and re-upload to " u"replace it." ) else: del themeContainer[themeData.__name__] performImport = True else: performImport = True if performImport: themeContainer.importZip(themeZip) themeDirectory = queryResourceDirectory( THEME_RESOURCE_NAME, themeData.__name__ ) if themeDirectory is not None: # If we don't have a rules file, use the template if themeData.rules == u"/++{0:s}++{1:s}/{2:s}".format( THEME_RESOURCE_NAME, themeData.__name__, RULE_FILENAME, ) and not themeDirectory.isFile(RULE_FILENAME): templateThemeDirectory = queryResourceDirectory( THEME_RESOURCE_NAME, TEMPLATE_THEME ) themeDirectory.writeFile( RULE_FILENAME, templateThemeDirectory.readFile(RULE_FILENAME) ) if not themeDirectory.isFile(DEFAULT_THEME_FILENAME): IStatusMessage(self.request).add( _( u"A boilerplate rules.xml was added to " u"your theme, but no index.html file " u"found. Update rules.xml to reference " u"the current theme file." ), 'warning', ) plugins = getPlugins() pluginSettings = getPluginSettings(themeDirectory, plugins) if pluginSettings is not None: for name, plugin in plugins: plugin.onCreated( themeData.__name__, pluginSettings[name], pluginSettings ) if enableNewTheme: applyTheme(themeData) self.theme_settings.enabled = True if not self.errors: self.redirect( "{0}/++theme++{1}/@@theming-controlpanel-mapper".format( self.site_url, themeData.__name__ ) ) return False else: IStatusMessage(self.request).add( _(u"There were errors"), "error" ) self.renderOverlay('upload') return True if 'form.button.CreateTheme' in form: self.authorize() title = form.get('title') description = form.get('description') or '' baseOn = form.get('baseOn', TEMPLATE_THEME) enableImmediately = form.get('enableImmediately', True) if not title: self.errors['title'] = _(u"Title is required") IStatusMessage(self.request).add( _(u"There were errors"), 'error' ) self.renderOverlay('new-theme') return True else: if any(x.__name__ == title for x in getZODBThemes()): self.errors['title'] = _(u"Duplicate title") IStatusMessage(self.request).add( _(u"This title is already in use"), 'error' ) return True name = createThemeFromTemplate(title, description, baseOn) self._setup() if enableImmediately: themeData = self.getThemeData(self.availableThemes, name) applyTheme(themeData) self.theme_settings.enabled = True self.redirect( "{0}/++theme++{1}/@@theming-controlpanel-mapper".format( self.site_url, name ) ) return False if 'form.button.DeleteSelected' in form: self.authorize() toDelete = form.get('themes', []) themeDirectory = getOrCreatePersistentResourceDirectory() for theme in toDelete: del themeDirectory[theme] IStatusMessage(self.request).add(_(u"Theme deleted"), 'info') self._setup() return True return True
class ThemingControlpanel(BrowserView): def __call__(self): self.pskin = getToolByName(self.context, 'portal_skins') if self.update(): return self.index() return '' def _setup(self): registry = getUtility(IRegistry) self.theme_settings = registry.forInterface(IThemeSettings, False) self.link_settings = registry.forInterface(ILinkSchema, prefix="plone", check=False) self.zodbThemes = getZODBThemes() self.availableThemes = getAvailableThemes() self.selectedTheme = self.getSelectedTheme(self.availableThemes, self.theme_settings.rules) self.overlay = '' self.skinsVocabulary = getUtility(IVocabularyFactory, name='plone.app.vocabularies.Skins')( self.context) # Set response header to make sure control panel is never themed self.request.response.setHeader('X-Theme-Disabled', '1') def redirect(self, url): self.request.response.redirect(url) def get_mark_special_links(self): return self.link_settings.mark_special_links def set_mark_special_links(self, value): self.link_settings.mark_special_links = value mark_special_links = property(get_mark_special_links, set_mark_special_links) def get_ext_links_open_new_window(self): return self.link_settings.external_links_open_new_window def set_ext_links_open_new_window(self, value): self.link_settings.external_links_open_new_window = value ext_links_open_new_window = property(get_ext_links_open_new_window, set_ext_links_open_new_window) def update(self): # XXX: complexity too high: refactoring needed processInputs(self.request) self._setup() self.errors = {} form = self.request.form if 'form.button.Cancel' in form: IStatusMessage(self.request).add(_(u"Changes cancelled")) portalUrl = getToolByName(self.context, 'portal_url')() self.redirect("{0:s}/@@overview-controlpanel".format(portalUrl)) return False if 'form.button.Enable' in form: self.authorize() themeSelection = form.get('themeName', None) if themeSelection: themeData = self.getThemeData(self.availableThemes, themeSelection) applyTheme(themeData) self.theme_settings.enabled = True IStatusMessage(self.request).add( _(u"Theme enabled. Note that this control panel page is " u"never themed.")) self._setup() return True if 'form.button.InvalidateCache' in form: self.authorize() policy = theming_policy() policy.invalidateCache() return True if 'form.button.Disable' in form: self.authorize() applyTheme(None) self.theme_settings.enabled = False IStatusMessage(self.request).add(_(u"Theme disabled.")) self._setup() return True if 'form.button.AdvancedSave' in form: self.authorize() self.theme_settings.readNetwork = form.get('readNetwork', False) themeEnabled = form.get('themeEnabled', False) rules = form.get('rules', None) prefix = form.get('absolutePrefix', None) doctype = str(form.get('doctype', "")) hostnameBlacklist = form.get('hostnameBlacklist', []) parameterExpressions = {} parameterExpressionsList = form.get('parameterExpressions', []) for line in parameterExpressionsList: try: name, expression = line.split('=', 1) name = str(name.strip()) expression = str(expression.strip()) parameterExpressions[name] = expression except ValueError: message = _( 'error_invalid_parameter_expressions', default=u"Please ensure you enter one expression per " u"line, in the format <name> = <expression>.") self.errors['parameterExpressions'] = message themeBase = form.get('themeBase', None) markSpecialLinks = form.get('markSpecialLinks', None) extLinksOpenInNewWindow = form.get('extLinksOpenInNewWindow', None) if not self.errors: # Trigger onDisabled() on plugins if theme was active # previously and rules were changed if self.theme_settings.rules != rules: applyTheme(None) self.theme_settings.enabled = themeEnabled self.theme_settings.rules = rules self.theme_settings.absolutePrefix = prefix self.theme_settings.parameterExpressions = parameterExpressions self.theme_settings.hostnameBlacklist = hostnameBlacklist self.theme_settings.doctype = doctype # Theme base settings if themeBase is not None: self.pskin.default_skin = themeBase.encode('utf-8') if markSpecialLinks is not None: self.mark_special_links = markSpecialLinks if extLinksOpenInNewWindow is not None: self.ext_links_open_new_window = extLinksOpenInNewWindow IStatusMessage(self.request).add(_(u"Changes saved")) self._setup() return True else: IStatusMessage(self.request).add(_(u"There were errors"), 'error') self.redirectToFieldset('advanced') return False if 'form.button.Import' in form: self.authorize() enableNewTheme = form.get('enableNewTheme', False) replaceExisting = form.get('replaceExisting', False) themeArchive = form.get('themeArchive', None) themeZip = None performImport = False try: themeZip = zipfile.ZipFile(themeArchive) except ( zipfile.BadZipfile, zipfile.LargeZipFile, ): logger.exception("Could not read zip file") self.errors['themeArchive'] = _( 'error_invalid_zip', default=u"The uploaded file is not a valid Zip archive") if themeZip: try: themeData = extractThemeInfo(themeZip, checkRules=False) except ( ValueError, KeyError, ), e: logger.warn(str(e)) self.errors['themeArchive'] = _( 'error_no_rules_file', u"The uploaded file does not contain a valid theme " u"archive.") else: themeContainer = getOrCreatePersistentResourceDirectory() themeExists = themeData.__name__ in themeContainer if themeExists: if not replaceExisting: self.errors['themeArchive'] = _( 'error_already_installed', u"This theme is already installed. Select " u"'Replace existing theme' and re-upload to " u"replace it.") else: del themeContainer[themeData.__name__] performImport = True else: performImport = True if performImport: themeContainer.importZip(themeZip) themeDirectory = queryResourceDirectory( THEME_RESOURCE_NAME, themeData.__name__) if themeDirectory is not None: # If we don't have a rules file, use the template if themeData.rules == u"/++{0:s}++{1:s}/{2:s}".format( THEME_RESOURCE_NAME, themeData.__name__, RULE_FILENAME, ) and not themeDirectory.isFile(RULE_FILENAME): templateThemeDirectory = queryResourceDirectory( THEME_RESOURCE_NAME, TEMPLATE_THEME) themeDirectory.writeFile( RULE_FILENAME, templateThemeDirectory.readFile(RULE_FILENAME)) if not themeDirectory.isFile(DEFAULT_THEME_FILENAME): IStatusMessage(self.request).add( _(u"A boilerplate rules.xml was added to " u"your theme, but no index.html file " u"found. Update rules.xml to reference " u"the current theme file."), 'warning', ) plugins = getPlugins() pluginSettings = getPluginSettings(themeDirectory, plugins) if pluginSettings is not None: for name, plugin in plugins: plugin.onCreated(themeData.__name__, pluginSettings[name], pluginSettings) if enableNewTheme: applyTheme(themeData) self.theme_settings.enabled = True if not self.errors: portalUrl = getToolByName(self.context, 'portal_url')() self.redirect( "{0}/++theme++{1}/@@theming-controlpanel-mapper".format( portalUrl, themeData.__name__)) return False else: IStatusMessage(self.request).add(_(u"There were errors"), "error") self.renderOverlay('upload') return True if 'form.button.CreateTheme' in form: self.authorize() title = form.get('title') description = form.get('description') or '' baseOn = form.get('baseOn', TEMPLATE_THEME) enableImmediately = form.get('enableImmediately', True) if not title: self.errors['title'] = _(u"Title is required") IStatusMessage(self.request).add(_(u"There were errors"), 'error') self.renderOverlay('new-theme') return True else: if any(x.__name__ == title for x in getZODBThemes()): self.errors['title'] = _(u"Duplicate title") IStatusMessage(self.request).add( _(u"This title is already in use"), 'error') return True name = createThemeFromTemplate(title, description, baseOn) self._setup() if enableImmediately: themeData = self.getThemeData(self.availableThemes, name) applyTheme(themeData) self.theme_settings.enabled = True portalUrl = getToolByName(self.context, 'portal_url')() self.redirect( "{0}/++theme++{1}/@@theming-controlpanel-mapper".format( portalUrl, name)) return False if 'form.button.DeleteSelected' in form: self.authorize() toDelete = form.get('themes', []) themeDirectory = getOrCreatePersistentResourceDirectory() for theme in toDelete: del themeDirectory[theme] IStatusMessage(self.request).add(_(u"Theme deleted"), 'info') self._setup() return True return True