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
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=()),
[2237]67            "seo_canonical": self.getCanonical(),
[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
[2237]160    def getCanonical(self):
161        canonical = queryAdapter(self.context, ICanonicalLink)
162        return canonical and canonical.canonical_link or ""
[1265]163
[2237]164
[3134]165class SEOContextPropertiesView(BrowserView):
[1506]166    """ This class contains methods that allows to manage seo properties.
[1460]167    """
168    template = ViewPageTemplateFile('templates/seo_context_properties.pt')
169
[2139]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
[3134]176    def test(self, condition, first, second):
[1461]177        """
178        """
[2139]179        return condition and first or second
[1466]180
181    def validateSEOProperty(self, property, value):
[1506]182        """ Validate a seo property.
183        """
[2139]184        return ''
[1466]185
[1462]186    def setProperty(self, property, value, type='string'):
[1506]187        """ Add a new property.
188
[3134]189            Sets a new property with the given id, value and type or
190            changes it.
[1506]191        """
[1462]192        context = aq_inner(self.context)
[1466]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
[1461]200
201    def manageSEOProps(self, **kw):
[1506]202        """ Manage seo properties.
203        """
[1461]204        context = aq_inner(self.context)
[1466]205        state = ''
[1468]206        delete_list, seo_overrides_keys, seo_keys = [], [], []
[3134]207        seo_items = dict([(k[len(SEO_PREFIX):], v) \
208                         for k, v in kw.items() if k.startswith(SEO_PREFIX)])
[1460]209        for key in seo_items.keys():
210            if key.endswith(SUFFIX):
[1468]211                seo_overrides_keys.append(key[:-len(SUFFIX)])
[1460]212            else:
[1468]213                seo_keys.append(key)
214        for seo_key in seo_keys:
215            if seo_key == 'custommetatags':
216                self.manageSEOCustomMetaTagsProperties(**kw)
217            else:
[3134]218                if seo_key in seo_overrides_keys and \
219                              seo_items.get(seo_key + SUFFIX):
[1468]220                    seo_value = seo_items[seo_key]
[2139]221                    if seo_key == 'canonical':
222                        try:
[3134]223                            i_canonical_link = ICanonicalLink(self.context)
224                            i_canonical_link.canonical_link = seo_value
[2139]225                        except InvalidValue, e:
226                            state = "'%s' - wrong canonical url" % str(e)
227                    else:
228                        t_value = 'string'
[3134]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)
[1468]234                    if state:
235                        return state
[2139]236                elif seo_key == 'canonical':
237                    del ICanonicalLink(self.context).canonical_link
[3134]238                elif context.hasProperty(PROP_PREFIX + seo_key):
239                    delete_list.append(PROP_PREFIX + seo_key)
[1466]240        if delete_list:
241            context.manage_delProperties(delete_list)
242        return state
[1461]243
[1462]244    def setSEOCustomMetaTags(self, custommetatags):
[1506]245        """ Set seo custom metatags properties.
246        """
[3141]247        aq_inner(self.context)
[1463]248        for tag in custommetatags:
[3134]249            self.setProperty('%s%s' % (PROP_CUSTOM_PREFIX, tag['meta_name']),
250                             tag['meta_content'])
[1462]251
[1466]252    def delAllSEOCustomMetaTagsProperties(self):
[1506]253        """ Delete all seo custom metatags properties.
254        """
[1466]255        context = aq_inner(self.context)
[1461]256        delete_list = []
257        for property, value in context.propertyItems():
[3134]258            if property.startswith(PROP_CUSTOM_PREFIX) and \
259                                   not property == PROP_CUSTOM_PREFIX:
[1461]260                delete_list.append(property)
[1466]261        if delete_list:
262            context.manage_delProperties(delete_list)
[1461]263
[1466]264    def updateSEOCustomMetaTagsProperties(self, custommetatags):
[1506]265        """ Update seo custom metatags properties.
266        """
[1461]267        globalCustomMetaTags = []
[2139]268        if self.gseo:
269            custom_meta_tags = self.gseo.default_custom_metatags
[1461]270            for tag in custom_meta_tags:
271                name_value = tag.split(SEPERATOR)
272                if name_value[0]:
[2139]273                    globalCustomMetaTags.append(
[3134]274                        {'meta_name': name_value[0],
275                         'meta_content': len(name_value) > 1 and \
276                                         name_value[1] or ''})
[1461]277        for tag in custommetatags:
278            meta_name, meta_content = tag['meta_name'], tag['meta_content']
279            if meta_name:
[2139]280                if not [gmt for gmt in globalCustomMetaTags \
[3134]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)
[1466]285
286    def manageSEOCustomMetaTagsProperties(self, **kw):
[3134]287        """ Update seo custom metatags properties, if enabled checkbox override
288            or delete properties.
[1506]289
[2139]290            Change object properties by passing either a mapping object
291            of name:value pairs {'foo':6} or passing name=value parameters.
[1506]292        """
[3141]293        aq_inner(self.context)
[1466]294        self.delAllSEOCustomMetaTagsProperties()
295        if kw.get('seo_custommetatags_override'):
296            custommetatags = kw.get('seo_custommetatags', {})
297            self.updateSEOCustomMetaTagsProperties(custommetatags)
[1460]298
[2139]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.
[3134]306        sw = map(lambda x: unicode.encode(x, enc), self.gseo.stop_words)
[2139]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
[3134]317    def __call__(self):
[2139]318        """ Perform the update seo properties and redirect if necessary,
319            or render the page Call method.
[1460]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:
[3007]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.')
[3134]333                    kwargs = {'modification_date': DateTime()}
[3007]334                    context.plone_utils.contentEdit(context, **kwargs)
335                else:
336                    msgtype = "error"
337            else:
338                # Cancel
[3134]339                msg = _('seoproperties_canceled', default=u'No content SEO ' \
340                        'properties have been changed.')
[3007]341
342            context.plone_utils.addPortalMessage(msg, msgtype)
343            if msgtype == "info":
[1466]344                return request.response.redirect(self.context.absolute_url())
[3007]345
[1466]346        return self.template()
[2139]347
348
[3134]349class VisibilityCheckerView(BrowserView):
[2139]350    """ This class contains methods that visibility checker.
351    """
352
353    def checkVisibilitySEOAction(self):
354        """ Checks visibility 'SEO Properties' action for content
355        """
[3141]356        aq_inner(self.context)
[3134]357        plone = queryMultiAdapter((self, self.request),
358                                  name="plone_portal_state").portal()
[2139]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.