source: products/quintagroup.themetemplate/trunk/quintagroup/themetemplate/README.txt @ 1017

Last change on this file since 1017 was 1017, checked in by olha, 15 years ago

correct reST doc format

File size: 18.8 KB
Line 
1qplone3 theme template
2======================
3
4quintagroup.themetemplate is an enhanced "Plone 3 Theme" template from Zopeskel,
5that includes addcontent local command, which allows you to extend base Plone theme
6by additional elements, such as: skin layers, portlets, viewlets, css and js resources,
7and objects in zexp files. This package is an analogue of Archetype template in terms
8of its functionality.
9
10quintagroup.themetemplate package is used for development of all Quintagroup themes
11for Plone 3 (http://skins.quintagroup.com).
12
13Contents
14--------
151. Overview
162. Creating theme package
173. Extending theme
184. Release notes
19
20Overview
21========
22
23This theme template allows you to create initial theme package skeleton,
24i.e. create plone3 theme python package with nested namespace (this is different from
25deafult plone3_theme template in Zopeskel)
26
27After that you can extend theme package by the following elements:
28
29  - skin-layer(s)
30  - portlet(s)
31  - viewlet(s)
32  - css, js resource(s)
33  - objects in zexp files
34
35Creation of a package is performed with *paster create* PasteScript command.
36Theme extending with other resources can be done with *paster addcontent*
37local ZopeSkel command (extended in this product).
38
39
40With this theme template you can, fist of all, create initial theme package skeleton,
41i.e. create plone3 theme python package with nested namespace (this is different from
42deafult plone3_theme template in Zopeskel)
43
44After that you can extend theme package by the following elements:
45
46
47Creating theme package
48======================
49
50Let's create plone-3 theme python package.
51Use `paster create` command for that::
52
53    >>> paster('create -t qplone3_theme plone.example --no-interactive --overwrite')
54    paster create -t qplone3_theme plone.example --no-interactive
55    ...
56
57You got standard python package content with
58  - *quintagroup* upper level namespace.
59  - *plone.example-configure.zcml* - zcml file
60    for adding into package-includes directory
61
62Check that::
63
64    >>> package_dir = 'plone.example'
65    >>> objects = ('setup.py', 'quintagroup', 'plone.example-configure.zcml')
66    >>> [True for o in objects if o in os.listdir(package_dir)]
67    [True, True, True]
68
69
70*qplone3_theme* template - creates theme with nested namespace.
71
72By default - theme is placed in
73
74    quintagroup.theme.<package name without dot> namespace
75
76in our case - quintagroup.theme.ploneexample
77
78So check namespaces::
79
80    >>> theme_namespace = os.path.join(package_dir,'quintagroup','theme','ploneexample')
81    >>> os.path.isdir(theme_namespace)
82    True
83
84Theme holds 3 subdirectories (browser, profiles, skins)::
85
86    >>> cd(theme_namespace)
87    >>> dirs = ('skins', 'browser', 'profiles')
88    >>> [True for d in dirs if d in os.listdir('.')]
89    [True, True, True]
90
91And initialization files (__init__.py, configure.zcml) ::
92
93    >>> files = ('__init__.py', 'configure.zcml')
94    >>> [True for d in files if d in os.listdir('.')]
95    [True, True]
96   
97
98*browser* directory
99-------------------
100
101Browser directory contains:
102  - 'templates' resource directory
103  - interfaces.py module with IThemeSpecific marker interface
104  - configure.zcml, with registered theme marker interface
105
106    >>> ls('browser')
107    __init__.py
108    configure.zcml
109    interfaces.py
110    templates
111
112    >>> cat('browser/interfaces.py')
113    from plone.theme.interfaces import IDefaultPloneLayer
114    <BLANKLINE>
115    class IThemeSpecific(IDefaultPloneLayer):
116    ...
117
118    >>> cat('browser/configure.zcml')
119    <configure
120    ...
121        <interface
122            interface=".interfaces.IThemeSpecific"
123            type="zope.publisher.interfaces.browser.IBrowserSkinType"
124            name="Custom Theme"
125            />
126    ...
127
128As we see, default theme name is 'Custom Theme', but on theme
129creation you can point out your own name. Check this ...
130
131First create configuration file with different skin name
132    >>> conf_data = """
133    ... [pastescript]
134    ... skinname=My Theme Name
135    ... """
136    >>> file('theme_config.conf','w').write(conf_data)
137
138Create the same theme with your own skin name and check this
139    >>> paster('create -t qplone3_theme plone.example --no-interactive --overwrite --config=theme_config.conf')
140    paster create ...
141    >>> cd(package_dir)
142    >>> cat('quintagroup/theme/ploneexample/browser/configure.zcml')
143    <configure
144    ...
145        <interface
146            interface=".interfaces.IThemeSpecific"
147            type="zope.publisher.interfaces.browser.IBrowserSkinType"
148            name="My Theme Name"
149            />
150    ...
151
152
153*skins* directory
154------------------------
155
156It contains only README.txt file and NO SKIN LAYERS YET.
157This is a job for localcommand ;)
158
159But check whether I am right ...
160    >>> cd('quintagroup/theme/ploneexample')
161    >>> ls('skins')
162    README.txt
163
164
165*profiles* directory.
166--------------------------------
167There is 'default' and uninstall profiles inside
168    >>> 'default' in os.listdir('profiles')
169    True
170    >>> 'uninstall' in os.listdir('profiles')
171    True
172
173There are the following items in default profile:
174 - import_steps.xml - for any reason.
175 - skins.xml - for registering skins directory
176
177    >>> cd('profiles/default')
178    >>> 'import_steps.xml' in os.listdir('.')
179    True
180    >>> 'skins.xml' in os.listdir('.')
181    True
182
183*skins.xml* profile makes your theme default on installation
184and uses layers list from 'Plone Default' for our theme,
185without any new layers (yet).
186
187    >>> cat('skins.xml')
188    <?xml version="1.0"?>
189    <object name="portal_skins" ...
190            default_skin="My Theme Name">
191    ...
192    <skin-path name="My Theme Name" based-on="Plone Default">
193      <!-- -*- extra layer stuff goes here -*- -->
194    <BLANKLINE>
195    </skin-path>
196    ...
197
198*import_steps.xml* - call _setupVarious_ function from
199_setuphandlers.py_ module for additional installation steps.
200
201    >>> cat('import_steps.xml')
202    <?xml version="1.0"?>
203    ...
204    <import-step id="quintagroup.theme.ploneexample.various"
205    ...
206                 handler="quintagroup.theme.ploneexample.setuphandlers.setupVarious"
207    ...
208    </import-step>
209    ...
210
211Look at setuphandlers.py module
212    >>> cd('../..')
213    >>> cat('setuphandlers.py')
214        def setupVarious(context):
215    ...
216
217
218Extending theme
219===============
220
221One of the best features, which ZopeSkel package brings, is *localcommand*.
222
223This part shows how you can extend a theme (generated with qplone3_theme
224ZopeSkel template) with additional useful stuff:
225
226  - skin layers
227  - views
228  - viewlets
229  - portlets
230  - css
231  - javascripts
232  - objects in zexp files
233
234So, in qplone3_theme generated package you can use *addcontent* ZopeSkel
235local command.
236
237IMPORTANT TO NOTE: localcommand (addcontent in our case) should be
238called in any subdirectory of the generated theme package. And it won't
239work outside this package..
240
241    >>> paster('addcontent -a')
242    paster addcontent -a
243      ...
244        css_resource:    A Plone 3 CSS resource template
245      ...
246        import_zexps:    A template for importing zexp-objects into portal on installation
247        js_resource:     A Plone 3 JS resource template
248      N portlet:         A Plone 3 portlet
249      ...
250        skin_layer:      A Plone 3 Skin Layer
251      ...
252      N view:            A browser view skeleton
253        viewlet_hidden:  A Plone 3 Hidden Viewlet template
254        viewlet_order:   A Plone 3 Order Viewlet template
255      ...
256
257
258We can see a list of extention subtemplates, which can be used for our theme.
259'N' character tells us that these subtemplates are registered for other (archetype)
260template, but it does not matter - they can correctly extend our theme.
261
262
263Adding SKIN LAYER
264=================
265
266For that case use *skin_layer* subtemplate with *addcontent* local command
267
268    >>> paster('addcontent --no-interactive skin_layer')
269    paster addcontent --no-interactive skin_layer
270    Recursing into profiles
271    ...
272
273This command adds NEW 'skin_layer' (default name) directory to _skins_ directory,
274with only CONTENT.txt file inside.
275
276    >>> 'skin_layer' in os.listdir('skins')
277    True
278    >>> ls('skins/skin_layer')
279    CONTENT.txt
280
281*skins.xml* profile is also updated:
282
283    >>> cat('profiles/default/skins.xml')
284    <?xml version="1.0"?>
285    <object name="portal_skins" allow_any="False" cookie_persistence="False"
286       default_skin="My Theme Name">
287    ...
288     <object name="skin_layer"
289        meta_type="Filesystem Directory View"
290        directory="quintagroup.theme.ploneexample:skins/skin_layer"/>
291    <BLANKLINE>
292     <skin-path name="My Theme Name" based-on="Plone Default">
293    ...
294      <layer name="skin_layer"
295         insert-after="custom"/>
296    <BLANKLINE>
297     </skin-path>
298    ...
299
300We can see, that:
301  - skin_layer directory was registered as Filesystem Directory View
302  - skin_layer Filesystem Directory View was added to our theme layers list
303
304
305Adding PORTLET
306==========================
307
308Only initialization files are available in portlets directory before adding new portlet.
309
310    >>> ls('portlets')
311    __init__.py
312    configure.zcml
313
314Add portlet with *portlet* subtemplate.
315
316    >>> paster('addcontent --no-interactive portlet')
317    paster addcontent --no-interactive portlet
318    Recursing into portlets
319    ...
320
321After executing this local command ...
322
323configure.zcml file in the theme root directory - includes portlets registry:
324
325    >>> cat('configure.zcml')
326    <configure
327    ...
328    <include package=".portlets" />
329    ...
330
331exampleportlet.pt template and exampleportlet.py script added to portlets directory.
332    >>> files = ('exampleportlet.pt', 'exampleportlet.py')
333    >>> [True for d in files if d in os.listdir('portlets')]
334    [True, True]
335
336And portlets/configure.zcml - register new portlet
337    >>> cat('portlets/configure.zcml')
338    <configure
339    ...
340         <plone:portlet
341             name="quintagroup.theme.ploneexample.portlets.ExamplePortlet"
342             interface=".exampleportlet.IExamplePortlet"
343             assignment=".exampleportlet.Assignment"
344             view_permission="zope2.View"
345             edit_permission="cmf.ManagePortal"
346             renderer=".exampleportlet.Renderer"
347             addview=".exampleportlet.AddForm"
348             editview=".exampleportlet.EditForm"
349             />
350    ...
351
352Finally, new portlet type is registered in portlets.xml profile
353
354    >>> cat('profiles/default/portlets.xml')
355    <?xml version="1.0"?>
356    ...
357       <portlet
358         addview="quintagroup.theme.ploneexample.portlets.ExamplePortlet"
359         title="Example portlet"
360         description=""
361       />
362    ...
363
364Thanks to ZopeSkel developers for this subtempalte ;)
365
366
367
368Adding CSS resource
369===================
370
371Use *css_resource* subtemplate.
372
373    >>> paster("addcontent --no-interactive css_resource")
374    paster addcontent --no-interactive css_resource
375    Recursing into browser
376    ...
377    Recursing into profiles
378    ...
379
380This template adds (if does not exist yet) _stylesheets_ directory in _browser_
381directory
382
383    >>> 'stylesheets' in os.listdir('browser')
384    True
385
386In _stylesheets_ resource directory empty main.css stylesheet
387resource added
388
389    >>> 'main.css' in os.listdir('browser/stylesheets')
390    True
391    >>> cat('browser/stylesheets/main.css')
392    <BLANKLINE>
393
394
395New resource directory was registered in configure.zcml
396
397    >>> cat('browser/configure.zcml')
398    <configure
399    ...
400        <browser:resourceDirectory
401            name="quintagroup.theme.ploneexample.stylesheets"
402            directory="stylesheets"
403            layer=".interfaces.IThemeSpecific"
404            />
405    ...
406   
407
408And cssregistry.xml profile was added into profiles/default directory with
409registered main.css stylesheet
410
411    >>> 'cssregistry.xml' in os.listdir('profiles/default')
412    True
413    >>> cat('profiles/default/cssregistry.xml')
414    <?xml version="1.0"?>
415    <object name="portal_css">
416    <BLANKLINE>
417     <stylesheet title=""
418        id="++resource++quintagroup.theme.ploneexample.stylesheets/main.css"
419        media="screen" rel="stylesheet" rendering="inline"
420        cacheable="True" compression="safe" cookable="True"
421        enabled="1" expression=""/>
422    ...
423
424
425
426Adding JAVASCRIPT resource
427--------------------------
428
429Use *js_resource* subtemplate.
430
431    >>> paster('addcontent --no-interactive js_resource')
432    paster addcontent --no-interactive js_resource
433    Recursing into browser
434    ...
435    Recursing into profiles
436    ...
437
438This template adds (if does not exist yet) _scripts_ directory in _browser_
439directory
440
441    >>> 'scripts' in os.listdir('browser')
442    True
443
444
445Empty foo.js javascript file was added to _scripts_ directory
446
447    >>> 'foo.js' in os.listdir('browser/scripts')
448    True
449    >>> cat('browser/scripts/foo.js')
450    <BLANKLINE>
451
452
453New resource directory was registered in configure.zcml, if has not been registered yet.
454
455    >>> cat('browser/configure.zcml')
456    <configure
457    ...
458        <browser:resourceDirectory
459            name="quintagroup.theme.ploneexample.scripts"
460            directory="scripts"
461            layer=".interfaces.IThemeSpecific"
462            />
463    ...
464   
465
466cssregistry.xml profile was added into profiles/default directory (if does not exist yet),
467and register new foo.js javascript resource.
468
469    >>> 'jsregistry.xml' in os.listdir('profiles/default')
470    True
471    >>> cat('profiles/default/jsregistry.xml')
472    <?xml version="1.0"?>
473    <object name="portal_javascripts">
474    ...
475     <javascript
476        id="++resource++quintagroup.theme.ploneexample.scripts/foo.js"
477        inline="False" cacheable="True" compression="safe"
478        cookable="True" enabled="1"
479        expression=""
480        />
481    ...
482
483
484
485Test viewlets subtemplates
486==========================
487
488There are 2 types of viewlet subtemplates:
489 - viewlet_order
490 - viewlet_hidden
491
492The first one is used for adding new viewlets and setting
493viewlets order for the ViewletManager, the second one only hides
494viewlet in pointed ViewletManager.
495
496Ordered NEW viewlet
497-------------------
498
499Use *viewlet_order* subtemplate
500
501    >>> paster('addcontent --no-interactive viewlet_order')
502    paster addcontent --no-interactive viewlet_order
503    Recursing into browser
504    ...
505    Recursing into templates
506    ...
507    Recursing into profiles
508    ...
509
510This template adds (if not exist ;)) _viewlets.py_ module in browser directory.
511With added Example ViewletBase class, which is bound to templates/example_viewlet.pt
512template
513
514    >>> 'viewlets.py' in os.listdir('browser')
515    True
516   
517    >>> cat('browser/viewlets.py')
518    from Products.CMFCore.utils import getToolByName
519    from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
520    from plone.app.layout.viewlets import common
521    ...
522    class Example(common.ViewletBase):
523        render = ViewPageTemplateFile('templates/example_viewlet.pt')
524    <BLANKLINE>
525
526Check template file in templates directory.
527
528    >>> 'example_viewlet.pt' in os.listdir('browser/templates')
529    True
530    >>> cat('browser/templates/example_viewlet.pt')
531    <BLANKLINE>
532
533New viewlet is registered in configure.zcml
534
535    >>> cat('browser/configure.zcml')
536    <configure
537    ...
538       <browser:viewlet
539            name="quintagroup.theme.ploneexample.example"
540            manager="plone.app.layout.viewlets.interfaces.IPortalHeader"
541            class=".viewlets.Example"
542            layer=".interfaces.IThemeSpecific"
543            permission="zope2.View"
544            />
545    ...
546   
547
548viewlets.xml profile is added to profiles/default directory with new viewlet
549registration, ordered for specified viewlet manager.
550
551    >>> 'viewlets.xml' in os.listdir('profiles/default')
552    True
553    >>> cat('profiles/default/viewlets.xml')
554    <?xml version="1.0"?>
555    <object>
556    ...
557     <order manager="plone.portalheader"
558             based-on="Plone Default"
559             skinname="My Theme Name" >
560    ...
561        <viewlet name="quintagroup.theme.ploneexample.example" insert-after="*" />
562    <BLANKLINE>
563      </order>
564    <BLANKLINE>
565    </object>
566
567
568
569Hide EXISTING viewlet
570---------------------
571
572For that case you can use *viewlet_hidden* subtemplate
573
574    >>> paster('addcontent --no-interactive viewlet_hidden')
575    paster addcontent --no-interactive viewlet_hidden
576    Recursing into profiles
577    ...
578
579As we see from upper log - there is stuff for adding/updating profiles only.
580   
581
582There is viewlet.xml profile in profiles/default directory
583which hides viewlet for specified viewlet manager
584
585    >>> 'viewlets.xml' in os.listdir('profiles/default')
586    True
587    >>> cat('profiles/default/viewlets.xml')
588    <?xml version="1.0"?>
589    <object>
590    ...
591      <hidden manager="plone.portalheader" skinname="My Theme Name">
592    ...
593        <viewlet name="example" />
594    <BLANKLINE>
595      </hidden>
596    ...
597    </object>
598
599
600Adding ZEXPs importing
601======================
602
603Imagine situation, when you develop a theme, which uses some
604extra portal objects (documents with text for some potlets)
605Then customer of your theme can edit these objects according
606to his need.
607
608For this situation *import_zexps* subtemplate exists.
609
610*import_zexps* subtemplate extends your theme with
611mechanism for importing list of zexp formated files
612into portal root on theme instllation.
613
614    >>> paster('addcontent --no-interactive import_zexps')
615    paster addcontent --no-interactive import_zexps
616    ...
617    Recursing into import
618    ...
619    Recursing into profiles
620    ...
621    Inserting from setuphandlers.py_insert into ...
622    ...
623
624As we see from the upper log:
625   - 'import' directory was added into root of the theme
626   - profiles stuff was updated
627   - some stuff into setuphandlers.py module was inserted
628   
6291. There was empty 'import' directory added, where you
630   will put zexp objects for install into portal root.
631
632    >>> ls('import')
633    CONTENT.txt
634
635
6362. import_steps.xml was added in profiles/default directory (if does not exist yet),
637   which contains additional *quintagroup.theme.ploneexample.import_zexps* step.
638
639    >>> 'import_steps.xml' in os.listdir('profiles/default')
640    True
641
642    >>> cat('profiles/default/import_steps.xml')
643    <?xml version="1.0"?>
644    <import-steps>
645    ...
646      <import-step id="quintagroup.theme.ploneexample.import_zexps"
647                   version="..."
648                   handler="quintagroup.theme.ploneexample.setuphandlers.importZEXPs"
649                   title="My Theme Name: Import zexps objects">
650        <dependency step="skins" />
651        Import zexp objects into portal on My Theme Name theme installation
652      </import-step>
653    <BLANKLINE>
654    </import-steps>
655
6563. Check setuphandlers.py module - there must be importZEXPs function defined
657
658    >>> cat('setuphandlers.py')
659    def setupVarious(context):
660    ...
661    def importZEXPs(context):
662    ...
663
664Then simply prepare zexp objects and copy them to *import* directory.
665
666
667RELEASE NOTES !
668===============
669
670Before releasing theme - I suggest to clean up setup.py script:
671
672 - remove *theme_vars* argument (its value is useful only for
673   theme development)
674
675 - remove *entry_points* argument (same reason).
676   It's useless in plone for now.
677
678 - And remove *paster_plugins* argument too (it has sence
679   in conjunction with entry_points during theme developing)
680
681Steps mentioned above prevent possible problems with
682theme distribution/deployment.
Note: See TracBrowser for help on using the repository browser.