class PloneSoftwareCenter(ATCTMixin, BaseBTreeFolder): """A simple folderish archetype for the Software Center.""" implements(ISoftwareCenterContent) content_icon = 'product_icon.gif' archetype_name = 'Software Center' metatype = 'PloneSoftwareCenter' immediate_view = default_view = 'plonesoftwarecenter_view' suppl_views = () global_allow = 1 filter_content_types = 1 allowed_content_types = ('PSCProject',) schema = PloneSoftwareCenterSchema _at_rename_after_creation = True security = ClassSecurityInfo() typeDescription = "A container for software projects" if NEEDS_UPDATE: # pre Plone3/GS-based install actions = updateActions(ATCTMixin, ({ 'id' : 'local_roles', 'name' : 'Sharing', 'action' : 'string:${object_url}/sharing', 'permissions' : (permissions.ManageProperties,), }, { 'id' : 'view', 'name' : 'View', 'action' : 'string:${folder_url}/', 'permissions' : (permissions.View,), }, ) ) def canSelectDefaultPage(self): return False # Validator security.declarePrivate('validate_availableCategories') def validate_availableCategories(self, value): """Ensure that when setting available categories, we don't accidentally set the same category id as an existing project. """ catalog = getToolByName(self, 'portal_catalog') projects = catalog.searchResults( portal_type = 'PSCProject', path = '/'.join(self.getPhysicalPath())) categoryIds = [v.split('|')[0].strip() for v in value] projectIds = [p.getId for p in projects] empty = [] invalid = [] for cat in categoryIds: if not cat: empty.append(cat) elif cat in projectIds or categoryIds.count(cat) > 1: invalid.append(cat) if empty: return "Please enter categories as Short Name | Long name | Description." if invalid: return "The following short category names are in use, either " \ "by another category, or by a project in the software " \ "center: %s." % (','.join(invalid)) else: return None # Vocabulary methods security.declareProtected(permissions.View, 'getAvailableTopicsFromClassifiers') def getAvailableTopicsFromClassifiers(self): """Get categories in DisplayList form, extracted from all classifiers that starts with 'Topic'""" field = self.getField('availableClassifiers') classifiers = field.getAsGrid(field) vocab = {} for id, title, trove_id in classifiers: if trove_id.startswith('Topic'): vocab[id] = (title, trove_id) return vocab security.declareProtected(permissions.View, 'getAvailableCategoriesAsDisplayList') def getAvailableCategoriesAsDisplayList(self): """Get categories in DisplayList form.""" return self.getField('availableCategories').getAsDisplayList(self) security.declareProtected(permissions.View, 'getAvailableClassifiersAsDisplayList') def getAvailableClassifiersAsDisplayList(self): return self.getField('availableClassifiers').getAsDisplayList(self) security.declareProtected(permissions.View, 'getAvailableLicensesAsDisplayList') def getAvailableLicensesAsDisplayList(self): """Get licenses in DisplayList form.""" return self.getField('availableLicenses').getAsDisplayList(self) security.declareProtected(permissions.View, 'getAvailableVersionsAsDisplayList') def getAvailableVersionsAsDisplayList(self): """Get versions in DisplayList form.""" return DisplayList([(item, item) for item in self.getAvailableVersions()]) security.declareProtected(permissions.View, 'getAvailableVersionsAsDisplayList') def getAvailableSelfCertificationCriteriaAsDisplayList(self): """Get self-certification criteria in DisplayList form.""" try: return DisplayList([(item, item) for item in self.getAvailableSelfCertificationCriteria()]) except: return DisplayList([('no','criteria')]) # Mutator methods security.declareProtected(permissions.ModifyPortalContent, 'setProjectEvaluators') def setProjectEvaluators(self, value, **kw): orig = self.getProjectEvaluators() self.getField('projectEvaluators').set(self, value, **kw) usersToDemote = [id for id in orig if id not in value] usersToPromote = [id for id in value if id not in orig] for id in usersToDemote: roles = list(self.get_local_roles_for_userid(id)) while 'PSCEvaluator' in roles: roles.remove('PSCEvaluator') if not roles: self.manage_delLocalRoles([id]) else: self.manage_setLocalRoles(id, roles) for id in usersToPromote: roles = list(self.get_local_roles_for_userid(id)) if 'PSCEvaluator' not in roles: roles.append('PSCEvaluator') self.manage_setLocalRoles(id, roles) security.declareProtected(permissions.View, 'getFileStorageStrategyVocab') def getFileStorageStrategyVocab(self): """returns registered storage strategies""" return getFileStorageVocab(self) security.declareProtected(permissions.ModifyPortalContent, 'setStorageStrategy') def setStorageStrategy(self, value, **kw): """triggers an event before changing the field""" # the event will raise an error if any # project failed to migrate from one storage # to another old = self.getStorageStrategy() if old != value: event.notify(StorageStrategyChanging(self, old, value)) self.getField('storageStrategy').set(self, value, **kw)
class PSCProject(ATCTMixin, OrderedBaseFolder): """Project class that holds the information about the Software Project. """ implements(IProjectContent) archetype_name = 'Software Project' immediate_view = default_view = 'psc_project_view' suppl_views = () content_icon = 'product_icon.png' schema = PSCProjectSchema _at_rename_after_creation = True global_allow = False allowed_content_types = ( 'PSCReleaseFolder', 'PSCImprovementProposalFolder', 'PSCDocumentationFolder', ) security = ClassSecurityInfo() typeDescMsgId = 'description_edit_package' typeDescription = ('A Software Project contains details about a ' 'particular software package. It can keep track ' 'of meta-data about the project, as well as ' 'releases and improvement proposals.') if NEEDS_UPDATE: actions = updateActions(ATCTMixin, ( { 'id': 'local_roles', 'name': 'Sharing', 'action': 'string:${object_url}/sharing', 'permissions': (permissions.ManageProperties, ), }, { 'id': 'view', 'name': 'View', 'action': 'string:${folder_url}/', 'permissions': (permissions.View, ), }, )) def canSelectDefaultPage(self): return False security.declarePrivate('initializeArchetype') def initializeArchetype(self, **kwargs): """Initialize package. Projects are initialized with a release folder. """ OrderedBaseFolder.initializeArchetype(self, **kwargs) # if self.haveHelpCenter() and \ # not self.objectIds('PSCDocumentationFolder'): # self.invokeFactory('PSCDocumentationFolder', # config.DOCUMENTATION_ID) if not self.objectIds('PSCReleaseFolder'): self.invokeFactory('PSCReleaseFolder', config.RELEASES_ID) # if not self.objectIds('PSCImprovementProposalFolder'): # self.invokeFactory('PSCImprovementProposalFolder', # config.IMPROVEMENTS_ID) if not self.hasProperty('releaseCount'): self.manage_addProperty('releaseCount', 0, 'int') def _setAndIndexField(self, field_name, index_name, value): self.getField(field_name).set(self, value) self.reindexObject(idxs=[index_name]) catalog = getToolByName(self, 'portal_catalog') res = catalog.searchResults(portal_type=[ 'PSCRelease', 'PSCFile', 'PSCFileLink', 'PSCImprovementProposal' ], path='/'.join(self.getPhysicalPath())) for r in res: o = r.getObject() o.reindexObject(idxs=[index_name]) security.declareProtected(permissions.View, 'getView') def getView(self): """returns the view object""" return getMultiAdapter((self, self.REQUEST), name='project_view') security.declareProtected(permissions.ModifyPortalContent, 'setClassifiers') def setClassifiers(self, value): """Overrides classifiers mutator so we can reindex internal content. """ self._setAndIndexField('classifiers', 'getClassifiers', value) security.declareProtected(permissions.ModifyPortalContent, 'setCategories') def setCategories(self, value): """Overrides categories mutator so we can reindex internal content. """ self._setAndIndexField('categories', 'getCategories', value) security.declareProtected(permissions.View, 'getCategoryTitles') def getCategoryTitles(self): """Return selected categories as a list of category long names, for the user interface. """ vocab = self.getCategoriesVocab() values = [vocab.getValue(c) or c for c in self.getCategories()] values.sort() return values security.declareProtected(permissions.View, 'getVocabularyTitlesFromCLassifiers') def getVocabularyTitlesFromCLassifiers(self): """Return selected categories as a list of category long names, for the user interface. Uses the classifiers. """ vocab = self.getClassifiersVocab() values = [vocab.getValue(c) or c for c in self.getClassifiers()] values.sort() return values security.declareProtected(permissions.View, 'getClassifiersVocab') def getClassifiersVocab(self): """Get classifiers vocabulary from parent project area via acquisition. """ return self.getAvailableClassifiersAsDisplayList() security.declareProtected(permissions.View, 'getCategoriesVocab') def getCategoriesVocab(self): """Get categories vocabulary from parent project area via acquisition. """ return self.getAvailableCategoriesAsDisplayList() security.declareProtected(permissions.View, 'getSelfCertificationCriteriaVocab') def getSelfCertificationCriteriaVocab(self): """Get self-certification criteria vocabulary from parent project area via acquisition. """ return self.getAvailableSelfCertificationCriteriaAsDisplayList() security.declareProtected(permissions.View, 'getReleaseFolder') def getReleaseFolder(self): """Get the release folder. We only should have one, so only deal with case of single folder. May return None if no roadmap folder exists. """ type_filter = {'portal_type': 'PSCReleaseFolder'} folders = self.contentValues(filter=type_filter) if folders: return folders[0] else: return None security.declareProtected(permissions.View, 'getRoadmapFolder') def getRoadmapFolder(self): """Get the roadmap folder. We only should have one, so only deal with case of single folder. May return None if no roadmap folder exists. """ type_filter = {'portal_type': 'PSCImprovementProposalFolder'} folders = self.contentValues(filter=type_filter) if folders: return folders[0] else: return None security.declareProtected(permissions.View, 'getNotAddableTypes') def getNotAddableTypes(self): """Hide the release container types if it already exists. """ ignored = [] objectIds = self.objectIds() if config.RELEASES_ID in objectIds: ignored.append('PSCReleaseFolder') if config.IMPROVEMENTS_ID in objectIds: ignored.append('PSCImprovementProposalFolder') if not self.haveHelpCenter() or \ config.DOCUMENTATION_ID in objectIds: ignored.append('PSCDocumentationFolder') if config.TRACKER_ID in objectIds: ignored.append('PoiPscTracker') return ignored security.declareProtected(permissions.View, 'getVersionsVocab') def getVersionsVocab(self): """To ensure PloneHelpCenter integration works, return the versions defined, by looking at the versions found in the releases. """ catalog = getToolByName(self, 'portal_catalog') releases = catalog.searchResults(portal_type='PSCRelease', path='/'.join(self.getPhysicalPath())) return [r.getId for r in releases if r] security.declareProtected(permissions.View, 'getCurrentVersions') def getCurrentVersions(self): """To ensure PloneHelpCenter integration works, return the versions which are supported, by looking at the versions found in the releases and subtracting the ones listed in unsupportedVersions. """ allVersions = self.getVersionsVocab() unsupported = self.getUnsupportedVersions() return [v for v in allVersions if v not in unsupported] security.declareProtected(permissions.View, 'haveHelpCenter') def haveHelpCenter(self): """Test to see if PloneHelpCenter is installed """ ttool = getToolByName(self, 'portal_types') if 'HelpCenter' in ttool.objectIds(): return True else: return False security.declareProtected(permissions.View, 'getAvailableFeaturesAsDisplayList') def getAvailableFeaturesAsDisplayList(self): """Get list of Improvement Proposals in DisplayList form.""" catalog = getToolByName(self, 'portal_catalog') projectPath = self.getPhysicalPath() if len(projectPath) > 1 and projectPath[-1] == 'portal_factory': projectPath = projectPath[:-2] search = catalog.searchResults(portal_type='PSCImprovementProposal', path='/'.join(projectPath)) lst = DisplayList() for i in search: title = i.Title if len(title) > 40: title = title[:40] + '...' lst.add(i.UID, title) return lst def _distUtilsNameAvailable(self, ids): current_id = self.getId() sc = self.getParentNode() existing_projects = get_projects_by_distutils_ids(sc, ids) # make sure the names are not in another project already for project_id in existing_projects: if project_id != current_id: return False return True def getCompatibility(self): '''Get the compatibility of the product by getting the compatabilities of the LATEST release. This is used primarily by the index. ''' compatabilities = [] release = self.getLatestRelease() if release: for release_compatability in release.getCompatibility: compatabilities.append(release_compatability) compatabilities.sort(reverse=True) return set(compatabilities) def getLatestRelease(self): """Get the most recent final release brain or None if none can be found. """ releaseFolder = self.getReleaseFolder() res = None if releaseFolder: catalog = getToolByName(self, 'portal_catalog') res = catalog.searchResults(path='/'.join( releaseFolder.getPhysicalPath()), review_state='final', sort_on='Date', sort_order='reverse', portal_type='PSCRelease') if not res: return None else: return res[0] def mayBeUnmaintained(self): """Return True if there hasn't been a release in over a year""" lastRelease = self.getLatestReleaseDate() if not lastRelease: return False if DateTime.DateTime() - lastRelease > 360: return True return False def getLatestReleaseDate(self): """ Return the effective date of the last release. This is currently used for index and catalog data. """ latest = self.getLatestRelease() if latest: return latest.effective return None security.declareProtected(permissions.ModifyPortalContent, 'setDistutilsSecondaryIds') def setDistutilsSecondaryIds(self, names): if not self._distUtilsNameAvailable(names): raise Unauthorized self.getField('distutilsSecondaryIds').set(self, names) self.reindexObject() security.declareProtected(permissions.ModifyPortalContent, 'setDistutilsMainId') def setDistutilsMainId(self, name): if not self._distUtilsNameAvailable([name]): raise Unauthorized self.getField('distutilsMainId').set(self, name) self.reindexObject()
class TemplateUploadCenter(folder.ATFolder): """A Center to upload templates to a Plone site""" implements(ITemplateUploadCenter) archetype_name = 'Template Center' meta_type = "TemplateUploadCenter" immediate_view = default_view = "templateuploadcenter_view" suppl_views = () global_allow = 1 filter_content_types = 1 allowed_content_types = ('PSCProject', ) schema = TemplateUploadCenterSchema _at_rename_after_creation = True security = ClassSecurityInfo() typeDescription = "A container for software projects" if NEEDS_UPDATE: # pre Plone3/GS-based install actions = updateActions(ATCTMixin, ( { 'id': 'local_roles', 'name': 'Sharing', 'action': 'string:${object_url}/sharing', 'permissions': (permissions.ManageProperties, ), }, { 'id': 'view', 'name': 'View', 'action': 'string:${folder_url}/', 'permissions': (permissions.View, ), }, )) # Vocabulary methods security.declareProtected(permissions.View, 'getAvailableTopicsFromClassifiers') def getAvailableTopicsFromClassifiers(self): """Get categories in DisplayList form, extracted from all classifiers that starts with 'Topic'""" field = self.getField('availableClassifiers') classifiers = field.getAsGrid(field) vocab = {} for id, title, trove_id in classifiers: if trove_id.startswith('Topic'): vocab[id] = (title, trove_id) return vocab security.declareProtected(permissions.View, 'getAvailableCategoriesAsDisplayList') def getAvailableCategoriesAsDisplayList(self): """Get categories in DisplayList form.""" return self.getField('availableCategories').getAsDisplayList(self) security.declareProtected(permissions.View, 'getAvailableClassifiersAsDisplayList') def getAvailableClassifiersAsDisplayList(self): return self.getField('availableClassifiers').getAsDisplayList(self) security.declareProtected(permissions.View, 'getAvailableLicensesAsDisplayList') def getAvailableLicensesAsDisplayList(self): """Get licenses in DisplayList form.""" return self.getField('availableLicenses').getAsDisplayList(self) security.declareProtected(permissions.View, 'getAvailableVersionsAsDisplayList') def getAvailableVersionsAsDisplayList(self): """Get versions in DisplayList form.""" return DisplayList([(item, item) for item in self.getAvailableVersions()]) security.declareProtected(permissions.View, 'getAvailableVersionsAsDisplayList') def getAvailableSelfCertificationCriteriaAsDisplayList(self): """Get self-certification criteria in DisplayList form.""" try: return DisplayList([ (item, item) for item in self.getAvailableSelfCertificationCriteria() ]) except: return DisplayList([('no', 'criteria')]) # Mutator methods security.declareProtected(permissions.ModifyPortalContent, 'setProjectEvaluators') def setProjectEvaluators(self, value, **kw): orig = self.getProjectEvaluators() self.getField('projectEvaluators').set(self, value, **kw) usersToDemote = [id for id in orig if id not in value] usersToPromote = [id for id in value if id not in orig] for id in usersToDemote: roles = list(self.get_local_roles_for_userid(id)) while 'PSCEvaluator' in roles: roles.remove('PSCEvaluator') if not roles: self.manage_delLocalRoles([id]) else: self.manage_setLocalRoles(id, roles) for id in usersToPromote: roles = list(self.get_local_roles_for_userid(id)) if 'PSCEvaluator' not in roles: roles.append('PSCEvaluator') self.manage_setLocalRoles(id, roles) security.declareProtected(permissions.View, 'getFileStorageStrategyVocab') def getFileStorageStrategyVocab(self): """returns registered storage strategies""" return getFileStorageVocab(self) security.declareProtected(permissions.ModifyPortalContent, 'setStorageStrategy') def setStorageStrategy(self, value, **kw): """triggers an event before changing the field""" # the event will raise an error if any # project failed to migrate from one storage # to another old = self.getStorageStrategy() if old != value: event.notify(StorageStrategyChanging(self, old, value)) self.getField('storageStrategy').set(self, value, **kw)