root/qPloneSkinDump/branches/pastescript/utils.py

Revision 1010 (checked in by piv, 1 year ago)

portlets dump bug fixes and improvements

  • Property svn:eol-style set to native
Line 
1 import os, re, string, sets, time
2 from zope.app import zapi
3 from App.config import getConfiguration
4 from Products.CMFCore.utils import getToolByName
5
6 from config import *
7 from write_utils import writeProps, writeFileContent, writeObjectsMeta
8
9 from zope.interface import providedBy
10 from zope.schema import getFields
11 from zope.component import getMultiAdapter, getUtility, getSiteManager
12 from zope.publisher.interfaces.browser import IBrowserRequest
13 from five.customerize.interfaces import IViewTemplateContainer, ITTWViewTemplate
14 from plone.portlets.interfaces import IPortletAssignmentMapping, IPortletManager, IPlacelessPortletManager
15 from plone.portlets.interfaces import IPortletContext, IPortletDataProvider
16 from plone.portlets.interfaces import ILocalPortletAssignmentManager
17 from plone.app.customerize.registration import generateIdFromRegistration, interfaceName
18
19 from Products.GenericSetup.utils import _getDottedName, _resolveDottedName
20 from Products.GenericSetup.interfaces import IBody
21 from Products.GenericSetup.context import BaseContext
22
23 CSS_PATTERN = re.compile("^.+\.css$")
24 JS_PATTERN = re.compile("^.+\.js$")
25 _write_custom_meta_type_list = [
26     'Controller Page Template',
27     'Controller Python Script',
28     'Controller Validator',
29     'DTML Method',
30     'File',
31     'Image',
32     'Page Template',
33     'Script (Python)' ]
34 _acceptable_meta_types = _write_custom_meta_type_list + ['Folder',]
35 ospJoin = os.path.join
36
37 def get_product_listdirs():
38     """ Return a contents of all plugged in Products directories."""
39     products = sets.Set()
40     [products.update(os.listdir(product_dir)) for product_dir in Products.__path__]
41     return products
42
43 def get_id(obj):
44     """ Get real object's id."""
45     id = callable(obj.id) and obj.id() or obj.id
46     assert obj.getId() == id, "expected identical ids: '%s' != '%s'" % (obj.getId(), id)
47     return id
48
49 def getData(obj, meta_type):
50     """ Return object's data."""
51     return meta_type in ['Image', 'File'] and obj.manage_FTPget() or obj.document_src()
52
53 def dumpPortalViewCustomization(context):
54     result = []
55
56     components = getSiteManager(context)
57     localregs = [reg for reg in components.registeredAdapters() if (len(reg.required) in (2, 4, 5) and
58                                                                      reg.required[1].isOrExtends(IBrowserRequest) and
59                                                                      ITTWViewTemplate.providedBy(reg.factory))]
60     container = getUtility(IViewTemplateContainer)
61     for lreg in localregs:
62         ttw_id = generateIdFromRegistration(lreg)
63         if ttw_id not in container.objectIds():
64             continue
65         ttw = getattr(container, ttw_id)
66         ttw_info = {'for_name'  : interfaceName(lreg.required[0]),
67                     'type_name' : interfaceName(lreg.required[-1]),
68                     'view_name' : lreg.name,
69                     'kwargs'    : {'text' : ttw._text,
70                                    'content_type' : ttw.content_type,
71                                    'encoding' : ttw.output_encoding,
72                                    }}
73         result.append(ttw_info)
74
75     return result
76
77 def extractInfoFromAssignment(name, assignment):
78     klass = assignment.__class__
79     a = {'name' : name, 'class' : '%s' % _getDottedName(klass)}
80     data = assignment.data
81     kwargs = {}
82     for i in list(providedBy(data)):
83         if i.isOrExtends(IPortletDataProvider):
84             for field_name, field in getFields(i).items():
85                 kwargs[field_name] = field.get(assignment)
86     a['kwargs'] = kwargs
87     return a
88
89 def extractSiteWidePortlets(context, managers):
90     """ Extract site-wide portlets
91         Data structure:
92             '__site-wide-portlets__', [(<manager1_name>, <manager1_info>),
93                                        (<manager2_name>, <manager2_info>),
94                                        (<manager3_name>, <manager3_info>)])
95             <manager_info>:
96                 {'category1' : <catmapping1>,
97                  'category2' : <catmapping2>}
98             <catmapping>:
99                 {'key1' : <mapping1>,
100                  'key2' : <mapping2>}
101             <mapping>:
102                 {'assignment_name1' : <assignment1>,
103                  'assignment_name2' : <assignment2>}
104             <assignment>:
105                 {'name'   : 'Assignment',
106                  'class'  : 'dotted.path.to.assignment.class',
107                  'kwargs' : {'parameter1' : 'value1',
108                              'parameter2' : 'value2'}
109     """
110     info = []
111     for manager_name, manager in managers:
112         manager_info = {}
113         for category, catmapping in manager.items():
114             catmapping_info = {}
115             for key, mapping in catmapping.items():
116                 mapping_info = {}
117                 for name, assignment in mapping.items():
118                     mapping_info[name] = extractInfoFromAssignment(name, assignment)
119                 catmapping_info[key] = mapping_info
120             manager_info[category] = catmapping_info
121         info.append((manager_name, manager_info))
122     return info
123
124 def extractContextPortletsFromManager(context, manager):
125     """ Extract all contextual portlets from given object and portlet manager, and portlets blacklists
126         Data structure:
127         <manager_info> =
128             {'blacklists'  : [(GROUP_CATEGORY, True),
129                               (CONTENT_TYPE_CATEGORY, False),
130                               (CONTEXT_CATEGORY, None)],
131              'assignments' : [{'name'   : 'Assignment-2',
132                                'class'  : 'dotted.path.to.assignment.class',
133                                'kwargs' : {'parameter1' : 'value1',
134                                           'parameter2' : 'value2'}},
135                               {'name'   : 'Assignment',
136                                'class'  : 'dotted.path.to.assignment.class',
137                                'kwargs' : {'parameter1' : 'value1',
138                                             'parameter2' : 'value2'}]}
139     """
140
141     info = {}
142     info['assignments'] = assignments = []
143     info['blacklists'] = blacklists = []
144
145     # Extract contextual portlets
146     mapping = getMultiAdapter((context, manager), IPortletAssignmentMapping, context=context)
147     for name, assignment in mapping.items():
148         assignments.append(extractInfoFromAssignment(name, assignment))
149
150     # Extract blacklists for given object and manager
151     localassignmentmanager = getMultiAdapter((context, manager), ILocalPortletAssignmentManager)
152     blacklist = localassignmentmanager._getBlacklist()
153     if blacklist is not None:
154         for category, key in blacklist.items():
155             blacklists.append((category, key))
156
157     return info
158
159 def extractPortletsFromContext(context, slot_structure, typesToShow, managers):
160     """ Extract portlets for given object assigned through all portlet managers.
161         Data structure:
162             ('unique/path/to/context', [(<manager1_name>, <manager1_info>),
163                                         (<manager2_name>, <manager2_info>),
164                                         (<manager3_name>, <manager3_info>)])
165     """
166
167
168     info = []
169     key = '/'.join(context.getPhysicalPath()[2:])
170
171     for name, manager in managers:
172         info.append((name, extractContextPortletsFromManager(context, manager)))
173
174     slot_structure.append((key, info))
175
176     return slot_structure
177
178 def dumpAllPortlets(context, slot_structure, typesToShow, managers):
179     extractPortletsFromContext(context, slot_structure, typesToShow, managers)
180     if getattr(context.aq_base, 'isPrincipiaFolderish', 0):
181         for id, obj in context.contentItems():
182             if obj.portal_type in typesToShow:
183                 dumpAllPortlets(obj, slot_structure, typesToShow, managers)
184
185     return slot_structure
186
187 def dumpPortlets(context, dump_policy, dump_portlets_selection):
188     """ Extract portlets from given set of objects and site-wide portlets too.
189         Data structure:
190             SLOT_STRUCTURE =
191                 [(), (), ()]
192     """
193
194     portal = getToolByName(context, 'portal_url').getPortalObject()
195     portal_state = getMultiAdapter((portal, context.REQUEST), name=u'plone_portal_state')
196     typesToShow = portal_state.friendly_types()
197
198     components = getSiteManager(context)
199     managers = [r for r in components.registeredUtilities() if r.provided.isOrExtends(IPortletManager)]
200     context_managers = [(m.name, getUtility(IPortletManager, name=m.name, context=context)) for m in managers
201                                                                                if not IPlacelessPortletManager.providedBy(m.component)]
202     managers = [(m.name, getUtility(IPortletManager, name=m.name, context=context)) for m in managers]
203
204     slot_structure = []
205     if dump_policy == 'root':
206         extractPortletsFromContext(portal, slot_structure, typesToShow, context_managers)
207     elif dump_policy == 'all':
208         dumpAllPortlets(portal, slot_structure, typesToShow, context_managers)
209     elif dump_policy == 'selection':
210         for ppath in dump_portlets_selection:
211             obj = portal.restrictedTraverse(ppath)
212             extractPortletsFromContext(obj, slot_structure, typesToShow, context_managers)
213
214     slot_structure.append(('__site-wide-portlets__', extractSiteWidePortlets(portal, managers)))
215
216     return slot_structure
217
218 def buildSkinLayers(context, zmi_base_skin_name):
219     pskins = getToolByName(context,'portal_skins')
220     layers = (pskins.getSkinPath(zmi_base_skin_name) or '').split(',')
221     return "\n".join(['   <layer name="%s"/>' % l for l in layers])
222
223 def getFSSkinPath(folder, fs_product_name):
224     """ Return file system skin path for subdir."""
225
226     folder_path = '/'.join(folder.getPhysicalPath()[list(folder.getPhysicalPath()).index('portal_skins')+1:])
227     skinpath = "%s/%s/skins/%s" % (PRODUCTS_PATH, fs_product_name, folder_path)
228
229     # If in skin's subfolder - get its path
230     #skinp, subp = [obj.getPhysicalPath() for obj in [skin_obj, subdir]]
231     #if len(subp) != len(skinp):
232         ## adapt skinpath for creating directory
233         #skinpath += '/' + '/'.join( subp[len(skinp):] )
234     return skinpath
235
236 def dumpFolder(folder, fs_product_name):
237     skinpath = getFSSkinPath(folder, fs_product_name)
238     # Create directory in FS if not yet exist
239     if not os.path.exists(skinpath):
240         os.makedirs(skinpath)
241     # Loop of copying content from ZMIskin-folder to FSskin-folder
242     obj_meta = {}
243     for o in folder.objectValues():
244         meta_type = o.meta_type
245         id = get_id(o)
246         if meta_type in _acceptable_meta_types:
247             # Adding to .objects all acceptable meta_types.
248             # Fixing bug of id-meta_type confusing.
249             obj_meta[id] = meta_type
250         if meta_type == 'Folder':
251             # very plone specific
252             if id in ['stylesheet_properties', 'base_properties'] or id.startswith('base_properties'):
253                 writeProps(o, skinpath, extension = '.props')
254             else:
255                 dumpFolder(o, fs_product_name)
256         elif meta_type in _write_custom_meta_type_list:
257             #writeProps( o, skinpath )      # write object's properties
258             # extract content from object(depend on metatype) and write it to the file
259             writeFileContent(o, skinpath, getData(o, meta_type))
260         else:
261             print 'method ignoring ', meta_type
262     # write '.objects' file to directory if present objects with id without extension
263     if obj_meta :
264         writeObjectsMeta(obj_meta, skinpath)
265
266 def dumpSkin(context, skin_names=['custom',], fs_product_name='QSkinTemplate', erase_from_skin=0):
267     """Dump custom information to file."""
268     if type(skin_names) not in (type([]), type(())):
269         skin_names = [skin_names,]
270     for skin_name in list(skin_names):
271         folder = getToolByName(context, 'portal_skins')[skin_name]
272         dumpFolder(folder, fs_product_name)
273         # delete objects from the skin, if request
274         if erase_from_skin:
275             folder.manage_delObjects(ids = folder.objectIds())
276
277 def fillinFileTemplate(f_path_read, f_path_write=None, dict={}):
278     """ Fillin file template with data from dictionary."""
279     if not f_path_write:
280         f_path_write = f_path_read
281     f_tmpl = open(f_path_read, 'r')
282     tmpl = f_tmpl.read()
283     f_tmpl.close()
284     f_tmpl = open(f_path_write, 'w')
285     try:
286        f_tmpl.write(tmpl % dict)
287     except:
288         raise str(tmpl)
289     f_tmpl.close()
290
291 def getResourcesList(directory, resources_list, pattern=CSS_PATTERN):
292     """ Get resources list from 'directory' skin folder."""
293     for o in directory.objectValues():
294         meta_type = o.meta_type
295         id = get_id(o)
296         if meta_type == 'Folder':
297             # very plone specific
298             if id not in ['stylesheet_properties', 'base_properties'] \
299                and not id.startswith('base_properties'):
300                 css_list = getResourcesList(o, resources_list, pattern)
301         elif pattern.match(id):
302             resources_list.append( id )
303     return resources_list
304  
305 def getResourceProperties(context, regestry_id, prop_list, dflt=''):
306     """ Return list of dictionaries with all dumped resources properties."""
307     properties=[]
308     resource = getToolByName(context, regestry_id, None)
309     if resource:
310         for res in resource.getResources():
311             props = {}
312             for prop in prop_list:
313                 accessor = getattr(res, 'get%s' % prop.capitalize(), None)
314                 if accessor:
315                     props[prop] = accessor() or dflt
316             properties.append(props)
317     return properties
318
319 def getResourceListRegdata(context, subdir, rsrc_pattern, rsrc_name, rsrc_reg_props):
320     rsrc_list = getResourcesList(subdir, resources_list=[], pattern=rsrc_pattern)#---CSS--#000000#aabbcc
321     result_rsrc_list = []
322     [result_rsrc_list.append(item) for item in rsrc_list if item not in result_rsrc_list]
323     skin_css_regdata = getResourceProperties(context, rsrc_name, rsrc_reg_props)   # Get Data from CSS Regestry
324     return result_rsrc_list, skin_css_regdata
325
326 def copyDir(srcDirectory, dstDirectory, productName):
327     """Recursive copying from ZMIskin-folder to FS one"""
328     for item in os.listdir(srcDirectory):
329         src_path = ospJoin(srcDirectory, item)
330         dst_path = ospJoin(dstDirectory, item)
331         if os.path.isfile(src_path):
332             if os.path.exists(dst_path):
333                 continue
334             f_sorce = open(src_path,'r')
335             data = f_sorce.read()
336             f_sorce.close()
337             f_dst = open(dst_path,'w')
338             f_dst.write(data)
339             f_dst.close()
340         elif os.path.isdir(src_path):
341             if not os.path.exists(dst_path):
342                 os.mkdir(dst_path)
343             copyDir(src_path, dst_path, productName)
344
345 def fsDirectoryViewsXML(folder_names, product_name):
346     pattern = """ <object name="%(folder_name)s" meta_type="Filesystem Directory View"
347        directory="Products.%(product_name)s:skins/%(folder_name)s"/>\n"""
348     xml = ''
349     if type(folder_names) not in (type([]), type(())):
350         folder_names = [folder_names,]
351     for name in folder_names:
352        xml += pattern % {'product_name' : product_name, 'folder_name' : name}
353     return xml
354
355 def makeNewProduct(context, productName, productSkinName, \
356                    zmi_skin_names, zmi_base_skin_name, subdir,\
357                    doesCustomizeSlots, left_slots, right_slots, slot_forming, main_column, \
358                    doesExportObjects, import_policy, dump_CSS, dump_JS, \
359                    dump_portlets, dump_policy, dump_portlets_selection, dump_custom_views):
360     """Create new skin-product's directory and
361        copy skin-product template with little modification"""
362     products_path = PRODUCTS_PATH
363     productPath = ospJoin(products_path, productName)
364     if not (productName in os.listdir(products_path)):
365         os.mkdir(productPath)
366     files_to_remove = []
367     # Form CSS and JS importing list and regestry data (looking in subdir too) for Plone 2.1.0+
368     stylesheets_xml = ''
369     javascripts_xml = ''
370     #subdir = subdir or getToolByName(context, 'portal_skins')[zmi_skin_name]
371     portal_setup = getToolByName(context, 'portal_setup')
372     result_css_list = skin_css_regdata = result_js_list = skin_js_regdata = []
373     base_context = BaseContext(portal_setup, portal_setup.getEncoding())
374     if dump_CSS:
375         res_reg = getToolByName(context, 'portal_css', None)
376         exporter = zapi.queryMultiAdapter((res_reg, base_context), IBody)
377         if exporter is not None:
378             stylesheets_xml = exporter.body
379         #result_css_list, skin_css_regdata = getResourceListRegdata(context, subdir,
380                                             #CSS_PATTERN, 'portal_css', CSS_REG_PROPS)
381     if stylesheets_xml == '':
382         files_to_remove.append(ospJoin('profiles', 'default', 'cssregistry.xml'))
383     if dump_JS:
384         res_reg = getToolByName(context, 'portal_javascripts', None)
385         exporter = zapi.queryMultiAdapter((res_reg, base_context), IBody)
386         if exporter is not None:
387             javascripts_xml = exporter.body
388         #result_js_list, skin_js_regdata = getResourceListRegdata(context, subdir,
389                                             #JS_PATTERN, 'portal_javascripts', JS_REG_PROPS)
390     if javascripts_xml == '':
391         files_to_remove.append(ospJoin('profiles', 'default', 'jsregistry.xml'))
392
393     slots = ()
394     if dump_portlets:
395         slots = dumpPortlets(context, dump_policy, dump_portlets_selection)
396
397     # Get Slots customization information
398     if not doesCustomizeSlots:
399         left_slots = right_slots = None
400         slot_forming = main_column = None
401
402     # Prepare XML strings for add to skins.xml
403     skin_layers = buildSkinLayers(context, zmi_base_skin_name)
404
405     # dump customized objects from portal_view_customization
406     custom_views = []
407     if dump_custom_views:
408         custom_views = dumpPortalViewCustomization(context)
409
410     # Copy skin_template to SKIN_PRODUCT directory
411     templatePath = ospJoin(products_path, PROJECTNAME, TEMPLATE_PATH)
412     copyDir(templatePath, productPath, productName)
413     # Form data dictionary and form Skin Product's files
414     conf_dict = {"IMPORT_POLICY" : import_policy \
415                 ,"GENERATOR_PRODUCT" : PROJECTNAME \
416                 ,"SKIN_PRODUCT_NAME" : productName \
417                 ,"SKIN_NAME" : productSkinName \
418                 ,"BASE_SKIN_NAME" : zmi_base_skin_name \
419                 ,"DUMP_CSS": not not dump_CSS \
420                 ,"DUMP_JS": not not dump_JS \
421                 ,"CSS_LIST" : str(result_css_list) \
422                 ,"JS_LIST" : str(result_js_list) \
423                 ,"SKIN_CSS_REGDATA" : str(skin_css_regdata) \
424                 ,"SKIN_JS_REGDATA" : str(skin_js_regdata) \
425                 ,"LEFT_SLOTS" : str(left_slots) \
426                 ,"RIGHT_SLOTS" : str(right_slots) \
427                 ,"SLOT_FORMING" : slot_forming \
428                 ,"MAIN_COLUMN" : main_column \
429                 ,"product_name" : productName \
430