source: products/quintagroup.seoptimizer/branches/refactoring2.3.0/quintagroup/seoptimizer/browser/views.py @ 1935

Last change on this file since 1935 was 1935, checked in by mylan, 14 years ago

#180: Add delete canonical_link property, some optimization

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