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
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            "seo_noframes": self.getSEOProperty('qSEO_noframes',
65                                                default=''),
66            "meta_keywords": self.getSEOProperty('qSEO_keywords',
67                                                 'Subject', ()),
68            "seo_keywords": self.getSEOProperty('qSEO_keywords', default=()),
69            "seo_canonical": self.getSEOProperty(CANONICAL_PROPERTY),
70            # Add test properties
71            "has_seo_title": self.context.hasProperty('qSEO_title'),
72            "has_seo_robots": self.context.hasProperty('qSEO_robots'),
73            "has_seo_description":
74            self.context.hasProperty('qSEO_description'),
75            "has_seo_distribution":
76            self.context.hasProperty('qSEO_distribution'),
77            "has_html_comment": self.context.hasProperty('qSEO_html_comment'),
78            "has_noframes": self.context.hasProperty('qSEO_noframes'),
79            "has_seo_keywords": self.context.hasProperty('qSEO_keywords'),
80            "has_seo_canonical": self.context.hasProperty(CANONICAL_PROPERTY),
81        }
82        #seotags["seo_nonEmptylocalMetaTags"] = \
83        #    bool(seotags["seo_localCustomMetaTags"])
84        return seotags
85
86    def getSEOProperty(self, property_name, accessor='', default=None):
87        """ Get value from seo property by property name.
88        """
89        context = aq_inner(self.context)
90
91        if context.hasProperty(property_name):
92            return context.getProperty(property_name, default)
93
94        if accessor:
95            method = getattr(context, accessor, default)
96            if not callable(method):
97                return default
98
99            # Catch AttributeErrors raised by some AT applications
100            try:
101                value = method()
102            except AttributeError:
103                value = default
104
105            return value
106        return default
107
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).
112        """
113        glob = self.seo_globalCustomMetaTags()
114        loc = self.seo_localCustomMetaTags()
115        gnames = set(map(lambda x: x['meta_name'], glob))
116        lnames = set(map(lambda x: x['meta_name'], loc))
117        # Get untouch global, override global in custom
118        # and new custom meta tags
119        untouchglob = [t for t in glob
120                       if t['meta_name'] in list(gnames - lnames)]
121        return untouchglob + loc
122
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).
127        """
128        glob = self.seo_globalCustomMetaTags()
129        loc = self.seo_localCustomMetaTags()
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)]
133
134    def seo_localCustomMetaTags(self):
135        """ Returned seo custom metatags from properties in
136            context (local seo custom metatags).
137        """
138        result = []
139        property_prefix = 'qSEO_custom_'
140        context = aq_inner(self.context)
141        for property, value in context.propertyItems():
142            if property.startswith(property_prefix) and \
143                    property[len(property_prefix):]:
144                result.append({'meta_name': property[len(property_prefix):],
145                               'meta_content': value})
146        return result
147
148    @ram.cache(plone_instance_time)
149    def seo_globalCustomMetaTags(self):
150        """ Returned seo custom metatags from default_custom_metatags property
151            in seo_properties.
152        """
153        result = []
154        if self.gseo:
155            for tag in self.gseo.default_custom_metatags:
156                name_value = tag.split(SEPERATOR)
157                if name_value[0]:
158                    result.append({'meta_name': name_value[0],
159                                   'meta_content': len(name_value) == 2 and
160                                   name_value[1] or ''})
161        return result
162
163    # Not used
164    def getCanonical(self):
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 ""
170
171
172class SEOContextPropertiesView(BrowserView):
173    """ This class contains methods that allows to manage seo properties.
174    """
175    template = ViewPageTemplateFile('templates/seo_context_properties.pt')
176
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
183    def test(self, condition, first, second):
184        """
185        """
186        return condition and first or second
187
188    def validateSEOProperty(self, property, value):
189        """ Validate a seo property.
190        """
191        return ''
192
193    def setProperty(self, property, value, type='string'):
194        """ Add a new property.
195
196            Sets a new property with the given id, value and type or
197            changes it.
198        """
199        context = aq_inner(self.context)
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
207
208    def manageSEOProps(self, **kw):
209        """ Manage seo properties.
210        """
211        context = aq_inner(self.context)
212        state = ''
213        delete_list, seo_overrides_keys, seo_keys = [], [], []
214        seo_items = dict([(k[len(SEO_PREFIX):], v)
215                          for k, v in kw.items() if k.startswith(SEO_PREFIX)])
216        for key in seo_items.keys():
217            if key.endswith(SUFFIX):
218                seo_overrides_keys.append(key[:-len(SUFFIX)])
219            else:
220                seo_keys.append(key)
221        for seo_key in seo_keys:
222            if seo_key == 'custommetatags':
223                self.manageSEOCustomMetaTagsProperties(**kw)
224            else:
225                if seo_key in seo_overrides_keys and \
226                        seo_items.get(seo_key + SUFFIX):
227                    seo_value = seo_items[seo_key]
228                    if seo_key == 'canonical':
229                        try:
230                            i_canonical_link = ICanonicalLink(self.context)
231                            i_canonical_link.canonical_link = seo_value
232                        except InvalidValue, e:
233                            state = "'%s' - wrong canonical url" % str(e)
234                    else:
235                        t_value = 'string'
236                        if isinstance(seo_value, list) or \
237                                isinstance(seo_value, tuple):
238                            t_value = 'lines'
239                        state = self.setProperty(PROP_PREFIX + seo_key,
240                                                 seo_value, type=t_value)
241                    if state:
242                        return state
243                elif seo_key == 'canonical':
244                    del ICanonicalLink(self.context).canonical_link
245                elif context.hasProperty(PROP_PREFIX + seo_key):
246                    delete_list.append(PROP_PREFIX + seo_key)
247        if delete_list:
248            context.manage_delProperties(delete_list)
249        return state
250
251    def setSEOCustomMetaTags(self, custommetatags):
252        """ Set seo custom metatags properties.
253        """
254        aq_inner(self.context)
255        for tag in custommetatags:
256            self.setProperty('%s%s' % (PROP_CUSTOM_PREFIX, tag['meta_name']),
257                             tag['meta_content'])
258
259    def delAllSEOCustomMetaTagsProperties(self):
260        """ Delete all seo custom metatags properties.
261        """
262        context = aq_inner(self.context)
263        delete_list = []
264        for property, value in context.propertyItems():
265            if property.startswith(PROP_CUSTOM_PREFIX) and \
266                    not property == PROP_CUSTOM_PREFIX:
267                delete_list.append(property)
268        if delete_list:
269            context.manage_delProperties(delete_list)
270
271    def updateSEOCustomMetaTagsProperties(self, custommetatags):
272        """ Update seo custom metatags properties.
273        """
274        globalCustomMetaTags = []
275        if self.gseo:
276            custom_meta_tags = self.gseo.default_custom_metatags
277            for tag in custom_meta_tags:
278                name_value = tag.split(SEPERATOR)
279                if name_value[0]:
280                    globalCustomMetaTags.append(
281                        {'meta_name': name_value[0],
282                         'meta_content': len(name_value) > 1 and
283                         name_value[1] or ''})
284        for tag in custommetatags:
285            meta_name, meta_content = tag['meta_name'], tag['meta_content']
286            if meta_name:
287                if not [gmt for gmt in globalCustomMetaTags
288                        if (gmt['meta_name'] == meta_name and
289                            gmt['meta_content'] == meta_content)]:
290                    self.setProperty('%s%s' % (PROP_CUSTOM_PREFIX, meta_name),
291                                     meta_content)
292
293    def manageSEOCustomMetaTagsProperties(self, **kw):
294        """ Update seo custom metatags properties, if enabled checkbox override
295            or delete properties.
296
297            Change object properties by passing either a mapping object
298            of name:value pairs {'foo':6} or passing name=value parameters.
299        """
300        aq_inner(self.context)
301        self.delAllSEOCustomMetaTagsProperties()
302        if kw.get('seo_custommetatags_override'):
303            custommetatags = kw.get('seo_custommetatags', {})
304            self.updateSEOCustomMetaTagsProperties(custommetatags)
305
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.
313        sw = map(lambda x: unicode.encode(x, enc), self.gseo.stop_words)
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
324    def __call__(self):
325        """ Perform the update seo properties and redirect if necessary,
326            or render the page Call method.
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:
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.')
340                    kwargs = {'modification_date': DateTime()}
341                    context.plone_utils.contentEdit(context, **kwargs)
342                else:
343                    msgtype = "error"
344            else:
345                # Cancel
346                msg = _('seoproperties_canceled', default=u'No content SEO '
347                        'properties have been changed.')
348
349            context.plone_utils.addPortalMessage(msg, msgtype)
350            if msgtype == "info":
351                return request.response.redirect(self.context.absolute_url())
352
353        return self.template()
354
355
356class VisibilityCheckerView(BrowserView):
357    """ This class contains methods that visibility checker.
358    """
359
360    def checkVisibilitySEOAction(self):
361        """ Checks visibility 'SEO Properties' action for content
362        """
363        aq_inner(self.context)
364        plone = queryMultiAdapter((self, self.request),
365                                  name="plone_portal_state").portal()
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.