Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/__init__.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/__init__.py	(revision 1312)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/__init__.py	(revision 1313)
@@ -1,8 +1,12 @@
 from AccessControl import allow_module
-from util import SortedDict
+from zope.component import queryMultiAdapter
+
 from Acquisition import aq_inner
 from DateTime import DateTime
 
 from Products.CMFCore.utils import getToolByName
+
+from quintagroup.seoptimizer.interfaces import IKeywords, IMappingMetaTags
+from quintagroup.seoptimizer.util import SortedDict
 
 allow_module('quintagroup.seoptimizer.util')
@@ -18,5 +22,4 @@
 
 
-
 if _present:
     old_lmt = PloneTool.listMetaTags
@@ -25,17 +28,40 @@
         """Lists meta tags helper.
 
-        Creates a mapping of meta tags -> values for the listMetaTags script.
+        Creates a mapping of meta tags.
         """
-        result = {}
+
+        from quintagroup.seoptimizer.browser.interfaces import IPloneSEOLayer
+        if not IPloneSEOLayer.providedBy(self.REQUEST):
+            return old_lmt(getToolByName(self, 'plone_utils'), context)
+
+        result = SortedDict()
         site_props = getToolByName(self, 'portal_properties').site_properties
         use_all = site_props.getProperty('exposeDCMetaTags', None)
 
+        seo_context = queryMultiAdapter((context, self.REQUEST), name='seo_context')
+        adapter = IMappingMetaTags(context, None)
+        mapping_metadata = adapter and adapter.getMappingMetaTags() or SortedDict()
+
         if not use_all:
-            metadata_names = {'Description': METADATA_DCNAME['Description']}
+            metadata_names = mapping_metadata.has_key('DC.description') and {'DC.description': mapping_metadata['DC.description']} or SortedDict()
+            if mapping_metadata.has_key('description'):
+                metadata_names['description'] = mapping_metadata['description']
         else:
-            metadata_names = METADATA_DCNAME
+            metadata_names = mapping_metadata
 
-        for accessor, key in metadata_names.items():
-            method = getattr(aq_inner(context).aq_explicit, accessor, None)
+        for key, accessor in metadata_names.items():
+            if accessor == 'seo_keywords':
+                # Set the additional matching keywords, if any
+                adapter = IKeywords(context, None)
+                if adapter is not None:
+                    keywords = adapter.listKeywords()
+                    if keywords:
+                        result['keywords'] = keywords
+                continue
+
+            method = getattr(seo_context, accessor, None)
+            if method is None:
+                method = getattr(aq_inner(context).aq_explicit, accessor, None)
+
             if not callable(method):
                 continue
@@ -57,12 +83,11 @@
                 value = ', '.join(value)
 
-#          Exclusion meta tag description and keywords
-#            # Special cases
-#            if accessor == 'Description':
-#                result['description'] = value
-#            elif accessor == 'Subject':
-#                result['keywords'] = value
+            # Special cases
+            if accessor == 'Description' and not (result.has_key('description') or metadata_names.has_key('description')):
+                result['description'] = value
+            elif accessor == 'Subject' and not (result.has_key('keywords') or metadata_names.has_key('keywords')):
+                result['keywords'] = value
 
-            if use_all:
+            if accessor not in ('Description', 'Subject'):
                 result[key] = value
 
@@ -104,4 +129,10 @@
                 result['DC.date.valid_range'] = '%s - %s' % (eff_str, exp_str)
 
+        # add custom meta tags (added from qseo tab by user) for given context and default from configlet
+        custom_meta_tags = seo_context and seo_context.seo_customMetaTags() or []
+        for tag in custom_meta_tags:
+            if tag['meta_content']:
+                result[tag['meta_name']] = tag['meta_content']
+
         return result
 
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/adapters.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/adapters.py	(revision 1312)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/adapters.py	(revision 1313)
@@ -3,5 +3,7 @@
 from zope.component import queryMultiAdapter
 from Products.CMFCore.utils import getToolByName
-from quintagroup.seoptimizer.interfaces import IKeywords
+
+from quintagroup.seoptimizer.interfaces import IKeywords, IMappingMetaTags
+from quintagroup.seoptimizer.util import SortedDict
 
 
@@ -14,5 +16,5 @@
     def listKeywords(self):
         portal_props = getToolByName(self.context, 'portal_properties')
-        seo_props = getToolByName(portal_props, 'seo_properties')
+        seo_props = getToolByName(portal_props, 'seo_properties', None)
 
         # now set type is not using because of it unordered behaviour
@@ -24,17 +26,36 @@
         request = self.context.REQUEST
         seo_context = queryMultiAdapter((self.context, request), name='seo_context')
-        keywords = list(seo_context.seo_keywords())
-        lower_keywords = map(lambda x: x.lower(), keywords)
-        additional = seo_props.additional_keywords
+        if seo_context:
+            keywords = list(seo_context.seo_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)
+            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)
+            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)
+        return ''
 
-        return ', '.join(keywords)
+
+class MappingMetaTags(object):
+    implements(IMappingMetaTags)
+
+    def __init__(self, context):
+        self.context = context
+        self.portal_props = getToolByName(self.context, 'portal_properties')
+        self.seo_props = getToolByName(self.portal_props, 'seo_properties', None)
+
+    def getMappingMetaTags(self):
+        metadata_name = SortedDict()
+        if self.seo_props:
+            pmn = self.seo_props.getProperty('metatags_order')
+            pmn = pmn and pmn or ''
+            for mt in [mt.split(' ') for mt in pmn if len(mt.split(' '))==2]:
+                metadata_name[mt[0]] = mt[1]
+        return metadata_name
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/configure.zcml
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/configure.zcml	(revision 1312)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/configure.zcml	(revision 1313)
@@ -8,36 +8,4 @@
         manager="plone.app.layout.viewlets.interfaces.IHtmlHead"
         class=".viewlets.TitleCommentViewlet"
-        layer=".interfaces.IPloneSEOLayer"
-        permission="zope2.View"
-        />
-       
-    <!--browser:viewlet
-        name="quintagroup.seoptimizer.http_equiv"
-        manager="plone.app.layout.viewlets.interfaces.IHtmlHead"
-        class=".viewlets.HTTPEquiv"
-        layer=".interfaces.IPloneSEOLayer"
-        permission="zope2.View"
-        /-->
-
-    <!--browser:viewlet
-        name="quintagroup.seoptimizer.base"
-        manager="plone.app.layout.viewlets.interfaces.IHtmlHead"
-        class=".viewlets.BaseUrlViewlet"
-        layer=".interfaces.IPloneSEOLayer"
-        permission="zope2.View"
-        /-->
-
-    <!--browser:viewlet
-        name="quintagroup.seoptimizer.generator"
-        manager="plone.app.layout.viewlets.interfaces.IHtmlHead"
-        template="templates/generator.pt"
-        layer=".interfaces.IPloneSEOLayer"
-        permission="zope2.View"
-        /-->
-
-    <browser:viewlet
-        name="quintagroup.seoptimizer.metatags"
-        manager="plone.app.layout.viewlets.interfaces.IHtmlHead"
-        class=".viewlets.MetaTagsViewlet"
         layer=".interfaces.IPloneSEOLayer"
         permission="zope2.View"
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/keywords.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/keywords.py	(revision 1312)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/keywords.py	(revision 1313)
@@ -16,5 +16,4 @@
     def validateKeywords(self, text):
         """ see interface """
-
         # extract keywords from text
         if not text.strip():
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/templates/generator.pt
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/templates/generator.pt	(revision 1312)
+++ 	(revision )
@@ -1,1 +1,0 @@
-<meta name="generator" content="Plone - http://plone.org" />
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/templates/seo_controlpanel.pt
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/templates/seo_controlpanel.pt	(revision 1312)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/templates/seo_controlpanel.pt	(revision 1313)
@@ -45,4 +45,18 @@
                             <textarea cols="60" id="default_custom_metatags" name="default_custom_metatags" rows="15"
                                       tal:content="python:'\n'.join(view.getDefaultCustomMetatags())"></textarea>
+                        </div>
+                    </div>
+
+                    <div class="field">
+                        <label for="metatags_order" i18n:translate="label_metatags_order">
+                            Meta tags order in the page.
+                        </label>
+                        <div class="formHelp" i18n:translate="help_metatags_order">
+                            Fill in meta tags order (one per line) which will appear on qseo_properties
+                            edit tab. Example: "metaname accessor".
+                        </div>
+                        <div class="widget">
+                            <textarea cols="60" id="metatags_order" name="metatags_order" rows="15"
+                                      tal:content="python:'\n'.join(view.getMetaTagsOrder())"></textarea>
                         </div>
                     </div>
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/viewlets.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/viewlets.py	(revision 1312)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/viewlets.py	(revision 1313)
@@ -1,11 +1,8 @@
 from cgi import escape
 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
 from Products.CMFCore.utils import getToolByName
-from AccessControl import Unauthorized
-from quintagroup.seoptimizer.util import SortedDict
-from quintagroup.seoptimizer.interfaces import IKeywords 
+
 
 class TitleCommentViewlet(ViewletBase):
@@ -49,94 +46,4 @@
                 return u"%s\n%s"%(qseo_title, qseo_comments)
 
-#class HTTPEquiv(ViewletBase):
-#    
-#    def charset( self ):
-#        context = self.context.aq_inner
-#        site_properties = getToolByName( context, 'portal_properties').site_properties
-#        return site_properties.getProperty('default_charset', 'utf-8')
-#    
-#    def render( self ):
-#        return """<meta http-equiv="Content-Type" content="text/html; charset=%s" />"""%self.charset()
-#         
-#class BaseUrlViewlet( ViewletBase ):
-#    """
-#       simpel viewlet for base href rendering
-#    """
-#    def renderBase( self ):
-#        # returns correct base href
-#        context = self.context.aq_inner
-#        isFolder = getattr(context.aq_explicit, 'isPrincipiaFolderish', 0)
-#        base_url = context.absolute_url()
-#
-#        # when accessing via WEBDAV you're not allowed to access aq_explicit
-#        try:
-#            return isFolder and '%s/'%base_url or base_url
-#        except (Unauthorized, 'Unauthorized'):
-#            pass
-#
-#    def render( self ):
-#        return """<base href="%s" /><!--[if lt IE 7]></base><![endif]-->"""% self.renderBase()
-
-class MetaTagsViewlet( ViewletBase ):
-
-    def listMetaTags( self ):
-        context = self.context.aq_inner
-        portal_props = getToolByName(context, 'portal_properties')
-        pu = getToolByName(context, 'plone_utils')
-        seo_props = getToolByName(portal_props, 'seo_properties', None)
-        if seo_props is None:
-            return pu.listMetaTags(context)
-
-        site_props = getToolByName(portal_props, 'site_properties')
-        exposeDCMetaTags = site_props.exposeDCMetaTags
-
-        metaTags = SortedDict()
-        #metaTags.update(pu.listMetaTags(context))
-        metadataList = [
-            ('seo_description', 'description'),
-            ('seo_keywords',    'keywords'),
-            ('seo_robots',      'robots'),
-            ('seo_distribution','distribution')]
-
-        if exposeDCMetaTags:
-            metadataList.append(('seo_distribution', 'DC.distribution'))
-
-        seo_context = getMultiAdapter((self.context, self.request), name='seo_context')
-        for accessor, key in metadataList:
-            method = getattr(seo_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 and default from configlet
-        custom_meta_tags = seo_context.seo_customMetaTags()
-        for tag in custom_meta_tags:
-            if tag['meta_content']:
-                metaTags[tag['meta_name']] = tag['meta_content']
-
-        # Set the additional matching keywords, if any
-        adapter = IKeywords(context, None)
-        if adapter is not None:
-            keywords = adapter.listKeywords()
-            if keywords:
-                metaTags['keywords'] = keywords
-
-        return metaTags
-
-    def render( self ):
-        return '\n'.join([safe_unicode("""<meta name="%s" content="%s" />"""%(name, content)) \
-                                       for name, content in self.listMetaTags().items()])
 
 class CustomScriptViewlet( ViewletBase ):
@@ -148,5 +55,5 @@
         if seo_props is None:
             return '' 
-	return seo_props.getProperty('custom_script', '')
+        return seo_props.getProperty('custom_script', '')
 
     def render( self ):
@@ -162,3 +69,2 @@
         seo_context = getMultiAdapter((self.context, self.request), name='seo_context')
         return """<link rel="canonical" href="%s" />""" % seo_context.seo_canonical()
-
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/views.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/views.py	(revision 1312)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/browser/views.py	(revision 1313)
@@ -217,5 +217,5 @@
         sp = self.portal_properties.site_properties
         return sp.getProperty('exposeDCMetaTags')
-    
+
     def getDefaultCustomMetatags( self ):
         """
@@ -223,4 +223,10 @@
         seo = self.portal_properties.seo_properties
         return seo.getProperty('default_custom_metatags')
+
+    def getMetaTagsOrder( self ):
+        """
+        """
+        seo = self.portal_properties.seo_properties
+        return seo.getProperty('metatags_order')
 
     def getAdditionalKeywords( self ):
@@ -250,4 +256,5 @@
         additionalKeywords=request.get('additionalKeywords', [])
         default_custom_metatags=request.get('default_custom_metatags', [])
+        metatags_order=request.get('metatags_order', [])
 
         site_props = getToolByName(self.portal_properties, 'site_properties')
@@ -261,4 +268,5 @@
             seo_props.manage_changeProperties(additional_keywords=additionalKeywords)
             seo_props.manage_changeProperties(default_custom_metatags=default_custom_metatags)
+            seo_props.manage_changeProperties(metatags_order=metatags_order)
 
             for ptype in self.portal_types.objectValues():
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/configure.zcml
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/configure.zcml	(revision 1312)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/configure.zcml	(revision 1313)
@@ -4,6 +4,5 @@
     xmlns:browser="http://namespaces.zope.org/browser"
     xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
-    xmlns:cmf="http://namespaces.zope.org/cmf"
-    >
+    xmlns:cmf="http://namespaces.zope.org/cmf">
 
   <five:registerPackage package="." />
@@ -14,5 +13,6 @@
       directory="profiles/default"
       description="Install quintagroup.seoptimizer"
-      provides="Products.GenericSetup.interfaces.EXTENSION" />
+      provides="Products.GenericSetup.interfaces.EXTENSION"
+      />
 
   <genericsetup:registerProfile
@@ -21,19 +21,26 @@
       directory="profiles/uninstall"
       description="Uninstall quintagroup.seoptimizer"
-      provides="Products.GenericSetup.interfaces.EXTENSION" />
+      provides="Products.GenericSetup.interfaces.EXTENSION"
+      />
 
-    <browser:page
-        for="Products.Archetypes.interfaces.IBaseObject"
-        attribute="validateKeywords"
-        class=".browser.keywords.ValidateSEOKeywordsView"
-        name="checkSEOKeywords"
-        permission="cmf.ModifyPortalContent"
-        />
+  <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"
-    />
+      for="Products.Archetypes.public.BaseObject"
+      provides=".interfaces.IKeywords"
+      factory=".adapters.AdditionalKeywords"
+      />
+
+  <adapter
+      for="Products.Archetypes.public.BaseContent"
+      provides=".interfaces.IMappingMetaTags"
+      factory=".adapters.MappingMetaTags"
+      />
 
   <cmf:registerDirectory name="quintagroup.seoptimizer" />
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/interfaces.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/interfaces.py	(revision 1312)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/interfaces.py	(revision 1313)
@@ -7,2 +7,9 @@
         """Returns all the existing keywords for the current content type.
         """
+
+class IMappingMetaTags(Interface):
+    """
+    """
+    def getMappingMetaTags():
+        """Returns mapping {meta_name:accssesor} all the meta tags.
+        """
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/profiles/default/propertiestool.xml
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/profiles/default/propertiestool.xml	(revision 1312)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/profiles/default/propertiestool.xml	(revision 1313)
@@ -67,4 +67,22 @@
   <property name="additional_keywords" type="lines"/>
   <property name="default_custom_metatags" type="lines"/>
+  <property name="metatags_order" type="lines">
+   <element value="DC.publisher Publisher"/>
+   <element value="DC.description Description"/>
+   <element value="DC.contributors Contributors"/>
+   <element value="DC.creator Creator"/>
+   <element value="DC.format Format"/>
+   <element value="DC.rights Rights"/>
+   <element value="DC.language Language"/>
+   <element value="DC.date.modified ModificationDate"/>
+   <element value="DC.date.created CreationDate"/>
+   <element value="DC.type Type"/>
+   <element value="DC.subject Subject"/>
+   <element value="DC.distribution seo_distribution"/>
+   <element value="description seo_description"/>
+   <element value="keywords seo_keywords"/>
+   <element value="robots seo_robots"/>
+   <element value="distribution seo_distribution"/>
+  </property>
  </object>
  <object name="site_properties" meta_type="Plone Property Sheet">
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/profiles/default/viewlets.xml
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/profiles/default/viewlets.xml	(revision 1312)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/profiles/default/viewlets.xml	(revision 1313)
@@ -2,10 +2,5 @@
 <object>
   <order manager="plone.htmlhead" skinname="*">
-    <!--viewlet name="quintagroup.seoptimizer.http_equiv" /-->
     <viewlet name="plone.htmlhead.title" />
-    <!--viewlet name="quintagroup.seoptimizer.base" /-->
-    <!--viewlet name="quintagroup.seoptimizer.generator" /-->
-    <viewlet name="quintagroup.seoptimizer.metatags" />
-    <viewlet name="quintagroup.seoptimizer.canonicalurl" />
     <viewlet name="quintagroup.seoptimizer.customscript" />
     <viewlet name="plone.resourceregistries" />
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizer.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizer.py	(revision 1312)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizer.py	(revision 1313)
@@ -28,4 +28,6 @@
                     'meta_content' : ''}
                   ]
+view_metatags = ['DC.creator', 'DC.format', 'DC.date.modified', 'DC.date.created', 'DC.type',
+                   'DC.distribution', 'description', 'keywords', 'robots', 'distribution']
 
 global_custom_metatags = {'default_custom_metatags':'metatag1|global_metatag1value\nmetatag4|global_metatag4value'}
@@ -215,16 +217,24 @@
     def testDescription(self):
         m = re.match('.*<meta name="description" content="it is description" />', self.html, re.S|re.M)
+        if not m:
+            m = re.match('.*<meta content="it is description" name="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)
+        if not m:
+             m = re.match('.*<meta content="my1|key2" name="keywords" />', 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)
+        if not m:
+            m = re.match('.*<meta content="ALL" name="robots" />', 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)
+        if not m:
+            m = re.match('.*<meta content="Global" name="distribution" />', self.html, re.S|re.M)
         self.assert_(m, 'Distribution not set in')
 
@@ -234,10 +244,29 @@
 
     def testTagsOrder(self):
-        m = re.search('name="description".+name="keywords"', self.html, re.S|re.M)
+        mtop = self.sp.getProperty('metatags_order')
+        metatags_order = [t.split(' ')[0] for t in mtop if len(t.split(' '))==2 and t.split(' ')[0] in view_metatags]
+        m = re.search('.*'.join(['<meta.*name="%s".*/>' %t for t in metatags_order]), self.html, re.S|re.M)
         self.assert_(m, "Meta tags order not supported.")
+
+        mtop = list(mtop)
+        mtop.reverse()
+        metatags_order = [t.split(' ')[0] for t in mtop if len(t.split(' '))==2 and t.split(' ')[0] in view_metatags]
+        m = re.search('.*'.join(['<meta.*name="%s".*/>' %t for t in metatags_order]), self.html, re.S|re.M)
+        self.assertFalse(m, "Meta tags order not supported.")
+
+        self.sp.manage_changeProperties(**{'metatags_order':tuple(mtop)})
+        html = self.publish(self.abs_path, self.basic_auth).getBody()
+        m = re.search('.*'.join(['<meta.*name="%s".*/>' %t for t in metatags_order]), self.html, re.S|re.M)
+        self.assertFalse(m, "Meta tags order not supported.")
+
+        m = re.search('.*'.join(['<meta.*name="%s".*/>' %t for t in metatags_order]), 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)
+            if not m:
+                m = re.search('<meta content="%(meta_content)s" name="%(meta_name)s" />' % tag, self.html, re.S|re.M)
             if tag['meta_content']:
                 self.assert_(m, "Custom meta tag %s not applied." % tag['meta_name'])
@@ -245,4 +274,6 @@
                 self.assert_(not m, "Meta tag %s has no content, but is present in the page." % tag['meta_name'])
         m = re.search('<meta name="metatag4" content="global_metatag4value" />' , self.html, re.S|re.M)
+        if not m:
+            m = re.search('<meta content="global_metatag4value" name="metatag4" />' , self.html, re.S|re.M)
         self.assert_(m, "Global custom meta tag %s not applied." % 'metatag4')
 
@@ -253,6 +284,10 @@
         html = self.publish(self.abs_path, self.basic_auth).getBody()
         m = re.search('<meta name="metatag4" content="global_metatag4value" />' , html, re.S|re.M)
+        if not m:
+            m = re.search('<meta content="global_metatag4value" name="metatag4" />' , html, re.S|re.M)
         self.assert_(not m, "Global custom meta tag %s is prosent in the page." % 'metatag4')
         m = re.search('<meta name="metatag1" content="global_metatag1value" />' , html, re.S|re.M)
+        if not m:
+            m = re.search('<meta content="global_metatag1value" name="metatag1" />' , html, re.S|re.M)
         self.assert_(m, "Global custom meta tag %s is prosent in the page." % 'metatag4')
 
@@ -311,5 +346,7 @@
         self.my_doc.manage_addProperty('qSEO_keywords', ('foo',), 'lines')
         self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
-        m = re.match('.*<meta\ name="keywords"\ content="foo"\ />', self.html, re.S|re.M)
+        m = re.match('.*<meta\ content="foo"\ name="keywords"\ />', self.html, re.S|re.M)
+        if not m:
+            m = re.match('.*<meta\ name="keywords"\ content="foo"\ />', self.html, re.S|re.M)
         self.assert_(m, "No 'foo' keyword find")
 
@@ -317,5 +354,7 @@
         self.my_doc.manage_addProperty('qSEO_keywords', ('foo', 'bar'), 'lines')
         self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
-        m = re.match('.*<meta\ name="keywords"\ content="foo, bar"\ />', self.html, re.S|re.M)
+        m = re.match('.*<meta\ content="foo, bar"\ name="keywords"\ />', self.html, re.S|re.M)
+        if not m:
+            m = re.match('.*<meta\ name="keywords"\ content="foo, bar"\ />', self.html, re.S|re.M)
         self.assert_(m, "No 'foo, bar' keyword find")
 
@@ -329,5 +368,7 @@
         self.sp.additional_keywords = ('foo',)
         self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
-        m = re.match('.*<meta\ name="keywords"\ content="foo"\ />', self.html, re.S|re.M)
+        m = re.match('.*<meta\ content="foo"\ name="keywords"\ />', self.html, re.S|re.M)
+        if not m:
+            m = re.match('.*<meta\ name="keywords"\ content="foo"\ />', self.html, re.S|re.M)
         self.assert_(m, "No 'foo' keyword find")
 
@@ -336,5 +377,7 @@
         self.sp.additional_keywords = ('foo', 'bar')
         self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
-        m = re.match('.*<meta\ name="keywords"\ content="foo, bar"\ />', self.html, re.S|re.M)
+        m = re.match('.*<meta\ content="foo, bar"\ name="keywords"\ />', self.html, re.S|re.M)
+        if not m:
+            m = re.match('.*<meta\ name="keywords"\ content="foo, bar"\ />', self.html, re.S|re.M)
         self.assert_(m, "No 'foo, bar' keyword find")
 
@@ -344,5 +387,7 @@
         self.my_doc.manage_addProperty('qSEO_keywords', ('baz',), 'lines')
         self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
-        m = re.match('.*<meta\ name="keywords"\ content="baz,\ foo,\ bar"\ />', self.html, re.S|re.M)
+        m = re.match('.*<meta\ content="baz,\ foo,\ bar"\ name="keywords"\ />', self.html, re.S|re.M)
+        if not m:
+            m = re.match('.*<meta\ name="keywords"\ content="baz,\ foo,\ bar"\ />', self.html, re.S|re.M)
         self.assert_(m, "No 'foo, bar, baz' keyword find")
 
@@ -382,6 +427,11 @@
         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)
+        m1 = re.match('.*<meta\ name="DC.format"\ content=".*?"\ />', self.html, re.S|re.M)
+        if not m1:
+            m1 = re.match('.*<meta content=".*?" name="DC.format" />', self.html, re.S|re.M)
+        m2 = re.match('.*<meta name="DC.distribution" content=".*?" />', self.html, re.S|re.M)
+        if not m2:
+            m2 = re.match('.*<meta content=".*?" name="DC.distribution" />', self.html, re.S|re.M)
+        m = m1 or m2
         self.assert_(not m, 'DC meta tags avaliable when exposeDCMetaTags=False')
 
@@ -390,5 +440,11 @@
         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.type"\ content=".*?"\ />', self.html, re.S|re.M)
+        m1 = re.match('.*<meta\ content=".*?"\ name="DC.format"\ />', self.html, re.S|re.M)
+        if not m1:
+            m1 = re.match('.*<meta\ name="DC.format"\ content=".*?"\ />', self.html, re.S|re.M)
+        m2 = re.match('.*<meta\ content=".*?"\ name="DC.type"\ />', self.html, re.S|re.M)
+        if not m2:
+            m2 = re.match('.*<meta\ name="DC.type"\ content=".*?"\ />', self.html, re.S|re.M)
+        m = m1 and m2
         self.assert_(m, 'DC meta tags not avaliable when createManager=True')
 
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/util.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/util.py	(revision 1312)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/util.py	(revision 1313)
@@ -12,7 +12,8 @@
 class SortedDict(dict):
     security = ClassSecurityInfo()
-    security.declarePublic('items')    
+
+    security.declarePublic('items')
     def items(self):
-        primary_metatags = ['description', 'keywords']
+        primary_metatags = self.pmt
         lst = [(name,self[name]) for name in primary_metatags                    \
                                                  if name in self.keys()] +       \
@@ -20,4 +21,17 @@
                                                  if name not in primary_metatags]
         return lst
+
+
+    security.declarePublic('__init__')
+    def __init__(self, *args, **kwargs):
+        super(SortedDict,self).__init__(*args, **kwargs)
+        self.pmt = []
+
+
+    security.declarePublic('__setitem__')
+    def __setitem__(self, i, y):
+        super(SortedDict,self).__setitem__(i, y)
+        if i not in self.pmt:
+            self.pmt.append(i)
 
 try:
