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

Last change on this file since 1591 was 131, checked in by wald, 18 years ago

configlet up link text fixed

  • Property svn:eol-style set to native
File size: 22.7 KB
Line 
1import os, re, string, sets, time, types
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        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
224def 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
229def 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
242def 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
272def 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
284def 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
298def 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 
312def 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
326def 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
333def 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
353def 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
366def 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, profile, file_name), \
426                                  'fdata' : "# Marker file for %s profile of %s skin" % \
427                                            (profile, productName) })
428        files_to_add.append(locals()[varname])
429
430    # dump customized objects from portal_view_customization
431    custom_views = []
432    if dump_custom_views:
433        custom_views = dumpPortalViewCustomization(context)
434
435    # Copy skin_template to SKIN_PRODUCT directory
436    templatePath = ospJoin(PRODUCTS_PATH, PROJECTNAME, TEMPLATE_PATH)
437    copyDir(templatePath, productPath, productName)
438    # Form data dictionary and form Skin Product's files
439    conf_dict = {"IMPORT_POLICY" : import_policy \
440                ,"GENERATOR_PRODUCT" : PROJECTNAME \
441                ,"SKIN_PRODUCT_NAME" : productName \
442                ,"SKIN_NAME" : productSkinName \
443                ,"BASE_SKIN_NAME" : zmi_base_skin_name \
444                ,"DUMP_CSS": not not dump_CSS \
445                ,"DUMP_JS": not not dump_JS \
446                ,"CSS_LIST" : str(result_css_list) \
447                ,"JS_LIST" : str(result_js_list) \
448                ,"SKIN_CSS_REGDATA" : str(skin_css_regdata) \
449                ,"SKIN_JS_REGDATA" : str(skin_js_regdata) \
450                ,"LEFT_SLOTS" : str(left_slots) \
451                ,"RIGHT_SLOTS" : str(right_slots) \
452                ,"SLOT_FORMING" : slot_forming \
453                ,"MAIN_COLUMN" : main_column \
454                ,"product_name" : productName \
455                ,"skin_name" : productSkinName \
456                ,"skin_name_lowercase" : productSkinName.lower() \
457                ,"skin_name_capital" : '%s%s' % (productSkinName[0].upper(), productSkinName[1:]) \
458                ,"product_name_lowercase" : productName.lower() \
459                ,"viewlets_zcml" : '' \
460                ,"stylesheets_xml" : stylesheets_xml \
461                ,"javascripts_xml" : javascripts_xml \
462                ,"version" : time.strftime('%Y%m%d') \
463                ,"slot_structure" : str(slots) \
464                ,"skin_layers" : skin_layers \
465                ,"custom_views" : str(custom_views) \
466                ,"directory_views_xml" : fsDirectoryViewsXML(zmi_skin_names, productName) \
467                ,"remove_directory_views_xml" : fsDirectoryViewsXML(zmi_skin_names, productName, remove=True) \
468                ,"creation_date" : time.strftime('%d/%m/%Y') \
469                ,"install_profile_marker" : default_marker['fname'] \
470                ,"afterinstall_profile_marker" : afterinstall_marker['fname'] \
471                ,"uninstall_profile_marker" : uninstall_marker['fname'] \
472    }
473    sp_updated_files = ['config.py' \
474                       ,'README.txt' \
475                       ,'HISTORY.txt' \
476                       ,'setuphandlers.py' \
477                       ,'uninstallhandlers.py' \
478                       ,'profiles.zcml' \
479                       ,'utils.py' \
480                       ,'configure.zcml' \
481                       ,'skins.zcml' \
482                       ,ospJoin('Extensions', 'Install.py')\
483                       ,ospJoin('browser', 'interfaces.py')\
484                       ,ospJoin('browser', 'viewlets.zcml')\
485                       ,ospJoin('browser', 'configure.zcml')\
486                       ,ospJoin('profiles', 'default', 'skins.xml')\
487                       ,ospJoin('profiles', 'default', 'cssregistry.xml')\
488                       ,ospJoin('profiles', 'default', 'jsregistry.xml')\
489                       ,ospJoin('profiles', 'default', 'propertiestool.xml')\
490                       ,ospJoin('profiles', 'afterinstall', 'import_steps.xml')\
491                       ,ospJoin('profiles', 'uninstall', 'import_steps.xml')\
492                       ,ospJoin('profiles', 'uninstall', 'skins.xml')\
493                       ,ospJoin('browser', 'configure.zcml') \
494                       #,ospJoin('profiles', 'uninstall', 'cssregistry.xml')\
495                       #,ospJoin('profiles', 'uninstall', 'jsregistry.xml')\
496                       ]
497    for fp in sp_updated_files:
498        fillinFileTemplate(ospJoin(productPath, fp), dict=conf_dict)
499    for fp in files_to_remove:
500        os.remove(ospJoin(productPath,fp))
501    for data in files_to_add:
502        f = file(data['fpath'],'w')
503        f.write(data['fdata'])
504        f.close()
Note: See TracBrowser for help on using the repository browser.