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