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

Last change on this file was 3547, checked in by ktarasz, 12 years ago

fix pep8

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