[1822] | 1 | import re |
---|
[1788] | 2 | from zope.interface import Interface |
---|
| 3 | from zope.interface import implements |
---|
| 4 | from zope.component import adapts |
---|
| 5 | from zope.schema.vocabulary import SimpleTerm |
---|
| 6 | from zope.schema.vocabulary import SimpleVocabulary |
---|
[1822] | 7 | from zope.schema import Bool, Text, Choice, Tuple, List |
---|
[1788] | 8 | from zope.app.form.browser import RadioWidget |
---|
| 9 | |
---|
| 10 | from zope.formlib.form import FormFields |
---|
| 11 | from zope.app.component.hooks import getSite |
---|
| 12 | from zope.app.form.browser import TextAreaWidget |
---|
| 13 | |
---|
| 14 | from plone.app.controlpanel.form import ControlPanelForm |
---|
| 15 | from plone.app.controlpanel.widgets import MultiCheckBoxThreeColumnWidget |
---|
| 16 | |
---|
| 17 | from Products.CMFCore.utils import getToolByName |
---|
| 18 | from Products.CMFDefault.formlib.schema import ProxyFieldProperty |
---|
| 19 | from Products.CMFDefault.formlib.schema import SchemaAdapterBase |
---|
| 20 | from Products.CMFPlone.interfaces import IPloneSiteRoot |
---|
| 21 | |
---|
| 22 | from quintagroup.seoptimizer import SeoptimizerMessageFactory as _ |
---|
| 23 | |
---|
| 24 | |
---|
| 25 | # Global and local site keyword vocabularies |
---|
| 26 | keywordsSGVocabulary = SimpleVocabulary(( |
---|
| 27 | SimpleTerm(1, title="Plone categories override global SEO keywords"), |
---|
| 28 | SimpleTerm(2, title="Global SEO keywords override Plone categories"), |
---|
| 29 | SimpleTerm(3, title="Merge Plone categories and global SEO keywords"), |
---|
| 30 | )) |
---|
| 31 | |
---|
| 32 | keywordsLGVocabulary = SimpleVocabulary(( |
---|
| 33 | SimpleTerm(1, title="Global SEO keywords override local SEO keywords"), |
---|
| 34 | SimpleTerm(2, title="Merge global and local SEO keywords"), |
---|
| 35 | )) |
---|
| 36 | |
---|
| 37 | |
---|
| 38 | # Custom Widgets |
---|
| 39 | class TypedRadioWidgetNoValue(RadioWidget): |
---|
| 40 | _displayItemForMissingValue=False |
---|
| 41 | type = u'radio' |
---|
| 42 | |
---|
| 43 | def SEORadioWidget(field, request): |
---|
| 44 | return TypedRadioWidgetNoValue(field, field.vocabulary, request) |
---|
| 45 | |
---|
| 46 | |
---|
| 47 | # Configlet schema |
---|
| 48 | class ISEOConfigletSchema(Interface): |
---|
| 49 | |
---|
| 50 | exposeDCMetaTags = Bool( |
---|
| 51 | title=_("label_exposeDCMetaTags", |
---|
| 52 | default='Expose <abbr title="Dublin Core">DC</abbr> meta tags'), |
---|
| 53 | description=_("description_seo_dc_metatags", |
---|
| 54 | default='Controls if <abbr title="Dublin Core">DC</abbr> ' |
---|
| 55 | 'metatags are exposed to page header. They include ' |
---|
| 56 | 'DC.description, DC.type, DC.format, DC.creator and ' |
---|
| 57 | 'others.'), |
---|
| 58 | default=True, |
---|
| 59 | required=False) |
---|
| 60 | |
---|
[1822] | 61 | default_custom_metatags = List( |
---|
[1788] | 62 | title=_("label_default_custom_metatags", default='Default custom metatags.'), |
---|
| 63 | description=_("help_default_custom_metatags", |
---|
| 64 | default='Fill in custom metatag names (one per line) which will' |
---|
| 65 | 'appear on qseo_properties edit tab. Example: ' |
---|
| 66 | '"metaname|metacontent" or "metaname".'), |
---|
| 67 | required=False) |
---|
| 68 | |
---|
[1822] | 69 | metatags_order = List( |
---|
[1788] | 70 | title=_("label_metatags_order", |
---|
| 71 | default='Meta tags order in the page.'), |
---|
| 72 | description=_("help_metatags_order", |
---|
| 73 | default='Fill in meta tags (one per line) in the order in which' |
---|
| 74 | ' they will appear on site source pages. Example: ' |
---|
| 75 | '"metaname accessor".'), |
---|
| 76 | required=False) |
---|
| 77 | |
---|
[1822] | 78 | additional_keywords = List( |
---|
[1788] | 79 | title=_("label_additional_keywords", |
---|
| 80 | default='Additional keywords that should be added to the ' |
---|
| 81 | 'content types.'), |
---|
| 82 | description=_("help_additional_keywords", |
---|
| 83 | default='Use this field when you want that your content types ' |
---|
| 84 | 'receive additional keywords from the ones you manually ' |
---|
| 85 | 'specify. Enter one keyword per line.'), |
---|
| 86 | required=False) |
---|
| 87 | |
---|
| 88 | settings_use_keywords_sg = Choice( |
---|
| 89 | title=_("label_settings_use_keywords_sg", |
---|
| 90 | default='Settings to control Plone categories and global SEO ' |
---|
| 91 | 'keywords behaviour.'), |
---|
| 92 | description=_("help_settings_use_keywords_sg", |
---|
| 93 | default='Controls Plone categories (also known as keywords or ' |
---|
| 94 | 'tags) and global SEO keywords behaviour.'), |
---|
| 95 | required=False, |
---|
| 96 | vocabulary=keywordsSGVocabulary) |
---|
| 97 | |
---|
| 98 | settings_use_keywords_lg = Choice( |
---|
| 99 | title=_("label_settings_use_keywords_lg", |
---|
| 100 | default='Settings to control global SEO keywords vs local SEO ' |
---|
| 101 | 'keywords behaviour.'), |
---|
| 102 | description=_("help_settings_use_keywords_lg", |
---|
| 103 | default='Controls global and local SEO keywords behaviour.'), |
---|
| 104 | required=False, |
---|
| 105 | vocabulary=keywordsLGVocabulary) |
---|
| 106 | |
---|
| 107 | types_seo_enabled = Tuple( |
---|
| 108 | title=_("label_content_type_title", default='Content Types'), |
---|
| 109 | description=_("description_seo_content_types", |
---|
| 110 | default='Select content types that will have SEO properties ' |
---|
| 111 | 'enabled.'), |
---|
| 112 | required=False, |
---|
| 113 | missing_value=tuple(), |
---|
| 114 | value_type=Choice( |
---|
| 115 | vocabulary="plone.app.vocabularies.ReallyUserFriendlyTypes")) |
---|
| 116 | |
---|
| 117 | |
---|
| 118 | |
---|
| 119 | class SEOConfigletAdapter(SchemaAdapterBase): |
---|
| 120 | |
---|
| 121 | adapts(IPloneSiteRoot) |
---|
| 122 | implements(ISEOConfigletSchema) |
---|
| 123 | |
---|
| 124 | |
---|
| 125 | def __init__(self, context): |
---|
| 126 | super(SEOConfigletAdapter, self).__init__(context) |
---|
| 127 | self.portal = getSite() |
---|
| 128 | pprop = getToolByName(self.portal, 'portal_properties') |
---|
| 129 | self.context = pprop.seo_properties |
---|
| 130 | self.siteprops = pprop.site_properties |
---|
| 131 | self.ttool = getToolByName(context, 'portal_types') |
---|
| 132 | |
---|
| 133 | def getExposeDC(self): |
---|
| 134 | return self.siteprops.getProperty('exposeDCMetaTags') |
---|
| 135 | |
---|
| 136 | def setExposeDC(self, value): |
---|
| 137 | return self.siteprops._updateProperty('exposeDCMetaTags', bool(value)) |
---|
| 138 | |
---|
| 139 | def getTypesSEOEnabled(self): |
---|
| 140 | ct_with_seo = self.context.content_types_with_seoproperties |
---|
| 141 | return [t for t in self.ttool.listContentTypes() if t in ct_with_seo] |
---|
| 142 | |
---|
| 143 | def setTypesSEOEnabled(self, value): |
---|
| 144 | value = [t for t in self.ttool.listContentTypes() if t in value] |
---|
| 145 | self.context._updateProperty('content_types_with_seoproperties', value) |
---|
| 146 | |
---|
| 147 | |
---|
| 148 | exposeDCMetaTags = property(getExposeDC, setExposeDC) |
---|
| 149 | types_seo_enabled = property(getTypesSEOEnabled, setTypesSEOEnabled) |
---|
[1822] | 150 | metatags_order = ProxyFieldProperty(ISEOConfigletSchema['metatags_order']) |
---|
| 151 | default_custom_metatags = ProxyFieldProperty(ISEOConfigletSchema['default_custom_metatags']) |
---|
| 152 | additional_keywords = ProxyFieldProperty(ISEOConfigletSchema['additional_keywords']) |
---|
| 153 | |
---|
[1788] | 154 | settings_use_keywords_sg = ProxyFieldProperty(ISEOConfigletSchema['settings_use_keywords_sg']) |
---|
| 155 | settings_use_keywords_lg = ProxyFieldProperty(ISEOConfigletSchema['settings_use_keywords_lg']) |
---|
| 156 | |
---|
| 157 | |
---|
| 158 | class SmallTextAreaWidget(TextAreaWidget): |
---|
| 159 | height = 5 |
---|
[1822] | 160 | splitter = re.compile(u'\\r?\\n', re.S|re.U) |
---|
[1788] | 161 | |
---|
[1822] | 162 | def _toFieldValue(self, value): |
---|
| 163 | return filter(None, self.splitter.split(value)) |
---|
[1788] | 164 | |
---|
[1822] | 165 | def _toFormValue(self, value): |
---|
| 166 | return u'\r\n'.join(list(value)) |
---|
| 167 | |
---|
| 168 | |
---|
[1788] | 169 | class SEOConfiglet(ControlPanelForm): |
---|
| 170 | |
---|
| 171 | form_fields = FormFields(ISEOConfigletSchema) |
---|
| 172 | form_fields['default_custom_metatags'].custom_widget = SmallTextAreaWidget |
---|
| 173 | form_fields['additional_keywords'].custom_widget = SmallTextAreaWidget |
---|
[1822] | 174 | form_fields['metatags_order'].custom_widget = SmallTextAreaWidget |
---|
[1788] | 175 | form_fields['settings_use_keywords_sg'].custom_widget = SEORadioWidget |
---|
| 176 | form_fields['settings_use_keywords_lg'].custom_widget = SEORadioWidget |
---|
| 177 | form_fields['types_seo_enabled'].custom_widget = MultiCheckBoxThreeColumnWidget |
---|
| 178 | form_fields['types_seo_enabled'].custom_widget.cssClass='label' |
---|
| 179 | |
---|
| 180 | label = _("Search Engine Optimizer configuration") |
---|
| 181 | description = _("seo_configlet_description", default="You can select what " |
---|
| 182 | "content types are qSEOptimizer-enabled, and control if " |
---|
| 183 | "Dublin Core metatags are exposed in the header of content " |
---|
| 184 | "pages.") |
---|
| 185 | form_name = _("") |
---|