Index: qSEOptimizer/tags/1.6.1/Extensions/Install.py
===================================================================
--- qSEOptimizer/tags/1.6.1/Extensions/Install.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/Extensions/Install.py	(revision 584)
@@ -0,0 +1,194 @@
+import string
+from App.Common import package_home
+from os.path import exists as path_exists, join as path_join
+from cStringIO import StringIO
+from Products.CMFCore.utils import getToolByName
+from Products.CMFCore.DirectoryView import addDirectoryViews
+from Products.qSEOptimizer import qSEO_globals
+from OFS.ObjectManager import BadRequestException
+
+from Products.qSEOptimizer.config import *
+
+try:
+    #for Plone-2.5 and higher
+    from Products.CMFCore.permissions import ManagePortal
+except ImportError:
+    from Products.CMFCore.CMFCorePermissions import ManagePortal
+
+configlets = ({'id':'qSEOptimizer',
+    'name':'Search Engine Optimizer',
+    'action':'string:${portal_url}/prefs_qseo_setup_form',
+    'condition':'',
+    'category':'Products',
+    'visible':1,
+    'appId':'qSEOptimizer',
+    'permission':ManagePortal,
+    'imageUrl':'search_icon.gif'},)
+
+qSEO_CONTENT = ['File','Document','News Item','BlogEntry']
+qSEO_FOLDER  = []
+qSEO_TYPES   = qSEO_CONTENT + qSEO_FOLDER
+try:
+    True
+except:
+    True = 1
+    False = 0
+
+def addPropertySheet(self, out):
+    """ Add seo_properties property sheet to portal_properties and some needed field to it """
+    portal_props = getToolByName(self, 'portal_properties')
+    if not hasattr(portal_props, PROPERTY_SHEET):
+        portal_props.addPropertySheet(PROPERTY_SHEET, SHEET_TITLE)
+        out.write('Added %s property sheet to portal_properties\n' % PROPERTY_SHEET)
+    else:
+        out.write('Skipped adding %s property sheet to portal_properties\n' % PROPERTY_SHEET)
+    sheet = getattr(portal_props, PROPERTY_SHEET)
+
+    if not sheet.hasProperty('stop_words'):
+        sheet._setProperty('stop_words', STOP_WORDS, 'lines')
+        out.write("Added 'stop_words' property field to %s property sheet\n" % PROPERTY_SHEET)
+
+    if not sheet.hasProperty('fields'):
+        sheet._setProperty('fields', FIELDS, 'lines')
+        out.write("Added 'fields' property field to %s property sheet\n" % PROPERTY_SHEET)
+
+    if not sheet.hasProperty('additional_keywords'):
+        sheet._setProperty('additional_keywords', [], 'lines')
+        out.write("Added 'additional_keywords' property field to %s property sheet\n" % PROPERTY_SHEET)
+
+    if not sheet.hasProperty('default_custom_metatags'):
+        sheet._setProperty('default_custom_metatags', DEFAULT_CUSTOM_METATAGS, 'lines')
+        out.write("Added 'default_custom_metatags' property field to %s property sheet\n" % PROPERTY_SHEET)
+
+def setupSkin(self, out, layers):
+    """Setup skins"""
+    skinstool=getToolByName(self, 'portal_skins')
+    addDirectoryViews(skinstool, 'skins', qSEO_globals)
+
+    for skin in skinstool.getSkinSelections():
+        modified = False
+        path = skinstool.getSkinPath(skin)
+        path = map( string.strip, string.split( path,',' ) )
+        for layer in layers:
+            if not layer in path:
+                try:
+                    path.insert(path.index('custom')+1, layer )
+                except ValueError:
+                    path.append(layer)
+                modified = True
+                out.write('  Layer %s sucessfully installed into skin %s.\n' % (layer,skin))
+            else:
+                out.write('  Layer %s was already installed into skin %s.\n' % (layer,skin))
+        if modified:
+            path = string.join( path, ', ' )
+            skinstool.addSkinSelection( skin, path )
+
+def removeSkin(self, layer):
+    """Remove layers"""
+    skinstool = getToolByName(self, 'portal_skins')
+    for skinName in skinstool.getSkinSelections():
+	original_path = skinstool.getSkinPath(skinName)
+	original_path = [l.strip() for l in original_path.split(',')]
+	new_path= []
+	for l in original_path:
+	    if (l == layer) or (l.startswith(layer+'/')):
+                continue
+	    new_path.append(l)
+	skinstool.addSkinSelection(skinName, ','.join(new_path))
+	
+def setupActions(self, out):
+    # And now update the relevant portal_type actions
+    out.write("Inspecting portal_types\n")
+    tool = getToolByName(self, 'portal_types')
+    for ptype in tool.objectValues():
+        if ptype.getId() in qSEO_TYPES:
+            #add the action for viewing versioning
+            try:
+                #for Plone-2.5 and higher
+                acts = filter(lambda x: x.id == 'seo_properties', ptype.listActions())
+                action = acts and acts[0] or None
+            except AttributeError:
+                action = ptype.getActionById('seo_properties', default=None )
+
+            if action is None:
+                out.write( '  Added SEO Properties tab for %s\n' % ptype.getId() )
+                ptype.addAction( 'seo_properties',
+                                 'SEO Properties',
+                                 'string:${object_url}/qseo_properties_edit_form',
+                                 '',
+                                 'Modify portal content',
+                                 'object',
+                                 visible=1
+                               )
+def removeActions(self):
+    # And now update the relevant portal_type actions
+    tool = getToolByName(self, 'portal_types')
+    for ptype in tool.objectValues():
+        if ptype.getId() in qSEO_TYPES:
+            #delet the action for viewing versioning
+            try:
+                #for Plone-2.5 and higher
+                acts = filter(lambda x: x.id == 'seo_properties', ptype.listActions())
+                action = acts and acts[0] or None
+            except AttributeError:
+                action = ptype.getActionById('seo_properties', default=None )
+            if action != None:
+                acts = list(ptype.listActions())
+                ptype.deleteActions([acts.index(a) for a in acts if a.getId()=='seo_properties'])
+
+def install(self):
+    """Install product"""
+    out = StringIO()
+    Layers =[]
+    mtool = getToolByName(self, 'portal_migration')
+    plone_version = mtool.getFileSystemVersion()
+    product_path = package_home(qSEO_globals)
+    versioned_skin = path_join(product_path, 'skins','qSEOptimizer', plone_version)
+    #add exposeDCMetaTags property to Plone 2.0.x
+    props = getToolByName(self, 'portal_properties').site_properties
+    if not hasattr(props, 'exposeDCMetaTags'):
+            props._setProperty('exposeDCMetaTags', True, 'boolean')
+
+    addPropertySheet(self, out)
+
+    Layers.append('qSEOptimizer')
+    out.write('  Searching for %s... ' % versioned_skin)
+    if path_exists(versioned_skin):
+        out.write('found.\n')
+        Layers.append('qSEOptimizer/%s' % plone_version)
+    elif str(plone_version) < "3":
+        out.write("not found.\nLimited functionality mode. Upgrade"
+                  "qSEOptimizer product or report to support@quintagroup.com"
+                  "if uprade not available.\n\n")
+    out.write('Call setupSkin... \n')
+    setupSkin(self, out, Layers)
+
+    out.write('Call setupActions... \n')
+    setupActions(self, out)
+
+    configTool = getToolByName(self, 'portal_controlpanel', None)
+    if configTool:
+        for conf in configlets:
+            configTool.registerConfiglet(**conf)
+            out.write('Added configlet %s\n' % conf['id'])
+
+    return out.getvalue()
+
+def uninstall(self):
+    """ Uninstall Products """
+    out = StringIO()
+
+    removeSkin(self, 'qSEOptimizer')
+    removeActions(self)
+
+    configTool = getToolByName(self, 'portal_controlpanel', None)
+    if configTool:
+        for conf in configlets:
+            try:
+                configTool.unregisterConfiglet(conf['id'])
+            except BadRequestException,KeyError:
+                portal_icons = getToolByName(self,'portal_actionicons')
+                portal_icons.manage_removeActionIcon(conf['category'],conf['id'])
+            out.write('Removed configlet %s\n' % conf['id'])
+
+    return 'qSEOptimizer succesfully removed'
Index: qSEOptimizer/tags/1.6.1/Extensions/__init__.py
===================================================================
--- qSEOptimizer/tags/1.6.1/Extensions/__init__.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/Extensions/__init__.py	(revision 584)
@@ -0,0 +1,1 @@
+# this file intentionally left blank
Index: qSEOptimizer/tags/1.6.1/HISTORY.txt
===================================================================
--- qSEOptimizer/tags/1.6.1/HISTORY.txt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/HISTORY.txt	(revision 584)
@@ -0,0 +1,158 @@
+1.6.1
+
+    * fixed breakage on getting of inexistent attribute
+    
+1.6.0
+
+    * added layer for plone 2.5.4-2 version
+    * fixed url action in qseo_properties_edit_form for plone 3.*
+
+1.5.1
+
+    * added support of additional keywords
+
+1.5.0
+
+    * added layer for plone 2.5.5 version
+
+1.4.0
+
+    * added layer for plone 2.5.4 version
+    
+    * refactored additional keywords adapter (now lynx is required)
+    
+    * fixed sorting of keywords in keywords meta tag
+
+1.3.0
+
+    * added default_custom_metatags functionality
+
+1.2.1
+
+    * refactored keywords checking mechanism
+
+1.2.0
+
+    * added custom meta tags functionality
+
+1.1.0
+
+    * Fixed to not reset the SEO property sheet content when reinstaling
+      the product.
+
+    * Added the additional keywords feature.
+
+    * Added a new test suite for the additinal keywords.
+
+    * Reformatted the history file.
+
+
+1.0.1
+
+    * Bug fix release
+
+    * Update HISTORY.txt and README.txt
+
+
+1.0.0
+
+    * Plone 3.0 support release
+
+    * Macros replaced with viewlets
+
+
+0.5.3
+
+    * Plone 2.5.3 support release.
+
+
+0.5.2
+
+    * Fixed IE related javascript bug in word counters.
+
+
+0.5
+
+    * Plone 2.1.4 support release.
+
+
+0.5-pre
+
+    * Added word statistics and field length counters.
+
+
+0.3.3
+
+    * Plone-2.0 compatibility release.
+
+    * Fixed bug that broke Plone-2.0.x with product installed in Zope and
+      not reinstalled in Plone.
+
+
+0.3.2
+
+    * Cleanup and Plone-2.1.2 compatibility.
+
+    * Cleaned up confilet.
+
+    * Made Plone-2.1.2 compatible.
+
+
+0.3.1
+
+    * Added exposeDCMetaTags propery to configlet.
+
+    * Added exposeDCMetaTags property to site_properties in Plone-2.0.x.
+
+    * More tests introduced.
+
+
+0.3.0
+
+    * Added abillity to select content types.
+
+    * Plone-2.1.1 compatibility.
+
+    * Configlet added.
+
+    * Tests added.
+
+
+0.2.0
+
+    * Initial public release.
+
+    * Plone-2.0.5 compatibility.
+
+    * Uninstaller added.
+
+
+0.1.2
+
+    * Added ability to edit HTML Comments.
+
+    * Removed developer' HTML Comments from Plone header.pt.
+
+
+0.1.1
+
+    * Renamed to qSEOptimizer (due to mistyping into initial product name).
+
+    * Added qseoptimizer.pot template for translators.
+
+    * Added Ukrainian translation.
+
+
+0.1
+
+    * Initial release.
+
+    * It is possible to edit html header META tags esential for search engines:
+
+      * description
+
+      * keywords
+
+      * robots
+
+      * distribution
Index: qSEOptimizer/tags/1.6.1/README.txt
===================================================================
--- qSEOptimizer/tags/1.6.1/README.txt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/README.txt	(revision 584)
@@ -0,0 +1,75 @@
+Quintagroup Search Engine Optimization Tool
+
+  This product was created to enhance SE visibility of Plone sites.
+
+  &copy; "Quintagroup":http://quintagroup.com, 2004-2007
+  
+  Product sponsored by "4webresults, SEO and Internet Marketing
+  Services provider":http://4webresults.com. Plone-2.1 compatibility, extra
+  configuration features sponsored by "Eogogics Inc.":http://eogogics.com, 
+  "4webresults":http://4webresults.com, and
+  "eLawSoftware":http:///www.elawsoftware.com.
+
+  Features
+  
+    Allows per document editing of:
+    
+      * HTML Title tag
+      
+      * META description tag
+      
+      * META keywords tag
+      
+      * HTML comment into page header
+      
+      * META robots tag
+      
+      * META Disposition tag
+
+    For Title, description keywords amd comment you have statistics
+    (total/stop/used words, field length counter).
+  
+  Usage
+  
+    * Install as Zope product
+    
+    * Install in your Plone instance with QuickInstaler (Plone Control
+      Panel -&gt; Add/remove Products)
+      
+    * Go to a document
+    
+    * Switch to SEO Properties tab
+    
+    * Select Override checkboxes of features you want to override
+    
+    * Type-in your SEO values
+    
+    * Save
+    
+    * Do this for all documents that needs enhanced SEO properties
+    
+Requirements
+
+  * Plone 2.0+ (compatible with all versions up to 3.0.1)
+
+Notes
+
+  * If you want to use product with zope-2.8.x then remove configure.zcml and overrides.zcml
+
+  * "Additional keywords that should be added to the content types." field in configlet is usable only in Plone 2.5 and higher.
+
+Links
+
+  * "Product Homepage":http://quintagroup.com/services/plone-development/products/qSEOptimizer
+
+Authors
+
+  * Myroslav Opyr
+  
+  * Volodymyr Romaniuk
+  
+  * Mykola Kharechko
+
+  * Vitaliy Podoba
+
+  * Volodymyr Cherepanyak
Index: qSEOptimizer/tags/1.6.1/TODO.txt
===================================================================
--- qSEOptimizer/tags/1.6.1/TODO.txt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/TODO.txt	(revision 584)
@@ -0,0 +1,5 @@
+Future
+
+  * 3 tests failures (lynx require running of a real host)
+
+  * Generic Setup integration for skin layers
Index: qSEOptimizer/tags/1.6.1/__init__.py
===================================================================
--- qSEOptimizer/tags/1.6.1/__init__.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/__init__.py	(revision 584)
@@ -0,0 +1,91 @@
+import Globals
+from Products.CMFCore.DirectoryView import registerDirectory
+from AccessControl import allow_module
+from Products.CMFCore.utils import getToolByName
+from util import SortedDict
+from os import path
+
+from Products.qSEOptimizer import config
+
+try:
+    from Products.qSEOptimizer.interfaces import IKeywords
+    zope_interface_available = True
+except ImportError:
+    zope_interface_available = False
+
+allow_module('Products.qSEOptimizer.util')
+qSEO_globals = globals()
+registerDirectory('skins', qSEO_globals)
+
+try:
+    # for Plone-2.1 and higher
+    from Products.CMFPlone.PloneTool import PloneTool
+    _present = hasattr(PloneTool, "listMetaTags")
+except ImportError:
+    _present = False
+
+def propertyItems(context):
+    """Return a list of (id,property) tuples.
+    """
+    return map(lambda i,c=context: (i['id'],getattr(c,i['id'],None)),
+                                context._properties)
+
+if _present:
+    old_lmt = PloneTool.listMetaTags
+
+    def listMetaTags(self, context):
+        portal_props = getToolByName(context, 'portal_properties')
+        seo_props = getToolByName(portal_props, 'seo_properties', None)
+        if seo_props is None:
+            return old_lmt(self, context)
+
+        site_props = getToolByName(portal_props, 'site_properties')
+        exposeDCMetaTags = site_props.exposeDCMetaTags
+
+        metaTags = SortedDict()
+        metaTags.update(old_lmt(self, context))
+        metadataList = [
+            ('qSEO_Description', 'description'),
+            ('qSEO_Keywords',    'keywords'),
+            ('qSEO_Robots',      'robots'),
+            ('qSEO_Distribution','distribution')]
+
+        if exposeDCMetaTags:
+            metadataList.append(('qSEO_Distribution', 'DC.distribution'))
+
+        for accessor, key in metadataList:
+            method = getattr(context, accessor, None)
+            if not callable(method):
+                # ups
+                continue
+            # Catch AttributeErrors raised by some AT applications
+            try:
+                value = method()
+            except AttributeError:
+                value = None
+
+            if not value:
+                continue
+            if isinstance(value, (tuple, list)):
+                value = ', '.join(value)
+
+            metaTags[key] = value
+
+        # add custom meta tags (added from qseo tab by user) for given context
+        property_prefix = 'qSEO_custom_'
+        for property, value in propertyItems(context):
+            idx = property.find(property_prefix)
+            if idx == 0 and len(property) > len(property_prefix):
+                metaTags[property[len(property_prefix):]] = value
+
+        # Set the additional matching keywords, if any
+        if zope_interface_available:
+            adapter = IKeywords(context, None)
+            if adapter is not None:
+                keywords = adapter.listKeywords()
+                if keywords:
+                    metaTags['keywords'] = keywords
+
+        return metaTags
+
+    PloneTool.listMetaTags = listMetaTags
Index: qSEOptimizer/tags/1.6.1/adapters.py
===================================================================
--- qSEOptimizer/tags/1.6.1/adapters.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/adapters.py	(revision 584)
@@ -0,0 +1,37 @@
+import re, commands
+from zope.interface import implements
+from Products.CMFCore.utils import getToolByName
+from Products.qSEOptimizer.interfaces import IKeywords
+
+
+class AdditionalKeywords(object):
+    implements(IKeywords)
+
+    def __init__(self, context):
+        self.context = context
+
+    def listKeywords(self):
+        portal_props = getToolByName(self.context, 'portal_properties')
+        seo_props = getToolByName(portal_props, 'seo_properties')
+
+        # now set type is not using because of it unordered behaviour
+        #original = set(self.context.qSEO_Keywords())
+        #additional = set(seo_props.additional_keywords)
+        #text = set(self.context.SearchableText().split())
+        #keywords = list(additional.intersection(text).union(original))
+
+        keywords = list(self.context.qSEO_Keywords())
+        lower_keywords = map(lambda x: x.lower(), keywords)
+        additional = seo_props.additional_keywords
+
+        is_test = self.context.REQUEST.get('qseo_without_additional_keywords', None)
+
+        if additional and is_test is None:
+            # extract words from url page using lynx browser
+            text = commands.getoutput('lynx --dump --nolist %s?qseo_without_additional_keywords=1' % self.context.absolute_url()).lower()
+            if text and text != 'sh: lynx: command not found':
+                for keyword in additional:
+                    if keyword.lower() not in lower_keywords and re.compile(r'\b%s\b' % keyword, re.I).search(text):
+                        keywords.append(keyword)
+
+        return ', '.join(keywords)
Index: qSEOptimizer/tags/1.6.1/browser/__init__.py
===================================================================
--- qSEOptimizer/tags/1.6.1/browser/__init__.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/browser/__init__.py	(revision 584)
@@ -0,0 +1,1 @@
+#
Index: qSEOptimizer/tags/1.6.1/browser/configure.zcml
===================================================================
--- qSEOptimizer/tags/1.6.1/browser/configure.zcml	(revision 584)
+++ qSEOptimizer/tags/1.6.1/browser/configure.zcml	(revision 584)
@@ -0,0 +1,13 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser">
+
+    <!-- The customized title -->
+    <browser:viewlet
+        name="plone.htmlhead.title"
+        manager="plone.app.layout.viewlets.interfaces.IHtmlHead"
+        class=".viewlets.TitleCommentViewlet"
+        permission="zope2.View"
+        />
+
+</configure>
Index: qSEOptimizer/tags/1.6.1/browser/interfaces.py
===================================================================
--- qSEOptimizer/tags/1.6.1/browser/interfaces.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/browser/interfaces.py	(revision 584)
@@ -0,0 +1,7 @@
+from zope.interface import Interface
+
+class IValidateSEOKeywordsView(Interface):
+    """ View for validating keywords on qSEO_properties_edit_form """
+
+    def validateKeywords(text):
+        """ Parse text and validate each keyword (extracted from text) for appearing on the context page """
Index: qSEOptimizer/tags/1.6.1/browser/keywords.py
===================================================================
--- qSEOptimizer/tags/1.6.1/browser/keywords.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/browser/keywords.py	(revision 584)
@@ -0,0 +1,111 @@
+import urllib, re, os, commands
+from xml.dom import minidom, Node
+
+from zope.interface import implements
+from Products.Five.browser import BrowserView
+
+from Products.CMFCore.utils import getToolByName
+from Products.CMFPlone import PloneMessageFactory as _
+
+from interfaces import IValidateSEOKeywordsView
+
+class ValidateSEOKeywordsView(BrowserView):
+
+    implements(IValidateSEOKeywordsView)
+
+    def validateKeywords(self, text):
+        """ see interface """
+
+        # extract keywords from text
+        if not text.strip():
+            return _(u'Keywords list is empty!')
+
+        keywords = map(lambda x: x.strip(), text.lower().split('\n'))
+        if not keywords:
+            return _(u'Keywords list is empty!')
+
+        # request html page of context object
+        url = '%s?qseo_without_additional_keywords=1' % self.context.absolute_url()
+        #try:
+            #page = urllib.urlopen(url)
+        #except IOError:
+            #return _('Could not find requested page')
+
+        #page_html = page.read()
+        #if not page_html:
+            #return _('Page is empty')
+
+        # extract words from body from html page
+
+        # this block work only with valid html
+        #doc = minidom.parseString(page_html)
+        #rootNode = doc.documentElement
+        #bodies = rootNode.getElementsByTagName('body')
+        #if len(bodies) > 0:
+            #body = bodies[0]
+        #else:
+            #return _(u'Invalid page html')
+        #page_words = []
+        #self.walkTextNodes(body, page_words)
+
+        # this block work even with invalid html
+        #pattern = re.compile('<\s*body[^>]*>(.*?)<\s*/\s*body\s*>', re.S|re.M|re.I)
+        #search = pattern.search(page_html)
+        #if search:
+            #body_html = search.group(1)
+        #else:
+            #return _('Invalid html code on page')
+
+        #page_text = self.strip_tags(body_html)
+        #page_words = page_text.lower().split()
+
+        # extract words from url page using lynx browser
+        page_text = commands.getoutput('lynx --dump --nolist %s' % url).lower()
+        if page_text and page_text != 'sh: lynx: command not found':
+            #page_words = page_text.lower().split()
+            page_text = page_text
+        else:
+            return _(u'Could not find lynx browser!')
+
+        # check every keyword on appearing in body of html page
+        missing = []
+        added = {}
+        for keyword in keywords:
+            if keyword not in added.keys() and not re.compile(r'\b%s\b' % keyword, re.I).search(page_text):
+                missing.append(keyword)
+                added[keyword] = 1
+
+        # return list of missing keywords
+        if missing:
+            msg = u"""Next keywords did not appear on the page:\n%s""" % '\n'.join(missing)
+        else:
+            msg = u"""All keywords found on the page!"""
+        return _(msg)
+
+    def walkTextNodes(self, parent, page_words=[]):
+        for node in parent.childNodes:
+            if node.nodeType == Node.ELEMENT_NODE:
+                self.walkTextNodes(node, page_words)
+            elif node.nodeType == Node.TEXT_NODE:
+                value = node.nodeValue
+                if value is not None:
+                    page_words.extend(map(lambda x: x.lower(), value.split()))
+
+    def strip_tags(self, in_text):
+        s_list = list(in_text)
+        i,j = 0,0
+
+        while i < len(s_list):
+            if s_list[i] == '<':
+                while s_list[i] != '>':
+                    # pop everything from the the left-angle bracket until the right-angle bracket
+                    s_list.pop(i)
+                    
+                # pops the right-angle bracket, too
+                s_list.pop(i)
+            else:
+                i=i+1
+                
+        # convert the list back into text
+        join_char=''
+        return join_char.join(s_list)
Index: qSEOptimizer/tags/1.6.1/browser/viewlets.py
===================================================================
--- qSEOptimizer/tags/1.6.1/browser/viewlets.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/browser/viewlets.py	(revision 584)
@@ -0,0 +1,35 @@
+from zope.component import getMultiAdapter
+from zope.viewlet.interfaces import IViewlet
+from Products.CMFPlone.utils import safe_unicode
+from plone.app.layout.viewlets.common import ViewletBase
+
+class TitleCommentViewlet(ViewletBase):
+
+    def update(self):
+        self.portal_state = getMultiAdapter((self.context, self.request),
+                                            name=u'plone_portal_state')
+        self.context_state = getMultiAdapter((self.context, self.request),
+                                             name=u'plone_context_state')
+        self.page_title = self.context_state.object_title
+        self.portal_title = self.portal_state.portal_title
+
+        self.override_title = self.context.hasProperty('qSEO_title')
+        self.override_comments = self.context.hasProperty('qSEO_html_comment')
+
+    def render(self):
+        std_title = u"<title>%s &mdash; %s</title>" % ( safe_unicode(self.page_title()),
+                                                        safe_unicode(self.portal_title())
+                                                      )
+        if not self.override_title:
+            if not self.override_comments:
+                return std_title
+            else:
+                qseo_comments = u"<!--%s-->"%safe_unicode(self.context.qSEO_HTML_Comment())
+                return u"%s\n%s"%(std_title, qseo_comments)
+        else:
+            qseo_title = u"<title>%s</title>" % safe_unicode(self.context.qSEO_Title())
+            if not self.override_comments:
+                return qseo_title
+            else:
+                qseo_comments = u"<!--%s-->"%safe_unicode(self.context.qSEO_HTML_Comment())
+                return u"%s\n%s"%(qseo_title, qseo_comments)
Index: qSEOptimizer/tags/1.6.1/config.py
===================================================================
--- qSEOptimizer/tags/1.6.1/config.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/config.py	(revision 584)
@@ -0,0 +1,16 @@
+PROJECT_NAME = 'qSEOptimizer'
+SKINS_DIR = 'skins'
+
+GLOBALS = globals()
+
+PROPERTY_SHEET = 'seo_properties'
+SHEET_TITLE = 'SEO Properties'
+
+STOP_WORDS = ['a', 'an', 'amp', 'and', 'are', 'arial', 'as', 'at', 'be', 'but', 'by', 'can', 'com', 'do', 'font',
+              'for', 'from', 'gif', 'had', 'has', 'have', 'he', 'helvetica', 'her', 'his', 'how', 'href', 'i',
+              'if', 'in', 'is', 'it', 'javascript', 'jpg', 'made', 'net', 'of', 'on', 'or', 'org', 'our', 'sans',
+              'see', 'serif', 'she', 'that', 'the', 'this', 'to', 'us', 'we', 'with', 'you', 'your']
+
+FIELDS = ['seo_title', 'seo_description', 'seo_keywords']
+
+DEFAULT_CUSTOM_METATAGS = []
Index: qSEOptimizer/tags/1.6.1/configure.zcml
===================================================================
--- qSEOptimizer/tags/1.6.1/configure.zcml	(revision 584)
+++ qSEOptimizer/tags/1.6.1/configure.zcml	(revision 584)
@@ -0,0 +1,19 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser">
+
+    <browser:page
+        for="Products.Archetypes.interfaces.IBaseObject"
+        attribute="validateKeywords"
+        class=".browser.keywords.ValidateSEOKeywordsView"
+        name="checkSEOKeywords"
+        permission="cmf.ModifyPortalContent"
+        />
+
+  <adapter
+    for="Products.Archetypes.public.BaseObject"
+    provides=".interfaces.IKeywords"
+    factory=".adapters.AdditionalKeywords"
+    />
+
+</configure>
Index: qSEOptimizer/tags/1.6.1/i18n/qseoptimizer-plone-uk.po
===================================================================
--- qSEOptimizer/tags/1.6.1/i18n/qseoptimizer-plone-uk.po	(revision 584)
+++ qSEOptimizer/tags/1.6.1/i18n/qseoptimizer-plone-uk.po	(revision 584)
@@ -0,0 +1,20 @@
+# Volodymyr Romaniuk <romanyuk@litech.net>, 2004.
+msgid ""
+msgstr ""
+"Project-Id-Version: qSEOptimizer-0.1.0\n"
+"POT-Creation-Date: 2004-11-17 10:53+0000\n"
+"PO-Revision-Date: 2004-11-17 18:40+0200\n"
+"Last-Translator: Volodymyr Romaniuk <romanyuk@litech.net>\n"
+"Language-Team: Ukrainian <support@quintagroup.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+"Language-Code: uk\n"
+"Language-Name: Українська\n"
+"Preferred-Encodings: utf-8 koi8-u windows-1251\n"
+"Domain: plone\n"
+
+msgid "SEO Properties"
+msgstr "Властивості seo"
+
Index: qSEOptimizer/tags/1.6.1/i18n/qseoptimizer-uk.po
===================================================================
--- qSEOptimizer/tags/1.6.1/i18n/qseoptimizer-uk.po	(revision 584)
+++ qSEOptimizer/tags/1.6.1/i18n/qseoptimizer-uk.po	(revision 584)
@@ -0,0 +1,133 @@
+# Ukrainian Langage Pack for qSEOptimizer-0.1.0
+# Myroslav Opyr <myroslav@quintagroup.com>, 204.
+msgid ""
+msgstr ""
+"Project-Id-Version: qSEOptimizer-0.1.0\n"
+"POT-Creation-Date: 2004-11-17 10:53+0000\n"
+"PO-Revision-Date: 2004-11-17 13:18+0200\n"
+"Last-Translator: Myroslav Opyr <myroslav@zope.net.ua>\n"
+"Language-Team: Ukrainian <support@quintagroup.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+"Language-Code: uk\n"
+"Language-Name: Українська\n"
+"Preferred-Encodings: utf-8 koi8-u windows-1251\n"
+"Domain: qseoptimizer\n"
+
+#. <p i18n:translate="description_edit_seo_properties">
+#.         This form allows you to edit html header meta tags that ape produced by
+#.         Plone CMS to incrase Search Engine visibility. The values generated by
+#.         Plone are present as default values in input fields. To override default
+#.         value, check corresponding checkbox and input value itself in
+#.         corresponding field.
+#. </p>
+#: qseo_properties_edit_form.cpt
+msgid "description_edit_seo_properties"
+msgstr "Ця форма дозволяє вам редагувати мета теги HTML заголовоку що генеруються системою управління всімтом Plone для того, щоб покращити видимість в пошукових системах. Значеня згенеровані Plone-ом присутні як значеня по замовчуванню полів редагування. Щоб перекрити значеня по замовчуанню, виберіть відповідне поле \"Перекривати\" та введіть значення у відповідне поле."
+
+#. <h1 i18n:translate="heading_edit_seo_properties">
+#.  Edit SEO Properties
+#. </h1>
+#: qseo_properties_edit_form.cpt
+msgid "heading_edit_seo_properties"
+msgstr "Редагування властивостей оптимізації для пошуковиків"
+
+#. <div class="formHelp" i18n:translate="help_seo_description">
+#.               Description of the document to be displayed right below title
+#.               in search engine results. It is indexed and considered to be
+#.               more important then the page content by several SE.
+#. </div>
+#: qseo_properties_edit_form.cpt
+msgid "help_seo_description"
+msgstr "Description of the document to be displayed right below title in search engine results. It is indexed and considered to be more important then the page content by several SE."
+
+#. <div class="formHelp" i18n:translate="help_seo_distribution">
+#.               Distribution meta tag to designate time and area disribution
+#.               of the document.
+#. </div>
+#: qseo_properties_edit_form.cpt
+msgid "help_seo_distribution"
+msgstr "Distribution meta tag to designate time and area disribution of the document."
+
+#. <div class="formHelp" i18n:translate="help_seo_keywords">
+#.               Keywords of the page to be indexed with.
+#.  <strong>
+#.   Enter each keyword in separate line, please.
+#.  </strong>
+#.               Though the relevance of listing meta keywords is of
+#.               questionable value now it is useful to document what
+#.               meta keywords you are focusing on for this page - for
+#.               furture reference.
+#. </div>
+#: qseo_properties_edit_form.cpt
+msgid "help_seo_keywords"
+msgstr "Keywords of the page to be indexed with. <strong>Enter each keyword in separate line, please.</strong> Though the relevance of listing meta keywords is of questionable value now it is useful to document what meta keywords you are focusing on for this page - for furture reference."
+
+#. <div class="formHelp" i18n:translate="help_seo_robots">
+#.               Robots meta tag value. Allows or denies indefing, following
+#.               links, etc. for spiders indexing your content.
+#. </div>
+#: qseo_properties_edit_form.cpt
+msgid "help_seo_robots"
+msgstr "Robots meta tag value. Allows or denies indefing, following links, etc. for spiders indexing your content."
+
+#. <div class="formHelp" i18n:translate="help_seo_title">
+#.               Text to be present into &lt;TITLE&gt; tag. It is displayed in
+#.               browser title bar. Search engines display it as a title of the
+#.               document.
+#. </div>
+#: qseo_properties_edit_form.cpt
+msgid "help_seo_title"
+msgstr "Text to be present into &lt;TITLE&gt; tag. It is displayed in browser title bar. Search engines display it as a title of the document."
+
+#. <label for="distribution" i18n:translate="label_distribution_tag">
+#.               Distribution Tag
+#. </label>
+#: qseo_properties_edit_form.cpt
+msgid "label_distribution_tag"
+msgstr "Тег Distribution"
+
+#. <label for="description" i18n:translate="label_meta_description_tag">
+#.               Meta Description Tag
+#. </label>
+#: qseo_properties_edit_form.cpt
+msgid "label_meta_description_tag"
+msgstr "Тег опису Meta Description"
+
+#. <label for="keywords" i18n:translate="label_meta_keywords_tag">
+#.               Meta Keywords Tag
+#. </label>
+#: qseo_properties_edit_form.cpt
+msgid "label_meta_keywords_tag"
+msgstr "Тег кліючових слів Meta Keywords"
+
+#. <label for="title_override" i18n:translate="label_override">
+#.               Override
+#. </label>
+#. <label for="description_override" i18n:translate="label_override">
+#.               Override
+#. </label>
+#. <label for="keywords_override" i18n:translate="label_override">
+#.               Override
+#. </label>
+## 2 more: qseo_properties_edit_form.cpt, qseo_properties_edit_form.cpt
+#: qseo_properties_edit_form.cpt
+msgid "label_override"
+msgstr "Перекривати"
+
+#. <label for="title" i18n:translate="label_page_title_tag">
+#.               Page Title Tag
+#. </label>
+#: qseo_properties_edit_form.cpt
+msgid "label_page_title_tag"
+msgstr "Тег заголовку сторінки Title"
+
+#. <label for="robots" i18n:translate="label_robot_tag">
+#.               Robot Tag
+#. </label>
+#: qseo_properties_edit_form.cpt
+msgid "label_robot_tag"
+msgstr "Тег Robot"
+
Index: qSEOptimizer/tags/1.6.1/i18n/qseoptimizer.pot
===================================================================
--- qSEOptimizer/tags/1.6.1/i18n/qseoptimizer.pot	(revision 584)
+++ qSEOptimizer/tags/1.6.1/i18n/qseoptimizer.pot	(revision 584)
@@ -0,0 +1,165 @@
+# --- PLEASE EDIT THE LINES BELOW CORRECTLY ---
+# SOME DESCRIPTIVE TITLE.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"POT-Creation-Date: 2004-11-17 10:53+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+"Language-Code: en\n"
+"Language-Name: English\n"
+"Preferred-Encodings: latin1 utf-8\n"
+"Domain: qseoptimizer\n"
+
+#: qseo_properties_edit_form.cpt
+#. <p i18n:translate="description_edit_seo_properties">
+#.         This form allows you to edit html header meta tags that ape produced by
+#.         Plone CMS to incrase Search Engine visibility. The values generated by
+#.         Plone are present as default values in input fields. To override default
+#.         value, check corresponding checkbox and input value itself in
+#.         corresponding field.
+#. </p>
+msgid "description_edit_seo_properties"
+msgstr "This form allows you to edit html header meta tags that ape produced by Plone CMS to incrase Search Engine visibility. The values generated by Plone are present as default values in input fields. To override default value, check corresponding checkbox and input value itself in corresponding field."
+
+
+#: qseo_properties_edit_form.cpt
+#. <div class="formHelp" i18n:translate="help_additional_keywords">
+#.     Use this field when you want that your content types receive
+#.     additional keywords from the ones you manually specify. Enter
+#.     one keyword per line.
+#. </div>
+msgid "help_additional_keywords"
+msgstr "Use this field when you want that your content types receive additional keywords from the ones you manually specify. Enter one keyword per line."
+
+
+#: qseo_properties_edit_form.cpt
+#. <h1 i18n:translate="heading_edit_seo_properties">
+#.  Edit SEO Properties
+#. </h1>
+msgid "heading_edit_seo_properties"
+msgstr "Edit SEO Properties"
+
+
+#: qseo_properties_edit_form.cpt
+#. <div class="formHelp" i18n:translate="help_seo_description">
+#.               Description of the document to be displayed right below title
+#.               in search engine results. It is indexed and considered to be
+#.               more important then the page content by several SE.
+#. </div>
+msgid "help_seo_description"
+msgstr "Description of the document to be displayed right below title in search engine results. It is indexed and considered to be more important then the page content by several SE."
+
+
+#: qseo_properties_edit_form.cpt
+#. <div class="formHelp" i18n:translate="help_seo_distribution">
+#.               Distribution meta tag to designate time and area disribution
+#.               of the document.
+#. </div>
+msgid "help_seo_distribution"
+msgstr "Distribution meta tag to designate time and area disribution of the document."
+
+
+#: qseo_properties_edit_form.cpt
+#. <div class="formHelp" i18n:translate="help_seo_keywords">
+#.               Keywords of the page to be indexed with.
+#.  <strong>
+#.   Enter each keyword in separate line, please.
+#.  </strong>
+#.               Though the relevance of listing meta keywords is of
+#.               questionable value now it is useful to document what
+#.               meta keywords you are focusing on for this page - for
+#.               furture reference.
+#. </div>
+msgid "help_seo_keywords"
+msgstr "Keywords of the page to be indexed with. <strong>Enter each keyword in separate line, please.</strong> Though the relevance of listing meta keywords is of questionable value now it is useful to document what meta keywords you are focusing on for this page - for furture reference."
+
+
+#: qseo_properties_edit_form.cpt
+#. <div class="formHelp" i18n:translate="help_seo_robots">
+#.               Robots meta tag value. Allows or denies indefing, following
+#.               links, etc. for spiders indexing your content.
+#. </div>
+msgid "help_seo_robots"
+msgstr "Robots meta tag value. Allows or denies indefing, following links, etc. for spiders indexing your content."
+
+
+#: qseo_properties_edit_form.cpt
+#. <div class="formHelp" i18n:translate="help_seo_title">
+#.               Text to be present into &lt;TITLE&gt; tag. It is displayed in
+#.               browser title bar. Search engines display it as a title of the
+#.               document.
+#. </div>
+msgid "help_seo_title"
+msgstr "Text to be present into &lt;TITLE&gt; tag. It is displayed in browser title bar. Search engines display it as a title of the document."
+
+
+#: qseo_properties_edit_form.cpt
+#. <label for="additionalKeywords" i18n:translate="label_additional_keywords">
+#.     Additional keywords that should be added to the content types.
+#. </label>
+msgid "label_additional_keywords"
+msgstr "Additional keywords that should be added to the content types."
+
+
+#: qseo_properties_edit_form.cpt
+#. <label for="distribution" i18n:translate="label_distribution_tag">
+#.               Distribution Tag
+#. </label>
+msgid "label_distribution_tag"
+msgstr "Distribution Tag"
+
+
+#: qseo_properties_edit_form.cpt
+#. <label for="description" i18n:translate="label_meta_description_tag">
+#.               Meta Description Tag
+#. </label>
+msgid "label_meta_description_tag"
+msgstr "Meta Description Tag"
+
+
+#: qseo_properties_edit_form.cpt
+#. <label for="keywords" i18n:translate="label_meta_keywords_tag">
+#.               Meta Keywords Tag
+#. </label>
+msgid "label_meta_keywords_tag"
+msgstr "Meta Keywords Tag"
+
+
+#: qseo_properties_edit_form.cpt
+#. <label for="title_override" i18n:translate="label_override">
+#.               Override
+#. </label>
+#: qseo_properties_edit_form.cpt
+#. <label for="description_override" i18n:translate="label_override">
+#.               Override
+#. </label>
+#: qseo_properties_edit_form.cpt
+#. <label for="keywords_override" i18n:translate="label_override">
+#.               Override
+#. </label>
+## 2 more: qseo_properties_edit_form.cpt, qseo_properties_edit_form.cpt
+msgid "label_override"
+msgstr "Override"
+
+
+#: qseo_properties_edit_form.cpt
+#. <label for="title" i18n:translate="label_page_title_tag">
+#.               Page Title Tag
+#. </label>
+msgid "label_page_title_tag"
+msgstr "Page Title Tag"
+
+
+#: qseo_properties_edit_form.cpt
+#. <label for="robots" i18n:translate="label_robot_tag">
+#.               Robot Tag
+#. </label>
+msgid "label_robot_tag"
+msgstr "Robot Tag"
Index: qSEOptimizer/tags/1.6.1/interfaces.py
===================================================================
--- qSEOptimizer/tags/1.6.1/interfaces.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/interfaces.py	(revision 584)
@@ -0,0 +1,8 @@
+from zope.interface import Interface
+
+class IKeywords(Interface):
+    """Handle the available keywords.
+    """
+    def listKeywords():
+        """Returns all the existing keywords for the current content type.
+        """
Index: qSEOptimizer/tags/1.6.1/overrides.zcml
===================================================================
--- qSEOptimizer/tags/1.6.1/overrides.zcml	(revision 584)
+++ qSEOptimizer/tags/1.6.1/overrides.zcml	(revision 584)
@@ -0,0 +1,7 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:zcml="http://namespaces.zope.org/zcml">
+    <include zcml:condition="installed plone.app.viewletmanager"
+             package=".browser" />
+
+</configure>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0-final/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0-final/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0-final/header.pt	(revision 584)
@@ -0,0 +1,129 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
+      i18n:domain="plone" tal:define="Iterator python:modules['Products.CMFPlone'].IndexIterator;
+                  tabindex python:Iterator();">
+
+<head metal:define-macro="html_header"
+      tal:define="ztu ztu|modules/ZTUtils;
+                  skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       here_title here/title_or_id;
+                       template_title template/title_or_id;
+                       here_title python:test( portal_title != here_title,
+                                               here_title,
+                                               template_title );
+                       seo_title here/qSEO_Title;"
+           tal:content="python: test(seo_title == here.title_or_id(), '%s - %s' % (portal_title, here_title), seo_title)">Title of page</title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+      <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+      <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+    
+    <meta tal:condition="here/listMetaTags|nothing"
+          tal:repeat="keyval here/listMetaTags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <link rel="Stylesheet" type="text/css" href="" tal:attributes="href string:$portal_url/ploneNS4.css" />
+
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneColumns.css"
+           tal:content="string:@import url($portal_url/ploneColumns.css);"></style>
+    
+    <style type="text/css" media="screen"
+           tal:content="string: @import url($portal_url/plone.css);"></style>
+
+    <style type="text/css" media="screen"
+           tal:condition="nothing"
+           tal:content="string: @import url($portal_url/ploneDeprecated.css);"></style>
+    
+    <link rel="alternate stylesheet" type="text/css" media="screen" href="" tal:attributes="href string:$portal_url/ploneTextSmall.css" title="Small Text" />
+    <link rel="alternate stylesheet" type="text/css" media="screen" href="" tal:attributes="href string:$portal_url/ploneTextLarge.css" title="Large Text" />
+
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneCustom.css"
+           tal:content="string:@import url($portal_url/ploneCustom.css);"></style>
+
+    <link rel="stylesheet" type="text/css" media="print" href=""
+          tal:attributes="href string:$portal_url/plonePrint.css" />
+
+    <link rel="stylesheet" type="text/css" media="projection" href=""
+          tal:attributes="href string:$portal_url/plonePresentation.css" />
+
+    <tal:iefixstart replace="structure string:&lt;!--[if IE]&gt;" />
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneIEFixes.css"
+           tal:content="string:@import url($portal_url/ploneIEFixes.css);"></style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" href="" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="search" href="" title="Search this site" tal:attributes="href string:$portal_url/search_form" />
+
+    <tal:comment tal:replace="nothing">
+       <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+       <link rel="contents" href="" title="Site Map" tal:attributes="href string:$portal_url/sitemap" />
+    </tal:comment>
+    <link rel="up" href="" title="Up one level" 
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:attributes="href python:test(parent, parent, '#')" />
+    <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    <link rel="author" href="" title="Author information" tal:attributes="href string:author" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+    Inserts rel links specified from a page.
+    </metal:block>
+
+    <meta http-equiv="imagetoolbar" content="no" />
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+    Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+    Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+    Inserts javascript specified from a page.
+    </metal:block>
+
+    <script tal:condition="not: isAnon"
+            type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_menu.js"></script>
+
+    <script type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_javascript_variables.js"></script>
+
+    <script type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_javascripts.js"></script>
+
+    <tal:block tal:define="showdefault python:test(portal.portal_memberdata.getProperty('formtooltips'), 'true', 0);
+                           formtooltips python:member.getProperty('formtooltips', showdefault)"
+               tal:omit-tag="">
+
+      <tal:oldjs tal:condition="nothing">
+        <script type="text/javascript"
+                tal:condition="formtooltips"
+                tal:attributes="src string:$portal_url/plone_formtooltip.js"></script>
+  
+        <tal:block tal:condition="not:formtooltips">
+          <tal:block tal:replace="structure string:&lt;script language='javascript' type='text/javascript' &gt;" />
+            <metal:block >function formtooltip(el,flag){}</metal:block>
+          <tal:block tal:replace="structure python:'&lt;/script&gt;'" />
+        </tal:block>
+      </tal:oldjs>
+      
+    </tal:block>
+</head>
+
+<body>
+</body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0-final/listMetaTags.py
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0-final/listMetaTags.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0-final/listMetaTags.py	(revision 584)
@@ -0,0 +1,91 @@
+## Script (Python) "listMetaTags"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=List Dublin Core for '<meta>' tags
+##
+
+from DateTime import DateTime
+from Products.CMFCore.utils import getToolByName
+
+site_props = getToolByName(context, 'portal_properties').site_properties
+exposeDCMetaTags = site_props.getProperty('exposeDCMetaTags',True)
+
+metadataList = [
+    # dublic core accessor name, metadata name
+    ('qSEO_Description', 'description'),
+    ('qSEO_Keywords',    'keywords'),
+    ('qSEO_Robots',      'robots'),
+    ('qSEO_Distribution','distribution'),
+]
+
+returnList = []
+if exposeDCMetaTags: 
+        metadataList.append(('qSEO_Distribution', 'DC.distribution'))
+        metadataList.append(('Description',      'DC.description'))
+        metadataList.append(('Subject',          'DC.subject'))
+        metadataList.append(('Creator',          'DC.creator'))
+        metadataList.append(('Contributors',     'DC.contributors'))
+        metadataList.append(('Publisher',        'DC.publisher'))
+        metadataList.append(('CreationDate',     'DC.date.created'))
+        metadataList.append(('ModificationDate', 'DC.date.modified'))
+        metadataList.append(('Type',             'DC.type'))
+        metadataList.append(('Format',           'DC.format'))
+        metadataList.append(('Language',         'DC.language'))
+        metadataList.append(('Rights',           'DC.rights'))
+
+for accessor, key in metadataList:
+    method = getattr(context, accessor, None)
+    if not callable(method):
+        # ups
+        continue
+
+    # Catch AttributeErrors raised by some AT applications
+    try:
+        value = method()
+    except AttributeError:
+        value = None
+
+    if not value:
+        # no data
+        continue
+    if accessor == 'Publisher' and value == 'No publisher':
+        # No publisher is hardcoded (XXX: still?)
+        continue
+    if same_type(value, ()) or same_type(value, []):
+        # convert a list to a string
+        value = ', '.join(value)
+    returnList.append( (key, value) )
+
+# Portions of following code was copy/pasted from the listMetaTags script from
+# CMFDefault.  This script is licensed under the ZPL 2.0 as stated here:
+# http://www.zope.org/Resources/ZPL
+# Zope Public License (ZPL) Version 2.0
+# This software is Copyright (c) Zope Corporation (tm) and Contributors. All rights reserved.
+created = context.CreationDate()
+
+effective = context.EffectiveDate()
+if effective and effective != 'None':
+    effective = DateTime(effective)
+else:
+    effective = None
+
+expires = context.ExpirationDate()
+if expires and expires != 'None':
+    expires = DateTime(expires)
+else:
+    expires = None
+
+#   Filter out DWIMish artifacts on effective / expiration dates
+eff_str = ( effective and effective.year() > 1000
+                      and effective != created ) and effective.Date() or ''
+exp_str = ( expires and expires.year() < 9000 ) and expires.Date() or ''
+
+if exp_str or exp_str:
+    returnList.append( ( 'DC.date.valid_range'
+                    , '%s - %s' % ( eff_str, exp_str ) ) )
+
+return returnList
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.1/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.1/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.1/header.pt	(revision 584)
@@ -0,0 +1,142 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone"
+      tal:define="Iterator python:modules['Products.CMFPlone'].IndexIterator;
+                  tabindex python:Iterator();">
+
+  <head metal:define-macro="html_header"
+        tal:define="ztu ztu|modules/ZTUtils;
+                    skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       here_title here/title_or_id;
+                       here_id here/getId;
+                       template_title template/title_or_id;
+                       here_title python: portal_title != here_title and here_title or template_title;
+                       here_title python: here_id != here_title and here_title or template_title;"
+           tal:content="string:$portal_title - $here_title">
+      Title of page
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+      <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+      <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta tal:condition="here/listMetaTags|nothing"
+          tal:repeat="keyval here/listMetaTags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <link rel="Stylesheet" type="text/css" href="" tal:attributes="href string:$portal_url/ploneNS4.css" />
+
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneColumns.css"
+           tal:content="string:@import url($portal_url/ploneColumns.css);">
+    </style>
+
+    <style type="text/css" media="screen"
+           tal:content="string: @import url($portal_url/plone.css);">
+    </style>
+
+    <style type="text/css" media="screen"
+           tal:condition="nothing"
+           tal:content="string: @import url($portal_url/ploneDeprecated.css);">
+    </style>
+
+    <link rel="alternate stylesheet" type="text/css" media="screen" href="" tal:attributes="href string:$portal_url/ploneTextSmall.css" title="Small Text" />
+    <link rel="alternate stylesheet" type="text/css" media="screen" href="" tal:attributes="href string:$portal_url/ploneTextLarge.css" title="Large Text" />
+
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneCustom.css"
+           tal:content="string:@import url($portal_url/ploneCustom.css);">
+    </style>
+
+    <link rel="stylesheet" type="text/css" media="print" href=""
+          tal:attributes="href string:$portal_url/plonePrint.css" />
+
+    <link rel="stylesheet" type="text/css" media="projection" href=""
+          tal:attributes="href string:$portal_url/plonePresentation.css" />
+
+    <tal:iefixstart replace="structure string:&lt;!--[if IE]&gt;" />
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneIEFixes.css"
+           tal:content="string:@import url($portal_url/ploneIEFixes.css);">
+    </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" href="" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="search" href="" title="Search this site" tal:attributes="href string:$portal_url/search_form" />
+
+    <tal:comment tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="contents" href="" title="Site Map" tal:attributes="href string:$portal_url/sitemap" />
+    </tal:comment>
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:attributes="href python:test(parent, parent, '#')" />
+    <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    <link rel="author" href="" title="Author information" tal:attributes="href string:author" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+
+    <meta http-equiv="imagetoolbar" content="no" />
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+    <script tal:condition="not: isAnon"
+            type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_menu.js">
+    </script>
+
+    <script type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_javascript_variables.js">
+    </script>
+
+    <script type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_javascripts.js">
+    </script>
+
+    <tal:block tal:define="showdefault python:test(portal.portal_memberdata.getProperty('formtooltips'), 'true', 0);
+                           formtooltips python:member.getProperty('formtooltips', showdefault)"
+               tal:omit-tag="">
+
+      <tal:oldjs tal:condition="nothing">
+        <script type="text/javascript"
+                tal:condition="formtooltips"
+                tal:attributes="src string:$portal_url/plone_formtooltip.js">
+        </script>
+
+        <tal:block tal:condition="not:formtooltips">
+          <tal:block tal:replace="structure string:&lt;script language='javascript' type='text/javascript' &gt;" />
+          <metal:block >function formtooltip(el,flag){}</metal:block>
+          <tal:block tal:replace="structure python:'&lt;/script&gt;'" />
+        </tal:block>
+      </tal:oldjs>
+
+    </tal:block>
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.1/listMetaTags.py
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.1/listMetaTags.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.1/listMetaTags.py	(revision 584)
@@ -0,0 +1,90 @@
+## Script (Python) "listMetaTags"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=List Dublin Core for '<meta>' tags
+##
+from DateTime import DateTime
+from Products.CMFCore.utils import getToolByName
+
+site_props = getToolByName(context, 'portal_properties').site_properties
+exposeDCMetaTags = site_props.getProperty('exposeDCMetaTags',True)
+
+metadataList = [
+    # dublic core accessor name, metadata name
+    ('qSEO_Description', 'description'),
+    ('qSEO_Keywords',    'keywords'),
+    ('qSEO_Robots',      'robots'),
+    ('qSEO_Distribution','distribution'),
+]
+
+returnList = []
+if exposeDCMetaTags: 
+        metadataList.append(('qSEO_Distribution', 'DC.distribution'))
+        metadataList.append(('Description',      'DC.description'))
+        metadataList.append(('Subject',          'DC.subject'))
+        metadataList.append(('Creator',          'DC.creator'))
+        metadataList.append(('Contributors',     'DC.contributors'))
+        metadataList.append(('Publisher',        'DC.publisher'))
+        metadataList.append(('CreationDate',     'DC.date.created'))
+        metadataList.append(('ModificationDate', 'DC.date.modified'))
+        metadataList.append(('Type',             'DC.type'))
+        metadataList.append(('Format',           'DC.format'))
+        metadataList.append(('Language',         'DC.language'))
+        metadataList.append(('Rights',           'DC.rights'))
+
+for accessor, key in metadataList:
+    method = getattr(context, accessor, None)
+    if not callable(method):
+        # ups
+        continue
+
+    # Catch AttributeErrors raised by some AT applications
+    try:
+        value = method()
+    except AttributeError:
+        value = None
+
+    if not value:
+        # no data
+        continue
+    if accessor == 'Publisher' and value == 'No publisher':
+        # No publisher is hardcoded (XXX: still?)
+        continue
+    if same_type(value, ()) or same_type(value, []):
+        # convert a list to a string
+        value = ', '.join(value)
+    returnList.append( (key, value) )
+
+# Portions of following code was copy/pasted from the listMetaTags script from
+# CMFDefault.  This script is licensed under the ZPL 2.0 as stated here:
+# http://www.zope.org/Resources/ZPL
+# Zope Public License (ZPL) Version 2.0
+# This software is Copyright (c) Zope Corporation (tm) and Contributors. All rights reserved.
+created = context.CreationDate()
+
+effective = context.EffectiveDate()
+if effective and effective != 'None':
+    effective = DateTime(effective)
+else:
+    effective = None
+
+expires = context.ExpirationDate()
+if expires and expires != 'None':
+    expires = DateTime(expires)
+else:
+    expires = None
+
+#   Filter out DWIMish artifacts on effective / expiration dates
+eff_str = ( effective and effective.year() > 1000
+                      and effective != created ) and effective.Date() or ''
+exp_str = ( expires and expires.year() < 9000 ) and expires.Date() or ''
+
+if exp_str or exp_str:
+    returnList.append( ( 'DC.date.valid_range'
+                    , '%s - %s' % ( eff_str, exp_str ) ) )
+
+return returnList
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.2/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.2/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.2/header.pt	(revision 584)
@@ -0,0 +1,143 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone"
+      tal:define="Iterator python:modules['Products.CMFPlone'].IndexIterator;
+                  tabindex python:Iterator();">
+
+  <head metal:define-macro="html_header"
+        tal:define="ztu ztu|modules/ZTUtils;
+                    skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       here_title here/title_or_id;
+                       here_id here/getId;
+                       template_title template/title_or_id;
+                       here_title python: portal_title != here_title and here_title or template_title;
+                       here_title python: here_id != here_title and here_title or template_title;
+                       seo_title here/qSEO_Title;"
+           tal:content="python: test(seo_title == here.title_or_id(), '%s - %s' % (portal_title, here_title), seo_title)">
+      Title of page
+    </title>
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+      <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+      <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta tal:define="metatags here/listMetaTags|nothing" 
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <link rel="Stylesheet" type="text/css" href="" tal:attributes="href string:$portal_url/ploneNS4.css" />
+
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneColumns.css"
+           tal:content="string:@import url($portal_url/ploneColumns.css);">
+    </style>
+
+    <style type="text/css" media="screen"
+           tal:content="string: @import url($portal_url/plone.css);">
+    </style>
+
+    <style type="text/css" media="screen"
+           tal:condition="nothing"
+           tal:content="string: @import url($portal_url/ploneDeprecated.css);">
+    </style>
+
+    <link rel="alternate stylesheet" type="text/css" media="screen" href="" tal:attributes="href string:$portal_url/ploneTextSmall.css" title="Small Text" />
+    <link rel="alternate stylesheet" type="text/css" media="screen" href="" tal:attributes="href string:$portal_url/ploneTextLarge.css" title="Large Text" />
+
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneCustom.css"
+           tal:content="string:@import url($portal_url/ploneCustom.css);">
+    </style>
+
+    <link rel="stylesheet" type="text/css" media="print" href=""
+          tal:attributes="href string:$portal_url/plonePrint.css" />
+
+    <link rel="stylesheet" type="text/css" media="projection" href=""
+          tal:attributes="href string:$portal_url/plonePresentation.css" />
+
+    <tal:iefixstart replace="structure string:&lt;!--[if IE]&gt;" />
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneIEFixes.css"
+           tal:content="string:@import url($portal_url/ploneIEFixes.css);">
+    </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" href="" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="search" href="" title="Search this site" tal:attributes="href string:$portal_url/search_form" />
+
+    <tal:comment tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="contents" href="" title="Site Map" tal:attributes="href string:$portal_url/sitemap" />
+    </tal:comment>
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:attributes="href python:test(parent, parent, '#')" />
+    <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    <link rel="author" href="" title="Author information" tal:attributes="href string:author" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+
+    <meta http-equiv="imagetoolbar" content="no" />
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+    <script tal:condition="not: isAnon"
+            type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_menu.js">
+    </script>
+
+    <script type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_javascript_variables.js">
+    </script>
+
+    <script type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_javascripts.js">
+    </script>
+
+    <tal:block tal:define="showdefault python:test(portal.portal_memberdata.getProperty('formtooltips'), 'true', 0);
+                           formtooltips python:member.getProperty('formtooltips', showdefault)"
+               tal:omit-tag="">
+
+      <tal:oldjs tal:condition="nothing">
+        <script type="text/javascript"
+                tal:condition="formtooltips"
+                tal:attributes="src string:$portal_url/plone_formtooltip.js">
+        </script>
+
+        <tal:block tal:condition="not:formtooltips">
+          <tal:block tal:replace="structure string:&lt;script language='javascript' type='text/javascript' &gt;" />
+          <metal:block >function formtooltip(el,flag){}</metal:block>
+          <tal:block tal:replace="structure python:'&lt;/script&gt;'" />
+        </tal:block>
+      </tal:oldjs>
+
+    </tal:block>
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.2/listMetaTags.py
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.2/listMetaTags.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.2/listMetaTags.py	(revision 584)
@@ -0,0 +1,90 @@
+## Script (Python) "listMetaTags"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=List Dublin Core for '<meta>' tags
+##
+from DateTime import DateTime
+from Products.CMFCore.utils import getToolByName
+
+site_props = getToolByName(context, 'portal_properties').site_properties
+exposeDCMetaTags = site_props.getProperty('exposeDCMetaTags',True)
+
+metadataList = [
+    # dublic core accessor name, metadata name
+    ('qSEO_Description', 'description'),
+    ('qSEO_Keywords',    'keywords'),
+    ('qSEO_Robots',      'robots'),
+    ('qSEO_Distribution','distribution'),
+]
+
+returnList = []
+if exposeDCMetaTags: 
+        metadataList.append(('qSEO_Distribution', 'DC.distribution'))
+        metadataList.append(('Description',      'DC.description'))
+        metadataList.append(('Subject',          'DC.subject'))
+        metadataList.append(('Creator',          'DC.creator'))
+        metadataList.append(('Contributors',     'DC.contributors'))
+        metadataList.append(('Publisher',        'DC.publisher'))
+        metadataList.append(('CreationDate',     'DC.date.created'))
+        metadataList.append(('ModificationDate', 'DC.date.modified'))
+        metadataList.append(('Type',             'DC.type'))
+        metadataList.append(('Format',           'DC.format'))
+        metadataList.append(('Language',         'DC.language'))
+        metadataList.append(('Rights',           'DC.rights'))
+
+for accessor, key in metadataList:
+    method = getattr(context, accessor, None)
+    if not callable(method):
+        # ups
+        continue
+
+    # Catch AttributeErrors raised by some AT applications
+    try:
+        value = method()
+    except AttributeError:
+        value = None
+
+    if not value:
+        # no data
+        continue
+    if accessor == 'Publisher' and value == 'No publisher':
+        # No publisher is hardcoded (XXX: still?)
+        continue
+    if same_type(value, ()) or same_type(value, []):
+        # convert a list to a string
+        value = ', '.join(value)
+    returnList.append( (key, value) )
+
+# Portions of following code was copy/pasted from the listMetaTags script from
+# CMFDefault.  This script is licensed under the ZPL 2.0 as stated here:
+# http://www.zope.org/Resources/ZPL
+# Zope Public License (ZPL) Version 2.0
+# This software is Copyright (c) Zope Corporation (tm) and Contributors. All rights reserved.
+created = context.CreationDate()
+
+effective = context.EffectiveDate()
+if effective and effective != 'None':
+    effective = DateTime(effective)
+else:
+    effective = None
+
+expires = context.ExpirationDate()
+if expires and expires != 'None':
+    expires = DateTime(expires)
+else:
+    expires = None
+
+#   Filter out DWIMish artifacts on effective / expiration dates
+eff_str = ( effective and effective.year() > 1000
+                      and effective != created ) and effective.Date() or ''
+exp_str = ( expires and expires.year() < 9000 ) and expires.Date() or ''
+
+if exp_str or exp_str:
+    returnList.append( ( 'DC.date.valid_range'
+                    , '%s - %s' % ( eff_str, exp_str ) ) )
+
+return returnList
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.3/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.3/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.3/header.pt	(revision 584)
@@ -0,0 +1,144 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone"
+      tal:define="Iterator python:modules['Products.CMFPlone'].IndexIterator;
+                  tabindex python:Iterator();">
+
+  <head metal:define-macro="html_header"
+        tal:define="ztu ztu|modules/ZTUtils;
+                    skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       here_title here/title_or_id;
+                       here_id here/getId;
+                       template_title template/title_or_id;
+                       here_title python: portal_title != here_title and here_title or template_title;
+                       here_title python: here_id != here_title and here_title or template_title;
+                       seo_title here/qSEO_Title;"
+           tal:content="python: test(seo_title == here.title_or_id(), '%s - %s' % (portal_title, here_title), seo_title)">
+      Title of page
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+      <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+      <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta tal:define="metatags here/listMetaTags|nothing" 
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <link rel="Stylesheet" type="text/css" href="" tal:attributes="href string:$portal_url/ploneNS4.css" />
+
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneColumns.css"
+           tal:content="string:@import url($portal_url/ploneColumns.css);">
+    </style>
+
+    <style type="text/css" media="screen"
+           tal:content="string: @import url($portal_url/plone.css);">
+    </style>
+
+    <style type="text/css" media="screen"
+           tal:condition="nothing"
+           tal:content="string: @import url($portal_url/ploneDeprecated.css);">
+    </style>
+
+    <link rel="alternate stylesheet" type="text/css" media="screen" href="" tal:attributes="href string:$portal_url/ploneTextSmall.css" title="Small Text" />
+    <link rel="alternate stylesheet" type="text/css" media="screen" href="" tal:attributes="href string:$portal_url/ploneTextLarge.css" title="Large Text" />
+
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneCustom.css"
+           tal:content="string:@import url($portal_url/ploneCustom.css);">
+    </style>
+
+    <link rel="stylesheet" type="text/css" media="print" href=""
+          tal:attributes="href string:$portal_url/plonePrint.css" />
+
+    <link rel="stylesheet" type="text/css" media="projection" href=""
+          tal:attributes="href string:$portal_url/plonePresentation.css" />
+
+    <tal:iefixstart replace="structure string:&lt;!--[if IE]&gt;" />
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneIEFixes.css"
+           tal:content="string:@import url($portal_url/ploneIEFixes.css);">
+    </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" href="" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="search" href="" title="Search this site" tal:attributes="href string:$portal_url/search_form" />
+
+    <tal:comment tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="contents" href="" title="Site Map" tal:attributes="href string:$portal_url/sitemap" />
+    </tal:comment>
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:attributes="href python:test(parent, parent, '#')" />
+    <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    <link rel="author" href="" title="Author information" tal:attributes="href string:author" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+
+    <meta http-equiv="imagetoolbar" content="no" />
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+    <script tal:condition="not: isAnon"
+            type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_menu.js">
+    </script>
+
+    <script type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_javascript_variables.js">
+    </script>
+
+    <script type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_javascripts.js">
+    </script>
+
+    <tal:block tal:define="showdefault python:test(portal.portal_memberdata.getProperty('formtooltips'), 'true', 0);
+                           formtooltips python:member.getProperty('formtooltips', showdefault)"
+               tal:omit-tag="">
+
+      <tal:oldjs tal:condition="nothing">
+        <script type="text/javascript"
+                tal:condition="formtooltips"
+                tal:attributes="src string:$portal_url/plone_formtooltip.js">
+        </script>
+
+        <tal:block tal:condition="not:formtooltips">
+          <tal:block tal:replace="structure string:&lt;script language='javascript' type='text/javascript' &gt;" />
+          <metal:block >function formtooltip(el,flag){}</metal:block>
+          <tal:block tal:replace="structure python:'&lt;/script&gt;'" />
+        </tal:block>
+      </tal:oldjs>
+
+    </tal:block>
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.3/listMetaTags.py
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.3/listMetaTags.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.3/listMetaTags.py	(revision 584)
@@ -0,0 +1,91 @@
+## Script (Python) "listMetaTags"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=List Dublin Core for '<meta>' tags
+##
+from DateTime import DateTime
+from Products.CMFCore.utils import getToolByName
+
+site_props = getToolByName(context, 'portal_properties').site_properties
+exposeDCMetaTags = site_props.getProperty('exposeDCMetaTags',True)
+
+metadataList = [
+    # dublic core accessor name, metadata name
+    ('qSEO_Description', 'description'),
+    ('qSEO_Keywords',    'keywords'),
+    ('qSEO_Robots',      'robots'),
+    ('qSEO_Distribution','distribution'),
+]
+
+returnList = []
+if exposeDCMetaTags: 
+        metadataList.append(('qSEO_Distribution', 'DC.distribution'))
+        metadataList.append(('Description',      'DC.description'))
+        metadataList.append(('Subject',          'DC.subject'))
+        metadataList.append(('Creator',          'DC.creator'))
+        metadataList.append(('Contributors',     'DC.contributors'))
+        metadataList.append(('Publisher',        'DC.publisher'))
+        metadataList.append(('CreationDate',     'DC.date.created'))
+        metadataList.append(('ModificationDate', 'DC.date.modified'))
+        metadataList.append(('Type',             'DC.type'))
+        metadataList.append(('Format',           'DC.format'))
+        metadataList.append(('Language',         'DC.language'))
+        metadataList.append(('Rights',           'DC.rights'))
+
+
+for accessor, key in metadataList:
+    method = getattr(context, accessor, None)
+    if not callable(method):
+        # ups
+        continue
+
+    # Catch AttributeErrors raised by some AT applications
+    try:
+        value = method()
+    except AttributeError:
+        value = None
+
+    if not value:
+        # no data
+        continue
+    if accessor == 'Publisher' and value == 'No publisher':
+        # No publisher is hardcoded (XXX: still?)
+        continue
+    if same_type(value, ()) or same_type(value, []):
+        # convert a list to a string
+        value = ', '.join(value)
+    returnList.append( (key, value) )
+
+# Portions of following code was copy/pasted from the listMetaTags script from
+# CMFDefault.  This script is licensed under the ZPL 2.0 as stated here:
+# http://www.zope.org/Resources/ZPL
+# Zope Public License (ZPL) Version 2.0
+# This software is Copyright (c) Zope Corporation (tm) and Contributors. All rights reserved.
+created = context.CreationDate()
+
+effective = context.EffectiveDate()
+if effective and effective != 'None':
+    effective = DateTime(effective)
+else:
+    effective = None
+
+expires = context.ExpirationDate()
+if expires and expires != 'None':
+    expires = DateTime(expires)
+else:
+    expires = None
+
+#   Filter out DWIMish artifacts on effective / expiration dates
+eff_str = ( effective and effective.year() > 1000
+                      and effective != created ) and effective.Date() or ''
+exp_str = ( expires and expires.year() < 9000 ) and expires.Date() or ''
+
+if exp_str or exp_str:
+    returnList.append( ( 'DC.date.valid_range'
+                    , '%s - %s' % ( eff_str, exp_str ) ) )
+
+return returnList
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.4/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.4/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.4/header.pt	(revision 584)
@@ -0,0 +1,144 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone"
+      tal:define="Iterator python:modules['Products.CMFPlone'].IndexIterator;
+                  tabindex python:Iterator();">
+
+  <head metal:define-macro="html_header"
+        tal:define="ztu ztu|modules/ZTUtils;
+                    skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       here_title here/title_or_id;
+                       here_id here/getId;
+                       template_title template/title_or_id;
+                       here_title python: portal_title != here_title and here_title or template_title;
+                       here_title python: here_id != here_title and here_title or template_title;
+                       seo_title here/qSEO_Title;"
+           tal:content="python: test(seo_title == here.title_or_id(), '%s - %s' % (portal_title, here_title), seo_title)">
+      Title of page
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+      <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+      <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta tal:define="metatags here/listMetaTags|nothing" 
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <link rel="Stylesheet" type="text/css" href="" tal:attributes="href string:$portal_url/ploneNS4.css" />
+
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneColumns.css"
+           tal:content="string:@import url($portal_url/ploneColumns.css);">
+    </style>
+
+    <style type="text/css" media="screen"
+           tal:content="string: @import url($portal_url/plone.css);">
+    </style>
+
+    <style type="text/css" media="screen"
+           tal:condition="nothing"
+           tal:content="string: @import url($portal_url/ploneDeprecated.css);">
+    </style>
+
+    <link rel="alternate stylesheet" type="text/css" media="screen" href="" tal:attributes="href string:$portal_url/ploneTextSmall.css" title="Small Text" />
+    <link rel="alternate stylesheet" type="text/css" media="screen" href="" tal:attributes="href string:$portal_url/ploneTextLarge.css" title="Large Text" />
+
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneCustom.css"
+           tal:content="string:@import url($portal_url/ploneCustom.css);">
+    </style>
+
+    <link rel="stylesheet" type="text/css" media="print" href=""
+          tal:attributes="href string:$portal_url/plonePrint.css" />
+
+    <link rel="stylesheet" type="text/css" media="projection" href=""
+          tal:attributes="href string:$portal_url/plonePresentation.css" />
+
+    <tal:iefixstart replace="structure string:&lt;!--[if IE]&gt;" />
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneIEFixes.css"
+           tal:content="string:@import url($portal_url/ploneIEFixes.css);">
+    </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" href="" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="search" href="" title="Search this site" tal:attributes="href string:$portal_url/search_form" />
+
+    <tal:comment tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="contents" href="" title="Site Map" tal:attributes="href string:$portal_url/sitemap" />
+    </tal:comment>
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:attributes="href python:test(parent, parent, '#')" />
+    <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    <link rel="author" href="" title="Author information" tal:attributes="href string:author" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+
+    <meta http-equiv="imagetoolbar" content="no" />
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+    <script tal:condition="not: isAnon"
+            type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_menu.js">
+    </script>
+
+    <script type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_javascript_variables.js">
+    </script>
+
+    <script type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_javascripts.js">
+    </script>
+
+    <tal:block tal:define="showdefault python:test(portal.portal_memberdata.getProperty('formtooltips'), 'true', 0);
+                           formtooltips python:member.getProperty('formtooltips', showdefault)"
+               tal:omit-tag="">
+
+      <tal:oldjs tal:condition="nothing">
+        <script type="text/javascript"
+                tal:condition="formtooltips"
+                tal:attributes="src string:$portal_url/plone_formtooltip.js">
+        </script>
+
+        <tal:block tal:condition="not:formtooltips">
+          <tal:block tal:replace="structure string:&lt;script language='javascript' type='text/javascript' &gt;" />
+          <metal:block >function formtooltip(el,flag){}</metal:block>
+          <tal:block tal:replace="structure python:'&lt;/script&gt;'" />
+        </tal:block>
+      </tal:oldjs>
+
+    </tal:block>
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.4/listMetaTags.py
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.4/listMetaTags.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.4/listMetaTags.py	(revision 584)
@@ -0,0 +1,90 @@
+## Script (Python) "listMetaTags"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=List Dublin Core for '<meta>' tags
+##
+from DateTime import DateTime
+from Products.CMFCore.utils import getToolByName
+
+site_props = getToolByName(context, 'portal_properties').site_properties
+exposeDCMetaTags = site_props.getProperty('exposeDCMetaTags',True)
+
+metadataList = [
+    # dublic core accessor name, metadata name
+    ('qSEO_Description', 'description'),
+    ('qSEO_Keywords',    'keywords'),
+    ('qSEO_Robots',      'robots'),
+    ('qSEO_Distribution','distribution'),
+]
+
+returnList = []
+if exposeDCMetaTags: 
+        metadataList.append(('qSEO_Distribution', 'DC.distribution'))
+        metadataList.append(('Description',      'DC.description'))
+        metadataList.append(('Subject',          'DC.subject'))
+        metadataList.append(('Creator',          'DC.creator'))
+        metadataList.append(('Contributors',     'DC.contributors'))
+        metadataList.append(('Publisher',        'DC.publisher'))
+        metadataList.append(('CreationDate',     'DC.date.created'))
+        metadataList.append(('ModificationDate', 'DC.date.modified'))
+        metadataList.append(('Type',             'DC.type'))
+        metadataList.append(('Format',           'DC.format'))
+        metadataList.append(('Language',         'DC.language'))
+        metadataList.append(('Rights',           'DC.rights'))
+
+for accessor, key in metadataList:
+    method = getattr(context, accessor, None)
+    if not callable(method):
+        # ups
+        continue
+
+    # Catch AttributeErrors raised by some AT applications
+    try:
+        value = method()
+    except AttributeError:
+        value = None
+
+    if not value:
+        # no data
+        continue
+    if accessor == 'Publisher' and value == 'No publisher':
+        # No publisher is hardcoded (XXX: still?)
+        continue
+    if same_type(value, ()) or same_type(value, []):
+        # convert a list to a string
+        value = ', '.join(value)
+    returnList.append( (key, value) )
+
+# Portions of following code was copy/pasted from the listMetaTags script from
+# CMFDefault.  This script is licensed under the ZPL 2.0 as stated here:
+# http://www.zope.org/Resources/ZPL
+# Zope Public License (ZPL) Version 2.0
+# This software is Copyright (c) Zope Corporation (tm) and Contributors. All rights reserved.
+created = context.CreationDate()
+
+effective = context.EffectiveDate()
+if effective and effective != 'None':
+    effective = DateTime(effective)
+else:
+    effective = None
+
+expires = context.ExpirationDate()
+if expires and expires != 'None':
+    expires = DateTime(expires)
+else:
+    expires = None
+
+#   Filter out DWIMish artifacts on effective / expiration dates
+eff_str = ( effective and effective.year() > 1000
+                      and effective != created ) and effective.Date() or ''
+exp_str = ( expires and expires.year() < 9000 ) and expires.Date() or ''
+
+if exp_str or exp_str:
+    returnList.append( ( 'DC.date.valid_range'
+                    , '%s - %s' % ( eff_str, exp_str ) ) )
+
+return returnList
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.5/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.5/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.5/header.pt	(revision 584)
@@ -0,0 +1,148 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone"
+      tal:define="Iterator python:modules['Products.CMFPlone'].IndexIterator;
+                  tabindex python:Iterator();">
+
+  <head metal:define-macro="html_header"
+        tal:define="ztu ztu|modules/ZTUtils;
+                    skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       page_title python:here.getPageTitle(here, template, portal_title);
+                       override python: here.hasProperty('qSEO_title')" >
+      <tal:seo condition="override" content="here/qSEO_Title" />
+      <tal:default condition="not:override">
+        <span tal:replace="page_title" />
+        &mdash;
+        <span tal:replace="portal_title" />
+      </tal:default>
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+      <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+      <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta tal:define="metatags here/listMetaTags|nothing" 
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <link rel="Stylesheet" type="text/css" href="" tal:attributes="href string:$portal_url/ploneNS4.css" />
+
+    <style type="text/css" media="screen" tal:condition="exists: portal/ploneColumns.css"
+           tal:content="structure string:<!-- @import url($portal_url/ploneColumns.css); -->">
+    </style>
+
+    <style type="text/css" media="screen"
+           tal:content="structure string:<!-- @import url($portal_url/plone.css); -->">
+    </style>
+
+    <style type="text/css" media="screen"
+           tal:condition="nothing"
+           tal:content="string: @import url($portal_url/ploneDeprecated.css);">
+    </style>
+
+    <link rel="alternate stylesheet" type="text/css" media="screen" href="" tal:attributes="href string:$portal_url/ploneTextSmall.css" title="Small Text" />
+    <link rel="alternate stylesheet" type="text/css" media="screen" href="" tal:attributes="href string:$portal_url/ploneTextLarge.css" title="Large Text" />
+
+    <link rel="stylesheet" type="text/css" media="print" href=""
+          tal:attributes="href string:$portal_url/plonePrint.css" />
+
+    <link rel="stylesheet" type="text/css" media="projection" href=""
+          tal:attributes="href string:$portal_url/plonePresentation.css" />
+
+    <tal:iefixstart replace="structure string:&lt;!--[if IE]&gt;" />
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneIEFixes.css"
+           tal:content="string:@import url($portal_url/ploneIEFixes.css);">
+    </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+
+    <style type="text/css" media="all" tal:condition="exists: portal/ploneCustom.css"
+           tal:content="structure string:<!-- @import url($portal_url/ploneCustom.css); -->">
+    </style>
+
+    <link rel="shortcut icon" href="" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="search" href="" title="Search this site" tal:attributes="href string:$portal_url/search_form" />
+
+    <tal:fixme tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="contents" href="" title="Site Map" tal:attributes="href string:$portal_url/sitemap" />
+    <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    <link rel="author" href="" title="Author information" tal:attributes="href string:author" />
+    </tal:fixme>
+
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:condition="parent"
+          tal:attributes="href parent" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+
+    <meta http-equiv="imagetoolbar" content="no" />
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+    <script tal:condition="not: isAnon"
+            type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_menu.js">
+    </script>
+
+    <script type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_javascript_variables.js">
+    </script>
+
+    <script type="text/javascript"
+            tal:attributes="src string:$portal_url/plone_javascripts.js">
+    </script>
+
+    <tal:block tal:define="showdefault python:test(portal.portal_memberdata.getProperty('formtooltips'), 'true', 0);
+                           anonymous portal/portal_membership/isAnonymousUser;
+                           formtooltips python:anonymous and showdefault or member.getProperty('formtooltips', showdefault)" 
+               tal:omit-tag="">
+
+      <tal:oldjs tal:condition="nothing">
+        <script type="text/javascript"
+                tal:condition="formtooltips"
+                tal:attributes="src string:$portal_url/plone_formtooltip.js">
+        </script>
+
+        <tal:block tal:condition="not:formtooltips">
+          <tal:block tal:replace="structure string:&lt;script language='javascript' type='text/javascript' &gt;" />
+          <metal:block >function formtooltip(el,flag){}</metal:block>
+          <tal:block tal:replace="structure python:'&lt;/script&gt;'" />
+        </tal:block>
+      </tal:oldjs>
+
+    </tal:block>
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.5/listMetaTags.py
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.5/listMetaTags.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.0.5/listMetaTags.py	(revision 584)
@@ -0,0 +1,90 @@
+## Script (Python) "listMetaTags"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=List Dublin Core for '<meta>' tags
+##
+from DateTime import DateTime
+from Products.CMFCore.utils import getToolByName
+
+site_props = getToolByName(context, 'portal_properties').site_properties
+exposeDCMetaTags = site_props.getProperty('exposeDCMetaTags',True)
+
+metadataList = [
+    # dublic core accessor name, metadata name
+    ('qSEO_Description', 'description'),
+    ('qSEO_Keywords',    'keywords'),
+    ('qSEO_Robots',      'robots'),
+    ('qSEO_Distribution','distribution'),
+]
+
+returnList = []
+if exposeDCMetaTags: 
+        metadataList.append(('qSEO_Distribution', 'DC.distribution'))
+        metadataList.append(('Description',      'DC.description'))
+        metadataList.append(('Subject',          'DC.subject'))
+        metadataList.append(('Creator',          'DC.creator'))
+        metadataList.append(('Contributors',     'DC.contributors'))
+        metadataList.append(('Publisher',        'DC.publisher'))
+        metadataList.append(('CreationDate',     'DC.date.created'))
+        metadataList.append(('ModificationDate', 'DC.date.modified'))
+        metadataList.append(('Type',             'DC.type'))
+        metadataList.append(('Format',           'DC.format'))
+        metadataList.append(('Language',         'DC.language'))
+        metadataList.append(('Rights',           'DC.rights'))
+
+for accessor, key in metadataList:
+    method = getattr(context, accessor, None)
+    if not callable(method):
+        # ups
+        continue
+
+    # Catch AttributeErrors raised by some AT applications
+    try:
+        value = method()
+    except AttributeError:
+        value = None
+
+    if not value:
+        # no data
+        continue
+    if accessor == 'Publisher' and value == 'No publisher':
+        # No publisher is hardcoded (XXX: still?)
+        continue
+    if same_type(value, ()) or same_type(value, []):
+        # convert a list to a string
+        value = ', '.join(value)
+    returnList.append( (key, value) )
+
+# Portions of following code was copy/pasted from the listMetaTags script from
+# CMFDefault.  This script is licensed under the ZPL 2.0 as stated here:
+# http://www.zope.org/Resources/ZPL
+# Zope Public License (ZPL) Version 2.0
+# This software is Copyright (c) Zope Corporation (tm) and Contributors. All rights reserved.
+created = context.CreationDate()
+
+effective = context.EffectiveDate()
+if effective and effective != 'None':
+    effective = DateTime(effective)
+else:
+    effective = None
+
+expires = context.ExpirationDate()
+if expires and expires != 'None':
+    expires = DateTime(expires)
+else:
+    expires = None
+
+#   Filter out DWIMish artifacts on effective / expiration dates
+eff_str = ( effective and effective.year() > 1000
+                      and effective != created ) and effective.Date() or ''
+exp_str = ( expires and expires.year() < 9000 ) and expires.Date() or ''
+
+if exp_str or exp_str:
+    returnList.append( ( 'DC.date.valid_range'
+                    , '%s - %s' % ( eff_str, exp_str ) ) )
+
+return returnList
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1.1/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1.1/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1.1/header.pt	(revision 584)
@@ -0,0 +1,100 @@
+<metal:block use-macro="here/global_defines/macros/defines" />
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone">
+
+  <head metal:define-macro="html_header"
+        tal:define="skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       page_title object_title;
+                       override python:here.hasProperty('qSEO_title')">
+        <tal:seo condition="override" content="here/qSEO_Title"/>
+        <tal:default condition="not:override">
+            <tal:pagetitle replace="page_title">
+                Inserts page title
+            </tal:pagetitle>
+            &mdash;
+            <tal:portaltitle replace="portal_title">
+             Inserts portal title
+            </tal:portaltitle>
+        </tal:default>
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+      <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+      <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta tal:define="metatags python:putils.listMetaTags(here).items()"
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <metal:js metal:use-macro="here/renderAllTheScripts/macros/jsrenderer">SSS
+    </metal:js>
+
+    <metal:css metal:use-macro="here/renderAllTheStylesheets/macros/cssrenderer">SSS
+    </metal:css>
+
+    <tal:iefixstart replace="structure string:&lt;!--[if lt IE 7]&gt;" />
+        <style type="text/css" media="all" tal:condition="exists: portal/IEFixes.css"
+               tal:content="string:@import url($portal_url/IEFixes.css);">
+        </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="home" href="" title="Front page" tal:attributes="href string:$portal_url" />
+    <link rel="search" title="Search this site" tal:attributes="href string:$portal_url/search_form" />
+    <link rel="author" title="Author information" tal:attributes="href string:${portal_url}/author/${here/Creator}" />
+    <link rel="contents" href="" title="Site Map" tal:attributes="href string:$portal_url/sitemap" />
+
+    <tal:fixme tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    </tal:fixme>
+
+    <link rel="alternate" href="" title="RSS 1.0" type="application/rss+xml"  
+          tal:condition="python: syntool.isSyndicationAllowed(here)"
+          tal:attributes="href string:$here_url/RSS" />
+
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:condition="parent"
+          tal:attributes="href parent" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+
+    <meta http-equiv="imagetoolbar" content="no" />
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1.2/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1.2/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1.2/header.pt	(revision 584)
@@ -0,0 +1,100 @@
+<metal:block use-macro="here/global_defines/macros/defines" />
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone">
+
+  <head metal:define-macro="html_header"
+        tal:define="skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       page_title object_title;
+                       override python:here.hasProperty('qSEO_title')">
+        <tal:seo condition="override" content="here/qSEO_Title"/>
+        <tal:default condition="not:override">
+            <tal:pagetitle replace="page_title">
+                Inserts page title
+            </tal:pagetitle>
+            &mdash;
+            <tal:portaltitle replace="portal_title">
+             Inserts portal title
+            </tal:portaltitle>
+        </tal:default>
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+      <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+      <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta tal:define="metatags python:putils.listMetaTags(here).items()"
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <metal:js metal:use-macro="here/renderAllTheScripts/macros/jsrenderer">SSS
+    </metal:js>
+
+    <metal:css metal:use-macro="here/renderAllTheStylesheets/macros/cssrenderer">SSS
+    </metal:css>
+
+    <tal:iefixstart replace="structure string:&lt;!--[if lt IE 7]&gt;" />
+        <style type="text/css" media="all" tal:condition="exists: portal/IEFixes.css"
+               tal:content="string:@import url($portal_url/IEFixes.css);">
+        </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="home" href="" title="Front page" tal:attributes="href string:$portal_url" />
+    <link rel="search" title="Search this site" tal:attributes="href string:$portal_url/search_form" />
+    <link rel="author" title="Author information" tal:attributes="href string:${portal_url}/author/${here/Creator}" />
+    <link rel="contents" href="" title="Site Map" tal:attributes="href string:$portal_url/sitemap" />
+
+    <tal:fixme tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    </tal:fixme>
+
+    <link rel="alternate" href="" title="RSS 1.0" type="application/rss+xml"  
+          tal:condition="python: syntool.isSyndicationAllowed(here)"
+          tal:attributes="href string:$here_url/RSS" />
+
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:condition="parent"
+          tal:attributes="href parent" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+
+    <meta http-equiv="imagetoolbar" content="no" />
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1.3/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1.3/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1.3/header.pt	(revision 584)
@@ -0,0 +1,137 @@
+<metal:block use-macro="here/global_defines/macros/defines" />
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone">
+
+  <head metal:define-macro="html_header"
+        tal:define="skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       page_title object_title;
+                       override python:here.hasProperty('qSEO_title')">
+        <tal:seo condition="override" content="here/qSEO_Title"/>
+        <tal:default condition="not:override">
+            <tal:pagetitle replace="page_title">
+                Inserts page title
+            </tal:pagetitle>
+            &mdash;
+            <tal:portaltitle replace="portal_title">
+             Inserts portal title
+            </tal:portaltitle>
+        </tal:default>
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+      <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+      <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta tal:define="metatags python:putils.listMetaTags(here).items()"
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <metal:js tal:define="scripts nocall:here/renderAllTheScripts|nothing"
+	      tal:condition="nocall:scripts">
+      <metal:block metal:use-macro="scripts/macros/jsrenderer">
+      </metal:block>
+    </metal:js>
+
+    <metal:css tal:define="css nocall:here/renderAllTheStylesheets|nothing">
+	<tal:block condition="nocall:css">
+          <metal:block metal:use-macro="css/macros/cssrenderer">
+          </metal:block>
+        </tal:block>
+        <tal:block condition="not:nocall:css">
+        <style type="text/css" media="all" tal:condition="exists:portal/base.css"
+               tal:content="string:@import url($portal_url/base.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/columns.css"
+               tal:content="string:@import url($portal_url/columns.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/public.css"
+               tal:content="string:@import url($portal_url/public.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/portlets.css"
+               tal:content="string:@import url($portal_url/portlets.css);">
+        </style>
+        </tal:block>
+    </metal:css>
+
+    <tal:iefixstart replace="structure string:&lt;!--[if lt IE 7]&gt;" />
+        <style type="text/css" media="all" tal:condition="exists: portal/IEFixes.css"
+               tal:content="string:@import url($portal_url/IEFixes.css);">
+        </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="home"
+          href=""
+          title="Front page"
+          i18n:attributes="title title_frontpage"
+          tal:attributes="href string:$portal_url" />
+    <link rel="search"
+          href=""
+          title="Search this site"
+          i18n:attributes="title title_search_this_site"
+          tal:attributes="href string:$portal_url/search_form" />
+    <link rel="author"
+          href=""
+          title="Author information"
+          i18n:attributes="title title_author_info"
+          tal:attributes="href string:${portal_url}/author/${here/Creator}" />
+    <link rel="contents"
+          href=""
+          title="Site Map"
+          i18n:attributes="title title_sitemap"
+          tal:attributes="href string:$portal_url/sitemap" />
+
+    <tal:fixme tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    </tal:fixme>
+
+    <link rel="alternate" href="" title="RSS 1.0" type="application/rss+xml"
+          tal:condition="python: syntool.isSyndicationAllowed(here)"
+          tal:attributes="href string:$here_url/RSS" />
+
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:condition="parent"
+          tal:attributes="href parent" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+
+    <meta http-equiv="imagetoolbar" content="no" />
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1.4/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1.4/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1.4/header.pt	(revision 584)
@@ -0,0 +1,137 @@
+<metal:block use-macro="here/global_defines/macros/defines" />
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone">
+
+  <head metal:define-macro="html_header"
+        tal:define="skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       page_title object_title;
+                       override python:here.hasProperty('qSEO_title')">
+        <tal:seo condition="override" content="here/qSEO_Title"/>
+        <tal:default condition="not:override">
+            <tal:pagetitle replace="page_title">
+                Inserts page title
+            </tal:pagetitle>
+            &mdash;
+            <tal:portaltitle replace="portal_title">
+             Inserts portal title
+            </tal:portaltitle>
+        </tal:default>
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+      <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+      <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta tal:define="metatags python:putils.listMetaTags(here).items()"
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <metal:js tal:define="scripts nocall:here/renderAllTheScripts|nothing"
+	      tal:condition="nocall:scripts">
+      <metal:block metal:use-macro="scripts/macros/jsrenderer">
+      </metal:block>
+    </metal:js>
+
+    <metal:css tal:define="css nocall:here/renderAllTheStylesheets|nothing">
+	<tal:block condition="nocall:css">
+          <metal:block metal:use-macro="css/macros/cssrenderer">
+          </metal:block>
+        </tal:block>
+        <tal:block condition="not:nocall:css">
+        <style type="text/css" media="all" tal:condition="exists:portal/base.css"
+               tal:content="string:@import url($portal_url/base.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/columns.css"
+               tal:content="string:@import url($portal_url/columns.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/public.css"
+               tal:content="string:@import url($portal_url/public.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/portlets.css"
+               tal:content="string:@import url($portal_url/portlets.css);">
+        </style>
+        </tal:block>
+    </metal:css>
+
+    <tal:iefixstart replace="structure string:&lt;!--[if lt IE 7]&gt;" />
+        <style type="text/css" media="all" tal:condition="exists: portal/IEFixes.css"
+               tal:content="string:@import url($portal_url/IEFixes.css);">
+        </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="home"
+          href=""
+          title="Front page"
+          i18n:attributes="title title_frontpage"
+          tal:attributes="href string:$portal_url" />
+    <link rel="search"
+          href=""
+          title="Search this site"
+          i18n:attributes="title title_search_this_site"
+          tal:attributes="href string:$portal_url/search_form" />
+    <link rel="author"
+          href=""
+          title="Author information"
+          i18n:attributes="title title_author_info"
+          tal:attributes="href string:${portal_url}/author/${here/Creator}" />
+    <link rel="contents"
+          href=""
+          title="Site Map"
+          i18n:attributes="title title_sitemap"
+          tal:attributes="href string:$portal_url/sitemap" />
+
+    <tal:fixme tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    </tal:fixme>
+
+    <link rel="alternate" href="" title="RSS 1.0" type="application/rss+xml"
+          tal:condition="python: syntool.isSyndicationAllowed(here)"
+          tal:attributes="href string:$here_url/RSS" />
+
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:condition="parent"
+          tal:attributes="href parent" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+
+    <meta http-equiv="imagetoolbar" content="no" />
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.1/header.pt	(revision 584)
@@ -0,0 +1,99 @@
+<metal:block use-macro="here/global_defines/macros/defines" />
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone">
+
+  <head metal:define-macro="html_header"
+        tal:define="skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       page_title here/pretty_title_or_id;
+                       override python:here.hasProperty('qSEO_title')">
+        <tal:seo condition="override" content="here/qSEO_Title"/>
+        <tal:default condition="not:override">
+            <tal:pagetitle replace="page_title">
+                Inserts page title
+            </tal:pagetitle>
+            &mdash;
+            <tal:portaltitle replace="portal_title">
+                Inserts portal title
+            </tal:portaltitle>
+        </tal:default>
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+      <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+      <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta tal:define="metatags python:putils.listMetaTags(here).items()"
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <metal:js metal:use-macro="here/renderAllTheScripts/macros/jsrenderer">
+    </metal:js>
+
+    <metal:css metal:use-macro="here/renderAllTheStylesheets/macros/cssrenderer">
+    </metal:css>
+
+    <tal:iefixstart replace="structure string:&lt;!--[if lt IE 7]&gt;" />
+        <style type="text/css" media="all" tal:condition="exists: portal/IEFixes.css"
+               tal:content="string:@import url($portal_url/IEFixes.css);">
+        </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="home" href="" title="Front page" tal:attributes="href string:$portal_url" />
+    <link rel="search" title="Search this site" tal:attributes="href string:$portal_url/search_form" />
+    <link rel="author" title="Author information" tal:attributes="href string:${portal_url}/author/${here/Creator}" />
+    <link rel="contents" href="" title="Site Map" tal:attributes="href string:$portal_url/sitemap" />
+    
+    <tal:fixme tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    </tal:fixme>
+
+    <link rel="alternate" href="" title="RSS 1.0" type="application/rss+xml"  
+          tal:condition="python: syntool.isSyndicationAllowed(here)"
+          tal:attributes="href string:$here_url/RSS" />
+
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:condition="parent"
+          tal:attributes="href parent" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+
+    <meta http-equiv="imagetoolbar" content="no" />
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.1/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.1/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.1/header.pt	(revision 584)
@@ -0,0 +1,142 @@
+<metal:block use-macro="here/global_defines/macros/defines" />
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone">
+
+  <head metal:define-macro="html_header"
+        tal:define="skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       page_title object_title;
+                       override python:here.hasProperty('qSEO_title')">
+        <tal:seo condition="override" content="here/qSEO_Title"/>
+        <tal:default condition="not:override">
+            <tal:pagetitle replace="page_title">
+                Inserts page title
+            </tal:pagetitle>
+            &mdash;
+            <tal:portaltitle replace="portal_title">
+             Inserts portal title
+            </tal:portaltitle>
+        </tal:default>
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+      <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+      <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta tal:define="metatags python:putils.listMetaTags(here).items()"
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <metal:js tal:define="scripts nocall:here/renderAllTheScripts|nothing"
+	      tal:condition="nocall:scripts">
+      <metal:block metal:use-macro="scripts/macros/jsrenderer">
+      </metal:block>
+    </metal:js>
+
+    <metal:css tal:define="css nocall:here/renderAllTheStylesheets|nothing">
+	<tal:block condition="nocall:css">
+          <metal:block metal:use-macro="css/macros/cssrenderer">
+          </metal:block>
+        </tal:block>
+        <tal:block condition="not:nocall:css">
+        <style type="text/css" media="all" tal:condition="exists:portal/base.css"
+               tal:content="string:@import url($portal_url/base.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/columns.css"
+               tal:content="string:@import url($portal_url/columns.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/public.css"
+               tal:content="string:@import url($portal_url/public.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/portlets.css"
+               tal:content="string:@import url($portal_url/portlets.css);">
+        </style>
+        </tal:block>
+    </metal:css>
+
+
+    <tal:iefixstart replace="structure string:&lt;!--[if lt IE 7]&gt;" />
+        <style type="text/css" media="all" tal:condition="exists: portal/IEFixes.css"
+               tal:content="string:@import url($portal_url/IEFixes.css);">
+        </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="home"
+          href=""
+          title="Front page"
+          i18n:attributes="title title_frontpage"
+          tal:attributes="href string:$portal_url" />
+    <link rel="search"
+          href=""
+          title="Search this site"
+          i18n:attributes="title title_search_this_site"
+          tal:attributes="href string:$portal_url/search_form" />
+    <link rel="author"
+          href=""
+          title="Author information"
+          i18n:attributes="title title_author_info"
+          tal:attributes="href string:${portal_url}/author/${here/Creator}" />
+    <link rel="contents"
+          href=""
+          title="Site Map"
+          i18n:attributes="title title_sitemap"
+          tal:attributes="href string:$portal_url/sitemap" />
+
+    <tal:fixme tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    </tal:fixme>
+
+    <link rel="alternate" href="" title="RSS 1.0" type="application/rss+xml"
+          tal:condition="python: syntool.isSyndicationAllowed(here)"
+          tal:attributes="href string:$here_url/RSS" />
+
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:condition="parent"
+          tal:attributes="href parent" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+
+    <meta http-equiv="imagetoolbar" content="no" />
+
+    <metal:block metal:define-slot="style_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      This is deprecated, please use style_slot instead.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.2/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.2/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.2/header.pt	(revision 584)
@@ -0,0 +1,141 @@
+<metal:block use-macro="here/global_defines/macros/defines" />
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone">
+
+  <head metal:define-macro="html_header"
+        tal:define="skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       page_title object_title;
+                       override python:here.hasProperty('qSEO_title')">
+        <tal:seo condition="override" content="here/qSEO_Title"/>
+        <tal:default condition="not:override">
+            <tal:pagetitle replace="page_title">
+                Inserts page title
+            </tal:pagetitle>
+            &mdash;
+            <tal:portaltitle replace="portal_title">
+             Inserts portal title
+            </tal:portaltitle>
+        </tal:default>
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+      <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+      <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta tal:define="metatags python:putils.listMetaTags(here).items()"
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <metal:js tal:define="scripts nocall:here/renderAllTheScripts|nothing"
+	      tal:condition="nocall:scripts">
+      <metal:block metal:use-macro="scripts/macros/jsrenderer">
+      </metal:block>
+    </metal:js>
+
+    <metal:css tal:define="css nocall:here/renderAllTheStylesheets|nothing">
+	<tal:block condition="nocall:css">
+          <metal:block metal:use-macro="css/macros/cssrenderer">
+          </metal:block>
+        </tal:block>
+        <tal:block condition="not:nocall:css">
+        <style type="text/css" media="all" tal:condition="exists:portal/base.css"
+               tal:content="string:@import url($portal_url/base.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/columns.css"
+               tal:content="string:@import url($portal_url/columns.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/public.css"
+               tal:content="string:@import url($portal_url/public.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/portlets.css"
+               tal:content="string:@import url($portal_url/portlets.css);">
+        </style>
+        </tal:block>
+    </metal:css>
+
+    <tal:iefixstart replace="structure string:&lt;!--[if IE]&gt;" />
+        <style type="text/css" media="all" tal:condition="exists: portal/IEFixes.css"
+               tal:content="string:@import url($portal_url/IEFixes.css);">
+        </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="home"
+          href=""
+          title="Front page"
+          i18n:attributes="title title_frontpage"
+          tal:attributes="href string:$portal_url" />
+    <link rel="search"
+          href=""
+          title="Search this site"
+          i18n:attributes="title title_search_this_site"
+          tal:attributes="href string:$portal_url/search_form" />
+    <link rel="author"
+          href=""
+          title="Author information"
+          i18n:attributes="title title_author_info"
+          tal:attributes="href string:${portal_url}/author/${here/Creator}" />
+    <link rel="contents"
+          href=""
+          title="Site Map"
+          i18n:attributes="title title_sitemap"
+          tal:attributes="href string:$portal_url/sitemap" />
+
+    <tal:fixme tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    </tal:fixme>
+
+    <link rel="alternate" href="" title="RSS 1.0" type="application/rss+xml"
+          tal:condition="python: syntool.isSyndicationAllowed(here)"
+          tal:attributes="href string:$here_url/RSS" />
+
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:condition="parent"
+          tal:attributes="href parent" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+
+    <meta http-equiv="imagetoolbar" content="no" />
+
+    <metal:block metal:define-slot="style_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      This is deprecated, please use style_slot instead.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.3-final/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.3-final/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.3-final/header.pt	(revision 584)
@@ -0,0 +1,144 @@
+<metal:block use-macro="here/global_defines/macros/defines" />
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone">
+
+  <head metal:define-macro="html_header"
+        tal:define="skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       page_title object_title;
+                       override python:here.hasProperty('qSEO_title')">
+
+	<tal:seo condition="override" content="here/qSEO_title" />
+	<tal:default condition="not:override">
+            <tal:hastitle condition="python:portal_title != page_title">
+                <tal:pagetitle replace="page_title">
+                    Inserts page title
+                </tal:pagetitle>
+                &mdash;
+            </tal:hastitle>
+            <tal:portaltitle replace="portal_title">
+                Inserts portal title
+            </tal:portaltitle>
+        </tal:default>
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+        <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+        <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta name="meta_name" content="meta_content"
+          tal:define="metatags python:putils.listMetaTags(here).items()"
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <metal:js tal:define="scripts nocall:here/renderAllTheScripts|nothing"
+	      tal:condition="nocall:scripts">
+      <metal:block metal:use-macro="scripts/macros/jsrenderer">
+      </metal:block>
+    </metal:js>
+
+    <metal:css tal:define="css nocall:here/renderAllTheStylesheets|nothing">
+	<tal:block condition="nocall:css">
+          <metal:block metal:use-macro="css/macros/cssrenderer">
+          </metal:block>
+        </tal:block>
+        <tal:block condition="not:nocall:css">
+        <style type="text/css" media="all" tal:condition="exists:portal/base.css"
+               tal:content="string:@import url($portal_url/base.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/columns.css"
+               tal:content="string:@import url($portal_url/columns.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/public.css"
+               tal:content="string:@import url($portal_url/public.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/portlets.css"
+               tal:content="string:@import url($portal_url/portlets.css);">
+        </style>
+        </tal:block>
+    </metal:css>
+
+    <tal:iefixstart replace="structure string:&lt;!--[if IE]&gt;" />
+        <style type="text/css" media="all" tal:condition="exists: portal/IEFixes.css"
+               tal:content="string:@import url($portal_url/IEFixes.css);">
+        </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="home"
+          href=""
+          title="Front page"
+          i18n:attributes="title title_frontpage"
+          tal:attributes="href string:$portal_url" />
+    <link rel="search"
+          href=""
+          title="Search this site"
+          i18n:attributes="title title_search_this_site"
+          tal:attributes="href string:$portal_url/search_form" />
+    <link rel="author"
+          href=""
+          title="Author information"
+          i18n:attributes="title title_author_info"
+          tal:attributes="href string:${portal_url}/author/${here/Creator}" />
+    <link rel="contents"
+          href=""
+          title="Site Map"
+          i18n:attributes="title title_sitemap"
+          tal:attributes="href string:$portal_url/sitemap" />
+
+    <tal:fixme tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    </tal:fixme>
+
+    <link rel="alternate" href="" title="RSS 1.0" type="application/rss+xml"
+          tal:condition="python: syntool.isSyndicationAllowed(here)"
+          tal:attributes="href string:$here_url/RSS" />
+
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:condition="parent"
+          tal:attributes="href parent" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+    <meta http-equiv="imagetoolbar" content="no" />
+    
+    <metal:block metal:define-slot="style_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      This is deprecated, please use style_slot instead.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.4-2/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.4-2/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.4-2/header.pt	(revision 584)
@@ -0,0 +1,144 @@
+<metal:block use-macro="here/global_defines/macros/defines" />
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone">
+
+  <head metal:define-macro="html_header"
+        tal:define="skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       page_title object_title;
+                       override python:here.hasProperty('qSEO_title')">
+
+	<tal:seo condition="override" content="here/qSEO_title" />
+	<tal:default condition="not:override">
+            <tal:hastitle condition="python:portal_title != page_title">
+                <tal:pagetitle replace="page_title">
+                    Inserts page title
+                </tal:pagetitle>
+                &mdash;
+            </tal:hastitle>
+            <tal:portaltitle replace="portal_title">
+                Inserts portal title
+            </tal:portaltitle>
+        </tal:default>
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+        <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+        <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta name="meta_name" content="meta_content"
+          tal:define="metatags python:putils.listMetaTags(here).items()"
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <metal:js tal:define="scripts nocall:here/renderAllTheScripts|nothing"
+	      tal:condition="nocall:scripts">
+      <metal:block metal:use-macro="scripts/macros/jsrenderer">
+      </metal:block>
+    </metal:js>
+
+    <metal:css tal:define="css nocall:here/renderAllTheStylesheets|nothing">
+	<tal:block condition="nocall:css">
+          <metal:block metal:use-macro="css/macros/cssrenderer">
+          </metal:block>
+        </tal:block>
+        <tal:block condition="not:nocall:css">
+        <style type="text/css" media="all" tal:condition="exists:portal/base.css"
+               tal:content="string:@import url($portal_url/base.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/columns.css"
+               tal:content="string:@import url($portal_url/columns.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/public.css"
+               tal:content="string:@import url($portal_url/public.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/portlets.css"
+               tal:content="string:@import url($portal_url/portlets.css);">
+        </style>
+        </tal:block>
+    </metal:css>
+
+    <tal:iefixstart replace="structure string:&lt;!--[if IE]&gt;" />
+        <style type="text/css" media="all" tal:condition="exists: portal/IEFixes.css"
+               tal:content="string:@import url($portal_url/IEFixes.css);">
+        </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="home"
+          href=""
+          title="Front page"
+          i18n:attributes="title title_frontpage"
+          tal:attributes="href string:$portal_url" />
+    <link rel="search"
+          href=""
+          title="Search this site"
+          i18n:attributes="title title_search_this_site"
+          tal:attributes="href string:$portal_url/search_form" />
+    <link rel="author"
+          href=""
+          title="Author information"
+          i18n:attributes="title title_author_info"
+          tal:attributes="href string:${portal_url}/author/${here/Creator}" />
+    <link rel="contents"
+          href=""
+          title="Site Map"
+          i18n:attributes="title title_sitemap"
+          tal:attributes="href string:$portal_url/sitemap" />
+
+    <tal:fixme tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    </tal:fixme>
+
+    <link rel="alternate" href="" title="RSS 1.0" type="application/rss+xml"
+          tal:condition="python: syntool.isSyndicationAllowed(here)"
+          tal:attributes="href string:$here_url/RSS" />
+
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:condition="parent"
+          tal:attributes="href parent" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+    <meta http-equiv="imagetoolbar" content="no" />
+    
+    <metal:block metal:define-slot="style_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      This is deprecated, please use style_slot instead.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.4-final/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.4-final/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.4-final/header.pt	(revision 584)
@@ -0,0 +1,144 @@
+<metal:block use-macro="here/global_defines/macros/defines" />
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone">
+
+  <head metal:define-macro="html_header"
+        tal:define="skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       page_title object_title;
+                       override python:here.hasProperty('qSEO_title')">
+
+	<tal:seo condition="override" content="here/qSEO_title" />
+	<tal:default condition="not:override">
+            <tal:hastitle condition="python:portal_title != page_title">
+                <tal:pagetitle replace="page_title">
+                    Inserts page title
+                </tal:pagetitle>
+                &mdash;
+            </tal:hastitle>
+            <tal:portaltitle replace="portal_title">
+                Inserts portal title
+            </tal:portaltitle>
+        </tal:default>
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+        <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+        <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta name="meta_name" content="meta_content"
+          tal:define="metatags python:putils.listMetaTags(here).items()"
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <metal:js tal:define="scripts nocall:here/renderAllTheScripts|nothing"
+	      tal:condition="nocall:scripts">
+      <metal:block metal:use-macro="scripts/macros/jsrenderer">
+      </metal:block>
+    </metal:js>
+
+    <metal:css tal:define="css nocall:here/renderAllTheStylesheets|nothing">
+	<tal:block condition="nocall:css">
+          <metal:block metal:use-macro="css/macros/cssrenderer">
+          </metal:block>
+        </tal:block>
+        <tal:block condition="not:nocall:css">
+        <style type="text/css" media="all" tal:condition="exists:portal/base.css"
+               tal:content="string:@import url($portal_url/base.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/columns.css"
+               tal:content="string:@import url($portal_url/columns.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/public.css"
+               tal:content="string:@import url($portal_url/public.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/portlets.css"
+               tal:content="string:@import url($portal_url/portlets.css);">
+        </style>
+        </tal:block>
+    </metal:css>
+
+    <tal:iefixstart replace="structure string:&lt;!--[if IE]&gt;" />
+        <style type="text/css" media="all" tal:condition="exists: portal/IEFixes.css"
+               tal:content="string:@import url($portal_url/IEFixes.css);">
+        </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="home"
+          href=""
+          title="Front page"
+          i18n:attributes="title title_frontpage"
+          tal:attributes="href string:$portal_url" />
+    <link rel="search"
+          href=""
+          title="Search this site"
+          i18n:attributes="title title_search_this_site"
+          tal:attributes="href string:$portal_url/search_form" />
+    <link rel="author"
+          href=""
+          title="Author information"
+          i18n:attributes="title title_author_info"
+          tal:attributes="href string:${portal_url}/author/${here/Creator}" />
+    <link rel="contents"
+          href=""
+          title="Site Map"
+          i18n:attributes="title title_sitemap"
+          tal:attributes="href string:$portal_url/sitemap" />
+
+    <tal:fixme tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    </tal:fixme>
+
+    <link rel="alternate" href="" title="RSS 1.0" type="application/rss+xml"
+          tal:condition="python: syntool.isSyndicationAllowed(here)"
+          tal:attributes="href string:$here_url/RSS" />
+
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:condition="parent"
+          tal:attributes="href parent" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+    <meta http-equiv="imagetoolbar" content="no" />
+    
+    <metal:block metal:define-slot="style_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      This is deprecated, please use style_slot instead.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.5/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.5/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5.5/header.pt	(revision 584)
@@ -0,0 +1,145 @@
+<metal:block use-macro="here/global_defines/macros/defines" />
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone">
+
+  <head metal:define-macro="html_header"
+        tal:define="skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="safe_unicode python:modules['Products.CMFPlone.utils'].safe_unicode;
+                       portal_title portal_properties/title;
+                       page_title object_title;
+                       override python:here.hasProperty('qSEO_title')">
+
+	<tal:seo condition="override" content="here/qSEO_title" />
+	<tal:default condition="not:override">
+            <tal:hastitle condition="python:safe_unicode(portal_title) != safe_unicode(page_title)">
+                <tal:pagetitle replace="page_title">
+                    Inserts page title
+                </tal:pagetitle>
+                &mdash;
+            </tal:hastitle>
+            <tal:portaltitle replace="portal_title">
+                Inserts portal title
+            </tal:portaltitle>
+        </tal:default>
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+        <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+        <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta name="meta_name" content="meta_content"
+          tal:define="metatags python:putils.listMetaTags(here).items()"
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <metal:js tal:define="scripts nocall:here/renderAllTheScripts|nothing"
+	      tal:condition="nocall:scripts">
+      <metal:block metal:use-macro="scripts/macros/jsrenderer">
+      </metal:block>
+    </metal:js>
+
+    <metal:css tal:define="css nocall:here/renderAllTheStylesheets|nothing">
+	<tal:block condition="nocall:css">
+          <metal:block metal:use-macro="css/macros/cssrenderer">
+          </metal:block>
+        </tal:block>
+        <tal:block condition="not:nocall:css">
+        <style type="text/css" media="all" tal:condition="exists:portal/base.css"
+               tal:content="string:@import url($portal_url/base.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/columns.css"
+               tal:content="string:@import url($portal_url/columns.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/public.css"
+               tal:content="string:@import url($portal_url/public.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/portlets.css"
+               tal:content="string:@import url($portal_url/portlets.css);">
+        </style>
+        </tal:block>
+    </metal:css>
+
+    <tal:iefixstart replace="structure string:&lt;!--[if IE]&gt;" />
+        <style type="text/css" media="all" tal:condition="exists: portal/IEFixes.css"
+               tal:content="string:@import url($portal_url/IEFixes.css);">
+        </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="home"
+          href=""
+          title="Front page"
+          i18n:attributes="title title_frontpage"
+          tal:attributes="href string:$portal_url" />
+    <link rel="search"
+          href=""
+          title="Search this site"
+          i18n:attributes="title title_search_this_site"
+          tal:attributes="href string:$portal_url/search_form" />
+    <link rel="author"
+          href=""
+          title="Author information"
+          i18n:attributes="title title_author_info"
+          tal:attributes="href string:${portal_url}/author/${here/Creator}" />
+    <link rel="contents"
+          href=""
+          title="Site Map"
+          i18n:attributes="title title_sitemap"
+          tal:attributes="href string:$portal_url/sitemap" />
+
+    <tal:fixme tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    </tal:fixme>
+
+    <link rel="alternate" href="" title="RSS 1.0" type="application/rss+xml"
+          tal:condition="python: syntool.isSyndicationAllowed(here)"
+          tal:attributes="href string:$here_url/RSS" />
+
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:condition="parent"
+          tal:attributes="href parent" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+    <meta http-equiv="imagetoolbar" content="no" />
+    
+    <metal:block metal:define-slot="style_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      This is deprecated, please use style_slot instead.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5/header.pt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5/header.pt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/2.5/header.pt	(revision 584)
@@ -0,0 +1,141 @@
+<metal:block use-macro="here/global_defines/macros/defines" />
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      lang="en"
+      i18n:domain="plone">
+
+  <head metal:define-macro="html_header"
+        tal:define="skinrequest python:request.get(here.portal_skins.getRequestVarname(), '');">
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+          tal:define="charset site_properties/default_charset|string:utf-8"
+          tal:attributes="content string:text/html;;charset=${charset}" />
+
+    <title tal:define="portal_title portal_properties/title;
+                       page_title object_title;
+                       override python:here.hasProperty('qSEO_title')">
+        <tal:seo condition="override" content="here/qSEO_Title"/>
+        <tal:default condition="not:override">
+            <tal:pagetitle replace="page_title">
+                Inserts page title
+            </tal:pagetitle>
+            &mdash;
+            <tal:portaltitle replace="portal_title">
+             Inserts portal title
+            </tal:portaltitle>
+        </tal:default>
+    </title>
+
+    <tal:html_comment define="comment here/qSEO_HTML_Comment|nothing" condition="comment">
+      <tal:html_comment_start replace="structure string:&lt;!--" />
+        <tal:comment replace="comment" />
+      <tal:html_comment_end replace="structure string:--&gt;" />
+    </tal:html_comment>
+
+    <metal:baseslot define-slot="base" />
+
+    <meta name="generator" content="Plone - http://plone.org" />
+
+    <meta tal:define="metatags python:putils.listMetaTags(here).items()"
+          tal:condition="metatags"
+          tal:repeat="keyval metatags"
+          tal:attributes="name python:keyval[0];
+                          content python:keyval[1];" />
+
+    <metal:js tal:define="scripts nocall:here/renderAllTheScripts|nothing"
+	      tal:condition="nocall:scripts">
+      <metal:block metal:use-macro="scripts/macros/jsrenderer">
+      </metal:block>
+    </metal:js>
+
+    <metal:css tal:define="css nocall:here/renderAllTheStylesheets|nothing">
+	<tal:block condition="nocall:css">
+          <metal:block metal:use-macro="css/macros/cssrenderer">
+          </metal:block>
+        </tal:block>
+        <tal:block condition="not:nocall:css">
+        <style type="text/css" media="all" tal:condition="exists:portal/base.css"
+               tal:content="string:@import url($portal_url/base.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/columns.css"
+               tal:content="string:@import url($portal_url/columns.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/public.css"
+               tal:content="string:@import url($portal_url/public.css);">
+        </style>
+        <style type="text/css" media="all" tal:condition="exists:portal/portlets.css"
+               tal:content="string:@import url($portal_url/portlets.css);">
+        </style>
+        </tal:block>
+    </metal:css>
+
+    <tal:iefixstart replace="structure string:&lt;!--[if lt IE 7]&gt;" />
+        <style type="text/css" media="all" tal:condition="exists: portal/IEFixes.css"
+               tal:content="string:@import url($portal_url/IEFixes.css);">
+        </style>
+    <tal:iefixend replace="structure string:&lt;![endif]--&gt;" />
+
+    <link rel="shortcut icon" type="image/x-icon" tal:attributes="href string:$portal_url/favicon.ico" />
+
+    <link rel="home"
+          href=""
+          title="Front page"
+          i18n:attributes="title title_frontpage"
+          tal:attributes="href string:$portal_url" />
+    <link rel="search"
+          href=""
+          title="Search this site"
+          i18n:attributes="title title_search_this_site"
+          tal:attributes="href string:$portal_url/search_form" />
+    <link rel="author"
+          href=""
+          title="Author information"
+          i18n:attributes="title title_author_info"
+          tal:attributes="href string:${portal_url}/author/${here/Creator}" />
+    <link rel="contents"
+          href=""
+          title="Site Map"
+          i18n:attributes="title title_sitemap"
+          tal:attributes="href string:$portal_url/sitemap" />
+
+    <tal:fixme tal:replace="nothing">
+      <link rel="help" href="" title="Help" tal:attributes="href string:$portal_url/help" />
+      <link rel="copyright" href="" title="Copyright information" tal:attributes="href string:copyright" />
+    </tal:fixme>
+
+    <link rel="alternate" href="" title="RSS 1.0" type="application/rss+xml"
+          tal:condition="python: syntool.isSyndicationAllowed(here)"
+          tal:attributes="href string:$here_url/RSS" />
+
+    <link rel="up" href="" title="Up one level"
+          tal:define="parent python:here.navigationParent(here, template_id)"
+          tal:condition="parent"
+          tal:attributes="href parent" />
+
+    <metal:block metal:define-slot="rel_slot" tal:replace="nothing">
+      Inserts rel links specified from a page.
+    </metal:block>
+
+    <meta http-equiv="imagetoolbar" content="no" />
+
+    <metal:block metal:define-slot="style_slot" tal:replace="nothing">
+      Inserts CSS specified from a page.
+    </metal:block>
+
+    <metal:block metal:define-slot="css_slot" tal:replace="nothing">
+      This is deprecated, please use style_slot instead.
+    </metal:block>
+
+    <metal:block metal:define-slot="head_slot" tal:replace="nothing">
+      Flexibility for published templates to inject stuff into the head tag.
+    </metal:block>
+
+    <metal:block metal:define-slot="javascript_head_slot" tal:replace="nothing">
+      Inserts javascript specified from a page.
+    </metal:block>
+
+  </head>
+
+  <body>
+  </body>
+
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/checkkeywords.js.dtml
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/checkkeywords.js.dtml	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/checkkeywords.js.dtml	(revision 584)
@@ -0,0 +1,118 @@
+/******************************************************************/
+
+var seo_keywords_url = '&dtml-absolute_url;/checkSEOKeywords';
+var KEYWORDS_IDS = ['seo_keywords',];
+// var KEYWORDS_REPORT = {};
+
+// function extractWords(data) {
+//     data = data // replace all non-word character with space
+//                 .replace(/[^a-zA-Z0-9\-\'\u2019\"\`]+/g, ' ')
+//                 // replace "-" and "'" symbols if it create groups event inside of token
+//                 .replace(/[\-\'\u2019\"\`]{2,}/g, ' ')
+//                 // replace all non-word characters and "-", "'" if it stay at word edge
+//                 .replace(/(?:^|\s+)[^a-zA-Z0-9]+|[^a-zA-Z0-9]+(?:\s+|$)/g, ' ')
+//                 // strip whitespaces
+//                 .replace(/^\s*(.*?)\s*$/, '$1');
+//     return data.split(/[^a-zA-Z0-9\-\'\u2019\"\`]+/);
+// }
+// 
+// function countTerm(node, word, id) {
+//     var contents = extractWords(node.nodeValue.toLowerCase());
+//     var term = word.toLowerCase();
+//     for (var i = 0, w; w = contents[i]; i++) {
+//         if (w == term) {
+//             KEYWORDS_REPORT[id][word] = 1;
+//             return 'found';
+//         }
+//     }
+//     return false;
+// }
+// 
+// function keywordsWalkTextNodes(node, func, data, id) {
+//     if (!node) return false;
+//     if (KEYWORDS_REPORT[id][data] == 1) return 'found';
+//     if (node.hasChildNodes) {
+//         if (node.nodeType == 3) {
+//             if (func(node, data, id) == 'found') {
+//                 return 'found';
+//             }
+//         }
+//         for (var i = 0; i < node.childNodes.length; i++) {
+//             if (keywordsWalkTextNodes(node.childNodes[i], func, data, id) == 'found') {
+//                 return 'found';
+//             }
+//         }
+//     }
+// }
+
+// function checkPageKeywords(event) {
+//     var event = event ? event:window.event;
+//     var target = event.target ? event.target : event.srcElement;
+//     if (!target) {return false;};
+//     var id = target.id.replace('_check_keywords', '')
+//     KEYWORDS_REPORT[id] = {};
+//     var area = document.getElementById(id);
+//     if (area && typeof(area.value) != 'undefined') {
+//         var terms = extractWords(area.value);
+//         for (var i = 0, term; term = terms[i]; i++) {
+//             if (KEYWORDS_REPORT[id][term] == 1) continue;
+//             KEYWORDS_REPORT[id][term] = 0;
+//             keywordsWalkTextNodes(document.body, countTerm, term, id);
+//         }
+//     }
+//     var report = '';
+//     for (var term in KEYWORDS_REPORT[id]) {
+//         if (KEYWORDS_REPORT[id][term] != 1)
+//             report += term + ' ';
+//     }
+//     KEYWORDS_REPORT[id] = {};
+//     if (report != '') {
+//         report = 'Next keywords did not appear on the page:\n' + report;
+//     } else {
+//         report = 'All keywords found on the page!';
+//     }
+//     alert(report);
+//     return false;
+// }
+
+function checkPageKeywords(event) {
+    var event = event ? event:window.event;
+    var target = event.target ? event.target : event.srcElement;
+    if (!target) {return false;};
+    var id = target.id.replace('_check_keywords', '')
+    var area = document.getElementById(id);
+    if (area && typeof(area.value) != 'undefined') {
+        var req = new XMLHttpRequest();
+        req.open("POST", seo_keywords_url, true);
+        req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+        req.onreadystatechange = function(request) {
+            if (req.readyState == 4) {
+                if (req.status == '200') {
+                    alert(req.responseText);
+                } else {
+                    alert('Error on server!');
+                }
+            }
+        };
+        req.send("text="+area.value);
+    }
+    return false;
+}
+
+function addKeywordsButton(event) {
+    for (var i = 0, id; id = KEYWORDS_IDS[i]; i++) {
+        var area = document.getElementById(id);
+        if (!area || (typeof(area.value) == 'undefined')) continue;
+        var button = document.createElement('INPUT');
+        button.type = 'button';
+        button.value = 'Check Keywords';
+        button.name = id+'_check_keywords';
+        button.id = id+'_check_keywords';
+        button.className = 'check-keywords-button';
+        area.parentNode.insertBefore(button, area.nextSibling);
+        registerEventListener(button, 'click', checkPageKeywords);
+//         KEYWORDS_REPORT[id] = {};
+    }
+}
+
+registerPloneFunction(addKeywordsButton);
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/custommetatags.js
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/custommetatags.js	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/custommetatags.js	(revision 584)
@@ -0,0 +1,352 @@
+customMetaTagsFunctions = new Object()
+
+customMetaTagsFunctions.getInputOrSelect = function(node) {
+    /* Get the (first) input or select form element under the given node */
+    
+    var inputs = node.getElementsByTagName("input");
+    if(inputs.length > 0) {
+        return inputs[0];
+    }
+    
+    var selects = node.getElementsByTagName("select");
+    if(selects.length > 0) {
+        return selects[0];
+    }
+
+    return null;
+}
+
+customMetaTagsFunctions.getWidgetRows = function(currnode) {
+	/* Return primary <tr>s of current node's parent DGW */
+	tbody = this.getParentElementById(currnode, "datagridwidget-tbody");
+	return this.getRows(tbody);
+}
+
+customMetaTagsFunctions.getRows = function(tbody) {
+	/* Return <tr> rows of <table> element */
+    
+	var rows = new Array()
+	
+	child = tbody.firstChild;
+	while(child != null) {
+		if(child.tagName != null) {
+			if(child.tagName.toLowerCase() == "tr") {
+				rows = rows.concat(child);
+			}
+		}
+		child = child.nextSibling;
+	}
+	              
+	return rows;   
+} 
+
+customMetaTagsFunctions.autoInsertRow = function(e) {
+    /* Add a new row when changing the last row 
+       (i.e. the infamous auto insert feature)
+    
+       Check that if this onchange event handler was
+       called from the last row. In this case,
+       add a new row for DGF.
+       
+    */
+
+    var currnode = window.event ? window.event.srcElement : e.currentTarget;
+    
+	// fetch required data structure   
+    var tbody = this.getParentElement(currnode, "TBODY");
+    var rows = this.getRows(tbody);        
+    var lastRow = rows[rows.length-1]; 
+    
+    var thisRow = this.getParentElementById(currnode, "datagridwidget-row");      
+    
+    /* Skip the very last row which is a hidden template row */
+    if(rows.length-1 ==(thisRow.rowIndex)) {
+	    // Create a new row
+	    var newtr = this.createNewRow(lastRow);
+	                                                        
+	    // Put new row to DOM tree before template row        
+		lastRow.parentNode.insertBefore(newtr, lastRow);
+		
+		// update orderindex hidden fields
+		this.updateOrderIndex(tbody);	        	    
+    }    
+}
+
+customMetaTagsFunctions.addRowAfter = function(currnode) {
+	/*
+		Creates a new row before the clicked row
+	*/
+	
+	// fetch required data structure
+    var tbody = this.getParentElementById(currnode, "datagridwidget-tbody"); 
+    var thisRow = this.getParentElementById(currnode, "datagridwidget-row"); 
+
+    var newtr = this.createNewRow(thisRow);
+        
+	thisRow.parentNode.insertBefore(newtr, thisRow);
+	
+	// update orderindex hidden fields
+	this.updateOrderIndex(tbody);	
+  
+}
+
+customMetaTagsFunctions.addRow = function(id) {
+	/* Explitcly add row for given DataGridField 
+	
+		@param id Archetypes field id for the widget	
+	*/
+	
+	// fetch required data structure
+    var tbody = document.getElementById("datagridwidget-tbody-" + id);    
+    var rows = this.getRows(tbody);    
+    var lastRow = rows[rows.length-1];
+        
+    var oldRows = rows.length;
+                  
+    // Create a new row
+    var newtr = this.createNewRow(lastRow);
+    
+    // Put new row to DOM tree before template row        
+	newNode = lastRow.parentNode.insertBefore(newtr, lastRow);
+	
+	// update orderindex hidden fields
+	this.updateOrderIndex(tbody);		
+      
+}
+
+customMetaTagsFunctions.createNewRow = function(tr) { 
+	/* Creates a new row 
+		   
+	   @param tr A row in a table where we'll be adding the new row
+	*/
+	
+    var tbody = this.getParentElementById(tr, "datagridwidget-tbody"); 
+    var rows = this.getRows(tbody);   
+    
+    // hidden template row 
+    var lastRow = rows[rows.length-1]; 
+	
+	var newtr = document.createElement("tr");
+    newtr.setAttribute("id", "datagridwidget-row");
+    newtr.setAttribute("class", "datagridwidget-row");
+    	
+	// clone template contents from the last row to the newly created row
+	// HOX HOX HOX
+	// If f****ng IE clones lastRow directly it doesn't work.
+	// lastRow is in hidden state and no matter what you do it remains hidden.
+	// i.e. overriding class doesn't bring it visible.
+	// In Firefox everything worked like a charm.
+	// So the code below is really a hack to satisfy Microsoft codeborgs.
+	// keywords: IE javascript clone clonenode hidden element render visibility visual
+	child = lastRow.firstChild;
+	while(child != null) {
+		newchild = child.cloneNode(true);
+		newtr.appendChild(newchild);
+		child = child.nextSibling;
+	}		
+	    
+    return newtr;	 
+}
+
+
+customMetaTagsFunctions.removeFieldRow = function(node) {
+    /* Remove the row in which the given node is found */
+    
+    var row = this.getParentElementById(node, 'datagridwidget-row');
+    var tbody = this.getParentElementById(node, 'datagridwidget-tbody');
+    tbody.removeChild(row);
+}
+
+customMetaTagsFunctions.moveRowDown = function(currnode){
+    /* Move the given row down one */
+           
+    var tbody = this.getParentElementById(currnode, "datagridwidget-tbody");    
+    
+    var rows = this.getWidgetRows(currnode);
+    
+    var row = this.getParentElementById(currnode, "datagridwidget-row");      
+    if(row == null) {
+    	alert("Couldn't find DataGridWidget row");
+    	return;
+    }
+    
+    var idx = null
+    
+    // We can't use nextSibling because of blank text nodes in some browsers
+    // Need to find the index of the row
+    for(var t = 0; t < rows.length; t++) {
+        if(rows[t] == row) {
+            idx = t;
+            break;
+        }
+    }
+
+    // Abort if the current row wasn't found
+    if(idx == null)
+        return;     
+        
+    // If this was the last row (before the blank row at the end used to create
+    // new rows), move to the top, else move down one.
+    if(idx + 2 == rows.length) {
+        var nextRow = rows.item[0]
+        this.shiftRow(row, nextRow)
+    } else {
+        var nextRow = rows[idx+1]
+        this.shiftRow(nextRow, row)
+    }
+    
+    this.updateOrderIndex(tbody)
+
+}
+
+customMetaTagsFunctions.moveRowUp = function(currnode){
+    /* Move the given row up one */
+    
+    var tbody = this.getParentElementById(currnode, "datagridwidget-tbody");    
+    var rows = this.getWidgetRows(currnode);
+    
+    var row = this.getParentElementById(currnode, "datagridwidget-row");      
+    if(row == null) {
+    	alert("Couldn't find DataGridWidget row");
+    	return;
+    }
+
+    var idx = null
+    
+    // We can't use nextSibling because of blank text nodes in some browsers
+    // Need to find the index of the row
+    for(var t = 0; t < rows.length; t++) {
+        if(rows[t] == row) {
+            idx = t;
+            break;
+        }
+    }
+    
+    // Abort if the current row wasn't found
+    if(idx == null)
+        return;
+        
+    // If this was the first row, move to the end (i.e. before the blank row
+    // at the end used to create new rows), else move up one
+    if(idx == 0) {
+        var previousRow = rows[rows.length - 1]
+        this.shiftRow(row, previousRow);
+    } else {
+        var previousRow = rows[idx-1];
+        this.shiftRow(row, previousRow);
+    }
+    
+    this.updateOrderIndex(tbody);
+}
+
+customMetaTagsFunctions.shiftRow = function(bottom, top){
+    /* Put node top before node bottom */
+    
+    bottom.parentNode.insertBefore(bottom, top)   
+}
+
+customMetaTagsFunctions.updateOrderIndex = function (tbody) {
+
+    /* Update the hidden orderindex fields to be in the right order */
+    
+    var xre = new RegExp(/^orderindex__/)
+    var idx = 0;
+    var cell;
+    
+    var rows = this.getRows(tbody); 
+    
+    /* Make sure that updateOrderIndex doesn't touch 
+       the template (last) row */
+    for(var i=0; i<rows.length-1; i++) {
+    
+	    for (var c = 0; (cell = rows[i].getElementsByTagName('INPUT').item(c)); c++) {
+	            
+	        if (cell.getAttribute('id')) {
+	            if (xre.exec(cell.id)) {
+	                cell.value = idx;
+	            }
+	        }           
+	        this.updateRadioButtonGroupName(this.getParentElement(cell, "TR"), idx);        
+	        idx++;
+	    }      
+	}
+}
+
+
+customMetaTagsFunctions.updateRadioButtonGroupName = function (row, newIndex) {
+	/* Adjust radio button group names after reordering 
+	
+	   Why we do this, see RadioColumn class comments
+	   
+	   TODO: If chain onchange -> updateOrderIndex -> updaterRadioButtonGroupName
+	   is triggered on Firefox, the value of checked radio button is put to the
+	   newly generated row instead of clicked row.
+	*/
+
+   var cell;
+   var xre = new RegExp(/^radio/)
+   var xre2 = new RegExp(/^checkbox/)
+   
+    for (var c = 0; (cell = row.getElementsByTagName('INPUT').item(c)); c++) {
+   	    	    
+   	   	if(cell.getAttribute('type')) {
+   	   		var type = cell.getAttribute('type');
+             if (xre.exec(type) || xre2.exec(type)) {          
+            	
+				var name = cell.getAttribute("NAME")
+				if(name == null) continue;
+
+				// save fieldId + columnId part
+				var baseLabel = name.substring(0, name.lastIndexOf("."));				
+				// update per row running id
+				cell.setAttribute("NAME", baseLabel + "." + newIndex);
+			}
+   	    }               
+	}
+}
+
+customMetaTagsFunctions.getParentElement = function(currnode, tagname) {
+    /* Find the first parent node with the given tag name */
+
+    tagname = tagname.toUpperCase();
+    var parent = currnode.parentNode;
+
+    while(parent.tagName.toUpperCase() != tagname) {
+        parent = parent.parentNode;
+        // Next line is a safety belt
+        if(parent.tagName.toUpperCase() == "BODY") 
+            return null;
+    }
+
+    return parent;
+}
+
+customMetaTagsFunctions.getParentElementById = function(currnode, id) {
+    /* Find the first parent node with the given id 
+    
+    	Id is partially matched: the beginning of
+    	an element id matches parameter id string.
+    
+    	Currnode: Node where ascending in DOM tree beings
+    	Id: Id string to look for. 
+    	    	
+    */
+    
+    id = id.toLowerCase();
+    var parent = currnode.parentNode;
+
+    while(true) {
+       
+    	var parentId = parent.getAttribute("id");
+    	if(parentId != null) {    	
+    		 if(parentId.toLowerCase().substring(0, id.length) == id) break;
+    	}
+    	    
+        parent = parent.parentNode;
+        // Next line is a safety belt
+        if(parent.tagName.toUpperCase() == "BODY") 
+            return null;
+    }
+
+    return parent;
+}
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/prefs_qseo_setup.cpy
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/prefs_qseo_setup.cpy	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/prefs_qseo_setup.cpy	(revision 584)
@@ -0,0 +1,48 @@
+## Script (Python) "prefs_seo_config"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters= portalTypes=[], exposeDCMetaTags=None, additionalKeywords=[], default_custom_metatags=[]
+##title=add action tab for selected portal types
+##
+from Products.CMFCore.utils import getToolByName
+
+portal_props = getToolByName(context, 'portal_properties')
+site_props = getToolByName(portal_props, 'site_properties')
+seo_props = getToolByName(portal_props, 'seo_properties')
+site_props.manage_changeProperties(exposeDCMetaTags=exposeDCMetaTags)
+seo_props.manage_changeProperties(additional_keywords=additionalKeywords)
+seo_props.manage_changeProperties(default_custom_metatags=default_custom_metatags)
+
+pt = getToolByName(context, 'portal_types')
+for ptype in pt.objectValues():
+    try:
+        #for Plone-2.5 and higher
+        acts = filter(lambda x: x.id == 'seo_properties', ptype.listActions())
+        action = acts and acts[0] or None
+    except AttributeError:
+        action = ptype.getActionById('seo_properties', default=None )
+
+    if ptype.getId() in portalTypes:
+        if action is None:
+            ptype.addAction('seo_properties',
+                            'SEO Properties',
+                            'string:${object_url}/qseo_properties_edit_form',
+                            '',
+                            'Modify portal content',
+                            'object',
+                            visible=1)
+    else:
+        if action !=None:
+            actions = list(ptype.listActions())
+            ptype.deleteActions([actions.index(a) for a in actions if a.getId()=='seo_properties'])
+
+
+msg = "Search Engine Optimizer configuration updated."
+try:
+    context.plone_utils.addPortalMessage(msg)
+    return state
+except AttributeError:
+    return state.set(context=context, portal_status_message=msg)
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/prefs_qseo_setup.cpy.metadata
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/prefs_qseo_setup.cpy.metadata	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/prefs_qseo_setup.cpy.metadata	(revision 584)
@@ -0,0 +1,14 @@
+#
+# $$
+#
+[default]
+title= Save Propertiest
+
+[security]
+View=0:Authenticated
+
+[validators]
+
+[actions]
+action.success=traverse_to:string:prefs_qseo_setup_form
+action.failure=traverse_to:string:prefs_qseo_setup_form
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/prefs_qseo_setup_form.cpt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/prefs_qseo_setup_form.cpt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/prefs_qseo_setup_form.cpt	(revision 584)
@@ -0,0 +1,119 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      lang="en-US"
+      metal:use-macro="here/prefs_main_template/macros/master"
+      i18n:domain="qPloneGoogleSitemaps">
+<body>
+    <div metal:fill-slot="prefs_configlet_main"
+        tal:define="errors python:request.get('errors', {})">
+
+                <h1>Search Engine Optimizer configuration</h1>
+                <a href=""
+                class="link-parent"
+                tal:attributes="href string: $portal_url/plone_control_panel"
+                i18n:translate="label_up_to_plone_setup">
+                Up to Plone Setup
+                </a>
+                <div class="documentDescription">
+                   You can select what content types are
+                   qSEOptimizer-enabled, and control if Dublin Core metatags
+                   are exposed in the header of content pages.
+                </div>
+
+                <form name="edit_form"
+                    action="."
+                    method="post"
+                    enctype="multipart/form-data"
+                    tal:attributes="action string:${here_url}/${template/getId}" >
+
+                    <div class="field">
+                        <input type="checkbox"
+                               id="exposeDCMetaTags"
+                               name="exposeDCMetaTags"
+                               tal:define="exposeDCMetaTags portal/portal_properties/site_properties/exposeDCMetaTags"
+                               tal:attributes="checked exposeDCMetaTags"/>
+                        <label for="exposeDCMetaTags">Expose <abbr title="Dublin Core">DC</abbr> meta tags</label>
+                        <div class="formHelp" i18n:translate="description_seo_dc_metatags">
+                           Controls if <abbr title="Dublin Core">DC</abbr> metatags are exposed to page
+                           header. They include DC.description, DC.type,
+                           DC.format, DC.creator and others.
+                        </div>
+                    </div>
+
+                    <div class="field"
+                         tal:define="default_custom_metatags portal/portal_properties/seo_properties/default_custom_metatags|python:[]">
+                        <label for="default_custom_metatags" i18n:translate="label_default_custom_metatags">
+                            Default custom metatags.
+                        </label>
+                        <div class="formHelp" i18n:translate="help_default_custom_metatags">
+                            Fill in custom metatag names (one per line) which will appear on qseo_properties
+                            edit tab.
+                        </div>
+                        <div class="widget">
+                            <textarea cols="60" id="default_custom_metatags" name="default_custom_metatags" rows="15"
+                                      tal:content="python:'\n'.join(default_custom_metatags)"></textarea>
+                        </div>
+                    </div>
+
+                    <div class="field"
+                         tal:define="additionalKeywords portal/portal_properties/seo_properties/additional_keywords">
+                        <label for="additionalKeywords" i18n:translate="label_additional_keywords">
+                            Additional keywords that should be added to the content types.
+                        </label>
+                        <div class="formHelp" i18n:translate="help_additional_keywords">
+                            Use this field when you want that your content types receive
+                            additional keywords from the ones you manually specify. Enter
+                            one keyword per line.
+                        </div>
+                        <div class="widget">
+                            <textarea cols="60" id="additionalKeywords" name="additionalKeywords" rows="15"
+                                      tal:content="python:'\n'.join(additionalKeywords)"></textarea>
+                        </div>
+                    </div>
+
+                    <fieldset class="portalTypes"
+                              tal:define="portal_types    portal/portal_types;
+                                          allTypes        portal_types/listContentTypes;
+                                          typeLists       python:modules['Products.qSEOptimizer.util'].createMultiColumnList(context, allTypes, numCols=3, sort_on='title_or_id')">
+                        <legend i18n:translate="label_content_type_title">Content Types</legend>
+
+                        <div class="formHelp" i18n:translate="description_seo_content_types">
+                            Select content types that will have SEO properties
+                            enabled.
+                        </div>
+                        <div style="float:left; margin-right: 2em;" tal:repeat="sublist typeLists">
+                            <ul class="configlets">
+                                <li tal:repeat="ptypeInfo sublist">
+                                    <tal:type define="type python:portal_types.getTypeInfo(ptypeInfo);
+                                                      name type/getId;">
+
+                                        <input  type="checkbox"
+                                                name="portalTypes:list"
+                                                tal:attributes="id name;
+                                                                value name;
+                                                                checked python:test(filter(lambda x:x.id == 'seo_properties', type.listActions()), True, None)"/>
+                                        <label tal:attributes="for python:type.getId()"
+                                               tal:content="type/Title"/>
+                                    </tal:type>
+                                </li>
+                            </ul>
+                        </div>
+                    </fieldset>
+
+                    <div class="formControls"
+                         tal:define="process_creation request/process_creation|nothing;"
+                         i18n:domain="plone">
+                         <input class="context"
+                             tabindex=""
+                             type="submit"
+                             name="form.button.Save"
+                             value="Save"
+                             i18n:attributes="value"
+                             tal:attributes="tabindex tabindex/next;" />
+                    </div>
+                    <input type="hidden" name="form.submitted" value="1" />
+                </form>
+    </div>
+ </body>
+</html>
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/prefs_qseo_setup_form.cpt.metadata
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/prefs_qseo_setup_form.cpt.metadata	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/prefs_qseo_setup_form.cpt.metadata	(revision 584)
@@ -0,0 +1,14 @@
+#
+# $$
+#
+[default]
+title= Setup actions
+
+[security]
+View=0:Authenticated
+
+[validators]
+
+[actions]
+action.success=traverse_to:string:prefs_qseo_setup
+action.failure=traverse_to:string:prefs_qseo_setup_form
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_CustomMetaTags.py
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_CustomMetaTags.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_CustomMetaTags.py	(revision 584)
@@ -0,0 +1,30 @@
+## Script (Python) "qSEO_CustomMetaTags"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=Return context's properties prefixed with qSEO_custom_
+##
+
+result = []
+added = []
+property_prefix = 'qSEO_custom_'
+for property, value in context.propertyItems():
+    idx = property.find(property_prefix)
+    if idx == 0 and len(property) > len(property_prefix):
+        added.append(property[len(property_prefix):])
+        result.append({'meta_name'    : property[len(property_prefix):],
+                       'meta_content' : value})
+
+from Products.CMFCore.utils import getToolByName
+site_properties = getToolByName(context, 'portal_properties')
+if hasattr(site_properties, 'seo_properties'):
+    names = getattr(site_properties.seo_properties, 'default_custom_metatags', [])
+    for name in names:
+        if name not in added:
+            result.append({'meta_name'    : name,
+                           'meta_content' : ''})
+
+return result
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Description.py
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Description.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Description.py	(revision 584)
@@ -0,0 +1,29 @@
+## Script (Python) "qSEO_Description"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=Generate Description from SEO properties
+##
+
+prop_name = 'qSEO_description'
+
+if context.hasProperty(prop_name):
+    return context.getProperty(prop_name)
+
+accessor = 'Description'
+
+method = getattr(context, accessor, None)
+if not callable(method):
+    # ups
+    return None
+
+# Catch AttributeErrors raised by some AT applications
+try:
+    value = method()
+except AttributeError:
+    value = None
+
+return value
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Distribution.py
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Distribution.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Distribution.py	(revision 584)
@@ -0,0 +1,16 @@
+## Script (Python) "qSEO_Description"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=Generate Description from SEO properties
+##
+
+prop_name = 'qSEO_distribution'
+
+if context.hasProperty(prop_name):
+    return context.getProperty(prop_name)
+
+return 'Global'
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_HTML_Comment.py
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_HTML_Comment.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_HTML_Comment.py	(revision 584)
@@ -0,0 +1,17 @@
+## Script (Python) "qSEO_HTML_Comment"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=Generate Description from SEO properties
+##
+
+prop_name = 'qSEO_html_comment'
+
+if context.hasProperty(prop_name):
+    return context.getProperty(prop_name)
+
+# No comment by default
+return ''
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Keywords.py
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Keywords.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Keywords.py	(revision 584)
@@ -0,0 +1,39 @@
+## Script (Python) "listMetaTags"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=Generate Keywords from SEO properties
+##
+
+prop_name = 'qSEO_keywords'
+add_keywords = 'additional_keywords'
+
+keywords = []
+if context.hasProperty(prop_name):
+    keywords = context.getProperty(prop_name)
+
+pprops = context.portal_properties
+sheet = getattr(pprops, 'seo_properties', None)
+if sheet and sheet.hasProperty(add_keywords):
+    keywords += sheet.getProperty(add_keywords)
+
+if keywords:
+    return keywords
+
+accessor = 'Subject'
+
+method = getattr(context, accessor, None)
+if not callable(method):
+    # ups
+    return None
+
+# Catch AttributeErrors raised by some AT applications
+try:
+    value = method()
+except AttributeError:
+    value = None
+
+return value
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Robots.py
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Robots.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Robots.py	(revision 584)
@@ -0,0 +1,17 @@
+## Script (Python) "qSEO_Description"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=Generate Description from SEO properties
+##
+
+prop_name = 'qSEO_robots'
+
+if context.hasProperty(prop_name):
+    return context.getProperty(prop_name)
+
+# by default make robots crawl website
+return 'ALL'
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Title.py
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Title.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qSEO_Title.py	(revision 584)
@@ -0,0 +1,29 @@
+## Script (Python) "qSEO_Description"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=Generate Description from SEO properties
+##
+
+prop_name = 'qSEO_title'
+
+if context.hasProperty(prop_name):
+    return context.getProperty(prop_name)
+
+accessor = 'Title'
+
+method = getattr(context, accessor, None)
+if not callable(method):
+    # ups
+    return None
+
+# Catch AttributeErrors raised by some AT applications
+try:
+    value = method()
+except AttributeError:
+    value = None
+
+return value
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qseo_properties_edit.cpy
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qseo_properties_edit.cpy	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qseo_properties_edit.cpy	(revision 584)
@@ -0,0 +1,59 @@
+## Controller Python Script "qseo_properties_edit"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind state=state
+##bind subpath=traverse_subpath
+##title=Update SEO Properties
+##parameters=title=None,description=None,keywords=None,html_comment=None,robots=None,distribution=None,title_override=0,description_override=0,keywords_override=0,html_comment_override=0,robots_override=0,distribution_override=0,custommetatags=[]
+
+def setProperty(context, property, value, type='string'):
+    if context.hasProperty(property):
+        context.manage_changeProperties({property: value})
+    else:
+        context.manage_addProperty(property, value, type)
+
+delete_list = []
+
+# update custom meta tags
+property_prefix = 'qSEO_custom_'
+custom_existing = []
+for property, value in context.propertyItems():
+    if property.find(property_prefix) == 0 and len(property) > len(property_prefix):
+        custom_existing.append(property)
+
+custom_updated = []
+for tag in custommetatags:
+    meta_name, meta_content = tag['meta_name'], tag['meta_content']
+    if meta_name and meta_content:
+        setProperty(context, '%s%s' % (property_prefix, meta_name), meta_content)
+        custom_updated.append('%s%s' % (property_prefix, meta_name))
+
+#add not updated custom metatags to delete list
+for tag in custom_existing:
+    if tag not in custom_updated:
+        delete_list.append(tag)
+
+setProperty(context, 'qSEO_title', title)
+setProperty(context, 'qSEO_description', description)
+setProperty(context, 'qSEO_keywords', keywords, 'lines')
+setProperty(context, 'qSEO_html_comment', html_comment)
+setProperty(context, 'qSEO_robots', robots)
+setProperty(context, 'qSEO_distribution', distribution)
+
+if not title_override:        delete_list.append('qSEO_title')
+if not description_override:  delete_list.append('qSEO_description')
+if not keywords_override:     delete_list.append('qSEO_keywords')
+if not html_comment_override: delete_list.append('qSEO_html_comment')
+if not robots_override:       delete_list.append('qSEO_robots')
+if not distribution_override: delete_list.append('qSEO_distribution')
+
+if delete_list: context.manage_delProperties(delete_list)
+
+msg ='Content SEO properties have been saved.'
+try:
+    context.plone_utils.addPortalMessage(msg)
+    return state
+except AttributeError:
+    return state.set(context=context, portal_status_message=msg)
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qseo_properties_edit.cpy.metadata
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qseo_properties_edit.cpy.metadata	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qseo_properties_edit.cpy.metadata	(revision 584)
@@ -0,0 +1,6 @@
+[validators]
+validators = 
+
+[actions]
+action.failure=redirect_to:string:qseo_properties_edit_form
+action.success=redirect_to_action:string:view
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qseo_properties_edit_form.cpt
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qseo_properties_edit_form.cpt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qseo_properties_edit_form.cpt	(revision 584)
@@ -0,0 +1,393 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
+      lang="en"
+      metal:use-macro="here/main_template/macros/master"
+      i18n:domain="qseoptimizer">
+
+  <metal:border fill-slot="top_slot">
+    <tal:border define="dummy python:request.set('enable_border',1)" />
+  </metal:border>
+  <metal:style_head fill-slot="style_slot">
+      <style type="text/css">
+          tr.datagridwidget-empty-row {
+              display:none;
+          }
+      </style>
+  </metal:style_head>
+  <metal:javascript_head fill-slot="javascript_head_slot">
+      <script type="text/javascript"
+              tal:condition="python:exists('portal/statistics.js')"
+              tal:attributes="src string:${here/absolute_url}/statistics.js">
+      </script>
+      <script type="text/javascript"
+              tal:condition="python:exists('portal/checkkeywords.js')"
+              tal:attributes="src string:${here/absolute_url}/checkkeywords.js">
+      </script>
+      <script type="text/javascript"
+              tal:condition="python:exists('portal/custommetatags.js')"
+              tal:attributes="src string:${here/absolute_url}/custommetatags.js">
+      </script>
+  </metal:javascript_head>
+  <body>
+
+    <div metal:fill-slot="main"
+         tal:define="errors options/state/getErrors;">
+
+      <h1 i18n:translate="heading_edit_seo_properties">Edit SEO Properties</h1>
+
+      <p i18n:translate="description_edit_seo_properties">
+        This form allows you to edit html header meta tags that ape produced by 
+        Plone CMS to incrase Search Engine visibility. The values generated by
+        Plone are present as default values in input fields. To override default
+        value, check corresponding checkbox and input value itself in 
+        corresponding field.
+      </p>
+
+
+      <form name="edit_form"
+            action=""
+            method="post"
+            tal:attributes="action string:${here/absolute_url}/${template/getId}">
+
+        <fieldset>
+
+          <legend i18n:domain="plone" i18n:translate="legend_item_properties">
+            Item Properties
+          </legend>
+
+          <div class="field">
+            <label i18n:translate="label_page_title_tag" for="title">
+              Page Title Tag
+            </label>
+
+            <div class="formHelp" i18n:translate="help_seo_title">
+              Text to be present into &lt;TITLE&gt; tag. It is displayed in 
+              browser title bar. Search engines display it as a title of the 
+              document.
+            </div>
+
+            <input type="checkbox"
+                   id="title_override"
+                   name="title_override"
+                   tabindex=""
+                   tal:attributes="checked python:test(here.hasProperty('qSEO_title'), 'on', None);
+                                   tabindex tabindex/next;"
+                   />
+
+            <label i18n:translate="label_override" for="title_override">
+              Override
+            </label>
+
+            <input type="text"
+                   id="seo_title"
+                   name="title"
+                   size="40"
+                   value=""
+                   tabindex=""
+                   tal:attributes="value request/title | here/qSEO_Title;
+                                   tabindex tabindex/next;"
+                   />
+          </div>
+
+
+          <div class="field">
+            <label i18n:translate="label_meta_description_tag" for="description">
+              Meta Description Tag
+            </label>
+
+            <div class="formHelp" i18n:translate="help_seo_description">
+              Description of the document to be displayed right below title
+              in search engine results. It is indexed and considered to be
+              more important then the page content by several SE.
+            </div>
+
+            <input type="checkbox"
+                   id="description_override"
+                   name="description_override"
+                   tabindex=""
+                   tal:attributes="checked python:test(here.hasProperty('qSEO_description'), 'on', None);
+                                   tabindex tabindex/next;"
+                   />
+
+            <label i18n:translate="label_override" for="description_override">
+              Override
+            </label>
+
+            <textarea 
+                      type="text"
+                      id="seo_description"
+                      name="description"
+                      rows="5"
+                      cols="15"
+                      tabindex=""
+                      tal:attributes="tabindex tabindex/next;"
+                      tal:content="request/description | here/qSEO_Description"
+                      ></textarea>
+
+          </div>
+            
+          <div class="field">
+            <label i18n:translate="label_meta_keywords_tag" for="keywords">
+              Meta Keywords Tag
+            </label>
+
+            <div class="formHelp" i18n:translate="help_seo_keywords">
+              Keywords of the page to be indexed with. 
+              <strong>Enter each keyword in separate line, please.</strong>
+              Though the relevance of listing meta keywords is of 
+              questionable value now it is useful to document what
+              meta keywords you are focusing on for this page - for
+              furture reference.
+            </div>
+
+            <input type="checkbox"
+                   id="keywords_override"
+                   name="keywords_override"
+                   tabindex=""
+                   tal:attributes="checked python:test(here.hasProperty('qSEO_keywords'), 'on', None);
+                                   tabindex tabindex/next;"
+                   />
+
+            <label i18n:translate="label_override" for="keywords_override">
+              Override
+            </label>
+
+            <textarea 
+                      id="seo_keywords"
+                      name="keywords:lines"
+                      rows="11"
+                      cols="15"
+                      wrap="off"
+                      tabindex=""
+                      tal:attributes="tabindex tabindex/next;"
+                      tal:define="keywords request/keywords | here/qSEO_Keywords"
+                      tal:content="python:'\n'.join(keywords)"
+                      ></textarea>
+          </div>
+
+          <div class="field">
+            <label i18n:translate="label_html_comment" for="html_commment">
+              HTML Comment
+            </label>
+
+            <div class="formHelp" i18n:translate="help_seo_html_comment">
+              Comments embedded into HTML header.
+            </div>
+
+            <input type="checkbox"
+                   id="html_comment_override"
+                   name="html_comment_override"
+                   tabindex=""
+                   tal:attributes="checked python:test(here.hasProperty('qSEO_html_comment'), 'on', None);
+                                   tabindex tabindex/next;"
+                   />
+
+            <label i18n:translate="label_override" for="html_comment_override">
+              Override
+            </label>
+
+            <textarea 
+                      id="seo_html_comment"
+                      name="html_comment"
+                      rows="5"
+                      cols="15"
+                      tabindex=""
+                      tal:attributes="tabindex tabindex/next;"
+                      tal:content="request/html_comment | here/qSEO_HTML_Comment"
+                      ></textarea>
+          </div>
+
+          <div class="field">
+            <label i18n:translate="label_robot_tag" for="robots">
+              Robot Tag
+            </label>
+
+            <div class="formHelp" i18n:translate="help_seo_robots">
+              Robots meta tag value. Allows or denies indefing, following 
+              links, etc. for spiders indexing your content.
+            </div>
+
+            <input type="checkbox"
+                   id="robots_override"
+                   name="robots_override"
+                   tabindex=""
+                   tal:attributes="checked python:test(here.hasProperty('qSEO_robots'), 'on', None);
+                                   tabindex tabindex/next;"
+                   />
+
+            <label i18n:translate="label_override" for="robots_override">
+              Override
+            </label>
+
+            <input type="text"
+                   id="seo_robots"
+                   name="robots"
+                   size="25"
+                   value=""
+                   tabindex=""
+                   tal:attributes="value request/robots | here/qSEO_Robots;
+                                   tabindex tabindex/next;"
+                   />
+          </div>
+
+          <div class="field">
+            <label i18n:translate="label_distribution_tag" for="distribution">
+              Distribution Tag
+            </label>
+
+            <div class="formHelp" i18n:translate="help_seo_distribution">
+              Distribution meta tag to designate time and area disribution
+              of the document.
+            </div>
+
+            <input type="checkbox"
+                   id="distribution_override"
+                   name="distribution_override"
+                   tabindex=""
+                   tal:attributes="checked python:test(here.hasProperty('qSEO_distribution'), 'on', None);
+                                   tabindex tabindex/next;"
+                   />
+
+            <label i18n:translate="label_override" for="distribution_override">
+              Override
+            </label>
+
+            <input type="text"
+                   id="seo_distribution"
+                   name="distribution"
+                   size="25"
+                   value=""
+                   tabindex=""
+                   tal:attributes="value request/distribution | here/qSEO_Distribution;
+                                   tabindex tabindex/next;"
+                   />
+          </div>
+
+          <div class="field"
+               tal:define="fieldName string:custommetatags;">
+            <label i18n:translate="label_custom_metatags" for="custom_metatags">
+              Custom Meta Tags
+            </label>
+
+            <div class="formHelp" i18n:translate="help_seo_custommetatags">
+              Here you can manage your custom meta tags.
+            </div>
+
+            <table class="custom-metatags-table"
+                   id="datagridwidget-table-custommetatags"
+                   style="width: 100%"
+                   tal:define="columnNames python:['meta name', 'meta content'];
+                               columnIds   python:['meta_name', 'meta_content'];
+                               fielddata context/qSEO_CustomMetaTags|python:[];">
+                <thead>
+                    <tr>
+                        <th class="discreet" style="text-align: left"
+                            tal:repeat="columnName columnNames"
+                            tal:content="columnName"
+                            i18n:translate="">Column name</th>
+                        <th />
+                        <th />
+                    </tr>
+                </thead>
+                <tbody id="datagridwidget-tbody-custommetatags">
+                    <tal:row-loop tal:repeat="row fielddata">
+                        <tr id="datagridwidget-row">
+                            <tal:vars define="fieldId string:${fieldName}_${repeat/row/number}">
+                                <td class="datagridwidget-cell"
+                                    tal:repeat="column columnIds">
+                                    <tal:cell tal:define="cell_value python:row.get(column);">
+                                        <input type="text"
+                                               id="columnId_fieldId"
+                                               value=""
+                                               name="fieldId.columnId:records"
+                                               style="width: 100%;"
+                                               tal:attributes="name string:${fieldName}.${column}:records;
+                                                               id string:${column}_${fieldId};
+                                                               value cell_value"/>
+                                    </tal:cell>
+                                </td>
+                                <td  class="datagridwidget-manipulator">
+                                    <img alt="Delete row"
+                                         onclick="customMetaTagsFunctions.removeFieldRow(this);return false"
+                                         tal:attributes="src string:${portal_url}/delete_icon.gif;" />
+                                </td>
+                                <td class="datagridwidget-hidden-data">
+                                    <input type="hidden"
+                                           tal:attributes="name string:${fieldName}.orderindex_:records;
+                                                           id string:orderindex__${fieldId};
+                                                           value repeat/row/number;" />
+                                </td>
+                            </tal:vars>
+                        </tr>
+                    </tal:row-loop>
+                    <tr id="datagridwidget-empty-row"
+                        class="datagridwidget-empty-row"
+                        tal:define="fieldId string:${fieldName}_new">
+                        <td class="datagridwidget-cell"
+                            tal:repeat="column columnIds">
+                            <span>
+                                <input type="text"
+                                       id="columnId_fieldName_new"
+                                       value=""
+                                       name="fieldName.columnId:records"
+                                       style="width: 100%;"
+                                       tal:attributes="id string:${column}_${fieldName}_new;
+                                                       name string:${fieldName}.${column}:records;" />
+                           </span>
+                        </td>
+                        <td  class="datagridwidget-manipulator">
+                            <img alt="Delete row"
+                                 onclick="customMetaTagsFunctions.removeFieldRow(this);return false"
+                                 tal:attributes="src string:${portal_url}/delete_icon.gif;" />
+                        </td>
+                        <td class="datagridwidget-hidden-data">
+                            <input type="hidden"
+                                   value="template_row_marker"
+                                   tal:attributes="name string:${fieldName}.orderindex_:records;
+                                                  id string:orderindex__${fieldId};" />
+                        </td>
+                    </tr>
+                </tbody>
+            </table>
+
+            <input type="button"
+                   class="datagridwidget-add-button"
+                   id="datagridwidget-add-button"
+                   value="Add new row"
+                   tal:attributes="onclick string:customMetaTagsFunctions.addRow('${fieldName}')" />
+
+          </div>
+
+          <div class="formControls"
+               tal:define="process_creation request/process_creation|nothing;"
+               i18n:domain="plone">
+            <input class="context"
+                   tabindex=""
+                   type="submit"
+                   name="form.button.Save"
+                   value="Save"
+                   i18n:attributes="value"
+                   tal:attributes="tabindex tabindex/next;" />
+
+            <input class="standalone"
+                   tabindex=""
+                   type="submit"
+                   name="form.button.Cancel"
+                   value="Cancel"
+                   i18n:attributes="value"
+                   tal:attributes="tabindex tabindex/next;" />
+          </div>
+        </fieldset>
+
+        <input type="hidden" name="form.submitted" value="1" />
+
+      </form>
+
+      <div metal:use-macro="here/document_byline/macros/byline">
+        Get the byline - contains details about author and modification date.
+      </div>
+
+    </div>
+
+  </body>
+</html>
+
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qseo_properties_edit_form.cpt.metadata
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qseo_properties_edit_form.cpt.metadata	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/qseo_properties_edit_form.cpt.metadata	(revision 584)
@@ -0,0 +1,14 @@
+[default]
+title=Edit SEO Properties
+
+[security]
+View=0:Authenticated
+
+[validators]
+validators..Save=
+validators..Cancel=
+
+[actions]
+action.success..Save=traverse_to:string:qseo_properties_edit
+action.success..Cancel=redirect_to_action:string:view
+action.failure=traverse_to:string:qseo_properties_edit_form
Index: qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/statistics.js.dtml
===================================================================
--- qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/statistics.js.dtml	(revision 584)
+++ qSEOptimizer/tags/1.6.1/skins/qSEOptimizer/statistics.js.dtml	(revision 584)
@@ -0,0 +1,105 @@
+/******************************************************************
+<dtml-with portal_properties>
+
+Word Wrapping Rules:
+
+    1. multiple spaces == one space 1
+    2. word can include "-" and "'" // [\-\']
+    3. token that not contain a-zA-Z0-9 symbols isn't a word
+    4. non-word characters inside (or on the edge of) word divide it in smaller words
+
+HTML code for text statistics: // for else fields than title in html code title will change accordingly to the field's id
+
+  <div id="title-statistics" class="statistics">
+      <span class="total-words">Total Words: <span class="value">5</span></span>
+      <span class="stop-words">Stop Words: <span class="value">2</span></span>
+      <span class="used-words">Used Words: <span class="value">3</span></span>
+      <span class="length-words">Length: <span class="value">24</span></span>
+  </div>
+
+*******************************************************************/
+
+<dtml-try>
+var stop_words = <dtml-var expr="list(seo_properties.stop_words)">;
+<dtml-except>
+var stop_words = [];
+</dtml-try>
+
+<dtml-try>
+var ids = <dtml-var expr="list(seo_properties.fields)">;
+<dtml-except>
+var ids = [];
+</dtml-try>
+
+var template = '<span class="total-words">Total Words: <span class="value">total_row</span></span> <span class="stop-words">Stop Words: <span class="value">stop_row</span></span> <span class="used-words">Used Words: <span class="value">used_row</span></span> <span class="length-words">Length: <span class="value">length_row</span></span>';
+
+var stop_dict = {};
+
+for (var j=0; word=stop_words[j]; j++) {stop_dict[word] = 1;};
+
+function countWords(data) {
+    var total = 0;
+    var stop  = 0;
+    var used  = 0;
+    var len = data.length || 0;
+    if (len != 0) {
+        data = data // replace all non-word character with space
+                   .replace(/[^a-zA-Z0-9\-\'\u2019\"\`]+/g, ' ')
+                    // replace "-" and "'" symbols if it create groups event inside of token
+                   .replace(/[\-\'\u2019\"\`]{2,}/g, ' ')
+                    // replace all non-word characters and "-", "'" if it stay at word edge
+                   .replace(/(?:^|\s+)[^a-zA-Z0-9]+|[^a-zA-Z0-9]+(?:\s+|$)/g, ' ')
+                   // strip whitespaces
+                   .replace(/^\s*(.*?)\s*$/, '$1');
+        var data_list = data.split(/[^a-zA-Z0-9\-\'\u2019\"\`]+/);
+        for (var i=0; word=data_list[i]; i++) {
+            stop += stop_dict[word.toLowerCase()] ? 1:0;
+        };
+        total = data_list.length;
+        used = total - stop;
+    };
+    return {'total': total, 'stop': stop, 'used': used, 'length': len};
+};
+
+function getHTML(source) {
+    var stats = countWords(source);
+    var html = template;
+    for (var p in stats) {
+        html = html.replace(p+'_row', stats[p]);
+    };
+    return html;
+};
+
+function listenField(event) {
+    var event = event ? event:window.event;
+    var target = null;
+    if (event.target) {
+        target = event.target;
+    } else if (event.srcElement) {
+        target = event.srcElement;
+    };
+    if (!target) {return false;};
+    var update = document.getElementById(target.id+'-statistics');
+    if (update) {
+        update.innerHTML = getHTML(target.value||'');
+    } else {
+        window.status = "Couldn\'t find element with id = \'"+target.id+"-statistics\' on this page";
+    };
+};
+
+function loadStatistics(event){
+    for (var i=0; id=ids[i]; i++) {
+        var el = document.getElementById(id);
+        if (el && (typeof(el.value) != 'undefined')) {
+            var div = document.createElement('DIV');
+            div.id = id+'-statistics';
+            div.className = 'statistics';
+            div.innerHTML = getHTML(el.value||'');
+            el.parentNode.insertBefore(div, el);
+            registerEventListener(el, 'keyup', listenField);
+        };
+    };
+};
+
+registerPloneFunction(loadStatistics);
+/*</dtml-with> Register onload function */
Index: qSEOptimizer/tags/1.6.1/tests/__init__.py
===================================================================
--- qSEOptimizer/tags/1.6.1/tests/__init__.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/tests/__init__.py	(revision 584)
@@ -0,0 +1,1 @@
+# This file intentionally left blank
Index: qSEOptimizer/tags/1.6.1/tests/framework.py
===================================================================
--- qSEOptimizer/tags/1.6.1/tests/framework.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/tests/framework.py	(revision 584)
@@ -0,0 +1,107 @@
+##############################################################################
+#
+# ZopeTestCase 
+#
+# COPY THIS FILE TO YOUR 'tests' DIRECTORY.
+#
+# This version of framework.py will use the SOFTWARE_HOME
+# environment variable to locate Zope and the Testing package.
+#
+# If the tests are run in an INSTANCE_HOME installation of Zope,
+# Products.__path__ and sys.path with be adjusted to include the
+# instance's Products and lib/python directories respectively.
+#
+# If you explicitly set INSTANCE_HOME prior to running the tests,
+# auto-detection is disabled and the specified path will be used 
+# instead.
+#
+# If the 'tests' directory contains a custom_zodb.py file, INSTANCE_HOME
+# will be adjusted to use it.
+#
+# If you set the ZEO_INSTANCE_HOME environment variable a ZEO setup 
+# is assumed, and you can attach to a running ZEO server (via the 
+# instance's custom_zodb.py).
+#
+##############################################################################
+#
+# The following code should be at the top of every test module:
+#
+# import os, sys
+# if __name__ == '__main__':
+#     execfile(os.path.join(sys.path[0], 'framework.py'))
+#
+# ...and the following at the bottom:
+#
+# if __name__ == '__main__':
+#     framework()
+#
+##############################################################################
+
+__version__ = '0.2.3'
+
+# Save start state
+#
+__SOFTWARE_HOME = os.environ.get('SOFTWARE_HOME', '')
+__INSTANCE_HOME = os.environ.get('INSTANCE_HOME', '')
+
+if __SOFTWARE_HOME.endswith(os.sep):
+    __SOFTWARE_HOME = os.path.dirname(__SOFTWARE_HOME)
+
+if __INSTANCE_HOME.endswith(os.sep):
+    __INSTANCE_HOME = os.path.dirname(__INSTANCE_HOME)
+
+# Find and import the Testing package
+#
+if not sys.modules.has_key('Testing'):
+    p0 = sys.path[0]
+    if p0 and __name__ == '__main__':
+        os.chdir(p0)
+        p0 = ''
+    s = __SOFTWARE_HOME
+    p = d = s and s or os.getcwd()
+    while d:
+        if os.path.isdir(os.path.join(p, 'Testing')):
+            zope_home = os.path.dirname(os.path.dirname(p))
+            sys.path[:1] = [p0, p, zope_home]
+            break
+        p, d = s and ('','') or os.path.split(p)
+    else:
+        print 'Unable to locate Testing package.',
+        print 'You might need to set SOFTWARE_HOME.'
+        sys.exit(1)
+
+import Testing, unittest
+execfile(os.path.join(os.path.dirname(Testing.__file__), 'common.py'))
+
+# Include ZopeTestCase support
+#
+if 1:   # Create a new scope
+
+    p = os.path.join(os.path.dirname(Testing.__file__), 'ZopeTestCase')
+
+    if not os.path.isdir(p):
+        print 'Unable to locate ZopeTestCase package.',
+        print 'You might need to install ZopeTestCase.'
+        sys.exit(1)
+
+    ztc_common = 'ztc_common.py'
+    ztc_common_global = os.path.join(p, ztc_common)
+
+    f = 0
+    if os.path.exists(ztc_common_global):
+        execfile(ztc_common_global)
+        f = 1
+    if os.path.exists(ztc_common):
+        execfile(ztc_common)
+        f = 1
+
+    if not f:
+        print 'Unable to locate %s.' % ztc_common
+        sys.exit(1)
+
+# Debug
+#
+print 'SOFTWARE_HOME: %s' % os.environ.get('SOFTWARE_HOME', 'Not set')
+print 'INSTANCE_HOME: %s' % os.environ.get('INSTANCE_HOME', 'Not set')
+sys.stdout.flush()
+
Index: qSEOptimizer/tags/1.6.1/tests/runalltests.py
===================================================================
--- qSEOptimizer/tags/1.6.1/tests/runalltests.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/tests/runalltests.py	(revision 584)
@@ -0,0 +1,29 @@
+#
+# Runs all tests in the current directory
+#
+# Execute like:
+#   python runalltests.py
+#
+# Alternatively use the testrunner: 
+#   python /path/to/Zope/utilities/testrunner.py -qa
+#
+
+import os, sys
+if __name__ == '__main__':
+    execfile(os.path.join(sys.path[0], 'framework.py')) 
+
+import unittest
+TestRunner = unittest.TextTestRunner
+suite = unittest.TestSuite()
+
+tests = os.listdir(os.curdir)
+tests = [n[:-3] for n in tests if n.startswith('test') and n.endswith('.py')]
+
+for test in tests:
+    m = __import__(test)
+    if hasattr(m, 'test_suite'):
+        suite.addTest(m.test_suite())
+
+if __name__ == '__main__':
+    TestRunner().run(suite)
+
Index: qSEOptimizer/tags/1.6.1/tests/testQSEOptimizer.py
===================================================================
--- qSEOptimizer/tags/1.6.1/tests/testQSEOptimizer.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/tests/testQSEOptimizer.py	(revision 584)
@@ -0,0 +1,364 @@
+#
+# Test installation script
+#
+
+import os, sys, string
+if __name__ == '__main__':
+    execfile(os.path.join(sys.path[0], 'framework.py'))
+
+from Products.PloneTestCase import PloneTestCase
+from Products.CMFCore.utils import getToolByName
+from Products.CMFQuickInstallerTool.InstalledProduct import InstalledProduct
+
+try:
+    from Products.CMFCore.CMFCorePermissions import ManagePortal
+except:
+    from Products.CMFCore.permissions import ManagePortal
+
+try:
+    from Products.CMFPlone.migrations import v2_5
+    HAS_PLONE_2_5 = True
+    del v2_5
+except ImportError:
+    HAS_PLONE_2_5 = False
+
+from AccessControl.SecurityManagement import newSecurityManager, noSecurityManager
+import re
+
+from Products.qSEOptimizer.config import *
+
+props = {'stop_words':STOP_WORDS, 'fields':FIELDS, 'additional_keywords': []}
+
+custom_metatags = [{'meta_name'    : 'metatag1',
+                    'meta_content' : 'metatag1value'},
+                   {'meta_name'    : 'metatag2',
+                    'meta_content' : 'metatag2value'}]
+
+configlets = ({'id':'qSEOptimizer',
+    'name':'Search Engine Optimizer',
+    'action':'string:${portal_url}/prefs_qseo_setup_form',
+    'condition':'',
+    'category':'Products',
+    'visible':1,
+    'appId':'qSEOptimizer',
+    'permission':ManagePortal},)
+
+PRODUCT = 'qSEOptimizer'
+qSEO_CONTENT = ['File','Document','News Item','BlogEntry']
+qSEO_FOLDER  = []
+qSEO_TYPES   = qSEO_CONTENT + qSEO_FOLDER
+
+PloneTestCase.installProduct('Five')
+PloneTestCase.installProduct(PRODUCT)
+PloneTestCase.setupPloneSite()
+
+class TestBeforeInstall(PloneTestCase.FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.basic_auth = 'mgr:mgrpw'
+        self.portal_path = '/%s' % self.portal.absolute_url(1)
+
+    def testAccessPortalRootAnonymous(self):
+        response = self.publish(self.portal_path)
+        self.assertEqual(response.getStatus(), 200)
+
+    def testAccessPortalRootAuthenticated(self):
+        response = self.publish(self.portal_path, self.basic_auth)
+        self.assertEqual(response.getStatus(), 200)
+
+
+class TestInstallation(PloneTestCase.PloneTestCase):
+
+    def afterSetUp(self):
+        self.properties = getToolByName(self.portal, 'portal_properties')
+        self.qi = self.portal.portal_quickinstaller
+        self.qi.installProduct(PRODUCT)
+
+    def testAddingPropertySheet(self):
+        """ Test adding property sheet to portal_properties tool """
+        self.failUnless(hasattr(self.properties.aq_base, PROPERTY_SHEET))
+
+    def testAddingPropertyFields(self):
+        """ Test adding property field to portal_properties.maps_properties sheet """
+        map_sheet = self.properties[PROPERTY_SHEET]
+        for key, value in props.items():
+            self.failUnless(map_sheet.hasProperty(key) and list(map_sheet.getProperty(key)) == value)
+
+    def test_configlet_install(self):
+        configTool = getToolByName(self.portal, 'portal_controlpanel', None)
+        self.assert_(PRODUCT in [a.getId() for a in configTool.listActions()], 'Configlet not found')
+
+    def test_actions_install(self):
+        portal_types = getToolByName(self.portal, 'portal_types')
+        for ptype in portal_types.objectValues():
+            try:
+                #for Plone-2.5 and higher
+                acts = filter(lambda x: x.id == 'seo_properties', ptype.listActions())
+                action = acts and acts[0] or None
+            except AttributeError:
+                action = ptype.getActionById('seo_properties', default=None )
+
+            if ptype.getId() in qSEO_TYPES:
+                self.assert_(action, 'Action for %s not found' % ptype.getId())
+            else:
+                self.assert_(not action, 'Action found for %s' % ptype.getId())
+
+    def test_skins_install(self):
+        skinstool=getToolByName(self.portal, 'portal_skins')
+
+        for skin in skinstool.getSkinSelections():
+            path = skinstool.getSkinPath(skin)
+            path = map( string.strip, string.split( path,',' ) )
+            self.assert_(PRODUCT in path, 'qSEOptimizer layer not found in %s' %skin)
+
+    def test_versionedskin_install(self):
+        skinstool=getToolByName(self.portal, 'portal_skins')
+        mtool = getToolByName(self.portal, 'portal_migration')
+        plone_version = mtool.getFileSystemVersion()
+        if plone_version < "3":
+            for skin in skinstool.getSkinSelections():
+                path = skinstool.getSkinPath(skin)
+                path = map( string.strip, string.split( path,',' ) )
+                self.assert_(PRODUCT+'/%s' % plone_version in path, 'qSEOptimizer versioned layer not found in %s' %skin)
+
+    def test_actions_uninstall(self):
+        self.qi.uninstallProducts([PRODUCT])
+        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'qSEOptimizer is already installed')
+        portal_types = getToolByName(self.portal, 'portal_types')
+        for ptype in portal_types.objectValues():
+            try:
+                #for Plone-2.5 and higher
+                acts = filter(lambda x: x.id == 'seo_properties', ptype.listActions())
+                action = acts and acts[0] or None
+            except AttributeError:
+                action = ptype.getActionById('seo_properties', default=None )
+
+            self.assert_(not action, 'Action for %s found after uninstallation' % ptype.getId())
+
+    def test_skins_uninstall(self):
+        self.qi.uninstallProducts([PRODUCT])
+        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'qSEOptimizer is already installed')
+        skinstool=getToolByName(self.portal, 'portal_skins')
+
+        for skin in skinstool.getSkinSelections():
+            path = skinstool.getSkinPath(skin)
+            path = map( string.strip, string.split( path,',' ) )
+            self.assert_(not PRODUCT in path, 'qSEOptimizer layer found in %s after uninstallation' %skin)
+
+    def test_versionedskin_uninstall(self):
+        self.qi.uninstallProducts([PRODUCT])
+        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'qSEOptimizer is already installed')
+        skinstool=getToolByName(self.portal, 'portal_skins')
+        mtool = getToolByName(self.portal, 'portal_migration')
+        plone_version = mtool.getFileSystemVersion()
+
+        for skin in skinstool.getSkinSelections():
+            path = skinstool.getSkinPath(skin)
+            path = map( string.strip, string.split( path,',' ) )
+            self.assert_(not PRODUCT+'/%s' % plone_version in path, 'qSEOptimizer versioned layer found in %s after uninstallation' %skin)
+
+    def test_configlet_uninstall(self):
+        self.qi.uninstallProducts([PRODUCT])
+        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'qSEOptimizer is already installed')
+
+        configTool = getToolByName(self.portal, 'portal_controlpanel', None)
+        self.assert_(not PRODUCT in [a.getId() for a in configTool.listActions()], 'Configlet found after uninstallation')
+
+
+class TestResponse(PloneTestCase.FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.qi = self.portal.portal_quickinstaller
+        self.qi.installProduct(PRODUCT)
+        self.portal.changeSkin('Plone Default')
+
+        self.basic_auth = 'mgr:mgrpw'
+        self.loginAsPortalOwner()
+
+        '''Preparation for functional testing'''
+        my_doc = self.portal.invokeFactory('Document', id='my_doc')
+        my_doc = self.portal['my_doc']
+
+        my_doc.qseo_properties_edit(title='hello world', description='it is description',
+                                    keywords='my1|key2', html_comment='no comments',
+                                    robots='ALL', distribution='Global', title_override=1,
+                                    description_override=1, keywords_override=1,
+                                    html_comment_override=1, robots_override=1,
+                                    distribution_override=1, custommetatags=custom_metatags)
+
+        wf_tool = self.portal.portal_workflow
+        wf_tool.doActionFor(my_doc, 'publish')
+
+        abs_path = "/%s" % my_doc.absolute_url(1)
+        self.html = self.publish(abs_path, self.basic_auth).getBody()
+
+    def testTitle(self):
+        m = re.match('.*<title>\\s*hello world\\s*</title>', self.html, re.S|re.M)
+        self.assert_(m, 'Title not set in')
+
+    def testDescription(self):
+        m = re.match('.*<meta name="description" content="it is description" />', self.html, re.S|re.M)
+        self.assert_(m, 'Description not set in')
+
+    def testKeywords(self):
+        m = re.match('.*<meta name="keywords" content="my1|key2" />', self.html, re.S|re.M)
+        self.assert_(m, 'Keywords not set in')
+
+    def testRobots(self):
+        m = re.match('.*<meta name="robots" content="ALL" />', self.html, re.S|re.M)
+        self.assert_(m, 'Robots not set in')
+
+    def testDistribution(self):
+        m = re.match('.*<meta name="distribution" content="Global" />', self.html, re.S|re.M)
+        self.assert_(m, 'Distribution not set in')
+
+    def testHTMLComments(self):
+        m = re.match('.*<!--\\s*no comments\\s*-->', self.html, re.S|re.M)
+        self.assert_(m, 'Comments not set in')
+
+    def testTagsOrder(self):
+        m = re.search('name="description".+name="keywords"', self.html, re.S|re.M)
+        self.assert_(m, "Meta tags order not supported.")
+
+    def testCustomMetaTags(self):
+        for tag in custom_metatags:
+            m = re.search('<meta name="%(meta_name)s" content="%(meta_content)s" />' % tag, self.html, re.S|re.M)
+            self.assert_(m, "Custom meta tag %s not applied." % tag['meta_name'])
+
+class TestAdditionalKeywords(PloneTestCase.FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.qi = self.portal.portal_quickinstaller
+        self.qi.installProduct(PRODUCT)
+        self.sp = self.portal.portal_properties.seo_properties
+        self.pu = self.portal.plone_utils
+        self.portal.changeSkin('Plone Default')
+
+        self.basic_auth = 'portal_manager:secret'
+        uf = self.app.acl_users
+        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
+        user = uf.getUserById('portal_manager')
+        if not hasattr(user, 'aq_base'):
+            user = user.__of__(uf)
+        newSecurityManager(None, user)
+
+        '''Preparation for functional testing'''
+        self.my_doc = self.portal.invokeFactory('Document', id='my_doc')
+        self.my_doc = self.portal['my_doc']
+
+    def test_additional_keywords_in_configlet(self):
+        path = self.portal.id+'/prefs_qseo_setup?additionalKeywords=foo\nbar'
+        self.publish(path, self.basic_auth)
+        self.assertEqual(self.sp.additional_keywords, ('foo', 'bar'))
+        self.publish(self.portal.id+'/prefs_qseo_setup', self.basic_auth)
+        self.assertEqual(self.sp.additional_keywords, ())
+
+    def test_listMetaTags_empty(self):
+        metatags = self.pu.listMetaTags(self.my_doc)
+        self.assert_('keywords' not in metatags)
+
+    def test_listMetaTags_one(self):        
+        self.my_doc.manage_addProperty('qSEO_keywords', ('foo',), 'lines')
+        metatags = self.pu.listMetaTags(self.my_doc)
+        keywords = metatags.get('keywords')
+        self.assertEqual(keywords, 'foo')
+
+    def test_listMetaTags_two(self):        
+        self.my_doc.manage_addProperty('qSEO_keywords', ('foo', 'bar'), 'lines')
+        metatags = self.pu.listMetaTags(self.my_doc)
+        keywords = metatags.get('keywords')
+        self.assertEqual(keywords, 'foo, bar')
+
+    def test_additional_keywords_in_listMetaTags_empty(self):        
+        self.sp.additional_keywords = ('foo',)
+        metatags = self.pu.listMetaTags(self.my_doc)
+        self.assert_('keywords' not in metatags)
+
+    def test_additional_keywords_in_listMetaTags_one(self):
+        self.my_doc.setText('<p>foo</p>')
+        self.sp.additional_keywords = ('foo',)
+        metatags = self.pu.listMetaTags(self.my_doc)
+        keywords = metatags.get('keywords')
+        self.assertEqual(keywords, 'foo')
+
+    def test_additional_keywords_in_listMetaTags_two(self):
+        self.my_doc.setText('<p>foo bar</p>')
+        self.sp.additional_keywords = ('foo', 'bar')
+        metatags = self.pu.listMetaTags(self.my_doc)
+        keywords = metatags.get('keywords')
+        self.assertEqual(keywords, 'foo, bar')
+
+    def test_additional_keywords_in_listMetaTags_merge(self):
+        self.my_doc.setText('<p>foo bar</p>')
+        self.sp.additional_keywords = ('foo', 'bar')
+        self.my_doc.manage_addProperty('qSEO_keywords', ('baz',), 'lines')
+        metatags = self.pu.listMetaTags(self.my_doc)
+        keywords = metatags.get('keywords')
+        self.assertEqual(keywords, 'baz, foo, bar')
+
+
+class TestExposeDCMetaTags(PloneTestCase.FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.qi = self.portal.portal_quickinstaller
+        self.sp = self.portal.portal_properties.site_properties
+        self.qi.installProduct(PRODUCT)
+        self.portal.changeSkin('Plone Default')
+
+        self.basic_auth = 'portal_manager:secret'
+        uf = self.app.acl_users
+        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
+        user = uf.getUserById('portal_manager')
+        if not hasattr(user, 'aq_base'):
+            user = user.__of__(uf)
+        newSecurityManager(None, user)
+
+        '''Preparation for functional testing'''
+        self.my_doc = self.portal.invokeFactory('Document', id='my_doc')
+        self.my_doc = self.portal['my_doc']
+
+    def test_exposeDCMetaTags_in_configletOn(self):
+        path = self.portal.id+'/prefs_qseo_setup?exposeDCMetaTags=on'
+        self.publish(path, self.basic_auth)
+        self.assert_(self.sp.exposeDCMetaTags)
+
+    def test_exposeDCMetaTags_in_configletOff(self):
+        self.publish(self.portal.id+'/prefs_qseo_setup', self.basic_auth)
+        self.assert_(not self.sp.exposeDCMetaTags)
+
+    def test_exposeDCMetaTagsPropertyOff(self):
+        self.sp.manage_changeProperties(exposeDCMetaTags = False)
+
+        self.my_doc.qseo_properties_edit()
+        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
+
+        m = re.match('.*<meta content=".*?" name="DC.format" />', self.html, re.S|re.M) or re.match('.*<meta content=".*?" name="DC.distribution" />', self.html, re.S|re.M)
+        self.assert_(not m, 'DC meta tags avaliable when exposeDCMetaTags=False')
+
+    def test_exposeDCMetaTagsPropertyOn(self):
+        self.sp.manage_changeProperties(exposeDCMetaTags = True)
+
+        self.my_doc.qseo_properties_edit()
+        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
+
+        m = re.match('.*<meta name="DC.format" content=".*?" />', self.html, re.S|re.M) and re.match('.*<meta name="DC.distribution" content=".*?" />', self.html, re.S|re.M)
+
+        self.assert_(m, 'DC meta tags not avaliable when createManager=True')
+
+
+TESTS = [TestInstallation, TestResponse, TestExposeDCMetaTags]
+
+def test_suite():
+    from unittest import TestSuite, makeSuite
+    suite = TestSuite()
+    for suite_class in TESTS:
+        suite.addTest(makeSuite(suite_class))
+
+    if HAS_PLONE_2_5:
+        suite.addTest(makeSuite(TestBeforeInstall))
+        suite.addTest(makeSuite(TestAdditionalKeywords))
+
+    return suite
+
+if __name__ == '__main__':
+    framework()
Index: qSEOptimizer/tags/1.6.1/util.py
===================================================================
--- qSEOptimizer/tags/1.6.1/util.py	(revision 584)
+++ qSEOptimizer/tags/1.6.1/util.py	(revision 584)
@@ -0,0 +1,25 @@
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+
+def createMultiColumnList(self,slist, numCols, sort_on='title_or_id'):
+    try:
+        mcl = self.createMultiColumnList(slist, numCols, sort_on=sort_on)
+        return mcl
+    except AttributeError:
+        return [slist]
+
+class SortedDict(dict):
+    security = ClassSecurityInfo()
+    security.declarePublic('items')    
+    def items(self):
+        primary_metatags = ['description', 'keywords']
+        lst = [(name,self[name]) for name in primary_metatags                    \
+                                                 if name in self.keys()] +       \
+              [(name, self[name]) for name in self.keys()                        \
+                                                 if name not in primary_metatags]
+        return lst
+
+try:
+    InitializeClass(SortedDict)
+except:
+    pass
Index: qSEOptimizer/tags/1.6.1/version.txt
===================================================================
--- qSEOptimizer/tags/1.6.1/version.txt	(revision 584)
+++ qSEOptimizer/tags/1.6.1/version.txt	(revision 584)
@@ -0,0 +1,1 @@
+1.6.1
