Skip to content

/ Zope / gocept svn checkins / Archive / 2008 / 2008-11 / SVN: r6981 - gocept.pagewithimage/trunk/gocept/pagewithimage

[ << ] [ >> ]

[ SVN: r6980 - in gocept.infrastructure/testing/pupp... ] [ SVN: r6983 - in CMFWebmail/trunk: . gocept ... ]

SVN: r6981 - gocept.pagewithimage/trunk/gocept/pagewithimage
Daniel Havlik <dh(at)gocept.com>
2008-11-04 08:53:50 [ FULL ]
Author: nilo
Date: Tue Nov  4 08:53:47 2008
New Revision: 6981

Log:
moved viewlet html js and css stuff into a template


Added:
   gocept.pagewithimage/trunk/gocept/pagewithimage/viewlet.pt
Modified:
   gocept.pagewithimage/trunk/gocept/pagewithimage/configure.zcml
   gocept.pagewithimage/trunk/gocept/pagewithimage/viewlet.py

Modified: gocept.pagewithimage/trunk/gocept/pagewithimage/configure.zcml
==============================================================================
--- gocept.pagewithimage/trunk/gocept/pagewithimage/configure.zcml	(original)
+++ gocept.pagewithimage/trunk/gocept/pagewithimage/configure.zcml	Tue Nov  4
08:53:47 2008
(at)(at) -18,7 +18,8 (at)(at)
     <browser:viewlet
         name="gocept.pagewithimageviewlet"
         manager="plone.app.layout.viewlets.interfaces.IAboveContentBody"
-        class=".viewlet.PageWithImageViewlet"
+        template="viewlet.pt"
+	class=".viewlet.PageWithImageViewlet"
         permission="zope2.View"
         />
 

Added: gocept.pagewithimage/trunk/gocept/pagewithimage/viewlet.pt
==============================================================================
--- (empty file)
+++ gocept.pagewithimage/trunk/gocept/pagewithimage/viewlet.pt	Tue Nov  4
08:53:47 2008
(at)(at) -0,0 +1,407 (at)(at)
+<script type="text/javascript">
+// If you would like to use a custom loading image or close button reference
them in the next two lines.
+var loadingImage = 'loading.gif';		
+var closeButton = 'close.gif';		
+
+// getPageScroll()
+// Returns array with x,y page scroll values.
+// Core code from - quirksmode.org
+function getPageScroll(){
+
+	var yScroll;
+
+	if (self.pageYOffset) {
+		yScroll = self.pageYOffset;
+	} else if (document.documentElement &&
document.documentElement.scrollTop){	 // Explorer 6 Strict
+		yScroll = document.documentElement.scrollTop;
+	} else if (document.body) {// all other Explorers
+		yScroll = document.body.scrollTop;
+	}
+
+	arrayPageScroll = new Array('',yScroll) 
+	return arrayPageScroll;
+}
+
+// getPageSize()
+// Returns array with page width, height and window width, height
+// Core code from - quirksmode.org
+// Edit for Firefox by pHaez
+function getPageSize(){
+	
+	var xScroll, yScroll;
+	
+	if (window.innerHeight && window.scrollMaxY) {	
+		xScroll = document.body.scrollWidth;
+		yScroll = window.innerHeight + window.scrollMaxY;
+	} else if (document.body.scrollHeight > document.body.offsetHeight){ //
all but Explorer Mac
+		xScroll = document.body.scrollWidth;
+		yScroll = document.body.scrollHeight;
+	} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and
Safari
+		xScroll = document.body.offsetWidth;
+		yScroll = document.body.offsetHeight;
+	}
+	
+	var windowWidth, windowHeight;
+	if (self.innerHeight) {	// all except Explorer
+		windowWidth = self.innerWidth;
+		windowHeight = self.innerHeight;
+	} else if (document.documentElement &&
document.documentElement.clientHeight) { // Explorer 6 Strict Mode
+		windowWidth = document.documentElement.clientWidth;
+		windowHeight = document.documentElement.clientHeight;
+	} else if (document.body) { // other Explorers
+		windowWidth = document.body.clientWidth;
+		windowHeight = document.body.clientHeight;
+	}	
+	
+	// for small pages with total height less then height of the viewport
+	if(yScroll < windowHeight){
+		pageHeight = windowHeight;
+	} else { 
+		pageHeight = yScroll;
+	}
+
+	// for small pages with total width less then width of the viewport
+	if(xScroll < windowWidth){	
+		pageWidth = windowWidth;
+	} else {
+		pageWidth = xScroll;
+	}
+
+
+	arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight) 
+	return arrayPageSize;
+}
+
+
+//
+// pause(numberMillis)
+// Pauses code execution for specified time. Uses busy code, not good.
+// Code from http://www.faqts.com/knowledge_base/view.phtml/aid/1602
+//
+function pause(numberMillis) {
+	var now = new Date();
+	var exitTime = now.getTime() + numberMillis;
+	while (true) {
+		now = new Date();
+		if (now.getTime() > exitTime)
+			return;
+	}
+}
+
+//
+// getKey(key)
+// Gets keycode. If 'x' is pressed then it hides the lightbox.
+//
+
+function getKey(e){
+	if (e == null) { // ie
+		keycode = event.keyCode;
+	} else { // mozilla
+		keycode = e.which;
+	}
+	key = String.fromCharCode(keycode).toLowerCase();
+	
+	if(key == 'x'){ hideLightbox(); }
+}
+
+
+//
+// listenKey()
+//
+function listenKey () {	document.onkeypress = getKey; }
+	
+
+//
+// showLightbox()
+// Preloads images. Pleaces new image in lightbox then centers and displays.
+//
+function showLightbox(objLink)
+{
+	// prep objects
+	var objOverlay = document.getElementById('overlay');
+	var objLightbox = document.getElementById('lightbox');
+	var objCaption = document.getElementById('lightboxCaption');
+	var objImage = document.getElementById('lightboxImage');
+	var objLoadingImage = document.getElementById('loadingImage');
+	var objLightboxDetails = document.getElementById('lightboxDetails');
+
+	
+	var arrayPageSize = getPageSize();
+	var arrayPageScroll = getPageScroll();
+
+	// center loadingImage if it exists
+	if (objLoadingImage) {
+		objLoadingImage.style.top = (arrayPageScroll[1] + ((arrayPageSize[3] - 35 -
objLoadingImage.height) / 2) + 'px');
+		objLoadingImage.style.left = (((arrayPageSize[0] - 20 -
objLoadingImage.width) / 2) + 'px');
+		objLoadingImage.style.display = 'block';
+	}
+
+	// set height of Overlay to take up whole page and show
+	objOverlay.style.height = (arrayPageSize[1] + 'px');
+	objOverlay.style.display = 'block';
+
+	// preload image
+	imgPreload = new Image();
+
+	imgPreload.onload=function(){
+		objImage.src = objLink.href;
+
+		// center lightbox and make sure that the top and left values are not
negative
+		// and the image placed outside the viewport
+		var lightboxTop = arrayPageScroll[1] + ((arrayPageSize[3] - 35 -
imgPreload.height) / 2);
+		var lightboxLeft = ((arrayPageSize[0] - 20 - imgPreload.width) / 2);
+		
+		objLightbox.style.top = (lightboxTop < 0) ? "0px" : lightboxTop + "px";
+		objLightbox.style.left = (lightboxLeft < 0) ? "0px" : lightboxLeft +
"px";
+
+
+		objLightboxDetails.style.width = imgPreload.width + 'px';
+		
+		if(objLink.getAttribute('title')){
+			objCaption.style.display = 'block';
+			//objCaption.style.width = imgPreload.width + 'px';
+			objCaption.innerHTML = objLink.getAttribute('title');
+		} else {
+			objCaption.style.display = 'none';
+		}
+		
+		// A small pause between the image loading and displaying is required with
IE,
+		// this prevents the previous image displaying for a short burst causing
flicker.
+		if (navigator.appVersion.indexOf("MSIE")!=-1){
+			pause(250);
+		} 
+
+		if (objLoadingImage) {	objLoadingImage.style.display = 'none'; }
+
+		// Hide select boxes as they will 'peek' through the image in IE
+		selects = document.getElementsByTagName("select");
+        for (i = 0; i != selects.length; i++) {
+                selects[i].style.visibility = "hidden";
+        }
+
+	
+		objLightbox.style.display = 'block';
+
+		// After image is loaded, update the overlay height as the new image might
have
+		// increased the overall page height.
+		arrayPageSize = getPageSize();
+		objOverlay.style.height = (arrayPageSize[1] + 'px');
+		
+		// Check for 'x' keypress
+		listenKey();
+
+		return false;
+	}
+
+	imgPreload.src = objLink.href;
+	
+}
+
+
+
+
+
+//
+// hideLightbox()
+//
+function hideLightbox()
+{
+	// get objects
+	objOverlay = document.getElementById('overlay');
+	objLightbox = document.getElementById('lightbox');
+
+	// hide lightbox and overlay
+	objOverlay.style.display = 'none';
+	objLightbox.style.display = 'none';
+
+	// make select boxes visible
+	selects = document.getElementsByTagName("select");
+    for (i = 0; i != selects.length; i++) {
+		selects[i].style.visibility = "visible";
+	}
+
+	// disable keypress listener
+	document.onkeypress = '';
+}
+
+
+
+
+//
+// initLightbox()
+// Function runs on window load, going through link tags looking for
rel="lightbox".
+// These links receive onclick events that enable the lightbox display for
their targets.
+// The function also inserts html markup at the top of the page which will be
used as a
+// container for the overlay pattern and the inline image.
+//
+function initLightbox()
+{
+	
+	if (!document.getElementsByTagName){ return; }
+	var anchors = document.getElementsByTagName("a");
+
+	// loop through all anchor tags
+	for (var i=0; i<anchors.length; i++){
+		var anchor = anchors[i];
+
+		if (anchor.getAttribute("href") && (anchor.getAttribute("rel") ==
"lightbox")){
+			anchor.onclick = function () {showLightbox(this); return false;}
+		}
+	}
+
+	var objBody = document.getElementsByTagName("body").item(0);
+	
+	// create overlay div and hardcode some functional styles (aesthetic styles
are in CSS file)
+	var objOverlay = document.createElement("div");
+	objOverlay.setAttribute('id','overlay');
+	objOverlay.onclick = function () {hideLightbox(); return false;}
+	objOverlay.style.display = 'none';
+	objOverlay.style.position = 'absolute';
+	objOverlay.style.top = '0';
+	objOverlay.style.left = '0';
+	objOverlay.style.zIndex = '90';
+ 	objOverlay.style.width = '100%';
+	objBody.insertBefore(objOverlay, objBody.firstChild);
+	
+	var arrayPageSize = getPageSize();
+	var arrayPageScroll = getPageScroll();
+
+	// preload and create loader image
+	var imgPreloader = new Image();
+	
+	// if loader image found, create link to hide lightbox and create
loadingimage
+	imgPreloader.onload=function(){
+
+		var objLoadingImageLink = document.createElement("a");
+		objLoadingImageLink.setAttribute('href','#');
+		objLoadingImageLink.onclick = function () {hideLightbox(); return false;}
+		objOverlay.appendChild(objLoadingImageLink);
+		
+		var objLoadingImage = document.createElement("img");
+		objLoadingImage.src = loadingImage;
+		objLoadingImage.setAttribute('id','loadingImage');
+		objLoadingImage.style.position = 'absolute';
+		objLoadingImage.style.zIndex = '150';
+		objLoadingImageLink.appendChild(objLoadingImage);
+
+		imgPreloader.onload=function(){};	//	clear onLoad, as IE will flip out
w/animated gifs
+
+		return false;
+	}
+
+	imgPreloader.src = loadingImage;
+
+	// create lightbox div, same note about styles as above
+	var objLightbox = document.createElement("div");
+	objLightbox.setAttribute('id','lightbox');
+	objLightbox.style.display = 'none';
+	objLightbox.style.position = 'absolute';
+	objLightbox.style.zIndex = '100';	
+	objBody.insertBefore(objLightbox, objOverlay.nextSibling);
+	
+	// create link
+	var objLink = document.createElement("a");
+	objLink.setAttribute('href','#');
+	objLink.setAttribute('title','Click to close');
+	objLink.onclick = function () {hideLightbox(); return false;}
+	objLightbox.appendChild(objLink);
+
+	// preload and create close button image
+	var imgPreloadCloseButton = new Image();
+
+	// if close button image found, 
+	imgPreloadCloseButton.onload=function(){
+
+		var objCloseButton = document.createElement("img");
+		objCloseButton.src = closeButton;
+		objCloseButton.setAttribute('id','closeButton');
+		objCloseButton.style.position = 'absolute';
+		objCloseButton.style.zIndex = '200';
+		objLink.appendChild(objCloseButton);
+
+		return false;
+	}
+
+	imgPreloadCloseButton.src = closeButton;
+
+	// create image
+	var objImage = document.createElement("img");
+	objImage.setAttribute('id','lightboxImage');
+	objLink.appendChild(objImage);
+	
+	// create details div, a container for the caption and keyboard message
+	var objLightboxDetails = document.createElement("div");
+	objLightboxDetails.setAttribute('id','lightboxDetails');
+	objLightbox.appendChild(objLightboxDetails);
+
+	// create caption
+	var objCaption = document.createElement("div");
+	objCaption.setAttribute('id','lightboxCaption');
+	objCaption.style.display = 'none';
+	objLightboxDetails.appendChild(objCaption);
+
+	// create keyboard message
+	var objKeyboardMsg = document.createElement("div");
+	objKeyboardMsg.setAttribute('id','keyboardMsg');
+	objKeyboardMsg.innerHTML = '' 
+	objLightboxDetails.appendChild(objKeyboardMsg);
+}
+
+//
+// addLoadEvent()
+// Adds event to window.onload without overwriting currently assigned onload
functions.
+// Function found at Simon Willison's weblog - http://simon.incutio.com/
+//
+function addLoadEvent(func)
+{	
+	var oldonload = window.onload;
+	if (typeof window.onload != 'function'){
+    	window.onload = func;
+	} else {
+		window.onload = function(){
+		oldonload();
+		func();
+		}
+	}
+
+}
+
+
+
+addLoadEvent(initLightbox);	// run initLightbox onLoad
+
+
+
+</script>
+<style type="text/css">
+
+#lightbox{
+	background-color:#eee;
+	padding: 10px;
+	border-bottom: 2px solid #666;
+	border-right: 2px solid #666;
+	}
+#lightboxDetails{
+	font-size: 0.8em;
+	padding-top: 0.4em;
+	}	
+#lightboxCaption{ float: left; }
+#keyboardMsg{ float: right; }
+#closeButton{ top: 5px; right: 5px; }
+
+#lightbox img{ border: none; clear: both;} 
+#overlay img{ border: none; }
+
+#overlay{ background-image: url(overlay.png); }
+
+* html #overlay{
+	background-color: #333;
+	back\ground-color: transparent;
+	background-image: url(blank.gif);
+	filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="overlay.png",
sizingMethod="scale");
+	}
+</style>
+<a href="%s" rel="lightbox" title="%s" class="docimage_link"
+    tal:attributes="href python:context.absolute_url() + '/docimage_large';
+                    title context/getImageCaption">
+<div style="float:right; margin-left:20px; margin-bottom:20px;"><img
src="%s/docimage_mini2" tal:attributes="src python:context.absolute_url() +
'/docimage_mini2'"/><p style="font-weight: bold;"
tal:content="context/getImageCaption">%s</p></div></a>

Modified: gocept.pagewithimage/trunk/gocept/pagewithimage/viewlet.py
==============================================================================
--- gocept.pagewithimage/trunk/gocept/pagewithimage/viewlet.py	(original)
+++ gocept.pagewithimage/trunk/gocept/pagewithimage/viewlet.py	Tue Nov  4
08:53:47 2008
(at)(at) -1,488 +1,17 (at)(at)
+# -*- coding: utf-8 -*-
+
 from plone.app.layout.viewlets.common import ViewletBase
 
 import Products.ATContentTypes.interface.document
-
-JS = u'''
-/*
-	Lightbox JS: Fullsize Image Overlays 
-	by Lokesh Dhakar - http://www.huddletogether.com
-
-	For more information on this script, visit:
-	http://huddletogether.com/projects/lightbox/
-
-	Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/
-	(basically, do anything you want, just leave my name and link)
-	
-	Table of Contents
-	-----------------
-	Configuration
-	
-	Functions
-	- getPageScroll()
-	- getPageSize()
-	- pause()
-	- getKey()
-	- listenKey()
-	- showLightbox()
-	- hideLightbox()
-	- initLightbox()
-	- addLoadEvent()
-	
-	Function Calls
-	- addLoadEvent(initLightbox)
-
-*/
-
-
-
-//
-// Configuration
-//
-
-// If you would like to use a custom loading image or close button reference
them in the next two lines.
-var loadingImage = 'loading.gif';		
-var closeButton = 'close.gif';		
-
-
-
-
-
-//
-// getPageScroll()
-// Returns array with x,y page scroll values.
-// Core code from - quirksmode.org
-//
-function getPageScroll(){
-
-	var yScroll;
-
-	if (self.pageYOffset) {
-		yScroll = self.pageYOffset;
-	} else if (document.documentElement &&
document.documentElement.scrollTop){	 // Explorer 6 Strict
-		yScroll = document.documentElement.scrollTop;
-	} else if (document.body) {// all other Explorers
-		yScroll = document.body.scrollTop;
-	}
-
-	arrayPageScroll = new Array('',yScroll) 
-	return arrayPageScroll;
-}
-
-
-
-//
-// getPageSize()
-// Returns array with page width, height and window width, height
-// Core code from - quirksmode.org
-// Edit for Firefox by pHaez
-//
-function getPageSize(){
-	
-	var xScroll, yScroll;
-	
-	if (window.innerHeight && window.scrollMaxY) {	
-		xScroll = document.body.scrollWidth;
-		yScroll = window.innerHeight + window.scrollMaxY;
-	} else if (document.body.scrollHeight > document.body.offsetHeight){ //
all but Explorer Mac
-		xScroll = document.body.scrollWidth;
-		yScroll = document.body.scrollHeight;
-	} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and
Safari
-		xScroll = document.body.offsetWidth;
-		yScroll = document.body.offsetHeight;
-	}
-	
-	var windowWidth, windowHeight;
-	if (self.innerHeight) {	// all except Explorer
-		windowWidth = self.innerWidth;
-		windowHeight = self.innerHeight;
-	} else if (document.documentElement &&
document.documentElement.clientHeight) { // Explorer 6 Strict Mode
-		windowWidth = document.documentElement.clientWidth;
-		windowHeight = document.documentElement.clientHeight;
-	} else if (document.body) { // other Explorers
-		windowWidth = document.body.clientWidth;
-		windowHeight = document.body.clientHeight;
-	}	
-	
-	// for small pages with total height less then height of the viewport
-	if(yScroll < windowHeight){
-		pageHeight = windowHeight;
-	} else { 
-		pageHeight = yScroll;
-	}
-
-	// for small pages with total width less then width of the viewport
-	if(xScroll < windowWidth){	
-		pageWidth = windowWidth;
-	} else {
-		pageWidth = xScroll;
-	}
-
-
-	arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight) 
-	return arrayPageSize;
-}
-
-
-//
-// pause(numberMillis)
-// Pauses code execution for specified time. Uses busy code, not good.
-// Code from http://www.faqts.com/knowledge_base/view.phtml/aid/1602
-//
-function pause(numberMillis) {
-	var now = new Date();
-	var exitTime = now.getTime() + numberMillis;
-	while (true) {
-		now = new Date();
-		if (now.getTime() > exitTime)
-			return;
-	}
-}
-
-//
-// getKey(key)
-// Gets keycode. If 'x' is pressed then it hides the lightbox.
-//
-
-function getKey(e){
-	if (e == null) { // ie
-		keycode = event.keyCode;
-	} else { // mozilla
-		keycode = e.which;
-	}
-	key = String.fromCharCode(keycode).toLowerCase();
-	
-	if(key == 'x'){ hideLightbox(); }
-}
-
-
-//
-// listenKey()
-//
-function listenKey () {	document.onkeypress = getKey; }
-	
-
-//
-// showLightbox()
-// Preloads images. Pleaces new image in lightbox then centers and displays.
-//
-function showLightbox(objLink)
-{
-	// prep objects
-	var objOverlay = document.getElementById('overlay');
-	var objLightbox = document.getElementById('lightbox');
-	var objCaption = document.getElementById('lightboxCaption');
-	var objImage = document.getElementById('lightboxImage');
-	var objLoadingImage = document.getElementById('loadingImage');
-	var objLightboxDetails = document.getElementById('lightboxDetails');
-
-	
-	var arrayPageSize = getPageSize();
-	var arrayPageScroll = getPageScroll();
-
-	// center loadingImage if it exists
-	if (objLoadingImage) {
-		objLoadingImage.style.top = (arrayPageScroll[1] + ((arrayPageSize[3] - 35 -
objLoadingImage.height) / 2) + 'px');
-		objLoadingImage.style.left = (((arrayPageSize[0] - 20 -
objLoadingImage.width) / 2) + 'px');
-		objLoadingImage.style.display = 'block';
-	}
-
-	// set height of Overlay to take up whole page and show
-	objOverlay.style.height = (arrayPageSize[1] + 'px');
-	objOverlay.style.display = 'block';
-
-	// preload image
-	imgPreload = new Image();
-
-	imgPreload.onload=function(){
-		objImage.src = objLink.href;
-
-		// center lightbox and make sure that the top and left values are not
negative
-		// and the image placed outside the viewport
-		var lightboxTop = arrayPageScroll[1] + ((arrayPageSize[3] - 35 -
imgPreload.height) / 2);
-		var lightboxLeft = ((arrayPageSize[0] - 20 - imgPreload.width) / 2);
-		
-		objLightbox.style.top = (lightboxTop < 0) ? "0px" : lightboxTop + "px";
-		objLightbox.style.left = (lightboxLeft < 0) ? "0px" : lightboxLeft +
"px";
-
-
-		objLightboxDetails.style.width = imgPreload.width + 'px';
-		
-		if(objLink.getAttribute('title')){
-			objCaption.style.display = 'block';
-			//objCaption.style.width = imgPreload.width + 'px';
-			objCaption.innerHTML = objLink.getAttribute('title');
-		} else {
-			objCaption.style.display = 'none';
-		}
-		
-		// A small pause between the image loading and displaying is required with
IE,
-		// this prevents the previous image displaying for a short burst causing
flicker.
-		if (navigator.appVersion.indexOf("MSIE")!=-1){
-			pause(250);
-		} 
-
-		if (objLoadingImage) {	objLoadingImage.style.display = 'none'; }
-
-		// Hide select boxes as they will 'peek' through the image in IE
-		selects = document.getElementsByTagName("select");
-        for (i = 0; i != selects.length; i++) {
-                selects[i].style.visibility = "hidden";
-        }
-
-	
-		objLightbox.style.display = 'block';
-
-		// After image is loaded, update the overlay height as the new image might
have
-		// increased the overall page height.
-		arrayPageSize = getPageSize();
-		objOverlay.style.height = (arrayPageSize[1] + 'px');
-		
-		// Check for 'x' keypress
-		listenKey();
-
-		return false;
-	}
-
-	imgPreload.src = objLink.href;
-	
-}
-
-
-
-
-
-//
-// hideLightbox()
-//
-function hideLightbox()
-{
-	// get objects
-	objOverlay = document.getElementById('overlay');
-	objLightbox = document.getElementById('lightbox');
-
-	// hide lightbox and overlay
-	objOverlay.style.display = 'none';
-	objLightbox.style.display = 'none';
-
-	// make select boxes visible
-	selects = document.getElementsByTagName("select");
-    for (i = 0; i != selects.length; i++) {
-		selects[i].style.visibility = "visible";
-	}
-
-	// disable keypress listener
-	document.onkeypress = '';
-}
-
-
-
-
-//
-// initLightbox()
-// Function runs on window load, going through link tags looking for
rel="lightbox".
-// These links receive onclick events that enable the lightbox display for
their targets.
-// The function also inserts html markup at the top of the page which will be
used as a
-// container for the overlay pattern and the inline image.
-//
-function initLightbox()
-{
-	
-	if (!document.getElementsByTagName){ return; }
-	var anchors = document.getElementsByTagName("a");
-
-	// loop through all anchor tags
-	for (var i=0; i<anchors.length; i++){
-		var anchor = anchors[i];
-
-		if (anchor.getAttribute("href") && (anchor.getAttribute("rel") ==
"lightbox")){
-			anchor.onclick = function () {showLightbox(this); return false;}
-		}
-	}
-
-	// the rest of this code inserts html at the top of the page that looks like
this:
-	//
-	// <div id="overlay">
-	//		<a href="#" onclick="hideLightbox(); return false;"><img
id="loadingImage" /></a>
-	//	</div>
-	// <div id="lightbox">
-	//		<a href="#" onclick="hideLightbox(); return false;" title="Click
anywhere to close image">
-	//			<img id="closeButton" />		
-	//			<img id="lightboxImage" />
-	//		</a>
-	//		<div id="lightboxDetails">
-	//			<div id="lightboxCaption"></div>
-	//			<div id="keyboardMsg"></div>
-	//		</div>
-	// </div>
-	
-	var objBody = document.getElementsByTagName("body").item(0);
-	
-	// create overlay div and hardcode some functional styles (aesthetic styles
are in CSS file)
-	var objOverlay = document.createElement("div");
-	objOverlay.setAttribute('id','overlay');
-	objOverlay.onclick = function () {hideLightbox(); return false;}
-	objOverlay.style.display = 'none';
-	objOverlay.style.position = 'absolute';
-	objOverlay.style.top = '0';
-	objOverlay.style.left = '0';
-	objOverlay.style.zIndex = '90';
- 	objOverlay.style.width = '100%';
-	objBody.insertBefore(objOverlay, objBody.firstChild);
-	
-	var arrayPageSize = getPageSize();
-	var arrayPageScroll = getPageScroll();
-
-	// preload and create loader image
-	var imgPreloader = new Image();
-	
-	// if loader image found, create link to hide lightbox and create
loadingimage
-	imgPreloader.onload=function(){
-
-		var objLoadingImageLink = document.createElement("a");
-		objLoadingImageLink.setAttribute('href','#');
-		objLoadingImageLink.onclick = function () {hideLightbox(); return false;}
-		objOverlay.appendChild(objLoadingImageLink);
-		
-		var objLoadingImage = document.createElement("img");
-		objLoadingImage.src = loadingImage;
-		objLoadingImage.setAttribute('id','loadingImage');
-		objLoadingImage.style.position = 'absolute';
-		objLoadingImage.style.zIndex = '150';
-		objLoadingImageLink.appendChild(objLoadingImage);
-
-		imgPreloader.onload=function(){};	//	clear onLoad, as IE will flip out
w/animated gifs
-
-		return false;
-	}
-
-	imgPreloader.src = loadingImage;
-
-	// create lightbox div, same note about styles as above
-	var objLightbox = document.createElement("div");
-	objLightbox.setAttribute('id','lightbox');
-	objLightbox.style.display = 'none';
-	objLightbox.style.position = 'absolute';
-	objLightbox.style.zIndex = '100';	
-	objBody.insertBefore(objLightbox, objOverlay.nextSibling);
-	
-	// create link
-	var objLink = document.createElement("a");
-	objLink.setAttribute('href','#');
-	objLink.setAttribute('title','Click to close');
-	objLink.onclick = function () {hideLightbox(); return false;}
-	objLightbox.appendChild(objLink);
-
-	// preload and create close button image
-	var imgPreloadCloseButton = new Image();
-
-	// if close button image found, 
-	imgPreloadCloseButton.onload=function(){
-
-		var objCloseButton = document.createElement("img");
-		objCloseButton.src = closeButton;
-		objCloseButton.setAttribute('id','closeButton');
-		objCloseButton.style.position = 'absolute';
-		objCloseButton.style.zIndex = '200';
-		objLink.appendChild(objCloseButton);
-
-		return false;
-	}
-
-	imgPreloadCloseButton.src = closeButton;
-
-	// create image
-	var objImage = document.createElement("img");
-	objImage.setAttribute('id','lightboxImage');
-	objLink.appendChild(objImage);
-	
-	// create details div, a container for the caption and keyboard message
-	var objLightboxDetails = document.createElement("div");
-	objLightboxDetails.setAttribute('id','lightboxDetails');
-	objLightbox.appendChild(objLightboxDetails);
-
-	// create caption
-	var objCaption = document.createElement("div");
-	objCaption.setAttribute('id','lightboxCaption');
-	objCaption.style.display = 'none';
-	objLightboxDetails.appendChild(objCaption);
-
-	// create keyboard message
-	var objKeyboardMsg = document.createElement("div");
-	objKeyboardMsg.setAttribute('id','keyboardMsg');
-	objKeyboardMsg.innerHTML = 'press <a href="#" onclick="hideLightbox();
return false;"><kbd>x</kbd></a> to close';
-	objLightboxDetails.appendChild(objKeyboardMsg);
-
-
-}
-
-
-
-
-//
-// addLoadEvent()
-// Adds event to window.onload without overwriting currently assigned onload
functions.
-// Function found at Simon Willison's weblog - http://simon.incutio.com/
-//
-function addLoadEvent(func)
-{	
-	var oldonload = window.onload;
-	if (typeof window.onload != 'function'){
-    	window.onload = func;
-	} else {
-		window.onload = function(){
-		oldonload();
-		func();
-		}
-	}
-
-}
-
-
-
-addLoadEvent(initLightbox);	// run initLightbox onLoad '''
-
-
-CSS = u'''
-#lightbox{
-	background-color:#eee;
-	padding: 10px;
-	border-bottom: 2px solid #666;
-	border-right: 2px solid #666;
-	}
-#lightboxDetails{
-	font-size: 0.8em;
-	padding-top: 0.4em;
-	}	
-#lightboxCaption{ float: left; }
-#keyboardMsg{ float: right; }
-#closeButton{ top: 5px; right: 5px; }
-
-#lightbox img{ border: none; clear: both;} 
-#overlay img{ border: none; }
-
-#overlay{ background-image: url(overlay.png); }
-
-* html #overlay{
-	background-color: #333;
-	back\ground-color: transparent;
-	background-image: url(blank.gif);
-	filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="overlay.png",
sizingMethod="scale");
-	}
-'''
+from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
 
 class PageWithImageViewlet(ViewletBase):
     """viewlet which displays the image of a page"""
+    template = ViewPageTemplateFile('viewlet.pt')
 
     def render(self):
         if Products.ATContentTypes.interface.document.IATDocument.providedBy(
                 self.context):
             if self.context.getDocimage():
-                return u'<script
type="text/javascript">%s</script><style
type="text/css">%s</style><a href="%s" rel="lightbox" title="%s"
class="docimage_link"><div style="float:right; margin-left:20px;
margin-bottom:20px;"><img src="%s/docimage_mini2" />'\
-                        '<p style="font-weight:
bold;">%s</p></div></a>' % (
-				JS,
-				CSS,
-		            self.context.absolute_url() + '/docimage_large',
-                             self.context.getImageCaption(),
-                            self.context.absolute_url(),
-                            self.context.getImageCaption(),)
+                return self.template()
         return u''

SVN: r6984 - gocept.pagewithimage/trunk/gocept/pagewithimage
Daniel Havlik <dh(at)gocept.com>
2008-11-05 11:25:21 [ FULL ]
Author: nilo
Date: Wed Nov  5 11:25:20 2008
New Revision: 6984

Log:
replacing reference browser widget of the document with alternative, which
allows sorting of references



Modified:
   gocept.pagewithimage/trunk/gocept/pagewithimage/__init__.py

Modified: gocept.pagewithimage/trunk/gocept/pagewithimage/__init__.py
==============================================================================
--- gocept.pagewithimage/trunk/gocept/pagewithimage/__init__.py	(original)
+++ gocept.pagewithimage/trunk/gocept/pagewithimage/__init__.py	Wed Nov  5
11:25:20 2008
(at)(at) -9,6 +9,20 (at)(at)
 def initialize(context):
     """Initializer called when used as a Zope 2 product."""
     from Products.ATContentTypes.content import document
+    import archetypes.referencebrowserwidget
+    document.ATDocument.schema._fields['relatedItems'].widget =\
+         archetypes.referencebrowserwidget.widget.ReferenceBrowserWidget(
+            allow_search = True,
+            allow_browse = True,
+            allow_sorting = True,
+            show_indexes = False,
+            force_close_on_insert = True,
+            label = _(u'label_related_items', default=u'Related Items'),
+            description = '',
+            visible = {'edit' : 'visible', 'view' : 'invisible' }
+            )
+
+
     from Products.ATContentTypes.content import folder
     image_field = ImageField('docimage',
             required = False,

SVN: r6986 - gocept.restmail/trunk
Christian Zagrodnick <cz(at)gocept.com>
2008-11-05 13:02:03 [ FULL ]
Author: zagy
Date: Wed Nov  5 13:02:02 2008
New Revision: 6986

Log:
require setuptools



Modified:
   gocept.restmail/trunk/setup.py

Modified: gocept.restmail/trunk/setup.py
==============================================================================
--- gocept.restmail/trunk/setup.py	(original)
+++ gocept.restmail/trunk/setup.py	Wed Nov  5 13:02:02 2008
(at)(at) -18,5 +18,6 (at)(at)
     install_requires=['lxml',
                       'python-cjson',
                       'uuid',
+                      'setuptools',
                       ],
 )

SVN: r6987 - gocept.restmail/trunk/gocept/restmail
Christian Zagrodnick <cz(at)gocept.com>
2008-11-05 13:02:09 [ FULL ]
Author: zagy
Date: Wed Nov  5 13:02:08 2008
New Revision: 6987

Log:
add mailhost in tests



Modified:
   gocept.restmail/trunk/gocept/restmail/tests.py

Modified: gocept.restmail/trunk/gocept/restmail/tests.py
==============================================================================
--- gocept.restmail/trunk/gocept/restmail/tests.py	(original)
+++ gocept.restmail/trunk/gocept/restmail/tests.py	Wed Nov  5 13:02:08 2008
(at)(at) -32,7 +32,7 (at)(at)
 
     globs = {}
 
-    products = ['SiteAccess', 'BTreeFolder2', 'ZCatalog', 'Five']
+    products = ['SiteAccess', 'BTreeFolder2', 'ZCatalog', 'Five', 'MailHost']
 
     def setUp(self):
         for product in self.products:

SVN: r6988 - CMFWebmail/trunk/gocept/webmail
Thomas Lotze <tl(at)gocept.com>
2008-11-05 13:16:24 [ FULL ]
Author: thomas
Date: Wed Nov  5 13:16:23 2008
New Revision: 6988

Log:
close the composer window when done with it


Modified:
   CMFWebmail/trunk/gocept/webmail/README.txt

Modified: CMFWebmail/trunk/gocept/webmail/README.txt
==============================================================================
--- CMFWebmail/trunk/gocept/webmail/README.txt	(original)
+++ CMFWebmail/trunk/gocept/webmail/README.txt	Wed Nov  5 13:16:23 2008
(at)(at) -263,7 +263,7 (at)(at)
 Subject: The quick brown fox jumped over the lazy dog.
 <BLANKLINE>
 <p><strong>blub</strong></p>
->>> go_home()
+>>> close_window()
 >>> time.sleep(1)

SVN: r6990 - in gocept.imapapi/trunk/gocept/imapapi: . testmessages
Thomas Lotze <tl(at)gocept.com>
2008-11-05 18:00:16 [ FULL ]
Author: thomas
Date: Wed Nov  5 18:00:14 2008
New Revision: 6990

Log:
read test messages from files


Added:
   gocept.imapapi/trunk/gocept/imapapi/testmessages/
   gocept.imapapi/trunk/gocept/imapapi/testmessages/00-header-encoding
   gocept.imapapi/trunk/gocept/imapapi/testmessages/01-everything-is-ok
Modified:
   gocept.imapapi/trunk/gocept/imapapi/tests.py

Added: gocept.imapapi/trunk/gocept/imapapi/testmessages/00-header-encoding
==============================================================================
--- (empty file)
+++ gocept.imapapi/trunk/gocept/imapapi/testmessages/00-header-encoding	Wed Nov
 5 18:00:14 2008
(at)(at) -0,0 +1,10 (at)(at)
+From: test(at)localhost
+X-IMAPAPI-Test: 1
+X-No-Encoding-Header: Text ü or not
+X-Wrong-Encoding-Header: =?ascii?q?Text_=C3=BC?=
+X-Unknown-Encoding-Header: =?foobarschnappeldiwutz?q?Text_=C3=BC?=
+X-Correct-Encoding-Header: =?utf-8?q?Text_=C3=BC?=
+Date: 02-Jul-2008 03:05:00 +0200
+Subject: Mail 1
+
+Everything is ok!
\ No newline at end of file

Added: gocept.imapapi/trunk/gocept/imapapi/testmessages/01-everything-is-ok
==============================================================================
--- (empty file)
+++ gocept.imapapi/trunk/gocept/imapapi/testmessages/01-everything-is-ok	Wed
Nov  5 18:00:14 2008
(at)(at) -0,0 +1,6 (at)(at)
+From: test(at)localhost
+X-IMAPAPI-Test: 2
+Date: 02-Jul-2008 03:06:00 +0200
+Subject: Mail 2
+
+Everything is ok!

Modified: gocept.imapapi/trunk/gocept/imapapi/tests.py
==============================================================================
--- gocept.imapapi/trunk/gocept/imapapi/tests.py	(original)
+++ gocept.imapapi/trunk/gocept/imapapi/tests.py	Wed Nov  5 18:00:14 2008
(at)(at) -4,6 +4,10 (at)(at)
 # $Id$
 """Test harness for gocept.imapapi."""
 
+import imaplib
+import os
+import os.path
+import time
 import unittest
 from zope.testing import doctest
 
(at)(at) -17,6 +21,41 (at)(at)
     return data
 
 
+def clear_inbox(server):
+    data = callIMAP(server, 'select', 'INBOX')
+    if int(data[0]) >= 1:
+        data = callIMAP(server, 'store', '1:*', '+FLAGS', '\\Deleted')
+    callIMAP(server, 'expunge')
+
+
+def load_messages(path, folder_name):
+    server = imaplib.IMAP4('localhost', 10143)
+    server.login('test', 'bsdf')
+    # Clean up the test folder from previous runs. We do not delete at the
+    # end of a run to preserve data for debugging purposes.
+    if folder_name == 'INBOX':
+        clear_inbox(server)
+    else:
+        callIMAP(server, 'delete', folder_name)
+        callIMAP(server, 'create', folder_name)
+
+    # Create messages in the test folder.
+    path = os.path.join(os.path.dirname(__file__), path)
+    for filename in sorted(os.listdir(path)):
+        if filename.startswith('.') or filename.endswith('~'):
+            continue
+        filepath = os.path.join(path, filename)
+        timestamp = os.path.getmtime(filepath)
+        localtime = time.localtime(timestamp)
+        date = time.strftime('"%d-%b-%Y %H:%M:%S +0200"', localtime)
+        message = open(filepath).read()
+        callIMAP(server, 'append', folder_name, '', date, message)
+
+    # Done.
+    status, data = server.logout()
+    assert status == 'BYE'
+
+
 def setUp(self):
     server = imaplib.IMAP4('localhost', 10143)
     server.login('test', 'bsdf')
(at)(at) -34,26 +73,10 (at)(at)
         callIMAP(server, 'delete', name)
 
     # Clear the INBOX from messages as we couldn't delete it earlier.
-    data = callIMAP(server, 'select', 'INBOX')
-    if int(data[0]) >= 1:
-        data = callIMAP(server, 'store', '1:*', '+FLAGS', '\\Deleted')
-    callIMAP(server, 'expunge')
+    clear_inbox(server)
 
     # Create a message in the INBOX
-    message = ('From: test(at)localhost\nX-IMAPAPI-Test: 1\n'
-               'X-No-Encoding-Header: Text \xFC or not\n'
-               'X-Wrong-Encoding-Header: =?ascii?q?Text_=C3=BC?=\n'
-               'X-Unknown-Encoding-Header:
=?foobarschnappeldiwutz?q?Text_=C3=BC?=\n'
-               'X-Correct-Encoding-Header: =?utf-8?q?Text_=C3=BC?=\n'
-               'Date: 02-Jul-2008 03:05:00 +0200\n'
-               'Subject: Mail 1\n\nEverything is ok!')
-    callIMAP(server, 'append', 'INBOX', '', '"02-Jul-2008 03:05:00 +0200"',
-             message)
-    message = ('From: test(at)localhost\nX-IMAPAPI-Test: 2\n'
-               'Date: 02-Jul-2008 03:06:00 +0200\n'
-               'Subject: Mail 2\n\nEverything is ok!')
-    callIMAP(server, 'append', 'INBOX', '', '"02-Jul-2008 03:06:00 +0200"',
-             message)
+    load_messages('testmessages', 'INBOX')
 
     # Create the standard hierarchy for tests
     callIMAP(server, 'create', 'INBOX/Baz')

SVN: r6995 - gocept.restmail/trunk/gocept/restmail
Sebastian Wehrmann <sw(at)gocept.com>
2008-11-06 09:30:47 [ FULL ]
Author: sweh
Date: Thu Nov  6 09:30:45 2008
New Revision: 6995

Log:
refactor quoting of message parts to user adapter lookups instead of eval()



Modified:
   gocept.restmail/trunk/gocept/restmail/configure.zcml
   gocept.restmail/trunk/gocept/restmail/draft.py
   gocept.restmail/trunk/gocept/restmail/draft.txt

Modified: gocept.restmail/trunk/gocept/restmail/configure.zcml
==============================================================================
--- gocept.restmail/trunk/gocept/restmail/configure.zcml	(original)
+++ gocept.restmail/trunk/gocept/restmail/configure.zcml	Thu Nov  6 09:30:45
2008
(at)(at) -46,4 +46,17 (at)(at)
 
   <adapter factory=".profile.lookup_profile" />
 
+  <adapter
+    for="gocept.imapapi.interfaces.IBodyPart"
+    provides="gocept.imapapi.interfaces.IBodyPart"
+    name="convert_text"
+    factory=".draft.convert_text"
+    />
+
+  <adapter
+    for="gocept.imapapi.interfaces.IBodyPart"
+    provides="gocept.imapapi.interfaces.IBodyPart"
+    name="convert_default"
+    factory=".draft.convert_default"
+    />
 </configure>

Modified: gocept.restmail/trunk/gocept/restmail/draft.py
==============================================================================
--- gocept.restmail/trunk/gocept/restmail/draft.py	(original)
+++ gocept.restmail/trunk/gocept/restmail/draft.py	Thu Nov  6 09:30:45 2008
(at)(at) -151,26 +151,25 (at)(at)
 
 
 def get_body_text(part):
-    """ XXX use adapters."""
     content_type = part['content_type']
     if content_type:
         major, minor = content_type.split('/')
-        try:
-            html_factory = eval('convert_%s_%s' % (major, minor))
-        except NameError:
-            html_factory = eval('convert_default')
-    return html_factory(part)
+        output = zope.component.queryAdapter(
+            part, name='convert_%s_%s' % (major, minor))
+        if not output:
+            output = zope.component.queryAdapter(
+                part, name='convert_%s' % major)
+    if not output:
+        output = zope.component.getAdapter(
+            part, name='convert_default')
+    return output 
 
 
 def convert_default(part):
     return "Message body part with content type %s." % part['content_type']
 
 
-def convert_text_plain(part):
-    return part.fetch()
-
-
-def convert_text_html(part):
+def convert_text(part):
     return part.fetch()
 
 

Modified: gocept.restmail/trunk/gocept/restmail/draft.txt
==============================================================================
--- gocept.restmail/trunk/gocept/restmail/draft.txt	(original)
+++ gocept.restmail/trunk/gocept/restmail/draft.txt	Thu Nov  6 09:30:45 2008
(at)(at) -329,10 +329,9 (at)(at)
 
 .. [#gettextpart]
     >>> def get_text_part(message):
-    ...    for content_type in ('text/html', 'text/plain'):
-    ...        part = message.body().find_one(content_type)
-    ...        if part:
-    ...            return part
+    ...     part = message.body()
+    ...     if part['content_type'] in ('text/html', 'text/plain'):
+    ...         return part
     
 
 .. [#createprofile]

SVN: r6997 - in gocept.imapapi/trunk/gocept/imapapi: . testmessages
Sebastian Wehrmann <sw(at)gocept.com>
2008-11-06 11:03:57 [ FULL ]
Author: sweh
Date: Thu Nov  6 11:03:56 2008
New Revision: 6997

Log:
add a checker for memory-adresses
add tests for messages of type multipart-alternative



Added:
   gocept.imapapi/trunk/gocept/imapapi/testmessages/01-multipart-alternative
Removed:
   gocept.imapapi/trunk/gocept/imapapi/testmessages/01-everything-is-ok
Modified:
   gocept.imapapi/trunk/gocept/imapapi/account.txt
   gocept.imapapi/trunk/gocept/imapapi/folder.txt
   gocept.imapapi/trunk/gocept/imapapi/message.txt
   gocept.imapapi/trunk/gocept/imapapi/tests.py

Modified: gocept.imapapi/trunk/gocept/imapapi/account.txt
==============================================================================
--- gocept.imapapi/trunk/gocept/imapapi/account.txt	(original)
+++ gocept.imapapi/trunk/gocept/imapapi/account.txt	Thu Nov  6 11:03:56 2008
(at)(at) -33,8 +33,8 (at)(at)
 
 >>> from pprint import pprint
 >>> pprint(dict(account.folders))
-{'Bar': <gocept.imapapi.folder.Folder object 'Bar' at 0x...>,
- 'INBOX': <gocept.imapapi.folder.Folder object 'INBOX' at 0x...>}
+{'Bar': <gocept.imapapi.folder.Folder object 'Bar' at 0x2615309>,
+ 'INBOX': <gocept.imapapi.folder.Folder object 'INBOX' at 0x2631278>}
 
 >>> account.messages
 Traceback (most recent call last):
(at)(at) -43,7 +43,7 (at)(at)
 Individual folders can be retrieved by specifying their name:
 
 >>> account.folders['INBOX']
-<gocept.imapapi.folder.Folder object 'INBOX' at 0x...>
+<gocept.imapapi.folder.Folder object 'INBOX' at 0x2176287>
 
 >>> account.folders['nofolder']
 Traceback (most recent call last):

Modified: gocept.imapapi/trunk/gocept/imapapi/folder.txt
==============================================================================
--- gocept.imapapi/trunk/gocept/imapapi/folder.txt	(original)
+++ gocept.imapapi/trunk/gocept/imapapi/folder.txt	Thu Nov  6 11:03:56 2008
(at)(at) -12,8 +12,8 (at)(at)
 >>> from pprint import pprint
 >>> account = Account('localhost', 10143, 'test', 'bsdf')
 >>> pprint(account.folders)
-{'Bar': <gocept.imapapi.folder.Folder object 'Bar' at 0x...>,
- 'INBOX': <gocept.imapapi.folder.Folder object 'INBOX' at 0x...>}
+{'Bar': <gocept.imapapi.folder.Folder object 'Bar' at 0x2612732>,
+ 'INBOX': <gocept.imapapi.folder.Folder object 'INBOX' at 0x2134232>}
 
 A folder knows about its name, path, separator, and depth in the hierarchy:
 
(at)(at) -30,7 +30,7 (at)(at)
 Folders also provide access to their direct sub-folders:
 
 >>> INBOX.folders
-{'Baz': <gocept.imapapi.folder.Folder object 'INBOX/Baz' at 0x...>}
+{'Baz': <gocept.imapapi.folder.Folder object 'INBOX/Baz' at 0x2318293>}
 
 Here's a folder that's a bit deeper in the hierarchy:
 
(at)(at) -53,9 +53,9 (at)(at)
 >>> from gocept.imapapi.folder import Folder
 >>> account.folders['Top level'] = Folder()
 >>> pprint(dict(account.folders))
-{'Bar': <gocept.imapapi.folder.Folder object 'Bar' at 0x...>,
- 'INBOX': <gocept.imapapi.folder.Folder object 'INBOX' at 0x...>,
- 'Top level': <gocept.imapapi.folder.Folder object 'Top level' at
0x...>}
+{'Bar': <gocept.imapapi.folder.Folder object 'Bar' at 0x2142346>,
+ 'INBOX': <gocept.imapapi.folder.Folder object 'INBOX' at 0x2762352>,
+ 'Top level': <gocept.imapapi.folder.Folder object 'Top level' at
0x2123423>}
 >>> top_level = account.folders['Top level']
 >>> top_level.name
 'Top level'
(at)(at) -64,7 +64,7 (at)(at)
 
 >>> top_level.folders['Subfolder'] = subfolder = Folder()
 >>> top_level.folders
-{'Subfolder': <gocept.imapapi.folder.Folder object 'Top level/Subfolder' at
0x...>}
+{'Subfolder': <gocept.imapapi.folder.Folder object 'Top level/Subfolder' at
0x2132423>}
 >>> subfolder.name
 'Subfolder'
 >>> subfolder.path
(at)(at) -77,8 +77,8 (at)(at)
 Messages can be retrieved from a folder using its `messages` attribute:
 
 >>> pprint(dict(INBOX.messages))
-{'...-...': <gocept.imapapi.message.Message object 'INBOX/...-...' at
0x...>,
- '...-...': <gocept.imapapi.message.Message object 'INBOX/...-...' at
0x...>}
+{'...-...': <gocept.imapapi.message.Message object 'INBOX/...-...' at
0x2162537>,
+ '...-...': <gocept.imapapi.message.Message object 'INBOX/...-...' at
0x2138986>}
 
 Individual messages have a name attribute which can be used to uniquely
 identify a message within a folder. We can use it to retrieve an individual

Modified: gocept.imapapi/trunk/gocept/imapapi/message.txt
==============================================================================
--- gocept.imapapi/trunk/gocept/imapapi/message.txt	(original)
+++ gocept.imapapi/trunk/gocept/imapapi/message.txt	Thu Nov  6 11:03:56 2008
(at)(at) -28,7 +28,8 (at)(at)
 
 >>> message = INBOX.messages.values()[0]
 >>> message
-<gocept.imapapi.message.Message object 'INBOX/...' at 0x...>
+<gocept.imapapi.message.Message object 'INBOX/...' at 0x2312872>
+
 
 Headers
 =======
(at)(at) -51,6 +52,17 (at)(at)
 >>> message.headers['X-No-Encoding-Header']
 u'Text \ufffd or not'
 
+Other popular headers like From, Date and Subject can be accessed by using the
+dictionary API, too. They are also decoded into unicode automatically:
+
+>>> message.headers['From']
+u'test(at)localhost'
+>>> message.headers['Date']
+u'02-Jul-2008 03:05:00 +0200'
+>>> message.headers['Subject']
+u'Mail 1'
+
+
 Raw
 ===
 
(at)(at) -61,11 +73,54 (at)(at)
 >>> message.raw
 'From: test(at)localhost\r\nX-IMAPAPI-Test: 1\r\nX-No-Encoding-Header: Text
\xfc or not\r\nX-Wrong-Encoding-Header:
=?ascii?q?Text_=C3=BC?=\r\nX-Unknown-Encoding-Header:
=?foobarschnappeldiwutz?q?Text_=C3=BC?=\r\nX-Correct-Encoding-Header:
=?utf-8?q?Text_=C3=BC?=\r\nDate: 02-Jul-2008 03:05:00 +0200\r\nSubject: Mail
1\r\n\r\nEverything is ok!'
 
+
+Message body
+============
+
 Plain text
-===========
+----------
 
 The message body itself can also be retrieved in a plain text version (which
 might include encoded mime parts):
 
 >>> print message.text
 Everything is ok!
+
+Multipart/alternative
+---------------------
+
+Messages with the type ``multipart-alternative`` contain of several parts,
+which contain the same content in differnt types (e.g. plain text and html).
+The mailer has to choose the proper part and display it.
+
+>>> message = INBOX.messages.values()[1]
+>>> message.body
+<gocept.imapapi.message.BodyPart object at 0x2428811>
+
+This testmessage contains of two parts. One contains plain text, the other one
+html quoted text:
+
+>>> from pprint import pprint
+>>> parts = message.body.parts
+>>> pprint(parts)
+[<gocept.imapapi.message.BodyPart object at 0x2428819>,
+ <gocept.imapapi.message.BodyPart object at 0x2428212>]
+>>> print parts[0]['content_type']
+text/plain
+>>> print parts[1]['content_type']
+text/html
+
+The parts content can be retrieved with the ``fetch`` method:
+
+>>> print parts[0].fetch()
+Everything is ok!
+
+>>> print parts[1].fetch()
+<html>
+  <head>
+    <title>Mail 2</title>
+  </head>
+  <body>
+    Everything is ok!
+  </body>
+</html>

Added:
gocept.imapapi/trunk/gocept/imapapi/testmessages/01-multipart-alternative
==============================================================================
--- (empty file)
+++
gocept.imapapi/trunk/gocept/imapapi/testmessages/01-multipart-alternative	Thu
Nov  6 11:03:56 2008
(at)(at) -0,0 +1,24 (at)(at)
+To: Thomas Lotze <tl(at)gocept.com>
+Message-Id: <18EE7CC0-44FB-4802-BA8E-58F7BE1F7274(at)gocept.com>
+Content-Type: multipart/alternative; boundary=gocept-0123456789
+Subject: Mail 2 
+Date: Thu, 25 Sep 2008 13:50:58 +0200
+
+--gocept-0123456789
+Content-Type: text/plain; charset=US-ASCII;
+Content-Transfer-Encoding: 7bit
+
+Everything is ok!
+
+--gocept-0123456789
+Content-Type: text/html; charset=US-ASCII
+Content-Transfer-Encoding: 7bit
+
+<html>
+  <head>
+    <title>Mail 2</title>
+  </head>
+  <body>
+    Everything is ok!
+  </body>
+</html>

Modified: gocept.imapapi/trunk/gocept/imapapi/tests.py
==============================================================================
--- gocept.imapapi/trunk/gocept/imapapi/tests.py	(original)
+++ gocept.imapapi/trunk/gocept/imapapi/tests.py	Thu Nov  6 11:03:56 2008
(at)(at) -7,13 +7,17 (at)(at)
 import imaplib
 import os
 import os.path
+import re
 import time
 import unittest
 from zope.testing import doctest
+import zope.testing.renormalizing
 
 import imaplib
 import gocept.imapapi.parser
 
+checker = zope.testing.renormalizing.RENormalizing([
+    (re.compile('0x[0-9a-f]{7}'), "<MEM ADDRESS>")])
 
 def callIMAP(server, function, *args, **kw):
     status, data = getattr(server, function)(*args, **kw)
(at)(at) -95,7 +99,8 (at)(at)
         'folder.txt',
         'message.txt',
         setUp=setUp,
-        optionflags=optionflags))
+        optionflags=optionflags,
+        checker=checker))
     suite.addTest(doctest.DocTestSuite(
         'gocept.imapapi.parser',
         optionflags=optionflags))

SVN: r7000 - gocept.webmail/trunk/gocept/webmail
Christian Zagrodnick <cz(at)gocept.com>
2008-11-06 11:06:24 [ FULL ]
Author: zagy
Date: Thu Nov  6 11:06:22 2008
New Revision: 7000

Log:
tests beschleunigt durch wait_for_text



Modified:
   gocept.webmail/trunk/gocept/webmail/README.txt

Modified: gocept.webmail/trunk/gocept/webmail/README.txt
==============================================================================
--- gocept.webmail/trunk/gocept/webmail/README.txt	(original)
+++ gocept.webmail/trunk/gocept/webmail/README.txt	Thu Nov  6 11:06:22 2008
(at)(at) -23,7 +23,6 (at)(at)
 short-cut for logging into that account:
 
 >>> wait_for_text('Return to homepage')
->>> time.sleep(0.5)
 >>> browser.getControl(name='login').click()
 
 Accessing the profile
(at)(at) -43,7 +42,7 (at)(at)
 The account management UI allows us to create, delete and edit accounts
 with identities. The UI can be accessed in a popup window:
 
->>> time.sleep(2)
+>>> time.sleep(1)
 >>> browser.getLink('Manage accounts').click()
 >>> time.sleep(3)
 >>> follow_window()
(at)(at) -51,7 +50,7 (at)(at)
 To add an account, you have to click on the `Add account` button:
 
 >>> browser.getLink('New').click()
->>> time.sleep(2)
+>>> wait_for_text('identitySave')
 
 A form appears, where you can fill in your account details. The port is
 predefined with the default imap port:
(at)(at) -70,7 +69,7 (at)(at)
 account list:
 
 >>> browser.getControl(name='identitySave').click()
->>> time.sleep(2)
+>>> wait_for_text('>ok</div>')
 >>> print_html(browser.contents)
 <html>...
 <title>Manage accounts</title>...
(at)(at) -134,7 +133,7 (at)(at)
 If you click on a folder the list of messages from that folder is displayed:
 
 >>> browser.getLink('INBOX').click()
->>> time.sleep(1)
+>>> wait_for_text('test(at)localhost')
 >>> tree = lxml.html.fromstring(browser.contents)
 
 The table has a `From` column and the INBOX contains two messages from
(at)(at) -164,7 +163,7 (at)(at)
 ====================
 
 >>> click_listing('Mail 1')
->>> time.sleep(1)
+>>> wait_for_text('Everything is ok!')
 >>> print_html(browser.contents)
 <html>
   ...
(at)(at) -207,7 +206,7 (at)(at)
 The draft is accessible through the draft folder:
 
 >>> browser.getLink('Drafts').click()
->>> time.sleep(2)
+>>> wait_for_text('lazy dog')
 >>> print_html(browser.contents)
 <html>...
 <thead><tr ...>
(at)(at) -224,7 +223,7 (at)(at)
 Drafts can be re-edited:
 
 >>> click_listing('The quick brown fox jumped over the lazy dog')
->>> time.sleep(1)
+>>> wait_for_text('blub')
 >>> browser.getLink('Edit').click()
 >>> time.sleep(2)
 >>> follow_window()
(at)(at) -238,7 +237,7 (at)(at)
 >>> go_home()
 
 >>> browser.getLink('Drafts').click()
->>> time.sleep(2)
+>>> wait_for_text('mailothertest')
 >>> print_html(browser.contents)
 <html>...
 <tbody ...><tr ...>
(at)(at) -274,7 +273,7 (at)(at)
 
 >>> click_tree('ben(at)example.com')
 >>> browser.getLink('INBOX').click()
->>> time.sleep(1)
+>>> wait_for_text('Mail 1')
 >>> click_listing('Mail 1')
 >>> browser.getLink('Reply').click()
 >>> time.sleep(4)
(at)(at) -295,7 +294,7 (at)(at)
 ... </blockquote>
 ... """
 >>> browser.js.set_editor_value('composeBody', example_text)
->>> time.sleep(4)
+>>> time.sleep(1)
 
 When the cursor is positioned in a quoted area, pressing enter results in the
 quoting being split. All HTML elements inside the body which contain the text
(at)(at) -371,6 +370,7 (at)(at)
 >>> follow_window()
 
 >>> click_listing('ben(at)example.com')
+>>> wait_for_text('identitySave')
 >>> browser.js.get_control_value('accountHost')
 'localhost'
 >>> browser.js.get_control_value('accountPort')
(at)(at) -390,7 +390,7 (at)(at)
 
 >>> browser.js.set_control_value('identityName', 'Ben Atzer')
 >>> browser.getControl(name='identitySave').click()
->>> time.sleep(2)
+>>> wait_for_text('>Ben Atzer')
 >>> print_html(browser.contents)
 <html>...
 <title>Manage accounts</title>...
(at)(at) -407,7 +407,7 (at)(at)
 --------------------------------
 
 >>> click_listing('ben(at)example.com')
->>> time.sleep(1)
+>>> wait_for_text('identitySave')
 >>> browser.getLink('Delete').click()
 
 This code is b0rked:
(at)(at) -456,7 +456,7 (at)(at)
 
     >>> import sys
     >>> def wait_for_text(string):
-    ...     print >> sys.stderr, string
+    ...     print >> sys.stderr, string,
     ...     for i in xrange(100):
     ...         if string in browser.contents:
     ...             break
(at)(at) -465,6 +465,7 (at)(at)
     ...         time.sleep(0.1)
     ...     else:
     ...         print >> sys.stderr, 'Did not find', string
+    ...     print >>sys.stderr, ""
  
     Define a helper function to expand/collapse entries in the mail folder
     tree:

SVN: r7047 - gocept.restmail/trunk/gocept/restmail/browser
Thomas Lotze <tl(at)gocept.com>
2008-11-18 14:23:02 [ FULL ]
Author: thomas
Date: Tue Nov 18 14:23:01 2008
New Revision: 7047

Log:
cannot reproduce the decoding error anymore that caused us to mangle HTML
bodies


Modified:
   gocept.restmail/trunk/gocept/restmail/browser/draft.py

Modified: gocept.restmail/trunk/gocept/restmail/browser/draft.py
==============================================================================
--- gocept.restmail/trunk/gocept/restmail/browser/draft.py	(original)
+++ gocept.restmail/trunk/gocept/restmail/browser/draft.py	Tue Nov 18 14:23:01
2008
(at)(at) -24,7 +24,7 (at)(at)
                 'date': self.context.date.isoformat(),
                 'to': self.context.to,
                 'subject': self.context.subject,
-                'body': self.context.body.replace('%', '%p').replace('<',
'%l')}
+                'body': self.context.body}
 
     (at)gocept.restmail.browser.json.view
     def save(self, account, to, subject, body):

SVN: r7048 - gocept.webmail/trunk/gocept/webmail/browser/resources
Thomas Lotze <tl(at)gocept.com>
2008-11-18 14:23:26 [ FULL ]
Author: thomas
Date: Tue Nov 18 14:23:25 2008
New Revision: 7048

Log:
cannot reproduce the decoding error anymore that caused us to mangle HTML
bodies


Modified:
   gocept.webmail/trunk/gocept/webmail/browser/resources/composer.js

Modified: gocept.webmail/trunk/gocept/webmail/browser/resources/composer.js
==============================================================================
---
gocept.webmail/trunk/gocept/webmail/browser/resources/composer.js	(original)
+++ gocept.webmail/trunk/gocept/webmail/browser/resources/composer.js	Tue Nov
18 14:23:25 2008
(at)(at) -203,8 +203,6 (at)(at)
                         };
                     };
                     var body = draft['body'];
-                    body = body.replace('%l', '<', 'g');
-                    body = body.replace('%p', '%', 'g');
                     composer.getControl('Body').value = body;
                     composer.editor.setEditorHTML(body);
             });

SVN: r7059 - gocept.restmail/trunk/gocept/restmail/browser
Sebastian Wehrmann <sw(at)gocept.com>
2008-11-22 05:42:24 [ FULL ]
Author: sweh
Date: Fri Nov 21 15:36:43 2008
New Revision: 7059

Log:
fix tests cause of changes in the json API for use with YUI 2.6.0
add a text/json header to json responses to prevent zope from adding stupid
html tags to html mails




Modified:
   gocept.restmail/trunk/gocept/restmail/browser/README.txt
   gocept.restmail/trunk/gocept/restmail/browser/draft.py
   gocept.restmail/trunk/gocept/restmail/browser/json.py
   gocept.restmail/trunk/gocept/restmail/browser/message.py

Modified: gocept.restmail/trunk/gocept/restmail/browser/README.txt
==============================================================================
--- gocept.restmail/trunk/gocept/restmail/browser/README.txt	(original)
+++ gocept.restmail/trunk/gocept/restmail/browser/README.txt	Fri Nov 21
15:36:43 2008
(at)(at) -98,7 +98,7 (at)(at)
 ...                               port=10143,
 ...                               user='test',
 ...                               password='bsdf'))
-{'url': 'http://localhost/profile/ben-example.com'}
+{'Response': {'url': 'http://localhost/profile/ben-example.com'}}
 
 XXX This needs to go to ``add-account``:
 
(at)(at) -113,17 +113,17 (at)(at)
 
 >>> import pprint
 >>> pprint.pprint(json_request('http://localhost/profile/(at)(at)accounts'))
-[{'address': 'ben(at)example.com',
-  'from': 'Ben Utzer &lt;ben(at)example.com&gt;',
-  'host': 'localhost',
-  'name': 'Ben Utzer',
-  'password': 'bsdf',
-  'port': 10143,
-  'sent_folder': '',
-  'smtp_server': 'default-smtp',
-  'status': 'ok',
-  'url': 'http://localhost/profile/ben-example.com',
-  'user': 'test'}]
+{'Response': [{'address': 'ben(at)example.com',
+               'from': 'Ben Utzer &lt;ben(at)example.com&gt;',
+               'host': 'localhost',
+               'name': 'Ben Utzer',
+               'password': 'bsdf',
+               'port': 10143,
+               'sent_folder': '',
+               'smtp_server': 'default-smtp',
+               'status': 'ok',
+               'url': 'http://localhost/profile/ben-example.com',
+               'user': 'test'}]}
 
 
 Account API
(at)(at) -135,19 +135,19 (at)(at)
 Accounts contain folders which can be browsed using the ``(at)(at)folders``
view:
 
 >>> pprint.pprint(json_request('http://localhost/profile/ben-example.com/(at)(at)folders'))
-[{'children': 0,
-  'name': 'Bar',
-  'url': 'http://localhost/profile/ben-example.com/+Bar'},
- {'children': 1,
-  'name': 'INBOX',
-  'url': 'http://localhost/profile/ben-example.com/+INBOX'}]
+{'Response': [{'children': 0,
+               'name': 'Bar',
+               'url': 'http://localhost/profile/ben-example.com/+Bar'},
+              {'children': 1,
+               'name': 'INBOX',
+               'url': 'http://localhost/profile/ben-example.com/+INBOX'}]}
 
 The ``(at)(at)folders`` view can be applied on folders as well:
 
 >>> pprint.pprint(json_request('http://localhost/profile/ben-example.com/+INBOX/(at)(at)folders'))
-[{'children': 0,
-  'name': 'Baz',
-  'url': 'http://localhost/profile/ben-example.com/+INBOX/+Baz'}]
+{'Response': [{'children': 0,
+               'name': 'Baz',
+               'url': 'http://localhost/profile/ben-example.com/+INBOX/+Baz'}]}
 
 
 Create new folders
(at)(at) -157,11 +157,11 (at)(at)
 
 >>> json_request('http://localhost/profile/ben-example.com/(at)(at)create_folder',
 ...              name="Sent")
-{'url': 'http://localhost/profile/ben-example.com/+Sent'}
+{'Response': {'url': 'http://localhost/profile/ben-example.com/+Sent'}}
 
 >>> json_request('http://localhost/profile/ben-example.com/+Sent/(at)(at)create_folder',
 ...              name="Old")
-{'url': 'http://localhost/profile/ben-example.com/+Sent/+Old'}
+{'Response': {'url': 'http://localhost/profile/ben-example.com/+Sent/+Old'}}
 
 
 
(at)(at) -173,14 +173,14 (at)(at)
 
 >>> messages = json_request('http://localhost/profile/ben-example.com/+INBOX/(at)(at)messages')
 >>> pprint.pprint(messages)
-[{'date': '02-Jul-2008 03:05:00 +0200',
-  'from': 'test(at)localhost',
-  'subject': 'Mail 1',
-  'url': 'http://localhost/profile/ben-example.com/+INBOX/*...'},
- {'date': '02-Jul-2008 03:06:00 +0200',
-  'from': 'test(at)localhost',
-  'subject': 'Mail 2',
-  'url': 'http://localhost/profile/ben-example.com/+INBOX/*...'}]
+{'Response': [{'date': '02-Jul-2008 03:05:00 +0200',
+               'from': 'test(at)localhost',
+               'subject': 'Mail 1',
+               'url': 'http://localhost/profile/ben-example.com/+INBOX/*...'},
+              {'date': '02-Jul-2008 03:06:00 +0200',
+               'from': 'test(at)localhost',
+               'subject': 'Mail 2',
+               'url': 'http://localhost/profile/ben-example.com/+INBOX/*...'}]}
 
 Please note that only `url` is promised to have a reasonable value.
 Messages might not provide the date, subject or from header in which
(at)(at) -204,17 +204,17 (at)(at)
 ...                               user='test',
 ...                               password='bsdf'))
 >>> pprint.pprint(json_request('http://localhost/profile/(at)(at)accounts'))
-[{'address': 'ben(at)example.com',
-  'from': 'Ben Utzer &lt;ben(at)example.com&gt;',
-  'host': 'localhost',
-  'name': 'Ben Utzer',
-  'password': 'bsdf',
-  'port': 10143,
-  'sent_folder': '+Sent',
-  'smtp_server': 'default-smtp',
-  'status': 'ok',
-  'url': 'http://localhost/profile/ben-example.com',
-  'user': 'test'}]
+{'Response': [{'address': 'ben(at)example.com',
+               'from': 'Ben Utzer &lt;ben(at)example.com&gt;',
+               'host': 'localhost',
+               'name': 'Ben Utzer',
+               'password': 'bsdf',
+               'port': 10143,
+               'sent_folder': '+Sent',
+               'smtp_server': 'default-smtp',
+               'status': 'ok',
+               'url': 'http://localhost/profile/ben-example.com',
+               'user': 'test'}]}
 
 Delete an account: (at)(at)delete
 ---------------------------
(at)(at) -232,8 +232,9 (at)(at)
 ...                               port=10143,
 ...                               user='test',
 ...                               password='bsdf'))
-{'url': 'http://localhost/profile/john.doe-example.com'}
->>> accounts = json_request('http://localhost/profile/(at)(at)accounts')
+{'Response': {'url': 'http://localhost/profile/john.doe-example.com'}}
+>>> response = json_request('http://localhost/profile/(at)(at)accounts')
+>>> accounts = response['Response']
 >>> [x['url'] for x in accounts]
 ['http://localhost/profile/ben-example.com',
'http://localhost/profile/john.doe-example.com']
 
(at)(at) -241,7 +242,8 (at)(at)
 
 >>> json_request('http://localhost/profile/john.doe-example.com/(at)(at)delete',
post=True)
 
->>> accounts = json_request('http://localhost/profile/(at)(at)accounts')
+>>> response = json_request('http://localhost/profile/(at)(at)accounts')
+>>> accounts = response['Response']
 >>> [x['url'] for x in accounts]
 ['http://localhost/profile/ben-example.com']
 
(at)(at) -256,7 +258,7 (at)(at)
 lists all headers of the message as well as its hierarchical
 structure:
 
->>> message = json_request(messages[0]['url']+'/(at)(at)metadata')
+>>> message =
json_request(messages['Response'][0]['url']+'/(at)(at)metadata')
 >>> pprint.pprint(message)
 {'headers': {'Date': '02-Jul-2008 03:05:00 +0200',
              'From': 'test(at)localhost',
(at)(at) -314,21 +316,21 (at)(at)
 After a message was created, its data is empty:
 
 >>> pprint.pprint(json_request(draft['url']+'/(at)(at)data'))
-{'account': '',
- 'body': '',
- 'date': '<ISO DATE>',
- 'subject': '',
- 'to': ''}
+{'Response': {'account': '',
+              'body': '',
+              'date': '<ISO DATE>',
+              'subject': '',
+              'to': ''}}
 
 
 Getting a list of existing draft messages
 -----------------------------------------
 
 >>> pprint.pprint(json_request('http://localhost/profile/(at)(at)drafts'))
-[{'date': '<ISO DATE>',
-  'subject': '',
-  'to': '',
-  'url': 'http://localhost/profile/drafts/<GUID>'}]
+{'Response': [{'date': '<ISO DATE>',
+               'subject': '',
+               'to': '',
+               'url': 'http://localhost/profile/drafts/<GUID>'}]}
 
 
 Updating draft messages
(at)(at) -342,11 +344,11 (at)(at)
 ...              subject=u'asdf\xe4',
 ...              body='HelloHello')
 >>> pprint.pprint(json_request(draft['url']+'/(at)(at)data'))
-{'account': 'http://localhost/profile/ben-example.com',
- 'body': 'HelloHello',
- 'date': '<ISO DATE>',
- 'subject': u'asdf\xe4',
- 'to': 'ct(at)gocept.com'}
+{'Response': {'account': 'http://localhost/profile/ben-example.com',
+              'body': 'HelloHello',
+              'date': '<ISO DATE>',
+              'subject': u'asdf\xe4',
+              'to': 'ct(at)gocept.com'}}
 
 >>> json_request(draft['url']+'/(at)(at)save',
 ...              to='ct(at)gocept.com',
(at)(at) -354,11 +356,11 (at)(at)
 ...              subject=u'asdf',
 ...              body='HelloHello')
 >>> pprint.pprint(json_request(draft['url']+'/(at)(at)data'))
-{'account': 'http://localhost/profile/ben-example.com',
- 'body': 'HelloHello',
- 'date': '<ISO DATE>',
- 'subject': 'asdf',
- 'to': 'ct(at)gocept.com'}
+{'Response': {'account': 'http://localhost/profile/ben-example.com',
+              'body': 'HelloHello',
+              'date': '<ISO DATE>',
+              'subject': 'asdf',
+              'to': 'ct(at)gocept.com'}}
 
 
 Sending draft messages
(at)(at) -385,10 +387,10 (at)(at)
 HTTPError: HTTP Error 404: Not Found
 
 >>> pprint.pprint(json_request('http://localhost/profile/ben-example.com/+Sent/(at)(at)messages'))
-[{'date': None,
-  'from': 'Ben Utzer <ben(at)example.com>',
-  'subject': 'asdf',
-  'url': 'http://localhost/profile/ben-example.com/+Sent/*...'}]
+{'Response': [{'date': None,
+               'from': 'Ben Utzer <ben(at)example.com>',
+               'subject': 'asdf',
+               'url': 'http://localhost/profile/ben-example.com/+Sent/*...'}]}
 
 
 Replying to messages
(at)(at) -398,17 +400,17 (at)(at)
 data pre-filled such as the quoted message body, the subject line and the
 recipient:
 
->>> reply = json_request(messages[0]['url']+'/(at)(at)reply',
post=True)
+>>> reply =
json_request(messages['Response'][0]['url']+'/(at)(at)reply', post=True)
 >>> pprint.pprint(reply)
-{'url': 'http://localhost/profile/drafts/<GUID>'}
->>> pprint.pprint(json_request(reply['url']+'/(at)(at)data'))
-{'account': 'http://localhost/profile/ben-example.com',
- 'body':
'%lhtml>\n%lhead>%l/head>\n%lbody>\n%lblockquote>%lpre>Everything
is ok!\n%l/pre>%l/blockquote>\n%l/body>\n%l/html>\n',
- 'date': '<ISO DATE>',
- 'subject': 'Re: Mail 1',
- 'to': 'test(at)localhost'}
+{'Response': {'url': 'http://localhost/profile/drafts/<GUID>'}}
+>>>
pprint.pprint(json_request(reply['Response']['url']+'/(at)(at)data'))
+{'Response': {'account': 'http://localhost/profile/ben-example.com',
+              'body':
'<html>\n<head></head>\n<body>\n<blockquote><pre>Everything
is ok!\n</pre></blockquote>\n</body>\n</html>\n',
+              'date': '<ISO DATE>',
+              'subject': 'Re: Mail 1',
+              'to': 'test(at)localhost'}}
 
->>> json_request(reply['url']+'/(at)(at)send', post=True)
+>>> json_request(reply['Response']['url']+'/(at)(at)send', post=True)
 Content-Type: text/html; charset="us-ascii"
 MIME-Version: 1.0
 Content-Transfer-Encoding: 7bit

Modified: gocept.restmail/trunk/gocept/restmail/browser/draft.py
==============================================================================
--- gocept.restmail/trunk/gocept/restmail/browser/draft.py	(original)
+++ gocept.restmail/trunk/gocept/restmail/browser/draft.py	Fri Nov 21 15:36:43
2008
(at)(at) -24,7 +24,7 (at)(at)
                 'date': self.context.date.isoformat(),
                 'to': self.context.to,
                 'subject': self.context.subject,
-                'body': self.context.body.strip()}
+                'body': self.context.body}
 
     (at)gocept.restmail.browser.json.view
     def save(self, account, to, subject, body):

Modified: gocept.restmail/trunk/gocept/restmail/browser/json.py
==============================================================================
--- gocept.restmail/trunk/gocept/restmail/browser/json.py	(original)
+++ gocept.restmail/trunk/gocept/restmail/browser/json.py	Fri Nov 21 15:36:43
2008
(at)(at) -17,9 +17,11 (at)(at)
     """
     def decorated(self):
         body = self.request.get('BODY', NULL_BODY)
+        self.request.RESPONSE.setHeader('Content-Type', 'text/json')
         args = cjson.decode(body.decode('utf-8'))
         result = func(self, **args)
-        result = {'Response': result}
+        if result:
+            result = {'Response': result}
         return cjson.encode(result)
     decorated.__name__ = func.__name__
     return decorated

Modified: gocept.restmail/trunk/gocept/restmail/browser/message.py
==============================================================================
--- gocept.restmail/trunk/gocept/restmail/browser/message.py	(original)
+++ gocept.restmail/trunk/gocept/restmail/browser/message.py	Fri Nov 21
15:36:43 2008
(at)(at) -58,11 +58,12 (at)(at)
 
         return data
 
+    (at)gocept.restmail.browser.json.view
     def reply(self):
         """Create a new draft message."""
         profile = gocept.restmail.interfaces.IProfile(self.context)
         draft = profile.new_draft(self.context)
-        return cjson.encode({'url': zope.app.zapi.absoluteURL(draft,
self.request)})
+        return {'url': zope.app.zapi.absoluteURL(draft, self.request)}
 
 
 class BodyPartWebAPI(object):

MailBoxer