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

Last change on this file since 1013 was 1013, checked in by olha, 16 years ago

README.txt modified, info about Quintagroup themes added.

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