mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-03-03 22:26:24 +00:00
merge of '7659385c293a64fbec59fda5d5bf34bcd7b51d96'
and 'f8b29d68c36441f92d7ffe8056a95fd25a2fd409' Monotone-Parent: 7659385c293a64fbec59fda5d5bf34bcd7b51d96 Monotone-Parent: f8b29d68c36441f92d7ffe8056a95fd25a2fd409 Monotone-Revision: 7822887b219614cd66e9d1e19da4799f884657bf Monotone-Author: dgehl@inverse.ca Monotone-Date: 2007-10-18T01:44:28 Monotone-Branch: ca.inverse.sogo
This commit is contained in:
445
ChangeLog
445
ChangeLog
@@ -1,7 +1,446 @@
|
||||
2007-10-17 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/MailerUI/UIxMailActions.m ([UIxMailActions -copyAction]):
|
||||
implemented new web method.
|
||||
|
||||
* SoObjects/Mailer/SOGoMailObject.m ([SOGoMailObject
|
||||
-copyToFolderNamed:folderNameinContext:]): new method with the
|
||||
code cut/pasted from -moveToFolderNamed:inContext:.
|
||||
([SOGoMailObject -moveToFolderNamed:folderNameinContext:]):
|
||||
modified to use the code from -copyToFolderNamed:inContext:, which
|
||||
is common between the two actions.
|
||||
|
||||
* SoObjects/Mailer/SOGoMailAccount.m ([SOGoMailAccount -draftsFolderNameInContext:_ctx])
|
||||
([SOGoMailAccount -sentFolderNameInContext:])
|
||||
([SOGoMailAccount -trashFolderNameInContext:]): modified to take
|
||||
the user settings into account.
|
||||
|
||||
* UI/MailerUI/UIxMailFolderActions.m ([UIxMailFolderActions -setAsDraftsFolderAction])
|
||||
([UIxMailFolderActions -setAsSentFolderAction])
|
||||
([UIxMailFolderActions -setAsTrashFolderAction]): new web methods
|
||||
that change the purpose of the active folder to "Sent", "Drafts"
|
||||
or "Trash".
|
||||
|
||||
* UI/SOGoUI/SOGoACLAdvisory.m ([SOGoACLAdvisory -subject]):
|
||||
returns the subject as quoted-printable.
|
||||
|
||||
* UI/SOGoUI/SOGoACLAdvisory.[hm]: added two intermediary classes:
|
||||
SOGoACLAdditionAdvisory and SOGoACLRemovalAdvisory implementing
|
||||
the "aclMethod" method for the subsequent language-dependent
|
||||
subclasses.
|
||||
|
||||
* UI/SOGoUI/SOGoFolderAdvisory.m ([SOGoFolderAdvisory -subject]):
|
||||
returns the subject as quoted-printable.
|
||||
|
||||
* UI/Scheduler/UIxAppointmentEditor.m ([UIxAppointmentEditor
|
||||
-dealloc]): release item, aptStartDate and aptEndDate.
|
||||
|
||||
2007-10-16 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/Mailer/SOGoMailFolder.m ([SOGoMailFolder
|
||||
-initWithName:newNameinContainer:newContainer]): the owner of a
|
||||
shared folder is set to "nobody" by default.
|
||||
|
||||
* UI/Common/UIxAclEditor.m ([UIxAclEditor -hasOwner]): new method
|
||||
that returns whether the object has an owner or not.
|
||||
|
||||
2007-10-15 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/SOGo/SOGoFolder.m ([SOGoFolder -ocsFolder]): create
|
||||
the folder even if the current user is not its owner.
|
||||
|
||||
2007-10-10 Ludovic Marcotte <ludovic@inverse.ca>
|
||||
|
||||
* We now send advisory emails when folders
|
||||
are created / deleted.
|
||||
|
||||
* Fixed the sending of advisory emails upon
|
||||
ACL changes on folders.
|
||||
|
||||
2007-10-10 Ludovic Marcotte <ludovic@inverse.ca>
|
||||
|
||||
* UI/Scheduler/UIxComponentEditor.m
|
||||
Implemented event/task priority support.
|
||||
|
||||
* SoObjects/Contacts/SOGoContactGCSFolder.m
|
||||
Added CardDAV support.
|
||||
|
||||
* SoObjects/SOGo/LDAPUserManager.m and SOGoUser.m
|
||||
Implemented From: based on LDAP results based on
|
||||
the MailFieldNames attribute (an array) specified
|
||||
in every LDAP-based authentication sources.
|
||||
|
||||
* UI/MailPartViewers/UIxMailPartTextViewer.m and
|
||||
UI/WebServerResources/MailerUI.css
|
||||
We avoid replacing "\r\n" and "\n" with <br /> and
|
||||
rather use CSS capabilities for proper formatting.
|
||||
This is _WAY_ faster on very large mails.
|
||||
|
||||
2007-10-10 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* UI/Scheduler/UIxComponentEditor.m
|
||||
([UIxComponentEditor -componentCalendar]): returns the calendar
|
||||
object of the current event.
|
||||
|
||||
2007-10-05 Ludovic Marcotte <ludovic@inverse.ca>
|
||||
|
||||
* UI/WebServerResources/MailerUI.js
|
||||
We check if at least one message is selected
|
||||
before performing a Reply/Reply All/Forward
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
and others - implemented support for recurring
|
||||
events (with some known limitations right now,
|
||||
all soon to be fixed).
|
||||
|
||||
2007-10-04 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* Main/SOGo.m ([SOGo -isUserName:_keyinContext:_ctx]): removed
|
||||
the constraint that a username can't start with a digit.
|
||||
|
||||
2007-10-02 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* Moved SOPE/sope-gdl1/GDLContentStore from the default trunk
|
||||
repository to Inverse's branch.
|
||||
|
||||
2007-09-28 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* SoObjects/Mailer/SOGoDraftObject.m
|
||||
([SOGoDraftObject -isValidAttachmentName:_name]): removed
|
||||
constraint on space in file name.
|
||||
([SOGoDraftObject -saveAttachment:_attachwithMetadata:metadata]):
|
||||
now removes from file name all characters preceding a backslash.
|
||||
This happens with IE7 because the complete attachment file path
|
||||
is sent.
|
||||
|
||||
2007-09-25 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* SoObjects/Appointments/SOGoAptMailNotification.m
|
||||
([SOGoAptMailNotification -appointmentURL]): set personal as the
|
||||
default calendar where to add the event.
|
||||
|
||||
* UI/MainUI/SOGoUserHomePage.m ([SOGoUserHomePage +initialize]):
|
||||
activate the SOGoUIxDefaultModule user defaults.
|
||||
|
||||
2007-09-21 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* UI/SOGoUI/UIxComponent.m
|
||||
([UIxComponent -shortUserNameForDisplay]): returns the string
|
||||
"wrongusernamepassword" when authentication failed.
|
||||
|
||||
2007-09-17 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/MailPartViewers/UIxMailPartICalViewer.m
|
||||
([UIxMailPartICalViewer -calendarFolder]): returns the "personal"
|
||||
entry of the Calendars parent folder.
|
||||
|
||||
* UI/MailerUI/UIxMailListView.m ([UIxMailListView
|
||||
-messageSubject]): new accessor method to work-around a problem
|
||||
within SOPE where a subject could be returned as an NSData.
|
||||
|
||||
* SoObjects/SOGo/SOGoParentFolder.m ([SOGoParentFolder
|
||||
-appendPersonalSources]): make sure the value of the "c_path4" of
|
||||
the returned rows are not NSNull, otherwise, discard them.
|
||||
|
||||
2007-09-16 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/Contacts/SOGoContactGCSFolder.m ([SOGoContactGCSFolder
|
||||
-compare:otherFolder]): new overriden method that compares two
|
||||
contact foldes based on their class and then transfer the control
|
||||
to the super method in SOGoFolder.
|
||||
|
||||
* SoObjects/Contacts/SOGoContactLDAPFolder.m
|
||||
([SOGoContactLDAPFolder -compare:otherFolder]): new method that
|
||||
compare two contact folders based on their class and then their
|
||||
display name.
|
||||
|
||||
* SoObjects/SOGo/SOGoFolder.m ([SOGoFolder -compare:otherFolder]):
|
||||
new method for sorting folders. The folders are compared based on
|
||||
their ownership, whether they are a main folder and finally
|
||||
depending on their display name.
|
||||
|
||||
* SoObjects/SOGo/SOGoObject.m ([SOGoObject
|
||||
-pathArrayToSOGoObject]): do not reorder the paths if the third
|
||||
element is an instance of NSNull.
|
||||
|
||||
* SoObjects/SOGo/SOGoParentFolder.m ([SOGoParentFolder
|
||||
-subFolders]): returns a sorted array using the "compare:"
|
||||
selector.
|
||||
|
||||
2007-09-14 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/Scheduler/UIxCalendarSelector.m ([UIxCalendarSelector
|
||||
-calendars]): also returns the owner of the listed folders.
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
([-deleteEntriesWithIds:ids]): moved method into SOGoFolder.
|
||||
|
||||
* UI/Scheduler/UIxCalMainView.m ([-batchDeleteAction]): moved
|
||||
method into UIxFolderActions.
|
||||
|
||||
* SoObjects/Appointments/SOGoFreeBusyObject.m ([SOGoFreeBusyObject
|
||||
-fetchFreeBusyInfosFrom:_startDateto:_endDate]): fetch the
|
||||
freebusy info from the "personal" calendar.
|
||||
|
||||
* UI/Common/UIxParentFolderActions.m ([UIxParentFolderActions
|
||||
-createFolderAction]): new standardized method for requesting
|
||||
folder creations among gcs-based modules.
|
||||
|
||||
* UI/Common/UIxParentFolderActions.[hm]: new action class module.
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentFolders.m: new class
|
||||
module, equivalent to the SOGoParentFolder's child
|
||||
SOGoContactFolders, but for calendars.
|
||||
|
||||
* SoObjects/SOGo/SOGoObject.m ([SOGoObject -labelForKey:key]): new
|
||||
method that returns translated strings for controller bundles
|
||||
(same as what UIxComponent does for view bundles).
|
||||
([SOGoObject -pathArrayToSOGoObject]): new method that returns
|
||||
the real path to a subscribed folder (if subscribed).
|
||||
([SOGoObject +globallyUniqueObjectId]): move method from SOGoFolder.
|
||||
([SOGoObject -globallyUniqueObjectId]): new instance method
|
||||
calling its class equivalent.
|
||||
|
||||
2007-09-12 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/MainUI/SOGoRootPage.m ([SOGoRootPage -defaultAction]): test
|
||||
whether the user is logged in and if so, redirect to his/her
|
||||
homepage.
|
||||
([SOGoRootPage -appendToResponse:inContext:]): removed useless
|
||||
method.
|
||||
|
||||
2007-09-11 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/SOGo/SOGoFolder.m ([SOGoFolder
|
||||
+folderWithName:aNameandDisplayName:aDisplayNameinContainer:aContainer]):
|
||||
new method.
|
||||
([SOGoFolder -displayName]): new method.
|
||||
([SOGoFolder -delete]): accept to proceed only if nameInContainer
|
||||
!= "personal".
|
||||
|
||||
* SoObjects/Contacts/SOGoContactLDAPFolder.m
|
||||
([SOGoContactLDAPFolder
|
||||
+folderWithName:aNameandDisplayName:aDisplayNameinContainer:aContainer]):
|
||||
renamed from "contactFolderWithName..." for compatibility with SOGoFolder.
|
||||
|
||||
* SoObjects/Contacts/SOGoContactGCSFolder.m ([SOGoContactGCSFolder
|
||||
+contactFolderWithName:aNameandDisplayName:aDisplayNameinContainer:aContainer]):
|
||||
removed method, reimplemented in SOGoFolder.
|
||||
([SOGoContactGCSFolder -displayName]): removed method,
|
||||
reimplemented in SOGoFolder.
|
||||
([-delete]): removed method, modified in SOGoFolder.
|
||||
|
||||
* SoObjects/Contacts/SOGoContactFolders.[hm]: modified class to be
|
||||
a subclass of SOGoParentFolder.
|
||||
|
||||
* SoObjects/SOGo/SOGoParentFolder.[hm]: new class module derived
|
||||
from SOGoContactFolders and modified to be more content-independent.
|
||||
|
||||
* UI/MailerUI/UIxMailActions.m ([UIxMailActions -markMessageUnreadAction])
|
||||
([UIxMailActions -markMessageReadAction]): new methods moved from
|
||||
UIxMailListView and adapted to invoke the client object directly,
|
||||
since the previous versions had to to a lookup from the parent
|
||||
SOGoMailFolder.
|
||||
|
||||
* UI/MailerUI/UIxMailListView.m ([-markMessageUnreadAction]): move
|
||||
method into UIxMailActions.
|
||||
([-markMessageReadAction]): same as above.
|
||||
([-viewAction]): removed useless method.
|
||||
([-javaScriptOK]): removed useless method.
|
||||
([-isJavaScriptRequest]): removed useless method.
|
||||
([-lookupActiveMessage]): removed useless method.
|
||||
|
||||
* UI/Common/WODirectAction+SOGo.m ([WODirectAction
|
||||
-responseWithStatus:status]): new method that returns a WOResponse
|
||||
initialized with the specified status code.
|
||||
([WODirectAction -responseWith204]): new method that invokes the
|
||||
above one with "204" as parameter.
|
||||
([WODirectAction -redirectToLocation:newLocation]): rewrote method
|
||||
to make use of -responseWithStatus:.
|
||||
|
||||
* UI/SOGoUI/UIxComponent.m ([UIxComponent -responseWith204]): new
|
||||
method that returns a WOResponse initialized with the 204 status
|
||||
code.
|
||||
|
||||
* UI/MailerUI/UIxMailListView.m ([UIxMailListView -sortedUIDs]):
|
||||
always use a "not deleted" search qualifier along with the user
|
||||
qualifier (if present).
|
||||
|
||||
2007-09-10 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/Contacts/UIxContactFoldersView.m ([UIxContactFoldersView
|
||||
-contactSearchAction]): only return the records which have an
|
||||
email set.
|
||||
|
||||
* SoObjects/Mailer/SOGoMailObject.m ([SOGoMailObject
|
||||
-trashInContext:_ctx]): no longer expunge the mailbox after
|
||||
marking a message deleted.
|
||||
([SOGoMailObject -moveToFolderNamed:folderNameinContext:]): same
|
||||
as above.
|
||||
|
||||
* UI/MailerUI/UIxMailView.m ([-deleteAction]): removed method.
|
||||
([-trashAction]): moved method into UIxMailActions.
|
||||
([-moveAction]): moved method into UIxMailActions.
|
||||
|
||||
2007-09-07 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/MailPartViewers/UIxMailPartHTMLViewer.m
|
||||
([_UIxHTMLMailContentHandler
|
||||
-endElement:_localNamenamespace:_nsrawName:_rawName]): remove HTML
|
||||
comments from the CSS code, do not add the CSS code to the body
|
||||
content and remove references of body from the CSS declarations.
|
||||
([UIxMailPartHTMLViewer -cssContent]): new accessor method.
|
||||
([UIxMailPartHTMLViewer -flatContentAsString]): separated code
|
||||
common with cssContent in a different method and invoke it only
|
||||
once.
|
||||
|
||||
* UI/MainUI/SOGoRootPage.[hm]: made a subclass of UIxComponent
|
||||
instead of UIxPageFrame.
|
||||
|
||||
2007-09-06 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/MainUI/SOGoRootPage.m ([-defaultAction]): commented out.
|
||||
([-appendToResponse:responseinContext:ctx]): commented out.
|
||||
([SOGoRootPage -connectURL]): new accessor that returns the full
|
||||
url the the "connect" method.
|
||||
([-connectAction]): rewrote method to return a properly formatted
|
||||
auth. cookie based on the username and password passed as
|
||||
parameter.
|
||||
|
||||
* UI/MainUI/SOGoUserHomePage.m ([SOGoUserHomePage -logoffAction]):
|
||||
set the value of the cookie to "discard" and set its expiration
|
||||
date to yesterday.
|
||||
|
||||
* SoObjects/SOGo/SOGoWebAuthenticator.m ([SOGoWebAuthenticator
|
||||
-preprocessCredentialsInContext:context]): consider the user
|
||||
anonymous if the cookie value is "discard".
|
||||
([SOGoWebAuthenticator
|
||||
-setupAuthFailResponse:responsewithReason:reasoninContext:context]):
|
||||
set the expiration date of the cookie to yesterday.
|
||||
|
||||
* UI/SOGoUI/UIxComponent.m ([UIxComponent -applicationPath]):
|
||||
returns the path to the application if the clientObject is not a
|
||||
SOGoObject.
|
||||
|
||||
* SoObjects/SOGo/SOGoUserFolder.m ([SOGoUserFolder +initialize]):
|
||||
moved the requirement of authentication from the SOGo application
|
||||
class to here.
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentObject.m
|
||||
([SOGoAppointmentObject -saveContentString:_iCalbaseSequence:_v]):
|
||||
check whether the new appointment object is still relevant before
|
||||
sending a notification.
|
||||
|
||||
2007-09-05 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/SOGo/SOGoWebAuthenticator.m ([SOGoWebAuthenticator
|
||||
-setupAuthFailResponse:responsewithReason:reasoninContext:context]):
|
||||
render the login page through the SoDefaultRenderer.
|
||||
|
||||
* UI/MainUI/SOGoRootPage.m ([SOGoRootPage
|
||||
-isPublicInContext:localContext]): new overriden method that
|
||||
returns YES.
|
||||
|
||||
* UI/Scheduler/UIxCalendarSelector.m ([UIxCalendarSelector
|
||||
-currentCalendarLogin]): replace css-unsafe characters with _.
|
||||
|
||||
* UI/SOGoUI/UIxComponent.m ([UIxComponent
|
||||
-shortUserNameForDisplay]): simplified method.
|
||||
([-user]): removed method since [context activeUser] is as useful.
|
||||
|
||||
2007-09-04 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/MainUI/SOGoUserHomePage.m ([SOGoUserHomePage -logoffAction]):
|
||||
set the cookie path to "/".
|
||||
|
||||
* Main/SOGo.m ([SOGo -authenticatorInContext:_ctx]): choose the
|
||||
authenticator based on the request handler key. "dav" returns the
|
||||
SOGoDAVAuthenticator, anything else returns the Web authenticator.
|
||||
|
||||
* SoObjects/SOGo/SOGoDAVAuthenticator.m: renamed module from
|
||||
"SOGoAuthenticator".
|
||||
|
||||
* SoObjects/SOGo/SOGoWebAuthenticator.m: new class module
|
||||
implementing a subclass of SoCookieAuthenticator, designed for
|
||||
web-based cookie authentication of users.m
|
||||
|
||||
* UI/MainUI/SOGoUserHomePage.m ([SOGoUserHomePage -logoffAction]):
|
||||
new method that resets the authentification cookie.
|
||||
|
||||
2007-08-29 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/SOGo/LDAPSource.m ([LDAPSource
|
||||
-checkLogin:loginToCheckandPassword:passwordToCheck]): initialize
|
||||
didBind to NO to make sure no false authentication is returned if
|
||||
the bind operation is not executed.
|
||||
|
||||
2007-08-28 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/Mailer/SOGoDraftObject.m: added support for the
|
||||
"In-Reply-To" header field when replying.
|
||||
|
||||
* UI/MainUI/SOGoUserHomePage.m: add the "c_" prefix to the quick
|
||||
table field names that are queried.
|
||||
|
||||
* SoObjects/Appointments/SOGoFreeBusyObject.m ([SOGoFreeBusyObject
|
||||
-iCalStringForFreeBusyInfos:_infosfrom:_startDateto:_endDate]):
|
||||
add the "c_" prefix to the quick table field names that are
|
||||
queried.
|
||||
|
||||
2007-08-24 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
([SOGoAppointmentFolder -lookupCalendarFolderForUID:uid]): add
|
||||
"personal" to the ocs path of the appointment folder.
|
||||
|
||||
* UI/MailPartViewers/UIxMailPartViewer.m ([UIxMailPartViewer
|
||||
-flatContentAsString]): use latin1 when the encoding is not
|
||||
specified, and to reencode data chunk which were not correctly
|
||||
decoded with the original charset.
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentFolder.m ([SOGoAppointmentFolder -aclUsersForObjectAtPath:objectPathArray])
|
||||
([SOGoAppointmentFolder -aclsForUser:uidforObjectAtPath:objectPathArray])
|
||||
([SOGoAppointmentFolder -setRoles:rolesforUser:uidforObjectAtPath:objectPathArray])
|
||||
([SOGoAppointmentFolder
|
||||
-removeAclsForUsers:usersforObjectAtPath:objectPathArray]):
|
||||
override those methods to use the "personal" additional directory.
|
||||
|
||||
* SoObjects/SOGo/SOGoUserFolder.m ([-ocsPrivateCalendarPath]):
|
||||
append "/personal" to the calendar path to simulate a single
|
||||
calendar in a choice of many.
|
||||
|
||||
* SoObjects/Mailer/SOGoMailFolder.m ([SOGoMailFolder
|
||||
-lookupName:_keyinContext:acquire:_acquire]): moved the lookup
|
||||
methods back here. Moved the folder existence check here, and do
|
||||
it on self only when the lookup happens for a non-folder object.
|
||||
This permits to accept entries for folders with parents who
|
||||
don't really exist.
|
||||
|
||||
2007-08-23 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/Scheduler/UIxTaskEditor.m ([UIxTaskEditor
|
||||
-shouldTakeValuesFromRequest:requestinContext:context]): same as
|
||||
below.
|
||||
|
||||
* UI/Scheduler/UIxAppointmentEditor.m ([UIxAppointmentEditor
|
||||
-shouldTakeValuesFromRequest:requestinContext:context]):
|
||||
redesigned method since any method called can be received from a
|
||||
POST or a GET. Instead we check the method call itself and we
|
||||
accept only if it has the "save" prefix.
|
||||
|
||||
* SoObjects/Appointments/SOGoAptMailNotification.m
|
||||
([SOGoAptMailNotification -getSubject]): returns the subject an a
|
||||
quoted-printable encoded string, if needed.
|
||||
|
||||
* SoObjects/Mailer/SOGoDraftObject.m ([NSString
|
||||
-asQPSubjectString:encoding]): moved method into
|
||||
NSString+Utilities.m.
|
||||
|
||||
2007-08-22 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/PreferencesUI/UIxPreferences.m ([UIxPreferences -messageForwardingList])
|
||||
([UIxPreferences -itemMessageForwardingText])
|
||||
* UI/PreferencesUI/UIxPreferences.m ([UIxPreferences
|
||||
-messageForwardingList])
|
||||
([UIxPreferences -itemMessageForwardingText])D
|
||||
([UIxPreferences -userMessageForwarding])
|
||||
([UIxPreferences -setUserMessageForwarding:newMessageForwarding]):
|
||||
new template methods for manage the user preference regarding
|
||||
@@ -3289,7 +3728,7 @@
|
||||
2006-09-13 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/Contacts/UIxContactView.m: added many wrapper methods to
|
||||
display blocks of data à la Thunderbird Addressbook. If data is
|
||||
display blocks of data à la Thunderbird Addressbook. If data is
|
||||
available, those wrappers (around the NGVCard methods) will
|
||||
enclose the results in a proper HTML output with the correct label
|
||||
(if present), otherwise it will return an empty string.
|
||||
|
||||
@@ -5,6 +5,7 @@ include $(GNUSTEP_MAKEFILES)/common.make
|
||||
|
||||
SUBPROJECTS = \
|
||||
SOPE/NGCards \
|
||||
SOPE/sope-gdl1/GDLContentStore \
|
||||
OGoContentStore \
|
||||
SoObjects \
|
||||
Main \
|
||||
|
||||
@@ -5,6 +5,9 @@ include $(GNUSTEP_MAKEFILES)/common.make
|
||||
include ../Version
|
||||
include ./Version
|
||||
|
||||
ADDITIONAL_INCLUDE_DIRS += -I../SOPE/sope-gdl1/
|
||||
ADDITIONAL_LIB_DIRS += -L../SOPE/sope-gdl1/GDLContentStore/obj/
|
||||
|
||||
SOGOD = sogod-$(MAJOR_VERSION).$(MINOR_VERSION)
|
||||
TOOL_NAME = $(SOGOD)
|
||||
|
||||
|
||||
23
Main/SOGo.m
23
Main/SOGo.m
@@ -41,10 +41,11 @@
|
||||
|
||||
#import <WEExtensions/WEResourceManager.h>
|
||||
|
||||
#import <SoObjects/SOGo/SOGoAuthenticator.h>
|
||||
#import <SoObjects/SOGo/SOGoDAVAuthenticator.h>
|
||||
#import <SoObjects/SOGo/SOGoPermissions.h>
|
||||
#import <SoObjects/SOGo/SOGoUserFolder.h>
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
#import <SoObjects/SOGo/SOGoPermissions.h>
|
||||
#import <SoObjects/SOGo/SOGoWebAuthenticator.h>
|
||||
|
||||
#import "build.h"
|
||||
#import "SOGoProductLoader.h"
|
||||
@@ -103,7 +104,7 @@ static BOOL debugObjectAllocation = NO;
|
||||
/* SoClass security declarations */
|
||||
sInfo = [self soClassSecurityInfo];
|
||||
/* require View permission to access the root (bound to authenticated ...) */
|
||||
[sInfo declareObjectProtected: SoPerm_View];
|
||||
// [sInfo declareObjectProtected: SoPerm_View];
|
||||
|
||||
/* to allow public access to all contained objects (subkeys) */
|
||||
[sInfo setDefaultAccess: @"allow"];
|
||||
@@ -231,9 +232,18 @@ static BOOL debugObjectAllocation = NO;
|
||||
|
||||
/* authenticator */
|
||||
|
||||
- (id) authenticatorInContext: (id) _ctx
|
||||
- (id) authenticatorInContext: (WOContext *) context
|
||||
{
|
||||
return [$(@"SOGoAuthenticator") sharedSOGoAuthenticator];
|
||||
id authenticator;
|
||||
NSString *key;
|
||||
|
||||
key = [[context request] requestHandlerKey];
|
||||
if ([key isEqualToString: @"dav"])
|
||||
authenticator = [SOGoDAVAuthenticator sharedSOGoDAVAuthenticator];
|
||||
else
|
||||
authenticator = [SOGoWebAuthenticator sharedSOGoWebAuthenticator];
|
||||
|
||||
return authenticator;
|
||||
}
|
||||
|
||||
/* name lookup */
|
||||
@@ -244,9 +254,6 @@ static BOOL debugObjectAllocation = NO;
|
||||
if ([_key length] < 1)
|
||||
return NO;
|
||||
|
||||
if (isdigit([_key characterAtIndex:0]))
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
25
NEWS
25
NEWS
@@ -1,4 +1,27 @@
|
||||
0.9.0-20070xxx
|
||||
0.9.0-200710XX
|
||||
--------------
|
||||
- the user can now configure his folders as drafts, trash or sent folder;
|
||||
- added the ability the move and copy message across mail folders;
|
||||
|
||||
0.9.0-200709XX
|
||||
--------------
|
||||
- implemented cookie-based identification in the web interface;
|
||||
- fixed a bug where a false positive happening whenever a wrong user login was
|
||||
given during an indirect bind;
|
||||
- remove the constraint that a username can't begin with a digit;
|
||||
- deleting a message no longer expunges its parent folder;
|
||||
- implemented support for multiple calendars;
|
||||
- it is now possible to rename folders;
|
||||
- fixed search in message content;
|
||||
- added tooltips for toolbar buttons (English and French);
|
||||
- added checkmarks in live search options popup menus;
|
||||
- added browser detection with recommanded alternatives;
|
||||
- support for resizable columns in tables;
|
||||
- improved support for multiple selection in tables and lists;
|
||||
- improved IE7 and Safari support: attendees selector, email file attachments;
|
||||
- countless bugfixes;
|
||||
|
||||
0.9.0-20070824
|
||||
--------------
|
||||
- added the ability to choose the default module from the
|
||||
application settings: "Calendars", "Contacts" or "Mail";
|
||||
|
||||
@@ -13,6 +13,9 @@ libOGoContentStore_HEADER_FILES_DIR = .
|
||||
libOGoContentStore_HEADER_FILES_INSTALL_DIR = /OGoContentStore
|
||||
# no headers, commented out: FHS_HEADER_DIRS = OGoContentStore
|
||||
|
||||
ADDITIONAL_INCLUDE_DIRS += -I../SOPE/sope-gdl1/
|
||||
ADDITIONAL_LIB_DIRS += -L../SOPE/sope-gdl1/GDLContentStore/obj/
|
||||
|
||||
libOGoContentStore_OBJC_FILES += \
|
||||
iCalEntityObject+OCS.m \
|
||||
iCalRepeatableEntityObject+OCS.m \
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
/* state */
|
||||
|
||||
- (void)resetExceptResult
|
||||
- (void) resetExceptResult
|
||||
{
|
||||
if (content)
|
||||
{
|
||||
@@ -130,7 +130,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)endValueTag
|
||||
- (void) endValueTag
|
||||
{
|
||||
[types removeAllObjects];
|
||||
[args removeAllObjects];
|
||||
@@ -151,14 +151,14 @@
|
||||
currentGroup = nil;
|
||||
}
|
||||
|
||||
- (void)startVCardSet
|
||||
- (void) startVCardSet
|
||||
{
|
||||
currentCardGroup = nil;
|
||||
currentGroup = nil;
|
||||
vcs.isInVCardSet = 1;
|
||||
}
|
||||
|
||||
- (void)endVCardSet
|
||||
- (void) endVCardSet
|
||||
{
|
||||
vcs.isInVCardSet = 0;
|
||||
}
|
||||
|
||||
@@ -262,10 +262,9 @@ static void NGMonthDaySet_fillWithByDayX(NGMonthDaySet *daySet,
|
||||
interval = [self->rrule repeatInterval];
|
||||
until = [self lastInstanceStartDate]; // TODO: maybe replace
|
||||
byMonthDay = [self->rrule byMonthDay];
|
||||
|
||||
|
||||
/* check whether the range to be processed is beyond the 'until' date */
|
||||
|
||||
|
||||
/* check whether the range to be processed is beyond the 'until' date */
|
||||
if (until != nil) {
|
||||
if ([until compare:rStart] == NSOrderedAscending) /* until before start */
|
||||
return nil;
|
||||
@@ -314,7 +313,7 @@ static void NGMonthDaySet_fillWithByDayX(NGMonthDaySet *daySet,
|
||||
continue;
|
||||
|
||||
/* first check whether we are in the interval */
|
||||
|
||||
|
||||
if ((monthIdxInRecurrence % interval) != 0)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -100,9 +100,10 @@ static Class yearlyCalcClass = Nil;
|
||||
rule = [_rRules objectAtIndex:i];
|
||||
if (![rule isKindOfClass:iCalRecurrenceRuleClass])
|
||||
rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:rule];
|
||||
|
||||
|
||||
calc = [self recurrenceCalculatorForRecurrenceRule:rule
|
||||
withFirstInstanceCalendarDateRange:_fir];
|
||||
|
||||
rs = [calc recurrenceRangesWithinCalendarDateRange:_r];
|
||||
[ranges addObjectsFromArray:rs];
|
||||
}
|
||||
@@ -158,6 +159,7 @@ static Class yearlyCalcClass = Nil;
|
||||
unsigned k;
|
||||
|
||||
exDate = [exDates objectAtIndex:i];
|
||||
|
||||
for (k = 0; k < rCount; k++) {
|
||||
unsigned rIdx;
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
2007-08-27 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* VSSaxDriver.m ([VSSaxDriver
|
||||
-parseFromSource:_sourcesystemId:_sysId]): report only tags for
|
||||
which the content is not empty.
|
||||
|
||||
2007-05-03 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* VSSaxDriver.m ([VSSaxDriver +initialize]): removed the space
|
||||
|
||||
@@ -471,20 +471,17 @@ static VSStringFormatter *stringFormatter = nil;
|
||||
withAttrs: (SaxAttributes *) _attrs
|
||||
andContent: (NSString *) _content
|
||||
{
|
||||
/*
|
||||
This is called for all non-BEGIN|END types.
|
||||
*/
|
||||
VSSaxTag *a;
|
||||
NSString *testContent;
|
||||
|
||||
// _content = [stringFormatter stringByUnescapingRFC2445Text: _content];
|
||||
/* This is called for all non-BEGIN|END types. */
|
||||
testContent = [[_content unescapedFromCard] stringByReplacingString: @";"
|
||||
withString: @""];
|
||||
|
||||
/* check whether type should be reported as an attribute in XML */
|
||||
|
||||
[self _beginTag: _tagName group: _group withAttrs: _attrs];
|
||||
|
||||
if ([_content length] > 0)
|
||||
if ([[testContent stringByTrimmingSpaces] length] > 0)
|
||||
{
|
||||
VSSaxTag *a;
|
||||
|
||||
[self _beginTag: _tagName group: _group withAttrs: _attrs];
|
||||
|
||||
a = [(VSSaxTag *)[VSSaxTag alloc]
|
||||
initWithContentString: [_content unescapedFromCard]];
|
||||
if (a)
|
||||
@@ -492,9 +489,9 @@ static VSStringFormatter *stringFormatter = nil;
|
||||
[elementList addObject: a];
|
||||
[a release];
|
||||
}
|
||||
}
|
||||
|
||||
[self _endTag: _tagName];
|
||||
[self _endTag: _tagName];
|
||||
}
|
||||
}
|
||||
|
||||
/* report events for collected elements */
|
||||
|
||||
482
SOPE/sope-gdl1/GDLContentStore/COPYING
Normal file
482
SOPE/sope-gdl1/GDLContentStore/COPYING
Normal file
@@ -0,0 +1,482 @@
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the library GPL. It is
|
||||
numbered 2 because it goes with version 2 of the ordinary GPL.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Library General Public License, applies to some
|
||||
specially designated Free Software Foundation software, and to any
|
||||
other libraries whose authors decide to use it. You can use it for
|
||||
your libraries, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if
|
||||
you distribute copies of the library, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link a program with the library, you must provide
|
||||
complete object files to the recipients so that they can relink them
|
||||
with the library, after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
Our method of protecting your rights has two steps: (1) copyright
|
||||
the library, and (2) offer you this license which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
Also, for each distributor's protection, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
library. If the library is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original
|
||||
version, so that any problems introduced by others will not reflect on
|
||||
the original authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that companies distributing free
|
||||
software will individually obtain patent licenses, thus in effect
|
||||
transforming the program into proprietary software. To prevent this,
|
||||
we have made it clear that any patent must be licensed for everyone's
|
||||
free use or not licensed at all.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the ordinary
|
||||
GNU General Public License, which was designed for utility programs. This
|
||||
license, the GNU Library General Public License, applies to certain
|
||||
designated libraries. This license is quite different from the ordinary
|
||||
one; be sure to read it in full, and don't assume that anything in it is
|
||||
the same as in the ordinary license.
|
||||
|
||||
The reason we have a separate public license for some libraries is that
|
||||
they blur the distinction we usually make between modifying or adding to a
|
||||
program and simply using it. Linking a program with a library, without
|
||||
changing the library, is in some sense simply using the library, and is
|
||||
analogous to running a utility program or application program. However, in
|
||||
a textual and legal sense, the linked executable is a combined work, a
|
||||
derivative of the original library, and the ordinary General Public License
|
||||
treats it as such.
|
||||
|
||||
Because of this blurred distinction, using the ordinary General
|
||||
Public License for libraries did not effectively promote software
|
||||
sharing, because most developers did not use the libraries. We
|
||||
concluded that weaker conditions might promote sharing better.
|
||||
|
||||
However, unrestricted linking of non-free programs would deprive the
|
||||
users of those programs of all benefit from the free status of the
|
||||
libraries themselves. This Library General Public License is intended to
|
||||
permit developers of non-free programs to use free libraries, while
|
||||
preserving your freedom as a user of such programs to change the free
|
||||
libraries that are incorporated in them. (We have not seen how to achieve
|
||||
this as regards changes in header files, but we have achieved it as regards
|
||||
changes in the actual functions of the Library.) The hope is that this
|
||||
will lead to faster development of free libraries.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, while the latter only
|
||||
works together with the library.
|
||||
|
||||
Note that it is possible for a library to be covered by the ordinary
|
||||
General Public License rather than by this special one.
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library which
|
||||
contains a notice placed by the copyright holder or other authorized
|
||||
party saying it may be distributed under the terms of this Library
|
||||
General Public License (also called "this License"). Each licensee is
|
||||
addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also compile or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
c) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
d) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the source code distributed need not include anything that is normally
|
||||
distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Library General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
4
SOPE/sope-gdl1/GDLContentStore/COPYRIGHT
Normal file
4
SOPE/sope-gdl1/GDLContentStore/COPYRIGHT
Normal file
@@ -0,0 +1,4 @@
|
||||
Copyright (C) 2004 SKYRIX Software AG
|
||||
|
||||
|
||||
Contact: info@skyrix.com
|
||||
329
SOPE/sope-gdl1/GDLContentStore/ChangeLog
Normal file
329
SOPE/sope-gdl1/GDLContentStore/ChangeLog
Normal file
@@ -0,0 +1,329 @@
|
||||
2007-08-29 Wolfgang Sourdeau <WSourdeau@Inverse.CA>
|
||||
|
||||
* EOQualifier+GCS.m: rewrote comparison code, now uses UPPER instead of
|
||||
the PostgreSQL specific ILIKE. Fixes OGo bug #1906 (v4.7.49)
|
||||
|
||||
2007-07-20 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* GCSFolderManager.m: added 'some' rollback after an error (v4.7.48)
|
||||
|
||||
2007-07-20 Wolfgang Sourdeau <WSourdeau@Inverse.CA>
|
||||
|
||||
* GCSFolderManager.m: fixed a bug in last check, DROP TABLE is allowed
|
||||
to fail in the given context (bug #1883) (v4.7.47)
|
||||
|
||||
2007-07-11 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* GCSFolderManager.m: added some error checking, plenty of open ends
|
||||
pending (eg folder creation not wrapped in a transaction) (v4.7.46)
|
||||
|
||||
2007-06-29 Wolfgang Sourdeau <WSourdeau@Inverse.CA>
|
||||
|
||||
* GCSFolderManager.m: fixed folder creation to populate empty path
|
||||
fields with NULLs (OGo bug #1883) (v4.7.45)
|
||||
|
||||
2007-04-25 Wolfgang Sourdeau <WSourdeau@Inverse.CA>
|
||||
|
||||
* GCSFolder.[hm]: added methods to delete ACL records (OGo bug #1866)
|
||||
(v4.7.44)
|
||||
|
||||
2007-04-22 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* GCSChannelManager.m: improved error log (v4.7.43)
|
||||
|
||||
2007-04-17 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* fixed a few GNUstep compilation warnings (v4.7.42)
|
||||
|
||||
2007-03-21 Wolfgang Sourdeau <WSourdeau@Inverse.CA>
|
||||
|
||||
* GCSFolder.[hm], GCSFolderManager.[hm]: added ability to create and
|
||||
delete GCS folders programmatically (OGo bug #1850) (v4.7.41)
|
||||
|
||||
2007-02-12 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* GCSFolder.m: fixed a gnustep-base compilation warning (v4.7.40)
|
||||
|
||||
2007-02-09 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* use -errorWithFormat:, fixed a few logging crashes (incomplete format
|
||||
strings) (v4.5.39)
|
||||
|
||||
2007-02-08 Wolfgang Sourdeau <WSourdeau@Inverse.CA>
|
||||
|
||||
* GCSFolder.m: added a gnustep-base hack to properly format bool
|
||||
numbers for SQL. Base returns YES or NO in -stringValue while
|
||||
libFoundation/NGExt returns 0 or 1 (v4.5.39)
|
||||
|
||||
2007-01-15 Wolfgang Sourdeau <WSourdeau@Inverse.CA>
|
||||
|
||||
* GCSFolder.[hm], GCSFolderManager.m: added support for content table
|
||||
ACLs (v4.5.38)
|
||||
|
||||
2006-08-31 Wolfgang Sourdeau <WSourdeau@Inverse.CA>
|
||||
|
||||
* EOQualifier+GCS.m: added support for OR qualifiers and for case
|
||||
insensitive-like qualifiers on PostgreSQL (v4.5.37)
|
||||
|
||||
2006-07-04 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* use %p for pointer formats, fixed gcc 4.1 warnings (v4.5.36)
|
||||
|
||||
2005-08-16 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* GNUmakefile, GNUmakefile.preamble: added OSX framework compilation
|
||||
(v4.5.35)
|
||||
|
||||
2005-07-23 Sebastian Reitenbach <reitenbach@rapideye.de>
|
||||
|
||||
* GNUmakefile.preamble: added OpenBSD linking flags (v4.5.34)
|
||||
|
||||
2005-07-13 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* GCSFolder.h: added -versionOfContentWithName: method to header file
|
||||
(v4.5.33)
|
||||
|
||||
* GCSFolder.m: return a proper exception if the extractor was unable to
|
||||
create a quickrow for a given content object (v4.5.32)
|
||||
|
||||
* GCSFolder.m: added -writeContent:toName:baseVersion: to support
|
||||
consistent update operations (eg using etags), properly increase
|
||||
content object version on update operations (v4.5.31)
|
||||
|
||||
* GCSFolderManager.m, GCSFolder.m: changed not to use EOF
|
||||
attribute-name 'beautification', eg 'c_name' will stay 'c_name'
|
||||
instead of being transformed into 'cName' (v4.5.30)
|
||||
|
||||
2005-07-11 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* GCSFolderManager.m: added automatic discovery of folder types by
|
||||
scanning for .ocs files (v4.5.29)
|
||||
|
||||
2005-04-25 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* fixed gcc 4.0 warnings (v4.5.28)
|
||||
|
||||
2005-03-21 Helge Hess <helge.hess@skyrix.com>
|
||||
|
||||
* GNUmakefile: added FHS support (v4.5.27)
|
||||
|
||||
2005-03-20 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* moved OGoContentStore as GDLContentStore into sope-gdl1, removed
|
||||
dependencies on NGiCal and removed some SOGo specific things
|
||||
(v4.5.26)
|
||||
|
||||
2005-03-07 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* appointment.ocs: added missing 'partstates' field (v0.9.25)
|
||||
|
||||
2005-03-04 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* v0.9.24
|
||||
|
||||
* ocs_gensql.m: started tool to create SQL CREATE from ocs model file
|
||||
|
||||
* OCSFolderType.m: small change to the factory API, changed to use
|
||||
NGResourceLocator
|
||||
|
||||
2005-03-03 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* OCSFolderManager.m: fixed a bug in subfolder listing (v0.9.23)
|
||||
|
||||
2005-03-01 Marcus Mueller <znek@mulle-kybernetik.com>
|
||||
|
||||
* v0.9.22
|
||||
|
||||
* appointment.ocs: added 'cycleenddate' and 'cycleinfo' to address
|
||||
previous performance issues
|
||||
|
||||
* OCSiCalFieldExtractor.m: set 'cycleenddate' and 'cycleinfo' for
|
||||
recurrent events. Reverted setting of 'enddate' to the previous
|
||||
behaviour since 'cycleenddate' is dedicated to the task now
|
||||
|
||||
* iCalRepeatableEntityObject+OCS.[hm]: new category used by the
|
||||
OCSiCalFieldExtractor to extract cycleInfo in an appropriate format
|
||||
|
||||
* sql/generate-folderinfo-sql-for-users.sh,
|
||||
sql/foldertablecreate-helge-privcal.psql,
|
||||
sql/foldertablecreate-helge-privcal.sqlite,
|
||||
sql/generate-folderinfo-sql-for-users-sqlite.sh: adjusted to new
|
||||
schema
|
||||
|
||||
2005-03-01 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* OCSFolder.m: added support for storing content and quick info in
|
||||
the same table (untested) (v0.9.21)
|
||||
|
||||
2005-02-21 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* v0.9.20
|
||||
|
||||
* OCSFolderManager.m: removed quoting of SQL table and column names
|
||||
(breaks with SQLite and isn't necessary for PG), fixed URL pooling
|
||||
for SQLite
|
||||
|
||||
* NSURL+OCS.m: use tablename for last path component
|
||||
|
||||
2005-02-12 Marcus Mueller <znek@mulle-kybernetik.com>
|
||||
|
||||
* OCSiCalFieldExtractor.m: uses new iCalEvent API to determine correct
|
||||
'enddate' for recurrent events. This is an optimization which can
|
||||
save quite some time for complex rules. (v0.9.19)
|
||||
|
||||
2004-12-17 Marcus Mueller <znek@mulle-kybernetik.com>
|
||||
|
||||
* v0.9.18
|
||||
|
||||
* OCSiCalFieldExtractor.m: extract participants' state
|
||||
|
||||
* sql/generate-folderinfo-sql-for-user.sh, sql/appointment-create.psql,
|
||||
sql/foldertablecreate-helge-privcal.psql: updated with new schema.
|
||||
|
||||
2004-12-15 Marcus Mueller <znek@mulle-kybernetik.com>
|
||||
|
||||
* OCSiCalFieldExtractor.m: partmails + cn's are concatenated by '\n'
|
||||
now - this directly eliminates any ambiguities. Also, instead of
|
||||
using 'email' for partmails and orgmail, the extractor uses the
|
||||
'rfc822Email' value which strips away any preceeding 'mailto:'
|
||||
prefix, compacting the representation and speeding up comparison.
|
||||
Also, "iscycle", "isallday" and "isopaque" are now provided by
|
||||
NGiCal and thus always extracted (v0.9.17)
|
||||
|
||||
2004-12-13 Marcus Mueller <znek@mulle-kybernetik.com>
|
||||
|
||||
* sql/generate-folderinfo-sql-for-user.sh: fixed critical error in
|
||||
Contacts folder_info, type was 'Appointment' but MUST be 'Contact'
|
||||
(v0.9.16)
|
||||
|
||||
2004-12-10 Marcus Mueller <znek@mulle-kybernetik.com>
|
||||
|
||||
* sql: updated all generation scripts to the latest version (v0.9.15)
|
||||
|
||||
2004-12-09 Marcus Mueller <znek@mulle-kybernetik.com>
|
||||
|
||||
* v0.9.14
|
||||
|
||||
* appointment.ocs: added "ispublic", "isopaque", "status" and
|
||||
"orgmail".
|
||||
|
||||
* OCSiCalFieldExtractor.m: updated to extract new fields (see above)
|
||||
|
||||
* sql: updated generate-folderinfo-sql-for-users.sh
|
||||
|
||||
2004-10-19 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* OCSFolder.m: added new method -fetchContentsOfAllFiles method which
|
||||
fetches the contents of all files stored in the folder (required for
|
||||
iCal generation, such bulk fetches should be avoided if possible!)
|
||||
(v0.9.13)
|
||||
|
||||
2004-10-15 Marcus Mueller <znek@mulle-kybernetik.com>
|
||||
|
||||
* OCSStringFormatter.[hm]: minor cleanup (v0.9.12)
|
||||
|
||||
* v0.9.11
|
||||
|
||||
* OCSStringFormatter.[hm]: new class to format strings according to
|
||||
Database requirements (escaping etc.).
|
||||
|
||||
* OCSFolder.m: uses new OCSStringFormatter now.
|
||||
|
||||
2004-09-25 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* fixed compilation on MacOSX (v0.9.10)
|
||||
|
||||
2004-09-10 Helge Hess <helge.hess@skyrix.com>
|
||||
|
||||
* v0.9.9
|
||||
|
||||
* fixed some gcc warnings
|
||||
|
||||
* GNUmakefile.preamble: added pathes to compile against an FHS SOPE
|
||||
|
||||
* OCSiCalFieldExtractor.m: fixed type of sequence iCalEvent field
|
||||
|
||||
2004-09-01 Marcus Mueller <znek@mulle-kybernetik.com>
|
||||
|
||||
* GNUmakefile: install type models into $(GNUSTEP_USER_ROOT) (v0.9.8)
|
||||
|
||||
2004-08-27 Helge Hess <helge.hess@skyrix.com>
|
||||
|
||||
* v0.9.7
|
||||
|
||||
* OCSChannelManager.m: use PostgreSQL as adaptor, not PostgreSQL72
|
||||
|
||||
* OCSFolder.m: added support for doing folder sorting in SQL
|
||||
|
||||
2004-08-26 Helge Hess <helge.hess@skyrix.com>
|
||||
|
||||
* v0.9.6
|
||||
|
||||
* added OCSContactFieldExtractor
|
||||
|
||||
* sql: added sample contact folder create scripts
|
||||
|
||||
* OCSFolderType.m: read extractor class name from type model
|
||||
|
||||
* OCSFolderManager.m: added contact type model per default (v0.9.5)
|
||||
|
||||
2004-08-25 Helge Hess <helge.hess@skyrix.com>
|
||||
|
||||
* GNUmakefile: automatically install OCSTypeModels (v0.9.4)
|
||||
|
||||
2004-08-15 Helge Hess <helge.hess@skyrix.com>
|
||||
|
||||
* OCSFolder.m: added content deletion (v0.9.3)
|
||||
|
||||
* OCSFolder.m: added sanity check to store method (v0.9.2)
|
||||
|
||||
2004-08-14 Helge Hess <helge.hess@skyrix.com>
|
||||
|
||||
* v0.9.1
|
||||
|
||||
* OCSiCalFieldExtractor.m: extract new quick fields: location,
|
||||
partmails, sequence (does not yet handle allday and cycle due to
|
||||
NGiCal restrictions)
|
||||
|
||||
* appointment.ocs, sql/foldertablecreate-helge-privcal.psql,
|
||||
sql/testapt-agenor-helge-privcal.psql, sql/appointment-create.psql:
|
||||
added quick fields: isallday, iscycle, location, partmails, sequence
|
||||
|
||||
* started ocs_recreatequick tool intended for recreating a quick table
|
||||
based on the content table of a folder
|
||||
|
||||
2004-07-20 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* OCSChannelManager.m: fixed a bug in the channel GC which resulted
|
||||
in an exception during the GC NSTimer
|
||||
|
||||
2004-07-16 Helge Hess <helge.hess@skyrix.com>
|
||||
|
||||
* improved error handling in various files
|
||||
|
||||
2004-07-02 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* OCSChannelManager.m: added garbage collector for channel pools
|
||||
|
||||
2004-06-30 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* OCSChannelManager.m: implemented pooling
|
||||
|
||||
* OCSFolder.m: added quick fetches
|
||||
|
||||
* GNUmakefile.preamble: fix link path
|
||||
|
||||
* GNUmakefile (libOGoContentStore_HEADER_FILES_INSTALL_DIR): install
|
||||
headers in OGoContentStore
|
||||
|
||||
* GNUmakefile.preamble (ocs_ls_TOOL_LIBS): added static dependencies
|
||||
for OSX
|
||||
|
||||
2004-06-30 Marcus Mueller <znek@mulle-kybernetik.com>
|
||||
|
||||
* ocs_cat.m, ocs_ls.m, ocs_mkdir.m: fixed for gnustep compile.
|
||||
|
||||
2004-06-29 Helge Hess <helge.hess@opengroupware.org>
|
||||
|
||||
* created ChangeLog
|
||||
|
||||
40
SOPE/sope-gdl1/GDLContentStore/EOAdaptorChannel+GCS.h
Normal file
40
SOPE/sope-gdl1/GDLContentStore/EOAdaptorChannel+GCS.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GDLContentStore_EOAdaptorChannel_GCS_H__
|
||||
#define __GDLContentStore_EOAdaptorChannel_GCS_H__
|
||||
|
||||
#include <GDLAccess/EOAdaptorChannel.h>
|
||||
|
||||
@protocol GCSEOAdaptorChannel
|
||||
|
||||
- (NSException *) createGCSFolderTableWithName: (NSString *) tableName;
|
||||
- (NSException *) createGCSFolderACLTableWithName: (NSString *) tableName;
|
||||
|
||||
@end
|
||||
|
||||
@interface EOAdaptorChannel(GCS)
|
||||
|
||||
- (BOOL)tableExistsWithName:(NSString *)_tableName;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __GDLContentStore_EOAdaptorChannel_GCS_H__ */
|
||||
50
SOPE/sope-gdl1/GDLContentStore/EOAdaptorChannel+GCS.m
Normal file
50
SOPE/sope-gdl1/GDLContentStore/EOAdaptorChannel+GCS.m
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "EOAdaptorChannel+GCS.h"
|
||||
#include "common.h"
|
||||
|
||||
@implementation EOAdaptorChannel(GCS)
|
||||
|
||||
- (BOOL)tableExistsWithName:(NSString *)_tableName {
|
||||
NSException *ex;
|
||||
NSString *sql;
|
||||
BOOL didOpen;
|
||||
|
||||
didOpen = NO;
|
||||
if (![self isOpen]) {
|
||||
if (![self openChannel])
|
||||
return NO;
|
||||
didOpen = YES;
|
||||
}
|
||||
|
||||
sql = @"SELECT COUNT(*) FROM ";
|
||||
sql = [sql stringByAppendingString:_tableName];
|
||||
sql = [sql stringByAppendingString:@" WHERE 1 = 2"];
|
||||
|
||||
ex = [[[self evaluateExpressionX:sql] retain] autorelease];
|
||||
[self cancelFetch];
|
||||
|
||||
if (didOpen) [self closeChannel];
|
||||
return ex != nil ? NO : YES;
|
||||
}
|
||||
|
||||
@end /* EOAdaptorChannel(GCS) */
|
||||
35
SOPE/sope-gdl1/GDLContentStore/EOQualifier+GCS.h
Normal file
35
SOPE/sope-gdl1/GDLContentStore/EOQualifier+GCS.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GDLContentStore_EOQualifier_GCS_H__
|
||||
#define __GDLContentStore_EOQualifier_GCS_H__
|
||||
|
||||
#include <EOControl/EOQualifier.h>
|
||||
|
||||
@class NSMutableString;
|
||||
|
||||
@interface EOQualifier(GCS)
|
||||
|
||||
- (void)_gcsAppendToString:(NSMutableString *)_ms;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __GDLContentStore_EOQualifier_GCS_H__ */
|
||||
156
SOPE/sope-gdl1/GDLContentStore/EOQualifier+GCS.m
Normal file
156
SOPE/sope-gdl1/GDLContentStore/EOQualifier+GCS.m
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
Copyright (C) 2004-2007 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "EOQualifier+GCS.h"
|
||||
#include "common.h"
|
||||
|
||||
@implementation EOQualifier(GCS)
|
||||
|
||||
- (void)_appendAndQualifier:(EOAndQualifier *)_q
|
||||
toString:(NSMutableString *)_ms
|
||||
{
|
||||
// TODO: move to EOQualifier category
|
||||
NSArray *qs;
|
||||
unsigned i, count;
|
||||
|
||||
qs = [_q qualifiers];
|
||||
if ((count = [qs count]) == 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (i != 0) [_ms appendString:@" AND "];
|
||||
if (count > 1) [_ms appendString:@"("];
|
||||
[[qs objectAtIndex:i] _gcsAppendToString:_ms];
|
||||
if (count > 1) [_ms appendString:@")"];
|
||||
}
|
||||
}
|
||||
- (void)_appendOrQualifier:(EOAndQualifier *)_q
|
||||
toString:(NSMutableString *)_ms
|
||||
{
|
||||
// TODO: move to EOQualifier category
|
||||
NSArray *qs;
|
||||
unsigned i, count;
|
||||
|
||||
qs = [_q qualifiers];
|
||||
if ((count = [qs count]) == 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (i != 0) [_ms appendString:@" OR "];
|
||||
if (count > 1) [_ms appendString:@"("];
|
||||
[[qs objectAtIndex:i] _gcsAppendToString:_ms];
|
||||
if (count > 1) [_ms appendString:@")"];
|
||||
}
|
||||
}
|
||||
- (void)_appendKeyValueQualifier:(EOKeyValueQualifier *)_q
|
||||
toString:(NSMutableString *)_ms
|
||||
{
|
||||
id val;
|
||||
NSString *qKey, *qOperator, *qValue, *qFormat;
|
||||
BOOL isCI;
|
||||
|
||||
qKey = [_q key];
|
||||
|
||||
if ((val = [_q value])) {
|
||||
SEL op = [_q selector];
|
||||
|
||||
if ([val isNotNull]) {
|
||||
isCI = NO;
|
||||
|
||||
if (sel_eq(op, EOQualifierOperatorEqual))
|
||||
qOperator = @"=";
|
||||
else if (sel_eq(op, EOQualifierOperatorNotEqual))
|
||||
qOperator = @"!=";
|
||||
else if (sel_eq(op, EOQualifierOperatorLessThan))
|
||||
qOperator = @"<";
|
||||
else if (sel_eq(op, EOQualifierOperatorGreaterThan))
|
||||
qOperator = @">";
|
||||
else if (sel_eq(op, EOQualifierOperatorLessThanOrEqualTo))
|
||||
qOperator = @"<=";
|
||||
else if (sel_eq(op, EOQualifierOperatorGreaterThanOrEqualTo))
|
||||
qOperator = @">=";
|
||||
else if (sel_eq(op, EOQualifierOperatorLike))
|
||||
qOperator = @"LIKE";
|
||||
else if (sel_eq(op, EOQualifierOperatorCaseInsensitiveLike)) {
|
||||
isCI = YES;
|
||||
qOperator = @"LIKE";
|
||||
}
|
||||
else {
|
||||
[self errorWithFormat:@"%s: unsupported operation for null: %@",
|
||||
__PRETTY_FUNCTION__, NSStringFromSelector(op)];
|
||||
}
|
||||
|
||||
if ([val isKindOfClass:[NSNumber class]])
|
||||
qValue = [val stringValue];
|
||||
else if ([val isKindOfClass:[NSString class]]) {
|
||||
qValue = [NSString stringWithFormat: @"'%@'", val];
|
||||
}
|
||||
else {
|
||||
[self errorWithFormat:@"%s: unsupported value class: %@",
|
||||
__PRETTY_FUNCTION__, NSStringFromClass([val class])];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sel_eq(op, EOQualifierOperatorEqual)) {
|
||||
qOperator = @"IS";
|
||||
qValue = @"NULL";
|
||||
}
|
||||
else if (sel_eq(op, EOQualifierOperatorEqual)) {
|
||||
qOperator = @"IS NOT";
|
||||
qValue = @"NULL";
|
||||
}
|
||||
else {
|
||||
[self errorWithFormat:@"%s: invalid operation for null: %@",
|
||||
__PRETTY_FUNCTION__, NSStringFromSelector(op)];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
qOperator = @"IS";
|
||||
qValue = @"NULL";
|
||||
}
|
||||
|
||||
if (isCI)
|
||||
qFormat = @"UPPER(%@) %@ UPPER(%@)";
|
||||
else
|
||||
qFormat = @"%@ %@ %@";
|
||||
|
||||
[_ms appendFormat: qFormat, qKey, qOperator, qValue];
|
||||
}
|
||||
|
||||
- (void)_appendQualifier:(EOQualifier *)_q toString:(NSMutableString *)_ms {
|
||||
if (_q == nil) return;
|
||||
|
||||
if ([_q isKindOfClass:[EOAndQualifier class]])
|
||||
[self _appendAndQualifier:(id)_q toString:_ms];
|
||||
else if ([_q isKindOfClass:[EOOrQualifier class]])
|
||||
[self _appendOrQualifier:(id)_q toString:_ms];
|
||||
else if ([_q isKindOfClass:[EOKeyValueQualifier class]])
|
||||
[self _appendKeyValueQualifier:(id)_q toString:_ms];
|
||||
else
|
||||
[self errorWithFormat:@"unknown qualifier: %@", _q];
|
||||
}
|
||||
|
||||
- (void)_gcsAppendToString:(NSMutableString *)_ms {
|
||||
[self _appendQualifier:self toString:_ms];
|
||||
}
|
||||
|
||||
@end /* EOQualifier(GCS) */
|
||||
58
SOPE/sope-gdl1/GDLContentStore/GCSChannelManager.h
Normal file
58
SOPE/sope-gdl1/GDLContentStore/GCSChannelManager.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GDLContentStore_GCSChannelManager_H__
|
||||
#define __GDLContentStore_GCSChannelManager_H__
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
/*
|
||||
GCSChannelManager
|
||||
|
||||
This object manages the connection pooling.
|
||||
*/
|
||||
|
||||
@class NSURL, NSMutableDictionary, NSMutableArray, NSTimer;
|
||||
@class EOAdaptorChannel, EOAdaptor;
|
||||
|
||||
@interface GCSChannelManager : NSObject
|
||||
{
|
||||
NSMutableDictionary *urlToAdaptor;
|
||||
|
||||
NSMutableArray *availableChannels;
|
||||
NSMutableArray *busyChannels;
|
||||
NSTimer *gcTimer;
|
||||
}
|
||||
|
||||
+ (id)defaultChannelManager;
|
||||
|
||||
/* channels */
|
||||
|
||||
- (EOAdaptorChannel *)acquireOpenChannelForURL:(NSURL *)_url;
|
||||
- (void)releaseChannel:(EOAdaptorChannel *)_channel;
|
||||
|
||||
/* checking for tables */
|
||||
|
||||
- (BOOL)canConnect:(NSURL *)_url;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __GDLContentStore_GCSChannelManager_H__ */
|
||||
516
SOPE/sope-gdl1/GDLContentStore/GCSChannelManager.m
Normal file
516
SOPE/sope-gdl1/GDLContentStore/GCSChannelManager.m
Normal file
@@ -0,0 +1,516 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "GCSChannelManager.h"
|
||||
#include "NSURL+GCS.h"
|
||||
#include "EOAdaptorChannel+GCS.h"
|
||||
#include <GDLAccess/EOAdaptor.h>
|
||||
#include <GDLAccess/EOAdaptorContext.h>
|
||||
#include <GDLAccess/EOAdaptorChannel.h>
|
||||
#include "common.h"
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- implemented pooling
|
||||
- auto-close channels which are very old?!
|
||||
(eg missing release due to an exception)
|
||||
*/
|
||||
|
||||
@interface GCSChannelHandle : NSObject
|
||||
{
|
||||
@public
|
||||
NSURL *url;
|
||||
EOAdaptorChannel *channel;
|
||||
NSDate *creationTime;
|
||||
NSDate *lastReleaseTime;
|
||||
NSDate *lastAcquireTime;
|
||||
}
|
||||
|
||||
- (EOAdaptorChannel *)channel;
|
||||
- (BOOL)canHandleURL:(NSURL *)_url;
|
||||
- (NSTimeInterval)age;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCSChannelManager
|
||||
|
||||
static BOOL debugOn = NO;
|
||||
static BOOL debugPools = NO;
|
||||
static int ChannelExpireAge = 180;
|
||||
static NSTimeInterval ChannelCollectionTimer = 5 * 60;
|
||||
|
||||
+ (void)initialize {
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
debugOn = [ud boolForKey:@"GCSChannelManagerDebugEnabled"];
|
||||
debugPools = [ud boolForKey:@"GCSChannelManagerPoolDebugEnabled"];
|
||||
|
||||
ChannelExpireAge = [[ud objectForKey:@"GCSChannelExpireAge"] intValue];
|
||||
if (ChannelExpireAge < 1)
|
||||
ChannelExpireAge = 180;
|
||||
|
||||
ChannelCollectionTimer =
|
||||
[[ud objectForKey:@"GCSChannelCollectionTimer"] intValue];
|
||||
if (ChannelCollectionTimer < 1)
|
||||
ChannelCollectionTimer = 5*60;
|
||||
}
|
||||
|
||||
+ (NSString *)adaptorNameForURLScheme:(NSString *)_scheme {
|
||||
// TODO: map scheme to adaptors (eg 'postgresql://' to PostgreSQL
|
||||
return @"PostgreSQL";
|
||||
}
|
||||
|
||||
+ (id)defaultChannelManager {
|
||||
static GCSChannelManager *cm = nil;
|
||||
if (cm == nil)
|
||||
cm = [[self alloc] init];
|
||||
return cm;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
if ((self = [super init])) {
|
||||
self->urlToAdaptor = [[NSMutableDictionary alloc] initWithCapacity:4];
|
||||
self->availableChannels = [[NSMutableArray alloc] initWithCapacity:16];
|
||||
self->busyChannels = [[NSMutableArray alloc] initWithCapacity:16];
|
||||
|
||||
self->gcTimer = [[NSTimer scheduledTimerWithTimeInterval:
|
||||
ChannelCollectionTimer
|
||||
target:self selector:@selector(_garbageCollect:)
|
||||
userInfo:nil repeats:YES] retain];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (self->gcTimer) [self->gcTimer invalidate];
|
||||
[self->gcTimer release];
|
||||
|
||||
[self->busyChannels release];
|
||||
[self->availableChannels release];
|
||||
[self->urlToAdaptor release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* DB key */
|
||||
|
||||
- (NSString *)databaseKeyForURL:(NSURL *)_url {
|
||||
/*
|
||||
We need to build a proper key that omits passwords and URL path components
|
||||
which are not required.
|
||||
*/
|
||||
NSString *key;
|
||||
|
||||
key = [NSString stringWithFormat:@"%@\n%@\n%@\n%@",
|
||||
[_url host], [_url port],
|
||||
[_url user], [_url gcsDatabaseName]];
|
||||
return key;
|
||||
}
|
||||
|
||||
/* adaptors */
|
||||
|
||||
- (NSDictionary *)connectionDictionaryForURL:(NSURL *)_url {
|
||||
NSMutableDictionary *md;
|
||||
id tmp;
|
||||
|
||||
md = [NSMutableDictionary dictionaryWithCapacity:4];
|
||||
|
||||
if ((tmp = [_url host]) != nil)
|
||||
[md setObject:tmp forKey:@"hostName"];
|
||||
if ((tmp = [_url port]) != nil)
|
||||
[md setObject:tmp forKey:@"port"];
|
||||
if ((tmp = [_url user]) != nil)
|
||||
[md setObject:tmp forKey:@"userName"];
|
||||
if ((tmp = [_url password]) != nil)
|
||||
[md setObject:tmp forKey:@"password"];
|
||||
|
||||
if ((tmp = [_url gcsDatabaseName]) != nil)
|
||||
[md setObject:tmp forKey:@"databaseName"];
|
||||
|
||||
[self debugWithFormat:@"build connection dictionary for URL %@: %@",
|
||||
[_url absoluteString], md];
|
||||
return md;
|
||||
}
|
||||
|
||||
- (EOAdaptor *)adaptorForURL:(NSURL *)_url {
|
||||
EOAdaptor *adaptor;
|
||||
NSString *key;
|
||||
|
||||
if (_url == nil)
|
||||
return nil;
|
||||
if ((key = [self databaseKeyForURL:_url]) == nil)
|
||||
return nil;
|
||||
if ((adaptor = [self->urlToAdaptor objectForKey:key]) != nil) {
|
||||
[self debugWithFormat:@"using cached adaptor: %@", adaptor];
|
||||
return adaptor; /* cached :-) */
|
||||
}
|
||||
|
||||
[self debugWithFormat:@"creating new adaptor for URL: %@", _url];
|
||||
|
||||
if ([EOAdaptor respondsToSelector:@selector(adaptorForURL:)]) {
|
||||
adaptor = [EOAdaptor adaptorForURL:_url];
|
||||
}
|
||||
else {
|
||||
NSString *adaptorName;
|
||||
NSDictionary *condict;
|
||||
|
||||
adaptorName = [[self class] adaptorNameForURLScheme:[_url scheme]];
|
||||
if ([adaptorName length] == 0) {
|
||||
[self errorWithFormat:@"cannot handle URL: %@", _url];
|
||||
return nil;
|
||||
}
|
||||
|
||||
condict = [self connectionDictionaryForURL:_url];
|
||||
|
||||
if ((adaptor = [EOAdaptor adaptorWithName:adaptorName]) == nil) {
|
||||
[self errorWithFormat:@"did not find adaptor '%@' for URL: %@",
|
||||
adaptorName, _url];
|
||||
return nil;
|
||||
}
|
||||
|
||||
[adaptor setConnectionDictionary:condict];
|
||||
}
|
||||
|
||||
[self->urlToAdaptor setObject:adaptor forKey:key];
|
||||
return adaptor;
|
||||
}
|
||||
|
||||
/* channels */
|
||||
|
||||
- (GCSChannelHandle *)findBusyChannelHandleForChannel:(EOAdaptorChannel *)_ch {
|
||||
NSEnumerator *e;
|
||||
GCSChannelHandle *handle;
|
||||
|
||||
e = [self->busyChannels objectEnumerator];
|
||||
while ((handle = [e nextObject])) {
|
||||
if ([handle channel] == _ch)
|
||||
return handle;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
- (GCSChannelHandle *)findAvailChannelHandleForURL:(NSURL *)_url {
|
||||
NSEnumerator *e;
|
||||
GCSChannelHandle *handle;
|
||||
|
||||
e = [self->availableChannels objectEnumerator];
|
||||
while ((handle = [e nextObject])) {
|
||||
if ([handle canHandleURL:_url])
|
||||
return handle;
|
||||
|
||||
if (debugPools) {
|
||||
[self logWithFormat:@"DBPOOL: cannot use handle (%@ vs %@)",
|
||||
[_url absoluteString], [handle->url absoluteString]];
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (EOAdaptorChannel *)_createChannelForURL:(NSURL *)_url {
|
||||
EOAdaptor *adaptor;
|
||||
EOAdaptorContext *adContext;
|
||||
EOAdaptorChannel *adChannel;
|
||||
|
||||
if ((adaptor = [self adaptorForURL:_url]) == nil)
|
||||
return nil;
|
||||
|
||||
if ((adContext = [adaptor createAdaptorContext]) == nil) {
|
||||
[self errorWithFormat:@"could not create adaptor context!"];
|
||||
return nil;
|
||||
}
|
||||
if ((adChannel = [adContext createAdaptorChannel]) == nil) {
|
||||
[self errorWithFormat:@"could not create adaptor channel!"];
|
||||
return nil;
|
||||
}
|
||||
return adChannel;
|
||||
}
|
||||
|
||||
- (EOAdaptorChannel *)acquireOpenChannelForURL:(NSURL *)_url {
|
||||
// TODO: naive implementation, add pooling!
|
||||
EOAdaptorChannel *channel;
|
||||
GCSChannelHandle *handle;
|
||||
NSCalendarDate *now;
|
||||
|
||||
now = [NSCalendarDate date];
|
||||
|
||||
/* look for cached handles */
|
||||
|
||||
if ((handle = [self findAvailChannelHandleForURL:_url]) != nil) {
|
||||
// TODO: check age?
|
||||
[self->busyChannels addObject:handle];
|
||||
[self->availableChannels removeObject:handle];
|
||||
ASSIGN(handle->lastAcquireTime, now);
|
||||
|
||||
if (debugPools)
|
||||
[self logWithFormat:@"DBPOOL: reused cached DB channel!"];
|
||||
return [[handle channel] retain];
|
||||
}
|
||||
|
||||
if (debugPools) {
|
||||
[self logWithFormat:@"DBPOOL: create new DB channel for URL: %@",
|
||||
[_url absoluteString]];
|
||||
}
|
||||
|
||||
/* create channel */
|
||||
|
||||
if ((channel = [self _createChannelForURL:_url]) == nil)
|
||||
return nil;
|
||||
|
||||
if ([channel isOpen])
|
||||
;
|
||||
else if (![channel openChannel]) {
|
||||
[self errorWithFormat:@"could not open channel %@ for URL: %@",
|
||||
channel, [_url absoluteString]];
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* create handle for channel */
|
||||
|
||||
handle = [[GCSChannelHandle alloc] init];
|
||||
handle->url = [_url retain];
|
||||
handle->channel = [channel retain];
|
||||
handle->creationTime = [now retain];
|
||||
handle->lastAcquireTime = [now retain];
|
||||
|
||||
[self->busyChannels addObject:handle];
|
||||
[handle release];
|
||||
|
||||
return [channel retain];
|
||||
}
|
||||
- (void)releaseChannel:(EOAdaptorChannel *)_channel {
|
||||
GCSChannelHandle *handle;
|
||||
|
||||
if ((handle = [self findBusyChannelHandleForChannel:_channel]) != nil) {
|
||||
NSCalendarDate *now;
|
||||
|
||||
now = [NSCalendarDate date];
|
||||
|
||||
handle = [handle retain];
|
||||
ASSIGN(handle->lastReleaseTime, now);
|
||||
|
||||
[self->busyChannels removeObject:handle];
|
||||
|
||||
if ([[handle channel] isOpen] && [handle age] < ChannelExpireAge) {
|
||||
// TODO: consider age
|
||||
[self->availableChannels addObject:handle];
|
||||
if (debugPools) {
|
||||
[self logWithFormat:
|
||||
@"DBPOOL: keeping channel (age %ds, #%d): %@",
|
||||
(int)[handle age], [self->availableChannels count],
|
||||
[handle->url absoluteString]];
|
||||
}
|
||||
[_channel release];
|
||||
[handle release];
|
||||
return;
|
||||
}
|
||||
|
||||
if (debugPools) {
|
||||
[self logWithFormat:
|
||||
@"DBPOOL: freeing old channel (age %ds)", (int)[handle age]];
|
||||
}
|
||||
|
||||
/* not reusing channel */
|
||||
[handle release]; handle = nil;
|
||||
}
|
||||
|
||||
if ([_channel isOpen])
|
||||
[_channel closeChannel];
|
||||
|
||||
[_channel release];
|
||||
}
|
||||
|
||||
/* checking for tables */
|
||||
|
||||
- (BOOL)canConnect:(NSURL *)_url {
|
||||
/*
|
||||
this can check for DB connect as well as for table URLs (whether a table
|
||||
exists)
|
||||
*/
|
||||
EOAdaptorChannel *channel;
|
||||
NSString *table;
|
||||
BOOL result;
|
||||
|
||||
if ((channel = [self acquireOpenChannelForURL:_url]) == nil) {
|
||||
if (debugOn) [self debugWithFormat:@"could not acquire channel: %@", _url];
|
||||
return NO;
|
||||
}
|
||||
if (debugOn) [self debugWithFormat:@"acquired channel: %@", channel];
|
||||
result = YES; /* could open channel */
|
||||
|
||||
/* check whether table exists */
|
||||
|
||||
table = [_url gcsTableName];
|
||||
if ([table length] > 0)
|
||||
result = [channel tableExistsWithName:table];
|
||||
|
||||
/* release channel */
|
||||
|
||||
[self releaseChannel:channel]; channel = nil;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* collect old channels */
|
||||
|
||||
- (void)_garbageCollect:(NSTimer *)_timer {
|
||||
NSMutableArray *handlesToRemove;
|
||||
unsigned i, count;
|
||||
|
||||
if ((count = [self->availableChannels count]) == 0)
|
||||
/* no available channels */
|
||||
return;
|
||||
|
||||
/* collect channels to expire */
|
||||
|
||||
handlesToRemove = [[NSMutableArray alloc] initWithCapacity:4];
|
||||
for (i = 0; i < count; i++) {
|
||||
GCSChannelHandle *handle;
|
||||
|
||||
handle = [self->availableChannels objectAtIndex:i];
|
||||
if (![[handle channel] isOpen]) {
|
||||
[handlesToRemove addObject:handle];
|
||||
continue;
|
||||
}
|
||||
if ([handle age] > ChannelExpireAge) {
|
||||
[handlesToRemove addObject:handle];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove channels */
|
||||
count = [handlesToRemove count];
|
||||
if (debugPools)
|
||||
[self logWithFormat:@"DBPOOL: garbage collecting %d channels.", count];
|
||||
for (i = 0; i < count; i++) {
|
||||
GCSChannelHandle *handle;
|
||||
|
||||
handle = [[handlesToRemove objectAtIndex:i] retain];
|
||||
[self->availableChannels removeObject:handle];
|
||||
if ([[handle channel] isOpen])
|
||||
[[handle channel] closeChannel];
|
||||
[handle release];
|
||||
}
|
||||
|
||||
[handlesToRemove release];
|
||||
}
|
||||
|
||||
/* debugging */
|
||||
|
||||
- (BOOL)isDebuggingEnabled {
|
||||
return debugOn;
|
||||
}
|
||||
|
||||
/* description */
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableString *ms;
|
||||
|
||||
ms = [NSMutableString stringWithCapacity:256];
|
||||
[ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
|
||||
|
||||
[ms appendFormat:@" #adaptors=%d", [self->urlToAdaptor count]];
|
||||
|
||||
[ms appendString:@">"];
|
||||
return ms;
|
||||
}
|
||||
|
||||
@end /* GCSChannelManager */
|
||||
|
||||
@implementation GCSChannelHandle
|
||||
|
||||
- (void)dealloc {
|
||||
[self->channel release];
|
||||
[self->creationTime release];
|
||||
[self->lastReleaseTime release];
|
||||
[self->lastAcquireTime release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* accessors */
|
||||
|
||||
- (EOAdaptorChannel *)channel {
|
||||
return self->channel;
|
||||
}
|
||||
|
||||
- (BOOL)canHandleURL:(NSURL *)_url {
|
||||
BOOL isSQLite;
|
||||
|
||||
if (_url == nil) {
|
||||
[self logWithFormat:@"MISMATCH: no url .."];
|
||||
return NO;
|
||||
}
|
||||
if (_url == self->url)
|
||||
return YES;
|
||||
|
||||
isSQLite = [[_url scheme] isEqualToString:@"sqlite"];
|
||||
|
||||
if (!isSQLite && ![[self->url host] isEqual:[_url host]]) {
|
||||
[self logWithFormat:@"MISMATCH: different host (%@ vs %@)",
|
||||
[self->url host], [_url host]];
|
||||
return NO;
|
||||
}
|
||||
if (![[self->url gcsDatabaseName] isEqualToString:[_url gcsDatabaseName]]) {
|
||||
[self logWithFormat:@"MISMATCH: different db .."];
|
||||
return NO;
|
||||
}
|
||||
if (!isSQLite) {
|
||||
if (![[self->url user] isEqual:[_url user]]) {
|
||||
[self logWithFormat:@"MISMATCH: different user .."];
|
||||
return NO;
|
||||
}
|
||||
if ([[self->url port] intValue] != [[_url port] intValue]) {
|
||||
[self logWithFormat:@"MISMATCH: different port (%@ vs %@) ..",
|
||||
[self->url port], [_url port]];
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)age {
|
||||
return [[NSCalendarDate calendarDate]
|
||||
timeIntervalSinceDate:self->creationTime];
|
||||
}
|
||||
|
||||
/* NSCopying */
|
||||
|
||||
- (id)copyWithZone:(NSZone *)_zone {
|
||||
return [self retain];
|
||||
}
|
||||
|
||||
/* description */
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableString *ms;
|
||||
|
||||
ms = [NSMutableString stringWithCapacity:256];
|
||||
[ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
|
||||
|
||||
[ms appendFormat:@" channel=0x%p", self->channel];
|
||||
if (self->creationTime) [ms appendFormat:@" created=%@", self->creationTime];
|
||||
if (self->lastReleaseTime)
|
||||
[ms appendFormat:@" last-released=%@", self->lastReleaseTime];
|
||||
if (self->lastAcquireTime)
|
||||
[ms appendFormat:@" last-acquired=%@", self->lastAcquireTime];
|
||||
|
||||
[ms appendString:@">"];
|
||||
return ms;
|
||||
}
|
||||
|
||||
@end /* GCSChannelHandle */
|
||||
39
SOPE/sope-gdl1/GDLContentStore/GCSContext.h
Normal file
39
SOPE/sope-gdl1/GDLContentStore/GCSContext.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GDLContentStore_GCSContext_H__
|
||||
#define __GDLContentStore_GCSContext_H__
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
/*
|
||||
GCSContext
|
||||
|
||||
Context passed to all operations.
|
||||
*/
|
||||
|
||||
@interface GCSContext : NSObject
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __GDLContentStore_GCSContext_H__ */
|
||||
26
SOPE/sope-gdl1/GDLContentStore/GCSContext.m
Normal file
26
SOPE/sope-gdl1/GDLContentStore/GCSContext.m
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "GCSContext.h"
|
||||
#include "common.h"
|
||||
|
||||
@implementation GCSContext
|
||||
@end /* GCSContext */
|
||||
37
SOPE/sope-gdl1/GDLContentStore/GCSFieldExtractor.h
Normal file
37
SOPE/sope-gdl1/GDLContentStore/GCSFieldExtractor.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GDLContentStore_GCSFieldExtractor_H__
|
||||
#define __GDLContentStore_GCSFieldExtractor_H__
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class NSString, NSMutableDictionary;
|
||||
|
||||
@interface GCSFieldExtractor : NSObject
|
||||
{
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)extractQuickFieldsFromContent:(NSString *)_content;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __GDLContentStore_GCSFieldExtractor_H__ */
|
||||
31
SOPE/sope-gdl1/GDLContentStore/GCSFieldExtractor.m
Normal file
31
SOPE/sope-gdl1/GDLContentStore/GCSFieldExtractor.m
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "GCSFieldExtractor.h"
|
||||
#include "common.h"
|
||||
|
||||
@implementation GCSFieldExtractor
|
||||
|
||||
- (NSMutableDictionary *)extractQuickFieldsFromContent:(NSString *)_content {
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end /* GCSFieldExtractor */
|
||||
61
SOPE/sope-gdl1/GDLContentStore/GCSFieldInfo.h
Normal file
61
SOPE/sope-gdl1/GDLContentStore/GCSFieldInfo.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GDLContentStore_GCSFieldInfo_H__
|
||||
#define __GDLContentStore_GCSFieldInfo_H__
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
/*
|
||||
GCSFieldInfo
|
||||
|
||||
The field info inside an .ocs schema file.
|
||||
|
||||
The field info objects are stored in an GCSFolderType.
|
||||
*/
|
||||
|
||||
@class NSString, NSArray;
|
||||
|
||||
@interface GCSFieldInfo : NSObject
|
||||
{
|
||||
NSString *columnName;
|
||||
NSString *sqlType;
|
||||
BOOL allowsNull;
|
||||
BOOL isPrimaryKey;
|
||||
}
|
||||
|
||||
+ (NSArray *)fieldsForPropertyList:(NSArray *)_plist;
|
||||
- (id)initWithPropertyList:(id)_plist;
|
||||
|
||||
/* accessors */
|
||||
|
||||
- (NSString *)columnName;
|
||||
- (NSString *)sqlType;
|
||||
- (BOOL)doesAllowNull;
|
||||
- (BOOL)isPrimaryKey;
|
||||
|
||||
/* generating SQL */
|
||||
|
||||
- (NSString *)sqlCreateSection;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __GDLContentStore_GCSFieldInfo_H__ */
|
||||
127
SOPE/sope-gdl1/GDLContentStore/GCSFieldInfo.m
Normal file
127
SOPE/sope-gdl1/GDLContentStore/GCSFieldInfo.m
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "GCSFieldInfo.h"
|
||||
#include "common.h"
|
||||
|
||||
@implementation GCSFieldInfo
|
||||
|
||||
+ (NSArray *)fieldsForPropertyList:(NSArray *)_plist {
|
||||
NSMutableArray *fields;
|
||||
unsigned i, count;
|
||||
|
||||
if (_plist == nil)
|
||||
return nil;
|
||||
|
||||
count = [_plist count];
|
||||
fields = [NSMutableArray arrayWithCapacity:count];
|
||||
for (i = 0; i < count; i++) {
|
||||
GCSFieldInfo *field;
|
||||
|
||||
field = [[GCSFieldInfo alloc] initWithPropertyList:
|
||||
[_plist objectAtIndex:i]];
|
||||
if (field != nil) [fields addObject:field];
|
||||
[field release];
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
- (id)initWithPropertyList:(id)_plist {
|
||||
if ((self = [super init])) {
|
||||
NSDictionary *plist = _plist;
|
||||
|
||||
self->columnName = [[plist objectForKey:@"columnName"] copy];
|
||||
self->sqlType = [[plist objectForKey:@"sqlType"] copy];
|
||||
|
||||
self->allowsNull = [[plist objectForKey:@"allowsNull"] boolValue];
|
||||
self->isPrimaryKey = [[plist objectForKey:@"isPrimaryKey"] boolValue];
|
||||
|
||||
if (![self->columnName isNotNull] || ![self->sqlType isNotNull]) {
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self->columnName release];
|
||||
[self->sqlType release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* accessors */
|
||||
|
||||
- (NSString *)columnName {
|
||||
return self->columnName;
|
||||
}
|
||||
- (NSString *)sqlType {
|
||||
return self->sqlType;
|
||||
}
|
||||
|
||||
- (BOOL)doesAllowNull {
|
||||
return self->allowsNull;
|
||||
}
|
||||
- (BOOL)isPrimaryKey {
|
||||
return self->isPrimaryKey;
|
||||
}
|
||||
|
||||
/* generating SQL */
|
||||
|
||||
- (NSString *)sqlCreateSection {
|
||||
NSMutableString *ms;
|
||||
|
||||
ms = [NSMutableString stringWithCapacity:32];
|
||||
[ms appendString:[self columnName]];
|
||||
[ms appendString:@" "];
|
||||
[ms appendString:[self sqlType]];
|
||||
|
||||
[ms appendString:@" "];
|
||||
if (![self doesAllowNull]) [ms appendString:@"NOT "];
|
||||
[ms appendString:@"NULL"];
|
||||
|
||||
if ([self isPrimaryKey]) [ms appendString:@" PRIMARY KEY"];
|
||||
return ms;
|
||||
}
|
||||
|
||||
/* description */
|
||||
|
||||
- (void)appendAttributesToDescription:(NSMutableString *)ms {
|
||||
id tmp;
|
||||
|
||||
if ((tmp = [self columnName]) != nil) [ms appendFormat:@" column=%@", tmp];
|
||||
if ((tmp = [self sqlType]) != nil) [ms appendFormat:@" sql=%@", tmp];
|
||||
|
||||
if ([self doesAllowNull]) [ms appendString:@" allows-null"];
|
||||
if ([self isPrimaryKey]) [ms appendString:@" pkey"];
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableString *ms;
|
||||
|
||||
ms = [NSMutableString stringWithCapacity:256];
|
||||
[ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
|
||||
[self appendAttributesToDescription:ms];
|
||||
[ms appendString:@">"];
|
||||
return ms;
|
||||
}
|
||||
|
||||
@end /* GCSFieldInfo */
|
||||
128
SOPE/sope-gdl1/GDLContentStore/GCSFolder.h
Normal file
128
SOPE/sope-gdl1/GDLContentStore/GCSFolder.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
Copyright (C) 2004-2007 SKYRIX Software AG
|
||||
Copyright (C) 2007 Helge Hess
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GDLContentStore_GCSFolder_H__
|
||||
#define __GDLContentStore_GCSFolder_H__
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
/*
|
||||
GCSFolder
|
||||
|
||||
TODO: document
|
||||
|
||||
Fixed Quick-Table SQL fields:
|
||||
- "c_name" (name of the file in the folder)
|
||||
|
||||
Fixed BLOB-Table SQL fields:
|
||||
- "c_name" (name of the file in the folder)
|
||||
- "c_content" (content of the file in the folder)
|
||||
- "c_version" (update revision of the file in the folder)
|
||||
*/
|
||||
|
||||
@class NSString, NSURL, NSNumber, NSArray, NSException, NSMutableString;
|
||||
@class NSDictionary;
|
||||
@class EOQualifier, EOFetchSpecification;
|
||||
@class EOAdaptorChannel;
|
||||
@class GCSFolderManager, GCSFolderType, GCSChannelManager;
|
||||
|
||||
@interface GCSFolder : NSObject
|
||||
{
|
||||
GCSFolderManager *folderManager;
|
||||
GCSFolderType *folderInfo;
|
||||
|
||||
NSNumber *folderId;
|
||||
NSString *folderName;
|
||||
NSString *path;
|
||||
NSURL *location;
|
||||
NSURL *quickLocation;
|
||||
NSURL *aclLocation;
|
||||
NSString *folderTypeName;
|
||||
|
||||
struct {
|
||||
int requiresFolderSelect:1;
|
||||
int sameTableForQuick:1;
|
||||
int reserved:30;
|
||||
} ofFlags;
|
||||
}
|
||||
|
||||
- (id)initWithPath:(NSString *)_path primaryKey:(id)_folderId
|
||||
folderTypeName:(NSString *)_ftname folderType:(GCSFolderType *)_ftype
|
||||
location:(NSURL *)_loc quickLocation:(NSURL *)_qloc
|
||||
aclLocation: (NSURL *)_aloc
|
||||
folderManager:(GCSFolderManager *)_fm;
|
||||
|
||||
/* accessors */
|
||||
|
||||
- (NSNumber *)folderId;
|
||||
- (NSString *)folderName;
|
||||
- (NSString *)path;
|
||||
- (NSURL *)location;
|
||||
- (NSURL *)quickLocation;
|
||||
- (NSURL *)aclLocation;
|
||||
- (NSString *)folderTypeName;
|
||||
|
||||
- (GCSFolderManager *)folderManager;
|
||||
- (GCSChannelManager *)channelManager;
|
||||
|
||||
- (NSString *)storeTableName;
|
||||
- (NSString *)quickTableName;
|
||||
- (NSString *)aclTableName;
|
||||
- (BOOL)isQuickInfoStoredInContentTable;
|
||||
|
||||
/* connection */
|
||||
|
||||
- (EOAdaptorChannel *)acquireStoreChannel;
|
||||
- (EOAdaptorChannel *)acquireQuickChannel;
|
||||
- (EOAdaptorChannel *)acquireAclChannel;
|
||||
- (void)releaseChannel:(EOAdaptorChannel *)_channel;
|
||||
|
||||
- (BOOL)canConnectStore;
|
||||
- (BOOL)canConnectQuick;
|
||||
|
||||
/* operations */
|
||||
|
||||
- (NSArray *)subFolderNames;
|
||||
- (NSArray *)allSubFolderNames;
|
||||
|
||||
- (NSNumber *)versionOfContentWithName:(NSString *)_name;
|
||||
|
||||
- (NSString *)fetchContentWithName:(NSString *)_name;
|
||||
- (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name
|
||||
baseVersion:(unsigned int)_baseVersion;
|
||||
- (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name;
|
||||
- (NSException *)deleteContentWithName:(NSString *)_name;
|
||||
|
||||
- (NSException *)deleteFolder;
|
||||
|
||||
- (NSDictionary *)fetchContentsOfAllFiles;
|
||||
|
||||
- (NSArray *)fetchFields:(NSArray *)_flds
|
||||
fetchSpecification:(EOFetchSpecification *)_fs;
|
||||
- (NSArray *)fetchFields:(NSArray *)_flds matchingQualifier:(EOQualifier *)_q;
|
||||
- (NSArray *)fetchAclMatchingQualifier:(EOQualifier *)_q;
|
||||
- (void)deleteAclMatchingQualifier:(EOQualifier *)_q;
|
||||
- (void)deleteAclWithSpecification:(EOFetchSpecification *)_fs;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __GDLContentStore_GCSFolder_H__ */
|
||||
1101
SOPE/sope-gdl1/GDLContentStore/GCSFolder.m
Normal file
1101
SOPE/sope-gdl1/GDLContentStore/GCSFolder.m
Normal file
File diff suppressed because it is too large
Load Diff
83
SOPE/sope-gdl1/GDLContentStore/GCSFolderManager.h
Normal file
83
SOPE/sope-gdl1/GDLContentStore/GCSFolderManager.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GDLContentStore_GCSFolderManager_H__
|
||||
#define __GDLContentStore_GCSFolderManager_H__
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
/*
|
||||
GCSFolderManager
|
||||
|
||||
Objects of this class manage the "folder_info" table, they manage the
|
||||
model and manage the tables required for a folder.
|
||||
*/
|
||||
|
||||
@class NSString, NSArray, NSURL, NSDictionary, NSException;
|
||||
@class GCSChannelManager, GCSFolder, GCSFolderType;
|
||||
|
||||
@interface GCSFolderManager : NSObject
|
||||
{
|
||||
GCSChannelManager *channelManager;
|
||||
NSDictionary *nameToType;
|
||||
NSURL *folderInfoLocation;
|
||||
}
|
||||
|
||||
+ (id)defaultFolderManager;
|
||||
- (id)initWithFolderInfoLocation:(NSURL *)_url;
|
||||
|
||||
/* accessors */
|
||||
|
||||
- (NSURL *)folderInfoLocation;
|
||||
- (NSString *)folderInfoTableName;
|
||||
|
||||
/* connection */
|
||||
|
||||
- (GCSChannelManager *)channelManager;
|
||||
- (BOOL)canConnect;
|
||||
|
||||
/* handling folder names */
|
||||
|
||||
- (NSString *)internalNameFromPath:(NSString *)_path;
|
||||
- (NSArray *)internalNamesFromPath:(NSString *)_path;
|
||||
- (NSString *)pathFromInternalName:(NSString *)_name;
|
||||
|
||||
/* operations */
|
||||
|
||||
- (BOOL)folderExistsAtPath:(NSString *)_path;
|
||||
- (NSArray *)listSubFoldersAtPath:(NSString *)_path recursive:(BOOL)_flag;
|
||||
|
||||
- (GCSFolder *)folderAtPath:(NSString *)_path;
|
||||
|
||||
- (NSException *)createFolderOfType:(NSString *)_type withName:(NSString *)_name atPath:(NSString *)_path;
|
||||
- (NSException *)deleteFolderAtPath:(NSString *)_path;
|
||||
|
||||
/* folder types */
|
||||
|
||||
- (GCSFolderType *)folderTypeWithName:(NSString *)_name;
|
||||
|
||||
/* cache management */
|
||||
|
||||
- (void)reset;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __GDLContentStore_GCSFolderManager_H__ */
|
||||
853
SOPE/sope-gdl1/GDLContentStore/GCSFolderManager.m
Normal file
853
SOPE/sope-gdl1/GDLContentStore/GCSFolderManager.m
Normal file
@@ -0,0 +1,853 @@
|
||||
/*
|
||||
Copyright (C) 2004-2007 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "GCSFolderManager.h"
|
||||
#include "GCSChannelManager.h"
|
||||
#include "GCSFolderType.h"
|
||||
#include "GCSFolder.h"
|
||||
#include "NSURL+GCS.h"
|
||||
#include "EOAdaptorChannel+GCS.h"
|
||||
#include "common.h"
|
||||
#include <GDLAccess/EOAdaptorChannel.h>
|
||||
#include <NGExtensions/NGResourceLocator.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
Required database schema:
|
||||
|
||||
<arbitary table>
|
||||
c_path
|
||||
c_path1, path2, path3... [quickPathCount times]
|
||||
c_foldername
|
||||
|
||||
TODO:
|
||||
- add a local cache?
|
||||
*/
|
||||
|
||||
@implementation GCSFolderManager
|
||||
|
||||
static GCSFolderManager *fm = nil;
|
||||
static BOOL debugOn = NO;
|
||||
static BOOL debugSQLGen = NO;
|
||||
static BOOL debugPathTraversal = NO;
|
||||
static int quickPathCount = 4;
|
||||
static NSArray *emptyArray = nil;
|
||||
#if 0
|
||||
static NSString *GCSPathColumnName = @"c_path";
|
||||
static NSString *GCSTypeColumnName = @"c_folder_type";
|
||||
static NSString *GCSTypeRecordName = @"c_folder_type";
|
||||
#endif
|
||||
static NSString *GCSPathRecordName = @"c_path";
|
||||
static NSString *GCSGenericFolderTypeName = @"Container";
|
||||
static const char *GCSPathColumnPattern = "c_path%i";
|
||||
static NSCharacterSet *asciiAlphaNumericCS = nil;
|
||||
|
||||
+ (void)initialize {
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
debugOn = [ud boolForKey:@"GCSFolderManagerDebugEnabled"];
|
||||
debugSQLGen = [ud boolForKey:@"GCSFolderManagerSQLDebugEnabled"];
|
||||
emptyArray = [[NSArray alloc] init];
|
||||
if (!asciiAlphaNumericCS)
|
||||
{
|
||||
asciiAlphaNumericCS
|
||||
= [NSCharacterSet characterSetWithCharactersInString:
|
||||
@"0123456789"
|
||||
@"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
@"abcdefghijklmnopqrstuvwxyz"];
|
||||
[asciiAlphaNumericCS retain];
|
||||
}
|
||||
}
|
||||
|
||||
+ (id)defaultFolderManager {
|
||||
NSString *s;
|
||||
NSURL *url;
|
||||
if (fm) return fm;
|
||||
|
||||
s = [[NSUserDefaults standardUserDefaults] stringForKey:@"OCSFolderInfoURL"];
|
||||
if ([s length] == 0) {
|
||||
NSLog(@"ERROR(%s): default 'OCSFolderInfoURL' is not configured.",
|
||||
__PRETTY_FUNCTION__);
|
||||
return nil;
|
||||
}
|
||||
if ((url = [NSURL URLWithString:s]) == nil) {
|
||||
NSLog(@"ERROR(%s): default 'OCSFolderInfoURL' is not a valid URL: '%@'",
|
||||
__PRETTY_FUNCTION__, s);
|
||||
return nil;
|
||||
}
|
||||
if ((fm = [[self alloc] initWithFolderInfoLocation:url]) == nil) {
|
||||
NSLog(@"ERROR(%s): could not create folder manager with URL: '%@'",
|
||||
__PRETTY_FUNCTION__, [url absoluteString]);
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSLog(@"Note: setup default manager at: %@", url);
|
||||
return fm;
|
||||
}
|
||||
|
||||
- (NSDictionary *)loadDefaultFolderTypes {
|
||||
NSMutableDictionary *typeMap;
|
||||
NSArray *types;
|
||||
unsigned i, count;
|
||||
|
||||
|
||||
types = [[GCSFolderType resourceLocator] lookupAllFilesWithExtension:@"ocs"
|
||||
doReturnFullPath:NO];
|
||||
if ((count = [types count]) == 0) {
|
||||
[self logWithFormat:@"Note: no GCS folder types found."];
|
||||
return nil;
|
||||
}
|
||||
|
||||
typeMap = [NSMutableDictionary dictionaryWithCapacity:count];
|
||||
|
||||
[self logWithFormat:@"Note: loading %d GCS folder types:", count];
|
||||
for (i = 0, count = [types count]; i < count; i++) {
|
||||
NSString *type;
|
||||
GCSFolderType *typeObject;
|
||||
|
||||
type = [[types objectAtIndex:i] stringByDeletingPathExtension];
|
||||
typeObject = [[GCSFolderType alloc] initWithFolderTypeName:type];
|
||||
|
||||
[self logWithFormat:@" %@: %s",
|
||||
type, [typeObject isNotNull] ? "OK" : "FAIL"];
|
||||
[typeMap setObject:typeObject forKey:type];
|
||||
[typeObject release];
|
||||
}
|
||||
|
||||
return typeMap;
|
||||
}
|
||||
|
||||
- (id)initWithFolderInfoLocation:(NSURL *)_url {
|
||||
if (_url == nil) {
|
||||
[self logWithFormat:@"ERROR(%s): missing folder info url!",
|
||||
__PRETTY_FUNCTION__];
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
if ((self = [super init])) {
|
||||
self->channelManager = [[GCSChannelManager defaultChannelManager] retain];
|
||||
self->folderInfoLocation = [_url retain];
|
||||
|
||||
if ([[self folderInfoTableName] length] == 0) {
|
||||
[self logWithFormat:@"ERROR(%s): missing tablename in URL: %@",
|
||||
__PRETTY_FUNCTION__, [_url absoluteString]];
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* register default folder types */
|
||||
self->nameToType = [[self loadDefaultFolderTypes] copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self->nameToType release];
|
||||
[self->folderInfoLocation release];
|
||||
[self->channelManager release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* accessors */
|
||||
|
||||
- (NSURL *)folderInfoLocation {
|
||||
return self->folderInfoLocation;
|
||||
}
|
||||
|
||||
- (NSString *)folderInfoTableName {
|
||||
return [[self folderInfoLocation] gcsTableName];
|
||||
}
|
||||
|
||||
/* connection */
|
||||
|
||||
- (GCSChannelManager *)channelManager {
|
||||
return self->channelManager;
|
||||
}
|
||||
|
||||
- (EOAdaptorChannel *)acquireOpenChannel {
|
||||
EOAdaptorChannel *ch;
|
||||
|
||||
ch = [[self channelManager] acquireOpenChannelForURL:
|
||||
[self folderInfoLocation]];
|
||||
return ch;
|
||||
}
|
||||
- (void)releaseChannel:(EOAdaptorChannel *)_channel {
|
||||
[[self channelManager] releaseChannel:_channel];
|
||||
if (debugOn) [self debugWithFormat:@"released channel: %@", _channel];
|
||||
}
|
||||
|
||||
- (BOOL)canConnect {
|
||||
return [[self channelManager] canConnect:[self folderInfoLocation]];
|
||||
}
|
||||
|
||||
- (NSArray *)performSQL:(NSString *)_sql {
|
||||
EOAdaptorChannel *channel;
|
||||
NSException *ex;
|
||||
NSMutableArray *rows;
|
||||
NSDictionary *row;
|
||||
NSArray *attrs;
|
||||
|
||||
/* acquire channel */
|
||||
|
||||
if ((channel = [self acquireOpenChannel]) == nil) {
|
||||
if (debugOn) [self debugWithFormat:@"could not acquire channel!"];
|
||||
return nil;
|
||||
}
|
||||
if (debugOn) [self debugWithFormat:@"acquired channel: %@", channel];
|
||||
|
||||
/* run SQL */
|
||||
|
||||
if ((ex = [channel evaluateExpressionX:_sql]) != nil) {
|
||||
[self logWithFormat:@"ERROR(%s): cannot execute\n SQL '%@':\n %@",
|
||||
__PRETTY_FUNCTION__, _sql, ex];
|
||||
[self releaseChannel:channel];
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* fetch results */
|
||||
|
||||
attrs = [channel describeResults:NO /* do not beautify names */];
|
||||
rows = [NSMutableArray arrayWithCapacity:16];
|
||||
while ((row = [channel fetchAttributes:attrs withZone:NULL]) != nil)
|
||||
[rows addObject:row];
|
||||
|
||||
[self releaseChannel:channel];
|
||||
return rows;
|
||||
}
|
||||
|
||||
/* row factory */
|
||||
|
||||
- (GCSFolder *)folderForRecord:(NSDictionary *)_record {
|
||||
GCSFolder *folder;
|
||||
GCSFolderType *folderType;
|
||||
NSString *folderTypeName, *locationString, *folderName, *path;
|
||||
NSNumber *folderId;
|
||||
NSURL *location, *quickLocation, *aclLocation;
|
||||
|
||||
if (_record == nil) return nil;
|
||||
|
||||
folderTypeName = [_record objectForKey:@"c_folder_type"];
|
||||
if (![folderTypeName isNotNull]) {
|
||||
[self logWithFormat:@"ERROR(%s): missing type in folder: %@",
|
||||
__PRETTY_FUNCTION__, _record];
|
||||
return nil;
|
||||
}
|
||||
if ((folderType = [self folderTypeWithName:folderTypeName]) == nil) {
|
||||
[self logWithFormat:
|
||||
@"ERROR(%s): could not resolve type '%@' of folder: %@",
|
||||
__PRETTY_FUNCTION__,
|
||||
folderTypeName, [_record valueForKey:@"c_path"]];
|
||||
return nil;
|
||||
}
|
||||
|
||||
folderId = [_record objectForKey:@"c_folder_id"];
|
||||
folderName = [_record objectForKey:@"c_path"];
|
||||
path = [self pathFromInternalName:folderName];
|
||||
|
||||
locationString = [_record objectForKey:@"c_location"];
|
||||
location = [locationString isNotNull]
|
||||
? [NSURL URLWithString:locationString]
|
||||
: nil;
|
||||
if (location == nil) {
|
||||
[self logWithFormat:@"ERROR(%s): missing folder location in record: %@",
|
||||
__PRETTY_FUNCTION__, _record];
|
||||
return nil;
|
||||
}
|
||||
|
||||
locationString = [_record objectForKey:@"c_quick_location"];
|
||||
quickLocation = [locationString isNotNull]
|
||||
? [NSURL URLWithString:locationString]
|
||||
: nil;
|
||||
|
||||
if (quickLocation == nil) {
|
||||
[self logWithFormat:@"WARNING(%s): missing quick location in record: %@",
|
||||
__PRETTY_FUNCTION__, _record];
|
||||
}
|
||||
|
||||
locationString = [_record objectForKey:@"c_acl_location"];
|
||||
aclLocation = [locationString isNotNull]
|
||||
? [NSURL URLWithString:locationString]
|
||||
: nil;
|
||||
|
||||
folder = [[GCSFolder alloc] initWithPath:path primaryKey:folderId
|
||||
folderTypeName:folderTypeName
|
||||
folderType:folderType
|
||||
location:location quickLocation:quickLocation
|
||||
aclLocation:aclLocation
|
||||
folderManager:self];
|
||||
return [folder autorelease];
|
||||
}
|
||||
|
||||
/* path SQL */
|
||||
|
||||
- (NSString *)generateSQLWhereForInternalNames:(NSArray *)_names
|
||||
exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs
|
||||
{
|
||||
/* generates a WHERE qualifier for matching the "quick" entries */
|
||||
NSMutableString *sql;
|
||||
unsigned i, count;
|
||||
|
||||
if ((count = [_names count]) == 0) {
|
||||
[self debugWithFormat:@"WARNING(%s): passed in empty name array!",
|
||||
__PRETTY_FUNCTION__];
|
||||
return @"1 = 2";
|
||||
}
|
||||
|
||||
sql = [NSMutableString stringWithCapacity:(count * 8)];
|
||||
for (i = 0; i < quickPathCount; i++) {
|
||||
NSString *pathColumn;
|
||||
char buf[32];
|
||||
|
||||
sprintf(buf, GCSPathColumnPattern, (i + 1));
|
||||
pathColumn = [[NSString alloc] initWithCString:buf];
|
||||
|
||||
/* Note: the AND addition must be inside the if's for non-exact stuff */
|
||||
|
||||
if (i < count) {
|
||||
/* exact match, regular column */
|
||||
if ([sql length] > 0) [sql appendString:@" AND "];
|
||||
[sql appendString:pathColumn];
|
||||
[sql appendFormat:@" = '%@'", [_names objectAtIndex:i]];
|
||||
}
|
||||
else if (_beExact) {
|
||||
/* exact match, ensure that all additional quick-cols are NULL */
|
||||
if ([sql length] > 0) [sql appendString:@" AND "];
|
||||
[sql appendString:pathColumn];
|
||||
[sql appendString:@" IS NULL"];
|
||||
if (debugPathTraversal) [self logWithFormat:@"BE EXACT, NULL columns"];
|
||||
}
|
||||
else if (_directSubs) {
|
||||
/* fetch immediate subfolders */
|
||||
if ([sql length] > 0) [sql appendString:@" AND "];
|
||||
[sql appendString:pathColumn];
|
||||
if (i == count) {
|
||||
/* if it is a direct subfolder, the next path cannot be empty */
|
||||
[sql appendString:@" IS NOT NULL"];
|
||||
if (debugPathTraversal)
|
||||
[self logWithFormat:@"DIRECT SUBS, first level"];
|
||||
}
|
||||
else {
|
||||
/* but for 'direct' subfolders, all following things must be empty */
|
||||
[sql appendString:@" IS NULL"];
|
||||
if (debugPathTraversal)
|
||||
[self logWithFormat:@"DIRECT SUBS, lower level"];
|
||||
}
|
||||
}
|
||||
|
||||
[pathColumn release];
|
||||
}
|
||||
|
||||
if (_beExact && (count > quickPathCount)) {
|
||||
[sql appendString:@" AND c_foldername = '"];
|
||||
[sql appendString:[_names lastObject]];
|
||||
[sql appendString:@"'"];
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
- (NSString *)generateSQLPathFetchForInternalNames:(NSArray *)_names
|
||||
exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs
|
||||
{
|
||||
/* fetches the 'path' subset for a given quick-names */
|
||||
NSMutableString *sql;
|
||||
NSString *ws;
|
||||
|
||||
ws = [self generateSQLWhereForInternalNames:_names
|
||||
exactMatch:_beExact orDirectSubfolderMatch:_directSubs];
|
||||
if ([ws length] == 0)
|
||||
return nil;
|
||||
|
||||
sql = [NSMutableString stringWithCapacity:256];
|
||||
[sql appendString:@"SELECT c_path FROM "];
|
||||
[sql appendString:[self folderInfoTableName]];
|
||||
[sql appendString:@" WHERE "];
|
||||
[sql appendString:ws];
|
||||
if (debugSQLGen) [self logWithFormat:@"PathFetch-SQL: %@", sql];
|
||||
return sql;
|
||||
}
|
||||
|
||||
/* handling folder names */
|
||||
|
||||
- (BOOL)_isStandardizedPath:(NSString *)_path {
|
||||
if (![_path isAbsolutePath]) return NO;
|
||||
if ([_path rangeOfString:@".."].length > 0) return NO;
|
||||
if ([_path rangeOfString:@"~"].length > 0) return NO;
|
||||
if ([_path rangeOfString:@"//"].length > 0) return NO;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)internalNameFromPath:(NSString *)_path {
|
||||
// TODO: ensure proper path and SQL escaping!
|
||||
|
||||
if (![self _isStandardizedPath:_path]) {
|
||||
[self debugWithFormat:@"%s: not a standardized path: '%@'",
|
||||
__PRETTY_FUNCTION__, _path];
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ([_path hasSuffix:@"/"] && [_path length] > 1)
|
||||
_path = [_path substringToIndex:([_path length] - 1)];
|
||||
|
||||
return _path;
|
||||
}
|
||||
- (NSArray *)internalNamesFromPath:(NSString *)_path {
|
||||
NSString *fname;
|
||||
NSArray *fnames;
|
||||
|
||||
if ((fname = [self internalNameFromPath:_path]) == nil)
|
||||
return nil;
|
||||
|
||||
if ([fname hasPrefix:@"/"])
|
||||
fname = [fname substringFromIndex:1];
|
||||
|
||||
fnames = [fname componentsSeparatedByString:@"/"];
|
||||
if ([fnames count] == 0)
|
||||
return nil;
|
||||
|
||||
return fnames;
|
||||
}
|
||||
- (NSString *)pathFromInternalName:(NSString *)_name {
|
||||
/* for incomplete pathes, like '/Users/helge/' */
|
||||
return _name;
|
||||
}
|
||||
- (NSString *)pathPartFromInternalName:(NSString *)_name {
|
||||
/* for incomplete pathes, like 'Users/' */
|
||||
return _name;
|
||||
}
|
||||
|
||||
- (NSDictionary *)filterRecords:(NSArray *)_records forPath:(NSString *)_path {
|
||||
unsigned i, count;
|
||||
NSString *name;
|
||||
|
||||
if (_records == nil) return nil;
|
||||
if ((name = [self internalNameFromPath:_path]) == nil) return nil;
|
||||
|
||||
for (i = 0, count = [_records count]; i < count; i++) {
|
||||
NSDictionary *record;
|
||||
NSString *recName;
|
||||
|
||||
record = [_records objectAtIndex:i];
|
||||
recName = [record objectForKey:GCSPathRecordName];
|
||||
#if 0
|
||||
[self logWithFormat:@"check '%@' vs '%@' (%@)...",
|
||||
name, recName, [_records objectAtIndex:i]];
|
||||
#endif
|
||||
|
||||
if ([name isEqualToString:recName])
|
||||
return [_records objectAtIndex:i];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)folderExistsAtPath:(NSString *)_path {
|
||||
NSString *fname;
|
||||
NSArray *fnames, *records;
|
||||
NSString *sql;
|
||||
unsigned count;
|
||||
|
||||
if ((fnames = [self internalNamesFromPath:_path]) == nil) {
|
||||
[self debugWithFormat:@"got no internal names for path: '%@'", _path];
|
||||
return NO;
|
||||
}
|
||||
|
||||
sql = [self generateSQLPathFetchForInternalNames:fnames
|
||||
exactMatch:YES orDirectSubfolderMatch:NO];
|
||||
if ([sql length] == 0) {
|
||||
[self debugWithFormat:@"got no SQL for names: %@", fnames];
|
||||
return NO;
|
||||
}
|
||||
|
||||
if ((records = [self performSQL:sql]) == nil) {
|
||||
[self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'",
|
||||
__PRETTY_FUNCTION__, sql];
|
||||
return NO;
|
||||
}
|
||||
|
||||
if ((count = [records count]) == 0)
|
||||
return NO;
|
||||
|
||||
fname = [self internalNameFromPath:_path];
|
||||
if (count == 1) {
|
||||
NSDictionary *record;
|
||||
NSString *sname;
|
||||
|
||||
record = [records objectAtIndex:0];
|
||||
sname = [record objectForKey:GCSPathRecordName];
|
||||
return [fname isEqualToString:sname];
|
||||
}
|
||||
|
||||
[self logWithFormat:@"records: %@", records];
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSArray *)listSubFoldersAtPath:(NSString *)_path recursive:(BOOL)_recursive{
|
||||
NSMutableArray *result;
|
||||
NSString *fname;
|
||||
NSArray *fnames, *records;
|
||||
NSString *sql;
|
||||
unsigned i, count;
|
||||
|
||||
if ((fnames = [self internalNamesFromPath:_path]) == nil) {
|
||||
[self debugWithFormat:@"got no internal names for path: '%@'", _path];
|
||||
return nil;
|
||||
}
|
||||
|
||||
sql = [self generateSQLPathFetchForInternalNames:fnames
|
||||
exactMatch:NO orDirectSubfolderMatch:(_recursive ? NO : YES)];
|
||||
if ([sql length] == 0) {
|
||||
[self debugWithFormat:@"got no SQL for names: %@", fnames];
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ((records = [self performSQL:sql]) == nil) {
|
||||
[self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'",
|
||||
__PRETTY_FUNCTION__, sql];
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ((count = [records count]) == 0)
|
||||
return emptyArray;
|
||||
|
||||
result = [NSMutableArray arrayWithCapacity:(count > 128 ? 128 : count)];
|
||||
|
||||
fname = [self internalNameFromPath:_path];
|
||||
fname = [fname stringByAppendingString:@"/"]; /* add slash */
|
||||
for (i = 0; i < count; i++) {
|
||||
NSDictionary *record;
|
||||
NSString *sname, *spath;
|
||||
|
||||
record = [records objectAtIndex:i];
|
||||
sname = [record objectForKey:GCSPathRecordName];
|
||||
if (![sname hasPrefix:fname]) /* does not match at all ... */
|
||||
continue;
|
||||
|
||||
/* strip prefix and following slash */
|
||||
sname = [sname substringFromIndex:[fname length]];
|
||||
spath = [self pathPartFromInternalName:sname];
|
||||
|
||||
if (_recursive) {
|
||||
if ([spath length] > 0) [result addObject:spath];
|
||||
}
|
||||
else {
|
||||
/* direct children only, so exclude everything with a slash */
|
||||
if ([sname rangeOfString:@"/"].length == 0 && [spath length] > 0)
|
||||
[result addObject:spath];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (GCSFolder *)folderAtPath:(NSString *)_path {
|
||||
NSMutableString *sql;
|
||||
NSArray *fnames, *records;
|
||||
NSString *ws;
|
||||
NSDictionary *record;
|
||||
|
||||
if ((fnames = [self internalNamesFromPath:_path]) == nil) {
|
||||
[self debugWithFormat:@"got no internal names for path: '%@'", _path];
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* generate SQL to fetch folder attributes */
|
||||
|
||||
ws = [self generateSQLWhereForInternalNames:fnames
|
||||
exactMatch:YES orDirectSubfolderMatch:NO];
|
||||
|
||||
sql = [NSMutableString stringWithCapacity:256];
|
||||
[sql appendString:@"SELECT "];
|
||||
[sql appendString:@"c_folder_id, "];
|
||||
[sql appendString:@"c_path, "];
|
||||
[sql appendString:@"c_location, c_quick_location, c_acl_location,"];
|
||||
[sql appendString:@" c_folder_type"];
|
||||
[sql appendString:@" FROM "];
|
||||
[sql appendString:[self folderInfoTableName]];
|
||||
[sql appendString:@" WHERE "];
|
||||
[sql appendString:ws];
|
||||
|
||||
if (debugSQLGen) [self logWithFormat:@"folderAtPath: %@", sql];
|
||||
|
||||
/* fetching */
|
||||
|
||||
if ((records = [self performSQL:sql]) == nil) {
|
||||
[self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'",
|
||||
__PRETTY_FUNCTION__, sql];
|
||||
return nil;
|
||||
}
|
||||
|
||||
// TODO: need to filter on path
|
||||
// required when we start to have deeper hierarchies
|
||||
// => isn't that already done below?
|
||||
|
||||
if ([records count] != 1) {
|
||||
if ([records count] == 0) {
|
||||
[self debugWithFormat:@"found no records for path: '%@'", _path];
|
||||
return nil;
|
||||
}
|
||||
|
||||
[self logWithFormat:@"ERROR(%s): more than one row for path: '%@'",
|
||||
__PRETTY_FUNCTION__, _path];
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ((record = [self filterRecords:records forPath:_path]) == nil) {
|
||||
[self debugWithFormat:@"found no record for path: '%@'", _path];
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [self folderForRecord:record];
|
||||
}
|
||||
|
||||
- (NSString *)baseTableNameWithUID:(NSString *)_uid {
|
||||
NSDate *now;
|
||||
unichar currentChar;
|
||||
unsigned int count, max, done;
|
||||
NSMutableString *newUID;
|
||||
|
||||
newUID = [NSMutableString string];
|
||||
now = [NSDate date];
|
||||
|
||||
max = [_uid length];
|
||||
done = 0;
|
||||
count = 0;
|
||||
while (done < 8 && count < max)
|
||||
{
|
||||
currentChar = [_uid characterAtIndex: count];
|
||||
if ([asciiAlphaNumericCS characterIsMember: currentChar])
|
||||
{
|
||||
[newUID appendFormat: @"%c", currentChar];
|
||||
done++;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
return [NSString stringWithFormat: @"%@%u",
|
||||
newUID, [now timeIntervalSince1970]];
|
||||
}
|
||||
|
||||
- (NSException *)createFolderOfType:(NSString *)_type
|
||||
withName:(NSString*)_name atPath:(NSString *)_path
|
||||
{
|
||||
// TBD: badly broken, needs to be wrapped in a transaction.
|
||||
// TBD: would be best to perform all operations as a single SQL statement.
|
||||
GCSFolderType *ftype;
|
||||
NSString *tableName, *quickTableName, *aclTableName;
|
||||
NSString *baseURL, *pathElement;
|
||||
EOAdaptorChannel <GCSEOAdaptorChannel> *channel;
|
||||
NSEnumerator *pathElements;
|
||||
NSMutableArray *paths;
|
||||
NSException *error;
|
||||
NSString *sql;
|
||||
|
||||
paths = [[NSMutableArray alloc] initWithCapacity: 5];
|
||||
|
||||
pathElements = [[_path componentsSeparatedByString: @"/"] objectEnumerator];
|
||||
while ((pathElement = [pathElements nextObject]) != nil) {
|
||||
NSString *p = [[NSString alloc] initWithFormat: @"'%@'", pathElement];
|
||||
[paths addObject: p];
|
||||
[p release]; p = nil;
|
||||
}
|
||||
while ([paths count] < 5)
|
||||
[paths addObject: @"NULL"];
|
||||
|
||||
// TBD: fix SQL injection issue!
|
||||
sql = [NSString stringWithFormat: @"SELECT * FROM %@ WHERE c_path = '%@'",
|
||||
[self folderInfoTableName], _path];
|
||||
if ([[self performSQL: sql] isNotEmpty]) {
|
||||
return [NSException exceptionWithName:@"GCSExitingFolder"
|
||||
reason:@"a folder already exists at that path"
|
||||
userInfo:nil];
|
||||
}
|
||||
if ((ftype = [self folderTypeWithName:_type]) == nil) {
|
||||
return [NSException exceptionWithName:@"GCSMissingFolderType"
|
||||
reason:@"missing folder type"userInfo:nil];
|
||||
}
|
||||
if ((channel = [self acquireOpenChannel]) == nil) {
|
||||
return [NSException exceptionWithName:@"GCSNoChannel"
|
||||
reason:@"could not open channel"
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
tableName = [self baseTableNameWithUID: [paths objectAtIndex: 2]];
|
||||
quickTableName = [tableName stringByAppendingString: @"_quick"];
|
||||
aclTableName = [tableName stringByAppendingString: @"_acl"];
|
||||
|
||||
sql = [@"DROP TABLE " stringByAppendingString:quickTableName];
|
||||
if ((error = [channel evaluateExpressionX:sql]) != nil)
|
||||
; // 'DROP TABLE' is allowed to fail (DROP IF EXISTS is not in PG<8.2)
|
||||
|
||||
sql = [@"DROP TABLE " stringByAppendingString:tableName];
|
||||
if ((error = [channel evaluateExpressionX:sql]) != nil)
|
||||
; // 'DROP TABLE' is allowed to fail (DROP IF EXISTS is not in PG<8.2)
|
||||
|
||||
sql = [@"DROP TABLE " stringByAppendingString:aclTableName];
|
||||
if ((error = [channel evaluateExpressionX:sql]) != nil)
|
||||
; // 'DROP TABLE' is allowed to fail (DROP IF EXISTS is not in PG<8.2)
|
||||
|
||||
if ((error = [channel createGCSFolderTableWithName: tableName]) != nil)
|
||||
return error;
|
||||
|
||||
sql = [ftype sqlQuickCreateWithTableName: quickTableName];
|
||||
if (debugSQLGen) [self logWithFormat:@"quick-Create: %@", sql];
|
||||
|
||||
if ((error = [channel evaluateExpressionX:sql]) != nil) {
|
||||
/* 'rollback' TBD: wrap in proper tx */
|
||||
sql = [@"DROP TABLE " stringByAppendingString:tableName];
|
||||
if ((error = [channel evaluateExpressionX:sql]) != nil) {
|
||||
[self warnWithFormat:@"failed to drop freshly created table: %@",
|
||||
tableName];
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
if (debugSQLGen) [self logWithFormat:@"acl-Create: %@", sql];
|
||||
if ((error = [channel createGCSFolderACLTableWithName: aclTableName])
|
||||
!= nil) {
|
||||
/* 'rollback' TBD: wrap in proper tx */
|
||||
sql = [@"DROP TABLE " stringByAppendingString:quickTableName];
|
||||
if ((error = [channel evaluateExpressionX:sql]) != nil) {
|
||||
[self warnWithFormat:@"failed to drop freshly created table: %@",
|
||||
tableName];
|
||||
}
|
||||
sql = [@"DROP TABLE " stringByAppendingString:tableName];
|
||||
if ((error = [channel evaluateExpressionX:sql]) != nil) {
|
||||
[self warnWithFormat:@"failed to drop freshly created table: %@",
|
||||
tableName];
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
// TBD: fix SQL injection issues
|
||||
baseURL
|
||||
= [[folderInfoLocation absoluteString] stringByDeletingLastPathComponent];
|
||||
|
||||
sql = [NSString stringWithFormat: @"INSERT INTO %@"
|
||||
@" (c_path, c_path1, c_path2, c_path3, c_path4,"
|
||||
@" c_foldername, c_location, c_quick_location,"
|
||||
@" c_acl_location, c_folder_type)"
|
||||
@" VALUES ('%@', %@, %@, %@, %@, '%@', '%@/%@',"
|
||||
@" '%@/%@', '%@/%@', '%@')",
|
||||
[self folderInfoTableName], _path,
|
||||
[paths objectAtIndex: 1], [paths objectAtIndex: 2],
|
||||
[paths objectAtIndex: 3], [paths objectAtIndex: 4],
|
||||
_name,
|
||||
baseURL, tableName,
|
||||
baseURL, quickTableName,
|
||||
baseURL, aclTableName,
|
||||
_type];
|
||||
if ((error = [channel evaluateExpressionX:sql]) != nil)
|
||||
return error;
|
||||
|
||||
[paths release]; paths = nil;
|
||||
[self releaseChannel: channel];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSException *)deleteFolderAtPath:(NSString *)_path {
|
||||
GCSFolder *folder;
|
||||
NSArray *fnames;
|
||||
NSString *sql, *ws;
|
||||
EOAdaptorChannel *channel;
|
||||
NSException *ex;
|
||||
|
||||
if ((folder = [self folderAtPath:_path]) == nil) {
|
||||
return [NSException exceptionWithName:@"GCSMissingFolder"
|
||||
reason:@"missing folder"
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
if ((fnames = [self internalNamesFromPath:_path]) == nil) {
|
||||
[self debugWithFormat:@"got no internal names for path: '%@'", _path];
|
||||
return nil;
|
||||
}
|
||||
|
||||
ws = [self generateSQLWhereForInternalNames:fnames
|
||||
exactMatch:YES orDirectSubfolderMatch:NO];
|
||||
|
||||
sql = [NSString stringWithFormat: @"DELETE FROM %@ WHERE %@",
|
||||
[self folderInfoTableName], ws];
|
||||
if ((channel = [self acquireOpenChannel]) == nil) {
|
||||
return [NSException exceptionWithName:@"GCSNoChannel"
|
||||
reason:@"could not "
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
if ((ex = [channel evaluateExpressionX:sql]) != nil) {
|
||||
[self releaseChannel:channel];
|
||||
return ex;
|
||||
}
|
||||
|
||||
[self releaseChannel:channel];
|
||||
|
||||
return [folder deleteFolder];
|
||||
}
|
||||
|
||||
/* folder types */
|
||||
|
||||
- (GCSFolderType *)folderTypeWithName:(NSString *)_name {
|
||||
NSString *specificName;
|
||||
GCSFolderType *type;
|
||||
|
||||
if ([_name length] == 0)
|
||||
_name = GCSGenericFolderTypeName;
|
||||
|
||||
specificName = [NSString stringWithFormat: @"%@-%@",
|
||||
_name, [folderInfoLocation scheme]];
|
||||
type = [self->nameToType objectForKey: [specificName lowercaseString]];
|
||||
if (!type)
|
||||
type = [self->nameToType objectForKey:[_name lowercaseString]];
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/* cache management */
|
||||
|
||||
- (void)reset {
|
||||
/* does nothing in the moment, but we need a way to signal refreshes */
|
||||
}
|
||||
|
||||
/* debugging */
|
||||
|
||||
- (BOOL)isDebuggingEnabled {
|
||||
return debugOn;
|
||||
}
|
||||
|
||||
/* description */
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableString *ms;
|
||||
|
||||
ms = [NSMutableString stringWithCapacity:256];
|
||||
[ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
|
||||
|
||||
[ms appendFormat:@" url=%@", [self->folderInfoLocation absoluteString]];
|
||||
[ms appendFormat:@" channel-manager=0x%p", [self channelManager]];
|
||||
|
||||
[ms appendString:@">"];
|
||||
return ms;
|
||||
}
|
||||
|
||||
@end /* GCSFolderManager */
|
||||
82
SOPE/sope-gdl1/GDLContentStore/GCSFolderType.h
Normal file
82
SOPE/sope-gdl1/GDLContentStore/GCSFolderType.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GDLContentStore_GCSFolderType_H__
|
||||
#define __GDLContentStore_GCSFolderType_H__
|
||||
|
||||
/*
|
||||
GCSFolderType
|
||||
|
||||
This is the "model" of a folder, it specifies what quick attributes are
|
||||
available, in which tables it is stored and how it is retrieved.
|
||||
|
||||
For now, we only support one 'quick' table (we might want to have multiple
|
||||
ones in the future).
|
||||
|
||||
Note: using the 'folderQualifier' we are actually prepared for 'multiple
|
||||
folders in a single table' setups. So in case we want to go that
|
||||
route later, we can still do it :-)
|
||||
*/
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class NSString, NSArray, NSDictionary;
|
||||
@class EOQualifier;
|
||||
@class NGResourceLocator;
|
||||
@class GCSFolder, GCSFieldExtractor;
|
||||
|
||||
@interface GCSFolderType : NSObject
|
||||
{
|
||||
NSString *blobTablePattern; // eg 'SOGo_$folderId$_blob
|
||||
NSString *quickTablePattern; // eg 'SOGo_$folderId$_quick
|
||||
NSArray *fields; // GCSFieldInfo objects
|
||||
NSDictionary *fieldDict; // maps a name to GCSFieldInfo
|
||||
EOQualifier *folderQualifier; // to further limit the table set
|
||||
NSString *extractorClassName;
|
||||
GCSFieldExtractor *extractor;
|
||||
}
|
||||
|
||||
+ (id)folderTypeWithName:(NSString *)_type;
|
||||
|
||||
- (id)initWithPropertyList:(id)_plist;
|
||||
- (id)initWithContentsOfFile:(NSString *)_path;
|
||||
- (id)initWithFolderTypeName:(NSString *)_typeName;
|
||||
|
||||
/* operations */
|
||||
|
||||
- (NSString *)blobTableNameForFolder:(GCSFolder *)_folder;
|
||||
- (NSString *)quickTableNameForFolder:(GCSFolder *)_folder;
|
||||
|
||||
/* generating SQL */
|
||||
|
||||
- (NSString *)sqlQuickCreateWithTableName:(NSString *)_tabName;
|
||||
|
||||
/* quick support */
|
||||
|
||||
- (GCSFieldExtractor *)quickExtractor;
|
||||
|
||||
/* locator used to find .ocs files */
|
||||
|
||||
+ (NGResourceLocator *)resourceLocator;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __GDLContentStore_GCSFolderType_H__ */
|
||||
198
SOPE/sope-gdl1/GDLContentStore/GCSFolderType.m
Normal file
198
SOPE/sope-gdl1/GDLContentStore/GCSFolderType.m
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "GCSFolderType.h"
|
||||
#include "GCSFolder.h"
|
||||
#include "GCSFieldInfo.h"
|
||||
#include "GCSFieldExtractor.h"
|
||||
#include "common.h"
|
||||
#include <EOControl/EOKeyValueCoding.h>
|
||||
#include <NGExtensions/NGResourceLocator.h>
|
||||
|
||||
@implementation GCSFolderType
|
||||
|
||||
- (id)initWithPropertyList:(id)_plist {
|
||||
if ((self = [super init])) {
|
||||
NSDictionary *plist = _plist;
|
||||
|
||||
self->blobTablePattern = [[plist objectForKey:@"blobTablePattern"] copy];
|
||||
self->quickTablePattern = [[plist objectForKey:@"quickTablePattern"]copy];
|
||||
|
||||
self->extractorClassName =
|
||||
[[plist objectForKey:@"extractorClassName"] copy];
|
||||
// TODO: qualifier;
|
||||
|
||||
self->fields = [[GCSFieldInfo fieldsForPropertyList:
|
||||
[plist objectForKey:@"fields"]] retain];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithContentsOfFile:(NSString *)_path {
|
||||
NSDictionary *plist;
|
||||
|
||||
plist = [NSDictionary dictionaryWithContentsOfFile:_path];
|
||||
if (plist == nil) {
|
||||
NSLog(@"ERROR(%s): could not read dictionary at path %@",
|
||||
__PRETTY_FUNCTION__, _path);
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
return [self initWithPropertyList:plist];
|
||||
}
|
||||
|
||||
+ (NGResourceLocator *)resourceLocator {
|
||||
NGResourceLocator *loc;
|
||||
|
||||
// TODO: fix me, GCS instead of OCS
|
||||
loc = [NGResourceLocator resourceLocatorForGNUstepPath:
|
||||
@"Library/OCSTypeModels"
|
||||
fhsPath:@"share/ocs"];
|
||||
return loc;
|
||||
}
|
||||
|
||||
+ (id)folderTypeWithName:(NSString *)_typeName {
|
||||
NSString *filename, *path;
|
||||
|
||||
// TODO: fix me, GCS instead of OCS
|
||||
filename = [_typeName stringByAppendingPathExtension:@"ocs"];
|
||||
path = [[self resourceLocator] lookupFileWithName:filename];
|
||||
|
||||
if (path != nil)
|
||||
return [[self alloc] initWithContentsOfFile:path];
|
||||
|
||||
NSLog(@"ERROR(%s): did not find model for type: '%@'",
|
||||
__PRETTY_FUNCTION__, _typeName);
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)initWithFolderTypeName:(NSString *)_typeName {
|
||||
// DEPRECATED
|
||||
[self release];
|
||||
return [[GCSFolderType folderTypeWithName:_typeName] retain];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self->extractor release];
|
||||
[self->extractorClassName release];
|
||||
[self->blobTablePattern release];
|
||||
[self->quickTablePattern release];
|
||||
[self->fields release];
|
||||
[self->fieldDict release];
|
||||
[self->folderQualifier release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* operations */
|
||||
|
||||
- (NSString *)blobTableNameForFolder:(GCSFolder *)_folder {
|
||||
return [self->blobTablePattern
|
||||
stringByReplacingVariablesWithBindings:_folder];
|
||||
}
|
||||
- (NSString *)quickTableNameForFolder:(GCSFolder *)_folder {
|
||||
return [self->quickTablePattern
|
||||
stringByReplacingVariablesWithBindings:_folder];
|
||||
}
|
||||
|
||||
- (EOQualifier *)qualifierForFolder:(GCSFolder *)_folder {
|
||||
NSArray *keys;
|
||||
NSDictionary *bindings;
|
||||
|
||||
keys = [[self->folderQualifier allQualifierKeys] allObjects];
|
||||
if ([keys count] == 0)
|
||||
return self->folderQualifier;
|
||||
|
||||
bindings = [_folder valuesForKeys:keys];
|
||||
return [self->folderQualifier qualifierWithBindings:bindings
|
||||
requiresAllVariables:NO];
|
||||
}
|
||||
|
||||
/* generating SQL */
|
||||
|
||||
- (NSString *)sqlQuickCreateWithTableName:(NSString *)_tabName {
|
||||
NSMutableString *sql;
|
||||
unsigned i, count;
|
||||
|
||||
sql = [NSMutableString stringWithCapacity:512];
|
||||
[sql appendString:@"CREATE TABLE "];
|
||||
[sql appendString:_tabName];
|
||||
[sql appendString:@" (\n"];
|
||||
|
||||
count = [self->fields count];
|
||||
for (i = 0; i < count; i++) {
|
||||
if (i > 0) [sql appendString:@",\n"];
|
||||
|
||||
[sql appendString:@" "];
|
||||
[sql appendString:[[self->fields objectAtIndex:i] sqlCreateSection]];
|
||||
}
|
||||
|
||||
[sql appendString:@"\n)"];
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
/* quick support */
|
||||
|
||||
- (GCSFieldExtractor *)quickExtractor {
|
||||
Class clazz;
|
||||
|
||||
if (self->extractor != nil) {
|
||||
return [self->extractor isNotNull]
|
||||
? self->extractor : (GCSFieldExtractor *)nil;
|
||||
}
|
||||
|
||||
clazz = self->extractorClassName
|
||||
? NSClassFromString(self->extractorClassName)
|
||||
: [GCSFieldExtractor class];
|
||||
if (clazz == Nil) {
|
||||
[self logWithFormat:@"ERROR: did not find field extractor class!"];
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ((self->extractor = [[clazz alloc] init]) == nil) {
|
||||
[self logWithFormat:@"ERROR: could not create field extractor of class %@",
|
||||
clazz];
|
||||
return nil;
|
||||
}
|
||||
return self->extractor;
|
||||
}
|
||||
|
||||
/* description */
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableString *ms;
|
||||
|
||||
ms = [NSMutableString stringWithCapacity:256];
|
||||
[ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
|
||||
|
||||
[ms appendFormat:@" blobtable='%@'", self->blobTablePattern];
|
||||
[ms appendFormat:@" quicktable='%@'", self->quickTablePattern];
|
||||
[ms appendFormat:@" fields=%@", self->fields];
|
||||
[ms appendFormat:@" extractor=%@", self->extractorClassName];
|
||||
|
||||
if (self->folderQualifier)
|
||||
[ms appendFormat:@" qualifier=%@", self->folderQualifier];
|
||||
|
||||
[ms appendString:@">"];
|
||||
return ms;
|
||||
}
|
||||
|
||||
@end /* GCSFolderType */
|
||||
37
SOPE/sope-gdl1/GDLContentStore/GCSStringFormatter.h
Normal file
37
SOPE/sope-gdl1/GDLContentStore/GCSStringFormatter.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GDLContentStore_GCSStringFormatter_H__
|
||||
#define __GDLContentStore_GCSStringFormatter_H__
|
||||
|
||||
#include <NGExtensions/NSString+Escaping.h>
|
||||
|
||||
@interface GCSStringFormatter : NSObject < NGStringEscaping >
|
||||
{
|
||||
}
|
||||
|
||||
+ (id)sharedFormatter;
|
||||
|
||||
- (NSString *)stringByFormattingString:(NSString *)_s;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __GDLContentStore_GCSStringFormatter_H__ */
|
||||
63
SOPE/sope-gdl1/GDLContentStore/GCSStringFormatter.m
Normal file
63
SOPE/sope-gdl1/GDLContentStore/GCSStringFormatter.m
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "GCSStringFormatter.h"
|
||||
#include "common.h"
|
||||
|
||||
@implementation GCSStringFormatter
|
||||
|
||||
static NSCharacterSet *escapeSet = nil;
|
||||
|
||||
+ (void)initialize {
|
||||
static BOOL didInit = NO;
|
||||
|
||||
if(didInit)
|
||||
return;
|
||||
|
||||
didInit = YES;
|
||||
escapeSet =
|
||||
[[NSCharacterSet characterSetWithCharactersInString:@"\\'"] retain];
|
||||
}
|
||||
|
||||
+ (id)sharedFormatter {
|
||||
static id sharedInstance = nil;
|
||||
if(!sharedInstance) {
|
||||
sharedInstance = [[self alloc] init];
|
||||
}
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (NSString *)stringByFormattingString:(NSString *)_s {
|
||||
NSString *s;
|
||||
|
||||
s = [_s stringByEscapingCharactersFromSet:escapeSet
|
||||
usingStringEscaping:self];
|
||||
return [NSString stringWithFormat:@"'%@'", s];
|
||||
}
|
||||
|
||||
- (NSString *)stringByEscapingString:(NSString *)_s {
|
||||
if([_s isEqualToString:@"\\"]) {
|
||||
return @"\\\\"; /* easy ;-) */
|
||||
}
|
||||
return @"\\'";
|
||||
}
|
||||
|
||||
@end /* GCSStringFormatter */
|
||||
77
SOPE/sope-gdl1/GDLContentStore/GNUmakefile
Normal file
77
SOPE/sope-gdl1/GDLContentStore/GNUmakefile
Normal file
@@ -0,0 +1,77 @@
|
||||
# GNUstep makefiles
|
||||
|
||||
include ../../../config.make
|
||||
include $(GNUSTEP_MAKEFILES)/common.make
|
||||
include ./Version
|
||||
|
||||
GNUSTEP_INSTALLATION_DIR = ${GNUSTEP_LOCAL_ROOT}
|
||||
|
||||
ifneq ($(frameworks),yes)
|
||||
LIBRARY_NAME = libGDLContentStore
|
||||
else
|
||||
FRAMEWORK_NAME = GDLContentStore
|
||||
endif
|
||||
|
||||
libGDLContentStore_PCH_FILE = common.h
|
||||
libGDLContentStore_SOVERSION=$(MAJOR_VERSION).$(MINOR_VERSION)
|
||||
libGDLContentStore_VERSION=$(MAJOR_VERSION).$(MINOR_VERSION).$(SUBMINOR_VERSION)
|
||||
|
||||
TOOL_NAME = gcs_ls gcs_mkdir gcs_cat gcs_recreatequick gcs_gensql
|
||||
|
||||
libGDLContentStore_HEADER_FILES_DIR = .
|
||||
libGDLContentStore_HEADER_FILES_INSTALL_DIR = /GDLContentStore
|
||||
FHS_HEADER_FILES_INSTALL_DIR = $(libGDLContentStore_HEADER_FILES_INSTALL_DIR)
|
||||
|
||||
libGDLContentStore_HEADER_FILES += \
|
||||
NSURL+GCS.h \
|
||||
EOAdaptorChannel+GCS.h \
|
||||
\
|
||||
GCSContext.h \
|
||||
GCSFieldInfo.h \
|
||||
GCSFolder.h \
|
||||
GCSFolderManager.h \
|
||||
GCSFolderType.h \
|
||||
GCSChannelManager.h \
|
||||
GCSFieldExtractor.h \
|
||||
GCSStringFormatter.h \
|
||||
|
||||
libGDLContentStore_OBJC_FILES += \
|
||||
NSURL+GCS.m \
|
||||
EOAdaptorChannel+GCS.m \
|
||||
EOQualifier+GCS.m \
|
||||
\
|
||||
GCSContext.m \
|
||||
GCSFieldInfo.m \
|
||||
GCSFolder.m \
|
||||
GCSFolderManager.m \
|
||||
GCSFolderType.m \
|
||||
GCSChannelManager.m \
|
||||
GCSFieldExtractor.m \
|
||||
GCSStringFormatter.m \
|
||||
|
||||
gcs_ls_OBJC_FILES += gcs_ls.m
|
||||
gcs_mkdir_OBJC_FILES += gcs_mkdir.m
|
||||
gcs_cat_OBJC_FILES += gcs_cat.m
|
||||
gcs_gensql_OBJC_FILES += gcs_gensql.m
|
||||
gcs_recreatequick_OBJC_FILES += gcs_recreatequick.m
|
||||
|
||||
|
||||
# framework support
|
||||
|
||||
GDLContentStore_PCH_FILE = $(libGDLContentStore_PCH_FILE)
|
||||
GDLContentStore_HEADER_FILES = $(libGDLContentStore_HEADER_FILES)
|
||||
GDLContentStore_OBJC_FILES = $(libGDLContentStore_OBJC_FILES)
|
||||
GDLContentStore_SUBPROJECTS = $(libGDLContentStore_SUBPROJECTS)
|
||||
|
||||
|
||||
# building
|
||||
|
||||
-include GNUmakefile.preamble
|
||||
ifneq ($(frameworks),yes)
|
||||
include $(GNUSTEP_MAKEFILES)/library.make
|
||||
else
|
||||
include $(GNUSTEP_MAKEFILES)/framework.make
|
||||
endif
|
||||
include $(GNUSTEP_MAKEFILES)/tool.make
|
||||
-include GNUmakefile.postamble
|
||||
include fhs.make
|
||||
78
SOPE/sope-gdl1/GDLContentStore/GNUmakefile.preamble
Normal file
78
SOPE/sope-gdl1/GDLContentStore/GNUmakefile.preamble
Normal file
@@ -0,0 +1,78 @@
|
||||
# compilation settings
|
||||
|
||||
SOPE_ROOT=../..
|
||||
|
||||
ADDITIONAL_CPPFLAGS += -Wall
|
||||
|
||||
ADDITIONAL_INCLUDE_DIRS += -I. -I..
|
||||
|
||||
|
||||
# dependencies
|
||||
|
||||
libGDLContentStore_LIBRARIES_DEPEND_UPON += \
|
||||
-lGDLAccess \
|
||||
-lNGExtensions -lEOControl \
|
||||
-lDOM -lSaxObjC
|
||||
|
||||
GDLContentStore_LIBRARIES_DEPEND_UPON += \
|
||||
-framework GDLAccess \
|
||||
-framework NGExtensions -framework EOControl \
|
||||
-framework DOM -framework SaxObjC
|
||||
|
||||
ifneq ($(frameworks),yes)
|
||||
GCS_TOOL_LIBS += \
|
||||
-lGDLContentStore -lGDLAccess \
|
||||
-lNGExtensions -lEOControl \
|
||||
-lDOM -lSaxObjC
|
||||
else
|
||||
GCS_TOOL_LIBS += \
|
||||
-framework GDLContentStore -framework GDLAccess \
|
||||
-framework NGExtensions -framework EOControl \
|
||||
-framework DOM -framework SaxObjC
|
||||
endif
|
||||
|
||||
gcs_ls_TOOL_LIBS += $(GCS_TOOL_LIBS)
|
||||
gcs_mkdir_TOOL_LIBS += $(GCS_TOOL_LIBS)
|
||||
gcs_cat_TOOL_LIBS += $(GCS_TOOL_LIBS)
|
||||
gcs_recreatequick_TOOL_LIBS += $(GCS_TOOL_LIBS)
|
||||
gcs_gensql_TOOL_LIBS += $(GCS_TOOL_LIBS)
|
||||
|
||||
gcs_ls_PCH_FILE += common.h
|
||||
gcs_mkdir_PCH_FILE += common.h
|
||||
gcs_cat_PCH_FILE += common.h
|
||||
gcs_recreatequick_PCH_FILE += common.h
|
||||
gcs_gensql_PCH_FILE += common.h
|
||||
|
||||
|
||||
# library/framework search pathes
|
||||
|
||||
DEP_DIRS = \
|
||||
. \
|
||||
../GDLAccess \
|
||||
$(SOPE_ROOT)/sope-core/NGExtensions \
|
||||
$(SOPE_ROOT)/sope-core/EOControl \
|
||||
$(SOPE_ROOT)/sope-xml/DOM \
|
||||
$(SOPE_ROOT)/sope-xml/SaxObjC
|
||||
|
||||
ifneq ($(frameworks),yes)
|
||||
ADDITIONAL_LIB_DIRS += \
|
||||
$(foreach dir,$(DEP_DIRS),\
|
||||
-L$(GNUSTEP_BUILD_DIR)/$(dir)/$(GNUSTEP_OBJ_DIR_NAME))
|
||||
else
|
||||
ADDITIONAL_LIB_DIRS += \
|
||||
$(foreach dir,$(DEP_DIRS),-F$(GNUSTEP_BUILD_DIR)/$(dir))
|
||||
endif
|
||||
|
||||
SYSTEM_LIB_DIR += $(CONFIGURE_SYSTEM_LIB_DIR)
|
||||
|
||||
|
||||
# platform specific settings
|
||||
|
||||
ifeq ($(FOUNDATION_LIB),apple)
|
||||
libGDLContentStore_PREBIND_ADDR="0xC7700000"
|
||||
libGDLContentStore_LDFLAGS += -seg1addr $(libGDLContentStore_PREBIND_ADDR)
|
||||
endif
|
||||
|
||||
ifeq ($(findstring openbsd3, $(GNUSTEP_HOST_OS)), openbsd3)
|
||||
GCS_TOOL_LIBS += -liconv
|
||||
endif
|
||||
41
SOPE/sope-gdl1/GDLContentStore/NSURL+GCS.h
Normal file
41
SOPE/sope-gdl1/GDLContentStore/NSURL+GCS.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GDLContentStore_NSURL_GCS_H__
|
||||
#define __GDLContentStore_NSURL_GCS_H__
|
||||
|
||||
#import <Foundation/NSURL.h>
|
||||
|
||||
/*
|
||||
"Database URLs"
|
||||
|
||||
We use the schema:
|
||||
postgresql://[user]:[password]@[host]:[port]/[dbname]/[tablename]
|
||||
*/
|
||||
|
||||
@interface NSURL(GCS)
|
||||
|
||||
- (NSString *)gcsDatabaseName;
|
||||
- (NSString *)gcsTableName;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __GDLContentStore_NSURL_GCS_H__ */
|
||||
51
SOPE/sope-gdl1/GDLContentStore/NSURL+GCS.m
Normal file
51
SOPE/sope-gdl1/GDLContentStore/NSURL+GCS.m
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "NSURL+GCS.h"
|
||||
#include "common.h"
|
||||
|
||||
@implementation NSURL(GCS)
|
||||
|
||||
- (NSString *)gcsPathComponent:(unsigned)_idx {
|
||||
NSString *p;
|
||||
NSArray *pcs;
|
||||
unsigned len;
|
||||
|
||||
p = [self path];
|
||||
if ([p length] == 0)
|
||||
return nil;
|
||||
|
||||
pcs = [p componentsSeparatedByString:@"/"];
|
||||
if ((len = [pcs count]) == 0)
|
||||
return nil;
|
||||
if (len <= _idx)
|
||||
return nil;
|
||||
return [pcs objectAtIndex:_idx];
|
||||
}
|
||||
|
||||
- (NSString *)gcsDatabaseName {
|
||||
return [self gcsPathComponent:1];
|
||||
}
|
||||
- (NSString *)gcsTableName {
|
||||
return [[self path] lastPathComponent];
|
||||
}
|
||||
|
||||
@end /* NSURL(GCS) */
|
||||
117
SOPE/sope-gdl1/GDLContentStore/README
Normal file
117
SOPE/sope-gdl1/GDLContentStore/README
Normal file
@@ -0,0 +1,117 @@
|
||||
Storage Backend
|
||||
===============
|
||||
|
||||
The storage backend implements the "low level" folder abstraction, which is
|
||||
basically an arbitary "BLOB" containing some document. The feature is that
|
||||
we extract "quick access" / "searchable" attributes from the document content.
|
||||
|
||||
Further it contains the "folder management" API, as named folders can be stored
|
||||
in different databases.
|
||||
Note: we need a way to tell where "new" folders should be created
|
||||
Note: to sync with LDAP we need to periodically delete or archive old folders
|
||||
|
||||
Folders have associated a type (like 'calendar') which defines the query
|
||||
attributes and serialization format.
|
||||
|
||||
TODO
|
||||
====
|
||||
- fix some OCS naming
|
||||
- defaults
|
||||
- lookup directories
|
||||
- hierarchies deeper than 4 (properly filter on path in OCS)
|
||||
|
||||
Open Questions
|
||||
==============
|
||||
|
||||
System-meta-data in the blob-table or in the quick-table?
|
||||
- master data belongs into the blob table
|
||||
- could be regular 'NSxxx' keys to differentiate meta data from
|
||||
|
||||
Class Hierarchy
|
||||
===============
|
||||
|
||||
[NSObject]
|
||||
OCSContext - tracking context
|
||||
OCSFolder - represents a single folder
|
||||
OCSFolderManager - manages folders
|
||||
OCSFolderType - the mapping info for a specific folder-type
|
||||
OCSFieldInfo - mapping info for one 'quick field'
|
||||
OCSChannelManager - maintains EOAdaptorChannel objects
|
||||
|
||||
TBD:
|
||||
- field 'extractor'
|
||||
- field 'value' (eg array values for participants?)
|
||||
- BLOB archiver/unarchiver
|
||||
|
||||
Defaults
|
||||
========
|
||||
|
||||
OCSFolderInfoURL - the DB URL where the folder-info table is located
|
||||
eg: http://OGo:OGo@localhost/test/folder_info
|
||||
|
||||
OCSFolderManagerDebugEnabled - enable folder-manager debug logs
|
||||
OCSFolderManagerSQLDebugEnabled - enable folder-manager SQL gen debug logs
|
||||
|
||||
OCSChannelManagerDebugEnabled - enable channel debug pooling logs
|
||||
OCSChannelManagerPoolDebugEnabled - debug pool handle allocation
|
||||
|
||||
OCSChannelExpireAge - if that age in seconds is exceeded, a channel
|
||||
will be removed from the pool
|
||||
OCSChannelCollectionTimer - time in seconds. each n-seconds the pool will be
|
||||
checked for channels too old
|
||||
|
||||
[PGDebugEnabled] - enable PostgreSQL adaptor debugging
|
||||
|
||||
URLs
|
||||
====
|
||||
|
||||
"Database URLs"
|
||||
|
||||
We use the schema:
|
||||
postgresql://[user]:[password]@[host]:[port]/[dbname]/[tablename]
|
||||
|
||||
Support Tools
|
||||
=============
|
||||
|
||||
- tools we need:
|
||||
- one to recreate a quick table based on the blob table
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
- need to use http:// URLs for connect info, until generic URLs in
|
||||
libFoundation are fixed (the parses breaks on the login/password parts)
|
||||
|
||||
QA
|
||||
==
|
||||
|
||||
Q: Why do we use two tables, we could store the quick columns in the blob?
|
||||
==
|
||||
They could be in the same table. We considered using separate tables since the
|
||||
quick table is likely to be recreated now and then if BLOB indexing
|
||||
requirements change.
|
||||
Actually one could even use different _quick tables which share a common BLOB
|
||||
table.
|
||||
(a quick table is nothing more than a database index and like with DB indexes
|
||||
multiple ones for different requirements can make sense).
|
||||
|
||||
Further it might improve caching behaviour for row based caches (the quick
|
||||
table is going to be queried much more often) - not sure whether this is
|
||||
relevant with PostgreSQL, probably not?
|
||||
|
||||
Q: Can we use a VARCHAR primary key?
|
||||
==
|
||||
We asked in the postgres IRC channel and apparently the performance penalty of
|
||||
string primary keys isn't big.
|
||||
We could also use an 'internal' int sequence in addition (might be useful for
|
||||
supporting ZideLook)
|
||||
Motivation: the 'iCalendar' ID is a string and usually looks like a GUID.
|
||||
|
||||
Q: Why using VARCHAR instead of TEXT in the BLOB?
|
||||
==
|
||||
To quote PostgreSQL documentation:
|
||||
"There are no performance differences between these three types, apart from
|
||||
the increased storage size when using the blank-padded type."
|
||||
So varchar(xx) is just a large TEXT. Since we intend to store mostly small
|
||||
snippets of data (tiny XML fragments), we considered VARCHAR the more
|
||||
appropriate type.
|
||||
14
SOPE/sope-gdl1/GDLContentStore/Version
Normal file
14
SOPE/sope-gdl1/GDLContentStore/Version
Normal file
@@ -0,0 +1,14 @@
|
||||
# Version file
|
||||
|
||||
MAJOR_VERSION:=4
|
||||
MINOR_VERSION:=7
|
||||
SUBMINOR_VERSION:=49
|
||||
|
||||
# v4.5.29 requires libNGExtensions v4.5.161
|
||||
# v4.5.26 does not require libNGiCal anymore
|
||||
# v0.9.19 requires libNGiCal v4.5.40
|
||||
# v0.9.18 requires libNGiCal v4.5.38
|
||||
# v0.9.17 requires libNGiCal v4.5.37
|
||||
# v0.9.11 requires libFoundation v1.0.63
|
||||
# v0.9.11 requires libNGExtensions v4.3.125
|
||||
# v0.9.7 requires libGDLAccess v1.1.35
|
||||
36
SOPE/sope-gdl1/GDLContentStore/common.h
Normal file
36
SOPE/sope-gdl1/GDLContentStore/common.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright (C) 2004 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
// $Id: common.h 673 2005-03-20 19:09:53Z helge $
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
|
||||
#include <NGExtensions/NGExtensions.h>
|
||||
|
||||
#if NeXT_RUNTIME || APPLE_RUNTIME
|
||||
# define objc_free(__mem__) free(__mem__)
|
||||
# define objc_malloc(__size__) malloc(__size__)
|
||||
# define objc_calloc(__cnt__, __size__) calloc(__cnt__, __size__)
|
||||
# define objc_realloc(__ptr__, __size__) realloc(__ptr__, __size__)
|
||||
# ifndef sel_eq
|
||||
# define sel_eq(sela,selb) (sela==selb?YES:NO)
|
||||
# endif
|
||||
#endif
|
||||
44
SOPE/sope-gdl1/GDLContentStore/fhs.make
Normal file
44
SOPE/sope-gdl1/GDLContentStore/fhs.make
Normal file
@@ -0,0 +1,44 @@
|
||||
# postprocessing
|
||||
|
||||
# FHS support (this is a hack and is going to be done by gstep-make!)
|
||||
|
||||
# NOTE: you need to define FHS_HEADER_FILES_INSTALL_DIR for one library
|
||||
|
||||
ifneq ($(FHS_INSTALL_ROOT),)
|
||||
|
||||
FHS_INCLUDE_DIR=$(FHS_INSTALL_ROOT)/include/
|
||||
FHS_BIN_DIR=$(FHS_INSTALL_ROOT)/bin
|
||||
FHS_LIB_DIR=$(CONFIGURE_FHS_INSTALL_LIBDIR)
|
||||
|
||||
NONFHS_LIBDIR="$(GNUSTEP_LIBRARIES)/$(GNUSTEP_TARGET_LDIR)/"
|
||||
NONFHS_LIBNAME="$(LIBRARY_NAME)$(LIBRARY_NAME_SUFFIX)$(SHARED_LIBEXT)"
|
||||
NONFHS_BINDIR="$(GNUSTEP_TOOLS)/$(GNUSTEP_TARGET_LDIR)"
|
||||
|
||||
|
||||
fhs-header-dirs ::
|
||||
$(MKDIRS) $(FHS_INCLUDE_DIR)$(FHS_HEADER_FILES_INSTALL_DIR)
|
||||
|
||||
fhs-bin-dirs ::
|
||||
$(MKDIRS) $(FHS_BIN_DIR)
|
||||
|
||||
|
||||
move-headers-to-fhs :: fhs-header-dirs
|
||||
@echo "moving headers to $(FHS_INCLUDE_DIR) .."
|
||||
mv $(GNUSTEP_HEADERS)$(FHS_HEADER_FILES_INSTALL_DIR)/*.h \
|
||||
$(FHS_INCLUDE_DIR)$(FHS_HEADER_FILES_INSTALL_DIR)/
|
||||
|
||||
move-libs-to-fhs ::
|
||||
@echo "moving libs to $(FHS_LIB_DIR) .."
|
||||
mv $(NONFHS_LIBDIR)/$(NONFHS_LIBNAME)* $(FHS_LIB_DIR)/
|
||||
|
||||
move-tools-to-fhs :: fhs-bin-dirs
|
||||
@echo "moving tools from $(NONFHS_BINDIR) to $(FHS_BIN_DIR) .."
|
||||
for i in $(TOOL_NAME); do \
|
||||
mv "$(NONFHS_BINDIR)/$${i}" $(FHS_BIN_DIR); \
|
||||
done
|
||||
|
||||
move-to-fhs :: move-headers-to-fhs move-libs-to-fhs move-tools-to-fhs
|
||||
|
||||
after-install :: move-to-fhs
|
||||
|
||||
endif
|
||||
122
SOPE/sope-gdl1/GDLContentStore/gcs_cat.m
Normal file
122
SOPE/sope-gdl1/GDLContentStore/gcs_cat.m
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class NSUserDefaults, NSArray;
|
||||
@class GCSFolderManager;
|
||||
|
||||
@interface Tool : NSObject
|
||||
{
|
||||
NSUserDefaults *ud;
|
||||
GCSFolderManager *folderManager;
|
||||
}
|
||||
|
||||
+ (int)runWithArgs:(NSArray *)_args;
|
||||
- (int)run;
|
||||
|
||||
@end
|
||||
|
||||
#include <GDLContentStore/GCSFolder.h>
|
||||
#include <GDLContentStore/GCSFolderManager.h>
|
||||
#include "common.h"
|
||||
|
||||
@implementation Tool
|
||||
|
||||
- (id)init {
|
||||
if ((self = [super init])) {
|
||||
self->ud = [[NSUserDefaults standardUserDefaults] retain];
|
||||
self->folderManager = [[GCSFolderManager defaultFolderManager] retain];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (void)dealloc {
|
||||
[self->ud release];
|
||||
[self->folderManager release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* operation */
|
||||
|
||||
- (int)runOnPath:(NSString *)_path {
|
||||
GCSFolder *folder;
|
||||
NSString *dirname, *filename;
|
||||
NSString *content;
|
||||
|
||||
dirname = [_path stringByDeletingLastPathComponent];
|
||||
filename = [_path lastPathComponent];
|
||||
|
||||
if ((folder = [self->folderManager folderAtPath:dirname]) == nil) {
|
||||
[self logWithFormat:@"did not find folder for file: '%@'", dirname];
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((content = [folder fetchContentWithName:filename]) == nil) {
|
||||
[self logWithFormat:@"did not find file: '%@'", _path];
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("%s\n", [content cString]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (int)run {
|
||||
NSEnumerator *e;
|
||||
NSString *path;
|
||||
|
||||
[self logWithFormat:@"manager: %@", self->folderManager];
|
||||
|
||||
if (![self->folderManager canConnect]) {
|
||||
[self logWithFormat:@"cannot connect folder-info database!"];
|
||||
return 1;
|
||||
}
|
||||
|
||||
e = [[[NSProcessInfo processInfo] argumentsWithoutDefaults]
|
||||
objectEnumerator];
|
||||
[e nextObject]; // skip tool name
|
||||
|
||||
while ((path = [e nextObject]))
|
||||
[self runOnPath:path];
|
||||
|
||||
return 0;
|
||||
}
|
||||
+ (int)runWithArgs:(NSArray *)_args {
|
||||
return [(Tool *)[[[self alloc] init] autorelease] run];
|
||||
}
|
||||
|
||||
@end /* Tool */
|
||||
|
||||
int main(int argc, char **argv, char **env) {
|
||||
NSAutoreleasePool *pool;
|
||||
int rc;
|
||||
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
#if LIB_FOUNDATION_LIBRARY
|
||||
[NSProcessInfo initializeWithArguments:argv count:argc environment:env];
|
||||
#endif
|
||||
|
||||
rc = [Tool runWithArgs:
|
||||
[[NSProcessInfo processInfo] argumentsWithoutDefaults]];
|
||||
|
||||
[pool release];
|
||||
return rc;
|
||||
}
|
||||
114
SOPE/sope-gdl1/GDLContentStore/gcs_gensql.m
Normal file
114
SOPE/sope-gdl1/GDLContentStore/gcs_gensql.m
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
Copyright (C) 2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class NSUserDefaults, NSArray;
|
||||
@class GCSFolderManager;
|
||||
|
||||
@interface Tool : NSObject
|
||||
{
|
||||
NSUserDefaults *ud;
|
||||
}
|
||||
|
||||
+ (int)runWithArgs:(NSArray *)_args;
|
||||
- (int)run;
|
||||
|
||||
@end
|
||||
|
||||
#include <GDLContentStore/GCSFolderType.h>
|
||||
#include "common.h"
|
||||
|
||||
@implementation Tool
|
||||
|
||||
- (id)init {
|
||||
if ((self = [super init])) {
|
||||
self->ud = [[NSUserDefaults standardUserDefaults] retain];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (void)dealloc {
|
||||
[self->ud release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* operation */
|
||||
|
||||
- (int)runOnTable:(NSString *)_tableName typeName:(NSString *)_typeName {
|
||||
GCSFolderType *folderType;
|
||||
|
||||
if ((folderType = [GCSFolderType folderTypeWithName:_typeName]) != nil) {
|
||||
NSString *s;
|
||||
|
||||
s = [folderType sqlQuickCreateWithTableName:_tableName];
|
||||
|
||||
fwrite([s cString], 1, [s cStringLength], stdout);
|
||||
printf("\n");
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "ERROR: did not find GCS type: '%s'\n",
|
||||
[_typeName cString]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (int)run {
|
||||
NSEnumerator *e;
|
||||
NSString *tableName, *typeName;
|
||||
|
||||
e = [[[NSProcessInfo processInfo] argumentsWithoutDefaults]
|
||||
objectEnumerator];
|
||||
[e nextObject]; // skip tool name
|
||||
|
||||
while ((tableName = [e nextObject]) != nil) {
|
||||
typeName = [e nextObject];
|
||||
if (typeName == nil) {
|
||||
[self logWithFormat:@"got tablename '%@' but no type?!", tableName];
|
||||
break;
|
||||
}
|
||||
|
||||
[self runOnTable:tableName typeName:typeName];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+ (int)runWithArgs:(NSArray *)_args {
|
||||
return [(Tool *)[[[self alloc] init] autorelease] run];
|
||||
}
|
||||
|
||||
@end /* Tool */
|
||||
|
||||
int main(int argc, char **argv, char **env) {
|
||||
NSAutoreleasePool *pool;
|
||||
int rc;
|
||||
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
#if LIB_FOUNDATION_LIBRARY
|
||||
[NSProcessInfo initializeWithArguments:argv count:argc environment:env];
|
||||
#endif
|
||||
|
||||
rc = [Tool runWithArgs:
|
||||
[[NSProcessInfo processInfo] argumentsWithoutDefaults]];
|
||||
|
||||
[pool release];
|
||||
return rc;
|
||||
}
|
||||
140
SOPE/sope-gdl1/GDLContentStore/gcs_ls.m
Normal file
140
SOPE/sope-gdl1/GDLContentStore/gcs_ls.m
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class NSUserDefaults, NSArray;
|
||||
@class GCSFolderManager;
|
||||
|
||||
@interface Tool : NSObject
|
||||
{
|
||||
NSUserDefaults *ud;
|
||||
GCSFolderManager *folderManager;
|
||||
}
|
||||
|
||||
+ (int)runWithArgs:(NSArray *)_args;
|
||||
- (int)run;
|
||||
|
||||
@end
|
||||
|
||||
#include <GDLContentStore/GCSFolder.h>
|
||||
#include <GDLContentStore/GCSFolderManager.h>
|
||||
#include "common.h"
|
||||
|
||||
@implementation Tool
|
||||
|
||||
- (id)init {
|
||||
if ((self = [super init])) {
|
||||
self->ud = [[NSUserDefaults standardUserDefaults] retain];
|
||||
self->folderManager = [[GCSFolderManager defaultFolderManager] retain];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (void)dealloc {
|
||||
[self->ud release];
|
||||
[self->folderManager release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* operation */
|
||||
|
||||
- (int)runOnPath:(NSString *)_path {
|
||||
NSArray *subfolders;
|
||||
unsigned i, count;
|
||||
GCSFolder *folder;
|
||||
|
||||
[self logWithFormat:@"ls path: '%@'", _path];
|
||||
|
||||
#if 0 // we do not necessarily need the whole hierarchy
|
||||
if (![self->folderManager folderExistsAtPath:_path])
|
||||
[self logWithFormat:@"folder does not exist: '%@'", _path];
|
||||
#endif
|
||||
|
||||
subfolders = [self->folderManager
|
||||
listSubFoldersAtPath:_path
|
||||
recursive:[ud boolForKey:@"r"]];
|
||||
if (subfolders == nil) {
|
||||
[self logWithFormat:@"cannot list folder: '%@'", _path];
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0, count = [subfolders count]; i < count; i++) {
|
||||
printf("%s\n", [[subfolders objectAtIndex:i] cString]);
|
||||
}
|
||||
|
||||
folder = [self->folderManager folderAtPath:_path];
|
||||
|
||||
if ([folder isNotNull]) {
|
||||
NSLog(@"folder: %@", folder);
|
||||
|
||||
NSLog(@" can%s connect store: %@", [folder canConnectStore] ? "" : "not",
|
||||
[[folder location] absoluteString]);
|
||||
NSLog(@" can%s connect quick: %@", [folder canConnectQuick] ? "" : "not",
|
||||
[[folder quickLocation] absoluteString]);
|
||||
}
|
||||
else {
|
||||
NSLog(@"ERROR: could not create folder object for path: '%@'", _path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (int)run {
|
||||
NSEnumerator *e;
|
||||
NSString *path;
|
||||
|
||||
[self logWithFormat:@"manager: %@", self->folderManager];
|
||||
|
||||
if (![self->folderManager canConnect]) {
|
||||
[self logWithFormat:@"cannot connect folder-info database!"];
|
||||
return 1;
|
||||
}
|
||||
|
||||
e = [[[NSProcessInfo processInfo] argumentsWithoutDefaults]
|
||||
objectEnumerator];
|
||||
[e nextObject]; // skip tool name
|
||||
|
||||
while ((path = [e nextObject]) != nil)
|
||||
[self runOnPath:path];
|
||||
|
||||
return 0;
|
||||
}
|
||||
+ (int)runWithArgs:(NSArray *)_args {
|
||||
return [(Tool *)[[[self alloc] init] autorelease] run];
|
||||
}
|
||||
|
||||
@end /* Tool */
|
||||
|
||||
int main(int argc, char **argv, char **env) {
|
||||
NSAutoreleasePool *pool;
|
||||
int rc;
|
||||
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
#if LIB_FOUNDATION_LIBRARY
|
||||
[NSProcessInfo initializeWithArguments:argv count:argc environment:env];
|
||||
#endif
|
||||
|
||||
rc = [Tool runWithArgs:
|
||||
[[NSProcessInfo processInfo] argumentsWithoutDefaults]];
|
||||
|
||||
[pool release];
|
||||
return rc;
|
||||
}
|
||||
126
SOPE/sope-gdl1/GDLContentStore/gcs_mkdir.m
Normal file
126
SOPE/sope-gdl1/GDLContentStore/gcs_mkdir.m
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class NSUserDefaults, NSArray;
|
||||
@class GCSFolderManager;
|
||||
|
||||
@interface Tool : NSObject
|
||||
{
|
||||
NSUserDefaults *ud;
|
||||
GCSFolderManager *folderManager;
|
||||
}
|
||||
|
||||
+ (int)runWithArgs:(NSArray *)_args;
|
||||
- (int)run;
|
||||
|
||||
@end
|
||||
|
||||
#include <GDLContentStore/GCSFolder.h>
|
||||
#include <GDLContentStore/GCSFolderManager.h>
|
||||
#include "common.h"
|
||||
|
||||
@implementation Tool
|
||||
|
||||
- (id)init {
|
||||
if ((self = [super init])) {
|
||||
self->ud = [[NSUserDefaults standardUserDefaults] retain];
|
||||
self->folderManager = [[GCSFolderManager defaultFolderManager] retain];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (void)dealloc {
|
||||
[self->ud release];
|
||||
[self->folderManager release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* operation */
|
||||
|
||||
- (int)runOnPath:(NSString *)_path type:(NSString *)_type {
|
||||
NSException *error;
|
||||
|
||||
[self logWithFormat:@"mkdir %@ at path: '%@'", _type, _path];
|
||||
|
||||
if ([self->folderManager folderExistsAtPath:_path]) {
|
||||
[self logWithFormat:@"folder already exist at path: '%@'", _path];
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((error = [self->folderManager createFolderOfType:_type atPath:_path])) {
|
||||
[self logWithFormat:@"creation of folder %@ at %@ failed: %@",
|
||||
_type, _path, error];
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ([self->folderManager folderExistsAtPath:_path])
|
||||
[self logWithFormat:@"CREATED."];
|
||||
else
|
||||
[self logWithFormat:@"cannot find fresh folder?"];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (int)run {
|
||||
NSEnumerator *e;
|
||||
NSString *type;
|
||||
NSString *path;
|
||||
|
||||
[self logWithFormat:@"manager: %@", self->folderManager];
|
||||
|
||||
if (![self->folderManager canConnect]) {
|
||||
[self logWithFormat:@"cannot connect folder-info database!"];
|
||||
return 1;
|
||||
}
|
||||
|
||||
e = [[[NSProcessInfo processInfo] argumentsWithoutDefaults]
|
||||
objectEnumerator];
|
||||
[e nextObject]; // skip tool name
|
||||
|
||||
type = [[[e nextObject] copy] autorelease];
|
||||
|
||||
while ((path = [e nextObject]))
|
||||
[self runOnPath:path type:type];
|
||||
|
||||
return 0;
|
||||
}
|
||||
+ (int)runWithArgs:(NSArray *)_args {
|
||||
return [(Tool *)[[[self alloc] init] autorelease] run];
|
||||
}
|
||||
|
||||
@end /* Tool */
|
||||
|
||||
int main(int argc, char **argv, char **env) {
|
||||
NSAutoreleasePool *pool;
|
||||
int rc;
|
||||
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
#if LIB_FOUNDATION_LIBRARY
|
||||
[NSProcessInfo initializeWithArguments:argv count:argc environment:env];
|
||||
#endif
|
||||
|
||||
rc = [Tool runWithArgs:
|
||||
[[NSProcessInfo processInfo] argumentsWithoutDefaults]];
|
||||
|
||||
[pool release];
|
||||
return rc;
|
||||
}
|
||||
121
SOPE/sope-gdl1/GDLContentStore/gcs_recreatequick.m
Normal file
121
SOPE/sope-gdl1/GDLContentStore/gcs_recreatequick.m
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
|
||||
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
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class NSUserDefaults, NSArray;
|
||||
@class GCSFolderManager;
|
||||
|
||||
@interface Tool : NSObject
|
||||
{
|
||||
NSUserDefaults *ud;
|
||||
GCSFolderManager *folderManager;
|
||||
}
|
||||
|
||||
+ (int)runWithArgs:(NSArray *)_args;
|
||||
- (int)run;
|
||||
|
||||
@end
|
||||
|
||||
#include <GDLContentStore/GCSFolder.h>
|
||||
#include <GDLContentStore/GCSFolderManager.h>
|
||||
#include "common.h"
|
||||
|
||||
@implementation Tool
|
||||
|
||||
- (id)init {
|
||||
if ((self = [super init])) {
|
||||
self->ud = [[NSUserDefaults standardUserDefaults] retain];
|
||||
self->folderManager = [[GCSFolderManager defaultFolderManager] retain];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (void)dealloc {
|
||||
[self->ud release];
|
||||
[self->folderManager release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* operation */
|
||||
|
||||
- (int)runOnPath:(NSString *)_path {
|
||||
GCSFolder *folder;
|
||||
|
||||
[self logWithFormat:@"update quick from store at path: '%@'", _path];
|
||||
|
||||
if (![self->folderManager folderExistsAtPath:_path]) {
|
||||
[self logWithFormat:@"no folder exist at path: '%@'", _path];
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((folder = [self->folderManager folderAtPath:_path]) == nil) {
|
||||
[self logWithFormat:@"got no folder object for path: '%@'", _path];
|
||||
return 2;
|
||||
}
|
||||
[self logWithFormat:@" folder: %@", folder];
|
||||
|
||||
// TODO:
|
||||
[self logWithFormat:@" should recreate folder .."];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (int)run {
|
||||
NSEnumerator *e;
|
||||
NSString *path;
|
||||
|
||||
[self logWithFormat:@"manager: %@", self->folderManager];
|
||||
|
||||
if (![self->folderManager canConnect]) {
|
||||
[self logWithFormat:@"cannot connect folder-info database!"];
|
||||
return 1;
|
||||
}
|
||||
|
||||
e = [[[NSProcessInfo processInfo] argumentsWithoutDefaults]
|
||||
objectEnumerator];
|
||||
[e nextObject]; // skip tool name
|
||||
|
||||
while ((path = [e nextObject]))
|
||||
[self runOnPath:path];
|
||||
|
||||
return 0;
|
||||
}
|
||||
+ (int)runWithArgs:(NSArray *)_args {
|
||||
return [(Tool *)[[[self alloc] init] autorelease] run];
|
||||
}
|
||||
|
||||
@end /* Tool */
|
||||
|
||||
int main(int argc, char **argv, char **env) {
|
||||
NSAutoreleasePool *pool;
|
||||
int rc;
|
||||
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
#if LIB_FOUNDATION_LIBRARY
|
||||
[NSProcessInfo initializeWithArguments:argv count:argc environment:env];
|
||||
#endif
|
||||
|
||||
rc = [Tool runWithArgs:
|
||||
[[NSProcessInfo processInfo] argumentsWithoutDefaults]];
|
||||
|
||||
[pool release];
|
||||
return rc;
|
||||
}
|
||||
435
SOPE/sope-patchset-r1544.diff
Normal file
435
SOPE/sope-patchset-r1544.diff
Normal file
@@ -0,0 +1,435 @@
|
||||
Index: sope-mime/NGImap4/NGImap4Connection.m
|
||||
===================================================================
|
||||
--- sope-mime/NGImap4/NGImap4Connection.m (revision 1544)
|
||||
+++ sope-mime/NGImap4/NGImap4Connection.m (working copy)
|
||||
@@ -381,7 +381,7 @@
|
||||
|
||||
if (debugCache) [self logWithFormat:@" no folders cached yet .."];
|
||||
|
||||
- result = [[self client] list:(onlyFetchInbox ? @"INBOX" : @"*")
|
||||
+ result = [[self client] list:(onlyFetchInbox ? @"INBOX" : @"")
|
||||
pattern:@"*"];
|
||||
if (![[result valueForKey:@"result"] boolValue]) {
|
||||
[self errorWithFormat:@"Could not list mailbox hierarchy!"];
|
||||
Index: sope-mime/NGImap4/NGImap4ResponseParser.m
|
||||
===================================================================
|
||||
--- sope-mime/NGImap4/NGImap4ResponseParser.m (revision 1544)
|
||||
+++ sope-mime/NGImap4/NGImap4ResponseParser.m (working copy)
|
||||
@@ -84,6 +84,8 @@
|
||||
static NSDictionary *_parseMultipartBody(NGImap4ResponseParser *self,
|
||||
BOOL isBodyStructure);
|
||||
|
||||
+static NSArray *_parseLanguages();
|
||||
+
|
||||
static NSString *_parseBodyString(NGImap4ResponseParser *self,
|
||||
BOOL _convertString);
|
||||
static NSString *_parseBodyDecodeString(NGImap4ResponseParser *self,
|
||||
@@ -1627,6 +1629,29 @@
|
||||
return _parseBodyDecodeString(self, _convertString, NO /* no decode */);
|
||||
}
|
||||
|
||||
+static NSArray *_parseLanguages(NGImap4ResponseParser *self) {
|
||||
+ NSMutableArray *languages;
|
||||
+ NSString *language;
|
||||
+
|
||||
+ languages = [NSMutableArray array];
|
||||
+ if (_la(self, 0) == '(') {
|
||||
+ while (_la(self, 0) != ')') {
|
||||
+ _consume(self,1);
|
||||
+ language = _parseBodyString(self, YES);
|
||||
+ if ([language length])
|
||||
+ [languages addObject: language];
|
||||
+ }
|
||||
+ _consume(self,1);
|
||||
+ }
|
||||
+ else {
|
||||
+ language = _parseBodyString(self, YES);
|
||||
+ if ([language length])
|
||||
+ [languages addObject: language];
|
||||
+ }
|
||||
+
|
||||
+ return languages;
|
||||
+}
|
||||
+
|
||||
static NSDictionary *_parseBodyParameterList(NGImap4ResponseParser *self)
|
||||
{
|
||||
NSMutableDictionary *list;
|
||||
@@ -1734,10 +1759,11 @@
|
||||
*encoding, *bodysize;
|
||||
NSDictionary *parameterList;
|
||||
NSMutableDictionary *dict;
|
||||
+ NSArray *languages;
|
||||
|
||||
type = [_parseBodyString(self, YES) lowercaseString];
|
||||
_consumeIfMatch(self, ' ');
|
||||
- subtype = _parseBodyString(self, YES);
|
||||
+ subtype = [_parseBodyString(self, YES) lowercaseString];
|
||||
_consumeIfMatch(self, ' ');
|
||||
parameterList = _parseBodyParameterList(self);
|
||||
_consumeIfMatch(self, ' ');
|
||||
@@ -1762,7 +1788,8 @@
|
||||
_consumeIfMatch(self, ' ');
|
||||
[dict setObject:_parseBodyString(self, YES) forKey:@"lines"];
|
||||
}
|
||||
- else if ([type isEqualToString:@"message"]) {
|
||||
+ else if ([type isEqualToString:@"message"]
|
||||
+ && [subtype isEqualToString:@"rfc822"]) {
|
||||
if (_la(self, 0) != ')') {
|
||||
_consumeIfMatch(self, ' ');
|
||||
_consumeIfMatch(self, '(');
|
||||
@@ -1805,14 +1832,9 @@
|
||||
forKey: @"disposition"];
|
||||
if (_la(self, 0) != ')') {
|
||||
_consume(self,1);
|
||||
- if (_la(self, 0) == '(') {
|
||||
- [dict setObject: _parseBodyParameterList(self)
|
||||
- forKey: @"language"];
|
||||
- }
|
||||
- else {
|
||||
- [dict setObject: _parseBodyString(self, YES)
|
||||
- forKey: @"language"];
|
||||
- }
|
||||
+ languages = _parseLanguages(self);
|
||||
+ if ([languages count])
|
||||
+ [dict setObject: languages forKey: @"languages"];
|
||||
if (_la(self, 0) != ')') {
|
||||
_consume(self,1);
|
||||
[dict setObject: _parseBodyString(self, YES)
|
||||
@@ -1829,6 +1851,7 @@
|
||||
static NSDictionary *_parseMultipartBody(NGImap4ResponseParser *self,
|
||||
BOOL isBodyStructure) {
|
||||
NSMutableArray *parts;
|
||||
+ NSArray *languages;
|
||||
NSString *kind;
|
||||
NSMutableDictionary *dict;
|
||||
|
||||
@@ -1854,14 +1877,9 @@
|
||||
forKey: @"disposition"];
|
||||
if (_la(self, 0) != ')') {
|
||||
_consume(self,1);
|
||||
- if (_la(self, 0) == '(') {
|
||||
- [dict setObject: _parseBodyParameterList(self)
|
||||
- forKey: @"language"];
|
||||
- }
|
||||
- else {
|
||||
- [dict setObject: _parseBodyString(self, YES)
|
||||
- forKey: @"language"];
|
||||
- }
|
||||
+ languages = _parseLanguages(self);
|
||||
+ if ([languages count])
|
||||
+ [dict setObject: languages forKey: @"languages"];
|
||||
if (_la(self, 0) != ')') {
|
||||
_consume(self,1);
|
||||
[dict setObject: _parseBodyString(self, YES)
|
||||
Index: sope-mime/NGMime/NGMimeBodyPart.m
|
||||
===================================================================
|
||||
--- sope-mime/NGMime/NGMimeBodyPart.m (revision 1544)
|
||||
+++ sope-mime/NGMime/NGMimeBodyPart.m (working copy)
|
||||
@@ -31,18 +31,6 @@
|
||||
return 2;
|
||||
}
|
||||
|
||||
-static NGMimeType *defaultType = nil;
|
||||
-
|
||||
-+ (void)initialize {
|
||||
- static BOOL isInitialized = NO;
|
||||
- if (!isInitialized) {
|
||||
- isInitialized = YES;
|
||||
-
|
||||
- defaultType =
|
||||
- [[NGMimeType mimeType:@"text/plain; charset=us-ascii"] retain];
|
||||
- }
|
||||
-}
|
||||
-
|
||||
+ (id)bodyPartWithHeader:(NGHashMap *)_header {
|
||||
return [[[self alloc] initWithHeader:_header] autorelease];
|
||||
}
|
||||
@@ -156,13 +144,12 @@
|
||||
if (!Fields)
|
||||
Fields = (NGMimeHeaderNames *)[NGMimePartParser headerFieldNames];
|
||||
|
||||
-
|
||||
type = [self->header objectForKey:Fields->contentType];
|
||||
|
||||
if (![type isKindOfClass:[NGMimeType class]])
|
||||
type = [NGMimeType mimeType:[type stringValue]];
|
||||
|
||||
- return (type != nil ? type : (id)defaultType);
|
||||
+ return type;
|
||||
}
|
||||
|
||||
- (NSString *)contentId {
|
||||
Index: sope-mime/NGMime/NGMimeBodyParser.m
|
||||
===================================================================
|
||||
--- sope-mime/NGMime/NGMimeBodyParser.m (revision 1544)
|
||||
+++ sope-mime/NGMime/NGMimeBodyParser.m (working copy)
|
||||
@@ -67,7 +67,10 @@
|
||||
if (_data == nil) return nil;
|
||||
|
||||
ctype = [_part contentType];
|
||||
-
|
||||
+ if (!ctype
|
||||
+ && [_d respondsToSelector: @selector(parser:contentTypeOfPart:)])
|
||||
+ ctype = [_d parser: self contentTypeOfPart: _part];
|
||||
+
|
||||
if (![ctype isKindOfClass:[NGMimeType class]])
|
||||
ctype = [NGMimeType mimeType:[ctype stringValue]];
|
||||
|
||||
Index: sope-mime/NGMime/NGMimePartParser.h
|
||||
===================================================================
|
||||
--- sope-mime/NGMime/NGMimePartParser.h (revision 1544)
|
||||
+++ sope-mime/NGMime/NGMimePartParser.h (working copy)
|
||||
@@ -117,6 +117,7 @@
|
||||
BOOL parserParseRawBodyDataOfPart:1;
|
||||
BOOL parserBodyParserForPart:1;
|
||||
BOOL parserDecodeBodyOfPart:1;
|
||||
+ BOOL parserContentTypeOfPart:1;
|
||||
} delegateRespondsTo;
|
||||
|
||||
|
||||
@@ -275,6 +276,9 @@
|
||||
- (id<NGMimeBodyParser>)parser:(NGMimePartParser *)_parser
|
||||
bodyParserForPart:(id<NGMimePart>)_part;
|
||||
|
||||
+- (NGMimeType *)parser:(id)_parser
|
||||
+ contentTypeOfPart:(id<NGMimePart>)_part;
|
||||
+
|
||||
@end /* NSObject(NGMimePartParserDelegate) */
|
||||
|
||||
@interface NSObject(NGMimePartParser)
|
||||
Index: sope-mime/NGMime/NGMimePartParser.m
|
||||
===================================================================
|
||||
--- sope-mime/NGMime/NGMimePartParser.m (revision 1544)
|
||||
+++ sope-mime/NGMime/NGMimePartParser.m (working copy)
|
||||
@@ -1091,7 +1091,10 @@
|
||||
id<NGMimeBodyParser> bodyParser = nil;
|
||||
|
||||
ctype = [_p contentType];
|
||||
-
|
||||
+ if (!ctype
|
||||
+ && self->delegateRespondsTo.parserContentTypeOfPart)
|
||||
+ ctype = [self->delegate parser: self contentTypeOfPart: _p];
|
||||
+
|
||||
contentType = ([ctype isKindOfClass:[NGMimeType class]])
|
||||
? ctype
|
||||
: [NGMimeType mimeType:[ctype stringValue]];
|
||||
Index: sope-gdl1/PostgreSQL/PostgreSQL72Channel.h
|
||||
===================================================================
|
||||
--- sope-gdl1/PostgreSQL/PostgreSQL72Channel.h (revision 1544)
|
||||
+++ sope-gdl1/PostgreSQL/PostgreSQL72Channel.h (working copy)
|
||||
@@ -28,6 +28,7 @@
|
||||
#define ___PostgreSQL72_Channel_H___
|
||||
|
||||
#include <GDLAccess/EOAdaptorChannel.h>
|
||||
+#include <GDLContentStore/EOAdaptorChannel+GCS.h>
|
||||
#include <libpq-fe.h>
|
||||
|
||||
@class NSArray, NSString, NSMutableDictionary;
|
||||
@@ -40,7 +41,7 @@
|
||||
int modification;
|
||||
} PostgreSQL72FieldInfo;
|
||||
|
||||
-@interface PostgreSQL72Channel : EOAdaptorChannel
|
||||
+@interface PostgreSQL72Channel : EOAdaptorChannel <GCSEOAdaptorChannel>
|
||||
{
|
||||
// connection is valid after an openChannel call
|
||||
PGConnection *connection;
|
||||
Index: sope-gdl1/PostgreSQL/PostgreSQL72Channel.m
|
||||
===================================================================
|
||||
--- sope-gdl1/PostgreSQL/PostgreSQL72Channel.m (revision 1544)
|
||||
+++ sope-gdl1/PostgreSQL/PostgreSQL72Channel.m (working copy)
|
||||
@@ -713,6 +713,39 @@
|
||||
return ms;
|
||||
}
|
||||
|
||||
+/* GCSEOAdaptorChannel protocol */
|
||||
+static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (\n" \
|
||||
+ @" c_name VARCHAR (256) NOT NULL,\n"
|
||||
+ @" c_content VARCHAR (100000) NOT NULL,\n"
|
||||
+ @" c_creationdate INT4 NOT NULL,\n"
|
||||
+ @" c_lastmodified INT4 NOT NULL,\n"
|
||||
+ @" c_version INT4 NOT NULL,\n"
|
||||
+ @" c_deleted INT4 NULL\n"
|
||||
+ @")");
|
||||
+static NSString *sqlFolderACLFormat = (@"CREATE TABLE %@ (\n" \
|
||||
+ @" c_uid VARCHAR (256) NOT NULL,\n"
|
||||
+ @" c_object VARCHAR (256) NOT NULL,\n"
|
||||
+ @" c_role VARCHAR (80) NOT NULL\n"
|
||||
+ @")");
|
||||
+
|
||||
+- (NSException *) createGCSFolderTableWithName: (NSString *) tableName
|
||||
+{
|
||||
+ NSString *sql;
|
||||
+
|
||||
+ sql = [NSString stringWithFormat: sqlFolderFormat, tableName];
|
||||
+
|
||||
+ return [self evaluateExpressionX: sql];
|
||||
+}
|
||||
+
|
||||
+- (NSException *) createGCSFolderACLTableWithName: (NSString *) tableName
|
||||
+{
|
||||
+ NSString *sql;
|
||||
+
|
||||
+ sql = [NSString stringWithFormat: sqlFolderACLFormat, tableName];
|
||||
+
|
||||
+ return [self evaluateExpressionX: sql];
|
||||
+}
|
||||
+
|
||||
@end /* PostgreSQL72Channel */
|
||||
|
||||
@implementation PostgreSQL72Channel(PrimaryKeyGeneration)
|
||||
Index: sope-appserver/NGObjWeb/GNUmakefile.postamble
|
||||
===================================================================
|
||||
--- sope-appserver/NGObjWeb/GNUmakefile.postamble (revision 1544)
|
||||
+++ sope-appserver/NGObjWeb/GNUmakefile.postamble (working copy)
|
||||
@@ -23,14 +23,20 @@
|
||||
|
||||
# install makefiles
|
||||
|
||||
-after-install ::
|
||||
+after-install :: $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make
|
||||
+
|
||||
+ifneq ($(GNUSTEP_MAKE_VERSION),1.3.0)
|
||||
+after-install :: $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/woapp.make $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/wobundle.make
|
||||
+endif
|
||||
+
|
||||
+$(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make: ngobjweb.make
|
||||
$(MKDIRS) $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/
|
||||
$(INSTALL_DATA) ngobjweb.make $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make
|
||||
|
||||
-ifneq ($(GNUSTEP_MAKE_VERSION),1.3.0)
|
||||
-after-install ::
|
||||
+$(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/woapp.make: woapp-gs.make
|
||||
$(INSTALL_DATA) woapp-gs.make \
|
||||
$(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/woapp.make
|
||||
+
|
||||
+$(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/wobundle.make: wobundle-gs.make
|
||||
$(INSTALL_DATA) wobundle-gs.make \
|
||||
$(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/wobundle.make
|
||||
-endif
|
||||
Index: sope-appserver/NGObjWeb/WOContext.m
|
||||
===================================================================
|
||||
--- sope-appserver/NGObjWeb/WOContext.m (revision 1544)
|
||||
+++ sope-appserver/NGObjWeb/WOContext.m (working copy)
|
||||
@@ -64,11 +64,13 @@
|
||||
static BOOL testNSURLs = NO;
|
||||
static BOOL newCURLStyle = NO;
|
||||
static NSString *WOApplicationSuffix = nil;
|
||||
+static NSURL *redirectURL = nil;
|
||||
|
||||
+ (void)initialize {
|
||||
static BOOL didInit = NO;
|
||||
NSUserDefaults *ud;
|
||||
NSString *cn;
|
||||
+ NSString *url;
|
||||
|
||||
if (didInit) return;
|
||||
|
||||
@@ -91,6 +93,9 @@
|
||||
debugCursor = [ud boolForKey:@"WODebugCursor"] ? 1 : 0;
|
||||
debugComponentAwake = [ud boolForKey:@"WODebugComponentAwake"];
|
||||
WOApplicationSuffix = [[ud stringForKey:@"WOApplicationSuffix"] copy];
|
||||
+ url = [ud stringForKey:@"WOApplicationRedirectURL"];
|
||||
+ if (url != nil)
|
||||
+ redirectURL = [NSURL URLWithString: url];
|
||||
}
|
||||
|
||||
+ (id)contextWithRequest:(WORequest *)_r {
|
||||
@@ -503,6 +508,11 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ if (redirectURL) {
|
||||
+ // Use URL from user defaults (WOApplicationRedirectURL)
|
||||
+ return redirectURL;
|
||||
+ }
|
||||
+
|
||||
if ((serverURL = [rq headerForKey:@"x-webobjects-server-url"]) == nil) {
|
||||
if ((host = [rq headerForKey:@"host"]))
|
||||
serverURL = [@"http://" stringByAppendingString:host];
|
||||
Index: sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m
|
||||
===================================================================
|
||||
--- sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m (revision 1544)
|
||||
+++ sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m (working copy)
|
||||
@@ -216,6 +216,12 @@
|
||||
assocCount++;
|
||||
}
|
||||
}
|
||||
+ if (count > 0) {
|
||||
+ if ((self->isAbsolute = OWGetProperty(_config, @"absolute"))) {
|
||||
+ count--;
|
||||
+ assocCount++;
|
||||
+ }
|
||||
+ }
|
||||
|
||||
self->rest = _config;
|
||||
|
||||
Index: sope-appserver/NGObjWeb/DynamicElements/_WOComplexHyperlink.m
|
||||
===================================================================
|
||||
--- sope-appserver/NGObjWeb/DynamicElements/_WOComplexHyperlink.m (revision 1544)
|
||||
+++ sope-appserver/NGObjWeb/DynamicElements/_WOComplexHyperlink.m (working copy)
|
||||
@@ -40,6 +40,7 @@
|
||||
WOAssociation *string;
|
||||
WOAssociation *target;
|
||||
WOAssociation *disabled;
|
||||
+ WOAssociation *isAbsolute;
|
||||
WOElement *template;
|
||||
|
||||
/* new in WO4: */
|
||||
@@ -359,6 +360,7 @@
|
||||
{
|
||||
if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
|
||||
self->href = _info->href;
|
||||
+ self->isAbsolute = _info->isAbsolute;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -374,6 +376,9 @@
|
||||
// TODO: we need a binding to disable rewriting!
|
||||
NSRange r;
|
||||
|
||||
+ if ([[self->isAbsolute valueInContext:_ctx] boolValue] == YES)
|
||||
+ return NO;
|
||||
+
|
||||
r = [_s rangeOfString:@":"];
|
||||
if (r.length == 0)
|
||||
return YES;
|
||||
Index: sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h
|
||||
===================================================================
|
||||
--- sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h (revision 1544)
|
||||
+++ sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h (working copy)
|
||||
@@ -41,7 +41,8 @@
|
||||
WOAssociation *pageName;
|
||||
WOAssociation *actionClass;
|
||||
WOAssociation *directActionName;
|
||||
-
|
||||
+ WOAssociation *isAbsolute;
|
||||
+
|
||||
BOOL sidInUrl;
|
||||
|
||||
/* 'ivar' associations */
|
||||
Index: sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m
|
||||
===================================================================
|
||||
--- sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m (revision 1544)
|
||||
+++ sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m (working copy)
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <NGObjWeb/WOCookie.h>
|
||||
#include <NGExtensions/NSData+gzip.h>
|
||||
#include <NGHttp/NGHttp.h>
|
||||
+#include <NGMime/NGMimeType.h>
|
||||
#include "common.h"
|
||||
|
||||
#include <string.h>
|
||||
@@ -1016,6 +1017,12 @@
|
||||
- (void)parser:(NGMimePartParser *)_parser didParseHeader:(NGHashMap *)_header {
|
||||
}
|
||||
|
||||
+- (NGMimeType *)parser:(id)_parser
|
||||
+ contentTypeOfPart:(id<NGMimePart>)_part
|
||||
+{
|
||||
+ return [NGMimeType mimeType: @"text/plain; charset=utf-8"];
|
||||
+}
|
||||
+
|
||||
@end /* WOHttpAdaptor */
|
||||
|
||||
@implementation WOCoreApplication(SimpleParserSelection)
|
||||
@@ -1,4 +1,10 @@
|
||||
#!/bin/bash
|
||||
# chkconfig: - 85 15
|
||||
# description: SOGo is a groupware server
|
||||
# processname: sogod-0.9
|
||||
# config: /etc/sysconfig/sogo
|
||||
# config: /etc/httpd/conf.d/SOGo.conf
|
||||
# pidfile: /var/run/sogo/sogod.pid
|
||||
|
||||
# SOGo init script for RedHat
|
||||
#
|
||||
@@ -22,18 +28,17 @@
|
||||
# Boston, MA 02111-1307, USA.
|
||||
|
||||
# sogod Scalable OpenGroupware.org (Inverse edition)
|
||||
#
|
||||
# chkconfig: - 85 15
|
||||
# description: SOGo is a groupware server
|
||||
# processname: sogod-0.9
|
||||
# config: /etc/sysconfig/sogo
|
||||
# config: /etc/httpd/conf.d/SOGo.conf
|
||||
# pidfile: /var/run/sogo/sogod.pid
|
||||
|
||||
PATH=/sbin:/bin:/usr/sbin:/usr/bin
|
||||
|
||||
. /etc/rc.d/init.d/functions
|
||||
|
||||
if [ -z "$GNUSTEP_SYSTEM_ROOT" ]
|
||||
then
|
||||
. /usr/GNUstep/System/Library/Makefiles/GNUstep.sh
|
||||
fi
|
||||
|
||||
REAL_DAEMON=$GNUSTEP_SYSTEM_ROOT/Tools/sogod-0.9
|
||||
DAEMON=/usr/sbin/sogod
|
||||
NAME=sogo
|
||||
DESC="Scalable OpenGroupware.Org (Inverse edition)"
|
||||
@@ -52,26 +57,24 @@ test -x $DAEMON || exit 0
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n $"Starting $DESC: " /
|
||||
echo -n $"Starting $DESC: "
|
||||
daemon --user sogo --pidfile $PIDFILE $DAEMON
|
||||
echo "$NAME."
|
||||
;;
|
||||
stop)
|
||||
echo -n $"Stopping $DESC: "
|
||||
killproc --pidfile $PIDFILE $DAEMON
|
||||
rm -f $PIDFILE
|
||||
killproc -p $PIDFILE $REAL_DAEMON && rm -f $PIDFILE
|
||||
echo "$NAME."
|
||||
;;
|
||||
restart|force-reload)
|
||||
echo -n $"Restarting $DESC: "
|
||||
killproc --pidfile $PIDFILE $DAEMON
|
||||
rm -f $PIDFILE
|
||||
killproc -p $PIDFILE $REAL_DAEMON && rm -f $PIDFILE
|
||||
sleep 1
|
||||
daemon --user sogo --pidfile $PIDFILE $DAEMON
|
||||
echo "$NAME."
|
||||
;;
|
||||
status)
|
||||
status $DAEMON
|
||||
status $REAL_DAEMON
|
||||
;;
|
||||
*)
|
||||
N=/etc/init.d/$NAME
|
||||
|
||||
87
Scripts/sogo-init.d-rhel4
Executable file
87
Scripts/sogo-init.d-rhel4
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
# chkconfig: - 85 15
|
||||
# description: SOGo is a groupware server
|
||||
# processname: sogod-0.9
|
||||
# config: /etc/sysconfig/sogo
|
||||
# config: /etc/httpd/conf.d/SOGo.conf
|
||||
# pidfile: /var/run/sogo/sogod.pid
|
||||
|
||||
# SOGo init script for RedHat
|
||||
#
|
||||
# Copyright (C) 2007 Inverse groupe conseil
|
||||
#
|
||||
# Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This file is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; see the file COPYING. If not, write to
|
||||
# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
# Boston, MA 02111-1307, USA.
|
||||
|
||||
# sogod Scalable OpenGroupware.org (Inverse edition)
|
||||
|
||||
PATH=/sbin:/bin:/usr/sbin:/usr/bin
|
||||
|
||||
. /etc/rc.d/init.d/functions
|
||||
|
||||
if [ -z "$GNUSTEP_SYSTEM_ROOT" ]
|
||||
then
|
||||
. /usr/GNUstep/System/Library/Makefiles/GNUstep.sh
|
||||
fi
|
||||
|
||||
REAL_DAEMON=$GNUSTEP_SYSTEM_ROOT/Tools/sogod-0.9
|
||||
DAEMON=/usr/sbin/sogod
|
||||
NAME=sogo
|
||||
DESC="Scalable OpenGroupware.Org (Inverse edition)"
|
||||
|
||||
PIDFILE=/var/run/sogo/sogod.pid
|
||||
|
||||
SOGO_ARGS=""
|
||||
|
||||
if [ -f /etc/sysconfig/sogo ]; then
|
||||
. /etc/sysconfig/sogo
|
||||
fi
|
||||
|
||||
test -x $DAEMON || exit 0
|
||||
|
||||
#set -e
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n $"Starting $DESC: "
|
||||
daemon su - sogo -c $DAEMON
|
||||
echo "$NAME."
|
||||
;;
|
||||
stop)
|
||||
echo -n $"Stopping $DESC: "
|
||||
killproc `basename $REAL_DAEMON` && rm -f $PIDFILE
|
||||
echo "$NAME."
|
||||
;;
|
||||
restart|force-reload)
|
||||
echo -n $"Restarting $DESC: "
|
||||
killproc `basename $REAL_DAEMON` && rm -f $PIDFILE
|
||||
sleep 1
|
||||
daemon su - sogo -c $DAEMON
|
||||
echo "$NAME."
|
||||
;;
|
||||
status)
|
||||
status $REAL_DAEMON
|
||||
;;
|
||||
*)
|
||||
N=/etc/init.d/$NAME
|
||||
echo "Usage: $N {start|stop|restart|force-reload|status}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
PIDFILE=/var/run/sogo/sogod.pid
|
||||
PROGRAM=sogod-0.9
|
||||
oldpid=`pgrep -u $USER sogod-0.9`
|
||||
|
||||
if [ -n "$oldpid" ]
|
||||
then
|
||||
echo SOGo already launched.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
. /usr/GNUstep/System/Library/Makefiles/GNUstep.sh
|
||||
|
||||
echo $$ > $PIDFILE
|
||||
exec $GNUSTEP_SYSTEM_ROOT/Tools/sogod-0.9 >& /var/log/sogo/sogod.log
|
||||
$GNUSTEP_SYSTEM_ROOT/Tools/$PROGRAM >& /var/log/sogo/sogod.log &
|
||||
|
||||
newpid=`pgrep -u $USER $PROGRAM | awk '{ print $1 }'`
|
||||
echo $newpid > $PIDFILE
|
||||
|
||||
|
||||
70
Scripts/sql-update-20070822.sh
Executable file
70
Scripts/sql-update-20070822.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
|
||||
# this script only work with PostgreSQL
|
||||
|
||||
defaultusername=$USER
|
||||
defaulthostname=localhost
|
||||
defaultdatabase=$USER
|
||||
indextable=sogo_folder_info
|
||||
|
||||
read -p "Username ($defaultusername): " username
|
||||
read -p "Hostname ($defaulthostname): " hostname
|
||||
read -p "Database ($defaultdatabase): " database
|
||||
|
||||
if [ -z "$username" ]
|
||||
then
|
||||
username=$defaultusername
|
||||
fi
|
||||
if [ -z "$hostname" ]
|
||||
then
|
||||
hostname=$defaulthostname
|
||||
fi
|
||||
if [ -z "$database" ]
|
||||
then
|
||||
database=$defaultdatabase
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "You will now be requested your password twice..."
|
||||
echo "After that, a list of SQL operations will scroll."
|
||||
echo ""
|
||||
|
||||
sqlscript=""
|
||||
|
||||
function addField() {
|
||||
oldIFS="$IFS"
|
||||
IFS=" "
|
||||
part="`echo -e \"ALTER TABLE $table ADD COLUMN c_deleted INTEGER;\\n\"`";
|
||||
sqlscript="$sqlscript$part"
|
||||
IFS="$oldIFS"
|
||||
}
|
||||
|
||||
tables=`psql -t -U $username -h $hostname $database -c "select split_part(c_location, '/', 5) from $indextable where c_folder_type != 'Container';"`
|
||||
|
||||
for table in $tables;
|
||||
do
|
||||
addField
|
||||
done
|
||||
|
||||
sqlscript="$sqlscript;update $indextable set c_path4 = 'personal', c_path = '/Users/' || c_path2 || '/Calendar/personal' where c_path3 = 'Calendar' and c_path4 is null;"
|
||||
|
||||
function updateCalendarLocation() {
|
||||
oldIFS="$IFS"
|
||||
IFS=" "
|
||||
user="`echo $table | cut -f 1 -d :`"
|
||||
tablename="`echo $table | cut -f 2 -d :`"
|
||||
newstart="/$user/Calendar/personal";
|
||||
part="update $tablename set c_object = replace(c_object, '/$user/Calendar', '$newstart') where c_object not like '$newstart%';";
|
||||
sqlscript="$sqlscript$part"
|
||||
IFS="$oldIFS"
|
||||
}
|
||||
|
||||
tables=`psql -t -U $username -h $hostname $database -c "select c_path2 || ':' || split_part(c_acl_location, '/', 5) from $indextable where c_folder_type = 'Appointment';"`
|
||||
for table in $tables;
|
||||
do
|
||||
updateCalendarLocation
|
||||
done
|
||||
|
||||
echo "$sqlscript" | psql -q -e -U $username -h $hostname $database > /dev/null
|
||||
|
||||
echo "Please ignore the errors above. They just mean that the migration was already done for the elements in question.";
|
||||
1
SoObjects/Appointments/English.lproj/Localizable.strings
Normal file
1
SoObjects/Appointments/English.lproj/Localizable.strings
Normal file
@@ -0,0 +1 @@
|
||||
"Personal Calendar" = "Personal Calendar";
|
||||
1
SoObjects/Appointments/French.lproj/Localizable.strings
Normal file
1
SoObjects/Appointments/French.lproj/Localizable.strings
Normal file
@@ -0,0 +1 @@
|
||||
"Personal Calendar" = "Agenda personnel";
|
||||
@@ -6,7 +6,9 @@ WOBUNDLE_NAME = Appointments
|
||||
|
||||
Appointments_PRINCIPAL_CLASS = SOGoAppointmentsProduct
|
||||
|
||||
# Appointments_LANGUAGES = English French
|
||||
Appointments_LANGUAGES = English French German
|
||||
|
||||
Appointments_LOCALIZED_RESOURCE_FILES=Localizable.strings
|
||||
|
||||
Appointments_OBJC_FILES = \
|
||||
Product.m \
|
||||
@@ -17,6 +19,7 @@ Appointments_OBJC_FILES = \
|
||||
SOGoAppointmentObject.m \
|
||||
SOGoTaskObject.m \
|
||||
SOGoAppointmentFolder.m \
|
||||
SOGoAppointmentFolders.m \
|
||||
SOGoGroupAppointmentFolder.m \
|
||||
SOGoFreeBusyObject.m \
|
||||
\
|
||||
@@ -32,13 +35,20 @@ Appointments_RESOURCE_FILES += \
|
||||
|
||||
Appointments_COMPONENTS += \
|
||||
SOGoAptMailEnglishInvitation.wo \
|
||||
SOGoAptMailFrenchInvitation.wo \
|
||||
SOGoAptMailEnglishUpdate.wo \
|
||||
SOGoAptMailFrenchUpdate.wo \
|
||||
SOGoAptMailEnglishRemoval.wo \
|
||||
SOGoAptMailFrenchRemoval.wo \
|
||||
SOGoAptMailEnglishDeletion.wo \
|
||||
SOGoAptMailFrenchInvitation.wo \
|
||||
SOGoAptMailFrenchUpdate.wo \
|
||||
SOGoAptMailFrenchRemoval.wo \
|
||||
SOGoAptMailFrenchDeletion.wo \
|
||||
SOGoAptMailGermanInvitation.wo \
|
||||
SOGoAptMailGermanUpdate.wo \
|
||||
SOGoAptMailGermanRemoval.wo \
|
||||
SOGoAptMailGermanDeletion.wo \
|
||||
|
||||
ADDITIONAL_INCLUDE_DIRS += -I../../SOPE/sope-gdl1/
|
||||
ADDITIONAL_LIB_DIRS += -L../../SOPE/sope-gdl1/GDLContentStore/obj/
|
||||
|
||||
-include GNUmakefile.preamble
|
||||
include $(GNUSTEP_MAKEFILES)/wobundle.make
|
||||
|
||||
1
SoObjects/Appointments/German.lproj/Localizable.strings
Normal file
1
SoObjects/Appointments/German.lproj/Localizable.strings
Normal file
@@ -0,0 +1 @@
|
||||
"Personal Calendar" = "Personal Calendar";
|
||||
@@ -53,6 +53,8 @@
|
||||
NSMutableDictionary *uidToFilename;
|
||||
}
|
||||
|
||||
- (BOOL) isActive;
|
||||
|
||||
/* selection */
|
||||
|
||||
- (NSArray *) calendarUIDs;
|
||||
@@ -82,8 +84,6 @@
|
||||
- (NSArray *) fetchFreeBusyInfosFrom: (NSCalendarDate *) _startDate
|
||||
to: (NSCalendarDate *) _endDate;
|
||||
|
||||
- (void) deleteEntriesWithIds: (NSArray *) ids;
|
||||
|
||||
/* URL generation */
|
||||
|
||||
- (NSString *) baseURLForAptWithUID: (NSString *) _uid
|
||||
@@ -113,8 +113,6 @@
|
||||
|
||||
- (NSArray *) fetchAllSOGoAppointments;
|
||||
|
||||
- (NSArray *) calendarFolders;
|
||||
|
||||
- (NSString *) roleForComponentsWithAccessClass: (iCalAccessClass) accessClass
|
||||
forUser: (NSString *) uid;
|
||||
|
||||
|
||||
@@ -125,11 +125,6 @@ static NSNumber *sharedYes = nil;
|
||||
return logger;
|
||||
}
|
||||
|
||||
- (BOOL) folderIsMandatory
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
/* selection */
|
||||
|
||||
- (NSArray *) calendarUIDs
|
||||
@@ -226,6 +221,7 @@ static NSNumber *sharedYes = nil;
|
||||
return filterData;
|
||||
}
|
||||
|
||||
#warning filters is leaked here
|
||||
- (NSArray *) _parseCalendarFilters: (id <DOMElement>) parentNode
|
||||
{
|
||||
NSEnumerator *children;
|
||||
@@ -263,6 +259,7 @@ static NSNumber *sharedYes = nil;
|
||||
max = [filters count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
#warning huh? why not objectAtIndex: count?
|
||||
currentFilter = [filters objectAtIndex: 0];
|
||||
apts = [self fetchCoreInfosFrom: [currentFilter objectForKey: @"start"]
|
||||
to: [currentFilter objectForKey: @"end"]
|
||||
@@ -573,13 +570,16 @@ static NSNumber *sharedYes = nil;
|
||||
|
||||
md = [[_record mutableCopy] autorelease];
|
||||
|
||||
/* cycle is in _r */
|
||||
/* cycle is in _r. We also have to override the c_startdate/c_enddate with the date values of
|
||||
the reccurence since we use those when displaying events in SOGo Web */
|
||||
tmp = [_r startDate];
|
||||
[tmp setTimeZone: timeZone];
|
||||
[md setObject:tmp forKey:@"startDate"];
|
||||
[md setObject: [NSNumber numberWithInt: [tmp timeIntervalSince1970]] forKey: @"c_startdate"];
|
||||
tmp = [_r endDate];
|
||||
[tmp setTimeZone: timeZone];
|
||||
[md setObject:tmp forKey:@"endDate"];
|
||||
[md setObject: [NSNumber numberWithInt: [tmp timeIntervalSince1970]] forKey: @"c_enddate"];
|
||||
|
||||
return md;
|
||||
}
|
||||
@@ -638,20 +638,24 @@ static NSNumber *sharedYes = nil;
|
||||
rules = [cycleinfo objectForKey:@"rules"];
|
||||
exRules = [cycleinfo objectForKey:@"exRules"];
|
||||
exDates = [cycleinfo objectForKey:@"exDates"];
|
||||
|
||||
|
||||
ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange:_r
|
||||
firstInstanceCalendarDateRange:fir
|
||||
recurrenceRules:rules
|
||||
exceptionRules:exRules
|
||||
exceptionDates:exDates];
|
||||
count = [ranges count];
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
NGCalendarDateRange *rRange;
|
||||
id fixedRow;
|
||||
|
||||
rRange = [ranges objectAtIndex:i];
|
||||
fixedRow = [self fixupCycleRecord:row cycleRange:rRange];
|
||||
if (fixedRow != nil) [_ma addObject:fixedRow];
|
||||
if (fixedRow != nil)
|
||||
{
|
||||
[_ma addObject:fixedRow];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -746,7 +750,7 @@ static NSNumber *sharedYes = nil;
|
||||
privacySqlString = @"and (c_isopaque = 1)";
|
||||
else
|
||||
{
|
||||
#warning we do not manage all the user's possible emails
|
||||
#warning we do not manage all the possible user emails
|
||||
email = [[activeUser primaryIdentity] objectForKey: @"email"];
|
||||
|
||||
privacySqlString
|
||||
@@ -850,21 +854,20 @@ static NSNumber *sharedYes = nil;
|
||||
ma = [NSMutableArray arrayWithArray: records];
|
||||
}
|
||||
|
||||
/* fetch recurrent apts now */
|
||||
sql = [NSString stringWithFormat: @"(c_iscycle = 1)%@%@%@",
|
||||
dateSqlString, componentSqlString, privacySqlString];
|
||||
/* fetch recurrent apts now. we do NOT consider the date range when doing that
|
||||
as the c_startdate/c_enddate of a recurring event is always set to the first
|
||||
recurrence - others are generated on the fly */
|
||||
sql = [NSString stringWithFormat: @"(c_iscycle = 1)%@%@", componentSqlString, privacySqlString];
|
||||
|
||||
qualifier = [EOQualifier qualifierWithQualifierFormat: sql];
|
||||
|
||||
[fields addObject: @"c_cycleinfo"];
|
||||
|
||||
records = [_folder fetchFields: fields matchingQualifier: qualifier];
|
||||
|
||||
if (records)
|
||||
{
|
||||
if (logger)
|
||||
[self debugWithFormat: @"fetched %i cyclic records: %@",
|
||||
[records count], records];
|
||||
if (r)
|
||||
if (r) {
|
||||
records = [self fixupCyclicRecords: records fetchRange: r];
|
||||
}
|
||||
if (!ma)
|
||||
ma = [NSMutableArray arrayWithCapacity: [records count]];
|
||||
|
||||
@@ -905,7 +908,6 @@ static NSNumber *sharedYes = nil;
|
||||
component: _component];
|
||||
}
|
||||
|
||||
|
||||
- (NSArray *) fetchFreeBusyInfosFrom: (NSCalendarDate *) _startDate
|
||||
to: (NSCalendarDate *) _endDate
|
||||
{
|
||||
@@ -932,33 +934,13 @@ static NSNumber *sharedYes = nil;
|
||||
@"c_status", @"c_classification",
|
||||
@"c_isallday", @"c_isopaque",
|
||||
@"c_participants", @"c_partmails",
|
||||
@"c_partstates", @"c_sequence", @"c_priority",
|
||||
@"c_partstates", @"c_sequence", @"c_priority", @"c_cycleinfo",
|
||||
nil];
|
||||
|
||||
return [self fetchFields: infos from: _startDate to: _endDate
|
||||
component: _component];
|
||||
}
|
||||
|
||||
- (void) deleteEntriesWithIds: (NSArray *) ids
|
||||
{
|
||||
Class objectClass;
|
||||
unsigned int count, max;
|
||||
NSString *currentId;
|
||||
id deleteObject;
|
||||
|
||||
max = [ids count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
currentId = [ids objectAtIndex: count];
|
||||
objectClass
|
||||
= [self objectClassForResourceNamed: currentId];
|
||||
deleteObject = [objectClass objectWithName: currentId
|
||||
inContainer: self];
|
||||
[deleteObject delete];
|
||||
[deleteObject primaryDelete];
|
||||
}
|
||||
}
|
||||
|
||||
/* URL generation */
|
||||
|
||||
- (NSString *) baseURLForAptWithUID: (NSString *)_uid
|
||||
@@ -1029,7 +1011,7 @@ static NSNumber *sharedYes = nil;
|
||||
calendarFolder = [SOGoAppointmentFolder objectWithName: @"Calendar"
|
||||
inContainer: userFolder];
|
||||
[calendarFolder
|
||||
setOCSPath: [NSString stringWithFormat: @"/Users/%@/Calendar", uid]];
|
||||
setOCSPath: [NSString stringWithFormat: @"/Users/%@/Calendar/personal", uid]];
|
||||
[calendarFolder setOwner: uid];
|
||||
|
||||
return calendarFolder;
|
||||
@@ -1041,21 +1023,28 @@ static NSNumber *sharedYes = nil;
|
||||
/* Note: can return NSNull objects in the array! */
|
||||
NSMutableArray *folders;
|
||||
NSEnumerator *e;
|
||||
NSString *uid;
|
||||
|
||||
NSString *uid, *ownerLogin;
|
||||
id folder;
|
||||
|
||||
ownerLogin = [self ownerInContext: context];
|
||||
|
||||
if ([_uids count] == 0) return nil;
|
||||
folders = [NSMutableArray arrayWithCapacity:16];
|
||||
e = [_uids objectEnumerator];
|
||||
while ((uid = [e nextObject])) {
|
||||
id folder;
|
||||
while ((uid = [e nextObject]))
|
||||
{
|
||||
if ([uid isEqualToString: ownerLogin])
|
||||
folder = self;
|
||||
else
|
||||
{
|
||||
folder = [self lookupCalendarFolderForUID: uid];
|
||||
if (![folder isNotNull])
|
||||
[self logWithFormat:@"Note: did not find folder for uid: '%@'", uid];
|
||||
}
|
||||
|
||||
folder = [self lookupCalendarFolderForUID: uid];
|
||||
if (![folder isNotNull])
|
||||
[self logWithFormat:@"Note: did not find folder for uid: '%@'", uid];
|
||||
|
||||
/* Note: intentionally add 'null' folders to allow a mapping */
|
||||
[folders addObject:folder ? folder : [NSNull null]];
|
||||
}
|
||||
[folders addObject: folder];
|
||||
}
|
||||
|
||||
return folders;
|
||||
}
|
||||
|
||||
@@ -1196,67 +1185,67 @@ static NSNumber *sharedYes = nil;
|
||||
return events;
|
||||
}
|
||||
|
||||
#warning We only support ONE calendar per user at this time
|
||||
- (BOOL) _appendSubscribedFolders: (NSDictionary *) subscribedFolders
|
||||
toFolderList: (NSMutableArray *) calendarFolders
|
||||
{
|
||||
NSEnumerator *keys;
|
||||
NSString *currentKey;
|
||||
NSMutableDictionary *currentCalendar;
|
||||
BOOL firstShouldBeActive;
|
||||
unsigned int count;
|
||||
// #warning We only support ONE calendar per user at this time
|
||||
// - (BOOL) _appendSubscribedFolders: (NSDictionary *) subscribedFolders
|
||||
// toFolderList: (NSMutableArray *) calendarFolders
|
||||
// {
|
||||
// NSEnumerator *keys;
|
||||
// NSString *currentKey;
|
||||
// NSMutableDictionary *currentCalendar;
|
||||
// BOOL firstShouldBeActive;
|
||||
// unsigned int count;
|
||||
|
||||
firstShouldBeActive = YES;
|
||||
// firstShouldBeActive = YES;
|
||||
|
||||
keys = [[subscribedFolders allKeys] objectEnumerator];
|
||||
currentKey = [keys nextObject];
|
||||
count = 1;
|
||||
while (currentKey)
|
||||
{
|
||||
currentCalendar = [NSMutableDictionary new];
|
||||
[currentCalendar autorelease];
|
||||
[currentCalendar
|
||||
setDictionary: [subscribedFolders objectForKey: currentKey]];
|
||||
[currentCalendar setObject: currentKey forKey: @"folder"];
|
||||
[calendarFolders addObject: currentCalendar];
|
||||
if ([[currentCalendar objectForKey: @"active"] boolValue])
|
||||
firstShouldBeActive = NO;
|
||||
count++;
|
||||
currentKey = [keys nextObject];
|
||||
}
|
||||
// keys = [[subscribedFolders allKeys] objectEnumerator];
|
||||
// currentKey = [keys nextObject];
|
||||
// count = 1;
|
||||
// while (currentKey)
|
||||
// {
|
||||
// currentCalendar = [NSMutableDictionary new];
|
||||
// [currentCalendar autorelease];
|
||||
// [currentCalendar
|
||||
// setDictionary: [subscribedFolders objectForKey: currentKey]];
|
||||
// [currentCalendar setObject: currentKey forKey: @"folder"];
|
||||
// [calendarFolders addObject: currentCalendar];
|
||||
// if ([[currentCalendar objectForKey: @"active"] boolValue])
|
||||
// firstShouldBeActive = NO;
|
||||
// count++;
|
||||
// currentKey = [keys nextObject];
|
||||
// }
|
||||
|
||||
return firstShouldBeActive;
|
||||
}
|
||||
// return firstShouldBeActive;
|
||||
// }
|
||||
|
||||
- (NSArray *) calendarFolders
|
||||
{
|
||||
NSMutableDictionary *userCalendar, *calendarDict;
|
||||
NSMutableArray *calendarFolders;
|
||||
SOGoUser *calendarUser;
|
||||
BOOL firstActive;
|
||||
// - (NSArray *) calendarFolders
|
||||
// {
|
||||
// NSMutableDictionary *userCalendar, *calendarDict;
|
||||
// NSMutableArray *calendarFolders;
|
||||
// SOGoUser *calendarUser;
|
||||
// BOOL firstActive;
|
||||
|
||||
calendarFolders = [NSMutableArray new];
|
||||
[calendarFolders autorelease];
|
||||
// calendarFolders = [NSMutableArray new];
|
||||
// [calendarFolders autorelease];
|
||||
|
||||
calendarUser = [SOGoUser userWithLogin: [self ownerInContext: context]
|
||||
roles: nil];
|
||||
userCalendar = [NSMutableDictionary new];
|
||||
[userCalendar autorelease];
|
||||
[userCalendar setObject: @"/" forKey: @"folder"];
|
||||
[userCalendar setObject: @"Calendar" forKey: @"displayName"];
|
||||
[calendarFolders addObject: userCalendar];
|
||||
// calendarUser = [SOGoUser userWithLogin: [self ownerInContext: context]
|
||||
// roles: nil];
|
||||
// userCalendar = [NSMutableDictionary new];
|
||||
// [userCalendar autorelease];
|
||||
// [userCalendar setObject: @"/" forKey: @"folder"];
|
||||
// [userCalendar setObject: @"Calendar" forKey: @"displayName"];
|
||||
// [calendarFolders addObject: userCalendar];
|
||||
|
||||
calendarDict = [[calendarUser userSettings] objectForKey: @"Calendar"];
|
||||
firstActive = [[calendarDict objectForKey: @"activateUserFolder"] boolValue];
|
||||
firstActive = ([self _appendSubscribedFolders:
|
||||
[calendarDict objectForKey: @"SubscribedFolders"]
|
||||
toFolderList: calendarFolders]
|
||||
|| firstActive);
|
||||
[userCalendar setObject: [NSNumber numberWithBool: firstActive]
|
||||
forKey: @"active"];
|
||||
// calendarDict = [[calendarUser userSettings] objectForKey: @"Calendar"];
|
||||
// firstActive = [[calendarDict objectForKey: @"activateUserFolder"] boolValue];
|
||||
// firstActive = ([self _appendSubscribedFolders:
|
||||
// [calendarDict objectForKey: @"SubscribedFolders"]
|
||||
// toFolderList: calendarFolders]
|
||||
// || firstActive);
|
||||
// [userCalendar setObject: [NSNumber numberWithBool: firstActive]
|
||||
// forKey: @"active"];
|
||||
|
||||
return calendarFolders;
|
||||
}
|
||||
// return calendarFolders;
|
||||
// }
|
||||
|
||||
// - (NSArray *) fetchContentObjectNames
|
||||
// {
|
||||
@@ -1297,4 +1286,16 @@ static NSNumber *sharedYes = nil;
|
||||
return @"IPF.Appointment";
|
||||
}
|
||||
|
||||
- (BOOL) isActive
|
||||
{
|
||||
NSUserDefaults *settings;
|
||||
NSArray *activeFolders;
|
||||
|
||||
settings = [[context activeUser] userSettings];
|
||||
activeFolders
|
||||
= [[settings objectForKey: @"Calendar"] objectForKey: @"ActiveFolders"];
|
||||
|
||||
return [activeFolders containsObject: nameInContainer];
|
||||
}
|
||||
|
||||
@end /* SOGoAppointmentFolder */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* SOGoMailEnglishForward.m - this file is part of SOGo
|
||||
/* SOGoAppointmentFolders.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse groupe conseil
|
||||
*
|
||||
@@ -20,10 +20,13 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import "SOGoMailForward.h"
|
||||
#ifndef SOGOAPPOINTMENTFOLDERS_H
|
||||
#define SOGOAPPOINTMENTFOLDERS_H
|
||||
|
||||
#import <SoObjects/SOGo/SOGoParentFolder.h>
|
||||
|
||||
@interface SOGoAppointmentFolders : SOGoParentFolder
|
||||
|
||||
@interface SOGoMailEnglishForward : SOGoMailForward
|
||||
@end
|
||||
|
||||
@implementation SOGoMailEnglishForward
|
||||
@end
|
||||
#endif /* SOGOAPPOINTMENTFOLDERS_H */
|
||||
@@ -1,4 +1,4 @@
|
||||
/* SOGoACLEnglishAdditionAdvisory.h - this file is part of SOGo
|
||||
/* SOGoAppointmentFolders.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007 Inverse groupe conseil
|
||||
*
|
||||
@@ -20,12 +20,27 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef SOGOACLFRENCHADDITIONADVISORY_H
|
||||
#define SOGOACLFRENCHADDITIONADVISORY_H
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import "SOGoACLAdvisory.h"
|
||||
#import "SOGoAppointmentFolder.h"
|
||||
|
||||
#import "SOGoAppointmentFolders.h"
|
||||
|
||||
@implementation SOGoAppointmentFolders
|
||||
|
||||
+ (NSString *) gcsFolderType
|
||||
{
|
||||
return @"Appointment";
|
||||
}
|
||||
|
||||
+ (Class) subFolderClass
|
||||
{
|
||||
return [SOGoAppointmentFolder class];
|
||||
}
|
||||
|
||||
- (NSString *) defaultFolderName
|
||||
{
|
||||
return @"Personal calendar";
|
||||
}
|
||||
|
||||
@interface SOGoACLEnglishAdditionAdvisory : SOGoACLAdvisory
|
||||
@end
|
||||
|
||||
#endif /* SOGOACLFRENCHADDITIONADVISORY_H */
|
||||
@@ -46,13 +46,6 @@
|
||||
|
||||
@interface SOGoAppointmentObject : SOGoCalendarComponent
|
||||
|
||||
/* folder management */
|
||||
|
||||
- (id) lookupHomeFolderForUID: (NSString *) _uid
|
||||
inContext: (id) _ctx;
|
||||
- (NSArray *) lookupCalendarFoldersForUIDs: (NSArray *) _uids
|
||||
inContext: (id) _ctx;
|
||||
|
||||
/* "iCal multifolder saves" */
|
||||
|
||||
- (NSException *) saveContentString: (NSString *) _iCal
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
@@ -32,6 +34,8 @@
|
||||
#import <SoObjects/SOGo/SOGoPermissions.h>
|
||||
|
||||
#import "NSArray+Appointments.h"
|
||||
#import "SOGoAppointmentFolder.h"
|
||||
|
||||
#import "SOGoAppointmentObject.h"
|
||||
|
||||
@implementation SOGoAppointmentObject
|
||||
@@ -94,25 +98,17 @@
|
||||
return uids;
|
||||
}
|
||||
|
||||
/* folder management */
|
||||
|
||||
- (id)lookupHomeFolderForUID:(NSString *)_uid inContext:(id)_ctx {
|
||||
// TODO: what does this do? lookup the home of the organizer?
|
||||
return [[self container] lookupHomeFolderForUID:_uid inContext:_ctx];
|
||||
}
|
||||
- (NSArray *)lookupCalendarFoldersForUIDs:(NSArray *)_uids inContext:(id)_ctx {
|
||||
return [[self container] lookupCalendarFoldersForUIDs:_uids inContext:_ctx];
|
||||
}
|
||||
|
||||
/* store in all the other folders */
|
||||
|
||||
- (NSException *)saveContentString:(NSString *)_iCal inUIDs:(NSArray *)_uids {
|
||||
- (NSException *) saveContentString: (NSString *) _iCal
|
||||
inUIDs: (NSArray *) _uids
|
||||
{
|
||||
NSEnumerator *e;
|
||||
id folder;
|
||||
NSException *allErrors = nil;
|
||||
|
||||
e = [[self lookupCalendarFoldersForUIDs:_uids inContext: context]
|
||||
objectEnumerator];
|
||||
e = [[container lookupCalendarFoldersForUIDs:_uids inContext: context]
|
||||
objectEnumerator];
|
||||
while ((folder = [e nextObject]) != nil) {
|
||||
NSException *error;
|
||||
SOGoAppointmentObject *apt;
|
||||
@@ -156,8 +152,8 @@
|
||||
id folder;
|
||||
NSException *allErrors = nil;
|
||||
|
||||
e = [[self lookupCalendarFoldersForUIDs:_uids inContext: context]
|
||||
objectEnumerator];
|
||||
e = [[container lookupCalendarFoldersForUIDs:_uids inContext: context]
|
||||
objectEnumerator];
|
||||
while ((folder = [e nextObject])) {
|
||||
NSException *error;
|
||||
SOGoAppointmentObject *apt;
|
||||
@@ -179,6 +175,14 @@
|
||||
}
|
||||
|
||||
/* "iCal multifolder saves" */
|
||||
- (BOOL) _aptIsStillRelevant: (iCalEvent *) appointment
|
||||
{
|
||||
NSCalendarDate *now;
|
||||
|
||||
now = [NSCalendarDate calendarDate];
|
||||
|
||||
return ([[appointment endDate] earlierDate: now] == now);
|
||||
}
|
||||
|
||||
- (NSException *) saveContentString: (NSString *) _iCal
|
||||
baseSequence: (int) _v
|
||||
@@ -319,9 +323,11 @@
|
||||
if (delError != nil) return delError;
|
||||
|
||||
/* email notifications */
|
||||
if ([self sendEMailNotifications])
|
||||
if ([self sendEMailNotifications]
|
||||
&& [self _aptIsStillRelevant: newApt])
|
||||
{
|
||||
attendees = [NSMutableArray arrayWithArray: [changes insertedAttendees]];
|
||||
attendees
|
||||
= [NSMutableArray arrayWithArray: [changes insertedAttendees]];
|
||||
[attendees removePerson: organizer];
|
||||
[self sendEMailUsingTemplateNamed: @"Invitation"
|
||||
forOldObject: nil
|
||||
@@ -338,19 +344,20 @@
|
||||
toAttendees: attendees];
|
||||
}
|
||||
|
||||
attendees = [NSMutableArray arrayWithArray:[changes deletedAttendees]];
|
||||
attendees
|
||||
= [NSMutableArray arrayWithArray: [changes deletedAttendees]];
|
||||
[attendees removePerson: organizer];
|
||||
if ([attendees count])
|
||||
{
|
||||
iCalEvent *canceledApt;
|
||||
iCalEvent *cancelledApt;
|
||||
|
||||
canceledApt = [newApt copy];
|
||||
[(iCalCalendar *) [canceledApt parent] setMethod: @"cancel"];
|
||||
cancelledApt = [newApt copy];
|
||||
[(iCalCalendar *) [cancelledApt parent] setMethod: @"cancel"];
|
||||
[self sendEMailUsingTemplateNamed: @"Removal"
|
||||
forOldObject: nil
|
||||
andNewObject: canceledApt
|
||||
andNewObject: cancelledApt
|
||||
toAttendees: attendees];
|
||||
[canceledApt release];
|
||||
[cancelledApt release];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +403,7 @@
|
||||
attendees = [NSMutableArray arrayWithArray:[apt attendees]];
|
||||
[attendees removePerson:[apt organizer]];
|
||||
|
||||
/* flag appointment as being canceled */
|
||||
/* flag appointment as being cancelled */
|
||||
[(iCalCalendar *) [apt parent] setMethod: @"cancel"];
|
||||
[apt increaseSequence];
|
||||
|
||||
|
||||
@@ -22,17 +22,19 @@
|
||||
#include "SOGoAptMailNotification.h"
|
||||
|
||||
@interface SOGoAptMailEnglishDeletion : SOGoAptMailNotification
|
||||
{
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SOGoAptMailEnglishDeletion
|
||||
@end
|
||||
|
||||
@interface SOGoAptMailFrenchDeletion : SOGoAptMailNotification
|
||||
{
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SOGoAptMailFrenchDeletion
|
||||
@end
|
||||
|
||||
@interface SOGoAptMailGermanDeletion : SOGoAptMailNotification
|
||||
@end
|
||||
|
||||
@implementation SOGoAptMailGermanDeletion
|
||||
@end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<#IsSubject>Appointment <#AptStartDate /> at <#AptStartTime /> has been canceled</#IsSubject>
|
||||
<#IsSubject>Appointment <#AptStartDate /> at <#AptStartTime /> has been cancelled</#IsSubject>
|
||||
<#IsBody>
|
||||
The appointment at <#AptStartDate /> <#AptStartTime /> has been cancelled by <#Organizer />.
|
||||
</#IsBody>
|
||||
@@ -1,4 +1,4 @@
|
||||
<#IsSubject>Le rendez-vous du <#AptStartDate /> à <#AptStartTime /> a été supprimé</#IsSubject>
|
||||
<#IsSubject>Le rendez-vous du <#AptStartDate /> à <#AptStartTime /> a été supprimé</#IsSubject>
|
||||
<#IsBody>
|
||||
Le rendez-vous du <#AptStartDate /> à <#AptStartTime /> a été supprimé par <#Organizer />.
|
||||
Le rendez-vous du <#AptStartDate /> à <#AptStartTime /> a été supprimé par <#Organizer />.
|
||||
</#IsBody>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<#IsSubject>Rendez-vous le <#AptStartDate /> à <#AptStartTime /></#IsSubject>
|
||||
<#IsSubject>Rendez-vous le <#AptStartDate /> à <#AptStartTime /></#IsSubject>
|
||||
<#IsBody>
|
||||
Vous êtes invité par <#Organizer /> à une réunion.
|
||||
Vous êtes invité par <#Organizer /> à une réunion.
|
||||
<#HasHomePageURL>
|
||||
Veuillez donner votre réponse à l'adresse <#HomePageURL />.
|
||||
Veuillez donner votre réponse à l'adresse <#HomePageURL />.
|
||||
</#HasHomePageURL>
|
||||
</#IsBody>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<#IsSubject>Supprimé de la réunion du <#AptStartDate /> à <#AptStartTime /></#IsSubject>
|
||||
<#IsSubject>Supprimé de la réunion du <#AptStartDate /> à <#AptStartTime /></#IsSubject>
|
||||
<#IsBody>
|
||||
Vous ne faites plus parti de la liste des invités de la réunion du <#AptStartDate /> à <#AptStartTime /> organisée par <#Organizer />.
|
||||
Vous ne faites plus parti de la liste des invités de la réunion du <#AptStartDate /> à <#AptStartTime /> organisée par <#Organizer />.
|
||||
</#IsBody>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<#IsSubject>Rendez-vous du <#OldAptStartDate /> à <#OldAptStartTime /> modifié</#IsSubject>
|
||||
<#IsSubject>Rendez-vous du <#OldAptStartDate /> à <#OldAptStartTime /> modifié</#IsSubject>
|
||||
<#IsBody>
|
||||
La réunion qui devait se dérouler le <#OldAptStartDate /> à <#OldAptStartTime /> est maintenant prévue le <#NewAptStartDate /> à <#NewAptStartTime />.
|
||||
Vous êtes invité à accepter ou refuser de participer à la réunion pour cette nouvelle date à l'adresse <#HomePageURL />.
|
||||
La réunion qui devait se dérouler le <#OldAptStartDate /> à <#OldAptStartTime /> est maintenant prévue le <#NewAptStartDate /> à <#NewAptStartTime />.
|
||||
Vous êtes invité à accepter ou refuser de participer à la réunion pour cette nouvelle date à l'adresse <#HomePageURL />.
|
||||
</#IsBody>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<#IsSubject>Der Termin am <#AptStartDate /> um <#AptStartTime /> wurde gelöscht.</#IsSubject>
|
||||
<#IsSubject>Der Termin am <#AptStartDate /> um <#AptStartTime /> wurde gelöscht.</#IsSubject>
|
||||
<#IsBody>
|
||||
Der Termin am <#AptStartDate /> um <#AptStartTime /> wurde von <#Organizer /> gelöscht.
|
||||
Der Termin am <#AptStartDate /> um <#AptStartTime /> wurde von <#Organizer /> gelöscht.
|
||||
</#IsBody>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<#IsSubject>Termin am <#AptStartDate /> um <#AptStartTime /></#IsSubject>
|
||||
<#IsBody>
|
||||
<#Organizer /> lädt Sie zu einem Termin am <#AptStartDate /> um <#AptStartTime /> ein.
|
||||
<#Organizer /> lädt Sie zu einem Termin am <#AptStartDate /> um <#AptStartTime /> ein.
|
||||
<#HasHomePageURL>
|
||||
Bitte benutzen Sie folgende URL, um anzugeben, ob Sie an dem Termin teilnehmen können: <#HomePageURL />.
|
||||
Bitte benutzen Sie folgende URL, um anzugeben, ob Sie an dem Termin teilnehmen können: <#HomePageURL />.
|
||||
</#HasHomePageURL>
|
||||
</#IsBody>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<#IsSubject>Von dem Termin am <#AptStartDate /> um <#AptStartTime /> gelöscht</#IsSubject>
|
||||
<#IsSubject>Von dem Termin am <#AptStartDate /> um <#AptStartTime /> gelöscht</#IsSubject>
|
||||
<#IsBody>
|
||||
Sie sind nicht mehr eingeladen zu dem Termin am <#AptStartDate /> um <#AptStartTime /> organisiert von <#Organizer />.
|
||||
</#IsBody>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<#IsSubject>Veränderung des Termins am <#OldAptStartDate /> um <#OldAptStartTime /></#IsSubject>
|
||||
<#IsSubject>Veränderung des Termins am <#OldAptStartDate /> um <#OldAptStartTime /></#IsSubject>
|
||||
<#IsBody>
|
||||
Der Termin, der ursprünglich am <#OldAptStartDate /> um <#OldAptStartTime /> stattfinden sollte, ist jetzt für den <#NewAptStartDate /> um <#NewAptStartTime /> geplant.
|
||||
Bitte geben Sie an folgender URL an, ob Sie an diesem Termin zum neuen Datum teilnehmen können: <#HomePageURL />.
|
||||
Der Termin, der ursprünglich am <#OldAptStartDate /> um <#OldAptStartTime /> stattfinden sollte, ist jetzt für den <#NewAptStartDate /> um <#NewAptStartTime /> geplant.
|
||||
Bitte geben Sie an folgender URL an, ob Sie an diesem Termin zum neuen Datum teilnehmen können: <#HomePageURL />.
|
||||
</#IsBody>
|
||||
|
||||
@@ -19,20 +19,22 @@
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "SOGoAptMailNotification.h"
|
||||
#import "SOGoAptMailNotification.h"
|
||||
|
||||
@interface SOGoAptMailEnglishInvitation : SOGoAptMailNotification
|
||||
{
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SOGoAptMailEnglishInvitation
|
||||
@end
|
||||
|
||||
@interface SOGoAptMailFrenchInvitation : SOGoAptMailNotification
|
||||
{
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SOGoAptMailFrenchInvitation
|
||||
@end
|
||||
|
||||
@interface SOGoAptMailGermanInvitation : SOGoAptMailNotification
|
||||
@end
|
||||
|
||||
@implementation SOGoAptMailGermanInvitation
|
||||
@end
|
||||
|
||||
@@ -48,21 +48,21 @@
|
||||
- (id) newApt;
|
||||
- (void) setNewApt: (iCalEntityObject *) _newApt;
|
||||
|
||||
- (NSString *)homePageURL;
|
||||
- (void)setHomePageURL: (NSString *)_homePageURL;
|
||||
- (NSString *) homePageURL;
|
||||
- (void) setHomePageURL: (NSString *) _homePageURL;
|
||||
|
||||
- (NSTimeZone *)viewTZ;
|
||||
- (void)setViewTZ:(NSTimeZone *)_viewTZ;
|
||||
- (NSTimeZone *) viewTZ;
|
||||
- (void) setViewTZ: (NSTimeZone *) _viewTZ;
|
||||
|
||||
/* Helpers */
|
||||
|
||||
- (NSCalendarDate *)oldStartDate;
|
||||
- (NSCalendarDate *)newStartDate;
|
||||
- (NSCalendarDate *) oldStartDate;
|
||||
- (NSCalendarDate *) newStartDate;
|
||||
|
||||
/* Content Generation */
|
||||
|
||||
- (NSString *)getSubject;
|
||||
- (NSString *)getBody;
|
||||
- (NSString *) getSubject;
|
||||
- (NSString *) getBody;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGCards/iCalEntityObject.h>
|
||||
|
||||
#import <SoObjects/SOGo/NSString+Utilities.h>
|
||||
|
||||
#import "SOGoAptMailNotification.h"
|
||||
|
||||
@interface SOGoAptMailNotification (PrivateAPI)
|
||||
@@ -88,7 +90,7 @@ static NSTimeZone *EST = nil;
|
||||
NSString *aptUID;
|
||||
|
||||
aptUID = [[self newApt] uid];
|
||||
return [NSString stringWithFormat:@"%@/Calendar/%@/edit?mail-invitation=yes",
|
||||
return [NSString stringWithFormat:@"%@/Calendar/personal/%@/edit?mail-invitation=yes",
|
||||
[self homePageURL],
|
||||
aptUID];
|
||||
}
|
||||
@@ -141,7 +143,8 @@ static NSTimeZone *EST = nil;
|
||||
[self name]];
|
||||
subject = @"ERROR: missing subject!";
|
||||
}
|
||||
return subject;
|
||||
|
||||
return [subject asQPSubjectString: @"utf-8"];
|
||||
}
|
||||
|
||||
- (NSString *)getBody {
|
||||
|
||||
@@ -19,20 +19,22 @@
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "SOGoAptMailNotification.h"
|
||||
#import "SOGoAptMailNotification.h"
|
||||
|
||||
@interface SOGoAptMailEnglishRemoval : SOGoAptMailNotification
|
||||
{
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SOGoAptMailEnglishRemoval
|
||||
@end
|
||||
|
||||
@interface SOGoAptMailFrenchRemoval : SOGoAptMailNotification
|
||||
{
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SOGoAptMailFrenchRemoval
|
||||
@end
|
||||
|
||||
@interface SOGoAptMailGermanRemoval : SOGoAptMailNotification
|
||||
@end
|
||||
|
||||
@implementation SOGoAptMailGermanRemoval
|
||||
@end
|
||||
|
||||
@@ -567,7 +567,7 @@ static BOOL sendEMailNotifications = NO;
|
||||
}
|
||||
else
|
||||
role = SOGoCalendarRole_Organizer;
|
||||
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoPermissions.h>
|
||||
|
||||
#import "SOGoAppointmentFolder.h"
|
||||
|
||||
#import "SOGoFreeBusyObject.h"
|
||||
|
||||
@interface SOGoFreeBusyObject (PrivateAPI)
|
||||
@@ -80,17 +82,18 @@
|
||||
- (NSArray *) fetchFreeBusyInfosFrom: (NSCalendarDate *) _startDate
|
||||
to: (NSCalendarDate *) _endDate
|
||||
{
|
||||
id calFolder;
|
||||
SOGoAppointmentFolder *calFolder;
|
||||
// SoSecurityManager *sm;
|
||||
NSArray *infos;
|
||||
|
||||
calFolder = [container lookupName: @"Calendar" inContext: nil acquire: NO];
|
||||
calFolder = [[container lookupName: @"Calendar" inContext: nil acquire: NO]
|
||||
lookupName: @"personal" inContext: nil acquire: NO];
|
||||
// sm = [SoSecurityManager sharedSecurityManager];
|
||||
// if (![sm validatePermission: SOGoPerm_FreeBusyLookup
|
||||
// onObject: calFolder
|
||||
// inContext: context])
|
||||
infos = [calFolder fetchFreeBusyInfosFrom: _startDate
|
||||
to: _endDate];
|
||||
infos = [calFolder fetchFreeBusyInfosFrom: _startDate
|
||||
to: _endDate];
|
||||
// else
|
||||
// {
|
||||
// infos = [NSArray new];
|
||||
@@ -169,9 +172,9 @@
|
||||
info = [events nextObject];
|
||||
while (info)
|
||||
{
|
||||
if ([[info objectForKey: @"isopaque"] boolValue])
|
||||
if ([[info objectForKey: @"c_isopaque"] boolValue])
|
||||
{
|
||||
type = [self _fbTypeForEventStatus: [info objectForKey: @"status"]];
|
||||
type = [self _fbTypeForEventStatus: [info objectForKey: @"c_status"]];
|
||||
[freebusy addFreeBusyFrom: [info objectForKey: @"startDate"]
|
||||
to: [info objectForKey: @"endDate"]
|
||||
type: type];
|
||||
|
||||
@@ -44,13 +44,6 @@
|
||||
|
||||
@interface SOGoTaskObject : SOGoCalendarComponent
|
||||
|
||||
/* folder management */
|
||||
|
||||
- (id) lookupHomeFolderForUID: (NSString *) _uid
|
||||
inContext: (id) _ctx;
|
||||
- (NSArray *) lookupCalendarFoldersForUIDs: (NSArray *) _uids
|
||||
inContext: (id) _ctx;
|
||||
|
||||
/* "iCal multifolder saves" */
|
||||
|
||||
- (NSException *) saveContentString: (NSString *) _iCal
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
#import "NSArray+Appointments.h"
|
||||
#import "SOGoAptMailNotification.h"
|
||||
#import "SOGoAppointmentFolder.h"
|
||||
|
||||
#import "SOGoTaskObject.h"
|
||||
|
||||
@@ -118,17 +119,6 @@ static NSString *mailTemplateDefaultLanguage = nil;
|
||||
return uids;
|
||||
}
|
||||
|
||||
/* folder management */
|
||||
|
||||
- (id)lookupHomeFolderForUID:(NSString *)_uid inContext:(id)_ctx {
|
||||
// TODO: what does this do? lookup the home of the organizer?
|
||||
return [[self container] lookupHomeFolderForUID:_uid inContext:_ctx];
|
||||
}
|
||||
|
||||
- (NSArray *)lookupCalendarFoldersForUIDs:(NSArray *)_uids inContext:(id)_ctx {
|
||||
return [[self container] lookupCalendarFoldersForUIDs:_uids inContext:_ctx];
|
||||
}
|
||||
|
||||
/* store in all the other folders */
|
||||
|
||||
- (NSException *)saveContentString:(NSString *)_iCal inUIDs:(NSArray *)_uids {
|
||||
@@ -136,7 +126,7 @@ static NSString *mailTemplateDefaultLanguage = nil;
|
||||
id folder;
|
||||
NSException *allErrors = nil;
|
||||
|
||||
e = [[self lookupCalendarFoldersForUIDs: _uids inContext: context]
|
||||
e = [[container lookupCalendarFoldersForUIDs: _uids inContext: context]
|
||||
objectEnumerator];
|
||||
while ((folder = [e nextObject]) != nil) {
|
||||
NSException *error;
|
||||
@@ -170,38 +160,38 @@ static NSString *mailTemplateDefaultLanguage = nil;
|
||||
}
|
||||
return allErrors;
|
||||
}
|
||||
- (NSException *)deleteInUIDs:(NSArray *)_uids {
|
||||
NSEnumerator *e;
|
||||
id folder;
|
||||
NSException *allErrors = nil;
|
||||
// - (NSException *)deleteInUIDs:(NSArray *)_uids {
|
||||
// NSEnumerator *e;
|
||||
// id folder;
|
||||
// NSException *allErrors = nil;
|
||||
|
||||
e = [[self lookupCalendarFoldersForUIDs: _uids inContext: context]
|
||||
objectEnumerator];
|
||||
while ((folder = [e nextObject])) {
|
||||
NSException *error;
|
||||
SOGoTaskObject *task;
|
||||
// e = [[container lookupCalendarFoldersForUIDs: _uids inContext: context]
|
||||
// objectEnumerator];
|
||||
// while ((folder = [e nextObject])) {
|
||||
// NSException *error;
|
||||
// SOGoTaskObject *task;
|
||||
|
||||
task = [folder lookupName: [self nameInContainer]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
if (![task isNotNull]) {
|
||||
[self logWithFormat:@"Note: did not find '%@' in folder: %@",
|
||||
[self nameInContainer], folder];
|
||||
continue;
|
||||
}
|
||||
if ([task isKindOfClass: [NSException class]]) {
|
||||
[self logWithFormat:@"Exception: %@", [(NSException *) task reason]];
|
||||
continue;
|
||||
}
|
||||
// task = [folder lookupName: [self nameInContainer]
|
||||
// inContext: context
|
||||
// acquire: NO];
|
||||
// if (![task isNotNull]) {
|
||||
// [self logWithFormat:@"Note: did not find '%@' in folder: %@",
|
||||
// [self nameInContainer], folder];
|
||||
// continue;
|
||||
// }
|
||||
// if ([task isKindOfClass: [NSException class]]) {
|
||||
// [self logWithFormat:@"Exception: %@", [(NSException *) task reason]];
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if ((error = [task primaryDelete]) != nil) {
|
||||
[self logWithFormat:@"Note: failed to delete in folder: %@", folder];
|
||||
// TODO: make compound
|
||||
allErrors = error;
|
||||
}
|
||||
}
|
||||
return allErrors;
|
||||
}
|
||||
// if ((error = [task primaryDelete]) != nil) {
|
||||
// [self logWithFormat:@"Note: failed to delete in folder: %@", folder];
|
||||
// // TODO: make compound
|
||||
// allErrors = error;
|
||||
// }
|
||||
// }
|
||||
// return allErrors;
|
||||
// }
|
||||
|
||||
/* "iCal multifolder saves" */
|
||||
|
||||
@@ -376,15 +366,15 @@ static NSString *mailTemplateDefaultLanguage = nil;
|
||||
// attendees = [NSMutableArray arrayWithArray:[changes deletedAttendees]];
|
||||
// [attendees removePerson: organizer];
|
||||
// if ([attendees count]) {
|
||||
// iCalToDo *canceledApt;
|
||||
// iCalToDo *cancelledApt;
|
||||
|
||||
// canceledApt = [newApt copy];
|
||||
// [(iCalCalendar *) [canceledApt parent] setMethod: @"cancel"];
|
||||
// cancelledApt = [newApt copy];
|
||||
// [(iCalCalendar *) [cancelledApt parent] setMethod: @"cancel"];
|
||||
// [self sendEMailUsingTemplateNamed: @"Removal"
|
||||
// forOldObject: nil
|
||||
// andNewObject: canceledApt
|
||||
// andNewObject: cancelledApt
|
||||
// toAttendees: attendees];
|
||||
// [canceledApt release];
|
||||
// [cancelledApt release];
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -406,45 +396,48 @@ static NSString *mailTemplateDefaultLanguage = nil;
|
||||
- delete in removed folders
|
||||
- send iMIP mail for all folders not found
|
||||
*/
|
||||
iCalToDo *task;
|
||||
NSArray *removedUIDs;
|
||||
NSMutableArray *attendees;
|
||||
// iCalToDo *task;
|
||||
// NSArray *removedUIDs;
|
||||
// NSMutableArray *attendees;
|
||||
|
||||
/* load existing content */
|
||||
[self primaryDelete];
|
||||
|
||||
return nil;
|
||||
// /* load existing content */
|
||||
|
||||
task = (iCalToDo *) [self component: NO];
|
||||
// task = (iCalToDo *) [self component: NO];
|
||||
|
||||
/* compare sequence if requested */
|
||||
// /* compare sequence if requested */
|
||||
|
||||
if (_v != 0) {
|
||||
// TODO
|
||||
}
|
||||
// if (_v != 0) {
|
||||
// // TODO
|
||||
// }
|
||||
|
||||
removedUIDs = [self attendeeUIDsFromTask:task];
|
||||
// removedUIDs = [self attendeeUIDsFromTask:task];
|
||||
|
||||
if ([self sendEMailNotifications])
|
||||
{
|
||||
/* send notification email to attendees excluding organizer */
|
||||
attendees = [NSMutableArray arrayWithArray:[task attendees]];
|
||||
[attendees removePerson:[task organizer]];
|
||||
// if ([self sendEMailNotifications])
|
||||
// {
|
||||
// /* send notification email to attendees excluding organizer */
|
||||
// attendees = [NSMutableArray arrayWithArray:[task attendees]];
|
||||
// [attendees removePerson:[task organizer]];
|
||||
|
||||
/* flag task as being canceled */
|
||||
[(iCalCalendar *) [task parent] setMethod: @"cancel"];
|
||||
[task increaseSequence];
|
||||
// /* flag task as being cancelled */
|
||||
// [(iCalCalendar *) [task parent] setMethod: @"cancel"];
|
||||
// [task increaseSequence];
|
||||
|
||||
/* remove all attendees to signal complete removal */
|
||||
[task removeAllAttendees];
|
||||
// /* remove all attendees to signal complete removal */
|
||||
// [task removeAllAttendees];
|
||||
|
||||
/* send notification email */
|
||||
[self sendEMailUsingTemplateNamed: @"Deletion"
|
||||
forOldObject: nil
|
||||
andNewObject: task
|
||||
toAttendees: attendees];
|
||||
}
|
||||
// /* send notification email */
|
||||
// [self sendEMailUsingTemplateNamed: @"Deletion"
|
||||
// forOldObject: nil
|
||||
// andNewObject: task
|
||||
// toAttendees: attendees];
|
||||
// }
|
||||
|
||||
/* perform */
|
||||
// /* perform */
|
||||
|
||||
return [self deleteInUIDs:removedUIDs];
|
||||
// return [self deleteInUIDs:removedUIDs];
|
||||
}
|
||||
|
||||
- (NSException *)saveContentString:(NSString *)_iCalString {
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
|
||||
#import <NGCards/iCalPerson.h>
|
||||
|
||||
#import <SoObjects/SOGo/NSArray+Utilities.h>
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
|
||||
@@ -32,44 +34,31 @@
|
||||
|
||||
- (BOOL) userIsParticipant: (SOGoUser *) user
|
||||
{
|
||||
NSEnumerator *emails;
|
||||
NSArray *identities;
|
||||
NSString *currentEmail;
|
||||
BOOL response;
|
||||
NSEnumerator *participants;
|
||||
iCalPerson *currentParticipant;
|
||||
BOOL isParticipant;
|
||||
|
||||
response = NO;
|
||||
isParticipant = NO;
|
||||
|
||||
identities = [user allIdentities];
|
||||
emails = [[identities objectsForKey: @"email"] objectEnumerator];
|
||||
currentEmail = [emails nextObject];
|
||||
while (!response && currentEmail)
|
||||
if ([self isParticipant: currentEmail])
|
||||
response = YES;
|
||||
participants = [[self participants] objectEnumerator];
|
||||
currentParticipant = [participants nextObject];
|
||||
while (!isParticipant
|
||||
&& currentParticipant)
|
||||
if ([user hasEmail: [currentParticipant rfc822Email]])
|
||||
isParticipant = YES;
|
||||
else
|
||||
currentEmail = [emails nextObject];
|
||||
currentParticipant = [participants nextObject];
|
||||
|
||||
return response;
|
||||
return isParticipant;
|
||||
}
|
||||
|
||||
- (BOOL) userIsOrganizer: (SOGoUser *) user
|
||||
{
|
||||
NSEnumerator *emails;
|
||||
NSArray *identities;
|
||||
NSString *currentEmail;
|
||||
BOOL response;
|
||||
NSString *orgMail;
|
||||
|
||||
response = NO;
|
||||
orgMail = [[self organizer] rfc822Email];
|
||||
|
||||
identities = [user allIdentities];
|
||||
emails = [[identities objectsForKey: @"email"] objectEnumerator];
|
||||
currentEmail = [emails nextObject];
|
||||
while (!response && currentEmail)
|
||||
if ([self isOrganizer: currentEmail])
|
||||
response = YES;
|
||||
else
|
||||
currentEmail = [emails nextObject];
|
||||
|
||||
return response;
|
||||
return [user hasEmail: orgMail];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
};
|
||||
|
||||
classes = {
|
||||
SOGoAppointmentFolder = {
|
||||
superclass = "SOGoParentFolder";
|
||||
};
|
||||
SOGoAppointmentFolder = {
|
||||
superclass = "SOGoFolder";
|
||||
defaultRoles = {
|
||||
|
||||
1
SoObjects/Contacts/English.lproj/Localizable.strings
Normal file
1
SoObjects/Contacts/English.lproj/Localizable.strings
Normal file
@@ -0,0 +1 @@
|
||||
"Personal Address Book" = "Personal Address Book";
|
||||
1
SoObjects/Contacts/French.lproj/Localizable.strings
Normal file
1
SoObjects/Contacts/French.lproj/Localizable.strings
Normal file
@@ -0,0 +1 @@
|
||||
"Personal Address Book" = "Carnet d'adresses personnel";
|
||||
@@ -19,6 +19,9 @@ Contacts_RESOURCE_FILES += \
|
||||
Version \
|
||||
product.plist \
|
||||
|
||||
ADDITIONAL_INCLUDE_DIRS += -I../../SOPE/sope-gdl1/
|
||||
ADDITIONAL_LIB_DIRS += -L../../SOPE/sope-gdl1/GDLContentStore/obj/
|
||||
|
||||
-include GNUmakefile.preamble
|
||||
include $(GNUSTEP_MAKEFILES)/bundle.make
|
||||
-include GNUmakefile.postamble
|
||||
|
||||
1
SoObjects/Contacts/German.lproj/Localizable.strings
Normal file
1
SoObjects/Contacts/German.lproj/Localizable.strings
Normal file
@@ -0,0 +1 @@
|
||||
"Personal Calendar" = "Personal Calendar";
|
||||
@@ -43,16 +43,6 @@
|
||||
|
||||
@protocol SOGoContactFolder <NSObject>
|
||||
|
||||
+ (id <SOGoContactFolder>) contactFolderWithName: (NSString *) aName
|
||||
andDisplayName: (NSString *) aDisplayName
|
||||
inContainer: (SOGoObject *) aContainer;
|
||||
|
||||
- (id <SOGoContactFolder>) initWithName: (NSString *) aName
|
||||
andDisplayName: (NSString *) aDisplayName
|
||||
inContainer: (SOGoObject *) aContainer;
|
||||
|
||||
- (NSString *) displayName;
|
||||
|
||||
- (NSArray *) lookupContactsWithFilter: (NSString *) filter
|
||||
sortBy: (NSString *) sortKey
|
||||
ordering: (NSComparisonResult) sortOrdering;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SOGoContactFolders.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006 Inverse groupe conseil
|
||||
* Copyright (C) 2006, 2007 Inverse groupe conseil
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
@@ -23,25 +23,9 @@
|
||||
#ifndef SOGOCONTACTFOLDERS_H
|
||||
#define SOGOCONTACTFOLDERS_H
|
||||
|
||||
#import <SOGo/SOGoObject.h>
|
||||
#import <SoObjects/SOGo/SOGoParentFolder.h>
|
||||
|
||||
@class NSMutableDictionary;
|
||||
@class NSString;
|
||||
@class WOResponse;
|
||||
|
||||
@interface SOGoContactFolders : SOGoObject
|
||||
{
|
||||
NSMutableDictionary *contactFolders;
|
||||
NSString *OCSPath;
|
||||
}
|
||||
|
||||
- (NSString *) defaultSourceName;
|
||||
|
||||
- (void) setBaseOCSPath: (NSString *) newOCSPath;
|
||||
|
||||
- (NSArray *) contactFolders;
|
||||
|
||||
- (WOResponse *) newFolderWithName: (NSString *) name;
|
||||
@interface SOGoContactFolders : SOGoParentFolder
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SOGoContactFolders.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006 Inverse groupe conseil
|
||||
* Copyright (C) 2006, 2007 Inverse groupe conseil
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
@@ -20,7 +20,6 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/* exchange folder types: */
|
||||
/* MailItems IPF.Note
|
||||
ContactItems IPF.Contact
|
||||
AppointmentItems IPF.Appointment
|
||||
@@ -28,23 +27,11 @@
|
||||
TaskItems IPF.Task
|
||||
JournalItems IPF.Journal */
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
#import <NGObjWeb/WOApplication.h>
|
||||
#import <NGObjWeb/WOContext.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGObjWeb/SoUser.h>
|
||||
|
||||
#import <GDLContentStore/GCSFolderManager.h>
|
||||
#import <GDLContentStore/GCSChannelManager.h>
|
||||
#import <GDLAccess/EOAdaptorChannel.h>
|
||||
#import <GDLContentStore/NSURL+GCS.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
|
||||
#import <SoObjects/SOGo/LDAPUserManager.h>
|
||||
#import <SoObjects/SOGo/SOGoPermissions.h>
|
||||
|
||||
#import "SOGoContactGCSFolder.h"
|
||||
#import "SOGoContactLDAPFolder.h"
|
||||
@@ -52,89 +39,14 @@
|
||||
|
||||
@implementation SOGoContactFolders
|
||||
|
||||
- (id) init
|
||||
+ (NSString *) gcsFolderType
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
contactFolders = nil;
|
||||
OCSPath = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
return @"Contact";
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
+ (Class) subFolderClass
|
||||
{
|
||||
if (contactFolders)
|
||||
[contactFolders release];
|
||||
if (OCSPath)
|
||||
[OCSPath release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) _fetchPersonalFolders: (NSString *) sql
|
||||
withChannel: (EOAdaptorChannel *) fc
|
||||
{
|
||||
NSArray *attrs;
|
||||
NSDictionary *row;
|
||||
SOGoContactGCSFolder *ab;
|
||||
BOOL hasPersonal;
|
||||
NSString *key, *path;
|
||||
|
||||
hasPersonal = NO;
|
||||
[fc evaluateExpressionX: sql];
|
||||
attrs = [fc describeResults: NO];
|
||||
row = [fc fetchAttributes: attrs withZone: NULL];
|
||||
while (row)
|
||||
{
|
||||
ab = [SOGoContactGCSFolder
|
||||
contactFolderWithName: [row objectForKey: @"c_path4"]
|
||||
andDisplayName: [row objectForKey: @"c_foldername"]
|
||||
inContainer: self];
|
||||
key = [row objectForKey: @"c_path4"];
|
||||
hasPersonal = (hasPersonal || [key isEqualToString: @"personal"]);
|
||||
[ab setOCSPath: [NSString stringWithFormat: @"%@/%@",
|
||||
OCSPath, key]];
|
||||
[contactFolders setObject: ab forKey: key];
|
||||
row = [fc fetchAttributes: attrs withZone: NULL];
|
||||
}
|
||||
|
||||
if (!hasPersonal)
|
||||
{
|
||||
ab = [SOGoContactGCSFolder contactFolderWithName: @"personal"
|
||||
andDisplayName: @"Contacts"
|
||||
inContainer: self];
|
||||
path = [NSString stringWithFormat:
|
||||
@"/Users/%@/Contacts/personal",
|
||||
[self ownerInContext: context]];
|
||||
[ab setOCSPath: path];
|
||||
[contactFolders setObject: ab forKey: @"personal"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) appendPersonalSources
|
||||
{
|
||||
GCSChannelManager *cm;
|
||||
EOAdaptorChannel *fc;
|
||||
NSURL *folderLocation;
|
||||
NSString *sql;
|
||||
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
folderLocation
|
||||
= [[GCSFolderManager defaultFolderManager] folderInfoLocation];
|
||||
fc = [cm acquireOpenChannelForURL: folderLocation];
|
||||
if (fc)
|
||||
{
|
||||
sql = [NSString
|
||||
stringWithFormat: (@"SELECT c_path4, c_foldername FROM %@"
|
||||
@" WHERE c_path2 = '%@'"
|
||||
@" AND c_folder_type = 'Contact'"),
|
||||
[folderLocation gcsTableName], [self ownerInContext: context]];
|
||||
[self _fetchPersonalFolders: sql withChannel: fc];
|
||||
[cm releaseChannel: fc];
|
||||
// sql = [sql stringByAppendingFormat:@" WHERE %@ = '%@'",
|
||||
// uidColumnName, [self uid]];
|
||||
}
|
||||
return [SOGoContactGCSFolder class];
|
||||
}
|
||||
|
||||
- (void) appendSystemSources
|
||||
@@ -150,142 +62,18 @@
|
||||
while (currentSourceID)
|
||||
{
|
||||
displayName = [um displayNameForSourceWithID: currentSourceID];
|
||||
currentFolder = [SOGoContactLDAPFolder contactFolderWithName: currentSourceID
|
||||
currentFolder = [SOGoContactLDAPFolder folderWithName: currentSourceID
|
||||
andDisplayName: displayName
|
||||
inContainer: self];
|
||||
[currentFolder setLDAPSource: [um sourceWithID: currentSourceID]];
|
||||
[contactFolders setObject: currentFolder forKey: currentSourceID];
|
||||
[subFolders setObject: currentFolder forKey: currentSourceID];
|
||||
currentSourceID = [sourceIDs nextObject];
|
||||
}
|
||||
}
|
||||
|
||||
- (WOResponse *) newFolderWithName: (NSString *) name
|
||||
- (NSString *) defaultFolderName
|
||||
{
|
||||
SOGoContactGCSFolder *newFolder;
|
||||
WOResponse *response;
|
||||
|
||||
newFolder = [SOGoContactGCSFolder contactFolderWithName: name
|
||||
andDisplayName: name
|
||||
inContainer: self];
|
||||
if ([newFolder isKindOfClass: [NSException class]])
|
||||
response = (WOResponse *) newFolder;
|
||||
else
|
||||
{
|
||||
[newFolder setOCSPath: [NSString stringWithFormat: @"%@/%@",
|
||||
OCSPath, name]];
|
||||
if ([newFolder create])
|
||||
{
|
||||
response = [WOResponse new];
|
||||
[response setStatus: 201];
|
||||
[response autorelease];
|
||||
}
|
||||
else
|
||||
response = [NSException exceptionWithHTTPStatus: 400
|
||||
reason: @"The new folder could not be created"];
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (void) initContactSources
|
||||
{
|
||||
if (!contactFolders)
|
||||
{
|
||||
contactFolders = [NSMutableDictionary new];
|
||||
[self appendPersonalSources];
|
||||
[self appendSystemSources];
|
||||
}
|
||||
}
|
||||
|
||||
- (id) lookupName: (NSString *) name
|
||||
inContext: (WOContext *) lookupContext
|
||||
acquire: (BOOL) acquire
|
||||
{
|
||||
id obj;
|
||||
|
||||
/* first check attributes directly bound to the application */
|
||||
obj = [super lookupName: name inContext: lookupContext acquire: NO];
|
||||
if (!obj)
|
||||
{
|
||||
if (!contactFolders)
|
||||
[self initContactSources];
|
||||
|
||||
obj = [contactFolders objectForKey: name];
|
||||
if (!obj)
|
||||
obj = [NSException exceptionWithHTTPStatus: 404];
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
- (NSArray *) toManyRelationshipKeys
|
||||
{
|
||||
if (!contactFolders)
|
||||
[self initContactSources];
|
||||
|
||||
return [contactFolders allKeys];
|
||||
}
|
||||
|
||||
- (NSArray *) contactFolders
|
||||
{
|
||||
if (!contactFolders)
|
||||
[self initContactSources];
|
||||
|
||||
return [contactFolders allValues];
|
||||
}
|
||||
|
||||
/* acls */
|
||||
- (NSArray *) aclsForUser: (NSString *) uid
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// - (NSString *) roleOfUser: (NSString *) uid
|
||||
// {
|
||||
// NSArray *roles, *traversalPath;
|
||||
// NSString *objectName, *role;
|
||||
|
||||
// role = nil;
|
||||
// traversalPath = [context objectForKey: @"SoRequestTraversalPath"];
|
||||
// if ([traversalPath count] > 2)
|
||||
// {
|
||||
// objectName = [traversalPath objectAtIndex: 2];
|
||||
// roles = [[context activeUser]
|
||||
// rolesForObject: [self lookupName: objectName
|
||||
// inContext: context
|
||||
// acquire: NO]
|
||||
// inContext: context];
|
||||
// if ([roles containsObject: SOGoRole_Assistant]
|
||||
// || [roles containsObject: SOGoRole_Delegate])
|
||||
// role = SOGoRole_Assistant;
|
||||
// }
|
||||
|
||||
// return role;
|
||||
// }
|
||||
|
||||
- (BOOL) davIsCollection
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *) davContentType
|
||||
{
|
||||
return @"httpd/unix-directory";
|
||||
}
|
||||
|
||||
- (void) setBaseOCSPath: (NSString *) newOCSPath
|
||||
{
|
||||
if (OCSPath)
|
||||
[OCSPath release];
|
||||
OCSPath = newOCSPath;
|
||||
if (OCSPath)
|
||||
[OCSPath retain];
|
||||
}
|
||||
|
||||
/* web interface */
|
||||
- (NSString *) defaultSourceName
|
||||
{
|
||||
return @"personal";
|
||||
return @"Personal Address Book";
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -30,9 +30,6 @@
|
||||
@class NSString;
|
||||
|
||||
@interface SOGoContactGCSFolder : SOGoFolder <SOGoContactFolder>
|
||||
{
|
||||
NSString *displayName;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -21,14 +21,20 @@
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
#import <NGObjWeb/SoObject+SoDAV.h>
|
||||
#import <NGObjWeb/WOContext.h>
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGObjWeb/WORequest.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <EOControl/EOQualifier.h>
|
||||
#import <EOControl/EOSortOrdering.h>
|
||||
#import <GDLContentStore/GCSFolder.h>
|
||||
#import <DOM/DOMProtocols.h>
|
||||
#import <SaxObjC/SaxObjC.h>
|
||||
#import <SaxObjC/XMLNamespaces.h>
|
||||
|
||||
#import <SoObjects/SOGo/NSDictionary+Utilities.h>
|
||||
#import "SOGoContactGCSEntry.h"
|
||||
@@ -41,47 +47,6 @@
|
||||
|
||||
@implementation SOGoContactGCSFolder
|
||||
|
||||
+ (id <SOGoContactFolder>) contactFolderWithName: (NSString *) aName
|
||||
andDisplayName: (NSString *) aDisplayName
|
||||
inContainer: (SOGoObject *) aContainer
|
||||
{
|
||||
SOGoContactGCSFolder *folder;
|
||||
|
||||
folder = [[self alloc] initWithName: aName
|
||||
andDisplayName: aDisplayName
|
||||
inContainer: aContainer];
|
||||
[folder autorelease];
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[displayName release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id <SOGoContactFolder>) initWithName: (NSString *) newName
|
||||
andDisplayName: (NSString *) newDisplayName
|
||||
inContainer: (SOGoObject *) newContainer
|
||||
{
|
||||
if ((self = [self initWithName: newName
|
||||
inContainer: newContainer]))
|
||||
ASSIGN (displayName, newDisplayName);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL) folderIsMandatory
|
||||
{
|
||||
return [nameInContainer isEqualToString: @"personal"];
|
||||
}
|
||||
|
||||
- (NSString *) displayName
|
||||
{
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/* name lookup */
|
||||
|
||||
- (id <SOGoContactObject>) lookupContactWithId: (NSString *) recordId
|
||||
@@ -105,7 +70,6 @@
|
||||
BOOL isPut;
|
||||
|
||||
isPut = NO;
|
||||
/* first check attributes directly bound to the application */
|
||||
obj = [super lookupName:_key inContext:_ctx acquire:NO];
|
||||
if (!obj)
|
||||
{
|
||||
@@ -210,6 +174,140 @@
|
||||
return newRecords;
|
||||
}
|
||||
|
||||
- (BOOL) _isValidFilter: (NSString *) theString
|
||||
{
|
||||
if ([theString caseInsensitiveCompare: @"sn"] == NSOrderedSame)
|
||||
return YES;
|
||||
|
||||
if ([theString caseInsensitiveCompare: @"givenname"] == NSOrderedSame)
|
||||
return YES;
|
||||
|
||||
if ([theString caseInsensitiveCompare: @"mail"] == NSOrderedSame)
|
||||
return YES;
|
||||
|
||||
if ([theString caseInsensitiveCompare: @"telephonenumber"] == NSOrderedSame)
|
||||
return YES;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSDictionary *) _parseContactFilter: (id <DOMElement>) filterElement
|
||||
{
|
||||
NSMutableDictionary *filterData;
|
||||
id <DOMNode> parentNode;
|
||||
id <DOMNodeList> ranges;
|
||||
|
||||
parentNode = [filterElement parentNode];
|
||||
|
||||
if ([[parentNode tagName] isEqualToString: @"filter"] &&
|
||||
[self _isValidFilter: [filterElement attribute: @"name"]])
|
||||
{
|
||||
ranges = [filterElement getElementsByTagName: @"text-match"];
|
||||
|
||||
if ([ranges count] && [[[ranges objectAtIndex: 0] childNodes] count])
|
||||
{
|
||||
filterData = [NSMutableDictionary new];
|
||||
[filterData autorelease];
|
||||
[filterData setObject: [[[[ranges objectAtIndex: 0] childNodes] lastObject] data]
|
||||
forKey: [filterElement attribute: @"name"]];
|
||||
}
|
||||
}
|
||||
else
|
||||
filterData = nil;
|
||||
|
||||
return filterData;
|
||||
}
|
||||
|
||||
#warning filters is leaked here
|
||||
- (NSArray *) _parseContactFilters: (id <DOMElement>) parentNode
|
||||
{
|
||||
NSEnumerator *children;
|
||||
id<DOMElement> node;
|
||||
NSMutableArray *filters;
|
||||
NSDictionary *filter;
|
||||
|
||||
filters = [NSMutableArray new];
|
||||
|
||||
children = [[parentNode getElementsByTagName: @"prop-filter"]
|
||||
objectEnumerator];
|
||||
|
||||
node = [children nextObject];
|
||||
|
||||
while (node)
|
||||
{
|
||||
filter = [self _parseContactFilter: node];
|
||||
if (filter)
|
||||
[filters addObject: filter];
|
||||
node = [children nextObject];
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
- (void) appendObject: (NSDictionary *) object
|
||||
withBaseURL: (NSString *) baseURL
|
||||
toREPORTResponse: (WOResponse *) r
|
||||
{
|
||||
SOGoContactGCSEntry *component;
|
||||
Class componentClass;
|
||||
NSString *name, *etagLine, *contactString;
|
||||
|
||||
name = [object objectForKey: @"c_name"];
|
||||
componentClass = [SOGoContactGCSEntry class];
|
||||
|
||||
component = [componentClass objectWithName: name inContainer: self];
|
||||
|
||||
[r appendContentString: @" <D:response>\r\n"];
|
||||
[r appendContentString: @" <D:href>"];
|
||||
[r appendContentString: baseURL];
|
||||
if (![baseURL hasSuffix: @"/"])
|
||||
[r appendContentString: @"/"];
|
||||
[r appendContentString: name];
|
||||
[r appendContentString: @"</D:href>\r\n"];
|
||||
|
||||
[r appendContentString: @" <D:propstat>\r\n"];
|
||||
[r appendContentString: @" <D:prop>\r\n"];
|
||||
etagLine = [NSString stringWithFormat: @" <D:getetag>%@</D:getetag>\r\n",
|
||||
[component davEntityTag]];
|
||||
[r appendContentString: etagLine];
|
||||
[r appendContentString: @" </D:prop>\r\n"];
|
||||
[r appendContentString: @" <D:status>HTTP/1.1 200 OK</D:status>\r\n"];
|
||||
[r appendContentString: @" </D:propstat>\r\n"];
|
||||
[r appendContentString: @" <C:addressbook-data>"];
|
||||
contactString = [[component contentAsString] stringByEscapingXMLString];
|
||||
[r appendContentString: contactString];
|
||||
[r appendContentString: @"</C:addressbook-data>\r\n"];
|
||||
[r appendContentString: @" </D:response>\r\n"];
|
||||
}
|
||||
|
||||
- (void) _appendComponentsMatchingFilters: (NSArray *) filters
|
||||
toResponse: (WOResponse *) response
|
||||
{
|
||||
unsigned int count, max;
|
||||
NSDictionary *currentFilter, *contact;
|
||||
NSEnumerator *contacts;
|
||||
NSString *baseURL;
|
||||
|
||||
baseURL = [self baseURLInContext: context];
|
||||
|
||||
max = [filters count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
currentFilter = [filters objectAtIndex: count];
|
||||
contacts = [[self lookupContactsWithFilter: [[currentFilter allValues] lastObject]
|
||||
sortBy: @"c_givenname"
|
||||
ordering: NSOrderedDescending]
|
||||
objectEnumerator];
|
||||
|
||||
while ((contact = [contacts nextObject]))
|
||||
{
|
||||
[self appendObject: contact
|
||||
withBaseURL: baseURL
|
||||
toREPORTResponse: response];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *) lookupContactsWithFilter: (NSString *) filter
|
||||
sortBy: (NSString *) sortKey
|
||||
ordering: (NSComparisonResult) sortOrdering
|
||||
@@ -218,13 +316,11 @@
|
||||
EOQualifier *qualifier;
|
||||
EOSortOrdering *ordering;
|
||||
|
||||
// NSLog (@"fetching records matching '%@', sorted by '%@' in order %d",
|
||||
// filter, sortKey, sortOrdering);
|
||||
|
||||
fields = folderListingFields;
|
||||
qualifier = [self _qualifierForFilter: filter];
|
||||
dbRecords = [[self ocsFolder] fetchFields: fields
|
||||
matchingQualifier: qualifier];
|
||||
|
||||
if ([dbRecords count] > 0)
|
||||
{
|
||||
records = [self _flattenedRecords: dbRecords];
|
||||
@@ -242,7 +338,7 @@
|
||||
// else
|
||||
// [self errorWithFormat:@"(%s): fetch failed!", __PRETTY_FUNCTION__];
|
||||
|
||||
//[self debugWithFormat:@"fetched %i records.", [records count]];
|
||||
[self debugWithFormat:@"fetched %i records.", [records count]];
|
||||
return records;
|
||||
}
|
||||
|
||||
@@ -251,6 +347,32 @@
|
||||
return [NSArray arrayWithObject: @"urn:ietf:params:xml:ns:carddav"];
|
||||
}
|
||||
|
||||
- (id) davAddressbookQuery: (id) queryContext
|
||||
{
|
||||
WOResponse *r;
|
||||
NSArray *filters;
|
||||
id <DOMDocument> document;
|
||||
|
||||
r = [context response];
|
||||
[r setStatus: 207];
|
||||
[r setContentEncoding: NSUTF8StringEncoding];
|
||||
[r setHeader: @"text/xml; charset=\"utf-8\"" forKey: @"content-type"];
|
||||
[r setHeader: @"no-cache" forKey: @"pragma"];
|
||||
[r setHeader: @"no-cache" forKey: @"cache-control"];
|
||||
[r appendContentString:@"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"];
|
||||
[r appendContentString: @"<D:multistatus xmlns:D=\"DAV:\""
|
||||
@" xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\r\n"];
|
||||
|
||||
document = [[context request] contentAsDOMDocument];
|
||||
filters = [self _parseContactFilters: [document documentElement]];
|
||||
|
||||
[self _appendComponentsMatchingFilters: filters
|
||||
toResponse: r];
|
||||
[r appendContentString:@"</D:multistatus>\r\n"];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
- (NSArray *) davComplianceClassesInContext: (id)_ctx
|
||||
{
|
||||
NSMutableArray *classes;
|
||||
@@ -273,14 +395,6 @@
|
||||
return @"vcard-collection";
|
||||
}
|
||||
|
||||
- (NSException *) delete
|
||||
{
|
||||
return (([nameInContainer isEqualToString: @"personal"])
|
||||
? [NSException exceptionWithHTTPStatus: 403
|
||||
reason: @"the 'personal' folder cannot be deleted"]
|
||||
: [super delete]);
|
||||
}
|
||||
|
||||
// /* GET */
|
||||
|
||||
// - (id) GETAction: (id)_ctx
|
||||
@@ -300,6 +414,20 @@
|
||||
// return r;
|
||||
// }
|
||||
|
||||
/* sorting */
|
||||
- (NSComparisonResult) compare: (id) otherFolder
|
||||
{
|
||||
NSComparisonResult comparison;
|
||||
|
||||
if ([NSStringFromClass([otherFolder class])
|
||||
isEqualToString: @"SOGoContactLDAPFolder"])
|
||||
comparison = NSOrderedAscending;
|
||||
else
|
||||
comparison = [super compare: otherFolder];
|
||||
|
||||
return comparison;
|
||||
}
|
||||
|
||||
/* folder type */
|
||||
|
||||
- (NSString *) folderType
|
||||
|
||||
@@ -37,9 +37,12 @@
|
||||
BOOL ignoreSoObjectHunger;
|
||||
}
|
||||
|
||||
- (id <SOGoContactFolder>) initWithName: (NSString *) newName
|
||||
andDisplayName: (NSString *) newDisplayName
|
||||
inContainer: (SOGoObject *) newContainer;
|
||||
+ (id) folderWithName: (NSString *) aName
|
||||
andDisplayName: (NSString *) aDisplayName
|
||||
inContainer: (id) aContainer;
|
||||
- (id) initWithName: (NSString *) newName
|
||||
andDisplayName: (NSString *) newDisplayName
|
||||
inContainer: (id) newContainer;
|
||||
- (void) setLDAPSource: (LDAPSource *) newLdapSource;
|
||||
|
||||
@end
|
||||
|
||||
@@ -41,11 +41,11 @@
|
||||
|
||||
@implementation SOGoContactLDAPFolder
|
||||
|
||||
+ (id <SOGoContactFolder>) contactFolderWithName: (NSString *) aName
|
||||
andDisplayName: (NSString *) aDisplayName
|
||||
inContainer: (SOGoObject *) aContainer
|
||||
+ (id) folderWithName: (NSString *) aName
|
||||
andDisplayName: (NSString *) aDisplayName
|
||||
inContainer: (id) aContainer
|
||||
{
|
||||
SOGoContactLDAPFolder *folder;
|
||||
id folder;
|
||||
|
||||
folder = [[self alloc] initWithName: aName
|
||||
andDisplayName: aDisplayName
|
||||
@@ -68,9 +68,9 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id <SOGoContactFolder>) initWithName: (NSString *) newName
|
||||
andDisplayName: (NSString *) newDisplayName
|
||||
inContainer: (SOGoObject *) newContainer
|
||||
- (id) initWithName: (NSString *) newName
|
||||
andDisplayName: (NSString *) newDisplayName
|
||||
inContainer: (id) newContainer
|
||||
{
|
||||
if ((self = [self initWithName: newName
|
||||
inContainer: newContainer]))
|
||||
@@ -256,7 +256,28 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
/* sorting */
|
||||
- (NSComparisonResult) compare: (id) otherFolder
|
||||
{
|
||||
NSComparisonResult comparison;
|
||||
|
||||
if ([NSStringFromClass([otherFolder class])
|
||||
isEqualToString: @"SOGoContactGCSFolder"])
|
||||
comparison = NSOrderedDescending;
|
||||
else
|
||||
comparison
|
||||
= [[self displayName]
|
||||
localizedCaseInsensitiveCompare: [otherFolder displayName]];
|
||||
|
||||
return comparison;
|
||||
}
|
||||
|
||||
/* acls */
|
||||
- (NSString *) ownerInContext: (WOContext *) noContext
|
||||
{
|
||||
return @"nobody";
|
||||
}
|
||||
|
||||
/* TODO: this might change one day when we support LDAP acls */
|
||||
- (NSArray *) aclsForUser: (NSString *) uid
|
||||
{
|
||||
|
||||
@@ -9,12 +9,7 @@
|
||||
|
||||
classes = {
|
||||
SOGoContactFolders = {
|
||||
superclass = "SOGoFolder";
|
||||
protectedBy = "Access Contents Information";
|
||||
defaultRoles = {
|
||||
"Access Contents Information" = ( "Authenticated" );
|
||||
"WebDAV Access" = ( "Authenticated" );
|
||||
};
|
||||
superclass = "SOGoParentFolder";
|
||||
};
|
||||
SOGoContactGCSFolder = {
|
||||
superclass = "SOGoFolder";
|
||||
|
||||
@@ -30,9 +30,7 @@ Mailer_OBJC_FILES += \
|
||||
SOGoDraftsFolder.m \
|
||||
SOGoDraftObject.m \
|
||||
\
|
||||
SOGoMailForward.m \
|
||||
SOGoMailEnglishForward.m \
|
||||
SOGoMailFrenchForward.m
|
||||
SOGoMailForward.m
|
||||
|
||||
Mailer_RESOURCE_FILES += \
|
||||
Version \
|
||||
@@ -40,7 +38,11 @@ Mailer_RESOURCE_FILES += \
|
||||
|
||||
Mailer_RESOURCE_FILES += \
|
||||
SOGoMailEnglishForward.wo \
|
||||
SOGoMailFrenchForward.wo
|
||||
SOGoMailFrenchForward.wo \
|
||||
SOGoMailGermanForward.wo
|
||||
|
||||
ADDITIONAL_INCLUDE_DIRS += -I../../SOPE/sope-gdl1/
|
||||
ADDITIONAL_LIB_DIRS += -L../../SOPE/sope-gdl1/GDLContentStore/obj/
|
||||
|
||||
-include GNUmakefile.preamble
|
||||
include $(GNUSTEP_MAKEFILES)/wobundle.make
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
NGImap4Envelope *envelope;
|
||||
int IMAP4ID;
|
||||
NSMutableDictionary *headers;
|
||||
NSString *inReplyTo;
|
||||
NSString *text;
|
||||
NSString *sourceURL;
|
||||
NSString *sourceFlag;
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
|
||||
#import <SoObjects/SOGo/NSArray+Utilities.h>
|
||||
#import <SoObjects/SOGo/NSCalendarDate+SOGo.h>
|
||||
#import <SoObjects/SOGo/NSString+Utilities.h>
|
||||
#import <SoObjects/SOGo/SOGoMailer.h>
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
#import "SOGoMailAccount.h"
|
||||
@@ -64,37 +65,8 @@
|
||||
|
||||
static NSString *contentTypeValue = @"text/plain; charset=utf-8";
|
||||
static NSString *headerKeys[] = {@"subject", @"to", @"cc", @"bcc",
|
||||
@"from", @"replyTo", nil};
|
||||
|
||||
@interface NSString (NGMimeHelpers)
|
||||
|
||||
- (NSString *) asQPSubjectString: (NSString *) encoding;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSString (NGMimeHelpers)
|
||||
|
||||
- (NSString *) asQPSubjectString: (NSString *) encoding;
|
||||
{
|
||||
NSString *qpString, *subjectString;
|
||||
NSData *subjectData, *destSubjectData;
|
||||
|
||||
subjectData = [self dataUsingEncoding: NSUTF8StringEncoding];
|
||||
destSubjectData = [subjectData dataByEncodingQuotedPrintable];
|
||||
|
||||
qpString = [[NSString alloc] initWithData: destSubjectData
|
||||
encoding: NSASCIIStringEncoding];
|
||||
[qpString autorelease];
|
||||
if ([qpString length] > [self length])
|
||||
subjectString = [NSString stringWithFormat: @"=?%@?Q?%@?=",
|
||||
encoding, qpString];
|
||||
else
|
||||
subjectString = self;
|
||||
|
||||
return subjectString;
|
||||
}
|
||||
|
||||
@end
|
||||
@"from", @"replyTo",
|
||||
nil};
|
||||
|
||||
@implementation SOGoDraftObject
|
||||
|
||||
@@ -128,6 +100,7 @@ static BOOL showTextAttachmentsInline = NO;
|
||||
text = @"";
|
||||
sourceURL = nil;
|
||||
sourceFlag = nil;
|
||||
inReplyTo = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -141,6 +114,7 @@ static BOOL showTextAttachmentsInline = NO;
|
||||
[path release];
|
||||
[sourceURL release];
|
||||
[sourceFlag release];
|
||||
[inReplyTo release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@@ -190,7 +164,7 @@ static BOOL showTextAttachmentsInline = NO;
|
||||
id headerValue;
|
||||
unsigned int count;
|
||||
|
||||
for (count = 0; count < 6; count++)
|
||||
for (count = 0; count < 7; count++)
|
||||
{
|
||||
headerValue = [newHeaders objectForKey: headerKeys[count]];
|
||||
if (headerValue)
|
||||
@@ -216,6 +190,11 @@ static BOOL showTextAttachmentsInline = NO;
|
||||
return text;
|
||||
}
|
||||
|
||||
- (void) setInReplyTo: (NSString *) newInReplyTo
|
||||
{
|
||||
ASSIGN (inReplyTo, newInReplyTo);
|
||||
}
|
||||
|
||||
- (void) setSourceURL: (NSString *) newSourceURL
|
||||
{
|
||||
ASSIGN (sourceURL, newSourceURL);
|
||||
@@ -237,6 +216,8 @@ static BOOL showTextAttachmentsInline = NO;
|
||||
[infos setObject: headers forKey: @"headers"];
|
||||
if (text)
|
||||
[infos setObject: text forKey: @"text"];
|
||||
if (inReplyTo)
|
||||
[infos setObject: inReplyTo forKey: @"inReplyTo"];
|
||||
if (IMAP4ID > -1)
|
||||
[infos setObject: [NSNumber numberWithInt: IMAP4ID]
|
||||
forKey: @"IMAP4ID"];
|
||||
@@ -291,6 +272,10 @@ static BOOL showTextAttachmentsInline = NO;
|
||||
value = [infoDict objectForKey: @"sourceFlag"];
|
||||
if (value)
|
||||
[self setSourceFlag: value];
|
||||
|
||||
value = [infoDict objectForKey: @"inReplyTo"];
|
||||
if (value)
|
||||
[self setInReplyTo: value];
|
||||
}
|
||||
|
||||
- (NSString *) relativeImap4Name
|
||||
@@ -498,15 +483,21 @@ static BOOL showTextAttachmentsInline = NO;
|
||||
- (void) fetchMailForReplying: (SOGoMailObject *) sourceMail
|
||||
toAll: (BOOL) toAll
|
||||
{
|
||||
NSString *contentForReply;
|
||||
NSString *contentForReply, *msgID;
|
||||
NSMutableDictionary *info;
|
||||
NGImap4Envelope *sourceEnvelope;
|
||||
|
||||
[sourceMail fetchCoreInfos];
|
||||
|
||||
info = [NSMutableDictionary dictionaryWithCapacity: 16];
|
||||
[info setObject: [sourceMail subjectForReply] forKey: @"subject"];
|
||||
|
||||
sourceEnvelope = [sourceMail envelope];
|
||||
[self _fillInReplyAddresses: info replyToAll: toAll
|
||||
envelope: [sourceMail envelope]];
|
||||
envelope: sourceEnvelope];
|
||||
msgID = [sourceEnvelope messageID];
|
||||
if ([msgID length] > 0)
|
||||
[self setInReplyTo: msgID];
|
||||
contentForReply = [sourceMail contentForReply];
|
||||
[self setText: contentForReply];
|
||||
[self setHeaders: info];
|
||||
@@ -587,7 +578,7 @@ static BOOL showTextAttachmentsInline = NO;
|
||||
|
||||
- (BOOL) isValidAttachmentName: (NSString *) _name
|
||||
{
|
||||
static NSString *sescape[] = { @"/", @"..", @"~", @"\"", @"'", @" ", nil };
|
||||
static NSString *sescape[] = { @"/", @"..", @"~", @"\"", @"'", nil };
|
||||
unsigned i;
|
||||
NSRange r;
|
||||
|
||||
@@ -620,7 +611,8 @@ static BOOL showTextAttachmentsInline = NO;
|
||||
withMetadata: (NSDictionary *) metadata
|
||||
{
|
||||
NSString *p, *name, *mimeType;
|
||||
|
||||
NSRange r;
|
||||
|
||||
if (![_attach isNotNull]) {
|
||||
return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
|
||||
reason: @"Missing attachment content!"];
|
||||
@@ -630,7 +622,13 @@ static BOOL showTextAttachmentsInline = NO;
|
||||
return [NSException exceptionWithHTTPStatus:500 /* Server Error */
|
||||
reason: @"Could not create folder for draft!"];
|
||||
}
|
||||
|
||||
name = [metadata objectForKey: @"filename"];
|
||||
r = [name rangeOfString: @"\\"
|
||||
options: NSBackwardsSearch];
|
||||
if (r.length > 0)
|
||||
name = [name substringFromIndex: r.location + 1];
|
||||
|
||||
if (![self isValidAttachmentName: name])
|
||||
return [self invalidAttachmentNameError: name];
|
||||
|
||||
@@ -1029,7 +1027,9 @@ static BOOL showTextAttachmentsInline = NO;
|
||||
[map setObjects:[map objectsForKey: @"from"] forKey: @"reply-to"];
|
||||
|
||||
/* add subject */
|
||||
|
||||
if (inReplyTo)
|
||||
[map setObject: inReplyTo forKey: @"in-reply-to"];
|
||||
|
||||
if ([(s = [headers objectForKey: @"subject"]) length] > 0)
|
||||
[map setObject: [s asQPSubjectString: @"utf-8"]
|
||||
forKey: @"subject"];
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <NGImap4/NGImap4Connection.h>
|
||||
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
|
||||
#import "SOGoMailFolder.h"
|
||||
#import "SOGoMailManager.h"
|
||||
#import "SOGoDraftsFolder.h"
|
||||
@@ -333,10 +335,32 @@ static BOOL useAltNamespace = NO;
|
||||
return [NSString stringWithFormat: @"folder%@", inboxFolderName];
|
||||
}
|
||||
|
||||
- (NSString *) _userFolderNameWithPurpose: (NSString *) purpose
|
||||
{
|
||||
NSUserDefaults *ud;
|
||||
NSMutableDictionary *mailSettings;
|
||||
NSString *folderName;
|
||||
|
||||
folderName = nil;
|
||||
ud = [[context activeUser] userSettings];
|
||||
mailSettings = [ud objectForKey: @"Mail"];
|
||||
if (mailSettings)
|
||||
folderName
|
||||
= [mailSettings objectForKey: [NSString stringWithFormat: @"%@Folder",
|
||||
purpose]];
|
||||
|
||||
return folderName;
|
||||
}
|
||||
|
||||
- (NSString *) draftsFolderNameInContext: (id) _ctx
|
||||
{
|
||||
/* SOGo managed folder */
|
||||
return [NSString stringWithFormat: @"folder%@", draftsFolderName];
|
||||
NSString *folderName;
|
||||
|
||||
folderName = [self _userFolderNameWithPurpose: @"Drafts"];
|
||||
if (!folderName)
|
||||
folderName = draftsFolderName;
|
||||
|
||||
return [NSString stringWithFormat: @"folder%@", folderName];
|
||||
}
|
||||
|
||||
- (NSString *) sieveFolderNameInContext: (id) _ctx
|
||||
@@ -346,12 +370,24 @@ static BOOL useAltNamespace = NO;
|
||||
|
||||
- (NSString *) sentFolderNameInContext: (id)_ctx
|
||||
{
|
||||
return [NSString stringWithFormat: @"folder%@", sentFolderName];
|
||||
NSString *folderName;
|
||||
|
||||
folderName = [self _userFolderNameWithPurpose: @"Sent"];
|
||||
if (!folderName)
|
||||
folderName = sentFolderName;
|
||||
|
||||
return [NSString stringWithFormat: @"folder%@", folderName];
|
||||
}
|
||||
|
||||
- (NSString *) trashFolderNameInContext: (id)_ctx
|
||||
{
|
||||
return [NSString stringWithFormat: @"folder%@", trashFolderName];
|
||||
NSString *folderName;
|
||||
|
||||
folderName = [self _userFolderNameWithPurpose: @"Trash"];
|
||||
if (!folderName)
|
||||
folderName = trashFolderName;
|
||||
|
||||
return [NSString stringWithFormat: @"folder%@", folderName];
|
||||
}
|
||||
|
||||
- (SOGoMailFolder *) inboxFolderInContext: (id) _ctx
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
|
||||
#import <NGObjWeb/SoObject+SoDAV.h>
|
||||
#import <NGObjWeb/SoHTTPAuthenticator.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
@@ -63,7 +63,7 @@ static BOOL useAltNamespace = NO;
|
||||
|
||||
folder = [mailAccount sharedFolderName];
|
||||
if (folder && [path hasPrefix: folder])
|
||||
[self setOwner: @"anyone"];
|
||||
[self setOwner: @"nobody"];
|
||||
else
|
||||
{
|
||||
folder = [mailAccount otherUsersFolderName];
|
||||
@@ -73,7 +73,7 @@ static BOOL useAltNamespace = NO;
|
||||
if ([names count] > 1)
|
||||
[self setOwner: [names objectAtIndex: 1]];
|
||||
else
|
||||
[self setOwner: @"anyone"];
|
||||
[self setOwner: @"nobody"];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,42 +225,6 @@ static BOOL useAltNamespace = NO;
|
||||
|
||||
/* name lookup */
|
||||
|
||||
- (id) lookupImap4Folder: (NSString *) _key
|
||||
inContext: (id) _ctx
|
||||
{
|
||||
// TODO: we might want to check for existence prior controller creation
|
||||
NSURL *sf;
|
||||
SOGoMailFolder *newFolder;
|
||||
|
||||
/* check whether URL exists */
|
||||
|
||||
sf = [self imap4URL];
|
||||
sf = [NSURL URLWithString: [_key substringFromIndex: 6]
|
||||
relativeToURL: sf];
|
||||
|
||||
// - sf = [NSURL URLWithString:[[sf path] stringByAppendingPathComponent:_key]
|
||||
// - relativeToURL:sf];
|
||||
|
||||
if ([[self imap4Connection] doesMailboxExistAtURL: sf])
|
||||
newFolder = [SOGoMailFolder objectWithName: _key inContainer: self];
|
||||
else
|
||||
newFolder = nil;
|
||||
/*
|
||||
We may not return 404, confuses path traversal - but we still do in the
|
||||
calling method. Probably the traversal process should be fixed to
|
||||
support 404 exceptions (as stop traversal _and_ acquisition).
|
||||
*/
|
||||
|
||||
return newFolder;
|
||||
}
|
||||
|
||||
- (id) lookupImap4Message: (NSString *) _key
|
||||
inContext: (id) _ctx
|
||||
{
|
||||
// TODO: we might want to check for existence prior controller creation
|
||||
return [SOGoMailObject objectWithName: _key inContainer: self];
|
||||
}
|
||||
|
||||
- (id) lookupName: (NSString *) _key
|
||||
inContext: (id)_ctx
|
||||
acquire: (BOOL) _acquire
|
||||
@@ -268,13 +232,18 @@ static BOOL useAltNamespace = NO;
|
||||
id obj;
|
||||
|
||||
if ([_key hasPrefix: @"folder"])
|
||||
obj = [self lookupImap4Folder: _key inContext: _ctx];
|
||||
obj = [SOGoMailFolder objectWithName: _key inContainer: self];
|
||||
else
|
||||
{
|
||||
if (isdigit ([_key characterAtIndex: 0]))
|
||||
obj = [self lookupImap4Message: _key inContext: _ctx];
|
||||
if ([[self imap4Connection] doesMailboxExistAtURL: [self imap4URL]])
|
||||
{
|
||||
if (isdigit ([_key characterAtIndex: 0]))
|
||||
obj = [SOGoMailObject objectWithName: _key inContainer: self];
|
||||
else
|
||||
obj = [super lookupName: _key inContext: _ctx acquire: NO];
|
||||
}
|
||||
else
|
||||
obj = [super lookupName: _key inContext: _ctx acquire: NO];
|
||||
obj = nil;
|
||||
}
|
||||
|
||||
if (!obj && _acquire)
|
||||
@@ -616,7 +585,7 @@ static BOOL useAltNamespace = NO;
|
||||
return userPath;
|
||||
}
|
||||
|
||||
- (NSString *) httpURLForAdvisoryToUser: (NSString *) uid;
|
||||
- (NSString *) httpURLForAdvisoryToUser: (NSString *) uid
|
||||
{
|
||||
SOGoUser *user;
|
||||
NSString *otherUsersPath, *url;
|
||||
@@ -640,7 +609,7 @@ static BOOL useAltNamespace = NO;
|
||||
return url;
|
||||
}
|
||||
|
||||
- (NSString *) resourceURLForAdvisoryToUser: (NSString *) uid;
|
||||
- (NSString *) resourceURLForAdvisoryToUser: (NSString *) uid
|
||||
{
|
||||
NSURL *selfURL, *userURL;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user