928383,
1,
'{
- givenname="Donald"; cn="Donald Duck"; sn="Duck";
+ givenName="Donald"; cn="Donald Duck"; sn="Duck";
l="Entenhausen";
mail="dd@entenhausen.com";
o="Geldspeicher AG";
2004-08-27 Helge Hess <helge.hess@skyrix.com>
+ * v0.9.3
+
+ * SOGoContactObject.m: use 'cn' as davDisplayName
+
* SOGoContactObject.[hm]: can now decode the property list stored in
the store (v0.9.2)
}
records = [self fixupRecords:records];
if (debugOn)
- [self logWithFormat:@"fetched %i records: %@", [records count], records];
+ [self logWithFormat:@"fetched %i records.", [records count]];
return records;
}
return [super valueForKey:_key];
}
+/* DAV */
+
+- (NSString *)davDisplayName {
+ NSString *n;
+
+ if ((n = [self valueForKey:@"cn"]))
+ return n;
+
+ return [self nameInContainer];
+}
+
/* GET */
- (id)GETAction:(WOContext *)_ctx {
# $Id$
-SUBMINOR_VERSION:=2
+SUBMINOR_VERSION:=3
2004-08-27 Helge Hess <helge.hess@skyrix.com>
+ * first working version of contacts UI (v0.9.4)
+
* removed GET from product.plist, the SoObject directly implements GET
now and redirects to the view method (v0.9.3)
/*
- Copyright (C) 2000-2004 SKYRIX Software AG
+ Copyright (C) 2004 SKYRIX Software AG
- This file is part of OGo
+ This file is part of OpenGroupware.org.
OGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
#include <SOGoUI/UIxComponent.h>
+@class NSMutableDictionary;
+
@interface UIxContactEditor : UIxComponent
{
-
+ NSString *contentString;
+ NSString *errorText;
+ NSMutableDictionary *snapshot; /* contains the values for editing */
}
@end
+#include <Contacts/SOGoContactObject.h>
+#include <Contacts/SOGoContactFolder.h>
#include "common.h"
@implementation UIxContactEditor
+- (id)init {
+ if ((self = [super init])) {
+ self->snapshot = [[NSMutableDictionary alloc] initWithCapacity:16];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self->snapshot release];
+ [self->errorText release];
+ [self->contentString release];
+ [super dealloc];
+}
+
+/* accessors */
+
+- (void)setContentString:(NSString *)_cstr {
+ ASSIGNCOPY(self->contentString, _cstr);
+}
+- (NSString *)contentString {
+ return self->contentString;
+}
+
+- (NSString *)contentStringTemplate {
+ return @"{}"; /* empty property list */
+}
+
+- (NSMutableDictionary *)snapshot {
+ return self->snapshot;
+}
+
+- (void)setErrorText:(NSString *)_txt {
+ ASSIGNCOPY(self->errorText, _txt);
+}
+- (NSString *)errorText {
+ return self->errorText;
+}
+- (BOOL)hasErrorText {
+ return [self->errorText length] > 0 ? YES : NO;
+}
+
+/* load/store content format */
+
+- (void)loadValuesFromContentString:(NSString *)_s {
+ NSDictionary *plist;
+
+ if ((plist = [_s propertyList]) == nil) {
+ [self logWithFormat:@"ERROR: could not parse content string!"];
+ return;
+ }
+
+ [self->snapshot removeAllObjects];
+ [self->snapshot addEntriesFromDictionary:plist];
+}
+
+- (void)_fixupSnapshot {
+ // TODO: perform sanity checking, eg build CN on demand
+ NSString *cn, *gn, *sn;
+
+ cn = [self->snapshot objectForKey:@"cn"];
+ gn = [self->snapshot objectForKey:@"givenName"];
+ sn = [self->snapshot objectForKey:@"sn"];
+
+ if (![sn isNotNull] || [sn length] == 0)
+ sn = nil;
+ if (![cn isNotNull] || [cn length] == 0)
+ cn = nil;
+
+ if (sn == nil) {
+ if (cn == nil)
+ sn = @"[noname]";
+ else {
+ // TODO: need a better name parser here
+ NSRange r;
+
+ r = [cn rangeOfString:@" "];
+ sn = (r.length > 0)
+ ? [cn substringFromIndex:(r.location + r.length)]
+ : cn;
+ }
+ [self->snapshot setObject:sn forKey:@"sn"];
+ }
+
+ if (sn == nil && gn == nil)
+ cn = @"[noname]";
+ else if (sn == nil)
+ cn = gn;
+ else if (gn == nil)
+ cn = sn;
+ else
+ cn = [[gn stringByAppendingString:@" "] stringByAppendingString:sn];
+ [self->snapshot setObject:cn forKey:@"cn"];
+}
+
+- (void)saveValuesIntoRecord:(NSMutableDictionary *)_record {
+ [self _fixupSnapshot];
+ [_record addEntriesFromDictionary:[self snapshot]];
+}
+
+/* helper */
+
+- (NSString *)_completeURIForMethod:(NSString *)_method {
+ // TODO: this is a DUP of UIxAppointmentEditor
+ NSString *uri;
+ NSRange r;
+
+ uri = [[[self context] request] uri];
+
+ /* first: identify query parameters */
+ r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
+ if (r.length > 0)
+ uri = [uri substringToIndex:r.location];
+
+ /* next: append trailing slash */
+ if (![uri hasSuffix:@"/"])
+ uri = [uri stringByAppendingString:@"/"];
+
+ /* next: append method */
+ uri = [uri stringByAppendingString:_method];
+
+ /* next: append query parameters */
+ return [self completeHrefForMethod:uri];
+}
+
+/* actions */
+
+- (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
+ return YES;
+}
+
+- (id)defaultAction {
+ // TODO: very similiar to apt-editor (apt editor would need to use std names
+ NSString *c;
+
+ /* load iCalendar file */
+
+ c = [[self clientObject] contentAsString];
+ if ([c length] == 0) /* a new contact */
+ c = [self contentStringTemplate];
+
+ [self setContentString:c];
+ [self loadValuesFromContentString:c];
+
+ return self;
+}
+
+- (BOOL)isWriteableClientObject {
+ return [[self clientObject]
+ respondsToSelector:@selector(saveContentString:)];
+}
+
- (id)saveAction {
-#if 0
NSException *ex;
-
- ex = [[self clientObject] saveContentString:content];
+ NSString *recstr;
+ id record;
+
+ if (![self isWriteableClientObject]) {
+ /* return 400 == Bad Request */
+ return [NSException exceptionWithHTTPStatus:400
+ reason:@"method cannot be invoked on "
+ @"the specified object"];
+ }
+
+ record = [[[[self contentString] propertyList] mutableCopy] autorelease];
+ if (record == nil) {
+ [self setErrorText:@"Invalid property list data ..."]; // localize
+ return self;
+ }
+
+ [self saveValuesIntoRecord:record];
+
+ recstr = [record description]; // make plist
+ ex = [[self clientObject] saveContentString:recstr];
if (ex != nil) {
[self setErrorText:[ex reason]];
return self;
}
return [self redirectToLocation:[self _completeURIForMethod:@".."]];
-#else
+}
+
+- (id)testAction {
+ [self logWithFormat:@"test ..."];
return self;
-#endif
}
-@end
+- (id)newAction {
+ // TODO: this is almost a DUP of UIxAppointmentEditor
+ /*
+ This method creates a unique ID and redirects to the "edit" method on the
+ new ID.
+ It is actually a folder method and should be defined on the folder.
+
+ Note: 'clientObject' is the SOGoAppointmentFolder!
+ Update: remember that there are group folders as well.
+ */
+ NSString *uri, *objectId, *nextMethod;
+
+ objectId = [NSClassFromString(@"SOGoContactFolder") globallyUniqueObjectId];
+ if ([objectId length] == 0) {
+ return [NSException exceptionWithHTTPStatus:500 /* Internal Error */
+ reason:@"could not create a unique ID"];
+ }
+
+ nextMethod = [NSString stringWithFormat:@"../%@/edit", objectId];
+ uri = [self _completeURIForMethod:nextMethod];
+ return [self redirectToLocation:uri];
+}
+
+@end /* UIxContactEditor */
className="UIxPageFrame"
title="name"
>
+ <style>
+ table.editsec {
+ background-color: #e8e8e0;
+ width: 100%;
+ }
+ </style>
+
+ <form var:href="clientObject.baseURL">
+ <table cellspacing="0" cellpadding="5" width="100%">
+ <tr>
+ <td>
+ <table cellpadding="0" cellspacing="0" width="100%">
+ <tr>
+ <td width="5"/>
+ <td class="window_label">
+ <var:string label:value="Appointment editor" /></td>
+ <td width="36" align="right" valign="center">
+ <var:component className="UIxWinClose" />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <var:if condition="hasErrorText">
+ <div style="background-color: #AA0000;">
+ <var:string value="errorText" />
+ </div>
+ <hr />
+ </var:if>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <table border="0" cellpadding="2" cellspacing="0" class="editsec">
+ <tr>
+ <td align="left" colspan="2">
+ <span class="aptview_title">
+ <var:string label:value="Common" />
+ </span>
+ </td>
+ </tr>
+ <tr valign="top">
+ <td align="right" width="15%">
+ <span class="aptview_text">
+ <var:string label:value="Lastname" />:
+ </span>
+ </td>
+ <td align="left" bgcolor="#FFFFF0">
+ <span class="aptview_text">
+ <input type="text" name="sn" var:value="snapshot.sn"
+ size="60" />
+ </span>
+ </td>
+ </tr>
+ <tr valign="top">
+ <td align="right" width="15%">
+ <span class="aptview_text">
+ <var:string label:value="Firstname" />:
+ </span>
+ </td>
+ <td align="left" bgcolor="#FFFFF0">
+ <span class="aptview_text">
+ <input type="text" name="givenName"
+ var:value="snapshot.givenName"
+ size="60" />
+ </span>
+ </td>
+ </tr>
+ </table>
+ <br />
+
+ <table border="0" cellpadding="2" cellspacing="0" class="editsec">
+ <tr>
+ <td align="left" colspan="2">
+ <span class="aptview_title">
+ <var:string label:value="Telephones" />
+ </span>
+ </td>
+ </tr>
+ <tr valign="top">
+ <td align="right" width="15%">
+ <span class="aptview_text">
+ <var:string label:value="Phone" />:
+ </span>
+ </td>
+ <td align="left" bgcolor="#FFFFF0">
+ <span class="aptview_text">
+ <input type="text" name="telephoneNumber"
+ var:value="snapshot.telephoneNumber"
+ size="60" />
+ </span>
+ </td>
+ </tr>
+ <tr valign="top">
+ <td align="right" width="15%">
+ <span class="aptview_text">
+ <var:string label:value="Facsimile" />:
+ </span>
+ </td>
+ <td align="left" bgcolor="#FFFFF0">
+ <span class="aptview_text">
+ <input type="text" name="facsimileTelephoneNumber"
+ var:value="snapshot.facsimileTelephoneNumber"
+ size="60" />
+ </span>
+ </td>
+ </tr>
+ </table>
+ <br />
+
+ <table border="0" cellpadding="2" cellspacing="0" class="editsec">
+ <tr>
+ <td align="left" colspan="2">
+ <span class="aptview_title">
+ <var:string label:value="Addresses" />
+ </span>
+ </td>
+ </tr>
+ <tr valign="top">
+ <td align="right" width="15%">
+ <span class="aptview_text">
+ <var:string label:value="Postal" />:
+ </span>
+ </td>
+ <td align="left" bgcolor="#FFFFF0">
+ <span class="aptview_text">
+ <textarea name="postalAddress" rows="3" cols="60"
+ wrap="physical"
+ var:value="snapshot.postalAddress" />
+ </span>
+ </td>
+ </tr>
+ <tr valign="top">
+ <td align="right" width="15%">
+ <span class="aptview_text">
+ <var:string label:value="Home" />:
+ </span>
+ </td>
+ <td align="left" bgcolor="#FFFFF0">
+ <span class="aptview_text">
+ <textarea name="homePostalAddress" rows="3" cols="60"
+ wrap="physical"
+ var:value="snapshot.homePostalAddress" />
+ </span>
+ </td>
+ </tr>
+ </table>
+ <br />
+ <table border="0" cellpadding="2" cellspacing="0" class="editsec">
+ <tr>
+ <td align="left" colspan="2">
+ <span class="aptview_title">
+ <var:string label:value="Extended" />
+ </span>
+ </td>
+ </tr>
+ <tr valign="top">
+ <td align="right" width="15%">
+ <span class="aptview_text">
+ <var:string label:value="Email" />:
+ </span>
+ </td>
+ <td align="left" bgcolor="#FFFFF0">
+ <span class="aptview_text">
+ <input type="text" name="mail"
+ var:value="snapshot.mail"
+ size="60" />
+ </span>
+ </td>
+ </tr>
+ <tr valign="top">
+ <td align="right" width="15%">
+ <span class="aptview_text">
+ <var:string label:value="URL" />:
+ </span>
+ </td>
+ <td align="left" bgcolor="#FFFFF0">
+ <span class="aptview_text">
+ <input type="text" name="labeledURI"
+ var:value="snapshot.labeledURI"
+ size="60" />
+ </span>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <input type="submit" label:value="Save" name="save:method" />
+ <span class="button_auto_env"
+ ><a href="../weekoverview"
+ var:queryDictionary="queryParameters"
+ class="button_auto"
+ ><var:string label:value="Cancel" /></a></span>
+ <var:if condition="isUIxDebugEnabled">
+ <input type="submit" value="Test" name="test:method" />
+ </var:if>
+ </td>
+ </tr>
+ </table>
+
+ <input type="hidden" name="content" var:value="contentString" />
+ </form>
+
<var:if condition="isUIxDebugEnabled">
- <hr />
- clientObject: <var:string value="clientObject" />
+ <small>
+ <hr />
+ clientObject: <var:string value="clientObject" />
+ </small>
</var:if>
</var:component>
\ No newline at end of file
<var:string value="clientObject.telephoneNumber"/></span>
</td>
</tr>
+ <tr valign="top">
+ <td align="right" width="15%" bgcolor="#E8E8E0">
+ <span class="aptview_text">Fax:</span>
+ </td>
+ <td align="left" bgcolor="#FFFFF0">
+ <span class="aptview_text">
+ <var:string value="clientObject.facsimileTelephoneNumber"
+ /></span>
+ </td>
+ </tr>
<tr valign="top">
<td align="right" width="15%" bgcolor="#E8E8E0">
<span class="aptview_text">Organisation:</span>
/*
- Copyright (C) 2000-2004 SKYRIX Software AG
+ Copyright (C) 2004 SKYRIX Software AG
- This file is part of OGo
+ This file is part of OpenGroupware.org.
OGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
@interface UIxContactsListView : UIxComponent
{
-
+ id contact;
}
@end
@implementation UIxContactsListView
-@end
+- (void)dealloc {
+ [self->contact release];
+ [super dealloc];
+}
+
+/* accessors */
+
+- (void)setContact:(id)_contact {
+ ASSIGN(self->contact, _contact);
+}
+- (id)contact {
+ return self->contact;
+}
+
+/* notifications */
+
+- (void)sleep {
+ [self->contact release]; self->contact = nil;
+ [super sleep];
+}
+
+@end /* UIxContactsListView */
# $Id$
-SUBMINOR_VERSION:=3
+SUBMINOR_VERSION:=4
protectedBy = "View";
pageName = "UIxContactsListView";
};
+ new = {
+ protectedBy = "View";
+ pageName = "UIxContactEditor";
+ actionName = "new";
+ };
};
};
pageName = "UIxContactEditor";
actionName = "save";
};
+ test = {
+ protectedBy = "View";
+ pageName = "UIxContactEditor";
+ actionName = "test";
+ };
};
};
};
2004-08-27 Helge Hess <helge.hess@skyrix.com>
+ * UIxAppointmentEditor.m: prepared to share code with contact editor
+ (v0.9.73)
+
* UIxCalView.m: minor code cleanups (v0.9.72)
2004-08-25 Marcus Mueller <znek@mulle-kybernetik.com>
[apt release];
}
+/* contact editor compatibility */
+
+- (void)setContentString:(NSString *)_s {
+ [self setICalString:_s];
+}
+- (NSString *)contentStringTemplate {
+ return [self iCalStringTemplate];
+}
+
+- (void)loadValuesFromContentString:(NSString *)_s {
+ [self loadValuesFromICalString:_s];
+}
+
/* actions */
- (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
/* load iCalendar file */
+ // TODO: can't we use [clientObject contentAsString]?
ical = [[self clientObject] valueForKey:@"iCalString"];
if ([ical length] == 0) /* a new appointment */
- ical = [self iCalStringTemplate];
+ ical = [self contentStringTemplate];
- [self setICalString:ical];
- [self loadValuesFromICalString:ical];
+ [self setContentString:ical];
+ [self loadValuesFromContentString:ical];
return self;
}
<tr>
<td width="5"/>
<td class="window_label">
- <var:string label:value="Appointment editor" /></td>
+ <var:string label:value="Contact Editor" /></td>
<td width="36" align="right" valign="center">
<var:component className="UIxWinClose" />
</td>
</td>
</tr>
</table>
+
<input type="hidden" name="ical" var:value="iCalString" />
</form>
+
<var:if condition="isUIxDebugEnabled">
- <hr />
- clientObject: <var:string value="clientObject" />
+ <small>
+ <hr />
+ clientObject: <var:string value="clientObject" />
+ </small>
</var:if>
</var:component>
# $Id$
-SUBMINOR_VERSION:=71
+SUBMINOR_VERSION:=73
# v0.9.70 requires libNGExtensions v4.3.107
# v0.9.67 requires SOPE 4.3