source: products/quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/views.py @ 3141

Last change on this file since 3141 was 3141, checked in by zidane, 13 years ago

fixes pyflakes

  • Property svn:eol-style set to native
File size: 14.9 KB
Line 
1from time import time
2from DateTime import DateTime
3from Acquisition import aq_inner
4from zope.component import queryAdapter
5from zope.component import queryMultiAdapter
6from zope.schema.interfaces import InvalidValue
7
8from plone.memoize import view, ram
9
10from Products.Five.browser import BrowserView
11from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
12from Products.CMFPlone.utils import getSiteEncoding
13
14from quintagroup.canonicalpath.interfaces import ICanonicalLink
15from quintagroup.canonicalpath.adapters import PROPERTY_LINK \
16                                               as CANONICAL_PROPERTY
17
18from quintagroup.seoptimizer.browser.seo_configlet import ISEOConfigletSchema
19from quintagroup.seoptimizer import SeoptimizerMessageFactory as _
20
21SEPERATOR = '|'
22SEO_PREFIX = 'seo_'
23PROP_PREFIX = 'qSEO_'
24SUFFIX = '_override'
25PROP_CUSTOM_PREFIX = 'qSEO_custom_'
26
27
28# Ram cache function, which depends on plone instance and time
29def plone_instance_time(method, self, *args, **kwargs):
30    return (self.pps.portal(), time() // (60 * 60))
31
32
33class SEOContext(BrowserView):
34    """ This class contains methods that allows to edit html header meta tags.
35    """
36
37    def __init__(self, *args, **kwargs):
38        super(SEOContext, self).__init__(*args, **kwargs)
39        self.pps = queryMultiAdapter((self.context, self.request),
40                                     name="plone_portal_state")
41        self.pcs = queryMultiAdapter((self.context, self.request),
42                                     name="plone_context_state")
43        self.gseo = queryAdapter(self.pps.portal(), ISEOConfigletSchema)
44        self._seotags = self._getSEOTags()
45
46    def __getitem__(self, key):
47        return self._seotags.get(key, '')
48
49    @view.memoize
50    def _getSEOTags(self):
51        seotags = {
52            "seo_title": self.getSEOProperty('qSEO_title',
53                                             default=self.pcs.object_title()),
54            "seo_robots": self.getSEOProperty('qSEO_robots', default='ALL'),
55            "seo_description": self.getSEOProperty('qSEO_description',
56                                                   accessor='Description'),
57            "seo_distribution": self.getSEOProperty('qSEO_distribution',
58                                                    default="Global"),
59            "seo_customMetaTags": self.seo_customMetaTags(),
60            # "seo_localCustomMetaTags": self.seo_localCustomMetaTags(),
61            # "seo_globalCustomMetaTags": self.seo_globalCustomMetaTags(),
62            "seo_html_comment": self.getSEOProperty('qSEO_html_comment',
63                                                    default=''),
64            "meta_keywords": self.getSEOProperty('qSEO_keywords',
65                                                 'Subject', ()),
66            "seo_keywords": self.getSEOProperty('qSEO_keywords', default=()),
67            "seo_canonical": self.getCanonical(),
68            # Add test properties
69            "has_seo_title": self.context.hasProperty('qSEO_title'),
70            "has_seo_robots": self.context.hasProperty('qSEO_robots'),
71            "has_seo_description": \
72                     self.context.hasProperty('qSEO_description'),
73            "has_seo_distribution": \
74                     self.context.hasProperty('qSEO_distribution'),
75            "has_html_comment": self.context.hasProperty('qSEO_html_comment'),
76            "has_seo_keywords": self.context.hasProperty('qSEO_keywords'),
77            "has_seo_canonical": self.context.hasProperty(CANONICAL_PROPERTY),
78            }
79        #seotags["seo_nonEmptylocalMetaTags"] = \
80        #    bool(seotags["seo_localCustomMetaTags"])
81        return seotags
82
83    def getSEOProperty(self, property_name, accessor='', default=None):
84        """ Get value from seo property by property name.
85        """
86        context = aq_inner(self.context)
87
88        if context.hasProperty(property_name):
89            return context.getProperty(property_name, default)
90
91        if accessor:
92            method = getattr(context, accessor, default)
93            if not callable(method):
94                return default
95
96            # Catch AttributeErrors raised by some AT applications
97            try:
98                value = method()
99            except AttributeError:
100                value = default
101
102            return value
103        return default
104
105    def seo_customMetaTags(self):
106        """Returned seo custom metatags from default_custom_metatags property
107           in seo_properties (global seo custom metatags) with update from seo
108           custom metatags properties in context (local seo custom metatags).
109        """
110        glob = self.seo_globalCustomMetaTags()
111        loc = self.seo_localCustomMetaTags()
112        gnames = set(map(lambda x: x['meta_name'], glob))
113        lnames = set(map(lambda x: x['meta_name'], loc))
114        # Get untouch global, override global in custom
115        # and new custom meta tags
116        untouchglob = [t for t in glob \
117                         if t['meta_name'] in list(gnames - lnames)]
118        return untouchglob + loc
119
120    def seo_globalWithoutLocalCustomMetaTags(self):
121        """Returned seo custom metatags from default_custom_metatags property
122           in seo_properties (global seo custom metatags) without seo custom
123           metatags from properties in context (local seo custom metatags).
124        """
125        glob = self.seo_globalCustomMetaTags()
126        loc = self.seo_localCustomMetaTags()
127        gnames = set(map(lambda x: x['meta_name'], glob))
128        lnames = set(map(lambda x: x['meta_name'], loc))
129        return [t for t in glob if t['meta_name'] in list(gnames - lnames)]
130
131    def seo_localCustomMetaTags(self):
132        """ Returned seo custom metatags from properties in
133            context (local seo custom metatags).
134        """
135        result = []
136        property_prefix = 'qSEO_custom_'
137        context = aq_inner(self.context)
138        for property, value in context.propertyItems():
139            if property.startswith(property_prefix) and \
140               property[len(property_prefix):]:
141                result.append({'meta_name': property[len(property_prefix):],
142                               'meta_content': value})
143        return result
144
145    @ram.cache(plone_instance_time)
146    def seo_globalCustomMetaTags(self):
147        """ Returned seo custom metatags from default_custom_metatags property
148            in seo_properties.
149        """
150        result = []
151        if self.gseo:
152            for tag in self.gseo.default_custom_metatags:
153                name_value = tag.split(SEPERATOR)
154                if name_value[0]:
155                    result.append({'meta_name': name_value[0],
156                                   'meta_content': len(name_value) == 2 and \
157                                                    name_value[1] or ''})
158        return result
159
160    def getCanonical(self):
161        canonical = queryAdapter(self.context, ICanonicalLink)
162        return canonical and canonical.canonical_link or ""
163
164
165class SEOContextPropertiesView(BrowserView):
166    """ This class contains methods that allows to manage seo properties.
167    """
168    template = ViewPageTemplateFile('templates/seo_context_properties.pt')
169
170    def __init__(self, *args, **kwargs):
171        super(SEOContextPropertiesView, self).__init__(*args, **kwargs)
172        self.pps = queryMultiAdapter((self.context, self.request),
173                                     name="plone_portal_state")
174        self.gseo = queryAdapter(self.pps.portal(), ISEOConfigletSchema)
175
176    def test(self, condition, first, second):
177        """
178        """
179        return condition and first or second
180
181    def validateSEOProperty(self, property, value):
182        """ Validate a seo property.
183        """
184        return ''
185
186    def setProperty(self, property, value, type='string'):
187        """ Add a new property.
188
189            Sets a new property with the given id, value and type or
190            changes it.
191        """
192        context = aq_inner(self.context)
193        state = self.validateSEOProperty(property, value)
194        if not state:
195            if context.hasProperty(property):
196                context.manage_changeProperties({property: value})
197            else:
198                context.manage_addProperty(property, value, type)
199        return state
200
201    def manageSEOProps(self, **kw):
202        """ Manage seo properties.
203        """
204        context = aq_inner(self.context)
205        state = ''
206        delete_list, seo_overrides_keys, seo_keys = [], [], []
207        seo_items = dict([(k[len(SEO_PREFIX):], v) \
208                         for k, v in kw.items() if k.startswith(SEO_PREFIX)])
209        for key in seo_items.keys():
210            if key.endswith(SUFFIX):
211                seo_overrides_keys.append(key[:-len(SUFFIX)])
212            else:
213                seo_keys.append(key)
214        for seo_key in seo_keys:
215            if seo_key == 'custommetatags':
216                self.manageSEOCustomMetaTagsProperties(**kw)
217            else:
218                if seo_key in seo_overrides_keys and \
219                              seo_items.get(seo_key + SUFFIX):
220                    seo_value = seo_items[seo_key]
221                    if seo_key == 'canonical':
222                        try:
223                            i_canonical_link = ICanonicalLink(self.context)
224                            i_canonical_link.canonical_link = seo_value
225                        except InvalidValue, e:
226                            state = "'%s' - wrong canonical url" % str(e)
227                    else:
228                        t_value = 'string'
229                        if type(seo_value) == type([]) or \
230                           type(seo_value) == type(()):
231                            t_value = 'lines'
232                        state = self.setProperty(PROP_PREFIX + seo_key,
233                                                 seo_value, type=t_value)
234                    if state:
235                        return state
236                elif seo_key == 'canonical':
237                    del ICanonicalLink(self.context).canonical_link
238                elif context.hasProperty(PROP_PREFIX + seo_key):
239                    delete_list.append(PROP_PREFIX + seo_key)
240        if delete_list:
241            context.manage_delProperties(delete_list)
242        return state
243
244    def setSEOCustomMetaTags(self, custommetatags):
245        """ Set seo custom metatags properties.
246        """
247        aq_inner(self.context)
248        for tag in custommetatags:
249            self.setProperty('%s%s' % (PROP_CUSTOM_PREFIX, tag['meta_name']),
250                             tag['meta_content'])
251
252    def delAllSEOCustomMetaTagsProperties(self):
253        """ Delete all seo custom metatags properties.
254        """
255        context = aq_inner(self.context)
256        delete_list = []
257        for property, value in context.propertyItems():
258            if property.startswith(PROP_CUSTOM_PREFIX) and \
259                                   not property == PROP_CUSTOM_PREFIX:
260                delete_list.append(property)
261        if delete_list:
262            context.manage_delProperties(delete_list)
263
264    def updateSEOCustomMetaTagsProperties(self, custommetatags):
265        """ Update seo custom metatags properties.
266        """
267        globalCustomMetaTags = []
268        if self.gseo:
269            custom_meta_tags = self.gseo.default_custom_metatags
270            for tag in custom_meta_tags:
271                name_value = tag.split(SEPERATOR)
272                if name_value[0]:
273                    globalCustomMetaTags.append(
274                        {'meta_name': name_value[0],
275                         'meta_content': len(name_value) > 1 and \
276                                         name_value[1] or ''})
277        for tag in custommetatags:
278            meta_name, meta_content = tag['meta_name'], tag['meta_content']
279            if meta_name:
280                if not [gmt for gmt in globalCustomMetaTags \
281                        if (gmt['meta_name'] == meta_name and \
282                            gmt['meta_content'] == meta_content)]:
283                    self.setProperty('%s%s' % (PROP_CUSTOM_PREFIX, meta_name),
284                                     meta_content)
285
286    def manageSEOCustomMetaTagsProperties(self, **kw):
287        """ Update seo custom metatags properties, if enabled checkbox override
288            or delete properties.
289
290            Change object properties by passing either a mapping object
291            of name:value pairs {'foo':6} or passing name=value parameters.
292        """
293        aq_inner(self.context)
294        self.delAllSEOCustomMetaTagsProperties()
295        if kw.get('seo_custommetatags_override'):
296            custommetatags = kw.get('seo_custommetatags', {})
297            self.updateSEOCustomMetaTagsProperties(custommetatags)
298
299    def getPropertyStopWords(self):
300        """ Get property 'stop_words' from SEO Properties tool.
301        """
302        enc = getSiteEncoding(self.context)
303        # self.gseo.stop_words return list of unicode objects,
304        # and may contains stop words in different languages.
305        # So we must return encoded strings.
306        sw = map(lambda x: unicode.encode(x, enc), self.gseo.stop_words)
307        return str(sw)
308
309    def getPropertyFields(self):
310        """ Get property 'fields' from SEO Properties tool.
311        """
312        # self.gseo.fields return list of unicode objects,
313        # so *str* use as encoding function from unicode to latin-1 string.
314        fields_id = map(str, self.gseo.fields)
315        return str(fields_id)
316
317    def __call__(self):
318        """ Perform the update seo properties and redirect if necessary,
319            or render the page Call method.
320        """
321        context = aq_inner(self.context)
322        request = self.request
323        form = self.request.form
324        submitted = form.get('form.submitted', False)
325        if submitted:
326            msgtype = "info"
327            save = form.get('form.button.Save', False)
328            if save:
329                msg = self.manageSEOProps(**form)
330                if not msg:
331                    msg = _('seoproperties_saved',
332                            default=u'Content SEO properties have been saved.')
333                    kwargs = {'modification_date': DateTime()}
334                    context.plone_utils.contentEdit(context, **kwargs)
335                else:
336                    msgtype = "error"
337            else:
338                # Cancel
339                msg = _('seoproperties_canceled', default=u'No content SEO ' \
340                        'properties have been changed.')
341
342            context.plone_utils.addPortalMessage(msg, msgtype)
343            if msgtype == "info":
344                return request.response.redirect(self.context.absolute_url())
345
346        return self.template()
347
348
349class VisibilityCheckerView(BrowserView):
350    """ This class contains methods that visibility checker.
351    """
352
353    def checkVisibilitySEOAction(self):
354        """ Checks visibility 'SEO Properties' action for content
355        """
356        aq_inner(self.context)
357        plone = queryMultiAdapter((self, self.request),
358                                  name="plone_portal_state").portal()
359        adapter = ISEOConfigletSchema(plone)
360        return bool(self.context.portal_type in adapter.types_seo_enabled)
Note: See TracBrowser for help on using the repository browser.