Index: qPloneGoogleSitemaps/tags/0.8.2/Extensions/Install.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/Extensions/Install.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/Extensions/Install.py	(revision 637)
@@ -0,0 +1,17 @@
+from Products.CMFCore.utils import getToolByName
+
+def install(self, reinstall=False):
+    """ Install skin with GenericSetup install profile
+    """
+    ps = getToolByName(self, 'portal_setup')
+    mtool = getToolByName(self, 'portal_migration')
+    plone_version = mtool.getFileSystemVersion()
+
+    if plone_version.startswith('3'):
+        # if this is plone 3.x
+        (ps.aq_base).__of__(self).runAllImportStepsFromProfile("profile-Products.qPloneGoogleSitemap:default")
+    else:
+        active_context_id = ps.getImportContextID()
+        ps.setImportContext("Products.qPloneGoogleSitemap:default")
+        ps.runAllImportSteps()
+        ps.setImportContext(active_context_id)
Index: qPloneGoogleSitemaps/tags/0.8.2/Extensions/__init__.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/Extensions/__init__.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/Extensions/__init__.py	(revision 637)
@@ -0,0 +1,1 @@
+# this file intentionally left blank
Index: qPloneGoogleSitemaps/tags/0.8.2/Extensions/ping_googlesitemap.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/Extensions/ping_googlesitemap.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/Extensions/ping_googlesitemap.py	(revision 637)
@@ -0,0 +1,9 @@
+from zope.event import notify
+from Products.qPloneGoogleSitemaps.events import AfterTransitionEvent
+
+def pingGoogleSitemap(stch):
+    notify(AfterTransitionEvent(stch.object,
+           stch.workflow, stch.old_state, stch.new_state,
+           stch.transition, stch.status, stch.kwargs))
+    return 0
+
Index: qPloneGoogleSitemaps/tags/0.8.2/HISTORY.txt
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/HISTORY.txt	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/HISTORY.txt	(revision 637)
@@ -0,0 +1,64 @@
+0.8.2 Fix bug of qPGSMs breakage on object creation
+      in plone-3      
+
+0.8.1 Prevetn QuickInstaller confuse in <= plone-2.5
+
+  *Added Install.py external script
+
+0.8 Multiple Sitemap type support
+
+  * Added support for content, mobile, news sitemap
+
+  * Implement Sitemap XML protocol version 0.9
+
+  * Many verification files can be added to site
+
+  * Update tests
+
+0.6 Plone-3.0 compatibility
+
+  * Added plone-3 compatibility.
+
+0.5 Added tests
+
+  * Fix up bug in sed command s
+
+0.4.10 Configlet split into tabs
+
+  * Added workflow transitions configuration
+
+0.4.9 Introduced different sort options
+
+  * Added blackout list
+  
+  * Added opportunity to use sed command s
+
+0.4 Introduced sitemap ping to Google
+
+  * Added Google ping
+  
+  * Added configuration page
+  
+  * Added filter by content-types and states
+  
+0.3 Introduced product configlet 
+
+  * Added product configlet
+
+  * Added product usage docs on configlet
+
+0.2 Introduced XML format
+
+  * Moved to XML sitemaps format
+
+  * Added lastmod element to inform Google of object change date
+
+  * sitemap is comressed via HTTP gzip compression
+
+0.1 Initial version, proof of concept (i.e, Google working ;)
+
+  * plain text sitemaps format
+
+  * simple installer, no configuration
+
+  * no UI, just the working code ;)
Index: qPloneGoogleSitemaps/tags/0.8.2/README.txt
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/README.txt	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/README.txt	(revision 637)
@@ -0,0 +1,26 @@
+Quintagroup Plone Google Sitemaps
+
+  The product allows Plone websites to get better visibility for Google search engine by providing it with complete listing of URLs to website content. Different sitemap types are enabled: content, mobile or news sitemap. Different sitemap types index their own content and do not depend on other sitemaps. 
+  To enable Google Sitemaps on your site, add a sitemap to Google Sitemap settings.
+
+Requirements
+
+  * Plone 2.5
+  * Plone 3.x 
+
+Links
+
+  * See "Google Sitemaps": https://www.google.com/webmasters/sitemaps website for more information.
+  * "Product Homepage": http://quintagroup.com/services/plone-development/products/google-sitemaps
+  * "Documentation Area": http://projects.quintagroup.com/products/wiki/qPloneGoogleSitemaps
+  * "Download": http://sourceforge.net/projects/quintagroup
+
+Authors
+
+  * Myroslav Opyr
+
+  * Andriy Mylenkyi
+
+  * Nazar Ilchuk
+  
+  * Mykola Kharechko
Index: qPloneGoogleSitemaps/tags/0.8.2/TODO.txt
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/TODO.txt	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/TODO.txt	(revision 637)
@@ -0,0 +1,9 @@
+The items that are planned to implement within short timeframe
+
+* Provide changefreq and priority elements in url
+
+* pre-Compress the sitemaps
+
+* Make it possible to break sitemap into parts (with the use of siteindex)
+
+* implement several siteindex split policies: location, modification date, etc.
Index: qPloneGoogleSitemaps/tags/0.8.2/__init__.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/__init__.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/__init__.py	(revision 637)
@@ -0,0 +1,58 @@
+import Globals
+
+from Products.CMFCore.DirectoryView import registerDirectory
+from AccessControl import allow_module
+
+registerDirectory('skins', globals())
+qPGS_globals = globals()
+
+allow_module('Products.qPloneGoogleSitemaps.utils.py')
+allow_module('Products.qPloneGoogleSitemaps.config.py')
+
+from zope.i18nmessageid import MessageFactory
+from Products.qPloneGoogleSitemaps import config
+
+from Products.Archetypes import atapi
+from Products.CMFCore.utils import ContentInit
+from Products.CMFCore.permissions import setDefaultRoles
+
+# Define a message factory for when this product is internationalised.
+# This will be imported with the special name "_" in most modules. Strings
+# like _(u"message") will then be extracted by i18n tools for translation.
+
+qPloneGoogleSitemapsMessageFactory = MessageFactory('qPloneGoogleSitemaps')
+
+def initialize(context):
+    """Initializer called when used as a Zope 2 product.
+
+    This is referenced from configure.zcml. Regstrations as a "Zope 2 product"
+    is necessary for GenericSetup profiles to work, for example.
+
+    Here, we call the Archetypes machinery to register our content types
+    with Zope and the CMF.
+    """
+
+    # Retrieve the content types that have been registered with Archetypes
+    # This happens when the content type is imported and the registerType()
+    # call in the content type's module is invoked. Actually, this happens
+    # during ZCML processing, but we do it here again to be explicit. Of
+    # course, even if we import the module several times, it is only run
+    # once.
+
+    content_types, constructors, ftis = atapi.process_types(
+        atapi.listTypes(config.PROJECTNAME),
+        config.PROJECTNAME)
+
+    # Now initialize all these content types. The initialization process takes
+    # care of registering low-level Zope 2 factories, including the relevant
+    # add-permission. These are listed in config.py. We use different
+    # permissions for each content type to allow maximum flexibility of who
+    # can add which content types, where. The roles are set up in rolemap.xml
+    # in the GenericSetup profile.
+
+    for atype, constructor in zip(content_types, constructors):
+        ContentInit('%s: %s' % (config.PROJECTNAME, atype.portal_type),
+            content_types      = (atype,),
+            permission         = config.ADD_PERMISSIONS[atype.portal_type],
+            extra_constructors = (constructor,),
+            ).initialize(context)
Index: qPloneGoogleSitemaps/tags/0.8.2/browser/__init__.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/browser/__init__.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/browser/__init__.py	(revision 637)
@@ -0,0 +1,1 @@
+#
Index: qPloneGoogleSitemaps/tags/0.8.2/browser/commonview.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/browser/commonview.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/browser/commonview.py	(revision 637)
@@ -0,0 +1,94 @@
+from string import find
+from zope.interface import implements, Interface, Attribute
+
+from Products.Five import BrowserView
+from Products.CMFCore.utils import getToolByName
+
+from Products.qPloneGoogleSitemaps import qPloneGoogleSitemapsMessageFactory as _
+from utils import additionalURLs, applyOperations
+
+
+class ISitemapView(Interface):
+    """
+    Sitemap view interface
+    """
+
+    def results():
+        """ Return list of dictionary objects
+            which confirm Sitemap conditions
+        """
+
+    def getAdditionalURLs():
+        """ Return additional URL list
+        """
+
+    def updateRequest():
+        """ Add compression header to RESPONSE
+            if allowed
+        """
+
+    numEntries = Attribute("Return number of entries")
+
+class CommonSitemapView(BrowserView):
+    """
+    Sitemap browser view
+    """
+    implements(ISitemapView)
+
+    # key, function map for extend return results
+    # with mapping data
+    additional_maps = ()
+
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+    @property
+    def portal_catalog(self):
+        return getToolByName(self.context, 'portal_catalog')
+
+    @property
+    def portal(self):
+        return getToolByName(self.context, 'portal_url').getPortalObject()
+
+    def getFilteredObjects(self):
+        """ Return brains
+        """
+        return []
+
+    def results(self):
+        """ Prepare mapping for template
+        """
+        result = []
+        objects = self.getFilteredObjects()
+        blackout_list = self.context.getBlackout_list()
+        reg_exps = self.context.getReg_exp()
+
+        brain_url_map = applyOperations([ob for ob in objects 
+            if (ob.getId not in blackout_list)],
+            reg_exps)
+
+        # Prepare dictionary for view
+        for url, b in brain_url_map.items():
+            res_map = {'url' : url,}
+            [res_map.update({k : f(b)}) for k, f in self.additional_maps]
+            result.append(res_map)
+        self.num_entries = len(result)
+        return result
+
+    def updateRequest(self):
+        self.request.RESPONSE.setHeader('Content-Type', 'text/xml')
+        try:
+            compression = self.context.enableHTTPCompression()
+            if compression:
+                compression(request=self.request)
+        except:
+            pass
+
+    def getAdditionalURLs(self):
+        return additionalURLs(self.context)
+
+    @property
+    def numEntries(self):
+        return len(self.results()) + len(self.getAdditionalURLs())
Index: qPloneGoogleSitemaps/tags/0.8.2/browser/configletview.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/browser/configletview.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/browser/configletview.py	(revision 637)
@@ -0,0 +1,140 @@
+from zope.component import queryMultiAdapter
+from zope.interface import implements, Interface, Attribute
+
+from Products.Five import BrowserView
+from Products.CMFCore.utils import getToolByName
+
+from Products.qPloneGoogleSitemaps import qPloneGoogleSitemapsMessageFactory as _
+from Products.qPloneGoogleSitemaps.interfaces import ISitemap
+
+def splitNum(num):
+    res = []
+    prefn = 3
+    for c in str(num)[::-1]:
+        res.insert(0,c)
+        if not len(res)%prefn:
+            res.insert(0,',')
+            prefn += 4
+    return "".join(res[0]==',' and res[1:] or res)
+
+class IConfigletSettingsView(Interface):
+    """
+    Sitemap view interface
+    """
+
+    sitemaps = Attribute("return mapping of sitemap's type to list of appropriate objects")
+    hasContentSM = Attribute("Return boolean about existance content sitemap")
+    hasMobileSM = Attribute("Return boolean about existance mobile sitemap")
+    hasNewsSM = Attribute("Return boolean about existance news sitemap")
+    sitemaps = Attribute("List of sitemap typs")
+    sm_types = Attribute("List of sitemap typs")
+
+    def sitemapsDict():
+        """ Return dictionary like object with data for table
+        """
+    def sitemapsURLByType():
+        """ Return dictionary like object with sitemap_type as key
+            and sitemap object(s) as value
+        """
+    def getVerificationFiles():
+        """ Return list of existent verification files on site.
+            Update googlesitemap_properties.verification_file
+            property for only existent files
+        """
+
+
+class ConfigletSettingsView(BrowserView):
+    """
+    Configlet settings browser view
+    """
+    implements(IConfigletSettingsView)
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+        catalog = getToolByName(self.context, 'portal_catalog')
+        self.sitemaps = [i.getObject() for i in catalog(portal_type='Sitemap')]
+
+
+    @property
+    def sm_types(self):
+        return [i.getSitemapType() for i in self.sitemaps]
+
+    @property
+    def hasContentSM(self):
+        return 'content' in self.sm_types
+
+    @property
+    def hasMobileSM(self):
+        return 'mobile' in self.sm_types
+
+    @property
+    def hasNewsSM(self):
+        return 'news' in self.sm_types
+
+    def sitemapsURLByType(self):
+        sitemaps = {}
+        for sm in self.sitemaps:
+            smlist = sitemaps.setdefault(sm.getSitemapType(),[])
+            smlist.append({'url':sm.absolute_url(),'id':sm.id})
+        sitemaps['all'] = sitemaps.setdefault('content',[]) + \
+                          sitemaps.setdefault('mobile',[]) + \
+                          sitemaps.setdefault('news',[])
+        return sitemaps
+
+    def sitemapsURLs(self):
+        sitemaps = {}
+        for sm in self.sitemaps:
+            smlist = sitemaps.setdefault(sm.getSitemapType(),[])
+            smlist.append(sm.absolute_url())
+        return sitemaps
+
+
+    def sitemapsDict(self):
+        content, mobile, news = [],[],[]
+        for sm in self.sitemaps:
+            data = self.getSMData(sm)
+            if data['sm_type'] == 'Content':
+                content.append(data)
+            elif data['sm_type'] == 'Mobile':
+                mobile.append(data)
+            elif data['sm_type'] == 'News':
+                news.append(data)
+        return content + mobile + news
+
+    def getSMData(self, ob):
+        size, entries = self.getSitemapData(ob)
+        return {'sm_type'    : ob.getSitemapType().capitalize(),
+                'sm_id'      : ob.id,
+                'sm_url'     : ob.absolute_url(),
+                'sm_size'    : size and splitNum(size) or '',
+                'sm_entries' : entries and splitNum(entries) or '',
+               }
+
+    def getSitemapData(self, ob):
+        size, entries = (0, 0)
+        view = ob and ob.defaultView() or None
+        if view:
+            resp = self.request.RESPONSE
+            bview = queryMultiAdapter((ob,self.request), name=view)
+            if bview:
+                try:
+                    size = len(bview())
+                    entries = bview.numEntries
+                    self.request.RESPONSE.setHeader('Content-Type', 'text/html')
+                except:
+                    pass
+        return (size, entries)
+
+    def getVerificationFiles(self):
+        vfs = []
+        pp = getToolByName(self.context, 'portal_properties')
+        props = getattr(pp,'googlesitemap_properties')
+        if props:
+            portal_ids = getToolByName(self.context, 'portal_url').getPortalObject().objectIds()
+            props_vfs = list(props.getProperty('verification_filenames',[]))
+            vfs = [vf for vf in props_vfs if vf in portal_ids]
+            if not props_vfs==vfs:
+                props._updateProperty('verification_filenames', vfs)
+        return vfs
Index: qPloneGoogleSitemaps/tags/0.8.2/browser/configure.zcml
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/browser/configure.zcml	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/browser/configure.zcml	(revision 637)
@@ -0,0 +1,43 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain="Products.qPloneGoogleSitemaps">
+
+    <!-- -*- extra stuff goes here -*- -->
+
+    <browser:page
+      for="..interfaces.ISitemap"
+      name="sitemap.xml"
+      class=".sitemapview.SitemapView"
+      template="templates/sitemapview.pt"
+      allowed_interface=".commonview.ISitemapView"
+      permission="zope.Public"
+    />
+
+    <browser:page
+      for="..interfaces.ISitemap"
+      name="mobile-sitemap.xml"
+      class=".mobilesitemapview.MobileSitemapView"
+      template="templates/mobilesitemapview.pt"
+      allowed_interface=".commonview.ISitemapView"
+      permission="zope.Public"
+    />
+
+    <browser:page
+      for="..interfaces.ISitemap"
+      name="news-sitemap.xml"
+      class=".newssitemapview.NewsSitemapView"
+      template="templates/newssitemapview.pt"
+      allowed_interface=".commonview.ISitemapView"
+      permission="zope.Public"
+    />
+
+    <browser:page
+      for="*"
+      name="sitemap_settings"
+      class=".configletview.ConfigletSettingsView"
+      allowed_interface=".configletview.IConfigletSettingsView"
+      permission="zope.Public"
+    />
+
+</configure>
Index: qPloneGoogleSitemaps/tags/0.8.2/browser/mobilesitemapview.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/browser/mobilesitemapview.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/browser/mobilesitemapview.py	(revision 637)
@@ -0,0 +1,21 @@
+from DateTime import DateTime
+from commonview import *
+
+class MobileSitemapView(CommonSitemapView):
+    """
+    Mobile Sitemap browser view
+    """
+    implements(ISitemapView)
+
+    additional_maps = (
+        ('modification_date', lambda x:DateTime(x.ModificationDate).HTML4()),
+    )
+
+    def getFilteredObjects(self):
+        path = self.portal.getPhysicalPath()
+        portal_types = self.context.getPortalTypes()
+        review_states = self.context.getStates()
+        return self.portal_catalog(path = path,
+                portal_type = portal_types,
+                review_state = review_states, 
+                hasMobileContent = True)
Index: qPloneGoogleSitemaps/tags/0.8.2/browser/newssitemapview.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/browser/newssitemapview.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/browser/newssitemapview.py	(revision 637)
@@ -0,0 +1,25 @@
+from DateTime import DateTime
+from commonview import *
+
+class NewsSitemapView(CommonSitemapView):
+    """
+    Mobile Sitemap browser view
+    """
+    implements(ISitemapView)
+
+    additional_maps = (
+        ('publication_date', lambda x:DateTime(x.EffectiveDate).HTML4()),
+        ('keywords', lambda x:', '.join(x.Subject))
+    )
+
+    def getFilteredObjects(self):
+        path = self.portal.getPhysicalPath()
+        portal_types = self.context.getPortalTypes()
+        review_states = self.context.getStates()
+        min_date = DateTime() - 3
+        res = self.portal_catalog(path = path,
+                portal_type = portal_types,
+                review_state = review_states,
+                effective = {"query": min_date,
+                             "range": "min" })
+        return res
Index: qPloneGoogleSitemaps/tags/0.8.2/browser/sitemapview.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/browser/sitemapview.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/browser/sitemapview.py	(revision 637)
@@ -0,0 +1,21 @@
+from DateTime import DateTime
+from commonview import *
+
+
+class SitemapView(CommonSitemapView):
+    """
+    Sitemap browser view
+    """
+    implements(ISitemapView)
+
+    additional_maps = (
+        ('modification_date', lambda x:DateTime(x.ModificationDate).HTML4()),
+    )
+
+    def getFilteredObjects(self):
+        path = self.portal.getPhysicalPath()
+        portal_types = self.context.getPortalTypes()
+        review_states = self.context.getStates()
+        return self.portal_catalog(path = path,
+                portal_type = portal_types,
+                review_state = review_states)
Index: qPloneGoogleSitemaps/tags/0.8.2/browser/templates/mobilesitemapview.pt
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/browser/templates/mobilesitemapview.pt	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/browser/templates/mobilesitemapview.pt	(revision 637)
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
+        xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0"
+        xmlns:tal="http://xml.zope.org/namespaces/tal"
+        tal:define="results view/results;
+                    dummy view/updateRequest;">
+  <url tal:condition="results"
+       tal:repeat="result results">
+    <loc tal:content="result/url">http://www.yoursite.com/</loc>
+    <lastmod tal:content="result/modification_date">2005-01-01</lastmod>
+    <mobile:mobile />
+  </url>
+  <url tal:define="URLs view/getAdditionalURLs"
+       tal:condition="URLs"
+       tal:repeat="URL URLs">
+    <loc tal:content="URL">http://www.yoursite.com/</loc>
+    <mobile:mobile />
+  </url>
+</urlset>
Index: qPloneGoogleSitemaps/tags/0.8.2/browser/templates/newssitemapview.pt
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/browser/templates/newssitemapview.pt	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/browser/templates/newssitemapview.pt	(revision 637)
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
+        xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"
+        xmlns:tal="http://xml.zope.org/namespaces/tal"
+        tal:define="results view/results;
+                    dummy view/updateRequest;">
+  <url tal:condition="results"
+       tal:repeat="result results">
+    <loc tal:content="result/url">http://www.yoursite.com/</loc>
+    <news:news>
+        <news:publication_date tal:content="result/publication_date"> 2006-08-14T03:30:00Z </news:publication_date>
+        <news:keywords tal:content="result/keywords">Business, Mergers, Acquisitions</news:keywords>
+    </news:news>
+  </url>
+</urlset>
Index: qPloneGoogleSitemaps/tags/0.8.2/browser/templates/sitemapview.pt
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/browser/templates/sitemapview.pt	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/browser/templates/sitemapview.pt	(revision 637)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
+        xmlns:tal="http://xml.zope.org/namespaces/tal"
+        xmlns:metal="http://xml.zope.org/namespaces/metal"
+        tal:define="results view/results;
+                    dummy view/updateRequest;">
+  <url tal:condition="results"
+       tal:repeat="result results">
+    <loc tal:content="result/url">http://www.yoursite.com/</loc>
+    <lastmod tal:content="result/modification_date">2005-01-01</lastmod>
+  </url>
+  <url tal:define="URLs view/getAdditionalURLs"
+       tal:condition="URLs"
+       tal:repeat="URL URLs">
+    <loc tal:content="URL">http://www.yoursite.com/</loc>
+  </url>
+</urlset>
Index: qPloneGoogleSitemaps/tags/0.8.2/browser/utils.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/browser/utils.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/browser/utils.py	(revision 637)
@@ -0,0 +1,52 @@
+import re
+
+from DateTime import DateTime
+
+from Products.CMFCore.utils import getToolByName
+import Products.qPloneGoogleSitemaps.config as config
+
+ADD_ZOPE = re.compile('^/')
+ADD_PLONE = re.compile('^[^http://|https://|\\\]')
+OPERATIONS_PARSE = re.compile(r"(.?[^\\])/(.*[^\\]|)/(.*[^\\]|)/")
+
+def searchAndReplace(string, what, with):
+    """Emulate sed command s/"""
+    res = re.sub(what,with,string)
+    return res
+OPERATORS = {'s': searchAndReplace,}
+
+def applyOperations(objects, operations):
+    """Parse Operations """
+    operations=[OPERATIONS_PARSE.match(op).groups() for op in operations]
+    result = {}
+    for ob in objects:
+        url = ob.getURL()
+        for operator, what, with in operations:
+            url = OPERATORS[operator](url, what, with.replace("\\", ""))
+        #TODO: Remove or replace following condition
+        #it is senseless in the case we need intelligent
+        #result set. Better condition would be to place
+        #freshest brain into result
+        if url in result.keys():
+            continue
+        #TODO: replace brain with only data necessary to 
+        #generate sitemap
+        result[url]=ob
+    return result
+
+def additionalURLs(context):
+    """Add URLs to sitemap that arn't objects"""
+    res = []
+
+    plone_home = getToolByName(context, 'portal_url')()
+    root = context.getPhysicalRoot().absolute_url()
+    URLs =  context.getUrls()
+
+    for url in URLs:
+        if ADD_ZOPE.match(url):
+            res.append(root+url)
+        elif ADD_PLONE.match(url):
+            res.append(plone_home+'/'+url)
+        else:
+            res.append(url)
+    return res
Index: qPloneGoogleSitemaps/tags/0.8.2/config.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/config.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/config.py	(revision 637)
@@ -0,0 +1,27 @@
+"""Common configuration constants
+"""
+
+PROJECTNAME = 'qPloneGoogleSitemaps'
+
+ADD_PERMISSIONS = {
+    # -*- extra stuff goes here -*-
+    'Sitemap': 'qPloneGoogleSitemaps: Add Sitemap',
+}
+
+SITEMAPS_VIEW_MAP = {
+    'content'   : 'sitemap.xml',
+    'mobile'    : 'mobile-sitemap.xml',
+    'news'      : 'news-sitemap.xml'
+}
+
+ping_googlesitemap = 'pingGoogleSitemap'
+testing = 0
+
+AVAILABLE_WF_SCRIPTS = [ping_googlesitemap, '']
+
+try:
+    from Products.DCWorkflow.events import AfterTransitionEvent
+except ImportError:
+    IS_PLONE_3 = False
+else:
+    IS_PLONE_3 = True
Index: qPloneGoogleSitemaps/tags/0.8.2/configure.zcml
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/configure.zcml	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/configure.zcml	(revision 637)
@@ -0,0 +1,24 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:five="http://namespaces.zope.org/five"
+    xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
+    i18n_domain="Products.qPloneGoogleSitemaps">
+
+    <!-- Include the sub-packages that use their own configure.zcml files. -->
+    <include package=".browser" />
+    <include package=".content" />
+
+    <!-- Register the installation GenericSetup extension profile -->
+    <genericsetup:registerProfile
+      name="default"
+      title="Plone Google Sitemaps"
+      directory="profiles/default"
+      description=""
+      provides="Products.GenericSetup.interfaces.EXTENSION"
+      />
+
+    <!-- -*- extra stuff goes here -*- -->
+
+    <subscriber handler=".handlers.pingGoogle"/>
+
+</configure>
Index: qPloneGoogleSitemaps/tags/0.8.2/content/__init__.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/content/__init__.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/content/__init__.py	(revision 637)
@@ -0,0 +1,1 @@
+#
Index: qPloneGoogleSitemaps/tags/0.8.2/content/configure.zcml
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/content/configure.zcml	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/content/configure.zcml	(revision 637)
@@ -0,0 +1,23 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:five="http://namespaces.zope.org/five"
+    i18n_domain="Products.qPloneGoogleSitemaps">
+
+    <!-- configure your content components here -->
+
+    <!-- -*- extra stuff goes here -*- -->
+    <class class=".sitemap.Sitemap">
+
+        <require
+            permission="zope2.View"
+            interface="..interfaces.ISitemap"
+            />
+
+        <require
+            permission="cmf.ModifyPortalContent"
+            set_schema="..interfaces.ISitemap"
+            />
+
+    </class>
+
+</configure>
Index: qPloneGoogleSitemaps/tags/0.8.2/content/sitemap.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/content/sitemap.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/content/sitemap.py	(revision 637)
@@ -0,0 +1,203 @@
+"""Definition of the Sitemap content type
+"""
+
+from zope.interface import implements, directlyProvides
+
+from Products.Archetypes import atapi
+from Products.ATContentTypes.content import base
+from Products.ATContentTypes.content import schemata
+from Products.CMFCore.utils import getToolByName
+
+from Products.qPloneGoogleSitemaps import qPloneGoogleSitemapsMessageFactory as _
+from Products.qPloneGoogleSitemaps.interfaces import ISitemap
+from Products.qPloneGoogleSitemaps.config import * 
+
+SitemapSchema = schemata.ATContentTypeSchema.copy() + atapi.Schema((
+
+    # -*- Your Archetypes field definitions here ... -*-
+    atapi.StringField(
+        name='sitemapType',
+        storage = atapi.AnnotationStorage(),
+        required=True,
+        default='content',
+        vocabulary=SITEMAPS_VIEW_MAP.keys(),
+        widget=atapi.SelectionWidget(
+            label=_(u"Sitemap type"),
+            visible = {'edit':'invisible', 'view':'invisible'},
+            description=_(u"Select Type of the sitemap."),
+        ),
+    ),
+    atapi.LinesField(
+        name='portalTypes',
+        storage = atapi.AnnotationStorage(),
+        required=True,
+        default=['Document',],
+        vocabulary="availablePortalTypes",
+        #schemata ='default',
+        widget=atapi.MultiSelectionWidget(
+            label=_(u"Define the types"),
+            description=_(u"Define the types to be included in sitemap."),
+        ),
+    ),
+    atapi.LinesField(
+        name='states',
+        storage = atapi.AnnotationStorage(),
+        required=True,
+        default=['published',],
+        vocabulary="getWorkflowStates",
+        #schemata ='default',
+        widget=atapi.MultiSelectionWidget(
+            label=_(u"Review status"),
+            description=_(u"You may include items in sitemap depend of their " \
+                          u"review state."),
+        ),
+    ),
+    atapi.LinesField(
+        name='blackout_list',
+        storage = atapi.AnnotationStorage(),
+        required=False,
+        #default='',
+        #schemata ='default',
+        widget=atapi.LinesWidget(
+            label=_(u"Blackout entries"),
+            description=_(u"The objects with the given ids will not be " \
+                          u"included in sitemap."),
+        ),
+    ),
+    atapi.LinesField(
+        name='reg_exp',
+        storage = atapi.AnnotationStorage(),
+        required=False,
+        #default='',
+        #schemata ='default',
+        widget=atapi.LinesWidget(
+            label=_(u"URL processing Regular Expressions"),
+            description=_(u"Provide regular expressions (in Perl syntax), " \
+                          u"one per line to be applied to URLs before " \
+                          u"including them into Sitemap. For instance, " \
+                          u"\"s/\/index_html//\" will remove /index_html " \
+                          u"from URLs representing default documents."),
+        ),
+    ),
+    atapi.LinesField(
+        name='urls',
+        storage = atapi.AnnotationStorage(),
+        required=False,
+        #default='',
+        #schemata ='default',
+        widget=atapi.LinesWidget(
+            label=_(u"Additional URLs"),
+            description=_(u"Define additional URLs that are not objects and " \
+                          u"that should be included in sitemap."),
+        ),
+    ),
+    atapi.LinesField(
+        name='pingTransitions',
+        storage = atapi.AnnotationStorage(),
+        required=False,
+        vocabulary='getWorkflowTransitions',
+        #schemata="default",
+        widget=atapi.MultiSelectionWidget(
+            label=_(u"Pinging workflow transitions"),
+            description=_(u"Select workflow transitions for pinging google on."),
+        ),
+    ),
+
+))
+
+# Set storage on fields copied from ATContentTypeSchema, making sure
+# they work well with the python bridge properties.
+
+SitemapSchema['id'].widget.ignore_visible_ids = True
+SitemapSchema['title'].storage = atapi.AnnotationStorage()
+SitemapSchema['title'].required=False
+SitemapSchema['title'].widget.visible = {'edit':'invisible', 'view':'invisible'}
+SitemapSchema['description'].storage = atapi.AnnotationStorage()
+SitemapSchema['description'].widget.visible = {'edit':'invisible', 'view':'invisible'}
+
+schemata.finalizeATCTSchema(SitemapSchema, moveDiscussion=False)
+SitemapSchema['relatedItems'].schemata='metadata'
+SitemapSchema['relatedItems'].widget.visible = {'edit':'invisible', 'view':'invisible'}
+
+class Sitemap(base.ATCTContent):
+    """Search engine Sitemap content type"""
+    implements(ISitemap)
+
+    portal_type = "Sitemap"
+    schema = SitemapSchema
+
+    #title = atapi.ATFieldProperty('title')
+    #description = atapi.ATFieldProperty('description')
+
+    def at_post_create_script(self):
+        # Set default layout on creation
+        default_layout = SITEMAPS_VIEW_MAP[self.getSitemapType()]
+        self._setProperty('layout', default_layout)
+
+    def availablePortalTypes(self):
+        pt = getToolByName(self, 'portal_types')
+        types = pt.listContentTypes()
+        return atapi.DisplayList(zip(types,types))
+
+    def getWorkflowStates(self):
+        pw = getToolByName(self,'portal_workflow')
+        states = list(set([v for k,v in pw.listWFStatesByTitle()]))
+        states.sort()
+        return atapi.DisplayList(zip(states, states))
+
+    def getWorkflowTransitions(self):
+        wf_trans = []
+        pw = getToolByName(self,'portal_workflow')
+        for wf_id in pw.getWorkflowIds():
+            wf = pw.getWorkflowById(wf_id)
+            if not wf:
+                continue
+            for wf_tr in wf.transitions.values():
+                if wf_tr.after_script_name in AVAILABLE_WF_SCRIPTS:
+                    wf_trans.append(("%s#%s" % (wf_id,wf_tr.id),
+                        "%s : %s (%s)" % (wf_id,wf_tr.id,wf_tr.title_or_id())))
+        return atapi.DisplayList(wf_trans)
+
+    def setPingTransitions(self, value, **kw):
+        """Add 'Ping sitemap' afterscript for selected workflow transitions.
+        """
+        self.getField('pingTransitions').set(self, value)
+        if not IS_PLONE_3:
+            # Update Workflow if needed
+            pw = getToolByName(self, 'portal_workflow')
+            #ping_googlesitemap = PING_EMETHODS_MAP[self.getSitemapType()]
+            transmap = {}
+            for key in value:
+                if key.find('#')>0:
+                    ids = key.split('#')
+                    wfid = ids[0]
+                    if not wfid in transmap.keys():
+                        transmap[wfid]=[]
+                    transmap[wfid].append(ids[1])
+            for wfid in transmap.keys():
+                workflow = pw.getWorkflowById(wfid)
+                if ping_googlesitemap not in workflow.scripts.objectIds():
+                    workflow.scripts.manage_addProduct['ExternalMethod'].manage_addExternalMethod(
+                        ping_googlesitemap,
+                        'Ping sitemap',
+                        'qPloneGoogleSitemaps.ping_googlesitemap',
+                        ping_googlesitemap)
+                transitions_set = transmap[wfid]
+                for transition in workflow.transitions.values():
+                    trid = transition.id
+                    tras = transition.after_script_name
+                    if (tras == '') and (trid in transitions_set):
+                        #set
+                        after_script = ping_googlesitemap
+                    elif (tras == ping_googlesitemap) and not (trid in transitions_set):
+                        #reset
+                        after_script = ''
+                    else:
+                        #avoid properties set
+                        continue
+                    transition.setProperties(title=transition.title,
+                        new_state_id=transition.new_state_id,
+                        after_script_name=after_script,
+                        actbox_name=transition.actbox_name)
+
+atapi.registerType(Sitemap, PROJECTNAME)
Index: qPloneGoogleSitemaps/tags/0.8.2/events.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/events.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/events.py	(revision 637)
@@ -0,0 +1,22 @@
+try:
+    from Products.DCWorkflow.events import AfterTransitionEvent
+except ImportError:
+    # Copy AfterTransitionEvent from Plone-3/Products.DCWorkflow.events
+    from zope.interface import implements
+    from zope.app.event.objectevent import ObjectEvent
+    from Products.qPloneGoogleSitemaps.interfaces import *
+
+    class TransitionEvent(ObjectEvent):
+        implements(ITransitionEvent)
+
+        def __init__(self, obj, workflow, old_state, new_state, transition, status, kwargs):
+            ObjectEvent.__init__(self, obj)
+            self.workflow = workflow
+            self.old_state = old_state
+            self.new_state = new_state
+            self.transition = transition
+            self.status = status
+            self.kwargs = kwargs
+
+    class AfterTransitionEvent(TransitionEvent):
+        implements(IAfterTransitionEvent)
Index: qPloneGoogleSitemaps/tags/0.8.2/handlers.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/handlers.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/handlers.py	(revision 637)
@@ -0,0 +1,26 @@
+from zope.component import adapter
+from Products.CMFCore.utils import getToolByName
+#from Products.qPloneGoogleSitemaps.events import AfterTransitionEvent
+from Products.qPloneGoogleSitemaps.interfaces import IAfterTransitionEvent
+from Products.qPloneGoogleSitemaps.utils import ping_google
+
+@adapter(IAfterTransitionEvent)
+def pingGoogle(event):
+    tr_id = getattr(event.transition,'id','')
+    if not tr_id:
+        # object under creation
+        return 0
+
+    object = event.object
+    catalog = getToolByName(object, 'portal_catalog')
+
+    sitemaps = [b.getObject() for b in catalog(portal_type='Sitemap')]
+    if sitemaps:
+        plone_home = getToolByName(object, 'portal_url').getPortalObject().absolute_url()
+        wftrans_name = "%s#%s" % (event.workflow.id, tr_id)
+        obj_ptype = object.portal_type
+        for sm in sitemaps:
+            if wftrans_name in sm.getPingTransitions() \
+               and obj_ptype in sm.getPortalTypes():
+                ping_google(plone_home, sm.id)
+    return 0
Index: qPloneGoogleSitemaps/tags/0.8.2/interfaces.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/interfaces.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/interfaces.py	(revision 637)
@@ -0,0 +1,36 @@
+from zope import schema
+from zope.interface import Interface
+
+from zope.app.container.constraints import contains
+from zope.app.container.constraints import containers
+
+from Products.qPloneGoogleSitemaps import qPloneGoogleSitemapsMessageFactory as _
+
+# -*- extra stuff goes here -*-
+
+class ISitemap(Interface):
+    """Search engine Sitemap content type"""
+
+try:
+    from Products.DCWorkflow.interfaces import IAfterTransitionEvent
+except ImportError:
+    # Copy IAfterTransitionEvent from Plone-3/Products.DCWorkflow.interfaces
+    from zope.interface import Interface, Attribute
+    from zope.app.event.interfaces import IObjectEvent
+
+    class ITransitionEvent(IObjectEvent):
+        """An event that's fired upon a workflow transition.
+        """
+
+        workflow = Attribute(u"The workflow definition triggering the transition")
+        old_state = Attribute(u"The state definition of the workflow state before the transition")
+        new_state = Attribute(u"The state definition of the workflow state before after transition")
+        transition = Attribute(u"The transition definition taking place. "
+                                "May be None if this is the 'transition' to the initial state.")
+        status = Attribute(u"The status dict of the object.")
+        kwargs = Attribute(u"Any keyword arguments passed to doActionFor() when the transition was invoked")
+
+    class IAfterTransitionEvent(ITransitionEvent):
+
+        """An event that's fired after a workflow transition.
+        """
Index: qPloneGoogleSitemaps/tags/0.8.2/profiles/default/actionicons.xml
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/profiles/default/actionicons.xml	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/profiles/default/actionicons.xml	(revision 637)
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<action-icons>
+ <action-icon category="controlpanel"
+              action_id="qPloneGoogleSitemaps"
+              title="Google Sitemaps" priority="0"
+              icon_expr="qplonegooglesitemaps.gif"/>
+</action-icons>
Index: qPloneGoogleSitemaps/tags/0.8.2/profiles/default/catalog.xml
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/profiles/default/catalog.xml	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/profiles/default/catalog.xml	(revision 637)
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<object name="portal_catalog" meta_type="CMF Catalog">
+ <index name="hasMobileContent" meta_type="FieldIndex">
+  <indexed_attr value="hasMobileContent"/>
+ </index>
+</object>
Index: qPloneGoogleSitemaps/tags/0.8.2/profiles/default/controlpanel.xml
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/profiles/default/controlpanel.xml	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/profiles/default/controlpanel.xml	(revision 637)
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<object name="portal_controlpanel" meta_type="Plone Control Panel Tool">
+ <configlet title="Google Sitemaps" action_id="qPloneGoogleSitemaps"
+    appId="qPloneGoogleSitemaps" category="Products" condition_expr=""
+    url_expr="string:${portal_url}/prefs_gsm_overview"
+    visible="True">
+  <permission>Manage portal</permission>
+ </configlet>
+</object>
Index: qPloneGoogleSitemaps/tags/0.8.2/profiles/default/factorytool.xml
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/profiles/default/factorytool.xml	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/profiles/default/factorytool.xml	(revision 637)
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<!-- This import step informs the portal_factory tool that we want it to
+     manage our content types. This tool deals with the fact that in
+     Archetypes and most CMF types, the object is actually created before
+     the first edit form is filled in. That is, there are no true "add forms".
+     The factory tool creates a temporary object in lieu of an "add form",
+     and then moves the object to the proper location after it has been
+     successfully saved.
+  -->
+<object name="portal_factory" meta_type="Plone Factory Tool">
+  <factorytypes>
+    <!-- -*- extra stuff goes here -*- -->
+    <type portal_type="Sitemap" />
+  </factorytypes>
+</object>
Index: qPloneGoogleSitemaps/tags/0.8.2/profiles/default/metadata.xml
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/profiles/default/metadata.xml	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/profiles/default/metadata.xml	(revision 637)
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<metadata>
+  <version>0.8.1</version>
+</metadata>
Index: qPloneGoogleSitemaps/tags/0.8.2/profiles/default/propertiestool.xml
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/profiles/default/propertiestool.xml	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/profiles/default/propertiestool.xml	(revision 637)
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<object name="portal_properties" meta_type="Plone Properties Tool">
+ <object name="navtree_properties" meta_type="Plone Property Sheet">
+  <property name="metaTypesNotToList" type="lines" purge="False">
+   <element value="Sitemap"/>
+  </property>
+ </object>
+ <object name="site_properties" meta_type="Plone Property Sheet">
+  <property name="types_not_searched" type="lines" purge="False">
+   <element value="Sitemap"/>
+  </property>
+ </object>
+ <object name="googlesitemap_properties" meta_type="Plone Property Sheet">
+  <property name="verification_filenames" type="lines" />
+ </object>
+</object>
Index: qPloneGoogleSitemaps/tags/0.8.2/profiles/default/rolemap.xml
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/profiles/default/rolemap.xml	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/profiles/default/rolemap.xml	(revision 637)
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<rolemap>
+  <permissions>
+    <!-- -*- extra stuff goes here -*- -->
+    <permission name="qPloneGoogleSitemaps: Add Sitemap" acquire="False">
+      <role name="Manager" />
+      <role name="Contributor" />
+    </permission>
+  </permissions>
+</rolemap>
Index: qPloneGoogleSitemaps/tags/0.8.2/profiles/default/skins.xml
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/profiles/default/skins.xml	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/profiles/default/skins.xml	(revision 637)
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<object name="portal_skins">
+ <object name="qPloneGoogleSitemaps" meta_type="Filesystem Directory View"
+    directory="qPloneGoogleSitemaps/skins/qPloneGoogleSitemaps"/>
+  <skin-path name="*">
+  <layer name="qPloneGoogleSitemaps" insert-after="custom"/>
+ </skin-path>
+</object>
Index: qPloneGoogleSitemaps/tags/0.8.2/profiles/default/types.xml
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/profiles/default/types.xml	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/profiles/default/types.xml	(revision 637)
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<!-- This file registers new types with portal_types. The types are
+     then configured with the corresponding files in types/*.xml. Note
+     that spaces are allowed in type names, but the corresponding XML file
+     uses an underscore instead. The "Factory-based Type Information with
+     dynamic views" refers to an FTI from Products.CMFDynamicViewFTI,
+     which supports Plone's "display" menu.
+  -->
+<object name="portal_types" meta_type="Plone Types Tool">
+  <!-- -*- extra stuff goes here -*- -->
+  <object name="Sitemap"
+    meta_type="Factory-based Type Information with dynamic views" />
+</object>
Index: qPloneGoogleSitemaps/tags/0.8.2/profiles/default/types/Sitemap.xml
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/profiles/default/types/Sitemap.xml	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/profiles/default/types/Sitemap.xml	(revision 637)
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<object name="Sitemap"
+   meta_type="Factory-based Type Information with dynamic views"
+   xmlns:i18n="http://xml.zope.org/namespaces/i18n">
+  <property name="title">Sitemap</property>
+  <property name="description">Search engine's Sitemap</property>
+  <property name="content_meta_type">Sitemap</property>
+  <property name="content_icon">document_icon.gif</property>
+  <property name="product">qPloneGoogleSitemaps</property>
+  <property name="factory">addSitemap</property>
+  <property name="immediate_view">atct_edit</property>
+  <property name="global_allow">True</property>
+  <property name="filter_content_types">False</property>
+  <property name="allow_discussion">False</property>
+  <property name="default_view">sitemap.xml</property>
+  <property name="view_methods">
+    <element value="sitemap.xml" />
+    <element value="mobile-sitemap.xml" />
+    <element value="news-sitemap.xml" />
+    <element value="base_view" />
+  </property>
+  <alias from="(Default)" to="(dynamic view)" />
+  <alias from="edit" to="atct_edit" />
+  <alias from="sharing" to="@@sharing" />
+  <alias from="view" to="(selected layout)" />
+  <action title="View" action_id="view" category="object" condition_expr=""
+    url_expr="string:${object_url}/" visible="True">
+    <permission value="View" />
+  </action>
+  <action title="Edit" action_id="edit" category="object" condition_expr=""
+    url_expr="string:${object_url}/edit" visible="True">
+    <permission value="Modify portal content" />
+  </action>
+</object>
Index: qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/getNotAddableTypes.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/getNotAddableTypes.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/getNotAddableTypes.py	(revision 637)
@@ -0,0 +1,12 @@
+## Script (Python) "getNotAddableTypes"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=
+##
+# customize this script to filter addable portal types based on
+# context, the current user or other criteria
+return ('Favorite','Sitemap')
Index: qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/gsm_create_verify_file.cpy
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/gsm_create_verify_file.cpy	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/gsm_create_verify_file.cpy	(revision 637)
@@ -0,0 +1,25 @@
+## Script (Python) "create_verify_file"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=verify_filename
+##title=Create file for verification
+##
+
+from Products.CMFCore.utils import getToolByName
+from Products.qPloneGoogleSitemaps.utils import BadRequestException
+portal = getToolByName(context, 'portal_url').getPortalObject()
+try:
+    portal.manage_addFile(verify_filename,title='Verification File')
+    portal[verify_filename].manage_addProperty('CreatedBy','qPloneGoogleSitemaps','string')
+except BadRequestException:
+    pass
+else:
+    props = getToolByName(context,'portal_properties').googlesitemap_properties
+    vfiles = list(props.getProperty('verification_filenames',[]))
+    vfiles.append(verify_filename)
+    props.manage_changeProperties(verification_filenames = vfiles)
+
+return state.set(portal_status_message = 'Plone Google Sitemap updated.')
Index: qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/gsm_create_verify_file.cpy.metadata
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/gsm_create_verify_file.cpy.metadata	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/gsm_create_verify_file.cpy.metadata	(revision 637)
@@ -0,0 +1,14 @@
+#
+# $$
+#
+[default]
+title=Create verify file
+
+[security]
+View=0:Authenticated
+
+[validators]
+
+[actions]
+action.success=traverse_to:string:prefs_gsm_verification
+action.failure=traverse_to:string:prefs_gsm_verification
Index: qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/gsm_edit_settings.cpy
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/gsm_edit_settings.cpy	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/gsm_edit_settings.cpy	(revision 637)
@@ -0,0 +1,68 @@
+## Script (Python) "gsm_edit_settings"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##bind state=state
+##parameters=
+##title=Configure Plone Google Sitemap
+##
+
+from Products.CMFCore.utils import getToolByName
+from Products.qPloneGoogleSitemaps.utils import ping_google
+
+portal_url = getToolByName(context,'portal_url')
+portalURL = portal_url()
+portal = portal_url.getPortalObject()
+req = context.REQUEST
+message = ""
+
+def addSMByType(parent, sm_type, sm_id):
+    new_id = portal.invokeFactory(id=sm_id, type_name="Sitemap", sitemapType=sm_type)
+    sm = getattr(portal, new_id)
+    if sm:
+        sm.markCreationFlag()
+        return new_id
+    return None
+
+if req.get('form.button.AddContent', False):
+    new_id = addSMByType(portal, 'content', 'sitemap.xml')
+    if new_id:
+        return req.RESPONSE.redirect("%s/%s/edit" % (portalURL, new_id))
+    else:
+        message = "Can't create content sitemap"
+elif req.get('form.button.AddMobile', False):
+    new_id = addSMByType(portal, 'mobile', 'mobile-sitemap.xml')
+    if new_id:
+        return req.RESPONSE.redirect("%s/%s/edit" % (portalURL, new_id))
+    else:
+        message = "Can't create mobile sitemap"
+elif req.get('form.button.AddNews', False):
+    new_id = addSMByType(portal, 'news', 'news-sitemap.xml')
+    if new_id:
+        return req.RESPONSE.redirect("%s/%s/edit" % (portalURL, new_id))
+    else:
+        message = "Can't create news sitemap"
+else:
+    smselected = req.get('smselected', [])
+
+    if req.get('form.button.Delete', False):
+        portal.manage_delObjects(ids=smselected[:])
+        message = "Succesfully deleted: %s" % smselected
+
+    elif req.get('form.button.Ping', False):
+        pinged = []
+        message = "Google pinged. It will review your sitemap as soon as it will be able to. Processed: %s"
+        for sm_id in smselected:
+            try:
+                ping_google(portalURL, sm_id)
+            except:
+                message = "Cannot contact Google. Try again in a while. But pinged for: %s"
+                break
+            else:
+                pinged.append(sm_id)
+        message = message % pinged
+
+return state.set(next_action='traverse_to:string:prefs_gsm_settings',
+                 portal_status_message = message)
Index: qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/hasMobileContent.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/hasMobileContent.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/hasMobileContent.py	(revision 637)
@@ -0,0 +1,9 @@
+## Script (Python) "searchForGoogleSitemapObjects"
+##bind container=
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=path=None
+##
+return len(getattr(context,'mobile_content',''))>0
Index: qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_overview.cpt
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_overview.cpt	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_overview.cpt	(revision 637)
@@ -0,0 +1,138 @@
+<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', {})">
+
+    <div class="documentEditable">
+        <ul class="contentViews">
+            <li class="selected">
+                <a href=""
+                   tal:attributes="href string:${template/id}"
+                   i18n:translate="label_properties">Overview</a>
+            </li>
+
+            <li class="">
+                <a href=""
+                   tal:attributes="href string: prefs_gsm_verification"
+                   i18n:translate="label_properties">Verification</a>
+            </li>
+
+            <li class="">
+               <a href=""
+                  tal:attributes="href string: prefs_gsm_settings">
+                  Settings</a>
+            </li>
+
+        </ul>
+
+        <div class="contentActions">
+        &nbsp;
+        </div>
+        <div class="documentContent">
+            <h1 i18n:translate="heading_plonegooglesitemaps">Plone Google Sitemaps</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"
+                i18n:translate="description_plonegooglesitemaps">
+            Plone Google Sitemaps allows search engine to have better visibility of your 
+            <tal:url replace="portal_url" i18n:name="url">example.com</tal:url> 
+            Plone website.
+            </div>
+ 
+            <h2>Post-install instructions</h2>
+
+            <p>Congratulations, you've installed Plone Google Sitemaps product from 
+            Quintagroup.</p>
+
+<!--              -->
+            <dl tal:define="view here/@@sitemap_settings;
+                            sitemaps view/sitemapsURLByType;
+                            all sitemaps/all | nothing">
+            <dt>First step</dt>
+            <dd>To enable Google Sitemaps on your site, add a sitemap at 
+            <a href="prefs_gsm_settings"
+                tal:attributes="href string:$portal_url/prefs_gsm_settings">Google Sitemap settings</a> tab.
+            You can add one of the following sitemap types: <strong>content, mobile or news</strong>.
+            Different sitemap types index their own content and do not depend on other sitemaps.
+            </dd>
+            <dt>Second step</dt>
+            <dd>
+            <tal:condition-true condition="all">
+            Your site is already Google Sitemaps enabled. Check 
+            <tal:loop repeat="smdata all">
+            <a href="google-sitemaps"
+               tal:attributes="href smdata/url"
+               tal:content="smdata/id">sitemap id</a>
+            <span tal:omit-tag=""
+                  tal:condition="not: repeat/smdata/end">,</span>
+            </tal:loop>
+            to see generated sitemap document(s) of your website. 
+            </tal:condition-true>
+            <tal:condition-false condition="not:all">
+            After adding Google Sitemap(s), check whether they where generated correctly.
+            </tal:condition-false>
+
+            They should contain URLs of all published objects. If you see
+            something that should not be indexed by Google, retract objects or
+            exclude sections with robots.txt (that GoogleBot obeys).
+            </dd>
+            <dt>Third step</dt>
+            <dd>Make Google know about your sitemap(s):
+            <ol>
+                <li>go to <a href="https://www.google.com/webmasters/sitemaps">Google 
+                Sitemaps Central</a> (logging in / signing up as necessary)</li>
+                <tal:condition-true condition="all">
+
+                <li>add your sitemap URL(s): 
+                  <ul>
+                    <tal:loop repeat="smdata all">
+                    <li>
+                      <code tal:content="smdata/url">http://example.com/sitemaps.xml</code>
+                    </li>
+                    </tal:loop>
+                  </ul>
+                </tal:condition-true>
+
+                <tal:condition-false condition="not:all">
+                <li>This step will display urls to your sitemap(s) after adding it(them) in previous step(s)</li>
+                </tal:condition-false>
+
+            </ol>
+            It takes a while for your sitemap to be processed by Google. 
+            It usually processes the sitemap within 24 hours so do not expect your 
+            submission to be processed instantly.
+            </dd>
+            </dl>
+            <h2>Helpful resources</h2>
+            <dl>
+            <dt><a href="http://groups-beta.google.com/group/google-sitemaps">google-sitemaps discussion group</a><dt>
+            <dd>As Google Sitemaps is emerging technology, we are all learning from it.
+            Google set up discussion group to find your ideas about it. You can find 
+            help from other webmasters as they usually found answers to questions that 
+            you have. If not you are free to ask, to praise and to ramble ;)</dd>
+            <dt><a href="http://plone.org/products/qplonegooglesitemaps">Plone Google Sitemaps 
+            product page</a></dt>
+            <dd>Stay tuned. We are preparing new and fresh releases. Product page will 
+            help you to keep informed about them.</dd>
+            <dt><a href="http://projects.quintagroup.com/products/wiki/qPloneGoogleSitemaps">Plone Google Sitemaps documentation area.</dt>
+            <dd>Visit product's documentation area to familiarize yourself with qPloneGoogleSitemaps product in
+            more details.
+            </dd>
+
+            </dl>
+        </div>
+    </div>
+  </div>
+</body>
+</html>
Index: qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_settings.cpt
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_settings.cpt	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_settings.cpt	(revision 637)
@@ -0,0 +1,161 @@
+<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', {})">
+
+        <div class="documentEditable">
+            <ul class="contentViews">
+                <li class="">
+                    <a href=""
+                       tal:attributes="href string: prefs_gsm_overview">
+                       Overview</a>
+                </li>
+                <li class="">
+                    <a href=""
+                       tal:attributes="href string:prefs_gsm_verification">
+                       Verification</a>
+                </li>
+                <li class="selected">
+                <a href=""
+                   tal:attributes="href string:${template/id}">
+                   Settings</a>
+                </li>
+            </ul>
+            <div class="contentActions">
+            &nbsp;
+            </div>
+            <div class="documentContent">
+                <h1 i18n:translate="heading_plonegooglesitemaps">Plone Google Sitemaps</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>
+
+                
+
+                <form name="edit_form"
+                    action="."
+                    method="post"
+                    enctype="multipart/form-data"
+                    tal:attributes="action string:${here_url}/${template/getId}" >
+
+                <fieldset tal:define="view here/@@sitemap_settings;
+                                      sitemaps view/sitemapsDict;">
+                    <legend i18n:translate="legend_plonesitemap_configuration_details">
+                        Google SiteMaps configuration
+                    </legend>
+
+                    <table class="listing"
+                           summary="Sitemaps management"
+                           i18n:attributes="summary summary_sitemaps_management;"
+                           tal:condition="sitemaps">
+                        <thead>
+                            <tr>
+                            <th></th>
+                            <th>Type</th>
+                            <th>Edit Sitemap</th>
+                            <th>Size (bytes)</th>
+                            <th>Entries</th>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            <tal:items tal:repeat="data sitemaps">
+                            <tr tal:define="oddrow repeat/data/odd;"
+                                tal:attributes="class python:test(oddrow, 'even', 'odd')" >
+                                <td>
+                                <input type="checkbox" class="noborder"
+                                        name="smselected:list" id="#" value="#" tabindex=""
+                                        tal:attributes="value    data/sm_id;
+                                                        id       string:cb_${data/sm_id};
+                                                        alt      data/sm_type;
+                                                        title    data/sm_type;
+                                                        tabindex tabindex/next;" />
+                                </td>
+                                <td>
+                                <strong tal:content="data/sm_type"> Sitemap type </strong>
+                                </td>
+                                <td>
+                                <a alt="" title="" href="http://"
+                                    tal:attributes="href string:${data/sm_url}/edit;
+                                                    title data/sm_id;
+                                                    alt string:${data/sm_id}"
+                                    tal:content="data/sm_id">Edit default sitemap type </a>
+                                </td>
+                                <td tal:content="string:${data/sm_size}"> 10 000 in bytes
+                                </td>
+                                <td tal:content="string:${data/sm_entries}"> 50 000 items
+                                </td>
+                            </tr>
+                            </tal:items>
+                            <tr>
+                            <th colspan="3"><strong>Limitations:</strong></th>
+                            <th><strong>10,485,760</strong></th>
+                            <th><strong>50,000</strong></th>
+                            </tr>
+                        </tbody>
+                        </table>
+
+                        <div class="formControls"
+                            tal:define="process_creation request/process_creation|nothing;"
+                            i18n:domain="plone">
+                            <input class="context"
+                                tabindex=""
+                                type="submit"
+                                name="form.button.AddContent"
+                                value="Add Content Sitemap"
+                                i18n:attributes="value"
+                                tal:condition="not:view/hasContentSM"
+                                tal:attributes="tabindex tabindex/next;" />
+                            <input class="context"
+                                tabindex=""
+                                type="submit"
+                                name="form.button.AddMobile"
+                                value="Add Mobile Sitemap"
+                                i18n:attributes="value"
+                                tal:condition="not:view/hasMobileSM"
+                                tal:attributes="tabindex tabindex/next;" />
+                            <input class="context"
+                                tabindex=""
+                                type="submit"
+                                name="form.button.AddNews"
+                                value="Add News Sitemap"
+                                i18n:attributes="value"
+                                tal:condition="not:view/hasNewsSM"
+                                tal:attributes="tabindex tabindex/next;" />
+                            <tal:block condition="sitemaps">
+                            <input class="context"
+                                tabindex=""
+                                type="submit"
+                                name="form.button.Delete"
+                                value="Delete"
+                                i18n:attributes="value"
+                                tal:attributes="tabindex tabindex/next;" />
+                            <input class="context"
+                                tabindex=""
+                                type="submit"
+                                name="form.button.Ping"
+                                value="Ping"
+                                i18n:attributes="value"
+                                tal:attributes="tabindex tabindex/next;" />
+                            </tal:block>
+                        </div>
+
+                    <tal:block condition="sitemaps">
+                    <p><strong>Note</strong>: Ping button allows you to make additional ping (apart from automatic one).</p>
+                    </tal:block>
+
+                    </fieldset>
+                    <input type="hidden" name="form.submitted" value="1" />
+                </form>
+            </div>
+        </div>
+    </div>
+</body>
+</html>
Index: qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_settings.cpt.metadata
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_settings.cpt.metadata	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_settings.cpt.metadata	(revision 637)
@@ -0,0 +1,19 @@
+#
+# $$
+#
+[default]
+title=Plone Google Sitemap Configuration
+
+[security]
+View=0:Authenticated
+
+[validators]
+
+[actions]
+action.success..AddContent=traverse_to:string:gsm_edit_settings
+action.success..AddMobile=traverse_to:string:gsm_edit_settings
+action.success..AddNews=traverse_to:string:gsm_edit_settings
+action.success..Delete=traverse_to:string:gsm_edit_settings
+action.success..Ping=traverse_to:string:gsm_edit_settings
+action.success=traverse_to:string:gsm_edit_settings
+action.failure=redirect_to:string:prefs_gsm_settings
Index: qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_verification.cpt
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_verification.cpt	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_verification.cpt	(revision 637)
@@ -0,0 +1,83 @@
+<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', {});
+                    view here/@@sitemap_settings;">
+
+        <div class="documentEditable">
+            <ul class="contentViews">
+                <li class="">
+                    <a href=""
+                    tal:attributes="href string: prefs_gsm_overview"
+                    i18n:translate="label_properties">Overview</a>
+                </li>
+
+                <li class="selected">
+                    <a href=""
+                    tal:attributes="href string:${template/id}"
+                    i18n:translate="label_properties">Verification</a>
+                </li>
+
+                <li class="">
+                <a href=""
+                    tal:attributes="href string:prefs_gsm_settings">
+                    Settings</a>
+                </li>
+
+            </ul>
+
+            <div class="contentActions">
+            &nbsp;
+            </div>
+            <div class="documentContent">
+                <h1 i18n:translate="heading_plonegooglesitemaps">Plone Google Sitemaps</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>
+
+                <form tal:define="vfiles view/getVerificationFiles;
+                                  step1 python:test(vfiles, 'discreet', '');
+                                  step2 python:test(not vfiles, 'discreet', '')">
+                    <ol>
+                        <li tal:attributes="class step1">
+                            Go to <a href=""
+                                        tal:attributes="href string: https://www.google.com/webmasters/sitemaps/verify?siteUrl=${portal/absolute_url}&hl=en"
+                                        tal:omit-tag="step1">
+                        site verification page</a> and login if necessary.
+                        </li>
+                        <li tal:attributes="class step1">Provide verification file name:
+                            <p><input name="verify_filename" id="verify_filename" type="text"></p>
+                        </li>
+                        <li tal:attributes="class step1">
+                                <input name="form.button.CreateFile" id="create_file" type="submit" value="Create verification file"/>
+                                <input type="hidden" name="form.submitted" value="1" />
+                        </li>
+                        <li class="discreet"
+                            tal:attributes="class step2">Go to <a href=""
+                                        tal:attributes="href string: https://www.google.com/webmasters/sitemaps/verify?siteUrl=${portal/absolute_url}&hl=en"
+                                        tal:omit-tag="step2">
+                        site verification page</a> and press "Check Status" button.
+                        </li>
+                        <li class="discreet"
+                            tal:attributes="class step2"
+                            tal:condition="vfiles">
+                            <strong>Existent verification files:</strong>
+                            <ul><li tal:repeat="vf vfiles"
+                                    tal:content="vf">verification file id
+                            </li></ul>
+                        </li>
+                    </ol>
+                </form>
+            </div>
+        </div>
+    </div>
+</body>
+</html>
Index: qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_verification.cpt.metadata
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_verification.cpt.metadata	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/skins/qPloneGoogleSitemaps/prefs_gsm_verification.cpt.metadata	(revision 637)
@@ -0,0 +1,14 @@
+#
+# $$
+#
+[default]
+title=verification
+
+[security]
+View=0:Authenticated
+
+[validators]
+
+[actions]
+action.success..CreateFile=traverse_to:string:gsm_create_verify_file
+action.failure=traverse_to:string:prefs_gsm_verification
Index: qPloneGoogleSitemaps/tags/0.8.2/tests/XMLParser.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/tests/XMLParser.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/tests/XMLParser.py	(revision 637)
@@ -0,0 +1,23 @@
+from xml.parsers.expat import ParserCreate
+import re
+
+def parse(data):
+    def start_el(name, attrs):
+        ParsedXML['start'][name] = attrs
+
+    def char_handler(data):
+        patt = re.compile('\S+', re.UNICODE)
+        if patt.search(data):
+            ParsedXML['data'].append(data)
+
+    ParsedXML = {'start':{}, 'end':[], 'data':[]}
+    parser = ParserCreate()
+    parser.StartElementHandler = start_el
+    parser.CharacterDataHandler = char_handler
+    parser.Parse(data)
+    return ParsedXML
+
+def hasURL(xml, url):
+    parsed_sitemap = parse(xml)
+    data = parsed_sitemap['data']
+    return url in data
Index: qPloneGoogleSitemaps/tags/0.8.2/tests/framework.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/tests/framework.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/tests/framework.py	(revision 637)
@@ -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: qPloneGoogleSitemaps/tags/0.8.2/tests/runalltests.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/tests/runalltests.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/tests/runalltests.py	(revision 637)
@@ -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: qPloneGoogleSitemaps/tags/0.8.2/tests/testqPloneGoogleSitemaps.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/tests/testqPloneGoogleSitemaps.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/tests/testqPloneGoogleSitemaps.py	(revision 637)
@@ -0,0 +1,397 @@
+#
+# Tests for qPloneGoogleSitemaps
+#
+import re, sys
+from urllib import urlencode
+from StringIO import StringIO
+
+from Products.Five import zcml
+from Products.Five import fiveconfigure
+from Products.PloneTestCase import PloneTestCase
+
+from Products.CMFPlone.utils import _createObjectByType
+
+import Products.qPloneGoogleSitemaps
+from Products.qPloneGoogleSitemaps.config import ping_googlesitemap
+
+from XMLParser import parse, hasURL
+Products.qPloneGoogleSitemaps.config.testing = 1
+
+PRODUCT = 'qPloneGoogleSitemaps'
+PRODUCTS = (PRODUCT,)
+
+# Import configure.zcml for qPloneGoogleSitemaps
+fiveconfigure.debug_mode = True
+zcml.load_config('configure.zcml', Products.qPloneGoogleSitemaps)
+fiveconfigure.debug_mode = False
+
+PloneTestCase.installProduct(PRODUCT)
+PloneTestCase.setupPloneSite(extension_profiles=("Products.%s:default" % PRODUCT,))
+
+class TestqPloneGoogleSitemapsInstallation(PloneTestCase.PloneTestCase):
+
+    def afterSetUp(self):
+        self.loginAsPortalOwner()
+
+    def testType(self):
+        pt = self.portal.portal_types
+        self.assert_('Sitemap' in pt.objectIds(), 
+            'No "Sitemap" type after installation')
+        #Test views
+        views = pt.getTypeInfo('Sitemap').view_methods
+        self.assert_('sitemap.xml' in views, 
+            'No "sitemap.xml" view for Sitemap type')
+        self.assert_('mobile-sitemap.xml' in views, 
+            'No "mobile-sitemap.xml" view for Sitemap type')
+        self.assert_('news-sitemap.xml' in views, 
+            'No "news-sitemap.xml" view for Sitemap type')
+
+    def testGSMProperties(self):
+        pp = self.portal.portal_properties
+
+        # Test types_not_searched
+        self.assert_("Sitemap" in pp['site_properties'].getProperty('types_not_searched'), 
+            'No "Sitemap" added to types not searched on installation')
+        # Test metaTypesNotToList
+        self.assert_("Sitemap" in pp['navtree_properties'].getProperty('metaTypesNotToList'), 
+            'No "Sitemap" added to types not to list on installation')
+
+        # Test 'googlesitemap_properties'
+        self.assert_('googlesitemap_properties' in pp.objectIds(), 
+            'No "googlesitemap_properties" after installation')
+        qsmprops = pp['googlesitemap_properties']
+        self.assert_(qsmprops.hasProperty('verification_filenames'),
+            'No "verification_filenames" property added on installation')
+
+    def testSkins(self):
+        ps = self.portal.portal_skins
+        self.assert_('qPloneGoogleSitemaps' in ps.objectIds(), 
+            'No "qPloneGoogleSitemaps" skin layer in portal_skins')
+        self.assert_('qPloneGoogleSitemaps' in ps.getSkinPath(ps.getDefaultSkin()),
+            'No "qPloneGoogleSitemaps" skin layer in default skin')
+
+    def testConfiglet(self):
+        cp = self.portal.portal_controlpanel
+        self.assert_([1 for ai in cp.listActionInfos() if ai['id']=='qPloneGoogleSitemaps'], 
+            'No "qPloneGoogleSitemaps" configlet added to plone control panel')
+
+    def testCatalog(self):
+        catalog = self.portal.portal_catalog
+        self.assert_('hasMobileContent' in catalog.indexes(),
+            'No "hasMobileContent" index in portal_catalog')
+
+
+class TestSitemapType(PloneTestCase.FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.loginAsPortalOwner()
+        self.auth = 'admin:admin'
+        self.contentSM = _createObjectByType('Sitemap', self.portal, id='google-sitemaps')
+        self.portal.portal_membership.addMember('admin', 'admin', ('Manager',), [])
+
+    def testFields(self):
+        field_ids = map(lambda x:x.getName(), self.contentSM.Schema().fields())
+        # test old Sitemap settings fields
+        self.assert_('id' in field_ids)
+        self.assert_('portalTypes' in field_ids)
+        self.assert_('states' in field_ids)
+        self.assert_('blackout_list' in field_ids)
+        self.assert_('urls' in field_ids)
+        self.assert_('pingTransitions' in field_ids)
+        # test new sitemap type field
+        self.assert_('sitemapType' in field_ids)
+
+    def testSitemapTypes(self):
+        sitemap_types = self.contentSM.getField('sitemapType').Vocabulary().keys()
+        self.assert_('content' in sitemap_types)
+        self.assert_('mobile' in sitemap_types)
+        self.assert_('news' in sitemap_types)
+
+    def testAutoSetLayout(self):
+        response = self.publish('/%s/createObject?type_name=Sitemap' % \
+                                self.portal.absolute_url(1), basic=self.auth)
+        location = response.getHeader('location')
+        newurl = location[location.find('/'+self.portal.absolute_url(1)):]
+
+        msm_id = 'mobile_sitemap'
+        form = {'id': msm_id,
+                'sitemapType':'mobile',
+                'portalTypes':['Document',],
+                'states':['published'],
+                'form_submit':'Save',
+                'form.submitted':1,
+                }
+        post_data = StringIO(urlencode(form))
+        response = self.publish(newurl, request_method='POST', stdin=post_data, basic=self.auth)
+        msitemap = getattr(self.portal, msm_id)
+
+        self.assertEqual(msitemap.defaultView(), 'mobile-sitemap.xml')
+
+    def txestPingSetting(self):
+        pwf = self.portal.portal_workflow['plone_workflow']
+        self.assertEqual(self.contentSM.getPingTransitions(), ())
+
+        self.contentSM.setPingTransitions(('plone_workflow#publish',))
+        self.assertEqual(self.contentSM.getPingTransitions(), ('plone_workflow#publish',))
+        self.assert_(ping_googlesitemap in pwf.scripts.keys(),"Not add wf script")
+
+
+class TestqPloneGoogleSitemaps(PloneTestCase.FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.loginAsPortalOwner()
+
+        self.workflow = self.portal.portal_workflow
+        self.auth = 'admin:admin'
+        _createObjectByType('Sitemap', self.portal, id='google-sitemaps')
+        self.sitemapUrl = '/'+self.portal.absolute_url(1) + '/google-sitemaps'
+        self.portal.portal_membership.addMember('admin', 'admin', ('Manager',), [])
+        self.gsm_props = self.portal.portal_properties['googlesitemap_properties']
+
+        # Add testing document to portal
+        my_doc = self.portal.invokeFactory('Document', id='my_doc')
+        self.my_doc = self.portal['my_doc']
+        self.my_doc.edit(text_format='plain', text='hello world')
+
+
+
+    def testSitemap(self):
+        sitemap = self.publish(self.sitemapUrl, self.auth).getBody()
+        parsed_sitemap = parse(sitemap)
+        start = parsed_sitemap['start']
+        data = parsed_sitemap['data']
+        self.assertEqual(len(start.keys()), 1)
+        self.assert_('urlset' in start.keys())
+
+        self.workflow.doActionFor(self.my_doc, 'publish')
+
+        sitemap = self.publish(self.sitemapUrl, self.auth).getBody()
+        parsed_sitemap = parse(sitemap)
+        start = parsed_sitemap['start']
+        data = parsed_sitemap['data']
+        self.assertEqual(len(start.keys()), 4)
+        self.assert_('urlset' in start.keys())
+        self.assert_('url' in start.keys())
+        self.assert_('loc' in start.keys())
+        self.assert_('lastmod' in start.keys())
+
+        self.assert_(data[0] == self.my_doc.absolute_url(0), 'Incorect url')
+
+    def testVerificationFileCreation(self):
+        self.portal.gsm_create_verify_file('verif_file')
+
+        vf_created = hasattr(self.portal, 'verif_file')
+        self.assert_(vf_created, 'Verification file not created')
+
+    def testVerificationForm(self):
+        verifyConfigUrl = '/'+self.portal.absolute_url(1) + '/prefs_gsm_verification'
+        verif_config = self.publish(verifyConfigUrl, self.auth).getBody()
+        rexp_input_acitve = re.compile('<input\s+name="verify_filename"\s+([^>]*)>', re.I|re.S)
+        rexp_button_acitve = re.compile('<input\s+name="form.button.CreateFile"\s+([^>]*)>', re.I|re.S)
+        rexp_delete_button = re.compile('<input\s+name="form.button.DeleteFile"\s+[^>]*>', re.I|re.S)
+
+        input_acitve = rexp_input_acitve.search(verif_config)
+        button_acitve = rexp_button_acitve.search(verif_config)
+        delete_button = rexp_delete_button.match(verif_config)
+
+        self.assert_(input_acitve and not 'disabled' in input_acitve.groups(1))
+        self.assert_(button_acitve and not 'disabled' in button_acitve.groups(1))
+        self.assert_(not delete_button)
+
+        self.portal.gsm_create_verify_file('verif_file')
+
+        input_acitve = rexp_input_acitve.search(verif_config)
+        button_acitve = rexp_button_acitve.search(verif_config)
+        delete_button = rexp_delete_button.match(verif_config)
+
+        verif_config = self.publish(verifyConfigUrl, self.auth).getBody()
+        self.assert_(input_acitve and not 'disabled' in input_acitve.groups(1))
+        self.assert_(not delete_button)
+
+    def testMultiplyVerificationFiles(self):
+        verifyConfigUrl = '/'+self.portal.absolute_url(1) + '/prefs_gsm_verification'
+
+        form = {'verify_filename':'verif_file_1',
+                'form.button.CreateFile': 'Create verification file',
+                'form.submitted':1}
+        post_data = StringIO(urlencode(form))
+        response = self.publish(verifyConfigUrl, request_method='POST',
+                                stdin=post_data, basic=self.auth)
+        self.assertEqual(response.getStatus(), 200)
+        self.assert_('verif_file_1' in self.gsm_props.getProperty('verification_filenames',[]),
+                     self.gsm_props.getProperty('verification_filenames',[]))
+
+        form = {'verify_filename':'verif_file_2',
+                'form.button.CreateFile': 'Create verification file',
+                'form.submitted':1}
+        post_data = StringIO(urlencode(form))
+        response = self.publish(verifyConfigUrl, request_method='POST',
+                                stdin=post_data, basic=self.auth)
+        self.assertEqual(response.getStatus(), 200)
+        self.assert_([1 for vf in ['verif_file','verif_file_2'] \
+                      if vf in self.gsm_props.getProperty('verification_filenames',[])],
+                     self.gsm_props.getProperty('verification_filenames',[]))
+
+
+class TestSettings(PloneTestCase.FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.loginAsPortalOwner()
+
+        self.workflow = self.portal.portal_workflow
+        self.gsm_props = self.portal.portal_properties['googlesitemap_properties']
+        self.auth = 'admin:admin'
+        self.contentSM = _createObjectByType('Sitemap', self.portal, id='google-sitemaps')
+
+        self.sitemapUrl = '/'+self.portal.absolute_url(1) + '/google-sitemaps'
+
+        self.portal.portal_membership.addMember('admin', 'admin', ('Manager',), [])
+
+        # Add testing document to portal
+        my_doc = self.portal.invokeFactory('Document', id='my_doc')
+        self.my_doc = self.portal['my_doc']
+        self.my_doc.edit(text_format='plain', text='hello world')
+        self.my_doc_url = self.my_doc.absolute_url()
+
+    def testMetaTypeToDig(self):
+        self.workflow.doActionFor(self.my_doc, 'publish')
+        sitemap = self.publish(self.sitemapUrl, self.auth).getBody()
+        self.assert_(hasURL(sitemap, self.my_doc_url))
+
+        self.contentSM.setPortalTypes([])
+
+        sitemap = self.publish(self.sitemapUrl, self.auth).getBody()
+        self.assert_(not hasURL(sitemap, self.my_doc_url))
+
+        self.contentSM.setPortalTypes(['Document'])
+
+        sitemap = self.publish(self.sitemapUrl, self.auth).getBody()
+        self.assert_(hasURL(sitemap, self.my_doc_url))
+
+    def testStates(self):
+        self.workflow.doActionFor(self.my_doc, 'publish')
+        self.contentSM.setStates(['visible'])
+
+        sitemap = self.publish(self.sitemapUrl, self.auth).getBody()
+        self.assert_(not hasURL(sitemap, self.my_doc_url))
+
+        self.contentSM.setStates(['published'])
+
+        sitemap = self.publish(self.sitemapUrl, self.auth).getBody()
+        self.assert_(hasURL(sitemap, self.my_doc_url))
+
+    def test_blackout_entries(self):
+        self.workflow.doActionFor(self.my_doc, 'publish')
+        self.contentSM.setBlackout_list((self.my_doc.getId(),))
+
+        sitemap = self.publish(self.sitemapUrl, self.auth).getBody()
+        self.assert_(not hasURL(sitemap, self.my_doc_url))
+
+        self.contentSM.setBlackout_list([])
+        sitemap = self.publish(self.sitemapUrl, self.auth).getBody()
+        self.assert_(hasURL(sitemap, self.my_doc_url))
+
+    def test_regexp(self):
+        self.workflow.doActionFor(self.my_doc, 'publish')
+        sitemap = self.publish(self.sitemapUrl, self.auth).getBody()
+        self.assert_(not hasURL(sitemap, self.portal.absolute_url()))
+
+        regexp = "s/\/%s//"%self.my_doc.getId()
+        self.contentSM.setReg_exp([regexp])
+
+        sitemap = self.publish(self.sitemapUrl, self.auth).getBody()
+        self.assert_(hasURL(sitemap, self.portal.absolute_url()))
+
+    def test_add_urls(self):
+        self.contentSM.setUrls(['http://w1', 'w2', '/w3'])
+        w1_url = 'http://w1'
+        w2_url = self.portal.absolute_url() + '/w2'
+        w3_url = self.portal.getPhysicalRoot().absolute_url() + '/w3'
+        sitemap = self.publish(self.sitemapUrl, self.auth).getBody()
+
+        self.assert_(hasURL(sitemap, w1_url))
+        self.assert_(hasURL(sitemap, w2_url))
+        self.assert_(hasURL(sitemap, w3_url))
+
+
+class TestPinging(PloneTestCase.FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.loginAsPortalOwner()
+
+        self.workflow = self.portal.portal_workflow
+        self.gsm_props = self.portal.portal_properties['googlesitemap_properties']
+        self.auth = 'admin:admin'
+        self.portal.portal_membership.addMember('admin', 'admin', ('Manager',), [])
+        # Add sitemaps
+        self.contentSM = _createObjectByType('Sitemap', self.portal, id='google-sitemaps')
+        self.contentSM.setPingTransitions(('plone_workflow#publish',))
+        self.newsSM = _createObjectByType('Sitemap', self.portal, id='news-sitemaps')
+        self.newsSM.setPortalTypes(('News Item','Document'))
+        self.newsSM.setPingTransitions(('plone_workflow#publish',))
+        self.sitemapUrl = '/'+self.portal.absolute_url(1) + '/google-sitemaps'
+        # Add testing document to portal
+        my_doc = self.portal.invokeFactory('Document', id='my_doc')
+        self.my_doc = self.portal['my_doc']
+        my_news = self.portal.invokeFactory('News Item', id='my_news')
+        self.my_news = self.portal['my_news']
+
+    def testAutomatePinging(self):
+        # 1. Check for pinging both sitemaps
+        back_out, myout = sys.stdout, StringIO()
+        sys.stdout = myout
+        self.workflow.doActionFor(self.my_doc, 'publish')
+        myout.seek(0)
+        data = myout.read()
+        sys.stdout = back_out
+        self.assert_('Pinged %s sitemap to google' % self.contentSM.absolute_url() in data,
+                     "Not pinged %s: '%s'" % (self.contentSM.id, data))
+        self.assert_('Pinged %s sitemap to google' % self.newsSM.absolute_url() in data,
+                     "Not pinged %s: '%s'" % (self.newsSM.id, data))
+
+        # 2. Check for pinging only news-sitemap sitemaps
+        back_out, myout = sys.stdout, StringIO()
+        sys.stdout = myout
+        self.workflow.doActionFor(self.my_news, 'publish')
+        myout.seek(0)
+        data = myout.read()
+        sys.stdout = back_out
+
+        self.assert_('Pinged %s sitemap to google' % self.newsSM.absolute_url() in data,
+                     "Not pinged %s: '%s'" % (self.newsSM.id, data))
+        self.assert_(not 'Pinged %s sitemap to google' % self.contentSM.absolute_url() in data,
+                     "Pinged %s on news: '%s'" % (self.contentSM.id, data))
+
+    def testPingingWithSetupForm(self):
+        # Ping news and content sitemaps
+        formUrl = '/'+self.portal.absolute_url(1) + '/prefs_gsm_settings'
+        qs = 'smselected:list=%s&smselected:list=%s&form.button.Ping=1&form.submitted=1' % \
+             (self.contentSM.id, self.newsSM.id)
+
+        back_out, myout = sys.stdout, StringIO()
+        sys.stdout = myout
+        response = self.publish("%s?%s" % (formUrl, qs), basic=self.auth)
+        myout.seek(0)
+        data = myout.read()
+        sys.stdout = back_out
+
+        self.assert_('Pinged %s sitemap to google' % self.contentSM.absolute_url() in data,
+                     "Not pinged %s: '%s'" % (self.contentSM.id, data))
+        self.assert_('Pinged %s sitemap to google' % self.newsSM.absolute_url() in data,
+                     "Not pinged %s: '%s'" % (self.newsSM.id, data))
+
+
+
+def test_suite():
+    from unittest import TestSuite, makeSuite
+    suite = TestSuite()
+    suite.addTest(makeSuite(TestqPloneGoogleSitemapsInstallation))
+    suite.addTest(makeSuite(TestSitemapType))
+    suite.addTest(makeSuite(TestqPloneGoogleSitemaps))
+    suite.addTest(makeSuite(TestSettings))
+    suite.addTest(makeSuite(TestPinging))
+
+    return suite
+
+if __name__ == '__main__':
+    framework()
Index: qPloneGoogleSitemaps/tags/0.8.2/utils.py
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/utils.py	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/utils.py	(revision 637)
@@ -0,0 +1,23 @@
+from urllib2 import urlopen
+from urllib  import quote as urlquote
+
+from Globals import DevelopmentMode
+from OFS.ObjectManager import BadRequestException
+
+from Products.qPloneGoogleSitemaps.config import testing
+
+def ping_google(url, sitemap_id):
+    """Ping sitemap to Google"""
+    if DevelopmentMode:
+        #prevent pinging in debug mode
+        print "Pinging Google ...", url, sitemap_id
+        return 0
+
+    resurl = url + "/" + sitemap_id
+    sitemap_url = urlquote(resurl)
+    g = urlopen('http://www.google.com/webmasters/sitemaps/ping?sitemap='+sitemap_url)
+    result = g.read()
+    g.close()
+    if testing:
+        print "Pinged %s sitemap to google" % resurl
+    return 0
Index: qPloneGoogleSitemaps/tags/0.8.2/version.txt
===================================================================
--- qPloneGoogleSitemaps/tags/0.8.2/version.txt	(revision 637)
+++ qPloneGoogleSitemaps/tags/0.8.2/version.txt	(revision 637)
@@ -0,0 +1,1 @@
+0.8.2
