class MimeTypeRegexPredicate(SimpleItem): """ Predicate matching only on 'typ', using regex matching for string patterns (other objects conforming to 'match' can also be passed). """ implements(IContentTypeRegistryPredicate) __implements__ = z2IContentTypeRegistryPredicate pattern = None PREDICATE_TYPE = 'mimetype_regex' security = ClassSecurityInfo() def __init__(self, id): self.id = id security.declareProtected(ManagePortal, 'getPatternStr') def getPatternStr(self): if self.pattern is None: return 'None' return self.pattern.pattern security.declareProtected(ManagePortal, 'edit') def edit(self, pattern): if pattern == 'None': pattern = None if type(pattern) is type(''): pattern = re.compile(pattern) self.pattern = pattern # # ContentTypeRegistryPredicate interface # security.declareObjectPublic() def __call__(self, name, typ, body): """ Return true if the rule matches, else false. """ if self.pattern is None: return 0 return self.pattern.match(typ) security.declareProtected(ManagePortal, 'getTypeLabel') def getTypeLabel(self): """ Return a human-readable label for the predicate type. """ return self.PREDICATE_TYPE security.declareProtected(ManagePortal, 'predicateWidget') predicateWidget = DTMLResource('dtml/patternWidget', globals())
class FSDTMLMethod(RestrictedDTML, RoleManager, FSObject, HTML): """FSDTMLMethods act like DTML methods but are not directly modifiable from the management interface.""" meta_type = 'Filesystem DTML Method' manage_options = (( { 'label': 'Customize', 'action': 'manage_main' }, { 'label': 'View', 'action': '', 'help': ('OFSP', 'DTML-DocumentOrMethod_View.stx') }, { 'label': 'Proxy', 'action': 'manage_proxyForm', 'help': ('OFSP', 'DTML-DocumentOrMethod_Proxy.stx') }, ) + Cacheable.manage_options) _proxy_roles = () _cache_namespace_keys = () # Use declarative security security = ClassSecurityInfo() security.declareObjectProtected(View) security.declareProtected(ViewManagementScreens, 'manage_main') manage_main = DTMLResource('dtml/custdtml', globals()) _reading = 0 def __init__(self, id, package=None, entry_subpath=None, filepath=None, fullname=None, properties=None): FSObject.__init__(self, id, package, entry_subpath, filepath, fullname, properties) # Normally called via HTML.__init__ but we don't need the rest that # happens there. self.initvars(None, {}) def _createZODBClone(self): """Create a ZODB (editable) equivalent of this object.""" return DTMLMethod(self.read(), __name__=self.getId()) def _readFile(self, reparse): self.raw = self._readFileAsResourceOrDirect() if reparse: self._reading = 1 # Avoid infinite recursion try: self.cook() finally: self._reading = 0 # Hook up chances to reload in debug mode security.declarePrivate('read_raw') def read_raw(self): if not self._reading: self._updateFromFS() return HTML.read_raw(self) #### The following is mainly taken from OFS/DTMLMethod.py ### index_html = None # Prevent accidental acquisition # Documents masquerade as functions: func_code = DTMLMethod.func_code default_content_type = 'text/html' def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw): """Render the document given a client object, REQUEST mapping, Response, and key word arguments.""" self._updateFromFS() if not self._cache_namespace_keys: data = self.ZCacheable_get(default=_marker) if data is not _marker: # Return cached results. return data kw['document_id'] = self.getId() kw['document_title'] = self.title __traceback_info__ = self._filepath security = getSecurityManager() security.addContext(self) try: r = HTML.__call__(self, client, REQUEST, **kw) if client is None: # Called as subtemplate, so don't need error propagation! if RESPONSE is None: result = r else: result = decapitate(r, RESPONSE) if not self._cache_namespace_keys: self.ZCacheable_set(result) return result if not isinstance(r, basestring) or RESPONSE is None: if not self._cache_namespace_keys: self.ZCacheable_set(r) return r finally: security.removeContext(self) have_key = RESPONSE.headers.has_key if not (have_key('content-type') or have_key('Content-Type')): if self.__dict__.has_key('content_type'): c = self.content_type else: c, e = guess_content_type(self.getId(), r) RESPONSE.setHeader('Content-Type', c) if RESPONSE is not None: # caching policy manager hook _setCacheHeaders(self, {}) result = decapitate(r, RESPONSE) if not self._cache_namespace_keys: self.ZCacheable_set(result) return result def getCacheNamespaceKeys(self): ''' Returns the cacheNamespaceKeys. ''' return self._cache_namespace_keys def setCacheNamespaceKeys(self, keys, REQUEST=None): ''' Sets the list of names that should be looked up in the namespace to provide a cache key. ''' ks = [] for key in keys: key = strip(str(key)) if key: ks.append(key) self._cache_namespace_keys = tuple(ks) if REQUEST is not None: return self.ZCacheable_manage(self, REQUEST) # Zope 2.3.x way: def validate(self, inst, parent, name, value, md=None): return getSecurityManager().validate(inst, parent, name, value) security.declareProtected(FTPAccess, 'manage_FTPget') manage_FTPget = DTMLMethod.manage_FTPget.im_func security.declareProtected(ViewManagementScreens, 'PrincipiaSearchSource') PrincipiaSearchSource = DTMLMethod.PrincipiaSearchSource.im_func security.declareProtected(ViewManagementScreens, 'document_src') document_src = DTMLMethod.document_src.im_func security.declareProtected(ViewManagementScreens, 'manage_haveProxy') manage_haveProxy = DTMLMethod.manage_haveProxy.im_func
class CatalogTool(UniqueObject, ZCatalog, ActionProviderBase): """ This is a ZCatalog that filters catalog queries. """ implements(ICatalogTool) __implements__ = (z2ICatalogTool, ZCatalog.__implements__, ActionProviderBase.__implements__) id = 'portal_catalog' meta_type = 'CMF Catalog' security = ClassSecurityInfo() manage_options = (ZCatalog.manage_options + ActionProviderBase.manage_options + ({ 'label': 'Overview', 'action': 'manage_overview' }, )) def __init__(self): ZCatalog.__init__(self, self.getId()) # # ZMI methods # security.declareProtected(ManagePortal, 'manage_overview') manage_overview = DTMLResource('dtml/explainCatalogTool', globals()) # # 'portal_catalog' interface methods # def _listAllowedRolesAndUsers(self, user): result = list(user.getRoles()) result.append('Anonymous') result.append('user:%s' % user.getId()) return result def _convertQuery(self, kw): # Convert query to modern syntax for k in 'effective', 'expires': kusage = k + '_usage' if not kw.has_key(kusage): continue usage = kw[kusage] if not usage.startswith('range:'): raise ValueError("Incorrect usage %s" % ` usage `) kw[k] = {'query': kw[k], 'range': usage[6:]} del kw[kusage] # searchResults has inherited security assertions. def searchResults(self, REQUEST=None, **kw): """ Calls ZCatalog.searchResults with extra arguments that limit the results to what the user is allowed to see. """ user = _getAuthenticatedUser(self) kw['allowedRolesAndUsers'] = self._listAllowedRolesAndUsers(user) if not _checkPermission(AccessInactivePortalContent, self): now = DateTime() self._convertQuery(kw) # Intersect query restrictions with those implicit to the tool for k in 'effective', 'expires': if kw.has_key(k): range = kw[k]['range'] or '' query = kw[k]['query'] if not isinstance(query, (tuple, list)): query = (query, ) else: range = '' query = None if range.find('min') > -1: lo = min(query) else: lo = None if range.find('max') > -1: hi = max(query) else: hi = None if k == 'effective': if hi is None or hi > now: hi = now if lo is not None and hi < lo: return () else: # 'expires': if lo is None or lo < now: lo = now if hi is not None and hi < lo: return () # Rebuild a query if lo is None: query = hi range = 'max' elif hi is None: query = lo range = 'min' else: query = (lo, hi) range = 'min:max' kw[k] = {'query': query, 'range': range} return ZCatalog.searchResults(self, REQUEST, **kw) __call__ = searchResults security.declarePrivate('unrestrictedSearchResults') def unrestrictedSearchResults(self, REQUEST=None, **kw): """Calls ZCatalog.searchResults directly without restrictions. This method returns every also not yet effective and already expired objects regardless of the roles the caller has. CAUTION: Care must be taken not to open security holes by exposing the results of this method to non authorized callers! If you're in doubt if you should use this method or 'searchResults' use the latter. """ return ZCatalog.searchResults(self, REQUEST, **kw) def __url(self, ob): return '/'.join(ob.getPhysicalPath()) manage_catalogFind = DTMLResource('dtml/catalogFind', globals()) def catalog_object(self, obj, uid=None, idxs=None, update_metadata=1, pghandler=None): # Wraps the object with workflow and accessibility # information just before cataloging. wftool = getToolByName(self, 'portal_workflow', None) if wftool is not None: vars = wftool.getCatalogVariablesFor(obj) else: vars = {} w = IndexableObjectWrapper(vars, obj) ZCatalog.catalog_object(self, w, uid, idxs, update_metadata, pghandler) security.declarePrivate('indexObject') def indexObject(self, object): """Add to catalog. """ url = self.__url(object) self.catalog_object(object, url) security.declarePrivate('unindexObject') def unindexObject(self, object): """Remove from catalog. """ url = self.__url(object) self.uncatalog_object(url) security.declarePrivate('reindexObject') def reindexObject(self, object, idxs=[], update_metadata=1, uid=None): """Update catalog after object data has changed. The optional idxs argument is a list of specific indexes to update (all of them by default). The update_metadata flag controls whether the object's metadata record is updated as well. If a non-None uid is passed, it will be used as the catalog uid for the object instead of its physical path. """ if uid is None: uid = self.__url(object) if idxs != []: # Filter out invalid indexes. valid_indexes = self._catalog.indexes.keys() idxs = [i for i in idxs if i in valid_indexes] self.catalog_object(object, uid, idxs, update_metadata)
class MemberDataTool(UniqueObject, SimpleItem, PropertyManager, ActionProviderBase): """ This tool wraps user objects, making them act as Member objects. """ implements(IMemberDataTool) __implements__ = (z2IMemberDataTool, ActionProviderBase.__implements__) id = 'portal_memberdata' meta_type = 'CMF Member Data Tool' _v_temps = None _properties = () security = ClassSecurityInfo() manage_options = (ActionProviderBase.manage_options + ({ 'label': 'Overview', 'action': 'manage_overview' }, { 'label': 'Contents', 'action': 'manage_showContents' }) + PropertyManager.manage_options + SimpleItem.manage_options) # # ZMI methods # security.declareProtected(ManagePortal, 'manage_overview') manage_overview = DTMLResource('dtml/explainMemberDataTool', globals()) security.declareProtected(ViewManagementScreens, 'manage_showContents') manage_showContents = DTMLResource('dtml/memberdataContents', globals()) def __init__(self): self._members = OOBTree() # Create the default properties. self._setProperty('email', '', 'string') self._setProperty('portal_skin', '', 'string') self._setProperty('listed', '', 'boolean') self._setProperty('login_time', '2000/01/01', 'date') self._setProperty('last_login_time', '2000/01/01', 'date') # # 'portal_memberdata' interface methods # security.declarePrivate('getMemberDataContents') def getMemberDataContents(self): ''' Return the number of members stored in the _members BTree and some other useful info ''' membertool = getToolByName(self, 'portal_membership') members = self._members user_list = membertool.listMemberIds() member_list = members.keys() member_count = len(members) orphan_count = 0 for member in member_list: if member not in user_list: orphan_count = orphan_count + 1 return [{'member_count': member_count, 'orphan_count': orphan_count}] security.declarePrivate('searchMemberData') def searchMemberData(self, search_param, search_term, attributes=()): """ Search members. """ res = [] if not search_param: return res membership = getToolByName(self, 'portal_membership') if len(attributes) == 0: attributes = ('id', 'email') if search_param == 'username': search_param = 'id' for user_id in self._members.keys(): u = membership.getMemberById(user_id) if u is not None: memberProperty = u.getProperty searched = memberProperty(search_param, None) if searched is not None and searched.find(search_term) != -1: user_data = {} for desired in attributes: if desired == 'id': user_data['username'] = memberProperty(desired, '') else: user_data[desired] = memberProperty(desired, '') res.append(user_data) return res security.declarePrivate('searchMemberDataContents') def searchMemberDataContents(self, search_param, search_term): """ Search members. This method will be deprecated soon. """ res = [] if search_param == 'username': search_param = 'id' mtool = getToolByName(self, 'portal_membership') for member_id in self._members.keys(): user_wrapper = mtool.getMemberById(member_id) if user_wrapper is not None: memberProperty = user_wrapper.getProperty searched = memberProperty(search_param, None) if searched is not None and searched.find(search_term) != -1: res.append({ 'username': memberProperty('id'), 'email': memberProperty('email', '') }) return res security.declarePrivate('pruneMemberDataContents') def pruneMemberDataContents(self): """ Delete data contents of all members not listet in acl_users. """ membertool = getToolByName(self, 'portal_membership') members = self._members user_list = membertool.listMemberIds() for member_id in list(members.keys()): if member_id not in user_list: del members[member_id] security.declarePrivate('wrapUser') def wrapUser(self, u): ''' If possible, returns the Member object that corresponds to the given User object. ''' id = u.getId() members = self._members if not members.has_key(id): # Get a temporary member that might be # registered later via registerMemberData(). temps = self._v_temps if temps is not None and temps.has_key(id): m = temps[id] else: base = aq_base(self) m = MemberData(base, id) if temps is None: self._v_temps = {id: m} if hasattr(self, 'REQUEST'): # No REQUEST during tests. self.REQUEST._hold(CleanupTemp(self)) else: temps[id] = m else: m = members[id] # Return a wrapper with self as containment and # the user as context. return m.__of__(self).__of__(u) security.declarePrivate('registerMemberData') def registerMemberData(self, m, id): """ Add the given member data to the _members btree. """ self._members[id] = aq_base(m) security.declarePrivate('deleteMemberData') def deleteMemberData(self, member_id): """ Delete member data of specified member. """ members = self._members if members.has_key(member_id): del members[member_id] return 1 else: return 0
""" Converts Subject string into a List for content filter view. """ for sub in obj.Subject(): if sub in self.filterSubject: return 1 return 0 def __call__(self, content): for predicate in self.predicates: try: if not predicate(content): return 0 except (AttributeError, KeyError, IndexError, ValueError): # predicates are *not* allowed to throw exceptions return 0 return 1 def __str__(self): """ Return a stringified description of the filter. """ return '; '.join(self.description) manage_addPortalFolder = PortalFolder.manage_addPortalFolder.im_func manage_addPortalFolderForm = DTMLResource('folderAdd', globals())
class ActionsTool(UniqueObject, IFAwareObjectManager, OrderedFolder, ActionProviderBase): """ Weave together the various sources of "actions" which are apropos to the current user and context. """ implements(IActionsTool) __implements__ = (z2IActionsTool, OrderedFolder.__implements__, ActionProviderBase.__implements__) id = 'portal_actions' meta_type = 'CMF Actions Tool' _product_interfaces = (IActionCategory, ) action_providers = ('portal_types', 'portal_workflow', 'portal_actions') security = ClassSecurityInfo() manage_options = ((OrderedFolder.manage_options[0], ActionProviderBase.manage_options[0], { 'label': 'Action Providers', 'action': 'manage_actionProviders' }, { 'label': 'Overview', 'action': 'manage_overview' }) + OrderedFolder.manage_options[2:]) # # ZMI methods # security.declareProtected(ManagePortal, 'manage_overview') manage_overview = DTMLResource('dtml/explainActionsTool', globals()) manage_actionProviders = DTMLResource('dtml/manageActionProviders', globals()) security.declareProtected(ManagePortal, 'manage_aproviders') def manage_aproviders(self, apname='', chosen=(), add_provider=0, del_provider=0, REQUEST=None): """ Manage action providers through-the-web. """ providers = list(self.listActionProviders()) new_providers = [] if add_provider: providers.append(apname) elif del_provider: for item in providers: if item not in chosen: new_providers.append(item) providers = new_providers self.action_providers = tuple(providers) if REQUEST is not None: return self.manage_actionProviders( self, REQUEST, manage_tabs_message='Providers changed.') security.declareProtected(ManagePortal, 'manage_editActionsForm') def manage_editActionsForm(self, REQUEST, manage_tabs_message=None): """ Show the 'Actions' management tab. """ actions = [ai.getMapping() for ai in self._actions] # possible_permissions is in AccessControl.Role.RoleManager. pp = self.possible_permissions() return self._actions_form(self, REQUEST, actions=actions, possible_permissions=pp, management_view='Actions', manage_tabs_message=manage_tabs_message) # # ActionProvider interface # security.declarePrivate('listActions') def listActions(self, info=None, object=None): """ List all the actions defined by a provider. """ actions = list(self._actions) for category in self.objectValues(): actions.extend(category.listActions()) return tuple(actions) # # Programmatically manipulate the list of action providers # security.declareProtected(ManagePortal, 'listActionProviders') def listActionProviders(self): """ List the ids of all Action Providers queried by this tool. """ return self.action_providers security.declareProtected(ManagePortal, 'addActionProvider') def addActionProvider(self, provider_name): """ Add an Action Provider id to the providers queried by this tool. """ ap = list(self.action_providers) if hasattr(self, provider_name) and provider_name not in ap: ap.append(provider_name) self.action_providers = tuple(ap) security.declareProtected(ManagePortal, 'deleteActionProvider') def deleteActionProvider(self, provider_name): """ Delete an Action Provider id from providers queried by this tool. """ ap = list(self.action_providers) if provider_name in ap: ap.remove(provider_name) self.action_providers = tuple(ap) # # 'portal_actions' interface methods # security.declarePublic('listFilteredActionsFor') def listFilteredActionsFor(self, object=None): """ List all actions available to the user. """ actions = [] # Include actions from specific tools. for provider_name in self.listActionProviders(): provider = getattr(self, provider_name) if IActionProvider.providedBy(provider) or \ z2IActionProvider.isImplementedBy(provider): actions.extend(provider.listActionInfos(object=object)) # Include actions from object. if object is not None: if IActionProvider.providedBy(object) or \ z2IActionProvider.isImplementedBy(object): actions.extend(object.listActionInfos(object=object)) # Reorganize the actions by category. filtered_actions = { 'user': [], 'folder': [], 'object': [], 'global': [], 'workflow': [], } for action in actions: catlist = filtered_actions.setdefault(action['category'], []) catlist.append(action) return filtered_actions
class FSPropertiesObject(FSObject, PropertyManager): """FSPropertiesObjects simply hold properties.""" meta_type = 'Filesystem Properties Object' manage_options = ({'label': 'Customize', 'action': 'manage_main'}, ) security = ClassSecurityInfo() security.declareProtected(ViewManagementScreens, 'manage_main') manage_main = DTMLResource('dtml/custprops', globals()) # Declare all (inherited) mutating methods private. security.declarePrivate('manage_addProperty') security.declarePrivate('manage_editProperties') security.declarePrivate('manage_delProperties') security.declarePrivate('manage_changeProperties') security.declarePrivate('manage_propertiesForm') security.declarePrivate('manage_propertyTypeForm') security.declarePrivate('manage_changePropertyTypes') security.declareProtected(ViewManagementScreens, 'manage_doCustomize') def manage_doCustomize(self, folder_path, RESPONSE=None): """Makes a ZODB Based clone with the same data. Calls _createZODBClone for the actual work. """ # Overridden here to provide a different redirect target. FSObject.manage_doCustomize(self, folder_path, RESPONSE) if RESPONSE is not None: fpath = tuple(folder_path.split('/')) folder = self.restrictedTraverse(fpath) RESPONSE.redirect('%s/%s/manage_propertiesForm' % (folder.absolute_url(), self.getId())) def _createZODBClone(self): """Create a ZODB (editable) equivalent of this object.""" # Create a Folder to hold the properties. obj = Folder() obj.id = self.getId() map = [] for p in self._properties: # This should be secure since the properties come # from the filesystem. setattr(obj, p['id'], getattr(self, p['id'])) map.append({ 'id': p['id'], 'type': p['type'], 'mode': 'wd', }) obj._properties = tuple(map) return obj def _readFile(self, reparse): """Read the data from the filesystem. Read the file (indicated by exandpath(self._filepath), and parse the data if necessary. """ data = self._readFileAsResourceOrDirect() lines = data.splitlines() map = [] lino = 0 for line in lines: lino = lino + 1 line = line.strip() if not line or line[0] == '#': continue try: propname, proptv = line.split(':', 1) #XXX multi-line properties? proptype, propvstr = proptv.split('=', 1) propname = propname.strip() proptype = proptype.strip() propvstr = propvstr.strip() converter = get_converter(proptype, lambda x: x) propvalue = converter(propvstr) # Should be safe since we're loading from # the filesystem. setattr(self, propname, propvalue) map.append({ 'id': propname, 'type': proptype, 'mode': '', 'default_value': propvalue, }) except: raise ValueError, ('Error processing line %s of %s:\n%s' % (lino, fp, line)) self._properties = tuple(map) if DevelopmentMode: # Provide an opportunity to update the properties. def __of__(self, parent): self = ImplicitAcquisitionWrapper(self, parent) self._updateFromFS() return self
class FiveActionsTool(UniqueObject, SimpleItem, ActionProviderBase): """Five actions tool. Provides a bridge that makes Zope 3 menus available as CMF actions. """ __implements__ = ActionProviderBase.__implements__ id = 'portal_fiveactions' meta_type = 'CMF Five Actions Tool' security = ClassSecurityInfo() manage_options = (({ 'label': 'Overview', 'action': 'manage_overview' }, ) + SimpleItem.manage_options) # # ZMI methods # security.declareProtected(ManagePortal, 'manage_overview') manage_overview = DTMLResource('dtml/explainFiveActionsTool', globals()) # # ActionProvider # security.declarePrivate('listActions') def listActions(self, info=None, object=None): """ List all the actions defined by a provider. """ if object is None and info is not None: # BBB (according to the interface) object = info.content if object is None: # The tool itself doesn't provide any action return () actions = [] for mid in globalBrowserMenuService._registry.keys(): menu = getMenu(mid, object, self.REQUEST) for entry in menu: # The action needs a unique name, so we'll build one # from the object_id and the action url. That is sure # to be unique. action = str(entry['action']) if object is None: act_id = 'action_%s' % action else: act_id = 'action_%s_%s' % (object.getId(), action) if entry['filter'] is None: filter = None else: filter = Expression(text=str(entry['filter'])) act = ActionInformation(id=act_id, title=str(entry['title']), action=Expression(text='string:%s' % action), condition=filter, category=str(mid), visible=1) actions.append(act) return tuple(actions)
class UndoTool(UniqueObject, SimpleItem, ActionProviderBase): """ This tool is used to undo changes. """ implements(IUndoTool) __implements__ = (z2IUndoTool, ActionProviderBase.__implements__) id = 'portal_undo' meta_type = 'CMF Undo Tool' security = ClassSecurityInfo() manage_options = (ActionProviderBase.manage_options + SimpleItem.manage_options + ({ 'label': 'Overview', 'action': 'manage_overview' }, )) # # ZMI methods # security.declareProtected(ManagePortal, 'manage_overview') manage_overview = DTMLResource('dtml/explainUndoTool', globals()) # # 'portal_undo' interface methods # security.declareProtected(ListUndoableChanges, 'listUndoableTransactionsFor') def listUndoableTransactionsFor(self, object, first_transaction=None, last_transaction=None, PrincipiaUndoBatchSize=None): '''Lists all transaction IDs the user is allowed to undo. ''' # arg list for undoable_transactions() changed in Zope 2.2. portal = self.aq_inner.aq_parent transactions = portal.undoable_transactions( first_transaction=first_transaction, last_transaction=last_transaction, PrincipiaUndoBatchSize=PrincipiaUndoBatchSize) for t in transactions: # Ensure transaction ids don't have embedded LF. t['id'] = t['id'].replace('\n', '') if not _checkPermission(ManagePortal, portal): # Filter out transactions done by other members of the portal. user_id = _getAuthenticatedUser(self).getId() transactions = filter(lambda record, user_id=user_id: record[ 'user_name'].split()[-1] == user_id, transactions) return transactions security.declarePublic('undo') def undo(self, object, transaction_info): """ Undo the list of transactions passed in 'transaction_info', first verifying that the current user is allowed to undo them. """ # Belt and suspenders: make sure that the user is actually # allowed to undo the transation(s) in transaction_info. xids = {} # set of allowed transaction IDs allowed = self.listUndoableTransactionsFor(object) for xid in map(lambda x: x['id'], allowed): xids[xid] = 1 if type(transaction_info) == type(''): transaction_info = [transaction_info] for tinfo in transaction_info: if not xids.get(tinfo, None): raise AccessControl_Unauthorized object.manage_undo_transactions(transaction_info)
class ActionProviderBase: """ Provide ActionTabs and management methods for ActionProviders """ implements(IActionProvider) __implements__ = z2IActionProvider security = ClassSecurityInfo() _actions = () _actions_form = DTMLResource('dtml/editToolsActions', globals()) manage_options = ({ 'label': 'Actions', 'action': 'manage_editActionsForm', 'help': ('CMFCore', 'Actions.stx') }, ) # # ActionProvider interface # security.declarePrivate('listActions') def listActions(self, info=None, object=None): """ List all the actions defined by a provider. """ return self._actions or () security.declarePrivate('getActionObject') def getActionObject(self, action): """Return the actions object or None if action doesn't exist. """ # separate cataegory and id from action sep = action.rfind('/') if sep == -1: raise ValueError('Actions must have the format <category>/<id>.') category, id = action[:sep], action[sep + 1:] # search for action and return first one found for ai in self.listActions(): if id == ai.getId() and category == ai.getCategory(): return ai # no action found return None security.declarePublic('listActionInfos') def listActionInfos(self, action_chain=None, object=None, check_visibility=1, check_permissions=1, check_condition=1, max=-1): # List ActionInfo objects. # (method is without docstring to disable publishing) # ec = self._getExprContext(object) actions = self.listActions(object=object) actions = [ActionInfo(action, ec) for action in actions] if action_chain: filtered_actions = [] if isinstance(action_chain, basestring): action_chain = (action_chain, ) for action_ident in action_chain: sep = action_ident.rfind('/') category, id = action_ident[:sep], action_ident[sep + 1:] for ai in actions: if id == ai['id'] and category == ai['category']: filtered_actions.append(ai) actions = filtered_actions action_infos = [] for ai in actions: if check_visibility and not ai['visible']: continue if check_permissions and not ai['allowed']: continue if check_condition and not ai['available']: continue action_infos.append(ai) if max + 1 and len(action_infos) >= max: break return action_infos security.declarePublic('getActionInfo') def getActionInfo(self, action_chain, object=None, check_visibility=0, check_condition=0): """ Get an ActionInfo object specified by a chain of actions. """ action_infos = self.listActionInfos(action_chain, object, check_visibility=check_visibility, check_permissions=False, check_condition=check_condition) if not action_infos: if object is None: provider = self else: provider = object msg = 'Action "%s" not available for %s' % (action_chain, '/'.join( provider.getPhysicalPath())) raise ValueError(msg) for ai in action_infos: if ai['allowed']: return ai raise AccessControl_Unauthorized('You are not allowed to access any ' 'of the specified Actions.') # # ZMI methods # security.declareProtected(ManagePortal, 'manage_editActionsForm') def manage_editActionsForm(self, REQUEST, manage_tabs_message=None): """ Show the 'Actions' management tab. """ actions = [ai.getMapping() for ai in self.listActions()] # possible_permissions is in AccessControl.Role.RoleManager. pp = self.possible_permissions() return self._actions_form(self, REQUEST, actions=actions, possible_permissions=pp, management_view='Actions', manage_tabs_message=manage_tabs_message) security.declareProtected(ManagePortal, 'addAction') def addAction(self, id, name, action, condition, permission, category, visible=1, REQUEST=None): """ Add an action to our list. """ if not name: raise ValueError('A name is required.') action = action and str(action) or '' condition = condition and str(condition) or '' if not isinstance(permission, tuple): permission = (str(permission), ) new_actions = self._cloneActions() new_action = ActionInformation(id=str(id), title=str(name), category=str(category), condition=condition, permissions=permission, visible=bool(visible), action=action) new_actions.append(new_action) self._actions = tuple(new_actions) if REQUEST is not None: return self.manage_editActionsForm(REQUEST, manage_tabs_message='Added.') security.declareProtected(ManagePortal, 'changeActions') def changeActions(self, properties=None, REQUEST=None): """ Update our list of actions. """ if properties is None: properties = REQUEST actions = [] for index in range(len(self._actions)): actions.append(self._extractAction(properties, index)) self._actions = tuple(actions) if REQUEST is not None: return self.manage_editActionsForm( REQUEST, manage_tabs_message='Actions changed.') security.declareProtected(ManagePortal, 'deleteActions') def deleteActions(self, selections=(), REQUEST=None): """ Delete actions indicated by indexes in 'selections'. """ sels = list(map(int, selections)) # Convert to a list of integers. old_actions = self._cloneActions() new_actions = [] for index in range(len(old_actions)): if index not in sels: new_actions.append(old_actions[index]) self._actions = tuple(new_actions) if REQUEST is not None: return self.manage_editActionsForm( REQUEST, manage_tabs_message=('Deleted %d action(s).' % len(sels))) security.declareProtected(ManagePortal, 'moveUpActions') def moveUpActions(self, selections=(), REQUEST=None): """ Move the specified actions up one slot in our list. """ sels = list(map(int, selections)) # Convert to a list of integers. sels.sort() new_actions = self._cloneActions() for idx in sels: idx2 = idx - 1 if idx2 < 0: # Wrap to the bottom. idx2 = len(new_actions) - 1 # Swap. a = new_actions[idx2] new_actions[idx2] = new_actions[idx] new_actions[idx] = a self._actions = tuple(new_actions) if REQUEST is not None: return self.manage_editActionsForm( REQUEST, manage_tabs_message=('Moved up %d action(s).' % len(sels))) security.declareProtected(ManagePortal, 'moveDownActions') def moveDownActions(self, selections=(), REQUEST=None): """ Move the specified actions down one slot in our list. """ sels = list(map(int, selections)) # Convert to a list of integers. sels.sort() sels.reverse() new_actions = self._cloneActions() for idx in sels: idx2 = idx + 1 if idx2 >= len(new_actions): # Wrap to the top. idx2 = 0 # Swap. a = new_actions[idx2] new_actions[idx2] = new_actions[idx] new_actions[idx] = a self._actions = tuple(new_actions) if REQUEST is not None: return self.manage_editActionsForm( REQUEST, manage_tabs_message=('Moved down %d action(s).' % len(sels))) # # Helper methods # security.declarePrivate('_cloneActions') def _cloneActions(self): """ Return a list of actions, cloned from our current list. """ return map(lambda x: x.clone(), list(self._actions)) security.declarePrivate('_extractAction') def _extractAction(self, properties, index): """ Extract an ActionInformation from the funky form properties. """ id = str(properties.get('id_%d' % index, '')) title = str(properties.get('name_%d' % index, '')) action = str(properties.get('action_%d' % index, '')) condition = str(properties.get('condition_%d' % index, '')) category = str(properties.get('category_%d' % index, '')) visible = bool(properties.get('visible_%d' % index, False)) permissions = properties.get('permission_%d' % index, ()) if not title: raise ValueError('A title is required.') if category == '': category = 'object' if isinstance(permissions, basestring): permissions = (permissions, ) return ActionInformation(id=id, title=title, action=action, condition=condition, permissions=permissions, category=category, visible=visible) def _getOAI(self, object): return getOAI(self, object) def _getExprContext(self, object): return getExprContext(self, object)
class FSFile(FSObject): """FSFiles act like images but are not directly modifiable from the management interface.""" # Note that OFS.Image.File is not a base class because it is mutable. meta_type = 'Filesystem File' manage_options = ({ 'label': 'Customize', 'action': 'manage_main' }, ) + Cacheable.manage_options security = ClassSecurityInfo() security.declareObjectProtected(View) def __init__(self, id, package=None, entry_subpath=None, filepath=None, fullname=None, properties=None): if fullname: id = fullname # Use the whole filename. FSObject.__init__(self, id, package, entry_subpath, filepath, fullname, properties) security.declareProtected(ViewManagementScreens, 'manage_main') manage_main = DTMLResource('dtml/custfile', globals()) content_type = 'unknown/unknown' def _createZODBClone(self): return File(self.getId(), '', self._readFile(1)) def _get_content_type(self, filename, body, id, content_type=None): # Consult self.content_type first, this is either # the default (unknown/unknown) or it got a value from a # .metadata file default_type = 'unknown/unknown' if getattr(self, 'content_type', default_type) != default_type: return self.content_type # Use the (imperfect) content type guessing # mechanism from OFS.Image, which ultimately uses the # Python mimetypes module. if not isinstance(body, basestring): body = body.data content_type, enc = guess_content_type(filename, body, content_type) return content_type def _getFilename(self): path = self._filepath or self._entry_subpath dir, fn = os.path.split(path) return fn def _readFile(self, reparse): data = self._readFileAsResourceOrDirect() if reparse or self.content_type == 'unknown/unknown': self.ZCacheable_invalidate() filename = self._getFilename() self.content_type = self._get_content_type(filename, data, self.id) return data #### The following is mainly taken from OFS/File.py ### def __str__(self): self._updateFromFS() return str(self._readFile(0)) security.declareProtected(View, 'index_html') def index_html(self, REQUEST, RESPONSE): """ The default view of the contents of a File or Image. Returns the contents of the file or image. Also, sets the Content-Type HTTP header to the objects content type. """ self._updateFromFS() data = self._readFile(0) data_len = len(data) last_mod = self._file_mod_time status = 200 # HTTP If-Modified-Since header handling. header = REQUEST.get_header('If-Modified-Since', None) if header is not None: header = header.split(';')[0] # Some proxies seem to send invalid date strings for this # header. If the date string is not valid, we ignore it # rather than raise an error to be generally consistent # with common servers such as Apache (which can usually # understand the screwy date string as a lucky side effect # of the way they parse it). try: mod_since = long(DateTime(header).timeTime()) except: mod_since = None if mod_since is not None: if last_mod > 0 and last_mod <= mod_since: status = 304 data = '' #Last-Modified will get stomped on by a cache policy it there is #one set.... RESPONSE.setStatus(status) RESPONSE.setHeader('Last-Modified', rfc1123_date(last_mod)) RESPONSE.setHeader('Content-Type', self.content_type) if status != 304: # Avoid setting content-length for a 304. See RFC 2616. # Zope might still, for better or for worse, set a # content-length header with value "0". RESPONSE.setHeader('Content-Length', data_len) #There are 2 Cache Managers which can be in play.... #need to decide which to use to determine where the cache headers #are decided on. if self.ZCacheable_getManager() is not None: self.ZCacheable_set(None) else: _setCacheHeaders(_ViewEmulator().__of__(self), extra_context={}) return data security.declareProtected(View, 'getContentType') def getContentType(self): """Get the content type of a file or image. Returns the content type (MIME type) of a file or image. """ self._updateFromFS() return self.content_type security.declareProtected(FTPAccess, 'manage_FTPget') manage_FTPget = index_html
class DirectoryViewSurrogate (Folder): """ Folderish DirectoryView. """ meta_type = 'Filesystem Directory View' all_meta_types = () _isDirectoryView = 1 security = ClassSecurityInfo() def __init__(self, real, data, objects): d = self.__dict__ d.update(data) d.update(real.__dict__) d['_real'] = real d['_objects'] = objects def __setattr__(self, name, value): d = self.__dict__ d[name] = value setattr(d['_real'], name, value) def __delattr__(self, name): d = self.__dict__ del d[name] delattr(d['_real'], name) security.declareProtected(ManagePortal, 'manage_propertiesForm') manage_propertiesForm = DTMLResource( 'dtml/dirview_properties' , globals() ) security.declareProtected(ManagePortal, 'manage_properties') def manage_properties( self, dirpath, REQUEST=None ): """ Update the directory path of the DirectoryView. """ self.__dict__['_real']._dirpath = _normalizeDirPath(dirpath) if REQUEST is not None: REQUEST['RESPONSE'].redirect( '%s/manage_propertiesForm' % self.absolute_url() ) security.declareProtected(AccessContentsInformation, 'getCustomizableObject') def getCustomizableObject(self): ob = aq_parent(aq_inner(self)) while getattr(ob, '_isDirectoryView', 0): ob = aq_parent(aq_inner(ob)) return ob security.declareProtected(AccessContentsInformation, 'listCustFolderPaths') def listCustFolderPaths(self, adding_meta_type=None): """ List possible customization folders as key, value pairs. """ rval = [] ob = self.getCustomizableObject() listFolderHierarchy(ob, '', rval, adding_meta_type) rval.sort() return rval security.declareProtected(AccessContentsInformation, 'getDirPath') def getDirPath(self): return self.__dict__['_real']._dirpath security.declarePublic('getId') def getId(self): return self.id
rval.sort() return rval security.declareProtected(AccessContentsInformation, 'getDirPath') def getDirPath(self): return self.__dict__['_real']._dirpath security.declarePublic('getId') def getId(self): return self.id InitializeClass(DirectoryViewSurrogate) #manage_addDirectoryViewForm = HTMLFile('dtml/addFSDirView', globals()) manage_addDirectoryViewForm = DTMLResource('dtml/addFSDirView', globals()) def createDirectoryView(parent, minimal_fp, id=None): """ Add either a DirectoryView or a derivative object. """ info = _dirreg.getDirectoryInfo(minimal_fp) if info is None: # maybe old data minimal_fp = _normalizeDirPath(minimal_fp) info = _dirreg.getDirectoryInfo(minimal_fp) if info is None: raise ValueError('Not a registered directory: %s' % minimal_fp) if not id: id = minimal_fp.split('/')[-1] else: id = str(id) ob = DirectoryView(id, minimal_fp)
class RegistrationTool(UniqueObject, SimpleItem, ActionProviderBase): """ Create and modify users by making calls to portal_membership. """ implements(IRegistrationTool) __implements__ = (z2IRegistrationTool, ActionProviderBase.__implements__) id = 'portal_registration' meta_type = 'CMF Registration Tool' member_id_pattern = '' default_member_id_pattern = "^[A-Za-z][A-Za-z0-9_]*$" _ALLOWED_MEMBER_ID_PATTERN = re.compile(default_member_id_pattern) security = ClassSecurityInfo() manage_options = (ActionProviderBase.manage_options + ({ 'label': 'Overview', 'action': 'manage_overview' }, { 'label': 'Configure', 'action': 'manage_configuration' }) + SimpleItem.manage_options) # # ZMI methods # security.declareProtected(ManagePortal, 'manage_overview') manage_overview = DTMLResource('dtml/explainRegistrationTool', globals()) security.declareProtected(ManagePortal, 'manage_configuration') manage_configuration = DTMLResource('dtml/configureRegistrationTool', globals()) security.declareProtected(ManagePortal, 'manage_editIDPattern') def manage_editIDPattern(self, pattern, REQUEST=None): """Edit the allowable member ID pattern TTW""" pattern.strip() if len(pattern) > 0: self.member_id_pattern = pattern self._ALLOWED_MEMBER_ID_PATTERN = re.compile(pattern) else: self.member_id_pattern = '' self._ALLOWED_MEMBER_ID_PATTERN = re.compile( self.default_member_id_pattern) if REQUEST is not None: msg = 'Member ID Pattern changed' return self.manage_configuration(manage_tabs_message=msg) security.declareProtected(ManagePortal, 'getIDPattern') def getIDPattern(self): """ Return the currently-used member ID pattern """ return self.member_id_pattern security.declareProtected(ManagePortal, 'getDefaultIDPattern') def getDefaultIDPattern(self): """ Return the currently-used member ID pattern """ return self.default_member_id_pattern # # 'portal_registration' interface methods # security.declarePublic('isRegistrationAllowed') def isRegistrationAllowed(self, REQUEST): '''Returns a boolean value indicating whether the user is allowed to add a member to the portal. ''' return _checkPermission(AddPortalMember, self.aq_inner.aq_parent) security.declarePublic('testPasswordValidity') def testPasswordValidity(self, password, confirm=None): '''If the password is valid, returns None. If not, returns a string explaining why. ''' return None security.declarePublic('testPropertiesValidity') def testPropertiesValidity(self, new_properties, member=None): '''If the properties are valid, returns None. If not, returns a string explaining why. ''' return None security.declarePublic('generatePassword') def generatePassword(self): """ Generate a valid password. """ # we don't use these to avoid typos: OQ0Il1 chars = 'ABCDEFGHJKLMNPRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789' return ''.join([choice(chars) for i in range(6)]) security.declareProtected(AddPortalMember, 'addMember') def addMember(self, id, password, roles=('Member', ), domains='', properties=None): '''Creates a PortalMember and returns it. The properties argument can be a mapping with additional member properties. Raises an exception if the given id already exists, the password does not comply with the policy in effect, or the authenticated user is not allowed to grant one of the roles listed (where Member is a special role that can always be granted); these conditions should be detected before the fact so that a cleaner message can be printed. ''' if not self.isMemberIdAllowed(id): raise ValueError( _('The login name you selected is already in ' 'use or is not valid. Please choose another.')) failMessage = self.testPasswordValidity(password) if failMessage is not None: raise ValueError(failMessage) if properties is not None: failMessage = self.testPropertiesValidity(properties) if failMessage is not None: raise ValueError(failMessage) # Limit the granted roles. # Anyone is always allowed to grant the 'Member' role. _limitGrantedRoles(roles, self, ('Member', )) membership = getToolByName(self, 'portal_membership') membership.addMember(id, password, roles, domains, properties) member = membership.getMemberById(id) self.afterAdd(member, id, password, properties) return member security.declareProtected(AddPortalMember, 'isMemberIdAllowed') def isMemberIdAllowed(self, id): '''Returns 1 if the ID is not in use and is not reserved. ''' if len(id) < 1 or id == 'Anonymous User': return 0 if not self._ALLOWED_MEMBER_ID_PATTERN.match(id): return 0 membership = getToolByName(self, 'portal_membership') if membership.getMemberById(id) is not None: return 0 return 1 security.declarePublic('afterAdd') def afterAdd(self, member, id, password, properties): '''Called by portal_registration.addMember() after a member has been added successfully.''' pass security.declareProtected(MailForgottenPassword, 'mailPassword') def mailPassword(self, forgotten_userid, REQUEST): '''Email a forgotten password to a member. Raises an exception if user ID is not found. ''' raise NotImplementedError
class ContentTypeRegistry(SimpleItem): """ Registry for rules which map PUT args to a CMF Type Object. """ implements(IContentTypeRegistry) __implements__ = z2IContentTypeRegistry meta_type = 'Content Type Registry' id = 'content_type_registry' manage_options = ({ 'label': 'Predicates', 'action': 'manage_predicates' }, { 'label': 'Test', 'action': 'manage_testRegistry' }) + SimpleItem.manage_options security = ClassSecurityInfo() def __init__(self): self.predicate_ids = () self.predicates = PersistentMapping() # # ZMI # security.declarePublic('listPredicateTypes') def listPredicateTypes(self): """ """ return map(lambda x: x[0], _predicate_types) security.declareProtected(ManagePortal, 'manage_predicates') manage_predicates = DTMLResource('dtml/registryPredList', globals()) security.declareProtected(ManagePortal, 'doAddPredicate') def doAddPredicate(self, predicate_id, predicate_type, REQUEST): """ """ self.addPredicate(predicate_id, predicate_type) REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_predicates' + '?manage_tabs_message=Predicate+added.') security.declareProtected(ManagePortal, 'doUpdatePredicate') def doUpdatePredicate(self, predicate_id, predicate, typeObjectName, REQUEST): """ """ self.updatePredicate(predicate_id, predicate, typeObjectName) REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_predicates' + '?manage_tabs_message=Predicate+updated.') security.declareProtected(ManagePortal, 'doMovePredicateUp') def doMovePredicateUp(self, predicate_id, REQUEST): """ """ predicate_ids = list(self.predicate_ids) ndx = predicate_ids.index(predicate_id) if ndx == 0: msg = "Predicate+already+first." else: self.reorderPredicate(predicate_id, ndx - 1) msg = "Predicate+moved." REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_predicates' + '?manage_tabs_message=%s' % msg) security.declareProtected(ManagePortal, 'doMovePredicateDown') def doMovePredicateDown(self, predicate_id, REQUEST): """ """ predicate_ids = list(self.predicate_ids) ndx = predicate_ids.index(predicate_id) if ndx == len(predicate_ids) - 1: msg = "Predicate+already+last." else: self.reorderPredicate(predicate_id, ndx + 1) msg = "Predicate+moved." REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_predicates' + '?manage_tabs_message=%s' % msg) security.declareProtected(ManagePortal, 'doRemovePredicate') def doRemovePredicate(self, predicate_id, REQUEST): """ """ self.removePredicate(predicate_id) REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_predicates' + '?manage_tabs_message=Predicate+removed.') security.declareProtected(ManagePortal, 'manage_testRegistry') manage_testRegistry = DTMLResource('dtml/registryTest', globals()) security.declareProtected(ManagePortal, 'doTestRegistry') def doTestRegistry(self, name, content_type, body, REQUEST): """ """ typeName = self.findTypeName(name, content_type, body) if typeName is None: typeName = '<unknown>' else: types_tool = getToolByName(self, 'portal_types') typeName = types_tool.getTypeInfo(typeName).Title() REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_testRegistry' + '?testResults=Type:+%s' % urllib.quote(typeName)) # # Predicate manipulation # security.declarePublic('getPredicate') def getPredicate(self, predicate_id): """ Find the predicate whose id is 'id'; return the predicate object, if found, or else None. """ return self.predicates.get(predicate_id, (None, None))[0] security.declarePublic('listPredicates') def listPredicates(self): """ Return a sequence of tuples, '( id, ( predicate, typeObjectName ) )' for all predicates in the registry """ result = [] for predicate_id in self.predicate_ids: result.append((predicate_id, self.predicates[predicate_id])) return tuple(result) security.declarePublic('getTypeObjectName') def getTypeObjectName(self, predicate_id): """ Find the predicate whose id is 'id'; return the name of the type object, if found, or else None. """ return self.predicates.get(predicate_id, (None, None))[1] security.declareProtected(ManagePortal, 'addPredicate') def addPredicate(self, predicate_id, predicate_type): """ Add a predicate to this element of type 'typ' to the registry. """ if predicate_id in self.predicate_ids: raise ValueError, "Existing predicate: %s" % predicate_id klass = None for key, value in _predicate_types: if key == predicate_type: klass = value if klass is None: raise ValueError, "Unknown predicate type: %s" % predicate_type self.predicates[predicate_id] = (klass(predicate_id), None) self.predicate_ids = self.predicate_ids + (predicate_id, ) security.declareProtected(ManagePortal, 'updatePredicate') def updatePredicate(self, predicate_id, predicate, typeObjectName): """ Update a predicate in this element. """ if not predicate_id in self.predicate_ids: raise ValueError, "Unknown predicate: %s" % predicate_id predObj = self.predicates[predicate_id][0] mapply(predObj.edit, (), predicate.__dict__) self.assignTypeName(predicate_id, typeObjectName) security.declareProtected(ManagePortal, 'removePredicate') def removePredicate(self, predicate_id): """ Remove a predicate from the registry. """ del self.predicates[predicate_id] idlist = list(self.predicate_ids) ndx = idlist.index(predicate_id) idlist = idlist[:ndx] + idlist[ndx + 1:] self.predicate_ids = tuple(idlist) security.declareProtected(ManagePortal, 'reorderPredicate') def reorderPredicate(self, predicate_id, newIndex): """ Move a given predicate to a new location in the list. """ idlist = list(self.predicate_ids) ndx = idlist.index(predicate_id) pred = idlist[ndx] idlist = idlist[:ndx] + idlist[ndx + 1:] idlist.insert(newIndex, pred) self.predicate_ids = tuple(idlist) security.declareProtected(ManagePortal, 'assignTypeName') def assignTypeName(self, predicate_id, typeObjectName): """ Bind the given predicate to a particular type object. """ pred, oldTypeObjName = self.predicates[predicate_id] self.predicates[predicate_id] = (pred, typeObjectName) # # ContentTypeRegistry interface # def findTypeName(self, name, typ, body): """ Perform a lookup over a collection of rules, returning the the name of the Type object corresponding to name/typ/body. Return None if no match found. """ for predicate_id in self.predicate_ids: pred, typeObjectName = self.predicates[predicate_id] if pred(name, typ, body): return typeObjectName return None
class MajorMinorPredicate(SimpleItem): """ Predicate matching on 'major/minor' content types. Empty major or minor implies wildcard (all match). """ implements(IContentTypeRegistryPredicate) __implements__ = z2IContentTypeRegistryPredicate major = minor = None PREDICATE_TYPE = 'major_minor' security = ClassSecurityInfo() def __init__(self, id): self.id = id security.declareProtected(ManagePortal, 'getMajorType') def getMajorType(self): """ Get major content types. """ if self.major is None: return 'None' return ' '.join(self.major) security.declareProtected(ManagePortal, 'getMinorType') def getMinorType(self): """ Get minor content types. """ if self.minor is None: return 'None' return ' '.join(self.minor) security.declareProtected(ManagePortal, 'edit') def edit(self, major, minor, COMMA_SPLIT=re.compile(r'[, ]')): if major == 'None': major = None if type(major) is type(''): major = filter(None, COMMA_SPLIT.split(major)) if minor == 'None': minor = None if type(minor) is type(''): minor = filter(None, COMMA_SPLIT.split(minor)) self.major = major self.minor = minor # # ContentTypeRegistryPredicate interface # security.declareObjectPublic() def __call__(self, name, typ, body): """ Return true if the rule matches, else false. """ if self.major is None: return 0 if self.minor is None: return 0 typ = typ or '/' if not '/' in typ: typ = typ + '/' major, minor = typ.split('/', 1) if self.major and not major in self.major: return 0 if self.minor and not minor in self.minor: return 0 return 1 security.declareProtected(ManagePortal, 'getTypeLabel') def getTypeLabel(self): """ Return a human-readable label for the predicate type. """ return self.PREDICATE_TYPE security.declareProtected(ManagePortal, 'predicateWidget') predicateWidget = DTMLResource('dtml/majorMinorWidget', globals())
class TypesTool(UniqueObject, IFAwareObjectManager, Folder, ActionProviderBase): """ Provides a configurable registry of portal content types. """ implements(ITypesTool) __implements__ = (z2ITypesTool, ActionProviderBase.__implements__) id = 'portal_types' meta_type = 'CMF Types Tool' _product_interfaces = (ITypeInformation, ) security = ClassSecurityInfo() manage_options = (Folder.manage_options[:1] + ({ 'label': 'Aliases', 'action': 'manage_aliases' }, ) + ActionProviderBase.manage_options + ({ 'label': 'Overview', 'action': 'manage_overview' }, ) + Folder.manage_options[1:]) # # ZMI methods # security.declareProtected(ManagePortal, 'manage_overview') manage_overview = DTMLResource('dtml/explainTypesTool', globals()) security.declareProtected(ManagePortal, 'manage_aliases') manage_aliases = PageTemplateResource('typesAliases.zpt', globals()) # # ObjectManager methods # def all_meta_types(self): # this is a workaround and should be removed again if allowedTypes # have an interface we can use in _product_interfaces all = TypesTool.inheritedAttribute('all_meta_types')(self) others = [ mt for mt in Products.meta_types if mt['name'] in allowedTypes ] return tuple(all) + tuple(others) # # other methods # security.declareProtected(ManagePortal, 'manage_addTypeInformation') def manage_addTypeInformation(self, add_meta_type, id=None, typeinfo_name=None, RESPONSE=None): """Create a TypeInformation in self. """ # BBB: typeinfo_name is ignored if not id: raise BadRequest('An id is required.') for mt in Products.meta_types: if mt['name'] == add_meta_type: klass = mt['instance'] break else: raise ValueError, ('Meta type %s is not a type class.' % add_meta_type) id = str(id) ob = klass(id) self._setObject(id, ob) if RESPONSE is not None: RESPONSE.redirect('%s/manage_main' % self.absolute_url()) security.declareProtected(ManagePortal, 'manage_setTIMethodAliases') def manage_setTIMethodAliases(self, REQUEST): """ Config method aliases. """ form = REQUEST.form aliases = {} for k, v in form['aliases'].items(): v = v.strip() if v: aliases[k] = v for ti in self.listTypeInfo(): _dict = {} for k, v in form[ti.getId()].items(): if aliases.has_key(k): _dict[aliases[k]] = v ti.setMethodAliases(_dict) REQUEST.RESPONSE.redirect('%s/manage_aliases' % self.absolute_url()) security.declareProtected(AccessContentsInformation, 'getTypeInfo') def getTypeInfo(self, contentType): """ Return an instance which implements the TypeInformation interface, corresponding to the specified 'contentType'. If contentType is actually an object, rather than a string, attempt to look up the appropriate type info using its portal_type. """ if not isinstance(contentType, basestring): if hasattr(aq_base(contentType), 'getPortalTypeName'): contentType = contentType.getPortalTypeName() if contentType is None: return None else: return None ob = getattr(self, contentType, None) if getattr(aq_base(ob), '_isTypeInformation', 0): return ob else: return None security.declareProtected(AccessContentsInformation, 'listTypeInfo') def listTypeInfo(self, container=None): """ Return a sequence of instances which implement the TypeInformation interface, one for each content type registered in the portal. """ rval = [] for t in self.objectValues(): # Filter out things that aren't TypeInformation and # types for which the user does not have adequate permission. if not getattr(aq_base(t), '_isTypeInformation', 0): continue if not t.getId(): # XXX What's this used for ? # Not ready. continue # check we're allowed to access the type object if container is not None: if not t.isConstructionAllowed(container): continue rval.append(t) return rval security.declareProtected(AccessContentsInformation, 'listContentTypes') def listContentTypes(self, container=None, by_metatype=0): """ List type info IDs. Passing 'by_metatype' is deprecated (type information may not correspond 1:1 to an underlying meta_type). This argument will be removed when CMFCore/dtml/catalogFind.dtml doesn't need it anymore. """ typenames = {} for t in self.listTypeInfo(container): if by_metatype: warn( 'TypeInformation.listContentTypes(by_metatype=1) is ' 'deprecated.', DeprecationWarning) name = t.Metatype() else: name = t.getId() if name: typenames[name] = None result = typenames.keys() result.sort() return result security.declarePublic('constructContent') def constructContent(self, type_name, container, id, RESPONSE=None, *args, **kw): """ Build an instance of the appropriate content class in 'container', using 'id'. """ info = self.getTypeInfo(type_name) if info is None: raise ValueError('No such content type: %s' % type_name) ob = info.constructInstance(container, id, *args, **kw) if RESPONSE is not None: immediate_url = '%s/%s' % (ob.absolute_url(), info.immediate_view) RESPONSE.redirect(immediate_url) return ob.getId() security.declarePrivate('listActions') def listActions(self, info=None, object=None): """ List all the actions defined by a provider. """ actions = list(self._actions) if object is None and info is not None: object = info.object if object is not None: type_info = self.getTypeInfo(object) if type_info is not None: actions.extend(type_info.listActions()) return actions security.declareProtected(ManagePortal, 'listMethodAliasKeys') def listMethodAliasKeys(self): """ List all defined method alias names. """ _dict = {} for ti in self.listTypeInfo(): aliases = ti.getMethodAliases() for k, v in aliases.items(): _dict[k] = 1 rval = _dict.keys() rval.sort() return rval
class SkinsTool(UniqueObject, SkinsContainer, Folder, ActionProviderBase): """ This tool is used to supply skins to a portal. """ implements(ISkinsTool) __implements__ = (z2ISkinsTool, SkinsContainer.__implements__, ActionProviderBase.__implements__) id = 'portal_skins' meta_type = 'CMF Skins Tool' allow_any = 0 cookie_persistence = 0 default_skin = '' request_varname = 'portal_skin' selections = None security = ClassSecurityInfo() manage_options = (modifiedOptions() + ({ 'label': 'Overview', 'action': 'manage_overview' }, ) + ActionProviderBase.manage_options) def __init__(self): self.selections = PersistentMapping() def _getSelections(self): sels = self.selections if sels is None: # Backward compatibility. self.selections = sels = PersistentMapping() return sels # # ZMI methods # security.declareProtected(ManagePortal, 'manage_overview') manage_overview = DTMLResource('dtml/explainSkinsTool', globals()) security.declareProtected(ManagePortal, 'manage_propertiesForm') manage_propertiesForm = DTMLResource('dtml/skinProps', globals()) # the following two methods override those in FindSupport, to # support marking of objects used in specific skins security.declareProtected(ManagePortal, 'manage_findResult') manage_findResult = DTMLResource('dtml/findResult', globals(), management_view='Find') security.declareProtected(ManagePortal, 'manage_findForm') manage_findForm = DTMLResource('dtml/findForm', globals(), management_view='Find') security.declareProtected(ManagePortal, 'manage_skinLayers') def manage_skinLayers(self, chosen=(), add_skin=0, del_skin=0, skinname='', skinpath='', REQUEST=None): """ Change the skinLayers. """ sels = self._getSelections() if del_skin: for name in chosen: del sels[name] if REQUEST is not None: for key in sels.keys(): fname = 'skinpath_%s' % key val = REQUEST[fname] # if val is a list from the new lines field # then munge it back into a comma delimited list # for hysterical reasons if isinstance(val, list): val = ','.join([layer.strip() for layer in val]) if sels[key] != val: self.testSkinPath(val) sels[key] = val if add_skin: skinpath = ','.join([layer.strip() for layer in skinpath]) self.testSkinPath(skinpath) sels[str(skinname)] = skinpath if REQUEST is not None: return self.manage_propertiesForm( self, REQUEST, management_view='Properties', manage_tabs_message='Skins changed.') security.declareProtected(ManagePortal, 'isFirstInSkin') def isFirstInSkin(self, template_path, skin=None): """ Is the specified template the one that would get returned from the current skin? """ if skin is None or skin == 'None': skin = self.getDefaultSkin() template = self.restrictedTraverse(template_path) name = template.getId() skin_path = self.getSkinPath(skin) if not skin_path: return 0 parts = list(skin_path.split(",")) found = "" for part in parts: part = part.strip() if part[0] == "_": continue partob = getattr(self, part, None) if partob: skin_template = getattr(partob.aq_base, name, None) if skin_template: found = skin_template break if found == template: return 1 else: return 0 security.declareProtected(ManagePortal, 'manage_properties') def manage_properties(self, default_skin='', request_varname='', allow_any=0, chosen=(), add_skin=0, del_skin=0, skinname='', skinpath='', cookie_persistence=0, REQUEST=None): """ Changes portal_skin properties. """ self.default_skin = str(default_skin) self.request_varname = str(request_varname) self.allow_any = allow_any and 1 or 0 self.cookie_persistence = cookie_persistence and 1 or 0 if REQUEST is not None: return self.manage_propertiesForm( self, REQUEST, management_view='Properties', manage_tabs_message='Properties changed.') security.declarePrivate('PUT_factory') def PUT_factory(self, name, typ, body): """ Dispatcher for PUT requests to non-existent IDs. Returns an object of the appropriate type (or None, if we don't know what to do). """ major, minor = typ.split('/', 1) if major == 'image': return Image(id=name, title='', file='', content_type=typ) if major == 'text': if minor == 'x-python': return PythonScript(id=name) if minor in ('html', 'xml'): return ZopePageTemplate(name) return DTMLMethod(__name__=name) return None # Make the PUT_factory replaceable PUT_factory__replaceable__ = REPLACEABLE security.declarePrivate('testSkinPath') def testSkinPath(self, p): """ Calls SkinsContainer.getSkinByPath(). """ self.getSkinByPath(p, raise_exc=1) # # 'SkinsContainer' interface methods # security.declareProtected(AccessContentsInformation, 'getSkinPath') def getSkinPath(self, name): """ Convert a skin name to a skin path. """ sels = self._getSelections() p = sels.get(name, None) if p is None: if self.allow_any: return name return p # Can be None security.declareProtected(AccessContentsInformation, 'getDefaultSkin') def getDefaultSkin(self): """ Get the default skin name. """ return self.default_skin security.declareProtected(AccessContentsInformation, 'getRequestVarname') def getRequestVarname(self): """ Get the variable name to look for in the REQUEST. """ return self.request_varname # # UI methods # security.declareProtected(AccessContentsInformation, 'getAllowAny') def getAllowAny(self): ''' Used by the management UI. Returns a flag indicating whether users are allowed to use arbitrary skin paths. ''' return self.allow_any security.declareProtected(AccessContentsInformation, 'getCookiePersistence') def getCookiePersistence(self): ''' Used by the management UI. Returns a flag indicating whether the skins cookie is persistent or not. ''' return self.cookie_persistence security.declareProtected(AccessContentsInformation, 'getSkinPaths') def getSkinPaths(self): ''' Used by the management UI. Returns the list of skin name to skin path mappings as a sorted list of tuples. ''' sels = self._getSelections() rval = [] for key, value in sels.items(): rval.append((key, value)) rval.sort() return rval # # 'portal_skins' interface methods # security.declarePublic('getSkinSelections') def getSkinSelections(self): """ Get the sorted list of available skin names. """ sels = self._getSelections() rval = list(sels.keys()) rval.sort() return rval security.declareProtected(View, 'updateSkinCookie') def updateSkinCookie(self): """ If needed, updates the skin cookie based on the member preference. """ mtool = getToolByName(self, 'portal_membership') utool = getToolByName(self, 'portal_url') member = mtool.getAuthenticatedMember() if hasattr(aq_base(member), 'portal_skin'): mskin = member.portal_skin if mskin: req = self.REQUEST cookie = req.cookies.get(self.request_varname, None) if cookie != mskin: resp = req.RESPONSE portal_path = req['BASEPATH1'] + '/' + utool(1) if not self.cookie_persistence: # *Don't* make the cookie persistent! resp.setCookie(self.request_varname, mskin, path=portal_path) else: expires = (DateTime('GMT') + 365).rfc822() resp.setCookie(self.request_varname, mskin, path=portal_path, expires=expires) # Ensure updateSkinCookie() doesn't try again # within this request. req.cookies[self.request_varname] = mskin req[self.request_varname] = mskin return 1 return 0 security.declareProtected(View, 'clearSkinCookie') def clearSkinCookie(self): """ Expire the skin cookie. """ req = self.REQUEST resp = req.RESPONSE utool = getToolByName(self, 'portal_url') portal_path = req['BASEPATH1'] + '/' + utool(1) resp.expireCookie(self.request_varname, path=portal_path) security.declareProtected(ManagePortal, 'addSkinSelection') def addSkinSelection(self, skinname, skinpath, test=0, make_default=0): ''' Adds a skin selection. ''' sels = self._getSelections() skinpath = str(skinpath) # Basic precaution to make sure the stuff we want to ignore in # DirectoryViews gets prevented from ending up in a skin path path_elems = [x.strip() for x in skinpath.split(',')] ignored = base_ignore + ignore for elem in path_elems[:]: if elem in ignored or ignore_re.match(elem): path_elems.remove(elem) skinpath = ','.join(path_elems) if test: self.testSkinPath(skinpath) sels[str(skinname)] = skinpath if make_default: self.default_skin = skinname
class FSPageTemplate(FSObject, Script, PageTemplate): """Wrapper for Page Template. """ meta_type = 'Filesystem Page Template' _owner = None # Unowned manage_options = (( { 'label': 'Customize', 'action': 'manage_main' }, { 'label': 'Test', 'action': 'ZScriptHTML_tryForm' }, ) + Cacheable.manage_options) security = ClassSecurityInfo() security.declareObjectProtected(View) security.declareProtected(ViewManagementScreens, 'manage_main') manage_main = DTMLResource('dtml/custpt', globals()) # Declare security for unprotected PageTemplate methods. security.declarePrivate('pt_edit', 'write') def __init__(self, id, package=None, entry_subpath=None, filepath=None, fullname=None, properties=None): FSObject.__init__(self, id, package, entry_subpath, filepath, fullname, properties) self.ZBindings_edit(self._default_bindings) def _createZODBClone(self): """Create a ZODB (editable) equivalent of this object.""" obj = ZopePageTemplate(self.getId(), self._text, self.content_type) obj.expand = 0 obj.write(self.read()) return obj # def ZCacheable_isCachingEnabled(self): # return 0 def _readFile(self, reparse): data = self._readFileAsResourceOrDirect() if reparse: # If we already have a content_type set it must come from a # .metadata file and we should always honor that. The content # type is initialized as text/html by default, so we only # attempt further detection if the default is encountered. # One previous misbehavior remains: It is not possible to # force a text./html type if parsing detects it as XML. if getattr(self, 'content_type', 'text/html') == 'text/html': xml_info = xml_detect_re.match(data) if xml_info: # Smells like xml # set "content_type" from the XML declaration encoding = xml_info.group(1) or 'utf-8' self.content_type = 'text/xml; charset=%s' % encoding self.write(data) security.declarePrivate('read') def read(self): # Tie in on an opportunity to auto-update self._updateFromFS() return FSPageTemplate.inheritedAttribute('read')(self) ### The following is mainly taken from ZopePageTemplate.py ### expand = 0 func_defaults = None func_code = ZopePageTemplate.func_code _default_bindings = ZopePageTemplate._default_bindings security.declareProtected(View, '__call__') def pt_macros(self): # Tie in on an opportunity to auto-reload self._updateFromFS() return FSPageTemplate.inheritedAttribute('pt_macros')(self) def pt_render(self, source=0, extra_context={}): self._updateFromFS() # Make sure the template has been loaded. if not source: # If we have a conditional get, set status 304 and return # no content if _checkConditionalGET(self, extra_context): return '' result = FSPageTemplate.inheritedAttribute('pt_render')(self, source, extra_context) if not source: _setCacheHeaders(self, extra_context) return result security.declareProtected(ViewManagementScreens, 'pt_source_file') def pt_source_file(self): """ Return a file name to be compiled into the TAL code. """ return 'file:%s' % self._filepath security.declarePrivate('_ZPT_exec') _ZPT_exec = ZopePageTemplate._exec.im_func security.declarePrivate('_exec') def _exec(self, bound_names, args, kw): """Call a FSPageTemplate""" try: response = self.REQUEST.RESPONSE except AttributeError: response = None # Read file first to get a correct content_type default value. self._updateFromFS() if not kw.has_key('args'): kw['args'] = args bound_names['options'] = kw try: response = self.REQUEST.RESPONSE if not response.headers.has_key('content-type'): response.setHeader('content-type', self.content_type) except AttributeError: pass security = getSecurityManager() bound_names['user'] = security.getUser() # Retrieve the value from the cache. keyset = None if self.ZCacheable_isCachingEnabled(): # Prepare a cache key. keyset = { # Why oh why? # All this code is cut and paste # here to make sure that we # dont call _getContext and hence can't cache # Annoying huh? 'here': self.aq_parent.getPhysicalPath(), 'bound_names': bound_names } result = self.ZCacheable_get(keywords=keyset) if result is not None: # Got a cached value. return result # Execute the template in a new security context. security.addContext(self) try: result = self.pt_render(extra_context=bound_names) if keyset is not None: # Store the result in the cache. self.ZCacheable_set(result, keywords=keyset) return result finally: security.removeContext(self) return result # Copy over more methods security.declareProtected(FTPAccess, 'manage_FTPget') manage_FTPget = ZopePageTemplate.manage_FTPget.im_func security.declareProtected(View, 'get_size') get_size = ZopePageTemplate.get_size.im_func getSize = get_size security.declareProtected(ViewManagementScreens, 'PrincipiaSearchSource') PrincipiaSearchSource = ZopePageTemplate.PrincipiaSearchSource.im_func security.declareProtected(ViewManagementScreens, 'document_src') document_src = ZopePageTemplate.document_src.im_func pt_getContext = ZopePageTemplate.pt_getContext.im_func ZScriptHTML_tryParams = ZopePageTemplate.ZScriptHTML_tryParams.im_func source_dot_xml = Src()
class FSPythonScript (FSObject, Script): """FSPythonScripts act like Python Scripts but are not directly modifiable from the management interface.""" meta_type = 'Filesystem Script (Python)' _params = _body = '' _v_f = None _proxy_roles = () _owner = None # Unowned manage_options=( ( {'label':'Customize', 'action':'manage_main'}, {'label':'Test', 'action':'ZScriptHTML_tryForm', 'help': ('PythonScripts', 'PythonScript_test.stx')}, ) + Cacheable.manage_options ) # Use declarative security security = ClassSecurityInfo() security.declareObjectProtected(View) security.declareProtected(View, 'index_html',) # Prevent the bindings from being edited TTW security.declarePrivate('ZBindings_edit','ZBindingsHTML_editForm', 'ZBindingsHTML_editAction') security.declareProtected(ViewManagementScreens, 'manage_main') manage_main = DTMLResource('dtml/custpy', globals()) def _createZODBClone(self): """Create a ZODB (editable) equivalent of this object.""" obj = PythonScript(self.getId()) obj.write(self.read()) return obj def _readFile(self, reparse): """Read the data from the filesystem. Read the file (indicated by exandpath(self._filepath), and parse the data if necessary. """ data = self._readFileAsResourceOrDirect() self._write(data, reparse) def _validateProxy(self, roles=None): pass def __render_with_namespace__(self, namespace): '''Calls the script.''' self._updateFromFS() return Script.__render_with_namespace__(self, namespace) def __call__(self, *args, **kw): '''Calls the script.''' self._updateFromFS() return Script.__call__(self, *args, **kw) #### The following is mainly taken from PythonScript.py ### def _exec(self, bound_names, args, kw): """Call a Python Script Calling a Python Script is an actual function invocation. """ # do caching keyset = None if self.ZCacheable_isCachingEnabled(): # Prepare a cache key. keyset = kw.copy() asgns = self.getBindingAssignments() name_context = asgns.getAssignedName('name_context', None) if name_context: keyset[name_context] = self.aq_parent.getPhysicalPath() name_subpath = asgns.getAssignedName('name_subpath', None) if name_subpath: keyset[name_subpath] = self._getTraverseSubpath() # Note: perhaps we should cache based on name_ns also. keyset['*'] = args result = self.ZCacheable_get(keywords=keyset, default=_marker) if result is not _marker: # Got a cached value. return result # Prepare the function. f = self._v_f if f is None: # The script has errors. __traceback_supplement__ = ( FSPythonScriptTracebackSupplement, self, 0) raise RuntimeError, '%s has errors.' % self._filepath # Updating func_globals directly is not thread safe here. # In normal PythonScripts, every thread has its own # copy of the function. But in FSPythonScripts # there is only one copy. So here's another way. new_globals = f.func_globals.copy() new_globals['__traceback_supplement__'] = ( FSPythonScriptTracebackSupplement, self) if bound_names: new_globals.update(bound_names) if f.func_defaults: f = new.function(f.func_code, new_globals, f.func_name, f.func_defaults) else: f = new.function(f.func_code, new_globals, f.func_name) # Execute the function in a new security context. security=getSecurityManager() security.addContext(self) try: result = f(*args, **kw) if keyset is not None: # Store the result in the cache. self.ZCacheable_set(result, keywords=keyset) return result finally: security.removeContext(self) security.declareProtected(ViewManagementScreens, 'getModTime') # getModTime defined in FSObject security.declareProtected(ViewManagementScreens, 'ZScriptHTML_tryForm') # ZScriptHTML_tryForm defined in Shared.DC.Scripts.Script.Script def ZScriptHTML_tryParams(self): """Parameters to test the script with.""" param_names = [] for name in self._params.split(','): name = name.strip() if name and name[0] != '*': param_names.append( name.split('=', 1)[0] ) return param_names security.declareProtected(ViewManagementScreens, 'read') def read(self): self._updateFromFS() return self._source security.declareProtected(ViewManagementScreens, 'document_src') def document_src(self, REQUEST=None, RESPONSE=None): """Return unprocessed document source.""" if RESPONSE is not None: RESPONSE.setHeader('Content-Type', 'text/plain') return self._source security.declareProtected(ViewManagementScreens, 'PrincipiaSearchSource') def PrincipiaSearchSource(self): "Support for searching - the document's contents are searched." return "%s\n%s" % (self._params, self._body) security.declareProtected(ViewManagementScreens, 'params') def params(self): return self._params security.declareProtected(ViewManagementScreens, 'manage_haveProxy') manage_haveProxy = PythonScript.manage_haveProxy.im_func security.declareProtected(ViewManagementScreens, 'body') def body(self): return self._body security.declareProtected(ViewManagementScreens, 'get_size') def get_size(self): return len(self.read()) security.declareProtected(FTPAccess, 'manage_FTPget') def manage_FTPget(self): "Get source for FTP download" self.REQUEST.RESPONSE.setHeader('Content-Type', 'text/plain') return self.read() def _write(self, text, compile): ''' Parses the source, storing the body, params, title, bindings, and source in self. If compile is set, compiles the function. ''' ps = PythonScript(self.id) ps.write(text) if compile: ps._makeFunction(1) self._v_f = f = ps._v_f if f is not None: self.func_code = f.func_code self.func_defaults = f.func_defaults else: # There were errors in the compile. # No signature. self.func_code = bad_func_code() self.func_defaults = None self._body = ps._body self._params = ps._params if not self.title: self.title = ps.title self._setupBindings(ps.getBindingAssignments().getAssignedNames()) self._source = ps.read() # Find out what the script sees. def func_defaults(self): # This ensures func_code and func_defaults are # set when the code hasn't been compiled yet, # just in time for mapply(). Truly odd, but so is mapply(). :P self._updateFromFS() return self.__dict__.get('func_defaults', None) func_defaults = ComputedAttribute(func_defaults, 1) def func_code(self): # See func_defaults. self._updateFromFS() return self.__dict__.get('func_code', None) func_code = ComputedAttribute(func_code, 1) def title(self): # See func_defaults. self._updateFromFS() return self.__dict__.get('title', None) title = ComputedAttribute(title, 1) def getBindingAssignments(self): # Override of the version in Bindings.py. # This version ensures that bindings get loaded on demand. if not hasattr(self, '_bind_names'): # Set a default first to avoid recursion self._setupBindings() # Now do it for real self._updateFromFS() return self._bind_names
class MembershipTool(UniqueObject, Folder, ActionProviderBase): """ This tool accesses member data through an acl_users object. It can be replaced with something that accesses member data in a different way. """ implements(IMembershipTool) __implements__ = (z2IMembershipTool, ActionProviderBase.__implements__) id = 'portal_membership' meta_type = 'CMF Membership Tool' memberareaCreationFlag = 1 security = ClassSecurityInfo() manage_options=( ({ 'label' : 'Configuration' , 'action' : 'manage_mapRoles' },) + ActionProviderBase.manage_options + ( { 'label' : 'Overview' , 'action' : 'manage_overview' }, ) + Folder.manage_options) # # ZMI methods # security.declareProtected(ManagePortal, 'manage_overview') manage_overview = DTMLResource( 'dtml/explainMembershipTool', globals() ) # # 'portal_membership' interface methods # security.declareProtected(ManagePortal, 'manage_mapRoles') manage_mapRoles = DTMLResource('dtml/membershipRolemapping', globals() ) security.declareProtected(SetOwnPassword, 'setPassword') def setPassword(self, password, domains=None): '''Allows the authenticated member to set his/her own password. ''' registration = getToolByName(self, 'portal_registration', None) if not self.isAnonymousUser(): member = self.getAuthenticatedMember() if registration: failMessage = registration.testPasswordValidity(password) if failMessage is not None: raise BadRequest(failMessage) member.setSecurityProfile(password=password, domains=domains) else: raise BadRequest('Not logged in.') security.declarePublic('getAuthenticatedMember') def getAuthenticatedMember(self): ''' Returns the currently authenticated member object or the Anonymous User. Never returns None. ''' u = _getAuthenticatedUser(self) if u is None: u = nobody return self.wrapUser(u) security.declarePrivate('wrapUser') def wrapUser(self, u, wrap_anon=0): """ Set up the correct acquisition wrappers for a user object. Provides an opportunity for a portal_memberdata tool to retrieve and store member data independently of the user object. """ b = getattr(u, 'aq_base', None) if b is None: # u isn't wrapped at all. Wrap it in self.acl_users. b = u u = u.__of__(self.acl_users) if (b is nobody and not wrap_anon) or hasattr(b, 'getMemberId'): # This user is either not recognized by acl_users or it is # already registered with something that implements the # member data tool at least partially. return u # Apply any role mapping if we have it if hasattr(self, 'role_map'): for portal_role in self.role_map.keys(): if (self.role_map.get(portal_role) in u.roles and portal_role not in u.roles): u.roles.append(portal_role) mdtool = getToolByName(self, 'portal_memberdata', None) if mdtool is not None: try: u = mdtool.wrapUser(u) except ConflictError: raise except: from zLOG import LOG, ERROR import sys LOG('CMFCore.MembershipTool', ERROR, 'Error during wrapUser', error=sys.exc_info(), ) return u security.declareProtected(ManagePortal, 'getPortalRoles') def getPortalRoles(self): """ Return all local roles defined by the portal itself, which means roles that are useful and understood by the portal object """ parent = self.aq_inner.aq_parent roles = list( parent.userdefined_roles() ) # This is *not* a local role in the portal but used by it roles.append('Manager') roles.append('Owner') return roles security.declareProtected(ManagePortal, 'setRoleMapping') def setRoleMapping(self, portal_role, userfolder_role): """ set the mapping of roles between roles understood by the portal and roles coming from outside user sources """ if not hasattr(self, 'role_map'): self.role_map = PersistentMapping() if len(userfolder_role) < 1: del self.role_map[portal_role] else: self.role_map[portal_role] = userfolder_role return MessageDialog( title ='Mapping updated', message='The Role mappings have been updated', action ='manage_mapRoles') security.declareProtected(ManagePortal, 'getMappedRole') def getMappedRole(self, portal_role): """ returns a role name if the portal role is mapped to something else or an empty string if it is not """ if hasattr(self, 'role_map'): return self.role_map.get(portal_role, '') else: return '' security.declarePublic('getMembersFolder') def getMembersFolder(self): """ Get the members folder object. """ parent = aq_parent( aq_inner(self) ) members = getattr(parent, 'Members', None) return members security.declareProtected(ManagePortal, 'getMemberareaCreationFlag') def getMemberareaCreationFlag(self): """ Returns the flag indicating whether the membership tool will create a member area if an authenticated user from an underlying user folder logs in first without going through the join process """ return self.memberareaCreationFlag security.declareProtected(ManagePortal, 'setMemberareaCreationFlag') def setMemberareaCreationFlag(self): """ sets the flag indicating whether the membership tool will create a member area if an authenticated user from an underlying user folder logs in first without going through the join process """ if not hasattr(self, 'memberareaCreationFlag'): self.memberareaCreationFlag = 0 if self.memberareaCreationFlag == 0: self.memberareaCreationFlag = 1 else: self.memberareaCreationFlag = 0 return MessageDialog( title ='Member area creation flag changed', message='Member area creation flag has been updated', action ='manage_mapRoles') security.declarePublic('createMemberArea') def createMemberArea(self, member_id=''): """ Create a member area for 'member_id' or authenticated user. """ if not self.getMemberareaCreationFlag(): return None members = self.getMembersFolder() if not members: return None if self.isAnonymousUser(): return None # Note: We can't use getAuthenticatedMember() and getMemberById() # because they might be wrapped by MemberDataTool. user = _getAuthenticatedUser(self) user_id = user.getId() if member_id in ('', user_id): member = user member_id = user_id else: if _checkPermission(ManageUsers, self): member = self.acl_users.getUserById(member_id, None) if member: member = member.__of__(self.acl_users) else: raise ValueError('Member %s does not exist' % member_id) else: return None if hasattr( aq_base(members), member_id ): return None else: f_title = "%s's Home" % member_id members.manage_addPortalFolder( id=member_id, title=f_title ) f=getattr(members, member_id) f.manage_permission(View, ['Owner','Manager','Reviewer'], 0) f.manage_permission(AccessContentsInformation, ['Owner','Manager','Reviewer'], 0) # Grant Ownership and Owner role to Member f.changeOwnership(member) f.__ac_local_roles__ = None f.manage_setLocalRoles(member_id, ['Owner']) return f security.declarePublic('createMemberarea') createMemberarea = createMemberArea security.declareProtected(ManageUsers, 'deleteMemberArea') def deleteMemberArea(self, member_id): """ Delete member area of member specified by member_id. """ members = self.getMembersFolder() if not members: return 0 if hasattr( aq_base(members), member_id ): members.manage_delObjects(member_id) return 1 else: return 0 security.declarePublic('isAnonymousUser') def isAnonymousUser(self): ''' Returns 1 if the user is not logged in. ''' u = _getAuthenticatedUser(self) if u is None or u.getUserName() == 'Anonymous User': return 1 return 0 security.declarePublic('checkPermission') def checkPermission(self, permissionName, object, subobjectName=None): ''' Checks whether the current user has the given permission on the given object or subobject. ''' if subobjectName is not None: object = getattr(object, subobjectName) return _checkPermission(permissionName, object) security.declarePublic('credentialsChanged') def credentialsChanged(self, password): ''' Notifies the authentication mechanism that this user has changed passwords. This can be used to update the authentication cookie. Note that this call should *not* cause any change at all to user databases. ''' if not self.isAnonymousUser(): acl_users = self.acl_users user = _getAuthenticatedUser(self) name = user.getUserName() # this really does need to be the user name, and not the user id, # because we're dealing with authentication credentials if hasattr(acl_users.aq_base, 'credentialsChanged'): # Use an interface provided by LoginManager. acl_users.credentialsChanged(user, name, password) else: req = self.REQUEST p = getattr(req, '_credentials_changed_path', None) if p is not None: # Use an interface provided by CookieCrumbler. change = self.restrictedTraverse(p) change(user, name, password) security.declareProtected(ManageUsers, 'getMemberById') def getMemberById(self, id): ''' Returns the given member. ''' user = self._huntUser(id, self) if user is not None: user = self.wrapUser(user) return user def _huntUser(self, username, context): """Find user in the hierarchy starting from bottom level 'start'. """ uf = context.acl_users while uf is not None: user = uf.getUserById(username) if user is not None: return user container = aq_parent(aq_inner(uf)) parent = aq_parent(aq_inner(container)) uf = getattr(parent, 'acl_users', None) return None def __getPUS(self): # Gets something we can call getUsers() and getUserNames() on. acl_users = self.acl_users if hasattr(acl_users, 'getUsers'): return acl_users else: # This hack works around the absence of getUsers() in LoginManager. # Gets the PersistentUserSource object that stores our users for us in acl_users.UserSourcesGroup.objectValues(): if us.meta_type == 'Persistent User Source': return us.__of__(acl_users) security.declareProtected(ManageUsers, 'listMemberIds') def listMemberIds(self): '''Lists the ids of all members. This may eventually be replaced with a set of methods for querying pieces of the list rather than the entire list at once. ''' user_folder = self.__getPUS() return [ x.getId() for x in user_folder.getUsers() ] security.declareProtected(ManageUsers, 'listMembers') def listMembers(self): '''Gets the list of all members. ''' return map(self.wrapUser, self.__getPUS().getUsers()) security.declareProtected(ListPortalMembers, 'searchMembers') def searchMembers( self, search_param, search_term ): """ Search the membership """ md = getToolByName( self, 'portal_memberdata' ) return md.searchMemberData( search_param, search_term ) security.declareProtected(View, 'getCandidateLocalRoles') def getCandidateLocalRoles(self, obj): """ What local roles can I assign? """ member = self.getAuthenticatedMember() member_roles = member.getRolesInContext(obj) if _checkPermission(ManageUsers, obj): local_roles = self.getPortalRoles() if 'Manager' not in member_roles: local_roles.remove('Manager') else: local_roles = [ role for role in member_roles if role not in ('Member', 'Authenticated') ] local_roles.sort() return tuple(local_roles) security.declareProtected(View, 'setLocalRoles') def setLocalRoles(self, obj, member_ids, member_role, reindex=1): """ Add local roles on an item. """ if ( _checkPermission(ChangeLocalRoles, obj) and member_role in self.getCandidateLocalRoles(obj) ): for member_id in member_ids: roles = list(obj.get_local_roles_for_userid( userid=member_id )) if member_role not in roles: roles.append( member_role ) obj.manage_setLocalRoles( member_id, roles ) if reindex: # It is assumed that all objects have the method # reindexObjectSecurity, which is in CMFCatalogAware and # thus PortalContent and PortalFolder. obj.reindexObjectSecurity() security.declareProtected(View, 'deleteLocalRoles') def deleteLocalRoles(self, obj, member_ids, reindex=1, recursive=0): """ Delete local roles of specified members. """ if _checkPermission(ChangeLocalRoles, obj): for member_id in member_ids: if obj.get_local_roles_for_userid(userid=member_id): obj.manage_delLocalRoles(userids=member_ids) break if recursive and hasattr( aq_base(obj), 'contentValues' ): for subobj in obj.contentValues(): self.deleteLocalRoles(subobj, member_ids, 0, 1) if reindex: # reindexObjectSecurity is always recursive obj.reindexObjectSecurity() security.declarePrivate('addMember') def addMember(self, id, password, roles, domains, properties=None): '''Adds a new member to the user folder. Security checks will have already been performed. Called by portal_registration. ''' acl_users = self.acl_users if hasattr(acl_users, '_doAddUser'): acl_users._doAddUser(id, password, roles, domains) else: # The acl_users folder is a LoginManager. Search for a UserSource # with the needed support. for source in acl_users.UserSourcesGroup.objectValues(): if hasattr(source, 'addUser'): source.__of__(self).addUser(id, password, roles, domains) raise "Can't add Member", "No supported UserSources" if properties is not None: member = self.getMemberById(id) member.setMemberProperties(properties) security.declareProtected(ManageUsers, 'deleteMembers') def deleteMembers(self, member_ids, delete_memberareas=1, delete_localroles=1): """ Delete members specified by member_ids. """ # Delete members in acl_users. acl_users = self.acl_users if _checkPermission(ManageUsers, acl_users): if isinstance(member_ids, basestring): member_ids = (member_ids,) member_ids = list(member_ids) for member_id in member_ids[:]: if not acl_users.getUserById(member_id, None): member_ids.remove(member_id) try: acl_users.userFolderDelUsers(member_ids) except (NotImplementedError, 'NotImplemented'): raise NotImplementedError('The underlying User Folder ' 'doesn\'t support deleting members.') else: raise AccessControl_Unauthorized('You need the \'Manage users\' ' 'permission for the underlying User Folder.') # Delete member data in portal_memberdata. mdtool = getToolByName(self, 'portal_memberdata', None) if mdtool is not None: for member_id in member_ids: mdtool.deleteMemberData(member_id) # Delete members' home folders including all content items. if delete_memberareas: for member_id in member_ids: self.deleteMemberArea(member_id) # Delete members' local roles. if delete_localroles: utool = getToolByName(self, 'portal_url', None) self.deleteLocalRoles( utool.getPortalObject(), member_ids, reindex=1, recursive=1 ) return tuple(member_ids) security.declarePublic('getHomeFolder') def getHomeFolder(self, id=None, verifyPermission=0): """Returns a member's home folder object or None. Set verifyPermission to 1 to return None when the user doesn't have the View permission on the folder. """ return None security.declarePublic('getHomeUrl') def getHomeUrl(self, id=None, verifyPermission=0): """Returns the URL to a member's home folder or None. Set verifyPermission to 1 to return None when the user doesn't have the View permission on the folder. """ return None
class URLTool(UniqueObject, SimpleItem, ActionProviderBase): """ CMF URL Tool. """ implements(IURLTool) __implements__ = (z2IURLTool, ActionProviderBase.__implements__) id = 'portal_url' meta_type = 'CMF URL Tool' security = ClassSecurityInfo() security.declareObjectProtected(View) manage_options = (ActionProviderBase.manage_options + ({ 'label': 'Overview', 'action': 'manage_overview' }, ) + SimpleItem.manage_options) # # ZMI methods # security.declareProtected(ManagePortal, 'manage_overview') manage_overview = DTMLResource('dtml/explainURLTool', globals()) # # 'portal_url' interface methods # security.declarePublic('__call__') def __call__(self, relative=0, *args, **kw): """ Get by default the absolute URL of the portal. """ return self.getPortalObject().absolute_url(relative=relative) security.declarePublic('getPortalObject') def getPortalObject(self): """ Get the portal object itself. """ return aq_parent(aq_inner(self)) security.declarePublic('getRelativeContentPath') def getRelativeContentPath(self, content): """ Get the path for an object, relative to the portal root. """ portal_path_length = len(self.getPortalObject().getPhysicalPath()) content_path = content.getPhysicalPath() return content_path[portal_path_length:] security.declarePublic('getRelativeContentURL') def getRelativeContentURL(self, content): """ Get the URL for an object, relative to the portal root. """ return '/'.join(self.getRelativeContentPath(content)) security.declarePublic('getRelativeUrl') getRelativeUrl = getRelativeContentURL security.declarePublic('getPortalPath') def getPortalPath(self): """ Get the portal object's URL without the server URL component. """ return '/'.join(self.getPortalObject().getPhysicalPath())
class CMFCatalogAware(Base): """Mix-in for notifying portal_catalog and portal_workflow """ security = ClassSecurityInfo() # The following methods can be overriden using inheritence so that # it's possible to specifiy another catalog tool or workflow tool # for a given content type def _getCatalogTool(self): return getToolByName(self, 'portal_catalog', None) def _getWorkflowTool(self): return getToolByName(self, 'portal_workflow', None) # Cataloging methods # ------------------ security.declareProtected(ModifyPortalContent, 'indexObject') def indexObject(self): """ Index the object in the portal catalog. """ catalog = self._getCatalogTool() if catalog is not None: catalog.indexObject(self) security.declareProtected(ModifyPortalContent, 'unindexObject') def unindexObject(self): """ Unindex the object from the portal catalog. """ catalog = self._getCatalogTool() if catalog is not None: catalog.unindexObject(self) security.declareProtected(ModifyPortalContent, 'reindexObject') def reindexObject(self, idxs=[]): """ Reindex the object in the portal catalog. If idxs is present, only those indexes are reindexed. The metadata is always updated. Also update the modification date of the object, unless specific indexes were requested. """ if idxs == []: # Update the modification date. if hasattr(aq_base(self), 'notifyModified'): self.notifyModified() catalog = self._getCatalogTool() if catalog is not None: catalog.reindexObject(self, idxs=idxs) _cmf_security_indexes = ('allowedRolesAndUsers', ) security.declareProtected(ModifyPortalContent, 'reindexObjectSecurity') def reindexObjectSecurity(self, skip_self=False): """Reindex security-related indexes on the object. Recurses in the children to reindex them too. If skip_self is True, only the children will be reindexed. This is a useful optimization if the object itself has just been fully reindexed, as there's no need to reindex its security twice. """ catalog = self._getCatalogTool() if catalog is None: return path = '/'.join(self.getPhysicalPath()) # XXX if _getCatalogTool() is overriden we will have to change # this method for the sub-objects. for brain in catalog.unrestrictedSearchResults(path=path): brain_path = brain.getPath() if brain_path == path and skip_self: continue # Get the object ob = brain._unrestrictedGetObject() if ob is None: # BBB: Ignore old references to deleted objects. # Can happen only when using # catalog-getObject-raises off in Zope 2.8 LOG('reindexObjectSecurity', PROBLEM, "Cannot get %s from catalog" % brain_path) continue # Recatalog with the same catalog uid. s = getattr(ob, '_p_changed', 0) catalog.reindexObject(ob, idxs=self._cmf_security_indexes, update_metadata=0, uid=brain_path) if s is None: ob._p_deactivate() # Workflow methods # ---------------- security.declarePrivate('notifyWorkflowCreated') def notifyWorkflowCreated(self): """ Notify the workflow that self was just created. """ wftool = self._getWorkflowTool() if wftool is not None: wftool.notifyCreated(self) # Opaque subitems # --------------- security.declareProtected(AccessContentsInformation, 'opaqueItems') def opaqueItems(self): """ Return opaque items (subelements that are contained using something that is not an ObjectManager). """ items = [] # Call 'talkback' knowing that it is an opaque item. # This will remain here as long as the discussion item does # not implement ICallableOpaqueItem (backwards compatibility). if hasattr(aq_base(self), 'talkback'): talkback = self.talkback if talkback is not None: items.append((talkback.id, talkback)) # Other opaque items than 'talkback' may have callable # manage_after* and manage_before* hooks. # Loop over all attributes and add those to 'items' # implementing 'ICallableOpaqueItem'. self_base = aq_base(self) for name in self_base.__dict__.keys(): obj = getattr(self_base, name) if ICallableOpaqueItem.providedBy(obj) \ or z2ICallableOpaqueItem.isImplementedBy(obj): items.append((obj.getId(), obj)) return tuple(items) security.declareProtected(AccessContentsInformation, 'opaqueIds') def opaqueIds(self): """ Return opaque ids (subelements that are contained using something that is not an ObjectManager). """ return [t[0] for t in self.opaqueItems()] security.declareProtected(AccessContentsInformation, 'opaqueValues') def opaqueValues(self): """ Return opaque values (subelements that are contained using something that is not an ObjectManager). """ return [t[1] for t in self.opaqueItems()] # Hooks # ----- def manage_afterAdd(self, item, container): """ Add self to the catalog. (Called when the object is created or moved.) """ self.indexObject() self.__recurse('manage_afterAdd', item, container) def manage_afterClone(self, item): """ Add self to the workflow. (Called when the object is cloned.) """ self.notifyWorkflowCreated() self.__recurse('manage_afterClone', item) # Make sure owner local role is set after pasting # The standard Zope mechanisms take care of executable ownership current_user = _getAuthenticatedUser(self) if current_user is not None: local_role_holders = [x[0] for x in self.get_local_roles()] self.manage_delLocalRoles(local_role_holders) self.manage_setLocalRoles(current_user.getId(), ['Owner']) def manage_beforeDelete(self, item, container): """ Remove self from the catalog. (Called when the object is deleted or moved.) """ self.__recurse('manage_beforeDelete', item, container) self.unindexObject() def __recurse(self, name, *args): """ Recurse in both normal and opaque subobjects. """ values = self.objectValues() opaque_values = self.opaqueValues() for subobjects in values, opaque_values: for ob in subobjects: s = getattr(ob, '_p_changed', 0) if hasattr(aq_base(ob), name): getattr(ob, name)(*args) if s is None: ob._p_deactivate() # ZMI # --- manage_options = ({ 'label': 'Workflows', 'action': 'manage_workflowsTab', }, ) _manage_workflowsTab = DTMLResource('dtml/zmi_workflows', globals()) security.declareProtected(ManagePortal, 'manage_workflowsTab') def manage_workflowsTab(self, REQUEST, manage_tabs_message=None): """ Tab displaying the current workflows for the content object. """ ob = self wftool = self._getWorkflowTool() # XXX None ? if wftool is not None: wf_ids = wftool.getChainFor(ob) states = {} chain = [] for wf_id in wf_ids: wf = wftool.getWorkflowById(wf_id) if wf is not None: # XXX a standard API would be nice if hasattr(wf, 'getReviewStateOf'): # Default Workflow state = wf.getReviewStateOf(ob) elif hasattr(wf, '_getWorkflowStateOf'): # DCWorkflow state = wf._getWorkflowStateOf(ob, id_only=1) else: state = '(Unknown)' states[wf_id] = state chain.append(wf_id) return self._manage_workflowsTab( REQUEST, chain=chain, states=states, management_view='Workflows', manage_tabs_message=manage_tabs_message)
class DiscussionTool(UniqueObject, SimpleItem, ActionProviderBase): implements(IOldstyleDiscussionTool) __implements__ = (z2IOldstyleDiscussionTool, ActionProviderBase.__implements__) id = 'portal_discussion' meta_type = 'Oldstyle CMF Discussion Tool' # This tool is used to find the discussion for a given content object. security = ClassSecurityInfo() manage_options = ({ 'label': 'Overview', 'action': 'manage_overview' }, ) + SimpleItem.manage_options # # ZMI methods # security.declareProtected(ManagePortal, 'manage_overview') manage_overview = DTMLResource('dtml/explainDiscussionTool', globals()) # # 'portal_discussion' interface methods # security.declarePublic('getDiscussionFor') def getDiscussionFor(self, content): '''Gets the PortalDiscussion object that applies to content. ''' return OldDiscussable(content).__of__(content) security.declarePublic('isDiscussionAllowedFor') def isDiscussionAllowedFor(self, content): ''' Returns a boolean indicating whether a discussion is allowed for the specified content. ''' if hasattr(content, 'allow_discussion'): return content.allow_discussion typeInfo = getToolByName(self, 'portal_types').getTypeInfo(content) if typeInfo: return typeInfo.allowDiscussion() return 0 security.declarePrivate('listActions') def listActions(self, info=None, object=None): # Return actions for reply and show replies if object is not None or info is None: info = self._getOAI(object) content = info.object if content is None or not self.isDiscussionAllowedFor(content): return () discussion = self.getDiscussionFor(content) if discussion.aq_base == content.aq_base: discussion_url = info.object_url else: discussion_url = discussion.absolute_url() actions = ({ 'name': 'Reply', 'url': discussion_url + '/discussion_reply_form', 'permissions': [ReplyToItem], 'category': 'object' }, ) return actions
class FSSTXMethod( FSObject ): """ A chunk of StructuredText, rendered as a skin method of a CMFSite. """ meta_type = 'Filesystem STX Method' manage_options=( { 'label' : 'Customize' , 'action' : 'manage_main' } , { 'label' : 'View' , 'action' : '' , 'help' : ('OFSP' ,'DTML-DocumentOrMethod_View.stx' ) } ) security = ClassSecurityInfo() security.declareObjectProtected( View ) security.declareProtected( ViewManagementScreens, 'manage_main') manage_main = DTMLResource( 'dtml/custstx', globals() ) # # FSObject interface # def _createZODBClone(self): """ Create a ZODB (editable) equivalent of this object. """ raise NotImplementedError, "See next week's model." def _readFile( self, reparse ): self.raw = self._readFileAsResourceOrDirect() if reparse: self.cook() # # "Wesleyan" interface (we need to be "methodish"). # class func_code: pass func_code=func_code() func_code.co_varnames= () func_code.co_argcount=0 func_code.__roles__=() func_defaults__roles__=() func_defaults=() index_html = None # No accidental acquisition default_content_type = 'text/html' def cook( self ): if not hasattr( self, '_v_cooked' ): self._v_cooked = HTML(self.raw, level=1, header=0) return self._v_cooked _default_template = HTML( """\ <dtml-var standard_html_header> <div class="Desktop"> <dtml-var cooked> </div> <dtml-var standard_html_footer>""" ) def __call__( self, REQUEST={}, RESPONSE=None, **kw ): """ Return our rendered StructuredText. """ self._updateFromFS() if RESPONSE is not None: RESPONSE.setHeader( 'Content-Type', 'text/html' ) return self._render(REQUEST, RESPONSE, **kw) security.declarePrivate( '_render' ) def _render( self, REQUEST={}, RESPONSE=None, **kw ): """ Find the appropriate rendering template and use it to render us. """ template = getattr( self, 'stxmethod_view', self._default_template ) if getattr( template, 'isDocTemp', 0 ): posargs = ( self, REQUEST, RESPONSE ) else: posargs = () return template(*posargs, **{ 'cooked' : self.cook() } ) security.declareProtected( FTPAccess, 'manage_FTPget' ) def manage_FTPget( self ): """ Fetch our source for delivery via FTP. """ return self.raw security.declareProtected( ViewManagementScreens, 'PrincipiaSearchSource' ) def PrincipiaSearchSource( self ): """ Fetch our source for indexing in a catalog. """ return self.raw security.declareProtected( ViewManagementScreens, 'document_src' ) def document_src( self ): """ Fetch our source for indexing in a catalog. """ return self.raw
class WorkflowTool(UniqueObject, Folder, ActionProviderBase): """ Mediator tool, mapping workflow objects """ implements(IWorkflowTool) __implements__ = (z2IWorkflowTool, ActionProviderBase.__implements__) id = 'portal_workflow' meta_type = 'CMF Workflow Tool' _chains_by_type = None # PersistentMapping _default_chain = ('default_workflow',) _default_cataloging = 1 security = ClassSecurityInfo() manage_options = ( { 'label' : 'Workflows' , 'action' : 'manage_selectWorkflows' } , { 'label' : 'Overview', 'action' : 'manage_overview' } ) + Folder.manage_options # # ZMI methods # security.declareProtected( ManagePortal, 'manage_overview' ) manage_overview = DTMLResource( 'dtml/explainWorkflowTool', globals() ) _manage_addWorkflowForm = DTMLResource('dtml/addWorkflow', globals()) security.declareProtected( ManagePortal, 'manage_addWorkflowForm') def manage_addWorkflowForm(self, REQUEST): """ Form for adding workflows. """ wft = [] for key in _workflow_factories.keys(): wft.append(key) wft.sort() return self._manage_addWorkflowForm(REQUEST, workflow_types=wft) security.declareProtected( ManagePortal, 'manage_addWorkflow') def manage_addWorkflow(self, workflow_type, id, RESPONSE=None): """ Adds a workflow from the registered types. """ factory = _workflow_factories[workflow_type] ob = factory(id) self._setObject(id, ob) if RESPONSE is not None: RESPONSE.redirect(self.absolute_url() + '/manage_main?management_view=Contents') def all_meta_types(self): return ( {'name': 'Workflow', 'action': 'manage_addWorkflowForm', 'permission': ManagePortal },) _manage_selectWorkflows = DTMLResource('dtml/selectWorkflows', globals()) security.declareProtected( ManagePortal, 'manage_selectWorkflows') def manage_selectWorkflows(self, REQUEST, manage_tabs_message=None): """ Show a management screen for changing type to workflow connections. """ cbt = self._chains_by_type ti = self._listTypeInfo() types_info = [] for t in ti: id = t.getId() title = t.Title() if title == id: title = None if cbt is not None and cbt.has_key(id): chain = ', '.join(cbt[id]) else: chain = '(Default)' types_info.append({'id': id, 'title': title, 'chain': chain}) return self._manage_selectWorkflows( REQUEST, default_chain=', '.join(self._default_chain), types_info=types_info, management_view='Workflows', manage_tabs_message=manage_tabs_message) security.declareProtected( ManagePortal, 'manage_changeWorkflows') def manage_changeWorkflows(self, default_chain, props=None, REQUEST=None): """ Changes which workflows apply to objects of which type. """ if props is None: props = REQUEST cbt = self._chains_by_type if cbt is None: self._chains_by_type = cbt = PersistentMapping() ti = self._listTypeInfo() # Set up the chains by type. if not (props is None): for t in ti: id = t.getId() field_name = 'chain_%s' % id chain = props.get(field_name, '(Default)').strip() if chain == '(Default)': # Remove from cbt. if cbt.has_key(id): del cbt[id] else: chain = chain.replace(',', ' ') ids = [] for wf_id in chain.split(' '): if wf_id: if not self.getWorkflowById(wf_id): raise ValueError, ( '"%s" is not a workflow ID.' % wf_id) ids.append(wf_id) cbt[id] = tuple(ids) # Set up the default chain. default_chain = default_chain.replace(',', ' ') ids = [] for wf_id in default_chain.split(' '): if wf_id: if not self.getWorkflowById(wf_id): raise ValueError, ( '"%s" is not a workflow ID.' % wf_id) ids.append(wf_id) self._default_chain = tuple(ids) if REQUEST is not None: return self.manage_selectWorkflows(REQUEST, manage_tabs_message='Changed.') # # portal_workflow implementation. # security.declarePrivate('getCatalogVariablesFor') def getCatalogVariablesFor(self, ob): """ Returns a mapping of the catalog variables that apply to ob. o Invoked by portal_catalog. o Allows workflows to add variables to the catalog based on workflow status, making it possible to implement queues. """ wfs = self.getWorkflowsFor(ob) if wfs is None: return None # Iterate through the workflows backwards so that # earlier workflows can override later workflows. wfs.reverse() vars = {} for wf in wfs: v = wf.getCatalogVariablesFor(ob) if v is not None: vars.update(v) return vars security.declarePrivate('listActions') def listActions(self, info=None, object=None): """ Returns a list of actions to be displayed to the user. o Invoked by the portal_actions tool. o Allows workflows to include actions to be displayed in the actions box. o Object actions are supplied by workflows that apply to the object. o Global actions are supplied by all workflows. """ if object is not None or info is None: info = self._getOAI(object) chain = self.getChainFor(info.object) did = {} actions = [] for wf_id in chain: did[wf_id] = 1 wf = self.getWorkflowById(wf_id) if wf is not None: a = wf.listObjectActions(info) if a is not None: actions.extend(a) a = wf.listGlobalActions(info) if a is not None: actions.extend(a) wf_ids = self.getWorkflowIds() for wf_id in wf_ids: if not did.has_key(wf_id): wf = self.getWorkflowById(wf_id) if wf is not None: a = wf.listGlobalActions(info) if a is not None: actions.extend(a) return actions security.declarePublic('getActionsFor') def getActionsFor(self, ob): """ Return a list of action dictionaries for 'ob', just as though queried via 'ActionsTool.listFilteredActionsFor'. """ warn('getActionsFor() is deprecated and will be removed in CMF 1.7. ' 'Please use listActionInfos() instead.', DeprecationWarning) return self.listActions( WorkflowInformation( ob ) ) security.declarePublic('doActionFor') def doActionFor(self, ob, action, wf_id=None, *args, **kw): """ Execute the given workflow action for the object. o Invoked by user interface code. o Allows the user to request a workflow action. o The workflow object must perform its own security checks. """ wfs = self.getWorkflowsFor(ob) if wfs is None: wfs = () if wf_id is None: if not wfs: raise WorkflowException('No workflows found.') found = 0 for wf in wfs: if wf.isActionSupported(ob, action, **kw): found = 1 break if not found: raise WorkflowException( 'No workflow provides the "%s" action.' % action) else: wf = self.getWorkflowById(wf_id) if wf is None: raise WorkflowException( 'Requested workflow definition not found.') return self._invokeWithNotification( wfs, ob, action, wf.doActionFor, (ob, action) + args, kw) security.declarePublic('getInfoFor') def getInfoFor(self, ob, name, default=_marker, wf_id=None, *args, **kw): """ Return a given workflow-specific property for an object. o Invoked by user interface code. o Allows the user to request information provided by the workflow. o The workflow object must perform its own security checks. """ if wf_id is None: wfs = self.getWorkflowsFor(ob) if wfs is None: if default is _marker: raise WorkflowException('No workflows found.') else: return default found = 0 for wf in wfs: if wf.isInfoSupported(ob, name): found = 1 break if not found: if default is _marker: raise WorkflowException( 'No workflow provides "%s" information.' % name) else: return default else: wf = self.getWorkflowById(wf_id) if wf is None: if default is _marker: raise WorkflowException( 'Requested workflow definition not found.') else: return default res = wf.getInfoFor(ob, name, default, *args, **kw) if res is _marker: raise WorkflowException('Could not get info: %s' % name) return res security.declarePrivate('notifyCreated') def notifyCreated(self, ob): """ Notify all applicable workflows that an object has been created and put in its new place. """ wfs = self.getWorkflowsFor(ob) for wf in wfs: wf.notifyCreated(ob) self._reindexWorkflowVariables(ob) security.declarePrivate('notifyBefore') def notifyBefore(self, ob, action): """ Notifies all applicable workflows of an action before it happens, allowing veto by exception. o Unless an exception is thrown, either a notifySuccess() or notifyException() can be expected later on. o The action usually corresponds to a method name. """ wfs = self.getWorkflowsFor(ob) for wf in wfs: wf.notifyBefore(ob, action) security.declarePrivate('notifySuccess') def notifySuccess(self, ob, action, result=None): """ Notify all applicable workflows that an action has taken place. """ wfs = self.getWorkflowsFor(ob) for wf in wfs: wf.notifySuccess(ob, action, result) security.declarePrivate('notifyException') def notifyException(self, ob, action, exc): """ Notify all applicable workflows that an action failed. """ wfs = self.getWorkflowsFor(ob) for wf in wfs: wf.notifyException(ob, action, exc) security.declarePrivate('getHistoryOf') def getHistoryOf(self, wf_id, ob): """ Return the history of an object. o Invoked by workflow definitions. """ if hasattr(aq_base(ob), 'workflow_history'): wfh = ob.workflow_history return wfh.get(wf_id, None) return () security.declarePrivate('getStatusOf') def getStatusOf(self, wf_id, ob): """ Return the last entry of a workflow history. o Invoked by workflow definitions. """ wfh = self.getHistoryOf(wf_id, ob) if wfh: return wfh[-1] return None security.declarePrivate('setStatusOf') def setStatusOf(self, wf_id, ob, status): """ Append an entry to the workflow history. o Invoked by workflow definitions. """ wfh = None has_history = 0 if hasattr(aq_base(ob), 'workflow_history'): history = ob.workflow_history if history is not None: has_history = 1 wfh = history.get(wf_id, None) if wfh is not None: wfh = list(wfh) if not wfh: wfh = [] wfh.append(status) if not has_history: ob.workflow_history = PersistentMapping() ob.workflow_history[wf_id] = tuple(wfh) # # Administration methods # security.declareProtected( ManagePortal, 'setDefaultChain') def setDefaultChain(self, default_chain): """ Set the default chain for this tool """ default_chain = default_chain.replace(',', ' ') ids = [] for wf_id in default_chain.split(' '): if wf_id: if not self.getWorkflowById(wf_id): raise ValueError, ( '"%s" is not a workflow ID.' % wf_id) ids.append(wf_id) self._default_chain = tuple(ids) security.declareProtected( ManagePortal, 'setChainForPortalTypes') def setChainForPortalTypes(self, pt_names, chain): """ Set a chain for a specific portal type. """ cbt = self._chains_by_type if cbt is None: self._chains_by_type = cbt = PersistentMapping() if isinstance(chain, basestring): chain = [ wf.strip() for wf in chain.split(',') if wf.strip() ] ti = self._listTypeInfo() for t in ti: id = t.getId() if id in pt_names: cbt[id] = tuple(chain) security.declareProtected( ManagePortal, 'updateRoleMappings') def updateRoleMappings(self, REQUEST=None): """ Allow workflows to update the role-permission mappings. """ wfs = {} for id in self.objectIds(): wf = self.getWorkflowById(id) if hasattr(aq_base(wf), 'updateRoleMappingsFor'): wfs[id] = wf portal = aq_parent(aq_inner(self)) count = self._recursiveUpdateRoleMappings(portal, wfs) if REQUEST is not None: return self.manage_selectWorkflows(REQUEST, manage_tabs_message= '%d object(s) updated.' % count) else: return count security.declarePrivate('getWorkflowById') def getWorkflowById(self, wf_id): """ Retrieve a given workflow. """ wf = getattr(self, wf_id, None) if getattr(wf, '_isAWorkflow', 0): return wf else: return None security.declarePrivate('getDefaultChainFor') def getDefaultChainFor(self, ob): """ Return the default chain, if applicable, for ob. """ types_tool = getToolByName( self, 'portal_types', None ) if ( types_tool is not None and types_tool.getTypeInfo( ob ) is not None ): return self._default_chain return () security.declarePrivate('getChainFor') def getChainFor(self, ob): """ Returns the chain that applies to the given object. If we get a string as the ob parameter, use it as the portal_type. """ cbt = self._chains_by_type if isinstance(ob, basestring): pt = ob elif hasattr(aq_base(ob), 'getPortalTypeName'): pt = ob.getPortalTypeName() else: pt = None if pt is None: return () chain = None if cbt is not None: chain = cbt.get(pt, None) # Note that if chain is not in cbt or has a value of # None, we use a default chain. if chain is None: chain = self.getDefaultChainFor(ob) if chain is None: return () return chain security.declarePrivate('getWorkflowIds') def getWorkflowIds(self): """ Return the list of workflow ids. """ wf_ids = [] for obj_name, obj in self.objectItems(): if getattr(obj, '_isAWorkflow', 0): wf_ids.append(obj_name) return tuple(wf_ids) security.declareProtected(ManagePortal, 'getWorkflowsFor') def getWorkflowsFor(self, ob): """ Find the workflows for the type of the given object. """ res = [] for wf_id in self.getChainFor(ob): wf = self.getWorkflowById(wf_id) if wf is not None: res.append(wf) return res # # Helper methods # security.declarePrivate( '_listTypeInfo' ) def _listTypeInfo(self): """ List the portal types which are available. """ pt = getToolByName(self, 'portal_types', None) if pt is None: return () else: return pt.listTypeInfo() security.declarePrivate( '_invokeWithNotification' ) def _invokeWithNotification(self, wfs, ob, action, func, args, kw): """ Private utility method: call 'func', and deal with exceptions indicating that the object has been deleted or moved. """ reindex = 1 for w in wfs: w.notifyBefore(ob, action) try: res = func(*args, **kw) except ObjectDeleted, ex: res = ex.getResult() reindex = 0 except ObjectMoved, ex: res = ex.getResult() ob = ex.getNewObject()
class ExtensionPredicate(SimpleItem): """ Predicate matching on filename extensions. """ implements(IContentTypeRegistryPredicate) __implements__ = z2IContentTypeRegistryPredicate extensions = None PREDICATE_TYPE = 'extension' security = ClassSecurityInfo() def __init__(self, id): self.id = id security.declareProtected(ManagePortal, 'getExtensions') def getExtensions(self): """ Get filename extensions. """ if self.extensions is None: return 'None' return ' '.join(self.extensions) security.declareProtected(ManagePortal, 'edit') def edit(self, extensions, COMMA_SPLIT=re.compile(r'[, ]')): if extensions == 'None': extensions = None if type(extensions) is type(''): extensions = filter(None, COMMA_SPLIT.split(extensions)) self.extensions = extensions # # ContentTypeRegistryPredicate interface # security.declareObjectPublic() def __call__(self, name, typ, body): """ Return true if the rule matches, else false. """ if self.extensions is None: return 0 base, ext = os.path.splitext(name) if ext and ext[0] == '.': ext = ext[1:] return ext in self.extensions security.declareProtected(ManagePortal, 'getTypeLabel') def getTypeLabel(self): """ Return a human-readable label for the predicate type. """ return self.PREDICATE_TYPE security.declareProtected(ManagePortal, 'predicateWidget') predicateWidget = DTMLResource('dtml/extensionWidget', globals())
class CachingPolicyManager(SimpleItem): """ Manage the set of CachingPolicy objects for the site; dispatch to them from skin methods. """ implements(ICachingPolicyManager) __implements__ = z2ICachingPolicyManager id = 'caching_policy_manager' meta_type = 'CMF Caching Policy Manager' security = ClassSecurityInfo() def __init__(self): self._policy_ids = () self._policies = PersistentMapping() # # ZMI # manage_options = (({ 'label': 'Policies', 'action': 'manage_cachingPolicies', 'help': ('CMFCore', 'CPMPolicies.stx') }, ) + SimpleItem.manage_options) security.declareProtected(ManagePortal, 'manage_cachingPolicies') manage_cachingPolicies = DTMLResource('dtml/cachingPolicies', globals()) security.declarePublic('listPolicies') def listPolicies(self): """ Return a sequence of tuples, '( policy_id, ( policy, typeObjectName ) )' for all policies in the registry """ result = [] for policy_id in self._policy_ids: result.append((policy_id, self._policies[policy_id])) return tuple(result) security.declareProtected(ManagePortal, 'addPolicy') def addPolicy( self, policy_id, predicate # TALES expr (def. 'python:1') , mtime_func # TALES expr (def. 'object/modified') , max_age_secs # integer, seconds (def. 0) , no_cache # boolean (def. 0) , no_store # boolean (def. 0) , must_revalidate # boolean (def. 0) , vary # string value , etag_func # TALES expr (def. '') , REQUEST=None, s_max_age_secs=None # integer, seconds (def. None) , proxy_revalidate=0 # boolean (def. 0) , public=0 # boolean (def. 0) , private=0 # boolean (def. 0) , no_transform=0 # boolean (def. 0) , enable_304s=0 # boolean (def. 0) , last_modified=1 # boolean (def. 1) , pre_check=None # integer, default None , post_check=None # integer, default None ): """ Add a caching policy. """ if max_age_secs is None or str(max_age_secs).strip() == '': max_age_secs = None else: max_age_secs = int(max_age_secs) if s_max_age_secs is None or str(s_max_age_secs).strip() == '': s_max_age_secs = None else: s_max_age_secs = int(s_max_age_secs) if pre_check is None or str(pre_check).strip() == '': pre_check = None else: pre_check = int(pre_check) if post_check is None or str(post_check).strip() == '': post_check = None else: post_check = int(post_check) self._addPolicy(policy_id, predicate, mtime_func, max_age_secs, no_cache, no_store, must_revalidate, vary, etag_func, s_max_age_secs, proxy_revalidate, public, private, no_transform, enable_304s, last_modified, pre_check, post_check) if REQUEST is not None: REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_cachingPolicies' + '?manage_tabs_message=' + 'Policy+added.') security.declareProtected(ManagePortal, 'updatePolicy') def updatePolicy( self, policy_id, predicate # TALES expr (def. 'python:1') , mtime_func # TALES expr (def. 'object/modified') , max_age_secs # integer, seconds (def. 0) , no_cache # boolean (def. 0) , no_store # boolean (def. 0) , must_revalidate # boolean (def. 0) , vary # string value , etag_func # TALES expr (def. '') , REQUEST=None, s_max_age_secs=None # integer, seconds (def. 0) , proxy_revalidate=0 # boolean (def. 0) , public=0 # boolean (def. 0) , private=0 # boolean (def. 0) , no_transform=0 # boolean (def. 0) , enable_304s=0 # boolean (def. 0) , last_modified=1 # boolean (def. 1) , pre_check=0 # integer, default=None , post_check=0 # integer, default=None ): """ Update a caching policy. """ if max_age_secs is None or str(max_age_secs).strip() == '': max_age_secs = None else: max_age_secs = int(max_age_secs) if s_max_age_secs is None or str(s_max_age_secs).strip() == '': s_max_age_secs = None else: s_max_age_secs = int(s_max_age_secs) if pre_check is None or str(pre_check).strip() == '': pre_check = None else: pre_check = int(pre_check) if post_check is None or str(post_check).strip() == '': post_check = None else: post_check = int(post_check) self._updatePolicy(policy_id, predicate, mtime_func, max_age_secs, no_cache, no_store, must_revalidate, vary, etag_func, s_max_age_secs, proxy_revalidate, public, private, no_transform, enable_304s, last_modified, pre_check, post_check) if REQUEST is not None: REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_cachingPolicies' + '?manage_tabs_message=' + 'Policy+updated.') security.declareProtected(ManagePortal, 'movePolicyUp') def movePolicyUp(self, policy_id, REQUEST=None): """ Move a caching policy up in the list. """ policy_ids = list(self._policy_ids) ndx = policy_ids.index(policy_id) if ndx == 0: msg = "Policy+already+first." else: self._reorderPolicy(policy_id, ndx - 1) msg = "Policy+moved." if REQUEST is not None: REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_cachingPolicies' + '?manage_tabs_message=%s' % msg) security.declareProtected(ManagePortal, 'movePolicyDown') def movePolicyDown(self, policy_id, REQUEST=None): """ Move a caching policy down in the list. """ policy_ids = list(self._policy_ids) ndx = policy_ids.index(policy_id) if ndx == len(policy_ids) - 1: msg = "Policy+already+last." else: self._reorderPolicy(policy_id, ndx + 1) msg = "Policy+moved." if REQUEST is not None: REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_cachingPolicies' + '?manage_tabs_message=%s' % msg) security.declareProtected(ManagePortal, 'removePolicy') def removePolicy(self, policy_id, REQUEST=None): """ Remove a caching policy. """ self._removePolicy(policy_id) if REQUEST is not None: REQUEST['RESPONSE'].redirect( self.absolute_url() + '/manage_cachingPolicies' + '?manage_tabs_message=Policy+removed.') # # Policy manipulation methods. # security.declarePrivate('_addPolicy') def _addPolicy(self, policy_id, predicate, mtime_func, max_age_secs, no_cache, no_store, must_revalidate, vary, etag_func, s_max_age_secs=None, proxy_revalidate=0, public=0, private=0, no_transform=0, enable_304s=0, last_modified=1, pre_check=None, post_check=None): """ Add a policy to our registry. """ policy_id = str(policy_id).strip() if not policy_id: raise ValueError, "Policy ID is required!" if policy_id in self._policy_ids: raise KeyError, "Policy %s already exists!" % policy_id self._policies[policy_id] = CachingPolicy( policy_id, predicate, mtime_func, max_age_secs, no_cache, no_store, must_revalidate, vary, etag_func, s_max_age_secs, proxy_revalidate, public, private, no_transform, enable_304s, last_modified, pre_check, post_check) idlist = list(self._policy_ids) idlist.append(policy_id) self._policy_ids = tuple(idlist) security.declarePrivate('_updatePolicy') def _updatePolicy(self, policy_id, predicate, mtime_func, max_age_secs, no_cache, no_store, must_revalidate, vary, etag_func, s_max_age_secs=None, proxy_revalidate=0, public=0, private=0, no_transform=0, enable_304s=0, last_modified=1, pre_check=None, post_check=None): """ Update a policy in our registry. """ if policy_id not in self._policy_ids: raise KeyError, "Policy %s does not exist!" % policy_id self._policies[policy_id] = CachingPolicy( policy_id, predicate, mtime_func, max_age_secs, no_cache, no_store, must_revalidate, vary, etag_func, s_max_age_secs, proxy_revalidate, public, private, no_transform, enable_304s, last_modified, pre_check, post_check) security.declarePrivate('_reorderPolicy') def _reorderPolicy(self, policy_id, newIndex): """ Reorder a policy in our registry. """ if policy_id not in self._policy_ids: raise KeyError, "Policy %s does not exist!" % policy_id idlist = list(self._policy_ids) ndx = idlist.index(policy_id) pred = idlist[ndx] idlist = idlist[:ndx] + idlist[ndx + 1:] idlist.insert(newIndex, pred) self._policy_ids = tuple(idlist) security.declarePrivate('_removePolicy') def _removePolicy(self, policy_id): """ Remove a policy from our registry. """ if policy_id not in self._policy_ids: raise KeyError, "Policy %s does not exist!" % policy_id del self._policies[policy_id] idlist = list(self._policy_ids) ndx = idlist.index(policy_id) idlist = idlist[:ndx] + idlist[ndx + 1:] self._policy_ids = tuple(idlist) # # 'portal_caching' interface methods # security.declareProtected(View, 'getHTTPCachingHeaders') def getHTTPCachingHeaders(self, content, view_method, keywords, time=None): """ Return a list of HTTP caching headers based on 'content', 'view_method', and 'keywords'. """ context = createCPContext(content, view_method, keywords, time=time) for policy_id, policy in self.listPolicies(): headers = policy.getHeaders(context) if headers: return headers return () security.declareProtected(View, 'getModTimeAndETag') def getModTimeAndETag(self, content, view_method, keywords, time=None): """ Return the modification time and ETag for the content object, view method, and keywords as the tuple (modification_time, etag, set_last_modified_header), where modification_time is a DateTime, or None. """ context = createCPContext(content, view_method, keywords, time=time) for policy_id, policy in self.listPolicies(): if policy.getEnable304s() and policy.testPredicate(context): last_modified = policy._mtime_func(context) if type(last_modified) is type(''): last_modified = DateTime(last_modified) content_etag = None if policy.getETagFunc(): content_etag = policy._etag_func(context) return (last_modified, content_etag, policy.getLastModified()) return None
class FSImage(FSObject): """FSImages act like images but are not directly modifiable from the management interface.""" # Note that OFS.Image.Image is not a base class because it is mutable. meta_type = 'Filesystem Image' _data = None manage_options = ({ 'label': 'Customize', 'action': 'manage_main' }, ) + Cacheable.manage_options security = ClassSecurityInfo() security.declareObjectProtected(View) def __init__(self, id, package=None, entry_subpath=None, filepath=None, fullname=None, properties=None): id = fullname or id # Use the whole filename. FSObject.__init__(self, id, package, entry_subpath, filepath, fullname, properties) security.declareProtected(ViewManagementScreens, 'manage_main') manage_main = DTMLResource('dtml/custimage', globals()) content_type = 'unknown/unknown' def _createZODBClone(self): return Image(self.getId(), '', self._readFile(1)) def _readFile(self, reparse): data = self._data = self._readFileAsResourceOrDirect() if reparse or self.content_type == 'unknown/unknown': self.ZCacheable_invalidate() ct, width, height = getImageInfo(data) self.content_type = ct self.width = width self.height = height return data #### The following is mainly taken from OFS/Image.py ### __str__ = Image.__str__.im_func _image_tag = Image.tag.im_func security.declareProtected(View, 'tag') def tag(self, *args, **kw): # Hook into an opportunity to reload metadata. self._updateFromFS() return self._image_tag(*args, **kw) security.declareProtected(View, 'index_html') def index_html(self, REQUEST, RESPONSE): """ The default view of the contents of a File or Image. Returns the contents of the file or image. Also, sets the Content-Type HTTP header to the objects content type. """ self._updateFromFS() data = self._data data_len = len(data) last_mod = self._file_mod_time status = 200 # HTTP If-Modified-Since header handling. header = REQUEST.get_header('If-Modified-Since', None) if header is not None: header = header.split(';')[0] # Some proxies seem to send invalid date strings for this # header. If the date string is not valid, we ignore it # rather than raise an error to be generally consistent # with common servers such as Apache (which can usually # understand the screwy date string as a lucky side effect # of the way they parse it). try: mod_since = long(DateTime(header).timeTime()) except: mod_since = None if mod_since is not None: if last_mod > 0 and last_mod <= mod_since: status = 304 data = '' #Last-Modified will get stomped on by a cache policy it there is #one set.... RESPONSE.setStatus(status) RESPONSE.setHeader('Last-Modified', rfc1123_date(last_mod)) RESPONSE.setHeader('Content-Type', self.content_type) if status != 304: # Avoid setting content-length for a 304. See RFC 2616. # Zope might still, for better or for worse, set a # content-length header with value "0". RESPONSE.setHeader('Content-Length', data_len) #There are 2 Cache Managers which can be in play.... #need to decide which to use to determine where the cache headers #are decided on. if self.ZCacheable_getManager() is not None: self.ZCacheable_set(None) else: _setCacheHeaders(_ViewEmulator().__of__(self), extra_context={}) return data security.declareProtected(View, 'getContentType') def getContentType(self): """Get the content type of a file or image. Returns the content type (MIME type) of a file or image. """ self._updateFromFS() return self.content_type security.declareProtected(View, 'get_size') def get_size(self): """ Return the size of the image. """ self._updateFromFS() return self._data and len(self._data) or 0 security.declareProtected(FTPAccess, 'manage_FTPget') manage_FTPget = index_html