From: Martin A. <svn...@pl...> - 2012-07-30 21:48:34
|
Repository: plone.app.theming Branch: refs/heads/optilude-ace Date: 2012-07-24T15:40:13-07:00 Author: Martin Aspeli (optilude) <opt...@gm...> Commit: https://github.com/plone/plone.app.theming/commit/fca148748132ab42d1c69c0bf511e641fe55c293 Render user guide Files changed: A src/plone/app/theming/browser/resources/userguide.rst M docs/TODO.txt M setup.py M src/plone/app/theming/browser/controlpanel.pt M src/plone/app/theming/browser/controlpanel.py D docs/userguide.rst diff --git a/docs/TODO.txt b/docs/TODO.txt index 1283f01..76f1df3 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -11,8 +11,13 @@ General Control panel ------------- +[ ] Handle errors in new/copy dialogue properly + - remove error when clicking button + - maintain title in copy overlay +[ ] Fix anchor links in use guide (base tag problem) + [x] Update layout and language to match revised mockups -[ ] Expose user guide +[x] Expose user guide [x] Explicit activate/deactivate action per theme [x] Explicit copy per theme diff --git a/docs/userguide.rst b/docs/userguide.rst deleted file mode 100644 index 35e1269..0000000 --- a/docs/userguide.rst +++ /dev/null @@ -1,750 +0,0 @@ -Diazo theming -============= - -This guide provides an overview of Diazo theming in Plone. - -.. contents:: Contents - :local: - -What is a Diazo theme? ----------------------- - -A "theme" makes a website (in this case, one powered by Plone) take on a -particular look and feel. - -*Diazo* (formerly known as XDV) is a technology that can be used to theme -websites. It is not specific to Plone per se, but has been created by the Plone -community and, as of Plone 4.3, provides the default way to apply a theme to a -Plone site. You can learn more about Diazo at http://diazo.org. - -Diazo themes may be a little different to themes you have created in other -systems, and indeed to themes you may have created for earlier versions of -Plone. A Diazo theme is really about transforming some content - in this case -the output from "vanilla" Plone - into a different set of HTML markup by -applying a set of rules to combine a static mock-up of the end result you want -with the dynamic content coming from Plone. - -In comparison, the previous way to theme a Plone site (like the way many other -content management systems are themed) relies on selectively overriding the -templates and scripts that Plone uses to build a page with custom versions -that produce different HTML markup. The latter approach can be more powerful, -certainly, but also requires much deeper knowledge of Plone's internals and -command of server-side technologies such as Zope Page Templates and even Python. -Diazo themes, by contrast, are easy to understand for web designers and non- -developers alike. - -A Diazo theme consists of three elements: - -1. One or more HTML mockups, also referred to as *theme* files, that represent - the desired look and feel. - - These will contain placeholders for content that is to be provided by the - Plone content management system. Mockups usually reference CSS, JavaScript - and image files by relative path. The most common way to create a theme is to - use desktop software like Dreamweaver or a text editor to create the relevant - markup, styles and scripts, and test the theme locally in a web browser. -2. The *content* that is being themed. In this case, that is the output from - Plone. -3. A *rules file*, which defines how the placeholders in the theme (i.e. the - HTML mockup) should be replaced by relevant markup in the content. - - The rules file uses XML syntax (similar to HTML). Here is a very simple - example:: - - <?xml version="1.0" encoding="UTF-8"?> - <rules - xmlns="http://namespaces.plone.org/diazo" - xmlns:css="http://namespaces.plone.org/diazo/css" - xmlns:xsl="http://www.w3.org/1999/XSL/Transform">` - - <theme href="theme.html" /> - - <replace css:content-children="#content" css:theme-children="#main" /> - - </rules> - - Here, we are replacing the contents (child nodes) of a placeholder element - with HTML id ``main`` in the theme file (``theme.html``, found in the same - directory as the ``rules.xml`` file, as referenced by the ``<theme />`` rule) - with the contents (children) of the element with the HTML id ``content`` in - the markup generated by Plone. - - When this theme is applied, the result will look very much like the static - HTML file ``theme.html`` (and its referenced CSS, JavaScript and image - files), except the placeholder that is identified by the node in the theme - with id ``main`` will be filled by Plone's main content area. - -Plone ships with an example theme called, appropriately, *Example theme*, which -uses the venerable `Twitter Bootstrap <http://twitter.github.com/bootstrap/>`_ -to build a simple yet functional theme exposing most of Plone's core -functionality. You are advised to study it - in particular the ``rules.xml`` -file - to learn more about how Diazo themes work. - -Using the control panel ------------------------ - -TODO - -Selecting a theme -~~~~~~~~~~~~~~~~~ - -TODO: Describe how to enable a theme in the control panel - -Creating a new theme -~~~~~~~~~~~~~~~~~~~~ - -TODO: Describe actions: cloning and creating anew - -Uploading an existing theme -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -TODO: Describe uploading a theme - with or without rules.xml file to start with - -The file manager -~~~~~~~~~~~~~~~~ - -TODO: Describe purpose and UI - -The rule builder -~~~~~~~~~~~~~~~~ - -TODO: Describe purpose and UI - filesystem and ZODB themes - -Advanced settings -~~~~~~~~~~~~~~~~~ - -TODO: Describe the available settings at a high level - -Reference ---------- - -The remainder of this guide contains reference materials useful for theme -builders. - -Deploying and testing themes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To build and test a theme, you must first create a static HTML mockup of the -look and feel you want, and then build a rules file to describe how Plone's -content maps to the placeholders in this mockup. - -The mockup can be created anywhere using whatever tool you feel most comfortable -building web pages in. To simplify integration with Plone, you are recommended -to make sure it uses relative links for resources like CSS, JavaScript and image -files, so that it will render properly when opened in a web browser from a local -file. Plone will convert these relative links to the appropriate absolute paths -automatically, ensuring the theme works no matter which URL the user is viewing -when the theme is applied to a Plone site. - -There are several ways to get the theme into Plone: - -1. On the filesystem - -If you used an installer or a standard "buildout" to set up your Plone site, -you should have a directory called ``resources`` in the root of your Plone -installation (this is created using the ``resources`` option to the buildout -recipe ``plone.recipe.zope2instance``. See -http://pypi.python.org/pypi/plone.recipe.zope2instance for more details.) - -You can find (or create) a ``theme`` directory inside this directory, which is -used to contain themes. Each theme needs its own directory with a unique name. -Create one (e.g. ``resources/theme/mytheme``) and put your HTML files and any -references resources inside this directory. You can use subdirectories if you -wish, but you are recommended to keep the basic theme HTML files at the top -of the theme directory. - -You will also need a rules file called ``rules.xml`` inside this directory. If -you haven't got one yet, start with an empty one:: - - <?xml version="1.0" encoding="UTF-8"?> - <rules - xmlns="http://namespaces.plone.org/diazo" - xmlns:css="http://namespaces.plone.org/diazo/css" - xmlns:xsl="http://www.w3.org/1999/XSL/Transform">` - - <theme href="theme.html" /> - <replace css:content-children="#content" css:theme-children="#main" /> - - </rules> - -Provided you are running Zope in debug mode (e.g. you start it up with -``bin/instance fg``), changes to the theme and rules should take effect -immediately. You can preview or enable the theme through the *Themes* control -panel, and then iteratively modify the ``rules.xml`` file or the theme mockup -as you wish. - -2. Through the web - -If you prefer (or do not have filesystem access), you can create themes entirely -through the Plone control panel, either by duplicating an existing theme, or -starting from scratch with a near-empty theme. - -TODO: Describe specific UI to do this - -Once a theme has been created, you can modify it through the in-Plone theme -file manager or rule builder. See below for more details. - -3. As a zip file - -Themes can be downloaded from Plone as Zip files, which can then be uploaded -into other sites. - -TODO: Describe specific UI to do this - -In fact, you can create valid theme zip archives by compressing a theme -directory on the filesystem using a standard compression tool such as *7-Zip* or -*Winzip* (for Windows) or the built-in *Compress* action in the Mac OS X Finder. -Just make sure you compress exactly one folder that contains all the theme files -and the ``rules.xml`` file. (Do not compress the contents of the folder -directly: when unpacked, the zip file should produce exactly one folder which -in turn contains all the relevant files). - -4. In a Python package (programmers only) - -If you are creating a Python package containing Plone customisations that you -intend to install into your site, you can let it register a theme for -installation into the site. - -To do this, place a directory called e.g. ``theme`` at the top of the package, -next to the Zope ``configure.zcml`` file, and add a ``<plone:static />`` -declaration to the ``configure.zcml`` file:: - - <configure - xmlns:plone="http://namespaces.plone.org/plone" - xmlns="http://namespaces.zope.org/zope"> - - ... - - <plone:static name="mytheme" directory="theme" type="theme" /> - - ... - - </configure> - -Notice the declaration of the ``plone`` namespace at the root ``<configure />`` -element. Place the theme files and the ``rules.xml`` file into the ``theme`` -directory. - -If your package has a GenericSetup profile, you can automatically enable the -theme upon installation of this profile by adding a ``theme.xml`` file in the -``profiles/default`` directory, containing e.g.:: - - <theme> - <name>mytheme</name> - <enabled>true</enabled> - </theme> - -The manifest file -~~~~~~~~~~~~~~~~~ - -It is possible to give additional information about a theme by placing a file -called ``manifest.cfg`` next to the ``rules.xml`` file at the top of a theme -directory. - -This file may look like this:: - - [theme] - title = My theme - description = A test theme - -As shown here, the manifest file can be used to provide a more user friendly -title and a longer description for the theme, for use in the control panel. -Only the ``[theme]`` header is required - all other keys are optional. - -You can also set:: - - rules = http://example.org/myrules.xml - -to use a different rule file name than ``rules.xml`` (you should provide a URL -or relative path), and:: - - prefix = /some/prefix - -to change the absolute path prefix (see *Advanced settings*). - -Extensions to the Diazo theming engine can add support for additional blocks of -configurable parameters. - -Rules syntax -~~~~~~~~~~~~ - -The following is a short summary of the Diazo rules syntax. See -http://diazo.org for more details and further examples. - -Selectors -+++++++++ - -Each rule is represented by an XML tag that operates on one or more HTML -elements in the content and/or theme. The elements to operate on are indicated -using attributes of the rules known as *selectors*. - -The easiest way to select elements is to use a CSS expression selector, such as -``css:content="#content"`` or ``css:theme="#main .content"``. Any valid CSS 3 -expression (including pseudo-selectors like ``:first-child`` may be used. - -The standard selectors, ``css:theme`` and ``css:content``, operate on the -element(s) that are matched. If you want to operate on the children of the -matched element instead, use ``css:theme-children="..."`` or -``css:content-children="..."`` instead. - -If you cannot construct a suitable CSS 3 expression, you can use XPath -expressions such as ``content="/head/link"`` or ``theme="//div[@id='main']"`` -(note the lack of a ``css:`` prefix when using XPath expressions). The two -approaches are equivalent, and you can mix and match freely, but you cannot -have e.g. both a ``css:theme`` and a ``theme`` attribute on a single rule. To -operate on children of a node selected with an XPath expression, use -``theme-children="..."`` or ``content-children="..."``. - -You can learn more about XPath at http://www.w3schools.com/xpath/default.asp. - -Conditions -++++++++++ - -By default, every rule is executed, though rules that do not match any elements -will of course do nothing. You can make a rule, set of rules or theme reference -(see below) conditional upon an element appearing in the content by adding an -attribute to the rule like ``css:if-content="#some-element"`` (to use an XPath -expression instead, drop the ``css:`` prefix). If no elements match the -expression, the rule is ignored. - -**Tip:** if a ``<replace />`` rule matches an element in the theme but not in -the content, the theme node will be dropped (replaced with nothing). If you do -not want this behavior and you are unsure if the content will contain the -relevant element(s), you can use ``css:if-content`` conditional rule. Since -this is a common scenario, there is a shortcut: ``css:if-content=""`` means -"use the expression from the ``css:content`` attribute". - -Similarly, you can construct a condition based on the path of the current -request by using an attribute like ``if-path="/news"`` (note that there is no -``css:if-path`` ). If the path starts with a slash, it will match from the root -of the Plone site. If it ends with a slash, it will match to the end of the URL. -You can set an absolute path by using a leading and a trailing slash. - -Finally, you can use arbitrary XPath expressions against any defined variable -using an attribute like ``if="$host = 'localhost'"`` . By default, the variables -``url`` , ``scheme`` , ``host`` and ``base`` are available, representing the -current URL. Themes may define additional variables in their manifests. - -Available rules -+++++++++++++++ - -The various rule types are summarized below. - -``rules`` -######### - -:: - - <rules> - ... - </rules> - -Wraps a set of rules. Must be used as the root element of the rules file. Nested -``<rules />`` can be used with a *condition* to apply a single condition to a -set of rules. - -When used as the root element of the rules file, the various XML namespaces must -be declared:: - - <rules - xmlns="http://namespaces.plone.org/diazo" - xmlns:css="http://namespaces.plone.org/diazo/css" - xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> - ... - </rules> - -``theme`` and ``notheme`` -######################### - -:: - - <theme href="theme.html" /> - <theme href="news.html" if-path="/news" /> - <notheme if="$host = 'admin.example.org'" /> - -Choose the theme file to be used. The ``href`` is a path relative to the rules -file. If multiple ``<theme />`` elements are present, at most one may be given -without a condition. The first theme with a condition that is true will be used, -with the unconditional theme, if any, used as a fallback. - -``<notheme />`` can be used to specify a condition under which no theme -should be used. ``<notheme />`` takes precedence over ``<theme />``. - -**Tip:** To ensure you do not accidentally style non-Plone pages, add a -condition like ``css:if-condition="#visual-portal-wrapper"`` to the last theme -listed, and do not have any unconditional themes. - -``replace`` -########### - -:: - - <replace - css:content="#content" - css:theme="#main" - /> - -Replaces the matched element(s) in the theme with the matched element(s) from -the content. - -``before`` and ``after`` -######################## - -:: - - <before - css:content-children="#portal-column-one" - css:theme-children="#portlets" - /> - - <after - css:content-children="#portal-column-two" - css:theme-children="#portlets" - /> - -Inserts the matched element(s) from the content before or after the matched -element(s) in the theme. By using ``theme-children`` , you can insert the -matched content element(s) as the first (prepend) or last (append) element(s) -inside the matched theme element(s). - -``drop`` and ``strip`` -###################### - -:: - - <drop css:content=".documentByLine" /> - <drop theme="/head/link" /> - <drop css:theme="#content *" attributes="onclick onmouseup" /> - - <strip css:content="#parent-fieldname-text" /> - -Remove element(s) from the theme or content. Note that unlike most other rules, -a ``<drop />`` or ``<strip />`` rule can operate on the ``theme`` or -``content`` , but not both. ``<drop />`` removes the matched element(s) and -any children, whereas ``<strip />`` removes the matched element(s), but leaves -any children in place. - -``<drop />`` may be given a whitespace-separated list of ``attributes`` to -drop. In this case, the matched element(s) themselves will not be removed. Use -``attributes="*"`` to drop all attributes. - -``merge`` and ``copy`` -###################### - -:: - - <merge - attributes="class" - css:content="body" - css:theme="body" - /> - - <copy - attributes="class" - css:content="#content" - css:theme="#main" - /> - -These rules operate on attributes. ``<merge />`` will add the contents of the -named attribute(s) in the theme to the value(s) of any existing attributes with -the same name(s) in the content, separated by whitespace. It is mainly used to -merge CSS classes. - -``<copy />`` will copy attributes from the matched element(s) in the content -to the matched element(s) in the theme, fully replacing any attributes with -the same name that may already be in the theme. - -The ``attributes`` attribute can contain a whitespace-separated list of -attributes, or the special value ``*`` to operate on all attributes of the -matched element. - -Advanced modification -++++++++++++++++++++++ - -Instead of selecting markup to insert into the theme from the content, you can -place markup directly into the rules file, as child nodes of the relevant rule -element:: - - <after css:theme="head"> - <style type="text/css"> - body > h1 { color: red; } - </style> - </after> - -This also works on the content, allowing you to modify it on the fly before any -rules are applied:: - - <replace css:content="#portal-searchbox input.searchButton"> - <button type="submit"> - <img src="images/search.png" alt="Search" /> - </button> - </replace> - -In addition to including static HTML in this manner, you can use XSLT -instructions that operate on the content. You can even use ``css:`` selectors -directly in the XSLT.:: - - <replace css:theme="#details"> - <dl id="details"> - <xsl:for-each css:select="table#details > tr"> - <dt><xsl:copy-of select="td[1]/text()"/></dt> - <dd><xsl:copy-of select="td[2]/node()"/></dd> - </xsl:for-each> - </dl> - </replace> - -Rules may operate on content that is fetched from somewhere other than the -current page being rendered by Plone, by using the ``href`` attribute to specify -a path of a resource relative to the root of the Plone site:: - - <after - css:theme-children="#leftnav" - css:content=".navitem" - href="/@@extra-nav" - /> - -Theme parameters -~~~~~~~~~~~~~~~~ - -It is possible to pass arbitrary parameters to your theme, which can be -referenced as variables in XPath expressions. Parameters can be set in Plone's -theming control panel, and may be imported from a ``manifest.cfg`` file. - -For example, you could have a parameter ``mode`` that could be set to the -string ``live`` or ``test``. In your rules, you could do something like this -to insert a warning when you are on the test server:: - - <before css:theme-children="body" if="$mode = 'test'"> - <span class="warning">Warning: This is the test server</span> - </before> - -You could even use the parameter value directly, e.g.:: - - <before css:theme-children="body"> - <span class="info">This is the <xsl:value-of select="$mode" /> server</span> - </before> - -The following parameters are always available to Plone themes: - -``scheme`` - The scheme portion of the inbound URL, usually ``http`` or ``https``. -``host`` - The hostname in the inbound URL. -``path`` - The path segment of the inbound URL. This will not include any virtual - hosting tokens, i.e. it is the path the end user sees. -``base`` - The Zope base url (the ``BASE1`` request variable). - -You can add additional parameters through the control panel, using TALES -expressions. Parameters are listed on the *Advanced* tab, one per line, in -the form ``<name> = <expression>``. - -For example, if you want to avoid theming any pages that are loaded by Plone's -overlays, you can make use of the ``ajax_load`` request parameter that they -set. Your rules file might include:: - - <notheme if="$ajax_load" /> - -To add this parameter as well as the ``mode`` parameter outlined earlier, you -could add the following in the control panel:: - - ajax_load = python: 'ajax_load' in request.form - mode = string: test - -The right hand side is a TALES expression. It *must* evaluate to a string, -integer, float, boolean or ``None``: lists, dicts and objects are not -supported. ``python:``, ``string:`` and path expressions work as they do -in Zope Page Templates. - -The following variables are available when constructing these TALES expressions: - -``context`` - The context of the current request, usually a content object. -``request`` - The current request. -``portal`` - The portal root object. -``context_state`` - The ``@@plone_context_state`` view, from which you can look up additional - values such as the context's URL or default view. -``portal_state`` - The ``@@plone_portal_state`` view, form which you can look up additional - values such as the navigation root URL or whether or not the current - user is logged in. - -See ``plone.app.layout`` for details about the ``@@plone_context_state`` and -``@@plone_portal_state`` views. - -Theme parameters are usually integral to a theme, and will therefore be set -based on a theme's manifest when a theme is imported or enabled. This is done -using the ``[theme:parameters]`` section in the ``manifest.cfg`` file. For -example:: - - [theme] - title = My theme - description = A test theme - - [theme:parameters] - ajax_load = python: 'ajax_load' in request.form - mode = string: test - -Theme debugging -~~~~~~~~~~~~~~~ - -When Zope is in development mode (e.g. running in the foreground in a console -with ``bin/instance fg``), the theme will be re-compiled on each request. In -non-development mode, it is compiled once when first accessed, and then only re- -compiled the control panel values are changed. - -Also, in development mode, it is possible to temporarily disable the theme -by appending a query string parameter ``diazo.off=1``. For example:: - - http://localhost:8080/Plone/some-page?diazo.off=1 - -The parameter is ignored in non-development mode. - -Commonly used rules -~~~~~~~~~~~~~~~~~~~ - -The following recipes illustrate rules commonly used in building Plone themes: - -To copy the page title:: - - <replace css:theme="title" css:content="title" /> - -To copy the ``<base />`` tag (necessary for Plone's links to work):: - - <replace css:theme="base" css:content="base" /> - -If there is no ``<base />`` tag in the theme, you can do: - - <before css:theme-children="head" css:content="base" /> - -To drop all styles and JavaScript resources from the theme and copy them -from Plone's ``portal_css`` tool instead:: - - <!-- Drop styles in the head - these are added back by including them from Plone --> - <drop theme="/html/head/link" /> - <drop theme="/html/head/style" /> - - <!-- Pull in Plone CSS --> - <after theme-children="/html/head" content="/html/head/link | /html/head/style" /> - -To copy Plone's JavaScript resources:: - - <!-- Pull in Plone CSS --> - <after theme-children="/html/head" content="/html/head/script" /> - -To copy the class of the ``<body />`` tag (necessary for certain Plone -JavaScript functions and styles to work properly):: - - <!-- Body --> - <merge attributes="class" css:theme="body" css:content="body" /> - -Advanced: Using portal_css to manage your CSS -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Plone's "resource registries", including the ``portal_css`` tool, can be used -to manage CSS stylesheets. This offers several advantages over simply linking -to your stylesheets in the template, such as: - -* Detailed control over the ordering of stylesheets -* Merging of stylesheets to reduce the number of downloads required to render - your page -* On-the-fly stylesheet compression (e.g. whitespace removal) -* The ability to include or exclude a stylesheet based on an expression - -It is usually desirable (and sometimes completely necessary) to leave the -theme file untouched, but you can still use ``portal_css`` to manage your -stylesheets. The trick is to: - -* Register your theme's styles with Plone's ``portal_css`` tool (this is - normally best done when you ship a theme in a Pyton package - there is - currently no way to automate this for a theme imported from a Zip file or - created through the web) -* Drop the theme's styles with a rule, and then -* Include all styles from Plone - -For example, you could add the following rules:: - - <drop theme="/html/head/link" /> - <drop theme="/html/head/style" /> - - <!-- Pull in Plone CSS --> - <after theme-children="/html/head" content="/html/head/link | /html/head/style" /> - -The use of an "or" expression for the content in the ``after />`` rule means -that the relative ordering of link and style elements is maintained. - -To register stylesheets upon product installation using GenericSetup, use the -``cssregistry.xml`` import step in your GenericSetup ``profiles/default`` -directory:: - - <?xml version="1.0"?> - <object name="portal_css"> - - <!-- Set conditions on stylesheets we don't want to pull in --> - <stylesheet - expression="not:request/HTTP_X_THEME_ENABLED | nothing" - id="public.css" - /> - - <!-- Add new stylesheets --> - <stylesheet title="" authenticated="False" cacheable="True" - compression="safe" conditionalcomment="" cookable="True" enabled="on" - expression="request/HTTP_X_THEME_ENABLED | nothing" - id="++theme++my.theme/css/styles.css" media="" rel="stylesheet" - rendering="link" - applyPrefix="True" - /> - - </object> - -There is one important caveat, however. Your stylesheet may include relative -URL references of the following form: - - background-image: url(../images/bg.jpg); - -If your stylesheet lives in a resource directory (e.g. it is registered in -``portal_css`` with the id ``++theme++my.theme/css/styles.css``), this -will work fine so long as the registry (and Zope) is in debug mode. The -relative URL will be resolved by the browser to -``++theme++my.theme/images/bg.jpg``. - -However, you may find that the relative URL breaks when the registry is put -into production mode. This is because resource merging also changes the URL -of the stylesheet to be something like:: - - /plone-site/portal_css/Suburst+Theme/merged-cachekey-1234.css - -To correct for this, you must set the ``applyPrefix`` flag to ``true`` when -installing your CSS resource using ``cssregistry.xml``. There is a -corresponding flag in the ``portal_css`` user interface. - -It is sometimes useful to show some of Plone's CSS in the styled site. You -can achieve this by using an Diazo ``<after />`` rule or similar to copy the -CSS from Plone's generated ``<head />`` into the theme. You can use the -``portal_css`` tool to turn off the style sheets you do not want. - -However, if you also want the site to be usable in non-themed mode (e.g. on a -separate URL), you may want to have a larger set of styles enabled when Diazo -is not used. To make this easier, you can use the following expressions as -conditions in the ``portal_css`` tool (and ``portal_javascripts`` if relevant), -in ``portal_actions``, in page templates, and other places that use TAL -expression syntax:: - - request/HTTP_X_THEME_ENABLED | nothing - -This expression will return True if Diazo is currently enabled, in which case -an HTTP header "X-Theme-Enabled" will be set. - -If you later deploy the theme to a fronting web server such as nginx, you can -set the same request header there to get the same effect, even if -``plone.app.theming`` is uninstalled. - -Use:: - - not: request/HTTP_X_THEME_ENABLED | nothing - -to 'hide' a style sheet from the themed site. \ No newline at end of file diff --git a/setup.py b/setup.py index 3962d5c..cd9ddba 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ version=version, description="Integrates the Diazo theming engine with Plone", long_description=open("README.txt").read() + "\n\n" + - open(os.path.join("docs", "userguide.rst")).read() + "\n\n" + + open(os.path.join("src", "plone", "app", "theming", "browser", "resources", "userguide.rst")).read() + "\n\n" + open(os.path.join("docs", "HISTORY.txt")).read(), # Get more strings from http://www.python.org/pypi?%3Aaction=list_classifiers classifiers=[ diff --git a/src/plone/app/theming/browser/controlpanel.pt b/src/plone/app/theming/browser/controlpanel.pt index ab02fa5..ae97f7c 100644 --- a/src/plone/app/theming/browser/controlpanel.pt +++ b/src/plone/app/theming/browser/controlpanel.pt @@ -18,6 +18,7 @@ tal:attributes="href string:${context/portal_url}/++resource++plone.app.theming/controlpanel.css" /> +<script type="text/javascript" charset="utf-8" tal:content="string:var OPEN_OVERLAY='${view/overlay}';;"></script> <script type="text/javascript" charset="utf-8"> jQuery(function($) { @@ -29,17 +30,19 @@ return false; }); - // move to top so overlay works + // move to top so overlays work $(".overlay").appendTo("body"); // Create/copy overlay - $("#new-theme-overlay").overlay(); + $("#overlay-new-theme").overlay(); + $("#overlayTitleNewTheme").show(); + $("#overlayTitleCopyTheme").hide(); $("#createButton").click(function() { $("#baseOn").val('template'); $("#overlayTitleNewTheme").show(); $("#overlayTitleCopyTheme").hide(); - $("#new-theme-overlay").data('overlay').load(); + $("#overlay-new-theme").data('overlay').load(); return false; }); @@ -48,43 +51,49 @@ $("#overlayTitleNewTheme").hide(); $("#overlayTitleCopyTheme span").html($(this).attr('data-title')); $("#overlayTitleCopyTheme").show(); - $("#new-theme-overlay").data('overlay').load(); + $("#overlay-new-theme").data('overlay').load(); return false; }); - $("#new-theme-overlay input[name=form.button.Cancel]").click(function() { - $("#new-theme-overlay").data('overlay').close(); + $("#overlay-new-theme input[name=form.button.Cancel]").click(function() { + $("#overlay-new-theme").data('overlay').close(); return false; }); // Delete confirm overlay - $("#delete-confirm-overlay").overlay(); + $("#overlay-delete-confirm").overlay(); $(".deleteLink").click(function() { $("#deleteConfirmTheme").val($(this).attr('data-theme')); $("#overlayTitleDeleteConfirm span").html($(this).attr('data-title')); - $("#delete-confirm-overlay").data('overlay').load(); + $("#overlay-delete-confirm").data('overlay').load(); return false; }); - $("#delete-confirm-overlay input[name=form.button.Cancel]").click(function() { - $("#delete-confirm-overlay").data('overlay').close(); + $("#overlay-delete-confirm input[name=form.button.Cancel]").click(function() { + $("#overlay-delete-confirm").data('overlay').close(); return false; }); // Upload overlay - $("#upload-overlay").overlay(); + $("#overlay-upload").overlay(); $("#uploadButton").click(function() { - $("#upload-overlay").data('overlay').load(); + $("#overlay-upload").data('overlay').load(); return false; }); - $("#upload-overlay input[name=form.button.Cancel]").click(function() { - $("#upload-overlay").data('overlay').close(); + $("#overlay-upload input[name=form.button.Cancel]").click(function() { + $("#overlay-upload").data('overlay').close(); return false; }); + // Open overlay if the window hash told us to + if(OPEN_OVERLAY) { + var triggeredOverlay = $("#overlay-" + OPEN_OVERLAY).data('overlay'); + if(triggeredOverlay) triggeredOverlay.load(); + } + }); }); @@ -120,7 +129,7 @@ </dl> <dl class="enableFormTabbing"> - <dt id="fieldsetlegend-basic">Themes</dt> + <dt id="fieldsetlegend-basic" i18n:translate="">Themes</dt> <dd id="fieldset-basic"> <p i18n:translate="description_basic_settings"> @@ -158,8 +167,6 @@ i18n:translate="">(active)</span> </span> - - <div class="themeEntryWrapper"> <a class="previewImageContainer" @@ -231,7 +238,7 @@ </dd> - <dt id="fieldsetlegend-advanced">Advanced settings</dt> + <dt id="fieldsetlegend-advanced" i18n:translate="">Advanced settings</dt> <dd id="fieldset-advanced"> <p i18n:translate="description_advanced"> @@ -419,10 +426,15 @@ </dd> + <dt id="fieldsetlegend-help" i18n:translate="">User guide</dt> + <dd id="fieldset-help" tal:content="structure view/render_userguide"> + + </dd> + </dl> <!-- Delete confirmation overlay --> - <div id="delete-confirm-overlay" class="overlay overlay-ajax"> + <div id="overlay-delete-confirm" class="overlay overlay-ajax"> <div class="close"><span i18n:translate="">Close</span></div> <div class="pb-ajax"> <div> @@ -461,7 +473,7 @@ </div> <!-- New/copy overlay --> - <div id="new-theme-overlay" class="overlay overlay-ajax"> + <div id="overlay-new-theme" class="overlay overlay-ajax"> <div class="close"> <span i18n:translate="">Close</span> </div> @@ -474,19 +486,24 @@ Please enter the details of your new theme </p> - <form name="create" method="post" tal:attributes="action request/URL"> + <form name="create" method="post" tal:attributes="action request/URL" tal:define="errors view/errors"> - <div class="field"> + <div + tal:define="error errors/title | nothing" + tal:attributes="class python:'field error' if error else 'field'"> <label for="title" i18n:translate="label_new_theme_title">Title</label> <div class="formHelp" i18n:translate="help_new_theme_title"> Enter a short, descriptive title for your theme </div> + <div tal:content="error" tal:condition="error" /> + <input type="text" name="title" id="title" + tal:attributes="value request/title | nothing" /> </div> @@ -497,14 +514,19 @@ </div> <textarea name="description" - id="description"></textarea> + id="description" + tal:content="request/description | nothing"></textarea> </div> - <input type="hidden" name="baseOn" id="baseOn" value="template" /> + <input type="hidden" name="baseOn" id="baseOn" tal:attributes="value request/baseOn | string:template" /> - <div class="field"> + <div + class="field" + tal:define="selected python:request.get('enableImmediately', False)"> <input type="hidden" value="" name="enableImmediately:boolean:default" /> - <input type="checkbox" value="1" name="enableImmediately:boolean" id="enableImmediately" /> + <input type="checkbox" value="1" name="enableImmediately:boolean" id="enableImmediately" + tal:attributes="checked python:'enableImmediately' if selected else None" + /> <label for="enableImmediately" i18n:translate="label_enable_immediately">Immediately enable new theme</label> <div class="formHelp" i18n:translate="help_enable_immediately"> Select this option to enable the newly created theme @@ -512,7 +534,6 @@ </div> </div> - <div class="formControls"> <input type="submit" @@ -539,7 +560,7 @@ </div> <!-- Upload overlay --> - <div id="upload-overlay" class="overlay overlay-ajax"> + <div id="overlay-upload" class="overlay overlay-ajax"> <div class="close"> <span i18n:translate="">Close</span> </div> @@ -548,11 +569,11 @@ <h1 class="documentFirstHeading" i18n:translate="theming_controlpanel_upload">Upload theme</h1> <p class="documentDescription" i18n:translate="description_import"> - You can import a ZIP file containing an existing theme. + You can import a Zip file containing an existing theme. This should contain a single top level directory, which will be used as the theme identifier. If no Diazo <code>rules.xml</code> or <code>manifest.cfg</code> file is found in this directory, a - default rules file will be created. + default <code>rules.xml</code> file will be created. </p> <form @@ -560,14 +581,20 @@ method="post" enctype="multipart/form-data" class="enableUnloadProtection" - tal:attributes="action request/URL"> + tal:attributes="action request/URL" + tal:define="errors view/errors"> - <div class="field"> + <div + class="field" + tal:define="error errors/themeArchive | nothing" + tal:attributes="class python:'field error' if error else 'field'"> <div class="formHelp" i18n:translate="help_theme_archive"> Select a file to upload. </div> + <div tal:content="error" tal:condition="error" /> + <input type="file" name="themeArchive" @@ -576,22 +603,31 @@ </div> - <div class="field"> + <div + class="field" + tal:define="selected python:request.get('enableNewTheme', False)"> <input type="hidden" value="" name="enableNewTheme:boolean:default" /> - <input type="checkbox" value="1" name="enableNewTheme:boolean" id="enableNewTheme" /> + <input type="checkbox" value="1" name="enableNewTheme:boolean" id="enableNewTheme" + tal:attributes="checked python:'enableNewTheme' if selected else None" + /> <label for="enableNewTheme" i18n:translate="label_enable_new_theme">Immediately enable new theme</label> <div class="formHelp" i18n:translate="help_enable_new_theme"> - Select this option to enable the newly uploaded theme immediately. + Select this option to enable the newly uploaded theme + immediately. </div> </div> - <div class="field"> + <div + class="field" + tal:define="selected python:request.get('replaceExisting', False)"> <input type="hidden" value="" name="replaceExisting:boolean:default" /> - <input type="checkbox" value="1" name="replaceExisting:boolean" id="replaceExisting" /> + <input type="checkbox" value="" name="replaceExisting:boolean" id="replaceExisting" + tal:attributes="checked python:'replaceExisting' if selected else None" + /> <label for="replaceExisting" i18n:translate="label_replace_existing">Replace existing theme</label> <div class="formHelp" i18n:translate="help_replace_existing"> - Select this option to replace any existing theme that may have been uploaded previously - containing the same top level theme directory. + Select this option to replace any existing theme that + may have been uploaded previously. </div> </div> diff --git a/src/plone/app/theming/browser/controlpanel.py b/src/plone/app/theming/browser/controlpanel.py index 95d460c..ad84e61 100644 --- a/src/plone/app/theming/browser/controlpanel.py +++ b/src/plone/app/theming/browser/controlpanel.py @@ -1,3 +1,6 @@ +import pkg_resources +import docutils.core + import logging import zipfile @@ -48,11 +51,11 @@ def __call__(self): return '' def _setup(self): - self.settings = getUtility(IRegistry).forInterface( - IThemeSettings, False) + self.settings = getUtility(IRegistry).forInterface(IThemeSettings, False) self.zodbThemes = getZODBThemes() self.availableThemes = getAvailableThemes() self.selectedTheme = self.getSelectedTheme(self.availableThemes, self.settings.rules) + self.overlay = '' # Set response header to make sure control panel is never themed self.request.response.setHeader('X-Theme-Disabled', '1') @@ -225,7 +228,8 @@ def update(self): IStatusMessage(self.request).add( _(u"There were errors"), "error" ) - self._setup() + + self.renderOverlay('upload') return True if 'form.button.CreateTheme' in form: @@ -241,7 +245,8 @@ def update(self): IStatusMessage(self.request).add( _(u"There were errors"), 'error') - self._setup() + + self.renderOverlay('new-theme') return True else: @@ -320,5 +325,13 @@ def redirectToFieldset(self, fieldset): self.redirect("%s/%s#fieldsetlegend-%s" % ( portalUrl, self.__name__, fieldset,)) + def renderOverlay(self, overlay): + self.overlay = overlay + def authorize(self): return authorize(self.context, self.request) + + def render_userguide(self): + rstSource = pkg_resources.resource_string('plone.app.theming.browser', 'resources/userguide.rst') + parts = docutils.core.publish_parts(source=rstSource, writer_name='html') + return parts['body_pre_docinfo'] + parts['fragment'] diff --git a/src/plone/app/theming/browser/resources/userguide.rst b/src/plone/app/theming/browser/resources/userguide.rst new file mode 100644 index 0000000..35e1269 --- /dev/null +++ b/src/plone/app/theming/browser/resources/userguide.rst @@ -0,0 +1,750 @@ +Diazo theming +============= + +This guide provides an overview of Diazo theming in Plone. + +.. contents:: Contents + :local: + +What is a Diazo theme? +---------------------- + +A "theme" makes a website (in this case, one powered by Plone) take on a +particular look and feel. + +*Diazo* (formerly known as XDV) is a technology that can be used to theme +websites. It is not specific to Plone per se, but has been created by the Plone +community and, as of Plone 4.3, provides the default way to apply a theme to a +Plone site. You can learn more about Diazo at http://diazo.org. + +Diazo themes may be a little different to themes you have created in other +systems, and indeed to themes you may have created for earlier versions of +Plone. A Diazo theme is really about transforming some content - in this case +the output from "vanilla" Plone - into a different set of HTML markup by +applying a set of rules to combine a static mock-up of the end result you want +with the dynamic content coming from Plone. + +In comparison, the previous way to theme a Plone site (like the way many other +content management systems are themed) relies on selectively overriding the +templates and scripts that Plone uses to build a page with custom versions +that produce different HTML markup. The latter approach can be more powerful, +certainly, but also requires much deeper knowledge of Plone's internals and +command of server-side technologies such as Zope Page Templates and even Python. +Diazo themes, by contrast, are easy to understand for web designers and non- +developers alike. + +A Diazo theme consists of three elements: + +1. One or more HTML mockups, also referred to as *theme* files, that represent + the desired look and feel. + + These will contain placeholders for content that is to be provided by the + Plone content management system. Mockups usually reference CSS, JavaScript + and image files by relative path. The most common way to create a theme is to + use desktop software like Dreamweaver or a text editor to create the relevant + markup, styles and scripts, and test the theme locally in a web browser. +2. The *content* that is being themed. In this case, that is the output from + Plone. +3. A *rules file*, which defines how the placeholders in the theme (i.e. the + HTML mockup) should be replaced by relevant markup in the content. + + The rules file uses XML syntax (similar to HTML). Here is a very simple + example:: + + <?xml version="1.0" encoding="UTF-8"?> + <rules + xmlns="http://namespaces.plone.org/diazo" + xmlns:css="http://namespaces.plone.org/diazo/css" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform">` + + <theme href="theme.html" /> + + <replace css:content-children="#content" css:theme-children="#main" /> + + </rules> + + Here, we are replacing the contents (child nodes) of a placeholder element + with HTML id ``main`` in the theme file (``theme.html``, found in the same + directory as the ``rules.xml`` file, as referenced by the ``<theme />`` rule) + with the contents (children) of the element with the HTML id ``content`` in + the markup generated by Plone. + + When this theme is applied, the result will look very much like the static + HTML file ``theme.html`` (and its referenced CSS, JavaScript and image + files), except the placeholder that is identified by the node in the theme + with id ``main`` will be filled by Plone's main content area. + +Plone ships with an example theme called, appropriately, *Example theme*, which +uses the venerable `Twitter Bootstrap <http://twitter.github.com/bootstrap/>`_ +to build a simple yet functional theme exposing most of Plone's core +functionality. You are advised to study it - in particular the ``rules.xml`` +file - to learn more about how Diazo themes work. + +Using the control panel +----------------------- + +TODO + +Selecting a theme +~~~~~~~~~~~~~~~~~ + +TODO: Describe how to enable a theme in the control panel + +Creating a new theme +~~~~~~~~~~~~~~~~~~~~ + +TODO: Describe actions: cloning and creating anew + +Uploading an existing theme +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +TODO: Describe uploading a theme - with or without rules.xml file to start with + +The file manager +~~~~~~~~~~~~~~~~ + +TODO: Describe purpose and UI + +The rule builder +~~~~~~~~~~~~~~~~ + +TODO: Describe purpose and UI - filesystem and ZODB themes + +Advanced settings +~~~~~~~~~~~~~~~~~ + +TODO: Describe the available settings at a high level + +Reference +--------- + +The remainder of this guide contains reference materials useful for theme +builders. + +Deploying and testing themes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To build and test a theme, you must first create a static HTML mockup of the +look and feel you want, and then build a rules file to describe how Plone's +content maps to the placeholders in this mockup. + +The mockup can be created anywhere using whatever tool you feel most comfortable +building web pages in. To simplify integration with Plone, you are recommended +to make sure it uses relative links for resources like CSS, JavaScript and image +files, so that it will render properly when opened in a web browser from a local +file. Plone will convert these relative links to the appropriate absolute paths +automatically, ensuring the theme works no matter which URL the user is viewing +when the theme is applied to a Plone site. + +There are several ways to get the theme into Plone: + +1. On the filesystem + +If you used an installer or a standard "buildout" to set up your Plone site, +you should have a directory called ``resources`` in the root of your Plone +installation (this is created using the ``resources`` option to the buildout +recipe ``plone.recipe.zope2instance``. See +http://pypi.python.org/pypi/plone.recipe.zope2instance for more details.) + +You can find (or create) a ``theme`` directory inside this directory, which is +used to contain themes. Each theme needs its own directory with a unique name. +Create one (e.g. ``resources/theme/mytheme``) and put your HTML files and any +references resources inside this directory. You can use subdirectories if you +wish, but you are recommended to keep the basic theme HTML files at the top +of the theme directory. + +You will also need a rules file called ``rules.xml`` inside this directory. If +you haven't got one yet, start with an empty one:: + + <?xml version="1.0" encoding="UTF-8"?> + <rules + xmlns="http://namespaces.plone.org/diazo" + xmlns:css="http://namespaces.plone.org/diazo/css" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform">` + + <theme href="theme.html" /> + <replace css:content-children="#content" css:theme-children="#main" /> + + </rules> + +Provided you are running Zope in debug mode (e.g. you start it up with +``bin/instance fg``), changes to the theme and rules should take effect +immediately. You can preview or enable the theme through the *Themes* control +panel, and then iteratively modify the ``rules.xml`` file or the theme mockup +as you wish. + +2. Through the web + +If you prefer (or do not have filesystem access), you can create themes entirely +through the Plone control panel, either by duplicating an existing theme, or +starting from scratch with a near-empty theme. + +TODO: Describe specific UI to do this + +Once a theme has been created, you can modify it through the in-Plone theme +file manager or rule builder. See below for more details. + +3. As a zip file + +Themes can be downloaded from Plone as Zip files, which can then be uploaded +into other sites. + +TODO: Describe specific UI to do this + +In fact, you can create valid theme zip archives by compressing a theme +directory on the filesystem using a standard compression tool such as *7-Zip* or +*Winzip* (for Windows) or the built-in *Compress* action in the Mac OS X Finder. +Just make sure you compress exactly one folder that contains all the theme files +and the ``rules.xml`` file. (Do not compress the contents of the folder +directly: when unpacked, the zip file should produce exactly one folder which +in turn contains all the relevant files). + +4. In a Python package (programmers only) + +If you are creating a Python package containing Plone customisations that you +intend to install into your site, you can let it register a theme for +installation into the site. + +To do this, place a directory called e.g. ``theme`` at the top of the package, +next to the Zope ``configure.zcml`` file, and add a ``<plone:static />`` +declaration to the ``configure.zcml`` file:: + + <configure + xmlns:plone="http://namespaces.plone.org/plone" + xmlns="http://namespaces.zope.org/zope"> + + ... + + <plone:static name="mytheme" directory="theme" type="theme" /> + + ... + + </configure> + +Notice the declaration of the ``plone`` namespace at the root ``<configure />`` +element. Place the theme files and the ``rules.xml`` file into the ``theme`` +directory. + +If your package has a GenericSetup profile, you can automatically enable the +theme upon installation of this profile by adding a ``theme.xml`` file in the +``profiles/default`` directory, containing e.g.:: + + <theme> + <name>mytheme</name> + <enabled>true</enabled> + </theme> + +The manifest file +~~~~~~~~~~~~~~~~~ + +It is possible to give additional information about a theme by placing a file +called ``manifest.cfg`` next to the ``rules.xml`` file at the top of a theme +directory. + +This file may look like this:: + + [theme] + title = My theme + description = A test theme + +As shown here, the manifest file can be used to provide a more user friendly +title and a longer description for the theme, for use in the control panel. +Only the ``[theme]`` header is required - all other keys are optional. + +You can also set:: + + rules = http://example.org/myrules.xml + +to use a different rule file name than ``rules.xml`` (you should provide a URL +or relative path), and:: + + prefix = /some/prefix + +to change the absolute path prefix (see *Advanced settings*). + +Extensions to the Diazo theming engine can add support for additional blocks of +configurable parameters. + +Rules syntax +~~~~~~~~~~~~ + +The following is a short summary of the Diazo rules syntax. See +http://diazo.org for more details and further examples. + +Selectors ++++++++++ + +Each rule is represented by an XML tag that operates on one or more HTML +elements in the content and/or theme. The elements to operate on are indicated +using attributes of the rules known as *selectors*. + +The easiest way to select elements is to use a CSS expression selector, such as +``css:content="#content"`` or ``css:theme="#main .content"``. Any valid CSS 3 +expression (including pseudo-selectors like ``:first-child`` may be used. + +The standard selectors, ``css:theme`` and ``css:content``, operate on the +element(s) that are matched. If you want to operate on the children of the +matched element instead, use ``css:theme-children="..."`` or +``css:content-children="..."`` instead. + +If you cannot construct a suitable CSS 3 expression, you can use XPath +expressions such as ``content="/head/link"`` or ``theme="//div[@id='main']"`` +(note the lack of a ``css:`` prefix when using XPath expressions). The two +approaches are equivalent, and you can mix and match freely, but you cannot +have e.g. both a ``css:theme`` and a ``theme`` attribute on a single rule. To +operate on children of a node selected with an XPath expression, use +``theme-children="..."`` or ``content-children="..."``. + +You can learn more about XPath at http://www.w3schools.com/xpath/default.asp. + +Conditions +++++++++++ + +By default, every rule is executed, though rules that do not match any elements +will of course do nothing. You can make a rule, set of rules or theme reference +(see below) conditional upon an element appearing in the content by adding an +attribute to the rule like ``css:if-content="#some-element"`` (to use an XPath +expression instead, drop the ``css:`` prefix). If no elements match the +expression, the rule is ignored. + +**Tip:** if a ``<replace />`` rule matches an element in the theme but not in +the content, the theme node will be dropped (replaced with nothing). If you do +not want this behavior and you are unsure if the content will contain the +relevant element(s), you can use ``css:if-content`` conditional rule. Since +this is a common scenario, there is a shortcut: ``css:if-content=""`` means +"use the expression from the ``css:content`` attribute". + +Similarly, you can construct a condition based on the path of the current +request by using an attribute like ``if-path="/news"`` (note that there is no +``css:if-path`` ). If the path starts with a slash, it will match from the root +of the Plone site. If it ends with a slash, it will match to the end of the URL. +You can set an absolute path by using a leading and a trailing slash. + +Finally, you can use arbitrary XPath expressions against any defined variable +using an attribute like ``if="$host = 'localhost'"`` . By default, the variables +``url`` , ``scheme`` , ``host`` and ``base`` are available, representing the +current URL. Themes may define additional variables in their manifests. + +Available rules ++++++++++++++++ + +The various rule types are summarized below. + +``rules`` +######### + +:: + + <rules> + ... + </rules> + +Wraps a set of rules. Must be used as the root element of the rules file. Nested +``<rules />`` can be used with a *condition* to apply a single condition to a +set of rules. + +When used as the root element of the rules file, the various XML namespaces must +be declared:: + + <rules + xmlns="http://namespaces.plone.org/diazo" + xmlns:css="http://namespaces.plone.org/diazo/css" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + ... + </rules> + +``theme`` and ``notheme`` +######################### + +:: + + <theme href="theme.html" /> + <theme href="news.html" if-path="/news" /> + <notheme if="$host = 'admin.example.org'" /> + +Choose the theme file to be used. The ``href`` is a path relative to the rules +file. If multiple ``<theme />`` elements are present, at most one may be given +without a condition. The first theme with a condition that is true will be used, +with the unconditional theme, if any, used as a fallback. + +``<notheme />`` can be used to specify a condition under which no theme +should be used. ``<notheme />`` takes precedence over ``<theme />``. + +**Tip:** To ensure you do not accidentally style non-Plone pages, add a +condition like ``css:if-condition="#visual-portal-wrapper"`` to the last theme +listed, and do not have any unconditional themes. + +``rep... [truncated message content] |