source: products/qPloneSkinDump/branches/pastescript/utils.py @ 1552

Last change on this file since 1552 was 1, checked in by myroslav, 18 years ago

Building directory structure

  • Property svn:eol-style set to native
File size: 20.3 KB
Line 
1import os, re, string, sets, time
2from zope.app import zapi
3from App.config import getConfiguration
4from Products.CMFCore.utils import getToolByName
5
6from config import *
7from write_utils import writeProps, writeFileContent, writeObjectsMeta
8
9from zope.interface import providedBy
10from zope.schema import getFields
11from zope.component import getMultiAdapter, getUtility, getSiteManager
12from zope.publisher.interfaces.browser import IBrowserRequest
13from five.customerize.interfaces import IViewTemplateContainer, ITTWViewTemplate
14from plone.portlets.interfaces import IPortletAssignmentMapping, IPortletManager, IPlacelessPortletManager
15from plone.portlets.interfaces import IPortletContext, IPortletDataProvider
16from plone.portlets.interfaces import ILocalPortletAssignmentManager
17from plone.app.customerize.registration import generateIdFromRegistration, interfaceName
18
19from Products.GenericSetup.utils import _getDottedName, _resolveDottedName
20from Products.GenericSetup.interfaces import IBody
21from Products.GenericSetup.context import BaseContext
22
23CSS_PATTERN = re.compile("^.+\.css$")
24JS_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',]
35ospJoin = os.path.join
36
37def 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
43def 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
49def 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
53def 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
77def 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
89def 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
124def 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
159def 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
178def 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
187def 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
218def 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
223def 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
236def 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
266def 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
277def 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
291def 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 
305def 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
319def 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
326def 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
345def 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
355def 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                ,"skin_name" : productSkinName \
431                ,"skin_name_lowercase" : productSkinName.lower() \
432                ,"skin_name_capital" : '%s%s' % (productSkinName[0].upper(), productSkinName[1:]) \
433                ,"product_name_lowercase" : productName.lower() \
434                ,"viewlets_zcml" : '' \
435                ,"stylesheets_xml" : stylesheets_xml \
436                ,"javascripts_xml" : javascripts_xml \
437                ,"version" : time.strftime('%Y%m%d') \
438                ,"slot_structure" : str(slots) \
439                ,"skin_layers" : skin_layers \
440                ,"custom_views" : str(custom_views) \
441                ,"directory_views_xml" : fsDirectoryViewsXML(zmi_skin_names, productName)}
442    sp_updated_files = ['config.py' \
443                       ,'README.txt' \
444                       ,'setuphandlers.py' \
445                       ,'profiles.zcml' \
446                       ,'utils.py' \
447                       ,'configure.zcml' \
448                       ,'skins.zcml' \
449                       ,ospJoin('browser', 'interfaces.py')\
450                       ,ospJoin('browser', 'viewlets.zcml')\
451                       ,ospJoin('browser', 'configure.zcml')\
452                       ,ospJoin('profiles', 'default', 'import_steps.xml')\
453                       ,ospJoin('profiles', 'default', 'skins.xml')\
454                       ,ospJoin('profiles', 'default', 'cssregistry.xml')\
455                       ,ospJoin('profiles', 'default', 'jsregistry.xml')\
456                       ,ospJoin('browser', 'configure.zcml')
457                       ]
458    for fp in sp_updated_files:
459        fillinFileTemplate(ospJoin(productPath, fp), dict=conf_dict)
460    for fp in files_to_remove:
461        os.remove(ospJoin(productPath,fp))
Note: See TracBrowser for help on using the repository browser.