]> err.no Git - scalable-opengroupware.org/commitdiff
first working version of addressbook
authorhelge <helge@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Fri, 27 Aug 2004 01:48:23 +0000 (01:48 +0000)
committerhelge <helge@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Fri, 27 Aug 2004 01:48:23 +0000 (01:48 +0000)
git-svn-id: http://svn.opengroupware.org/SOGo/trunk@280 d1b88da0-ebda-0310-925b-ed51d893ca5b

16 files changed:
OGoContentStore/sql/testcontact-agenor-test-contact.psql
SOGo/SoObjects/Contacts/ChangeLog
SOGo/SoObjects/Contacts/SOGoContactFolder.m
SOGo/SoObjects/Contacts/SOGoContactObject.m
SOGo/SoObjects/Contacts/Version
SOGo/UI/Contacts/ChangeLog
SOGo/UI/Contacts/UIxContactEditor.m
SOGo/UI/Contacts/UIxContactEditor.wox
SOGo/UI/Contacts/UIxContactView.wox
SOGo/UI/Contacts/UIxContactsListView.m
SOGo/UI/Contacts/Version
SOGo/UI/Contacts/product.plist
SOGo/UI/Scheduler/ChangeLog
SOGo/UI/Scheduler/UIxAppointmentEditor.m
SOGo/UI/Scheduler/UIxAppointmentEditor.wox
SOGo/UI/Scheduler/Version

index 6b873db71ab27c77820a02675c5d2f1eb0dba3f7..47274d14b65ceffdf96c02ca4251407523b807f5 100644 (file)
@@ -15,7 +15,7 @@ VALUES (
   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";
index dd949d5461703a944c65077f52a1025adf199619..6ce174c06198fb45bef83c892b3dfbfc248104b8 100644 (file)
@@ -1,5 +1,9 @@
 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)
        
index 849a3722836b8078c164f17273c7e4872f579758..1628c039c87837c4ac75513b1687ef0919b06872 100644 (file)
@@ -117,7 +117,7 @@ static BOOL debugOn = YES;
   }
   records = [self fixupRecords:records];
   if (debugOn)
-    [self logWithFormat:@"fetched %i records: %@", [records count], records];
+    [self logWithFormat:@"fetched %i records.", [records count]];
   return records;
 }
 
index c446ce5bca72727d2fb1799d7c6faa635bdcd2b2..0fd3b1f1b5baa35133f5bb2abd6c7f5ab9224e28 100644 (file)
   return [super valueForKey:_key];
 }
 
+/* DAV */
+
+- (NSString *)davDisplayName {
+  NSString *n;
+  
+  if ((n = [self valueForKey:@"cn"]))
+    return n;
+  
+  return [self nameInContainer];
+}
+
 /* GET */
 
 - (id)GETAction:(WOContext *)_ctx {
index 1bf86739af6e070f2b3946837285f951d5e6a275..fc795f55c1387b14d99ba68f17adc4f527276f1f 100644 (file)
@@ -1,3 +1,3 @@
 # $Id$
 
-SUBMINOR_VERSION:=2
+SUBMINOR_VERSION:=3
index 23ad47021d398856495cc96713871f4549fbebdf..97f46b645373339d122d02da2cef52df5691c854 100644 (file)
@@ -1,5 +1,7 @@
 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)
        
index 9f7caf4a4e019654fc8fce7a4e80bfdb37f8198a..d065a590b791f74d67c86666c038088a5f830d48 100644 (file)
@@ -1,7 +1,7 @@
 /*
-  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 */
index 7f53e297705a3f1445658fe412ecf84ee220902f..0bba57ebf478959a1d196601e99cf87bf38f7817 100644 (file)
@@ -7,8 +7,217 @@
                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
index dac801aa5ec3f47f79b2b391e9b71dabd3ba76c0..acd29bedf2c6c4396ed4d802961b435c4188b623 100644 (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>
index 5e6610c3f31c75ed19b296d997a75c5e8667ef4c..0b6f09b772bb00b4f17a7f7501cc9afd7d05bfef 100644 (file)
@@ -1,7 +1,7 @@
 /*
-  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
@@ -25,7 +25,7 @@
 
 @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 */
index fc795f55c1387b14d99ba68f17adc4f527276f1f..749d66dc7109fb3f49932aae8e0c044ad7d4e6c0 100644 (file)
@@ -1,3 +1,3 @@
 # $Id$
 
-SUBMINOR_VERSION:=3
+SUBMINOR_VERSION:=4
index 850db67787e0a3e254b46ce250587180585ee8ac..bc87e563058c15aee7bfac560d517bef0556bdd0 100644 (file)
           protectedBy = "View";
           pageName    = "UIxContactsListView"; 
         };
+        new = { 
+          protectedBy = "View";
+          pageName    = "UIxContactEditor"; 
+          actionName  = "new";
+        };
       };
     };
 
           pageName    = "UIxContactEditor"; 
           actionName  = "save";
         };
+        test = { 
+          protectedBy = "View";
+          pageName    = "UIxContactEditor"; 
+          actionName  = "test";
+        };
       };
     };
   };
index 345e6de33bfae4157bec17105aebb2693fa11160..b0af041929eef0f5f345c0ca0bf7d24144d3dfba 100644 (file)
@@ -1,5 +1,8 @@
 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>
index f27a0fe453e8d76f160c32bdf890ba9f207d904b..016d2ebdcf9218f6ec56c3f86c0e2a0e3358fe08 100644 (file)
   [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;
 }
index 903a2013d3c20623b5f62ac854454c91affde77c..68e09ef67ac5f0dde819caf30044397004839dea 100644 (file)
@@ -15,7 +15,7 @@
             <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>
index 7138cac69809a15b750dee5746bee0ac231fdae3..394f8e3463bd7f18f1d38b2f08ec6fe7825686b9 100644 (file)
@@ -1,6 +1,6 @@
 # $Id$
 
-SUBMINOR_VERSION:=71
+SUBMINOR_VERSION:=73
 
 # v0.9.70 requires libNGExtensions v4.3.107
 # v0.9.67 requires SOPE 4.3