From ca3a9f2a62b629fc6f45c921c5d05c2473950e31 Mon Sep 17 00:00:00 2001 From: Lars Kneschke Date: Mon, 20 Feb 2006 10:59:39 +0000 Subject: [PATCH] removed wrong imported directory --- egwical/doc/EgwIcal-BUGS-and-TODOS.txt | 95 -- egwical/doc/EgwIcal-Timezone-handling.txt | 183 --- egwical/doc/EgwIcal-intro.txt | 182 --- egwical/doc/egwical-doxydoc.tgz | Bin 53757 -> 0 bytes egwical/doc/egwical-wurh-pattern.txt | 205 --- egwical/inc/class.bocalupdate_vevents.inc.php | 1075 -------------- egwical/inc/class.boical.inc.compat.php | 69 - egwical/inc/class.boinfolog_vtodos.inc.php | 561 -------- egwical/inc/class.egwical.inc.php | 1278 ----------------- egwical/inc/class.vcalinfolog.inc.compat.php | 72 - 10 files changed, 3720 deletions(-) delete mode 100644 egwical/doc/EgwIcal-BUGS-and-TODOS.txt delete mode 100644 egwical/doc/EgwIcal-Timezone-handling.txt delete mode 100644 egwical/doc/EgwIcal-intro.txt delete mode 100644 egwical/doc/egwical-doxydoc.tgz delete mode 100644 egwical/doc/egwical-wurh-pattern.txt delete mode 100644 egwical/inc/class.bocalupdate_vevents.inc.php delete mode 100644 egwical/inc/class.boical.inc.compat.php delete mode 100644 egwical/inc/class.boinfolog_vtodos.inc.php delete mode 100644 egwical/inc/class.egwical.inc.php delete mode 100644 egwical/inc/class.vcalinfolog.inc.compat.php diff --git a/egwical/doc/EgwIcal-BUGS-and-TODOS.txt b/egwical/doc/EgwIcal-BUGS-and-TODOS.txt deleted file mode 100644 index 1cd51ba64d..0000000000 --- a/egwical/doc/EgwIcal-BUGS-and-TODOS.txt +++ /dev/null @@ -1,95 +0,0 @@ -/*! \page pageegwicalbugandtodo BUGS and TODOS - -
-Know BUGS and things todo
----------------------------
-TODO means: should be done/would be nice  in a newer release
-BUG means: known problem needs to be fixed
-FAIl means: known failure to provide (not easily to be done/fixed
-
-(note: this list is probably not quite up todate)
-
-
-release: VERSION 0.9.0 NEED TO UPDATE THIS!!
-
-new in V0.7.78 versus v0.7.70:
-
-  bovevents:
-
-+ whole day events import/export seems to work nice
-+ continue on erroneous overwrites (errors only visible in errorlog)
-+ no error messages for forbidden overwrite events of non owned events
-+ errormessages (when activated) only shows problematic events
-++ basic (multiple) VALARM import and export, working! (without action select)
-+ allow for vevents with either DTEND or DURATION
-+ provided (horde iCalendar) patch to prevent segfaults on some bad iCal inputs
-
-1) EgwIcal package general (V0.9)
-----------------
-
-
-[ ]1.2 TODO get it nicely into egw cvs
-[+/-]1.7 TODO/WISH allow for import of ATTENDEEs not in Egw yet (create new addressbook entry?)
-         [+](ad hoc solution: add CN and mailto to description)
-[+/-]1.4 TODO check and generate source code documentation(phpdoc or doxygen)
-
-[ ]1.5 TODO test these routines in use for syncml import/export
-
-1.6 TODO/WISH: nothing done yet for vfreebusy, vjournal, notes, components 
-    only vevents and vtodos are supported.
-
-
-3) egwical.egwical (V0.9.01)
---------------------
-
-[+?]3.1 TODO check/implement correct timezone handling (seems ok) 
-[ ]3.9 TEST with syncml 		
-[ ]3.2 TODO/WISH handle location (GEO), link(?), url, delegation, ()CONTACT?
-[ ]3.3 TODO write more documentation (continue mainpage)
-[ ]3.4 TODO add accumulting buffer system to egwical
-
-4) egwical.bocalupdate_vevents (V0.9.02)
-------------------------------
-
-[+?]4.1 TODO check/implement correct timezone handling (related 3.1)
-[ ]4.2a TODO/WISH handle URL, GEO etc. fields. (needs extra fields handling in bocalupdate!)
-[ ]4.2b FAIL rrule-COUNT import not implemented in egw?
-[+]4.2 BUG!!!!  rrule-BYDAY import goes wrong. FIXED (v0.7.76)
-[+]4.3 TODO check and improve ORGANIZER,ATTENDEE<-> participants mapping
-	     ATTENDEE s import and export (appears to work oke >= V0.7.71) 
-[+]4.4 TODO handle import and export of ALARMS
-[ ]4.7 TODO (ad hoc) add non egw known attendees to description on import
-[ ]4.8 TODO move compatibility code to compat classes.
-
-
-4.9 see 3.2
-
-
-5) egwical.boinfolog_vtodos (V0.9.02)
-----------------------------
-5.1 TODO check/implement correct timezone handling  (related 3.1)
-5.3 FAIL import/export more than 1 category per task (egw doesnot allow)
-5.3 TODO check and improve ORGANIZER,ATTENDEE<-> info_responsible etc mapping
-5.4 TODO handle import and export of ALARMS
-5.5 TODO rewrite code into supportedFields structure (for next version)
-[ ]5.9 TODO rewrite into skeleton structure analog to egwical.bocalupdate_vevents
-
-
-
-10.) in  used routines from others:
-
-10.1 BUG Horde_iCalendar(1.2rc6): EXDATE bug
-  -[+]fixed by patch 'exdate ....'
-10.2 BUG  Horde_iCalendar(1.2rc6): standard.php Warning
-  -[+]fixed by patch '...??..'
-
-10.3 BUG infolog(1.2rc6): datetime is 1 hour wrong for untimed due field in tasks
-
-11) detected errors/flaws in other programs
-
-11.1 Korganizer 3.5: recurrence endondate display is interpreted different from egw and
-mozilla (and probable also rfc 2445) 
-
-
-
-*/ \ No newline at end of file diff --git a/egwical/doc/EgwIcal-Timezone-handling.txt b/egwical/doc/EgwIcal-Timezone-handling.txt deleted file mode 100644 index d75b3c49b3..0000000000 --- a/egwical/doc/EgwIcal-Timezone-handling.txt +++ /dev/null @@ -1,183 +0,0 @@ -/*! \page pageegwicaltzh EgwIcal Timezone Handling - -Short description on the timezone handling in EgwIcal - -@date 20060216 -@version 0.9.04 -@author JVL -@note Document needs further editing and annotations!! - - - -In EgwIcal the timed data of Egroupware like events and tasks get -converted to elements for an iCalendar. In the Egroupware server we -are dealing with socalled "egw server" time, in applications that a -user of egw runs, the user can set a "locale" and thereby defining a -"local timezone". And finally when events and task get exported to an -iCalendar that is sent to a client or uploaded from it, they may also -have their timesettings defined according to some "timezone". - -How is this all handled in EgwIcal? - - - -@section secegwicalgentzh EgwIcal general Timezone handling - -In general EgwIcal distinguishes between two different types of time -descriptions for an event. An event can either be a: - - - -For handling DT events it can be profitable to use a globally unified standard -for describing the timingly definitions, like e.g. UTC. -And indeed: - - - -For handling D events you can best give a (timezone/geo) relative -description together with the GEO location. Then someone who wants to -visit such a event can checkout the location and see what timezone -they have there at moment of the event, calculate it back to UTC and -then she knows when to get there. -Egw and EgwIcal are no very sophisticated in handling these: - -- RE.1 (I am not 100% sure:) Egw tries to handle DATE events relative to - the UserInterface its timezone: the so called USER-TIME. - -and for EgwIcal: - -- REI.1 EgwIcal tries to handle DATE events relative to - the UserInterface timezone (USER-TIME) of the IcalSrv proces of the - logged in user. - -and keep in mind: - -- REI.2 EgwIcal currently will only import or export DATE events - for whole day events or for occurence settings of recurrent - events - -In the following sections I will explain a bit more what this means -for import of Ical data from clients and export to it. - - - - -@section secegwicalimptzh EgwIcal Timezone handling for icaldata import from a client. - -..... - -@subsection subsecvtmz VTIMEZONE import handling. - - No VTIMEZONE ical elements are parsed or used - This has a consequence: - - - no basic import timezone setting via this supported - - - no imported timezones with identifiers that can be referenced by - VEVENTS or VTODOS are supported - -@subsection subsecdtimp DATETIME formatted data import handling. - - DATETIME formatted data is inspected for its timezone. If the - timezone is UTC ('Z') then it is converted (i.e. copied) to UTC - before processing further. If the timezone is not given of is - referenced with a key to a VTIMEZONE component in the Icalendar - file, this timezone info is discarded and a "timezoned" DATETIME - is just interpreted as relative to the USERTIME of the IcalSrv - process: any timezone info is neglected. - - Clearly this can be wrong in many occasions: thus this should be - considered a BUG! - -@subsection subsecdimp DATE formatted data import handling. - - DATE formatted data is ust interpreted as relative to the USERTIME of the IcalSrv - process: any timezone info is neglected. - - -@subsection subsecrruleimp Importing Recurrence Elements - - - - Doing recurrence calculation in UTC is notably not a good thing to - do because DayTime Savings changes during the runtime of the - recurring event can cause troubles. The best thing to do is to - calculate the recurrence in the geo/time-zone of the definer. See - @url ... of the CAlConnect Group for this. But as clients often dont - export their timezones, (and we dont do any processing of it anyhow) - we do the following: - -
    -
  1. first the START and (if there UNTIL) dates are converted to UTC - dates. Then these are converted to the IcalSrv locale setting. - -
  2. Then the recurrence rules are evaluated, and converted to the info - Egw needs. Note: Egw cannot use the COUNT field, so EgwIcal has to - rewrite it to a UNTIL value. -
- - - -@section secegwicalexptzh EgwIcal Timezone handling for icaldata export to a client. - -On export .... - -- all Egw DATETIME info is exported in UTC time. - -- all Egw DATE info is exported without any timezone info, - and thus should best be considered in the USERTIME of the IcalSrv - process. - - -@subsection subsecrruleexp Exporting Recurrence Elements - -On export Recurrence Rules are interpreted as follows: - - - - - -Hope this explains things a bit. - -Jan - -*/ \ No newline at end of file diff --git a/egwical/doc/EgwIcal-intro.txt b/egwical/doc/EgwIcal-intro.txt deleted file mode 100644 index 1f01966be9..0000000000 --- a/egwical/doc/EgwIcal-intro.txt +++ /dev/null @@ -1,182 +0,0 @@ -/*! @mainpage EgwIcal package for Egroupware - -@NOTE this text is not up to date -@version 0.9.01 -@author jvl - -The EgwIcal package provides routines for converting between iCalendar RFC -2445 conformant dataelements (like VEVENTS, VTODOS, VALARMS etc.) and -corresponding Egroupware database entries (like calendar events, -infolog tasks etc.). EgwIcal also provides routines to do import to -and export from the Egroupware system for these iCalendar elements, -thereby doing the conversion on the fly. Finally, EgwIcal provides a -buffering feature where mixed sets of these elements can be accumulated and -then be imported or exported all at once. - -@section secinstall SHORT experimental Install How-To: - - - Install the EgwIcal package by either checking out the egwical module from cvs - or untarring some tarball egwical-vx.y.x.tgz of the package (when available). - - -@section secusage Usage of the EgwIcal system - -The EgwIcal system allows four(or 3?) characteristic ways to help you -with manipulating iCalendar data in a Egw context:..... - -@section secusageiecnv Converting iCalendar elements to Egw elements - -To convert a iCalendar datacomponent e.g. of type "VEVENT" into a -corresponding egw component of e.g. "calendar event" you can use the -following in your code: - - ...... - - -@section secusageeicnv Converting Egw elements to iCalendar elements. - -To convert a egw component of e.g. "calendar event" into a -corresponding iCalendar datacomponent of e.g. type "VEVENT"you can use the -following in your code: - - ...... - - -@section secusageimport Importing iCalendar elements into Egw -applications storage. - -To import a iCalendar datacomponentof e.g. type "VEVENT" as a -corresponding egw component of type "calendar event" into your -calendar application (its database) you can use the -following in your code: - - ...... - - -@section secusageexport Exporting Egw application elements as iCalendar elements. - -To export as a egw datacomponent of e.g. type "calendar event" from your -calendar application (its database) as a iCalendar datacomponentof e.g. type "VEVENT", -you can use the following in your code: - - ...... - - -@section secusageexpimpbuffer Exporting (mixed) collections of iCalendar elements. - -Beside directly exporting single elements or sets of -iCalendar data generated generated from a egw application like -calendar, you can also use the "buffer" system of EgwIcal, to collect -various sets of such dataelements, even from different Egw -applications (like calendar and infolog), and then later on export -these all together as one big iCalendar. - -To do collection followed by export you can use code like below: - ...... - - - -@section secimplementation How is EgwIcal implemented? - -To use or extend EgwIcal best, as a developer, it may be good know a bit about -its implementation. -EgwIcal is built according to the Workers Union Representatives -Hierarchy (WURH) pattern. Quite a mouthfull, and probably there are -somewhere better names for this pattern (see ...). In @ref pageegwicalwurh -you can read more on this. - -Basically it means that there is one super "worker" class -@ref egwical that uses for specific work like e.g. conversion -between VEVENTS and calendar tasks, a specific subclass -like e.g. bocalupdate_vevents that is connected to the -egwical object via a socalled "representative" member in the registry $reg_rscs -(attribute). - -Currently EgwIcal has the following subclasses: - -- bocalupdate_vevents for manipulating calendar events as - VEVENTS. - -- boinfolog_vtodos for manipulating infolog tasks as VTODOS. - -Future subclasses might be: - -- ???_vjournals for manipulating .... as VJOURNALS. - -- bocal_vfreebusies for manipulating calendar data as - VFREEBUSY elements. - -- addressbook_vcard for manipulating addressdata data as - VCARDS. - -- ???_vnotes for manipulating .... as VNOTES. - - - -@section secdeletioncheatcode Cheatcode for Deleting Calendars, events or todos - -Egwical provides a special "hackish" feature specifically meant for -use with the crippeld ical-over-http (aka webcal) implementation that -the package IcalSrv provides for Egroupware. - -This IcalSrv implementation does not support deletion of egw calendar -or infolog elements directly (see the IcalSrv package). As a help out -the "cheat" is implemented that: you can though delete an item by -simply giving the VEVENT or VTODO 'SUMMARY' field a value -X-DELETE or _DELETED_ and then importing it -through the Egwical system. I you do so the the corresponding egw -element from calendar or infolog will get deleted. - - -@section secpatches Patches - - -You may need to apply some of the patches found in PATCHES. -Currently there a two patches to fix a warning and a bug (related to EXDATE -fields handling) in the horde routines. -Note these may be already committed in the CVS version. - - - -@section sectest Extra testing - -The package contains two "compatibility" classes (@ref boical and @ref vcalinfolog) with -which you can replace -the current infolog/inc/class.vcalinfolog.inc.php resp. -calendar/inc/class.boical.inc.php -files. - -When done so, the egw syncml, ical import/export and infolog import/export in egw -will all use the new code. This way you can discover more bugs and failure in the code :) - -But, when not renamed, these programs will use the old code (and thus are not broken or -improved by the new one..) - - -@section secdocumentation Documentation - -There is (somewhere) complete doxygen generated documentation for the -EgwIcal package. Otherwise you can generate it yourself by using -doxygen on the source filetree. Maybe phpdocumenter can give you also -something usefull. And maybe you are just reading it now this very moment... - - - -@section secbugandtodo BuG and todo list - - @todo notably timezone handling in EgwIcal should be documented and -possible features to allow import/export and use of iCalendar -VTIMEZONE components should be provided. - - -see BUGs and TODOs in @ref pageegwicalbugandtodo - - - - -have fun - - -JVL - - -*/ \ No newline at end of file diff --git a/egwical/doc/egwical-doxydoc.tgz b/egwical/doc/egwical-doxydoc.tgz deleted file mode 100644 index a3c46fdf8b3335e83afbc99a4a1a1eb77a912512..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53757 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 diff --git a/egwical/doc/egwical-wurh-pattern.txt b/egwical/doc/egwical-wurh-pattern.txt deleted file mode 100644 index b33329a01c..0000000000 --- a/egwical/doc/egwical-wurh-pattern.txt +++ /dev/null @@ -1,205 +0,0 @@ -/*! -\page pageegwicalwurh EgwIcal Workers Union Representatives Hierarchy Pattern -implementation. - -@author JVL -@date 20060214 -@version v0.9.02 - -The current Egw Ical package provides routines for exporting iCalendar -data like vevents, vtodos, vcards etc. from the various corresponding -Egroupware packages (the "backends"). And vice versa importing these to the datastores -of these packages. On the other hand there can also be a variety of -interface components that transport e.g these exported vevents to -further applications (the "clients"). Think e.g on the "icalsrv" -service to transport whole sets of vevents in one go over http to a -client, or the "syncml" service that transports them to syncml -speaking device, or simple routine that let you save or upload a -calendar file from within the webgui. - -An iCalendar data object can hold a set of these various kinds of -data-elements as parts of itself. And these data-elements itself can -also be composed of sub-elements (like e.g. VALARM and VTIMEZONE -components). - - -So in this situation we have Wholes (like an iCalendar object) with -Parts (like groups of Vevents and groups of Vtodos) on the one hand. -And on the other hand we have dedicated subappliations that may handle -these groups of Part data. To implement this we can make use of a -smart combination of two implementation primitives "compounds" and -"classes and subclasses". - -In the WURH pattern the combination of these two is very rather -outspoken and quite entangled and even a bit tricky: each specific -type of Part is "represented" by specific subclass of the class of -Whole compound. The data in the parts can now be manipulated in two -ways: - -1) all together as whole, via methods of the compound, or -2) all in the group of a part, by the methods of the (associated) -subclass its representative. - -@note I forgot the official (GOF) name of the pattern (if there is -any..) - - - -\section secwurhexample Example WURH pattern implementation: Meal preparation - -As simple example should explain this: Meal preparation URH pattern -implementation - -think of the preparation of meal, that consist of soup and meat. -To "cook" the meal, the soup has to be boiled and the meat has to be -baked. - -in OO notation: <>- or . . means member of, -<- means subclass of - -Classes: -
-MealCooker <- SoupCooker;     MealCooker <- MeatCooker;
-MealCooker <>- SoupCooker;    MealCooker <>- MeatCooker;
-
-Methods: -
-MealCooker->cook();  SoupCooker->boil();   MeatCooker->bake();
-
-and possibly the cook() method may even, for easy of use in the Soup and -Meat classes by implemented (overridden) by the boil() resp. bake() method. - -Now the code to prepare to have $mymealck prepare a meail with $mysoupck and $mymeatck (without -subclass overrriding): -
-$mymealck = New MealCooker;
-$mymeatck = New MeatCooker;
-$mysoupck = New SoupCooker;
-
-$meat_in_mealck =  $mymealck->addMeatHandler($mymeatck);  
-$soup_in mealck =  $mymealck->addSoupHandler($mysoupck);
-
-// these are in class MealCooker implemented as
-// $this.m = meatcooker_config($mymeatck); resp. $this.s = soupcooker_config($mysoupck); 
-
- -Now cooking the meal is done by, cooking the soup in its appropiate -way (namely "boiling()" )via its union representative ($soup_in_mealck). Note that it is -called "union" representative because multiple SoupCookers parts may have been -added already to use. And also cooking the meat in its appropiate way -("baking()") likewise. Thus -
-$mymealck->cook([$apiec_of_meat,$avolum_of_soup]) can be done by calling:
-
-		$meat_in_mealck->bake($apiece_of_meat);
-		$soup_in_mealck->boil($avolume_of_soup);
-
-and then you can serve boths parts in one go with: -
-$mymealck->serve();
-
- -If we implemented the specific ways of cooking as overriding subclass -methods for Meal->cook() then in simple cases we dont even need to use -the representatives to call the preparation as we implement to main -cook() method to call all itself on all its parts: -
-$mymealck = New MealCooker;
-$mymeatck = New MeatCooker;
-$mysoupck = New SoupCooker;
-
-		  $mymealck->addMeatHandler($mymeatck);  
-		  $mymealck->addSoupHandler($mysoupck);
-
- -And $mymealck->cook([$apiec_of_meat,$avolum_of_soup]) can then done by -just calling: - -
-  $meat_in_mealck->bake($apiece_of_meat);
-  $soup_in_mealck->boil($avolume_of_soup);
-
-  // which effects in $mymealck.m->cook($apiece_o_fmeat);
-  //           and    $mymealck.s ->cook($avolume_of_soup);
-  // that effects again in $mymealck.m->bake($apiece_o_fmeat);
-  //            and    $mymealck.s->boil($avolume_of_soup);
-
-
- -So this allows for using of manipulation methods of the whole (in case -of simple generic actions reimplemented in the subclasses of the -parts) together with using more specific methods for specific -parts. (e.g. $soup_in_mealck->set_boil_time(10) etc.) - - - -\section securhinei WURH pattern usage in Egwical. - -The Egwical class manages an complete iCalendar component with -VEVENTS, VTODOS etc. as parts. The handling of these part(unions)s is -done by specific subclasses of Egwical, like e.g. infolog_bovtodo and -calendar_bovevents. -The system can be used in two different modes of operation: - -- 1) for conversion of egw tasks, events, etc. to their iCal - counterparts. (the cnv_ methods). - -- 2) for filling the (internal) egwical object with al lot of vtodos and - vevents, either coming from import or from conversion of egw - counterparts. When the compound is filled it can (as a whole) be - exported or imported. - - -add 1) So e.g. exporting a group of events refered to by $event_ids as -VEVENTS goes as follows: -
-$cal = New Bocal;                       // build calendar
-$event_ids = $cal->search("filt_def');  // get group of egw event ids to export.
-
-$ei = New Egwical;		                // build Compound iCalendar processor
-$bve = $ei->addRsc($cal);               // add calendar part and get representive
-$vcalstr = $bve->cnv2VEVENTS($event_ids); // export some events
-
-
- -if we also want to export some VTODOs this goes as follows: - -
-$binf = New Infolog                     // build infolog app. object
-$task_ids = $binf->search("filt_def');  // get group of egw task ids to export.
-
-$bvt = $ei->addRsc($binf);              // add infolog part and get representive
-$vcalstr = $bvt->cnv2VTODOS($task_ids);
-
-
-add 2) the same egw elements from 1) are now first collected in -egwical and then as a whole exported. - -
-$cal = New Bocal;                       // build calendar
-$event_ids = $cal->search("filt_def');  // get group of egw event ids to export.
-
-$ei = New Egwical;		                // build Compound iCalendar processor
-$bve = $ei->addRsc($cal);               // add calendar sys part and get representive
-	 $bve->clear();                     // empty the list of Vevents in bve
-	 $bve->addEventsOntoVEVENTS($event_ids); // add the converted events to vevents in $ei
-
-
-if we also want to add some VTODOs this goes as follows: -
-$binf = New Infolog                     // build infolog app. object
-$task_ids = $binf->search("filt_def');  // get group of egw task ids to export.
-
- $bvt = $ei->addRsc($binf);             // add infolog sys part and get representive
-	  $bvt->clear();                    // empty the list of Vtodos  in bvt
-	  $bvt>addTasksOntoVTODOS($task_ids); // add the converted tasks to vtodos in $ei
-
-
-and finally export the whole iCalendar -
-$ei->export()
-
- - ---------- - -*/ \ No newline at end of file diff --git a/egwical/inc/class.bocalupdate_vevents.inc.php b/egwical/inc/class.bocalupdate_vevents.inc.php deleted file mode 100644 index f263e65ee5..0000000000 --- a/egwical/inc/class.bocalupdate_vevents.inc.php +++ /dev/null @@ -1,1075 +0,0 @@ - (This version. new api rewrite, - * refactoring, and extension). - * @author Lars Kneschke (parts from boical that are reused here) - * @author Ralf Becker (parts from boical that are - * reused here) - * @version 0.9.03 (First WURH version, most stuff used from old bovevents class) - * @since 0.9.03 changed mke_RECUR2rar() api - * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License - */ - class bocalupdate_vevents extends egwical - { - - /** - * @private - * @var boolean - * Switch to print extra debugging about imported and exported events to the httpd errorlog - * stream. - */ - var $evdebug = true; - - /** - * @private - * @var object - * The egw bocal calendar that will be used to transport events from and to - * This is set by setRsc() - */ - var $mycal = null; - - /** - * Describe the provided work capabilities of the class. - * @return string The description as entries for the @ref $reg_workers registry - * table. - */ - function provides_work() - { - return - array('bocalupdate' => array('workerclass' => 'bocalupdate_vevents', - 'workerobj' => null, - 'icalsup' => array('VEVENT')), - // next one is a subclass of bocalupdate, we can work for that too - 'boical' => array('workerclass' => 'bocalupdate_vevents', - 'workerobj' => null, - 'icalsup' => array('VEVENT')), - ); - } - - /** - * Our Constructor, sets the basic class members @ref $ei , @ref supportedFields - * @ref $ical2egwFields and @ref $iprovide_work - */ - function bocalupdate_vevents($prodid='all') - { - // call our abstract superclass constructor - egwical::egwical(); - //@todo rewrite supportedFields setting to distribute it over the egwical - // baseclass and the subclasses cleverly - $this->_set_ical2egwFields(); // add VEVENT and event pairs only - $this->setSupportedFields($prodid); - - return true; - } - - /** - * Set the egw resource that this worker will handle. - * This worker is only capable of handling bocalupdate calendar objects, so it should - * be of that class. This method is mostly called indirectly from a egwical compound - * addRsc() call. But you can call it also directly (if you know what your doing ..) - * @return boolean false on error, true else - */ - function setRsc($egw_rsc) - { - if(!is_a($egw_rsc,'bocalupdate')) - return false; - $this->mycal = $egw_rsc; - return true; - } - - // -------- below only conversion and import/export stuff ----- - - /** - * @private - * @var array $ical2egwFields - * An array containing roughly the mapping from iCalendar - * to egw fields. Set by constructor. - */ - var $ical2egwFields; - - /** - * @private - * @var array $supportedFields - * An array with the current supported fields of the - * importing/exporting device. - * To detect if a certain ical property (eg ORGANIZER) is supported in the current - * data import/export do a isset($this->supportedFields['ORGANIZER']). - * To detect if a certain egw field (eg status) is supported in the current - * data import/export do a - * in_array(array_flatten(array_values($this->supportedFields)),'status') - * or something like that (not tested, implemented, or needed yet..) Maybe should - * implement a method for this.. - * @note This table should probably better be in class @ref egwical - */ - var $supportedFields; - - - - - /** - * @var boolean - * Switch that determines if uid matching is tried. - * - * For a more on uidmatching @see \secimpumatch - * - * If $uid_matching is true then: - * - on import of a vevent the update routines will first try to - * find an existing egw event with the same uid value as present - * in the UID field of the newly to be imported vevent. If this - * succeeds this egw event will get updated with the info from - * the vevent. If this fails a new event will be generated and - * the uid taken from the vevent will be stored in its uid - * field. - * - * if $uid_matching is false then: - * - On import the VEVENT UID field will be checked, if it - * appears to be a previously exported uid value then the - * encoded egw id of the old egw event is retrieved and used for - * update. If it doesnot have a uid value with a valid egw id - * encoding, then the its is handled as being a new VEVENT to be - * imported, and a new egw id will be generated. The old vevent - * uid will though be saved for possible later use, (just as - * with uid_matching on). - */ - var $uid_matching = false; - - /** - * @var boolean - * Switch that determines if events not anymore in egw are allowed to be reimported - * - * Default this is on - */ - var $reimport_missing_events = true; - - - - /** - * Export Egw events and add them to a Horde_iCalendar. - * - * The eGW events in $events are exported to iCalendar VEVENTS and then these are added to - * the Horde_iCalendar object &$hIcal. - * Note that only supported Fields are exported as VEVENTS to the iCalendar. - * - * @section secexpeuid Egw uid export switch - * If $euid_export is set, then for each exported event, the current value of the event uid - * as stored in Egw, will be used to produce a value for the vevent its UID field. When off - * a new UID value will generated with the egw event id encoded. - * - * @param Horde_iCalendar &$hIcal - * object to wich the produced VEvents are added. - * @param array $events the array with eGW events (or event id's) that will be exported - * @param boolean $euid_export switch to enable export of the egw uid fields, when off - * default) the vevents uid fields get a value generated with the egw id encoded. - * @return boolean|int $ok/$vcnt on error: false / on success: nof vevents exported - * @ref $supportedFields determines which fields of VEVENT will be exported - */ - function exportEventsOntoIcal(&$hIcal, $events, $euid_export=false, - $reimport_missing_events=false) - { - $vexpcnt =0; // number of vevents exported - - $veExportFields =& $this->supportedFields; - - if (!is_array($events)) $events = array($events); - - foreach($events as $event) { - // event was passed as an event id - if (!is_array($event)){ - $eid = $event; - if( !$event = $this->mycal->read($eid,null,false,'server')){ - // server = timestamp in server-time(!) - return false; // no permission to read $cal_id - } - // event was passed as an array of fields - } else { - $eid = $event['id']; - // now read it again to get all fields (including our alarms) - $event = $this->mycal->read($eid); - } - - // error_log('>>>>>>>>>>>' .'event to export=' . print_r($event,true)); - - // now create a UID value - if ($euid_export) { - // put egw uid into VEVENT, to allow client to sync with his uids - $eventGUID = $event['uid']; - } else { - $eventGUID = $this->mki_v_guid($eid,'calendar'); - } - - $vevent = Horde_iCalendar::newComponent('VEVENT',$hIcal); - $parameters = $attributes = array(); - // to important to let supportedFields decide on this - $attributes['UID'] = $eventGUID; - - foreach($veExportFields as $veFieldName) { - - switch($veFieldName) { - case 'UID': - // already set - break; - - case 'ATTENDEE': - foreach((array)$event['participants'] as $pid => $partstat) { - if (!is_numeric($pid)) continue; - - list($propval,$propparams) = - $this->mki_vp_4ATTENDEE($pid,$partstat,$event['owner']); - // NOTE: we need to add it already: multiple ATTENDEE fields may be occur - $this->addAttributeOntoVevent($vevent,'ATTENDEE',$propval,$propparams); - } - break; - - case 'CLASS': - $attributes['CLASS'] = $event['public'] ? 'PUBLIC' : 'PRIVATE'; - break; - - // according to rfc, ORGANIZER not used for events in the own calendar - case 'ORGANIZER': - if (!isset($event['participants'][$event['owner']]) - || count($event['participants']) > 1) { - $attributes['ORGANIZER'] = $this->mki_v_CAL_ADDRESS($event['owner']); - $parameters['ORGANIZER'] = $this->mki_p_CN($event['owner']); - } - break; - - // Note; wholeday detection may change the DTEND value later! - case 'DTEND': - // if(date('H:i:s',$event['end']) == '23:59:59') - // $event['end']++; - $attributes[$veFieldName] = $event['end']; - break; - - case 'RRULE': - if ($event['recur_type'] == MCAL_RECUR_NONE) - break; // no recuring event - $attributes['RRULE'] = $this->mki_v_RECUR($event['recur_type'], - $event['recur_data'], - $event['recur_interval'], - $event['start'], - $event['recur_enddate']); - break; - - case 'EXDATE': - if ($event['recur_exception']) { - list( $attributes['EXDATE'], $parameters['EXDATE'])= - $this->mki_vp_4EXDATE($event['recur_exception'],false); - } - break; - - case 'PRIORITY': - if (is_numeric($eprio = $event['priority']) && ($eprio >0) ) - $attributes['PRIORITY'] = $this->mki_v_prio($eprio); - break; - - case 'TRANSP': - $attributes['TRANSP'] = $event['non_blocking'] ? 'TRANSPARENT' : 'OPAQUE'; - break; - - case 'CATEGORIES': - if ($catids = $event['category']){ - $catnamescstr = $this->cats_ids2idnamescstr(explode(',',$catids)); - $attributes['CATEGORIES'] = $catnamescstr; - } - break; - - // @todo find out about AALARM, DALARM, Is this in the RFC !? - case 'AALARM': - foreach($event['alarm'] as $alarmID => $alarmData) { - $attributes['AALARM'] = $hIcal->_exportDateTime($alarmData['time']); - // lets take only the first alarm - break; - } - break; - - case 'DALARM': - foreach($event['alarm'] as $alarmID => $alarmData) { - $attributes['DALARM'] = $hIcal->_exportDateTime($alarmData['time']); - // lets take only the first alarm - break; - } - break; - - case 'VALARM': - foreach($event['alarm'] as $alarmID => $alarmData) { - $this->mki_c_VALARM($alarmData, $vevent, - $event['start'], $veExportFields); - } - break; - - case 'STATUS': // note: custom field in event - if (! $evstat = strtoupper($event['status'])) - $evstat = 'CONFIRMED'; //default.. - $attributes['STATUS'] = $evstat; - break; - - default: - // only use default for level1 VEVENT fields - if(strpos($veFieldName, '/') !== false) - break; - // use first related field only for the simple conversion - $efield = $this->ical2egwFields[$veFieldName][0]; - if ($event[$efield]) { // dont write empty fields - $attributes[$veFieldName] = $event[$efield]; - } - break; - } - - } //end foreach - - // wholeday detector (DTEND =23:59:59 && DTSTART = 00:00) - // if detected the times will be exported in VALUE=DATE format - if(((date('H:i:s',$event['end']) == '23:59:59') || - (date('H:i:s',$event['end']) == '00:00:00')) - && (date('H:i',$event['start'] == '00:00'))){ - $attributes['DTSTART'] = - $this->hi->_parseDate(date('Ymd',$event['start'])); - $attributes['DTEND'] = - $this->hi->_parseDate(date('Ymd',$event['end']+1)); - $parameters['DTEND']['VALUE'] = 'DATE'; - $parameters['DTSTART']['VALUE'] = 'DATE'; - // error_log('WHOLE DAY DETECTED'); - } - - // handle created and modified field setting - $created = $this->get_TSdbAdd($event['id'],'calendar'); - if (!$created && !$modified) - $created = $event['modified']; - if ($created) - $attributes['CREATED'] = $created; - if (!$modified) - $modified = $event['modified']; - if ($modified) - $attributes['LAST-MODIFIED'] = $modified; - - // add all collected attributes (not yet added) to the vevent - foreach($attributes as $aname => $avalue) { - $this->addAttributeOntoVevent($vevent, - $aname, - $avalue, - $parameters[$aname]); - } - $hIcal->addComponent($vevent); - $vexpcnt += 1; - } - - return $vexpcnt; //return nof vevents exported - } - - - - - /** - * Import all VEVENTS from a Horde_iCalendar into Egw - * - * The ical VEVENTS components that are contained in de $hIcal Horde_iCalendar - * are converted to eGW events and imported into the eGW calendar. - * Depending on the value of $importMode, the conversion will generate either eGW - * events with completely new id s (DUPLICATE mode) or generate ids created after - * the VEVENT;UID field so that VEVENTS that refer to already existing eGW events - * will be used to update these (OVERWRITE mode). - * - * @section secimpumatch Uidmatching - * When $uid_matching is not set, the default situation, the uid field of each vevent - * to be imported will examined to check if it has a valid egw id encoded. If so the import - * will try to update the egw event indicated by this id with the contents of the vevent. - * When this doesnot succeed an appropiate error or skip (if you had not enough write rights) - * will be the result. If there can be no valid egw id be decoded, the vevent will be considered - * as a new one and an hence a new egw id will automatically be produced. - * - * When $uid_matching is enabled, the value of the uid field of the vevent will matched against - * all the uid fields of existing egw events. If a matching egw event with id is found, - * the import - * routine will try to update this event. If no success an appropiate error will be generated. - * If no match is found, the import proceeds, just as without uidmatching, by generating a - * new egw event with a new id. The events uid field will be filled with the vevents uid, - * for possible later re-use. - * - * @note Mostly it is best to disable uidmatching. It prevents that multiple duplicates - * of a event will be created in Egw, that may not be accessible anymore via the Ical-Service - * interface. Only use it when you really need to reimport an already once imported calendar - * because you accidentally deleted parts of it in Egw. Better still would be copy these lost - * events into a downloaded version of your original calendar and then update this one without - * the uid_matching enabled. (It has namely no effect for new events and the old (i.e. - * already downloaded to the client) events will be recognized without uidmatching. - * - * @param Horde_iCalendar &$hIcal object with ical VEVENT objects - * @param string $importMode toggle for duplicate (ICAL_IMODE_DUPLICATE) - * or overwrite (ICAL_IMODE_OVERWRITE) import mode - * @param int $cal_id strange parameter, at least for -1 create new events - * and if 0 then always add user to participants - * JVL: THIS NEEDS TO BE CLARIFIED! - * @param boolean $reimport_missing_events enable the import of previously exported events - * that are now gone in egw (probably deleted by someone else) Default false. - * @return boolean| int $false|$evcnt on error: false | on success: nof imported elms - * @ref $supportedFields determins the VEVENTS that will be used for import - */ - function importVEventsFromIcal(&$hIcal, $importMode='OVERWRITE', $cal_id=0, - $reimport_missing_events=false) - { - $overwritemode = stristr($importMode,'overwrite') ? true : false; - $evokcnt = 0; // nof events imported ok - $everrcnt = 0; // nof events imported erroneous - $evskipcnt = 0; // nof events imported skipped (user !== owner) - $evdelcnt = 0; // nof events deleted ok - $evmisskipcnt =0; // nof missing event updates skipped - - $veImportFields =& $this->supportedFields; - -// error_log('veImportFields::'. print_r($veImportFields,true)); - - $eidOk = false; // returning false, if file contains no components - $user_id = $GLOBALS['egw_info']['user']['account_id']; - - foreach($hIcal->getComponents() as $vevent) { - // HANDLE ONLY VEVENTS HERE - if(!is_a($vevent, 'Horde_iCalendar_vevent')) - continue; - -// $event = array('participants' => array()); - $event = array('title' => 'Untitled'); - $alarms = array(); - unset($owner_id); - $evduration = false; - $nonegw_participants = array(); - - // handle UID field always first according to uid_matching algorithm - $cur_eid = false; // current egw event id - $cur_owner_id = false; // current egw event owner id - $cur_event = false; // and the whole array of possibly correspond egw event - // import action description (just for fun and debug) : - // NEW|NEW-NONUID|NEW-FOR-MISSING - // DEL-MISSING|DEL-READ|DEL-READ-UID| - // UPD-MISSING|UPD-READ|UPD-READ-UID - $imp_action = 'NEW-NONUID'; - - if($uidval = $vevent->getAttribute('UID')){ - // ad hoc hack: egw hates slashes in a uid so we replace these anyhow with - - $vuid = strtr($uidval,'/','-'); - $event['uid'] = $vuid; - - if(!$this->uid_matching){ - - // UID_MATCHING DISABLED, try to decode cur_eid from uid - if ($cur_eid = $this->mke_guid2id($vuid,'calendar')){ - // yes a request to import a previously exported event! - if ($cur_event = $this->mycal->read($cur_eid)){ - // oke we can read the old event - $cur_owner_id = $cur_event['owner']; - $imp_action = 'UPD-READ'; - $event['id'] = $cur_eid; - } elseif($reimport_missing_events){ - // else: a pity couldnot read the corresponding cur_event, - // maybe it was deleted in egw already.. - $imp_action = 'UPD-MISSING'; - unset($event['id']); // import as a new one - } else{ - // go on with next vevent - $evmisskipcnt += 1; - continue; - } - }else{ - // no decodable egw id there, so per definition no corresponding egw event - // so will just import the vevent as a new event - $imp_action = 'NEW'; - } - - //UID_MATCHING ENABLED - } elseif($overwritemode && $cal_id <= 0 && !empty($vuid)){ - // go do uidmatching, search for a egw event with the vuid as uid field - if ($cur_event = $this->mycal->read($vuid)) { - $cur_eid = $uidmatch_event['id']; - $cur_owner_id = $uidmatch_event['owner']; - $imp_action = 'UPD-READ-UID'; - $event['id'] = $cur_eid; - }else{ - // uidmatch failed, insert as new - $imp_action = 'NEW'; - } - } - - } - - // lets see what other supported veImportFields we can get from the vevent - foreach($vevent->_attributes as $attr) { - $attrval = $GLOBALS['egw']->translation->convert($attr['value'],'UTF-8'); - - - // SKIP UNSUPPORTED VEVENT FIELDS - if(!in_array($attr['name'],$veImportFields)) - continue; - -// error_log('cnv field:' . $attr['name'] . ' val:' . $attrval); - - switch($attr['name']) { - // oke again these strange ALARM properties... - case 'AALARM': - case 'DALARM': - if (preg_match('/.*Z$/',$attrval,$matches)) { - $alarmTime = $hIcal->_parseDateTime($attrval); - $alarms[$alarmTime] = array('time' => $alarmTime); - } - break; - - case 'CLASS': - $event['public'] = (int)(strtolower($attrval) == 'public'); - break; - - case 'DESCRIPTION': - $event['description'] = $attrval; - break; - - case 'DTEND': - // will be reviewed after all fields are collected - $event['end'] = $attrval; - break; - - // note: DURATION and DTEND are mutually exclusive - case 'DURATION': - // duration after eventstart in secs - $evduration = $attrval; - break; - - case 'DTSTART': - // will be reviewed after all fields are collected - $event['start'] = $attrval; - break; - - case 'LOCATION': - $event['location'] = $attrval; - break; - - case 'RRULE': - // we may need to find a startdate first so delegate to later - // by putting it in event['RECUR'] - $event['RECUR'] = $attrval; - break; - case 'EXDATE': - if (($exdays = $this->mke_EXDATEpv2udays($attr['params'], $attrval)) - !== false ){ - foreach ($exdays as $day){ - $event['recur_exception'][] = $day; - } - } - break; - - case 'SUMMARY': - $event['title'] = $attrval; - break; - - case 'TRANSP': - $event['non_blocking'] = $attrval == 'TRANSPARENT'; - break; - // JVL: rewrite! - case 'PRIORITY': - $event['priority'] = $this->mke_prio($attrval); - break; - - case 'CATEGORIES': - $catnames = explode(',',$attrval); - $catidcstr = $this->cats_names2idscstr($catnames,$user_id,'calendar'); - $event['category'] .= (!empty($event['category'])) - ? ',' . $catidcstr : $catidcstr; - break; - - // when we encounter an new valid cal_address but not yet in egw db - // should we import it? - case 'ATTENDEE': - if ($pid = $this->mke_CAL_ADDRESS2pid($attrval)){ - if( $epartstat = $this->mke_params2partstat($attr['params'])){ - $event['participants'][$pid] = $epartstat; - } elseif ($pid == $event['owner']){ - $event['participants'][$pid] = 'A'; - } else { - $event['participants'][$pid] = 'U'; - } - // egw unknown participant, add to nonegw_participants list - } else { - $nonegw_participants[] = - $this->mke_ATTENDEE2cneml($attrval,$attr['params']); - } - break; - - // make organizer into a accepting participant - case 'ORGANIZER': // make him - if ($pid = $this->mke_CAL_ADDRESS2pid($attrval)) - $event['participants'][$pid] = 'A'; - //$event['owner'] = $pid; - break; - - case 'CREATED': // will be written direct to the event - if ($event['modified']) break; - // fall through - - case 'LAST-MODIFIED': // will be written direct to the event - $event['modified'] = $attrval; - break; - - case 'STATUS': // note: custom field in event - $event['status'] = strtoupper($attrval); - break; - - default: - error_log('VEVENT field:' .$attr['name'] .':' - . $attrval . 'HAS NO CONVERSION YET'); - } - } // end of fields loop - - // now all fields are gathered do some checking and combinations - - // we may have a RECUR value set? Then convert to egw recur def - if ($recurval = $event['RECUR']){ -//error_log('recurval=' . $recurval . '='); - if(!($recur = $this->mke_RECUR2rar($recurval,$event['start'])) == false){ - foreach($recur as $rf => $rfval){ - $event[$rf] = $rfval; - } - } - unset($event['RECUR']); - } - - // build endtime from duration if dtend was not set - if (!isset($event['end']) && ($evduration !== false)){ - $event['end'] = $this->mke_DDT2utime($event['start']) + $evduration; - } - - // a trick for whole day handling or ...?? - if(date('H:i:s',$event['end']) == '00:00:00') - $event['end']--; - - // check vevent for subcomponents (VALARM only at the moment) - // maybe some day do it recursively... (would be better..) - foreach($vevent->getComponents() as $valarm) { - // SKIP anything but a VALARM - if(!is_a($valarm, 'Horde_iCalendar_valarm')) - continue; - $this->upde_c_VALARM2alarms($alarms,$valarm,$user_id,$veImportFields); - } - - // AD HOC solution: add nonegw participants to the description - // should be controlable by class member switch - if (count($nonegw_participants) > 0) - $this->upde_nonegwParticipants2description($event['description'], - $nonegw_participants); - - // handle fixed id call (for boical compatibility) - // @todo test boical compatibility (esp. with $cal_id>0 case) - if($cal_id > 0) { - $event['id'] = $cal_id; - } - - // SORRY THE PARTICPANTS HANDLING OF EGW IS NOT YET CLEAR TO ME (JVL) - // so I do the bold solution to add ourself to participants list if we are not on yet - if(!isset($event['participants'][$user_id])) - $event['participants'][$user_id] = 'A'; - - // error_log('<< ok <<<<' . 'event read for import=' . print_r($event,true)); - - - // -- finally we come to the import into egw --- - - if (($event['title'] == 'X-DELETE') || ($event['title'] == '_DELETED_')){ - - - // -------- DELETION -------------------- - // error_log('delete event=' . print_r($event,true)); - $imp_action = 'DEL-' . $imp_action; - if(! $cur_eid) { - $this->_errorlog_evupd('ERROR: ' . $imp_action, - $user_id, $event, false); - $everrcnt += 1; - continue; - } else { - // event to delete is found readable - if($eidOk = $this->mycal->delete($cur_eid)){ - // DELETE OK - $evdelcnt += 1; - - // ASSUME Alarms are deleted by egw on delete of the event... - // otherwise we should use this code: - // delete the old alarms - //foreach($cur_event['alarm'] as $alarmID => $alarmData) { - // $this->delete_alarm($alarmID); - //} - continue; - } elseif ($user_id != $cur_owner_id){ - // DELETE BAD but it wasnt ours anyway so skip it - if ($this->evdebug) - $this->_errorlog_evupd('SKIPPED: ' . $imp_action . ' (INSUFFICIENT RIGHTS)', - $user_id, $event, $cur_event); - $evskipcnt += 1; - continue; - } else { - // DELETE BAD and it was ours - $this->_errorlog_evupd('ERROR: ' . $imp_action . '(** INTERNAL ERROR ? **)', - $user_id, $event, $cur_event); - $everrcnt += 1; - continue; - } - - } - - // -------- UPDATE -------------------- - } elseif ($eidOk = $this->mycal->update($event, TRUE)){ - // UPDATE OKE ,now update alarms - $evokcnt += 1; // nof imported ok vevents - // handle the found alarms - if(in_array('VALARM',$veImportFields)){ - // delete the old alarms for the event, note: we could also have used $cur_event - // but jus to be sure - if(!$updatedEvent = $this->mycal->read($eidOk)){ - error_log('ERROR reading event for Alarm update, will skip update..'); - continue; - } - - // ******** for serious debugging only.. ************** - // if ($this->evdebug){ - // $this->_errorlog_evupd('OK: ' . $imp_action, - // $user_id, $event, $cur_event); - //error_log('event readback dump:' . print_r($updatedEvent,true)); - // } - // ******** eof serious debugging only.. ************** - - foreach($updatedEvent['alarm'] as $alarmID => $alarmData) { - $this->delete_alarm($alarmID); - } - // set new alarms - foreach($alarms as $alarm) { - if(!isset($alarm['offset'])){ - $alarm['offset'] = $event['start'] - $alarm['time']; - } elseif (!isset($alarm['time'])){ - $alarm['time'] = $event['start'] - $alarm['offset']; - } - $alarm['owner'] = $user_id; -// error_log('setting egw alarm as:' . print_r($alarm,true)); - $this->save_alarm($eidOk, $alarm); - } - } - continue; - - // ---UPDATE BAD -------- - } elseif ($user_id != $cur_owner_id){ - // UPDATE BAD, but other ones event, so skip - if ($this->evdebug) - $this->_errorlog_evupd('SKIPPED: ' . $imp_action . ' (INSUFFICIENT RIGHTS)', - $user_id, $event, $cur_event); - $evskipcnt += 1; - continue; - } else { - // UPDATE BAD and we own it or it was a new one - $this->_errorlog_evupd('ERROR: ' . $imp_action . '(** INTERNAL ERROR ? **)', - $user_id, $event, $cur_event); - $everrcnt += 1; - continue; - } - error_log('CODING ERROR: SHOULDNOT GET HERE'); - } // for each - - if (($everrcnt > 0) || $this->evdebug) - error_log('** user[' . $user_id . '] vevents imports: ' . $everrcnt . ' BAD,' . - $evskipcnt . ' skip-(insufficient rights), ' . $evmisskipcnt . - ' skip-(ignore reimport missings), ' . - $evokcnt . ' upd-ok, ' . $evdelcnt . ' del-ok'); - return ($everrcnt > 0) ? false : $evokcnt+ $evdelcnt; - } - - - /** - * @private - * Log event update problems to http errorlog - * @param string $fault description of the fault type - * @param ind $user_id the id of the logged in user - * @param array $new_event the info converted from the vevent to be imported - * @param array|false $cur_event_ids settings of owner, id and uid field of a possibly found - * corresponding egw event. When no such event found: false. - */ - function _errorlog_evupd($fault='ERROR', $user_id, &$new_event, $cur_event) - { - // ex output: - // ** bovevents import for user(12 [pietje]): ERROR - // current egw event: id=24, owner=34, uid='adaafa'\n - // vevent info event: id=24, owner=--, uid='dfafasdf'\n - - $uname =(is_numeric($user_id)) - ? $user_id . '[' . $GLOBALS['egw']->accounts->id2name($user_id) . ']' - : '--'; - if ($cur_event === false){ - $cid = $cown = $cuid = '--'; - }else{ - $cid = $cur_event['id']; - $cown = $cur_event['owner']; - $cuid = $cur_event['uid']; - } - $nid = ($vi = $new_event['id']) ? $vi : '--'; - $nown = ($vi = $new_event['owner']) ? $vi : '--'; - $nuid = ($vi = $new_event['uid']) ? $vi : '--'; - - error_log('** bovevents import for user (' . $cur_eid . - '['. $uname . ']):' . $fault . '\n' . - 'current egw event: id=' . $cid . ',owner=' . $cown . ',uid=' . $cuid .'\n' . - 'vevent info event: id=' . $nid . ',owner=' . $nown . ',uid=' . $nuid .'\n' ); -// error_log('vevent info event dump:' . print_r($new_event,true) . '\n <<-----------<<\n'); - } - - - /** - * @private - * - * Fill member var that holds the iCalendar property to Egw fields mapping. - * - * Copy keys from this var to the supportedFields member var to allow import/export - * of the field refered to by the key. - * @todo Maybe someday rethink the ical2egwFields trafo system by rewriting it in paths - * starting from iCalendar/Component=>Field or iCalendar/Comp/SubComp etc. - * @see $ical2egwFields member var that holds the mapping - */ - function _set_ical2egwFields() - { - $this->ical2egwFields = - array( - 'UID' => array('uid'), - 'CLASS' => array('public'), - 'SUMMARY' => array('title'), - 'DESCRIPTION' => array('description'), - 'LOCATION' => array('location'), - 'DTSTART' => array('start'), - 'DTEND' => array('end'), - 'DURATION' => array('end-duration'), - 'ORGANIZER' => array('owner'), - 'ATTENDEE' => array('participants'), - 'RRULE' => array('recur_type','recur_interval','recur_data','recur_enddate'), - 'EXDATE' => array('recur_exception'), - 'PRIORITY' => array('priority'), - 'TRANSP' => array('non_blocking'), - 'CATEGORIES'=> array('category'), - 'URL' => array(''), - 'CONTACT' => array(''), - 'GEO' => array(''), - 'CREATED' => array(''), - 'AALARM' => array('alarms'), // NON RFC2445!! - 'DALARM' => array('alarms'), // NON RFC2445!! - 'VALARM' => array('alarms'), - 'VALARM/TRIGGER' => array('alarms/time') - ); - return true; - } - - - /** - * Set the list of ical fields that are supported during the next imports and exports. - * - * The list of iCal fields that should be converted during the following imports and exports - * of VEVENTS is set. This is done by providing a productmanufacturer name and - * (optionally) a prductname. In a small lookup table the set of currently supported - * fields for this is searched and then set thus in the class member @ref $supportedFields. - * - * @note JVL: I can only see sense in defining supported fields in iCal fields as - * these are the fields (terminology) that the devices have in common. - * in addressbook this approach is also --correctly-- taken. Why not here? - * @param string $_productManufacturer a string indicating the device manufacturer - * @param string $_productName a further specification of the current device that is used - * for import or export. - */ - function setSupportedFields($_productManufacturer='file', $_productName='') - { - $defaultFields = array('CLASS','SUMMARY','DESCRIPTION','LOCATION','DTSTART', - 'DTEND','RRULE','EXDATE','PRIORITY'); - // not: 'TRANSP','ATTENDEE','ORGANIZER','CATEGORIES','URL','CONTACT' - - switch(strtolower($_productManufacturer)) { - case 'nexthaus corporation': - switch(strtolower($_productName)){ - default: - // participants disabled until working correctly - // $this->supportedFields = array_merge($defaultFields,array('ATTENDEE')); - $this->supportedFields = $defaultFields; - break; - } - break; - - // multisync does not provide anymore information then the manufacturer - // we suppose multisync with evolution - case 'the multisync project': - switch(strtolower($_productName)) { - case 'd750i': - default: - $this->supportedFields = $defaultFields; - break; - } - break; - case 'sonyericsson': - switch(strtolower($_productName)){ - default: - $this->supportedFields = $defaultFields; - break; - } - break; - - case 'synthesis ag': - switch(strtolower($_productName)){ - default: - $this->supportedFields = $defaultFields; - break; - } - break; - // used outside of SyncML, eg. by the calendar itself ==> all possible fields - case 'file': - case 'all': - $this->supportedFields = - array_merge($defaultFields, - array('ATTENDEE','ORGANIZER','TRANSP','CATEGORIES', - 'DURATION','VALARM','VALARM/TRIGGER')); -// error_log('OKE setsupportedFields (all)to:'. print_r($this->supportedFields,true)); - break; - - // the fallback for SyncML - default: - error_log("Client not found: $_productManufacturer $_productName"); - $this->supportedFields = $defaultFields; - break; - } - } - - - /** - * - * Exports calendar events as an iCalendar string - * - * @note -- PART OF calendar.boical API COMPATIBILITY INTERFACE ----------- - * @param int/array $events (array of) cal_id or array of the events - * @param string $method='PUBLISH' - * @return string|boolean string with vCal or false on error - * (eg. no permission to read the event) - * - * @see _hiCal class member to hold a temporary Horde_iCalendar object - */ - function &exportVCal($events,$version='1.0',$method='PUBLISH') - { - $hIcal = &new Horde_iCalendar; - $euid_export = false; - - // set some header values of the Horde_iCalendar object - $hIcal->setAttribute('PRODID', '-//eGroupWare//NONSGML eGroupWare Calendar ' - . $GLOBALS['egw_info']['apps']['calendar']['version'].'//' - . strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang'])); - $hIcal->setAttribute('VERSION',$version); - $hIcal->setAttribute('METHOD',$method); - - // convert the eGW events to VEVENTS and add them to hIcal - if(!$this->exportEventsOntoIcal($hIcal, $events,$euid_export)) - return false; - - // conversion oke, now let Horde stringify it and deliver as result - $vcal = $hIcal->exportvCalendar(); - // JVL: destroy the object by hand or does automagic this in php ? - $hIcal = null; - - return $vcal; - - } - - - - /** - * Convert VEVENT components from an iCalendar string into eGW calendar events - * and write these to the eGW calendar as new events or changes of existing events - * - * @note -- PART OF calendar.boical API COMPATIBILITY INTERFACE ----------- - * @param string $_vcalData ical data string to be imported - * @param int $cal_id id of the eGW event to fill with the VEvent data - * when -1 import the VEvent content to new EGW events - * (JVL HACK when 0 allow change but no deletion user is added to participants - * if needed) - * @return boolean $ok false on failure | true on success - */ - function importVCal($_vcalData, $cal_id=-1) - { - - $hIcal = &new Horde_iCalendar; - // our (patched) horde classes, do NOT unfold folded lines, - // which causes a lot trouble in the import, so we do it here - $_vcalData = preg_replace("/[\r\n]+ /",'',$_vcalData); - - // let the Horde_iCalendar object parse the Vcal string into its components - if(!$hIcal->parsevCalendar($_vcalData)){ - return FALSE; - } - $importMode = 'OVERWRITE'; - - // now import the found VEVENTS into eGW calendar - if(!$this->importVEventsFromIcal($hIcal, $importMode, $cal_id)) - { - //error_log('importVCal(): errors in importVEventsFromIcal'); - $hIcal = null; - return false; - } - - $hIcal = null; - return true; - } - - - } - - -?> diff --git a/egwical/inc/class.boical.inc.compat.php b/egwical/inc/class.boical.inc.compat.php deleted file mode 100644 index 71d1681c3c..0000000000 --- a/egwical/inc/class.boical.inc.compat.php +++ /dev/null @@ -1,69 +0,0 @@ -ei =& CreateObject('egwical.egwical'); - $this->wkobj =& $this->ei->addRsc($this); - // alternatively the fast, shortcut, road for knowingly experts only: - //$this->wkobj =& CreateObject('egwical.bocalupdate_vevents'); - //$this->wkobj->setRsc($this); - - if ($this->wkobj == false){ - error_log('boical constructor: couldnot add boical resource to egwical: FATAL'); - return false; - } - } - - - // now implement the compatibility methods, that are all moved to egwical! - - function &exportVCal($events,$version='1.0',$method='PUBLISH') - { - return $this->wkobj->exportVCal($events,$version,$method); - } - - function importVCal($_vcalData, $cal_id=-1) - { - return $this->wkobj->importVCal($_vcalData, $cal_id); - } - - - function setSupportedFields($_productManufacturer='file', $_productName='') - { - return $this->wkobj->setSupportedFields($_productManufacturer, $_productName); - } - - } -?> diff --git a/egwical/inc/class.boinfolog_vtodos.inc.php b/egwical/inc/class.boinfolog_vtodos.inc.php deleted file mode 100644 index d3ad5a9a98..0000000000 --- a/egwical/inc/class.boinfolog_vtodos.inc.php +++ /dev/null @@ -1,561 +0,0 @@ - * - * -------------------------------------------- * - * This program 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 of the License. * - **************************************************************************/ - - /* JVL Todo V0.7: - * - add structure and API terminology from class.bovevents.inc.php:DONE - * - rewrite all vevent to vtodo strings: DONE.. - * - Maybe add a supportFields system as done in calendar.bovevents, to allow for - * handling vtodos for various devices - * - if done document the supportFields method and show how it can be used - * - find out how to do deletion based on imported VTODOS ? Can that be done? - * - check the usage and conversions of user time and server times - * - add compatibility API for the class infolog.vcalinfolog: DONE but UNTESTED - * - add ORGANIZER export: DONE (V0.51) removed (dont know map field) - * - add ORGANIZER import: ... maybe map t info_responsible - * - add CATEGORIES export: DONE (V0.52) - * - add CATEGORIE import: DONE (V0.7.01) - * - add "subtask" export: DONE (v0.52) - * - add "subtask" import: PARTLY - * - rewrite PRIORITY export: DONE (V0.52) - * - rewrite PRIORITY import:DONE (V0.7.01) - * - repair datecreated: DONT know map field - * - repair date modified export: PARTLY done - * - repair startdate or enddate without time details: DONE (V0.7.02) - */ - - - // require_once EGW_SERVER_ROOT.'/infolog/inc/class.boinfolog.inc.php'; - require_once EGW_SERVER_ROOT.'/phpgwapi/inc/horde/Horde/iCalendar.php'; -// require_once EGW_SERVER_ROOT.'/icalsrv/inc/class.egwical.inc.php'; - - /** - * - * iCal vtodos import and export via Horde iCalendar classes - * @note the routines in this package should be used OO only so that de constructor - * can initialize the data common to the import and export routines - * @note this package provides compatibilty routines for class infolog.vcalinfolog - * this can e.g. be used by making infolog.vcalinfolog a simple extension of - * infolog.bovtodos - * - * @todo move the compatibility functions for vcalinfolog completely to the compat class. - * There is no need to have them here anymore. - * @todo rewrite bovtodos to use a ical2egw and supportedFields system - * @todo IMPORTANT rewrite bovtodos to handle uid_matching analogous to bovevents - * - * @package egwical - * @author Jan van Lieshout This version. - * @author Lars Kneschke (parts of reused code) - * @author Ralf Becker (parts of reused code) - * @version 0.9.02 First for use with new WURH egwical class - * @license http://opensource.org/licenses/gpl-license.php GPL - - * GNU General Public License - */ - - class boinfolog_vtodos extends egwical - { - - /** - * @var object - * The egw infolog object that will be used to transport events from and to - * This is set by setRsc() - */ - var $myinf = null; - - - /** - * Describe the provided work capabilities of the class. - * @return string The description as entries for the @ref $reg_workers registry - * table. - */ - function provides_work() - { - return - array('boinfolog' => array('workerclass' => 'boinfolog_vtodos', - 'workerobj' => $null, - 'icalsup' => array('VTODO')), - 'vcalinfolog' => array('workerclass' => 'boinfolog_vtodos', - 'workerobj' => $null, - 'icalsup' => array('VTODO')) - ); - - } - - - /* - * Our Constructor, fills the basic class members - * and set the description of our worker capabilities. - */ - function bovtodos() { - - // call superclass constructor by hand - boinfolog::boinfolog(); - - $this->TASKMAGIC = $GLOBALS['egw_info']['server']['install_id'] - ? $GLOBALS['egw_info']['server']['install_id'] - : 'local'; - - // $this->setSupportedFields(); //not implemented yet - return true; - } - - - /** - * Set the egw resource that this worker will handle. - * This worker is only capable of handling boinfolog objects, so it should - * be of that class. This method is mostly called indirectly from a egwical compound - * addRsc() call. But you can call it also directly (if you know what your doing ..) - * @return boolean false on error, true else - */ - function setRsc($egw_rsc) - { - if(!is_a($egw_rsc,'boinfolog')) - return false; - $this->myinf = $egw_rsc; - return true; - } - - - // --- conversion and import code -- - - /** - * @private - * @var $TASKMAGIC - * Magic unique number used for de/encoding our uids. - * - * This string that contains global unique magic number that is - * unique for our current database installed etc. It is used to recognize - * earlier exported VTODO or VEVENT UID fields as referring to their eGW counterparts. - */ - var $TASKMAGIC='dummy'; - - - - - // Some helper functions first - - - /** - * generate a unique id, with the todo id encoded into it, which can be - * used for later synchronisation. - * - * @param $todo_id string|int eGW id of the content - * @use $TASKMAGIC string that holds our unique ID - * @return false|string on error: false - * on success the global unique id - */ - function _id2guid($todo_id) - { - if (empty($todo_id)) - return false; - - return 'infolog_task'.'-'.$todo_id.'-'. $this->TASKMAGIC; - } - - - - /** - * get the local content id from a global UID - * - * @param string $globalUid the global UID - * @return false|int on error: false - * on success: local egw todo id - */ - function _guid2id($VTodoUID) - { - // error_log('_guid2id: trying to recover id from' . $VTodoUID); - if (!preg_match('/^infolog_task-(\d+)-' . - $this->TASKMAGIC . '$/',$VTodoUID,$matches)) - return false; - - // error_log("_guid2id: found (" . $matches[1] . ")"); - return $matches[1]; - } - - - - /** - * export the eGW todos in $todos to iCalendar VTODOS and add these to - * the Horde_iCalendar object &$hIcal - * Note: that because eGW does not store uid fields for tasks in its db we - * are in general not able to recoginize VTODOS by their uid-field. - * Because of this it is only possible to have a VTODO overwrite an internal - * eGW todo (task) when this VTODO was in an earlier fase build as export of an - * internal eGW todo. In other words to later on change your imported VTODO, - * you first have export it and in the client make your changes on this exemplar. - * - * @param &$hIcal Horde_iCalendar object to wich the produced VTodos are added - * @param $todos array with either id s (tids) for a eGW todoData structs - * or an array of such todoData structs, that will be exported - * @param boolean $euid_export if true export the uid field (Note: Currently not available!) - * else generate a uid from with the task id encoded (Default setting) - * @return $ok/$vcnt boolean/int on error: false / on success: nof vtodos exported - * @use members supportedFields(), _id2guid() - */ - function exportTodosOntoIcal(&$hIcal, $todos, $euid_export=false) - { - //NOTE: $euid_export has currently no effect -# error_log("ical_export_add_Todos here, for " . count($todos) . "todos"); - - $todo = array(); // container for each todo to be exported - $tid = null; // id of the todo to be exported - $vexpcnt =0; // number of vtodos exported -# $options = array('CHARSET' => 'UTF-8','ENCODING' => 'QUOTED-PRINTABLE'); - - - if (!is_array($todos)) $todos = array($todos); - - foreach($todos as $todo) { - // some hocuspocus to handle the polymorphy of the $todos arg - if (!is_array($todo) - && !($todo = $this->myinf->read($todo))){ - - return false; // no permission to read $tid - } - $tid = $todo['info_id']; - // oke, now sure $todo is a todoData array and $tid its info_id field.. - //_debug_array($todo); - - $todo = $GLOBALS['egw']->translation-> - convert($todo,$GLOBALS['egw']->translation->charset(),'UTF-8'); - -# error_log('todo to export=' . print_r($todo,true)); - - //someday: $this->newComponent() ??? - $vtodo = Horde_iCalendar::newComponent('VTODO',$hIcal); - - $vGUID = $this->_id2guid($tid); - - // if (!$euid_export) - // append Non Recoverable so _guid2id() wont recognize it later - // $vGUID .= 'NR'; - $vtodo->setAttribute('UID',$vGUID); - // for subtasks set the parent - // egw2vtodo: info_id_parent => pid -> RELATED-TO:parent_uid - if ($parid = $todo['info_id_parent']) - $vtodo->setAttribute('RELATED-TO', $this->_id2guid($parid)); - - $vtodo->setAttribute('SUMMARY', $todo['info_subject']); - $vtodo->setParameter('SUMMARY', $options); - $vtodo->setAttribute('DESCRIPTION', $todo['info_des']); - $vtodo->setParameter('DESCRIPTION', $options); - if($todo['info_startdate']) - $vtodo->setAttribute('DTSTART', $todo['info_startdate']); - if($todo['info_enddate']) - $vtodo->setAttribute('DUE', $todo['info_enddate']); - $vtodo->setAttribute('DTSTAMP',time()); - - $lastmodDate = $todo['info_datemodified']; - $vtodo->setAttribute('LAST-MODIFIED', $lastmodDate ); - - if ($createDate = $this->get_TSdbAdd($tid,'infolog')){ - $vtodo->setAttribute( 'CREATED', $createDate); - } else { - $vtodo->setAttribute( 'CREATED', $lastmodDate); - } - - // egw2VTOD: owner -> ORGANIZER field - if ($tfrom_id = $todo['info_owner']){ - $mailtoOrganizer = $GLOBALS['egw']->accounts->id2name($tfrom_id,'account_email'); - $vtodo->setAttribute('ORGANIZER', $this->mki_v_CAL_ADDRESS($tfrom_id)); - $vtodo->setParameter('ORGANIZER', $this->mki_p_CN($tfrom_id)); - } - - $vtodo->setAttribute('CLASS', - ($todo['info_access'] == 'public')?'PUBLIC':'PRIVATE'); - // CATEGORIES, value= all category names from info_cat field comma-separated list - // n.b. dont mind catid ==0 (this is none categorie, I think) - if ($catids = $todo['info_cat']){ - $catnamescstr = $this->cats_ids2idnamescstr(explode(',',$catids)); - $vtodo->setAttribute('CATEGORIES',$catnamescstr); - } - - - // egw2vtodo status trafo: - // done -> COMPLETE:lastmoddate, PERCENT-COMPLETE:100, STATUS:COMPLETED - // ongoing -> STATUS: IN-PROCESS - // offer -> STATUS: NEEDS-ACTION, PERCENT-COMPLETE:0 - switch ($todo['info_status']){ - case 'done': - $vtodo->setAttribute('COMPLETED',$lastmodDate); // for ko35, lastmod? - $vtodo->setAttribute('PERCENT-COMPLETE','100'); - $vtodo->setAttribute('STATUS','COMPLETED'); - break; - case 'ongoing': - $vtodo->setAttribute('STATUS','IN-PROCESS'); - break; - case 'offer': - $vtodo->setAttribute('STATUS','NEEDS-ACTION'); -# $vtodo->setAttribute('PERCENT-COMPLETE',"0"); - break; - default: - // check for percentages - if (ereg('([0-9]+)%',$todo['info_status'],$matches)){ - $vtodo->setAttribute('PERCENT-COMPLETE',$matches[1]); - $vtodo->setAttribute('STATUS','IN-PROCESS'); - }else{ - $vtodo->setAttribute('STATUS','NEEDS-ACTION'); - } - } - - if (is_numeric($eprio = $todo['info_priority']) && ($eprio >0) ) - $vtodo->setAttribute('PRIORITY', - $this->mki_v_prio($eprio) ); - -# $vtodo->setAttribute('TRANSP','OPAQUE'); - - $hIcal->addComponent($vtodo); - $vexpcnt += 1; - } - - return $vexpcnt; //return nof vtodos exported - } - - - - /* @note PART OF COMPATIBILITY API for INFOLOG.VCALINFOLOG - * @note UNTESTED - * Export a single eGW task as a VTODO string - * - * @param $_taskID int/string id of the eGW task to be exported - * @param $_version string version the produced iCalendar content should get - * @return false|string on error | content of the resulting VTODO iCal element - */ - function exportVTODO($_taskID, $_version) - { - $hIcal = &new Horde_iCalendar; - $hIcal->setAttribute('VERSION',$_version); - $hIcal->setAttribute('METHOD','PUBLISH'); - - if(! $tcnt = $this->exportTodosOntoIcal(&$hIcal, array($_taskID), true)) - return false; - - return $hIcal->exportvCalendar(); - } - - - - /** - * Convert the ical VTODOS components that are contained in de $hIcal Horde_iCalendar - * to eGW todos and import these into the eGW calendar. - * Depending on the value of $importMode, the conversion will generate either eGW - * todos with completely new id s (DUPLICATE mode) or try to recover an egw id from - * the VTODO;UID field (so called OVERWRITE mode). Note that because eGW currently - * does not store todo uid field info in its database, such recovering is only - * possible for previously exported todos. - * - * @param &$hIcal Horde_iCalendar object with ical VTODO objects - * @param $importMode string toggle for duplicate (ICAL_IMODE_DUPLICATE) - * or overwrite (ICAL_IMODE_OVERWRITE) import mode - * @return $false|$timpcnt on error: false | on success: nof imported elms - * @use .supportedFields() to steer the VTODOS to eGW todos conversion - * @use members _guid2id() - */ - function importVTodosFromIcal(&$hIcal, $importMode='DUPLICATE') - { - - $overwritemode = stristr($importMode,'overwrite') ? true : false; -# $ftid = $fixed_taskId; - $timpcnt = 0; // nof todos imported - $tidOk = true; // return true, if hIcal contains no vtodo components - - foreach($hIcal->getComponents() as $component) { - // ($ftid < 0) => recover id (overwritemode) or use no id - // ($ftid > 0) => use this value to set the id (compatibility mode) - - if(is_a($component, 'Horde_iCalendar_vtodo')){ - $tidOk = $this->_importVTodoIcalComponent(&$component, $overwritemode, -1); - if (!$tidOk){ - error_log('infolog.bovtodos.importVTodosFromIcal(): ' - . ' ERROR importing VTODO '); - break; // stop at first error - } - - - $timpcnt += 1; // nof imported ok vtodos - } - } - return (!$tidOk) ? false : $timpcnt; - } - - - /* convert a single vtodo horde icalendar component to a eGW todo and write it to - * the infolog system. - * - * @note this routine should better not be exported - * @param &$hIcalComponent Horde_iCalendar_vtodo element that contains the VTODO - * that is to be converted and imported - * @param $overwriteMode boolean generate a new eGW todo (when false) or allow - * overwrite of an existing one (when true) - * @param $newtask_id int/string if >0 : the id of the eGW todo that must be - * overwritten. if <0 : generation of new task or recover taskId from UID field - * @return false | int on error: false | on success: the id of the eGW todo - * that was produced/changed - */ - function _importVTodoIcalComponent(&$hIcalComponent, $overwriteMode, $newtask_id) - { - $ftid = $newtask_id; - $todo = array(); //container for eGW todo - $user_id = $this->owner; // we logged in? - - if(!is_a($hIcalComponent, 'Horde_iCalendar_vtodo')) - return false; - - if($ftid > 0) { - // just go for a change of the content of the eGW task with id=$ftid - $todo['info_id'] = $ftid; - // we will now ignore a UID field later in this VTodo - } - - // now process all the fields found - foreach($hIcalComponent->_attributes as $attributes) { -# error_log( $attributes['name'].' - '.$attributes['value']); - //$attributes['value'] = - // $GLOBALS['egw']->translation->convert($attributes['value'],'UTF-8'); - switch($attributes['name']){ - case 'UID': - if ($ftid > 0) // fixed id mode so we got id from $newtask_id - break; - $vguid = $attributes['value']; - if( $overwriteMode && $tid = $this->_guid2id($vguid)){ - // an old id was recovered from the UID field, we will use it - $todo['info_id'] = $tid; - #error_log('import: using existing id:'.$tid); - } // else we leave the info_id empty so automatically a new todo gets created - break; - // rfc s4.8.1.3.egw2vtodo: public|private|confidential - case 'CLASS': - $todo['info_access'] = strtolower($attributes['value']); - break; -# case 'ORGANIZER': -# $todo['info_from'] = $attributes['value']; -# may be put it in info_responsible field ? -# // full name + mailto see bovevents on a method to handle this - break; - - case 'DESCRIPTION': - $todo['info_des'] = $attributes['value']; - break; - case 'DUE': - $todo['info_enddate'] = $this->mke_DDT2utime($attributes['value']); - break; - case 'DTSTART': - $todo['info_startdate'] = $this->mke_DDT2utime($attributes['value']); - break; - case 'PRIORITY': - $todo['info_priority'] = $this->mke_prio($attributes['value']); - break; - // rfc s4.8.1.11 egw2vtodo status trafo: (now use it backwards) - // done -> COMPLETE:lastmoddate, PERCENT-COMPLETE:100, STATUS:COMPLETED - // ongoing -> STATUS: IN-PROCESS - // offer -> STATUS: NEEDS-ACTION, PERCENT-COMPLETE:0 - case 'STATUS': - switch (strtolower($attributes['value'])){ - case 'completed': - $todo['info_status'] = 'done'; - break; - case 'cancelled': - $todo['info_status'] = 'done'; - break; - case 'in-process': - $todo['info_status'] = 'ongoing'; - break; - default: - // probably == 'needs-action' - $todo['info_status'] = 'offer'; - } - break; - // date and time when completed - case 'COMPLETED': - $todo['info_status'] = 'done'; - break; - case 'PERCENT-COMPLETE': - $pcnt = (int) $attributes['value']; - if ($pcnt < 1) { - $todo['info_status'] = 'offer'; - }elseif($pcnt > 99) { - $todo['info_status'] = 'done'; - }else{ - $todo['info_status'] = $pcnt . '%'; // better should do rounded to 10s - } - break; - case 'SUMMARY': - $todo['info_subject'] = $attributes['value']; - break; - case 'RELATED-TO': - $todo['info_id_parent'] = $this->_guid2id($attributes['value']); - break; - // unfortunately infolog can handle only one cat atm - case 'CATEGORIES': - $catnames = explode(',',$attributes['value']); - $catids = $this->cats_names2idscstr($catnames,$user_id,'infolog'); - $todo['info_cat'] = $catids; - break; - - case 'LAST-MODIFIED': - $todo['info_datemodified'] = $attributes['value']; - break; - - default: -// error_log('VTODO field:' .$attributes['name'] .':' -// . $attributes['value'] . 'HAS NO CONVERSION YET'); - } - } - // error_log('todo=' . print_r($todo,true)); - - if($todo['info_subject'] == 'X-DELETE' && $tid){ - // delete the todo (secret HACK, donot use...) - return $this->myinf->delete($tid); - }else{ - $tidOk = $this->myinf->write($todo,true,false); - // error_log('ok import id:'. $tidOk .' VTODO UID:' . $vguid); - return $tidOk; - } - } - - - - - - /* @note PART OF COMPATIBILITY API for INFOLOG.VCALINFOLOG - * @note UNTESTED - * Import a single iCalendar VTODO string as eGW task(aka: todo) - * - * @param $_vcalData string content of an VTODO iCalender element - * @param $_taskID int/string id of the eGW task to be overwritten - * when < 0 a new task will get produced - * @return false | int on error: false | on success: the id of the eGW todo - * that was produced/changed - */ - function importVTODO(&$_vcalData, $_taskID=-1) - { - $hIcal = &new Horde_iCalendar; - if(!$hIcal->parsevCalendar($_vcalData)) - return FALSE; - - $components = $hIcal->getComponents(); - if(count($components) < 1) - return false; - - return $this->_importVTodoIcalComponent(&$components[0], - true, $_taskID); - } - - - - } - - -?> diff --git a/egwical/inc/class.egwical.inc.php b/egwical/inc/class.egwical.inc.php deleted file mode 100644 index 0bd5ee97a5..0000000000 --- a/egwical/inc/class.egwical.inc.php +++ /dev/null @@ -1,1278 +0,0 @@ - - *
  • mki_ - * This is for methods that MaKe a Ical thing like a component, field, fieldvalue or - * fieldparameter. Thus these are subdived in:
  • - *
      - *
    • mki_c to make ical Components like VEVENTS or VALARMS
    • - *
    • mki_v to make ical field Values like e.g. a ATTENDEE field value
    • - *
    • mki_vp to make both ical field Values and Parameters
    • - *
    - * - *
  • mke_ - * This is for methods that MaKe a Egw things like a field of egw event or task.
  • - *
  • updi_ - * This is for methods that UPDate an Ical component or field. Note that the Ical component - * to be updated will be passed by reference to these routines
  • - *
  • upde_ - * This is for methods that UPDate an Egw entity like an event or task or.... Note again - * that the Egw entity will be passed by reference to these routines.
  • - * - * - * @section secworkersubclasses Worker Subclasses - * Egwical is implemented following the WURH pattern (see @ref pageegwicalwurh). - * Currently there are two worker subclasses available: - * - bocalupdate_vevents to convert between egw calendar events and VEVENTS - * and allow import and export of these. When added as a resource to an Egwical object, - * the representative member for this subclass is set in the attribute $this->buverpr - * - * - boinfolog_vtodos to convert between egw infolog tasks events and VTODOS - * and allow import and export of these. When added as a resource to an Egwical object, - * the representative member for this subclass is set in the attribute $this->ivtrpr - * - * @note At this moment (V 0.9.01) there is still a little unpleasant piece of extra - * coding needed to get - * it working: when you develop a new workerssubclass you must enter it manually in - * the egwical constructor, - * so that it can later on automatically be found and matched to the appropiat egw resource. - * (I couldnot find an easy way to search for existing subclasses so therefore it is needed) - * - * @note in future this package may become a full wrapper around the Horde_iCalendar - * libraries. This way we can keep the relevant egw and ical objects alive between - * calls so that code could be made faster and less memory consuming.. - * - * @since 0.9.0 api is incompatible with lower versions - * @author Jan van Lieshout (This version) - * @author Lars Kneschke (original code of reused parts) - * @author Ralf Becker (original code of reused parts) - * - * - * @version 0.9.04 (First wurh pattern implementation, with RRULE count= impl.) - * @date 20060216 - * @license http://opensource.org/licenses/gpl-license.php GPL - - * GNU General Public License - */ - class egwical - { - - /** - * @var Horde_iCalendar - * Placeholder object used to access various Horde_iCalendar methods - * In here the constructor will create a Horde_iCalendar object that can be used - * by the various routines in the class - */ - var $hi; - - // ----------- first the implementation of the WURH pattern ------------ - - -// /** Description of the resource handling capacity this object provides -// * @var array -// * Decsription, in the form of an entry for the @ref $reg_rscworkers registry, -// * of the worker capabilities this object provides. -// * When an object of this (sub) class is instatiated and added to a egwical object as -// * a socalled worker representative, this capability description is copied into the -// * (compounds) reg_rscworkers registry. -// * @note in genuine worker subclasses this variable must be initialized -// * by the constructor. -// */ -// var $iprovide_work = null; - - - /** Registry for Resource Worker classes - * - * @var array - * The resource workers registry is an array that holds - * an entry for each type of resource classs with the appropiate worker class and - * the currently set worker (representative) instantiated object of that class. - * @note the (instantiated) worker representative object in the registry entry is - * firstly set when an appropiate worker subclass is actually instantiated during - * runtime. (This setting then is done by the constructor of the worker class of course. - * that uses the info from the workerclass its variable @ref $iprovide_rscwork) - * - * Structure of the registry: - *
    -	   *  $reg_rscworkers = array($rsclass_name =>
    -	   *                          array('workerclass' =>$workerclass_name,
    -	   *                                'workerobj'   => $worker_obj,
    -	   *                                'vcsup'     =>array($velmc1,$velmc2,..)),
    -	   *                           ..)
    -	   *
    - * example of an entry: - *
    -	   *  $reg_rscworkers['boinfolog'] = array('workerclass' =>'boinfolog_vtodos',
    -	   *                                       'workerobj'   => null,
    -	   *                                       'icalsup'     =>array('VTODO'))
    -	   * 
    - */ - var $reg_rscworkers = array(); - - - /** Registry for Resources - * - * @var array - * - * The resources registry is an array that holds an entry for - * types of icalendar component class ('VEVENT, VTODO etc.) - * with: - *- 1) an appropiate egw resource object for storing and - * retrieving the info for these components (thus a calendar- or - * infolog- or .. etc. object) and - *- 2) an (instantiated) worker object to handle transport from - * and to the resource. - * The entries of this registry get filled when the - * resources are added to the egwical object by calls to the - * addRsc() method. - * @note multiple entries for the same icalendar component are allowed. - * This can occur when multiple resources can provide e.g. VEVENT data. - * - * Structure of the registry: - *
    -	   *   reg_rscs = array($icalcompclass_name => array($rsc_obj, $worker_obj),..)
    -	   *
    - * example of an entry: - *
    -	   *  reg_rscs['VTODO'] = array(null, null)
    -	   * 
    - */ - var $reg_rscs = array(); - - - - /** - * Constructor, init the auxiliary object @ref $hi and @ref $TASKMAGIC - * and instantiate the @ref $reg_rscworkers workers registry and the - * @ref $reg_rscs resources - * registry. - * @note At this moment (V 0.9.01): when you develop a new workerssubclass you must - * manually add its characteristics via a class call to its provides_work() function - * from within the constructor code. - * (It is needed because I couldnot find an easy way to search for existing subclasses - * yet) - * - */ - function egwical() - { - // actually this would only be needed by the abstract superclass? - $this->hi = &new Horde_iCalendar; - - $this->TASKMAGIC = $GLOBALS['egw_info']['server']['install_id'] - ? $GLOBALS['egw_info']['server']['install_id'] - : 'local'; - // update following list when a new worker subclasses becomes available - // (i.e. copy the info from its $iprovide_work var) - $this->reg_rscworkers = - array_merge( - $this->reg_rscworkers, - bocalupdate_vevents::provides_work(), - boinfolog_vtodos::provides_work() - ); - // error_log('**now reg_workers=' . print_r($this->reg_rscworkers,true)); - $this->reg_rscs = array(); - } - - - /** - * Add a egw resource. - * - * The egw resource (calendar, infolog, ..) in $egw_rsc is added - * to egwical. This will be used for storing and retrieving - * types of iCalendar components (VEVENT, VTODO,..) for which it has corresponding - * egw data elements (like egw events or egw tasks. - * - * Following the WURH pattern, adding the egw resource $rsc will result in - * the creation of a dedicated representative "worker" subclass object to handle the - * transport to and from the resource. The appropiate worker class is looked up in the - * $reg_workers registry. When the worker is created, the resource and its worker will - * be entered as a resource worker pair in the egwical $reg_rscs registry. - * This representative worker object will also be - * returned by reference to the caller. Using this reference the caller can later on do - * specific settings for the worker, (like setSupportedFields()). - * - * @param object $egw_rsc egw resource (like calendar or infolog,..) object that will be - * used by the egwical representative worker class to transport converted - * ical components to and from. - * @return egwical|false $rpr_worker the representative worker class for handling the - * added egw resource. On error false is returned. - */ - function addRsc($egw_rsc) - { - //step 1: detect the egw_rsc class - if (!$rsc_classname = get_class($egw_rsc)) - // bad value for $egw_rsc - return false; - - //step 1a check that the egwrsc is not already registered - foreach($this->reg_rscs as $vc => $rscwkpair){ - if($rscwkpair[0] === $egw_rcs){ - error_log('Warning: egwical.addRsc(): trying add resource multiple times,' . - ' for class: ' . $rsc_classname . ' : ignored'); - return $rscwkpair[1]; - } - } - - //step 1: detect the egw_rsc class - if (!$rsc_classname = get_class($egw_rsc)) - // bad value for $egw_rsc - return false; - - // step 2a: look in reg_rscworkers for appropiate worker class - if (!$wkprov = $this->reg_rscworkers[$rsc_classname]){ - error_log('Error: egwical.addRsc(): no workerclass available yet for resourcetype:' . - $class_name . ' sorry'); - return false; - } - - // step 2b create a workerobject of the correct type - // note that we dont reuse an already available workerobj of the right type because - // probably the workerclasses are not reentrantly coded yet... - if(! $wkobj =& CreateObject('egwical.' . $wkprov['workerclass'])){ - // workerclass object creation problem - return false; - } - // step 2c give the workerobj the egw_rsc to handle - $wkobj->setRsc($egw_rsc); - - // step3: use the found worker info to register the egw_rsc and workerobj for - // each of the supported type of ical elements - foreach ($wkprov['icalsup'] as $icomptype ){ - //error_log('adding reg_rscs['. $icomptype . '] entry'); - $this->reg_rscs = array_merge($this->reg_rscs, - array($icomptype => array($egw_rsc, $wkobj))); - } - // step 4: return the workerclass (representative obj) - - return $wkobj; - } - - - - // ------------- second: below only generic conversion stuff -------------- - - - // --- generic conversion auxilliary routines ------------- - // --- note: this could be left out in the abstract baseclass instantiation - // but is not that much, so leave it get duplicated... - - /** - * @private - * @var string - * Magic unique number used for de/encoding our uids. - * - * This string that contains global unique magic number that is - * unique for our current database installed etc. It is used to recognize - * earlier exported VTODO or VEVENT UID fields as referring to their eGW counterparts. - */ - var $TASKMAGIC='dummy'; - - - /** - * @var array $status_ical2egw - * Conversion of the egw used priority values(0..3) to corresponding ical values(0..9). - * @private - */ - var $priority_egw2ical = - array( - 0 => 0, // undefined - 1 => 9, // low - 2 => 5, // normal - 3 => 1, // high - ); - /** - * @var array $status_ical2egw conversation of the priority ical => egw - * Conversion of the icalendar used priority values(0..9) to corresponding egw values (0..3). - * @private - */ - var $priority_ical2egw = - array( - 0 => 0, // undefined - 9 => 1, 8 => 1, 7 => 1, // low - 6 => 2, 5 => 2, 4 => 2, // normal - 3 => 3, 2 => 3, 1 => 3, // high - ); - - - /** - * @var array $partstatus_egw2ical - * Conversion of the egw used participant status values to the corresponding icalendar - * attendee status terminology. - * @private - */ - var $partstatus_egw2ical = - array( - 'U' => 'NEEDS-ACTION', - 'A' => 'ACCEPTED', - 'R' => 'DECLINED', - 'T' => 'TENTATIVE', - ); - /** - * @var array - * Conversion of the icalendar used attendee status values to the corresponding icalendar - * participants status terminology. - * @private - */ - var $partstatus_ical2egw = - array( - 'NEEDS-ACTION' => 'U', - 'ACCEPTED' => 'A', - 'DECLINED' => 'R', - 'TENTATIVE' => 'T', - ); - - - /** - * @var array $recur_egw2ical - * Conversion of egw recur-type to ical FREQ values for RRULE fields - * @private - */ - var $recur_egw2ical = - array( - MCAL_RECUR_DAILY => 'DAILY', - MCAL_RECUR_WEEKLY => 'WEEKLY', - MCAL_RECUR_MONTHLY_MDAY => 'MONTHLY', - MCAL_RECUR_MONTHLY_WDAY => 'MONTHLY', - MCAL_RECUR_YEARLY => 'YEARLY', - ); - // BYMONHTDAY={1..31}, BYDAY={1..5}{MO..SO} - - /** - * @var array - * recur_days translates MCAL recur-days to verbose labels - * (copied from class.bocal.inc.php file - * @private - */ - var $recur_days = - array( - MCAL_M_MONDAY => 'Monday', - MCAL_M_TUESDAY => 'Tuesday', - MCAL_M_WEDNESDAY => 'Wednesday', - MCAL_M_THURSDAY => 'Thursday', - MCAL_M_FRIDAY => 'Friday', - MCAL_M_SATURDAY => 'Saturday', - MCAL_M_SUNDAY => 'Sunday', - ); - - /** - * @var array - * Get sequential indexes for the daynames in a week. Used for recurrence count - * calculations. - */ - var $dowseqid = - array('SU' => 1, 'MO' => 2, 'TU' => 3, 'WE' => 4, 'TH' => 5, 'FR' => 6, 'SA' => 7); - - - // --- generic conversion auxilliary routines ------------- - - /** - * Parse a vCalendar string into an Horde_iCalendar object. - * - * To actually parse the string, the Horde_iCalendar in member @ref $hi is used. - * @param string $vcalstr the icalendar input string - * @return boolean|Horde_iCalendar the resulting parsed elements collected in a - * horde ical object. On error: false - */ - function parsevCalendar($vcalstr) - { - // unfoldlines as this was removed from our horde stuff - $vcalstr = preg_replace("/[\r\n]+ /",'',$vcalstr); - - // $this->hi->clear(); - if(!$this->hi->parsevCalendar($vcalstr)){ - error_log('egwical parsevCalendar: ERROR- couldnot parse..'); - return false; - } - - return $this->hi; - } - - - - - /** - * Generate ical UID from egw id. - * - * generate a unique id, with the egw id encoded into it, which can be - * used for later synchronisation. - * @param string|int $egw_id eGW id of the egw entity (event, task,..) - * @param string $app_prefix prefix to use in ecnoding the name - * - * @return string|false on success the global unique id. On error: false. - * - * Uses @ref $TASKMAGIC string that holds our unique ID - */ - function mki_v_guid($egw_id,$app_prefix='egw') - { - if (empty($egw_id)) - return false; - return $app_prefix .'-' . $egw_id. '-' . $this->TASKMAGIC; - } - - - - /** - * Try to decode an egw id from a ical UID - * - * @param string $guid the global Icalendar UID value - * @param string $app_prefix prefix to be found in the encoding - * @return false|int On error: false. - * On success: local egw todo id. - */ - function mke_guid2id($guid,$app_prefix='egw') - { - // error_log('mke_guid2id: trying to recover id from' . $guid); - if (!preg_match('/^' . $app_prefix . '-(\d+)-' . - $this->TASKMAGIC . '$/',$guid,$matches)) - return false; - - // error_log("mke_guid2id: found (" . $matches[1] . ")"); - return $matches[1]; - } - - - - /** - * Get database add date of event or todo - * @private - * @param int $id id of event or todo - * @param string $appname name of the application (='calendar' or 'infolog') - * @return int $createdate of db insert or false on error - */ - function get_TSdbAdd($id,$appname='calendar') - { - if (!(($appname == 'calendar') || ($appname == 'infolog'))) - return false; - if (! $auid = $GLOBALS['egw']->common->generate_uid($appname,$id)) - return false; - - return $GLOBALS['egw']->contenthistory->getTSforAction($auid,'add'); - } - - - /** - * Convert a egw prio into a value for the ical property PRIORITY - * @param int $eprio priority in egw (0..3) - * @return int $iprio conversion of $eprio as value (0..9) for the ical PRIORITY prop - */ - function mki_v_prio($eprio = 0) - { - return $this->priority_egw2ical[$eprio]; - } - - /** - * Convert a ical prio into a value for egw - * @param int $iprio priority in ical (0..9) - * @return int $eprio conversion of $iprio as value (0..3) for egw - */ - function mke_prio($iprio = 0) - { - return $this->priority_ical2egw[$iprio]; - } - - - - /** - * Translate cat-ids to array with id-name pairs - * - * JVLNOTE: boldly copied from class.xmlrpc_server.inc.php because I donot know how - * to instantiate $GLOBALS['server'] (that provides this method) atm. - * @note THIS CODE SHOULD BE SOMEWHERE ELSE: IT HAS NOTHING TO DO WITH ICAL!! - * @param array $cids the list with category ids - * @return string|false $idnamescstr commasep string with names for the category ids or - * on error false - */ - function cats_ids2idnamescstr($cids) - { - if(empty($cids)) - return false; - - if (!is_object($GLOBALS['egw']->categories)) - $GLOBALS['egw']->categories = CreateObject('phpgwapi.categories'); - - $idnames = array(); - foreach($cids as $cid) { - if ($cid) - $idnames[$cid] = stripslashes($GLOBALS['egw']->categories->id2name($cid)); - } - return implode(',',$idnames); - } - - - // ************ JVL CHECK THE CODE BENEATH ***************** - // oke: seems to work for a single categorie (tested form bovtodos calls) - - /** - * Translate catnames back to cat-ids creating/modifying cats on the fly - * - * JVLNOTE boldly copied from class.xmlrpc_server.inc.php because I donot know how - * to instantiate $GLOBALS['server'] (that provides this method) atm. - * - * @note THIS CODE SHOULD BE SOMEWHERE ELSE: IT HAS NOTHING TO DO WITH ICAL!! - * @param array $cnames list with category names - * @return string $cidscstr commasep string with ids generated or found for - * the category names. - */ - function cats_names2idscstr($cnames,$owner_id,$app_name='infolog') - { - if (empty($cnames)) - return false; - - if (!is_object($catsys =& $GLOBALS['egw']->categories)) { - $GLOBALS['egw']->categories =& CreateObject('phpgwapi.categories', - $owner_id,$app_name); - $catsys =& $GLOBALS['egw']->categories; - } - $cids = array(); - foreach($cnames as $name) { - if (!($cid = $catsys->name2id($name))) { - // existing cat-name use the id - // new cat - $cid = $catsys->add(array('name' => $name,'descr' => $name)); - } - $cids[] = (int)$cid; - } - return implode(',',$cids); - } - - - /** - * Convert and egw account id into a iCalendar CAL-ADDRESS type value string - * @param int $aid egw account(person) id - * @return string $cls cal_address format string (mailto:. On error - * the emailadr part will stay empty. - */ - function mki_v_CAL_ADDRESS($aid) - { - $mailtoAid = $GLOBALS['egw']->accounts->id2name($aid,'account_email'); - return $mailtoAid ? 'MAILTO:'.$mailtoAid : 'MAILTO:'; - } - - - /** - * Convert and egw account id into a iCalendar CN type parameter string - * @param int $account_id egw account(person) id - * @return string $cnparam CN param value format string. On erro this will be empty. - */ - function mki_p_CN($account_id) - { - $cns = trim($GLOBALS['egw']->accounts->id2name($account_id,'account_firstname') - . ' ' - . $GLOBALS['egw']->accounts->id2name($account_id,'account_lastname')); - - return array('CN' => $cns ? $cns : ''); - } - - - /** - * Convert a horde_icalendar parsed attribute date- or date-time value - * to a unix timestamp. - * @note this is just a hack because horde_icalendar converts only date-times to utime - * @param array|string $ddtval DATE array or DATE-TIME utime string - * @return int $utime unix time of the date or date time - */ - function mke_DDT2utime($ddtval) - { - if(!is_array($ddtval)){ - // assume an already parsed(by Horde_iCalendar) date-time value - return $ddtval; - } else { - //assume a DATE, BUT WHERE DO I GET A POSSIBLE TIMEZONE FROM? - // assume user time zone (for utc use gmmktime() - return @mktime(0,0,0,$ddtval['month'],$ddtval['mday'],$ddtval['year']); - } - } - - - /** - * Convert a unix timestamp to a 6 field hash array in the current active timezone - * - * This is basically alike the php getdate() function but with different field names - * - * The a6date array has fields as in the php getdate() function: - * - year four digit year field - * - month integer month number - * - mday integer day of month number - * - hour integer hour - * - minute integer minutes - * - second integer seconds - * - * @param int $utime a unixtimestamp assumed in utc timezone - * @return array The date in a6date in local timezone format. - */ - function utimetoa6($utime) - { - $t=getdate($utime); - return array('hour' => $t['hours'], 'minute' => $t['minutes'], - 'second' => $t['seconds'],'month' => $t['mon'], - 'mday' => $t['mday'],'year' => $t['year']); - } - - - /** - * Convert a 6 field hash array in the current active timezone to a unix timestamp. - * - * This is basically the inverseof php getdate() function. - * - * The a6date array has fields as in the php getdate() function: - * - year four digit year field - * - month integer month number note: mon, not month!! - * - mday integer day of month number - * - hour integer hour - * - minute integer minutes - * - second integer seconds - * - * @param array $a6 The date in a6date in local timezone format. - * @return int a unixtimestamp assumed in utc timezone - */ - function a6toutime ($a6) - { - return mktime($a6['hour'],$a6['minute'],$a6['second'], - $a6['month'],$a6['mday'],$a6['year']); - } - - - /** - * Convert the egw person id and its participant status into - * an ATTENDEE value and parameterslist - * - * The resulting value of the ATTENDEE field will be in CAL_ADDRESS type format. - * The resulting parameterlist may contain fields of the following: - * - ROLE={CHAIR|REQ-PARTICIPANT|OPT-PARTICIPANT|NON-PARTICIPANT} - * this parameter is NOT used by eGW atm. - * - RSVP={TRUE|FALSE} - * resonse is expected, not set in eGW then status will have value U. - * - PARTSTAT={NEEDS-ACTION|ACCEPTED|DECLINED|TENTATIVE|DELEGATED| - * COMPLETED|IN-PROGRESS} everything from delegated is NOT used by eGW atm. - * - CUTYPE={INDIVIDUAL|GROUP|RESOURCE|ROOM|UNKNOWN} only GROUP or INDIVIDUAL - * are produced atm. - * - * @param int $pid egw id of a participant - * @param array $partstat egw particpant status of person with $uid - * @param int $owner_id id of the owner of the todo or event (needed to set the CHAIR) - * @return array ($val,$params) list with value and parameter-array for ATTENDEE property - * @note no error handling atm - */ - function mki_vp_4ATTENDEE($pid,$partstat,$owner_id) - { - $atdval = $this->mki_v_CAL_ADDRESS($pid); - // first parameter - $atdpars = $this->mki_p_CN($pid); - $atdpars['ROLE'] = ($pid == $owner_id) ? 'CHAIR' : 'REQ-PARTICIPANT'; - $atdpars['RSVP'] = $partstat == 'U' ? 'TRUE' : 'FALSE'; - $atdpars['CUTYPE'] = $GLOBALS['egw']->accounts->get_type($uid) == 'g' - ? 'GROUP' : 'INDIVIDUAL'; - $atdpars['PARTSTAT'] = $this->partstatus_egw2ical[$partstat]; - - return array($atdval,$atdpars); - } - - - /** - * Make a value of type RECUR for a ical RRULE property - * - * A simple example: - * ( RRULE) : (FREQ=MONTHLY;COUNT=10;INTERVAL=2) - * here the first part between parenthesis is property and the - * second is a value of type RECUR - * - * @param string $recur_type the type of recurrence frequence we have - * @param mixed $recur_data Todo describe this parameter... - * @param int $recur_interval Todo describe this parameter... - * @param utime $recur_enddate the final date that the recurrence ends - * @return string ($recurval) a value format as RECUR for the RRULE property - * (if a time is set) - */ - function mki_v_RECUR($recur_type,$recur_data,$recur_interval,$recur_start,$recur_enddate) - { - $recur = array(); - $recurval ='FREQ=' . $this->recur_egw2ical[$recur_type]; - - switch ($recur_type) { - case MCAL_RECUR_WEEKLY: - $days = array(); - foreach($this->recur_days as $did => $day) { - if ($recur_data & $did) - $days[] = strtoupper(substr($day,0,2)); - } - $recur['BYDAY'] = implode(',',$days); - break; - case MCAL_RECUR_MONTHLY_MDAY: // date of the month: BYMONTDAY={1..31} - $recur['BYMONTHDAY'] = (int) date('d',$recur_start); - break; - case MCAL_RECUR_MONTHLY_WDAY: // weekday of the month: BDAY={1..5}{MO..SO} - $recur['BYDAY'] = (1 + (int) ((date('d',$recur_start)-1) / 7)) - . strtoupper(substr(date('l',$recur_start),0,2)); - break; - } - - if ($recur_interval > 1) - $recur['INTERVAL'] = $recur_interval; - - if ($recur_enddate){ - -// $expdt= $this->hi->_exportDateTime($recur_enddate); -// error_log('EXPORT UNTIL=' . $recur_enddate . ' expdDT:' . $expdt); - - $recur['UNTIL'] = $this->hi->_exportDateTime($recur_enddate); - } - foreach($recur as $parnam => $parval){ - $recurval .= ';' . $parnam . '=' . $parval; - } - return $recurval; - } - - /** - * Make a value (commasep string of dates) for the EXDATE property - * - * In the conversion you can chose between a commastring of DATES or DATE-TIMES - * @param array $recur_exceptions list with utime exception dates - * @param boolean $dtmode if true generate DATE-TIME dates else DATES - * @return array ($exdval, $exdparams) a list with the value and parameters generated - */ - function mki_vp_4EXDATE($recur_exceptions,$dtmode=false) - { - $days = array(); - foreach($recur_exceptions as $day) { - $days[] = date('Ymd',$day); - } - - $exdparams = array(); - if(!$dtmode) - $exdparams['VALUE'] = 'DATE'; - return array( implode(',',$days), $exdparams); - } - - /** - * Convert DDT possible DATE|DATE-TIME params and a value commalist - * into an array of utime dates. - * - * Some examples - *
    -	   * ex1: ...;VALUE=DATE:20060123,20060124
    -	   * ex2: ...:20060118T101500Z,20060119T1000Z
    -	   * ex3: ...:VALUE=DATE-TIME:20060118T101500Z,20060119T1000Z
    -	   * 
    - * @note unfortunately horde_icalendar will parse ex1 into an array of - * array(month => .. , mday => .. , year=> ) - * @param array $dvals list of dates - * @return array $udays list with the days from the input list in utime format - */ - function mke_EXDATEpv2udays($params, $dvals) - { - //$exdays = (!is_array($dvals)) ? $dvals : array($dvals); - $exdays = $dvals; - - if (count($exdays) < 1) - return false; -//error_log('EXDAYS params=' . print_r($params,true)); -//error_log('EXDAYS exploded=' . print_r($exdays,true)); - if($params['VALUE'] == 'DATE'){ - // list is in awful horde DATE mode - // convert the date somehow to udays - $udays = array(); - foreach ($exdays as $day){ - $udays[] = $this->mke_DDT2utime($day); - } - } else { - // assume list is in DT mode - $udays = &$exdays; - } - - return $udays; - } - - - - /** - * Convert a RECUR value into the corresponding egw recur fields. - * - * A value of type RECUR (for a ical RRULE property) is parsed - * into the 4 related egw fields. Fields unfilled stay false A - * simple example: ( RRULE) : - * (FREQ=MONTHLY;COUNT=10;INTERVAL=2) here the first - * part between parenthesis is property and the second is a - * value of type RECUR - * - * @bug RECUR: MONTHLY;BYMONTHDAY, only ok if startdate is also on this MONTHDAY - * egw problem. - * - * @todo RECUR: COUNT=xx;WEEKLY;BYDAY, may miss the last occurence, if not started - * on a BYDAY day: to be fixed! prio=low - * - * @todo RECUR: YEARLY seems only to support the most basic variant?? To be checked! - * - * @author JVL (required some thinking..) - * @param string $recur RECUR type value of RRULE - * @param mixed $rstart start date in UTC format - * @return array $rar a assoc array with keys: 'recur_type', 'recur_data', 'recur_interval' - * and 'recur_enddate'. On error: false - * @note the class var @ref $hi is used as auxiliary Horde_iCalendar object - */ - function mke_RECUR2rar($recur,$rstart) - { - $ustart = $this->mke_DDT2utime($rstart); - - // a6sd is in Icalsrv usertime - $a6sd = $this->utimetoa6($ustart); - -// error_log('IMPORT RECURVAL=' . $recur . 'ustart=' .$ustart); - - - $r_data = 0; - $dow =array(); // for weekly count calc - $r_type = $r_interval = $r_end = $r_count = false; - - $type = preg_match('/FREQ=([^;: ]+)/i',$recur,$matches) - ? $matches[1] : $recur[0]; - if ($type == false) - return false; - - // vCard 2.0 values for all types - if (preg_match('/UNTIL=([0-9TZ]+)/',$recur,$matches)) - $r_end = $this->hi->_parseDateTime($matches[1]); - - if (preg_match('/INTERVAL=([0-9]+)/',$recur,$matches)) - $r_interval = (int) $matches[1]; - - // with count given we must calculate r_end - if (preg_match('/COUNT=([0-9]+)/',$recur,$matches)){ - $r_count = (int) $matches[1]; - // count calculation auxvars - $c_interval = ($r_interval) ? $r_interval : 1; //interval - $c_count = ($r_count - 1)*$c_interval; - } - - switch($type) { - - case 'W': - case 'WEEKLY': - $days = array(); - if(preg_match('/W(\d+) (.*) (.*)/',$recur, $recurMatches)) { // 1.0 - $r_interval = $recurMatches[1]; - $c_interval = $r_interval; - $days = explode(' ',trim($recurMatches[2])); - } elseif (preg_match('/BYDAY=([^;: ]+)/',$recur,$recurMatches)) { // 2.0 - $days = explode(',',$recurMatches[1]); - } - if ($days) { - foreach($this->recur_days as $mid => $day) { - if (in_array(strtoupper(substr($day,0,2)), $days)){ //WAS ERROR IN BOICAL!! - $r_data |= $mid; - } - } - $r_type = MCAL_RECUR_WEEKLY; - } - // --------- r_end calculation from COUNT and BYDAYs --- - if ($r_count) { - $c_count = ($r_count - 1)*$c_interval; - foreach($days as $wdd){ - $dow[] = $this->dowseqid[$wdd]; - } - sort($dow); - $ustart_seqid = $this->dowseqid[strtoupper(substr(date('D',$ustart),0,2))]; - // find index of start day 0.. - $sdi = 0; //in case start is not on a byday - foreach($dow as $i) { - if ($dow[$i] == $ustart_seqid){ // hope start is on byday - $sdi = $i; break; - } elseif($dow[$i] >= $ustart_seqid){ // else next byday - $sdi = $i; break; - } - } - $edi = $sdi + $c_count; // end day index - $dur = 0; // duration until end in days - $wic = count($dow); // week indexes count - $dur = 7 * floor($edi / $wic) + $dow[($edi % $wic)]; - $dur -= $dow[$sdi]; - $a6sd['mday'] += intval($dur); - $r_end = $this->a6toutime($a6sd); - // destroy $a6sd here.. - -//error_log('count=' . $c_count .'sdi=' . $sdi . ' edi=' . $edi . -// ' wic=' .$wic . ' dur=' .$dur . ' r_end=' . $r_end); - } - break; - - case 'D': // 1.0 - if(!preg_match('/D(\d+) (.*)/',$recur, $recurMatches)) - break; - $c_interval = $r_interval = $recurMatches[1]; - $r_end = $this->hi->_parseDateTime($recurMatches[2]); - // fall-through - - case 'DAILY': // 2.0 - $r_type = MCAL_RECUR_DAILY; - if ($r_count) { // count calc is still experimental! - $c_count = ($r_count - 1)*$c_interval; - $a6sd['mday'] += $c_count; - $r_end = $this->a6toutime($a6sd); - } - break; - - case 'MONTHLY': - $r_type = strstr($recur,'BYDAY') ? - MCAL_RECUR_MONTHLY_WDAY : MCAL_RECUR_MONTHLY_MDAY; - // break; - //fall through - - case 'M': - if(preg_match('/MD(\d+) (.*)/',$recur, $recurMatches)) { - $r_type = MCAL_RECUR_MONTHLY_MDAY; - $c_interval = $r_interval = $recurMatches[1]; - } elseif(preg_match('/MP(\d+) (.*) (.*) (.*)/',$recur, $recurMatches)) { - $r_type = MCAL_RECUR_MONTHLY_WDAY; - $c_interval = $r_interval = $recurMatches[1]; - } - // - if ($r_count) { // count calc is still experimental! - switch ($r_type) { - case MCAL_RECUR_MONTHLY_MDAY: - // error_log('DOING MOTNHLY MDAY'); Egw doesnot handle this special, see todo - $c_count = ($r_count - 1)*$c_interval; // maybe changed because 1.0 found - $a6sd['month'] += $c_count; - $r_end = $this->a6toutime($a6sd); - break; - - case MCAL_RECUR_MONTHLY_WDAY: - $c_count = ($r_count - 1)*$c_interval; // maybe changed because 1.0 found - // startday - $dowsd = date('w',$this->a6toutime($a6sd)); // day of week for sd - //end day, first try - $a6ed = array_diff($a6sd,array()); $a6ed['month'] += $c_count; - //day1 of startmonth - $a6smd1 = array_diff($a6sd,array()); $a6smd1['mday'] = 1; - $dowsmd1 = date('w',$this->a6toutime($a6smd1)); // day of week for smd1 - - //startdate as day of 5week segment, anchored on and afer smd1 - $do5wsegsd = $dowsmd1 + $a6sd['mday']; - if($dowsmd1 > $dowsd) - $do5wsegsd -= 7; - - $a6ed['mday'] =1; - $dowemd1 = date('w',$this->a6toutime($a6ed)); // endmonthday1 as day of week - $a6ed['mday'] = $do5wsegsd - $dowemd1; - if($dowemd1 > $dowsd) - $a6ed['mday'] += 7; - -// error_log('dowsd='. $dowsd . ' dowsmd1='. $dowsmd1 . ' do5wsegsd=' . $do5wsegsd . -// ' dowemd1='. $dowemd1 . ' edmday=' . $a6ed['mday'] ); - $r_end = $this->a6toutime($a6ed); - break; - } - } - break; - - - case 'Y': // 1.0 - if(!preg_match('/YM(\d+) (.*)/',$recur, $recurMatches)) - break; - $c_interval = $r_interval = $recurMatches[1]; - // fall-through - - case 'YEARLY': // 2.0 - $r_type = MCAL_RECUR_YEARLY; - if ($r_count) { // count calc is still experimental! - // is there only this BYMONTHDAY support? - $c_count = ($r_count - 1)*$c_interval; // maybe changed because 1.0 found - $a6sd['year'] += $c_count; - $r_end = $this->a6toutime($a6sd); - } - break; - } - - return array('recur_type' => $r_type, - 'recur_data' => $r_data, - 'recur_interval' => $r_interval, - 'recur_enddate' => $r_end ); - } - - - /** - * Parse a CAL_ADDRESS and try to find the associated egw person_id - * @param string $attrval CAL_ADDRESS type value string - * @return int|false $pid associated (by email) egw pid. On error: false. - */ - function mke_CAL_ADDRESS2pid($attrval) - { - if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attrval,$matches) && - ($pid = $GLOBALS['egw']->accounts->name2id(strtolower($matches[1]),'account_email'))){ - return $pid; - } else { - return false; - } - } - - /** - * Parse a CAL_ADDRESS and PARAMS to find the CN name and email - * @param string $aval CAL_ADDRESS type value string - * @param array $aparams parameters for a ATTENDEE - * @return array $cneml assoc array with 'cn' and 'mailto' field - */ - function mke_ATTENDEE2cneml($aval,$aparams) - { - $cneml = array('cn' => '', 'mailto' => ''); - - // if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$aval,$matches)) - // lets be a bit more relaxed here (rfc1378).. - // try for "Fnam Lnam " first - if (preg_match('/MAILTO:([^<]+)<([@.a-z0-9_-]+)>/i',$aval,$matches)){ - $cneml['cn'] = $matches[1]; - $cneml['mailto'] = $matches[2]; - // try for second - }elseif (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$aval,$matches)){ - $cneml['mailto'] = $matches[1]; - } - // a CN from the params overrules one from the mailto - if(isset($aparams['CN'])) - $cneml['cn'] = $aparams['CN']; - - return $cneml; - } - - /** - * Update an egw event description with a list of nonegw participants. - * - * note: this is a adhoc solution, preferably the nonegw participants - * should be added automatically to the addressbook - * @param string &$edescription the participants are append to this string as - * a string formatted ([cn: name:mailto: eml] [] ... ) - * @param array &$ne_participants array of the non egw participants as - * ('cn' =>, 'mailto' =>) pairs - * @return true - */ - function upde_nonegwParticipants2description(&$edescription,&$ne_participants) - { - $edescription.= "\n - non egw participants:\n("; - $neplist = array(); - foreach ($ne_participants as $nep){ -// $li = '[cn:' . $nep['cn']; -// $li .= ($nep['mailto']) ? ';mailto:' . $nep['mailto'] .']' : ']'; - $li = '[' . $nep['cn']; - $li .= ($nep['mailto']) ? '<' . $nep['mailto'] .'>]' : ']'; - - $neplist[]= $li; - } - $edescription .= implode("\n",$neplist) . ')'; - - return true; - } - - - /** - * Search a ical parameterlist for possible setting for a egw participant status. - * - * Parse the params array to find a PARTSTAT param, convert this to - * a egw partstatus (may occur e.g. in ATTENDEE params) - * @param array $params params of e.g. an ical ATTENDEE field - * @return array|false $epartstatus egw term for particpant status if detected else false - */ - function mke_params2partstat($params) - { - if (!isset($params['PARTSTAT'])) - return false; - - return $this->partstatus_ical2egw[strtoupper($params['PARTSTAT'])]; - } - - /** - * Add (append) an new attribute (aka field) to the vevent. - * - * @param horde_iCalendar_vevent $vevent obj to which the attribute is added - * @param string $aname name for the new attribute - * @param mixed $avalue value for the new attribute - * @param array $aparams parameters for the new attribute - * @return true - */ - function addAttributeOntoVevent(&$vevent,$aname,&$avalue,&$aparams) - { - - // it appears that translation->convert() can translate an array - // (that is: the values!, not the keys though) - // so lets apply it to the avalue and aparams, that should be enough! -// error_log('n:' . $aname . 'v:' . $avalue); - $valueData = - $GLOBALS['egw']->translation->convert($avalue, - $GLOBALS['egw']->translation->charset(), - 'UTF-8'); - $paramData = - $GLOBALS['egw']->translation->convert( $aparams, - $GLOBALS['egw']->translation->charset(), - 'UTF-8'); -// error_log('n:' . $aname . 'v:' . $valueData); - $vevent->setAttribute($aname, $valueData, $paramData); - $options = array(); - // JVL:is this really needed? - if (is_string($valueData)){ - -// // JVL: TEMPORARY SWITCHED OFF... TURN ON AGAIN!!! -// if(!(in_array($aname, array('RRULE'))) -// && preg_match('/([\000-\012\015\016\020-\037\075])/',$valueData)) { -// $options['ENCODING'] = 'QUOTED-PRINTABLE'; -// } - - if( (preg_match('/([\177-\377])/',$valueData))) { - $options['CHARSET'] = 'UTF-8'; - } - } - $vevent->setParameter($aname, $options); - - return true; - } - - - /** - * Convert egw alarm info to a ical VALARM object. - * - * Make a VALARM object form data in $alarms and $utstart (in utc) - * and with $vevent as container - * @param array &$alarm a single egw alarm array to be used - * @param horde_object &$vcomp that will be the container for the valarm - * mostly vevent or vtodo. - * @param array &$veExportFields list with fields that may get imported - * @return horde_iCalendar_valarm|false valarm object or, on error, false. - */ - function mki_c_VALARM(&$alarm, &$vcomp, $utstart,&$veExportFields){ - -// error_log('export comp-alarm-field:' . print_r($alarm,true)); - - $valarm = Horde_iCalendar::newComponent('VALARM',$vevent); - - //try first an offset - if($durtime = -$alarm['offset']){ - $valarm->setAttribute('TRIGGER', - $durtime, - array('VALUE' => 'DURATION', - 'RELATED' => 'START')); - // no success then try a date-time - } elseif($dtime = $alarm['time']){ - $valarm->setAttribute('TRIGGER', - $ddtime, - array('VALUE' => 'DATE-TIME')); - } else{ - $valarm = null; - return false; - } - $vcomp->addComponent($valarm); - - return $valarm; - } - - - /** - * Update the egw alarms array with info from a VALARM - * @param array &$alarms the the egw alarms array to be updated - * @param horde_iCalendar_valarm $valarm ref to the valarm component to be updated - * @param int $user_id the user that will own the alarms found - * @param array &$veImportFields with fields that may get imported - * @return true - */ - function upde_c_VALARM2alarms(&$alarms,&$valarm,$user_id,&$veImportFields){ - - // lets see what supported veImportFields we can get from the valarm - foreach($valarm->_attributes as $vattr) { - // $vattrval = $GLOBALS['egw']->translation->convert($vattr['value'],'UTF-8'); - // handle only supported fields - if(!in_array('VALARM/' . $vattr['name'], $veImportFields)) - continue; - - switch($vattr['name']) { - case 'TRIGGER': - $vtype = (isset($vattr['params']['VALUE'])) - ? $vattr['params']['VALUE'] : 'DURATION'; //default type - switch ($vtype) { - case 'DURATION': - $alarms[] = array('offset' => -$vattr['value']); - break; - case 'DATE-TIME': - $alarms[] = array('time' => $vattr['value']); - break; - default: - // we should also do ;RELATED=START|END - error_log('VALARM/TRIGGER: unsupported value type:' . $vtype); - } - break; -// case 'ACTION': -// break; -// case 'DISPLAY': -// break; - - default: - error_log('VALARM field:' .$vattr['name'] .':' - . print_r($vattrval,true) . ' HAS NO CONVERSION YET'); - } - } -// error_log('updated alarms to:' . print_r($alarms,true)); - return true; - } - - - - - - - } - -?> \ No newline at end of file diff --git a/egwical/inc/class.vcalinfolog.inc.compat.php b/egwical/inc/class.vcalinfolog.inc.compat.php deleted file mode 100644 index 19d81d5694..0000000000 --- a/egwical/inc/class.vcalinfolog.inc.compat.php +++ /dev/null @@ -1,72 +0,0 @@ -ei =& CreateObject('egwical.egwical'); - // $this->wkobj =& $this->ei->addRsc($this); - // or the fast, shortcut, road for knowingly experts only: - $this->wkobj =& CreateObject('egwical.boinfolog_vtodos'); - $this->wkobj->setRsc($this); - - if ($this->wkobj == false){ - error_log('boical constructor: couldnot add bocal resource to egwical: FATAL'); - return false; - } - } - - - // now implement the compatibility methods, that are all moved to egwical! - - function exportVTODO($_taskID, $_version) - { - return $this->wkobj->exportVTODO($_taskID, $_version); - } - - function importVTODO(&$_vcalData, $_taskID=-1) - { - return $this->wkobj->importVTODO($_vcalData, $_taskID); - } - - -// function setSupportedFields($_productManufacturer='file', $_productName='') -// { -// return $this->wkobj->setSupportedFields($_productManufacturer, $_productName); -// } - - } -?>