source: products/quintagroup.themetemplate/tags/0.11/quintagroup/themetemplate/README.txt @ 1023

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

README.txt unnecessary lines deleted.

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