diff --git a/README.rst b/README.rst
index ec78881..6ca2eae 100644
--- a/README.rst
+++ b/README.rst
@@ -68,6 +68,14 @@ IMPORTANT! For Plone 3.0.x you should use plone.browserlayer 1.0.rc3. Be sure to
Notes
-----
+* For Plone 5 versions - use Plone SEO 5.0 release and up https://pypi.python.org/pypi/quintagroup.seoptimizer/5.0. In your buildout.cfg file's egg section set product version::
+
+ [buildout]
+ ....
+ eggs =
+ ...
+ quintagroup.seoptimizer >=5.0
+
* For Plone 4 versions - use Plone SEO 4.0 release and up https://pypi.python.org/pypi/quintagroup.seoptimizer/4.0. In your buildout.cfg file's egg section set product version::
[buildout]
diff --git a/quintagroup/seoptimizer/adapters.py b/quintagroup/seoptimizer/adapters.py
index efed41d..eb07305 100644
--- a/quintagroup/seoptimizer/adapters.py
+++ b/quintagroup/seoptimizer/adapters.py
@@ -1,6 +1,8 @@
from zope.interface import implements
from zope.component import queryAdapter
from zope.component import queryMultiAdapter
+from zope.component import getUtility
+from plone.registry.interfaces import IRegistry
from quintagroup.seoptimizer.util import SortedDict
from quintagroup.seoptimizer.interfaces import IMetaKeywords, IMappingMetaTags
@@ -47,14 +49,16 @@ class MappingMetaTags(object):
def __init__(self, context):
self.context = context
- pps = queryMultiAdapter((self.context, self.context.REQUEST),
- name="plone_portal_state")
- self.gseo = queryAdapter(pps.portal(), ISEOConfigletSchema)
+# pps = queryMultiAdapter((self.context, self.context.REQUEST),
+# name="plone_portal_state")
+# self.gseo = queryAdapter(pps.portal(), ISEOConfigletSchema)
+ self.gseo = getUtility(IRegistry).forInterface(ISEOConfigletSchema)
def getMappingMetaTags(self):
""" See interface.
"""
metadata_name = SortedDict()
+
if self.gseo:
for mt in self.gseo.metatags_order:
if mt in METADATA_MAPS:
diff --git a/quintagroup/seoptimizer/browser/configure.zcml b/quintagroup/seoptimizer/browser/configure.zcml
index 818bc15..1521c90 100644
--- a/quintagroup/seoptimizer/browser/configure.zcml
+++ b/quintagroup/seoptimizer/browser/configure.zcml
@@ -34,7 +34,7 @@
own, .interfaces.IPloneSEOLayer layer -->
-
+
+
+
= 4.3
- from zope.component.hooks import getSite
-from zope.app.form.browser import TextAreaWidget
-
-from plone.fieldsets.fieldsets import FormFieldsets
-from plone.app.controlpanel.form import ControlPanelForm
-from plone.app.controlpanel.widgets import MultiCheckBoxThreeColumnWidget
+# getSite = None
+# try:
+# # Plone < 4.3
+# from zope.app.component import hooks
+# getSite = hooks.getSite
+# except ImportError:
+# # Plone >= 4.3
+# from zope.component.hooks import getSite
+# from zope.app.form.browser import TextAreaWidget
+#
+# from plone.fieldsets.fieldsets import FormFieldsets
+# from plone.app.controlpanel.form import ControlPanelForm
+# from plone.app.controlpanel.widgets import MultiCheckBoxThreeColumnWidget
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.utils import safe_unicode
-from Products.CMFDefault.formlib.schema import ProxyFieldProperty
-from Products.CMFDefault.formlib.schema import SchemaAdapterBase
-from Products.CMFPlone.interfaces import IPloneSiteRoot
+# from Products.CMFDefault.formlib.schema import ProxyFieldProperty
+# from Products.CMFDefault.formlib.schema import SchemaAdapterBase
+# from Products.CMFPlone.interfaces import IPloneSiteRoot
from quintagroup.seoptimizer import SeoptimizerMessageFactory as _
@@ -50,15 +51,17 @@ class ISEOConfigletBaseSchema(Interface):
default='Fill in meta tags (one per line) in the order '
'in which they will appear on site source pages. '
'Example: "metaname accessor".'),
+ default=list(),
+ value_type=TextLine(title=_(u"label_metatags_order")),
required=False)
- types_seo_enabled = Tuple(
+ types_seo_enabled = List(
title=_("label_content_type_title", default='Content Types'),
description=_("description_seo_content_types",
default='Select content types that will have SEO '
'properties enabled.'),
required=False,
- missing_value=tuple(),
+ missing_value=List(),
value_type=Choice(
vocabulary="plone.app.vocabularies.ReallyUserFriendlyTypes"))
@@ -69,6 +72,8 @@ class ISEOConfigletBaseSchema(Interface):
default='Fill in custom metatag names (one per line) '
'which will appear on qseo_properties edit tab. '
'Example: "metaname|metacontent" or "metaname".'),
+ default=list(),
+ value_type=TextLine(title=_(u"label_default_custom_metatags",default="Default custom metatags")),
required=False)
@@ -84,16 +89,20 @@ class ISEOConfigletAdvancedSchema(Interface):
fields = List(
title=_("label_fields", default='Fields for keywords statistic '
'calculation.'),
- description=_("help_fields", default='Fill in filds (one per line)'
+ description=_("help_fields", default='Fill in fields (one per line)'
'which statistics of keywords usage should '
'be calculated for.'),
+ default=list(),
+ value_type=TextLine(title=_(u"label_fields")),
required=False)
stop_words = List(
title=_("label_stop_words", default='Stop words.'),
description=_("help_stop_words", default='Fill in stop words '
- '(one per line) which will be excluded from kewords '
+ '(one per line) which will be excluded from keywords '
'statistics calculation.'),
+ default=list(),
+ value_type=TextLine(title=_(u"label_stop_words")),
required=False)
external_keywords_test = Bool(
@@ -114,99 +123,141 @@ class ISEOConfigletSchema(ISEOConfigletBaseSchema,
"""
-class SEOConfigletAdapter(SchemaAdapterBase):
-
- adapts(IPloneSiteRoot)
- implements(ISEOConfigletSchema)
-
- def __init__(self, context):
- super(SEOConfigletAdapter, self).__init__(context)
- self.portal = getSite()
- pprop = getToolByName(self.portal, 'portal_properties')
- self.context = pprop.seo_properties
- self.siteprops = pprop.site_properties
- self.ttool = getToolByName(context, 'portal_types')
- self.encoding = pprop.site_properties.default_charset
-
- def getExposeDC(self):
- return self.siteprops.getProperty('exposeDCMetaTags')
-
- def setExposeDC(self, value):
- return self.siteprops._updateProperty('exposeDCMetaTags', bool(value))
-
- def getTypesSEOEnabled(self):
- ct_with_seo = self.context.content_types_with_seoproperties
- return [t for t in self.ttool.listContentTypes() if t in ct_with_seo]
-
- def setTypesSEOEnabled(self, value):
- value = [t for t in self.ttool.listContentTypes() if t in value]
- self.context._updateProperty('content_types_with_seoproperties', value)
-
- def getCustomScript(self):
- description = getattr(self.context, 'custom_script', u'')
- return safe_unicode(description)
-
- def setCustomScript(self, value):
- if value is not None:
- self.context.custom_script = value.encode(self.encoding)
- else:
- self.context.custom_script = ''
-
- exposeDCMetaTags = property(getExposeDC, setExposeDC)
- seo_default_custom_metatag = ISEOConfigletSchema['default_custom_metatags']
- default_custom_metatags = ProxyFieldProperty(seo_default_custom_metatag)
- metatags_order = ProxyFieldProperty(ISEOConfigletSchema['metatags_order'])
- types_seo_enabled = property(getTypesSEOEnabled, setTypesSEOEnabled)
- custom_script = property(getCustomScript, setCustomScript)
- fields = ProxyFieldProperty(ISEOConfigletSchema['fields'])
- stop_words = ProxyFieldProperty(ISEOConfigletSchema['stop_words'])
- seo_external_keywords_test = ISEOConfigletSchema['external_keywords_test']
- external_keywords_test = ProxyFieldProperty(seo_external_keywords_test)
-
-
-class Text2ListWidget(TextAreaWidget):
- height = 5
- splitter = re.compile(u'\\r?\\n', re.S | re.U)
-
- def _toFieldValue(self, input):
- if input == self._missing:
- return self.context._type()
- else:
- return self.context._type(filter(None, self.splitter.split(input)))
-
- def _toFormValue(self, value):
- if value == self.context.missing_value or \
- value == self.context._type():
- return self._missing
- else:
- return u'\r\n'.join(list(value))
-
-
-# Fieldset configurations
-baseset = FormFieldsets(ISEOConfigletBaseSchema)
-baseset.id = 'seobase'
-baseset.label = _(u'label_seobase', default=u'Base')
-
-advancedset = FormFieldsets(ISEOConfigletAdvancedSchema)
-advancedset.id = 'seoadvanced'
-advancedset.label = _(u'label_seoadvanced', default=u'Advanced')
-
-
-class SEOConfiglet(ControlPanelForm):
-
- form_fields = FormFieldsets(baseset, advancedset)
- type_seo_enabled = MultiCheckBoxThreeColumnWidget
-
- form_fields['default_custom_metatags'].custom_widget = Text2ListWidget
- form_fields['metatags_order'].custom_widget = Text2ListWidget
- form_fields['types_seo_enabled'].custom_widget = type_seo_enabled
- form_fields['types_seo_enabled'].custom_widget.cssClass = 'label'
- form_fields['fields'].custom_widget = Text2ListWidget
- form_fields['stop_words'].custom_widget = Text2ListWidget
+try:
+ # only in z3c.form 2.0
+ from z3c.form.browser.textlines import TextLinesFieldWidget
+except ImportError:
+ from plone.z3cform.textlines import TextLinesFieldWidget
- label = _("Search Engine Optimizer configuration")
+class SEOConfigletEditForm(controlpanel.RegistryEditForm):
+
+ schema = ISEOConfigletSchema
+ label = _("Search Engine Optimizer configuration")
description = _("seo_configlet_description", default="You can select what "
"content types are qSEOptimizer-enabled, and control if "
"Dublin Core metatags are exposed in the header of content"
" pages.")
- form_name = ""
+# form_fields = FormFieldsets(baseset, advancedset)
+# type_seo_enabled = MultiCheckBoxThreeColumnWidget
+# form_fields['types_seo_enabled'].custom_widget = type_seo_enabled
+# form_fields['types_seo_enabled'].custom_widget.cssClass = 'label'
+
+ def updateFields(self):
+ super(SEOConfigletEditForm, self).updateFields()
+ self.fields['default_custom_metatags'].widgetFactory = TextLinesFieldWidget
+ self.fields['metatags_order'].widgetFactory = TextLinesFieldWidget
+ self.fields['fields'].widgetFactory = TextLinesFieldWidget
+ self.fields['stop_words'].widgetFactory = TextLinesFieldWidget
+
+ def updateWidgets(self):
+ super(SEOConfigletEditForm, self).updateWidgets()
+ self.widgets['default_custom_metatags'].rows = 8
+ self.widgets['default_custom_metatags'].style = u'width: 30%;'
+ self.widgets['metatags_order'].rows = 8
+ self.widgets['metatags_order'].style = u'width: 30%;'
+ self.widgets['fields'].rows = 8
+ self.widgets['fields'].style = u'width: 30%;'
+ self.widgets['stop_words'].rows = 8
+ self.widgets['stop_words'].style = u'width: 30%;'
+
+
+
+class SEOConfigletControlPanel(controlpanel.ControlPanelFormWrapper):
+ form = SEOConfigletEditForm
+
+# class SEOConfigletAdapter(SchemaAdapterBase):
+#
+# adapts(IPloneSiteRoot)
+# implements(ISEOConfigletSchema)
+#
+# def __init__(self, context):
+# super(SEOConfigletAdapter, self).__init__(context)
+# self.portal = getSite()
+# pprop = getToolByName(self.portal, 'portal_properties')
+# self.context = pprop.seo_properties
+# self.siteprops = pprop.site_properties
+# self.ttool = getToolByName(context, 'portal_types')
+# self.encoding = pprop.site_properties.default_charset
+#
+# def getExposeDC(self):
+# return self.siteprops.getProperty('exposeDCMetaTags')
+#
+# def setExposeDC(self, value):
+# return self.siteprops._updateProperty('exposeDCMetaTags', bool(value))
+#
+# def getTypesSEOEnabled(self):
+# ct_with_seo = self.context.content_types_with_seoproperties
+# return [t for t in self.ttool.listContentTypes() if t in ct_with_seo]
+#
+# def setTypesSEOEnabled(self, value):
+# value = [t for t in self.ttool.listContentTypes() if t in value]
+# self.context._updateProperty('content_types_with_seoproperties', value)
+#
+# def getCustomScript(self):
+# description = getattr(self.context, 'custom_script', u'')
+# return safe_unicode(description)
+#
+# def setCustomScript(self, value):
+# if value is not None:
+# self.context.custom_script = value.encode(self.encoding)
+# else:
+# self.context.custom_script = ''
+#
+# exposeDCMetaTags = property(getExposeDC, setExposeDC)
+# seo_default_custom_metatag = ISEOConfigletSchema['default_custom_metatags']
+# default_custom_metatags = ProxyFieldProperty(seo_default_custom_metatag)
+# metatags_order = ProxyFieldProperty(ISEOConfigletSchema['metatags_order'])
+# types_seo_enabled = property(getTypesSEOEnabled, setTypesSEOEnabled)
+# custom_script = property(getCustomScript, setCustomScript)
+# fields = ProxyFieldProperty(ISEOConfigletSchema['fields'])
+# stop_words = ProxyFieldProperty(ISEOConfigletSchema['stop_words'])
+# seo_external_keywords_test = ISEOConfigletSchema['external_keywords_test']
+# external_keywords_test = ProxyFieldProperty(seo_external_keywords_test)
+#
+#
+# class Text2ListWidget(TextAreaWidget):
+# height = 5
+# splitter = re.compile(u'\\r?\\n', re.S | re.U)
+#
+# def _toFieldValue(self, input):
+# if input == self._missing:
+# return self.context._type()
+# else:
+# return self.context._type(filter(None, self.splitter.split(input)))
+#
+# def _toFormValue(self, value):
+# if value == self.context.missing_value or \
+# value == self.context._type():
+# return self._missing
+# else:
+# return u'\r\n'.join(list(value))
+#
+#
+# # Fieldset configurations
+# baseset = FormFieldsets(ISEOConfigletBaseSchema)
+# baseset.id = 'seobase'
+# baseset.label = _(u'label_seobase', default=u'Base')
+#
+# advancedset = FormFieldsets(ISEOConfigletAdvancedSchema)
+# advancedset.id = 'seoadvanced'
+# advancedset.label = _(u'label_seoadvanced', default=u'Advanced')
+#
+#
+# class SEOConfiglet(ControlPanelForm):
+#
+# form_fields = FormFieldsets(baseset, advancedset)
+# type_seo_enabled = MultiCheckBoxThreeColumnWidget
+#
+# form_fields['default_custom_metatags'].custom_widget = Text2ListWidget
+# form_fields['metatags_order'].custom_widget = Text2ListWidget
+# form_fields['types_seo_enabled'].custom_widget = type_seo_enabled
+# form_fields['types_seo_enabled'].custom_widget.cssClass = 'label'
+# form_fields['fields'].custom_widget = Text2ListWidget
+# form_fields['stop_words'].custom_widget = Text2ListWidget
+#
+# label = _("Search Engine Optimizer configuration")
+# description = _("seo_configlet_description", default="You can select what "
+# "content types are qSEOptimizer-enabled, and control if "
+# "Dublin Core metatags are exposed in the header of content"
+# " pages.")
+# form_name = ""
diff --git a/quintagroup/seoptimizer/browser/viewlets.py b/quintagroup/seoptimizer/browser/viewlets.py
index d6afafb..eed79e0 100644
--- a/quintagroup/seoptimizer/browser/viewlets.py
+++ b/quintagroup/seoptimizer/browser/viewlets.py
@@ -5,6 +5,8 @@
from zope.component import queryAdapter
from zope.component import queryMultiAdapter
from zope.component import getMultiAdapter
+from zope.component import getUtility
+from plone.registry.interfaces import IRegistry
from plone.app.layout.viewlets.common import ViewletBase
from Products.CMFPlone.utils import safe_unicode, getSiteEncoding
@@ -40,7 +42,8 @@ def listMetaTags(self):
result = SortedDict()
pps = queryMultiAdapter((self.context, self.request),
name="plone_portal_state")
- seo_global = queryAdapter(pps.portal(), ISEOConfigletSchema)
+# seo_global = queryAdapter(pps.portal(), ISEOConfigletSchema)
+ seo_global = getUtility(IRegistry).forInterface(ISEOConfigletSchema)
seo_context = queryMultiAdapter((self.context, self.request),
name='seo_context')
@@ -205,15 +208,18 @@ class CustomScriptViewlet(ViewletBase):
""" Simple viewlet for custom script rendering.
"""
def getCustomScript(self):
- pps = queryMultiAdapter((self.context, self.request),
- name="plone_portal_state")
- gseo = queryAdapter(pps.portal(), ISEOConfigletSchema)
+# pps = queryMultiAdapter((self.context, self.request),
+# name="plone_portal_state")
+# gseo = queryAdapter(pps.portal(), ISEOConfigletSchema)
+ gseo = getUtility(IRegistry).forInterface(ISEOConfigletSchema)
if gseo:
return gseo.custom_script
return ''
def render(self):
- return safe_unicode("""%s""" % self.getCustomScript())
+ if not bool(self.getCustomScript()):st=''
+
+ return safe_unicode("""%s""" % st)
class CanonicalUrlViewlet(ViewletBase):
diff --git a/quintagroup/seoptimizer/browser/views.py b/quintagroup/seoptimizer/browser/views.py
index 38b58c6..80772d9 100644
--- a/quintagroup/seoptimizer/browser/views.py
+++ b/quintagroup/seoptimizer/browser/views.py
@@ -3,10 +3,11 @@
from Acquisition import aq_inner
from zope.component import queryAdapter
from zope.component import queryMultiAdapter
+from zope.component import getUtility
from zope.schema.interfaces import InvalidValue
from plone.memoize import view, ram
-
+from plone.registry.interfaces import IRegistry
from Products.Five.browser import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.CMFPlone.utils import getSiteEncoding
@@ -40,7 +41,8 @@ def __init__(self, *args, **kwargs):
name="plone_portal_state")
self.pcs = queryMultiAdapter((self.context, self.request),
name="plone_context_state")
- self.gseo = queryAdapter(self.pps.portal(), ISEOConfigletSchema)
+# self.gseo = queryAdapter(self.pps.portal(), ISEOConfigletSchema)
+ self.gseo = getUtility(IRegistry).forInterface(ISEOConfigletSchema)
self._seotags = self._getSEOTags()
def __getitem__(self, key):
@@ -81,6 +83,7 @@ def _getSEOTags(self):
}
#seotags["seo_nonEmptylocalMetaTags"] = \
# bool(seotags["seo_localCustomMetaTags"])
+
return seotags
def getSEOProperty(self, property_name, accessor='', default=None):
@@ -151,8 +154,8 @@ def seo_globalCustomMetaTags(self):
in seo_properties.
"""
result = []
- if self.gseo:
- for tag in self.gseo.default_custom_metatags:
+ if self.gseo and self.gseo.default_custom_metatags :
+ for tag in set(self.gseo.default_custom_metatags):
name_value = tag.split(SEPERATOR)
if name_value[0]:
result.append({'meta_name': name_value[0],
@@ -178,7 +181,8 @@ def __init__(self, *args, **kwargs):
super(SEOContextPropertiesView, self).__init__(*args, **kwargs)
self.pps = queryMultiAdapter((self.context, self.request),
name="plone_portal_state")
- self.gseo = queryAdapter(self.pps.portal(), ISEOConfigletSchema)
+# self.gseo = queryAdapter(self.pps.portal(), ISEOConfigletSchema)
+ self.gseo = getUtility(IRegistry).forInterface(ISEOConfigletSchema)
def test(self, condition, first, second):
"""
@@ -363,5 +367,7 @@ def checkVisibilitySEOAction(self):
aq_inner(self.context)
plone = queryMultiAdapter((self, self.request),
name="plone_portal_state").portal()
- adapter = ISEOConfigletSchema(plone)
- return bool(self.context.portal_type in adapter.types_seo_enabled)
+
+# adapter = ISEOConfigletSchema(plone)
+ setting = getUtility(IRegistry).forInterface(ISEOConfigletSchema)
+ return bool(self.context.portal_type in set(setting.types_seo_enabled))
diff --git a/quintagroup/seoptimizer/profiles/default/metadata.xml b/quintagroup/seoptimizer/profiles/default/metadata.xml
index 2df1440..68fece7 100644
--- a/quintagroup/seoptimizer/profiles/default/metadata.xml
+++ b/quintagroup/seoptimizer/profiles/default/metadata.xml
@@ -1,4 +1,4 @@
- 4.1.1
+ 5.0.0
diff --git a/quintagroup/seoptimizer/profiles/default/propertiestool.xml b/quintagroup/seoptimizer/profiles/default/propertiestool.xml
deleted file mode 100644
index 5cefe28..0000000
--- a/quintagroup/seoptimizer/profiles/default/propertiestool.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-
-
diff --git a/quintagroup/seoptimizer/profiles/default/registry.xml b/quintagroup/seoptimizer/profiles/default/registry.xml
new file mode 100644
index 0000000..a7cf076
--- /dev/null
+++ b/quintagroup/seoptimizer/profiles/default/registry.xml
@@ -0,0 +1,178 @@
+types_seo_enabled
+
+
+ False
+ Make keywords test by opening context url as external resource with urllib2.openurl(). This is useful when xdv/Deliverance transformation is used on the site.
+ False
+ External keywords check
+
+ False
+
+
+
+ True
+ Controls if DCmetatags are exposed to page header. They include DC.description, DC.type, DC.format, DC.creator and others.
+ False
+ Expose DCmeta tags
+
+ True
+
+
+
+
+ This JavaScript code will be included in the rendered HTML as entered in the page header.
+ False
+ Header JavaScript
+
+
+
+
+
+
+ Fill in fields (one per line) which statistics of keywords usage should be calculated for.
+ Fields for keywords statistic calculation.
+
+ Fields for keywords statistic calculation.
+
+
+
+ seo_title
+ seo_description
+ seo_keywords
+
+
+
+
+
+ Select content types that will have SEO properties enabled.
+ Content Types
+
+ Content Types
+
+
+
+ Document
+ File
+ News Item
+
+
+
+
+
+ Fill in meta tags (one per line) in the order in which they will appear on site source pages.Example: "metaname accessor".
+ Meta tags order in the page.
+
+ Meta tags order
+
+
+
+ DC.publisher
+ DC.description
+ DC.contributors
+ DC.creator
+ DC.format
+ DC.rights
+ DC.language
+ DC.date.modified
+ DC.date.created
+ DC.type
+ DC.subject
+ DC.distribution
+ description
+ keywords
+ robots
+ distribution
+
+
+
+
+
+ Fill in custom metatag names (one per line) which will appear on qseo_properties edit tab.Example: "metaname|metacontent" or "metaname".
+ Default custom metatags.
+
+ Default custom metatags.
+
+
+
+
+
+
+
+ Fill in fields (one per line) which statistics of keywords usage should be calculated for.
+ Fields for keywords statistic calculation.
+
+ Category
+
+
+
+ seo_title
+ seo_description
+ seo_keywords
+
+
+
+
+
+ Fill in stop words (one per line) which will be excluded from keywords statistics calculation.
+ Stop words.
+
+ Stop words.
+
+
+
+ a
+ an
+ amp
+ and
+ are
+ arial
+ as
+ at
+ be
+ but
+ by
+ can
+ com
+ do
+ font
+ for
+ from
+ gif
+ had
+ has
+ have
+ he
+ helvetica
+ her
+ how
+ href
+ i
+ if
+ in
+ is
+ it
+ javascript
+ jpg
+ made
+ net
+ of
+ on
+ or
+ org
+ our
+ sans
+ see
+ serif
+ she
+ that
+ the
+ this
+ to
+ us
+ we
+ with
+ you
+ your
+
+
+
\ No newline at end of file
diff --git a/setup.py b/setup.py
index b904a03..970c0e4 100644
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@
import os
from setuptools import setup, find_packages
-version = '4.3'
+version = '5.0'
setup(name='quintagroup.seoptimizer',
version=version,
@@ -24,6 +24,7 @@
"Framework :: Plone :: 4.1",
"Framework :: Plone :: 4.2",
"Framework :: Plone :: 4.3",
+ "Framework :: Plone :: 5.0",
"Framework :: Zope2",
"Framework :: Zope3",
"Programming Language :: Python",