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

Last change on this file since 3344 was 3224, checked in by vmaksymiv, 13 years ago

fixed getting canonical url property

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