Author: aclark Date: Sun Mar 2 14:41:48 2008 New Revision: 59826 Added: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/pypi.py - copied unchanged from r59825, Products.PloneSoftwareCenter/trunk/Products/PloneSoftwareCenter/browser/pypi.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/pypisimple.pt - copied unchanged from r59825, Products.PloneSoftwareCenter/trunk/Products/PloneSoftwareCenter/browser/pypisimple.pt Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/pypisimple.py - copied unchanged from r59825, Products.PloneSoftwareCenter/trunk/Products/PloneSoftwareCenter/browser/pypisimple.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/verify_filetype.py - copied unchanged from r59825, Products.PloneSoftwareCenter/trunk/Products/PloneSoftwareCenter/browser/verify_filetype.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/version_predicate.py - copied unchanged from r59825, Products.PloneSoftwareCenter/trunk/Products/PloneSoftwareCenter/browser/version_predicate.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/tests/TROVE.txt - copied unchanged from r59825, Products.PloneSoftwareCenter/trunk/Products/PloneSoftwareCenter/tests/TROVE.txt Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/tests/project.egg - copied unchanged from r59825, Products.PloneSoftwareCenter/trunk/Products/PloneSoftwareCenter/tests/project.egg Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/tests/project.tgz - copied unchanged from r59825, Products.PloneSoftwareCenter/trunk/Products/PloneSoftwareCenter/tests/project.tgz Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/tests/pypi.txt - copied unchanged from r59825, Products.PloneSoftwareCenter/trunk/Products/PloneSoftwareCenter/tests/pypi.txt Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/tests/test_functional.py - copied unchanged from r59825, Products.PloneSoftwareCenter/trunk/Products/PloneSoftwareCenter/tests/test_functional.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/tests/test_pypi.py - copied unchanged from r59825, Products.PloneSoftwareCenter/trunk/Products/PloneSoftwareCenter/tests/test_pypi.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/tests/test_trove.py - copied unchanged from r59825, Products.PloneSoftwareCenter/trunk/Products/PloneSoftwareCenter/tests/test_trove.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/trove.py - copied unchanged from r59825, Products.PloneSoftwareCenter/trunk/Products/PloneSoftwareCenter/trove.py Removed: Products.PloneSoftwareCenter/branches/blob-support/Products.PloneSoftwareCenter.egg-info/ Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/README.txt Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/TODO.txt Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/category.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/configure.zcml Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/project.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/root.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/config.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/configure.zcml Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/content/project.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/content/release.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/content/root.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/profiles/default/catalog.xml Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/profiles/default/types/PloneSoftwareCenter.xml Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/profiles/default/workflows/psc_package_workflow/definition.xml Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/skins/plonesoftwarecenter/plonesoftwarecenter.css.dtml Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/skins/plonesoftwarecenter/plonesoftwarecenter_ploneorg.pt Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/skins/plonesoftwarecenter/psc_project_view.pt Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/tests/test_project.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/tests/test_security.py Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/tests/test_softwarecenter.py Log: Merge trunk to branch. Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/README.txt ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/README.txt (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/README.txt Sun Mar 2 14:41:48 2008 @@ -62,3 +62,7 @@ Development, Maintenance -- "Alex Clark":http://aclark.net + + PyPI integration, from an initial branch started by Sidnei -- + "Tarek Ziadé":mailto:ta...@zi..., "Ingeniweb":http://ingeniweb.com + Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/TODO.txt ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/TODO.txt (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/TODO.txt Sun Mar 2 14:41:48 2008 @@ -141,7 +141,7 @@ [ ] - Link to other reviews from this person, reviewer stats [ ] o Add ratings to products - [ ] - Simple thumbs up/down rating (also on documentation) + [ ] - Simple thumbs up/down rating (also on documentation) or Amazon style star ratings [ ] o Add development blog/press release type, aggregator? [ ] - How to avoid duplication of e.g. release notes? Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/category.py ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/category.py (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/category.py Sun Mar 2 14:41:48 2008 @@ -32,7 +32,10 @@ if states: query['review_state'] = states if category: - query['getCategories'] = category + if self.context.getUseClassifiers(): + query['getClassifiers'] = category + else: + query['getCategories'] = category if limit: query['sort_limit'] = limit if sort_on: Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/configure.zcml ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/configure.zcml (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/configure.zcml Sun Mar 2 14:41:48 2008 @@ -112,4 +112,45 @@ class=".migration.MigrationView" permission="cmf.ManagePortal" /> + <!-- Pypi APIs --> + <page + name="file_upload" + for="Products.PloneSoftwareCenter.interfaces.ISoftwareCenterContent" + class=".pypi.PyPIView" + permission="zope2.View" + attribute="file_upload" + /> + + <page + name="submit" + for="Products.PloneSoftwareCenter.interfaces.ISoftwareCenterContent" + class=".pypi.PyPIView" + permission="zope2.View" + attribute="submit" + /> + + <page + name="simple" + for="Products.PloneSoftwareCenter.interfaces.ISoftwareCenterContent" + class=".pypisimple.PyPISimpleView" + permission="zope2.View" + template="pypisimple.pt" + /> + + <page + name="list_classifiers" + for="Products.PloneSoftwareCenter.interfaces.ISoftwareCenterContent" + class=".pypi.PyPIView" + permission="zope2.View" + attribute="list_classifiers" + /> + + <page + name="verify" + for="Products.PloneSoftwareCenter.interfaces.ISoftwareCenterContent" + class=".pypi.PyPIView" + permission="zope2.View" + attribute="verify" + /> + </configure> Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/project.py ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/project.py (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/project.py Sun Mar 2 14:41:48 2008 @@ -92,8 +92,10 @@ def display_categories(self): """Get a list of categories, separated by commas, for display """ - - return ', '.join(self.context.getCategoryTitles()) + if self.context.useClassifiers: + return ', '.join(self.context.getVocabularyTitlesFromCLassifiers()) + else: + return ', '.join(self.context.getCategoryTitles()) def similar_search_url(self): """Get a url to search for projects by the same author Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/root.py ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/root.py (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/browser/root.py Sun Mar 2 14:41:48 2008 @@ -50,7 +50,7 @@ """Return number of releases """ return len(self.catalog(portal_type = 'PSCRelease', path = self.context_path)) - + def categories(self): """Get categories to list @@ -59,38 +59,58 @@ releases is a list of dicts with keys title, description, parent_url, review_state, date + """ def parent_url(url): return '/'.join(url.split('/')[:-2]) - vocab = self.context.getAvailableCategoriesAsDisplayList() - uniqueCategories = self.catalog.uniqueValuesFor('getCategories') - field = self.context.getField('availableCategories') - + # we are using either classifiers, either categories + if self.context.getUseClassifiers(): + filtered_values = self.catalog.uniqueValuesFor('getClassifiers') + field = self.context.getField('availableClassifiers') + vocab = self.context.getAvailableTopicsFromClassifiers() + classifiers = field.getAsGrid(field) + field_name = 'getClassifiers' + else: + vocab = self.context.getAvailableCategoriesAsDisplayList() + filtered_values = self.catalog.uniqueValuesFor('getCategories') + field = self.context.getField('availableCategories') + field_name = 'getCategories' + for cat in vocab.keys(): - if cat in uniqueCategories: + if cat in filtered_values: id = field.lookup(self.context, cat, 0) name = field.lookup(self.context, cat, 1) description = field.lookup(self.context, cat, 2) - rss_url = "%s/search_rss?portal_type=PSCRelease&sort_on=Date&sort_order=reverse&path=%s&getCategories=%s&review_state=alpha&review_state=beta&review_state=release-candidate&review_state=final" % (self.portal_url, self.context_path, cat,) + rss_url = ("%s/search_rss?portal_type=PSCRelease&sort_on=Date&" + "sort_order=reverse&path=%s&getCategories=%s&" + "review_state=alpha&review_state=beta&" + "review_state=release-candidate&" + "review_state=final") % (self.portal_url, + self.context_path, + cat) releases = [] - for r in self.catalog(path = self.context_path, - portal_type = 'PSCRelease', - getCategories = cat, - sort_on = 'Date', - sort_order = 'reverse', - sort_limit = 5)[:5]: + release_query = {'path': self.context_path, + 'portal_type': 'PSCRelease', + field_name: cat, + 'sort_on': 'Date', + 'sort_order': 'reverse', + 'sort_limit': 5} + + project_query = {field_name: cat, + 'portal_type': 'PSCProject', + 'path': self.context_path} + + for r in self.catalog(**release_query)[:5]: releases.append(dict(title = r.Title, description = r.Description, parent_url = parent_url(r.getURL()), review_state = r.review_state, date = r.Date)) - num_projects = len(self.catalog(path = self.context_path, - portal_type = 'PSCProject', - getCategories = cat)) + num_projects = len(self.catalog(**project_query)) yield dict(name = name, description = description, rss_url = rss_url, releases = releases, Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/config.py ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/config.py (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/config.py Sun Mar 2 14:41:48 2008 @@ -2,6 +2,10 @@ $Id$ """ from zLOG import LOG, PROBLEM +import os +from Globals import package_home +from trove import TroveClassifier + # Use ExternalStorage for PSCFile? USE_EXTERNAL_STORAGE = False @@ -51,3 +55,7 @@ LOG('PloneSoftwareCenter', PROBLEM, 'ExternalStorage N/A, falling back to AttributeStorage') USE_EXTERNAL_STORAGE = False + +trove_default = os.path.join(package_home(GLOBALS), 'TROVE.txt') +trove = TroveClassifier(trove_default) + Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/configure.zcml ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/configure.zcml (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/configure.zcml Sun Mar 2 14:41:48 2008 @@ -14,6 +14,15 @@ description="What's powering the products section on plone.org" provides="Products.GenericSetup.interfaces.EXTENSION" /> + <five:traversable class=".content.release.PSCRelease" /> + + <content class=".content.project.PSCProject"> + <implements + interface="contentratings.interfaces.IUserRatable + contentratings.interfaces.IEditorRatable + zope.app.annotation.interfaces.IAttributeAnnotatable" + /> + </content> <!-- Avoid deprecation warnings for manage_afterAdd and friends that only show up because some base classes that we inherit from use Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/content/project.py ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/content/project.py (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/content/project.py Sun Mar 2 14:41:48 2008 @@ -70,6 +70,22 @@ rows=25, ), ), + + LinesField('classifiers', + multiValued=1, + required=1, + vocabulary='getClassifiersVocab', + enforceVocabulary=1, + index='KeywordIndex:schema', + widget=MultiSelectionWidget( + label='Classifiers', + label_msgid='label_classifiers', + description='Trove classifiers for this item.', + description_msgid='help_classifiers', + i18n_domain='plonesoftwarecenter', + rows=6, + ), + ), LinesField('categories', multiValued=1, @@ -86,6 +102,21 @@ ), ), + LinesField('classifiers', + multiValued=1, + required=1, + vocabulary='getClassifiersVocab', + enforceVocabulary=1, + index='KeywordIndex:schema', + widget=MultiSelectionWidget( + label='Classifiers', + label_msgid="label_classifiers", + description='Classifiers that this item should appear in.', + description_msgid="help_classifiers", + i18n_domain="plonesoftwarecenter", + ), + ), + LinesField('selfCertifiedCriteria', multiValued=1, required=0, @@ -327,13 +358,9 @@ if not self.hasProperty('releaseCount'): self.manage_addProperty('releaseCount', 0, 'int') - security.declareProtected(permissions.ModifyPortalContent, - 'setCategories') - def setCategories(self, value): - """Overrides categories mutator so we can reindex internal content. - """ - self.getField('categories').set(self, value) - self.reindexObject(idxs=['getCategories']) + 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', @@ -343,7 +370,22 @@ path='/'.join(self.getPhysicalPath())) for r in res: o = r.getObject() - o.reindexObject(idxs=['getCategories']) + o.reindexObject(idxs=[index_name]) + + + 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): @@ -354,6 +396,24 @@ 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): @@ -445,24 +505,21 @@ 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)) - - items = [s for s in search] - items.sort(lambda x, y: cmp(int(x.getId), int(y.getId))) - lst = DisplayList() - for i in items: - title = i.Title - if len(title) > 40: - title = title[:40] + '...' - - lst.add(i.UID, title) + """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 registerType(PSCProject, config.PROJECTNAME) Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/content/release.py ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/content/release.py (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/content/release.py Sun Mar 2 14:41:48 2008 @@ -38,9 +38,10 @@ label='Version', label_msgid='label_release_version', description="This field is also used in the URL " - "of the item, so don't use spaces and special " - "characters. Do NOT include alpha/beta states, this is handled by the " - "workflow states. Example: '0.4'.", + "of the item, so please don't use spaces and special " + "characters. Also, please do NOT include the alpha, beta, or release candidate " + "state as this is handled by the " + "workflow. Example: '0.1'.", description_msgid='help_release_version', i18n_domain='plonesoftwarecenter', ), @@ -222,7 +223,7 @@ LinesField( name='compatibility', - required=0, + required=1, searchable=1, index='KeywordIndex:schema', vocabulary='getCompatibilityVocab', Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/content/root.py ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/content/root.py (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/content/root.py Sun Mar 2 14:41:48 2008 @@ -21,9 +21,10 @@ from Products.ArchAddOn.Fields import SimpleDataGridField from Products.ArchAddOn.Widgets import SimpleDataGridWidget - +from Products.ATReferenceBrowserWidget.ATReferenceBrowserWidget import ReferenceBrowserWidget from Products.PloneSoftwareCenter.config import PROJECTNAME +from Products.PloneSoftwareCenter.config import trove PloneSoftwareCenterSchema = OrderedBaseFolder.schema.copy() + Schema(( @@ -41,8 +42,41 @@ ), ), + SimpleDataGridField('availableClassifiers', + columns=3, + column_names=('id', 'title', 'trove-id'), + default=trove.get_datagrid(), + widget=SimpleDataGridWidget( + label='Classifiers', + label_msgid='label_classifiers', + description=('Define the Trove classifiers. ' + 'The format is "Id | Title | Trove id". ' + 'The Id must be unique, and Trove id corresponds ' + 'to the Trove value'), + description_msgid='help_classifiers_vocab', + i18n_domain='plonesoftwarecenter', + rows=6, + ), + ), + + BooleanField('useClassifiers', + required=0, + widget=BooleanWidget( + label="Use Classifiers to display Categories (with Topic :: *).", + label_msgid="label_use_classifier", + description_msgid="description_use_classifier", + description=("Indicate whether the Software Center uses the " + "Classifiers field to display projects. " + "In that case it gets all lines starting with " + "'Topic' and builds the category list with them." + ), + i18n_domain="plonesoftwarecenter", + ), + ), + + SimpleDataGridField('availableCategories', - columns=3, + columns=3, column_names=('id', 'title', 'description'), default=[ 'standalone|Stand-alone products|Projects that are self-contained.', @@ -84,6 +118,7 @@ LinesField('availableVersions', default=[ + 'Plone 3.0', 'Plone 2.5', 'Plone 2.1', 'Plone 2.0.5', @@ -151,8 +186,35 @@ ), ), -)) + ReferenceField('featuredProject', + multiValued=0, + allowed_types=('PSCProject',), + relationship='Rel1', + widget=ReferenceBrowserWidget( + default_search_index='SearchableText', + label='Featured Project', + label_msgid='label_featured_project', + description='Featured project for the software center.', + description_msgid='help_featured_project', + i18n_domain='plonesoftwarecenter', + ), + ), + ReferenceField('featuredProjectRelease', + multiValued=0, + allowed_types=('PSCRelease',), + relationship='Rel2', + widget=ReferenceBrowserWidget( + default_search_index='SearchableText', + label='Featured Project Release', + label_msgid='label_featured_project_release', + description='Featured project release for the featured project of the software center.', + description_msgid='help_featured_project', + i18n_domain='plonesoftwarecenter', + ), + ), + +)) class PloneSoftwareCenter(ATCTMixin, BaseBTreeFolder): """A simple folderish archetype for the Software Center.""" @@ -228,14 +290,31 @@ else: return None - # Vocabulary methods - - security.declareProtected(permissions.View, 'getAvailableCategoriesAsDisplayList') + 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.""" Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/profiles/default/catalog.xml ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/profiles/default/catalog.xml (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/profiles/default/catalog.xml Sun Mar 2 14:41:48 2008 @@ -3,6 +3,9 @@ <index name="getCategories" meta_type="KeywordIndex"> <indexed_attr value="getCategories"/> </index> + <index name="getClassifiers" meta_type="KeywordIndex"> + <indexed_attr value="getClassifiers"/> + </index> <index name="getCompatibility" meta_type="KeywordIndex"> <indexed_attr value="getCompatibility"/> </index> @@ -17,6 +20,7 @@ </index> <column value="UID"/> <column value="getCategories"/> + <column value="getClassifiers"/> <column value="getCategoryTitles"/> <column value="getCompatibility"/> <column value="getProposalTypes"/> Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/profiles/default/types/PloneSoftwareCenter.xml ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/profiles/default/types/PloneSoftwareCenter.xml (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/profiles/default/types/PloneSoftwareCenter.xml Sun Mar 2 14:41:48 2008 @@ -19,6 +19,7 @@ <property name="default_view">plonesoftwarecenter_view</property> <property name="view_methods"> <element value="plonesoftwarecenter_view"/> + <element value="plonesoftwarecenter_ploneorg"/> </property> <property name="default_view_fallback">False</property> <alias from="(Default)" to="(dynamic view)"/> Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/profiles/default/workflows/psc_package_workflow/definition.xml ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/profiles/default/workflows/psc_package_workflow/definition.xml (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/profiles/default/workflows/psc_package_workflow/definition.xml Sun Mar 2 14:41:48 2008 @@ -70,6 +70,7 @@ </permission-map> <permission-map name="Add portal content" acquired="False"> <permission-role>Manager</permission-role> + <permission-role>Owner</permission-role> </permission-map> <permission-map name="Modify portal content" acquired="False"> Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/skins/plonesoftwarecenter/plonesoftwarecenter.css.dtml ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/skins/plonesoftwarecenter/plonesoftwarecenter.css.dtml (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/skins/plonesoftwarecenter/plonesoftwarecenter.css.dtml Sun Mar 2 14:41:48 2008 @@ -52,5 +52,8 @@ color: #74AE0B; } +.poweredBy { +} + /* </dtml-with> */ Modified: Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/skins/plonesoftwarecenter/plonesoftwarecenter_ploneorg.pt ============================================================================== --- Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/skins/plonesoftwarecenter/plonesoftwarecenter_ploneorg.pt (original) +++ Products.PloneSoftwareCenter/branches/blob-support/Products/PloneSoftwareCenter/skins/plonesoftwarecenter/plonesoftwarecenter_ploneorg.pt Sun Mar 2 14:41:48 2008 @@ -6,283 +6,287 @@ <head> <metal:css fill-slot="css_slot"> - <style type="text/css" media="all" - tal:content="string:@import url($portal_url/plonesoftwarecenter.css);"></style> - -<style type="text/css"> -.screenshot { - float: right; -} -.project { - margin-top: 1em; - padding-top: 1em; - border-top: 1px solid #8CACBB; -} -</style> - + <style type="text/css" media="all" + tal:content="string:@import url($portal_url/plonesoftwarecenter.css);"> + </style> + <style type="text/css"> + .screenshot { + float: right; + } + .project { + margin-top: 1em; + padding-top: 1em; + border-top: 1px solid #8CACBB; + } + </style> </metal:css> - <metal:block fill-slot="top_slot"> - <tal:noborder condition="python:not user.has_permission('Modify portal content', context)"> - <tal:noborderset tal:define="dummy python:request.set('disable_border', 1)" /> - </tal:noborder> + <tal:noborder condition="python:not user.has_permission('Modify portal content', context)"> + <tal:noborderset tal:define="dummy python:request.set('disable_border', 1)" /> + </tal:noborder> </metal:block> </head> <body> + <div metal:fill-slot="main" + tal:define="view context/@@category_view; + global getInfoFor nocall:here/portal_workflow/getInfoFor; + catalog nocall:here/portal_catalog; + herePath python:'/'.join(here.getPhysicalPath()); + currentCategory request/getCategories | string:; + currentVersion request/getCompatibility | string:current; + currentVersion python:test(currentVersion=='current',['Plone 3.0','Plone 2.5'],currentVersion); + currentVersion python:test(currentVersion=='any','',currentVersion); + getCategoryName nocall:view/category_name"> + + <div metal:use-macro="here/document_actions/macros/document_actions"> + Document actions (print, sendto etc) + </div> - <div metal:fill-slot="main" - tal:define="view context/@@category_view; - global getInfoFor nocall:here/portal_workflow/getInfoFor; - catalog nocall:here/portal_catalog; - herePath python:'/'.join(here.getPhysicalPath()); - currentCategory request/getCategories | string:; - currentVersion request/getCompatibility | string:current; - currentVersion python:test(currentVersion=='current',['Plone 3.0','Plone 2.5'],currentVersion); - currentVersion python:test(currentVersion=='any','',currentVersion); - getCategoryName nocall:view/category_name"> - - - - <div metal:use-macro="here/document_actions/macros/document_actions"> - Document actions (print, sendto etc) - </div> - - <h1 tal:content="here/title_or_id" class="documentFirstHeading"> - Title or id - </h1> - - <div class="feedButton" > - <a href="" tal:attributes="href string:$here_url/search_rss?portal_type=PSCRelease&sort_on=Date&sort_order=reverse&review_state=alpha&review_state=beta&review_state=release-candidate&review_state=final"> - <img i18n:attributes="title title_rss_feed; alt label_rss_feed;" - tal:attributes="src string:$portal_url/rss.gif" - src="rss.gif" - alt="RSS Feed" - title="RSS feed of the latest releases" /> - </a> - </div> - - <p class="discreet" - tal:define="projectsCount python:len(catalog(portal_type = 'PSCProject', path = herePath)); - releasesCount python:len(catalog(portal_type = 'PSCRelease', path = herePath))"> - Currently listing <span tal:replace="projectsCount" /> projects with <span tal:replace="releasesCount" /> releases. - </p> - - - <p class="documentDescription" - tal:content="here/Description"> - Description. - </p> - - <div class="visualClear"><!-- --></div> + <h1 tal:content="here/title_or_id" class="documentFirstHeading"> + Title or id + </h1> + + <div class="feedButton" > + <a href="" tal:attributes="href string:$here_url/search_rss?portal_type=PSCRelease"><img + i18n:attributes="title title_rss_feed; alt label_rss_feed;" + tal:attributes="src string:$portal_url/rss.gif" src="rss.gif" + alt="RSS Feed" title="RSS feed of the latest releases" /></a> + </div> - <dl class="portlet" style="width: 23em; float: left"> - <dt class="portletHeader">Get Plone for your platform</dt> - <dd class="portletItem" style="text-align: center"> - <a href="/products/plone"><img height="40" width="40" alt="Windows" src="/platform_windows.gif" /><img height="40" width="40" alt="Mac" src="/platform_mac_os_x.gif" /><img height="40" width="40" alt="Linux" src="/platform_linux.gif" /></a> - <h3><a href="/products/plone" style="border-bottom: 1px solid #8CACBB">Download Plone</a></h3> - <span class="discreet">Latest release: Plone 3.0.2 (Oct 2007)</span> - </dd> - </dl> - - <dl class="portlet" style="float:right; width: 23em;"> - <dt class="portletHeader">Search for add-on products</dt> - - <dd class="portletItem"> - <form style="text-align: center;" - name="searchform" id="searchform" - action="/search" tal:attributes="action string:${portal_url}/search"> - - <label for="searchGadget" - class="hiddenStructure"> - Search add-ons - </label> - <input type="hidden" name="path" value="/Plone/products" tal:attributes="value python:'/'.join(here.getPhysicalPath())" /> - <input type="hidden" name="portal_type" value="PSCProject" /> - <input name="SearchableText" type="text" - size="12" - style="font-size: 150%" - title="Search add-ons" - accesskey="accesskeys-search" - tabindex="0" /> - - <input class="searchButton" name="Search" - style="font-size: 150%; background-position: 3px 5px; padding-left: 25px; padding-right: 10px" - type="submit" value="Search" - accesskey="accesskeys-search" /> - </form> - </dd> - </dl> - - <dl class="portlet" style="float:right; clear: right; width: 23em"> - - <dt class="portletHeader">Want to list your add-on product?</dt> - <dd class="portletItem" style="text-align: center"> - <form action="createObject" - tal:condition="python: user.has_permission('PloneSoftwareCenter: Add Project', context)"> - <input name="type_name" - type="hidden" - value="PSCProject" - /> - <input class="standalone" - type="submit" - style="font-size: 110%; padding: 3px 5px 3px 26px; background-position: 10px 5px" - value="Add new project" - /> - </form> - - <form tal:condition="python: not user.has_permission('Add portal content', context) and mtool.isAnonymousUser()" - tal:define="pss modules/Products/PythonScripts/standard" - tal:attributes="action python:'%s/login_form?came_from=%s' % - (here.portal_url(), - pss.url_quote(request['URL']))"> - - <input class="standalone" - type="submit" - style="font-size: 110%; padding: 3px 5px 3px 26px; background-position: 10px 5px" - value="Log in to add your project" - i18n:attributes="value" - /> - </form> - </dd> - </dl> - - <!-- <select style="float: left;" - onchange="window.location.href=this.options[this.selectedIndex].value"> - <option value="">Go directly to project…</option> - <option tal:repeat="project here/getActiveProjects" - tal:content="python:project.Title[:30]" - tal:attributes="value project/getURL"> - </option> - </select> --> + <p class="discreet" tal:define="projectsCount python:len(catalog(portal_type = 'PSCProject', path = herePath)); + releasesCount python:len(catalog(portal_type = 'PSCRelease', path = herePath))"> + Currently listing <span tal:replace="projectsCount" /> projects with <span tal:replace="releasesCount" /> releases. + </p> + <p class="documentDescription" tal:content="here/Description"> + Description + </p> + + <div class="visualClear"><!-- --></div> - - <div class="visualClear"><!----></div> - - <h2 style="margin-top:1em"> - Add-on Product Releases - </h2> - - <form id="product_select" class="even" style="padding: 1em" action="#" tal:attributes="action string:${here/absolute_url}"> - <label>Show</label> - <select name="getCategories"> - <option value="">All categories</option> - <tal:block tal:repeat="cat here/availableCategories"> - <option tal:define="csplit python:[s.strip() for s in cat.split('|')]; - cid python:csplit[0]" - tal:content="python:csplit[1]" - tal:attributes="value cid; - selected python:test(cid==currentCategory,'selected',nothing)" /> - </tal:block> - </select> - <label>for</label> - <select name="getCompatibility"> - <option value="current" - tal:attributes="selected python:test(len(currentVersion)==2,'selected',nothing)"> - Supported releases (3.0 or 2.5) - </option> - <tal:block tal:repeat="version python:('Plone 3.0','Plone 2.5','Plone 2.1', 'Plone 2.0')"> - <option tal:content="version" - tal:attributes="value version; - selected python:test(currentVersion==version,'selected',nothing)"> - Plone 3.0 - </option> - </tal:block> - <option value="any" tal:attributes="selected python:test(currentVersion=='','selected',nothing)"> - Any version + <tal:featured_project_exists tal:condition="python: here.getFeaturedProject()"> + <dl class="portlet" style="width: 23em; float: left"> + <dt class="portletHeader"> + Get <tal:featured_project tal:replace="python: here.getFeaturedProject().Title()"> + Featured Project </tal:featured_project> + for your platform + </dt> + <dd class="portletItem" style="text-align: center" + tal:define="project_url python: here.getFeaturedProject().absolute_url()"> + <a tal:attributes="href project_url"><img + height="40" width="40" alt="Windows" src="platform_windows.gif" /><img + height="40" width="40" alt="Mac" src="platform_mac_os_x.gif" /><img + height="40" width="40" alt="Linux" src="platform_linux.gif" /></a> + <h3><a tal:attributes="href project_url" + style="border-bottom: 1px solid #8CACBB">Download <tal:featured_project + tal:replace="python: here.getFeaturedProject().Title()">Featured Project</tal:featured_project></a></h3> + <span class="discreet">Latest release: + <tal:latest_release tal:replace="python: here.getFeaturedProjectRelease().getId()"> + Latest Release + </tal:latest_release> + </span> + </dd> + </dl> + </tal:featured_project_exists> + + <dl class="portlet" style="float:right; width: 23em;"> + <dt class="portletHeader">Search for add-on packages</dt> + + <dd class="portletItem"> + <form style="text-align: center;" + name="searchform" id="searchform" + action="/search" tal:attributes="action string:${portal_url}/search"> + + <label for="searchGadget" + class="hiddenStructure"> + Search add-ons + </label> + <input type="hidden" name="path" tal:attributes="value python:'/'.join(here.getPhysicalPath())" /> + <input type="hidden" name="portal_type" value="PSCProject" /> + <input name="SearchableText" type="text" + size="12" + style="font-size: 150%" + title="Search add-ons" + accesskey="accesskeys-search" + tabindex="0" /> + + <input class="searchButton" name="Search" + style="font-size: 150%; background-position: 3px 5px; padding-left: 25px; padding-right: 10px" + type="submit" value="Search" + accesskey="accesskeys-search" /> + </form> + </dd> + </dl> + + <dl class="portlet" style="float:right; clear: right; width: 23em"> + + <dt class="portletHeader">Want to list your add-on package?</dt> + <dd class="portletItem" style="text-align: center"> + <form action="createObject" + tal:condition="python: user.has_permission('PloneSoftwareCenter: Add Project', context)"> + <input name="type_name" + type="hidden" + value="PSCProject" + /> + <input class="standalone" + type="submit" + style="font-size: 110%; padding: 3px 5px 3px 26px; background-position: 10px 5px" + value="Add new project" + /> + </form> + + <form tal:condition="python: not user.has_permission('Add portal content', context) and mtool.isAnonymousUser()" + tal:define="pss modules/Products/PythonScripts/standard" + tal:attributes="action python:'%s/login_form?came_from=%s' % + (here.portal_url(), + pss.url_quote(request['URL']))"> + + <input class="standalone" + type="submit" + style="font-size: 110%; padding: 3px 5px 3px 26px; background-position: 10px 5px" + value="Log in to add your project" + i18n:attributes="value" + /> + </form> + </dd> + </dl> + + <!-- <select style="float: left;" + onchange="window.location.href=this.options[this.selectedIndex].value"> + <option value="">Go directly to project…</option> + <option tal:repeat="project here/getActiveProjects" + tal:content="python:project.Title[:30]" + tal:attributes="value project/getURL"> </option> - <tal:block condition="nothing" tal:repeat="version python:catalog.uniqueValuesFor('getCompatibility')"> - <option tal:content="version" - tal:attributes="value version; - selected python:test(currentVersion==version,'selected',nothing)"> - Plone 3.0 + </select> --> + + + + <div class="visualClear"><!----></div> + + <h2 style="margin-top:1em"> + Add-on Package Releases + </h2> + + <form id="product_select" class="even" style="padding: 1em" action="#" tal:attributes="action string:${here/absolute_url}"> + <label>Show</label> + <select name="getCategories"> + <option value="">All categories</option> + <tal:block tal:repeat="cat here/availableCategories"> + <option tal:define="csplit python:[s.strip() for s in cat.split('|')]; + cid python:csplit[0]" + tal:content="python:csplit[1]" + tal:attributes="value cid; + selected python:test(cid==currentCategory,'selected',nothing)" /> + </tal:block> + </select> + <label>for</label> + <select name="getCompatibility"> + <tal:block tal:repeat="version here/getAvailableVersions"> + <option tal:content="version" + tal:attributes="value version; + selected python:test(currentVersion==version,'selected',nothing)"> + Plone 3.0 + </option> + </tal:block> + <option value="any" tal:attributes="selected python:test(currentVersion=='','selected',nothing)"> + Any version </option> - </tal:block> - </select> - - <input class="context" type="submit" name="product_search" value="Update" /> - </form> - - <div tal:define="results python:catalog(getCategories=currentCategory, - getCompatibility=currentVersion, - portal_type='PSCRelease', - sort_on='effective', - sort_order='reverse'); - Batch python:modules['Products.CMFPlone'].Batch; - b_size python:20;b_start python:0;b_start request/b_start | b_start; - desc_length site_properties/search_results_description_length; - desc_ellipsis site_properties/ellipsis;"> - - <p tal:condition="not: results"> - <strong i18n:translate="description_no_results_found">No results were found.</strong> - </p> - - <p class="discreet"> - <span tal:replace="python:len(results)">234</span> releases matching your criteria, sorted with latest releases first. - </p> - - <div tal:condition="results" - tal:define="batch python:Batch(results, b_size, int(b_start), orphan=1);"> - - <div class="project" tal:repeat="result batch"> - <tal:entry define="obj result/getObject; - project python:obj.aq_parent.aq_parent; - project python:test(project.portal_type=='PSCProject',project,obj); - url result/getURL; - projectURL project/absolute_url; - result_type result/portal_type;"> + <tal:block condition="nothing" tal:repeat="version python:catalog.uniqueValuesFor('getCompatibility')"> + <option tal:content="version" + tal:attributes="value version; + selected python:test(currentVersion==version,'selected',nothing)"> + Plone 3.0 + </option> + </tal:block> + </select> + + <input class="context" type="submit" name="product_search" value="Update" /> + </form> - <div class="screenshot" tal:condition="project/logo|nothing"> - <a href="#" tal:attributes="href projectURL"><img tal:replace="structure project/logo" /></a> - </div> - - <div class="project_details"> - <h3 tal:define="item_wf_state result/review_state; - item_wf_state_class python:'state-' + normalizeString(item_wf_state);" - tal:attributes="class item_wf_state_class" - > - <a href="#" style="border-bottom: 1px solid #8CACBB" tal:attributes="href projectURL;" tal:content="result/pretty_title_or_id" /> - <span class="discreet" tal:content="python:toLocalizedTime(result.ModificationDate, long_format=0)"> - Modification Date - </span> + <div tal:define="results python:catalog(getCategories=currentCategory, + getCompatibility=currentVersion, + portal_type='PSCRelease', + sort_on='effective', + sort_order='reverse'); + Batch python:modules['Products.CMFPlone'].Batch; + b_size python:20;b_start python:0;b_start request/b_start | b_start; + desc_length site_properties/search_results_description_length; + desc_ellipsis site_properties/ellipsis;"> + + <p tal:condition="not: results"> + <strong i18n:translate="description_no_results_found">No results were found.</strong> + </p> + + <p class="discreet"> + <span tal:replace="python:len(results)">234</span> releases matching your criteria, sorted with latest releases first. + </p> + + <div tal:condition="results" + tal:define="batch python:Batch(results, b_size, int(b_start), orphan=1);"> + + <div class="project" tal:repeat="result batch"> + <tal:entry define="obj result/getObject; + project python:obj.aq_parent.aq_parent; + project python:test(project.portal_type=='PSCProject',project,obj); + url result/getURL; + projectURL project/absolute_url; + result_type result/portal_type;"> - </h3> - - <p class="discreet" - tal:condition="not:currentCategory"> - in <span tal:replace="python:', '.join([getCategoryName(id) for id in result.getCategories])" /> - </p> + <div class="screenshot" tal:condition="project/logo|nothing"> + <a href="#" tal:attributes="href projectURL"><img tal:replace="structure project/logo" /></a> + </div> - <div class="description"> - <p tal:content="python:here.cropText(project.Description(), desc_length, desc_ellipsis)"> - Project Description - </p> - <p> - <strong>New in this release:</strong> <br /> - <span tal:content="python:here.cropText(result.Description, desc_length, desc_ellipsis)"> - Release Description + <div class="project_details"> + <h3 tal:define="item_wf_state result/review_state; + item_wf_state_class python:'state-' + normalizeString(item_wf_state);" + tal:attributes="class item_wf_state_class"> + + <a href="#" style="border-bottom: 1px solid #8CACBB" tal:attributes="href projectURL;" + tal:content="result/pretty_title_or_id">Software Project</a> + + <span class="discreet" tal:content="python:toLocalizedTime(result.ModificationDate, long_format=0)"> + Modification Date </span> - </p> + + </h3> + + <p class="discreet" + tal:condition="not:currentCategory"> + in <span tal:replace="python:', '.join([getCategoryName(id) for id in result.getCategories])" /> + </p> + + <div class="description"> + <p tal:content="python:here.cropText(project.Description(), desc_length, desc_ellipsis)"> + Project Description + </p> + <p> + <strong>New in this release:</strong> <br /> + <span tal:content="python:here.cropText(result.Description, desc_length, desc_ellipsis)"> + Release Description + </span> + </p> + </div> </div> + </tal:entry> </div> - </tal:entry> + + <!-- Navigation --> + <div metal:use-macro="here/batch_macros/macros/navigation" /> + </div> - - <!-- Navigation --> - <div metal:use-macro="here/batch_macros/macros/navigation" /> - </div> - </div> - <div class="visualClear"><!----></div> - - <div class="poweredBy"> - Powered by <a class="link-plain" href="http://plone.org/products/plonesoftwarecenter">Plone Software Center</a> - </div> + <div class="visualClear"><!----></div> + + <div class="poweredBy"> + Powered by <img src="product_icon.gif"><... [truncated message content] |