NCCOOS Trac Projects: Top | Web | Platforms | Processing | Viz | Sprints | Sandbox | (Wind)

root/NCCOOSSite/trunk/NCCOOSite/Extensions/utils.py

Revision 13 (checked in by cbc, 17 years ago)

Initial import

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