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