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

Last change on this file was 3050, checked in by mylan, 13 years ago

#238: Added comment about reaason of usage '--overwrite' option

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