1 | import re |
---|
2 | from zope.interface import Interface |
---|
3 | from zope.interface import implements |
---|
4 | from zope.component import adapts |
---|
5 | from zope.schema import Bool, Choice, Tuple, List |
---|
6 | from zope.schema import SourceText |
---|
7 | |
---|
8 | from zope.app.component.hooks import getSite |
---|
9 | from zope.app.form.browser import TextAreaWidget |
---|
10 | |
---|
11 | from plone.fieldsets.fieldsets import FormFieldsets |
---|
12 | from plone.app.controlpanel.form import ControlPanelForm |
---|
13 | from plone.app.controlpanel.widgets import MultiCheckBoxThreeColumnWidget |
---|
14 | |
---|
15 | from Products.CMFCore.utils import getToolByName |
---|
16 | from Products.CMFPlone.utils import safe_unicode |
---|
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 | # Configlet schemas |
---|
25 | class ISEOConfigletBaseSchema(Interface): |
---|
26 | |
---|
27 | exposeDCMetaTags = Bool( |
---|
28 | title=_("label_exposeDCMetaTags", |
---|
29 | default='Expose <abbr title="Dublin Core">DC</abbr> ' \ |
---|
30 | 'meta tags'), |
---|
31 | description=_("description_seo_dc_metatags", |
---|
32 | default='Controls if <abbr title="Dublin Core">DC</abbr>' |
---|
33 | ' metatags are exposed to page header. They include ' |
---|
34 | 'DC.description, DC.type, DC.format, DC.creator and ' |
---|
35 | 'others.'), |
---|
36 | default=True, |
---|
37 | required=False) |
---|
38 | |
---|
39 | metatags_order = List( |
---|
40 | title=_("label_metatags_order", |
---|
41 | default='Meta tags order in the page.'), |
---|
42 | description=_("help_metatags_order", |
---|
43 | default='Fill in meta tags (one per line) in the order ' |
---|
44 | 'in which they will appear on site source pages. ' |
---|
45 | 'Example: "metaname accessor".'), |
---|
46 | required=False) |
---|
47 | |
---|
48 | types_seo_enabled = Tuple( |
---|
49 | title=_("label_content_type_title", default='Content Types'), |
---|
50 | description=_("description_seo_content_types", |
---|
51 | default='Select content types that will have SEO properties ' |
---|
52 | 'enabled.'), |
---|
53 | required=False, |
---|
54 | missing_value=tuple(), |
---|
55 | value_type=Choice( |
---|
56 | vocabulary="plone.app.vocabularies.ReallyUserFriendlyTypes")) |
---|
57 | |
---|
58 | default_custom_metatags = List( |
---|
59 | title=_("label_default_custom_metatags", |
---|
60 | default='Default custom metatags.'), |
---|
61 | description=_("help_default_custom_metatags", |
---|
62 | default='Fill in custom metatag names (one per line) ' \ |
---|
63 | 'which will appear on qseo_properties edit tab. ' \ |
---|
64 | 'Example: "metaname|metacontent" or "metaname".'), |
---|
65 | required=False) |
---|
66 | |
---|
67 | |
---|
68 | class ISEOConfigletAdvancedSchema(Interface): |
---|
69 | custom_script = SourceText( |
---|
70 | title=_("label_custom_script", default=u'Header JavaScript'), |
---|
71 | description=_("help_custom_script", |
---|
72 | default=u"This JavaScript code will be included in " |
---|
73 | "the rendered HTML as entered in the page header."), |
---|
74 | default=u'', |
---|
75 | required=False) |
---|
76 | |
---|
77 | fields = List( |
---|
78 | title=_("label_fields", default='Fields for keywords statistic ' |
---|
79 | 'calculation.'), |
---|
80 | description=_("help_fields", default='Fill in filds (one per line)' |
---|
81 | 'which statistics of keywords usage should ' |
---|
82 | 'be calculated for.'), |
---|
83 | required=False) |
---|
84 | |
---|
85 | stop_words = List( |
---|
86 | title=_("label_stop_words", default='Stop words.'), |
---|
87 | description=_("help_stop_words", default='Fill in stop words ' |
---|
88 | '(one per line) which will be excluded from kewords ' |
---|
89 | 'statistics calculation.'), |
---|
90 | required=False) |
---|
91 | |
---|
92 | external_keywords_test = Bool( |
---|
93 | title=_("label_external_keywords_test", |
---|
94 | default='External keywords check'), |
---|
95 | description=_("description_external_keywords_test", |
---|
96 | default='Make keywords test by opening context url as ' |
---|
97 | 'external resource with urllib2.openurl(). This is ' |
---|
98 | 'useful when xdv/Deliverance transformation is used ' |
---|
99 | 'on the site.'), |
---|
100 | default=False, |
---|
101 | required=False) |
---|
102 | |
---|
103 | |
---|
104 | class ISEOConfigletSchema(ISEOConfigletBaseSchema, |
---|
105 | ISEOConfigletAdvancedSchema): |
---|
106 | """Combined schema for the adapter lookup. |
---|
107 | """ |
---|
108 | |
---|
109 | |
---|
110 | class SEOConfigletAdapter(SchemaAdapterBase): |
---|
111 | |
---|
112 | adapts(IPloneSiteRoot) |
---|
113 | implements(ISEOConfigletSchema) |
---|
114 | |
---|
115 | def __init__(self, context): |
---|
116 | super(SEOConfigletAdapter, self).__init__(context) |
---|
117 | self.portal = getSite() |
---|
118 | pprop = getToolByName(self.portal, 'portal_properties') |
---|
119 | self.context = pprop.seo_properties |
---|
120 | self.siteprops = pprop.site_properties |
---|
121 | self.ttool = getToolByName(context, 'portal_types') |
---|
122 | self.encoding = pprop.site_properties.default_charset |
---|
123 | |
---|
124 | def getExposeDC(self): |
---|
125 | return self.siteprops.getProperty('exposeDCMetaTags') |
---|
126 | |
---|
127 | def setExposeDC(self, value): |
---|
128 | return self.siteprops._updateProperty('exposeDCMetaTags', bool(value)) |
---|
129 | |
---|
130 | def getTypesSEOEnabled(self): |
---|
131 | ct_with_seo = self.context.content_types_with_seoproperties |
---|
132 | return [t for t in self.ttool.listContentTypes() if t in ct_with_seo] |
---|
133 | |
---|
134 | def setTypesSEOEnabled(self, value): |
---|
135 | value = [t for t in self.ttool.listContentTypes() if t in value] |
---|
136 | self.context._updateProperty('content_types_with_seoproperties', value) |
---|
137 | |
---|
138 | def getCustomScript(self): |
---|
139 | description = getattr(self.context, 'custom_script', u'') |
---|
140 | return safe_unicode(description) |
---|
141 | |
---|
142 | def setCustomScript(self, value): |
---|
143 | if value is not None: |
---|
144 | self.context.custom_script = value.encode(self.encoding) |
---|
145 | else: |
---|
146 | self.context.custom_script = '' |
---|
147 | |
---|
148 | exposeDCMetaTags = property(getExposeDC, setExposeDC) |
---|
149 | seo_default_custom_metatag = ISEOConfigletSchema['default_custom_metatags'] |
---|
150 | default_custom_metatags = ProxyFieldProperty(seo_default_custom_metatag) |
---|
151 | metatags_order = ProxyFieldProperty(ISEOConfigletSchema['metatags_order']) |
---|
152 | types_seo_enabled = property(getTypesSEOEnabled, setTypesSEOEnabled) |
---|
153 | custom_script = property(getCustomScript, setCustomScript) |
---|
154 | fields = ProxyFieldProperty(ISEOConfigletSchema['fields']) |
---|
155 | stop_words = ProxyFieldProperty(ISEOConfigletSchema['stop_words']) |
---|
156 | seo_external_keywords_test = ISEOConfigletSchema['external_keywords_test'] |
---|
157 | external_keywords_test = ProxyFieldProperty(seo_external_keywords_test) |
---|
158 | |
---|
159 | |
---|
160 | class Text2ListWidget(TextAreaWidget): |
---|
161 | height = 5 |
---|
162 | splitter = re.compile(u'\\r?\\n', re.S | re.U) |
---|
163 | |
---|
164 | def _toFieldValue(self, input): |
---|
165 | if input == self._missing: |
---|
166 | return self.context._type() |
---|
167 | else: |
---|
168 | return self.context._type(filter(None, self.splitter.split(input))) |
---|
169 | |
---|
170 | def _toFormValue(self, value): |
---|
171 | if value == self.context.missing_value or \ |
---|
172 | value == self.context._type(): |
---|
173 | return self._missing |
---|
174 | else: |
---|
175 | return u'\r\n'.join(list(value)) |
---|
176 | |
---|
177 | |
---|
178 | # Fieldset configurations |
---|
179 | baseset = FormFieldsets(ISEOConfigletBaseSchema) |
---|
180 | baseset.id = 'seobase' |
---|
181 | baseset.label = _(u'label_seobase', default=u'Base') |
---|
182 | |
---|
183 | advancedset = FormFieldsets(ISEOConfigletAdvancedSchema) |
---|
184 | advancedset.id = 'seoadvanced' |
---|
185 | advancedset.label = _(u'label_seoadvanced', default=u'Advanced') |
---|
186 | |
---|
187 | |
---|
188 | class SEOConfiglet(ControlPanelForm): |
---|
189 | |
---|
190 | form_fields = FormFieldsets(baseset, advancedset) |
---|
191 | type_seo_enabled = MultiCheckBoxThreeColumnWidget |
---|
192 | |
---|
193 | form_fields['default_custom_metatags'].custom_widget = Text2ListWidget |
---|
194 | form_fields['metatags_order'].custom_widget = Text2ListWidget |
---|
195 | form_fields['types_seo_enabled'].custom_widget = type_seo_enabled |
---|
196 | form_fields['types_seo_enabled'].custom_widget.cssClass = 'label' |
---|
197 | form_fields['fields'].custom_widget = Text2ListWidget |
---|
198 | form_fields['stop_words'].custom_widget = Text2ListWidget |
---|
199 | |
---|
200 | label = _("Search Engine Optimizer configuration") |
---|
201 | description = _("seo_configlet_description", default="You can select what " |
---|
202 | "content types are qSEOptimizer-enabled, and control if " |
---|
203 | "Dublin Core metatags are exposed in the header of content" |
---|
204 | " pages.") |
---|
205 | form_name = _("") |
---|