root/qPloneSkinDump/branches/plone_3.0/utils.py

Revision 1170 (checked in by mylan, 6 months ago)

Fixed bug with importing portlet for single selected content object. Bump up version

  • Property svn:eol-style set to native
Line 
1 import os, re, string, sets, time, types
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         dump_portlets_selection = type(dump_portlets_selection) in types.StringTypes \
214                                       and dump_portlets_selection.split("/") \
215                                       or  dump_portlets_selection
216         for ppath in dump_portlets_selection:
217             obj = portal.restrictedTraverse(ppath)
218             extractPortletsFromContext(obj, slot_structure, typesToShow, context_managers)
219
220     slot_structure.append(('__site-wide-portlets__', extractSiteWidePortlets(portal, managers)))
221
222     return slot_structure
223
224 def buildSkinLayers(context, zmi_base_skin_name):
225     pskins = getToolByName(context,'portal_skins')
226     layers = (pskins.getSkinPath(zmi_base_skin_name) or '').split(',')
227     return "\n".join(['   <layer name="%s"/>' % l for l in layers])
228
229 def getFSSkinPath(folder, fs_dest_directory, fs_product_name):
230     """ Return file system skin path for subdir."""
231
232     folder_path = '/'.join(folder.getPhysicalPath()[list(folder.getPhysicalPath()).index('portal_skins')+1:])
233     skinpath = "%s/%s/skins/%s" % (fs_dest_directory, fs_product_name, folder_path)
234
235     # If in skin's subfolder - get its path
236     #skinp, subp = [obj.getPhysicalPath() for obj in [skin_obj, subdir]]
237     #if len(subp) != len(skinp):
238         ## adapt skinpath for creating directory
239         #skinpath += '/' + '/'.join( subp[len(skinp):] )
240     return skinpath
241
242 def dumpFolder(folder, fs_dest_directory, fs_product_name):
243     skinpath = getFSSkinPath(folder, fs_dest_directory, fs_product_name)
244     # Create directory in FS if not yet exist
245     if not os.path.exists(skinpath):
246         os.makedirs(skinpath)
247     # Loop of copying content from ZMIskin-folder to FSskin-folder
248     obj_meta = {}
249     for o in folder.objectValues():
250         meta_type = o.meta_type
251         id = get_id(o)
252         if meta_type in _acceptable_meta_types:
253             # Adding to .objects all acceptable meta_types.
254             # Fixing bug of id-meta_type confusing.
255             obj_meta[id] = meta_type
256         if meta_type == 'Folder':
257             # very plone specific
258             if id in ['stylesheet_properties', 'base_properties'] or id.startswith('base_properties'):
259                 writeProps(o, skinpath, extension = '.props')
260             else:
261                 dumpFolder(o, fs_product_name, fs_product_name)
262         elif meta_type in _write_custom_meta_type_list:
263             #writeProps( o, skinpath )      # write object's properties
264             # extract content from object(depend on metatype) and write it to the file
265             writeFileContent(o, skinpath, getData(o, meta_type))
266         else:
267             print 'method ignoring ', meta_type
268     # write '.objects' file to directory if present objects with id without extension
269     if obj_meta :
270         writeObjectsMeta(obj_meta, skinpath)
271
272 def dumpSkin(context, skin_names=['custom',], fs_dest_directory=PRODUCTS_PATH,
273              fs_product_name='QSkinTemplate', erase_from_skin=0):
274     """Dump custom information to file."""
275     if type(skin_names) not in (type([]), type(())):
276         skin_names = [skin_names,]
277     for skin_name in list(skin_names):
278         folder = getToolByName(context, 'portal_skins')[skin_name]
279         dumpFolder(folder, fs_dest_directory, fs_product_name)
280         # delete objects from the skin, if request
281         if erase_from_skin:
282             folder.manage_delObjects(ids = folder.objectIds())
283
284 def fillinFileTemplate(f_path_read, f_path_write=None, dict={}):
285     """ Fillin file template with data from dictionary."""
286     if not f_path_write:
287         f_path_write = f_path_read
288     f_tmpl = open(f_path_read, 'r')
289     tmpl = f_tmpl.read()
290     f_tmpl.close()
291     f_tmpl = open(f_path_write, 'w')
292     try:
293        f_tmpl.write(tmpl % dict)
294     except:
295         raise str(tmpl)
296     f_tmpl.close()
297
298 def getResourcesList(directory, resources_list, pattern=CSS_PATTERN):
299     """ Get resources list from 'directory' skin folder."""
300     for o in directory.objectValues():
301         meta_type = o.meta_type
302         id = get_id(o)
303         if meta_type == 'Folder':
304             # very plone specific
305             if id not in ['stylesheet_properties', 'base_properties'] \
306                and not id.startswith('base_properties'):
307                 css_list = getResourcesList(o, resources_list, pattern)
308         elif pattern.match(id):
309             resources_list.append( id )
310     return resources_list
311  
312 def getResourceProperties(context, regestry_id, prop_list, dflt=''):
313     """ Return list of dictionaries with all dumped resources properties."""
314     properties=[]
315     resource = getToolByName(context, regestry_id, None)
316     if resource:
317         for res in resource.getResources():
318             props = {}
319             for prop in prop_list:
320                 accessor = getattr(res, 'get%s' % prop.capitalize(), None)
321                 if accessor:
322                     props[prop] = accessor() or dflt
323             properties.append(props)
324     return properties
325
326 def getResourceListRegdata(context, subdir, rsrc_pattern, rsrc_name, rsrc_reg_props):
327     rsrc_list = getResourcesList(subdir, resources_list=[], pattern=rsrc_pattern)#---CSS--#000000#aabbcc
328     result_rsrc_list = []
329     [result_rsrc_list.append(item) for item in rsrc_list if item not in result_rsrc_list]
330     skin_css_regdata = getResourceProperties(context, rsrc_name, rsrc_reg_props)   # Get Data from CSS Regestry
331     return result_rsrc_list, skin_css_regdata
332
333 def copyDir(srcDirectory, dstDirectory, productName):
334     """Recursive copying from ZMIskin-folder to FS one"""
335     for item in os.listdir(srcDirectory):
336         src_path = ospJoin(srcDirectory, item)
337         dst_path = ospJoin(dstDirectory, item)
338         if os.path.isfile(src_path):
339             if os.path.exists(dst_path):
340                 continue
341             f_sorce = open(src_path,'r')
342             data = f_sorce.read()
343             f_sorce.close()
344             f_dst = open(dst_path,'w')
345             f_dst.write(data)
346             f_dst.close()
347         elif os.path.isdir(src_path) \
348              and not ".svn" in src_path:
349             if not os.path.exists(dst_path):
350                 os.mkdir(dst_path)
351             copyDir(src_path, dst_path, productName)
352
353 def fsDirectoryViewsXML(folder_names, product_name, remove=False):
354     pattern = """ <object name="%(folder_name)s" meta_type="Filesystem Directory View"
355        directory="Products.%(product_name)s:skins/%(folder_name)s" %(remove)s/>\n"""
356     xml = ''
357     if type(folder_names) not in (type([]), type(())):
358         folder_names = [folder_names,]
359     for name in folder_names:
360        xml += pattern % {'product_name' : product_name, \
361                          'folder_name'  : name, \
362                          'remove'       : remove and 'remove="True"' or '', \
363                         }
364     return xml
365
366 def makeNewProduct(context, destinationDir, productName, productSkinName, \
367                    zmi_skin_names, zmi_base_skin_name, subdir,\
368                    doesCustomizeSlots, left_slots, right_slots, slot_forming, main_column, \
369                    doesExportObjects, import_policy, dump_CSS, dump_JS, \
370                    dump_portlets, dump_policy, dump_portlets_selection, dump_custom_views):
371     """Create new skin-product's directory and
372        copy skin-product template with little modification"""
373     products_path = destinationDir
374     productPath = ospJoin(products_path, productName)
375     if not (productName in os.listdir(products_path)):
376         os.mkdir(productPath)
377     files_to_remove = []
378     files_to_add    = []
379     # Form CSS and JS importing list and regestry data (looking in subdir too) for Plone 2.1.0+
380     stylesheets_xml = ''
381     javascripts_xml = ''
382     #subdir = subdir or getToolByName(context, 'portal_skins')[zmi_skin_name]
383     portal_setup = getToolByName(context, 'portal_setup')
384     result_css_list = skin_css_regdata = result_js_list = skin_js_regdata = []
385     base_context = BaseContext(portal_setup, portal_setup.getEncoding())
386     if dump_CSS:
387         res_reg = getToolByName(context, 'portal_css', None)
388         exporter = zapi.queryMultiAdapter((res_reg, base_context), IBody)
389         if exporter is not None:
390             stylesheets_xml = exporter.body
391         #result_css_list, skin_css_regdata = getResourceListRegdata(context, subdir,
392                                             #CSS_PATTERN, 'portal_css', CSS_REG_PROPS)
393     if stylesheets_xml == '':
394         files_to_remove.append(ospJoin('profiles', 'default', 'cssregistry.xml'))
395     if dump_JS:
396         res_reg = getToolByName(context, 'portal_javascripts', None)
397         exporter = zapi.queryMultiAdapter((res_reg, base_context), IBody)
398         if exporter is not None:
399             javascripts_xml = exporter.body
400         #result_js_list, skin_js_regdata = getResourceListRegdata(context, subdir,
401                                             #JS_PATTERN, 'portal_javascripts', JS_REG_PROPS)
402     if javascripts_xml == '':
403         files_to_remove.append(ospJoin('profiles', 'default', 'jsregistry.xml'))
404
405     slots = ()
406     if dump_portlets:
407         slots = dumpPortlets(context, dump_policy, dump_portlets_selection)
408
409     # Get Slots customization information
410     if not doesCustomizeSlots:
411         left_slots = right_slots = None
412         slot_forming = main_column = None
413
414     # Prepare XML strings for add to skins.xml
415     skin_layers = buildSkinLayers(context, zmi_base_skin_name)
416
417     # Prepare profiles
418     default_marker, afterinstall_marker, uninstall_marker = {}, {}, {}
419     profiles = ['default', 'afterinstall', 'uninstall']
420     profiles_path = ospJoin(products_path, productName, 'profiles')
421     for profile in profiles:
422         varname = "%s_marker" % profile
423         file_name = "%s_%s.txt" % (productName.lower(), profile)
424         locals()[varname].update({'fname' : file_name, \
425                                   'fpath' : ospJoin(profiles_path</