Index: /qPingTool/branches/plone-3.1/History.txt
===================================================================
--- /qPingTool/branches/plone-3.1/History.txt	(revision 197)
+++ /qPingTool/branches/plone-3.1/History.txt	(revision 198)
@@ -1,2 +1,8 @@
+1.0.0 - Unreleased
+
+  * Converted `Extension/Install.py` into a complete GenericSetup profile.
+  * Updated tests to run with Plone 3.x.
+  * General code cleanup and modernization.
+
 0.3
 
Index: /qPingTool/branches/plone-3.1/PingInfo.py
===================================================================
--- /qPingTool/branches/plone-3.1/PingInfo.py	(revision 197)
+++ /qPingTool/branches/plone-3.1/PingInfo.py	(revision 198)
@@ -1,20 +1,20 @@
+from Globals import DTMLFile
+from AccessControl import ClassSecurityInfo
 from Products.Archetypes.public import *
-from Products.Archetypes.BaseContent import BaseContentMixin
-from Products.CMFCore.ActionInformation import ActionInformation
-from Products.CMFCore.Expression import Expression, createExprContext
-from Products.CMFCore.utils import getToolByName
-from Acquisition import aq_inner, aq_parent
+from Products.CMFCore.permissions import ModifyPortalContent
 from Products.CMFDefault.utils import _dtmldir
-from config import *
-from AccessControl import ClassSecurityInfo
-from Globals import InitializeClass, DTMLFile
+from Products.ATContentTypes.content.base import ATCTContent
+from Products.ATContentTypes.content.schemata import ATContentTypeSchema, finalizeATCTSchema
+from Products.ATContentTypes.lib.historyaware import HistoryAwareMixin
+from config import RSS_LIST, PROJECTNAME
 
-schema = BaseContentMixin.schema +  Schema((
-    #StringField('id',
-    #            required=1,
-    #            widget=StringWidget(size=70,
-    #                                label_msgid = 'label_id',
-    #                                description_msgid = 'help_id'),
-    #            ),
+PingInfoSchema =  ATContentTypeSchema.copy() +  Schema((
+    TextField('description',
+        default = '',
+        searchable = 0,
+        widget = TextAreaWidget(
+            label_msgid = 'label_description',
+            description_msgid = 'help_description',),
+               ),
     StringField('url',
                 required=1,
@@ -30,12 +30,14 @@
     StringField('rss_version',
 		vocabulary=RSS_LIST,
-		default='Blog',
+		default='Weblog',
                 widget=SelectionWidget(label_msgid = 'label_rss_version',
                                     description_msgid = 'help_rss_version'),
-                ),
-    ))
+    )),
+    marshall=RFC822Marshaller()
+    )
+    
+finalizeATCTSchema(PingInfoSchema)
 
-
-class PingInfo(BaseContentMixin):
+class PingInfo(ATCTContent, HistoryAwareMixin):
     """Ping Info container
        id - name of the server to ping
@@ -44,6 +46,8 @@
        rss_version - rss version supported by the server
     """
-
-    schema = schema
+    __implements__ = (ATCTContent.__implements__,
+                      HistoryAwareMixin.__implements__,
+                     )
+    schema = PingInfoSchema
 
     """
@@ -55,13 +59,7 @@
         return self.contributors
 
-    try:
-	from Products.CMFCore import permissions
-	security.declareProtected(permissions.ModifyPortalContent, 'manage_metadata' )
-    except:
-	from Products.CMFCore.CMFCorePermissions import ModifyPortalContent
-	security.declareProtected(ModifyPortalContent, 'manage_metadata' )
-
+    security.declareProtected(ModifyPortalContent, 'manage_metadata' )
     manage_metadata = DTMLFile('zmi_metadata', _dtmldir)
 
 
-registerType(PingInfo)
+registerType(PingInfo, PROJECTNAME)
Index: /qPingTool/branches/plone-3.1/PingTool.py
===================================================================
--- /qPingTool/branches/plone-3.1/PingTool.py	(revision 197)
+++ /qPingTool/branches/plone-3.1/PingTool.py	(revision 198)
@@ -1,21 +1,20 @@
-#from Globals import InitializeClass
 import os
+from Acquisition import aq_base
+from zLOG import LOG
+from zope.interface import implements
+from AccessControl import ClassSecurityInfo
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from Products.Archetypes.public import *
 from Products.CMFCore.ActionProviderBase import ActionProviderBase
+from Products.CMFCore.permissions import ManagePortal
+from Products.CMFCore.utils import getToolByName
+from Products.ATContentTypes.content.folder import ATFolder
+from Products.CMFPlone.interfaces.OrderedContainer import IOrderedContainer
 from Products.CMFPlone.PloneFolder import PloneFolder
-from config import TOOL_ID, PROJECTNAME
-from Products.Archetypes.public import *
-from Products.ATContentTypes.content.folder import ATFolder
-from Products.ATContentTypes.content.base import updateActions, updateAliases
-from Products.CMFPlone.interfaces.OrderedContainer import IOrderedContainer
-from Products.CMFCore.ActionInformation import ActionInformation
-from Products.CMFCore.Expression import Expression
-from Products.CMFCore.CMFCorePermissions import ManageProperties
-from Acquisition import aq_base
-from Products.CMFCore.utils import _getViewFor
-from Products.PageTemplates.PageTemplateFile import PageTemplateFile
 from Products.XMLRPCMethod.XMLRPCMethod import RPCThread, XMLRPCMethod
-from Products.CMFCore.utils import getToolByName
-from util import getCanonicalURL
-from zLOG import LOG
+
+from interfaces import IPingTool
+from adapter import ICanonicalURL
+from config import PROJECTNAME
 
 _marker = []
@@ -30,51 +29,16 @@
 
 
-class PingTool(ATFolder, PloneFolder, ActionProviderBase): #(BaseFolder, PloneFolder, ActionProviderBase):
-    """This tool serve for operation with ActionInfo objects
+class PingTool(ATFolder, PloneFolder):
     """
 
-    #schema = BaseSchema
-    filter_content_types = 1
-    allowed_content_types = ('PingInfo',)
-    global_allowed = 0
+    >>> IPingTool.implementedBy(PingTool)
+    True
+    """
+    security = ClassSecurityInfo()
 
-    meta_type = archetype_name = portal_type = 'PingTool'
+    implements(IPingTool)
+    __implements__ = (IOrderedContainer,)
 
-    ########
-    content_icon   = 'tool.gif'
-    immediate_view = 'view'
-    default_view   = 'view'
-
-    ########
-    __implements__ = (IOrderedContainer,)
-    _actions = ( ActionInformation(
-                    id='ping'
-                  , title='Ping setup'
-                  , action=Expression(
-                        text='string:${folder_url}/ping_setup')
-                  , condition=Expression(
-                        text='python: folder is object and portal.portal_syndication.isSyndicationAllowed(object)')
-                  , permissions=(ManageProperties,)
-                  , category='folder'
-                  , visible=1
-                  ),
-               )
-
-    actions = updateActions(ATFolder,
-        ({'id'         : 'view' \
-         ,'name'       : 'View' \
-         ,'action'     : 'string:folder_contents' \
-         ,'permissions': ('Manage portal',) \
-         ,'category'   :'object' \
-         },
-        )
-    )
-    
-    aliases = updateAliases(ATFolder,
-        {'(Default)'   : 'folder_listing' \
-        ,'view'        : 'folder_contents' \
-        },
-    )
-
+    archetype_name = portal_type = 'PingTool'
     manage_options =  (
             {'label' : 'Overview', 'action' : 'manage_overview'},
@@ -85,30 +49,39 @@
     manage_overview._need__name__ = 0
 
+    def om_icons(self):
+        """ Checking on ZMI for canonical_url setting."""
+        icons = ({'path':'misc_/qPingTool/tool.gif' \
+                    ,'alt':self.meta_type \
+                    ,'title':self.meta_type \
+                },)
+        if not ICanonicalURL(self).getCanonicalURL():
+            icons = icons + ({'path':'misc_/PageTemplates/exclamation.gif' \
+                                ,'alt':'Error' \
+                                ,'title':'PingTool needs setting canonical_url' \
+                                },)
+        return icons
+
+    security.declareProtected(ManagePortal, 'pingFeedReader')
     def pingFeedReader(self,context):
         """ ping """
-        status = 'success'
-        message = 'The servers are pinged'
-        if context.meta_type == 'BlogFolder':
-    	    blog = context.simpleblog_tool.getFrontPage(context)
-    	else:
-    	    blog = context
-
-        title = blog.Title()
-        portal = context.portal_url.getPortalObject()
-        canonical_url = getCanonicalURL(context)
+        
+        status = 'failed'
+        blog = context
+        pingProp = self.getPingProperties(blog)
+    	if not pingProp['enable_ping']:
+    	    message = 'Ping is dissabled.'
+    	    return status, message
+        canonical_url = ICanonicalURL(self).getCanonicalURL()
         if canonical_url:
             url = context.portal_url.getRelativeContentURL(blog)
             url = canonical_url + url
         else:
-            return status, 'Ping is impossible.See portal_pingtool.'
+            return status, 'Ping is impossible.Setup canonical_url.'
 
     	ps = getToolByName(context,'portal_syndication')
-    	rss_templates = {'Blog':'','RSS1':'/RSS','RSS2':'/RSS2'}
-        pingProp = self.getPingProperties(blog)
-    	result = 'ok'
-    	if not pingProp['enable_ping']:
-    	   message = 'Ping is dissabled'
-    	   return status, message
+    	rss_templates = {'Weblog':'','RSS1':'/RSS','RSS2':'/RSS2'}
     	if ps.isSyndicationAllowed(blog):
+            status = 'success'
+            message = 'The servers are pinged.'
 	    sites = pingProp['ping_sites']
 	    if sites:
@@ -121,13 +94,18 @@
                     PingMethod = XMLRPCMethod('myid',"",site_url,site_method,25)
                     blog_url = url + site_rss_version
+                    title = blog.Title()
                     try: 
                         #LOG('qPing', 0, title, blog_url, site_url)
-                        result = PingMethod(title,blog_url)
+                        result_ping = PingMethod(title,blog_url)
+                        result = result_ping['message']
                     except:
-			LOG('qPingTool', 100,"The site "+  site_url+" generated error for "+ blog_url, result)
-                    message += '\n'+ str(result)
+                        result = 'The site %s generated error for %s.' % (site_url, blog_url)
+			LOG('qPingTool', 100, result)
+                    message += '\nReturned message from %s: %s' % (site_url, str(result))
+        else:
+            message = 'The %s is not syndication allowed' % url 
         return status, message
 
-
+    security.declareProtected(ManagePortal, 'setupPing')
     def setupPing(self,context,
                   enable_ping=0,
@@ -137,5 +115,5 @@
         obj=aq_base(context)
         status = 'success'
-        message = 'Your changes have been saved'
+        message = "Changes saved."
         syInfo = getattr(obj, 'syndication_information', None)
 
@@ -149,4 +127,5 @@
         return status, message
 
+    security.declareProtected(ManagePortal, 'getPingProperties')
     def getPingProperties(self, context):
         """ """
@@ -159,16 +138,3 @@
         return  pingPropeties
 
-    def om_icons(self):
-        """ Checking on ZMI for canonical_url setting."""
-        icons = ({'path':'misc_/qPingTool/tool.gif' \
-                    ,'alt':self.meta_type \
-                    ,'title':self.meta_type \
-                },)
-        if not getCanonicalURL(self):
-            icons = icons + ({'path':'misc_/PageTemplates/exclamation.gif' \
-                                ,'alt':'Error' \
-                                ,'title':'PingTool needs setting canonical_url' \
-                                },)
-        return icons
-
-registerType(PingTool)
+registerType(PingTool, PROJECTNAME)
Index: ingTool/branches/plone-3.1/TODO.txt
===================================================================
--- /qPingTool/branches/plone-3.1/TODO.txt	(revision 197)
+++ 	(revision )
@@ -1,1 +1,0 @@
-*
Index: /qPingTool/branches/plone-3.1/__init__.py
===================================================================
--- /qPingTool/branches/plone-3.1/__init__.py	(revision 197)
+++ /qPingTool/branches/plone-3.1/__init__.py	(revision 198)
@@ -1,24 +1,7 @@
-from Globals import package_home
 from Products.Archetypes.public import process_types, listTypes
 from Products.CMFCore import utils
-from Products.CMFCore.DirectoryView import registerDirectory
-import os, os.path
 import PingInfo, PingTool
 from config import *
 
-try:
-    from adapter import registerAdapter
-    registerAdapter()
-except:
-    from Products.CMFCore.utils import getToolByName
-    from Products.CMFPlone.URLTool import URLTool
-    def getCanonicalURL(self):
-        portal = getToolByName(self, 'portal_url').getPortalObject()
-        return portal.getProperty('canonical_url', None)
-    #URLTool.security.declarePublic('getCanonicalURL')
-    URLTool.getCanonicalURL = getCanonicalURL
-
-
-registerDirectory(SKINS_DIR, GLOBALS)
 tools = ( PingTool.PingTool, )
 
@@ -27,5 +10,5 @@
     allow_module('Products.qPingTool.util')
 
-    utils.ToolInit("PingTool", tools=tools, product_name=PROJECTNAME, icon=TOOL_ICON,
+    utils.ToolInit("PingTool", tools=tools, icon=TOOL_ICON,
                   ).initialize(context)
 
Index: /qPingTool/branches/plone-3.1/adapter.py
===================================================================
--- /qPingTool/branches/plone-3.1/adapter.py	(revision 197)
+++ /qPingTool/branches/plone-3.1/adapter.py	(revision 198)
@@ -1,3 +1,6 @@
-from zope.interface import Interface, implements #, Attribute
+from zope.component import adapts
+from zope.interface import Interface, implements
+
+from interfaces import IPingTool
 from Products.CMFCore.utils import getToolByName
 
@@ -20,5 +23,12 @@
 class CanonicalURL(object):
     """ CanonicalURL adapter
+
+    >>> ICanonicalURL.implementedBy(CanonicalURL)
+    True
+    >>> ICanonicalURL(CanonicalURL(object)) is not None
+    True
     """
+
+    adapts(IPingTool)
     implements(ICanonicalURL)
 
@@ -55,9 +65,2 @@
         #portal = getToolByName(self, 'portal_url').getPortalObject()
         #return portal.hasProperty('canonical_url')
-
-# Register adapter
-
-def registerAdapter():
-    from Products.CMFPlone.interfaces import IPloneBaseTool
-    from zope.component import provideAdapter
-    provideAdapter(CanonicalURL, adapts=[IPloneBaseTool,], provides=ICanonicalURL )
Index: /qPingTool/branches/plone-3.1/browser/configure.zcml
===================================================================
--- /qPingTool/branches/plone-3.1/browser/configure.zcml	(revision 198)
+++ /qPingTool/branches/plone-3.1/browser/configure.zcml	(revision 198)
@@ -0,0 +1,13 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain="qPingTool">
+
+   <browser:page
+      for="*"
+      class=".ping.PingView"
+      template="ping_setup.pt"
+      permission="zope2.View"
+      name="ping_setup"/>
+
+</configure>
Index: /qPingTool/branches/plone-3.1/browser/ping.py
===================================================================
--- /qPingTool/branches/plone-3.1/browser/ping.py	(revision 198)
+++ /qPingTool/branches/plone-3.1/browser/ping.py	(revision 198)
@@ -0,0 +1,60 @@
+from Acquisition import aq_inner
+from Products.CMFPlone import PloneMessageFactory as _
+from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
+from Products.Five import BrowserView
+from Products.CMFCore.utils import getToolByName
+
+class PingView(BrowserView):
+    """A class with helper methods for use in views/templates.
+    """
+    template = ViewPageTemplateFile('ping_setup.pt')
+    ping_message = ''
+
+    def __call__(self):
+        postback = True
+        context = aq_inner(self.context)
+        portal = getToolByName(self.context, 'portal_url').getPortalObject()
+        portal_pingtool = getToolByName(portal, 'portal_pingtool')
+        form = self.request.form
+        ping = form.get('form.button.Ping', False)
+        setup_save = form.get('form.button.Save', False)
+        setup_cancel = form.get('form.button.Cancel', False)
+        if ping:
+            status, message = portal_pingtool.pingFeedReader(context)
+            point = message.find('.')+1
+            self.ping_message = message[point:]
+            state = self.get_state(status)
+            portal.plone_utils.addPortalMessage(message[:point], state)
+
+        elif setup_save:
+            enable_ping = form.get('enable_ping', False)
+            ping_sites = form.get('ping_sites', ())
+            status, message = portal_pingtool.setupPing(context, enable_ping, ping_sites)
+            state = self.get_state(status)
+            portal.plone_utils.addPortalMessage(_(message), state)
+
+        elif setup_cancel:
+            portal.plone_utils.addPortalMessage(_("Changes canceled."))
+            self.request.response.redirect(context.absolute_url())
+            postback = False
+
+        if postback:
+            return self.template()
+
+    def get_state(self, status):
+        if status == 'success':
+            state = 'info'
+        elif status == 'failed':
+            state = 'warning'
+        else:
+            state = 'info'
+        return state
+
+    def test(self, value, trueVal, falseVal):
+        """
+           helper method, mainly for setting html attributes.
+        """
+        if value:
+            return trueVal
+        else:
+            return falseVal
Index: /qPingTool/branches/plone-3.1/browser/ping_setup.pt
===================================================================
--- /qPingTool/branches/plone-3.1/browser/ping_setup.pt	(revision 198)
+++ /qPingTool/branches/plone-3.1/browser/ping_setup.pt	(revision 198)
@@ -0,0 +1,106 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
+      lang="en"
+      metal:use-macro="context/main_template/macros/master"
+      i18n:domain="qPingTool">
+
+  <body>
+
+    <div metal:fill-slot="main"
+         tal:define="pingProperties python: here.portal_pingtool.getPingProperties(here)">
+
+      <h1 i18n:translate="heading_edit_item">
+        Edit
+        <span i18n:name="itemtype">
+          <span i18n:translate=""
+                tal:content="python:here.getTypeInfo().Title()"
+                tal:omit-tag="">Item type</span>
+        </span>
+      </h1>
+
+      <p i18n:translate="description_edit_document">
+        Fill in the details of this document.
+      </p>
+      <div tal:define="message view/ping_message" tal:omit-tag="" tal:condition="message">
+        <pre tal:content="message"/>
+      </div>
+      <form name="edit_form"
+            action="."
+            method="post"
+            enctype="multipart/form-data"
+            tal:attributes="action string:${context/absolute_url}/@@ping_setup" >
+
+        <fieldset>
+
+          <legend i18n:translate="legend_ping_setup">Ping Setup Details</legend>
+
+          <div class="field">
+            <input type="checkbox"
+                   id="enable_ping"
+                   name="enable_ping"
+                   tal:attributes="checked python:pingProperties['enable_ping'] and 'checked' or None"
+                   />
+            <label for="enable_ping" i18n:translate="label_enable_ping">Enable Ping</label>
+          </div>
+
+          <div class="field" 
+	       tal:define="ping_sites python:pingProperties['ping_sites']">
+
+           <tal:block repeat="id here/portal_pingtool/objectIds"> 
+             <input type="checkbox"
+                    id="id"
+                    name="ping_sites:list"
+                    tal:attributes="checked python:id in ping_sites and 'checked' or None;
+ 		                    value id;
+				    id id; "
+                    />
+             <label  for="id"
+                     tal:content="python:here.portal_pingtool[id].Title()"
+                     tal:attributes="for id" >Site name</label><br/>
+           </tal:block>
+          </div>
+
+          <div class="formControls" 
+               tal:define="process_creation request/process_creation|nothing;">
+            <input type="submit"
+                   value="Apply Changes"
+                   name="form.button.Save"
+                   class="context"
+                   i18n:attributes="value label_apply_changes;"
+                   />
+            <input type="submit"
+                   value="Cancel"
+                   name="form.button.Cancel"
+                   class="context"
+                   i18n:attributes="value label_cancel;"
+                   />
+          </div>
+
+        </fieldset>
+        <fieldset>
+
+          <legend i18n:translate="legend_ping">Ping Now</legend>
+
+          <div class="formControls"
+               tal:define="process_creation request/process_creation|nothing;">
+            <input class="context"
+                   type="submit"
+                   name="form.button.Ping"
+                   value="Ping"
+                   i18n:attributes="value" 
+                   />
+          </div>
+
+        </fieldset>
+
+        <input type="hidden" name="form.submitted" value="1" />
+
+      </form>
+
+      <div metal:use-macro="here/document_byline/macros/byline">
+        Get the byline - contains details about author and modification date.
+      </div>
+
+    </div>
+
+  </body>
+</html>
Index: /qPingTool/branches/plone-3.1/config.py
===================================================================
--- /qPingTool/branches/plone-3.1/config.py	(revision 197)
+++ /qPingTool/branches/plone-3.1/config.py	(revision 198)
@@ -1,16 +1,13 @@
-from Products.CMFCore import CMFCorePermissions
 from Products.Archetypes.utils import DisplayList
+from Products.CMFCore.permissions import ManagePortal
 
-SKINS_DIR = 'skins'
 GLOBALS = globals()
 PROJECTNAME = 'qPingTool'
 
-ADD_PERMISSION = CMFCorePermissions.ManagePortal
-
-TOOL_ID = 'portal_pingtool'
+ADD_PERMISSION = ManagePortal
 
 TOOL_ICON = 'skins/qpingtool/tool.gif'
 
-RSS_LIST = DisplayList((('Blog','Blog'),('RSS','RSS1'),('RSS2','RSS2')))
+RSS_LIST = DisplayList((('Weblog','Weblog'),('RSS','RSS1'),('RSS2','RSS2')))
 
 CONFIGURATION_CONFIGLET = 'pingtool_config'
Index: /qPingTool/branches/plone-3.1/configure.zcml
===================================================================
--- /qPingTool/branches/plone-3.1/configure.zcml	(revision 198)
+++ /qPingTool/branches/plone-3.1/configure.zcml	(revision 198)
@@ -0,0 +1,13 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:five="http://namespaces.zope.org/five"
+    i18n_domain="qPingTool">
+
+   <include package=".browser" />
+
+   <adapter factory=".adapter.CanonicalURL" /> 
+
+   <include file="profiles.zcml" />
+   <include file="skins.zcml" />
+
+</configure>
Index: /qPingTool/branches/plone-3.1/interfaces.py
===================================================================
--- /qPingTool/branches/plone-3.1/interfaces.py	(revision 198)
+++ /qPingTool/branches/plone-3.1/interfaces.py	(revision 198)
@@ -0,0 +1,5 @@
+from zope.interface import Interface
+
+class IPingTool(Interface):
+    """ Interface for PingTool.
+    """
Index: /qPingTool/branches/plone-3.1/profiles.zcml
===================================================================
--- /qPingTool/branches/plone-3.1/profiles.zcml	(revision 198)
+++ /qPingTool/branches/plone-3.1/profiles.zcml	(revision 198)
@@ -0,0 +1,15 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
+    i18n_domain="qPingTool">
+
+  <genericsetup:registerProfile
+      name="qPingTool"
+      title="qPingTool"
+      directory="profiles/default"
+      description='Extension profile for the types of qPingTool.'
+      provides="Products.GenericSetup.interfaces.EXTENSION"
+      for="Products.CMFPlone.interfaces.IPloneSiteRoot"
+      />
+
+</configure>
Index: /qPingTool/branches/plone-3.1/profiles/default/actionicons.xml
===================================================================
--- /qPingTool/branches/plone-3.1/profiles/default/actionicons.xml	(revision 198)
+++ /qPingTool/branches/plone-3.1/profiles/default/actionicons.xml	(revision 198)
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<action-icons xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+    i18n:domain="qPingTool">
+ <action-icon category="controlpanel"
+              action_id="portal_pingtool"
+              title="Ping Tool" priority="0"
+              icon_expr="group.gif"
+              i18n:attributes="title"/>
+</action-icons>
Index: /qPingTool/branches/plone-3.1/profiles/default/actions.xml
===================================================================
--- /qPingTool/branches/plone-3.1/profiles/default/actions.xml	(revision 198)
+++ /qPingTool/branches/plone-3.1/profiles/default/actions.xml	(revision 198)
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!-- Install our custom portal_actions. Plone's defaults are in CMFPlone/profiles/default/actions.xml -->
+
+<!-- PORTAL ACTIONS -->
+<object name="portal_actions" meta_type="Plone Actions Tool"
+   xmlns:i18n="http://xml.zope.org/namespaces/i18n">
+ <object name="object" meta_type="CMF Action Category">
+  <object name="ping" meta_type="CMF Action">
+   <property name="title">Ping setup</property>
+   <property name="description" i18n:translate=""></property>
+   <property name="url_expr">string:${folder_url}/@@ping_setup</property>
+   <property name="available_expr">python: folder==object and portal.portal_syndication.isSyndicationAllowed(object)</property>
+   <property name="permissions">
+    <element value="Manage properties"/>
+   </property>
+   <property name="visible">True</property>
+  </object>
+ </object>
+</object>
Index: /qPingTool/branches/plone-3.1/profiles/default/controlpanel.xml
===================================================================
--- /qPingTool/branches/plone-3.1/profiles/default/controlpanel.xml	(revision 198)
+++ /qPingTool/branches/plone-3.1/profiles/default/controlpanel.xml	(revision 198)
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<object name="portal_controlpanel" meta_type="Plone Control Panel Tool"
+    xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+    i18n:domain="qPingTool">
+ <configlet title="Ping Tool"
+    action_id="portal_pingtool" appId="PingTool"
+    category="Products" condition_expr=""
+    url_expr="string:${portal_url}/portal_pingtool/folder_contents"
+    visible="True" i18n:attributes="title">
+  <permission>Manage portal</permission>
+ </configlet>
+</object>
Index: /qPingTool/branches/plone-3.1/profiles/default/import_steps.xml
===================================================================
--- /qPingTool/branches/plone-3.1/profiles/default/import_steps.xml	(revision 198)
+++ /qPingTool/branches/plone-3.1/profiles/default/import_steps.xml	(revision 198)
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<import-steps>
+  <import-step id="qPingTool.various"
+               version="20080903-01"
+               handler="Products.qPingTool.setuphandlers.setupVarious"
+               title="qPingTool miscellaneous import steps">
+    <dependency step="typeinfo"/>
+    <dependency step="toolset"/>
+    Various import steps that are not handled by GS import/export
+    handlers.
+  </import-step>
+</import-steps>
Index: /qPingTool/branches/plone-3.1/profiles/default/metadata.xml
===================================================================
--- /qPingTool/branches/plone-3.1/profiles/default/metadata.xml	(revision 198)
+++ /qPingTool/branches/plone-3.1/profiles/default/metadata.xml	(revision 198)
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<metadata>
+  <version>1.0.0</version>
+</metadata>
Index: /qPingTool/branches/plone-3.1/profiles/default/propertiestool.xml
===================================================================
--- /qPingTool/branches/plone-3.1/profiles/default/propertiestool.xml	(revision 198)
+++ /qPingTool/branches/plone-3.1/profiles/default/propertiestool.xml	(revision 198)
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<!-- Install our custom portal_properties. Plone's defaults are in CMFPlone/profiles/default/propertiestool.xml -->
+
+<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="PingTool"/>
+    <element value="PingInfo"/>
+  </property>
+ </object>
+ <object name="site_properties" meta_type="Plone Property Sheet">
+  <property name="use_folder_tabs" type="lines" purge="False"> 
+    <element value="PingTool"/>
+  </property> 
+  <property name="typesLinkToFolderContentsInFC" type="lines" purge="False">
+    <element value="PingTool"/>
+  </property>
+  <property name="typesUseViewActionInListings" type="lines" purge="False">
+    <element value="PingTool"/>
+  </property>
+ </object>
+</object>
Index: /qPingTool/branches/plone-3.1/profiles/default/qPingTool_various.txt
===================================================================
--- /qPingTool/branches/plone-3.1/profiles/default/qPingTool_various.txt	(revision 198)
+++ /qPingTool/branches/plone-3.1/profiles/default/qPingTool_various.txt	(revision 198)
@@ -0,0 +1,1 @@
+This file is used as a marker in setuphandlers.py.
Index: /qPingTool/branches/plone-3.1/profiles/default/skins.xml
===================================================================
--- /qPingTool/branches/plone-3.1/profiles/default/skins.xml	(revision 198)
+++ /qPingTool/branches/plone-3.1/profiles/default/skins.xml	(revision 198)
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<object name="portal_skins" allow_any="False" cookie_persistence="False">
+ <object name="qpingtool"
+    meta_type="Filesystem Directory View"
+    directory="Products.qPingTool:skins/qpingtool"/>
+ <skin-path name="Plone Default"
+            based-on="Plone Default">
+  <layer name="qpingtool"
+     insert-after="custom"/>
+ </skin-path>
+</object>
Index: /qPingTool/branches/plone-3.1/profiles/default/toolset.xml
===================================================================
--- /qPingTool/branches/plone-3.1/profiles/default/toolset.xml	(revision 198)
+++ /qPingTool/branches/plone-3.1/profiles/default/toolset.xml	(revision 198)
@@ -0,0 +1,5 @@
+<?xml version="1.0"?>
+<tool-setup>
+ <required tool_id="portal_pingtool"
+           class="Products.qPingTool.PingTool.PingTool"/>
+</tool-setup>
Index: /qPingTool/branches/plone-3.1/profiles/default/types.xml
===================================================================
--- /qPingTool/branches/plone-3.1/profiles/default/types.xml	(revision 198)
+++ /qPingTool/branches/plone-3.1/profiles/default/types.xml	(revision 198)
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<object name="portal_types" meta_type="Plone Types Tool">
+ <object name="PingInfo"
+    meta_type="Factory-based Type Information with dynamic views"/>
+ <object name="PingTool"
+    meta_type="Factory-based Type Information with dynamic views"/>
+</object>
Index: /qPingTool/branches/plone-3.1/profiles/default/types/PingInfo.xml
===================================================================
--- /qPingTool/branches/plone-3.1/profiles/default/types/PingInfo.xml	(revision 198)
+++ /qPingTool/branches/plone-3.1/profiles/default/types/PingInfo.xml	(revision 198)
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+<object name="PingInfo" meta_type="Factory-based Type Information with dynamic views"
+   i18n:domain="qPingTool" xmlns:i18n="http://xml.zope.org/namespaces/i18n">
+ <property name="title" i18n:translate="">Ping Info</property>
+ <property name="description" i18n:translate="">
+  Ping Info container
+  id - name of the server to ping
+  url - server ping url
+  method_name - ping method
+  rss_version - rss version supported by the server</property>
+ <property name="content_icon">document_icon.gif</property>
+ <property name="content_meta_type">PingInfo</property>
+ <property name="product">qPingTool</property>
+ <property name="factory">addPingInfo</property>
+ <property name="immediate_view">base_view</property>
+ <property name="global_allow">False</property>
+ <property name="filter_content_types">True</property>
+ <property name="allowed_content_types"/>
+ <property name="allow_discussion">False</property>
+ <property name="default_view">base_view</property>
+ <property name="view_methods">
+  <element value="base_view"/>
+ </property>
+ 
+ <alias from="(Default)" to="(dynamic view)"/>
+ <alias from="edit" to="base_edit"/>
+ <alias from="sharing" to="@@sharing"/>
+ <alias from="properties" to="base_metadata"/>
+ <alias from="view" to="(selected layout)"/>
+ 
+ <action title="View"
+         action_id="view"
+         category="object"
+         condition_expr=""
+         url_expr="string:${object_url}/view"
+         visible="True"
+         i18n:attributes="title">
+  <permission value="View"/>
+ </action>
+ <action title="Edit"
+         action_id="edit"
+         category="object"
+         condition_expr=""
+         url_expr="string:${object_url}/edit"
+         visible="True"
+         i18n:attributes="title">
+  <permission value="Modify portal content"/>
+ </action>
+ <action title="Properties"
+         action_id="metadata"
+         category="object"
+         condition_expr=""
+         url_expr="string:${object_url}/properties"
+         visible="True"
+         i18n:attributes="title">
+  <permission value="Modify portal content"/>
+ </action>
+ <action title="References"
+         action_id="references"
+         category="object"
+         condition_expr="object/archetype_tool/has_graphviz"
+         url_expr="string:${object_url}/reference_graph"
+         visible="True"
+         i18n:attributes="title">
+  <permission value="Modify portal content"/>
+  <permission value="Review portal content"/>
+ </action>
+</object>
Index: /qPingTool/branches/plone-3.1/profiles/default/types/PingTool.xml
===================================================================
--- /qPingTool/branches/plone-3.1/profiles/default/types/PingTool.xml	(revision 198)
+++ /qPingTool/branches/plone-3.1/profiles/default/types/PingTool.xml	(revision 198)
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<object name="PingTool" meta_type="Factory-based Type Information with dynamic views"
+   i18n:domain="qPingTool" xmlns:i18n="http://xml.zope.org/namespaces/i18n">
+ <property name="title" i18n:translate="">PingTool</property>
+ <property name="description"
+      i18n:translate="">This tool serve for operation with ActionInfo objects.</property>
+ <property name="content_icon">folder_icon.gif</property>
+ <property name="content_meta_type">PingTool</property>
+ <property name="product">qPingTool</property>
+ <property name="factory">addPingTool</property>
+ <property name="immediate_view">folder_listing</property>
+ <property name="global_allow">True</property>
+ <property name="filter_content_types">True</property>
+ <property name="allowed_content_types">
+  <element value="PingInfo"/>
+ </property>
+ <property name="allow_discussion">False</property>
+ <property name="default_view">folder_listing</property>
+ <property name="view_methods">
+  <element value="folder_summary_view"/>
+  <element value="folder_tabular_view"/>
+  <element value="atct_album_view"/>
+  <element value="folder_listing"/>
+ </property>
+ <property name="default_view_fallback">False</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}/view"
+         visible="True"
+         i18n:attributes="title">
+  <permission value="Manage portal"/>
+ </action>
+ <action title="Edit"
+         action_id="edit"
+         category="object"
+         condition_expr=""
+         url_expr="string:${object_url}/edit"
+         visible="True"
+         i18n:attributes="title">
+  <permission value="Modify portal content"/>
+ </action>
+
+</object>
Index: /qPingTool/branches/plone-3.1/setuphandlers.py
===================================================================
--- /qPingTool/branches/plone-3.1/setuphandlers.py	(revision 198)
+++ /qPingTool/branches/plone-3.1/setuphandlers.py	(revision 198)
@@ -0,0 +1,19 @@
+from Products.qPingTool.config import SITES_LIST
+
+def setupVarious(context):
+
+    # Ordinarily, GenericSetup handlers check for the existence of XML files.
+    # Here, we are not parsing an XML file, but we use this text file as a
+    # flag to check that we actually meant for this import step to be run.
+    # The file is found in profiles/default.
+
+    if context.readDataFile('qPingTool_various.txt') is None:
+        return
+
+    # Add additional setup code here
+    portal = context.getSite()
+    existent_sites = portal.portal_pingtool.objectIds()
+    for site in SITES_LIST:
+        if not site[0] in existent_sites:
+            portal.portal_pingtool.invokeFactory(id = site[0], type_name = "PingInfo", title = site[1],url = site[2])
+
Index: /qPingTool/branches/plone-3.1/skins.zcml
===================================================================
--- /qPingTool/branches/plone-3.1/skins.zcml	(revision 198)
+++ /qPingTool/branches/plone-3.1/skins.zcml	(revision 198)
@@ -0,0 +1,10 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:cmf="http://namespaces.zope.org/cmf"
+    i18n_domain="qPloneTool">
+
+   <!-- File System Directory Views registration -->
+   <cmf:registerDirectory
+       name="qpingtool" />
+
+</configure>
Index: ingTool/branches/plone-3.1/skins/qpingtool/ping_now.cpy
===================================================================
--- /qPingTool/branches/plone-3.1/skins/qpingtool/ping_now.cpy	(revision 197)
+++ 	(revision )
@@ -1,12 +1,0 @@
-## Controller Python Script "ping_now"
-##bind container=container
-##bind context=context
-##bind namespace=
-##bind script=script
-##bind state=state
-##bind subpath=traverse_subpath
-##parameters=
-##title=
-##
-status, message = context.portal_pingtool.pingFeedReader(context)
-return state.set(status=status, portal_status_message=message)
Index: ingTool/branches/plone-3.1/skins/qpingtool/ping_now.cpy.metadata
===================================================================
--- /qPingTool/branches/plone-3.1/skins/qpingtool/ping_now.cpy.metadata	(revision 197)
+++ 	(revision )
@@ -1,7 +1,0 @@
-[default]
-title=Ping Now
-
-
-[actions]
-action.success=redirect_to:string:ping_setup
-action.failure=redirect_to:string:ping_setup
Index: ingTool/branches/plone-3.1/skins/qpingtool/ping_setup.cpt
===================================================================
--- /qPingTool/branches/plone-3.1/skins/qpingtool/ping_setup.cpt	(revision 197)
+++ 	(revision )
@@ -1,116 +1,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
-      lang="en"
-      metal:use-macro="here/main_template/macros/master"
-      i18n:domain="plone">
-
-  <body>
-
-    <div metal:fill-slot="main"
-         tal:define="errors options/state/getErrors;
-                     editor python:member.getProperty('wysiwyg_editor','None');
-                     wysiwyg python:test(editor and editor!='None', editor, 'None');
-                     lockable python:hasattr(here, 'wl_isLocked');
-                     isLocked python:lockable and here.wl_isLocked();
-                     pingProperties python: here.portal_pingtool.getPingProperties(here)">
-
-      <h1 i18n:translate="heading_edit_item">
-        Edit
-        <span i18n:name="itemtype">
-          <span i18n:translate=""
-                tal:content="python:here.getTypeInfo().Title()"
-                tal:omit-tag="">Item type</span>
-        </span>
-      </h1>
-
-      <p i18n:translate="description_edit_document">
-        Fill in the details of this document.
-      </p>
-
-      <form name="edit_form"
-            action="."
-            method="post"
-            enctype="multipart/form-data"
-            tal:attributes="action string:${here/getId}/${template/getId}" >
-
-        <fieldset>
-
-          <legend i18n:translate="legend_ping_setup">Ping Setup Details</legend>
-
-          <div class="field">
-                  <input type="checkbox"
-                         id="enable_ping"
-                         name="enable_ping"
-                         tabindex=""
-                         tal:attributes="tabindex tabindex/next;
-			                 checked python:pingProperties['enable_ping'] and 'checked' or None"
-                         />
-                 <label for="enable_ping" i18n:translate="label_enable_ping">Enable Ping</label>
-          </div>
-          <div class="field" 
-	       tal:define="ping_sites python:pingProperties['ping_sites']">
-
-           <tal:block repeat="id here/portal_pingtool/objectIds"> 
-                  <input type="checkbox"
-                         id="id"
-                         name="ping_sites:list"
-                         tabindex=""
-                         tal:attributes="tabindex tabindex/next;
-			                 checked python:id in ping_sites and 'checked' or None;
-			                 value id;
-					 id id; "
-                         />
-                 <label for="id" tal:content="python:here.portal_pingtool[id].Title()" >Site name</label><br/>
-           </tal:block>
-          </div>
-
-
-          <div class="formControls"
-               tal:define="process_creation request/process_creation|nothing;">
-
-
-            <input class="context"
-                   tabindex=""
-                   type="submit"
-                   name="form.button.Save"
-                   value="Save"
-                   i18n:attributes="value"
-                   tal:attributes="tabindex tabindex/next;" />
-
-            <!--input class="standalone"
-                   tabindex=""
-                   type="submit"
-                   name="form.button.Cancel"
-                   value="Cancel"
-                   i18n:attributes="value"
-                   tal:attributes="tabindex tabindex/next;" /-->
-          </div>
-        </fieldset>
-        <fieldset>
-
-          <legend i18n:translate="legend_ping">Ping Now</legend>
-
-          <div class="formControls"
-               tal:define="process_creation request/process_creation|nothing;">
-            <input class="context"
-                   tabindex=""
-                   type="submit"
-                   name="form.button.Ping"
-                   value="Ping"
-                   i18n:attributes="value"
-                   tal:attributes="tabindex tabindex/next;" />
-          </div>
-
-        </fieldset>
-
-        <input type="hidden" name="form.submitted" value="1" />
-
-      </form>
-
-      <div metal:use-macro="here/document_byline/macros/byline">
-        Get the byline - contains details about author and modification date.
-      </div>
-
-    </div>
-
-  </body>
-</html>
Index: ingTool/branches/plone-3.1/skins/qpingtool/ping_setup.cpt.metadata
===================================================================
--- /qPingTool/branches/plone-3.1/skins/qpingtool/ping_setup.cpt.metadata	(revision 197)
+++ 	(revision )
@@ -1,13 +1,0 @@
-[default]
-title=Ping setup
-
-[security]
-View=1:Authenticated
-
-[validators]
-validators=
-
-[actions]
-action.success..Save=traverse_to:string:save_ping_setup
-action.success..Ping=traverse_to:string:ping_now
-action.failure=traverse_to:string:ping_setup
Index: ingTool/branches/plone-3.1/skins/qpingtool/save_ping_setup.cpy
===================================================================
--- /qPingTool/branches/plone-3.1/skins/qpingtool/save_ping_setup.cpy	(revision 197)
+++ 	(revision )
@@ -1,12 +1,0 @@
-## Controller Python Script "createObject"
-##bind container=container
-##bind context=context
-##bind namespace=
-##bind script=script
-##bind state=state
-##bind subpath=traverse_subpath
-##parameters= enable_ping=0,ping_sites=[],REQUEST=None
-##title=
-##
-status, message = context.portal_pingtool.setupPing(context, enable_ping, ping_sites)
-return state.set(status=status, portal_status_message=message)
Index: ingTool/branches/plone-3.1/skins/qpingtool/save_ping_setup.cpy.metadata
===================================================================
--- /qPingTool/branches/plone-3.1/skins/qpingtool/save_ping_setup.cpy.metadata	(revision 197)
+++ 	(revision )
@@ -1,8 +1,0 @@
-[default]
-title=Ping Setup
-
-
-[actions]
-action.success=redirect_to:string:ping_setup
-action.failure=redirect_to:string:ping_setup
-action.failed..Save=redirect_to:string:ping_setup
Index: ingTool/branches/plone-3.1/util.py
===================================================================
--- /qPingTool/branches/plone-3.1/util.py	(revision 197)
+++ 	(revision )
@@ -1,13 +1,0 @@
-from Products.CMFCore.utils import getToolByName
-
-def getCanonicalURL(context):
-    """ Uniform method for get portal canonical url."""
-    p_url = getToolByName(context, 'portal_url')
-    canonical_url = ""
-    try:
-        from adapter import ICanonicalURL
-        canonical_url = ICanonicalURL(p_url).getCanonicalURL()
-    except:
-        canonical_url = p_url.getCanonicalURL()
-
-    return canonical_url
Index: /qPingTool/branches/plone-3.1/www/overview.zpt
===================================================================
--- /qPingTool/branches/plone-3.1/www/overview.zpt	(revision 197)
+++ /qPingTool/branches/plone-3.1/www/overview.zpt	(revision 198)
@@ -1,63 +1,60 @@
 <tal:header tal:replace="structure here/manage_page_header|nothing">Header</tal:header>
 <tal:message tal:define="manage_tabs_message options/manage_tabs_message | request/manage_tabs_message | nothing"
-    tal:replace="structure here/manage_tabs">Tabs</tal:message>
+             tal:replace="structure here/manage_tabs">Tabs</tal:message>
 <tal:body 
-   tal:define="canonical_url_value request/canonical_url_value | python:modules['Products.qPingTool.util'].getCanonicalURL(here);
-               hasCanonicalURL request/hasCanonicalURL | python:test(canonical_url_value, 1, 0);
-               action python:test(hasCanonicalURL, 'Update', 'Add');
+   tal:define="portal here/portal_url/getPortalObject;
+               canonical_url_value request/canonical_url_value | python:portal.getProperty('canonical_url');
+               hasCanonicalURL request/hasCanonicalURL | python:portal.hasProperty('canonical_url');
                button request/submit | nothing;
                submitted request/submitted | nothing;">
+   <div tal:condition="submitted"
+        tal:omit-tag="">
+      <tal:common>
+         <div tal:condition="python:button=='Update' and hasCanonicalURL">
+            <tal:block define="result python:portal.manage_changeProperties(canonical_url=canonical_url_value)"/>
+         </div>
+         <div tal:condition="python:button=='Add' and not hasCanonicalURL">
+            <tal:block define="result python:portal.manage_addProperty('canonical_url', canonical_url_value, 'string')"/>
+         </div>
+      </tal:common>
+   </div>
 
-<div tal:condition="submitted"
-     tal:omit-tag="string:">
-  <tal:common
-    define="portal here/portal_url/getPortalObject;">
-    <div tal:condition="python:button=='Update'">
-        <tal:block define="result python:portal.manage_changeProperties(canonical_url=canonical_url_value)"/>
-    </div>
-    <div tal:condition="python:button=='Add'">
-        <tal:block define="result python:portal.manage_addProperty('canonical_url', canonical_url_value, 'string')"/>
-    </div>
-  </tal:common>
-</div>
+   <h3>Overview</h3>
+   <p>
+      <strong>portal_pingtool</strong> is symple tool to enable pinging of external feed agregators.
+   </p>
+   <div tal:omit-tag=""
+        tal:define="hasCanonicalURL request/hasCanonicalURL | python:portal.hasProperty('canonical_url');
+                    action python:test(hasCanonicalURL, 'Update', 'Add');">
+      <h3 tal:content="string:$action Canonical URL">Update Canonical URL</h3>
+      <p tal:condition="not:hasCanonicalURL">Until you setup canonical_url - Ping Tool will not work.</p>
 
-<h3>Overview</h3>
+      <form method="post" action="" tal:attributes="action python:'manage_'+template.getId()">
+         <input type="hidden" name="submitted" value="1" />
+         <p class="form-help"
+            tal:content="string:To $action the property, enter a canonical URL value and click the &quot;$action&quot; button">
+            To Add/Update the property, enter a canonical URL value and click the &quot;Add/Update&quot; button.
+         </p>
 
-<p> <strong>portal_pingtool</strong> is symple tool to enable pinging of external feed agregators.
-</p>
-
-
-<h3 tal:content="string:$action Canonical URL">Update Canonical URL</h3>
-
-  <p tal:condition="not:hasCanonicalURL">Until you setup canonical_url - Ping Tool will not work.</p>
-
-  <form method="put" action="" tal:attributes="action template/getId">
-    <input type="hidden" name="submitted" value="1" />
-
-    <p class="form-help"
-       tal:content="string:To $action the property, enter a canonical URL value and click the &quot;$action&quot; button">
-    To Add/Update the property, enter a canonical URL value
-    and click the &quot;Add/Update&quot; button.
-    </p>
-
-    <table>
-      <tr>
-        <td align="left" valign="top">
-          <div class="form-label">Canonical URL</div>
-        </td>
-        <td align="left" valign="top">
-          <input type="text" size="30" value="" name="canonical_url_value"
-              tal:attributes="value canonical_url_value | nothing"/>
-      </tr>
-      <tr>
-        <td align="right" valign="top">
-          <div class="form-element">
-            <input class="form-element" type="submit" name="submit" value="Save"
-                    tal:attributes="value string:$action;" />
-          </div>
-        </td>
-      </tr>
-    </table>
-  </form>
+         <table>
+            <tr>
+               <td align="left" valign="top">
+                  <div class="form-label">Canonical URL</div>
+               </td>
+               <td align="left" valign="top">
+                  <input type="text" size="30" value="" name="canonical_url_value"
+                     tal:attributes="value canonical_url_value | nothing"/>
+            </tr>
+            <tr>
+               <td align="right" valign="top">
+                  <div class="form-element">
+                     <input class="form-element" type="submit" name="submit" value="Save"
+                        tal:attributes="value string:$action;" />
+                  </div>
+               </td>
+            </tr>
+         </table>
+      </form>
+   </div>
 </tal:body>
 <tal:footer tal:replace="structure here/manage_page_footer|nothing">footer</tal:footer>
