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
RevLine 
[1468]1from sets import Set
[1908]2from time import time
[1475]3from DateTime import DateTime
[401]4from Acquisition import aq_inner
[896]5from zope.component import queryAdapter
[1824]6from zope.component import queryMultiAdapter
[1935]7from zope.schema.interfaces import InvalidValue
8
[1908]9from plone.memoize import view, ram
[392]10from plone.app.controlpanel.form import ControlPanelView
11
[408]12from Products.Five.browser import BrowserView
[1918]13from Products.CMFPlone.utils import getSiteEncoding
[401]14from Products.CMFCore.utils import getToolByName
15from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
[1480]16from Products.CMFPlone import PloneMessageFactory as pmf
[401]17
[1931]18from quintagroup.canonicalpath.interfaces import ICanonicalLink
19from quintagroup.canonicalpath.adapters import PROPERTY_LINK as CANONICAL_PROPERTY
20
[1671]21from quintagroup.seoptimizer import interfaces
[1821]22from quintagroup.seoptimizer.browser.seo_configlet import ISEOConfigletSchema
[1824]23from quintagroup.seoptimizer import SeoptimizerMessageFactory as _
[1463]24
[1183]25SEPERATOR = '|'
[1460]26SEO_PREFIX = 'seo_'
27PROP_PREFIX = 'qSEO_'
28SUFFIX = '_override'
[1461]29PROP_CUSTOM_PREFIX = 'qSEO_custom_'
30
[1908]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
[408]35class SEOContext( BrowserView ):
[1506]36    """ This class contains methods that allows to edit html header meta tags.
[408]37    """
[1782]38
[1815]39    def __init__(self, *args, **kwargs):
40        super(SEOContext, self).__init__(*args, **kwargs)
[1831]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)
[1815]44        self._seotags = self._getSEOTags()
[1782]45
[1815]46    def __getitem__(self, key):
47        return self._seotags.get(key, '')
48
49    @view.memoize
50    def _getSEOTags(self):
51        seotags = {
[1831]52            "seo_title": self.getSEOProperty( 'qSEO_title', default=self.pcs.object_title() ),
[1815]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(),
[1908]57            # "seo_localCustomMetaTags": self.seo_localCustomMetaTags(),
58            # "seo_globalCustomMetaTags": self.seo_globalCustomMetaTags(),
[1815]59            "seo_html_comment": self.getSEOProperty( 'qSEO_html_comment', default='' ),
[1824]60            "meta_keywords": self.getSEOProperty('qSEO_keywords', 'Subject', ()),
61            "seo_keywords": self.getSEOProperty('qSEO_keywords', default=()),
[1931]62            "seo_canonical": ICanonicalLink(self.context).canonical_link,
[1815]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'),
[1931]70            "has_seo_canonical": self.context.hasProperty(CANONICAL_PROPERTY),
[1815]71            }
[1908]72        #seotags["seo_nonEmptylocalMetaTags"] = bool(seotags["seo_localCustomMetaTags"])
[1815]73        return seotags
74
75    def getSEOProperty( self, property_name, accessor='', default=None ):
[1506]76        """ Get value from seo property by property name.
[408]77        """
[413]78        context = aq_inner(self.context)
79
80        if context.hasProperty(property_name):
[1815]81            return context.getProperty(property_name, default)
[1475]82
[413]83        if accessor:
[1815]84            method = getattr(context, accessor, default)
[413]85            if not callable(method):
[1815]86                return default
[408]87
[413]88            # Catch AttributeErrors raised by some AT applications
89            try:
90                value = method()
91            except AttributeError:
[1815]92                value = default
[1475]93
[413]94            return value
[1824]95        return default
[408]96
97    def seo_customMetaTags( self ):
[1819]98        """Returned seo custom metatags from default_custom_metatags property in seo_properties
[1506]99        (global seo custom metatags) with update from seo custom metatags properties in context (local seo custom metatags).
[408]100        """
[1824]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
[1265]107
108    def seo_globalWithoutLocalCustomMetaTags( self ):
[1819]109        """Returned seo custom metatags from default_custom_metatags property in seo_properties
[1506]110        (global seo custom metatags) without seo custom metatags from properties in context (local seo custom metatags).
[1265]111        """
[1824]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)]
[1265]116
117    def seo_localCustomMetaTags( self ):
[1506]118        """ Returned seo custom metatags from properties in context (local seo custom metatags).
[1265]119        """
[408]120        result = []
121        property_prefix = 'qSEO_custom_'
122        context = aq_inner(self.context)
123        for property, value in context.propertyItems():
[1265]124            if property.startswith(property_prefix) and property[len(property_prefix):]:
[408]125                result.append({'meta_name'    : property[len(property_prefix):],
126                               'meta_content' : value})
[1265]127        return result
[408]128
[1908]129    @ram.cache(plone_instance_time)
[1265]130    def seo_globalCustomMetaTags( self ):
[1506]131        """ Returned seo custom metatags from default_custom_metatags property in seo_properties.
[1265]132        """
133        result = []
[1824]134        if self.gseo:
[1900]135            for tag in self.gseo.default_custom_metatags:
[1183]136                name_value = tag.split(SEPERATOR)
[1265]137                if name_value[0]:
138                    result.append({'meta_name'    : name_value[0],
[1309]139                                   'meta_content' : len(name_value) == 2 and name_value[1] or ''})
[408]140        return result
[1265]141
[896]142
[1460]143class SEOContextPropertiesView( BrowserView ):
[1506]144    """ This class contains methods that allows to manage seo properties.
[1460]145    """
146    template = ViewPageTemplateFile('templates/seo_context_properties.pt')
147
[1900]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
[1461]155    def test( self, condition, first, second ):
156        """
157        """
158        return condition and first or second
[1466]159
160    def validateSEOProperty(self, property, value):
[1506]161        """ Validate a seo property.
162        """
[1931]163        return ''
[1466]164
[1462]165    def setProperty(self, property, value, type='string'):
[1506]166        """ Add a new property.
167
168        Sets a new property with the given id, value and type or changes it.
169        """
[1935]170        context = aq_inner(self.context) 
[1466]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
[1461]178
179    def manageSEOProps(self, **kw):
[1506]180        """ Manage seo properties.
181        """
[1461]182        context = aq_inner(self.context)
[1466]183        state = ''
[1468]184        delete_list, seo_overrides_keys, seo_keys = [], [], []
[1460]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):
[1468]188                seo_overrides_keys.append(key[:-len(SUFFIX)])
[1460]189            else:
[1468]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]
[1935]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)
[1468]206                    if state:
207                        return state
[1935]208                elif seo_key == 'canonical':
209                    del ICanonicalLink(self.context).canonical_link
[1468]210                elif context.hasProperty(PROP_PREFIX+seo_key):
211                    delete_list.append(PROP_PREFIX+seo_key)
[1466]212        if delete_list:
213            context.manage_delProperties(delete_list)
214        return state
[1461]215
[1462]216    def setSEOCustomMetaTags(self, custommetatags):
[1506]217        """ Set seo custom metatags properties.
218        """
[1462]219        context = aq_inner(self.context)
[1463]220        for tag in custommetatags:
221            self.setProperty('%s%s' % (PROP_CUSTOM_PREFIX, tag['meta_name']), tag['meta_content'])
[1462]222
[1466]223    def delAllSEOCustomMetaTagsProperties(self):
[1506]224        """ Delete all seo custom metatags properties.
225        """
[1466]226        context = aq_inner(self.context)
[1461]227        delete_list = []
228        for property, value in context.propertyItems():
[1466]229            if property.startswith(PROP_CUSTOM_PREFIX)  and not property == PROP_CUSTOM_PREFIX:
[1461]230                delete_list.append(property)
[1466]231        if delete_list:
232            context.manage_delProperties(delete_list)
[1461]233
[1466]234    def updateSEOCustomMetaTagsProperties(self, custommetatags):
[1506]235        """ Update seo custom metatags properties.
236        """
[1461]237        globalCustomMetaTags = []
[1900]238        if self.gseo:
239            custom_meta_tags = self.gseo.default_custom_metatags
[1461]240            for tag in custom_meta_tags:
241                name_value = tag.split(SEPERATOR)
242                if name_value[0]:
[1900]243                    globalCustomMetaTags.append(
244                        {'meta_name' : name_value[0],
245                         'meta_content' : len(name_value) == 1 and '' or name_value[1]})
[1461]246        for tag in custommetatags:
247            meta_name, meta_content = tag['meta_name'], tag['meta_content']
248            if meta_name:
[1900]249                if not [gmt for gmt in globalCustomMetaTags \
250                        if (gmt['meta_name']==meta_name and gmt['meta_content']==meta_content)]:
[1466]251                    self.setProperty('%s%s' % (PROP_CUSTOM_PREFIX, meta_name), meta_content)
252
253    def manageSEOCustomMetaTagsProperties(self, **kw):
[1506]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        """
[1461]259        context = aq_inner(self.context)
[1466]260        self.delAllSEOCustomMetaTagsProperties()
261        if kw.get('seo_custommetatags_override'):
262            custommetatags = kw.get('seo_custommetatags', {})
263            self.updateSEOCustomMetaTagsProperties(custommetatags)
[1460]264
[1918]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
[1460]283    def __call__( self ):
[1506]284        """ Perform the update seo properties and redirect if necessary, or render the page Call method.
[1460]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:
[1466]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)
[1475]295                kwargs = {'modification_date' : DateTime()} 
[1675]296                context.plone_utils.contentEdit(context, **kwargs)
[1466]297                return request.response.redirect(self.context.absolute_url())
298            context.plone_utils.addPortalMessage(state, 'error')
299        return self.template()
[1821]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)
[1824]310        plone = queryMultiAdapter((self, self.request),name="plone_portal_state").portal()
[1821]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.