source: products/qPloneSkinDump/branches/plone_3.0/utils.py @ 1

Last change on this file since 1 was 1, checked in by myroslav, 19 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#    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
80def 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
92def 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
127def 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
162def 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
181def 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
190def 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
221def 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
226def getFSSkinPath(folder, 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" % (PRODUCTS_PATH, 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
239def dumpFolder(folder, fs_product_name):
240    skinpath = getFSSkinPath(folder, 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)
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
269def dumpSkin(context, skin_names=['custom',], fs_product_name='QSkinTemplate', erase_from_skin=0):
270    """Dump custom information to file."""
271    if type(skin_names) not in (type([]), type(())):
272        skin_names = [skin_names,]
273    for skin_name in list(skin_names):
274        folder = getToolByName(context, 'portal_skins')[skin_name]
275        dumpFolder(folder, fs_product_name)
276        # delete objects from the skin, if request
277        if erase_from_skin:
278            folder.manage_delObjects(ids = folder.objectIds())
279
280def fillinFileTemplate(f_path_read, f_path_write=None, dict={}):
281    """ Fillin file template with data from dictionary."""
282    if not f_path_write:
283        f_path_write = f_path_read
284    f_tmpl = open(f_path_read, 'r')
285    tmpl = f_tmpl.read()
286    f_tmpl.close()
287    f_tmpl = open(f_path_write, 'w')
288    try:
289       f_tmpl.write(tmpl % dict)
290    except:
291        raise str(tmpl)
292    f_tmpl.close()
293
294def getResourcesList(directory, resources_list, pattern=CSS_PATTERN):
295    """ Get resources list from 'directory' skin folder."""
296    for o in directory.objectValues():
297        meta_type = o.meta_type
298        id = get_id(o)
299        if meta_type == 'Folder':
300            # very plone specific
301            if id not in ['stylesheet_properties', 'base_properties'] \
302               and not id.startswith('base_properties'):
303                css_list = getResourcesList(o, resources_list, pattern)
304        elif pattern.match(id):
305            resources_list.append( id )
306    return resources_list
307 
308def getResourceProperties(context, regestry_id, prop_list, dflt=''):
309    """ Return list of dictionaries with all dumped resources properties."""
310    properties=[]
311    resource = getToolByName(context, regestry_id, None)
312    if resource:
313        for res in resource.getResources():
314            props = {}
315            for prop in prop_list:
316                accessor = getattr(res, 'get%s' % prop.capitalize(), None)
317                if accessor:
318                    props[prop] = accessor() or dflt
319            properties.append(props)
320    return properties
321
322def getResourceListRegdata(context, subdir, rsrc_pattern, rsrc_name, rsrc_reg_props):
323    rsrc_list = getResourcesList(subdir, resources_list=[], pattern=rsrc_pattern)#---CSS--#000000#aabbcc
324    result_rsrc_list = []
325    [result_rsrc_list.append(item) for item in rsrc_list if item not in result_rsrc_list]
326    skin_css_regdata = getResourceProperties(context, rsrc_name, rsrc_reg_props)   # Get Data from CSS Regestry
327    return result_rsrc_list, skin_css_regdata
328
329def copyDir(srcDirectory, dstDirectory, productName):
330    """Recursive copying from ZMIskin-folder to FS one""" 
331    for item in os.listdir(srcDirectory):
332        src_path = ospJoin(srcDirectory, item)
333        dst_path = ospJoin(dstDirectory, item)
334        if os.path.isfile(src_path):
335            if os.path.exists(dst_path):
336                continue
337            f_sorce = open(src_path,'r')
338            data = f_sorce.read()
339            f_sorce.close()
340            f_dst = open(dst_path,'w')
341            f_dst.write(data)
342            f_dst.close()
343        elif os.path.isdir(src_path):
344            if not os.path.exists(dst_path):
345                os.mkdir(dst_path)
346            copyDir(src_path, dst_path, productName)
347
348def fsDirectoryViewsXML(folder_names, product_name):
349    pattern = """ <object name="%(folder_name)s" meta_type="Filesystem Directory View"
350       directory="Products.%(product_name)s:skins/%(folder_name)s"/>\n"""
351    xml = ''
352    if type(folder_names) not in (type([]), type(())):
353        folder_names = [folder_names,]
354    for name in folder_names:
355       xml += pattern % {'product_name' : product_name, 'folder_name' : name}
356    return xml
357
358def makeNewProduct(context, productName, productSkinName, \
359                   zmi_skin_names, zmi_base_skin_name, subdir,\
360                   doesCustomizeSlots, left_slots, right_slots, slot_forming, main_column, \
361                   doesExportObjects, import_policy, dump_CSS, dump_JS, \
362                   dump_portlets, dump_policy, dump_portlets_selection, dump_custom_views):
363    """Create new skin-product's directory and
364       copy skin-product template with little modification"""
365    products_path = PRODUCTS_PATH
366    productPath = ospJoin(products_path, productName)
367    if not (productName in os.listdir(products_path)):
368        os.mkdir(productPath)
369    files_to_remove = []
370    # Form CSS and JS importing list and regestry data (looking in subdir too) for Plone 2.1.0+
371    stylesheets_xml = ''
372    javascripts_xml = ''
373    #subdir = subdir or getToolByName(context, 'portal_skins')[zmi_skin_name]
374    portal_setup = getToolByName(context, 'portal_setup')
375    result_css_list = skin_css_regdata = result_js_list = skin_js_regdata = []
376    base_context = BaseContext(portal_setup, portal_setup.getEncoding())
377    if dump_CSS:
378        res_reg = getToolByName(context, 'portal_css', None)
379        exporter = zapi.queryMultiAdapter((res_reg, base_context), IBody)
380        if exporter is not None:
381            stylesheets_xml = exporter.body
382        #result_css_list, skin_css_regdata = getResourceListRegdata(context, subdir,
383                                            #CSS_PATTERN, 'portal_css', CSS_REG_PROPS)
384    if stylesheets_xml == '':
385        files_to_remove.append(ospJoin('profiles', 'default', 'cssregistry.xml'))
386    if dump_JS:
387        res_reg = getToolByName(context, 'portal_javascripts', None)
388        exporter = zapi.queryMultiAdapter((res_reg, base_context), IBody)
389        if exporter is not None:
390            javascripts_xml = exporter.body
391        #result_js_list, skin_js_regdata = getResourceListRegdata(context, subdir,
392                                            #JS_PATTERN, 'portal_javascripts', JS_REG_PROPS)
393    if javascripts_xml == '':
394        files_to_remove.append(ospJoin('profiles', 'default', 'jsregistry.xml'))
395
396    slots = ()
397    if dump_portlets:
398        slots = dumpPortlets(context, dump_policy, dump_portlets_selection)
399
400    # Get Slots customization information
401    if not doesCustomizeSlots:
402        left_slots = right_slots = None
403        slot_forming = main_column = None
404
405    # Prepare XML strings for add to skins.xml
406    skin_layers = buildSkinLayers(context, zmi_base_skin_name)
407
408    # dump customized objects from portal_view_customization
409    custom_views = []
410    if dump_custom_views:
411        custom_views = dumpPortalViewCustomization(context)
412
413    # Copy skin_template to SKIN_PRODUCT directory
414    templatePath = ospJoin(products_path, PROJECTNAME, TEMPLATE_PATH)
415    copyDir(templatePath, productPath, productName)
416    # Form data dictionary and form Skin Product's files
417    conf_dict = {"IMPORT_POLICY" : import_policy \
418                ,"GENERATOR_PRODUCT" : PROJECTNAME \
419                ,"SKIN_PRODUCT_NAME" : productName \
420                ,"SKIN_NAME" : productSkinName \
421                ,"BASE_SKIN_NAME" : zmi_base_skin_name \
422                ,"DUMP_CSS": not not dump_CSS \
423                ,"DUMP_JS": not not dump_JS \
424                ,"CSS_LIST" : str(result_css_list) \
425                ,"JS_LIST" : str(result_js_list) \
426                ,"SKIN_CSS_REGDATA" : str(skin_css_regdata) \
427                ,"SKIN_JS_REGDATA" : str(skin_js_regdata) \
428                ,"LEFT_SLOTS" : str(left_slots) \
429                ,"RIGHT_SLOTS" : str(right_slots) \
430                ,"SLOT_FORMING" : slot_forming \
431                ,"MAIN_COLUMN" : main_column \
432                ,"product_name" : productName \
433                ,"skin_name" : productSkinName \
434                ,"skin_name_lowercase" : productSkinName.lower() \
435                ,"skin_name_capital" : '%s%s' % (productSkinName[0].upper(), productSkinName[1:]) \
436                ,"product_name_lowercase" : productName.lower() \
437                ,"viewlets_zcml" : '' \
438                ,"stylesheets_xml" : stylesheets_xml \
439                ,"javascripts_xml" : javascripts_xml \
440                ,"version" : time.strftime('%Y%m%d') \
441                ,"slot_structure" : str(slots) \
442                ,"skin_layers" : skin_layers \
443                ,"custom_views" : str(custom_views) \
444                ,"directory_views_xml" : fsDirectoryViewsXML(zmi_skin_names, productName)}
445    sp_updated_files = ['config.py' \
446                       ,'README.txt' \
447                       ,'setuphandlers.py' \
448                       ,'profiles.zcml' \
449                       ,'utils.py' \
450                       ,'configure.zcml' \
451                       ,'skins.zcml' \
452                       ,ospJoin('browser', 'interfaces.py')\
453                       ,ospJoin('browser', 'viewlets.zcml')\
454                       ,ospJoin('browser', 'configure.zcml')\
455                       ,ospJoin('profiles', 'default', 'import_steps.xml')\
456                       ,ospJoin('profiles', 'default', 'skins.xml')\
457                       ,ospJoin('profiles', 'default', 'cssregistry.xml')\
458                       ,ospJoin('profiles', 'default', 'jsregistry.xml')\
459                       ,ospJoin('browser', 'configure.zcml')
460                       ]
461    for fp in sp_updated_files:
462        fillinFileTemplate(ospJoin(productPath, fp), dict=conf_dict)
463    for fp in files_to_remove:
464        os.remove(ospJoin(productPath,fp))
Note: See TracBrowser for help on using the repository browser.