mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-03-05 15:16:26 +00:00
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:
11
ChangeLog
11
ChangeLog
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user