initial import of egwical module

contributed by Jan v. Lieshout
This commit is contained in:
Lars Kneschke 2006-02-20 10:49:29 +00:00
parent 84c8db4e8b
commit fc41cacfd9
10 changed files with 3720 additions and 0 deletions

View File

@ -0,0 +1,95 @@
/*! \page pageegwicalbugandtodo BUGS and TODOS
<PRE>
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)
</PRE>
*/

View File

@ -0,0 +1,183 @@
/*! \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:
<ul>
<li>
DATETIME (DT) (described) event.
In this case the occurence of the event is described as taking place
on a certain day and within that day on a certain time. E.g. <code>
mon 12 june 2006 at 12:33 GMT+001 </code> Examples are: <i>the
opening of the Olympic games</i>.
DT events are thus absolute (in global world time) anchored in time:
everyone watching live TV, anywhere in the world can see it happen
at that same moment described by DT.
Result is that a DT described event will occur at different hours or
even days in different timezones. (Some people have to watch the
Olympic game openings at night..)
<li>DATE (D) (described) events
These events donot happen at an absolute global time but are only
well defined relative to a timezone.
A good example is "your birthday" or "new years day".
In principle this could be either <i>whole day</i> events (like
birthdays) or events timed within days (like the "easter sunday
service at 12:00").
</ul>
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:
<ul>
<li>RE.0, REI.0 <b> Egw and EgwIcal try to handle DATE-TIME events in UTC.</b>
Or of course, equivalently, some (time + UTC offset)-pair description.
</ul>
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:
- <b> RE.1 (I am not 100% sure:) Egw tries to handle DATE events relative to
the UserInterface its timezone: the so called USER-TIME.</b>
and for EgwIcal:
- <b> REI.1 EgwIcal tries to handle DATE events relative to
the UserInterface timezone (USER-TIME) of the IcalSrv proces of the
logged in user.</b>
and keep in mind:
- <b> REI.2 EgwIcal currently will only import or export DATE events
for <i>whole day</i> events or for <i>occurence settings of recurrent
events</i> </b>
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 <b> a "timezoned" DATETIME
is just interpreted as relative to the USERTIME of the IcalSrv
process: any timezone info is neglected</b>.
Clearly this can be wrong in many occasions: thus this should be
considered a BUG!
@subsection subsecdimp DATE formatted data import handling.
<b>DATE formatted data is ust interpreted as relative to the USERTIME of the IcalSrv
process: any timezone info is neglected</b>.
@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:
<ol>
<li> first the START and (if there UNTIL) dates are converted to UTC
dates. Then these are converted to the IcalSrv locale setting.
<li> 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.
</ol>
@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:
<ul>
<li> the START date, if in DATE-TIME format is converted to UTC.
If it is in DATE format exported right away, thus being in the
IcalSrv local timezone: <b>this may be wrong, but best possible!</b>.
<li> If there, an UNTIL date this is exported as UTC
</ul>
Hope this explains things a bit.
Jan
*/

View File

@ -0,0 +1,182 @@
/*! @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:
<code> ......
</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:
<code> ......
</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:
<code> ......
</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:
<code> ......
</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:
<code> ......
</code>
@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:
- <code>bocalupdate_vevents</code> for manipulating calendar events as
VEVENTS.
- <code>boinfolog_vtodos</code> for manipulating infolog tasks as VTODOS.
Future subclasses might be:
- <code>???_vjournals</code> for manipulating .... as VJOURNALS.
- <code>bocal_vfreebusies</code> for manipulating calendar data as
VFREEBUSY elements.
- <code>addressbook_vcard</code> for manipulating addressdata data as
VCARDS.
- <code>???_vnotes</code> 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
<code>X-DELETE</code> or <code>_DELETED_</code> 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 <code>infolog/inc/class.vcalinfolog.inc.php</code> resp.
<code>calendar/inc/class.boical.inc.php</code>
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
*/

Binary file not shown.

View File

@ -0,0 +1,205 @@
/*!
\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: <code><>- </code> or <code>.</code> . means member of,
<code><- </code> means subclass of
Classes:
<PRE>
MealCooker <- SoupCooker; MealCooker <- MeatCooker;
MealCooker <>- SoupCooker; MealCooker <>- MeatCooker;
</PRE>
Methods:
<PRE>
MealCooker->cook(); SoupCooker->boil(); MeatCooker->bake();
</PRE>
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):
<PRE>
$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);
</PRE>
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
<PRE>
$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);
</PRE>
and then you can serve boths parts in one go with:
<PRE>
$mymealck->serve();
</PRE>
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:
<PRE>
$mymealck = New MealCooker;
$mymeatck = New MeatCooker;
$mysoupck = New SoupCooker;
$mymealck->addMeatHandler($mymeatck);
$mymealck->addSoupHandler($mysoupck);
</PRE>
And <code> $mymealck->cook([$apiec_of_meat,$avolum_of_soup]) </code> can then done by
just calling:
<PRE>
$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);
</PRE>
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:
<PRE>
$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
</PRE>
if we also want to export some VTODOs this goes as follows:
<PRE>
$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);
</PRE>
add 2) the same egw elements from 1) are now first collected in
egwical and then as a whole exported.
<PRE>
$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
</PRE>
if we also want to add some VTODOs this goes as follows:
<PRE>
$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
</PRE>
and finally export the whole iCalendar
<PRE>
$ei->export()
</PRE>
----------
*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,69 @@
<?php
/**
* @file
* eGroupWare - compatibility replacement for file calendar/inc/class.boical.inc.php
* to start using the new egwical routines.
*
* http://www.egroupware.org *
* @author Jan van Lieshout *
* -------------------------------------------- *
* 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. *
* @version 0.9.00
\**************************************************************************/
/* THIS CLASS IS JUST HERE FOR BACKWARD COMPATIBILITY */
/* in future you should rewrite ical handling using the egwical class */
// require_once EGW_SERVER_ROOT.'/egwicalsrv/inc/calendar/class.bovevents.inc.php';
class boical extends bocalupdate
{
// introduce auxilliary egwical object
var $ei;
// introduce a worker obj (also accessible via $ei but this is shorter..)
var $wkobj;
function boical()
{
bocalupdate::bocalupdate(); // call superclass constructor
error_log("warning class: calendar.boical call DEPRECATED," .
"\nplease rewrite your code to use egwical class" .
"\n now temporary code fix used ");
// The longer road, using egwicals cleverness:
$this->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);
}
}
?>

View File

@ -0,0 +1,561 @@
<?php
/**
*@file
* eGroupWare - iCalendar VTODOS conversion, import and export for egw infolog
* application.
*
* http://www.egroupware.org *
* @author Jan van Lieshout *
* based on class.boical.inc.php and on class.vcalinfolog.inc.php
* originals written by Lars Kneschke <lkneschke@egroupware.org> *
* -------------------------------------------- *
* 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 <b>IMPORTANT</b> rewrite bovtodos to handle uid_matching analogous to bovevents
*
* @package egwical
* @author Jan van Lieshout <jvl (at)xs4all.nl> This version.
* @author Lars Kneschke <lkneschke@egroupware.org> (parts of reused code)
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de> (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);
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,72 @@
<?php
/**
* @file
* eGroupWare - compatibility replacement for file infolog/inc/class.vcalinfolog.inc.php
* to start using the new egwical routines.
*
* http://www.egroupware.org *
* @author Jan van Lieshout *
* -------------------------------------------- *
* 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. *
* @package egwical
* @version 0.9.02
\**************************************************************************/
/* THIS CLASS IS JUST HERE FOR BACKWARD COMPATIBILITY */
/* in future you should rewrite ical handling using the egwical class */
//require_once EGW_SERVER_ROOT.'/icalsrv/inc/infolog/class.bovtodos.inc.php';
class vcalinfolog extends boinfolog
{
// introduce auxilliary egwical object
var $ei;
// introduce a worker obj (also accessible via $ei but this is shorter..)
var $wkobj;
function vcalinfolog()
{
bovtodos::bovtodos(); // call superclass constructor
error_log("class: infolog.vcalinfolog DEPRECATED," .
"\nplease use in future class: infolog.bovtodos \n now auto delegating ");
error_log("warning class: vcalinfolog.vcalinfolog call DEPRECATED," .
"\nplease rewrite your code to use egwical class" .
"\n now temporary code fix used ");
// The longer road, using egwicals cleverness:
// $this->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);
// }
}
?>