merge of 'bc3bfc52f8785d114d335b22f794ece898dbd164'

and 'de4013a540009902497a1085be5975e4ff6c3a83'

Monotone-Parent: bc3bfc52f8785d114d335b22f794ece898dbd164
Monotone-Parent: de4013a540009902497a1085be5975e4ff6c3a83
Monotone-Revision: cd1bb1e892161fcc358630fd5cf9f3c3511c886f

Monotone-Author: flachapelle@inverse.ca
Monotone-Date: 2009-08-19T07:17:12
Monotone-Branch: ca.inverse.sogo
This commit is contained in:
Francis Lachapelle
2009-08-19 07:17:12 +00:00
7 changed files with 162 additions and 39 deletions

View File

@@ -1,3 +1,14 @@
2009-08-21 Cyril Robert <crobert@inverse.ca>
* UI/Contacts/UIxListView.m: Implementation, allows VLISTs to be displayed.
* UI/Contacts/UIxListView.m (checkListReferences): Added list integrity
verification (make sure all references are valid).
2009-08-21 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Tests/webdavlib.py (WebDAVQuery.xpath_evaluate): new method to
facilitate xpath evaluation from tests.
2009-08-20 Cyril Robert <crobert@inverse.ca>
* UI/Common/UIxParentFolderActions.m (createFolderAction): Mantis 2040:

View File

@@ -5,7 +5,6 @@ from config import hostname, port, username, password, subscriber_username, subs
import sys
import unittest
import webdavlib
import xml.xpath
import time
# TODO:
@@ -27,11 +26,11 @@ def fetchUserEmail(login):
propfind = webdavlib.WebDAVPROPFIND(resource,
["{urn:ietf:params:xml:ns:caldav}calendar-user-address-set"],
0)
propfind.xpath_namespace = { "D": "DAV:",
"C": "urn:ietf:params:xml:ns:caldav" }
client.execute(propfind)
xpath_context = xml.xpath.CreateContext(propfind.response["document"])
xpath_context.setNamespaces({ "D": "DAV:",
"C": "urn:ietf:params:xml:ns:caldav" })
nodes = xml.xpath.Evaluate('/D:multistatus/D:response/D:propstat/D:prop/C:calendar-user-address-set/D:href', None, xpath_context)
nodes = propfind.xpath_evaluate('/D:multistatus/D:response/D:propstat/D:prop/C:calendar-user-address-set/D:href',
None)
return nodes[0].childNodes[0].nodeValue
@@ -59,12 +58,6 @@ class DAVAclTest(unittest.TestCase):
def rightsToSOGoRights(self, rights):
self.fail("subclass must implement this method")
def xpath_query(self, query, top_node):
xpath_context = xml.xpath.CreateContext(top_node)
xpath_context.setNamespaces({ "D": "DAV:",
"C": "urn:ietf:params:xml:ns:caldav" })
return xml.xpath.Evaluate(query, None, xpath_context)
def setupRights(self, rights):
rights_str = "".join(["<%s/>" % x for x in self.rightsToSOGoRights(rights) ])
aclQuery = """<acl-query xmlns="urn:inverse:params:xml:ns:inverse-dav">
@@ -284,23 +277,24 @@ class DAVCalendarAclTest(DAVAclTest):
return event
def _calendarDataInMultistatus(self, top_node, filename,
def _calendarDataInMultistatus(self, query, filename,
response_tag = "D:response"):
event = None
response_nodes = self.xpath_query("/D:multistatus/%s" % response_tag,
top_node)
query.xpath_namespace = { "D": "DAV:",
"C": "urn:ietf:params:xml:ns:caldav" }
response_nodes = query.xpath_evaluate("/D:multistatus/%s" % response_tag)
for response_node in response_nodes:
href_node = self.xpath_query("D:href", response_node)[0]
href_node = query.xpath_evaluate("D:href", response_node)[0]
href = href_node.childNodes[0].nodeValue
if href.endswith(filename):
propstat_nodes = self.xpath_query("D:propstat", response_node)
propstat_nodes = query.xpath_evaluate("D:propstat", response_node)
for propstat_node in propstat_nodes:
status_node = self.xpath_query("D:status",
propstat_node)[0]
status_node = query.xpath_evaluate("D:status",
propstat_node)[0]
status = status_node.childNodes[0].nodeValue
data_nodes = self.xpath_query("D:prop/C:calendar-data",
propstat_node)
data_nodes = query.xpath_evaluate("D:prop/C:calendar-data",
propstat_node)
if status.endswith("200 OK"):
if (len(data_nodes) > 0
and len(data_nodes[0].childNodes) > 0):
@@ -323,8 +317,7 @@ class DAVCalendarAclTest(DAVAclTest):
1)
self.subscriber_client.execute(propfind)
if propfind.response["status"] != 403:
event = self._calendarDataInMultistatus(propfind.response["document"],
filename)
event = self._calendarDataInMultistatus(propfind, filename)
return event
@@ -338,8 +331,7 @@ class DAVCalendarAclTest(DAVAclTest):
[ url ])
self.subscriber_client.execute(multiget)
if multiget.response["status"] != 403:
event = self._calendarDataInMultistatus(multiget.response["document"],
url)
event = self._calendarDataInMultistatus(multiget, url)
return event
@@ -352,8 +344,8 @@ class DAVCalendarAclTest(DAVAclTest):
["{urn:ietf:params:xml:ns:caldav}calendar-data"])
self.subscriber_client.execute(sync_query)
if sync_query.response["status"] != 403:
event = self._calendarDataInMultistatus(sync_query.response["document"],
url, "D:sync-response")
event = self._calendarDataInMultistatus(sync_query, url,
"D:sync-response")
return event

View File

@@ -5,7 +5,6 @@ from config import hostname, port, username, password
import sys
import unittest
import webdavlib
import xml.xpath
import time
resource = '/SOGo/dav/%s/Calendar/test-webdavsync/' % username
@@ -19,11 +18,6 @@ class WebdavSyncTest(unittest.TestCase):
delete = webdavlib.WebDAVDELETE(resource)
self.client.execute(delete)
def _xpath_query(self, query, top_node):
xpath_context = xml.xpath.CreateContext(top_node)
xpath_context.setNamespaces({ "D": "DAV:" })
return xml.xpath.Evaluate(query, None, xpath_context)
def test(self):
# missing tests:
# invalid tokens: negative, non-numeric, > current timestamp
@@ -49,8 +43,7 @@ class WebdavSyncTest(unittest.TestCase):
self.assertEquals(query1.response["status"], 207,
("query1: invalid status code: %d (!= 207)"
% query1.response["status"]))
token_node = self._xpath_query("/D:multistatus/D:sync-token",
query1.response["document"])[0]
token_node = query1.xpath_evaluate("/D:multistatus/D:sync-token")[0]
# Implicit "assertion": we expect SOGo to return a token node, with a
# non-empty numerical value. Anything else will trigger an exception
token = int(token_node.childNodes[0].nodeValue)
@@ -62,8 +55,7 @@ class WebdavSyncTest(unittest.TestCase):
query2 = webdavlib.WebDAVSyncQuery(resource, "1234", [ "getetag" ])
self.client.execute(query2)
self.assertEquals(query2.response["status"], 403)
cond_nodes = self._xpath_query("/D:error/D:valid-sync-token",
query2.response["document"])
cond_nodes = query2.xpath_evaluate("/D:error/D:valid-sync-token")
self.assertTrue(len(cond_nodes) > 0,
"expected 'valid-sync-token' condition error")

View File

@@ -1,11 +1,15 @@
import cStringIO
import httplib
import M2Crypto.httpslib
import time
import xml.sax.saxutils
import xml.dom.ext.reader.Sax2
import xml.xpath
import sys
class WebDAVClient:
user_agent = "Mozilla/5.0"
def __init__(self, hostname, port, username, password, forcessl = False):
if port == "443" or forcessl:
self.conn = M2Crypto.httpslib.HTTPSConnection(hostname, int(port),
@@ -17,7 +21,7 @@ class WebDAVClient:
.encode('base64')[:-1])
def _prepare_headers(self, query, body):
headers = { "User-Agent": "Mozilla/5.0",
headers = { "User-Agent": self.user_agent,
"authorization": "Basic %s" % self.simpleauth_hash }
if body is not None:
headers["content-length"] = len(body)
@@ -89,6 +93,7 @@ class WebDAVQuery(HTTPQuery):
self.ns_mgr = _WD_XMLNS_MGR()
self.top_node = None
self.xml_response = None
self.xpath_namespace = { "D": "DAV:" }
def render(self):
if self.top_node is not None:
@@ -118,9 +123,18 @@ class WebDAVQuery(HTTPQuery):
and (headers["content-type"].startswith("application/xml")
or headers["content-type"].startswith("text/xml"))
and int(headers["content-length"]) > 0):
dom_response = xml.dom.ext.reader.Sax2.FromXml(self.response["body"])
reader = xml.dom.ext.reader.Sax2.Reader()
stream = cStringIO.StringIO(self.response["body"])
dom_response = reader.fromStream(stream)
self.response["document"] = dom_response.documentElement
def xpath_evaluate(self, query, top_node = None):
if top_node is None:
top_node = self.response["document"]
xpath_context = xml.xpath.CreateContext(top_node)
xpath_context.setNamespaces(self.xpath_namespace)
return xml.xpath.Evaluate(query, None, xpath_context)
class WebDAVMKCOL(WebDAVQuery):
method = "MKCOL"

View File

@@ -24,8 +24,14 @@
#define UIXLISTVIEW_H
#import <SOGoUI/UIxComponent.h>
#import <SoObjects/Contacts/SOGoContactGCSList.h>
@interface UIxListView : UIxComponent
{
NGVList *list;
SOGoContactGCSList *co;
id item;
}
@end

View File

@@ -20,8 +20,103 @@
* Boston, MA 02111-1307, USA.
*/
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/WOResponse.h>
#import <NGCards/NGVList.h>
#import <NGCards/NGVCard.h>
#import <NGCards/NGVCardReference.h>
#import "UIxListView.h"
@implementation UIxListView
- (NSString *) listName
{
return [list fn];
}
- (BOOL) hasNickname
{
return [list nickname] != nil;
}
- (NSString *) listNickname
{
return [list nickname];
}
- (BOOL) hasDescription
{
return [list description] != nil;
}
- (NSString *) listDescription
{
return [list description];
}
- (NSArray *) components
{
return [list cardReferences];
}
- (NSString *) itemText
{
NSString *rc;
rc = [NSString stringWithFormat: @"%@ <%@>",
[item fn], [item email]];
return rc;
}
- (void) checkListReferences
{
NSMutableArray *invalid;
NGVCardReference *card;
int i, count;
id test;
invalid = [NSMutableArray array];
count = [[list cardReferences] count];
for (i = 0; i < count; i++)
{
card = [[list cardReferences] objectAtIndex: i];
test = [[co container] lookupName: [card reference]
inContext: context
acquire: NO];
if ([test isKindOfClass: [NSException class]])
{
NSLog (@"%@ not found", [card reference]);
[invalid addObject: [card copy]];
}
}
count = [invalid count];
if (count > 0)
{
for (i = 0; i < count; i++)
[list deleteCardReference: [invalid objectAtIndex: i]];
[co save];
}
}
- (id <WOActionResults>) defaultAction
{
id rc;
co = [self clientObject];
list = [co vList];
if (list)
{
[self checkListReferences];
rc = self;
}
else
rc = [NSException exceptionWithHTTPStatus: 404 /* Not Found */
reason: @"could not locate contact"];
return rc;
}
@end

View File

@@ -9,4 +9,17 @@
className="UIxPageFrame"
title="name"
const:popup="YES"
>Unimplemented</var:component>
>
<h3 class="contactCardTitle"><var:string value="listName" /></h3>
<var:if condition="hasNickname">
<h4><var:string value="listNickname" /></h4>
</var:if>
<var:if condition="hasDescription">
<div class="listDescription"><var:string value="listDescription" /></div>
</var:if>
<ul class="listComponents">
<var:foreach list="components" item="item">
<li><var:string value="itemText"/></li>
</var:foreach>
</ul>
</var:component>