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 @@ - - - - SEO Properties - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - False - - - True - - 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 <abbr title="Dublin Core">DC</abbr>meta 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",