ILoginPasswordHostExtractionPlugin from Products.PluggableAuthService.interfaces.plugins import \ IChallengePlugin from Products.PluggableAuthService.interfaces.plugins import \ ICredentialsUpdatePlugin from Products.PluggableAuthService.interfaces.plugins import \ ICredentialsResetPlugin from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin from Products.PluggableAuthService.utils import classImplements class IInlineAuthHelper(Interface): """ Marker interface. """ manage_addInlineAuthHelperForm = PageTemplateFile( 'www/iaAdd', globals(), __name__='manage_addInlineAuthHelperForm') def addInlineAuthHelper( dispatcher , id , title=None , REQUEST=None ): """ Add an Inline Auth Helper to a Pluggable Auth Service. """ iah = InlineAuthHelper(id, title) dispatcher._setObject(iah.getId(), iah) if REQUEST is not None: REQUEST['RESPONSE'].redirect( '%s/manage_workspace' '?manage_tabs_message=' 'InlineAuthHelper+added.'
class Reference(Referenceable, SimpleItem): ## Added base level support for referencing References ## They respond to the UUID protocols, but are not ## catalog aware. This means that you can't move/rename ## reference objects and expect them to work, but you can't ## do this anyway. However they should fine the correct ## events when they are added/deleted, etc implements(IReference) security = ClassSecurityInfo() portal_type = 'Reference' meta_type = 'Reference' # XXX FIXME more security manage_options = (({ 'label': 'View', 'action': 'manage_view' }, ) + SimpleItem.manage_options) security.declareProtected(permissions.ManagePortal, 'manage_view') manage_view = PageTemplateFile('view_reference', _www) def __init__(self, id, sid, tid, relationship, **kwargs): self.id = id setattr(self, UUID_ATTR, id) self.sourceUID = sid self.targetUID = tid self.relationship = relationship self.__dict__.update(kwargs) def __repr__(self): return "<Reference sid:%s tid:%s rel:%s>" % ( self.sourceUID, self.targetUID, self.relationship) def UID(self): # the uid method for compat. return IUUID(self, None) # Convenience methods def getSourceObject(self): return self._optimizedGetObject(self.sourceUID) def getTargetObject(self): return self._optimizedGetObject(self.targetUID) # Catalog support def targetId(self): target = self.getTargetObject() if target is not None: return target.getId() return '' def targetTitle(self): target = self.getTargetObject() if target is not None: return target.Title() return '' def Type(self): return self.__class__.__name__ ### # Policy hooks, subclass away def addHook(self, tool, sourceObject=None, targetObject=None): #to reject the reference being added raise a ReferenceException pass def delHook(self, tool, sourceObject=None, targetObject=None): #to reject the delete raise a ReferenceException pass ### # OFS Operations Policy Hooks # These Hooks are experimental and subject to change def beforeTargetDeleteInformSource(self): # Called before target object is deleted so # the source can have a say. pass def beforeSourceDeleteInformTarget(self): # Called when the refering source Object is # about to be deleted. pass def manage_afterAdd(self, item, container): Referenceable.manage_afterAdd(self, item, container) # when copying a full site containe is the container of the plone site # and item is the plone site (at least for objects in portal root) base = container rc = getToolByName(container, REFERENCE_CATALOG) url = getRelURL(base, self.getPhysicalPath()) rc.catalog_object(self, url) def manage_beforeDelete(self, item, container): Referenceable.manage_beforeDelete(self, item, container) rc = getToolByName(container, REFERENCE_CATALOG) url = getRelURL(container, self.getPhysicalPath()) rc.uncatalog_object(url)
class PluggableAuthService(Folder, Cacheable): """ All-singing, all-dancing user folder. """ security = ClassSecurityInfo() meta_type = 'Pluggable Auth Service' _id = id = 'acl_users' _emergency_user = emergency_user _nobody = nobody maxlistusers = -1 # Don't allow local role form to try to list us! def getId(self): return self._id # # IUserFolder implementation # security.declareProtected(ManageUsers, 'getUser') def getUser(self, name): """ See IUserFolder. """ plugins = self._getOb('plugins') user_info = self._verifyUser(plugins, login=name) if not user_info: return None return self._findUser(plugins, user_info['id'], user_info['login']) security.declareProtected(ManageUsers, 'getUserById') def getUserById(self, id, default=None): """ See IUserFolder. """ plugins = self._getOb('plugins') user_info = self._verifyUser(plugins, user_id=id) if not user_info: return default return self._findUser(plugins, user_info['id'], user_info['login']) security.declarePublic('validate') # XXX: public? def validate(self, request, auth='', roles=_noroles): """ See IUserFolder. """ plugins = self._getOb('plugins') is_top = self._isTop() user_ids = self._extractUserIds(request, plugins) (accessed, container, name, value) = self._getObjectContext(request['PUBLISHED'], request) for user_id, login in user_ids: user = self._findUser(plugins, user_id, login, request=request) if aq_base(user) is emergency_user: if is_top: return user else: return None if self._authorizeUser(user, accessed, container, name, value, roles): return user if not is_top: return None # # No other user folder above us can satisfy, and we have no user; # return a constructed anonymous only if anonymous is authorized. # anonymous = self._createAnonymousUser(plugins) if self._authorizeUser(anonymous, accessed, container, name, value, roles): return anonymous return None security.declareProtected(SearchPrincipals, 'searchUsers') def searchUsers(self, **kw): """ Search for users """ search_id = kw.get('id', None) search_name = kw.get('name', None) result = [] max_results = kw.get('max_results', '') sort_by = kw.get('sort_by', '') # We apply sorting and slicing here across all sets, so don't # make the plugin do it if sort_by: del kw['sort_by'] if max_results: del kw['max_results'] if search_name: if kw.get('id') is not None: del kw['id'] # don't even bother searching by id kw['login'] = kw['name'] plugins = self._getOb('plugins') enumerators = plugins.listPlugins(IUserEnumerationPlugin) for enumerator_id, enum in enumerators: try: user_list = enum.enumerateUsers(**kw) for user_info in user_list: info = {} info.update(user_info) info['userid'] = info['id'] info['principal_type'] = 'user' if not info.has_key('title'): info['title'] = info['login'] result.append(info) except _SWALLOWABLE_PLUGIN_EXCEPTIONS: reraise(enum) logger.debug('UserEnumerationPlugin %s error' % enumerator_id, exc_info=True) if sort_by: result.sort(lambda a, b: cmp( a.get(sort_by, '').lower(), b.get(sort_by, '').lower())) if max_results: try: max_results = int(max_results) result = result[:max_results] except ValueError: pass return tuple(result) security.declareProtected(SearchPrincipals, 'searchGroups') def searchGroups(self, **kw): """ Search for groups """ search_id = kw.get('id', None) search_name = kw.get('name', None) result = [] max_results = kw.get('max_results', '') sort_by = kw.get('sort_by', '') # We apply sorting and slicing here across all sets, so don't # make the plugin do it if sort_by: del kw['sort_by'] if max_results: del kw['max_results'] if search_name: if kw.get('id') is not None: del kw['id'] if not kw.has_key('title'): kw['title'] = kw['name'] plugins = self._getOb('plugins') enumerators = plugins.listPlugins(IGroupEnumerationPlugin) for enumerator_id, enum in enumerators: try: group_list = enum.enumerateGroups(**kw) for group_info in group_list: info = {} info.update(group_info) info['groupid'] = info['id'] info['principal_type'] = 'group' if not info.has_key('title'): info['title'] = "(Group) %s" % info['groupid'] result.append(info) except _SWALLOWABLE_PLUGIN_EXCEPTIONS: reraise(enum) logger.debug('GroupEnumerationPlugin %s error' % enumerator_id, exc_info=True) if sort_by: result.sort(lambda a, b: cmp( a.get(sort_by, '').lower(), b.get(sort_by, '').lower())) if max_results: try: max_results = int(max_results) result = result[:max_results + 1] except ValueError: pass return tuple(result) security.declareProtected(SearchPrincipals, 'searchPrincipals') def searchPrincipals(self, groups_first=False, **kw): """ Search for principals (users, groups, or both) """ max_results = kw.get('max_results', '') search_id = kw.get('id', None) search_name = kw.get('name', None) if search_name: if not kw.has_key('title'): kw['title'] = search_name kw['login'] = search_name users = [d.copy() for d in self.searchUsers(**kw)] groups = [d.copy() for d in self.searchGroups(**kw)] if groups_first: result = groups + users else: result = users + groups if max_results: try: max_results = int(max_results) result = result[:max_results + 1] except ValueError: pass return tuple(result) security.declarePrivate('__creatable_by_emergency_user__') def __creatable_by_emergency_user__(self): return 1 security.declarePrivate('_setObject') def _setObject(self, id, object, roles=None, user=None, set_owner=0): # # Override ObjectManager's version to change the default for # 'set_owner' (we don't want to enforce ownership on contained # objects). Folder._setObject(self, id, object, roles, user, set_owner) security.declarePrivate('_delOb') def _delOb(self, id): # # Override ObjectManager's version to clean up any plugin # registrations for the deleted object # plugins = self._getOb('plugins', None) if plugins is not None: plugins.removePluginById(id) Folder._delOb(self, id) # # ZMI stuff # arrow_right_gif = ImageFile('www/arrow-right.gif', globals()) arrow_left_gif = ImageFile('www/arrow-left.gif', globals()) arrow_up_gif = ImageFile('www/arrow-up.gif', globals()) arrow_down_gif = ImageFile('www/arrow-down.gif', globals()) security.declareProtected(ManageUsers, 'manage_search') manage_search = PageTemplateFile('www/pasSearch', globals()) manage_options = (Folder.manage_options[:1] + ({ 'label': 'Search', 'action': 'manage_search' }, ) + Folder.manage_options[2:] + Cacheable.manage_options) security.declareProtected(ManageUsers, 'resultsBatch') def resultsBatch(self, results, REQUEST, size=20, orphan=2, overlap=0): """ ZMI helper for getting batching for displaying search results """ try: start_val = REQUEST.get('batch_start', '0') start = int(start_val) size = int(REQUEST.get('batch_size', size)) except ValueError: start = 0 batch = Batch(results, size, start, 0, orphan, overlap) if batch.end < len(results): qs = self._getBatchLink(REQUEST.get('QUERY_STRING', ''), start, batch.end) REQUEST.set('next_batch_url', '%s?%s' % (REQUEST.get('URL'), qs)) if start > 0: new_start = start - size - 1 if new_start < 0: new_start = 0 qs = self._getBatchLink(REQUEST.get('QUERY_STRING', ''), start, new_start) REQUEST.set('previous_batch_url', '%s?%s' % (REQUEST.get('URL'), qs)) return batch security.declarePrivate('_getBatchLink') def _getBatchLink(self, qs, old_start, new_start): """ Internal helper to generate correct query strings """ if new_start is not None: if not qs: qs = 'batch_start=%d' % new_start elif qs.startswith('batch_start='): qs = qs.replace('batch_start=%d' % old_start, 'batch_start=%d' % new_start) elif qs.find('&batch_start=') != -1: qs = qs.replace('&batch_start=%d' % old_start, '&batch_start=%d' % new_start) else: qs = '%s&batch_start=%d' % (qs, new_start) return qs # # Helper methods # security.declarePrivate('_extractUserIds') def _extractUserIds(self, request, plugins): """ request -> [ validated_user_id ] o For each set of extracted credentials, try to authenticate a user; accumulate a list of the IDs of such users over all our authentication and extraction plugins. """ try: extractors = plugins.listPlugins(IExtractionPlugin) except _SWALLOWABLE_PLUGIN_EXCEPTIONS: logger.debug('Extractor plugin listing error', exc_info=True) extractors = () if not extractors: extractors = (('default', DumbHTTPExtractor()), ) try: authenticators = plugins.listPlugins(IAuthenticationPlugin) except _SWALLOWABLE_PLUGIN_EXCEPTIONS: logger.debug('Authenticator plugin listing error', exc_info=True) authenticators = () result = [] for extractor_id, extractor in extractors: try: credentials = extractor.extractCredentials(request) except _SWALLOWABLE_PLUGIN_EXCEPTIONS: reraise(extractor) logger.debug('ExtractionPlugin %s error' % extractor_id, exc_info=True) continue if credentials: try: credentials['extractor'] = extractor_id # XXX: in key? # Test if ObjectCacheEntries.aggregateIndex would work items = credentials.items() items.sort() except _SWALLOWABLE_PLUGIN_EXCEPTIONS: # XXX: would reraise be good here, and which plugin to ask # whether not to swallow the exception - the extractor? logger.debug('Credentials error: %s' % credentials, exc_info=True) continue # First try to authenticate against the emergency # user and return immediately if authenticated user_id, name = self._tryEmergencyUserAuthentication( credentials) if user_id is not None: return [(user_id, name)] # Now see if the user ids can be retrieved from the cache view_name = createViewName('_extractUserIds', credentials.get('login')) keywords = createKeywords(**credentials) user_ids = self.ZCacheable_get(view_name=view_name, keywords=keywords, default=None) if user_ids is None: user_ids = [] for authenticator_id, auth in authenticators: try: # Masquerading: Authenticate auth_user auth_user_credentials = credentials.copy() login = credentials.get('login', '') auth_user_login, role_user_login = splitmasq(login) if role_user_login is not None: auth_user_credentials[ 'login'] = auth_user_login uid_and_info = auth.authenticateCredentials( auth_user_credentials) if uid_and_info is None: continue user_id, info = uid_and_info if role_user_login is not None: # Masquerading: Check if auth_user is allowed to masquerade if self._canMasquerade(plugins, user_id, info, request): logger.info('Masquerading allowed: %s', login) else: logger.warn('Masquerading denied: %s', login) continue # Masquerading: Return role_user role_user_info = self._verifyUser( plugins, login=role_user_login) if role_user_info is None: continue user_id, info = role_user_info[ 'id'], role_user_info['login'] except _SWALLOWABLE_PLUGIN_EXCEPTIONS: reraise(auth) msg = 'AuthenticationPlugin %s error' % ( authenticator_id, ) logger.debug(msg, exc_info=True) continue if user_id is not None: user_ids.append((user_id, info)) if user_ids: self.ZCacheable_set(user_ids, view_name=view_name, keywords=keywords) result.extend(user_ids) # Emergency user via HTTP basic auth always wins user_id, name = self._tryEmergencyUserAuthentication( DumbHTTPExtractor().extractCredentials(request)) if user_id is not None: return [(user_id, name)] return result security.declarePrivate('_tryEmergencyUserAuthentication') def _tryEmergencyUserAuthentication(self, credentials): """ credentials -> emergency_user or None """ try: eua = EmergencyUserAuthenticator() user_id, name = eua.authenticateCredentials(credentials) except _SWALLOWABLE_PLUGIN_EXCEPTIONS: logger.debug('Credentials error: %s' % credentials, exc_info=True) user_id, name = (None, None) return (user_id, name) security.declarePrivate('_getGroupsForPrincipal') def _getGroupsForPrincipal(self, principal, request=None, plugins=None, ignore_plugins=None): all_groups = {} if ignore_plugins is None: ignore_plugins = () if plugins is None: plugins = self._getOb('plugins') groupmakers = plugins.listPlugins(IGroupsPlugin) for groupmaker_id, groupmaker in groupmakers: if groupmaker_id in ignore_plugins: continue groups = groupmaker.getGroupsForPrincipal(principal, request) for group in groups: principal._addGroups([group]) all_groups[group] = 1 return all_groups.keys() security.declarePrivate('_createAnonymousUser') def _createAnonymousUser(self, plugins): """ Allow IAnonymousUserFactoryPlugins to create or fall back. """ factories = plugins.listPlugins(IAnonymousUserFactoryPlugin) for factory_id, factory in factories: anon = factory.createAnonymousUser() if anon is not None: return anon.__of__(self) return nobody.__of__(self) security.declarePrivate('_createUser') def _createUser(self, plugins, user_id, name): """ Allow IUserFactoryPlugins to create, or fall back to default. """ factories = plugins.listPlugins(IUserFactoryPlugin) for factory_id, factory in factories: user = factory.createUser(user_id, name) if user is not None: return user.__of__(self) return PropertiedUser(user_id, name).__of__(self) security.declarePrivate('_canMasquerade') def _canMasquerade(self, plugins, user_id, name=None, request=None): """ Return True if masquerading is enabled and user_id has the Manager or Masquerader role. """ if not masquerading(): return False user = self._findUser(plugins, user_id, name, request) if user is not None: roles = user.getRoles() if 'Manager' in roles or 'Masquerader' in roles: return True return False security.declarePrivate('_findUser') def _findUser(self, plugins, user_id, name=None, request=None): """ user_id -> decorated_user """ if user_id == self._emergency_user.getUserName(): return self._emergency_user # See if the user can be retrieved from the cache view_name = createViewName('_findUser', user_id) keywords = createKeywords(user_id=user_id, name=name) user = self.ZCacheable_get(view_name=view_name, keywords=keywords, default=None) if user is None: user = self._createUser(plugins, user_id, name) propfinders = plugins.listPlugins(IPropertiesPlugin) for propfinder_id, propfinder in propfinders: data = propfinder.getPropertiesForUser(user, request) if data: user.addPropertysheet(propfinder_id, data) groups = self._getGroupsForPrincipal(user, request, plugins=plugins) user._addGroups(groups) rolemakers = plugins.listPlugins(IRolesPlugin) for rolemaker_id, rolemaker in rolemakers: try: roles = rolemaker.getRolesForPrincipal(user, request) except _SWALLOWABLE_PLUGIN_EXCEPTIONS: logger.debug('IRolesPlugin %s error' % rolemaker_id, exc_info=True) else: if roles: user._addRoles(roles) user._addRoles(['Authenticated']) # Cache the user if caching is enabled base_user = aq_base(user) if getattr(base_user, '_p_jar', None) is None: self.ZCacheable_set(base_user, view_name=view_name, keywords=keywords) return user.__of__(self) security.declarePrivate('_verifyUser') def _verifyUser(self, plugins, user_id=None, login=None): """ user_id -> info_dict or None """ if user_id is None and login is None: # Avoid possible hugely expensive and/or wrong behavior of # plugin enumerators. return None # Masquerading: Lookup role_user auth_user_login, role_user_login = splitmasq(login) if role_user_login is not None: login = role_user_login criteria = {'exact_match': True} if user_id is not None: criteria['id'] = user_id if login is not None: criteria['login'] = login view_name = createViewName('_verifyUser', user_id or login) keywords = createKeywords(**criteria) cached_info = self.ZCacheable_get(view_name=view_name, keywords=keywords, default=None) if cached_info is not None: return cached_info enumerators = plugins.listPlugins(IUserEnumerationPlugin) for enumerator_id, enumerator in enumerators: try: info = enumerator.enumerateUsers(**criteria) if info: # Put the computed value into the cache self.ZCacheable_set(info[0], view_name=view_name, keywords=keywords) return info[0] except _SWALLOWABLE_PLUGIN_EXCEPTIONS: reraise(enumerator) msg = 'UserEnumerationPlugin %s error' % enumerator_id logger.debug(msg, exc_info=True) return None security.declarePrivate('_authorizeUser') def _authorizeUser(self, user, accessed, container, name, value, roles=_noroles): """ -> boolean (whether user has roles). o Add the user to the SM's stack, if successful. o Return """ user = aq_base(user).__of__(self) newSecurityManager(None, user) security = getSecurityManager() try: try: if roles is _noroles: if security.validate(accessed, container, name, value): return 1 else: if security.validate(accessed, container, name, value, roles): return 1 except: noSecurityManager() raise except Unauthorized: pass return 0 security.declarePrivate('_isTop') def _isTop(self): """ Are we the user folder in the root object? """ try: parent = aq_base(aq_parent(self)) if parent is None: return 0 return parent.isTopLevelPrincipiaApplicationObject except AttributeError: return 0 security.declarePrivate('_getObjectContext') def _getObjectContext(self, v, request): """ request -> ( a, c, n, v ) o 'a 'is the object the object was accessed through o 'c 'is the physical container of the object o 'n 'is the name used to access the object o 'v' is the object (value) we're validating access to o XXX: Lifted from AccessControl.User.BasicUserFolder._getobcontext """ if len(request.steps) == 0: # someone deleted root index_html request['RESPONSE'].notFoundError( 'no default view (root default view was probably deleted)') root = request['PARENTS'][-1] request_container = aq_parent(root) n = request.steps[-1] # default to accessed and container as v.aq_parent a = c = request['PARENTS'][0] # try to find actual container inner = aq_inner(v) innerparent = aq_parent(inner) if innerparent is not None: # this is not a method, we needn't treat it specially c = innerparent elif hasattr(v, 'im_self'): # this is a method, we need to treat it specially c = v.im_self c = aq_inner(v) # if pub's aq_parent or container is the request container, it # means pub was accessed from the root if a is request_container: a = root if c is request_container: c = root return a, c, n, v security.declarePrivate('_getEmergencyUser') def _getEmergencyUser(self): return emergency_user.__of__(self) security.declarePrivate('_doAddUser') def _doAddUser(self, login, password, roles, domains, **kw): """ Create a user with login, password and roles if, and only if, we have a registered user manager and role manager that will accept specific plugin interfaces. """ plugins = self._getOb('plugins') useradders = plugins.listPlugins(IUserAdderPlugin) roleassigners = plugins.listPlugins(IRoleAssignerPlugin) user = None if not (useradders and roleassigners): raise NotImplementedError("There are no plugins" " that can create" " users and assign roles to them.") for useradder_id, useradder in useradders: if useradder.doAddUser(login, password): # XXX: Adds user to cache, but without roles... user = self.getUser(login) break # No useradder was successful. if user is None: return for roleassigner_id, roleassigner in roleassigners: for role in roles: try: roleassigner.doAssignRoleToPrincipal(user.getId(), role) except _SWALLOWABLE_PLUGIN_EXCEPTIONS: reraise(roleassigner) logger.debug('RoleAssigner %s error' % roleassigner_id, exc_info=True) pass if user is not None: notify(PrincipalCreated(user)) return user security.declarePublic('all_meta_types') def all_meta_types(self): """ What objects can be put in here? """ import Products allowed_types = tuple(MultiPlugins) + (RAMCacheManager.meta_type, ) return [x for x in Products.meta_types if x['name'] in allowed_types] security.declarePrivate('manage_beforeDelete') def manage_beforeDelete(self, item, container): if item is self: try: del container.__allow_groups__ except: pass handle = self.meta_type + '/' + self.getId() BeforeTraverse.unregisterBeforeTraverse(container, handle) security.declarePrivate('manage_afterAdd') def manage_afterAdd(self, item, container): if item is self: container.__allow_groups__ = aq_base(self) handle = self.meta_type + '/' + self.getId() container = container.this() nc = BeforeTraverse.NameCaller(self.getId()) BeforeTraverse.registerBeforeTraverse(container, nc, handle) def __call__(self, container, req): """ The __before_publishing_traverse__ hook. """ resp = req['RESPONSE'] req._hold(ResponseCleanup(resp)) stack = getattr(resp, '_unauthorized_stack', []) stack.append(resp._unauthorized) resp._unauthorized_stack = stack resp._unauthorized = self._unauthorized resp._has_challenged = False # # Response override # def _unauthorized(self): req = self.REQUEST resp = req['RESPONSE'] if resp._has_challenged: # Been here already return if not self.challenge(req, resp): # Need to fall back here resp = self._cleanupResponse() resp._unauthorized() else: resp._has_challenged = True def challenge(self, request, response): plugins = self._getOb('plugins') # Find valid protocols for this request type valid_protocols = [] choosers = [] try: choosers = plugins.listPlugins(IChallengeProtocolChooser) except KeyError: # Work around the fact that old instances might not have # IChallengeProtocolChooser registered with the # PluginRegistry. pass for chooser_id, chooser in choosers: choosen = chooser.chooseProtocols(request) if choosen is None: continue valid_protocols.extend(choosen) # Go through all challenge plugins challengers = plugins.listPlugins(IChallengePlugin) protocol = None for challenger_id, challenger in challengers: challenger_protocol = getattr(challenger, 'protocol', challenger_id) if valid_protocols and challenger_protocol not in valid_protocols: # Skip invalid protocol for this request type. continue if protocol is None or protocol == challenger_protocol: if challenger.challenge(request, response): protocol = challenger_protocol if protocol is not None: # something fired, so it was a successful PAS challenge return True # nothing fired, so trigger the fallback return False def _cleanupResponse(self): resp = self.REQUEST['RESPONSE'] # No errors of any sort may propagate, and we don't care *what* # they are, even to log them. stack = getattr(resp, '_unauthorized_stack', []) if stack: resp._unauthorized = stack.pop() else: try: del resp._unauthorized except: pass return resp security.declarePublic('hasUsers') def hasUsers(self): """Zope quick start sacrifice. The quick start page expects a hasUsers() method. """ return True security.declarePublic('updateCredentials') def updateCredentials(self, request, response, login, new_password): """Central updateCredentials method This method is needed for cases where the credentials storage and the credentials extraction is handled by different plugins. Example case would be if the CookieAuthHelper is used as a Challenge and Extraction plugin only to take advantage of the login page feature but the credentials are not stored in the CookieAuthHelper cookie but somewhere else, like in a Session. """ plugins = self._getOb('plugins') cred_updaters = plugins.listPlugins(ICredentialsUpdatePlugin) for updater_id, updater in cred_updaters: updater.updateCredentials(request, response, login, new_password) security.declarePublic('logout') def logout(self, REQUEST): """Publicly accessible method to log out a user """ self.resetCredentials(REQUEST, REQUEST['RESPONSE']) # Little bit of a hack: Issuing a redirect to the same place # where the user was so that in the second request the now-destroyed # credentials can be acted upon to e.g. go back to the login page referrer = REQUEST.get('HTTP_REFERER') # optional header if referrer: REQUEST['RESPONSE'].redirect(referrer) security.declarePublic('resetCredentials') def resetCredentials(self, request, response): """Reset credentials by informing all active resetCredentials plugins """ user = getSecurityManager().getUser() if aq_base(user) is not nobody: plugins = self._getOb('plugins') cred_resetters = plugins.listPlugins(ICredentialsResetPlugin) for resetter_id, resetter in cred_resetters: resetter.resetCredentials(request, response)
from App.class_init import InitializeClass from OFS.SimpleItem import SimpleItem from Products.PageTemplates.PageTemplateFile import PageTemplateFile from persistent.mapping import PersistentMapping from be.ldapadmin import ldap_config from be.ldapadmin import query from be.ldapadmin.constants import NETWORK_NAME, ADDR_FROM from be.ldapadmin.ui_common import CommonTemplateLogic from be.ldapadmin.ui_common import SessionMessages from be.ldapadmin.ui_common import TemplateRenderer from be.ldapadmin.logic_common import _create_plain_message, load_template from be.ldapadmin.users_admin import ldap_edit_users log = logging.getLogger(__name__) manage_add_pwreset_tool_html = PageTemplateFile('zpt/pwreset_manage_add', globals()) manage_add_pwreset_tool_html.ldap_config_edit_macro = ldap_config.edit_macro manage_add_pwreset_tool_html.config_defaults = lambda: ldap_config.defaults def manage_add_pwreset_tool(parent, id, REQUEST=None): """ Create a new PasswordResetTool object """ form = (REQUEST is not None and REQUEST.form or {}) config = ldap_config.read_form(form) obj = PasswordResetTool(config) obj.title = form.get('title', id) obj._setId(id) parent._setObject(id, obj) if REQUEST is not None: REQUEST.RESPONSE.redirect(parent.absolute_url() + '/manage_workspace')
# [email protected] from AccessControl import ClassSecurityInfo from App.class_init import default__class_init__ as InitializeClass from OFS.Cache import Cacheable from Products.PageTemplates.PageTemplateFile import PageTemplateFile from zope.interface import implements from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin from Products.membrane.interfaces.plugins import IMembraneRoleManagerPlugin from Products.membrane.interfaces import user as user_ifaces from Products.membrane.utils import findMembraneUserAspect manage_addMembraneRoleManagerForm = PageTemplateFile( '../www/MembraneRoleManagerForm', globals(), __name__='manage_addMembraneRoleManager') def addMembraneRoleManager(dispatcher, id, title=None, REQUEST=None): """ Add a MembraneRoleManager to a Pluggable Auth Service. """ pmm = MembraneRoleManager(id, title) dispatcher._setObject(pmm.getId(), pmm) if REQUEST is not None: REQUEST['RESPONSE'].redirect( '%s/manage_workspace' '?manage_tabs_message=' 'MembraneRoleManager+added.' % dispatcher.absolute_url())
class CalendarTool (UniqueObject, SimpleItem): """ A tool for encapsulating how calendars work and are displayed """ id = 'portal_calendar' meta_type= 'CMF Calendar Tool' security = ClassSecurityInfo() implements(ICalendarTool) calendar_types = ('Event',) calendar_states = ('published',) use_session = False firstweekday = 6 # 6 is Sunday manage_options = (({'label' : 'Overview', 'action' : 'manage_overview'}, {'label' : 'Configure', 'action' : 'manage_configure'}, ) + SimpleItem.manage_options) # # ZMI methods # security.declareProtected( ManagePortal, 'manage_overview' ) manage_overview = PageTemplateFile('www/explainCalendarTool', globals(), __name__='manage_overview') security.declareProtected( ManagePortal, 'manage_configure' ) manage_configure = PageTemplateFile('www/configureCalendarTool', globals(), __name__='manage_configure') security.declareProtected( ManagePortal, 'edit_configuration' ) def edit_configuration( self , show_types , use_session , show_states=None , firstweekday=None ): """ Change the configuration of the calendar tool """ # XXX: this method violates the rules for tools/utilities: # it depends on self.REQUEST self.calendar_types = tuple(show_types) self.use_session = bool(use_session) if show_states is not None: self.calendar_states = tuple(show_states) if firstweekday is not None: try: fwd = int(firstweekday) if 0 <= fwd <= 6: # Do nothing with illegal values self.firstweekday = fwd except ValueError: # Do nothing with illegal values pass if hasattr(self.REQUEST, 'RESPONSE'): self.REQUEST.RESPONSE.redirect('manage_configure') security.declarePrivate('_getCalendar') def _getCalendar(self): """ Wrapper to ensure we set the first day of the week every time """ calendar.setfirstweekday(self.getFirstWeekDay()) return calendar security.declarePublic('getFirstWeekDay') def getFirstWeekDay(self): """ Get our first weekday setting """ return self.firstweekday security.declarePublic('getCalendarTypes') def getCalendarTypes(self): """ Returns a list of type that will show in the calendar """ return self.calendar_types security.declarePublic('getCalendarStates') def getCalendarStates(self): """ Returns a list of workflow states that will show in the calendar """ return self.calendar_states security.declarePublic('getUseSession') def getUseSession(self): """ Returns the Use_Session option """ return bool(self.use_session) security.declarePublic('getDays') def getDays(self): """ Returns a list of days with the correct start day first """ return self._getCalendar().weekheader(2).split() security.declarePublic('getWeeksList') def getWeeksList(self, month='1', year='2002'): """ Return a series of weeks, each containing an integer day number. A day number of 0 means that day is in the previous or next month. """ year = int(year) month = int(month) # daysByWeek is a list of days inside a list of weeks, like so: # [[0, 1, 2, 3, 4, 5, 6], # [7, 8, 9, 10, 11, 12, 13], # [14, 15, 16, 17, 18, 19, 20], # [21, 22, 23, 24, 25, 26, 27], # [28, 29, 30, 31, 0, 0, 0]] daysByWeek = self._getCalendar().monthcalendar(year, month) return daysByWeek security.declarePublic('getEventsForCalendar') def getEventsForCalendar(self, month='1', year='2002'): """ recreates a sequence of weeks, by days each day is a mapping. {'day': #, 'url': None} """ year = int(year) month = int(month) # daysByWeek is a list of days inside a list of weeks, like so: # [[0, 1, 2, 3, 4, 5, 6], # [7, 8, 9, 10, 11, 12, 13], # [14, 15, 16, 17, 18, 19, 20], # [21, 22, 23, 24, 25, 26, 27], # [28, 29, 30, 31, 0, 0, 0]] daysByWeek = self._getCalendar().monthcalendar(year, month) weeks = [] events = self.catalog_getevents(year, month) for week in daysByWeek: days = [] for day in week: if events.has_key(day): days.append(events[day]) else: days.append({'day': day, 'event': 0, 'eventslist':[]}) weeks.append(days) return weeks security.declarePublic('catalog_getevents') def catalog_getevents(self, year, month): """ given a year and month return a list of days that have events """ # XXX: this method violates the rules for tools/utilities: # it depends on a non-utility tool year = int(year) month = int(month) last_day = self._getCalendar().monthrange(year, month)[1] first_date = self.getBeginAndEndTimes(1, month, year)[0] last_date = self.getBeginAndEndTimes(last_day, month, year)[1] ctool = getToolByName(self, 'portal_catalog') query = ctool( portal_type=self.getCalendarTypes(), review_state=self.getCalendarStates(), start={'query': last_date, 'range': 'max'}, end={'query': first_date, 'range': 'min'}, sort_on='start' ) # compile a list of the days that have events eventDays={} for daynumber in range(1, 32): # 1 to 31 eventDays[daynumber] = {'eventslist': [], 'event': 0, 'day': daynumber} includedevents = [] for result in query: if result.getRID() in includedevents: break else: includedevents.append(result.getRID()) event={} # we need to deal with events that end next month if result.end.greaterThan(last_date): eventEndDay = last_day event['end'] = None else: eventEndDay = result.end.day() if result.end == result.end.earliestTime(): event['end'] = (result.end - 1).latestTime().Time() else: event['end'] = result.end.Time() # and events that started last month if result.start.lessThan(first_date): eventStartDay = 1 event['start'] = None else: eventStartDay = result.start.day() event['start'] = result.start.Time() event['title'] = result.Title or result.getId if eventStartDay != eventEndDay: allEventDays = range(eventStartDay, eventEndDay+1) eventDays[eventStartDay]['eventslist'].append( {'end': None, 'start': result.start.Time(), 'title': event['title']} ) eventDays[eventStartDay]['event'] = 1 for eventday in allEventDays[1:-1]: eventDays[eventday]['eventslist'].append( {'end': None, 'start': None, 'title': event['title']} ) eventDays[eventday]['event'] = 1 if (result.end == result.end.earliestTime() and event['end'] is not None): # ends some day this month at midnight last_day_data = eventDays[allEventDays[-2]] last_days_event = last_day_data['eventslist'][-1] last_days_event['end'] = (result.end-1).latestTime().Time() else: eventDays[eventEndDay]['eventslist'].append( { 'end': event['end'], 'start': None, 'title': event['title']} ) eventDays[eventEndDay]['event'] = 1 else: eventDays[eventStartDay]['eventslist'].append(event) eventDays[eventStartDay]['event'] = 1 # This list is not uniqued and isn't sorted # uniquing and sorting only wastes time # and in this example we don't need to because # later we are going to do an 'if 2 in eventDays' # so the order is not important. # example: [23, 28, 29, 30, 31, 23] return eventDays security.declarePublic('getEventsForThisDay') def getEventsForThisDay(self, thisDay): """ given an exact day return ALL events that: A) Start on this day OR B) End on this day OR C) Start before this day AND end after this day """ # XXX: this method violates the rules for tools/utilities: # it depends on a non-utility tool day, month, year = ( int(thisDay.day()) , int(thisDay.month()) , int(thisDay.year()) ) first_date, last_date = self.getBeginAndEndTimes(day, month, year) zone = first_date.localZone() after_midnight_str = '%d-%02d-%02d 00:01:00 %s' % (year,month,day,zone) after_midnight = DateTime(after_midnight_str) # Get all events that Start on this day ctool = getToolByName(self, 'portal_catalog') query = ctool( portal_type=self.getCalendarTypes(), review_state=self.getCalendarStates(), start={'query': (first_date, last_date), 'range': 'minmax'} ) # Get all events that End on this day query += ctool( portal_type=self.getCalendarTypes(), review_state=self.getCalendarStates(), end={'query': (after_midnight, last_date), 'range': 'minmax'} ) # Get all events that Start before this day AND End after this day query += ctool( portal_type=self.getCalendarTypes(), review_state=self.getCalendarStates(), start={'query': first_date, 'range': 'max'}, end={'query': last_date, 'range': 'min'} ) # Unique the results results = unique_results(query) # Sort by start date results.sort(sort_by_date) return results security.declarePublic('getPreviousMonth') def getPreviousMonth(self, month, year): """ Get a DateTime object for one month prior to the given year/month """ month = int(month) year = int(year) if month == 0 or month == 1: month, year = 12, year - 1 else: month -= 1 return DateTime(year, month, 1) security.declarePublic('getNextMonth') def getNextMonth(self, month, year): """ Get a DateTime object for one month after the given year/month """ month = int(month) year = int(year) if month == 12: month, year = 1, year + 1 else: month += 1 return DateTime(year, month, 1) security.declarePublic('getBeginAndEndTimes') def getBeginAndEndTimes(self, day, month, year): """ Get two DateTime objects representing the beginning and end of the given day """ day = int(day) month = int(month) year = int(year) begin = DateTime('%d/%02d/%02d 00:00:00' % (year, month, day)) end = DateTime('%d/%02d/%02d 23:59:59' % (year, month, day)) return (begin, end) security.declarePublic('getNextEvent') def getNextEvent(self, start_date=None): """ Get the next event that starts after start_date start_date is expected to be a DateTime instance """ # XXX: this method violates the rules for tools/utilities: # it depends on a non-utility tool if start_date is None: start_date = DateTime() ctool = getToolByName(self, 'portal_catalog') query = ctool( portal_type=self.getCalendarTypes(), review_state=self.getCalendarStates(), start={'query': start_date, 'range': 'min'}, sort_on='start') results = unique_results(query) if results: results.sort(sort_by_date) return results[0]
class NotificationTool(Folder): """ """ meta_type = core_constants.METATYPE_NOTIFICATIONTOOL icon = 'misc_/NaayaCore/NotificationTool.gif' meta_types = () all_meta_types = meta_types security = ClassSecurityInfo() # default configuration settings default_config = { 'admin_on_error': True, 'admin_on_edit': True, 'enable_instant': True, 'enable_daily': True, 'enable_anonymous': False, # Enable anonymous notifications 'daily_hour': 0, 'enable_weekly': True, 'weekly_day': 1, # 1 = monday, 7 = sunday 'weekly_hour': 0, 'enable_monthly': True, 'monthly_day': 1, # 1 = first day of the month 'monthly_hour': 0, 'notif_content_types': [], } def __init__(self, id, title): """ """ self.id = id self.title = title self.config = PersistentDict(self.default_config) self.timestamps = PersistentDict() # Confirmations list self.pending_anonymous_subscriptions = PersistentList() def get_config(self, key): return self.config.get(key) def get_location_link(self, location): if location: return self.restrictedTraverse(location, self.getSite()).absolute_url() else: return self.getSite().absolute_url() def _validate_subscription(self, **kw): """ Validate add/edit subscription for authorized and anonymous users """ if (kw['notif_type'] not in self.available_notif_types(kw['location']) and not (kw['notif_type'] == 'administrative' and self.checkPermissionPublishObjects())): raise i18n_exception(ValueError, 'Subscribing to ${notif_type} ' 'notifications in "${location}" not allowed', location=kw['location'] or self.getSite().title, notif_type=kw['notif_type']) try: obj = self.getSite().restrictedTraverse(kw['location']) except: raise i18n_exception(ValueError, 'This path is invalid or protected') try: subscription_container = ISubscriptionContainer(obj) except: raise i18n_exception(ValueError, 'Cannot subscribe to this folder') if kw.get('anonymous', False): # Check if subscription exists for this anonymous subscriber if not is_valid_email(kw.get('email', '')): raise i18n_exception( ValueError, 'Your e-mail address does not appear ' 'to be valid.') for id, subscription in subscription_container.list_with_keys(): # Normal subscriptions don't have e-mail if isinstance(subscription, AnonymousSubscription): if (subscription.email == kw['email'] and subscription.notif_type == kw['notif_type'] and subscription.lang == kw['lang']): raise i18n_exception(ValueError, 'Subscription already exists') def _sitemap_dict(self, form): """ Compose a sitemap dict """ node = form.get('node', '') if not node or node == '/': node = '' def traverse(objects, level=0, stop_level=2, exclude_root=False): """ Create a dict with node properties and children. This is a fixed level recursion. On some sites there are a lot of objects so we don't need to get the whole tree. """ res = [] for ob in objects: if ISubscriptionTarget.providedBy(ob) is False: continue children_objects = [] if level != stop_level: # Stop if the level is reached # Create a list of object's children if hasattr(ob, 'objectValues'): # Get only naaya container objects for child in ob.objectValues( self.get_naaya_containers_metatypes()): # Skip unsubmited/unapproved if not getattr(child, 'approved', False): continue elif not getattr(child, 'submitted', False): continue else: children_objects.append(child) if hasattr(ob, 'approved'): icon = ob.approved and ob.icon or ob.icon_marked else: icon = ob.icon children = traverse(children_objects, level + 1, stop_level) if exclude_root: # Return only the children if this is set return children res.append({ 'data': { 'title': self.utStrEscapeHTMLTags( self.utToUtf8(ob.title_or_id())), 'icon': icon }, 'attributes': { 'title': path_in_site(ob) }, 'children': children }) return res if node == '': tree_dict = traverse([self.getSite()]) else: tree_dict = traverse([self.restrictedTraverse(node)], exclude_root=True) return tree_dict security.declarePublic('sitemap') def sitemap(self, REQUEST=None, **kw): """ Return a json (for Ajax tree) representation of published objects marked with `ISubscriptionTarget` including the portal organized in a tree (sitemap) """ form = {} if REQUEST is not None: form = REQUEST.form REQUEST.RESPONSE.setHeader('content-type', 'application/json') else: form.update(kw) return json.dumps(self._sitemap_dict(form)) security.declarePrivate('add_account_subscription') def add_account_subscription(self, user_id, location, notif_type, lang, content_types=[]): """ Subscribe the user `user_id` """ self._validate_subscription(user_id=user_id, location=location, notif_type=notif_type, lang=lang, content_types=content_types) try: self.remove_account_subscription(user_id, location, notif_type, lang) except ValueError: pass obj = self.getSite().restrictedTraverse(location) subscription_container = ISubscriptionContainer(obj) subscription = AccountSubscription(user_id, notif_type, lang, content_types) subscription_container.add(subscription) security.declarePrivate('add_anonymous_subscription') def add_anonymous_subscription(self, **kw): """ Handle anonymous users """ self._validate_subscription(anonymous=True, **kw) subscription = AnonymousSubscription(**kw) # Add to temporary container self.pending_anonymous_subscriptions.append(subscription) # Send email email_tool = self.getSite().getEmailTool() email_from = email_tool.get_addr_from() email_template = EmailPageTemplateFile('emailpt/confirm.zpt', globals()) email_data = email_template.render_email(**{ 'key': subscription.key, 'here': self }) email_to = subscription.email email_tool.sendEmail(email_data['body_text'], email_to, email_from, email_data['subject']) security.declarePrivate('remove_account_subscription') def remove_account_subscription(self, user_id, location, notif_type, lang, content_types=None): obj = self.getSite().restrictedTraverse(location) subscription_container = ISubscriptionContainer(obj) n = utils.match_account_subscription(subscription_container, user_id, notif_type, lang, content_types) if n is None: raise ValueError('Subscription not found') subscription_container.remove(n) security.declarePrivate('unsubscribe_links_html') unsubscribe_links_html = PageTemplateFile("emailpt/unsubscribe_links.zpt", globals()) security.declarePrivate('remove_anonymous_subscription') def remove_anonymous_subscription(self, email, location, notif_type, lang): try: obj = self.getSite().restrictedTraverse(location) except: raise i18n_exception(ValueError, 'Invalid location') try: subscription_container = ISubscriptionContainer(obj) except: raise i18n_exception(ValueError, 'Invalid container') anonymous_subscriptions = [ (n, s) for n, s in subscription_container.list_with_keys() if hasattr(s, 'email') ] subscriptions = filter( lambda s: (s[1].email == email and s[1].location == location and s[ 1].notif_type == notif_type), anonymous_subscriptions) if len(subscriptions) == 1: subscription_container.remove(subscriptions[0][0]) else: raise i18n_exception(ValueError, 'Subscription not found') security.declareProtected(view, 'available_notif_types') def available_notif_types(self, location=''): if self.config['enable_instant']: yield 'instant' if self.config['enable_daily']: yield 'daily' if self.config['enable_weekly']: yield 'weekly' if self.config['enable_monthly']: yield 'monthly' security.declarePrivate('notify_maintainer') def notify_maintainer(self, ob, folder, **kwargs): """ Process and notify by email that B{p_object} has been uploaded into the B{p_folder}. """ auth_tool = self.getSite().getAuthenticationTool() emails = self.getMaintainersEmails(ob) person = self.REQUEST.AUTHENTICATED_USER.getUserName() if len(emails) > 0: maintainers_data = {} for email in emails: maintainers_data[email] = { 'ob': ob, 'here': self, 'person': auth_tool.name_from_userid(person), 'ob_edited': kwargs.get('ob_edited'), 'approved': ob.approved, 'container_basket': '%s/basketofapprovals_html' % folder.absolute_url(), } notif_logger.info('Maintainer notifications on %r', ofs_path(ob)) template = self._get_template('maintainer') self._send_notifications(maintainers_data, template) security.declarePrivate('notify_comment_maintainer') def notify_comment_maintainer(self, comment, parent, **kwargs): """ Process and notify by email that a comment B{comemnt} has been added to the object B{parent}. """ auth_tool = self.getSite().getAuthenticationTool() emails = self.getMaintainersEmails(parent) if len(emails) > 0: maintainers_data = {} for email in emails: maintainers_data[email] = { 'parent': parent, 'here': self, 'comment': comment, 'person': auth_tool.name_from_userid(comment.author), 'container_basket': '%s/basketofapprovals_html' % parent.absolute_url(), } notif_logger.info('Maintainer comment notifications on %r', ofs_path(parent)) template = self._get_template('maintainer') self._send_notifications(maintainers_data, template) security.declarePrivate('notify_administrative') def notify_administrative(self, ob, user_id, ob_edited=False): """ send administrative notifications because object `ob` was added or edited by the user `user_id` """ auth_tool = self.getSite().getAuthenticationTool() subscribers_data = utils.get_subscribers_data( self, ob, notif_type='administrative', **{ 'person': auth_tool.name_from_userid(user_id), 'ob_edited': ob_edited, 'approved': ob.approved, 'container_basket': '%s/basketofapprovals_html' % ob.aq_parent.absolute_url(), }) if len(subscribers_data.keys()) > 0: notif_logger.info('Administrative notifications on %r', ofs_path(ob)) template = self._get_template('administrative') self._send_notifications(subscribers_data, template) security.declarePrivate('notify_comment_administrative') def notify_comment_administrative(self, comment, parent, user_id): """ send administrative notifications because a comment was added to object `ob` by the user `user_id` """ auth_tool = self.getSite().getAuthenticationTool() subscribers_data = utils.get_subscribers_data( self, parent, notif_type='administrative', **{ 'comment': comment, 'parent': parent, 'here': self, 'person': auth_tool.name_from_userid(user_id), }) if len(subscribers_data.keys()) > 0: notif_logger.info('Administrative comment notifications on %r', ofs_path(parent)) template = self._get_template('administrative') self._send_notifications(subscribers_data, template) security.declarePrivate('notify_instant') def notify_instant(self, ob, user_id, ob_edited=False): """ send instant notifications because object `ob` was changed by the user `user_id` """ if not self.config['enable_instant']: return # Don't send notifications if the object is unapproved, but store them # into a queue to send them later when it becomes approved if not ob.approved: return auth_tool = self.getSite().getAuthenticationTool() subscribers_data = utils.get_subscribers_data( self, ob, **{ 'person': auth_tool.name_from_userid(user_id), 'ob_edited': ob_edited, }) if len(subscribers_data.keys()) > 0: notif_logger.info('Instant notifications on %r', ofs_path(ob)) template = self._get_template('instant') self._send_notifications(subscribers_data, template) security.declarePrivate('notify_comment_instant') def notify_comment_instant(self, comment, parent, user_id): """ send instant notifications because a comment was added to object `ob` by the user `user_id` """ if not self.config['enable_instant']: return # Don't send notifications if the object is unapproved, but store them # into a queue to send them later when it becomes approved if not parent.approved: return auth_tool = self.getSite().getAuthenticationTool() subscribers_data = utils.get_subscribers_data( self, parent, **{ 'comment': comment, 'parent': parent, 'person': auth_tool.name_from_userid(user_id), }) if len(subscribers_data.keys()) > 0: notif_logger.info('Comment instant notifications on %r', ofs_path(parent)) template = self._get_template('instant') self._send_notifications(subscribers_data, template) security.declarePrivate('notify_account_modification') def notify_account_modification(self, email, obj, username=None, new_roles=[], removed_roles=[]): """ Send notification that the user received or lost one or more roles in the specified location """ # Try to fix encoding for roles entered in other languages new_roles = [role.decode('utf8') for role in new_roles] removed_roles = [role.decode('utf8') for role in removed_roles] email_data = { email: { 'new_roles': new_roles, 'removed_roles': removed_roles, 'username': username, 'obj': obj, } } notif_logger.info('Account modification notification on %s' % self.getSite().getId()) template = self._get_template('account_modified') self._send_notifications(email_data, template) def _get_template(self, name): template = self._getOb('emailpt_%s' % name, None) if template is not None: return template.render_email template = self._getOb(name, None) if template is not None: return template.render_email template = email_templates.get(name, None) if template is not None: return template.render_email raise ValueError('template for %r not found' % name) def _send_notifications(self, messages_by_email, template): """ Send the notifications described in the `messages_by_email` data structure, using the specified EmailTemplate. `messages_by_email` should be a dictionary, keyed by email address. The values should be dictionaries suitable to be passed as kwargs (options) to the template. """ portal = self.getSite() email_tool = portal.getEmailTool() addr_from = email_tool.get_addr_from() for addr_to, kwargs in messages_by_email.iteritems(): translate = self.portal_i18n.get_translation kwargs.update({'portal': portal, '_translate': translate}) mail_data = template(**kwargs) notif_logger.info('.. sending notification to %r', addr_to) utils.send_notification(email_tool, addr_from, addr_to, mail_data['subject'], mail_data['body_text']) def _send_newsletter(self, notif_type, when_start, when_end): """ We'll look in the ``Products.Naaya.NySite.getActionLogger`` for object creation/modification log entries. Then we'll send notifications for the period between `when_start` and `when_end` using the `notif_type` template. """ notif_logger.info( 'Notifications newsletter on site %r, type %r, ' 'from %s to %s', ofs_path(self.getSite()), notif_type, when_start, when_end) objects_by_email = {} langs_by_email = {} subscriptions_by_email = {} anonymous_users = {} for log_type, ob in utils.get_modified_objects(self.getSite(), when_start, when_end): notif_logger.info('.. modified object: %r', ofs_path(ob)) for subscription in utils.fetch_subscriptions(ob, inherit=True): if subscription.notif_type != notif_type: continue if not subscription.check_permission(ob): continue email = subscription.get_email(ob) if email is None: continue content_types = getattr(subscription, 'content_types', []) if content_types and ob.meta_type not in content_types: continue notif_logger.info('.. .. sending newsletter to %r', email) objects_by_email.setdefault(email, []).append({ 'ob': ob, 'type': log_type, }) langs_by_email[email] = subscription.lang subscriptions_by_email[email] = subscription anonymous_users[email] = isinstance(subscription, AnonymousSubscription) messages_by_email = {} for email in objects_by_email: messages_by_email[email] = { 'objs': objects_by_email[email], '_lang': langs_by_email[email], 'subscription': subscriptions_by_email[email], 'here': self, 'anonymous': anonymous_users[email] } template = self._get_template(notif_type) self._send_notifications(messages_by_email, template) def _cron_heartbeat(self, when): transaction.commit() # commit earlier stuff; fresh transaction transaction.get().note('notifications cron at %s' % ofs_path(self)) # Clean temporary subscriptions after a week: if self.config.get('enable_anonymous', False): a_week_ago = when - timedelta(weeks=1) for tmp_subscription in self.pending_anonymous_subscriptions[:]: if tmp_subscription.datetime <= a_week_ago: self.pending_anonymous_subscriptions.remove( tmp_subscription) # daily newsletter ### if self.config['enable_daily']: # calculate the most recent daily newsletter time daily_time = time(hour=self.config['daily_hour']) latest_daily = datetime.combine(when.date(), daily_time) if latest_daily > when: latest_daily -= timedelta(days=1) # check if we should send a daily newsletter prev_daily = self.timestamps.get('daily', when - timedelta(days=1)) if prev_daily < latest_daily < when: self._send_newsletter('daily', prev_daily, when) self.timestamps['daily'] = when # weekly newsletter ### if self.config['enable_weekly']: # calculate the most recent weekly newsletter time weekly_time = time(hour=self.config['daily_hour']) t = datetime.combine(when.date(), weekly_time) days_delta = self.config['weekly_day'] - t.isoweekday() latest_weekly = t + timedelta(days=days_delta) if latest_weekly > when: latest_weekly -= timedelta(weeks=1) # check if we should send a weekly newsletter prev_weekly = self.timestamps.get('weekly', when - timedelta(weeks=1)) if prev_weekly < latest_weekly < when: self._send_newsletter('weekly', prev_weekly, when) self.timestamps['weekly'] = when # monthly newsletter ### if self.config['enable_monthly']: # calculate the most recent monthly newsletter time monthly_time = time(hour=self.config['monthly_hour']) the_day = utils.set_day_of_month(when.date(), self.config['monthly_day']) latest_monthly = datetime.combine(the_day, monthly_time) if latest_monthly > when: latest_monthly = utils.minus_one_month(latest_monthly) # check if we should send a monthly newsletter prev_monthly = self.timestamps.get('monthly', utils.minus_one_month(when)) if prev_monthly < latest_monthly < when: self._send_newsletter('monthly', prev_monthly, when) self.timestamps['monthly'] = when transaction.commit() # make sure our timestamp updates are saved def index_html(self, RESPONSE): """ redirect to admin page """ RESPONSE.redirect(self.absolute_url() + '/my_subscriptions_html') security.declareProtected(view, 'my_subscriptions_html') my_subscriptions_html = NaayaPageTemplateFile( 'zpt/index', globals(), 'naaya.core.notifications.my_subscriptions') security.declarePrivate('list_user_subscriptions') def user_subscriptions(self, user, cutoff_level=None): """ Returns all user subscriptions in the portal. Use with caution as this iterates almost all the objects in site. You can use `cutoff_level` to limit the depth. """ out = [] user_id = user.getId() for obj, n, subscription in utils.walk_subscriptions( self.getSite(), cutoff_level): if not isinstance(subscription, AccountSubscription): continue if subscription.user_id != user_id: continue out.append({ 'object': obj, 'notif_type': subscription.notif_type, 'content_types': getattr(subscription, 'content_types', []), 'lang': subscription.lang }) return out security.declareProtected(view, 'user_not_found') def user_not_found(self, REQUEST): """ Returns True if the user is not Anonymous, but is still not found by the AuthenticationTool (i.e. is maybe defined in the Zope root) """ user = REQUEST.AUTHENTICATED_USER if not isinstance(user, basestring): # with LDAP authentication, user is LDAP user instance user = user.id acl_tool = self.getAuthenticationTool() if acl_tool.get_user_with_userid(user) is None: return True security.declareProtected(view, 'list_my_subscriptions') def list_my_subscriptions(self, REQUEST): """ Returns a list of mappings (location, notif_type, lang) for all subscriptions of logged-in user """ user = REQUEST.AUTHENTICATED_USER if user.getId() is None and not self.config.get( 'enable_anonymous', False): raise Unauthorized # to force login subscriptions = self.user_subscriptions(user) for subscription in subscriptions: subscription['location'] = path_in_site(subscription['object']) del subscription['object'] return subscriptions security.declareProtected(view, 'my_first_subscription') def get_location_subscription(self, location, notif_type=None): """ Returns the first of the authenticated user's subscriptions in location """ for subscription in self.list_my_subscriptions(self.REQUEST): if subscription['location'] == location: if notif_type: if subscription['notif_type'] == notif_type: return subscription else: return subscription security.declareProtected(view, 'subscribe_me') def subscribe_me(self, REQUEST, location, notif_type, lang=None, content_types=[]): """ add subscription for currently-logged-in user """ # Even if some content types were selected (by turning off javascript) # they should be ignored, no filtering in administrative notifications if notif_type == 'administrative': content_types = [] if isinstance(content_types, basestring): content_types = [content_types] if lang is None: lang = self.gl_get_selected_language() REQUEST.form['lang'] = lang user_id = REQUEST.AUTHENTICATED_USER.getId() if location == '/': location = '' if user_id is None and not self.config.get('enable_anonymous', False): raise Unauthorized # to force login try: if user_id: self.add_account_subscription(user_id, location, notif_type, lang, content_types) if content_types: self.setSessionInfoTrans( 'You will receive ${notif_type} notifications' ' for any changes in "${location}" for objects of ' 'types ${content_types}.', notif_type=notif_type, location=location or self.getSite().title, content_types=', '.join(content_types)) else: self.setSessionInfoTrans( 'You will receive ${notif_type} notifications' ' for any changes in "${location}".', notif_type=notif_type, location=location) else: self.add_anonymous_subscription(**dict(REQUEST.form)) self.setSessionInfoTrans( 'An activation e-mail has been sent to ${email}. ' 'Follow the instructions to subscribe to ${notif_type} ' 'notifications for any changes in "${location}".', notif_type=notif_type, location=location, content_types=content_types, email=REQUEST.form.get('email')) except ValueError, msg: self.setSessionErrors([unicode(msg)]) return REQUEST.RESPONSE.redirect(self.absolute_url() + '/my_subscriptions_html')
_marker = {} def manage_addAuthomaticPlugin(context, id, title='', RESPONSE=None, **kw): """Create an instance of a Authomatic Plugin. """ plugin = AuthomaticPlugin(id, title, **kw) context._setObject(plugin.getId(), plugin) if RESPONSE is not None: RESPONSE.redirect('manage_workspace') manage_addAuthomaticPluginForm = PageTemplateFile( os.path.join(tpl_dir, 'add_plugin.pt'), globals(), __name__='addAuthomaticPlugin', ) @implementer(IAuthomaticPlugin, pas_interfaces.IAuthenticationPlugin, pas_interfaces.IPropertiesPlugin, pas_interfaces.IUserEnumerationPlugin, pas_interfaces.IRolesPlugin) class AuthomaticPlugin(BasePlugin): """Authomatic PAS Plugin """ security = ClassSecurityInfo() meta_type = 'Authomatic Plugin' BasePlugin.manage_options
msg += "Previewable setting changed. " if not bool(usable) is self.is_usable(): self.set_usable(bool(usable)) msg += "Usability setting changed. " if layout_id and layout_id != self._layout_id: self._layout_id = layout_id msg += "Layout object id changed. " return self.editSQLSource(manage_tabs_message=msg) InitializeClass(SQLSource) manage_addSQLSourceForm = PageTemplateFile( "www/sqlSourceAdd", globals(), __name__='manage_addSQLSourceForm') def manage_addSQLSource(context, id, title=None, REQUEST=None): """Add a SQLSource object """ source = SQLSource(id) title = unicode(title, source.management_page_charset) source.title = title context._setObject(id, source) source = context._getOb(id) source._set_statement('SELECT <dtml-var columns> FROM <dtml-var table>') # parameters form reset_parameter_form(source) reset_table_layout(source) add_and_edit(context, id, REQUEST, screen='editSQLSource') return ''
class SetupTool(Folder): """ Profile-based site configuration manager. """ implements(ISetupTool) meta_type = 'Generic Setup Tool' _import_context_id = '' security = ClassSecurityInfo() def __init__(self, id): self.id = str(id) self._import_registry = ImportStepRegistry() self._export_registry = ExportStepRegistry() self._export_registry.registerStep('step_registries', exportStepRegistries, 'Export import / export steps.', ) self._toolset_registry = ToolsetRegistry() # # ISetupTool API # security.declareProtected(ManagePortal, 'getEncoding') def getEncoding(self): """ See ISetupTool. """ return 'ascii' security.declareProtected(ManagePortal, 'getImportContextID') def getImportContextID(self): """ See ISetupTool. """ return self._import_context_id security.declareProtected(ManagePortal, 'setImportContext') def setImportContext(self, context_id, encoding=None): """ See ISetupTool. """ self._import_context_id = context_id context = self._getImportContext(context_id) self.applyContext(context, encoding) security.declareProtected(ManagePortal, 'applyContext') def applyContext(self, context, encoding=None): self._updateImportStepsRegistry(context, encoding) self._updateExportStepsRegistry(context, encoding) security.declareProtected(ManagePortal, 'getImportStepRegistry') def getImportStepRegistry(self): """ See ISetupTool. """ return self._import_registry security.declareProtected(ManagePortal, 'getExportStepRegistry') def getExportStepRegistry(self): """ See ISetupTool. """ return self._export_registry security.declareProtected(ManagePortal, 'getToolsetRegistry') def getToolsetRegistry(self): """ See ISetupTool. """ return self._toolset_registry security.declareProtected(ManagePortal, 'runImportStep') def runImportStep(self, step_id, run_dependencies=True, purge_old=None): """ See ISetupTool. """ context = self._getImportContext(self._import_context_id, purge_old) info = self._import_registry.getStepMetadata(step_id) if info is None: raise ValueError, 'No such import step: %s' % step_id dependencies = info.get('dependencies', ()) messages = {} steps = [] if run_dependencies: for dependency in dependencies: if dependency not in steps: message = self._doRunImportStep(dependency, context) messages[dependency] = message or '' steps.append(dependency) message = self._doRunImportStep(step_id, context) message_list = filter(None, [message]) message_list.extend( ['%s: %s' % x[1:] for x in context.listNotes()] ) messages[step_id] = '\n'.join(message_list) steps.append(step_id) return { 'steps' : steps, 'messages' : messages } security.declareProtected(ManagePortal, 'runAllImportSteps') def runAllImportSteps(self, purge_old=None): """ See ISetupTool. """ __traceback_info__ = self._import_context_id context = self._getImportContext(self._import_context_id, purge_old) return self._runImportStepsFromContext(context, purge_old=purge_old) security.declareProtected(ManagePortal, 'runExportStep') def runExportStep(self, step_id): """ See ISetupTool. """ return self._doRunExportSteps([step_id]) security.declareProtected(ManagePortal, 'runAllExportSteps') def runAllExportSteps(self): """ See ISetupTool. """ return self._doRunExportSteps(self._export_registry.listSteps()) security.declareProtected(ManagePortal, 'createSnapshot') def createSnapshot(self, snapshot_id): """ See ISetupTool. """ context = SnapshotExportContext(self, snapshot_id) messages = {} steps = self._export_registry.listSteps() for step_id in steps: handler = self._export_registry.getStep(step_id) if handler is None: raise ValueError('Invalid export step: %s' % step_id) messages[step_id] = handler(context) return { 'steps' : steps , 'messages' : messages , 'url' : context.getSnapshotURL() , 'snapshot' : context.getSnapshotFolder() } security.declareProtected(ManagePortal, 'compareConfigurations') def compareConfigurations(self, lhs_context, rhs_context, missing_as_empty=False, ignore_blanks=False, skip=SKIPPED_FILES, ): """ See ISetupTool. """ differ = ConfigDiff(lhs_context, rhs_context, missing_as_empty, ignore_blanks, skip, ) return differ.compare() security.declareProtected(ManagePortal, 'markupComparison') def markupComparison(self, lines): """ See ISetupTool. """ result = [] for line in lines.splitlines(): if line.startswith('** '): if line.find('File') > -1: if line.find('replaced') > -1: result.append(('file-to-dir', line)) elif line.find('added') > -1: result.append(('file-added', line)) else: result.append(('file-removed', line)) else: if line.find('replaced') > -1: result.append(('dir-to-file', line)) elif line.find('added') > -1: result.append(('dir-added', line)) else: result.append(('dir-removed', line)) elif line.startswith('@@'): result.append(('diff-range', line)) elif line.startswith(' '): result.append(('diff-context', line)) elif line.startswith('+'): result.append(('diff-added', line)) elif line.startswith('-'): result.append(('diff-removed', line)) elif line == '\ No newline at end of file': result.append(('diff-context', line)) else: result.append(('diff-header', line)) return '<pre>\n%s\n</pre>' % ( '\n'.join([('<span class="%s">%s</span>' % (cl, escape(l))) for cl, l in result])) # # ZMI # manage_options = (Folder.manage_options[:1] + ({'label' : 'Properties', 'action' : 'manage_tool' }, {'label' : 'Import', 'action' : 'manage_importSteps' }, {'label' : 'Export', 'action' : 'manage_exportSteps' }, {'label' : 'Snapshots', 'action' : 'manage_snapshots' }, {'label' : 'Comparison', 'action' : 'manage_showDiff' }, ) + Folder.manage_options[3:] # skip "View", "Properties" ) security.declareProtected(ManagePortal, 'manage_tool') manage_tool = PageTemplateFile('sutProperties', _wwwdir) security.declareProtected(ManagePortal, 'manage_updateToolProperties') def manage_updateToolProperties(self, context_id, RESPONSE): """ Update the tool's settings. """ self.setImportContext(context_id) RESPONSE.redirect('%s/manage_tool?manage_tabs_message=%s' % (self.absolute_url(), 'Properties+updated.')) security.declareProtected(ManagePortal, 'manage_importSteps') manage_importSteps = PageTemplateFile('sutImportSteps', _wwwdir) security.declareProtected(ManagePortal, 'manage_importSelectedSteps') def manage_importSelectedSteps(self, ids, run_dependencies, RESPONSE, create_report=True, ): """ Import the steps selected by the user. """ messages = {} if not ids: summary = 'No steps selected.' else: steps_run = [] for step_id in ids: result = self.runImportStep(step_id, run_dependencies) steps_run.extend(result['steps']) messages.update(result['messages']) summary = 'Steps run: %s' % ', '.join(steps_run) if create_report: name = self._mangleTimestampName('import-selected', 'log') self._createReport(name, result['steps'], result['messages']) return self.manage_importSteps(manage_tabs_message=summary, messages=messages) security.declareProtected(ManagePortal, 'manage_importSelectedSteps') def manage_importAllSteps(self, RESPONSE, create_report=True): """ Import all steps. """ result = self.runAllImportSteps() steps_run = 'Steps run: %s' % ', '.join(result['steps']) if create_report: name = self._mangleTimestampName('import-all', 'log') self._createReport(name, result['steps'], result['messages']) return self.manage_importSteps(manage_tabs_message=steps_run, messages=result['messages']) security.declareProtected(ManagePortal, 'manage_importTarball') def manage_importTarball(self, tarball, RESPONSE, create_report=True): """ Import steps from the uploaded tarball. """ if getattr(tarball, 'read', None) is not None: tarball = tarball.read() context = TarballImportContext(tool=self, archive_bits=tarball, encoding='UTF8', should_purge=True, ) result = self._runImportStepsFromContext(context, purge_old=True) steps_run = 'Steps run: %s' % ', '.join(result['steps']) if create_report: name = self._mangleTimestampName('import-all', 'log') self._createReport(name, result['steps'], result['messages']) return self.manage_importSteps(manage_tabs_message=steps_run, messages=result['messages']) security.declareProtected(ManagePortal, 'manage_exportSteps') manage_exportSteps = PageTemplateFile('sutExportSteps', _wwwdir) security.declareProtected(ManagePortal, 'manage_exportSelectedSteps') def manage_exportSelectedSteps(self, ids, RESPONSE): """ Export the steps selected by the user. """ if not ids: RESPONSE.redirect('%s/manage_exportSteps?manage_tabs_message=%s' % (self.absolute_url(), 'No+steps+selected.')) result = self._doRunExportSteps(ids) RESPONSE.setHeader('Content-type', 'application/x-gzip') RESPONSE.setHeader('Content-disposition', 'attachment; filename=%s' % result['filename']) return result['tarball'] security.declareProtected(ManagePortal, 'manage_exportAllSteps') def manage_exportAllSteps(self, RESPONSE): """ Export all steps. """ result = self.runAllExportSteps() RESPONSE.setHeader('Content-type', 'application/x-gzip') RESPONSE.setHeader('Content-disposition', 'attachment; filename=%s' % result['filename']) return result['tarball'] security.declareProtected(ManagePortal, 'manage_snapshots') manage_snapshots = PageTemplateFile('sutSnapshots', _wwwdir) security.declareProtected(ManagePortal, 'listSnapshotInfo') def listSnapshotInfo(self): """ Return a list of mappings describing available snapshots. o Keys include: 'id' -- snapshot ID 'title' -- snapshot title or ID 'url' -- URL of the snapshot folder """ result = [] snapshots = self._getOb('snapshots', None) if snapshots: for id, folder in snapshots.objectItems('Folder'): result.append({ 'id' : id , 'title' : folder.title_or_id() , 'url' : folder.absolute_url() }) return result security.declareProtected(ManagePortal, 'listProfileInfo') def listProfileInfo(self): """ Return a list of mappings describing registered profiles. o Keys include: 'id' -- profile ID 'title' -- profile title or ID 'description' -- description of the profile 'path' -- path to the profile within its product 'product' -- name of the registering product """ return _profile_registry.listProfileInfo() security.declareProtected(ManagePortal, 'listContextInfos') def listContextInfos(self): """ List registered profiles and snapshots. """ s_infos = [{ 'id': 'snapshot-%s' % info['id'], 'title': info['title'] } for info in self.listSnapshotInfo()] p_infos = [{ 'id': 'profile-%s' % info['id'], 'title': info['title'] } for info in self.listProfileInfo()] return tuple(s_infos + p_infos) security.declareProtected(ManagePortal, 'manage_createSnapshot') def manage_createSnapshot(self, RESPONSE, snapshot_id=None): """ Create a snapshot with the given ID. o If no ID is passed, generate one. """ if snapshot_id is None: snapshot_id = self._mangleTimestampName('snapshot') self.createSnapshot(snapshot_id) RESPONSE.redirect('%s/manage_snapshots?manage_tabs_message=%s' % (self.absolute_url(), 'Snapshot+created.')) security.declareProtected(ManagePortal, 'manage_showDiff') manage_showDiff = PageTemplateFile('sutCompare', _wwwdir) def manage_downloadDiff(self, lhs, rhs, missing_as_empty, ignore_blanks, RESPONSE, ): """ Crack request vars and call compareConfigurations. o Return the result as a 'text/plain' stream, suitable for framing. """ comparison = self.manage_compareConfigurations(lhs, rhs, missing_as_empty, ignore_blanks, ) RESPONSE.setHeader('Content-Type', 'text/plain') return _PLAINTEXT_DIFF_HEADER % (lhs, rhs, comparison) security.declareProtected(ManagePortal, 'manage_compareConfigurations') def manage_compareConfigurations(self, lhs, rhs, missing_as_empty, ignore_blanks, ): """ Crack request vars and call compareConfigurations. """ lhs_context = self._getImportContext(lhs) rhs_context = self._getImportContext(rhs) return self.compareConfigurations(lhs_context, rhs_context, missing_as_empty, ignore_blanks, ) # # Helper methods # security.declarePrivate('_getProductPath') def _getProductPath(self, product_name): """ Return the absolute path of the product's directory. """ try: # BBB: for GenericSetup 1.1 style product names product = __import__('Products.%s' % product_name , globals(), {}, ['initialize']) except ImportError: try: product = __import__(product_name , globals(), {}, ['initialize']) except ImportError: raise ValueError('Not a valid product name: %s' % product_name) return product.__path__[0] security.declarePrivate('_getImportContext') def _getImportContext(self, context_id, should_purge=None): """ Crack ID and generate appropriate import context. """ encoding = self.getEncoding() if context_id.startswith('profile-'): context_id = context_id[len('profile-'):] info = _profile_registry.getProfileInfo(context_id) if info.get('product'): path = os.path.join(self._getProductPath(info['product']) , info['path']) else: path = info['path'] if should_purge is None: should_purge = (info.get('type') != EXTENSION) return DirectoryImportContext(self, path, should_purge, encoding) # else snapshot context_id = context_id[len('snapshot-'):] if should_purge is None: should_purge = True return SnapshotImportContext(self, context_id, should_purge, encoding) security.declarePrivate('_updateImportStepsRegistry') def _updateImportStepsRegistry(self, context, encoding): """ Update our import steps registry from our profile. """ if context is None: context = self._getImportContext(self._import_context_id) xml = context.readDataFile(IMPORT_STEPS_XML) if xml is None: return info_list = self._import_registry.parseXML(xml, encoding) for step_info in info_list: id = step_info['id'] version = step_info['version'] handler = _resolveDottedName(step_info['handler']) dependencies = tuple(step_info.get('dependencies', ())) title = step_info.get('title', id) description = ''.join(step_info.get('description', [])) self._import_registry.registerStep(id=id, version=version, handler=handler, dependencies=dependencies, title=title, description=description, ) security.declarePrivate('_updateExportStepsRegistry') def _updateExportStepsRegistry(self, context, encoding): """ Update our export steps registry from our profile. """ if context is None: context = self._getImportContext(self._import_context_id) xml = context.readDataFile(EXPORT_STEPS_XML) if xml is None: return info_list = self._export_registry.parseXML(xml, encoding) for step_info in info_list: id = step_info['id'] handler = _resolveDottedName(step_info['handler']) title = step_info.get('title', id) description = ''.join(step_info.get('description', [])) self._export_registry.registerStep(id=id, handler=handler, title=title, description=description, ) security.declarePrivate('_doRunImportStep') def _doRunImportStep(self, step_id, context): """ Run a single import step, using a pre-built context. """ __traceback_info__ = step_id handler = self._import_registry.getStep(step_id) if handler is None: raise ValueError('Invalid import step: %s' % step_id) return handler(context) security.declarePrivate('_doRunExportSteps') def _doRunExportSteps(self, steps): """ See ISetupTool. """ context = TarballExportContext(self) messages = {} for step_id in steps: handler = self._export_registry.getStep(step_id) if handler is None: raise ValueError('Invalid export step: %s' % step_id) messages[step_id] = handler(context) return { 'steps' : steps , 'messages' : messages , 'tarball' : context.getArchive() , 'filename' : context.getArchiveFilename() } security.declarePrivate('_runImportStepsFromContext') def _runImportStepsFromContext(self, context, steps=None, purge_old=None): self.applyContext(context) if steps is None: steps = self._import_registry.sortSteps() messages = {} for step in steps: message = self._doRunImportStep(step, context) message_list = filter(None, [message]) message_list.extend( ['%s: %s' % x[1:] for x in context.listNotes()] ) messages[step] = '\n'.join(message_list) context.clearNotes() return { 'steps' : steps, 'messages' : messages } security.declarePrivate('_mangleTimestampName') def _mangleTimestampName(self, prefix, ext=None): """ Create a mangled ID using a timestamp. """ timestamp = time.gmtime() items = (prefix,) + timestamp[:6] if ext is None: fmt = '%s-%4d%02d%02d%02d%02d%02d' else: fmt = '%s-%4d%02d%02d%02d%02d%02d.%s' items += (ext,) return fmt % items security.declarePrivate('_createReport') def _createReport(self, name, steps, messages): """ Record the results of a run. """ lines = [] # Create report for step in steps: lines.append('=' * 65) lines.append('Step: %s' % step) lines.append('=' * 65) msg = messages[step] lines.extend(msg.split('\n')) lines.append('') report = '\n'.join(lines) if isinstance(report, unicode): report = report.encode('latin-1') file = File(id=name, title='', file=report, content_type='text/plain' ) self._setObject(name, file)
def _getExportTemplate(self): return PageTemplateFile('aitExport.xml', _xmldir)
class LocalChannel(SimpleItem, utils): """ """ implements(ILocalChannel) meta_type = METATYPE_LOCALCHANNEL icon = 'misc_/NaayaCore/LocalChannel.gif' manage_options = (( { 'label': 'Properties', 'action': 'manage_properties_html' }, { 'label': 'View', 'action': 'index_html' }, ) + SimpleItem.manage_options) security = ClassSecurityInfo() def __init__(self, id, title, description, language, type, objmetatype, numberofitems): """ """ self.id = id self.title = title self.description = description self.language = language self.type = type self.objmetatype = objmetatype self.numberofitems = numberofitems security.declarePrivate('syndicateThis') def syndicateThis(self): xml = rss_item_for_channel(self) return etree.tostring(xml, xml_declaration=False, encoding="utf-8") security.declareProtected(view_management_screens, 'manageProperties') def manageProperties(self, title='', description='', language=None, type='', objmetatype=[], numberofitems='', REQUEST=None): """ """ if language is None: language = self.gl_get_selected_language() objmetatype = self.utConvertToList(objmetatype) try: numberofitems = abs(int(numberofitems)) except: numberofitems = 0 self.title = title self.description = description self.language = language self.type = type self.objmetatype = objmetatype self.numberofitems = numberofitems self._p_changed = 1 if REQUEST: REQUEST.RESPONSE.redirect('manage_properties_html') def get_objects_for_rdf(self): #return the objects to be syndicated l_items = [] if len(self.objmetatype) > 0: l_howmany = -1 if self.numberofitems != 0: l_howmany = self.numberofitems l_items = self.query_translated_objects(meta_type=self.objmetatype, lang=self.language, approved=1, howmany=l_howmany) return l_items security.declareProtected(view, 'index_html') def index_html(self, feed='', REQUEST=None, RESPONSE=None): """ """ if feed == 'atom': return self.syndicateAtom(self, self.get_objects_for_rdf(), self.language) s = self.getSite() lang = self.language if lang == 'auto': lang = self.gl_get_selected_language() l_items = self.get_objects_for_rdf() namespaces = self.getNamespaceItemsList() nsmap = nsmap = get_nsmap(namespaces) header = [] for n in namespaces: header.append(str(n)) rdf_namespace = nsmap['rdf'] Rdf = ElementMaker(namespace=rdf_namespace, nsmap=nsmap) E = ElementMaker(None, nsmap=nsmap) xml = Rdf.RDF(rss_channel_for_channel(self, lang)) channel = xml[0] items = channel[-1] seq = etree.SubElement(items, '{%s}Seq' % rdf_namespace) for i in l_items: x = etree.SubElement(seq, '{%s}li' % rdf_namespace, resource=i.absolute_url()) if self.hasImage(): image = E.image(E.title(self.title), E.url(self.getImagePath()), E.link(s.absolute_url()), E.description(self.utToUtf8(self.description))) xml.append(image) received_items = ''.join([i.syndicateThis() for i in l_items]) received = '<rdf:RDF %s>%s</rdf:RDF>' % (''.join(header), received_items) xml_received = etree.XML(received, etree.XMLParser(strip_cdata=False)) xml.extend(xml_received) self.REQUEST.RESPONSE.setHeader('content-type', 'text/xml') return etree.tostring(xml, xml_declaration=True, encoding="utf-8") #zmi pages security.declareProtected(view_management_screens, 'manage_properties_html') manage_properties_html = PageTemplateFile('zpt/localchannel_properties', globals())
from zope.interface import implements from Globals import InitializeClass from AccessControl import ClassSecurityInfo from AccessControl.Permissions import view_management_screens, view from OFS.SimpleItem import SimpleItem from Products.PageTemplates.PageTemplateFile import PageTemplateFile from Products.NaayaCore.interfaces import ILocalChannel from Products.NaayaCore.constants import * from Products.NaayaCore.managers.utils import utils, get_nsmap, rss_channel_for_channel, rss_item_for_channel from lxml import etree from lxml.builder import ElementMaker manage_addLocalChannelForm = PageTemplateFile('zpt/localchannel_manage_add', globals()) def manage_addLocalChannel(self, id='', title='', description='', language=None, type='', objmetatype=[], numberofitems='', portlet='', REQUEST=None): """ """ id = self.utSlugify(id) if language is None: language = self.gl_get_selected_language()
class ZMSFormatProvider(ZMSItem.ZMSItem, ZMSTextformatManager.ZMSTextformatManager, ZMSCharformatManager.ZMSCharformatManager): # Properties. # ----------- meta_type = 'ZMSFormatProvider' zmi_icon = "fas fa-font" icon_clazz = zmi_icon # Management Options. # ------------------- manage_options_default_action = '../manage_customize' def manage_options(self): return [ self.operator_setitem(x, 'action', '../' + x['action']) for x in copy.deepcopy(self.aq_parent.manage_options()) ] manage_sub_options__roles__ = None def manage_sub_options(self): return ( { 'label': 'TAB_TEXTFORMATS', 'action': 'manage_textformats' }, { 'label': 'TAB_CHARFORMATS', 'action': 'manage_charformats' }, ) # Management Interface. # --------------------- manage = PageTemplateFile('zpt/ZMSFormatProvider/manage_textformats', globals()) manage_main = PageTemplateFile('zpt/ZMSFormatProvider/manage_textformats', globals()) manage_textformats = PageTemplateFile( 'zpt/ZMSFormatProvider/manage_textformats', globals()) manage_charformats = PageTemplateFile( 'zpt/ZMSFormatProvider/manage_charformats', globals()) # Management Permissions. # ----------------------- __administratorPermissions__ = ( 'manage_changeTextformat', 'manage_textformats', 'manage_changeCharformat', 'manage_charformats', ) __ac_permissions__ = (('ZMS Administrator', __administratorPermissions__), ) """ ############################################################################ # # Constructor # ############################################################################ """ ############################################################################ # ZMSFormatProvider.__init__: # # Initialise a new instance. ############################################################################ def __init__(self, textformats=[], charformats=[]): self.id = 'format_manager' self.textformats = copy.deepcopy(textformats) self.charformats = copy.deepcopy(charformats)
class UniqueIdHandlerTool(UniqueObject, SimpleItem, ActionProviderBase): __doc__ = __doc__ # copy from module implements(IUniqueIdHandler, IUniqueIdBrainQuery, IUniqueIdUnrestrictedQuery) __implements__ = ( ActionProviderBase.__implements__, SimpleItem.__implements__, ) id = 'portal_uidhandler' manage_options = ( ActionProviderBase.manage_options + ( {'label':'Query', 'action':'manage_queryObject'} , ) + SimpleItem.manage_options ) alternative_id = "portal_standard_uidhandler" meta_type = 'Unique Id Handler Tool' # make the uid attribute name available for the unit tests # not meant to be altered as long you don't know what you do!!! UID_ATTRIBUTE_NAME = UID_ATTRIBUTE_NAME # make the exception class available through the tool UniqueIdError = UniqueIdError security = ClassSecurityInfo() def _reindexObject(self, obj): # add uid index and colums to catalog if not yet done UID_ATTRIBUTE_NAME = self.UID_ATTRIBUTE_NAME catalog = getToolByName(self, 'portal_catalog') if UID_ATTRIBUTE_NAME not in catalog.indexes(): catalog.addIndex(UID_ATTRIBUTE_NAME, 'FieldIndex') catalog.addColumn(UID_ATTRIBUTE_NAME) # reindex catalog.reindexObject(obj) def _setUid(self, obj, uid): """Attaches a unique id to the object and does reindexing. """ # attach a unique id annotation to the object anno_tool = getToolByName(self, 'portal_uidannotation') annotation = anno_tool(obj, self.UID_ATTRIBUTE_NAME) annotation.setUid(uid) # reindex the object self._reindexObject(obj) security.declarePublic('register') def register(self, obj): """See IUniqueIdSet. """ uid = self.queryUid(obj, default=None) if uid is None: # generate a new unique id and set it generator = getToolByName(self, 'portal_uidgenerator') uid = generator() self._setUid(obj, uid) return uid security.declareProtected(ManagePortal, 'unregister') def unregister(self, obj): """See IUniqueIdSet. """ UID_ATTRIBUTE_NAME = self.UID_ATTRIBUTE_NAME if getattr(aq_base(obj), UID_ATTRIBUTE_NAME, None) is None: raise UniqueIdError, \ "No unique id available to be unregistered on '%s'" % obj # delete the uid and reindex delattr(obj, UID_ATTRIBUTE_NAME) self._reindexObject(obj) security.declarePublic('queryUid') def queryUid(self, obj, default=None): """See IUniqueIdQuery. """ uid = getattr(aq_base(obj), self.UID_ATTRIBUTE_NAME, None) # If 'obj' is a content object the 'uid' attribute is usually a # callable object. If 'obj' is a catalog brain the uid attribute # is non callable and possibly equals the 'Missing.MV' value. if uid is Missing.MV or uid is None: return default if callable(uid): return uid() return uid security.declarePublic('getUid') def getUid(self, obj): """See IUniqueIdQuery. """ uid = self.queryUid(obj, None) if uid is None: raise UniqueIdError, "No unique id available on '%s'" % obj return uid security.declarePrivate('setUid') def setUid(self, obj, uid, check_uniqueness=True): """See IUniqueIdSet. """ # None is the only value a unique id shall never have! if uid is None: raise UniqueIdError, "It's forbidden to set a unique id to 'None'." # check for uniqueness if enabled if check_uniqueness: result = self.queryObject(uid) if result is not None and result != obj: if callable(uid): uid = uid() raise UniqueIdError, \ "The unique id '%s' is already in use" % uid # everything is ok: set it! self._setUid(obj, uid) def _queryBrain(self, uid, searchMethodName, default=None): """This helper method does the "hard work" of querying the catalog and interpreting the results. """ if uid is None: return default # convert the uid to the right format generator = getToolByName(self, 'portal_uidgenerator') uid = generator.convert(uid) catalog = getToolByName(self, 'portal_catalog') searchMethod = getattr(catalog, searchMethodName) result = searchMethod({self.UID_ATTRIBUTE_NAME: uid}) len_result = len(result) # return None if no object found with this uid if len_result == 0: return default # print a message to the log if more than one object has # the same uid (uups!) if len_result > 1: logging.info("CMUid ASSERT:", "Uups, %s objects have '%s' as uid!!!" % \ (len_result, uid)) return result[0] security.declarePublic('queryBrain') def queryBrain(self, uid, default=None): """See IUniqueIdBrainQuery. """ return self._queryBrain(uid, 'searchResults', default) def _getBrain(self, uid, queryBrainMethod): brain = queryBrainMethod(uid, default=None) if brain is None: raise UniqueIdError, "No object found with '%s' as uid." % uid return brain security.declarePublic('getBrain') def getBrain(self, uid): """See IUniqueIdBrainQuery. """ return self._getBrain(uid, self.queryBrain) security.declarePublic('getObject') def getObject(self, uid): """See IUniqueIdQuery. """ return self.getBrain(uid).getObject() security.declarePublic('queryObject') def queryObject(self, uid, default=None): """See IUniqueIdQuery. """ try: return self.getObject(uid) except UniqueIdError: return default security.declarePrivate('unrestrictedQueryBrain') def unrestrictedQueryBrain(self, uid, default=None): """See IUniqueIdUnrestrictedQuery. """ return self._queryBrain(uid, 'unrestrictedSearchResults', default) security.declarePrivate('unrestrictedGetBrain') def unrestrictedGetBrain(self, uid): """See IUniqueIdUnrestrictedQuery. """ return self._getBrain(uid, self.unrestrictedQueryBrain) security.declarePrivate('unrestrictedGetObject') def unrestrictedGetObject(self, uid): """See IUniqueIdUnrestrictedQuery. """ return self.unrestrictedGetBrain(uid).getObject() security.declarePrivate('unrestrictedQueryObject') def unrestrictedQueryObject(self, uid, default=None): """See IUniqueIdUnrestrictedQuery. """ try: return self.unrestrictedGetObject(uid) except UniqueIdError: return default security.declareProtected(ManagePortal, 'manage_queryObject') manage_queryObject = PageTemplateFile('queryUID.pt', _wwwdir)
class SQLSource(EditableExternalSource, Folder, ZMIObject): grok.implements(IExternalSource) # register icon and factories silvaconf.icon('www/sqlsource.png') silvaconf.factory('manage_addSQLSourceForm') silvaconf.factory('manage_addSQLSource') silvaconf.zmi_addable(True) meta_type = "Silva SQL Source" security = ClassSecurityInfo() _sql_method_id = 'sql_method' _layout_id = 'layout' _default_batch_size = 10 _v_cached_parameters = None # ZMI Tabs manage_options = ( {'label':'Edit', 'action':'editSQLSource'}, {'label':'Parameters', 'action':'parameters/manage_main'}, ) + Folder.manage_options management_page_charset = 'utf-8' security.declareProtected(ViewManagementScreens, 'editSQLSource') editSQLSource = PageTemplateFile( 'www/sqlSourceEdit', globals(), __name__='sqlCodeSource') def __init__(self, id): self.id = id self._sql_method = None self._statement = None self._connection_id = None # ACCESSORS def layout_id(self): return self._layout_id def connection_id(self): return self._connection_id def statement(self): return self._statement def available_connection_ids(self): return SQLConnectionIDs(self) security.declareProtected(AccessContentsInformation, 'to_html') def to_html(self, content, request, **parameters): """ render HTML for SQL source """ values = self._get_data(parameters) names = values.names() batch_size = self._default_batch_size if parameters.get('sqlbatchsize'): batch_size = int(parameters.get('sqlbatchsize')) data = Batch( values.dictionaries(), factory=self._decode_dict_helper, count=batch_size, name=self.getId(), request=request) model = content if IVersion.providedBy(content): model = content.get_content() layout = self._getOb(self._layout_id) batch = getMultiAdapter((model, data, request), IBatching)() return layout( table=data, batch=batch, names=names, parameters=parameters) def _get_data(self, args): if not self._sql_method: self._set_sql_method() elif self._v_cached_parameters != self.get_parameters_form().get_field_ids(): self._set_sql_method() args = self._encode_dict_helper(args) return self._sql_method(REQUEST=args) def _decode_dict_helper(self, dictionary): for key, value in dictionary.items(): if type(value) is type(''): dictionary[key] = unicode( value, self._data_encoding, 'replace') return dictionary def _encode_dict_helper(self, dictionary): for key, value in dictionary.items(): if type(value) is type(u''): dictionary[key] = value.encode( self._data_encoding, 'replace') return dictionary # MODIFIERS def _set_statement(self, statement): self._statement = statement #invalidate sql method self._sql_method = None self._p_changed = 1 def _set_connection_id(self, id): self._connection_id = id #invalidate sql method self._sql_method = None self._p_changed = 1 def _set_sql_method(self): self._v_cached_parameters = parameters = self.get_parameters_form().get_field_ids() arguments = '\n'.join(parameters) self._sql_method = SQL( self._sql_method_id, '', self._connection_id, arguments.encode('ascii'), self._statement.encode('UTF-8')) self._p_changed = 1 # MANAGERS security.declareProtected(ViewManagementScreens, 'manage_editSQLSource') def manage_editSQLSource( self, title, data_encoding, statement, connection_id=None, description=None, cacheable=None, layout_id=None, reset_layout=None, reset_params=None, previewable=None, usable=None, ): """ Edit SQLSource object """ msg = u'' if reset_layout: reset_table_layout(self) return self.editSQLSource( manage_tabs_message="Table rendering pagetemplate reset " \ "to default layout.") if reset_params: reset_parameter_form(self) return self.editSQLSource( manage_tabs_message="Parameters form reset to default.") if data_encoding and data_encoding != self._data_encoding: try: unicode('abcd', data_encoding, 'replace') except LookupError: # unknown encoding, return error message msg += "Unknown encoding %s, not changed!" % data_encoding return self.editSQLSource(manage_tabs_message=msg) self.set_data_encoding(data_encoding) msg += "Data encoding changed. " if connection_id and connection_id != self._connection_id: self._set_connection_id(connection_id) msg += "Connection id changed. " if statement: statement = unicode(statement, 'UTF-8') self._set_statement(statement) msg += "SQL statement changed. " title = unicode(title, self.management_page_charset) if title and title != self.title: self.set_title(title) msg += "Title changed. " description = unicode(description, self.management_page_charset) if description != self._description: self.set_description(description) msg += "Description changed. " if not bool(cacheable) is self.is_cacheable(): self.set_cacheable(bool(cacheable)) msg += "Cacheability setting changed. " if not bool(previewable) is self.is_previewable(): self.set_previewable(bool(previewable)) msg += "Previewable setting changed. " if not bool(usable) is self.is_usable(): self.set_usable(bool(usable)) msg += "Usability setting changed. " if layout_id and layout_id != self._layout_id: self._layout_id = layout_id msg += "Layout object id changed. " return self.editSQLSource(manage_tabs_message=msg)
class SetupTool( UniqueObject, Folder ): """ Profile-based site configuration manager. """ __implements__ = ( ISetupTool, ) + Folder.__implements__ id = 'portal_setup' meta_type = 'Portal Setup Tool' _product_name = None _profile_directory = None _root_directory = None security = ClassSecurityInfo() def __init__( self ): self._import_registry = ImportStepRegistry() self._export_registry = ExportStepRegistry() self._export_registry.registerStep( 'step_registries' , exportStepRegistries , 'Export import / export steps.' ) self._toolset_registry = ToolsetRegistry() # # ISetupTool API # security.declareProtected( ManagePortal, 'getProfileProduct' ) def getProfileProduct( self ): """ See ISetupTool. """ return self._product_name security.declareProtected( ManagePortal, 'getProfileDirectory' ) def getProfileDirectory( self, relative_to_product=False ): """ See ISetupTool. """ return ( relative_to_product and self._profile_directory or self._getFullyQualifiedProfileDirectory() ) security.declareProtected( ManagePortal, 'setProfileDirectory' ) def setProfileDirectory( self, path, product_name=None, encoding=None ): """ See ISetupTool. """ if product_name is not None: root = self._root_directory = self._getProductPath( product_name ) if not os.path.exists( os.path.join( root, path ) ): raise ValueError, 'Invalid path: %s' % path else: if not os.path.exists( path ): raise ValueError, 'Invalid path: %s' % path self._root_directory = None self._profile_directory = path self._product_name = product_name self._updateImportStepsRegistry( encoding ) self._updateExportStepsRegistry( encoding ) self._updateToolsetRegistry( encoding ) security.declareProtected( ManagePortal, 'getImportStepRegistry' ) def getImportStepRegistry( self ): """ See ISetupTool. """ return self._import_registry security.declareProtected( ManagePortal, 'getImportStepRegistry' ) def getExportStepRegistry( self ): """ See ISetupTool. """ return self._export_registry security.declareProtected( ManagePortal, 'getToolsetRegistry' ) def getToolsetRegistry( self ): """ See ISetupTool. """ return self._toolset_registry security.declareProtected( ManagePortal, 'executeStep' ) def runImportStep( self, step_id, run_dependencies=True, purge_old=True ): """ See ISetupTool. """ profile_path = self._getFullyQualifiedProfileDirectory() context = DirectoryImportContext( self, profile_path, purge_old ) info = self._import_registry.getStepMetadata( step_id ) if info is None: raise ValueError, 'No such import step: %s' % step_id dependencies = info.get( 'dependencies', () ) messages = {} steps = [] if run_dependencies: for dependency in dependencies: if dependency not in steps: message = self._doRunImportStep( dependency, context ) messages[ dependency ] = message steps.append( dependency ) message = self._doRunImportStep( step_id, context ) messages[ step_id ] = message steps.append( step_id ) return { 'steps' : steps, 'messages' : messages } security.declareProtected( ManagePortal, 'runAllSetupSteps') def runAllImportSteps( self, purge_old=True ): """ See ISetupTool. """ profile_path = self._getFullyQualifiedProfileDirectory() context = DirectoryImportContext( self, profile_path, purge_old ) steps = self._import_registry.sortSteps() messages = {} for step in steps: message = self._doRunImportStep( step, context ) messages[ step ] = message return { 'steps' : steps, 'messages' : messages } security.declareProtected( ManagePortal, 'runExportStep') def runExportStep( self, step_id ): """ See ISetupTool. """ return self._doRunExportSteps( [ step_id ] ) security.declareProtected(ManagePortal, 'runAllExportSteps') def runAllExportSteps( self ): """ See ISetupTool. """ return self._doRunExportSteps( self._export_registry.listSteps() ) security.declareProtected( ManagePortal, 'createSnapshot') def createSnapshot( self, snapshot_id ): """ See ISetupTool. """ context = SnapshotExportContext( self, snapshot_id ) messages = {} steps = self._export_registry.listSteps() for step_id in steps: handler = self._export_registry.getStep( step_id ) if handler is None: raise ValueError( 'Invalid export step: %s' % step_id ) messages[ step_id ] = handler( context ) return { 'steps' : steps , 'messages' : messages , 'url' : context.getSnapshotURL() , 'snapshot' : context.getSnapshotFolder() } security.declareProtected(ManagePortal, 'compareConfigurations') def compareConfigurations( self , lhs_context , rhs_context , missing_as_empty=False , ignore_blanks=False , skip=( 'CVS', '.svn' ) ): """ See ISetupTool. """ differ = ConfigDiff( lhs_context , rhs_context , missing_as_empty , ignore_blanks , skip ) return differ.compare() security.declareProtected( ManagePortal, 'markupComparison') def markupComparison(self, lines): """ See ISetupTool. """ result = [] for line in lines.splitlines(): if line.startswith('** '): if line.find('File') > -1: if line.find('replaced') > -1: result.append( ( 'file-to-dir', line ) ) elif line.find('added') > -1: result.append( ( 'file-added', line ) ) else: result.append( ( 'file-removed', line ) ) else: if line.find('replaced') > -1: result.append( ( 'dir-to-file', line ) ) elif line.find('added') > -1: result.append( ( 'dir-added', line ) ) else: result.append( ( 'dir-removed', line ) ) elif line.startswith('@@'): result.append( ( 'diff-range', line ) ) elif line.startswith(' '): result.append( ( 'diff-context', line ) ) elif line.startswith('+'): result.append( ( 'diff-added', line ) ) elif line.startswith('-'): result.append( ( 'diff-removed', line ) ) elif line == '\ No newline at end of file': result.append( ( 'diff-context', line ) ) else: result.append( ( 'diff-header', line ) ) return '<pre>\n%s\n</pre>' % ( '\n'.join( [ ( '<span class="%s">%s</span>' % ( cl, escape( l ) ) ) for cl, l in result] ) ) # # ZMI # manage_options = ( Folder.manage_options[ :1 ] + ( { 'label' : 'Properties' , 'action' : 'manage_tool' } , { 'label' : 'Import' , 'action' : 'manage_importSteps' } , { 'label' : 'Export' , 'action' : 'manage_exportSteps' } , { 'label' : 'Snapshots' , 'action' : 'manage_snapshots' } , { 'label' : 'Comparison' , 'action' : 'manage_showDiff' } ) + Folder.manage_options[ 3: ] # skip "View", "Properties" ) security.declareProtected( ManagePortal, 'manage_tool' ) manage_tool = PageTemplateFile( 'sutProperties', _wwwdir ) security.declareProtected( ManagePortal, 'manage_updateToolProperties' ) def manage_updateToolProperties( self , profile_directory , profile_product , RESPONSE ): """ Update the tool's settings. """ profile_directory = profile_directory.strip() profile_product = profile_product.strip() if profile_directory.startswith( '.' ): raise ValueError( "Directories begining with '.' are not allowed." ) if profile_product and profile_directory.startswith( '/' ): raise ValueError( "Product may not be specified with absolute directories" ) self.setProfileDirectory( profile_directory, profile_product ) RESPONSE.redirect( '%s/manage_tool?manage_tabs_message=%s' % ( self.absolute_url(), 'Properties+updated.' ) ) security.declareProtected( ManagePortal, 'manage_importSteps' ) manage_importSteps = PageTemplateFile( 'sutImportSteps', _wwwdir ) security.declareProtected( ManagePortal, 'manage_importSelectedSteps' ) def manage_importSelectedSteps( self , ids , run_dependencies , purge_old , RESPONSE ): """ Import the steps selected by the user. """ if not ids: message = 'No+steps+selected.' else: steps_run = [] for step_id in ids: result = self.runImportStep( step_id , run_dependencies , purge_old ) steps_run.extend( result[ 'steps' ] ) message = 'Steps+run:%s' % '+,'.join( steps_run ) RESPONSE.redirect( '%s/manage_importSteps?manage_tabs_message=%s' % ( self.absolute_url(), message ) ) security.declareProtected( ManagePortal, 'manage_importSelectedSteps' ) def manage_importAllSteps( self, purge_old, RESPONSE ): """ Import all steps. """ result = self.runAllImportSteps( purge_old ) message = 'Steps+run:%s' % '+,'.join( result[ 'steps' ] ) RESPONSE.redirect( '%s/manage_importSteps?manage_tabs_message=%s' % ( self.absolute_url(), message ) ) security.declareProtected( ManagePortal, 'manage_exportSteps' ) manage_exportSteps = PageTemplateFile( 'sutExportSteps', _wwwdir ) security.declareProtected( ManagePortal, 'manage_exportSelectedSteps' ) def manage_exportSelectedSteps( self, ids, RESPONSE ): """ Export the steps selected by the user. """ if not ids: RESPONSE.redirect( '%s/manage_exportSteps?manage_tabs_message=%s' % ( self.absolute_url(), 'No+steps+selected.' ) ) result = self._doRunExportSteps( ids ) RESPONSE.setHeader( 'Content-type', 'application/x-gzip') RESPONSE.setHeader( 'Content-disposition' , 'attachment; filename=%s' % result[ 'filename' ] ) return result[ 'tarball' ] security.declareProtected( ManagePortal, 'manage_exportAllSteps' ) def manage_exportAllSteps( self, RESPONSE ): """ Export all steps. """ result = self.runAllExportSteps() RESPONSE.setHeader( 'Content-type', 'application/x-gzip') RESPONSE.setHeader( 'Content-disposition' , 'attachment; filename=%s' % result[ 'filename' ] ) return result[ 'tarball' ] security.declareProtected( ManagePortal, 'manage_snapshots' ) manage_snapshots = PageTemplateFile( 'sutSnapshots', _wwwdir ) security.declareProtected( ManagePortal, 'listSnapshotInfo' ) def listSnapshotInfo( self ): """ Return a list of mappings describing available snapshots. o Keys include: 'id' -- snapshot ID 'title' -- snapshot title or ID 'url' -- URL of the snapshot folder """ result = [] snapshots = self._getOb( 'snapshots', None ) if snapshots: for id, folder in snapshots.objectItems( 'Folder' ): result.append( { 'id' : id , 'title' : folder.title_or_id() , 'url' : folder.absolute_url() } ) return result security.declareProtected( ManagePortal, 'listProfileInfo' ) def listProfileInfo( self ): """ Return a list of mappings describing registered profiles. o Keys include: 'id' -- profile ID 'title' -- profile title or ID 'description' -- description of the profile 'path' -- path to the profile within its product 'product' -- name of the registering product """ return _profile_registry.listProfileInfo() security.declareProtected( ManagePortal, 'manage_createSnapshot' ) def manage_createSnapshot( self, RESPONSE, snapshot_id=None ): """ Create a snapshot with the given ID. o If no ID is passed, generate one. """ if snapshot_id is None: timestamp = time.gmtime() snapshot_id = 'snapshot-%4d%02d%02d%02d%02d%02d' % timestamp[:6] self.createSnapshot( snapshot_id ) RESPONSE.redirect( '%s/manage_snapshots?manage_tabs_message=%s' % ( self.absolute_url(), 'Snapshot+created.' ) ) security.declareProtected( ManagePortal, 'manage_showDiff' ) manage_showDiff = PageTemplateFile( 'sutCompare', _wwwdir ) def manage_downloadDiff( self , lhs , rhs , missing_as_empty , ignore_blanks , RESPONSE ): """ Crack request vars and call compareConfigurations. o Return the result as a 'text/plain' stream, suitable for framing. """ comparison = self.manage_compareConfigurations( lhs , rhs , missing_as_empty , ignore_blanks ) RESPONSE.setHeader( 'Content-Type', 'text/plain' ) return _PLAINTEXT_DIFF_HEADER % ( lhs, rhs, comparison ) security.declareProtected( ManagePortal, 'manage_compareConfigurations' ) def manage_compareConfigurations( self , lhs , rhs , missing_as_empty , ignore_blanks ): """ Crack request vars and call compareConfigurations. """ lhs_context = self._getImportContext( lhs ) rhs_context = self._getImportContext( rhs ) return self.compareConfigurations( lhs_context , rhs_context , missing_as_empty , ignore_blanks ) # # Helper methods # security.declarePrivate( '_getProductPath' ) def _getProductPath( self, product_name ): """ Return the absolute path of the product's directory. """ try: product = __import__( 'Products.%s' % product_name , globals(), {}, ['initialize' ] ) except ImportError: raise ValueError, 'Not a valid product name: %s' % product_name return product.__path__[0] security.declarePrivate( '_getImportContext' ) def _getImportContext( self, context_id ): """ Crack ID and generate appropriate import context. """ if context_id.startswith( 'profile-' ): context_id = context_id[ len( 'profile-' ): ] info = _profile_registry.getProfileInfo( context_id ) if info.get( 'product' ): path = os.path.join( self._getProductPath( info[ 'product' ] ) , info[ 'path' ] ) else: path = info[ 'path' ] return DirectoryImportContext( self, path ) # else snapshot context_id = context_id[ len( 'snapshot-' ): ] return SnapshotImportContext( self, context_id ) security.declarePrivate( '_getFullyQualifiedProfileDirectory' ) def _getFullyQualifiedProfileDirectory( self ): """ Return the fully-qualified directory path of our profile. """ if self._root_directory is not None: return os.path.join( self._root_directory , self._profile_directory ) return self._profile_directory security.declarePrivate( '_updateImportStepsRegistry' ) def _updateImportStepsRegistry( self, encoding ): """ Update our import steps registry from our profile. """ fq = self._getFullyQualifiedProfileDirectory() f = open( os.path.join( fq, IMPORT_STEPS_XML ), 'r' ) xml = f.read() f.close() info_list = self._import_registry.parseXML( xml, encoding ) for step_info in info_list: id = step_info[ 'id' ] version = step_info[ 'version' ] handler = _resolveDottedName( step_info[ 'handler' ] ) dependencies = tuple( step_info.get( 'dependencies', () ) ) title = step_info.get( 'title', id ) description = ''.join( step_info.get( 'description', [] ) ) self._import_registry.registerStep( id=id , version=version , handler=handler , dependencies=dependencies , title=title , description=description ) security.declarePrivate( '_updateExportStepsRegistry' ) def _updateExportStepsRegistry( self, encoding ): """ Update our export steps registry from our profile. """ fq = self._getFullyQualifiedProfileDirectory() f = open( os.path.join( fq, EXPORT_STEPS_XML ), 'r' ) xml = f.read() f.close() info_list = self._export_registry.parseXML( xml, encoding ) for step_info in info_list: id = step_info[ 'id' ] handler = _resolveDottedName( step_info[ 'handler' ] ) title = step_info.get( 'title', id ) description = ''.join( step_info.get( 'description', [] ) ) self._export_registry.registerStep( id=id , handler=handler , title=title , description=description ) security.declarePrivate( '_updateToolsetRegistry' ) def _updateToolsetRegistry( self, encoding ): """ Update our toolset registry from our profile. """ fq = self._getFullyQualifiedProfileDirectory() f = open( os.path.join( fq, TOOLSET_XML ), 'r' ) xml = f.read() f.close() self._toolset_registry.parseXML( xml, encoding ) security.declarePrivate( '_doRunImportStep' ) def _doRunImportStep( self, step_id, context ): """ Run a single import step, using a pre-built context. """ handler = self._import_registry.getStep( step_id ) if handler is None: raise ValueError( 'Invalid import step: %s' % step_id ) return handler( context ) security.declarePrivate( '_doRunExportSteps') def _doRunExportSteps( self, steps ): """ See ISetupTool. """ context = TarballExportContext( self ) messages = {} for step_id in steps: handler = self._export_registry.getStep( step_id ) if handler is None: raise ValueError( 'Invalid export step: %s' % step_id ) messages[ step_id ] = handler( context ) return { 'steps' : steps , 'messages' : messages , 'tarball' : context.getArchive() , 'filename' : context.getArchiveFilename() }
class StateDefinition(SimpleItem): """State definition""" meta_type = 'Workflow State' manage_options = ( { 'label': 'Properties', 'action': 'manage_properties' }, { 'label': 'Permissions', 'action': 'manage_permissions' }, { 'label': 'Groups', 'action': 'manage_groups' }, { 'label': 'Variables', 'action': 'manage_variables' }, ) title = '' description = '' transitions = () # The ids of possible transitions. permission_roles = None # { permission: [role] or (role,) } group_roles = None # { group name : (role,) } var_values = None # PersistentMapping if set. Overrides transition exprs. security = ClassSecurityInfo() security.declareObjectProtected(ManagePortal) def __init__(self, id): self.id = id def getId(self): return self.id def getWorkflow(self): return aq_parent(aq_inner(aq_parent(aq_inner(self)))) def getTransitions(self): return filter(self.getWorkflow().transitions.has_key, self.transitions) def getTransitionTitle(self, tid): t = self.getWorkflow().transitions.get(tid, None) if t is not None: return t.title return '' def getAvailableTransitionIds(self): return self.getWorkflow().transitions.keys() def getAvailableVarIds(self): return self.getWorkflow().variables.keys() def getManagedPermissions(self): return list(self.getWorkflow().permissions) def getAvailableRoles(self): return self.getWorkflow().getAvailableRoles() def getPermissionInfo(self, p): """Returns the list of roles to be assigned to a permission. """ roles = None if self.permission_roles: roles = self.permission_roles.get(p, None) if roles is None: return {'acquired': 1, 'roles': []} else: if isinstance(roles, tuple): acq = 0 else: acq = 1 return {'acquired': acq, 'roles': list(roles)} def getGroupInfo(self, group): """Returns the list of roles to be assigned to a group. """ if self.group_roles: return self.group_roles.get(group, ()) return () _properties_form = DTMLFile('state_properties', _dtmldir) def manage_properties(self, REQUEST, manage_tabs_message=None): """Show state properties ZMI form.""" return self._properties_form( REQUEST, management_view='Properties', manage_tabs_message=manage_tabs_message, ) def setProperties(self, title='', transitions=(), REQUEST=None, description=''): """Set the properties for this State.""" self.title = str(title) self.description = str(description) self.transitions = tuple(map(str, transitions)) if REQUEST is not None: return self.manage_properties(REQUEST, 'Properties changed.') _variables_form = DTMLFile('state_variables', _dtmldir) def manage_variables(self, REQUEST, manage_tabs_message=None): """Show State variables ZMI form.""" return self._variables_form( REQUEST, management_view='Variables', manage_tabs_message=manage_tabs_message, ) def getVariableValues(self): """Get VariableValues for management UI.""" vv = self.var_values if vv is None: return [] else: return vv.items() def getWorkflowVariables(self): """Get all variables that are available from the workflow and not handled yet. """ wf_vars = self.getAvailableVarIds() if self.var_values is None: return wf_vars ret = [] for vid in wf_vars: if not self.var_values.has_key(vid): ret.append(vid) return ret def addVariable(self, id, value, REQUEST=None): """Add a WorkflowVariable to State.""" if self.var_values is None: self.var_values = PersistentMapping() self.var_values[id] = value if REQUEST is not None: return self.manage_variables(REQUEST, 'Variable added.') def deleteVariables(self, ids=[], REQUEST=None): """Delete a WorkflowVariable from State.""" vv = self.var_values for id in ids: if vv.has_key(id): del vv[id] if REQUEST is not None: return self.manage_variables(REQUEST, 'Variables deleted.') def setVariables(self, ids=[], REQUEST=None): """Set values for Variables set by this State.""" if self.var_values is None: self.var_values = PersistentMapping() vv = self.var_values if REQUEST is not None: for id in vv.keys(): fname = 'varval_%s' % id vv[id] = str(REQUEST[fname]) return self.manage_variables(REQUEST, 'Variables changed.') _permissions_form = DTMLFile('state_permissions', _dtmldir) def manage_permissions(self, REQUEST, manage_tabs_message=None): """Present TTW UI for managing this State's permissions.""" return self._permissions_form( REQUEST, management_view='Permissions', manage_tabs_message=manage_tabs_message, ) def setPermissions(self, REQUEST): """Set the permissions in REQUEST for this State.""" pr = self.permission_roles if pr is None: self.permission_roles = pr = PersistentMapping() pr.clear() for p in self.getManagedPermissions(): roles = [] acquired = REQUEST.get('acquire_' + p, 0) for r in self.getAvailableRoles(): if REQUEST.get('%s|%s' % (p, r), 0): roles.append(r) roles.sort() if not acquired: roles = tuple(roles) pr[p] = roles return self.manage_permissions(REQUEST, 'Permissions changed.') def setPermission(self, permission, acquired, roles): """Set a permission for this State.""" pr = self.permission_roles if pr is None: self.permission_roles = pr = PersistentMapping() if acquired: roles = list(roles) else: roles = tuple(roles) pr[permission] = roles manage_groups = PageTemplateFile('state_groups.pt', _dtmldir) def setGroups(self, REQUEST, RESPONSE=None): """Set the group to role mappings in REQUEST for this State. """ map = self.group_roles if map is None: self.group_roles = map = PersistentMapping() map.clear() all_roles = self.getWorkflow().getRoles() for group in self.getWorkflow().getGroups(): roles = [] for role in all_roles: if REQUEST.get('%s|%s' % (group, role), 0): roles.append(role) roles.sort() roles = tuple(roles) map[group] = roles if RESPONSE is not None: RESPONSE.redirect( "%s/manage_groups?manage_tabs_message=Groups+changed." % self.absolute_url())
else: raise ValueError("Confirmation key not found") else: if REQUEST is not None: self.setSessionErrorsTrans("Confirmation key is invalid") else: raise ValueError("Confirmation key is invalid") if REQUEST is not None: return REQUEST.RESPONSE.redirect(self.absolute_url() + '/my_subscriptions_html') # Administration security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'admin_html') admin_html = PageTemplateFile('zpt/admin', globals()) security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'admin_get_subscriptions') def admin_get_subscriptions(self, user_query=''): user_query = user_query.strip() for obj, sub_id, subscription in utils.walk_subscriptions( self.getSite()): user = subscription.to_string(obj) if not user_query or re.match('.*%s.*' % user_query, user, re.IGNORECASE): yield { 'user': user, 'location': relative_object_path(obj, self.getSite()), 'sub_id': sub_id,
report = report.encode('latin-1') # BBB: ObjectManager won't allow unicode IDS if isinstance(name, unicode): name = name.encode('UTF-8') file = File(id=name, title='', file=report, content_type='text/plain' ) self._setObject(name, file) InitializeClass(SetupTool) _PLAINTEXT_DIFF_HEADER ="""\ Comparing configurations: '%s' and '%s' %s""" _TOOL_ID = 'setup_tool' addSetupToolForm = PageTemplateFile('toolAdd.zpt', _wwwdir) def addSetupTool(dispatcher, RESPONSE): """ """ dispatcher._setObject(_TOOL_ID, SetupTool(_TOOL_ID)) RESPONSE.redirect('%s/manage_main' % dispatcher.absolute_url())
from AccessControl.Permissions import manage_users from Products.PageTemplates.PageTemplateFile import PageTemplateFile from Products.PluggableAuthService import registerMultiPlugin import plugin manage_add_rpx_form = PageTemplateFile('browser/add_plugin', globals(), __name__='manage_add_rpx_form') def manage_add_rpx_helper(dispatcher, id, title=None, REQUEST=None): """Add an rpx Helper to the PluggableAuthentication Service.""" sp = plugin.RpxHelper(id, title) dispatcher._setObject(sp.getId(), sp) if REQUEST is not None: REQUEST['RESPONSE'].redirect('%s/manage_workspace' '?manage_tabs_message=' 'rpxHelper+added.' % dispatcher.absolute_url()) def register_rpx_plugin(): try: registerMultiPlugin(plugin.RpxHelper.meta_type) except RuntimeError: # make refresh users happy pass
class SetupTool(Folder): """ Profile-based site configuration manager. """ implements(ISetupTool) meta_type = 'Generic Setup Tool' _baseline_context_id = '' # BBB _import_context_id is a vestige of a stateful import context _import_context_id = '' _profile_upgrade_versions = {} security = ClassSecurityInfo() def __init__(self, id): self.id = str(id) self._import_registry = ImportStepRegistry() self._export_registry = ExportStepRegistry() self._toolset_registry = ToolsetRegistry() # # ISetupTool API # security.declareProtected(ManagePortal, 'getEncoding') def getEncoding(self): """ See ISetupTool. """ return 'utf-8' security.declareProtected(ManagePortal, 'getImportContextID') def getImportContextID(self): """ See ISetupTool. """ warn('getImportContextId, and the very concept of a stateful ' 'active import context, is deprecated. You can find the ' 'base profile that was applied using getBaselineContextID.', DeprecationWarning, stacklevel=2) return self._import_context_id security.declareProtected(ManagePortal, 'getBaselineContextID') def getBaselineContextID(self): """ See ISetupTool. """ return self._baseline_context_id security.declareProtected(ManagePortal, 'setImportContext') def setImportContext(self, context_id, encoding=None): """ See ISetupTool. """ warn('setImportContext is deprecated. Use setBaselineContext to ' 'specify the baseline context, and/or runImportStepFromProfile ' 'to run the steps from a specific import context.', DeprecationWarning, stacklevel=2) self._import_context_id = context_id context_type = BASE # snapshots are always baseline contexts if context_id.startswith('profile-'): profile_info = _profile_registry.getProfileInfo(context_id[8:]) context_type = profile_info['type'] if context_type == BASE: self.setBaselineContext(context_id, encoding) security.declareProtected(ManagePortal, 'setBaselineContext') def setBaselineContext(self, context_id, encoding=None): """ See ISetupTool. """ self._baseline_context_id = context_id self.applyContextById(context_id, encoding) security.declareProtected(ManagePortal, 'applyContextById') def applyContextById(self, context_id, encoding=None): context = self._getImportContext(context_id) self.applyContext(context, encoding) security.declareProtected(ManagePortal, 'applyContext') def applyContext(self, context, encoding=None): self._updateImportStepsRegistry(context, encoding) self._updateExportStepsRegistry(context, encoding) security.declareProtected(ManagePortal, 'getImportStepRegistry') def getImportStepRegistry(self): """ See ISetupTool. """ return self._import_registry security.declareProtected(ManagePortal, 'getExportStepRegistry') def getExportStepRegistry(self): """ See ISetupTool. """ return self._export_registry security.declareProtected(ManagePortal, 'getExportStep') def getExportStep(self, step, default=None): """Simple wrapper to query both the global and local step registry.""" res=_export_step_registry.getStep(step, default) if res is not default: return res return self._export_registry.getStep(step, default) security.declareProtected(ManagePortal, 'listExportSteps') def listExportSteps(self): steps = _export_step_registry.listSteps() + \ self._export_registry.listSteps() return tuple(set(steps)) security.declareProtected(ManagePortal, 'getImportStep') def getImportStep(self, step, default=None): """Simple wrapper to query both the global and local step registry.""" res=_import_step_registry.getStep(step, default) if res is not default: return res return self._import_registry.getStep(step, default) security.declareProtected(ManagePortal, 'getSortedImportSteps') def getSortedImportSteps(self): steps = _import_step_registry.listSteps() + \ self._import_registry.listSteps() step_infos = [ self.getImportStepMetadata(step) for step in set(steps) ] return tuple(_computeTopologicalSort(step_infos)) security.declareProtected(ManagePortal, 'getImportStepMetadata') def getImportStepMetadata(self, step, default=None): """Simple wrapper to query both the global and local step registry.""" res=self._import_registry.getStepMetadata(step, default) if res is not default: return res return _import_step_registry.getStepMetadata(step, default) security.declareProtected(ManagePortal, 'getExportStepMetadata') def getExportStepMetadata(self, step, default=None): """Simple wrapper to query both the global and local step registry.""" res=self._export_registry.getStepMetadata(step, default) if res is not default: return res return _export_step_registry.getStepMetadata(step, default) security.declareProtected(ManagePortal, 'getToolsetRegistry') def getToolsetRegistry(self): """ See ISetupTool. """ return self._toolset_registry security.declareProtected(ManagePortal, 'runImportStepFromProfile') def runImportStepFromProfile(self, profile_id, step_id, run_dependencies=True, purge_old=None): """ See ISetupTool. """ old_context = self._import_context_id context = self._getImportContext(profile_id, purge_old) self.applyContext(context) info = self.getImportStepMetadata(step_id) if info is None: self._import_context_id = old_context raise ValueError, 'No such import step: %s' % step_id dependencies = info.get('dependencies', ()) messages = {} steps = [] if run_dependencies: for dependency in dependencies: if dependency not in steps: steps.append(dependency) steps.append (step_id) full_import=(set(steps)==set(self.getSortedImportSteps())) event.notify(BeforeProfileImportEvent(self, profile_id, steps, full_import)) for step in steps: message = self._doRunImportStep(step, context) messages[step] = message or '' message_list = filter(None, [message]) message_list.extend( ['%s: %s' % x[1:] for x in context.listNotes()] ) messages[step_id] = '\n'.join(message_list) self._import_context_id = old_context event.notify(ProfileImportedEvent(self, profile_id, steps, full_import)) return { 'steps' : steps, 'messages' : messages } security.declareProtected(ManagePortal, 'runImportStep') def runImportStep(self, step_id, run_dependencies=True, purge_old=None): """ See ISetupTool. """ warn('The runImportStep method is deprecated. Please use ' 'runImportStepFromProfile instead.', DeprecationWarning, stacklevel=2) return self.runImportStepFromProfile(self._import_context_id, step_id, run_dependencies, purge_old, ) security.declareProtected(ManagePortal, 'runAllImportStepsFromProfile') def runAllImportStepsFromProfile(self, profile_id, purge_old=None, ignore_dependencies=False, archive=None): """ See ISetupTool. """ __traceback_info__ = profile_id old_context = self._import_context_id result = self._runImportStepsFromContext(purge_old=purge_old, profile_id=profile_id, archive=archive, ignore_dependencies=ignore_dependencies) if profile_id is None: prefix = 'import-all-from-tar' else: prefix = 'import-all-%s' % profile_id.replace(':', '_') name = self._mangleTimestampName(prefix, 'log') self._createReport(name, result['steps'], result['messages']) self._import_context_id = old_context return result security.declareProtected(ManagePortal, 'runAllImportSteps') def runAllImportSteps(self, purge_old=None): """ See ISetupTool. """ warn('The runAllImportSteps method is deprecated. Please use ' 'runAllImportStepsFromProfile instead.', DeprecationWarning, stacklevel=2) context_id = self._import_context_id return self.runAllImportStepsFromProfile(self._import_context_id, purge_old) security.declareProtected(ManagePortal, 'runExportStep') def runExportStep(self, step_id): """ See ISetupTool. """ return self._doRunExportSteps([step_id]) security.declareProtected(ManagePortal, 'runAllExportSteps') def runAllExportSteps(self): """ See ISetupTool. """ return self._doRunExportSteps(self.listExportSteps()) security.declareProtected(ManagePortal, 'createSnapshot') def createSnapshot(self, snapshot_id): """ See ISetupTool. """ context = SnapshotExportContext(self, snapshot_id) messages = {} steps = self.listExportSteps() for step_id in steps: handler = self.getExportStep(step_id) if handler is None: logger = logging.getLogger('GenericSetup') logger.error('Step %s has an invalid handler' % step_id) continue messages[step_id] = handler(context) return { 'steps' : steps , 'messages' : messages , 'url' : context.getSnapshotURL() , 'snapshot' : context.getSnapshotFolder() } security.declareProtected(ManagePortal, 'compareConfigurations') def compareConfigurations(self, lhs_context, rhs_context, missing_as_empty=False, ignore_blanks=False, skip=SKIPPED_FILES, ): """ See ISetupTool. """ differ = ConfigDiff(lhs_context, rhs_context, missing_as_empty, ignore_blanks, skip, ) return differ.compare() security.declareProtected(ManagePortal, 'markupComparison') def markupComparison(self, lines): """ See ISetupTool. """ result = [] for line in lines.splitlines(): if line.startswith('** '): if line.find('File') > -1: if line.find('replaced') > -1: result.append(('file-to-dir', line)) elif line.find('added') > -1: result.append(('file-added', line)) else: result.append(('file-removed', line)) else: if line.find('replaced') > -1: result.append(('dir-to-file', line)) elif line.find('added') > -1: result.append(('dir-added', line)) else: result.append(('dir-removed', line)) elif line.startswith('@@'): result.append(('diff-range', line)) elif line.startswith(' '): result.append(('diff-context', line)) elif line.startswith('+'): result.append(('diff-added', line)) elif line.startswith('-'): result.append(('diff-removed', line)) elif line == '\ No newline at end of file': result.append(('diff-context', line)) else: result.append(('diff-header', line)) return '<pre>\n%s\n</pre>' % ( '\n'.join([('<span class="%s">%s</span>' % (cl, escape(l))) for cl, l in result])) # # ZMI # manage_options = (Folder.manage_options[:1] + ({'label' : 'Profiles', 'action' : 'manage_tool' }, {'label' : 'Import', 'action' : 'manage_importSteps' }, {'label' : 'Export', 'action' : 'manage_exportSteps' }, {'label' : 'Upgrades', 'action' : 'manage_upgrades' }, {'label' : 'Snapshots', 'action' : 'manage_snapshots' }, {'label' : 'Comparison', 'action' : 'manage_showDiff' }, {'label' : 'Manage', 'action' : 'manage_stepRegistry' }, ) + Folder.manage_options[3:] # skip "View", "Properties" ) security.declareProtected(ManagePortal, 'manage_tool') manage_tool = PageTemplateFile('sutProperties', _wwwdir) security.declareProtected(ManagePortal, 'manage_updateToolProperties') def manage_updateToolProperties(self, context_id, RESPONSE): """ Update the tool's settings. """ self.setBaselineContext(context_id) RESPONSE.redirect('%s/manage_tool?manage_tabs_message=%s' % (self.absolute_url(), 'Properties+updated.')) security.declareProtected(ManagePortal, 'manage_importSteps') manage_importSteps = PageTemplateFile('sutImportSteps', _wwwdir) security.declareProtected(ManagePortal, 'manage_importSelectedSteps') def manage_importSelectedSteps(self, ids, run_dependencies, context_id=None): """ Import the steps selected by the user. """ messages = {} if not ids: summary = 'No steps selected.' else: if context_id is None: context_id = self.getBaselineContextID() steps_run = [] for step_id in ids: result = self.runImportStepFromProfile(context_id, step_id, run_dependencies) steps_run.extend(result['steps']) messages.update(result['messages']) summary = 'Steps run: %s' % ', '.join(steps_run) name = self._mangleTimestampName('import-selected', 'log') self._createReport(name, result['steps'], result['messages']) return self.manage_importSteps(manage_tabs_message=summary, messages=messages) security.declareProtected(ManagePortal, 'manage_importSelectedSteps') def manage_importAllSteps(self, context_id=None): """ Import all steps. """ if context_id is None: context_id = self.getBaselineContextID() result = self.runAllImportStepsFromProfile(context_id, purge_old=None) steps_run = 'Steps run: %s' % ', '.join(result['steps']) return self.manage_importSteps(manage_tabs_message=steps_run, messages=result['messages']) security.declareProtected(ManagePortal, 'manage_importExtensions') def manage_importExtensions(self, RESPONSE, profile_ids=()): """ Import all steps for the selected extension profiles. """ detail = {} if len(profile_ids) == 0: message = 'Please select one or more extension profiles.' RESPONSE.redirect('%s/manage_tool?manage_tabs_message=%s' % (self.absolute_url(), message)) else: message = 'Imported profiles: %s' % ', '.join(profile_ids) for profile_id in profile_ids: result = self.runAllImportStepsFromProfile(profile_id) for k, v in result['messages'].items(): detail['%s:%s' % (profile_id, k)] = v return self.manage_importSteps(manage_tabs_message=message, messages=detail) security.declareProtected(ManagePortal, 'manage_importTarball') def manage_importTarball(self, tarball): """ Import steps from the uploaded tarball. """ if getattr(tarball, 'read', None) is not None: tarball = tarball.read() result = self.runAllImportStepsFromProfile(None, True, archive=tarball) steps_run = 'Steps run: %s' % ', '.join(result['steps']) return self.manage_importSteps(manage_tabs_message=steps_run, messages=result['messages']) security.declareProtected(ManagePortal, 'manage_exportSteps') manage_exportSteps = PageTemplateFile('sutExportSteps', _wwwdir) security.declareProtected(ManagePortal, 'manage_exportSelectedSteps') def manage_exportSelectedSteps(self, ids, RESPONSE): """ Export the steps selected by the user. """ if not ids: RESPONSE.redirect('%s/manage_exportSteps?manage_tabs_message=%s' % (self.absolute_url(), 'No+steps+selected.')) result = self._doRunExportSteps(ids) RESPONSE.setHeader('Content-type', 'application/x-gzip') RESPONSE.setHeader('Content-disposition', 'attachment; filename=%s' % result['filename']) return result['tarball'] security.declareProtected(ManagePortal, 'manage_exportAllSteps') def manage_exportAllSteps(self, RESPONSE): """ Export all steps. """ result = self.runAllExportSteps() RESPONSE.setHeader('Content-type', 'application/x-gzip') RESPONSE.setHeader('Content-disposition', 'attachment; filename=%s' % result['filename']) return result['tarball'] security.declareProtected(ManagePortal, 'manage_upgrades') manage_upgrades = PageTemplateFile('setup_upgrades', _wwwdir) security.declareProtected(ManagePortal, 'upgradeStepMacro') upgradeStepMacro = PageTemplateFile('upgradeStep', _wwwdir) security.declareProtected(ManagePortal, 'manage_snapshots') manage_snapshots = PageTemplateFile('sutSnapshots', _wwwdir) security.declareProtected(ManagePortal, 'listSnapshotInfo') def listSnapshotInfo(self): """ Return a list of mappings describing available snapshots. o Keys include: 'id' -- snapshot ID 'title' -- snapshot title or ID 'url' -- URL of the snapshot folder """ result = [] snapshots = self._getOb('snapshots', None) if snapshots: for id, folder in snapshots.objectItems('Folder'): result.append({ 'id' : id , 'title' : folder.title_or_id() , 'url' : folder.absolute_url() }) return result security.declareProtected(ManagePortal, 'listProfileInfo') def listProfileInfo(self, for_=None): """ Return a list of mappings describing registered profiles. Base profile is listed first, extensions are sorted. o Keys include: 'id' -- profile ID 'title' -- profile title or ID 'description' -- description of the profile 'path' -- path to the profile within its product 'product' -- name of the registering product """ base = [] ext = [] for info in _profile_registry.listProfileInfo(for_): if info.get('type', BASE) == BASE: base.append(info) else: ext.append(info) ext.sort(lambda x, y: cmp(x['id'], y['id'])) return base + ext security.declareProtected(ManagePortal, 'listContextInfos') def listContextInfos(self): """ List registered profiles and snapshots. """ def readableType(x): if x is BASE: return 'base' elif x is EXTENSION: return 'extension' return 'unknown' s_infos = [{'id': 'snapshot-%s' % info['id'], 'title': info['title'], 'type': 'snapshot', } for info in self.listSnapshotInfo()] p_infos = [{'id': 'profile-%s' % info['id'], 'title': info['title'], 'type': readableType(info['type']), } for info in self.listProfileInfo()] return tuple(s_infos + p_infos) security.declareProtected(ManagePortal, 'getProfileImportDate') def getProfileImportDate(self, profile_id): """ See ISetupTool. """ prefix = ('import-all-%s-' % profile_id).replace(':', '_') candidates = [x for x in self.objectIds('File') if x[:-18]==prefix and x.endswith('.log')] if len(candidates) == 0: return None candidates.sort() last = candidates[-1] stamp = last[-18:-4] return '%s-%s-%sT%s:%s:%sZ' % (stamp[0:4], stamp[4:6], stamp[6:8], stamp[8:10], stamp[10:12], stamp[12:14], ) security.declareProtected(ManagePortal, 'manage_createSnapshot') def manage_createSnapshot(self, RESPONSE, snapshot_id=None): """ Create a snapshot with the given ID. o If no ID is passed, generate one. """ if snapshot_id is None: snapshot_id = self._mangleTimestampName('snapshot') self.createSnapshot(snapshot_id) return RESPONSE.redirect('%s/manage_snapshots?manage_tabs_message=%s' % (self.absolute_url(), 'Snapshot+created.')) security.declareProtected(ManagePortal, 'manage_showDiff') manage_showDiff = PageTemplateFile('sutCompare', _wwwdir) def manage_downloadDiff(self, lhs, rhs, missing_as_empty, ignore_blanks, RESPONSE, ): """ Crack request vars and call compareConfigurations. o Return the result as a 'text/plain' stream, suitable for framing. """ comparison = self.manage_compareConfigurations(lhs, rhs, missing_as_empty, ignore_blanks, ) RESPONSE.setHeader('Content-Type', 'text/plain') return _PLAINTEXT_DIFF_HEADER % (lhs, rhs, comparison) security.declareProtected(ManagePortal, 'manage_compareConfigurations') def manage_compareConfigurations(self, lhs, rhs, missing_as_empty, ignore_blanks, ): """ Crack request vars and call compareConfigurations. """ lhs_context = self._getImportContext(lhs) rhs_context = self._getImportContext(rhs) return self.compareConfigurations(lhs_context, rhs_context, missing_as_empty, ignore_blanks, ) security.declareProtected(ManagePortal, 'manage_stepRegistry') manage_stepRegistry = PageTemplateFile('sutManage', _wwwdir) security.declareProtected(ManagePortal, 'manage_deleteImportSteps') def manage_deleteImportSteps(self, ids, request=None): if request is None: request = self.REQUEST for id in ids: self._import_registry.unregisterStep(id) self._p_changed=True url = self.absolute_url() request.RESPONSE.redirect("%s/manage_stepRegistry" % url) security.declareProtected(ManagePortal, 'manage_deleteExportSteps') def manage_deleteExportSteps(self, ids, request=None): if request is None: request = self.REQUEST for id in ids: self._export_registry.unregisterStep(id) self._p_changed=True url = self.absolute_url() request.RESPONSE.redirect("%s/manage_stepRegistry" % url) # # Upgrades management # security.declareProtected(ManagePortal, 'getLastVersionForProfile') def getLastVersionForProfile(self, profile_id): """Return the last upgraded version for the specified profile. """ version = self._profile_upgrade_versions.get(profile_id, 'unknown') return version security.declareProtected(ManagePortal, 'setLastVersionForProfile') def setLastVersionForProfile(self, profile_id, version): """Set the last upgraded version for the specified profile. """ if isinstance(version, basestring): version = tuple(version.split('.')) prof_versions = self._profile_upgrade_versions.copy() prof_versions[profile_id] = version self._profile_upgrade_versions = prof_versions security.declareProtected(ManagePortal, 'getVersionForProfile') def getVersionForProfile(self, profile_id): """Return the registered filesystem version for the specified profile. """ return self.getProfileInfo( profile_id ).get('version', 'unknown') security.declareProtected(ManagePortal, 'profileExists') def profileExists(self, profile_id): """Check if a profile exists.""" try: self.getProfileInfo( profile_id ) except KeyError: return False else: return True security.declareProtected(ManagePortal, "getProfileInfo") def getProfileInfo(self, profile_id): if profile_id.startswith("profile-"): profile_id = profile_id[len('profile-'):] elif profile_id.startswith("snapshot-"): profile_id = profile_id[len('snapshot-'):] return _profile_registry.getProfileInfo(profile_id) security.declareProtected(ManagePortal, 'getDependenciesForProfile') def getDependenciesForProfile(self, profile_id): if profile_id.startswith("snapshot-"): return () if not self.profileExists( profile_id ): raise KeyError, profile_id try: return self.getProfileInfo( profile_id ).get('dependencies', ()) except KeyError: return () security.declareProtected(ManagePortal, 'listProfilesWithUpgrades') def listProfilesWithUpgrades(self): return listProfilesWithUpgrades() security.declarePrivate('_massageUpgradeInfo') def _massageUpgradeInfo(self, info): """Add a couple of data points to the upgrade info dictionary. """ info = info.copy() info['haspath'] = info['source'] and info['dest'] info['ssource'] = '.'.join(info['source'] or ('all',)) info['sdest'] = '.'.join(info['dest'] or ('all',)) return info security.declareProtected(ManagePortal, 'listUpgrades') def listUpgrades(self, profile_id, show_old=False): """Get the list of available upgrades. """ if show_old: source = None else: source = self.getLastVersionForProfile(profile_id) upgrades = listUpgradeSteps(self, profile_id, source) res = [] for info in upgrades: if type(info) == list: subset = [] for subinfo in info: subset.append(self._massageUpgradeInfo(subinfo)) res.append(subset) else: res.append(self._massageUpgradeInfo(info)) return res security.declareProtected(ManagePortal, 'manage_doUpgrades') def manage_doUpgrades(self, request=None): """Perform all selected upgrade steps. """ if request is None: request = self.REQUEST logger = logging.getLogger('GenericSetup') steps_to_run = request.form.get('upgrades', []) profile_id = request.get('profile_id', '') step = None for step_id in steps_to_run: step = _upgrade_registry.getUpgradeStep(profile_id, step_id) if step is not None: step.doStep(self) msg = "Ran upgrade step %s for profile %s" % (step.title, profile_id) logger.log(logging.INFO, msg) # We update the profile version to the last one we have reached # with runnning an upgrade step. if step and step.dest is not None: self.setLastVersionForProfile(profile_id, step.dest) url = self.absolute_url() request.RESPONSE.redirect("%s/manage_upgrades?saved=%s" % (url, profile_id)) # # Helper methods # security.declarePrivate('_getImportContext') def _getImportContext(self, context_id, should_purge=None, archive=None): """ Crack ID and generate appropriate import context. """ encoding = self.getEncoding() if context_id is not None: if context_id.startswith('profile-'): context_id = context_id[len('profile-'):] info = _profile_registry.getProfileInfo(context_id) if info.get('product'): path = os.path.join(_getProductPath(info['product']) , info['path']) else: path = info['path'] if should_purge is None: should_purge = (info.get('type') != EXTENSION) return DirectoryImportContext(self, path, should_purge, encoding) elif context_id.startswith('snapshot-'): context_id = context_id[len('snapshot-'):] if should_purge is None: should_purge = True return SnapshotImportContext(self, context_id, should_purge, encoding) if archive is not None: return TarballImportContext(tool=self, archive_bits=archive, encoding='UTF8', should_purge=should_purge, ) raise KeyError, 'Unknown context "%s"' % context_id security.declarePrivate('_updateImportStepsRegistry') def _updateImportStepsRegistry(self, context, encoding): """ Update our import steps registry from our profile. """ if context is None: context = self._getImportContext(self._import_context_id) xml = context.readDataFile(IMPORT_STEPS_XML) if xml is None: return info_list = self._import_registry.parseXML(xml, encoding) for step_info in info_list: id = step_info['id'] version = step_info['version'] handler = step_info['handler'] dependencies = tuple(step_info.get('dependencies', ())) title = step_info.get('title', id) description = ''.join(step_info.get('description', [])) self._import_registry.registerStep(id=id, version=version, handler=handler, dependencies=dependencies, title=title, description=description, ) security.declarePrivate('_updateExportStepsRegistry') def _updateExportStepsRegistry(self, context, encoding): """ Update our export steps registry from our profile. """ if context is None: context = self._getImportContext(self._import_context_id) xml = context.readDataFile(EXPORT_STEPS_XML) if xml is None: return info_list = self._export_registry.parseXML(xml, encoding) for step_info in info_list: id = step_info['id'] handler = step_info['handler'] title = step_info.get('title', id) description = ''.join(step_info.get('description', [])) self._export_registry.registerStep(id=id, handler=handler, title=title, description=description, ) security.declarePrivate('_doRunImportStep') def _doRunImportStep(self, step_id, context): """ Run a single import step, using a pre-built context. """ __traceback_info__ = step_id marker = object() handler = self.getImportStep(step_id) if handler is marker: raise ValueError('Invalid import step: %s' % step_id) if handler is None: msg = 'Step %s has an invalid import handler' % step_id logger = logging.getLogger('GenericSetup') logger.error(msg) return 'ERROR: ' + msg return handler(context) security.declarePrivate('_doRunExportSteps') def _doRunExportSteps(self, steps): """ See ISetupTool. """ context = TarballExportContext(self) messages = {} marker = object() for step_id in steps: handler = self.getExportStep(step_id, marker) if handler is marker: raise ValueError('Invalid export step: %s' % step_id) if handler is None: msg = 'Step %s has an invalid import handler' % step_id logger = logging.getLogger('GenericSetup') logger.error(msg) messages[step_id] = msg else: messages[step_id] = handler(context) return { 'steps' : steps , 'messages' : messages , 'tarball' : context.getArchive() , 'filename' : context.getArchiveFilename() } security.declareProtected(ManagePortal, 'getProfileDependencyChain') def getProfileDependencyChain(self, profile_id, seen=None): if seen is None: seen = set() elif profile_id in seen: return [] # cycle break seen.add( profile_id ) chain = [] dependencies = self.getDependenciesForProfile( profile_id ) for dependency in dependencies: chain.extend(self.getProfileDependencyChain( dependency, seen )) chain.append(profile_id) return chain security.declarePrivate('_runImportStepsFromContext') def _runImportStepsFromContext(self, steps=None, purge_old=None, profile_id=None, archive=None, ignore_dependencies=False, seen=None): if profile_id is not None and not ignore_dependencies: try: chain = self.getProfileDependencyChain( profile_id ) except KeyError, e: logger = logging.getLogger('GenericSetup') logger.error('Unknown step in dependency chain: %s' % str(e)) raise else:
class PasswordResetTool(SimpleItem): meta_type = 'LDAP Password Reset Tool' security = ClassSecurityInfo() icon = '++resource++be.ldapadmin-www/eionet_password_reset_tool.gif' session_messages = SESSION_MESSAGES manage_options = ( { 'label': 'Configure', 'action': 'manage_edit' }, { 'label': 'View', 'action': '' }, ) + SimpleItem.manage_options _render_template = TemplateRenderer(CommonTemplateLogic) def __init__(self, config={}): super(PasswordResetTool, self).__init__() self._config = PersistentMapping(config) self._tokens = PersistentMapping() security.declareProtected(view_management_screens, 'get_config') def get_config(self): return dict(self._config) security.declareProtected(view_management_screens, 'manage_edit') manage_edit = PageTemplateFile('zpt/pwreset_manage_edit', globals()) manage_edit.ldap_config_edit_macro = ldap_config.edit_macro security.declareProtected(view_management_screens, 'manage_edit_save') def manage_edit_save(self, REQUEST): """ save changes to configuration """ form = REQUEST.form new_config = ldap_config.read_form(form, edit=True) new_config['legacy_ldap_server'] = form.get('legacy_ldap_server', '') new_config['legacy_admin_dn'] = form.get('legacy_admin_dn', '') new_config['legacy_admin_pw'] = form.get('legacy_admin_pw', '') if not new_config['legacy_admin_pw']: del new_config['legacy_admin_pw'] # don't overwrite self._config.update(new_config) REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_edit') def _get_ldap_agent(self, bind=True, secondary=False): return ldap_config._get_ldap_agent(self, bind, secondary) def _predefined_filters(self): return sorted(self.objectValues([query.Query.meta_type]), key=lambda ob: ob.getId()) security.declareProtected(view, 'index_html') def index_html(self, REQUEST): """ view """ email = REQUEST.get('email', '') options = {'email': email} return self._render_template('zpt/pwreset_index.zpt', **options) def _new_token(self, user_id): token = random_token() self._tokens[token] = TokenData(user_id, datetime.utcnow()) return token def _send_token_email(self, addr_to, token, user_info): addr_from = ADDR_FROM email_template = load_template('zpt/pwreset_token_email.zpt') expiration_time = datetime.utcnow() + timedelta(days=1) options = { 'token_url': self.absolute_url() + "/confirm_email?token=" + token, 'user_info': user_info, 'context': self, 'network_name': NETWORK_NAME, 'expiration_time': expiration_time.strftime("%Y-%m-%d %H:%M:%S") } print(options['token_url']) message = _create_plain_message( email_template(**options).encode('utf-8')) message['From'] = addr_from message['To'] = addr_to message['Subject'] = "%s account password recovery" % NETWORK_NAME try: mailer = getUtility(IMailDelivery, name="Mail") mailer.send(addr_from, [addr_to], message.as_string()) except ComponentLookupError: mailer = getUtility(IMailDelivery, name="naaya-mail-delivery") try: mailer.send(addr_from, [addr_to], message.as_string()) except (ValueError, AssertionError): mailer.send(addr_from, [addr_to], message) security.declareProtected(view, 'ask_for_password_reset') def ask_for_password_reset(self, REQUEST=None, email=None, on_create=False): """ view """ if REQUEST is None: REQUEST = self.REQUEST if not email: email = REQUEST.form['email'] agent = self._get_ldap_agent() users = agent.search_user_by_email(email) # , no_disabled=True) if users: # some people have multiple accounts; send mail for each account. for user_info in users: if user_info['status'] == 'disabled': msg = "This email: %s belongs to a disabled account" % \ user_info['email'] _set_session_message(REQUEST, 'error', msg) location = (self.absolute_url() + '/messages_html?msg=email-disabled') else: token = self._new_token(user_info['id']) log.info( "Sending password recovery email to user %r at %r.", user_info['id'], email) self._send_token_email(email, token, user_info) location = (self.absolute_url() + '/messages_html?msg=email-sent') else: log.info("Requested password recovery with invalid email %r.", email) msg = "Email address not found in database." _set_session_message(REQUEST, 'error', msg) location = self.absolute_url() + '/' if REQUEST and not on_create: REQUEST.RESPONSE.redirect(location) security.declareProtected(view, 'messages_html') def messages_html(self, REQUEST): """ view """ options = { 'message-name': REQUEST.form['msg'], } return self._render_template('zpt/pwreset_message.zpt', **options) def _say_token_expired(self, REQUEST): msg = ("Password reset link is invalid, perhaps it has " "expired. Please try again.") _set_session_message(REQUEST, 'error', msg) location = self.absolute_url() + '/' REQUEST.RESPONSE.redirect(location) def _expire_tokens(self): expired = [] cutoff_time = datetime.utcnow() - timedelta(days=1) for token, token_data in self._tokens.iteritems(): if token_data.timestamp < cutoff_time: expired.append(token) for token in expired: log.info('Token %r expired.', token) del self._tokens[token] security.declareProtected(view, 'confirm_email') def confirm_email(self, REQUEST): """ view """ token = REQUEST.form['token'] self._expire_tokens() token_data = self._tokens.get(token, None) if token_data is None: return self._say_token_expired(REQUEST) options = { 'token': token, 'user_id': token_data.user_id, } return self._render_template('zpt/pwreset_new_password.zpt', **options) def reset_password(self, REQUEST): """ view """ token = REQUEST.form['token'] self._expire_tokens() token_data = self._tokens.get(token, None) if token_data is None: return self._say_token_expired(REQUEST) new_password = REQUEST.form['password'] if new_password != REQUEST.form['password-confirm']: _set_session_message(REQUEST, 'error', "Passwords do not match.") location = self.absolute_url() + '/confirm_email?token=' + token else: log.info("Restting password for user %r with token %r", token_data.user_id, token) agent = self._get_ldap_agent(bind=True) try: agent.set_user_password(token_data.user_id, None, new_password) except CONSTRAINT_VIOLATION as e: if e.message['info'] in [ 'Password fails quality checking policy' ]: try: defaultppolicy = agent.conn.search_s( 'cn=defaultppolicy,ou=pwpolicies,o=EIONET,' 'l=Europe', SCOPE_BASE) p_length = defaultppolicy[0][1]['pwdMinLength'][0] message = '%s (min. %s characters)' % ( e.message['info'], p_length) except NO_SUCH_OBJECT: message = e.message['info'] else: message = e.message['info'] _set_session_message(REQUEST, 'error', message) location = (self.absolute_url() + '/confirm_email?token=' + token) else: del self._tokens[token] location = (self.absolute_url() + '/messages_html?msg=password-reset') REQUEST.RESPONSE.redirect(location) security.declareProtected(view, 'can_edit_users') def can_edit_users(self): user = self.REQUEST.AUTHENTICATED_USER return bool(user.has_permission(ldap_edit_users, self))
security.declareProtected(view, 'index_html') def index_html(self, REQUEST=None, RESPONSE=None): """ """ return self.getFormsTool().getContent({'here': self}, 'youtube_index') security.declareProtected(PERMISSION_EDIT_OBJECTS, 'edit_html') def edit_html(self, REQUEST=None, RESPONSE=None): """ """ return self.getFormsTool().getContent({'here': self}, 'youtube_edit') InitializeClass(NyYoutube) manage_addNyYoutube_html = PageTemplateFile('zpt/youtube_manage_add', globals()) manage_addNyYoutube_html.kind = config['meta_type'] manage_addNyYoutube_html.action = 'addNyYoutube' config.update({ 'constructors': (manage_addNyYoutube_html, addNyYoutube), 'folder_constructors': [ ('manage_addNyYoutube_html', manage_addNyYoutube_html), ('youtube_add_html', youtube_add_html), ('addNyYoutube', addNyYoutube), ], 'add_method': addNyYoutube, 'validation': issubclass(NyYoutube, NyValidation), '_class': NyYoutube,
#Python imports #Zope imports from Globals import InitializeClass from AccessControl import ClassSecurityInfo from AccessControl.Permissions import view_management_screens, view import Products from OFS.Folder import Folder from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate from Products.PageTemplates.PageTemplateFile import PageTemplateFile #Product imports from Products.NaayaCore.constants import * from managers.portlets_templates import * manage_addPortlet_html = PageTemplateFile('zpt/portlet_manage_add', globals()) def addPortlet(self, id='', title='', portlettype='0', REQUEST=None): """ """ id = self.utCleanupId(id) if not id: id = PREFIX_PORTLET + self.utGenRandomId(6) content_type = 'text/html' try: portlettype = abs(int(portlettype)) except: portlettype = 0 body = PORTLETS_BODIES.get(portlettype, 0) ob = Portlet(id, title, body, content_type, portlettype) self._setObject(id, ob) if REQUEST:
class NyYoutube(Implicit, NyContentData, NyAttributes, NyItem, NyNonCheckControl, NyValidation, NyContentType): """ """ implements(INyYoutube) meta_type = config['meta_type'] meta_label = config['label'] icon = 'misc_/NaayaContent/NyYoutube.png' icon_marked = 'misc_/NaayaContent/NyYoutube_marked.png' manage_options = ( { 'label': 'Properties', 'action': 'manage_edit_html' }, { 'label': 'View', 'action': 'index_html' }, ) + NyItem.manage_options security = ClassSecurityInfo() def __init__(self, id, contributor): """ """ self.id = id NyValidation.__dict__['__init__'](self) NyItem.__dict__['__init__'](self) self.contributor = contributor # zmi actions security.declareProtected(view_management_screens, 'manageProperties') def manageProperties(self, REQUEST=None, **kwargs): """ """ if not self.checkPermissionEditObject(): raise EXCEPTION_NOTAUTHORIZED(EXCEPTION_NOTAUTHORIZED_MSG) if REQUEST is not None: schema_raw_data = dict(REQUEST.form) else: schema_raw_data = kwargs _lang = schema_raw_data.pop('_lang', schema_raw_data.pop('lang', None)) _releasedate = self.process_releasedate( schema_raw_data.pop('releasedate', ''), self.releasedate) _approved = int(bool(schema_raw_data.pop('approved', False))) if schema_raw_data['iframe_width'] in ['', '0']: schema_raw_data['iframe_width'] = 640 if schema_raw_data['iframe_height'] in ['', '0']: schema_raw_data['iframe_height'] = 360 if len(schema_raw_data['youtube_id']) > 11: try: schema_raw_data['youtube_id'] = schema_raw_data[ 'youtube_id'].split('watch?v=')[1][:11] except IndexError: schema_raw_data['youtube_id'] = schema_raw_data[ 'youtube_id'].split('&v=')[1][:11] form_errors = self.process_submitted_form( schema_raw_data, _lang, _override_releasedate=_releasedate) try: schema_raw_data['iframe_width'] = int( schema_raw_data['iframe_width']) except ValueError: form_errors['iframe_width'] = ['Integer value required.'] try: schema_raw_data['iframe_height'] = int( schema_raw_data['iframe_height']) except ValueError: form_errors['iframe_height'] = ['Integer value required.'] if not schema_raw_data['youtube_id']: form_errors['youtube_id'] = ['Youtube Id is mandatory'] if schema_raw_data['youtube_id']: yt_service = YouTubeService() try: yt_service.GetYouTubeVideoEntry( video_id=schema_raw_data['youtube_id']) except RequestError: form_errors['youtube_id'] = [ 'Invalid Youtube ID (inexisting video)' ] if form_errors: raise ValueError(form_errors.popitem()[1]) # pick a random error if _approved != self.approved: if _approved == 0: _approved_by = None else: _approved_by = self.REQUEST.AUTHENTICATED_USER.getUserName() self.approveThis(_approved, _approved_by) self._p_changed = 1 if self.discussion: self.open_for_comments() else: self.close_for_comments() self.recatalogNyObject(self) if REQUEST: REQUEST.RESPONSE.redirect('manage_edit_html?save=ok') security.declareProtected(PERMISSION_EDIT_OBJECTS, 'saveProperties') def saveProperties(self, REQUEST=None, **kwargs): """ """ if not self.checkPermissionEditObject(): raise EXCEPTION_NOTAUTHORIZED(EXCEPTION_NOTAUTHORIZED_MSG) if REQUEST is not None: schema_raw_data = dict(REQUEST.form) else: schema_raw_data = kwargs _lang = schema_raw_data.pop('_lang', schema_raw_data.pop('lang', None)) _releasedate = self.process_releasedate( schema_raw_data.pop('releasedate', ''), self.releasedate) if schema_raw_data['iframe_width'] in ['', '0']: schema_raw_data['iframe_width'] = 640 if schema_raw_data['iframe_height'] in ['', '0']: schema_raw_data['iframe_height'] = 360 if len(schema_raw_data['youtube_id']) > 11: try: schema_raw_data['youtube_id'] = schema_raw_data[ 'youtube_id'].split('watch?v=')[1][:11] except IndexError: schema_raw_data['youtube_id'] = schema_raw_data[ 'youtube_id'].split('&v=')[1][:11] form_errors = self.process_submitted_form( schema_raw_data, _lang, _override_releasedate=_releasedate) try: schema_raw_data['iframe_width'] = int( schema_raw_data['iframe_width']) except ValueError: form_errors['iframe_width'] = ['Integer value required.'] try: schema_raw_data['iframe_height'] = int( schema_raw_data['iframe_height']) except ValueError: form_errors['iframe_height'] = ['Integer value required.'] if not schema_raw_data['youtube_id']: form_errors['youtube_id'] = ['Youtube Id is mandatory'] if schema_raw_data['youtube_id']: yt_service = YouTubeService() try: yt_service.GetYouTubeVideoEntry( video_id=schema_raw_data['youtube_id']) except RequestError: form_errors['youtube_id'] = [ 'Invalid Youtube ID (inexisting video)' ] if not form_errors: if self.discussion: self.open_for_comments() else: self.close_for_comments() self._p_changed = 1 self.recatalogNyObject(self) # log date contributor = self.REQUEST.AUTHENTICATED_USER.getUserName() auth_tool = self.getAuthenticationTool() auth_tool.changeLastPost(contributor) notify(NyContentObjectEditEvent(self, contributor)) if REQUEST: self.setSessionInfoTrans(MESSAGE_SAVEDCHANGES, date=self.utGetTodayDate()) REQUEST.RESPONSE.redirect('%s/edit_html?lang=%s' % (self.absolute_url(), _lang)) else: if REQUEST is not None: self._prepare_error_response(REQUEST, form_errors, schema_raw_data) REQUEST.RESPONSE.redirect('%s/edit_html?lang=%s' % (self.absolute_url(), _lang)) else: raise ValueError(form_errors.popitem()[1]) # pick an error # zmi pages security.declareProtected(view_management_screens, 'manage_edit_html') manage_edit_html = PageTemplateFile('zpt/youtube_manage_edit', globals()) # site pages security.declareProtected(view, 'index_html') def index_html(self, REQUEST=None, RESPONSE=None): """ """ return self.getFormsTool().getContent({'here': self}, 'youtube_index') security.declareProtected(PERMISSION_EDIT_OBJECTS, 'edit_html') def edit_html(self, REQUEST=None, RESPONSE=None): """ """ return self.getFormsTool().getContent({'here': self}, 'youtube_edit')
logger = logging.getLogger('RhaptosRepository') def cmpTitle(x, y): """A method to provide a comparison between the titles of two record objects. Uses the sortTitle attribute if available; Title otherwise. Does case-less comparison. Returns a 'cmp' compatible value, suitable for use in a 'sort'. """ return int( cmp(x.sortTitle and x.sortTitle.lower() or x.Title.lower(), y.sortTitle and y.sortTitle.lower() or y.Title.lower())) manage_addRepositoryForm = PageTemplateFile( 'zpt/manage_addRepositoryForm', globals(), __name__='manage_addRepositoryForm') def manage_addRepository(self, id, title='', REQUEST=None): ''' ''' id = str(id) self = self.this() self._setObject(id, Repository(id, title)) if REQUEST is not None: REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_main')
from Globals import InitializeClass from AccessControl import ClassSecurityInfo from OFS.Folder import Folder from Products.PageTemplates.PageTemplateFile import PageTemplateFile from Products.NaayaCore.constants import * import Scheme import Template import Style import DiskFile import DiskTemplate manage_addSkinForm = PageTemplateFile('zpt/skin_add', globals()) def manage_addSkin(self, id='', title='', content=None, REQUEST=None): """ """ if content is None or content == '': ob = Skin(id, title) self._setObject(id, ob) if content == '': #create default empty templates self._getOb(id).createSkinFiles() else: self.manage_clone(self._getOb(content), id) ob = self._getOb(id) ob.title = title ob._p_changed = 1 if REQUEST is not None: return self.manage_main(self, REQUEST, update_menu=1)
from Products.PluggableAuthService.utils import classImplements from Products.PluggableAuthService.utils import csrf_only LOG = logging.getLogger('PluggableAuthService') class MultiplePrincipalError(Exception): pass class IZODBRoleManager(Interface): """ Marker interface. """ manage_addZODBRoleManagerForm = PageTemplateFile( 'www/zrAdd', globals(), __name__='manage_addZODBRoleManagerForm') def addZODBRoleManager(dispatcher, id, title=None, REQUEST=None): """ Add a ZODBRoleManager to a Pluggable Auth Service. """ zum = ZODBRoleManager(id, title) dispatcher._setObject(zum.getId(), zum) if REQUEST is not None: REQUEST['RESPONSE'].redirect('%s/manage_workspace' '?manage_tabs_message=' 'ZODBRoleManager+added.' % dispatcher.absolute_url())
manage_options = (PropertyManager.manage_options + SimpleItem.manage_options) _properties = ({ 'id': 'find_script', 'mode': 'w', 'type': 'string', 'label': 'Script that finds available elements', }, ) def findAvailableElements(self, slot): if not self.find_script: return None tool = aq_parent(aq_inner(aq_parent(aq_inner(self)))) s = tool.restrictedTraverse(self.find_script) return s(slot) addSlotClassForm = PageTemplateFile("addSlotClassForm", _www) def manage_addSlotClass(dispatcher, id, REQUEST=None): """Adds a slot class to a composite tool. """ ob = SlotClass() ob._setId(id) dispatcher._setObject(ob.getId(), ob) if REQUEST is not None: return dispatcher.manage_main(dispatcher, REQUEST)