source: products/qPloneSkinDump/branches/multipleslots/skin_template/Extensions/utils.py @ 1591

Last change on this file since 1591 was 1, checked in by myroslav, 19 years ago

Building directory structure

  • Property svn:eol-style set to native
File size: 21.9 KB
Line 
1import os, sys, re, string
2from StringIO import StringIO
3from time import gmtime, strftime
4from zLOG import LOG, INFO
5from zExceptions import BadRequest
6from App.config import getConfiguration
7from Products.CMFCore.utils import getToolByName
8from Products.CMFCore.DirectoryView import addDirectoryViews
9from Products.%(SKIN_PRODUCT_NAME)s.config import *
10
11######################################################################
12##                      IMPORTING UTILS                             ##
13######################################################################
14osp = os.path
15ALLOWED_IMPORT_POLICY = ["only_new", "backup", "overwrite"]
16INTRO_TO_INSTANCE = "< Started copying object files from Product import directory to Instance one."
17SUMMARY_TO_INSTANCE = "> Finished copying."
18INTRO_TO_ROOT = "< Started import %%s file[s] with '%%s' policy."
19SUMMARY_TO_ROOT = "> Finished importing."
20INTRO_CLEAN = "< Started cleaning Instance import directory."
21SUMMARY_CLEAN = "> Finished cleaning."
22CREXP_INVALID_ID = re.compile('^The id \"(.*?)\" is invalid - it is already in use.$', re.DOTALL|re.IGNORECASE|re.MULTILINE)
23CSS_BASE_IDS_QPSD053 = ['id','expression','enabled','cookable','media','rel','title','rendering']   # supporting qPSD-0.5.3 version
24################    CHECK IMPORTING    ################
25def checkIfImport():
26    """ Return if perform importing, based on checking
27        *zexp files in <SkinProduct>/import directory.
28    """
29    instance_ipath, product_ipath = getImportedPathes()
30    product_ilist = [i for i in os.listdir(product_ipath) \
31                     if osp.isfile(osp.join(product_ipath,i)) and i.endswith('.zexp')]
32    if product_ilist:
33        return 1
34    return 0
35
36################    IMPORTING TO PLONE'S IMPORT DIR   ################
37def getImportedPathes():
38    """ Return Plone instance and Skin product import pathes."""
39    # Based on instance path, construct import pathes
40    cfg = getConfiguration()
41    instance_ipath = osp.join(cfg.instancehome, "import")
42    product_ipath = osp.join(cfg.instancehome, 'Products', PRODUCT_NAME, "import")
43    # Check presence of Product import directory
44    if not osp.isdir(product_ipath):       
45        raise BadRequest, "Skin Product's import directory '%%s' - does not exist or is'nt direcory" %% product_ipath
46    # Check presence of Instance import directory
47    if not osp.isdir(instance_ipath):
48        raise BadRequest, "Instance import directory '%%s' - does not exist or isn't direcory" %% instance_ipath
49    return [instance_ipath, product_ipath]
50
51def copyFile(src_dir, dst_dir, f_name):
52    """ Copy file from src_dir to dst_dir under original name."""
53    try:
54        src_file = open(osp.join(src_dir, f_name),"rb")
55        dst_file = open(osp.join(dst_dir, f_name),"wb")
56        dst_file.write(src_file.read())
57        dst_file.close()
58        src_file.close()
59    except Exception, e:
60        msg = "!!! In copying files from <%%s> dir to <%%s> dir exception occur. Details: %%s." %% (src_dir,dst_dir, str(e))
61        print >> import_out, msg
62        LOG('performImportToPortal',INFO,'copyFile', msg)
63
64def moveToTemp(same_instance_files, instance_ipath, temp_dir_path):
65    """ Move samenamed files from Instanse's dir to temp dir."""
66    os.mkdir(temp_dir_path) # Create temp back_[date] dir
67    try:
68        [copyFile(instance_ipath, temp_dir_path, f_name) for f_name in same_instance_files]
69        [os.remove(osp.join(instance_ipath, f_name)) for f_name in same_instance_files]
70    except Exception, e:
71        msg = "!!! Exception occur during moving files from Instance's dir to temp dir. Detaile:%%s." %% str(e)
72        print >> import_out, msg
73        LOG('performImportToPortal',INFO,'moveToTemp', msg)
74   
75def copyToInstanceImport():
76    """ Perform copying imported files from <SkinProduct>/import dir
77        to Plone's instance import dir.
78    """
79    print >> import_out, INTRO_TO_INSTANCE
80    instance_ipath, product_ipath = getImportedPathes()
81    # Compose temp dir back_[date] dir path in Instance import directory
82    temp_dir_id = "back_%%s" %% strftime("%%Y%%m%%d%%H%%M%%S", gmtime())
83    temp_dir_path = osp.join(instance_ipath, temp_dir_id)
84    # Get *.zexp files from Skin Product's import dir and Plone's instance import dir files
85    product_ilist = [i for i in os.listdir(product_ipath) \
86                     if osp.isfile(osp.join(product_ipath,i)) and i.endswith('.zexp')]
87    instance_ilist = [i for i in os.listdir(instance_ipath) \
88                      if osp.isfile(osp.join(instance_ipath,i)) and i.endswith('.zexp')]
89    # Check for presence samenamed files in Instance and Product import directories.
90    same_instance_files = [f_name for f_name in instance_ilist if f_name in product_ilist]
91    if same_instance_files:
92        moveToTemp(same_instance_files, instance_ipath, temp_dir_path)
93    # Copy all *zexp files from Product's import dir to Instance's import dir
94    [copyFile(product_ipath, instance_ipath, f_name) for f_name in product_ilist]
95    print >> import_out, SUMMARY_TO_INSTANCE
96    return [instance_ipath, product_ipath, temp_dir_path, product_ilist]
97
98################    IMPORTING TO PORTAL   ################
99def importObject(portal, file_name):
100    """ Work around old Zope bug in importing."""
101    try:
102        portal.manage_importObject(file_name)
103    except:
104        portal._p_jar = portal.Destination()._p_jar
105        portal.manage_importObject(file_name)
106
107def makeBackUp(portal, portal_objects, temp_dir_path, obj_id):
108    """ Perfom backup same named portal objects in temp folder."""
109    # Get id of temp folder-object
110    durty_path,temp_id = osp.split(temp_dir_path)
111    if not temp_id:
112        durty_path,temp_id = osp.split(durty_path)
113    # Get temp folder-object
114    if temp_id not in portal_objects:
115        portal.invokeFactory('Folder', id=temp_id)
116        print >> import_out, "! Created '%%s' backup directory with same-ids " \
117                             "objects from portal root." %% temp_id
118    temp_dir = getattr(portal, temp_id)
119    # Move object with same id to temp folder-object
120    get_transaction().commit(1)
121    obj = portal.manage_cutObjects(ids=[obj_id])
122    temp_dir.manage_pasteObjects(obj)
123    print >> import_out, "! '%%s' Object moved from portal root to '%%s' backup directory." %% (obj_id, temp_id)
124
125def performImport(portal, temp_dir_path, file_name):
126    """ Importing an object to portal."""
127    portal_objects = portal.objectIds()
128    try:
129        portal.manage_importObject(file_name)
130    except Exception, e:
131        msg = str(e)
132        is_invalid_id = CREXP_INVALID_ID.match(msg)
133        if is_invalid_id:
134            obj_id = is_invalid_id.group(1)
135            if IMPORT_POLICY == "only_new":
136                msg = "! Object with '%%s' id was not importing because it's already exist " \
137                      "in portal root." %% obj_id
138                print >> import_out, msg
139            elif IMPORT_POLICY == "backup":
140                makeBackUp(portal, portal_objects, temp_dir_path, obj_id)
141                importObject(portal, file_name)
142            elif IMPORT_POLICY == "overwrite":
143                portal.manage_delObjects(ids=[obj_id])
144                importObject(portal, file_name)
145        else:
146            # work around old Zope bug in importing
147            portal._p_jar = portal.Destination()._p_jar
148            portal.manage_importObject(file_name)
149
150def importToPortalRoot(portal, product_file_names, temp_dir_path):
151    """ Import all objects from *zexp files to portal root (based on IMPORT_POLICY)."""
152    if not IMPORT_POLICY in ALLOWED_IMPORT_POLICY:
153        raise Exception("%%s - wrong import policy in '%%s/config.py' file. Must be one of the %%s" \
154                        %% (IMPORT_POLICY, PRODUCT_NAME, ALLOWED_IMPORT_POLICY) )
155    print >> import_out, INTRO_TO_ROOT %% (product_file_names, IMPORT_POLICY)
156    for file_name in product_file_names:
157        try:
158            performImport(portal, temp_dir_path, file_name)
159        except Exception, error:
160            msg = '!!! Under "%%s" policy importing exception occur: %%s.' %% (IMPORT_POLICY, str(error))
161            print >> import_out, msg
162            LOG('performImportToPortal',INFO,'importToPortalRoot', msg)
163    print >> import_out, SUMMARY_TO_ROOT
164
165################    CLEANING PLONE'S IMPORT DIR   ################
166def cleanInstanceImport(instance_ipath, product_file_names, temp_dir_path):
167    """ Cleaning Plone's import dir."""
168    print >> import_out, INTRO_CLEAN
169    # Erase all copied *zexp files from Instance's import dir
170    for f_name in product_file_names:
171        f_path = osp.join(instance_ipath, f_name)
172        if osp.exists(f_path) and osp.isfile(f_path):
173            os.remove(f_path)
174        else:
175            msg = '! "%%s" file was not deleted from "%%s" import directory.' %%\
176                   (f_name, osp.join(instance_ipath))
177            print >> import_out, msg
178            LOG('performImportToPortal',INFO,'cleanInstanceImport', msg)
179    # Move all files from temp back_[date] dir to Instance's import dir
180    if osp.exists(temp_dir_path) and osp.isdir(temp_dir_path):
181        f_names = os.listdir(temp_dir_path)
182        try:
183            [copyFile(temp_dir_path, instance_ipath, f_name) for f_name in f_names]
184            [os.remove(osp.join(temp_dir_path, f_name)) for f_name in f_names]
185            # Erase temp back_[date] dir
186            os.rmdir(temp_dir_path)
187        except Exception, e:
188            msg = "!!! In moving files from temp dir to Instance's import dir exception occur."
189            print >> import_out, msg
190            LOG('performImportToPortal',INFO,'moveFromTempToImport', msg)
191    print >> import_out, SUMMARY_CLEAN
192
193################    MAIN    ################
194def performImportToPortal(portal):
195    """ Import objects from Skin Product to Portal root."""
196    globals()['import_out'] = StringIO()
197    instance_ipath, product_ipath, temp_dir_path, product_file_names = copyToInstanceImport()
198    if product_file_names:
199        importToPortalRoot(portal, product_file_names, temp_dir_path)
200        cleanInstanceImport(instance_ipath, product_file_names, temp_dir_path)
201    else:
202        print >> import_out, "!!! Failure importing: there is no file for importing to be found."
203    result = import_out
204    del globals()['import_out']
205    return result.getvalue()
206
207######################################################################
208##              INSTALLATION/UNINSTALLATION UTILS                   ##
209######################################################################
210CSS_REG_PROPS = ['id', 'expression', 'enabled', 'cookable', 'cacheable' \
211                ,'media', 'rel', 'title', 'rendering', 'compression']
212JS_REG_PROPS = ['id', 'expression', 'enabled', 'cookable', 'cacheable' \
213               ,'inline', 'compression']
214
215def installSkin(portal, pp_up, out):
216    # Checking for presense SKIN_NAME in portal_skins directory view or among Skin Names
217    skinsTool = getToolByName(portal, 'portal_skins')
218    # Get unique product_skin_name and remember it in case of differ from SKIN_NAME.
219    product_skin_name = SKIN_NAME
220    skin_names = skinsTool.getSkinSelections()
221    if product_skin_name in skin_names:
222        idx = 0
223        while product_skin_name in skin_names:
224            product_skin_name = SKIN_NAME + str(idx)
225            idx += 1
226        addProperty(pp_up, 'q_actual_skin_name', product_skin_name, 'string', out)
227    # Add directory views
228    layer_skin_name = string.lower(SKIN_NAME)
229    addDirectoryViews(skinsTool, 'skins', GLOBALS)
230    print >> out,  "- added '%%s' directory views to portal_skins." %% layer_skin_name
231    # Get Default skin and remember it for backup on uninstallig
232    default_skin = skinsTool.getDefaultSkin()
233    addProperty(pp_up, 'q_default_skin', default_skin, 'string', out)
234    # Building list of layers for NEW SKIN
235    base_path = skinsTool.getSkinPath(BASE_SKIN_NAME)
236    new_path = map( string.strip, string.split(base_path,',') )
237    if layer_skin_name in new_path :
238        print >> out, "- %%s layer already present in '%%s' skin." %% (layer_skin_name, BASE_SKIN_NAME)
239        # Remove layer_skin_name from current position.
240        del new_path[new_path.index(layer_skin_name)]
241    # Add layer_skin_name just after 'custom' position
242    try: 
243        new_path.insert(new_path.index('custom')+1, layer_skin_name)
244    except ValueError:
245        new_path.append(layer_skin_name)
246
247    # Add subfolder path to the list of layers after "custom" layer if qMultipleSlot product isn't installed
248    has_qMultipleSLots = 'qmultipleslots' in new_path
249    if not has_qMultipleSLots and GENERATED_SUBFOLDER in skinsTool[layer_skin_name].objectIds():
250        skin_subfolder_layer = "%%s/%%s" %% (layer_skin_name, GENERATED_SUBFOLDER)
251        try:
252            new_path.insert(new_path.index('custom')+1, skin_subfolder_layer)
253        except ValueError:
254            new_path.append(skin_subfolder_layer)
255
256    new_path = string.join(new_path, ', ')
257    # Add NEW Skin and set it as dafault
258    skinsTool.addSkinSelection(product_skin_name, new_path, make_default=1)
259    print >> out, "Added %%s skin, bassed on %%s and set as default." %% (product_skin_name, BASE_SKIN_NAME)
260
261def uninstallSkin(skinsTool, actual_skin_name, initial_skin):
262    # Get 'portal_skins' object and list available skin names
263    # And remove SKIN_NAME from available skins, if it present
264    skin_names = skinsTool.getSkinSelections()
265    if actual_skin_name in skin_names :
266        skinsTool.manage_skinLayers(chosen=(actual_skin_name,), del_skin=1, REQUEST=None)
267        skin_names.remove(actual_skin_name)
268    # Remove product skin directory from skins tool
269    # AND Remove skin-product layer from available skins
270    skin_layer = SKIN_NAME.lower()
271    if skin_layer in skinsTool.objectIds():
272        skinsTool.manage_delObjects(skin_layer)
273    for skin_name in skin_names:
274        path = skinsTool.getSkinPath(skin_name)
275        path = [i.strip() for i in  path.split(',')]
276        # remove layer with generated main_template.pt
277        skin_subfolder_layer = "%%s/%%s" %% (skin_layer, GENERATED_SUBFOLDER)
278        if skin_subfolder_layer in path:
279            path.remove(skin_layer)
280        if skin_layer in path:
281            path.remove(skin_layer)
282            path = ','.join(path)
283            skinsTool.addSkinSelection(skin_name, path)
284    # If current default skin == actual_skin_name
285    # Set default skin in initial one (if initial skin still exist)
286    # or in 1st from available skin names list.
287    current_default_skin = skinsTool.getDefaultSkin()
288    if current_default_skin == actual_skin_name:
289        if initial_skin in skin_names :
290            skinsTool.manage_properties(default_skin=initial_skin, REQUEST=None)
291        elif len(skin_names)>0 :
292            skinsTool.manage_properties(default_skin=skin_names[0], REQUEST=None)
293
294def addProperty(p_sheet, p_id, p_value, p_type, out):
295    if p_sheet.hasProperty(p_id):
296        p_sheet._delProperty(p_id)
297    p_sheet._setProperty(p_id, p_value, p_type)
298    print >> out, "... added %%s PropertySheet to %%s." %% (p_id, p_sheet.getId())
299
300def getResourceProperties(obj, prop_list, dflt=''):
301    """ Return list of 2 items list-[property name, property value]."""
302    properties=[]
303    for prop in prop_list:
304        accessor = getattr(obj, 'get%%s' %% prop.capitalize(), None)
305        if accessor:
306            properties.append([prop, accessor() or dflt])
307    return properties
308
309def registerResource(pp_up, portal_res, resRegisterFunction, out \
310                    ,RESOURCE_SKIN_LIST, SKIN_RES_REGDATA, UP_PROPERTY, RES_REG_PROPS):
311    """ Register resources in portal's registry, remember existant settings."""
312    # Get original registered resources
313    portal_res_srings = []
314    for r in portal_res.getResources():
315        portal_res_srings.append(";".join(['%%s::%%s'%%(r[0],str(r[1])) \
316                                for r in getResourceProperties(r, RES_REG_PROPS)]))
317    addProperty(pp_up, UP_PROPERTY, portal_res_srings, 'lines', out)
318    # Tune Resource registry according to new skin needs
319    unexistent = [] # list of default resources,
320                    # which present in Skin-product, BUT absent in portal
321    portal_res_ids = portal_res.getResourceIds()
322    for res_dict in SKIN_RES_REGDATA:
323        if res_dict['id'] not in portal_res_ids:
324            # It's interesting - Resource Registry allow adding unexistent resource - use this
325            resRegisterFunction(**res_dict)
326            if res_dict['id'] not in RESOURCE_SKIN_LIST:
327                unexistent.append(res_dict['id'])
328        else:
329            pos = portal_res.getResourcePosition(res_dict['id'])
330            portal_res.unregisterResource(res_dict['id'])
331            resRegisterFunction(**res_dict)
332            portal_res.moveResource(res_dict['id'], pos)
333    if unexistent:
334        print >> out, "!!! - BAD: your Resource Regestry have'nt %%s resource(s), which may lead to some problems." %% unexistent
335
336def getVersion(res_list):
337    """Check version of skin product generator."""
338    return (res_list and not '::' in res_list[0] and '0.5') or '0.7'
339
340def uninstallResource(portal_res, original_res_list, RESOURCE_SKIN_LIST, resRegisterFunction):
341    # Prepare Resource Registry data for backup to original state
342    original_res_regestry = {}
343    genVersion = getVersion(original_res_list)
344    for rec in original_res_list:
345        resource = {}
346        if genVersion == '0.7':
347            [resource.update({prop.split('::')[0]:prop.split('::')[1]}) for prop in rec.split(";")]
348        elif genVersion == '0.5':
349            props = rec.split(";")
350            [resource.update({CSS_BASE_IDS_QPSD053[i]:props[i]}) for i in range(len(CSS_BASE_IDS_QPSD053))]
351        original_res_regestry[resource.pop('id')] = resource
352    # Work up actual Resource Registry
353    res_dict = portal_res.getResourcesDict()
354    for res_id in res_dict.keys():
355        # Remove from Resource Registry Skin product's resources
356        if res_id in RESOURCE_SKIN_LIST \
357           and res_id not in original_res_regestry.keys():
358            portal_res.unregisterResource(res_id)
359            continue
360        # Backup 'enabled' property Registry's resourses to it's original state
361        if original_res_regestry.has_key(res_id):
362            act_Enabled_state = res_dict[res_id].getEnabled()
363            orig_Enabled_state = original_res_regestry[res_id]['enabled']
364            if act_Enabled_state != orig_Enabled_state:
365                pos = portal_res.getResourcePosition(res_id)
366                resource = res_dict[res_id]
367                res = original_res_regestry[res_id]
368                portal_res.unregisterResource(res_id)
369                resRegisterFunction(res_id, **res)
370                portal_res.moveResource(res_id, pos)
371
372def customizeSlots(portal, pp_up, out):
373    # Get original Site's column lists
374    orig_left_slots = left_column = list(portal.left_slots)
375    orig_right_slots = right_column = list(portal.right_slots)
376    # Save original Site's LEFT and RIGHT slots
377    addProperty(pp_up, 'q_left_slots', orig_left_slots, 'lines', out)
378    addProperty(pp_up, 'q_right_slots', orig_right_slots, 'lines', out)
379    # blend-with-site - to portal's slots adding only new one from skin-porduct
380    # blend-with-skin - portal slots forming in the following manner:
381    #                   first adding skin-porduct's slots, than new one from portal
382    # replace - to portal's slots forming only from the skin-porduct's slot list
383    if SLOT_FORMING == "blend_with_skin":
384        left_column, right_column = formSlotsColumn(LEFT_SLOTS, RIGHT_SLOTS, 
385                                                    orig_left_slots, orig_right_slots, MAIN_COLUMN)
386    elif SLOT_FORMING == "blend_with_site":
387        left_column, right_column = formSlotsColumn(orig_left_slots, orig_right_slots,
388                                                    LEFT_SLOTS, RIGHT_SLOTS, MAIN_COLUMN )
389    elif SLOT_FORMING == "replace":
390        left_column, right_column = formSlotsColumn(LEFT_SLOTS, RIGHT_SLOTS, [], [], MAIN_COLUMN)
391    # REPLACE SITE's column slots
392    portal.left_slots = tuple(left_column)
393    portal.right_slots = tuple(right_column)
394    print >> out, "Complited portal slots customization ..."
395
396# main_column ("left" / "right" / "both") mean which of the MAIN column is favour
397def formSlotsColumn(main_left, main_right, slave_left=[], slave_right=[], main_column="both"):
398    result_left = main_left
399    result_right = main_right
400    if main_column == "left":
401    # 1) APPEND to MAIN_LEFT list *new for main_left column* slots from slave_left list
402    # 2) APPEND to MAIN_RIGHT list *new for both main columns* slots from slave_right
403    # 3) REMOVE slots from MAIN_RIGHT list, which are *doubled* in MAIN_LEFT
404        [result_left.append(slot) for slot in slave_left if slot not in result_left]
405        [result_right.append(slot) for slot in slave_right \
406                                   if slot not in result_right and slot not in result_left]
407        [result_right.remove(slot) for slot in result_left if slot in result_right]
408    elif main_column == "right":
409    # 1) APPEND to MAIN_LEFT list *new for main_right column* slots from slave_left list
410    # 2) APPEND to MAIN_RIGHT list *new for both main columns* slots from slave_right
411    # 3) REMOVE slots from MAIN_LEFT list, which are *doubled* in MAIN_RIGHT
412        [result_right.append(slot) for slot in slave_right if slot not in result_right]
413        [result_left.append(slot) for slot in slave_left \
414                                  if slot not in result_left and slot not in result_right]
415        [result_left.remove(slot) for slot in result_right if slot in result_left]
416    elif main_column == "both":
417    # 1) APPEND to MAIN_LEFT list *new for both main columns* slots from slave_left list
418    # 2) APPEND to MAIN_RIGHT list *new for both main columns* slots from slave_right
419        [result_left.append(slot) for slot in slave_left \
420                                  if slot not in result_left and slot not in result_right]
421        [result_right.append(slot) for slot in slave_right \
422                                   if slot not in result_right and slot not in result_left]
423    return [result_left, result_right]
424
425def getProperty(pp, ps, id, default=[]):
426    """ Get property from portal_properties/[property_sheet]"""
427    res = default
428    if ps in pp.objectIds() and pp[ps].hasProperty(id):
429        res = pp[ps].getProperty(id, default)
430    return res
Note: See TracBrowser for help on using the repository browser.