root/qPloneSkinDump/trunk/utils.py

Revision 1113 (checked in by mylan, 7 months ago)

Merged trunk with branches/plone_3.0 version

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