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