class MemberData(BaseMemberData): security = AccessControl.ClassSecurityInfo() security.declarePrivate('setMemberProperties') def setMemberProperties(self, mapping): """Overridden to store a copy of certain user data fields in an SQL database""" # Only pass relevant fields to the database db_args = dict([(f, v) for (f, v) in mapping.items() if f in DB_FIELDS and self.getProperty(f) != v]) dbtool = getToolByName(self, 'portal_moduledb') if dbtool and db_args: # We have to pass in aq_parent to be our own parent, # otheriwse the ZSQL method will acquire blank arguments # from the property sheet if not self.member_catalog(getUserName=self.getId()): zLOG.LOG( "MemberData", zLOG.INFO, "INSERT memberdata for %s: %s" % (self.getId(), db_args)) dbtool.sqlInsertMember(aq_parent=self.aq_parent, id=self.getId(), **db_args) else: zLOG.LOG( "MemberData", zLOG.INFO, "UPDATE memberdata for %s: %s" % (self.getId(), db_args)) dbtool.sqlUpdateMember(aq_parent=self.aq_parent, id=self.getId(), **db_args) BaseMemberData.setMemberProperties(self, mapping)
class CatalogTool(base.CatalogTool): security = AccessControl.ClassSecurityInfo() def reindexComments(self): pass def clearFindAndRebuild(self): """Empties catalog, then finds all contentish objects (i.e. objects with an indexObject method), and reindexes them. This may take a long time. """ def indexObject(obj, path): if (base_hasattr(obj, 'indexObject') and safe_callable(obj.indexObject)): try: obj.indexObject() pdtool = obj.portal_discussion if pdtool.isDiscussionAllowedFor(obj): tb = pdtool.getDiscussionFor(obj) for ob in tb.getReplies(): ob.indexObject() except TypeError: # Catalogs have 'indexObject' as well, but they # take different args, and will fail pass self.manage_catalogClear() portal = aq_parent(aq_inner(self)) portal.ZopeFindAndApply(portal, search_sub=True, apply_func=indexObject)
class Plugin(PAS.plugins.BasePlugin.BasePlugin): security = AccessControl.ClassSecurityInfo() meta_type = 'LDAPAlchemy Plugin' # Tell PAS not to swallow our exceptions, do not use for production _dont_swallow_my_exceptions = True @security.public def authenticateCredentials(self, credentials): """see PAS.interfaces.plugins.IAuthenticationPlugin """ login = credentials.get('login') pw = credentials.get('password') if not (login and pw): return None # XXX: no real ldapalchemy, yet, but barebone ldapy # also for now, we open/close the ldap connection for each call ld = ldapy.initialize('ldapi://ldapi') login_dn = 'cn=%s,o=o' % login try: ldapy.simple_bind_s(ld, login_dn, pw) except ldapy.InvalidCredentials: authinfo = None else: uid = login authinfo = (uid, login) ldapy.unbind(ld) return authinfo
class HasProtectedMethods(SimpleItem): security = AccessControl.ClassSecurityInfo() def __init__(self, id): self.id = id @security.public def public_method(self): pass @security.protected('ppp') def pp_method(self): pass @security.protected('qqq') def qq_method(self): pass @security.protected('rrr') def rr_method(self): pass @security.private def private_method(self): pass
class HasProtectedMethods(SimpleItem): security = AccessControl.ClassSecurityInfo() security.declarePublic('public_method') security.declareProtected('ppp', 'pp_method') security.declareProtected('qqq', 'qq_method') security.declareProtected('rrr', 'rr_method') security.declarePrivate('private_method') def __init__(self, id): self.id = id def public_method(self): pass def pp_method(self): pass def qq_method(self): pass def rr_method(self): pass def private_method(self): pass
class MemberData(BaseMemberData): __implements__ = IMemberData security = AccessControl.ClassSecurityInfo() def __init__(self, id): self.id = id def getId(self): """Override to return the id we've stored""" return self.id security.declarePrivate('notifyModified') def notifyModified(self): # Recatalog this member cat = getToolByName(self, MEMBER_CATALOG) cat.catalog_object(self) security.declarePublic('getUser') def getUser(self): # First try using the acqusition context user = aq_parent(self) bcontext = aq_base(user) bcontainer = aq_base(aq_parent(aq_inner(self))) if bcontext is bcontainer or not hasattr(bcontext, 'getUserName'): # OK, the wrapper didn't work so let's try looking up the user by ID user_folder = getToolByName(self, 'acl_users') user = user_folder.getUser(self.getId()) if user: return user else: raise 'MemberDataError', "Can't find user data for %s" % self.getId( ) def getTool(self): return getToolByName(self, 'portal_memberdata') security.declarePublic('getMemberId') def getMemberId(self): return self.getId() ### GRUF interface security.declarePublic('getGroups') def getGroups(self): """Check to see if a user has a given role or roles.""" try: return self.getUser().getGroups() except AttributeError: # Cope with users from non-GRUF user folders return ()
class EMailAspect(Products.AlphaFlow.aspect.Aspect): zope.interface.implements(IEMailAspect) security = AccessControl.ClassSecurityInfo() aspect_type = "email" security.declarePrivate('__call__') def __call__(self): """Send email.""" work_items = [self.getWorkItem()] Products.AlphaFlow.activities.notify._send_email( self, self.getDefinition(), work_items)
class ZopeVersion(Version.Version, AccessControl.Role.RoleManager, OFS.SimpleItem.Item): """The ZopeVersion class builds on the core Version class to provide the Zope management interface and other product trappings.""" security = AccessControl.ClassSecurityInfo() security.setDefaultAccess('deny') meta_type = 'Version' manage_options = (( { 'label': 'Information', 'action': 'manage_main', 'help': ('ZopeVersionControl', 'Version-Manage.stx') }, { 'label': 'Properties', 'action': 'manage_properties_form', 'help': ('ZopeVersionControl', 'Version-Properties.stx') }, ) + AccessControl.Role.RoleManager.manage_options + OFS.SimpleItem.Item.manage_options) icon = 'misc_/ZopeVersionControl/Version.gif' security.declareProtected('View management screens', 'manage_main') manage_main = DTMLFile('dtml/VersionManageMain', globals()) manage_main._setName('manage_main') manage = manage_main security.declareProtected('View management screens', 'manage_properties_form') manage_properties_form = DTMLFile('dtml/VersionProperties', globals()) security.declareProtected('Manage repositories', 'manage_edit') def manage_edit(self, REQUEST=None): """Change object properties.""" if REQUEST is not None: message = "Saved changes." return self.manage_properties_form(self, REQUEST, manage_tabs_message=message)
class UserFeedback(BaseContent): """ Feedback from Connexions user """ schema = schema security = AccessControl.ClassSecurityInfo() actions = ( { 'id': 'view', 'title': 'View', 'action': 'feedback_view', 'permissions': (CMFCorePermissions.View, ) }, { 'id': 'edit', 'title': 'Edit', 'action': 'base_edit', 'permissions': (CMFCorePermissions.ModifyPortalContent, ) }, { 'id': 'metadata', 'title': 'Properties', 'action': 'base_metadata', 'permissions': (CMFCorePermissions.ModifyPortalContent, ) }, { 'id': 'references', 'title': 'References', 'action': 'reference_edit', 'permissions': (CMFCorePermissions.ModifyPortalContent, ) }, ) security.declareProtected(CMFCorePermissions.View, 'CookedBody') def CookedBody(self, stx_level='ignored'): """CMF compatibility method """ return self.getText()
class LensRedirectContainer(LensMajorContainer): """Like a Major Container, but looks for its contents in the real one. """ archetype_name = "Lens Redirect Container" allowed_content_types = () security = AccessControl.ClassSecurityInfo() def __bobo_traverse__(self, request, key): """If we find child objects in the real lenses folder, return those instead. Ideally, I'd like to make them think they were in this folder, but I can't seem to figure out how. Instead, push the category value into the request, where the dispatcher script can look. """ stack = request.get('TraversalRequestNameStack') lens_tool = getToolByName(self, 'lens_tool') canonical = lens_tool.getMajorContainer() # first getattr is just a test (which should not be wrapped, acquisition being too broad), # second is actual object (which must be wrapped, or else later acquisition fails) userfolder = getattr(canonical.aq_base, key, None) and getattr( canonical, key, None) if userfolder: if type( request ) != dict: # a dict when key is template, and we don't want to get normal lenses_listing request.set('getCategory', self.getCategory()) return userfolder return LensMajorContainer.__bobo_traverse__( self, request, key) # uses Five; getattr(self, key, None) doesn't work anymore # A PUT on the folder is intentionally a no-op security.declareProtected(ModifyPortalContent, 'PUT') def PUT(self, REQUEST): return
class LensTool(UniqueObject, SimpleItem): """Globally available methods for interacting with the lens system.""" id = 'lens_tool' meta_type = 'Lens Tool' security = AccessControl.ClassSecurityInfo() manage_options = (({ 'label': 'Overview', 'action': 'manage_overview' }, ) + SimpleItem.manage_options) ## ZMI methods security.declareProtected(ManagePortal, 'manage_overview') manage_overview = PageTemplateFile('zpt/overview', globals()) def __init__(self): """Setup tool data.""" self.lenstypes = KNOWN_LENS_TYPES security.declareProtected(ManagePermission, 'manage_setConfig') def manage_setConfig(self, lenstypes, REQUEST=None): """Post-creation config; see overview.zpt.""" if type(lenstypes) in StringTypes: lenstypes = lenstypes.split() self.lenstypes = lenstypes if REQUEST is not None: REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_overview') security.declareProtected(ManagePermission, 'getLensTypes') def getLensTypes(self): """List of content types considered lenses.""" lenstypes = getattr(self, 'lenstypes', None) if lenstypes is None: # auto-upgrade step self.lenstypes = lenstypes = KNOWN_LENS_TYPES return lenstypes def searchResults(self, REQUEST=None, **kw): """Calls lenses_catalog.searchResults with extra arguments that limit the results to what the user is allowed to see. """ portal_catalog = getToolByName(self, 'portal_catalog') lens_catalog = getToolByName(self, 'lens_catalog') user = _getAuthenticatedUser(self) kw['allowedRolesAndUsers'] = portal_catalog._listAllowedRolesAndUsers( user) # portal_catalog checks expiry here, but we don't care return lens_catalog.searchResults(REQUEST=REQUEST, **kw) security.declarePrivate('_getEntriesFor') def _getEntriesFor(self, contentId, version=None, implicit=True, inside=None): """Internal method to return all entries pointing to a specified item. If a version is provided, only entries with a start version less than and a stop version greater than that version will be provided. params: contentId: key for item. string. version: version of item from 'contentId'. string in the form '1.10' or list of ints like [1,10]. optional; if not provided, no version filtering. List param is a bit faster. implicit: get implicitly included content, being content inside an included collection. boolean. inside: the collection we are inside, if any, so that entries for that collection can be returned. tuple: (contentId, version). version is as above. returns: (global entries, contextual entries), where each is a (lazy) list of SelectedContent brains """ insideId = inside and inside[0] or None insideVersion = inside and inside[1] or None if version: if type(version) in StringTypes: version = [int(x) for x in version.split('.') ] # must provide list, not tuple! if type(version) is not ListType: version = list(version) if inside: if type(insideVersion) in StringTypes: try: insideVersion = [int(x) for x in insideVersion.split('.') ] # must provide list, not tuple! except ValueError: # not an int, probably an unpublished course if insideVersion == "**new**": insideVersion = [ 1, 1 ] # just set dummy version, no course entries will be found else: raise if type(insideVersion) is not ListType: insideVersion = list(insideVersion) query = {} query['portal_type'] = 'SelectedContent' query['id'] = contentId if version: query['getVersionStart'] = {'query': [version], 'range': 'max'} query['getVersionStop'] = {'query': [version], 'range': 'min'} # Note: the version stop here is supposed to be open-ended; we do this by relying on endless entries # being a different type to sort to the end instead of the beginning. See VersionField for details. directentries = self.searchResults(**query) content_catalog = getToolByName(self, 'content').catalog collections = [ c for c in content_catalog(containedModuleIds=contentId) if c.objectId != insideId ] latest_version_str = content_catalog( objectId=contentId)[0].version # TODO: only works for published # leaving here to draw out errors if version: # Filter out any collections that don't contain this version of the module. collectionIds = [] for c in collections: pcp = c.getObject().getContainedObject(contentId) if pcp.version == 'latest': pcp_version_str = latest_version_str else: pcp_version_str = pcp.version pcp_version_tup = [int(x) for x in pcp_version_str.split('.')] if pcp_version_tup == version: collectionIds.append(c.objectId) else: collectionIds = [c.objectId for c in collections] containingentries = self.searchResults({ 'portal_type': 'SelectedContent', 'id': collectionIds, 'getImplicit': True }) allentries = directentries + containingentries if inside: query['id'] = insideId query['getVersionStart'] = { 'query': [insideVersion], 'range': 'max' } query['getVersionStop'] = { 'query': [insideVersion], 'range': 'min' } contextual = self.searchResults(**query) else: contextual = LazyCat([]) return (allentries, contextual) security.declarePublic('getListsIncluding') def getListsIncluding(self, contentId, version=None, categories=[], implicit=True, inside=None): """Return data about known lens content (per getLensTypes) which include entries for specified item. If a version is provided, only entries with a start version less than and a stop version greater than that version will be provided. params: contentId: key for item. string. version: version of item from 'contentId'. string. categories: "lens" types (endorsement, review, etc) to return. list of strings. Empty/None means all. implicit: get implicitly included content, being content inside an included collection. boolean. inside: the collection we are inside, if any, so that entries for that collection can be returned. tuple: (contentId, version) return {category: {listlocation:(listinfo,entryinfo)}} where listinfo={name:value} from CSL.listDict(), including lens id, names, category, state, location, et al. where entryinfo={name:value} including entry id, Title, tags, comment, fromImplicit """ lens_catalog = getToolByName(self, 'lens_catalog') catdict = {} entries = self._getEntriesFor(contentId, version, implicit, inside) # each list can contain only one entry, so we don't need a set... for entry in entries[1] + entries[0]: # get parent info, which among other things will tell us about implicitness parentpath = entry.getPath().split('/')[:-1] parent = self.restrictedTraverse( parentpath ) # TODO : might put this in a CSL metadata slot and get from catalog listinfo = parent.listDict() listloc = listinfo['location'] edict = {} #we're not us if entry.getId != contentId: if inside and entry.getId == inside[0]: edict['fromImplicit'] = 'contextual' else: edict['fromImplicit'] = 'implicit' else: edict['fromImplicit'] = False edict['id'] = entry.getId edict['Title'] = entry.Title edict['tags'] = ' '.join(entry.getTags) if 'getNamespaceTags' in lens_catalog.indexes(): # Legacy data with essentially unintialized metadata # will not return the desired iterablke type. if isinstance(entry.getNamespaceTags, (ListType, TupleType)): edict['namespaceTags'] = entry.getNamespaceTags else: edict['namespaceTags'] = [] edict['comment'] = entry.getPlainComment edict['approved'] = getattr(entry, 'getApproved', lambda: False) #separate lists by type (or only the specified type, if specified) cat = listinfo['category'] if not catdict.has_key(cat): catdict[cat] = {} if not catdict[cat].has_key(listloc): catdict[cat][listloc] = (listinfo, []) catdict[cat][listloc][1].append(edict) return catdict security.declarePublic('getListsOwned') def getListsOwned(self, memberid=None, **kwargs): """Return known lens content (per getLensTypes) owned by member 'memberid', as a catalog list. If memberid is None, default to authenticated user, if any. """ if not memberid: mship = getToolByName(self, 'portal_membership') m = mship.getAuthenticatedMember() if len( m.getRoles() ) > 1: # only one role == Anonymous; should be less fragile than name checks memberid = m.getId() if memberid: contentFilter = dict(portal_type=self.getLensTypes(), Creator=memberid) if kwargs: contentFilter.update(kwargs) pwned = self.searchResults(**contentFilter) # allowedRolesAndUsers is for viewing, but there's no similar index for editing, # which would be more appropriate... else: pwned = [] return pwned security.declarePublic('getRecentlyModifiedListsOwned') def getRecentlyModifiedListsOwned(self, memberid=None): """Return known lens content owned by member 'memberid' and sorted via modification date, as a catalog list. If memberid is None, default to authenticated user, if any. """ if not memberid: mship = getToolByName(self, 'portal_membership') m = mship.getAuthenticatedMember() if len( m.getRoles() ) > 1: # only one role == Anonymous; should be less fragile than name checks memberid = m.getId() if memberid: pwned = self.searchResults(portal_type='ContentSelectionLens', Creator=memberid, sort_on='modified', sort_order='descending') # allowedRolesAndUsers is for viewing, but there's no similar index for editing, # which would be more appropriate... else: pwned = [] return pwned security.declarePublic('getListsBy') def getListsBy(self, category=None, path=None, all=None, memberid=None, inclzero=True, incl_organised=False): """Return public lensish content (per getLensTypes) meeting certain criteria. Light catalog wrapper. Returns catalog results list. If 'path' is provided, restrict to that path. (Used for showing only those inside a folder.) If 'category' is None, all are provided. Otherwise, all in that category. If 'all' is true, will not restrict to public. If 'inclzero' if true, empty lenses will be included. This is the default, being the old behavior. Path should be as acceptable by lens_catalog; like that provided by '/'.join(context.getPhysicalPath()) """ # get the lenses args = {'portal_type': self.getLensTypes()} if category: args['getCategory'] = category if path: args['path'] = path if not all: args['review_state'] = ('published', 'published_open', 'private_open') if memberid: args['Creator'] = memberid results = self.searchResults(**args) if results: if not inclzero: results = [x for x in results if x.getCount > 0] if not incl_organised: # find all lenses referenced by a lensorganiser rc = getToolByName(self, 'reference_catalog') targetUIDs = [ b.targetUID for b in rc(relationship='lenses_lensorganizers') ] # filter out the lenses in lensorganisers if targetUIDs: results = [x for x in results if x.UID not in targetUIDs] return results security.declarePublic('getTagsForContent') def getTagsForContent(self, contentId, version=None, inside=None, tag_type='tags'): """Return the tags on any entries that point to specified item. If version is supplied, only return tags on entries that include the version in their range. params: contentId: key for item. string. version: version of item from 'contentId'. string. return: list of strings Theoretical companion would be 'getTagCloudForContent' which would return list of {tagname:number/percentage}) tag_type is one of ('tags', 'namespaceTags') """ insideId = inside and inside[0] or None #TODO : switched filter by version directentries = self.searchResults(portal_type='SelectedContent', id=contentId) content_catalog = getToolByName(self, 'content').catalog collectionIds = [ c.objectId for c in content_catalog(containedModuleIds=contentId) ] containingentries = self.searchResults({ 'portal_type': 'SelectedContent', 'id': collectionIds, 'getImplicit': True }) contextualentry = self.searchResults({ 'portal_type': 'SelectedContent', 'id': insideId }) allentries = directentries + containingentries + contextualentry #aggregate comments for each entry into set # (for a tag cloud, we could aggregate into a {tagname:number of occurences}) tagset = Set() for brain in allentries: if tag_type == 'tags': tags = brain.getTags elif tag_type == 'namespaceTags': # Legacy data with essentially uninitialized metadata # will not return the desired iterable type. if isinstance(brain.getNamespaceTags, (ListType, TupleType)): tags = brain.getNamespaceTags else: tags = [] else: tags = [] for tag in tags: tagset.add(tag) if tag_type == 'tags': return tuple(tagset) if tag_type == 'namespaceTags': return tuple(tagset) return [] security.declarePublic('getContentForTag') def getContentForTag(self, tag): """Return content that has this on any entries that point to that content. params: tag: string returns: list of entry brains (urls? id/version? objects?) """ contentFilter = dict(portal_type='SelectedContent') lens_catalog = getToolByName(self, 'lens_catalog') if 'getNamespaceTags' in lens_catalog.indexes(): if tag.find(TAGNAMESPACE_DELIMITER) != -1: contentFilter['getNamespaceTags'] = tag else: contentFilter['getTags'] = tag else: contentFilter['getTags'] = tag taggedentries = self.searchResults(**contentFilter) return taggedentries security.declarePublic('entriesToContent') def entriesToContent(self, entries): """Return list of result objects (catalog brains) for content referred to in list of entries passed in with 'entrylist'. Used to convert something like the output of 'getContentForTag' into a list of content brains for use by a results macro. params: entries: list of entry (SelectedContent) brains returns: list of content brains """ ids = [elt.getId for elt in entries] return self.content.catalog(objectId=ids) security.declarePrivate('getMajorContainer') def getMajorContainer(self): """Get the folder that contains all individual folders. Private because one shouldn't rely on there being a major container. """ toplevelcontainer = "lenses" portal = getToolByName(self, 'portal_url').getPortalObject() lenses = getattr(portal.aq_explicit, toplevelcontainer, None) if lenses is None: # make one... portal.invokeFactory('LensMajorContainer', id=toplevelcontainer, title="Lenses") lenses = getattr(portal, toplevelcontainer) return lenses security.declarePublic('getIndividualFolder') def getIndividualFolder(self, userid=None, create=True): """Return the folder that contains lenses for the current user. None for Anonymous. Pass 'userid' for a specific non-current user. Pass boolean value for 'create' to create folder if it doens't exist. Default: true. """ mship = getToolByName(self, 'portal_membership') if userid is None: m = mship.getAuthenticatedMember() else: m = mship.getMemberById(userid) if len( m.getRoles() ) > 1: # only one role == Anonymous; should be less fragile than name checks memberid = m.getId() lenses = self.getMajorContainer() namedfolder = getattr(lenses, memberid, None) if namedfolder is None and create: # make one... namedfolder = _createObjectByType('LensFolder', lenses, memberid) # set ownership namedfolder.__ac_local_roles__ = None namedfolder.manage_setLocalRoles(memberid, ['Owner']) return namedfolder return None security.declarePublic('getMemberFolder') def getMemberFolder(self, context): """ Traverse upwards from context until the member's folder is found """ folder = None parent = getattr(context, 'aq_parent', None) previous = context while parent is not None: if getattr(parent, 'portal_type', '') == 'LensMajorContainer': folder = previous break previous = parent parent = getattr(parent, 'aq_parent', None) return folder security.declarePublic('getOpenLenses') def getOpenLenses(self, full_objects=False, omit_contained_ids=None): """ Return open lenses params: full_objects: return full objects omit_contained_ids: do not include a lens in the result if an id in omit_contained_ids is contained by the lens """ if omit_contained_ids is not None: # Make iterable if type(omit_contained_ids) != type([]): omit_contained_ids = [omit_contained_ids] # Remove the paths for the ids to be omitted from result_paths result_paths = [ b.getPath() for b in self.searchResults( portal_type=self.getLensTypes(), review_state=['published_open', 'private_open']) ] remove_paths = [ '/'.join(b.getPath().split('/')[:-1]) for b in self.searchResults(path=result_paths, id=omit_contained_ids) ] new_paths = [] for pth in result_paths: if pth not in remove_paths: new_paths.append(pth) # Do the final query results = self.searchResults( portal_type=self.getLensTypes(), path=new_paths, review_state=['published_open', 'private_open']) else: # Nothing to omit results = self.searchResults( portal_type=self.getLensTypes(), review_state=['published_open', 'private_open']) if full_objects: return [r.getObject() for r in results] return results security.declarePublic('catalogQueueInfo') def catalogQueueInfo(self): """Fairly general method for indicating time left for queue catalog to process something just added. Put here because the lenses facility uses this, and it's convenient. Return tuple of raw size and estimated time in minutes. """ rate = 150.0 # per minute qcat = getToolByName(self, 'lens_catalog') if getattr(qcat, 'manage_size', None) is not None: size = qcat.manage_size() return (size, size / rate) return None security.declarePublic('catalogQueueProcess') def catalogQueueProcess(self): """Poke otherwise-resticted process method of queue catalog. """ qcat = getToolByName(self, 'lens_catalog') process = getattr(qcat, 'process', None) if process is not None: return process(25) security.declarePrivate('notify') def notify(self, lenses, object, template, **kwargs): """ Render the mail template provided using the object as context and email it to the adress associated with the lens. """ host = self.MailHost for lens in lenses: # try: # we cannot pass 'lens' as it might be private and thus unauthorized to the current user lensTitle = lens.Title() lensURL = lens.absolute_url() included = not lens[object.objectId].getVersionStop() lensCreator = lens.Creator() if lens.notifyOfChanges: mail_text = template(self, lensTitle=lensTitle, lensURL=lensURL, included=included, lensCreator=lensCreator, object=object, **kwargs) try: host.send(mail_text) except ConflictError: raise except Exception, e: import zLOG zLOG.LOG("Lensmaker", zLOG.ERROR, "Error sending mail: " + str(e))
class ZopeVersionHistory( VersionHistory.VersionHistory, AccessControl.Role.RoleManager, OFS.SimpleItem.Item, ): """The ZopeVersionHistory build on the core VersionHistory class to provide the Zope management interface and other product trappings.""" security = AccessControl.ClassSecurityInfo() security.setDefaultAccess('deny') meta_type = 'Version History' manage_options = (( { 'label': 'Contents', 'action': 'manage_main', 'help': ('ZopeVersionControl', 'VersionHistory-Manage.stx') }, { 'label': 'Properties', 'action': 'manage_properties_form', 'help': ('ZopeVersionControl', 'VersionHistory-Properties.stx') }, ) + AccessControl.Role.RoleManager.manage_options + OFS.SimpleItem.Item.manage_options) icon = 'misc_/ZopeVersionControl/VersionHistory.gif' security.declareProtected('View management screens', 'manage_main') manage_main = DTMLFile('dtml/VersionHistoryManageMain', globals()) manage_main._setName('manage_main') manage = manage_main security.declareProtected('View management screens', 'manage_properties_form') manage_properties_form = DTMLFile('dtml/VersionHistoryProperties', globals()) security.declareProtected('Manage repositories', 'manage_edit') def manage_edit(self, REQUEST=None): """Change object properties.""" if REQUEST is not None: message = "Saved changes." return self.manage_properties_form(self, REQUEST, manage_tabs_message=message) def __getitem__(self, name): activity = self._branches.get(name) if activity is not None: return activity.__of__(self) raise KeyError, name security.declarePrivate('objectIds') def objectIds(self, spec=None): return self._branches.keys() security.declarePrivate('objectValues') def objectValues(self, spec=None): return self._branches.values() security.declarePrivate('objectItems') def objectItems(self, spec=None): return self._branches.items()
class VersionSupport(ExtensionClass.Base): """Mixin class to support version-controllable resources.""" manage_options=( {'label': 'Version Control', 'action':'versionControlMain', 'help': ('ZopeVersionControl', 'VersionControl.stx'), 'filter': isAVersionableResource, }, ) security = AccessControl.ClassSecurityInfo() security.declareProtected('View management screens', 'versionControlMain') versionControlMain = DTMLFile('dtml/VersionControlMain', globals()) security.declareProtected('View management screens', 'versionControlLog') versionControlLog = DTMLFile('dtml/VersionControlLog', globals()) security.declarePrivate('haveRepository') def haveRepository(self): try: result = self.getRepository() except VersionControlError: return 0 return 1 security.declarePrivate('getRepository') def getRepository(self): # We currently only allow a single repository in a given context. if hasattr(self, '_v_repository'): return self._v_repository try: items = self.superValues('Repository') except: items = self.aq_inner.aq_parent.superValues('Repository') result = items and items[0] or None if result is None: raise VersionControlError( 'No versioning repository was found.' ) self._v_repository = result return result security.declarePublic('isAVersionableResource') def isAVersionableResource(self, object): return self.getRepository().isAVersionableResource(self) security.declarePublic('isUnderVersionControl') def isUnderVersionControl(self): return hasattr(self, '__vc_info__') security.declarePublic('isResourceUpToDate') def isResourceUpToDate(self): return self.getRepository().isResourceUpToDate(self) security.declarePublic('isResourceChanged') def isResourceChanged(self): return self.getRepository().isResourceChanged(self) security.declarePublic('getVersionInfo') def getVersionInfo(self): return self.getRepository().getVersionInfo(self) security.declareProtected(use_vc_permission, 'applyVersionControl') def applyVersionControl(self, REQUEST=None): """Place a resource under version control.""" repository = self.getRepository() object = repository.applyVersionControl(self) if REQUEST is not None: message="The resource has been placed under version control." return object.versionControlMain( object, REQUEST, manage_tabs_message=message ) security.declareProtected(use_vc_permission, 'checkoutResource') def checkoutResource(self, REQUEST=None): """Checkout a version-controlled resource.""" repository = self.getRepository() object = repository.checkoutResource(self) if REQUEST is not None: message="The resource has been checked out." return object.versionControlMain( object, REQUEST, manage_tabs_message=message ) security.declareProtected(use_vc_permission, 'checkinResource') def checkinResource(self, message='', REQUEST=None): """Checkout a version-controlled resource.""" repository = self.getRepository() object = repository.checkinResource(self, message) version = object.getVersionInfo().version_id if REQUEST is not None: message="The resource has been checked in [version %s]." % version return object.versionControlMain( object, REQUEST, manage_tabs_message=message ) security.declareProtected(use_vc_permission, 'uncheckoutResource') def uncheckoutResource(self, REQUEST=None): """Uncheckout a version-controlled resource.""" repository = self.getRepository() object = repository.uncheckoutResource(self) version = object.getVersionInfo().version_id if REQUEST is not None: message="The resource has been reverted to version %s." % version return object.versionControlMain( object, REQUEST, manage_tabs_message=message ) security.declareProtected(use_vc_permission, 'updateResource') def updateResource(self, selector, REQUEST=None): """Update a version-controlled resource.""" repository = self.getRepository() if selector == 'LATEST_VERSION': selector = None object = repository.updateResource(self, selector) version = object.getVersionInfo().version_id if REQUEST is not None: message="The resource has been updated to version %s." % version return object.versionControlMain( object, REQUEST, manage_tabs_message=message ) security.declareProtected(use_vc_permission, 'labelResource') def labelResource(self, label, force=0, REQUEST=None): """Label a version-controlled resource.""" repository = self.getRepository() object = repository.labelResource(self, label, force) if REQUEST is not None: message="The label has been applied to this resource." return object.versionControlMain( object, REQUEST, manage_tabs_message=message ) security.declareProtected(use_vc_permission, 'getVersionIds') def getVersionIds(self): return self.getRepository().getVersionIds(self) security.declareProtected(use_vc_permission, 'getLabelsForHistory') def getLabelsForHistory(self): return self.getRepository().getLabelsForHistory(self) security.declareProtected(use_vc_permission, 'getLabelsForVersion') def getLabelsForVersion(self): return self.getRepository().getLabelsForVersion(self) security.declareProtected(use_vc_permission, 'getLogEntries') def getLogEntries(self): return self.getRepository().getLogEntries(self)
class ZopeSimplate(Script, Simplate, Historical, Cacheable, Traversable, PropertyManager): "Zope wrapper for Simplate using python string replacement" if SUPPORTS_WEBDAV_LOCKS: __implements__ = (WriteLockInterface, ) meta_type = 'Simplate' func_defaults = None func_code = FuncCode((), 0) _default_bindings = {'name_subpath': 'traverse_subpath'} _default_content_fn = os.path.join(package_home(globals()), 'www', 'default.html') manage_options = ( {'label':'Edit', 'action':'simplate_editForm', 'help': ('Simplates', 'Simplate_Edit.stx')}, {'label':'Test', 'action':'ZScriptHTML_tryForm'}, ) + PropertyManager.manage_options \ + Historical.manage_options \ + SimpleItem.manage_options \ + Cacheable.manage_options _properties = ( { 'id': 'title', 'type': 'string', 'mode': 'w' }, { 'id': 'content_type', 'type': 'string', 'mode': 'w' }, #{'id':'expand', 'type':'boolean', 'mode': 'w'}, { 'id': 'value_paths', 'type': 'lines', 'mode': 'w' }, ) def __init__(self, id, text='(empty)', content_type=None, value_paths=[]): self.id = str(id) self.ZBindings_edit(self._default_bindings) if text is None: text = open(self._default_content_fn).read() self.simplate_edit(text, content_type) self.simplate_setValue_paths(value_paths) def _setPropValue(self, id, value): PropertyManager._setPropValue(self, id, value) self.ZCacheable_invalidate() security = AccessControl.ClassSecurityInfo() security.declareObjectProtected('View') security.declareProtected('View', '__call__') security.declareProtected( 'View management screens', 'simplate_editForm', 'manage_main', 'read', 'ZScriptHTML_tryForm', 'PrincipiaSearchSource', 'document_src', # 'source.html', # 'source.xml', ) security.declareProtected( 'FTP access', 'manage_FTPstat', 'manage_FTPget', 'manage_FTPlist', ) simplate_editForm = PageTemplateFile('www/simplateEdit.pt', globals(), __name__='simplate_editForm') simplate_editForm._owner = None manage = manage_main = simplate_editForm security.declareProtected( 'Change simplates', 'simplate_editAction', 'simplate_setTitle', 'simplate_setValue_paths', 'simplate_edit', 'simplate_upload', 'simplate_changePrefs', ) def simplate_editAction(self, REQUEST, title, text, content_type, value_paths): """Change the title and document.""" if SUPPORTS_WEBDAV_LOCKS and self.wl_isLocked(): raise ResourceLockedError, "File is locked via WebDAV" self.simplate_setTitle(title) self.simplate_setValue_paths(value_paths) self.simplate_edit(text, content_type) REQUEST.set('text', self.read()) # May not equal 'text'! message = "Saved changes." if getattr(self, '_v_warnings', None): message = ("<strong>Warning:</strong> <i>%s</i>" % '<br>'.join(self._v_warnings)) return self.simplate_editForm(manage_tabs_message=message) def simplate_setTitle(self, title): self._setPropValue('title', str(title)) def simplate_setValue_paths(self, value_paths): self._setPropValue('value_paths', list(value_paths)) self._cook() def simplate_upload(self, REQUEST, file=''): """Replace the document with the text in file.""" if SUPPORTS_WEBDAV_LOCKS and self.wl_isLocked(): raise ResourceLockedError, "File is locked via WebDAV" if type(file) is not StringType: if not file: raise ValueError, 'File not specified' file = file.read() self.write(file) message = 'Saved changes.' return self.simplate_editForm(manage_tabs_message=message) def simplate_changePrefs(self, REQUEST, height=None, width=None, dtpref_cols="100%", dtpref_rows="20"): """Change editing preferences.""" dr = {"Taller": 5, "Shorter": -5}.get(height, 0) dc = {"Wider": 5, "Narrower": -5}.get(width, 0) if isinstance(height, int): dtpref_rows = height if isinstance(width, int) or \ isinstance(width, str) and width.endswith('%'): dtpref_cols = width rows = str(max(1, int(dtpref_rows) + dr)) cols = str(dtpref_cols) if cols.endswith('%'): cols = str(min(100, max(25, int(cols[:-1]) + dc))) + '%' else: cols = str(max(35, int(cols) + dc)) e = (DateTime("GMT") + 365).rfc822() setCookie = REQUEST["RESPONSE"].setCookie setCookie("dtpref_rows", rows, path='/', expires=e) setCookie("dtpref_cols", cols, path='/', expires=e) REQUEST.other.update({"dtpref_cols": cols, "dtpref_rows": rows}) return self.manage_main() def ZScriptHTML_tryParams(self): """Parameters to test the script with.""" return [] def manage_historyCompare(self, rev1, rev2, REQUEST, historyComparisonResults=''): return ZopeSimplate.inheritedAttribute('manage_historyCompare')( self, rev1, rev2, REQUEST, historyComparisonResults=html_diff(rev1._text, rev2._text)) def simplate_getContext(self): root = self.getPhysicalRoot() context = self._getContext() c = { 'template': self, 'here': context, 'context': context, 'container': self._getContainer(), 'nothing': None, 'options': {}, 'root': root, 'request': getattr(root, 'REQUEST', None), 'modules': SecureModuleImporter, } return c def write(self, text): self.ZCacheable_invalidate() ZopeSimplate.inheritedAttribute('write')(self, text) def _exec(self, bound_names, args, kw): """Call a simplate""" if not kw.has_key('args'): kw['args'] = args bound_names['options'] = kw try: response = self.REQUEST.RESPONSE if not response.headers.has_key('content-type'): response.setHeader('content-type', self.content_type) except AttributeError: pass security = getSecurityManager() bound_names['user'] = security.getUser() # Retrieve the value from the cache. keyset = None if self.ZCacheable_isCachingEnabled(): # Prepare a cache key. keyset = {'here': self._getContext(), 'bound_names': bound_names} result = self.ZCacheable_get(keywords=keyset) if result is not None: # Got a cached value. return result # Execute the template in a new security context. security.addContext(self) try: result = self.simplate_render(extra_context=bound_names) if keyset is not None: # Store the result in the cache. self.ZCacheable_set(result, keywords=keyset) return result finally: security.removeContext(self) security.declareProtected( 'Change simplates', 'PUT', 'manage_FTPput', 'write', 'manage_historyCopy', 'manage_beforeHistoryCopy', 'manage_afterHistoryCopy', ) def PUT(self, REQUEST, RESPONSE): """ Handle HTTP PUT requests """ self.dav__init(REQUEST, RESPONSE) if SUPPORTS_WEBDAV_LOCKS: self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1) self.write(REQUEST.get('BODY', '')) RESPONSE.setStatus(204) return RESPONSE manage_FTPput = PUT def manage_FTPget(self): "Get source for FTP download" self.REQUEST.RESPONSE.setHeader('Content-Type', self.content_type) return self.read() def get_size(self): return len(self.read()) getSize = get_size def PrincipiaSearchSource(self): "Support for searching - the document's contents are searched." return self.read() def document_src(self, REQUEST=None, RESPONSE=None): """Return expanded document source.""" if RESPONSE is not None: RESPONSE.setHeader('Content-Type', 'text/plain') if REQUEST is not None and REQUEST.get('raw'): return self._text return self.read() def om_icons(self): """Return a list of icon URLs to be displayed by an ObjectManager""" icons = ({ 'path': 'misc_/Simplates/simplate.png', 'alt': self.meta_type, 'title': self.meta_type }, ) if not self._v_cooked: self._cook() if self._v_errors: icons = icons + ({ 'path': 'misc_/Simplates/exclamation.gif', 'alt': 'Error', 'title': 'This simplate has an error' }, ) return icons def __setstate__(self, state): # This is here for backward compatibility. :-( ZopeSimplate.inheritedAttribute('__setstate__')(self, state) def simplate_source_file(self): """Returns a file name to be compiled into the TAL code.""" try: return '/'.join(self.getPhysicalPath()) except: # This simplate is being compiled without an # acquisition context, so we don't know where it is. :-( return None if not SUPPORTS_WEBDAV_LOCKS: def wl_isLocked(self): return 0
class HitCountTool(UniqueObject, SimpleItem): __implements__ = (IHitCountTool) id = 'portal_hitcount' meta_type = 'HitCount Tool' security = AccessControl.ClassSecurityInfo() manage_options = (({ 'label': 'Overview', 'action': 'manage_overview' }, ) + SimpleItem.manage_options) def __init__(self): self._hits = {} self._recent_hit_counts = [] self._hit_counts = [] self._recent_daily_averages = [] self._daily_averages = [] self._startdate = self._inc_begin = self._inc_end = DateTime.DateTime() ## ZMI methods security.declareProtected(ManagePortal, 'manage_overview') manage_overview = PageTemplateFile('zpt/explainHitCountTool', globals()) # IHitCountTool Interface fulfillment def resetHitCounts(self): """ Reset all hit counts to 0 and set the start date to the current date and time """ if hasattr(self, '_hits'): self._hits.clear() else: self._hits = {} self._recent_hit_counts = [] self._hit_counts = [] self._recent_daily_averages = [] self._daily_averages = [] self._startdate = self._inc_begin = self._inc_end = DateTime.DateTime() try: objs = self.content.objectValues( ['Version Folder', 'Module Version Folder']) for ob in objs: self.registerObject(ob.id, ob.created()) except AttributeError: pass def registerObject(self, objectId, published_date): """Register an object with the HitCountTool""" self._hits[objectId] = Hits(published_date, self) self._recent_hit_counts.append((objectId, 0)) self._hit_counts.append((objectId, 0)) self._recent_daily_averages.append((objectId, 0)) self._daily_averages.append((objectId, 0)) self._p_changed = 1 def listRegisteredObjects(self): """Return a list of identifiers for registered objects""" return self._hits.keys() def incrementCounts(self, mapping, begin_date=None, end_date=None): """ Increment the hit counter for content objects. mapping must be a mapping from objectID to an integer number of hit counts. begin_date and end_date specify the range of time covered by this increment. If end_date is None, the current timestamp will be used. """ if not type(mapping) is DictType: raise TypeError self.recentHits = mapping self._inc_begin = begin_date or self._inc_end self._inc_end = end_date or DateTime.DateTime() #If the increment begin date is earlier than the start date, update it if begin_date and (begin_date < self._startdate): self._startdate = begin_date dateRange = self.getIncrementDateRange() end = dateRange[1] inc_start = dateRange[0] tool_start = self.getStartDate() for objectId, count in mapping.items(): try: hits = self._hits[objectId] except KeyError: raise ValueError, "Object %s not registered" % objectId hits.recent = count hits.total += count recent_age = end - (hits.published > inc_start and hits.published or inc_start) # If there is no age, just use the count as the average if recent_age: recent_avg = count * 1.0 / recent_age else: recent_avg = count full_age = end - (hits.published > tool_start and hits.published or tool_start) # If there is no age, just use the count as the average if full_age: avg = hits.total * 1.0 / full_age else: avg = hits.total hits._daily_average = (avg, recent_avg) rec_items = [(id, hits.recent) for (id, hits) in self._hits.items()] rec_items.sort(lambda a, b: cmp(b[1], a[1])) self._recent_hit_counts = rec_items items = [(id, hits.total) for (id, hits) in self._hits.items()] items.sort(lambda a, b: cmp(b[1], a[1])) self._hit_counts = items rec_items = [(id, hits.avgPerDay(True)) for (id, hits) in self._hits.items()] rec_items.sort(lambda a, b: cmp(b[1], a[1])) self._recent_daily_averages = rec_items items = [(id, hits.avgPerDay(False)) for (id, hits) in self._hits.items()] items.sort(lambda a, b: cmp(b[1], a[1])) self._daily_averages = items total_objects = len(self.listRegisteredObjects()) if total_objects: object_ranks, counts = zip(*items) object_ranks = list(object_ranks) recent_object_ranks, recent_counts = zip(*rec_items) recent_object_ranks = list(recent_object_ranks) else: object_ranks = [] counts = [] recent_object_ranks = [] recent_counts = [] for o_id, o_hits in self._hits.items(): try: index = object_ranks.index(o_id) + 1 except ValueError: # Object not ranked, place after ranked objects index = total_objects try: recent_index = recent_object_ranks.index(o_id) + 1 except ValueError: # Object not ranked, place after ranked objects recent_index = total_objects o_hits._percentile = ((total_objects - index) * 100.0 / total_objects, (total_objects - recent_index) * 100.0 / total_objects) self._p_changed = 1 def getHitCounts(self, recent=False): """ Return hit counts for all content objects. If recent is True, return the count for the previous increment, otherwise returns counts for all time The return value will be a list of (objectId, count) tuples in descending hit count order. """ if recent: return self._recent_hit_counts else: return self._hit_counts def getHitCountForObject(self, objectId, recent=False): """ Return the hit counts for the specified object ID. If recent is True, return the count for the previous increment, otherwise returns counts for all time """ try: hits = self._hits[objectId] except KeyError: return 0 if recent: return hits.recent else: return hits.total def getDailyAverages(self, recent=False): """ Return the average daily hit count for all registered objects. If recent=True, return the average for the previous increment, otherwise returns averages for all time. """ if recent: return self._recent_daily_averages else: return self._daily_averages def getDailyAverageForObject(self, objectId, recent=False): """ Return the average daily hit count for the specified object from its date of publication to the present (for the date range statistics are available). If recent=True, return the average for the previous increment, otherwise returns averge for all time. """ try: return self._hits[objectId].avgPerDay(recent) except KeyError: return 0 def getPercentileForObject(self, objectId, recent=False): """ Return the average daily hit count for the specified object from its date of publication to the present (for the date range statistics are available). If recent=True, return the average for the previous increment, otherwise returns averge for all time. """ try: return self._hits[objectId].getPercentile(recent) except KeyError: return 0 def setStartDate(self, date): """ Set the initial date from which hits are counted """ self._startdate = date def getStartDate(self): """ Get the initial date from which hits are counted """ return self._startdate def getIncrementDateRange(self): """ Return the date range covered by the most recent increment as a tuple (begin_date, end_date) """ return (self._inc_begin, self._inc_end) def dumpHitCounts(self): """Returns a transferable string object of all the hit count data. For use with restoreHitCounts""" pstr = StringIO.StringIO() kosher = pickle.Pickler(pstr) for attr in (self._hits, self._recent_hit_counts, self._hit_counts, self._recent_daily_averages, self._daily_averages, self._startdate, self._inc_begin, self._inc_end): kosher.dump(attr) pdata = pstr.getvalue() pstr.close() return pdata def restoreHitCounts(self, dumpstr): """Destructively overrides the hit count data with that provided in the dump string, which was previously generated by dumpHitCounts""" pstr = StringIO.StringIO(dumpstr) dill = pickle.Unpickler(pstr) self.resetHitCounts() self._hits.update(dill.load()) self._recent_hit_counts.extend(dill.load()) self._hit_counts.extend(dill.load()) self._recent_daily_averages.extend(dill.load()) self._daily_averages.extend(dill.load()) self._startdate = dill.load() self._inc_begin = dill.load() self._inc_end = dill.load()
class QueueTool(UniqueObject, SimpleItem): """ Tool for storage of Print files """ id = 'queue_tool' meta_type = 'Queue Tool' manage_options = (({'label':'Overview', 'action':'manage_overview'}, {'label':'Configure', 'action':'manage_configure'} )+ SimpleItem.manage_options ) manage_overview = PageTemplateFile('zpt/manage_overview_queue_tool.zpt', globals()) manage_configure = PageTemplateFile('zpt/manage_configure_queue_tool.zpt', globals()) security = AccessControl.ClassSecurityInfo() # set default time window to 1 hour DEFAULT_PROCESS_TIME_WINDOW = 3600 def __init__(self): """Initialize (singleton!) tool object.""" # user configurable self.dictServers = {} self.secProcessWindows = {} self.secDefaultProcessWindow = self.DEFAULT_PROCESS_TIME_WINDOW # unknown to the user self.pendingRequests = PersistentList([]) self.processingRequests = PersistentList([]) log('__init__ completed.') # this no workeee??? # Dependency checkers def printtool_dep(self, request, dep): """check for the existence of the given filetype instance (objectId, Version, extenstion) newer than the given timestamp""" pt = getToolByName(self,'rhaptos_print') depkey,deptype,depaction,depdetail = dep ptf_params = (depdetail['objectId'],depdetail['version'],depdetail['extension']) if pt.doesFileExist(*ptf_params): mod_date = DateTime(pt.getModificationDate(*ptf_params)) if mod_date > depdetail['newer']: return None return dep def url_dep(self, request, dep): """check for bits available from the given url, with a given mimetype and newer than the given timestamp""" pass def file_dep(self, request, dep): """check for the existence if a file at the given path, that is newer than the given timestamp""" depkey,deptype,depaction,depdetail = dep try: fstat = os.stat(depdetail['path']) except OSError: return dep if DateTime(fstat.st_ctime) > depdetail['newer']: return None return dep security.declareProtected(ManagePermission, 'manage_queue_tool') def manage_queue_tool(self, dictServers={}, dictProcessWindows={}, secDefaultProcessWindow=DEFAULT_PROCESS_TIME_WINDOW): """ Post creation configuration. See manage_configure_queue_tool.zpt """ self.dictServers = eval(dictServers) # string not dictionary returned self.secProcessWindows = eval(dictProcessWindows) # string not dictionary returned try: self.secDefaultProcessWindow = int(secDefaultProcessWindow) except ValueError: self.secDefaultProcessWindow = self.DEFAULT_PROCESS_TIME_WINDOW security.declareProtected(ManagePermission, 'add') def add(self, key, dictParams, callbackrequesthandler, priority=1): """ Add a request to the Pending Queue. """ # add() acquires mutex lock. caller is responsible for commiting the transaction. mutex.acquire() try: iListIndex = self.find(key, self.pendingRequests) if iListIndex is not None: # remove duplicate del self.pendingRequests[iListIndex] dictRequest = dictParams.copy() dictRequest['key'] = key dictRequest['requestHandler'] = callbackrequesthandler dictRequest['timeRequestMade'] = datetime.now() dictRequest['priority'] = priority # Walk list, insert immediately before first entry that is lower priority (higher value) for i,req in enumerate(self.pendingRequests): if req['priority'] > priority: self.pendingRequests.insert(i,req) break else: # didn't find one, tack on the end self.pendingRequests.append(dictRequest) # note that we explicitly and purposely do not commit the transaction here. # the caller is likely to be in a module/collection publish transaction. # when the caller's transaction commits, the QueueTool change (i.e. adding # a request) will also commit. thus, the request can not be processed until # after the publish transaction has completed (i.e. no race condition). finally: mutex.release() security.declarePrivate('find') def find(self, strKey, listRequests): """ Find key in persistent list, looking for duplicates. Return index if found. """ for i in range(0,len(listRequests)): if listRequests[i]['key'] == strKey: return i return None security.declarePrivate('gotRequest') def gotRequest(self): """ Determine if any requests are ready for processing. Return key of first one, if any, else False. Does dependency checking """ # requests must be pending ... if len(self.pendingRequests) == 0: return False # our host server can not be maxed out ... hostName = getfqdn() listHostProcess = [entry for entry in self.processingRequests if 'hostName' in entry and entry['hostName'] == hostName] if listHostProcess is not None: iProcessMax = hostName in self.dictServers and self.dictServers[hostName] or 1 if len(listHostProcess) >= iProcessMax: return False portal = self.portal_url.getPortalObject() # check requests for dependencies, bail out at first success for dictRequest in self.pendingRequests: key = dictRequest['key'] unmetDepends = self.checkDepends(dictRequest) if unmetDepends: if unmetDepends[2] == 'reenqueue': iListIndex = self.find(key, self.pendingRequests) portal.plone_log("... reenqueue request for key '%s'at index '%s' ..." % (dictRequest['key'],iListIndex)) if iListIndex is not None: portal.plone_log("... QueueTool.clockTick() has skipped request for key '%s' for failed dependency '%s' ..." % (dictRequest['key'],str(unmetDepends))) elif unmetDepends[2] == 'fail': self._email_depend_fail(dictRequest,unmetDepends) # removes req from pending as well else: portal.plone_log("... unknown failed dependency action for key '%s' ... skipping." % (dictRequest['key'],)) else: return key # fell through loop: no requests w/o unmet dependencies return False security.declarePrivate('getRequest') def getRequest(self, key=None): """ Get a request from the pending queue and put it in the processing queue or get an request already in the processing queue via key. """ # caller must acquire mutex lock. caller is responsible for commiting the transaction. if key is None: # get a request from the pending queue and put it in the processing queue reqKey = self.gotRequest() if reqKey: iListIndex = self.find(reqKey, self.pendingRequests) if iListIndex is not None: dictRequest = self.pendingRequests[iListIndex] del self.pendingRequests[iListIndex] self.processingRequests.append(dictRequest) return dictRequest else: return None else: return None else: # get an request already in the processing queue iListIndex = self.find(key, self.processingRequests) if iListIndex is not None: dictRequest = self.processingRequests[iListIndex] return dictRequest else: return None security.declarePrivate('start') def start(self, key): """ Start processing the queued request. """ # start() acquires mutex lock and is responsible for commiting the transaction. return security.declarePrivate('stop') def stop(self, key): """ Stop processing the queued request. """ # stop() acquires mutex lock and is responsible for commiting the transaction. mutex.acquire() try: self.removeRequest(key) # should only commit the above change transaction.commit() #import pdb; pdb.set_trace() #savepoint = transaction.savepoint() #for i in range(0,3): # try: # self.removeRequest(key) # # should only commit the above change # transaction.commit() # except Exception, e: # print "failure:", e # self.plone_log("... QueueTool.stop() failed to commit removing the reuest from the processing queue ... %s ..." % str(e)) # savepoint.rollback() # pass finally: mutex.release() security.declarePrivate('removeRequest') def removeRequest(self, key, fromList=None): """ Remove a request from the processing queue. """ # caller must acquire mutex lock. caller is responsible for commiting the transaction. if not fromList: fromList = self.processingRequests iListIndex = self.find(key, fromList) if iListIndex is not None: del fromList[iListIndex] security.declarePrivate('cleanupProcessing') def cleanupProcessing(self): """ Cleanup the processingRequests queue. Assume that processing entries that do not complete in a defined time window cored and did not call removeRequest(). """ # cleanupProcessing() acquires mutex lock and is responsible for commiting the transaction. mutex.acquire() try: timeNow = datetime.now() bChanged = False for request in self.processingRequests: timeProcessStarted = 'timeRequestProcessed' in request and request['timeRequestProcessed'] or None childPid = request.get('pid') reqHostName = request.get('hostName') myHostName = getfqdn() if childPid and reqHostName == myHostName: try: os.kill(childPid, 0) except OSError: if timeProcessStarted is not None: self._email_cleanup_message(request,0) bChanged = True if timeProcessStarted is not None: timeProcessing = timeNow - timeProcessStarted timeProcessingMax = timedelta(seconds=self._getProcessingMax(request)) if timeProcessing > timeProcessingMax: self._email_cleanup_message(request,timeProcessingMax) bChanged = True if bChanged: # commit the queue change(s) transaction.commit() finally: mutex.release() security.declarePrivate('_get_ProcessingMax') def _getProcessingMax(self,request): # Get process max timeout, checking for exact queue key, keyclass, and default, in that order. key = request['key'] key_class = key.split('_')[0] windows = self.secProcessWindows return windows.get(key, windows.get(key_class, self.secDefaultProcessWindow)) security.declarePrivate('_email_cleanup_message') def _email_cleanup_message(self,request,timeout): # email techsupport to notify that request has been forcibly been removed from the queue mailhost = self.MailHost portal = self.portal_url.getPortalObject() mfrom = portal.getProperty('email_from_address') mto = portal.getProperty('techsupport_email_address') or mfrom if mto: subject = "Request '%s' was forcibly removed from processing queue." % request['key'] messageText = "The request did not successfully complete within the required time window of %s. The request may have crashed, may be in an infinite loop, or may just be taking a really long time.\n\nThe server and pid referenced in the request need to be checked to see if the process is still active. If still active, the process may need to be killed. The QueueTool will no longer consider this an active request.\n\nThe complete request was:\n\n%s\n\n" % (timeout,str(request)) self._mailhost_send(messageText, mto, mfrom, subject) self.removeRequest(request['key']) self.processingRequests._p_changed = 1 security.declarePrivate('_mailhost_send') def _mailhost_send(self, messageText, mto, mfrom, subject): """attempt to send mail: log if fail""" portal = self.portal_url.getPortalObject() try: mailhost.send(messageText, mto, mfrom, subject) except: portal.plone_log("Can't send email:\n\nSubject:%s\n\n%s" % (subject,messageText)) security.declarePublic('ping') def ping(self): """Test method for ClockServer. Logs when called.""" self.plone_log("... QueueTool.ping() has been called ...") security.declarePublic('clockTick') def clockTick(self): """ Called on a clock event every N seconds. If work is available, a child zopectl process is launched. """ # clockTick() acquires mutex lock and is responsible for commiting the transaction. mutex.acquire() portal = self.portal_url.getPortalObject() try: if self.gotRequest(): dictRequest = self.getRequest() if dictRequest: transaction.commit() # make list change visible to child instance - other way was race condition self.launchRequest(dictRequest) portal.plone_log("... QueueTool.clockTick() has spawned a child process for key '%s' ..." % dictRequest['key']) finally: mutex.release() # removed "expired" request in processing self.cleanupProcessing() security.declarePublic('checkDepends') def checkDepends(self, request): """Check the request dependecies, in declared order. Return first unmet one. On success of all (or no declared dependencies) return 'None'""" for dep in request.get('depends',[]): try: checker = getattr(self,"%s_dep" % dep[1]) if checker(request,dep): return dep except AttributeError: self.portal_url.getPortalObject().plone_log('Skipping badly declared dependency: %s' % (dep,)) pass return None security.declarePrivate('_email_depend_fail') def _email_depend_fail(self,request,depend): # email techsupport to notify that request has been forcibly been removed from the queue mailhost = self.MailHost portal = self.portal_url.getPortalObject() mfrom = portal.getProperty('email_from_address') mto = portal.getProperty('techsupport_email_address') or mfrom if mto: subject = "Request '%s' was removed from pending queue. (dependency)" % request['key'] messageText = "The request declared a dependency that was not available at the time of processing. The QueueTool will no longer consider this an active request.\n\nThe failed dependency was:\n\n%s\n\nThe complete request was:\n\n%s\n\n" % (str(depend),str(request)) self._mailhost_send(messageText, mto, mfrom, subject) self.removeRequest(request['key'], self.pendingRequests) self.pendingRequests._p_changed = 1 security.declarePrivate('launchRequest') def launchRequest(self, dictRequest): """ Launch the input request. """ # caller must acquire mutex lock. caller is responsible for commiting the transaction. portal = self.portal_url.getPortalObject() if dictRequest is not None: key = dictRequest['key'] or None iListIndex = key is not None and self.find(key, self.processingRequests) if iListIndex is not None: # request is in the processing queue if 'timeRequestProcessed' not in dictRequest: # request has not already been started dictRequest['timeRequestProcessed'] = datetime.now() # dictRequest['ip'] = getipaddr() dictRequest['hostName'] = getfqdn() # dictRequest['pid'] = -1 key = dictRequest['key'] serverURL = dictRequest.get('serverURL',self.REQUEST['SERVER_URL']) callbackrequesthandler = dictRequest['requestHandler'] cmd = os.path.join(INSTANCE_HOME, 'bin', 'zopectl') callback = os.path.join(INSTANCE_HOME, 'Products', callbackrequesthandler) #pid = os.spawnl(os.P_NOWAIT, cmd, 'zopectl', 'run', callback, key) # call newer, better python API which allows for the stdout and stderr to be captured import subprocess f1 = open('/tmp/%s.stdout' % key, 'w') f2 = open('/tmp/%s.stderr' % key, 'w') pid = subprocess.Popen([cmd, "run", callback, key, serverURL], stdout=f1, stderr=f2).pid # child will call start() and stop() dictRequest['pid'] = pid self.processingRequests._p_changed = 1 security.declareProtected(ManagePermission, 'manage_tick') def manage_tick(self): """ZMI-useful variant of 'clockTick'. Goes back to overview page of tool. From anywhere else, use 'clockTick' instead. """ self.clockTick() self.REQUEST.RESPONSE.redirect('manage_overview') return security.declareProtected(ManagePermission, 'manage_cut_in_line') def manage_cut_in_line(self, key=""): """Move request to the head of the pendingRequests queue. """ # manage_cut_in_line() acquires mutex lock and is responsible for commiting the transaction. mutex.acquire() try: portal = self.portal_url.getPortalObject() iListIndex = self.find(key, self.pendingRequests) if iListIndex is not None and iListIndex > 0: dictRequest = self.pendingRequests[iListIndex] or None if dictRequest is not None: del self.pendingRequests[iListIndex] self.pendingRequests.insert(0, dictRequest) transaction.commit() portal.plone_log("... QueueTool.manage_cut_in_line: '%s' is cutting in line ..." % key) self.REQUEST.RESPONSE.redirect('manage_overview') finally: mutex.release() return security.declareProtected(ManagePermission, 'manage_remove') def manage_remove(self, key="", queue="pending"): """Remove request from a queue list """ # manage_cut_in_line() acquires mutex lock and is responsible for commiting the transaction. mutex.acquire() if queue == "pending": fromList = self.pendingRequests elif queue == "processing": fromList = self.processingRequests else: return try: self.removeRequest(key,fromList) portal = self.portal_url.getPortalObject() portal.plone_log("... QueueTool.manage_remove: '%s' has been removed ..." % key) self.REQUEST.RESPONSE.redirect('manage_overview') finally: mutex.release() return security.declareProtected(ManagePermission, 'getProcessingRequests') def getProcessingRequests(self): """Get process requests.""" return self.processingRequests security.declareProtected(ManagePermission, 'getPendingRequests') def getPendingRequests(self): """Get pending requests.""" return self.pendingRequests security.declarePublic('pending_count') def pending_count(self): """Return number of pending entries in the queue. Useful for monitoring.""" return len(self.getPendingRequests()) security.declarePublic('processing_count') def processing_count(self): """Return number of pending entries in the queue. Useful for monitoring.""" return len(self.getProcessingRequests())
class ProcessManager(Products.AlphaFlow.rolecache.RoleCache, UniqueObject, ActionProviderBase, OFS.Folder.Folder): """A process management object.""" zope.interface.implements(Products.AlphaFlow.interfaces.IProcessManager) id = 'workflow_manager' meta_type = "AlphaFlow Process Manager" plone_tool = 1 manage_options = ( dict(label='Overview', action='manage_overview'), dict(label='Processes', action='manage_definitions'), dict(label='Instances', action='manage_instances'), dict(label='Tools', action='manage_tools') ) + OFS.Folder.Folder.manage_options + \ ActionProviderBase.manage_options manage_options = [ opt for opt in manage_options if opt['label'] not in ['Contents', 'View', 'Properties'] ] security = AccessControl.ClassSecurityInfo() # Rolecache security settings security.declarePrivate('updateWorkItemCache') security.declarePrivate('updateCacheByContent') security.declarePrivate('updateCacheByInstance') security.declarePrivate('getDynamicRolesForWorkItem') security.declarePrivate('getDynamicRolesForInstance') security.declarePrivate('getDynamicRolesForContent') security.declarePrivate('listRelevantUsersForWorkItem') security.declarePrivate('listRelevantUsersForInstance') security.declarePrivate('listRelevantUsersForContent') security.declarePrivate('_get_role_cache') security.declarePrivate('_get_role_cache_entry') security.declarePrivate('_build_workitem_cache') security.declarePrivate('_aggregate_role_cache') processes = None instances = None def __init__(self, *args, **kwargs): ProcessManager.inheritedAttribute("__init__")(self, *args, **kwargs) self.portal_process_refs = BTrees.OOBTree.OOTreeSet() ######################### # IProcessManager security.declarePublic("initProcess") def initProcess(self, definition, obj): """Create a new process instance for a content object.""" # We need to handle the security for this methods ourselves. The user # has to have INIT_PROCESS on the content object. user = AccessControl.getSecurityManager().getUser() if not user.has_permission(Products.AlphaFlow.config.INIT_PROCESS, obj): raise zExceptions.Unauthorized( "initProcess", obj, Products.AlphaFlow.config.INIT_PROCESS) id = Products.AlphaFlow.utils.generateUniqueId(definition.getId()) instance = zope.component.getMultiAdapter( (definition, obj, id), Products.AlphaFlow.interfaces.ILifeCycleObject) self.instances._setObject(id, instance) return self.instances[id] security.declareProtected(Products.AlphaFlow.config.MANAGE_WORKFLOW, 'listProcessDefinitions') def listProcessDefinitions(self): """Returns all processes defined in the portal.""" rc = getToolByName(self, "reference_catalog") for uid in self.portal_process_refs: obj = rc.lookupObject(uid) if obj is None: continue yield obj security.declareProtected(Products.AlphaFlow.config.MANAGE_WORKFLOW, 'getStatistics') def getStatistics(self): """Return a dictionary with various statistical information""" cat = getToolByName(self, "workflow_catalog") query = dict(meta_type='Instance') all_count = len(cat(**query)) query['state'] = 'active' active_count = len(cat(**query)) query['state'] = 'failed' failed_count = len(cat(**query)) result = {} result['all_count'] = all_count result['active_count'] = active_count result['failed_count'] = failed_count return result security.declareProtected(Products.AlphaFlow.config.MANAGE_WORKFLOW, 'listInstances') def listInstances(self, **search): """Return a list of instance objects found by the specified search.""" query = dict(meta_type='Instance', sort_on='modified', sort_order='descending', **search) wc = getToolByName(self, 'workflow_catalog') instances = wc(query) instances = [x.getObject() for x in instances] instances = [x for x in instances if x is not None] return instances security.declareProtected(Products.AlphaFlow.config.MANAGE_WORKFLOW, "replaceInstances") def replaceInstances(self, old_version, new_process=None): """Terminate instances of old process version and restart with new process. XXX This method is untested. """ if new_process is None: new_process = old_version.aq_inner.aq_parent.current() old_instances = self.listInstances(process_uid=old_version.UID()) new_instances = [] for instance in old_instances: # First terminate the old process ... obj = instance.getContentObject() Products.AlphaFlow.interfaces.ILifeCycleController( instance).terminate( "Replaced with an instance of process %s." % new_process.getId()) # ... then create a new process. obj.assignProcess(new_process) new_instance = obj.getInstance() Products.AlphaFlow.interfaces.ILifeCycleController( new_instance).start( "Replaced an instance of process version %s." % old_version.UID()) new_instances.append(new_instance) return new_instances security.declarePrivate('cleanUpInstances') def cleanUpInstances(self): """Removes garbage process instances.""" orphans = [] for id, instance in self.instances.objectItems(): if (instance.state == "terminated" or instance.getContentObject() is None): orphans.append(id) continue if instance.getProcess() is None: orphans.append(id) self.instances.manage_delObjects(orphans) wc = getToolByName(self, 'workflow_catalog') wc.refreshCatalog(clear=1) security.declareProtected(Products.AlphaFlow.config.WORK_WITH_PROCESS, "queryWorkItems") def queryWorkItems(self, user): """Return list of work items for the given user.""" result = [] wc = getToolByName(self, "workflow_catalog") rc = getToolByName(self, "reference_catalog") user = AccessControl.getSecurityManager().getUser().getUserName() #XXX for wi in wc(listRelevantUsers=user, state="active", showInWorkList=True): content = rc.lookupObject(wi.getContentObjectUID) if content is None: continue result.append( dict(wi=wi, contentTitle=content.Title(), getViewUrl=wi.getViewUrl, getActivityTitleOrId=wi.getActivityTitleOrId, getShortInfo=wi.getShortInfo)) return result security.declarePublic("queryWorkItemsForCurrentUser") def queryWorkItemsForCurrentUser(self): """Return list of work items for the current user.""" user = AccessControl.getSecurityManager().getUser() return self.queryWorkItems(user) security.declareProtected(Products.AlphaFlow.config.MANAGE_WORKFLOW, 'pingCronItems') def pingCronItems(self): """Send a trigger to all time-dependent objects.""" # - We *lock* to avoid long running pings to run in # parallel doing the same work and ending up with conflicts. # - We lock *non-blocking* and return a message when we could not # acquire the lock to avoid subsequent pings to pile up. locked = ping_lock.acquire(False) if not locked: return ("AlphaFlow is already processing another ping. " "This ping was ignored.") try: zope.event.notify(Products.AlphaFlow.interfaces.CronPing(self)) finally: ping_lock.release() security.declarePrivate('restartHelper') def restartHelper(self, process, activity): """Restarts all work items of the given process and activity that are currently fallen out. """ wc = getToolByName(self, "workflow_catalog") restarted = 0 candidates = wc(process_uid=process, activity_id=activity, state="failed") for candidate in candidates: workitem = candidate.getObject() if workitem is None: continue # Restart it controller = \ Products.AlphaFlow.interfaces.ILifeCycleController(workitem) controller.reset("Reset by restart helper.") controller.start("Start by restart helper.") restarted += 1 if Products.AlphaFlow.config.ENABLE_ZODB_COMMITS: transaction.commit() return restarted security.declarePrivate('bulkDropin') def bulkDropin(self): """Recovers all instances that are currently fallen out.""" candidates = list(self.listInstances(state='failed')) dropped_in = 0 ignored = 0 for candidate in candidates: controller = \ Products.AlphaFlow.interfaces.ILifeCycleController(candidate) if controller.state != 'failed': # XXX For some reason I sometimes received instances multiple # times from the catalog. continue try: controller.recover("Bulk dropin via ZMI.") except: # XXX This happens when a work item is still failed. # This should use a dedicated exception. ignored += 1 else: dropped_in += 1 return dropped_in, ignored security.declarePrivate('doSanityCheck') def doSanityCheck(self): """Perform a sanity check and cleanup.""" issues = [] def _clean_object(o, path): local_roles = o.__ac_local_roles__ try: delattr(o.aq_base, '__ac_local_roles__') except: # don't care pass # IAlphaflowed have a class attribute __ac_local_roles__, which is # computed, we update this now: for user, roles in local_roles.items(): if 'Assignee' in roles: del roles[roles.index('Assignee')] local_roles[user] = roles o.__ac_local_roles__.update(local_roles) def _check_double_refs(o, path): # 1. check instance to content object mapping. # geee, this is going to be expensive # a. get a content object (c1) # b. get its instance # c. get instance's content object (c2) # d. see if c1 is c2 c1 = o instance = c1.getInstance() if instance is None: return c2 = instance.getContentObject() if c1.aq_base is not c2.aq_base: issues.append("Not sane: %r (%r, %r)" % (instance, c1, c2)) c1.alf_clearInstances() def _check(o, path): if Products.AlphaFlow.interfaces.IAlphaFlowed.providedBy(o): _check_double_refs(o, path) _clean_object(o, path) portal = getToolByName(self, 'portal_url').getPortalObject() cat = getToolByName(self, 'portal_catalog') cat.ZopeFindAndApply(portal, search_sub=True, apply_func=_check) return issues
class LinkMapTool(UniqueObject, BTreeFolder2): __implements__ = (ILinkMapTool) id = 'portal_linkmap' meta_type = 'LinkMap Tool' security = AccessControl.ClassSecurityInfo() manage_options=(( {'label':'Overview', 'action':'manage_overview'}, { 'label' : 'Catalog', 'action' : 'manage_catalog'}, ) + BTreeFolder2.manage_options ) ## ZMI methods security.declareProtected(ManagePortal, 'manage_overview') manage_overview = PageTemplateFile('zpt/explainLinkMapTool', globals() ) security.declareProtected(ManagePortal, 'manage_catalog') def manage_catalog(self, REQUEST=None): """Access to the ZCatalog""" if REQUEST is not None: REQUEST['RESPONSE'].redirect(self.catalog.absolute_url()+'/manage_catalogView') def __init__(self, *args, **kw): BTreeFolder2.__init__(self, *args, **kw) self._create_catalog() self._linkrange = (1,3) # currently unused; just a marker security.declarePrivate("_create_catalog") def _create_catalog(self): """Creates the ZCatalog instance for searching links""" # self.catalog = ZCatalog('catalog').__of__(self) self.catalog = ZCatalog('catalog') self.catalog.addIndex('source', 'FieldIndex') self.catalog.addIndex('strength', 'FieldIndex') self.catalog.addColumn('target') self.catalog.addColumn('category') self.catalog.addColumn('strength') self.catalog.addColumn('title') self._p_changed=1 security.declareProtected('LinkMap: Add Link', 'addLink') def addLink(self, source, target, title, category, strength, context=None): """Create a link""" id = self.generateId() self._setObject(id, ExtendedLink(id)) ob = getattr(self, id) ob.edit(source, target, title, category, strength) self.catalog.catalog_object(ob) security.declarePublic('searchLinks') def searchLinks(self, source=None, context=None): """Return all links for a particular source and context""" # FIXME: do we have to worry about 'latest' translation? results = self.catalog(source=source, sort_on='strength', sort_order='descending') return results def deleteLinks(self,objectId,version=None): """Delete all links for which the objectId is either source or target""" # This code assumes a ZRepository instance at /content myhost = urlparse.urlparse(self.REQUEST.SERVER_URL)[1] mypath = '/'.join(filter(None,['/content',objectId,version])) mylinks = [] # FIXME: once a better storage and search interface exists, we can use that for link in self.objectValues('Extended Link'): #Check source tokens = urlparse.urlparse(link.source) if (tokens[1] or myhost) == myhost and tokens[2].startswith(mypath): mylinks.append(link.id) else: #Check target tokens = urlparse.urlparse(link.target) if (tokens[1] or myhost) == myhost and tokens[2].startswith(mypath): mylinks.append(link.id) # Blow'em away! self.manage_delObjects(mylinks) security.setPermissionDefault('LinkMap: Add Link', ('Manager', 'Owner',))
class HTMLTextarea(WidgetBase, OFS.SimpleItem.Item, Persistent, Acquisition.Implicit, AccessControl.Role.RoleManager): """HTMLWidget for Textareas""" __implements__ = IWidget security = AccessControl.ClassSecurityInfo() security.declareProtected('View management screens', 'manage_properties') security = AccessControl.ClassSecurityInfo() meta_type = 'HTML Textarea' css_class = 'FormTextarea' user_css = '' aliases = [] colspan = 0 wrapping = 'virtual' manage_properties = HTMLFile('dtml/HTMLTextareaEdit', globals()) manage_options = ( { 'label': 'Properties', 'action': 'manage_properties' }, { 'label': 'Security', 'action': 'manage_access' }, ) ################## Some zopish management stuff def __init__(self, id, REQUEST=None): self.id = id self.__version__ = __version__ if REQUEST is not None: self.changeProperties(REQUEST) def __setstate__(self, state): HTMLTextarea.inheritedAttribute('__setstate__')(self, state) self._update_cache() security.declareProtected('Change Formulon instances', 'manage_edit') def manage_edit(self, REQUEST=None): """Sets the new properties.""" if REQUEST is not None: self.changeProperties(REQUEST) return MessageDialog(title='Edited', message="Properties for %s changed." % self.id, action='./manage_properties') security.declareProtected('Change Formulon instances', 'changeProperties') def changeProperties(self, REQUEST, encoding='iso-8859-1'): if REQUEST is None: return # Update the base properties self.changeBaseProperties(REQUEST, encoding) self.title = unicode(REQUEST.title, encoding) self.cols = REQUEST.cols self.rows = REQUEST.rows self.colspan = REQUEST.colspan self.aliases = REQUEST.aliases self.user_css = unicode(REQUEST.user_css, encoding) self.column = REQUEST.column if self.column < 1: self.column = 1 self.row = REQUEST.row if self.row < 1: self.row = 1 if REQUEST.has_key("wrapping"): self.wrapping = REQUEST["wrapping"] # Update TALES Methods tales = ['default', 'regexp', 'hint'] for x in tales: expression = REQUEST.get(x) if expression is None: continue setattr(self, x, TALESMethod(unicode(expression, encoding))) # cache the compiled regular expression: self._update_cache() def _update_cache(self): """Compiles a volatile attribute for caching the regex.""" self._v_regex = self._compile_regex() def _compile_regex(self, force=False): """Compiles the regex attribute. If force is False regex is only compiled if it starts with 'string:'. (This is for the cache.) """ if force or self.regexp.getExpression().startswith('string:'): try: return re.compile(self.callMethod(self.regexp), re.DOTALL) except AttributeError: # happens creating new widget because callMethod needs aq_parent return None #################### The widget specific code security.declareProtected('View', 'render') def render(self, parent, tabindex=None): doc = parent.ownerDocument node = parent.appendChild(doc.createElement("textarea")) if tabindex is not None: node.setAttribute("tabindex", str(tabindex)) if self.user_css: node.setAttribute("class", self.user_css) else: node.setAttribute("class", self.css_class) node.setAttribute("name", self.id + "_value") node.setAttribute("cols", str(self.cols)) node.setAttribute("rows", str(self.rows)) node.setAttribute("wrapping", self.wrapping) node.setAttribute("onChange", "reportChange(this)") node.appendChild(doc.createTextNode(self.value())) return node security.declareProtected('View', 'validate') def validate(self, against=None): __traceback_info__ = self.getId(), against against = against["value"] if isinstance(against, str): against = unicode(against, self.site_encoding) regex = getattr(self, '_v_regex', None) if regex is None: regex = self._compile_regex() if regex is not None: # starts with 'string:' so save it self._v_regex = regex else: regex = self._compile_regex(force=True) if not len(regex.findall(against)): raise ValidationError(self.callMethod(self.hint)) return against security.declareProtected('View', 'height') def height(self): height = self.rows - 2 if height < 1: height = 1 return height security.declareProtected('View', 'value') def value(self): try: a = self.REQUEST['__source_data__'][self.id] if a is None: a = '' return a except KeyError: pass try: return self.REQUEST[self.id] except KeyError: pass return self.callMethod(self.default) security.declareProtected('View', 'getValue') getValue = value
class ControllerValidator(BaseClass, ControllerBase): """Web-callable scripts written in a safe subset of Python. The function may include standard python code, so long as it does not attempt to use the "exec" statement or certain restricted builtins. """ meta_type = 'Controller Validator' implements(IControllerValidator) manage_options = ( {'label':'Edit', 'action':'ZPythonScriptHTML_editForm', 'help': ('PythonScripts', 'PythonScript_edit.stx')}, ) + BindingsUI.manage_options + ( {'label':'Test', 'action':'ZScriptHTML_tryForm', 'help': ('PythonScripts', 'PythonScript_test.stx')}, # {'label':'Actions','action':'manage_formActionsForm'}, {'label':'Proxy', 'action':'manage_proxyForm', 'help': ('OFSP','DTML-DocumentOrMethod_Proxy.stx')}, ) + Historical.manage_options + SimpleItem.manage_options + \ Cacheable.manage_options is_validator = 1 security = AccessControl.ClassSecurityInfo() security.declareObjectProtected('View') security.declareProtected('View', '__call__') security.declareProtected('View management screens', 'ZPythonScriptHTML_editForm', 'manage_main', 'read', 'ZScriptHTML_tryForm', 'PrincipiaSearchSource', 'document_src', 'params', 'body') security.declareProtected('Change Python Scripts', 'ZPythonScriptHTML_editAction', 'ZPythonScript_setTitle', 'ZPythonScript_edit', 'ZPythonScriptHTML_upload', 'ZPythonScriptHTML_changePrefs') def __init__(self, *args, **kwargs): # self.actions = FormActionContainer() return ControllerValidator.inheritedAttribute('__init__')(self, *args, **kwargs) def __call__(self, *args, **kwargs): result = ControllerValidator.inheritedAttribute('__call__')(self, *args, **kwargs) # if getattr(result, '__class__', None) == ControllerState and not result._isValidating(): # return self.getNext(result, self.REQUEST) return result def _getState(self): return getToolByName(self, 'portal_form_controller').getState(self, is_validator=1) def _notifyOfCopyTo(self, container, op=0): # BaseClass.inheritedAttribute('notifyOfCopyTo')(self, container, op) self._base_notifyOfCopyTo(container, op) def manage_afterAdd(self, object, container): BaseClass.inheritedAttribute('manage_afterAdd')(self, object, container) self._base_manage_afterAdd(object, container) def manage_afterClone(self, object): BaseClass.inheritedAttribute('manage_afterClone')(self, object) self._base_manage_afterClone(object)
class HTMLLabel(WidgetBase, OFS.SimpleItem.Item, Persistent, Acquisition.Implicit, AccessControl.Role.RoleManager): """HTMLWidget for Labels""" __implements__ = IWidget security = AccessControl.ClassSecurityInfo() meta_type = 'HTML Label' css_class = 'FormLabel' user_css = "" colspan = -1 aliases = [] security.declareProtected('View management screens', 'manage_properties') manage_properties = HTMLFile('dtml/HTMLLabelEdit', globals()) manage_options = ( { 'label': 'Properties', 'action': 'manage_properties' }, { 'label': 'Security', 'action': 'manage_access' }, ) ########### Zopish management stuff def __init__(self, id, REQUEST=None): self.id = id self.__version__ = __version__ if REQUEST is not None: self.changeProperties(REQUEST) security.declareProtected('Change Formulon instances', 'manage_edit') def manage_edit(self, REQUEST=None): """ set the new properties """ if REQUEST is not None: self.changeProperties(REQUEST) return MessageDialog(title='Edited', message="Properties for %s changed." % (self.id, ), action='./manage_properties') security.declareProtected('Change Formulon instances', 'changeProperties') def changeProperties(self, REQUEST=None, encoding='iso-8859-1'): if REQUEST is not None: # Update the base properties self.changeBaseProperties(REQUEST, encoding) self.value = unicode(REQUEST.value, encoding) self.title = self.value self.column = REQUEST.column self.user_css = REQUEST.user_css if self.column < 1: self.column = 1 self.row = REQUEST.row if self.row < 1: self.row = 1 self.colspan = REQUEST.colspan def title_or_id(self): assert isinstance(self.value, unicode) return self.value security.declareProtected('View', 'validate') def validate(self, against): return against security.declareProtected('View', 'height') def height(self): return 1 security.declareProtected('View', 'render') def render(self, parent, tabindex=None): node = parent.appendChild(parent.ownerDocument.createElement("span")) if self.user_css: node.setAttribute("class", self.user_css) else: node.setAttribute("class", self.css_class) node.appendChild(parent.ownerDocument.createTextNode(self.value)) return node security.declareProtected('View', 'getValue') def getValue(self): return None
class PDFLatexTool(UniqueObject, SimpleItem): __implements__ = (IPDFLatexTool) id = 'portal_pdflatex' meta_type = 'PDFLatex Tool' security = AccessControl.ClassSecurityInfo() manage_options=(( {'label':'Overview', 'action':'manage_overview'}, ) + SimpleItem.manage_options ) ## ZMI methods security.declareProtected(ManagePortal, 'manage_overview') manage_overview = PageTemplateFile('zpt/explainPDFLatexTool', globals() ) def convertObjectToPDF(self, object, style, **params): """ Convert the given object to a PDF file, possible using its subjects as necessary """ fs_tool = getToolByName(self, 'portal_fsimport') tempdir = tempfile.mkdtemp() fs_tool.exportToFS(tempdir, object) export = object.module_export_cnxml() if type(export) is unicode: export = export.encode('utf-8') export_file = open(os.path.join(tempdir, object.getId(), 'export.cnxml'), 'wb') export_file.write(export) export_file.close() zLOG.LOG('RhaptosPDFLatexTool',0, "module printing convertObjectToPDF") pdf = self.convertFSDirToPDF(os.path.join(tempdir, object.getId()), 'export.cnxml', style, **params) shutil.rmtree(tempdir) return pdf def convertFSDirToPDF(self, path, filename, style = '', **params): """ Produce a PDF from a directory on the filesystem """ # canonical name for module_export_template file os.rename(os.path.join(path, filename), os.path.join(path, 'module.mxt')) ## this might want to be refactored with Collection.triggerPrint at some point # look up makefile params on print tool # we don't visit a remote server, so this is not quite correct: we assume a local AsyncPrint for config # if not available, use Makefile-encoded values printtool = getToolByName(self,'rhaptos_print') portal = self.portal_url.getPortalObject() host = printtool.getHost() if host.startswith('http://'): host = host[7:] project_name = portal.Title() project_short_name = project_name if project_name == 'OpenStax CNX': project_name = 'The OpenStax CNX Project' env = os.environ env['HOST'] = host env['PROJECT_NAME'] = project_name env['PROJECT_SHORT_NAME'] = project_short_name env['EPUB_DIR'] = printtool.getEpubDir() env['PRINT_STYLE'] = style script_location = 'SCRIPTSDIR' in env and env['SCRIPTSDIR'] or '.' # Should be setup inside zope.conf on Zope client if not env.has_key('PRINT_DIR'): # BBB makefile = printtool.getMakefile() makefiledir = os.path.dirname(makefile) env['PRINT_DIR'] = makefiledir else: makefiledir = env['PRINT_DIR'] makefile = "%s/module_print.mak" % makefiledir # copy makefile into tempdir shutil.copy(makefile, path) # call makefile f = open(os.path.join(path, 'make.log'), 'w', 0) now = datetime.now() zLOG.LOG('RhaptosPDFLatexTool',0, "module printing starts in tempdir %s at %s" % (path, now) ) f.write(now.isoformat()+"\n") subprocess.call(['sh', '%s/module2pdf.sh' % script_location, 'module.pdf'], cwd=path, env=env, stdout=f, stderr=subprocess.STDOUT) f.write(now.isoformat()+"\n") f.close() # evaluate result foundPDF = True try: # read the PDF (if it doesn't exist, fail) f = open(os.path.join(path, 'module.pdf')) pdf = f.read() f.close() except IOError: foundPDF = False #log("-----> could not print %s; pdf probably missing" % self.collectionId) raise PDFLatexError, "No output file" # check for good PDF here. if not, do we set some property on callback object? # all our bad PDFs seem to have the problem of not ending on %%EOF so we check that # it's a nice light-weight check if not (foundPDF and ( pdf.endswith("%%EOF\n") or pdf.endswith("%%EOF\n\n") ) ): raise PDFLatexError, "Bad output file" zLOG.LOG('RhaptosPDFLatexTool',0, "module printing finished successfully in tempdir %s at %s in %s" % (path, now, datetime.now()-now) ) return pdf
class ControllerPythonScript(BaseClass, ControllerBase): """Web-callable scripts written in a safe subset of Python. The function may include standard python code, so long as it does not attempt to use the "exec" statement or certain restricted builtins. """ meta_type = 'Controller Python Script' manage_options = ( {'label':'Edit', 'action':'ZPythonScriptHTML_editForm', 'help': ('PythonScripts', 'PythonScript_edit.stx')}, ) + BindingsUI.manage_options + ( {'label':'Test', 'action':'ZScriptHTML_tryForm', 'help': ('PythonScripts', 'PythonScript_test.stx')}, {'label':'Validation', 'action':'manage_formValidatorsForm'}, {'label':'Actions', 'action':'manage_formActionsForm'}, {'label':'Proxy', 'action':'manage_proxyForm', 'help': ('OFSP','DTML-DocumentOrMethod_Proxy.stx')}, ) + Historical.manage_options + SimpleItem.manage_options + \ Cacheable.manage_options is_validator = 0 security = AccessControl.ClassSecurityInfo() security.declareObjectProtected('View') security.declareProtected('View', '__call__') security.declareProtected('View management screens', 'ZPythonScriptHTML_editForm', 'manage_main', 'read', 'ZScriptHTML_tryForm', 'PrincipiaSearchSource', 'document_src', 'params', 'body') security.declareProtected('Change Python Scripts', 'ZPythonScriptHTML_editAction', 'ZPythonScript_setTitle', 'ZPythonScript_edit', 'ZPythonScriptHTML_upload', 'ZPythonScriptHTML_changePrefs') def __init__(self, *args, **kwargs): self.validators = FormValidatorContainer() self.actions = FormActionContainer() return ControllerPythonScript.inheritedAttribute('__init__')(self, *args, **kwargs) def __call__(self, *args, **kwargs): REQUEST = self.REQUEST controller = getToolByName(self, 'portal_form_controller') controller_state = controller.getState(self, is_validator=0) controller_state = self.getButton(controller_state, REQUEST) validators = self.getValidators(controller_state, REQUEST).getValidators() # put all arguments into a dict c = self.func_code param_names = c.co_varnames[:c.co_argcount] argdict = {} index = 0 # grab the names for positional arguments out of the function code for a in args: argdict[param_names[index]] = a index += 1 argdict.update(kwargs) controller_state = controller.validate(controller_state, REQUEST, validators, argdict) if controller_state.getStatus() == 'success': result = ControllerPythonScript.inheritedAttribute('__call__')( self, *args, **kwargs) if getattr(result, '__class__', None) == ControllerState and not result._isValidating(): return self.getNext(result, self.REQUEST) return result else: return self.getNext(controller_state, self.REQUEST) def _getState(self): return getToolByName(self, 'portal_form_controller').getState(self, is_validator=0) def _notifyOfCopyTo(self, container, op=0): # BaseClass.inheritedAttribute('notifyOfCopyTo')(self, container, op) self._base_notifyOfCopyTo(container, op) def manage_afterAdd(self, object, container): BaseClass.inheritedAttribute('manage_afterAdd')(self, object, container) self._base_manage_afterAdd(object, container) def manage_afterClone(self, object): BaseClass.inheritedAttribute('manage_afterClone')(self, object) self._base_manage_afterClone(object)
class FavoritesLens(ContentSelectionLens): """A restricted sort of lens, for holding personal bookmarks, etc.""" archetype_name = "Favorites Lens" content_icon = 'star.png' security = AccessControl.ClassSecurityInfo() schema = schema actions = ( { 'id': 'view', 'title': 'View', 'action': Expression('string:${object_url}/lens_view'), 'permissions': (View, ), 'visible': 0 }, { 'id': 'contents', 'title': 'Edit lens contents', 'action': Expression('string:${object_url}/favorite_content_view'), 'permissions': (ModifyPortalContent, ) }, { 'id': 'edit', 'title': 'Edit lens properties', 'action': Expression('string:${object_url}/lens_edit'), 'permissions': (ModifyPortalContent, ) }, { 'id': 'preview', 'title': 'Preview lens', 'action': Expression('string:${object_url}/lens_preview'), 'permissions': (ModifyPortalContent, ) }, { 'id': 'metadata', # turn off 'properties' tab 'visible': 0 }, ) aliases = { '(Default)': '', 'edit': 'lens_edit', 'gethtml': '', 'index.html': '', 'properties': '', 'sharing': '', 'view': 'lens_view', 'contents': 'favorite_content_view', } security.declarePublic('workflowStateEditable') def workflowStateEditable(self): """Return a boolean whether or not the user may edit the workflow state of this lens.""" return False # not very smart at the moment, but accurate. security.declarePublic('suggestId') def suggestId(self): """Return the suggested id of this lens; used particularly for portal_factory create.""" return FAVORITE_ID security.declareProtected(View, 'notifyRead') def notifyRead(self, collectionId, moduleId): """The user read 'moduleId' in 'collectionId'. If we have that collection, mark that module as the "last read". No return value. """ if collectionId: entry = getattr(self, collectionId, None) if not entry is None: if getattr( entry, 'lastRead', None) != moduleId: # avoid touching object if possible entry.lastRead = moduleId security.declareProtected(View, 'getLastRead') def getLastRead(self, collectionId): """Return the 'moduleId' in 'collectionId' (if we have that collection) "last read" by the owner. If no collection by that id, return None. """ if collectionId: entry = getattr(self, collectionId, None) if not entry is None: moduleId = getattr(entry, 'lastRead', None) return moduleId
class BugTrackingTool(UniqueObject, SimpleItem, PropertyManager): implements(IBugTrackingTool) id = 'portal_bugtracking' meta_type = 'BugTracking Tool' security = AccessControl.ClassSecurityInfo() # ZMI methods manage_options = (( { 'label': 'Overview', 'action': 'manage_overview' }, { 'label': 'Properties', 'action': 'manage_propertiesForm' }, ) + SimpleItem.manage_options) #def __init__(self): # self.trac_dir = '/opt/trac/tracdev' ## ZMI methods security.declareProtected(ManagePortal, 'manage_overview') manage_overview = PageTemplateFile('zpt/explainBugTrackingTool', globals()) # IBugTrackingTool Interface fulfillment def submitBug(self, summary, email, name=None, contentUrl=None, bugType='Unknown', severity='minor', description=None): """ Submit a bug to the trac system """ if not hasattr(self, 'trac_xmlrpc'): raise ValueError, "Trac_xmlrpc is not configured" trac_xmlrpc = getattr(self, 'trac_xmlrpc') # Set some default values for properties that can be over_written if hasattr(self, 'trac_type'): trac_type = getattr(self, 'trac_type') else: trac_type = 'defect' if hasattr(self, 'trac_state'): trac_state = getattr(self, 'trac_state') else: trac_state = 'new' server = xmlrpclib.ServerProxy(trac_xmlrpc) attributes = {} attributes['version'] = 'Live' attributes['type'] = trac_type attributes['status'] = trac_state attributes['summary'] = summary attributes['keywords'] = 'ReportABug' if name and email: attributes['reporter'] = '%s <%s>' % (name, email) elif name: attributes['reporter'] = name elif email: attributes['reporter'] = email if description: attributes['description'] = description attributes['severity'] = severity if contentUrl: attributes['contentobj'] = contentUrl attributes['area'] = bugType # Create ticket through XML-RPC. The "True" is for the "notify" parameter. newid = server.ticket.create(summary, description, attributes, True) return (newid)
class ModuleDBTool(UniqueObject, SimpleItem): """Provide access to data stored in SQL for objects stored with RhaptosModuleStorage""" __implements__ = (IModuleDBTool) id = 'portal_moduledb' meta_type = 'Module DB Tool' security = AccessControl.ClassSecurityInfo() sqlModuleExists = ZSQLFile('sql/moduleExists', globals(), __name__='sqlModuleExists') sqlCountModules = ZSQLFile('sql/countModules', globals(), __name__='sqlCountModules') sqlGetModulesByAuthors = ZSQLFile('sql/getModulesByAuthors', globals(), __name__='sqlGetModulesByAuthors') sqlGetModulesByRole = ZSQLFile('sql/getModulesByRole', globals(), __name__='sqlGetModulesByRole') sqlGetModulesByKeyword = ZSQLFile('sql/getModulesByKeyword', globals(), __name__='sqlGetModulesByKeyword') sqlGetModulesByTitle = ZSQLFile('sql/getModulesByTitle', globals(), __name__='sqlGetModulesByTitle') sqlGetModulesByLanguage = ZSQLFile('sql/getModulesByLanguage', globals(), __name__='sqlGetModulesByLanguage') sqlInsertFTIWords = ZSQLFile('sql/insertFTIWords', globals(), __name__='sqlInsertFTIWords') sqlGetPeoplebyName = ZSQLFile('sql/getPeoplebyName', globals(), __name__='sqlGetPeoplebyName') sqlDeleteModule = ZSQLFile('sql/deleteModule', globals(), __name__='sqlDeleteModule') sqlGetModule = ZSQLFile('sql/getModule', globals(), __name__='sqlGetModule') sqlGetModules = ZSQLFile('sql/getModules', globals(), __name__='sqlGetModules') sqlGetLatestModule = ZSQLFile('sql/getLatestModule', globals(), __name__='sqlGetLatestModule') sqlGetNewestModules = ZSQLFile('sql/getNewestModules', globals(), __name__='sqlGetNewestModule') sqlGetKeywords = ZSQLFile('sql/getKeywords', globals(), __name__='sqlGetKeywords') sqlGetHistory = ZSQLFile('sql/getHistory', globals(), __name__='sqlGetHistory') sqlGetNextModuleId = ZSQLFile('sql/getNextModuleId', globals(), __name__='sqlGetNextModuleId') sqlGetNextCollectionId = ZSQLFile('sql/getNextCollectionId', globals(), __name__='sqlGetNextCollectionId') sqlInsertNewVersion = ZSQLFile('sql/insertNewVersion', globals(), __name__='sqlInsertNewVersion') sqlGetAbstractID = ZSQLFile('sql/getAbstractID', globals(), __name__=='sqlGetAbstractID') sqlInsertAbstract = ZSQLFile('sql/insertAbstract', globals(), __name__='sqlInsertAbstract') sqlGetKeywordID = ZSQLFile('sql/getKeywordID', globals(), __name__=='sqlGetKeywordID') sqlInsertKeyword = ZSQLFile('sql/insertKeyword', globals(), __name__='sqlInsertKeyword') sqlInsertModuleKeyword = ZSQLFile('sql/insertKeywords', globals(), __name__='sqlInsertModuleKeyword') sqlInsertModuleOptionalRole = ZSQLFile('sql/insertOptionalRoles', globals(), __name__='sqlInsertModuleOptionalRole') sqlGetLicense = ZSQLFile('sql/getLicense', globals(), __name__='sqlGetLicense') sqlGetLicenses = ZSQLFile('sql/getLicenses', globals(), __name__='sqlGetLicenses') sqlGetTags = ZSQLFile('sql/getTags', globals(), __name__='sqlGetTags') sqlSearchModules = ZSQLFile('sql/searchModules', globals(), __name__='sqlSearchModules') sqlSearchModulesByDate = ZSQLFile('sql/searchModulesByDate', globals(), __name__='sqlSearchModulesByDate') sqlGetKeywordByFirstChar = ZSQLFile('sql/getKeywordByFirstChar', globals(), __name__='sqlGetKeywordByFirstChar') sqlGetLanguageCounts = ZSQLFile('sql/getLanguages', globals(), __name__='sqlGetLanguageCounts') sqlWrapAbstract = ZSQLFile('sql/wrapAbstract', globals(), __name__='sqlWrapAbstract') sqlWrapText = ZSQLFile('sql/wrapText', globals(), __name__='sqlWrapText') sqlInsertModuleTag = ZSQLFile('sql/insertTag', globals(), __name__='sqlInsertModuleTag') sqlRegisterRating = ZSQLFile('sql/registerRating', globals(), __name__='sqlRegisterRating') sqlDeregisterRating = ZSQLFile('sql/deregisterRating', globals(), __name__='sqlDeregisterRating') sqlGetRating = ZSQLFile('sql/getRating', globals(), __name__='sqlGetRating') sqlGetModuleFile = ZSQLFile('sql/getModuleFile', globals(), __name__='sqlGetModuleFile') sqlGetModuleFileSize = ZSQLFile('sql/getModuleFileSize', globals(), __name__='sqlGetModuleFileSize') sqlGetModuleFilenames = ZSQLFile('sql/getModuleFilenames', globals(), __name__='sqlGetModuleFilenames') sqlGetFileByMd5 = ZSQLFile('sql/getFileByMd5', globals(), __name__='sqlGetFileByMd5') sqlInsertFile = ZSQLFile('sql/insertFile', globals(), __name__='sqlInsertFile') sqlInsertModuleFile = ZSQLFile('sql/insertModuleFile', globals(), __name__='sqlInsertModuleFile') sqlGetCollectionTree = ZSQLFile('sql/getCollectionTree', globals(), __name__='sqlGetCollectionTree') sqlGetPrintStyles = ZSQLFile('sql/getPrintStyles', globals(), __name__='sqlGetPrintStyles') # Member-data functions sqlGetAuthorByFirstChar = ZSQLFile('sql/getAuthorByFirstChar', globals(), __name__='sqlGetAuthorByFirstChar') sqlGetAuthorById = ZSQLFile('sql/getAuthorById', globals(), __name__='sqlGetAuthorById') sqlInsertMember = ZSQLFile('sql/insertMemberData', globals(), __name__='sqlInsertMember') sqlUpdateMember = ZSQLFile('sql/updateMemberData', globals(), __name__='sqlUpdateMember') sqlUpdateMemberPassword = ZSQLFile('sql/updateMemberPassword', globals(), __name__='sqlUpdateMemberPassword') sqlUpdateMemberGroups = ZSQLFile('sql/updateMemberGroups', globals(), __name__='sqlUpdateMemberGroups') ## ZMI methods manage_options=(( {'label':'Overview', 'action':'manage_overview'}, {'label':'Configure', 'action':'manage_moduledb_properties'}, ) + SimpleItem.manage_options ) security.declareProtected(ManagePortal, 'manage_overview') manage_overview = PageTemplateFile('zpt/explainModuleDBTool', globals() ) security.declareProtected(ManagePortal, 'manage_moduledb_properties') manage_moduledb_properties = PageTemplateFile('zpt/editModuleDBTool', globals() ) def __init__(self, db=None): self.db = db security.declareProtected(ManagePortal, 'setDB') def setDB(self, db): self.db = db security.declareProtected(ManagePortal, 'manage_editModuleDBTool') def manage_editModuleDBTool(self, connection, REQUEST=None): """Edit the ModuleDBTool parameters""" self.db = connection if REQUEST: return self.manage_moduledb_properties(manage_tabs_message="ModuleDBTool updated") security.declarePublic('getLicenseData') def getLicenseData(self, url): """ Get all the information about a given license from the database table. Returns a dictionary with keys: code, label, name, url, and version. Examples of code, label, and version: 'by', 'CC-BY 3.0', '3.0'. """ # FIXME: not the best place for this; should be somewhere in RhaptosContent probably licensedata = self.sqlGetLicense(url=url)[0] licensedict = {} for f in ['code', 'label', 'name', 'url', 'version']: licensedict[f] = licensedata[f] return licensedict def insertModuleVersion(self, object): """Insert new module version into the database""" # Get ID of (possibly new) abstract abstractId = self._getAbstractID(object.abstract) # Get ID of license try: licenseId = self.sqlGetLicense(url=object.license)[0].licenseid except IndexError: raise CommitError, "Unknown license: %s" % object.license # Set state to 'submitted' stateId = 1 # Put new version of module into the DB parentObj = object.getParent() parent = parentObj and self.sqlGetModule(id=parentObj.objectId,version=parentObj.version)[0].ident or None print_style = object.portal_type == 'Collection' and object.parameters.getProperty('printstyle') or None self.sqlInsertNewVersion(moduleid=object.objectId, portal_type=object.portal_type, version=object.version, name=object.title, created=object.created.HTML4(), revised=object.revised.HTML4(), authors=object.authors, maintainers=object.maintainers, licensors=object.licensors, parentauthors=object.parentAuthors, abstractid=abstractId, stateid=stateId, licenseid=licenseId, doctype=getattr(object, 'doctype', ''), # 0.6 loses doctype attr submitter=object.submitter, submitlog=object.submitlog, parent=parent, language=object.language, google_analytics=object.getGoogleAnalyticsTrackingCode(), print_style=print_style) # Put the file objects in the files table files = [f for f in object.objectValues() if hasattr(f,'data')] #It's a file object defaultFile = getattr(object,'getDefaultFile',lambda : None)() # Move index.cnxml (or other default file) to end of list, so all files referenced by it are in db first if defaultFile: files.append(files.pop(files.index(defaultFile))) for fob in files: fid = self._getFileID(fob) self.sqlInsertModuleFile(moduleid=object.objectId, version=object.version, fileid=fid, filename=fob.id) # Put non-blank keywords into module-keyword table for word in [' '.join(w.strip().split()) for w in object.keywords if w.strip()]: keywordId = self._getKeywordID(word) self.sqlInsertModuleKeyword(moduleid=object.objectId, version=object.version, keywordid=keywordId) # Insert optional roles, if any for role in object.optional_roles.keys(): value = getattr(object,role.lower()+'s',None) if value: self.sqlInsertModuleOptionalRole(moduleid=object.objectId, version=object.version,rolename=role,persons=value) # Stores subjects as tags if type(_utf8(object.subject)) == type(''): self.sqlInsertModuleTag(moduleid=object.objectId, version=object.version,tag=_utf8(object.subject)) else: for subj in object.subject: self.sqlInsertModuleTag(moduleid=object.objectId, version=object.version,tag=_utf8(subj)) # Put fulltext index words in place if object.SearchableText(): self.sqlInsertFTIWords(moduleid=object.objectId, version=object.version, modulecontents=object.SearchableText()) def _getAbstractID(self, abstract): """Return a unique (possibly new) ID for abstract text""" abstract = _utf8(abstract) result = self.sqlGetAbstractID(abstract=abstract) if not len(result): # If the abstract doesn't already exist, insert it self.sqlInsertAbstract(abstract=abstract) result = self.sqlGetAbstractID(abstract=abstract) return result[0].id def _getKeywordID(self, word): """Return a unique (possibly new) ID for keyword""" word = _utf8(word) result = self.sqlGetKeywordID(word=word) if not len(result): # If the keyword doesn't already exist, insert it self.sqlInsertKeyword(word=word) result = self.sqlGetKeywordID(word=word) return result[0].id def _getFileID(self,fileob): """Return the fileid for a file, stored in the DB""" # let's make sure we've got a utf-8 string fdata = _utf8(fileob.data) m = md5.new(fdata).hexdigest() sha = sha1(fdata).hexdigest() res = self.sqlGetFileByMd5(md5=m) for r in res: if sha1(r.file).hexdigest() == sha: return r.fileid # Fell through, must be new bytes res = self.sqlInsertFile(file = Binary(fdata), media_type=fileob.content_type) return res[0].fileid
class ZopeRepository(Repository.Repository, RoleManager, OFS.SimpleItem.Item): """The ZopeRepository class builds on the core Repository implementation to provide the Zope management interface and other product trappings.""" security = AccessControl.ClassSecurityInfo() meta_type = 'Repository' manage_options = (( { 'label': 'Contents', 'action': 'manage_main', 'help': ('ZopeVersionControl', 'Repository-Manage.stx') }, { 'label': 'Properties', 'action': 'manage_properties_form', 'help': ('ZopeVersionControl', 'Repository-Properties.stx') }, ) + RoleManager.manage_options + OFS.SimpleItem.Item.manage_options) security.declareProtected('View management screens', 'manage_main') manage_main = DTMLFile('dtml/RepositoryManageMain', globals()) manage_main._setName('manage_main') manage = manage_main def __init__(self, id=None, title=''): Repository.Repository.__init__(self) if id is not None: self._setId(id) self.title = title security.declareProtected('View management screens', 'manage_properties_form') manage_properties_form = DTMLFile('dtml/RepositoryProperties', globals()) security.declareProtected('Manage repositories', 'manage_edit') def manage_edit(self, title='', REQUEST=None): """Change object properties.""" self.title = title if REQUEST is not None: message = "Saved changes." return self.manage_properties_form(self, REQUEST, manage_tabs_message=message) def __getitem__(self, name): history = self._histories.get(name) if history is not None: return history.__of__(self) raise KeyError(name) security.declarePrivate('objectIds') def objectIds(self, spec=None): return SequenceWrapper(self, self._histories.keys()) security.declarePrivate('objectValues') def objectValues(self, spec=None): return SequenceWrapper(self, self._histories.values()) security.declarePrivate('objectItems') def objectItems(self, spec=None): return SequenceWrapper(self, self._histories.items(), 1)
class HTMLButton(WidgetBase, OFS.SimpleItem.Item, Persistent, Acquisition.Implicit, \ AccessControl.Role.RoleManager): """HTMLWidget for Buttons""" __implements__ = IWidget security = AccessControl.ClassSecurityInfo() meta_type = 'HTML Button' css_class = 'FormButton' user_css = '' colspan = 0 hint = '' title = u"" aliases = [] security.declareProtected('View management screens', 'manage_properties') manage_properties = HTMLFile('dtml/HTMLButtonEdit', globals()) manage_options = ( { 'label': 'Properties', 'action': 'manage_properties' }, { 'label': 'Security', 'action': 'manage_access' }, ) ########### Zopish management stuff def __init__(self, id, REQUEST=None): self.id = id self.__version__ = __version__ if REQUEST is not None: self.changeProperties(REQUEST) def title_or_id(self): return self.value security.declareProtected('Change Formulon instances', 'manage_edit') def manage_edit(self, REQUEST=None): """Save edited properties.""" self.changeProperties(REQUEST) return MessageDialog(title='Edited', message="Properties for %s changed." % (self.id, ), action='./manage_properties') security.declareProtected('Change Formulon instances', 'changeProperties') def changeProperties(self, REQUEST=None, encoding='iso-8859-1'): # iso-8859 is ZMI's encoding. if REQUEST is not None: # Update the base properties self.changeBaseProperties(REQUEST, encoding) self.type = REQUEST.type self.value = unicode(REQUEST.value, encoding) self.user_css = unicode(REQUEST.user_css, encoding) self.column = REQUEST.column if self.column < 1: self.column = 1 self.row = REQUEST.row if self.row < 1: self.row = 1 self.colspan = REQUEST.colspan if REQUEST.has_key('isimg'): self.isimg = 1 else: self.isimg = 0 self.imgsrc = REQUEST.imgsrc self.aliases = REQUEST.aliases ############ Here comes the widget specific code security.declareProtected('View', 'render') def render(self, parent, tabindex=None): node = parent.appendChild(parent.ownerDocument.createElement("input")) if self.type == 'submit' and self.isimg: node.setAttribute("type", "image") node.setAttribute("src", self.imgsrc) else: node.setAttribute("type", self.type) if not tabindex is None: node.setAttribute("tabindex", str(tabindex)) if not self.type == 'submit': node.setAttribute("onClick", "resetForm()") if self.user_css: node.setAttribute("class", self.user_css) else: node.setAttribute("class", self.css_class) node.setAttribute("name", self.id + "_value") node.setAttribute("value", self.value) return node security.declareProtected('View', 'validate') def validate(self, against=None): return against security.declareProtected('View', 'height') def height(self): return 1 security.declareProtected('View', 'getValue') def getValue(self): return self.value
class HTMLFileupload(WidgetBase, OFS.SimpleItem.Item, Persistent, Acquisition.Implicit, AccessControl.Role.RoleManager): """HTMLWidget for Fileuploads""" __implements__ = IWidget security = AccessControl.ClassSecurityInfo() meta_type = 'HTML Fileupload' css_class = 'FormFileupload' colspan = 1 required = 0 aliases = [] security.declareProtected('View management screens', 'manage_properties') manage_properties = HTMLFile('dtml/HTMLFileuploadEdit', globals()) manage_options = ( { 'label': 'Properties', 'action': 'manage_properties' }, { 'label': 'Security', 'action': 'manage_access' }, ) ################ Here comes Zope specific code def __init__(self, id, REQUEST): self.id = id self.__version__ = __version__ if REQUEST is not None: self.changeProperties(REQUEST) security.declareProtected('Change Formulon instances', 'manage_edit') def manage_edit(self, REQUEST=None): """ set the new properties """ if REQUEST is not None: self.changeProperties(REQUEST) return MessageDialog(title='Edited', message="Properties for %s changed." % self.id, action='./manage_properties') security.declareProtected('Change Formulon instances', 'changeProperties') def changeProperties(self, REQUEST=None, encoding='iso-8859-1'): if REQUEST is None: return # Update the base properties self.changeBaseProperties(REQUEST, encoding) self.title = unicode(REQUEST.title, encoding) self.column = REQUEST.column if self.column < 1: self.column = 1 self.row = REQUEST.row if self.row < 1: self.row = 1 self.colspan = REQUEST.colspan self.required = 0 if REQUEST.has_key('required'): self.required = 1 self.aliases = REQUEST.aliases # Update TALES Methods for x in ['hint']: expression = REQUEST.get(x) if expression is None: continue setattr(self, x, TALESMethod(unicode(expression, encoding))) ################# Here comes the widget specific code security.declareProtected('View', 'render') def render(self, parent, tabindex=None): doc = parent.ownerDocument widget = parent.appendChild(doc.createElement('input')) set = widget.setAttribute if tabindex is not None: set('tabindex', str(tabindex)) set('name', self.id + "_value") set('type', 'file') set('class', self.css_class) set("onChange", "reportChange(this)") return widget security.declareProtected('View', 'validate') def validate(self, against): filename = '' against = against.get('value') if isinstance(against, ZPublisher.HTTPRequest.FileUpload): filename = against.filename if self.required and not filename: raise ValidationError(self.callMethod(self.hint)) return against security.declareProtected('View', 'height') def height(self): return 1 security.declareProtected('View', 'getValue') def getValue(self): if self.REQUEST.has_key(self.id): return self.REQUEST[self.id] else: return None
class ModuleView(SimpleItem): """Dyamically created Zope object for displaying Modules""" security = AccessControl.ClassSecurityInfo() implements(IMDML) meta_type = 'Rhaptos Module View' icon = 'module_icon.gif' isPrincipiaFolderish = 1 state = 'public' actor = None def getPendingCollaborations(self): return {} def __init__(self, id, objectId, data=None, **kwargs): # Since this is a versioned object, the id is the version self.id = id self.objectId = objectId self._data = data def _log(self, message, severity): zLOG.LOG("ModuleView", severity, "%s (%s)" % (message, self.REQUEST['PATH_INFO'])) def _setLastModHeader(self): #print self.revised self.REQUEST.RESPONSE.setHeader('Last-Modified', rfc1123_date(self.revised)) def __getitem__(self, name): """basic get item""" if name == self.getDefaultFilename(): f = self.getDefaultFile() else: if name in self.objectIds(): f = self.getFile(name) else: raise KeyError, name return f security.declarePublic('index_html') def index_html(self): """Default display method""" # If we got here without specifying the trailing slash, redirect if not self.REQUEST.PATH_INFO[-1].endswith('/'): path = self.REQUEST.URL1 + '/' if self.REQUEST.QUERY_STRING: path = path + '?' + self.REQUEST.QUERY_STRING self.REQUEST.RESPONSE.redirect(path, status=301) # Otherwise, do the default else: return self.default() security.declarePublic('isPublic') def isPublic(self): """Boolean answer true iff collection is in versioned repository. Based currently on value of 'state' attribute. """ return True security.declarePublic('url') def url(self): """Return the canonical URL used to access the object""" return self.absolute_url() + '/' security.declarePrivate('_getDBProperty') def _getDBProperty(self, property): """Get module property from DB storage""" # Store local copy of DB data so we don't have to keep looking it up if not self._data: #self._log("Querying DB for %s" % self.objectId, zLOG.INFO) if not self.id or self.id == 'latest': data = self.portal_moduledb.sqlGetLatestModule( id=self.objectId) else: data = self.portal_moduledb.sqlGetModule(id=self.objectId, version=self.id) try: self._data = data[0] except IndexError: raise IndexError, "Couldn't find version %s of object %s" % ( self.id, self.objectId) return getattr(self._data, property) security.declarePublic('getMetadata') def getMetadata(self): """Returns a dictionary of metadata, so that children can implement IMDML""" metadata = {} repos = getToolByName(self, 'content') metadata['repository'] = repos.absolute_url() metadata['url'] = self.absolute_url() metadata['objectId'] = self.objectId metadata['title'] = self.Title() metadata['keywords'] = self.keywords metadata['subject'] = self.subject metadata['abstract'] = self.abstract metadata['language'] = self.language metadata['license'] = self.license metadata['version'] = self.version metadata['created'] = self.created metadata['revised'] = self.revised metadata['authors'] = self.authors metadata['maintainers'] = self.maintainers metadata['licensors'] = self.licensors # Optional roles, currently Translator, Editor for role, actors in self.roles.items(): metadata[role] = actors # Collection specific metadata below metadata['homepage'] = None metadata['institution'] = None metadata['coursecode'] = None metadata['instructor'] = None parent = {} pobj = self.getParent() if pobj: parent = pobj.getMetadata() metadata['parent'] = parent return metadata security.declarePublic('getKeywords') def getKeywords(self): """Return list of keywords""" return tuple([ row.word for row in self.portal_moduledb.sqlGetKeywords(ident=self.ident) ]) security.declarePublic('Title') def Title(self): """Return title; some performance optimizations over plain 'title' attribute.""" if not hasattr(self, '_cataloging') and self.isLatestVersion( self): # ...acquired from Repository # catalog retrieval only for 'latest'; we don't store values for old modules # _cataloging is a semaphore for when cataloging is going on right after publish try: cat = getToolByName(self, 'content').catalog mod = cat(objectId=self.objectId) if mod: return mod[0].Title except AttributeError: # not be in content context? pass return self.title security.declarePublic('wrapAbstract') def wrapAbstract(self, terms, open_wrap_tag='<b>', close_wrap_tag='</b>'): """Wrap matches to list of terms with tags. Returns tuple of (excerpt,abstract)""" q = '&'.join(terms) res = self.portal_moduledb.sqlWrapAbstract( moduleid=self.objectId, version=self.version, query=q, open_wrap_tag='OPEN_CNX_WRAP_TAG', close_wrap_tag='CLOSE_CNX_WRAP_TAG') headline, abstract = res.tuples()[0] cutoff = 20 headline_start = abstract.find(headline) if headline_start != -1: headline_end = headline_start + len(headline) head = abstract[:headline_start] tail = abstract[headline_end:] if len(' '.join(head.split()[2:])) > cutoff: headline = ' '.join(head.split()[:2]) + " ... " + headline else: headline = head + headline if len(' '.join(tail.split()[:-2])) > cutoff: headline = headline + " ... " + ' '.join(tail.split()[-2:]) else: headline = headline + tail return (headline.replace('OPEN_CNX_WRAP_TAG', open_wrap_tag).replace( 'CLOSE_CNX_WRAP_TAG', close_wrap_tag), abstract.replace('OPEN_CNX_WRAP_TAG', open_wrap_tag).replace( 'CLOSE_CNX_WRAP_TAG', close_wrap_tag)) security.declarePublic('wrapAbstract') def wrapBodyText(self, terms, open_wrap_tag='<b>', close_wrap_tag='</b>'): """Wrap matches to list of terms with tags. Returns tuple of (excerpt,fulltext)""" q = '&'.join(terms) res = self.portal_moduledb.sqlWrapText(text=self.bareText(), query=q, open_wrap_tag=open_wrap_tag, close_wrap_tag=close_wrap_tag) return (res[0].headline, res[0].fulltext) security.declarePublic('portal_type') portal_type = ComputedAttribute( lambda self: self._getDBProperty('portal_type'), 1) # FIXME: COMMENTED until such time as we want to have a different portal_type for ModuleView # see also Install.py #portal_type = ComputedAttribute(lambda self: "Published%s" % self._getDBProperty('portal_type'), 1) security.declarePublic('name') name = ComputedAttribute(lambda self: self._getDBProperty('name'), 1) title = name security.declarePublic('version') version = ComputedAttribute(lambda self: self._getDBProperty('version'), 1) security.declarePublic('doctype') doctype = ComputedAttribute(lambda self: self._getDBProperty('doctype'), 1) security.declarePublic('abstract') abstract = ComputedAttribute(lambda self: self._getDBProperty('abstract'), 1) security.declarePublic('license') license = ComputedAttribute(lambda self: self._getDBProperty('license'), 1) security.declarePublic('created') created = ComputedAttribute(lambda self: self._getDBProperty('created'), 1) security.declarePublic('revised') revised = ComputedAttribute(lambda self: self._getDBProperty('revised'), 1) security.declarePrivate('ident') ident = ComputedAttribute(lambda self: self._getDBProperty('ident'), 1) security.declarePublic('keywords') keywords = ComputedAttribute(getKeywords, 1) security.declarePublic('authors') authors = ComputedAttribute(lambda self: self._getDBProperty('authors'), 1) security.declarePublic('parentAuthors') parentAuthors = ComputedAttribute( lambda self: self._getDBProperty('parentAuthors'), 1) security.declarePublic('maintainers') maintainers = ComputedAttribute( lambda self: self._getDBProperty('maintainers'), 1) security.declarePublic('licensors') licensors = ComputedAttribute( lambda self: self._getDBProperty('licensors'), 1) security.declarePublic('submitter') submitter = ComputedAttribute( lambda self: self._getDBProperty('submitter'), 1) security.declarePublic('message') message = ComputedAttribute(lambda self: self._getDBProperty('submitlog'), 1) security.declarePublic('_links') _links = ComputedAttribute(lambda self: self.getContextLinks(), 1) security.declarePublic('language') language = ComputedAttribute(lambda self: self._getDBProperty('language'), 1) security.declarePublic('subject') subject = ComputedAttribute(lambda self: self._getDBProperty('subject'), 1) security.declarePublic('roles') roles = ComputedAttribute(lambda self: self._getDBProperty('roles'), 1) # collection attributes, which don't matter here, but set to make sure the catalog doesn't pick something else up # FIXME: a more general way to do this (probably defining all index/metadata names on the Repository object) when # types become more flexible. code = None collectionType = None institution = None instructor = None security.declarePrivate('getFile') def getFile(self, name): """Retrieve a module file from DB""" db_str = getattr(self, self.portal_moduledb.db).connection_string mf = ModuleFile(modid=self.objectId, version=self.version, name=name, db_connect=db_str, parent=self) try: # for when file is fetched on its own, we want to say some things about it; # we certainly don't want to over-ride the callers' response settings. response = self.REQUEST.RESPONSE if not response.headers.has_key('content-length'): response.setHeader('Content-Length', len(mf)) content_type = mf.content_type() if content_type and not response.headers.has_key('content-type'): response.setHeader('Content-Type', content_type) if not response.headers.has_key('last-modified'): self._setLastModHeader() self.REQUEST.RESPONSE.setHeader( 'Cache-Control', 'max-age=0, s-maxage=31536000, public, must-revalidate') except AttributeError: pass return mf security.declarePrivate('getFileSize') def getFileSize(self, name): """Retrieve a module file size from DB""" res = self.portal_moduledb.sqlGetModuleFileSize(id=self.objectId, version=self.version, filename=name) if len(res) == 1: filesize = res[0][0] return filesize else: raise KeyError, "No such file %s/%s/%s" % (self.objectId, self.version, name) security.declarePrivate('getDoctype') def getDoctype(self): """Get module's document type""" return self._getDBProperty('doctype') security.declarePublic('getContextLinks') def getContextLinks(self, context=None): """Return a list of links for this module""" if context: # Assume context is the URL of a collection # FIXME: this isn't very robust... (scheme, netloc, path, params, query, fragment) = urlparse(context) portal_url = getToolByName(self, 'portal_url') portal_path = portal_url.getPortalPath() # if path includes portal path strip it out if path.startswith(portal_path): path = path[len(portal_path):] # Strip off the leading slash so the traversal will be done # from portal root, not Zope root if path.startswith('/'): path = path[1:] course = portal_url.getPortalObject().restrictedTraverse(path) lm_links = course.getContainedObject(self.objectId).getLinks() else: lm_links = self.getLinks() links = [{ 'url': l.target, 'title': l.title, 'type': l.category, 'strength': l.strength } for l in lm_links] return links security.declarePublic('getLinks') def getLinks(self): """Return a list of links for this module""" # Get versioned URL relative to root urltool = getToolByName(self, 'portal_url') source = '/'.join(('', ) + urltool.getRelativeContentPath(self)[:-1] + (self.version, '')) # '/content/m9000/2.34/' return self.portal_linkmap.searchLinks(source) security.declarePublic('getParent') def getParent(self): id = self._getDBProperty('parent_id') version = self._getDBProperty('parent_version') if id: return self.getRhaptosObject(id, version) security.declarePrivate('getVersion') def getVersion(self): """Return version string or None if version is 'latest'""" if self.id == 'latest': return None else: return self.id security.declarePublic('source') def source(self): """Get module source""" self.REQUEST.RESPONSE.setHeader('Content-Type', "application/xml") self._setLastModHeader() if self.REQUEST.REQUEST_METHOD == 'HEAD': return return self.normalize() security.declarePrivate('default') def default(self): """Make render() the default method for viewing ZRhaptosModules""" REQUEST = self.REQUEST request_format = REQUEST.get('format', '') if request_format == 'pdf' or request_format == 'epub': self._setLastModHeader() if REQUEST.REQUEST_METHOD == 'HEAD': REQUEST.RESPONSE.setHeader( 'Cache-Control', 'max-age=0, s-maxage=31536000, public, must-revalidate') return # HEAD short-circuiting if self.id == 'latest': # Redirect to specific version: 302 since it'll change w/ each publish path = self.REQUEST.URL2 + '/' + self.version path = path + '/?' + self.REQUEST.QUERY_STRING self.REQUEST.RESPONSE.redirect(path, status=302) return if self.testIfModSince(): #True is 'not modified'; status code 304 is also 'not modified' REQUEST.RESPONSE.setHeader( 'Cache-Control', 'max-age=0, s-maxage=31536000, public, must-revalidate') REQUEST.RESPONSE.setStatus(304) return if request_format == 'pdf': file = self.downloadPDF() else: file = self.downloadEPUB() if file: REQUEST.RESPONSE.setHeader( 'Cache-Control', 'max-age=0, s-maxage=31536000, public, must-revalidate') return file elif request_format == 'offline': return self.downloadOfflineZip() else: resp = self.module_render() if REQUEST.RESPONSE.getStatus() in (301, 302): return resp pcs = self.portal_cache_settings view = 'module_render' member = pcs.getMember() rule, header_set = pcs.getRuleAndHeaderSet(REQUEST, self, view, member) if header_set is not None: expr_context = rule._getExpressionContext(REQUEST, self, view, member, keywords={}) else: expr_context = None _setCacheHeaders(self, {}, rule, header_set, expr_context) return resp security.declarePrivate('testIfModSince') def testIfModSince(self): """Test if the current REQUEST has an 'If-Modified-Since' header, and if so, test it. Returns True for 'not modified'. False means either 'was modified' or 'no header'""" ifmodsince = self.REQUEST.get_header('If-Modified-Since', None) try: if ifmodsince: ifmodsince = DateTime(ifmodsince) except DTSyntaxError: ifmodsince = None # bad date, just ignore, per spec. See also Zope's OFS/Image.py # short circuit for if-modified-since: spec recommends clients send value from # previous Modified header, so the = case could be significant return bool(ifmodsince and self.revised <= ifmodsince) def HEAD(self, REQUEST, RESPONSE): """Retrieve resource information without a response body.""" SimpleItem.HEAD(self, REQUEST, RESPONSE) if self.REQUEST.get('format', '') == 'pdf': self._setLastModHeader() security.declarePublic('normalize') def normalize(self): """Get modules's normalized source""" return self.getDefaultFile().normalize() security.declarePrivate('downloadOfflineZip') def downloadOfflineZip(self): """Returns a PDF version of the module return the ePub file from the PrintTool storage, if possible. if not possible, returns nothing. Returns: EPUB file or nothing """ request = self.REQUEST module_id = self.objectId module_version = self.version ptool = getToolByName(self, 'rhaptos_print') file = ptool.getFile(module_id, module_version, 'offline.zip') bIsOfflineZipFileCached = (file is not None and file.get_size() > 0) if not bIsOfflineZipFileCached: file = None if file is not None: offlinezipfilename = '%s_%s_offline.zip' % (module_id, module_version) request.RESPONSE.setHeader('Content-Type', 'application/zip') request.RESPONSE.setHeader( 'Content-Disposition', 'attachment; filename=%s' % offlinezipfilename) return file security.declarePrivate('downloadEPUB') def downloadEPUB(self): """Returns a PDF version of the module return the ePub file from the PrintTool storage, if possible. if not possible, returns nothing. Returns: EPUB file or nothing """ request = self.REQUEST module_id = self.objectId module_version = self.version ptool = getToolByName(self, 'rhaptos_print') file = ptool.getFile(module_id, module_version, 'epub') bIsEpubFileCached = (file is not None and file.get_size() > 0) if not bIsEpubFileCached: file = None if file is not None: epubfilename = '%s_%s.epub' % (module_id, module_version) request.RESPONSE.setHeader('Content-Type', 'application/epub+zip') request.RESPONSE.setHeader( 'Content-Disposition', 'attachment; filename=%s' % epubfilename) return file security.declarePrivate('downloadPDF') def downloadPDF(self): """Returns a PDF version of the module Checks for status in RhaptosPrintTool. If it is 'failed', method returns if it is succeeded, tries to get PDF from RhaptosPrintTool. If is not there, the PDF is generated on-demand and added to RhaptosPrintTool Returns: PDF file or nothing """ data = None fs_tool = getToolByName(self, 'portal_fsimport') printTool = getToolByName(self, 'rhaptos_print') printStatus = printTool.getStatus(self.objectId, self.version, 'pdf') if printStatus == 'failed': return else: if printStatus != None and printStatus != '': data = printTool.getFile(self.objectId, self.version, 'pdf') if data == None and printStatus != 'locked': wd = tempfile.mkdtemp() export = self.module_export_cnxml() export_file = open(os.path.join(wd, 'export.cnxml'), 'wb') if type(export) is unicode: export_file.write(export.encode('utf-8')) else: export_file.write(export) export_file.close() files = self.objectIds() for fname in files: #FIXME should be someway to offload the file-from-db-to-filesystem export = self.getFile(fname) export_file = open(os.path.join(wd, fname), 'wb') export_file.write(str(export)) export_file.close() pdf_tool = getToolByName(self, 'portal_pdflatex') params = {} data = pdf_tool.convertFSDirToPDF(wd, 'export.cnxml', **params) printTool.setFile(self.objectId, self.version, 'pdf', data) printTool.setStatus(self.objectId, self.version, 'pdf', 'succeeded') shutil.rmtree(wd) self.REQUEST.RESPONSE.setHeader('Content-Type', 'application/pdf') self.REQUEST.RESPONSE.setHeader( 'Content-Disposition', 'attachment; filename=%s.pdf' % self.objectId) return data security.declarePublic('checkout') def checkout(self, container, skipfiles=None): """Checkout a copy of the module""" if skipfiles is None: skipfiles = ['index.cnxml.html', 'index.html.cnxml'] files = self.objectIds() for fname in files: if fname not in skipfiles: self._createFile(fname, self.getFile(fname).read(), container) def _createFile(self, name, body, container): """Create a file from a name and bits into the specified ZODB container""" typ, encoding = mimetypes.guess_type(name) #registry = getToolByName(self, 'content_type_registry') #if not registry: # portal_type = 'File' #portal_type = registry.findTypeName(name, typ, body) or 'File' if name == self.getDefaultFilename(): portal_type = "CNXML Document" else: portal_type = 'UnifiedFile' content = getattr(container.aq_base, name, None) if not content: getToolByName(self, 'portal_types').constructContent(portal_type, container, name, file=body) else: # we may have existing index.cnxmls, for example content.update_data(body) security.declarePublic('getDefaultFilename') def getDefaultFilename(self): """Return filename used in the default 'view' of this module""" return 'index.cnxml' security.declarePublic('getDefaultFile') def getDefaultFile(self): """Return the file object used in the default 'view' of this module""" name = self.getDefaultFilename() content = self.getFile(name).read() portal_type = CNXMLFile f = portal_type(name, '', content, None, 'text/xml').__of__(self) return f security.declarePublic('SearchableText') def SearchableText(self): """Return the text of the module for searching""" content = self.getDefaultFile().getSource() bare = XMLService.transform(content, baretext) return bare security.declarePublic('objectIds') def objectIds(self): """Return the list of files in this module""" res = self.portal_moduledb.sqlGetModuleFilenames(id=self.objectId, version=self.version) if res: files = [r['filename'] for r in res] else: files = [] return files security.declarePublic('getAboutActions') def getAboutActions(self): return [{ 'id': 'module_render', 'url': '.', 'name': 'View' }, { 'id': 'about', 'url': 'about', 'name': 'About' }, { 'id': 'history', 'url': 'history', 'name': 'History' }, { 'id': 'print', 'url': '?format=pdf', 'name': 'Print' }] security.declarePublic('rating') def rating(self): # xxx: it seems impossibly difficult to just delegate to the wrapped # object. res = self.portal_moduledb.sqlGetRating(moduleid=self.objectId, version=self.version) if not res: return 0.0 totalrating = res[0].totalrating votes = res[0].votes if votes == 0: return 0.0 return round(totalrating * 1.0 / votes, 1) security.declarePublic('numberOfRatings') def numberOfRatings(self): # xxx: it seems impossibly difficult to just delegate to the wrapped # object. res = self.portal_moduledb.sqlGetRating(moduleid=self.objectId, version=self.version) if not res: return 0 return res[0].votes # ---- Local role manipulations to allow group memners access security.declarePrivate('_getLocalRoles') def _getLocalRoles(self): """Query the database for the local roles in this workgroup""" # Give all maintainers the 'Maintainer' role dict = {} for m in self.maintainers: dict[m] = ['Maintainer'] return dict __ac_local_roles__ = ComputedAttribute(_getLocalRoles, 1) def manage_addLocalRoles(self, userid, roles, REQUEST=None): """Set local roles for a user.""" pass def manage_setLocalRoles(self, userid, roles, REQUEST=None): """Set local roles for a user.""" pass def manage_delLocalRoles(self, userids, REQUEST=None): """Remove all local roles for a user.""" pass # Compatibility with CMF security.declarePublic('getIcon') def getIcon(self, *args): """CMF Combatibility method""" return self.icon security.declarePublic('enqueue') def enqueue(self): """Add module to queue tool to recreate module export zip, epub and offline HTML returns string message """ qtool = getToolByName(self, 'queue_tool') key = "modexport_%s" % self.objectId dictRequest = { "id": self.objectId, "version": self.version, "serverURL": self.REQUEST['SERVER_URL'] } script_location = 'SCRIPTSDIR' in os.environ and os.environ[ 'SCRIPTSDIR'] or '.' qtool.add( key, dictRequest, "%s/create_and_store_pub_module_export.zctl" % script_location) return "modexport enqueued" # Set default roles for these permissions security.setPermissionDefault('Edit Rhaptos Object', ('Manager', 'Owner', 'Maintainer'))