Back to index

plone3  3.1.7
InstalledProduct.py
Go to the documentation of this file.
00001 import logging
00002 from zope.interface import implements
00003 from zope.component import getSiteManager
00004 from zope.component import queryUtility
00005 
00006 from AccessControl import ClassSecurityInfo
00007 from Acquisition import aq_base
00008 from DateTime import DateTime
00009 from Globals import InitializeClass
00010 from OFS.SimpleItem import SimpleItem
00011 
00012 from Products.CMFCore.utils import getToolByName
00013 from Products.CMFCore.permissions import ManagePortal
00014 from Products.ExternalMethod.ExternalMethod import ExternalMethod
00015 from Products.GenericSetup.utils import _resolveDottedName
00016 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
00017 
00018 from Products.CMFQuickInstallerTool.interfaces.portal_quickinstaller \
00019     import IInstalledProduct
00020 from Products.CMFQuickInstallerTool.utils import updatelist, delObjects
00021 
00022 CMF21 = True
00023 try:
00024     from Products.CMFCore.ActionInformation import ActionCategory
00025 except ImportError:
00026     CMF21 = False
00027 
00028 
00029 logger = logging.getLogger('CMFQuickInstallerTool')
00030 
00031 DEFAULT_CASCADE = (
00032     'types', 'skins', 'actions', 'portalobjects', 'workflows', 'slots',
00033     'registrypredicates', 'adapters', 'utilities',
00034 )
00035 
00036 
00037 
00038 class InstalledProduct(SimpleItem):
00039     """Class storing information about an installed product
00040 
00041       Let's make sure that this implementation actually fulfills the
00042       'IInstalledProduct' API.
00043 
00044       >>> from zope.interface.verify import verifyClass
00045       >>> verifyClass(IInstalledProduct, InstalledProduct)
00046       True
00047     """
00048     implements(IInstalledProduct)
00049 
00050     meta_type = "Installed Product"
00051 
00052     manage_options=(
00053         {'label':'View','action':'manage_installationInfo'},
00054         ) + SimpleItem.manage_options
00055 
00056     security = ClassSecurityInfo()
00057 
00058     security.declareProtected(ManagePortal, 'manage_installationInfo')
00059     manage_installationInfo = PageTemplateFile(
00060         'forms/installed_product_overview', globals(),
00061         __name__='manage_installationInfo')
00062 
00063     default_cascade=['types', 'skins', 'actions', 'portalobjects',
00064                      'workflows', 'slots', 'registrypredicates',
00065                      'adapters', 'utilities']
00066 
00067     def __init__(self, id):
00068         self.id=id
00069         self.transcript=[]
00070         self.leftslots=[]
00071         self.rightslots=[]
00072         self.locked=None
00073         self.hidden=None
00074         self.installedversion=None
00075         self.status='new'
00076         self.error=False
00077         self.afterid = None
00078         self.beforeid = None
00079         for key in DEFAULT_CASCADE:
00080             setattr(self, key, [])
00081 
00082     security.declareProtected(ManagePortal, 'update')
00083     def update(self, settings, installedversion='', logmsg='',
00084                status='installed', error=False, locked=False, hidden=False,
00085                afterid=None, beforeid=None):
00086 
00087         # check for the availability of attributes before assigning
00088         for att in settings.keys():
00089             if not hasattr(self.aq_base, att):
00090                 setattr(self, att, [])
00091 
00092         qi = getToolByName(self, 'portal_quickinstaller')
00093         reg = qi.getAlreadyRegistered()
00094 
00095         for k in settings.keys():
00096             old = k in reg.keys() and reg[k] or []
00097             updatelist(getattr(self,k), settings[k], old)
00098 
00099         self.transcript.insert(0, {'timestamp':DateTime(), 'msg':logmsg})
00100         self.locked=locked
00101         self.hidden=hidden
00102         self.installedversion=installedversion
00103         self.afterid = afterid
00104         self.beforeid = beforeid
00105 
00106         if status:
00107             self.status=status
00108 
00109         self.error=error
00110 
00111     security.declareProtected(ManagePortal, 'log')
00112     def log(self, logmsg):
00113         """Adds a log to the transcript
00114         """
00115         self.transcript.insert(0, {'timestamp':DateTime(), 'msg':logmsg})
00116 
00117     security.declareProtected(ManagePortal, 'hasError')
00118     def hasError(self):
00119         """Returns if the prod is in error state
00120         """
00121         return getattr(self, 'error', False)
00122 
00123     security.declareProtected(ManagePortal, 'isLocked')
00124     def isLocked(self):
00125         """Is the product locked for uninstall
00126         """
00127         return getattr(self, 'locked', False)
00128 
00129     security.declareProtected(ManagePortal, 'isHidden')
00130     def isHidden(self):
00131         """Is the product hidden
00132         """
00133         return getattr(self, 'hidden', False)
00134 
00135     security.declareProtected(ManagePortal, 'isVisible')
00136     def isVisible(self):
00137         return not self.isHidden()
00138 
00139     security.declareProtected(ManagePortal, 'isInstalled')
00140     def isInstalled(self):
00141         return self.status=='installed'
00142 
00143     security.declareProtected(ManagePortal, 'getStatus')
00144     def getStatus(self):
00145         return self.status
00146 
00147     security.declareProtected(ManagePortal, 'getTypes')
00148     def getTypes(self):
00149         return self.types
00150 
00151     security.declareProtected(ManagePortal, 'getSkins')
00152     def getSkins(self):
00153         return self.skins
00154 
00155     security.declareProtected(ManagePortal, 'getActions')
00156     def getActions(self):
00157         return self.actions
00158 
00159     security.declareProtected(ManagePortal, 'getPortalObjects')
00160     def getPortalObjects(self):
00161         return self.portalobjects
00162 
00163     security.declareProtected(ManagePortal, 'getWorkflows')
00164     def getWorkflows(self):
00165         return self.workflows
00166 
00167     security.declareProtected(ManagePortal, 'getLeftSlots')
00168     def getLeftSlots(self):
00169         if getattr(self, 'leftslots', None) is None:
00170             self.leftslots = []
00171         return self.leftslots
00172 
00173     security.declareProtected(ManagePortal, 'getRightSlots')
00174     def getRightSlots(self):
00175         if getattr(self, 'rightslots', None) is None:
00176             self.rightslots = []
00177         return self.rightslots
00178 
00179     security.declareProtected(ManagePortal, 'getSlots')
00180     def getSlots(self):
00181         return self.getLeftSlots()+self.getRightSlots()
00182 
00183     security.declareProtected(ManagePortal, 'getValue')
00184     def getValue(self,name):
00185         return getattr(self,name,[])
00186     
00187     security.declareProtected(ManagePortal, 'getRegistryPredicates')
00188     def getRegistryPredicates(self):
00189         """Return the custom entries in the content_type_registry
00190         """
00191         return getattr(self, 'registrypredicates', [])
00192 
00193     security.declareProtected(ManagePortal, 'getAfterId')
00194     def getAfterId(self):
00195         return self.afterid
00196 
00197     security.declareProtected(ManagePortal, 'getBeforeId')
00198     def getBeforeId(self):
00199         return self.beforeid
00200 
00201     security.declareProtected(ManagePortal, 'getTranscriptAsText')
00202     def getTranscriptAsText(self):
00203         if getattr(self,'transcript',None):
00204             msgs = [t['timestamp'].ISO()+'\n'+str(t['msg'])
00205                     for t in self.transcript]
00206             return '\n=============\n'.join(msgs)
00207         else:
00208             return 'no messages'
00209 
00210     def _getMethod(self, modfunc):
00211         """Returns a method
00212         """
00213         try:
00214             productInCP = self.Control_Panel.Products[self.id]
00215         except KeyError:
00216             return None
00217 
00218         for mod, func in modfunc:
00219             if mod in productInCP.objectIds():
00220                 modFolder = productInCP[mod]
00221                 if func in modFolder.objectIds():
00222                     return modFolder[func]
00223 
00224             try:
00225                 return ExternalMethod('temp','temp',self.id+'.'+mod, func)
00226             except:
00227                 pass
00228 
00229         return None
00230 
00231     security.declareProtected(ManagePortal, 'getInstallMethod')
00232     def getInstallMethod(self):
00233         """ returns the installer method """
00234         res = self._getMethod((('Install','install'),
00235                                 ('Install','Install'),
00236                                 ('install','install'),
00237                                 ('install','Install'),
00238                                ))
00239         if res is None:
00240             raise AttributeError, ('No Install method found for '
00241                                    'product %s' % self.id)
00242         else:
00243             return res
00244 
00245     security.declareProtected(ManagePortal, 'getUninstallMethod')
00246     def getUninstallMethod(self):
00247         """ returns the uninstaller method """
00248         return self._getMethod((('Install','uninstall'),
00249                                 ('Install','Uninstall'),
00250                                 ('install','uninstall'),
00251                                 ('install','Uninstall'),
00252                                ))
00253 
00254     security.declareProtected(ManagePortal, 'getAfterInstallMethod')
00255     def getAfterInstallMethod(self):
00256         """ returns the after installer method """
00257         return self._getMethod((('Install','afterInstall'),
00258                                 ('install','afterInstall'),
00259                                ))
00260 
00261     security.declareProtected(ManagePortal, 'getBeforeUninstallMethod')
00262     def getBeforeUninstallMethod(self):
00263         """ returns the before uninstaller method """
00264         return self._getMethod((('Install','beforeUninstall'),
00265                                 ('install','beforeUninstall'),
00266                                ))
00267 
00268     security.declareProtected(ManagePortal, 'uninstall')
00269     def uninstall(self, cascade=default_cascade, reinstall=False, REQUEST=None):
00270         """Uninstalls the product and removes its dependencies
00271         """
00272         portal = getToolByName(self, 'portal_url').getPortalObject() 
00273 
00274         # TODO eventually we will land Event system and could remove
00275         # this 'removal_inprogress' hack
00276         if self.isLocked() and getattr(portal, 'removal_inprogress', False):
00277             raise ValueError, 'The product is locked and cannot be uninstalled!'
00278 
00279         res=''
00280         afterRes=''
00281 
00282         uninstaller = self.getUninstallMethod()
00283         beforeUninstall = self.getBeforeUninstallMethod()
00284 
00285         if uninstaller:
00286             uninstaller = uninstaller.__of__(portal)
00287             try:
00288                 res=uninstaller(portal, reinstall=reinstall)
00289                 # XXX log it
00290             except TypeError:
00291                 res=uninstaller(portal)
00292 
00293         if beforeUninstall:
00294             beforeUninstall = beforeUninstall.__of__(portal)
00295             beforeRes, cascade = beforeUninstall(portal, reinstall=reinstall,
00296                                                 product=self, cascade=cascade)
00297         
00298         self._cascadeRemove(cascade)
00299 
00300         self.status='uninstalled'
00301         self.log('uninstalled\n'+str(res)+str(afterRes))
00302 
00303         if REQUEST and REQUEST.get('nextUrl',None):
00304             return REQUEST.RESPONSE.redirect(REQUEST['nextUrl'])
00305 
00306     def _cascadeRemove(self, cascade):
00307         """Cascaded removal of objects
00308         """
00309         portal = getToolByName(self, 'portal_url').getPortalObject() 
00310         
00311         if 'types' in cascade:
00312             portal_types=getToolByName(self,'portal_types')
00313             delObjects(portal_types, getattr(aq_base(self), 'types', []))
00314 
00315         if 'skins' in cascade:
00316             portal_skins=getToolByName(self,'portal_skins')
00317             delObjects(portal_skins, getattr(aq_base(self), 'skins', []))
00318 
00319         if 'actions' in cascade and len(getattr(aq_base(self), 'actions', [])) > 0:
00320             portal_actions=getToolByName(self,'portal_actions')
00321             if CMF21:
00322                 for info in self.actions:
00323                     if isinstance(info, basestring):
00324                         action = info
00325                         # Product was installed before CMF 2.1
00326                         # Try to remove the action from all categories
00327                         for category in portal_actions.objectIds():
00328                             cat = portal_actions[category]
00329                             if action in cat.objectIds():
00330                                 cat._delObject(action)
00331                     else:
00332                         category, action = info
00333                         if category in portal_actions.objectIds():
00334                             cat = portal_actions[category]
00335                             if action in cat.objectIds():
00336                                 cat._delObject(action)
00337                             if len(cat.objectIds()) == 0:
00338                                 del cat
00339                                 portal_actions._delObject(category)
00340             else:
00341                 actids = [o.id.lower() for o in portal_actions._actions]
00342                 delactions = [actids.index(id) for id in
00343                               getattr(aq_base(self), 'actions', ()) if id in actids]
00344                 if delactions:
00345                     portal_actions.deleteActions(delactions)
00346 
00347         if 'portalobjects' in cascade:
00348             delObjects(portal, getattr(aq_base(self), 'portalobjects', []))
00349 
00350         if 'workflows' in cascade:
00351             portal_workflow=getToolByName(self, 'portal_workflow')
00352             delObjects(portal_workflow, getattr(aq_base(self), 'workflows', []))
00353 
00354         if 'slots' in cascade:
00355             if self.getLeftSlots():
00356                 portal.left_slots=[s for s in portal.left_slots
00357                                    if s not in self.getLeftSlots()]
00358             if self.getRightSlots():
00359                 portal.right_slots=[s for s in portal.right_slots
00360                                     if s not in self.getRightSlots()]
00361 
00362         if 'registrypredicates' in cascade:
00363             ctr = getToolByName(self,'content_type_registry')
00364             ids = [id for id, predicate in ctr.listPredicates()]
00365             predicates=getattr(aq_base(self),'registrypredicates',[])
00366             for p in predicates:
00367                 if p in ids:
00368                     ctr.removePredicate(p)
00369                 else:
00370                     logger.log("Failed to delete '%s' from content type " \
00371                                "registry" % p, severity=logging.WARNING)
00372 
00373         if 'adapters' in cascade:
00374             adapters = getattr(aq_base(self), 'adapters', [])
00375             if adapters:
00376                 sm = getSiteManager()
00377                 # TODO: expand this to actually cover adapter registrations
00378 
00379         if 'utilities' in cascade:
00380             utilities = getattr(aq_base(self), 'utilities', [])
00381             if utilities:
00382                 sm = getSiteManager()
00383                 for registration in utilities:
00384                     provided = _resolveDottedName(registration[0])
00385                     name = registration[1]
00386                     if queryUtility(provided, name=name) is not None:
00387                         sm.unregisterUtility(provided=provided, name=name)
00388 
00389         rr_js = getToolByName(self, 'portal_javascripts', None)
00390         rr_css = getToolByName(self, 'portal_css', None)
00391 
00392         if rr_js is not None:
00393             for js in getattr(aq_base(self),'resources_js',[]):
00394                 rr_js.unregisterResource(js)
00395         if rr_css is not None:
00396             for css in getattr(aq_base(self),'resources_css',[]):
00397                 rr_css.unregisterResource(css)
00398 
00399     security.declareProtected(ManagePortal, 'getInstalledVersion')
00400     def getInstalledVersion(self):
00401         """Return the version of the product in the moment of installation
00402         """
00403         return getattr(self, 'installedversion', None)
00404 
00405 InitializeClass(InstalledProduct)