def reindexIndexes(context): """Reindex some indexes. Indexes that are added in the catalog.xml file get cleared everytime the GenericSetup profile is applied. So we need to reindex them. Since we are forced to do that, we might as well make sure that these get reindexed in the correct order. """ if isNotECAssignmentBoxProfile(context): return site = context.getSite() pc = getToolByName(site, "portal_catalog") indexes = [ "isAssignmentBoxType", "isAssignmentType", "getRawAssignment_reference", "getRawRelatedItems", "review_state", ] # Don't reindex an index if it isn't actually in the catalog. # Should not happen, but cannot do any harm. ids = [id for id in indexes if id in pc.indexes()] if ids: pc.manage_reindexIndex(ids=ids) LOG.info("Indexes %s re-indexed." % indexes)
def sendEmail(self, addresses, subject, text): """ Send an e-mail message to the specified list of addresses. """ if not addresses: return portal_url = getToolByName(self, 'portal_url') #plone_utils = getToolByName(self, 'plone_utils') portal = portal_url.getPortalObject() fromAddress = portal.getProperty('email_from_address', None) #mailHost = plone_utils.getMailHost() #charset = plone_utils.getSiteEncoding() mailHost = getToolByName(portal, 'MailHost') #self.MailHost charset = portal.getProperty('email_charset', 'UTF-8') if fromAddress is None: LOG.error('Cannot send email: address or name is %s' % fromAddress) return try: if (type(text) == unicode): msg = MIMEText(text.encode(charset), 'plain', charset) else: msg = MIMEText(text, 'plain', charset) except Exception, e: LOG.error('Cannot send notification email: %s' % e) return
def hideToolsFromNavigation(context): """Hide auto-installed tool instances from navigation """ if isNotECAssignmentBoxProfile(context): return # this tools will be uncataloged tool_id = "ecab_utils" site = context.getSite() portal = getToolByName(site, "portal_url").getPortalObject() portalProperties = getToolByName(site, "portal_properties") navtreeProperties = getattr(portalProperties, "navtree_properties") if navtreeProperties.hasProperty("idsNotToList"): # get IDs of all unlisted items current = list(navtreeProperties.getProperty("idsNotToList") or []) # add our tools to list of unlisted items if tool_id not in current: current.append(tool_id) kwargs = {"idsNotToList": current} navtreeProperties.manage_changeProperties(**kwargs) # unindex our tools try: portal[tool_id].unindexObject() except: LOG.warn("Could not unindex object: %s" % tool_id)
def reindexIndexes(context): """Reindex some indexes. Indexes that are added in the catalog.xml file get cleared everytime the GenericSetup profile is applied. So we need to reindex them. Since we are forced to do that, we might as well make sure that these get reindexed in the correct order. """ if isNotECAssignmentBoxProfile(context): return site = context.getSite() pc = getToolByName(site, 'portal_catalog') indexes = [ 'isAssignmentBoxType', 'isAssignmentType', 'getRawAssignment_reference', 'getRawRelatedItems', 'review_state', ] # Don't reindex an index if it isn't actually in the catalog. # Should not happen, but cannot do any harm. ids = [id for id in indexes if id in pc.indexes()] if ids: pc.manage_reindexIndex(ids=ids) LOG.info('Indexes %s re-indexed.' % indexes)
def hideToolsFromNavigation(context): """Hide auto-installed tool instances from navigation """ if isNotECAssignmentBoxProfile(context): return # this tools will be uncataloged tool_id = 'ecab_utils' site = context.getSite() portal = getToolByName(site, 'portal_url').getPortalObject() portalProperties = getToolByName(site, 'portal_properties') navtreeProperties = getattr(portalProperties, 'navtree_properties') if navtreeProperties.hasProperty('idsNotToList'): # get IDs of all unlisted items current = list(navtreeProperties.getProperty('idsNotToList') or []) # add our tools to list of unlisted items if tool_id not in current: current.append(tool_id) kwargs = {'idsNotToList': current} navtreeProperties.manage_changeProperties(**kwargs) # unindex our tools try: portal[tool_id].unindexObject() except: LOG.warn('Could not unindex object: %s' % tool_id)
def summarize(self): """ Returns an dictionary containing summarized states of all assignments for current user - or all users if manager - in all subfolders. Only users with roles owner, reviewer or manager will see summarized states of all users. @return a dictionary containing user-id as key and summarized states as value """ result = {} # get current uses's id currentUser = self.portal_membership.getAuthenticatedMember() # check if current user is owner of this folder isOwner = currentUser.has_role(['Owner', 'Reviewer', 'Manager'], self) catalog = getToolByName(self, 'portal_catalog') if isOwner: brains = catalog.searchResults( path={ 'query': '/'.join(self.getPhysicalPath()), 'depth': 100, }, isAssignmentType=True, ) else: brains = catalog.searchResults( path={ 'query': '/'.join(self.getPhysicalPath()), 'depth': 100, }, Creator=currentUser.getId(), isAssignmentType=True, ) wf_states = self.getWfStates() #LOG.debug('wf_states: %s' % wf_states) n_states = len(wf_states) for brain in brains: key = brain.Creator reviewState = brain.review_state if key and reviewState: #LOG.debug('key: %s' % key) #LOG.debug('reviewState: %s' % reviewState) if not result.has_key(key): result[key] = [0 for i in range(n_states)] LOG.debug('result: %s' % result) result[key][wf_states.index(brain.review_state)] += 1 return result
def getWfStates(self): """ @deprecated use getWfStates in ecab_utils directly """ ecab_utils = getToolByName(self, 'ecab_utils', None) if (ecab_utils != None): return ecab_utils.getWfStates(config.ECA_WORKFLOW_ID) else: LOG.warn("Could not get tool by name: '%s'" % 'ecab_utils') return ()
def summarize(self): """ Returns an dictionary containing summarized states of all assignments for current user - or all users if manager - in all subfolders. Only users with roles owner, reviewer or manager will see summarized states of all users. @return a dictionary containing user-id as key and summarized states as value """ result = {} # get current uses's id currentUser = self.portal_membership.getAuthenticatedMember() # check if current user is owner of this folder isOwner = currentUser.has_role(['Owner', 'Reviewer', 'Manager'], self) catalog = getToolByName(self, 'portal_catalog') if isOwner: brains = catalog.searchResults(path = {'query':'/'.join(self.getPhysicalPath()), 'depth':100, }, isAssignmentType = True, ) else: brains = catalog.searchResults(path = {'query':'/'.join(self.getPhysicalPath()), 'depth':100, }, Creator = currentUser.getId(), isAssignmentType = True, ) wf_states = self.getWfStates() #LOG.debug('wf_states: %s' % wf_states) n_states = len(wf_states) for brain in brains: key = brain.Creator reviewState = brain.review_state if key and reviewState: #LOG.debug('key: %s' % key) #LOG.debug('reviewState: %s' % reviewState) if not result.has_key(key): result[key] = [0 for i in range(n_states)] LOG.debug('result: %s' % result) result[key][wf_states.index(brain.review_state)] += 1 return result
def installQIDependencies(context): """Install dependencies""" if isNotECAssignmentBoxProfile(context): return site = context.getSite() portal = getToolByName(site, 'portal_url').getPortalObject() quickinstaller = portal.portal_quickinstaller for dependency in config.DEPENDENCIES: if quickinstaller.isProductInstalled(dependency): LOG.info('Reinstalling dependency %s:' % dependency) quickinstaller.reinstallProducts([dependency]) transaction.savepoint() else: LOG.info('Installing dependency %s:' % dependency) quickinstaller.installProduct(dependency) transaction.savepoint()
def installQIDependencies(context): """Install dependencies""" if isNotECAssignmentBoxProfile(context): return site = context.getSite() portal = getToolByName(site, "portal_url").getPortalObject() quickinstaller = portal.portal_quickinstaller for dependency in config.DEPENDENCIES: if quickinstaller.isProductInstalled(dependency): LOG.info("Reinstalling dependency %s:" % dependency) quickinstaller.reinstallProducts([dependency]) transaction.savepoint() else: LOG.info("Installing dependency %s:" % dependency) quickinstaller.installProduct(dependency) transaction.savepoint()
LOG.error('Cannot send notification email: %s' % e) return msg['Subject'] = subjHeader msg['From'] = fromAddress msg['Subject'] = subject # This is a hack to suppress deprecation messages about send() # in SecureMailHost; the proposed alternative, secureSend(), # sucks. mailHost._v_send = 1 for address in addresses: if address: try: LOG.info("Sending email to %r" % address) msg['To'] = address mailHost.send(msg.as_string()) except ConflictError, ce: LOG.error('Failed sending email: %s' % ce) raise except Exception, e: LOG.error('Failed sending email from %s to %s' % (fromAddress, address)) LOG.error("Reason: %s: %r" % (e.__class__.__name__, str(e))) # end if # end for
class ECABTool(UniqueObject, BaseContent, BrowserDefaultMixin): """ """ security = ClassSecurityInfo() implements(IECABTool) meta_type = 'ECABTool' plone_tool = True _at_rename_after_creation = True schema = ECABTool_schema def __init__(self, id=None): """ Tool-constructors have no id argument, the id is fixed """ BaseContent.__init__(self, 'ecab_utils') self.setTitle('') def at_post_edit_script(self): """ Tool should not appear in portal_catalog """ self.unindexObject() # -- Methods -------------------------------------------------------------- #security.declarePrivate('getWfStates') def getWfStates(self, wfName=config.ECA_WORKFLOW_ID): """ @return a list containing all state keys in assignment's workflow """ wtool = self.portal_workflow return wtool.getWorkflowById(wfName).states.keys() #security.declarePublic('getWfStatesDisplayList') def getWfStatesDisplayList(self, wfName=config.ECA_WORKFLOW_ID): """ @return a DisplayList containing all state keys and state titles in assignment's workflow """ #LOG.info('xxx: getWfStatesDisplayList') dl = DisplayList(()) wtool = self.portal_workflow wf = wtool.getWorkflowById(wfName) stateKeys = self.getWfStates(wfName) for key in stateKeys: dl.add(key, wf.states[key].title) #return dl.sortedByValue() return dl #security.declarePrivate('getWfTransitions') def getWfTransitions(self, wfName=config.ECA_WORKFLOW_ID): """ @return all transitions for the given workflow """ result = {} wtool = self.portal_workflow wf = wtool.getWorkflowById(wfName) for tid in wf.transitions.keys(): tdef = wf.transitions.get(tid, None) if tdef is not None and \ tdef.trigger_type == TRIGGER_USER_ACTION and \ tdef.actbox_name and \ not result.has_key(tdef.id): result[tdef.id] = { 'id': tdef.id, 'title': tdef.title, 'title_or_id': tdef.title_or_id(), 'description': tdef.description, 'name': tdef.actbox_name } return tuple(result.values()) #security.declarePrivate('getWfTransitionsDisplayList') def getWfTransitionsDisplayList(self, wfName=config.ECA_WORKFLOW_ID): """ @return a DisplayList containing all transition keys and titles in assignment's workflow """ dl = DisplayList(()) #wtool = self.portal_workflow #wf = wtool.getWorkflowById(wfName) for transition in self.getWfTransitions(): # FIXME: not sure if this works with the result # from getWfTransitions dl.add(transition.id, transition.actbox_name) return dl.sortedByValue() #security.declarePublic('localizeNumber') def localizeNumber(self, format, value): """ A simple method for localized formatting of decimal numbers, similar to locale.format(). """ #LOG.info('format: %s' % format) #LOG.info('value: %s' % value) if not value: return None result = format % value fields = result.split(".") decimalSeparator = self.translate(msgid='decimal_separator', domain=config.I18N_DOMAIN, default='.') if len(fields) == 2: result = fields[0] + decimalSeparator + fields[1] elif len(fields) == 1: result = fields[0] else: raise ValueError, "Too many decimal points in result string" return result #security.declarePublic('getFullNameById') def getFullNameById(self, id): """ Returns the full name of a user by the given ID. If full name is devided into given and last name, we return it in the format Doo, John; otherwise we will return 'fullname' as provided by Plone. """ #LOG.debug('Here we are in ECABTool#getFullNameById') mtool = self.portal_membership member = mtool.getMemberById(id) error = False if not member: return id try: sn = member.getProperty('sn', None) givenName = member.getProperty('givenName', None) except: error = True #LOG.info('xdebug: sn, givenName: %s, %s' % (type(sn), type(givenName))) #LOG.info('xdebug: sn, givenName: %s, %s' % (sn, givenName)) if error or (not sn) or (not givenName): fullname = member.getProperty('fullname', '') if fullname == '': return id else: return fullname #if fullname.find(' ') == -1: # return fullname # #sn = fullname[fullname.rfind(' ') + 1:] #givenName = fullname[0:fullname.find(' ')] else: #return sn + ', ' + givenName # print 'givenName, sn: %s, %s' % (givenName, sn, ) return '%s, %s' % (sn, givenName) security.declarePublic('getUserPropertyById') def getUserPropertyById(self, userId, property, fallback=None): """ @return: Value for 'property' or None """ #LOG.info('xdebug: %s, %s, %s' % (userId, property, fallback, )) membership = getToolByName(self, 'portal_membership') member = membership.getMemberById(userId) try: return member.getProperty(property, fallback) except: return fallback #security.declarePublic('testAssignmentBoxType') def testAssignmentBoxType(self, item=None): """ Returns True if item has an attribut 'isAssignmentBoxType' or - in case item is a catalog brain- index 'isAssignmentBoxType' is True """ #LOG.debug('Here we are in ECABTool#testAssignmentBoxType: %s' % item) result = None if (item and hasattr(item, 'isAssignmentBoxType')): result = item.isAssignmentBoxType # dirty hack if repr(result) == 'Missing.Value': result = False else: result = False #LOG.info('result: %s' % repr(result)) return result #security.declarePublic('isGrader') def isGrader(self, item, id=None): """ Returns True if the user given by id has permission to grade the assignment given by item; otherwise False. If id is None, the check will be done for the current user. @param item an assignment @param id a user id """ mtool = self.portal_membership if not id: member = mtool.getAuthenticatedMember() else: member = mtool.getMemberById(id) return member.checkPermission(config.GradeAssignments, item) #security.declarePublic('getStatesToShow') def getStatesToShow(self, showSuperseded=False, state=None): """ Returns a list of state names which will be used as a filter for showing assignments. """ # FIXME: states are static names but they shoul better be taken from # workflow_tool for the given object result = ( 'submitted', 'pending', 'accepted', 'rejected', 'graded', ) if state is not None: if type(state) not in [tuple, list]: state = (state, ) result = [s for s in state if s in result] if showSuperseded: result += ('superseded', ) return result #security.declarePublic('findAssignments') def findAssignments(self, context, id): """ """ ct = getToolByName(self, 'portal_catalog') ntp = getToolByName(self, 'portal_properties').navtree_properties currentPath = None query = {} if context == self: currentPath = getToolByName(self, 'portal_url').getPortalPath() query['path'] = { 'query': currentPath, 'depth': ntp.getProperty('sitemapDepth', 2) } else: currentPath = '/'.join(context.getPhysicalPath()) query['path'] = {'query': currentPath, 'navtree': 1} query['portal_type'] = ('ECAssignment', ) #rawresult = ct(**query) rawresult = ct(path=currentPath, portal_type='ECAssignment', Creator=id) return rawresult #security.declarePublic('calculateMean') def calculateMean(self, list): """ """ try: stats = Statistics(map((float), list)) #except Exception, e: # LOG.warn("calculateMean: %s: %s" % (sys.exc_info()[0], e)) except: return None return stats.mean #security.declarePublic('calculateMedian') def calculateMedian(self, list): """ """ try: stats = Statistics(map((float), list)) #except Exception, e: # LOG.warn("calculateMedian: %s: %s" % (sys.exc_info()[0], e)) except: return None return stats.median #security.declarePublic('normalizeURL') def normalizeURL(self, url): """ Takes a URL (as returned by absolute_url(), for example) and replaces the hostname with the actual, fully-qualified hostname. """ url_parts = urlsplit(url) hostpart = url_parts[1] port = '' if hostpart.find(':') != -1: (hostname, port) = split(hostpart, ':') else: hostname = hostpart if hostname == 'localhost' or hostname == '127.0.0.1': hostname = getfqdn(gethostname()) else: hostname = getfqdn(hostname) if port: hostpart = join((hostname, port), ':') url = urlunsplit((url_parts[0], hostpart, \ url_parts[2], url_parts[3], url_parts[4])) return url #security.declarePublic('urlencode') def urlencode(self, *args, **kwargs): """ """ return urllib.urlencode(*args, **kwargs) #security.declarePublic('parseQueryString') def parseQueryString(self, *args, **kwargs): """ """ return cgi.parse_qs(*args, **kwargs) #security.declarePrivate('sendEmail') def sendEmail(self, addresses, subject, text): """ Send an e-mail message to the specified list of addresses. """ if not addresses: return portal_url = getToolByName(self, 'portal_url') #plone_utils = getToolByName(self, 'plone_utils') portal = portal_url.getPortalObject() fromAddress = portal.getProperty('email_from_address', None) #mailHost = plone_utils.getMailHost() #charset = plone_utils.getSiteEncoding() mailHost = getToolByName(portal, 'MailHost') #self.MailHost charset = portal.getProperty('email_charset', 'UTF-8') if fromAddress is None: LOG.error('Cannot send email: address or name is %s' % fromAddress) return try: if (type(text) == unicode): msg = MIMEText(text.encode(charset), 'plain', charset) else: msg = MIMEText(text, 'plain', charset) except Exception, e: LOG.error('Cannot send notification email: %s' % e) return try: if (type(subject) == unicode): subjHeader = Header(subject.encode(charset), charset) else: subjHeader = Header(subject, charset) except Exception, e: LOG.error('Cannot send notification email: %s' % e) return