Menu

ZideStore

ZideStore

ZideStore is OpenGroupware's integration server; it provides access to the functionality of the groupware server through a variety of protocols. Protocols are plugged into ZideStore via ZSP bundles which are each a collection of resources and shared libraries. The purpose of ZideStore is to facilitate interoperability of the server with as wide a variety of clients and other applications as possible.

Protocols

CalDAV

CalDAV is a standard proposed by the IETF to allow interoperability of collaboration systems when dealing with calendar information. CalDAV builds on top of HTTP, WebDAV, WebDAV ACLs and iCalendar to provide reasonably robust calendaring functionality that can be implemented by a variety of clients and servers. It is important to distinguish between CalDAV and iCalendar. CalDAV deals with events and collections of events, allowing events to be operated upon on a per-event basis. iCalender the specification used to encode each event. This differs significantly, but can be confused with, what is often referred to as an “iCalendar Server” (which ZideStore also is), see the section in this chapter on “iCalendar Limitations” for more information.

CalDAV is a more sophisticated specification that GroupDAV which adds a collection of operations specifically to meet the needs of calendaring applications. ZideStore support for the CalDAV specification is not yet complete and provides only partial compatibility with the various CalDAV clients (see the “CalDAV Clients” section of this chapter for details).

The most notable distinction between CalDAV and WebDAV is that CalDAV expands the protocol to include a set of REPORT operations. The REPORT operation itself is an extension to the WebDAV protocol1. The REPORT operation adds to the WebDAV protocol a way for clients to perform more complicated operations upon the servers data (including query operations). REPORT permits the consolidation of operations that would potentially require multiple round-trips between the client and the server into a single request – essentially moving the complexity of the application from the client to the server.

CALDAV:calendar-query

The calendar-query REPORT returns all the calendar objects in the specified collection that match the filter specified in the REPORT request.

CALDAV:calendar-multiget

CALDAV:free-busy-query

Free/Busy

Free/Busy is part of the iCalendar specification and is a way of representing the availability of a person or resource for the purposes of detecting conflicts or proposing scheduling. Free/Busy information can be retrieved from ZideStore via a simple HTTP request. Retrieval of free/busy information from ZideStore is covered in the Schedular section of this document.

GroupDAV

GroupDAV is a specification that is both a subset of CalDAV and a complement to CalDAV. One of the focuses of the GroupDAV protocol is to remain as simple as possible while still providing a useful set of functionality; like CalDAV the main point of GroupDAV is interoperability between a variety of clients, servers, and other applications. GroupDAV is a complement to CalDAV in that it provides a standard protocol, also built on the HTTP and WebDAV protocols, to access not just events but contacts, tasks, and other types of objects. It is a subset of CalDAV as it is build upon the same standard foundations and a server that is a GroupDAV server can implement CalDAV as well; in a sense, CalDAV on top of GroupDAV, in order to provide access to contacts and tasks as well as events.

iCalendar

iCalendar is actually not a protocol, but a specification for the exchange of calendar and event information. However iCalendar information is frequently delivered over networks as either e-mail attachments or HTTP; iCalendar over HTTP is almost a defacto standard simply from the prevalence of its use. ZideStore will deliver a user or groups calendar information in iCalendar format in response to certain HTTP requests making it possible to use simple iCalendar clients such as Mozilla's SunBird to view a calendar. However, working with calendars in this manner is a bulk operation making it very difficult to modify a single event – the entire calendar of events available to the client at a given URL is retrieved and save in a single bulk operation; this severely limits the usefulness of iCalendar over HTTP as a true groupware solution. iCalendar also has no way of representing access permissions, such as if a given event is available to a user for viewing but cannot be modified (read-only).

PrefsUI

The PrefsUI is not really so much a protocol as a simple application. If a user attempts to access the ZideStore server with a web browser they will access the PrefsUI bundle.

RSS

RSS is either “Really Simple Syndication” (RSS 2.0) or “Rich Site Summary” (RSS 0.91 and RSS 1.0). RSS is an XML over HTTP protocol used by RSS reader / news aggregator applications to display new or changed content from a feed or collection of feeds. A given feed may be a news oriented website, a blog, podcast site, or in the context of OpenGroupware a project.

WebDAV

WebDAV is the core protocol of the ZideStore server. Most content stored within the groupware server can be accessed by navigating the ZideStore folder tree with a WebDAV client; with the proper client this content can be modified in much the same fashion as a user would manage files and folders in a traditional filesystem.

zOGI

ZOGI is a ZideStore bundle that provides a simple XML-RPC API for utilizing the functionality provided by the OpenGroupware server.

Defaults

Cache Debugging

ZideStore does a significant amount of caching of data in order to improve performance and reduce load on the backend database. See the entries in this section for “Disk Cache” and “Memory Cache” for information on how to tune or set the caching parameters. Debugging of the cache can be enabled by setting the “SxDebugCache” to a value of “YES”, deleting this default will disable logging of cache operatons. Enabling cache debugging can have a detrimental affect on performance as it can significatly increase the amount of information being sent to the systems' log files.

Calendar Scope

The size of responses to event requests can be controlled by specifying the number of months that ZideStore will examine. Two defaults control, respectively, how many months into the past and months into the future that ZideStore will render in response to calendar requests. The span of events in the event collections such a the Calendar and Overview folders are controlled by the SxAptFolder_MonthsIntoPast and SxAptFolder_MonthsIntoFuture defaults. SxAptFolder_MonthsIntoPast defaults to two months and SxAptFolder_MonthsIntoFuture defaults to 12 months. Increasing or decreasing the span for which events are rendered can have a significance effect of server performance.

Content Caching

When a client requests the vCard for a contact or enterprise ZideStore caches the generated vCard as a file. This allows it to simply read the file and stream the content to the next client requesting the same vCard provided the contents of the database has not changed. This facilitates a very high throughput when clients syncronize with the server via WebDAV. The penalty is the creation of potentially ten of thousands of vCard files; directories containing very large numbers of files can demonstrate much worse performance for file operations than those with smaller numbers of files.

The default location for this content cache is the OpenGroupware documents folder1. If the documents folder is located on a SAN or a NAS, which introduce network latency, the throughput benefit of the cache may be diminished. For smaller sites this is unlikely to pose a significant problem; larger sites, or sites with very large numbers of contacts, should consider relocating the content cache and enable hashing of the cache content. If the documents folder is located on a SAN or a NAS relocating it to local storage is strongly recommended. Backup of cache contents is not a concern as the contents will automatically be regenerated in the event of data loss. Relocation of the content cache is performed by defining an alternative path via the “LSVCFCachePath” default2. The specified path must be accessible by the OpenGroupware user.

defaults write NSGlobalDomain LSVCFCachePath /var/opengroupware/vCard
defaults write NSGlobalDomain LSHashVCFCache YES

Enabling of hashing of cache content is enabled by setting the “LSHashVCFCache” to value of “YES”. If hashing is not enabled the cache consists of files stored in either the document root or the path defined via “LSVCFCachePath”; this configuration can manifest the large directory problem. If hashing is enabled the last two digits of the object's primary key are used to construct an additional path component where the object's vCard representation will be stored. For example: without hashing version seven of the contact with id 1,675,689 will be stored in the root of the cache path as “1675689.7.vcf”, if hashing is enabled the same vCard would be cached as “vcfdir89/1675689.7.vcf”. With hashing enabled the root of the cache will contain up to one hundred folders each of which will contain the appropriate subset of objects. In either case the filename uniquely identifies the specific version of the contact so that ZideStore can easily determine if it is a valid representation of the database contents.

Since even very large numbers of vCard files still consume relatively little storage it may be worthwhile for some sites to avoid even the use of local storage and to provision the content cache onto a RAM disk. On LINUX systems this is easily accomplished by the creation of a tmpfs filesystem of sufficient size1. Once the virtual volume has been provisioned and the permissions of the mount point set appropriately use the “LSVCFCachePath” default to relocate the content cache. This provides the advantage of avoiding disk I/O completely.

Group Overview Folders

For each user a “/Overview” folder is provided which contains all the events for which the user is participant. In some situations it may be useful to provide equivalent folders for teams, such a folder is called a “Group Overview” folder. Group Overview folders are disabled by default as they can significantly increase server load and may also contain a very large number of events. In order to enable group overview folders set the “ZLShowGroupOverviewCalendars” to “YES” in either the appropriate ZideStore domain or the NSGlobalDomain domain.

The “/public” folder is provided for when a user wishes to access all the appointments they have permission to see, so not Group Overview folder is provided for the “all intranet” team even if you enable Group Overview folders.

Memory Limit

The ZideStore service maintains a large in memory cache of recently cached objects and other related information. This results in the memory footprint of the service to be quite large. The maximum footprint of the service is controlled by SxVMemLimit default which has a default value of 200. The value of SxVMemLimit is in megabytes. This will cause the ZideStore service to reset itself once its footprint reaches 200 megabytes.

ZideStore URLs

Collections vs. Object

One important distinction in understating ZideStore URLs is the difference between a URL that references a collection and a URL that references an object. While HTTP as traditionally used by web browsers only uses URLs to reference objects (pages or images) other protocols which are based on HTTP support the concept of collections. Collections are very much like folders in a traditional filesystem; they have no content of their own but serve to provide a namespace within which objects exists as well as additional collections. The best example of this distinction is the ZideStore URLs for the Overview folder and the Overview ICS file. The “/zidestore/so/fred/Overview” URL refers to a collection that contains all of the appointments in which user “fred” is a participant. Protocols such as DAV, GroupDAV, and CalDAV can use this URL to enumerate these appointments. However the URL “/zidestore/so/fred/Overview/ics” refers to an iCalendar object whose contents is an enumeration of all the appointments in which user “fred” is a participant. This second URL can be requested by typing the URL into the address bar of your web browser or even using a utility like “curl”.

Free/Busy Information

Retrieval of a user's free/busy information is documented in the “Scheduler” chapter.

Folder Hierarchy

In the standard configuration a user's root collection will contain nine folders: Calendar, Enterprises, Overview, public, Trash, Contacts, Groups, Projects, and Tasks. The Calendar, Enterprise, and Contacts folder of the user's root collection contain the corresponding type of entities which are marked as private to the user.

The root Groups folder will contain an object for each of the teams of which the user is a member. The objects in the Groups folder are both folders and vCard objects; as a folder each group contains a vCard object of each account belonging to the team. Beneath the root Groups subfolder is a “Calendar” and an “Overview” folder. The “Calendar” folder contains all the appointments and having the appropriate group specified as the appointment's read access group1. The “Overview” folder contains all the appointments which have either the appropriate group or a member of that group as a participant2. Both “Calendar” and “Overview” are additionally constrained within the range defined by the SxAptFolder_MonthsIntoPast and SxAptFolder_MonthsIntoFuture defaults. For example the folder “/Groups/news editors/” contains a vCard for each member of the “news editors” team and the “/Groups/news editors/Calendar/” contains appointments where the read access group is “news editors”. The root Groups folder itself is read-only, team membership cannot be modified through ZideStore by putting or deleting vCard entries in a group folder.

  • User Folder
  • Contacts
  • Enterprises
  • Tasks
  • Calendar
  • public
    • Contacts
    • Enterprises
    • Calendar
  • Overview
  • Groups
    • {Team}
    • Overview
    • Calendar
    • Task
  • Trash

The root Overview folder is probably the most useful of the calendar folders, it contains every appointment where the user is a participant; this includes any appointment where a team of which the user is a member is a participant. This is the only selection of calendar events, by default, which is based on participation. Most collections presented to the client by ZideStore are based on permissions. The collection of appointments in the root Overview folder should be the same as that presented in the default view of the schedule application in the web UI.

The Contacts and Enterprises folder in the root user folder contain contacts that are marked as private, owned by the user, and not accounts. Likewise the root Calendar contacts all the appointments of the with the scope defined by SxAptFolder_MonthsIntoPast and SxAptFolder_MonthsIntoFuture defaults and with no read access team; appointments with no read access team are considered private. The contents of the root Tasks folder consists of non-archived tasks of an unspecified type, having the current user as the executor.

The Contacts and Enterprises folders of public folder display all the contact and enteprise entries, respectively, to which the current user has read access and are not marked as private1. Qualified objects are then filtered for permissions. It is important to note that in the OpenGroupware permissions model the application of ACLs trump the permissions applied by the privacy flag on an object; so a user may be able to view contacts and enterprises in the web interface that are not available via the public folders in ZideStore. This occurs when ACLs are applied to an object by the privacy flag is unset.

The public Calendar folder contains all appointments with the “all intranet” team as the read access team and qualified within the range specified by the SxAptFolder_MonthsIntoPast and SxAptFolder_MonthsIntoFuture defaults.

Properties

Attributes of objects are also exposed as WebDAV properties. Most objects in ZideLook support the common WebDAV properties of: “davDisplayName”, “davIsCollection”, “davIsFolder”, “davHasSubFolders”, “davContentLength”, and “davLastModified”

Tasks provide the following attributes: “sensitivity”, “priority”, “importance”, “actualWorkInMinutes”, “totalWorkInMinutes”, “isTeamTask”, “taskCompletion”, “taskStatus”, “subject”, “textdescription”, “keywords”, “taskCommonStart”, “taskCommonEnd”, and “taskCompletionDate” provides the task's completion date if the task is complete. “textdescription” contains the tasks initial comment. “taskCommonStart” contains the task's start date. “taskCommonEnd” contains the task's end date. “taskCompletionDate” contains the date the task was completed if the task is currently in a “done” state, otherwise it returns the current date.

vCard Retrieval

A simple authenticated GET request to an appropriate folder is all that is required to retrieve the vCard representation of an OpenGroupware object. The primary key of the object is the name of the resource within the collection; any or no file extension will be ignored (characters beyond the first period). The contents of each folder can be enumerated via a DAV request (see “Requesting a WebDAV collection“ pg.90). In the results of a folder enumeration either the “href” elements or the “getetag” properties can be used to identity resources that can be retrieved via a GET.

The rendering of vCards for Contacts and Enterprises is performed via execution of the “company::get-vcard” command. This command and the exact mapping of OGo object properties to vCard attributes is documented in the Logic Commands appendix's entry for “company::get-vcard”.

It is not possible to search for contacts or enterprises via simple HTTP, ZideStore has only very limited support for DASL. The common practice for clients such a Thunderbird, Evolution, or Microsoft Outlook (via ZideOne) is to enumerate a collection and download all objects to a local cache. Searching and other operations are then performed on the local cache with changes propagated by publishing changed objects back to the server using their [modified] vCard representations.

Updating Data Via HTTP

Putting A vCard

When a vCard is uploaded via an HTTP put operation to a collection (folder) the URL is examined to determine if the PUT is meant to be an update of an existing object or the creation of a new object. If no id is provided for the object it is assumed to be a request to create a new object. If the URL contains an id that value will be interpreted as the primary key of the object to update. For example, a PUT to the URL: “http://127.0.0.1/zidestore/dav/adam/public/Contacts/” will create a new contact while a PUT to the URL: ““http://127.0.0.1/zidestore/dav/adam/public/Contacts/10100.vcf” will result in an update to contact with the primary key of 10,100.

In the case of a collection (folder) of either Contact or Enterprise objects the update or creation is performed via the “company::set-vcard” Logic command1 after parsing the uploaded data via the parseVCardsFromSource method of the NGVCard class. If parsing of the vCard data fails the server will return a HTTP 400 error with the message “The request did not contain a valid vCard”. If saving the vCard data via the logic command fails the server will return an HTTP 500 error. The successful creation of a new object will return to the client a location header indicating the URL of the new object.

Mapping vCard Fields

The initial parsing of the vCard file is perfomed by the SAXXML parser provided by SOPE. The NGVCard class utilizes the SaxXMLReaderFactory class to the transform the vCard data into XML which is then managed via the NGVCardSaxHandler class. For a complete examination of the mapping of vCard attributes to OpenGroupware object properties see the documentation on the “company::set-vcard” command in the chapter “Logic”. Some information, including how to manually test vCard translation, is covered in the “iCalendar Parsing” section of the chapter “Debugging”.

Mapping vCard Address Fields

The mapping of addresses provided in vCards to those of Contact or Enterprise objects is performed by the “company::set-vcard” Logic command which uses the LSVCard_PersonAddressRevMapping and LSVCard_EnterpriseAddressRevMapping defaults which are built into the LSAddress bundle.

Mapping vCard Telephone Fields

Resolving Participants In Appointments

When presented with an event ZideStore processes the participants from the “ATTENDEE” attributes based upon the “MAILTO” value. In a vCalendar event the attendee attribute will look something like this:

ATTENDEE;CUTYPE="INDIVIDUAL";PARTSTAT="NEEDS-ACTION";ROLE="REQ-PARTICIPANT";RSVP="FALSE";CN="Adam Williams":MAILTO:adam@example.com

This single line contains all the information pertaining to the attendee. ZideStore ignores the “CUTYPE” attribute and relies solely on the “MAILTO” attribute which is compared with the “email1” extended attribute in the PostgreSQL database in order to match the attendee record with a contact or team1. Only the “email1” attribute will be checked; for purposes of resolving participants the “email2” and “email3” extended attributes are not considered. For each attendee attribute ZideStore will attempt four queries to locate the contact:
* Non-private contacts that are also accounts
* Non-private contacts that are not acocunts
* Private contacts (owned by the current user)
* Teams

If all of these checks fail ZideStore will attempt to create a contact record for the participant – when no database match was found ZideStore assumes the participant is an individual (not a group). For example, an attendee record of:

ATTENDEE;CUTYPE="INDIVIDUAL";PARTSTAT="NEEDS-ACTION";ROLE="REQ-PARTICIPANT";RSVP="FALSE";CN="Tom Bob":MAILTO:MAILTO:tbob@example.com

will create a new contact with first name “Tom”, last name “Bob”, and an e-mail address of “tbob@example.com” provided no record with an e-mail address of “tbob@example.com” exists. This creation of new records prevents data-loss but it is advisable to create contact records previously to using contacts as participants as the record created will have minimal information and be private to the user who created the appointment1. One result of the privacy is that, while the appointment may be available to user users, the new contact will not. If the intention of the user was to share the contact they will need to log into the system and change the permissions to appropriate values.

CalDAV Clients

CalDAV clients vary significantly in the features of the CalDAV specification that they exercise and how they deal with issues such a timezones. Therefore CalDAV support needs to be tested rather extensively on a per-client basis. If the CalDAV client you are attempting to use experiences problems when communicating with ZideStore please report the issue to the OpenGroupware project following the procedures described in “Reporting CalDAV Issues”.

Reporting CalDAV Issues

If a CalDAV client experiences issues communicating with ZideStore is is advised to capture the HTTP request from the client using the “WOHttpAdaptor_LogStream” default as described in the Debugging chapter. Then emulate the operation using the curl utility. If the response is not valid or ZideStore reports an error the exchange between the client and the server should be reported to the OpenGroupware buzilla along with the name and version of the client. It is also helpful if the exchange is captured with the “SoDebugObjectTraversal”, “SoRendererDebugEnabled”, and “PGDebugEnabled” defaults enabled.

ZideStore Bundles

ZideStore's core functionality can be extended by ZideStore Bundles. With a standard installation ZideStore already includes a set of bundles: “Appointments.zsp”, “Contacts.zsp”, “EvoConnect.zsp”, “PrefsUI.zsp”, “Projects.zsp”, “RSS.zsp”, “Tasks.zsp”, “WCAP.zsp”, “zOGI.zsp”, and “ZSCommon.zsp”. As you can probably guess by looking at this list much of ZideStore's core functionality is actually implemented via bundles.

ZideStore searches for and loads bundles when it is first started; the bundles loaded are enumerated in the error file. Immediately after being started, and before processing any requests, ZideStore will write log entries about that.

The “ PATHES” entry displays the paths that are searched for bundles, and subsequent entries up until the “vMemSize check enabled” entry enumerates the ZideStore bundles that were found and loaded.
For general information on bundles see the “Architecture” chapter.

RSS Support

Folders and objects in the ZideStore hierarchy may supply an RSS feed providing additional information about the collection or object. RSS support is invoked whenever a request is made with a filename suffix of “.rss” or “.xml”. A default RSS feed may be retrieved on a collection by requesting the “rss” object within that collection. Additional RSS feeds intended for specific purposes may be available, these are referred to as “special purpose” RSS feeds.

Special purpose RSS feeds support at least the “limit” parameter which determines the number of items that will be included in the feed. The default limit, unless documented otherwise, for a feed is 150 items.

The links provided by the auto-generated RSS must be configured for each site; if the links are not provided or are not valid some RSS readers will refuse to process or display the content of the feed. The channel link of RSS feeds generated by the server are set via the “RSSDefaultChannelLinkURL” default. It is intended that this link point to the OpenGroupware WebUI login page or some portal to groupware content. The individual link in each individual RSS item is build using the pattern provided by the “RSSDefaultItemLinkURL” default. With the item link URL the string “$objectId” is replaced by the primary key of the object related to the feed item; for instance, the id of the task when presenting a task action as an RSS item. The string “$GUID” is also replaced with the value of items “guid” tag. Each RSS feed may generate the “guid” for the item in a way specific to the feed. An important caveat in setting these defaults is that a URL may contain special characters, such as question marks, requiring the value to be properly quoted when writing the value.

defaults write NSGlobalDomain \
     RSSDefaultChannelLinkURL "http://ogo.example.com/"
defaults write NSGlobalDomain RSSDefaultItemLinkURL \
    '"http://ogo.example.com/OpenGroupware/wa/LSWViewAction/viewJob?jobId=$objectId"'

Taks Feeds

Task feeds are available in any of the Task folders1.

  • delegated-actions-rss – Includes actions performed upon tasks of which the user was the creator.
  • project-actions-rss – Includes actions performed on tasks attached to projects to which the user is assigned.
  • todo-actions-rss – Includes actions performed on tasks for which the user is the executor or belongs to a team which is the executor.

Developing ZideStore Bundles

Developing a ZideStore bundle is quite simple; all that is required is basic knowledge of Objective-C and the foundational classes. By developing custom bundles a site can add functionality to the server that can be accessed by a variety of clients; adding functionality directly on the server provides centralization and efficiency.

A bundle must contain a GNU make file, a product.plist describing the bundle, and one or more class files.

zAddition

zAddition is an example ZideStore bundle that provides an XML-RPC method which accepts two arguments and returns the sum if both arguments are integers. In this case the method can be executed in any folder, and the location of the folder is irrelevant to the result. For example, if ZideStore is running on the local host and the user's name is “adam” this method can be executed at “http://localhost/zidestore/so/adam/public” or “http://localhost/zidestore/so/adam/Contacts/” or any other valid folder.

{ 
  requires = ( ZSCommon ); 
  publicResources = ( ); 
  factories = { }; 
  classes = { }; 
  categories = { 
    SxFolder = { 
      methods = { 
          "demo.add" = { 
            protectedBy = "View"; 
            actionClass = "zAddition"; 
            actionName = "addIt"; 
            arguments = { positionalKeys = ( Fred, George ); }; 
       }; 
      }; 
    }; 
  }; 
}

The first step is to define a product.plist file which defines the the XML-RPC method the bundle is to provide and to map that method to a method of a class provided by the bundle. In the case of an XML-RPC method the arguments for the method must also be specified because the arguments to an XML-RPC method are positional.

The product.plist for zAddition specifies a method called “demo.add” that invokes the “addItAction” method of the “zAddition” class. Before the method is invoked the values of two arguments will be set in the object via key-value-coding. One important note is that although the value of “actionName” is “addIt” it is the “addItAction” method that the service will attempt to invoke. This is part of the SOPE naming convention.

If no method matching the one defined by “actionName” can be found the function will return a null value and the error will be recorded in the log file:

Oct 20 03:51:57 ogo-zidestore-1.5 [11247]: >zAddition> DirectAction class zAddition cannot handle action addItAction

The method being assigned to the “SxFolder” category and protected by “View” controls access to the function. In this case the protection has no substantial meaning since every user will have at least one folder in which they have view access. In other circumstances where the operation is bound to a more specific folder or entity the protection will be more relevant.

The next step after defining the product description is to code the actual objects. In the case of this example the object is trivially simple. The “setFred” and “setGeorge” accessors are automatically invoked via key-value-coding in order to pass the values of the parameters to the object1. once the parameters have each been passed to the object via the setter the action method, in this case “addItAction”, is invoked. The result of the method is automatically appropriately encoded, here as an XML-RPC response, and returned as the response to the client's request.

The make process will compile the classes and prepare the bundle for installation. The “GNUmakefile” includes a “config.make” file that specifies the paths and directives relating to the GNUSTEP environment. If you have built OpenGroupware from source you can simply steal a “config.make” file out of the build tree, otherwise adjust the example “config.make” provided to match your environment. The makefle itself specifies the files to compile, in the traditional role of a makefile, but the GNU makefiles also specify the bundle configuration. With a properly constructed GNU makefile simply executing “make” will build the bundle which is then ready to install1. The resulting bundle will be “zAddition.zsp” which is a combination o the “BUNDLE_NAME” and “BUNDLE_EXTENSION” parameters. Once the bundles has been named by setting the “BUNDLE_NAME” the source files, principle class, and resource files are specified by the “zAddition_OBJC_FILE”, “zAddition_PRINCIPLE_CLASS”, and “zAddition_RESOURCE_FILES” respectively.

Oct 20 03:58:08 ogo-zidestore-1.5 [12622]: |ogo-zidestore-1| register ZideStore product: zAddition.zsp

After compiling out bundle and placing it in the ZideStore bundle path simply restart ZideStore and the new bundle should be discovered and loaded. If it is successful a message like the following will appear in the log:

1
2
3
4
#!/usr/bin/env python 
import xmlrpclib 
server = xmlrpclib.Server('http://adam:fred@localhost/zidestore/so/adam/Contacts/') 
print(server.demo.add(4, 2))

If the bundle is loaded invoking the method via any language which supports XML-RPC should be trivial. The Python script like the one in “Text 16: zAddition product.plist“ can be used to ask ZideStore to sum four and two and to output the result to the display.

When the method is invoked the passing of the parameters to the object can be seen in the ZideStore error log due to the “logWithFormat” statements in the “SetFred” and “setGeorge” setters.

Oct 22 01:58:38 ogo-zidestore-1.5 [12931]: >zAddition> setFred assigned with 4 
Oct 22 01:58:38 ogo-zidestore-1.5 [12931]: >zAddition> setGeorge assigned with 2

The zAddition.h:

/* 
  Copyright (C) 2006-2007 Whitemice Consulting 
*/

#ifndef __zAddition_H__ 
#define __zAddition_H__

#include <Foundation/Foundation.h> 
#include <LSFoundation/LSCommandContext.h> 
#include <NGObjWeb/NSException+HTTP.h> 
#include <NGObjWeb/WODirectAction.h> 
#include "zAddition.h"

@interface zAddition : WODirectAction 
{ 
  id                   arg1, arg2; 
  LSCommandContext    *ctx; 
}

/* accessors */

- (void)setFred:(id)_arg; 
- (void)setGeorge:(id)_arg; 
- (id)arg1; 
- (id)arg2;

/* methods */

- (id)defaultAction; 
- (id)addItAction;

@end /* zAddtion */

#endif /* __zAddition_H__ */

The zAddition.m:

/* 
  Copyright (C) 2006-2007 Whitemice Consulting 
*/

#include "zAddition.h"

@implementation zAddition

-(id)init 
{ 
  self = [super init]; 
  [self logWithFormat:@"zAddition object initialized"]; 
  return self; 
}

- (void)dealloc 
{ 
  [self->arg1 release]; 
  [self->arg2 release]; 
  [super dealloc]; 
}

/* accessors */

- (void)setFred:(id)_arg 
{ 
  [self logWithFormat:@"setFred assigned with %@", _arg]; 
  ASSIGN(self->arg1, _arg); 
}

- (id)arg1 
{ 
  return self->arg1; 
}

- (void)setGeorge:(id)_arg 
{ 
  [self logWithFormat:@"setGeorge assigned with %@", _arg]; 
  ASSIGN(self->arg2, _arg); 
}

- (id)arg2 
{ 
  return self->arg2; 
}

- (id)defaultAction 
{ 
  return nil; 
}

- (id)addItAction 
{ 
  if (([arg1 isKindOfClass:[NSNumber class]]) && 
      ([arg2 isKindOfClass:[NSNumber class]])) 
    return [NSNumber numberWithInt:([arg1 intValue] + [arg2 intValue])]; 
  return [NSException exceptionWithHTTPStatus:500 
            reason:@"Cannot add things that aren't numbers."]; 
}

@end /* zAddition */

Interacting with ZideStore via “curl”

Curl is a simple command line tool provided with most UNIX and LINUX distributions used to interact with HTTP and FTP servers. Curl can be used as a simple ZideStore client.

Requesting a WebDAV collection

The “-X” parameter of curl is used to set the request method; if no method is specified curl defaults to GET. The “-X” parameter and the “--request” parameter are interchangable. If multiple request methods are specified the last value will be used. In WebDAV a collection is enumerated using a “BPROPFIND” request. For example, the Overview calendar for the user “adam” using the following command:

curl -d "<xml>" -H "Content-Type: text/xml" -u adam:******* -X PROPFIND \
  http://{serverName}/zidestore/dav/adam/Overview/

The response to the BPROPFIND request should be XML. Within the outer “DAV:multistatus” elements are a series of “DAV:response” elements. Each “DAV:response” element contains a “DAV:href” element – the content of the “DAV:href” element is the URL of the item within the collection. This URL can be retrieved with a simple GET request:

curl -u adam:******** http://gourd-amber/zidestore/dav/adam/Overview/10444446.ics

<?xml version="1.0" encoding="utf-8"?> 
<D:multistatus xmlns:D="DAV:"> 
  <D:response> 
    <D:href>/zidestore/dav/adam/Overview/10444446.ics</D:href> 
    <D:propstat> 
      <D:status>HTTP/1.1 200 OK</D:status> 
      <D:prop></D:prop> 
    </D:propstat> 
  </D:response> 
  .... 
</D:multistatus>

This request should produce a vCalendar representations of the appointment. By enumerating the appropriate collection and requesting the objects a client application or script can interrogate the contents of the groupware server. Performing a GET request with curl is equivalent to performing a “get” or “cat” command from within the cadaver WebDAV client.

Posting an object

In order to put an event or contact to the ZideStore service you need to perform a PUT operation. With curl this is simply an upload file operation; performed by providing the eponymously named “--upload-file” option with the name of the file containing the object to upload to the server. This file must be a value vCalendar (.vcf) if uploading to a calendar collection or a value vCard if uploading to a Contact collection. Team folders are read-only.

curl -u adam:***** --upload-file test.vcf http://{serverName}/zidestore/dav/adam/Calendar/

Remember that in order for ZideStore to identify appointment participants the participants must exist in the database with and “email1” attribute matching that of the “MAILTO” value of the “ATTENDEE” attribute.

When no filename is provided the object is created as a new object. Depending on the structure of the vCard or vCalendar file some keys may be lost. Lost keys will be noted in the ZideStore error log with a message like “Note: loosing keys: creator”.

Deleting An Object

Performing A CalDAV REPORT

By specifying a request method of “REPORT” (via the -X parameter) curl can be used to make CalDAV requests to ZideStore. This allows both “calendar-query” and “calendar-multiget” operations to be tested. The body of the request for any REPORT operation must be valid XML and declared as either “application/xml” or “text/xml” via a “Content-Type” header1. CalDAV REPORT operations are only supported within the context of a collection of events, such as either a “/Calendar” or “/Overview” folder.

curl -d '<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV"><D:p</D:prop><C:filter><C:comp-filter name="VCALENDAR"><C:comp-filter name="VEVENT"/></C:comp-filter></C:filter></C:calendar-query>' -H "Content-Type: application/xml" -u adam:fred123 -X REPORT http://localhost/zidestore/dav/adam/Overview/

curl -d '<C:calendar-multiget xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">ag/><D:getcontenttype/><C:calendar-data/></D:prop><D:href>/zidestore/dav/adam/Overview/12767410.ics</D:href><D:href>/zidestore/dav/adam/Overview/12768751.ics</D:href></C:calendar-multiget>' -H "Content-Type: application/xml" -u adam:fred123 -X REPORT http://localhost/zidestore/dav/adam/Overview/