[kupu-checkins] r33114 - in kupu/trunk/kupu: common doc plone plone/kupu_plone_layer plone/tests
duncan at codespeak.net
duncan at codespeak.net
Tue Oct 10 16:56:32 CEST 2006
Author: duncan
Date: Tue Oct 10 16:56:24 2006
New Revision: 33114
Added:
kupu/trunk/kupu/common/kupu_kjax.js (contents, props changed)
kupu/trunk/kupu/plone/kupu_plone_layer/kupu_kjax_support.xml.pt (contents, props changed)
kupu/trunk/kupu/plone/kupu_plone_layer/kupu_migration.xml.pt (contents, props changed)
kupu/trunk/kupu/plone/tests/test_links.py (contents, props changed)
kupu/trunk/kupu/plone/tests/test_urls.py (contents, props changed)
kupu/trunk/kupu/plone/zmi_links.pt (contents, props changed)
Modified:
kupu/trunk/kupu/common/kupubasetools.js
kupu/trunk/kupu/common/kupudrawers.js
kupu/trunk/kupu/common/kupuhelpers.js
kupu/trunk/kupu/doc/CHANGES.txt
kupu/trunk/kupu/plone/html2captioned.py
kupu/trunk/kupu/plone/kupu_config.pt
kupu/trunk/kupu/plone/kupu_plone_layer/kupuplone.css.dtml
kupu/trunk/kupu/plone/plonedrawers.py
kupu/trunk/kupu/plone/plonelibrarytool.py
kupu/trunk/kupu/plone/tests/runme.cmd
kupu/trunk/kupu/plone/tests/test_browserSupportsKupu.py
kupu/trunk/kupu/plone/tests/test_html2captioned.py
kupu/trunk/kupu/plone/tests/test_plonedrawer.py
Log:
Added a link maintenance page.
Added: kupu/trunk/kupu/common/kupu_kjax.js
==============================================================================
--- (empty file)
+++ kupu/trunk/kupu/common/kupu_kjax.js Tue Oct 10 16:56:24 2006
@@ -0,0 +1,94 @@
+/*****************************************************************************
+ *
+ * Copyright (c) 2003-2005 Kupu Contributors. All rights reserved.
+ *
+ * This software is distributed under the terms of the Kupu
+ * License. See LICENSE.txt for license text. For a list of Kupu
+ * Contributors see CREDITS.txt.
+ *
+ *****************************************************************************/
+
+/* Javascript to aid migration page. */
+
+function Migration() {};
+(function(p){
+ var fudge = new LibraryDrawer();
+ p._loadXML = fudge._loadXML;
+ p._xmlcallback = function(dom) {
+ this.xmldata = dom;
+ this.updateDisplay();
+ };
+ p.updateDisplay = function() {
+ var nodes = this.xmldata.selectNodes("//*[@kj:mode]");
+ for (var i = 0; i < nodes.length; i++) {
+ var n = nodes[i];
+ var mode = n.getAttribute('kj:mode');
+ n = document.importNode(n, true);
+ var id = n.getAttribute('id');
+ var target;
+ if (id) {
+ target = document.getElementById(id);
+ } else {
+ target = document.getElementById('kupu-default-target');
+ mode = 'append';
+ }
+ if (mode=='append') {
+ while(n.firstChild) {
+ target.appendChild(n.firstChild);
+ };
+ } else if (mode=='replace') {
+ Sarissa.copyChildNodes(n, target);
+ } else if (mode=='prepend') {
+ var t = target.firstChild;
+ while (n.firstChild) {
+ target.insertBefore(n.firstChild, t);
+ };
+ };
+ };
+ this.nextRequest();
+ };
+ p.nextRequest = function() {
+ var next = this.xmldata.selectSingleNode('//*[@kj:next]');
+ if (next) {
+ var xmluri = next.getAttribute('kj:next');
+ this._loadXML(xmluri, this._xmlcallback);
+ } else {
+ this.trace("complete");
+ };
+ };
+ p.newRequest = function(uri) {
+ this._loadXML(uri, this._xmlcallback);
+ };
+ p.clearLog = function() {
+ var el = document.getElementById("log");
+ while (el.firstChild) el.removeChild(el.firstChild);
+ };
+ p.submitForm = function(form) {
+ var fields = [];
+ function push(el, v) {
+ fields.push(el.name+"="+encodeURIComponent(v));
+ }
+ for(var i=0; i < form.elements.length; i++)
+ {
+ var el = form.elements[i];
+ var name = /input/i.test(el.tagName)?el.type:el.tagName;
+ if (/checkbox|radio/i.test(name) && !el.checked) continue;
+ if (/select/i.test(name)) {
+ push(el, options[el.selectedIndex].value);
+ continue;
+ }
+ if (/text|hidden|checkbox|radio|textarea/i.test(name)) {
+ push(el, el.value);
+ };
+ }
+ //alert(fields.join('\n'));
+ this._loadXML(form.getAttribute('action'), this._xmlcallback, fields.join('&'));
+ return false;
+ };
+ p.trace = function(s) {
+ var el = document.getElementById("log");
+ if (el) el.appendChild(newElement("div", [s]));
+ };
+})(Migration.prototype);
+
+var kj = new Migration();
Modified: kupu/trunk/kupu/common/kupubasetools.js
==============================================================================
--- kupu/trunk/kupu/common/kupubasetools.js (original)
+++ kupu/trunk/kupu/common/kupubasetools.js Tue Oct 10 16:56:24 2006
@@ -412,12 +412,14 @@
for (var i = 0; i < paraoptions.length; i++) {
select.appendChild(option(paraoptions[i]));
}
- var grp = document.createElement('optgroup');
- grp.label = 'Character styles';
- for (var i = 0; i < styleoptions.length; i++) {
- grp.appendChild(option(styleoptions[i]));
+ if (styleoptions.length) {
+ var grp = document.createElement('optgroup');
+ grp.label = 'Character styles';
+ for (var i = 0; i < styleoptions.length; i++) {
+ grp.appendChild(option(styleoptions[i]));
+ }
+ select.appendChild(grp);
}
- select.appendChild(grp);
}
if (inTable) {
var grp = tablegrp = document.createElement('optgroup');
Modified: kupu/trunk/kupu/common/kupudrawers.js
==============================================================================
--- kupu/trunk/kupu/common/kupudrawers.js (original)
+++ kupu/trunk/kupu/common/kupudrawers.js Tue Oct 10 16:56:24 2006
@@ -1199,7 +1199,7 @@
throw "Error loading XML";
};
var dom = xmlhttp.responseXML;
- if (!dom.documentElement) { /* IE bug! */
+ if (!dom || !dom.documentElement) { /* IE bug! */
dom = Sarissa.getDomDocument();
dom.loadXML(xmlhttp.responseText);
}
Modified: kupu/trunk/kupu/common/kupuhelpers.js
==============================================================================
--- kupu/trunk/kupu/common/kupuhelpers.js (original)
+++ kupu/trunk/kupu/common/kupuhelpers.js Tue Oct 10 16:56:24 2006
@@ -258,7 +258,7 @@
};
var name = child.nodeName.toLowerCase();
if (child.attributes[0] && /^_/.test(child.attributes[0])) {
- name += child.attributes[0].toLowerCase(); // Fix for Opera
+ name += child.attributes[0].name.toLowerCase(); // Fix for Opera
}
if (dict[name] != undefined) {
if (!dict[name].push) {
Modified: kupu/trunk/kupu/doc/CHANGES.txt
==============================================================================
--- kupu/trunk/kupu/doc/CHANGES.txt (original)
+++ kupu/trunk/kupu/doc/CHANGES.txt Tue Oct 10 16:56:24 2006
@@ -14,6 +14,11 @@
closest style round the selection (i.e. a span or block tag, or
removes the className if it hits a table tag with a class).
+ - Added a link maintenance page. Checks for bad links (i.e. those
+ kupu doesn't understand), also converts links from relative paths
+ to resolveuid and back again. Currently it doesn't actually change
+ anything though!
+
- 1.4 Beta 1
- Fixed some problems with handling of multi-valued form fields in the
Modified: kupu/trunk/kupu/plone/html2captioned.py
==============================================================================
--- kupu/trunk/kupu/plone/html2captioned.py (original)
+++ kupu/trunk/kupu/plone/html2captioned.py Tue Oct 10 16:56:24 2006
@@ -13,6 +13,7 @@
from DocumentTemplate.DT_Var import newline_to_br
import re
from cgi import escape
+from urlparse import urlsplit, urljoin, urlunsplit
__revision__ = '$Id$'
@@ -151,3 +152,318 @@
def initialize():
engine = getToolByName(portal, 'portal_transforms')
engine.registerTransform(register())
+
+ATTR_HREF = ATTR_VALUE % 'href'
+LINK_PATTERN = re.compile(
+ r'(?P<prefix>\<(?:img\s[^>]*src|a\s[^>]*href)=(?:"?))(?P<href>(?<=")[^"]*|[^ \/>]*)',
+ re.IGNORECASE)
+
+class Migration:
+ FIELDS = ('portal_type', 'typename', 'fieldname',
+ 'fieldlabel', 'position', 'action', 'dryrun',
+ 'image_tails'
+ )
+
+ def __init__(self, tool):
+ self.tool = tool
+ self.url_tool = getToolByName(tool, 'portal_url')
+ self.portal = self.url_tool.getPortalObject()
+ self.portal_base = self.url_tool.getPortalPath()
+ self.portal_base_url = self.portal.absolute_url()
+ self.prefix_length = len(self.portal_base)+1
+ self.uid_catalog = getToolByName(tool, 'uid_catalog')
+ self.reference_tool = getToolByName(tool, 'reference_catalog')
+ self.portal_catalog = getToolByName(tool, 'portal_catalog')
+ self._continue = True
+
+ def initFromRequest(self):
+ self.image_tails = self.tool._getImageSizes()
+ request = self.tool.REQUEST
+ fields = [f for f in request.form.get('fields',()) if f.get('selected',0)]
+ if fields:
+ f = fields[0]
+ self.portal_type = f.portal_type
+ self.typename = f.type
+ self.fieldname = f.name
+ self.fieldlabel = f.label
+ else:
+ self.portal_type = None
+ self.fieldname = None
+ self.fieldlabel = None
+
+ self.position = 0
+ self.action = request.form.get('button', None)
+ self.dryrun = request.form.get('dryrun', '') != 'I agree'
+
+ def saveState(self):
+ SESSION = self.tool.REQUEST.SESSION
+ SESSION['kupu_migrator'] = dict([(f, getattr(self, f, None)) for f in self.FIELDS])
+
+ def restoreState(self):
+ SESSION = self.tool.REQUEST.SESSION
+ state = SESSION['kupu_migrator']
+ for f in self.FIELDS:
+ setattr(self, f, state[f])
+
+ def clearState(self):
+ SESSION = self.tool.REQUEST.SESSION
+ if SESSION.has_key('kupu_migrator'):
+ del SESSION['kupu_migrator']
+
+ def status(self):
+ s = [ '%s=%s' % (f,getattr(self, f, 'unset')) for f in
+ self.FIELDS ]
+ return '\n'.join(s)
+
+ def getInfo(self):
+ info = {}
+ if self._continue:
+ info['nexturi'] = self.tool.absolute_url_path()+'/kupu_migration.xml?button=continue'
+ if hasattr(self, '_total'):
+ info['total'] = self._total
+ info['position'] = self.position
+ if self._total==0:
+ info['percent'] = '100%'
+ else:
+ info['percent'] = '%d%%' % ((100.*self.position)/self._total)
+ info['objects'] = getattr(self, '_objects', [])
+ action = getattr(self, 'action', '')
+ if action:
+ headings = { 'check': 'Bad links',
+ 'touid': 'Links converted to resolveuid form',
+ 'topath': 'Links converted to relative path',
+ }
+ heading = headings[action]
+ if self.typename:
+ heading += ' for %s (%s)' % (self.typename, self.fieldlabel)
+ info['heading'] = heading
+
+ if not self.action=='check':
+ if self.dryrun:
+ dryrun = 'Dryrun only, no changes are being made to your data'
+ else:
+ dryrun = '''Content is being updated
+ (actually that's a lie: until the code is more tested I'm not updating anything)'''
+ info['dryrun'] = dryrun
+
+ return info
+
+ def docontinue(self):
+ """Scan selected documents looking for convertible links"""
+ brains = self.portal_catalog.searchResults(portal_type=self.portal_type)
+ pos = self.position
+ self._total = total = len(brains)
+ brains = brains[pos:pos+10]
+ self.position = pos + len(brains)
+ if not brains:
+ self._continue = False
+ return False # Done
+
+ self._objects = res = []
+ for b in brains:
+ braininfo = self.brain_check(b)
+ if braininfo:
+ res.append(braininfo)
+
+ self._continue = True
+ return True
+
+ def brain_check(self, brain):
+ """Check the relative links within this object."""
+ def checklink(match):
+ matched = match.group(0)
+ newlink = link = match.group('href')
+ classification, uid, relpath, tail = self.classifyLink(link, base)
+
+ if self.action=='check':
+ if classification=='bad':
+ info.append(link)
+ elif self.action=='touid':
+ if classification=='internal':
+ if uid and uid==objuid:
+ newlink = tail
+ elif uid:
+ newlink = 'resolveuid/%s%s' % (uid, tail)
+ else:
+ newlink = relpath+tail
+
+ elif self.action=='topath':
+ if classification=='internal':
+ newlink = relpath+tail
+
+ if newlink != link:
+ prefix = match.group('prefix')
+ changes.append((match.start()+len(prefix), match.end(), newlink))
+ return prefix + newlink
+ return matched
+
+ info = []
+ changes = []
+ object = brain.getObject()
+ objuid = getattr(object.aq_base, 'UID', None)
+ if objuid:
+ objuid = objuid
+
+ base = object.absolute_url()
+ if getattr(object.aq_explicit, 'isPrincipiaFolderish', 0):
+ base += '/'
+ field = object.getField(self.fieldname)
+ data = field.getRaw(object)
+ newdata = LINK_PATTERN.sub(checklink, data)
+
+ if info or changes:
+ title = brain.Title
+ if not title:
+ title = getattr(brain, 'getId')
+ if not title:
+ title = '<object>'
+ if data != newdata:
+ diffs = htmlchanges(data, changes)
+ else:
+ diffs = None
+ return dict(title=title, info=info, url=object.absolute_url_path(),
+ diffs=diffs)
+ return None
+
+ def UIDfromURL(self, url):
+ """Convert an absolute URL to a UID"""
+ if not url.startswith(self.portal_base_url):
+ return None
+ path = url[len(self.portal_base_url)+1:]
+ if not path:
+ return None
+ try:
+ metadata = self.uid_catalog.getMetadataForUID(path)
+ except KeyError:
+ return None
+ return metadata.get('UID', None)
+
+ def brainfromurl(self, url):
+ """convert a url to a catalog brain"""
+ if not url.startswith(self.portal_base_url):
+ return None
+ url = self.portal_base + url[len(self.portal_base_url):]
+ brains = self.portal_catalog.searchResults(path=url)
+ if len(brains) != 1:
+ # Happens on Plone 2.0 :(
+ for b in brains:
+ if b.getPath()==url:
+ return b
+ return None
+ return brains[0]
+
+ def resolveToPath(self, absurl):
+ if 'resolveuid/' in absurl:
+ bits = absurl.split('resolveuid/', 1)
+ bits = bits[1].split('/',1)
+ uid = bits[0]
+ if len(bits)==1:
+ tail = ''
+ else:
+ tail = '/' + bits[1]
+
+ # TODO: should be able to convert uid to brain without
+ # touching the actual object.
+ obj = self.reference_tool.lookupObject(uid)
+ if obj is not None:
+ newurl = obj.absolute_url()
+ return uid, newurl, tail
+ return None, None, None
+
+ def classifyLink(self, url, base, first=True):
+ """Classify a link as:
+ internal, external, bad
+
+ Returns a tuple:
+ (classification, uid, relpath, tail)
+ giving potential urls: resolveuid/<uid><tail>
+ or: <relpath><table>
+ """
+ if url.startswith('portal_factory'):
+ url = url[14:]
+ absurl = urljoin(base, url)
+ if not absurl.startswith(self.portal_base_url):
+ return 'external', None, url, ''
+ absurl = absurl.strip('/')
+
+ scheme, netloc, path, query, fragment = urlsplit(absurl)
+ tail = urlunsplit(('','','',query,fragment))
+ absurl = urlunsplit((scheme,netloc,path,'',''))
+
+ if 'resolveuid/' in absurl:
+ UID, newurl, ntail = self.resolveToPath(absurl)
+ if UID is None:
+ return 'bad', None, url, ''
+ absurl = newurl
+ tail = ntail + tail
+ else:
+ UID = self.UIDfromURL(absurl)
+
+ brain = self.brainfromurl(absurl)
+ if not brain:
+ if first:
+ # Allow image size modifiers on the end of urls.
+ p = absurl.split('/')
+ absurl = '/'.join(p[:-1])
+ if p[-1] in self.image_tails:
+ tail = '/'+p[-1]+tail
+ c, uid, url, _ = self.classifyLink(absurl, base, first=False)
+ return c, uid, url, tail
+ return 'bad', None, url, ''
+
+ relative, _ = makeUrlRelative(absurl, base)
+ # Don't convert page-internal links to uids.
+ # Also fix up spurious portal_factory references
+ if not relative:
+ return 'internal', None, '', tail
+ return 'internal', UID, relative, tail
+
+def makeUrlRelative(url, base):
+ """Make a link relative to base.
+ This method assumes we have already checked that url and base have a common prefix.
+ """
+ sheme, netloc, path, query, fragment = urlsplit(url)
+ _, _, basepath, _, _ = urlsplit(base)
+
+ baseparts = basepath.split('/')
+ pathparts = path.split('/')
+
+ basetail = baseparts.pop(-1)
+
+ # Remove common elements
+ while pathparts and baseparts and baseparts[0]==pathparts[0]:
+ baseparts.pop(0)
+ pathparts.pop(0)
+
+ for i in range(len(baseparts)):
+ pathparts.insert(0, '..')
+
+ if not pathparts:
+ pathparts.insert(0, '.')
+ elif pathparts==[basetail]:
+ pathparts.pop(0)
+
+
+ return '/'.join(pathparts), urlunsplit(('','','',query,fragment))
+
+def htmlchanges(data, changes):
+ out = []
+ prev = 0
+ lastend = 0
+ for s,e,new in changes:
+ start = max(prev, s-10)
+ if start != prev:
+ if start-10 > prev:
+ out.append(html_quote(data[prev:prev+10]))
+ out.append('...')
+ else:
+ out.append(html_quote(data[prev:start]))
+ out.append(html_quote(data[start:s]))
+ out.append('<del>%s</del>' % html_quote(data[s:e]))
+ out.append('<ins>%s</ins>' % html_quote(new))
+ prev = e
+ if prev:
+ out.append(html_quote(data[prev:prev+10]))
+ if prev+10 < len(data):
+ out.append('...')
+ return ''.join(out)
Modified: kupu/trunk/kupu/plone/kupu_config.pt
==============================================================================
--- kupu/trunk/kupu/plone/kupu_config.pt (original)
+++ kupu/trunk/kupu/plone/kupu_config.pt Tue Oct 10 16:56:24 2006
@@ -33,7 +33,7 @@
<!-- simulating views -->
<ul class="contentViews"
tal:define="tabs python:('Config','kupu_config'),('Libraries','zmi_libraries'),('Resource Types','zmi_resource_types'),
- ('Documentation', 'zmi_docs'),;
+ ('Documentation', 'zmi_docs'),('Links', 'zmi_links'),;
tabs python:[ {'label':label, 'name':name} for (label,name) in tabs ];"
>
<li tal:repeat="tab tabs"
Added: kupu/trunk/kupu/plone/kupu_plone_layer/kupu_kjax_support.xml.pt
==============================================================================
--- (empty file)
+++ kupu/trunk/kupu/plone/kupu_plone_layer/kupu_kjax_support.xml.pt Tue Oct 10 16:56:24 2006
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<tal:block
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ xmlns:html="http://www.w3.org/TR/REC-html40"
+ define="charset here/portal_properties/site_properties/default_charset|here/portal_properties/default_charset|string:utf-8;
+ content_type python:request.RESPONSE.setHeader('Content-Type', 'text/xml;;charset=%s' % charset);">
+<html
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ xmlns:html="http://www.w3.org/TR/REC-html40"
+ xmlns:kj="http://kupu.oscom.org/namespaces/kjax">
+ <body>
+ <div tal:condition="request/qlen|nothing"
+ tal:define="length python:len(context.portal_catalog.searchResults(**request.form));"
+ id="query_length"
+ kj:mode="replace"
+ tal:content="string: $length objects in catalog">
+ </div>
+ </body>
+</html>
+</tal:block>
Added: kupu/trunk/kupu/plone/kupu_plone_layer/kupu_migration.xml.pt
==============================================================================
--- (empty file)
+++ kupu/trunk/kupu/plone/kupu_plone_layer/kupu_migration.xml.pt Tue Oct 10 16:56:24 2006
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="utf-8"?>
+<tal:block
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ xmlns:html="http://www.w3.org/TR/REC-html40"
+ define="charset here/portal_properties/site_properties/default_charset|here/portal_properties/default_charset|string:utf-8;
+ content_type python:request.RESPONSE.setHeader('Content-Type', 'text/xml;;charset=%s' % charset);">
+ <html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:kj="http://kupu.oscom.org/namespaces/kjax"
+
+ tal:define="pss modules/Products/PythonScripts/standard;
+ wantform not:request/form.submitted|request/button|nothing;
+ uri string:${context/absolute_url_path}/${template/getId}">
+ <body>
+ <div id="target" kj:mode="replace" tal:condition="wantform">
+ <form action="kupu_migration.xml"
+ method="post"
+ name="options_form"
+ onsubmit="return kj.submitForm(this);">
+ <fieldset>
+ <legend>Type (Field name)</legend>
+ <div tal:repeat="f context/getKupuFields">
+ <tal:var define="id string:type_radio_${repeat/f/index};
+ first repeat/f/start;
+ checked python:test(first,'checked',None);
+ pt python:pss.url_quote(f['portal_type']);
+ infouri string:${context/absolute_url_path}/kupu_kjax_support.xml?qlen=1&portal_type=$pt;">
+ <input type="hidden" tal:attributes="value f/portal_type" name="fields.portal_type:records" />
+ <input type="hidden" tal:attributes="value f/type" name="fields.type:records" />
+ <input type="hidden" tal:attributes="value f/name" name="fields.name:records" />
+ <input type="hidden" tal:attributes="value f/label" name="fields.label:records" />
+ <input type="radio" name="fields.selected:records" value="1"
+ tal:attributes="onclick string:kj.newRequest('$infouri');
+ id id;checked checked;" />
+ <label tal:attributes="for id;">
+ <span tal:omit-tag="" tal:content="string:${f/type} (${f/label})" />
+ </label><span tal:attributes="kj:next infouri" tal:condition="checked" />
+ </tal:var>
+ </div>
+ </fieldset>
+ <fieldset>
+ <legend>Info</legend>
+ <div id="query_length"></div>
+ </fieldset>
+ <fieldset>
+ <legend>Save changes</legend>
+ <div class="formHelp">
+ Unless you agree that you are willing to let it update
+ your content, this tool will normally only show you what it could do.
+ If you have backed up your data and want to convert your links, type
+ 'I agree' in the box below. The 'check links' option never changes content
+ whatever you type.
+ </div>
+ <input type="text" name="dryrun" value="" />
+ </fieldset>
+ <input type="hidden" name="form.submitted" value="1" />
+ <input type="hidden" name="button" value="" />
+ <input type="submit" value="check links" name="checklinks"
+ onclick="this.form.button.value='check'" />
+ <input type="submit" value="relative -> uids" name="touids"
+ onclick="this.form.button.value='touid'" />
+ <input type="submit" value="uids -> path" name="topath"
+ onclick="this.form.button.value='topath'" />
+ </form>
+ </div>
+ <div id="kupu-output" kj:mode="replace" tal:condition="wantform" />
+
+ <div tal:condition="not:wantform">
+ <tal:var tal:define="action request/button|nothing;
+ m python:context.link_migration(action);">
+ <div tal:condition="m/nexturi|nothing" tal:attributes="kj:next m/nexturi" />
+ <div id="target" kj:mode="replace">
+ <!-- <pre tal:content="context/link_migration">current status</pre> -->
+ <div tal:condition="m/position|nothing">
+ <!-- progress bar -->
+ <div class="kupu-progress"> 
+ <div class="kupu-progressbar"
+ tal:attributes="style string:width:${m/percent}"> </div>
+ <div class="kupu-progresstext"
+ tal:content="string:${m/position} of ${m/total}" />
+ </div>
+ </div>
+
+ <input type="button"
+ value="go again!"
+ tal:condition="not:m/nexturi|nothing"
+ tal:attributes="onclick string:kj.newRequest('${uri}')" />
+
+ <h3 tal:condition="m/heading|nothing" tal:content="m/heading|nothing" />
+ <div class="highlightedSearchTerm" style="text-align:center"
+ tal:condition="m/dryrun|nothing"
+ tal:content="m/dryrun|nothing" />
+ </div>
+ <div id="kupu-output" kj:mode="append"
+ tal:condition="m/objects|nothing">
+ <div tal:repeat="o m/objects">
+ <div style="margin-top:1em"><a tal:attributes="href o/url" tal:content="o/title" /></div>
+ <blockquote tal:condition="o/info|nothing">
+ <div tal:repeat="link o/info" tal:content="link" />
+ </blockquote>
+ <div style="padding-left:1em"
+ tal:content="structure o/diffs"
+ tal:condition="o/diffs|nothing" />
+ </div>
+ </div>
+ </tal:var>
+ </div>
+ </body>
+ </html>
+</tal:block>
Modified: kupu/trunk/kupu/plone/kupu_plone_layer/kupuplone.css.dtml
==============================================================================
--- kupu/trunk/kupu/plone/kupu_plone_layer/kupuplone.css.dtml (original)
+++ kupu/trunk/kupu/plone/kupu_plone_layer/kupuplone.css.dtml Tue Oct 10 16:56:24 2006
@@ -273,6 +273,17 @@
.even .kupu-preview-row {
background-color: &dtml-evenRowBackgroundColor;;
}
-
+.kupu-progress {
+ background-color: white;;
+ border: &dtml-borderWidth; &dtml-borderStyle; &dtml-contentViewBorderColor;;
+ width: 90%; margin: 1em; position: relative;
+}
+.kupu-progressbar {
+ background-color: &dtml-contentViewBackgroundColor;;
+ width: 50%; position:absolute;top:0;
+}
+.kupu-progresstext {
+ text-align:center;position:absolute;top:0;width:100%;
+}
/* </dtml-let></dtml-with> */
Modified: kupu/trunk/kupu/plone/plonedrawers.py
==============================================================================
--- kupu/trunk/kupu/plone/plonedrawers.py (original)
+++ kupu/trunk/kupu/plone/plonedrawers.py Tue Oct 10 16:56:24 2006
@@ -714,7 +714,15 @@
def canCaption(self, field):
return (getattr(field, 'default_output_type', None) in
('text/x-html-safe', 'text/x-html-captioned'))
-
+
+ security.declareProtected("View", "getKupuFields")
+ def getKupuFields(self, filter=1):
+ """Returns a list of all kupu editable fields"""
+ inuse = getToolByName(self, 'portal_catalog').uniqueValuesFor('portal_type')
+ for t,f,pt in self._getKupuFields():
+ if pt in inuse or not filter:
+ yield { 'type': t, 'name': f.getName(), 'label': f.widget.label, 'portal_type':pt }
+
def _getKupuFields(self):
"""Yield all fields which are editable using kupu"""
archetype_tool = getToolByName(self, 'archetype_tool', None)
@@ -726,18 +734,18 @@
for f in schema.fields():
w = f.widget
if isinstance(w, (RichWidget, VisualWidget)):
- yield typename, f
+ yield typename, f, t['portal_type']
security.declareProtected("View", "supportedCaptioning")
def supportedCaptioning(self):
"""Returns a list of document/fields which have support for captioning"""
- supported = [t+'/'+f.widget.label for (t,f) in self._getKupuFields() if self.canCaption(f) ]
+ supported = [t+'/'+f.widget.label for (t,f,pt) in self._getKupuFields() if self.canCaption(f) ]
return str.join(', ', supported)
security.declareProtected("View", "unsupportedCaptioning")
def unsupportedCaptioning(self):
"""Returns a list of document/fields which do not have support for captioning"""
- unsupp = [t+'/'+f.widget.label for (t,f) in self._getKupuFields() if not self.canCaption(f) ]
+ unsupp = [t+'/'+f.widget.label for (t,f,pt) in self._getKupuFields() if not self.canCaption(f) ]
return str.join(', ', unsupp)
def transformIsEnabled(self):
@@ -789,4 +797,20 @@
base = base[:posfactory]
return base
+ def _getImageSizes(self):
+ resource_type = self.getResourceType()
+ portal = getToolByName(self, 'portal_url').getPortalObject()
+ mediatypes = resource_type.get_portal_types()
+ catalog = getToolByName(self, 'portal_catalog')
+ adaptor = InfoAdaptor(self, resource_type, portal)
+
+ sizes = {}
+ for portal_type in mediatypes:
+ brains = catalog.searchResults(portal_type=portal_type, limit=1)[:1]
+ if brains:
+ info = adaptor.get_image_sizes(brains[0], portal_type, '')
+ for i in info:
+ sizes[i['uri']] = 1
+ return sizes
+
InitializeClass(PloneDrawers)
Modified: kupu/trunk/kupu/plone/plonelibrarytool.py
==============================================================================
--- kupu/trunk/kupu/plone/plonelibrarytool.py (original)
+++ kupu/trunk/kupu/plone/plonelibrarytool.py Tue Oct 10 16:56:24 2006
@@ -33,6 +33,7 @@
from Products.kupu.config import TOOLNAME, TOOLTITLE
from StringIO import StringIO
from urllib import quote_plus, unquote_plus
+import html2captioned
_default_libraries = (
dict(id="root",
@@ -110,9 +111,10 @@
# We load default values here, so __init__ can still be used
# in unit tests. Plus, it only makes sense to load these if
# we're being added to a Plone site anyway
- for lib in _default_libraries:
- self.addLibrary(**lib)
- self._res_types.update(_default_resource_types)
+ if not len(self._libraries):
+ for lib in _default_libraries:
+ self.addLibrary(**lib)
+ self._res_types.update(_default_resource_types)
security.declareProtected('View', "getLinkbyuid")
def getLinkbyuid(self):
@@ -371,6 +373,36 @@
_docs = f.read()
return _docs
+ security.declareProtected(permissions.ManageLibraries, "link_migration")
+ def link_migration(self, button='status'):
+ """Do link checking or conversion, a little bit at a time"""
+ action = button
+ migrator = html2captioned.Migration(self)
+ if action=='continue':
+ migrator.restoreState()
+ res = migrator.docontinue()
+ info = migrator.getInfo()
+ if res:
+ migrator.saveState()
+ else:
+ migrator.clearState()
+ return info
+ elif action=='status':
+ try:
+ migrator.restoreState()
+ except KeyError:
+ return "state cleared"
+ return migrator.status()
+
+ else:
+ migrator.initFromRequest()
+ migrator.saveState()
+ return migrator.getInfo()
+
+ security.declareProtected(permissions.ManageLibraries, "zmi_links")
+ zmi_links = PageTemplateFile("zmi_links.pt", globals())
+ zmi_links.title = 'kupu link maintenance'
+
security.declareProtected(permissions.ManageLibraries, "zmi_docs")
zmi_docs = PageTemplateFile("zmi_docs.pt", globals())
zmi_docs.title = 'kupu configuration documentation'
Modified: kupu/trunk/kupu/plone/tests/runme.cmd
==============================================================================
--- kupu/trunk/kupu/plone/tests/runme.cmd (original)
+++ kupu/trunk/kupu/plone/tests/runme.cmd Tue Oct 10 16:56:24 2006
@@ -7,9 +7,11 @@
set PRODUCTS_PATH=;%~D0%~P0..\..\..;%PLONEHOME%\Zope\lib\python\Products;%PLONEHOME%\Data\Products
set INSTANCE_HOME=%PLONEHOME%\Data
set SOFTWARE_HOME=%PLONEHOME%\Zope\lib\python
+ at set PYTHON=C:\Plone20\Zope\bin\python.exe
rem "%PLONEHOME%\Python\python.exe" %~D0%~P0test_browserSupportsKupu.py %2
rem "%PLONEHOME%\Python\python.exe" %~D0%~P0test_librarymanager.py
rem "%PLONEHOME%\Python\python.exe" %~D0%~P0test_html2captioned.py
rem "%PLONEHOME%\Python\python.exe" %~D0%~P0test_resourcetypemapper.py
-"%PLONEHOME%\Python\python.exe" "%~D0%~P0runalltests.py" %2
+rem "%PYTHON%" %~D0%~P0test_urls.py
+"%PYTHON%" "%~D0%~P0runalltests.py" %2
endlocal
Modified: kupu/trunk/kupu/plone/tests/test_browserSupportsKupu.py
==============================================================================
--- kupu/trunk/kupu/plone/tests/test_browserSupportsKupu.py (original)
+++ kupu/trunk/kupu/plone/tests/test_browserSupportsKupu.py Tue Oct 10 16:56:24 2006
@@ -21,7 +21,15 @@
from Products.CMFPlone.tests import PloneTestCase
from Products.CMFPlone.tests.PloneTestCase import portal_name, portal_owner
from AccessControl.SecurityManagement import newSecurityManager, noSecurityManager
+try:
+ import transaction
+except ImportError:
+ class dummy:
+ def get(self): return get_transaction()
+ def commit(self): return self.get().commit()
+ transaction = dummy()
+
def installKupu(quiet=0):
_start = time.time()
if not quiet: ZopeTestCase._print('Adding Kupu ... ')
@@ -40,7 +48,7 @@
# Log out
noSecurityManager()
- get_transaction().commit()
+ transaction.commit()
if not quiet: ZopeTestCase._print('done (%.3fs)\n' \
% (time.time()-_start,))
ZopeTestCase.close(app)
Modified: kupu/trunk/kupu/plone/tests/test_html2captioned.py
==============================================================================
--- kupu/trunk/kupu/plone/tests/test_html2captioned.py (original)
+++ kupu/trunk/kupu/plone/tests/test_html2captioned.py Tue Oct 10 16:56:24 2006
@@ -6,15 +6,6 @@
from Products.CMFPlone.tests import PloneTestCase
from os.path import join, abspath, dirname
-#try:
-# import Zope # Sigh, make product initialization happen
-# HAS_ZOPE = 1
-# Zope.startup()
-#except ImportError:
-# HAS_ZOPE = 0
-#except AttributeError: # Zope > 2.6
-# pass
-
from Products.PortalTransforms.tests.test_transforms import *
from Products.PortalTransforms.tests.utils import normalize_html
@@ -44,6 +35,8 @@
return self.description
def absolute_url(self):
return '[url for %s]' % self.uid
+ def absolute_url_path(self):
+ return '[url for %s]' % self.uid
class MockCatalogTool:
def lookupObject(self, uid):
Added: kupu/trunk/kupu/plone/tests/test_links.py
==============================================================================
--- (empty file)
+++ kupu/trunk/kupu/plone/tests/test_links.py Tue Oct 10 16:56:24 2006
@@ -0,0 +1,253 @@
+##############################################################################
+#
+# Copyright (c) 2003-2005 Kupu Contributors. All rights reserved.
+#
+# This software is distributed under the terms of the Kupu
+# License. See LICENSE.txt for license text. For a list of Kupu
+# Contributors see CREDITS.txt.
+#
+##############################################################################
+"""Tests for the link checking and migration code"""
+
+import os, sys
+if __name__ == '__main__':
+ execfile(os.path.join(sys.path[0], 'framework.py'))
+
+import Acquisition
+from Testing.ZopeTestCase import ZopeTestCase, installProduct
+from Products.CMFPlone.tests.PloneTestCase import portal_name, portal_owner
+from Products.Archetypes.tests import ArchetypesTestCase
+from AccessControl.SecurityManagement import newSecurityManager
+try:
+ from Products.ATContentTypes.lib import constraintypes
+except:
+ constraintypes = None
+from Products.kupu.plone.tests import TestContent
+
+try:
+ installProduct('Five', 1)
+except:
+ pass
+installProduct('ATContentTypes', 1)
+installProduct('kupu', 1)
+
+from Products.kupu.plone.plonelibrarytool import PloneKupuLibraryTool
+from Products.kupu.plone.html2captioned import Migration
+
+RESOURCES = dict(
+ linkable = ('Document', 'Image', 'File', 'Folder'),
+ mediaobject = ('Image',),
+ collection = ('Folder',),
+ containsanchors = ('Document',),
+ )
+
+# Type names vary according to the version of Plone and/or
+# ATContentTypes. Map the new names to the old ones here, and
+# turn it into an identity mapping later if we can.
+TypeMapping = {
+ 'Document': 'ATDocument',
+ 'Image': 'ATImage',
+ 'Link': 'ATLink',
+ 'Folder': 'ATFolder',
+ 'File': 'ATFile',
+ 'News Item': 'ATNewsItem',
+ 'Event': 'ATEvent',
+}
+def MapType(typename):
+ return TypeMapping[typename]
+
+class TestLinkCode(ArchetypesTestCase.ArcheSiteTestCase):
+ """Test the link checking code"""
+
+ def afterSetUp(self):
+ portal = self.portal
+ self.setRoles(['Manager',])
+ quickinstaller = portal.portal_quickinstaller
+ quickinstaller.installProduct('ATContentTypes')
+ quickinstaller.installProduct('kupu')
+ self.kupu = portal.kupu_library_tool
+ typestool = self.portal.portal_types
+ if not hasattr(typestool, 'ATDocument'):
+ # Use the type names without the AT prefix
+ for k in TypeMapping:
+ TypeMapping[k] = k
+
+ def loginPortalOwner(self):
+ '''Use if you need to manipulate the portal itself.'''
+ uf = self.app.acl_users
+ user = uf.getUserById(portal_owner).__of__(uf)
+ newSecurityManager(None, user)
+
+ def create(self, id, metatype='ATDocument', folder=None, **kwds):
+ '''Create an object in the cms portal'''
+ if folder is None:
+ folder = self.portal
+
+ folder.invokeFactory(MapType(metatype), id)
+ obj = getattr(folder, id)
+
+ if metatype=='Folder' and constraintypes:
+ obj.setConstrainTypesMode(constraintypes.DISABLED)
+
+ if metatype=='Document':
+ obj.setTitle('Simple document')
+ obj.setText('Sample document text')
+ for k, v in kwds.items():
+ field = obj.getField(k)
+ mutator = field.getMutator(obj)(v)
+
+ obj.reindexObject()
+ return obj
+
+ def setup_content(self):
+ self.setRoles(['Manager',])
+ self.loginPortalOwner()
+ f = self.create('folder', 'Folder')
+
+ for id in ('alpha', 'beta'):
+ self.create(id, 'Document', f, subject=['aspidistra'])
+ self.create('gamma', 'Image', f)
+
+ sub1 = self.create('sub1', 'Folder', f)
+ sub1.setSubject(['aspidistra'])
+ sub1.reindexObject()
+ sub2 = self.create('sub2', 'Folder', f)
+ self.create('delta', 'Folder', sub2)
+
+ portal = self.portal
+ tool = self.portal.kupu_library_tool
+ types = tool.zmi_get_resourcetypes()
+ #tool.deleteResource([ t.name for t in types])
+ for k,v in RESOURCES.items():
+ tool.addResourceType(k, [MapType(t) for t in v])
+
+ def test_relative(self):
+ self.setup_content()
+ migrator = Migration(self.kupu)
+ portal = self.portal
+ base = portal.folder.sub2.delta.absolute_url()
+ path = '../alpha'
+ expected = ('internal', portal.folder.alpha.UID(), path, '')
+ self.assertEquals(expected, migrator.classifyLink(path, base))
+
+ def test_external(self):
+ self.setup_content()
+ migrator = Migration(self.kupu)
+ portal = self.portal
+ base = portal.folder.sub2.delta.absolute_url()
+ path = 'mailto:me at nowhere'
+ expected = ('external', None, path, '')
+ self.assertEquals(expected, migrator.classifyLink(path, base))
+
+ def test_localexternal(self):
+ self.setup_content()
+ migrator = Migration(self.kupu)
+ portal = self.portal
+ base = portal.folder.sub2.delta.absolute_url()
+ path = 'http://nohost/plone/folder/alpha'
+ expected = ('internal', portal.folder.alpha.UID(), '../alpha', '')
+ self.assertEquals(expected, migrator.classifyLink(path, base))
+
+ def test_abspath(self):
+ self.setup_content()
+ migrator = Migration(self.kupu)
+ portal = self.portal
+ base = portal.folder.alpha.absolute_url()
+ path = '/plone/folder/beta'
+ expected = ('internal', portal.folder.beta.UID(), 'beta', '')
+ self.assertEquals(expected, migrator.classifyLink(path, base))
+
+ def test_anchor(self):
+ self.setup_content()
+ migrator = Migration(self.kupu)
+ portal = self.portal
+ base = portal.folder.alpha.absolute_url()
+ path = '#fred'
+ expected = ('internal', None, '', path)
+ self.assertEquals(expected, migrator.classifyLink(path, base))
+
+ def test_redundant(self):
+ self.setup_content()
+ migrator = Migration(self.kupu)
+ portal = self.portal
+ base = portal.folder.alpha.absolute_url()
+ path = 'alpha#fred'
+ expected = ('internal', None, '', '#fred')
+ self.assertEquals(expected, migrator.classifyLink(path, base))
+
+ def test_bad_portal_factory(self):
+ # Some version of kupu wrongly inserted jumplinks to
+ # portal_factory. Check these get cleaned.
+ self.setup_content()
+ migrator = Migration(self.kupu)
+ portal = self.portal
+ base = portal.folder.alpha.absolute_url()
+ path = 'portal_factory#fred'
+ expected = ('internal', None, '', '#fred')
+ self.assertEquals(expected, migrator.classifyLink(path, base))
+
+ def test_dot(self):
+ self.setup_content()
+ migrator = Migration(self.kupu)
+ portal = self.portal
+ base = portal.folder.alpha.absolute_url()
+ path = '.'
+ expected = ('internal', portal.folder.UID(), path, '')
+ self.assertEquals(expected, migrator.classifyLink(path, base))
+
+ def test_resolveuid(self):
+ self.setup_content()
+ migrator = Migration(self.kupu)
+ portal = self.portal
+ base = portal.folder.alpha.absolute_url()
+ path = 'resolveuid/' + portal.folder.beta.UID()
+ expected = ('internal', portal.folder.beta.UID(), 'beta', '')
+ self.assertEquals(expected, migrator.classifyLink(path, base))
+
+ def test_resolveuidEmbedded(self):
+ self.setup_content()
+ migrator = Migration(self.kupu)
+ portal = self.portal
+ base = portal.folder.alpha.absolute_url()
+ path = 'wibble/resolveuid/' + portal.folder.beta.UID() + '#fragment'
+ expected = ('internal', portal.folder.beta.UID(), 'beta', '#fragment')
+ self.assertEquals(expected, migrator.classifyLink(path, base))
+
+ def test_badlink(self):
+ self.setup_content()
+ migrator = Migration(self.kupu)
+ portal = self.portal
+ base = portal.folder.alpha.absolute_url()
+ path = 'wibble'
+ expected = ('bad', None, path, '')
+ self.assertEquals(expected, migrator.classifyLink(path, base))
+
+ def test_image(self):
+ self.setup_content()
+ migrator = Migration(self.kupu)
+ portal = self.portal
+ base = portal.folder.alpha.absolute_url()
+ path = 'gamma/image_thumb'
+ expected = ('internal', portal.folder.gamma.UID(), 'gamma', '/image_thumb')
+ self.assertEquals(expected, migrator.classifyLink(path, base))
+
+ def test_image2(self):
+ self.setup_content()
+ migrator = Migration(self.kupu)
+ portal = self.portal
+ base = portal.folder.alpha.absolute_url()
+ path = 'resolveuid/'+portal.folder.gamma.UID()+'/image_icon'
+ expected = ('internal', portal.folder.gamma.UID(), 'gamma', '/image_icon')
+ self.assertEquals(expected, migrator.classifyLink(path, base))
+
+
+if __name__ == '__main__':
+ framework()
+else:
+ # While framework.py provides its own test_suite()
+ # method the testrunner utility does not.
+ from unittest import TestSuite, makeSuite
+ def test_suite():
+ suite = TestSuite()
+ suite.addTest(makeSuite(TestLinkCode))
+ return suite
Modified: kupu/trunk/kupu/plone/tests/test_plonedrawer.py
==============================================================================
--- kupu/trunk/kupu/plone/tests/test_plonedrawer.py (original)
+++ kupu/trunk/kupu/plone/tests/test_plonedrawer.py Tue Oct 10 16:56:24 2006
@@ -27,6 +27,10 @@
constraintypes = None
from Products.kupu.plone.tests import TestContent
+try:
+ installProduct('Five', 1)
+except:
+ pass
installProduct('ATContentTypes', 1)
installProduct('kupu', 1)
Added: kupu/trunk/kupu/plone/tests/test_urls.py
==============================================================================
--- (empty file)
+++ kupu/trunk/kupu/plone/tests/test_urls.py Tue Oct 10 16:56:24 2006
@@ -0,0 +1,93 @@
+##############################################################################
+#
+# Copyright (c) 2003-2005 Kupu Contributors. All rights reserved.
+#
+# This software is distributed under the terms of the Kupu
+# License. See LICENSE.txt for license text. For a list of Kupu
+# Contributors see CREDITS.txt.
+#
+##############################################################################
+"""Tests for the library tool
+
+$Id: test_resourcetypemapper.py 21075 2005-12-12 12:59:59Z duncan $
+"""
+
+import os, sys
+if __name__ == '__main__':
+ execfile(os.path.join(sys.path[0], 'framework.py'))
+
+
+import unittest
+from urlparse import urlsplit, urljoin, urlunsplit
+from Products.kupu.plone.html2captioned import makeUrlRelative
+
+class test_urls(unittest.TestCase):
+ def test1(self):
+ data = 'http://host/site/a'
+ base = 'http://host/site/'
+ expected = 'a', ''
+ self.assertEquals(expected, makeUrlRelative(data, base))
+
+ def testRelativeLinks1(self):
+ data = 'http://localhost/cms/folder/jim#_ftnref1'
+ expected = "jim", "#_ftnref1"
+ base = 'http://localhost/cms/folder/fred'
+
+ self.assertEquals(expected, makeUrlRelative(data, base))
+
+ def testRelativeLinks2(self):
+ data = "http://localhost/cms/folder/otherdoc#key"
+ expected = "otherdoc", "#key"
+ base = 'http://localhost/cms/folder/'
+
+ self.assertEquals(expected, makeUrlRelative(data, base))
+
+ def testRelativeLinks3(self):
+ data = "http://localhost/cms/otherfolder/otherdoc"
+ expected = "../otherfolder/otherdoc", ""
+ base = 'http://localhost/cms/folder/'
+
+ self.assertEquals(expected, makeUrlRelative(data, base))
+
+ def testRelativeLinks4(self):
+ data = "http://localhost:9080/plone/Members/admin/art1"
+ expected = "", ""
+ base = 'http://localhost:9080/plone/Members/admin/art1'
+
+ self.assertEquals(expected, makeUrlRelative(data, base))
+
+ def testRelativeLinks5(self):
+ data = 'http://localhost:9080/plone/Members/admin/art1/subitem'
+ expected = "art1/subitem", ""
+ base = 'http://localhost:9080/plone/Members/admin/art1'
+
+ actual = makeUrlRelative(data, base)
+ self.assertEquals(actual, expected)
+
+ def testRelativeLinks6(self):
+ data = "http://localhost:9080/plone/Members/admin"
+ expected = ".", ""
+ base = 'http://localhost:9080/plone/Members/admin/art1'
+
+ actual = makeUrlRelative(data, base)
+ self.assertEquals(actual, expected)
+
+ def testRelativeQuery(self):
+ data = "http://localhost:9080/plone/Members/admin/jim?x=5"
+ expected = "jim", "?x=5"
+ base = 'http://localhost:9080/plone/Members/admin/art1'
+
+ actual = makeUrlRelative(data, base)
+ self.assertEquals(actual, expected)
+
+if __name__ == '__main__':
+ framework()
+else:
+ # While framework.py provides its own test_suite()
+ # method the testrunner utility does not.
+ from unittest import TestSuite, makeSuite
+ def test_suite():
+ suite = TestSuite()
+ suite.addTest(makeSuite(test_urls))
+ return suite
+
Added: kupu/trunk/kupu/plone/zmi_links.pt
==============================================================================
--- (empty file)
+++ kupu/trunk/kupu/plone/zmi_links.pt Tue Oct 10 16:56:24 2006
@@ -0,0 +1,32 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
+lang="en"
+metal:use-macro="here/kupu_config/macros/master"
+i18n:domain="plone">
+
+ <metal:cssslot fill-slot="css_slot">
+ <link href="kupustyles.css" rel="stylesheet" type="text/css" tal:attributes="href string:${portal_url}/kupustyles.css"/>
+ <link href="kupuplone.css" rel="stylesheet" type="text/css" tal:attributes="href string:${portal_url}/kupuplone.css"/>
+ <link href="kupudrawerstyles.css" rel="stylesheet" type="text/css" tal:attributes="href string:${portal_url}/kupudrawerstyles.css"/>
+ </metal:cssslot>
+ <body>
+
+ <div class="documentContent" metal:fill-slot="kupu_content">
+ <tal:if condition="not:exists:portal/portal_javascripts"
+ metal:use-macro="context/kupu_wysiwyg_support/macros/kupu_js_include">
+ </tal:if>
+ <script type="text/javascript"
+ tal:attributes="src context/kupu_kjax.js/absolute_url_path"
+ ></script>
+ <script type="text/javascript">
+registerPloneFunction(function() {kj.newRequest('kupu_migration.xml');});
+ </script>
+ <div id="target">
+ Loading kupu link maintenance...
+ </div>
+ <div id="kupu-output"></div>
+<!-- <div id="log" style="height:100px;overflow:scroll;"> -->
+<!-- </div> -->
+ </div>
+ </body>
+</html>
+
More information about the kupu-checkins
mailing list