diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/CHANGES b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/CHANGES new file mode 100644 index 00000000..2c82d57e --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/CHANGES @@ -0,0 +1,15 @@ +Changelog for qCal, since Version 0.0.2 +--------------------------------------------- + +January 14, 2010: v0.0.2 +-------------------- + + * Integrated qCal_DateTime sub-components into the library. + * Removed old qCal_Date component (replaced by qCal_DateTime, qCal_DateV2, + which has been renamed qCal_Date, and qCal_Time) + * Added full documentation for the library at http://qcal.lukevisinoni.com/ as + well as inside the "docs" sub-folder. + * Added this changelog. + * Added README and VERSION files. + +-- End of Changes -- diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/LICENSE b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/LICENSE new file mode 100644 index 00000000..fc8a5de7 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/README b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/README new file mode 100644 index 00000000..cc1ccb9d --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/README @@ -0,0 +1,33 @@ +qCal iCalendar Library for PHP5, by Luke Visinoni +------------------------------------------------------------------------------- + +qCal is an object-oriented, open-source iCalendar library for PHP5. +It is released under the LGPL license. + +Homepage: http://qcal.lukevisinoni.com/ +Documentation: http://qcal.lukevisinoni.com/doku.php?id=start +Mailing List: http://groups.google.com/group/qcal +Bugs: http://code.google.com/p/qcal/issues/list +Repository: https://qcal.googlecode.com/svn/ + +Installation +------------ + +This library requires no installation. At least not in the traditional sense. +Simply upload to a path within PHP's include path and include +"lib/autoload.php". + +For more info, visit http://qcal.lukevisinoni.com/doku.php?id=installation + +Show your support! +------------------ + +If you use the library and would like to show your support, feel free to make a +donation. I accept donations via PayPal. You can send me money at the following +e-mail address. + + luke.visinoni@gmail.com + +I didn't write this library to make money, but I do appreciate donations. In +fact, those users who donate to the project are by far cooler than those who +don't. I like donors better than non-donors. LOL! \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/VERSION b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/VERSION new file mode 100644 index 00000000..38c4ba3f --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/VERSION @@ -0,0 +1 @@ +qCal-v0.0.2 \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/autoload.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/autoload.php new file mode 100644 index 00000000..ee8f1243 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/autoload.php @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component.php new file mode 100644 index 00000000..7e28ffd7 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component.php @@ -0,0 +1,523 @@ + '-// Some Property Id//', + * 'someotherproperty' => null, + * qCal_Property_Version(2.0), + * ), array( + * qCal_Component_Daylight(), + * )); + */ + public function __construct($properties = array(), $components = array()) { + + foreach ($components as $component) { + // if value is an array, then each value inside of it will be a component + if ($component instanceof qCal_Component) { + $this->attach($component); + } + else throw new qCal_Exception_InvalidComponent('The second argument is optional, but if provided, must be an array of components'); + } + foreach ($properties as $name => $value) { + // if value is an array, then each value inside of it will be a property + if (is_array($value)) { + foreach ($value as $val) { + if ($val instanceof qCal_Property) { + $this->addProperty($val); + } else { + $this->addProperty($name, $val); + } + } + } else { + if ($value instanceof qCal_Property) { + $this->addProperty($value); + } else { + $this->addProperty($name, $value); + } + } + } + // I think it would make more sense to do validation at render time. That way you don't have + // to have all of the required components and properties when you instantiate. Also, that way + // components don't need to be aware of eachother until render time (or until validate() is called + // explicitly). @todo + // $this->validate(); + + } + /** + * @todo (lazy load functionality) Check that this is a valid component. This method is sort of lazy-loaded. It only gets called + * if the user has requested data that requires validation and the component has not been validated already. + * @todo Shouldn't this loop over children and validate them too? Maybe optionally? + */ + public function validate() { + + // if we're missing any required properties and they have no default, throw an exception + $properties = array(); + foreach ($this->getProperties() as $property) { + if (is_array($property)) { + foreach ($property as $prop) { + $properties[] = $prop->getName(); + } + } else { + $properties[] = $property->getName(); + } + } + $missing = array_diff($this->requiredProperties, array_unique($properties)); + foreach ($missing as $propertyname) { + // the property factory will throw an exception if it's passed a null value for a property with no default + try { + $property = qCal_Property::factory($propertyname, null); + $this->addProperty($property); + } catch (qCal_Exception_InvalidPropertyValue $e) { + // if that's the case, catch the exception and throw a missing property exception + throw new qCal_Exception_MissingProperty($this->getName() . " component requires " . $propertyname . " property"); + } + } + // this allows per-component validation :) + $this->doValidation(); + + } + /** + * Returns the component name + * @return string + */ + public function getName() { + + return $this->name; + + } + /** + * Returns true if this component can be attached to $component + * I'm sure there's a better way to do this, but this works for now + */ + public function canAttachTo(qCal_Component $component) { + + if (in_array($component->getName(), $this->allowedComponents)) return true; + + } + /** + * Attach a component to this component (alarm inside event for example) + * @todo There may be an issue with the way this is done. When parsing a file, if a component + * or property with a tzid comes before its corresponding vtimezone component, an exception + * will be thrown. I'm don't think the RFC specifies that requirement (that timezone components + * must come before their corresponding tzids) + * @todo Sub-components such as Vevent need to be able to access the main vcalendar object + * for several reasons. + * - If a vtodo has a tzid, it needs to be able to determine that the corresponding + * vtimezone component is available. + * - If components need to relate to eachother, they can only find eachother through + * the main vcalendar object. + * - Freebusy time can only be determined by polling all components in the main vcalendar + * object. + * - More to come probably + */ + public function attach(qCal_Component $component) { + + if (!$component->canAttachTo($this)) { + throw new qCal_Exception_InvalidComponent($component->getName() . ' cannot be attached to ' . $this->getName()); + } + $component->setParent($this); + // make sure if a timezone is requested that it is available... + $timezones = $this->getTimezones(); + $tzids = array_keys($timezones); + // we only need to check if tzid exists if we are attaching something other than a timezone... + if (!($component instanceof qCal_Component_Vtimezone)) { + foreach ($component->getProperties() as $pname => $properties) { + $pname = strtoupper($pname); // probably redundant... + foreach ($properties as $property) { + switch ($pname) { + case "TZID": + $tzid = $property->getValue(); + if (!array_key_exists($tzid, $tzids)) { + throw new qCal_Exception_MissingComponent('TZID "' . $tzid . '" not defined'); + } + break; + } + $params = $property->getParams(); + foreach ($params as $param => $val) { + $param = strtoupper($param); // probably redundant... + switch ($param) { + case "TZID": + $tzid = $val; + if (!array_key_exists($tzid, $tzids)) { + throw new qCal_Exception_MissingComponent('TZID "' . $tzid . '" not defined'); + } + break; + } + } + } + } + } + $this->children[$component->getName()][] = $component; + + } + /** + * Set the parent of this component + * @todo I'm not sure this will suffice. See the attach method for reasoning behind this. + */ + public function setParent(qCal_Component $component) { + + $this->parent = $component; + + } + /** + * Get the parent of this component (if there is one) + */ + public function getParent() { + + return $this->parent; + + } + /** + * The only thing I need this for so far is the parser, but it may come in handy for the facade as well + */ + static public function factory($name, $properties = array()) { + + if (empty($name)) return false; + // capitalize + $component = ucfirst(strtolower($name)); + $className = "qCal_Component_" . $component; + $fileName = str_replace("_", DIRECTORY_SEPARATOR, $className) . ".php"; + qCal_Loader::loadFile($fileName); + $class = new $className($properties); + return $class; + + } + + /** + * I'm not sure how this should work. Not sure if it should be setProperty, + * addProperty, both? Because properties on some components can be set multiple + * times, while some properties have multiple values. :( I am trying to consider + * a case where somebody needs to open a calendar, change a few properties on a + * component (change event time for instance). I think the way I'll handle properties + * that can be set multiple times is I'll create a method do delete properties based + * on values, parameters, etc. since they don't really have IDs. So I tihnk I'll go + * with addProperty :) + */ + public function addProperty($property, $value = null, $params = array()) { + + if (!($property instanceof qCal_Property)) { + $property = qCal_Property::factory($property, $value, $params); + } + if (!$property->of($this)) { + throw new qCal_Exception_InvalidProperty($this->getName() . " component does not allow " . $property->getName() . " property"); + } + if (!$property->allowMultiple()) { + unset($this->properties[$property->getName()]); + } + $this->properties[$property->getName()][] = $property; + + } + /** + * Returns property of this component by name + * + * @todo Since the same property can appear in a component more than once, this method + * doesn't make that much sense unless it returns all of the instances of the property + * @return array of qCal_Property + */ + public function getProperty($name) { + + $name = strtoupper($name); + if ($this->hasProperty($name)) { + return $this->properties[$name]; + } + + } + + /** + * Returns true if this component contains a property of $name + * + * @return boolean + */ + public function hasProperty($name) { + + $name = strtoupper($name); + return isset($this->properties[$name]); + + } + + /** + * Returns true if this component contains a property of $name + * + * @return boolean + */ + public function hasComponent($name) { + + $name = strtoupper($name); + return isset($this->children[$name]); + + } + /** + * Returns the child component requested + */ + public function getComponent($name) { + + $name = strtoupper($name); + if ($this->hasComponent($name)) { + return $this->children[$name]; + } + + } + + /* + public function clearProperties() { + + $this->properties = array(); + + } + + public function clearChildren() { + + $this->children = array(); + + } + */ + + public function getProperties() { + + return $this->properties; + + } + + public function getChildren() { + + return $this->children; + + } + + /** + * Gets the parent-most component in the tree. I would really like to come up + * with a cleaner way to access other components from within a component, but oh well. + */ + public function getRootComponent() { + + $parent = $this; + while (!($parent instanceof qCal_Component_Vcalendar)) { + if (!$parent->getParent()) break; + $parent = $parent->getParent(); + } + return $parent; + + } + + /** + * Renders the calendar, by default in icalendar format. If you pass + * in a renderer, it will use that instead + * + * @return mixed Depends on the renderer + * @todo Would it make more sense to pass the component to the renderer, or the renderer + * to the component? I'm not sure components should know about rendering. + */ + public function render(qCal_Renderer $renderer = null) { + + $this->validate(); + if (is_null($renderer)) $renderer = new qCal_Renderer_iCalendar(); + return $renderer->render($this); + + } + + /** + * Output the icalendar component as a string (render it) + */ + public function __toString() { + + return $this->render(); + + } + + /** + * getFreeBusyTime + * Looks through all of the data in the calendar and returns a qCal_Component_Vfreebusy object + * with free/busy time from $startdate to $enddate. The component will contain all components, but some + * may have their transparency set to "transparent". + * @todo This cannot be finished until recurring events are finished, since free/busy does not allow + * recurrence rules, each instance of a recurrence would need to be calculated out and passed into the free/busy + * component, so that the component would contain concrete instances of each event recurrence. + */ + public function getFreeBusyTime() { + + $root = $this->getRootComponent(); + foreach ($root->getChildren() as $children) { + foreach ($children as $child) { + // now get the object's free/busy time + } + } + + } + /** + * getTimeZones + */ + public function getTimezones() { + + $tzs = array(); + $root = $this->getRootComponent(); + foreach ($root->getChildren() as $children) { + foreach ($children as $child) { + // if the child is a vtimezone, add it to the results + // @todo make sure that tzid is available, throw exception otherwise + if ($child instanceof qCal_Component_Vtimezone) { + $tzid = $child->getTzid(); + $tzid = strtoupper($tzid); + $tzs[$tzid] = $child; + } + } + } + return $tzs; + + } + /** + * Get a specific timezone by tzid + * @param string The timezone identifier + */ + public function getTimezone($tzid) { + + $tzid = strtoupper($tzid); + $root = $this->getRootComponent(); + $timezones = $root->getTimezones(); + if (array_key_exists($tzid, $timezones)) { + return $timezones[$tzid]; + } + return false; + + } + /** + * Allows for components to get and set property values by calling + * qCal_Component::getPropertyName() and qCal_Component::setPropertyName('2.0') where propertyName is the property name + * to be set and $val is the property value. + * This is just a convenience facade, it isn't going to be used within the library as much as by end-users + * @todo I can't decided whether to maybe get rid of the facade methods at least for now since some properties + * can potentially return multiple values and that makes the interface inconsistent + */ + public function __call($method, $params) { + + $firstthree = substr($method, 0, 3); + $name = substr($method, 3); + if ($firstthree == "get") { + // if property is allowed multiple times, an array is returned, otherwise just the one component + if ($this->hasProperty($name)) { + $property = $this->getProperty($name); + if (!$property[0]->allowMultiple()) { + return $property[0]; + } else { + return $property; + } + } + } elseif ($firstthree == "set") { + $value = isset($params[0]) ? $params[0] : null; + $params = isset($params[1]) ? $params[1] : array(); + $property = qCal_Property::factory($name, $value, $params); + $this->addProperty($property); + } elseif ($firstthree == "add") { + // add property type + $property = qCal_Property::factory($name, $params); + $this->addProperty($property); + return $this; + } + // throw exception here? + // throw new qCal_Exception(); + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Daylight.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Daylight.php new file mode 100644 index 00000000..ea361a07 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Daylight.php @@ -0,0 +1,367 @@ +getAction(); + switch(strtoupper($action->getValue())) { + case "AUDIO": + // action, trigger (already covered by parent constructor) + // attach can only occur once + $attach = $this->getProperty('ATTACH'); + if (count($attach) > 1) { + throw new qCal_Exception_InvalidProperty('VALARM audio component can contain one and only one ATTACH property'); + } + break; + case "DISPLAY": + // action, trigger, description + if (!$this->hasProperty('DESCRIPTION')) { + throw new qCal_Exception_MissingProperty("DISPLAY VALARM component requires DESCRIPTION property"); + } + break; + case "EMAIL": + // action, description, trigger, summary + if (!$this->hasProperty('DESCRIPTION')) { + throw new qCal_Exception_MissingProperty("EMAIL VALARM component requires DESCRIPTION property"); + } + if (!$this->hasProperty('SUMMARY')) { + throw new qCal_Exception_MissingProperty("EMAIL VALARM component requires SUMMARY property"); + } + break; + case "PROCEDURE": + // action, attach, trigger + $attach = $this->getProperty('ATTACH'); + if (count($attach) > 1) { + throw new qCal_Exception_InvalidProperty('VALARM procedure component can contain one and only one ATTACH property'); + } + if (count($attach) < 1) { + throw new qCal_Exception_MissingProperty("PROCEDURE VALARM component requires ATTACH property"); + } + break; + } + if ($this->hasProperty('DURATION')) { + if (!$this->hasProperty('REPEAT')) { + throw new qCal_Exception_MissingProperty("VALARM component with a DURATION property requires a REPEAT property"); + } + } + if ($this->hasProperty('REPEAT')) { + if (!$this->hasProperty('DURATION')) { + throw new qCal_Exception_MissingProperty("VALARM component with a REPEAT property requires a DURATION property"); + } + } + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vcalendar.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vcalendar.php new file mode 100644 index 00000000..4478e94a --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vcalendar.php @@ -0,0 +1,59 @@ +getProperties(); + $propnames = array_keys($properties); + if (in_array('DTEND', $propnames) && in_array('DURATION', $propnames)) { + throw new qCal_Exception_InvalidProperty('DTEND and DURATION cannot both occur in the same VEVENT component'); + } + if (in_array('DTSTART', $propnames)) { + $dtstart = $this->getProperty('dtstart'); + $dtstart = $dtstart[0]; + // check that if dtstart is a DATE that dtend is a DATE + if ($dtstart->getType() == 'DATE') { + if (in_array('DTEND', $propnames)) { + $dtend = $this->getProperty('dtend'); + $dtend = $dtend[0]; + if ($dtend->getType() != 'DATE') { + throw new qCal_Exception_InvalidProperty('If DTSTART property is specified as a DATE property, so must DTEND'); + } + } + } + // check that dtstart comes before dtend + if (in_array('DTEND', $propnames)) { + $dtend = $this->getProperty('dtend'); + $dtend = $dtend[0]; + $startdate = strtotime($dtstart->getValue()); + $enddate = strtotime($dtend->getValue()); + if ($startdate > $enddate) { + throw new qCal_Exception_InvalidProperty('DTSTART property must come before DTEND'); + } + } + } + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vfreebusy.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vfreebusy.php new file mode 100644 index 00000000..003a67da --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vfreebusy.php @@ -0,0 +1,137 @@ +getChildren(); + if (!array_key_exists('DAYLIGHT', $children) && !array_key_exists('STANDARD', $children)) { + throw new qCal_Exception_MissingComponent('Either a STANDARD or DAYLIGHT component is required within a VTIMEZONE component'); + } + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vtodo.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vtodo.php new file mode 100644 index 00000000..702ede26 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vtodo.php @@ -0,0 +1,81 @@ + "january", + 2 => "february", + 3 => "march", + 4 => "april", + 5 => "may", + 6 => "june", + 7 => "july", + 8 => "august", + 9 => "september", + 10 => "october", + 11 => "november", + 12 => "december", + ); + /** + * @var array The month in a two-dimensional array (picture a calendar) + */ + protected $monthMap = array(); + /** + * Class constructor + * @param int The year of this date + * @param int The month of this date + * @param int The day of this date + */ + public function __construct($year = null, $month = null, $day = null, $rollover = false) { + + $this->setDate($year, $month, $day, $rollover); + + } + /** + * Set the date of this class + * The date defaults to now. If any part of the date is missing, it will default to whatever "now"'s + * date portion is. For instance, if the year provided is 2006 and no other portion is given, it will + * default to today's month and day, but in the year 2006. If, for any reason the date defaults to a + * nonsensical date, an exception will be thrown. For instance, if you specify the year as 2006, and + * the current date is february 29th, an exception will be thrown because the 29th of February does not + * exist in 2006. + * @param int The year of this date + * @param int The month of this date + * @param int The day of this date + * @throws qCal_Date_Exception_InvalidDate + */ + protected function setDate($year = null, $month = null, $day = null, $rollover = false) { + + $now = getdate(); + if (is_null($year)) { + $year = $now['year']; + } + if (is_null($month)) { + $month = $now['mon']; + } + if (is_null($day)) { + $day = $now['mday']; + } + + $this->date = gmmktime(0, 0, 0, $month, $day, $year); + $this->dateArray = self::gmgetdate($this->date); + if (!$rollover) { + if ($this->dateArray["mday"] != $day || $this->dateArray["mon"] != $month || $this->dateArray["year"] != $year) { + throw new qCal_DateTime_Exception_InvalidDate("Invalid date specified for qCal_Date: \"{$month}/{$day}/{$year}\""); + } + } + + // @todo Look into how much more efficient it might be to call date() only once and then break apart the result... + $formatString = "d|D|j|l|N|S|w|z|W|F|m|M|n|t|L|o|y|Y|c|r|U"; + $keys = explode("|", $formatString); + $vals = explode("|", gmdate($formatString, $this->date)); + $this->dateArray = array_merge($this->dateArray, array_combine($keys, $vals)); + return $this; + + } + /** + * This is a factory method. It allows you to create a date by string or by another date object (to make a copy) + */ + public static function factory($date) { + + if (is_integer($date)) { + // @todo Handle timestamps + } + if (is_string($date)) { + if (!$timestamp = strtotime($date)) { + // if unix timestamp can't be created throw an exception + throw new qCal_Date_Exception_InvalidDate("Invalid or ambiguous date string passed to qCal_Date::factory()"); + } + } + + $date = self::gmgetdate($timestamp); + $newdate = gmmktime(0, 0, 0, $date['mon'], $date['mday'], $date['year']); + $newdate = self::gmgetdate($newdate); + return new qCal_Date($newdate['year'], $newdate['mon'], $newdate['mday']); + + } + /** + * Set the format that should be used when calling either __toString() or format() without an argument. + * @param string $format + */ + public function setFormat($format) { + + $this->format = (string) $format; + return $this; + + } + /** + * Formats the date according to either the existing $this->format, or if the $format arg is passed + * in, it uses that. + * @param string The format that is to be used (according to php's date function). Only date-related metacharacters work. + */ + public function format($format) { + + $escape = false; + $meta = str_split($format); + $output = array(); + foreach($meta as $char) { + if ($char == '\\') { + $escape = true; + continue; + } + if (!$escape && array_key_exists($char, $this->dateArray)) { + $output[] = $this->dateArray[$char]; + } else { + $output[] = $char; + } + // reset this to false after every iteration that wasn't "continued" + $escape = false; + } + return implode($output); + + } + + /** + * Getters + * The next dozen or so methods are just your standard getters for things such as month, day, year, week day, etc. + */ + + /** + * Get the month (number) of this date + * @return integer A number between 1 and 12 inclusively + */ + public function getMonth() { + + return $this->dateArray["mon"]; + + } + /** + * Get the month of this date + * @return string The actual name of the month, capitalized + */ + public function getMonthName() { + + return $this->dateArray["month"]; + + } + /** + * Get the day of the month + * @return integer A number between 1 and 31 inclusively + */ + public function getDay() { + + return $this->dateArray["mday"]; + + } + /** + * Get the day of the year + * @return integer A number between 0 and 365 inclusively + */ + public function getYearDay($startFromOne = false) { + + $yearDay = $this->dateArray["yday"] + (integer) $startFromOne; + return $yearDay; + + } + /** + * Find how many days until the end of the year. + * For instance, if the date is December 25th, there are 6 days until the end of the year + */ + public function getNumDaysUntilEndOfYear() { + + $yearday = $this->getYearDay(true); + return $this->getNumDaysInYear() - $yearday; + + } + /** + * Get how many months until the end of the year + * @todo This is really rudimentary. There is more to this, but this works for now... + */ + public function getNumMonthsUntilEndOfYear() { + + return 12 - $this->getMonth(); + + } + /** + * Get the amount of days in the year (365 unless it is a leap-year, then it's 366) + */ + public function getNumDaysInYear() { + + return ($this->isLeapYear()) ? 366 : 365; + + } + /** + * Return the first day of the month as a qCal_Date object + * @return qCal_Date The first day of the month + */ + public function getFirstDayOfMonth() { + + return new qCal_Date($this->getYear(), $this->getMonth(), 1); + + } + /** + * Return the last day of the month as a qCal_Date object + * @return qCal_Date The last day of the month + */ + public function getLastDayOfMonth() { + + $lastday = $this->format("t"); + return new qCal_Date($this->getYear(), $this->getMonth(), $lastday); + + } + /** + * Get the number of days until the end of the month + */ + public function getNumDaysUntilEndOfMonth() { + + return $this->getNumDaysInMonth() - $this->getDay(); + + } + /** + * Get the year + * @return integer The year of this date, for example 1999 + */ + public function getYear() { + + return $this->dateArray["year"]; + + } + /** + * Get the day of the week + * @return integer A number between 0 (for Sunday) and 6 (for Saturday). + */ + public function getWeekDay() { + + return $this->dateArray["wday"]; + + } + /** + * Get the day of the week + * @return string The actual name of the day of the week, capitalized + */ + public function getWeekDayName() { + + return $this->dateArray["weekday"]; + + } + /** + * Get the amount of days in the current month of this year + * @return integer The number of days in the month + */ + public function getNumDaysInMonth() { + + return $this->dateArray["t"]; + + } + /** + * Get the week of the year + * @return integer The week of the year (0-51 I think) + * @todo This is not accurate if the week start isn't monday. I need to adjust for that + */ + public function getWeekOfYear() { + + return $this->dateArray["W"]; + + } + /** + * Get how many weeks until the end of the year + * @todo This is really rudimentary. There is more to this, but this works for now... + */ + public function getWeeksUntilEndOfYear() { + + return 52 - $this->getWeekOfYear(); + + } + /** + * Determine if this is a leap year + */ + public function isLeapYear() { + + return (boolean) $this->dateArray["L"]; + + } + /** + * Get a unix timestamp for the date + * @return integer The amount of seconds since unix epoch (January 1, 1970 UTC) + */ + public function getUnixTimestamp() { + + return $this->dateArray[0]; + + } + + /** + * Date magic + * This component is capable of doing some really convenient things with dates. + * It is capable of determining things such as how many days until the end of the year, + * which monday of the month it is (ie: third monday in february), etc. + */ + + /** + * Determine the number or Tuesdays (or whatever day of the week this date is) since the + * beginning or end of the month. + * @param integer $xth A positive or negative number that determines which weekday of the month we want + * @param string|integer $weekday Either Sunday-Saturday or 0-6 to specify the weekday we want + * @param string|integer $month Either January-December or 1-12 to specify the month we want + * @param integer $year A valid year to specify which year we want + */ + public function getXthWeekdayOfMonth($xth, $weekday = null, $month = null, $year = null) { + + $negpos = substr($xth, 0, 1); + if ($negpos == "+" || $negpos == "-") { + $xth = (integer) substr($xth, 1); + } else { + $negpos = "+"; + } + + if (is_null($weekday)) { + $weekday = $this->getWeekday(); + } + + if (ctype_digit((string) $weekday)) { + if (!array_key_exists($weekday, $this->weekdays)) { + throw new qCal_Date_Exception_InvalidWeekday("\"$weekday\" is not a valid weekday."); + } + } else { + $weekday = strtolower($weekday); + if (!in_array($weekday, $this->weekdays)) { + throw new qCal_Date_Exception_InvalidWeekday("\"$weekday\" is not a valid weekday."); + } + $wdays = array_flip($this->weekdays); + $weekday = $wdays[$weekday]; + } + + if (is_null($month)) { + $month = $this->getMonth(); + } + + if (ctype_digit((string) $month)) { + if (!array_key_exists($month, $this->months)) { + throw new qCal_Date_Exception_InvalidMonth("\"$month\" is not a valid month."); + } + } else { + $month = strtolower($month); + if (!in_array($month, $this->months)) { + throw new qCal_Date_Exception_InvalidMonth("\"$month\" is not a valid month."); + } + $mons = array_flip($this->months); + $month = $mons[$month]; + } + + if (is_null($year)) { + $year = $this->getYear(); + } + + if (!ctype_digit((string) $year) || strlen($year) != 4) { + throw new qCal_Date_Exception_InvalidYear("\"$year\" is not a valid year."); + } + + // now, using the year, month and numbered weekday, we need to find the actual day of the month... + $firstofmonth = new qCal_Date($year, $month, 1); + $numdaysinmonth = $firstofmonth->getNumDaysInMonth(); + $numweekdays = 0; // the number of weekdays that have occurred (in the loop) + $foundday = false; + if ($negpos == "+") { + $day = 1; + $wday = $firstofmonth->getWeekday(); + // while we are in the current month, loop + while ($day <= $numdaysinmonth) { + // if the specified weekday == the current week day in the loop + if ($weekday == $wday) { + $numweekdays++; + if ($numweekdays == $xth) { + // break out of the loop, we've found the right day! yay! + $foundday = $day; + break; + } + } + if ($wday == 6) $wday = 0; // reset to Sunday after Saturday + else $wday++; + $day++; + } + } else { + $day = $numdaysinmonth; + $lastofmonth = $firstofmonth->getLastDayOfMonth(); + $wday = $lastofmonth->getWeekday(); + while ($day >= 1) { + if ($weekday == $wday) { + $numweekdays++; + if ($numweekdays == $xth) { + // break out of the loop, we've found the right day! yay! + $foundday = $day; + break; + } + } + if ($wday == 0) $wday = 6; // reset to Saturday after Sunday + else $wday--; + $day--; + } + } + + if ($foundday && checkdate($month, $day, $year)) { + $date = new qCal_Date($year, $month, $day); + } else { + if ($day == 32) { + throw new qCal_DateTime_Exception_InvalidDate("You have specified an incorrect number of days for qCal_Date::getXthWeekdayOfMonth()"); + } else { + throw new qCal_DateTime_Exception_InvalidDate("You have entered an invalid date."); + } + } + + return $date; + + } + /** + * Determine the number or Tuesdays (or whatever day of the week this date is) since the + * beginning or end of the year. + */ + public function getXthWeekdayOfYear($xth, $weekday = null, $year = null) { + + $negpos = substr($xth, 0, 1); + if ($negpos == "+" || $negpos == "-") { + $xth = (integer) substr($xth, 1); + } else { + $negpos = "+"; + } + + if (is_null($weekday)) { + $weekday = $this->getWeekday(); + } + + if (ctype_digit((string) $weekday)) { + if (!array_key_exists($weekday, $this->weekdays)) { + throw new qCal_Date_Exception_InvalidWeekday("\"$weekday\" is not a valid weekday."); + } + } else { + $weekday = strtolower($weekday); + if (!in_array($weekday, $this->weekdays)) { + throw new qCal_Date_Exception_InvalidWeekday("\"$weekday\" is not a valid weekday."); + } + $wdays = array_flip($this->weekdays); + $weekday = $wdays[$weekday]; + } + + if (is_null($year)) { + $year = $this->getYear(); + } + + if (!ctype_digit((string) $year) || strlen($year) != 4) { + throw new qCal_Date_Exception_InvalidYear("\"$year\" is not a valid year."); + } + + // now find the specified day by counting either forwards or backwards to the day in question + $firstofyear = new qCal_Date($year, 1, 1); + $numdaysinyear = ($firstofyear->isLeapYear()) ? 366 : 365; + $numweekdays = 0; // the number of weekdays that have occurred within the loop + $found = false; // whether or not the specified day has been found + if ($negpos == "+") { + // count forward + // loop over every day of every month looking for the right one + $day = 1; + $wday = $firstofyear->getWeekDay(); + while ($day <= $numdaysinyear) { + // if the specified weekday == the current week day in the loop + if ($weekday == $wday) { + $numweekdays++; + if ($numweekdays == $xth) { + // break out of the loop, we've found the right day! yay! + $found = $day; + break; + } + } + if ($wday == 6) $wday = 0; // reset to Sunday after Saturday + else $wday++; + $day++; + } + } else { + // count backward + $lastofyear = new qCal_Date($year, 12, 31); + // count forward + // loop over every day of every month looking for the right one + $day = $numdaysinyear; + $wday = $lastofyear->getWeekDay(); + while ($day >= 1) { + // if the specified weekday == the current week day in the loop + if ($weekday == $wday) { + $numweekdays++; + if ($numweekdays == $xth) { + // break out of the loop, we've found the right day! yay! + $found = $day; + break; + } + } + if ($wday == 0) $wday = 6; // reset to Saturday after Sunday + else $wday--; + $day--; + } + } + + // @todo: Can't use checkdate here, so find another validation method... + if ($found) { + $date = new qCal_Date($year, 1, $found, true); // takes advantage of the rollover feature :) + } else { + throw new qCal_DateTime_Exception_InvalidDate("You have specified an incorrect number of days for qCal_Date::getXthWeekdayOfYear()"); + } + + return $date; + + } + + /** + * Magic methods + */ + + /** + * Output the date as a string. Options are as follows: + * @return string The formatted date + */ + public function __toString() { + + return $this->format($this->format); + + } + + /** + * Static methods + */ + + /** + * Because PHP does not provide a gmgetdate() function, I borrowed this one from the + * comments on the getdate() function page on php.net + * @param integer The timestamp to use to create the date + */ + public static function gmgetdate($timestamp = null) { + + $k = array('seconds','minutes','hours','mday','wday','mon','year','yday','weekday','month',0); + return(array_combine($k, split(":", gmdate('s:i:G:j:w:n:Y:z:l:F:U', is_null($timestamp) ? time() : $timestamp)))); + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime.php new file mode 100644 index 00000000..3de5929e --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime.php @@ -0,0 +1,190 @@ +setDate($date); + $this->setTime($time); + + } + /** + * Generate a datetime object via string + * @todo Should this accept qCal_Date and qCal_DateTime objects? + */ + public static function factory($datetime, $timezone = null) { + + if (is_null($timezone) || !($timezone instanceof qCal_Timezone)) { + // @todo Make sure this doesn't cause any issues + // detect if we're working with a UTC string like "19970101T180000Z", where the Z means use UTC time + if (strtolower(substr($datetime, -1)) == "z") { + $timezone = "UTC"; + } + $timezone = qCal_Timezone::factory($timezone); + } + // get the default timezone so we can set it back to it later + $tz = date_default_timezone_get(); + // set the timezone to GMT temporarily + date_default_timezone_set("GMT"); + + // handles unix timestamp + if (is_integer($datetime) || ctype_digit((string) $datetime)) { + $timestamp = $datetime; + } else { + // handles just about any string representation of date/time (strtotime) + if (is_string($datetime) || empty($datetime)) { + if (!$timestamp = strtotime($datetime)) { + // if unix timestamp can't be created throw an exception + throw new qCal_DateTime_Exception("Invalid or ambiguous date/time string passed to qCal_DateTime::factory()"); + } + } + } + + if (!isset($timestamp)) { + throw new qCal_DateTime_Exception("Could not generate a qCal_DateTime object."); + } + + list($year, $month, $day, $hour, $minute, $second) = explode("|", gmdate("Y|m|d|H|i|s", $timestamp)); + + // set the timezone back to what it was + date_default_timezone_set($tz); + + return new qCal_DateTime($year, $month, $day, $hour, $minute, $second, $timezone); + + } + /** + * Set the date component + */ + protected function setDate(qCal_Date $date) { + + $this->date = $date; + + } + /** + * Set the time component + */ + protected function setTime(qCal_Time $time) { + + $this->time = $time; + + } + /** + * Get time portion as object + */ + public function getTime() { + + return $this->time; + + } + /** + * Get date portion as object + */ + public function getDate() { + + return $this->date; + + } + /** + * Get unix timestamp + */ + public function getUnixTimestamp($useOffset = true) { + + return $this->date->getUnixTimestamp() + $this->time->getTimestamp($useOffset); + + } + /** + * Set the format to use when outputting as a string + */ + public function setFormat($format) { + + $this->format = (string) $format; + return $this; + + } + /** + * Format the date/time using PHP's date() function's meta-characters + * @todo It's obvious I need to find a better solution to formatting since I have repeated this method + * in three classes now... + */ + public function format($format) { + + $escape = false; + $meta = str_split($format); + $output = array(); + foreach($meta as $char) { + if ($char == '\\') { + $escape = true; + continue; + } + if (!$escape && $this->convertChar($char) != $char) { + $output[] = $this->convertChar($char); + } else { + $output[] = $char; + } + // reset this to false after every iteration that wasn't "continued" + $escape = false; + } + return implode($output); + + } + /** + * convert character + */ + protected function convertChar($char) { + + $char = $this->date->format($char); + $char = $this->time->format($char); + $char = $this->time->getTimezone()->format($char); + return $char; + + } + /** + * Output date/time as string + */ + public function __toString() { + + return $this->format($this->format); + + } + /** + * Get date/time as UTC + */ + public function getUtc($humanReadable = false) { + + if ($humanReadable) return gmdate('Y-m-d', $this->date->getUnixTimestamp()) . gmdate('\TH:i:s\Z', $this->time->getTimestamp()); + else return gmdate('Ymd', $this->date->getUnixTimestamp()) . gmdate('\THis\Z', $this->time->getTimestamp()); + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Duration.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Duration.php new file mode 100644 index 00000000..36cc1de0 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Duration.php @@ -0,0 +1,115 @@ + 604800, 'D' => 86400, 'H' => 3600, 'M' => 60, 'S' => 1); + /** + * Duration in seconds + */ + protected $duration; + /** + * If this is negative, this will be a minus symbol. Positive doesn't need a sign, so it is just null + */ + protected $sign; + /** + * Constructor + */ + public function __construct($duration = null) { + + $this->setDuration($duration); + + } + /** + * Set duration - accepts an integer (amount of seconds) or an icalendar-formatted duration string + */ + public function setDuration($duration) { + + $duration = strtoupper($duration); + // if plus or minus precedes number, remove it set in class + if (preg_match("/^[+-]/", (string) $duration, $matches)) { + if ($matches[0] == "-") $this->sign = "-"; + $duration = str_split($duration); + array_shift($duration); + $duration = implode("", $duration); + } + if (ctype_digit($duration)) { + $this->duration = $duration; + } else { + // convert value to duration in seconds + preg_match('/^P([0-9]+[W])?([0-9]+[D])?T?([0-9]+[H])?([0-9]+[M])?([0-9]+[S])?$/i', $duration, $matches); + // remove first element (which is just entire the matched string) + array_shift($matches); + $seconds = 0; + foreach ($matches as $duration) { + if (empty($duration)) continue; + $seconds += $this->calculateSeconds($duration); + } + $this->duration = $seconds; + } + return $this; + + } + /** + * Pass in a string like "15W" or "1D" and this will return how many seconds are in it + */ + protected function calculateSeconds($duration) { + + $amnt = preg_replace("/[^0-9]/i", "", $duration); + $inc = preg_replace("/[^A-Z]/i", "", $duration); + return $this->durations[$inc] * $amnt; + + } + /** + * Converts seconds to an icalendar-formatted duration string + */ + public function toICal() { + + $total = $this->duration; + $return = "P"; + // this is why order is important when defining $this->durations + foreach ($this->durations as $dur => $amnt) { + // how many "weeks" are in the value? + $quotient = (int) ($total / $amnt); + // get the remainder of the division + $remainder = $total - ($quotient*$amnt); + // now if we got a whole number as quotient, add this duration to the return string + if ($quotient) { + // if this is the first "time" duration, add the required T char + if ($dur == "H" || $dur == "M" || $dur == "S") { + if (!strpos($return, "T")) $return .= "T"; + } + $return .= $quotient . $dur; + } + $total = $remainder; + } + return $this->sign . $return; + + } + /** + * @todo Should this be the string representation? I dont really know. + */ + public function __toString() { + + return $this->toICal(); + + } + /** + * Get duration in seconds + */ + public function getSeconds() { + + return (integer) $this->sign . $this->duration; + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Exception.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Exception.php new file mode 100644 index 00000000..a688c0dc --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Exception.php @@ -0,0 +1,7 @@ +start = $start; + $this->end = $end; + if ($this->getSeconds() < 0) { + throw new qCal_DateTime_Exception_InvalidPeriod("The start date must come before the end date."); + } + + } + /** + * Converts to how many seconds between the two. because this is the smallest increment + * used in this class, seconds are used to determine other increments + */ + public function getSeconds() { + + return $this->end->getUnixTimestamp() - $this->start->getUnixTimestamp(); + + } + /** + * Returns start date + */ + public function getStart() { + + return $this->start; + + } + /** + * Returns end date + */ + public function getEnd() { + + return $this->end; + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur.php new file mode 100644 index 00000000..8e7877be --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur.php @@ -0,0 +1,452 @@ + 'Monday', + 'TU' => 'Tuesday', + 'WE' => 'Wednesday', + 'TH' => 'Thursday', + 'FR' => 'Friday', + 'SA' => 'Saturday', + 'SU' => 'Sunday', + ); + /** + * @var qCal_Date The start date/time of the recurrence + */ + protected $dtstart; + /** + * @var string frequency of the recurrence + */ + protected $freq; + /** + * @var qCal_Date The date/time which the recurrence ends + */ + protected $until; + /** + * @var integer The amount of recurrences + */ + protected $count; + /** + * @var integer Interval of recurrence (for every 3 days, "3" would be the interval) + */ + protected $interval; + /** + * @var integer|array An integer between 0 and 59 (for multiple, set as an array) + */ + protected $bysecond; + /** + * @var integer|array An integer between 0 and 59 (or an array of integers for multiple) + */ + protected $byminute; + /** + * @var integer|array An integer or array of integers between 0 and 23 + */ + protected $byhour; + /** + * @var string If present, represents the nth occurrence of a specific day within monthly or yearly + * so it can be something like +1MO (or simply 1MO) for the first monday within the month, whereas + * -1MO for the last monday of the month. Or it can be simply MO to represent every monday within the month + */ + protected $byday; + /** + * @var integer|array An integer or array of integers. -31 to -1 or 1 to 31. -10 would mean the tenth to last + * day of the month. [1,5,-5] would be the 1st, 5th, and 5th to last days of the month + */ + protected $bymonthday; + /** + * @var integer|array An integer or array of integers. -366 to -1 or 1 to 366. -306 represents the 306th to last + * day of the year (March 1st) + */ + protected $byyearday; + /** + * @var integer|array An integer or array of integers. -53 to -1 or 1 to 53. Only valid for yearly rules. + * 3 represents the third week of the year. + */ + protected $byweekno; + /** + * @var integer|array An integer or array of integers. 1 to 12. 3 would represent March + */ + protected $bymonth; + /** + * @var integer If present, it indicates the nth occurrence of the specific occurrence within the set of + * events specified by this recurrence rule + */ + protected $bysetpos; + /** + * @var string Must be one of the weekdays specified above (2 char). Specifies the day on which the work week + * starts. This is significant when a weekly rule has an interval greater than 1 and a byday rule part is specified. + * This is also significant when in a yearly rule when a byweekno rule part is specified. Defaults to "MO" + */ + protected $wkst = "MO"; + /** + * Constructor + * @param $freq string Must be one of the freqtypes specified above. + * @throws qCal_Date_Exception_InvalidRecur if a frequency other than those specified above is passed in + */ + public function __construct($dtstart = null) { + + $this->dtstart = is_null($dtstart) ? null : qCal_DateTime::factory($dtstart); + + } + /** + * Specifies the date when the recurrence stops, inclusively. If not present, and there is no count specified, + * then the recurrence goes on "forever". + * This is a getter as well as a setter (if no arg is supplied, it is a getter) + * @param $until string|qCal_Date|DateTime If time is specified, it must be UTC + * @throws qCal_Date_Exception_InvalidRecur + * @return self + */ + public function until($until = null) { + + if (is_null($until)) return $this->until; + if ($this->count()) throw new qCal_DateTime_Exception_InvalidRecur('A recurrence count and an until date cannot both be specified'); + $this->until = qCal_DateTime::factory($until); + return $this; + + } + /** + * Specifies the amount of recurrences before the recurrence ends. If neither this nor "until" is specified, + * the recurrence repeats "forever". + * This is a getter as well as a setter (if no arg is supplied, it is a getter) + * @param $count integer The amount of recurrences before it stops + * @throws qCal_Date_Exception_InvalidRecur + * @return self + */ + public function count($count = null) { + + if (is_null($count)) return $this->count; + if ($this->until()) throw new qCal_DateTime_Exception_InvalidRecur('A recurrence count and an until date cannot both be specified'); + $this->count = (integer) $count; + return $this; + + } + /** + * Specifies the start of the work-week, which is Monday by default + */ + public function wkst($wkst = null) { + + if (is_null($wkst)) return $this->wkst; + $abbrs = array_keys($this->weekdays); + if (!in_array($wkst, $abbrs)) throw new qCal_DateTime_Exception_InvalidRecur('"' . $wkst . '" is not a valid week day, must be one of the following: ' . implode(', ', $abbrs)); + $this->wkst = $wkst; + // @todo I wonder if re-sorting the weekdays array would help me in any way... + + } + /** + * Specifies the interval of recurrences + * This is a getter as well as a setter (if no arg is supplied, it is a getter) + * @param $interval integer The interval of recurrences, for instance every "3" days + * @throws qCal_DateTime_Exception_InvalidRecur + * @return self + */ + public function interval($interval = null) { + + if (is_null($interval)) return $this->interval; + $this->interval = (integer) $interval; + return $this; + + } + /** + * Specifies a rule which will happen on every nth second. + * This is a getter as well as a setter (if no arg is supplied, it is a getter) + * @param $second integer|array Can be an integer (or array of ints) between 0 and 59 + * @throws qCal_DateTime_Exception_InvalidRecur + * @return self + */ + public function bySecond($second = null) { + + if (is_null($second)) return $this->bysecond; + if (!is_array($second)) $second = array($second); + $this->bysecond = $second; + return $this; + + } + /** + * Specifies a rule which will happen on every nth minute + * This is a getter as well as a setter (if no arg is supplied, it is a getter) + * @param $minute integer|array Can be an integer (or array of ints) between 0 and 59 + * @throws qCal_DateTime_Exception_InvalidRecur + * @return self + */ + public function byMinute($minute = null) { + + if (is_null($minute)) return $this->byminute; + if (!is_array($minute)) $minute = array($minute); + $this->byminute = $minute; + return $this; + + } + /** + * Specifies a rule which will happen on every nth hour + * This is a getter as well as a setter (if no arg is supplied, it is a getter) + * @param $hour integer|array Can be an integer (or array of ints) between 0 and 23 + * @throws qCal_DateTime_Exception_InvalidRecur + * @return self + */ + public function byHour($hour = null) { + + if (is_null($hour)) return $this->byhour; + if (!is_array($hour)) $hour = array($hour); + $this->byhour = $hour; + return $this; + + } + /** + * Specifies a rule which will happen on whichever day is specified. For instance, "MO" would + * mean every monday. + * This is a getter as well as a setter (if no arg is supplied, it is a getter) + * Sets $this->byday into an array of arrays like array('SU' => 1) for '1SU' and array('SU' => 0) for 'SU' + * @param $day string|array Must be one of the 2-char week days specified above. Can be preceded by + * a positive or negative integer to represent, for instance, the third monday of the month (3MO) or second to last + * Sunday of the month (-2SU) + * @throws qCal_DateTime_Exception_InvalidRecur + * @return self + */ + public function byDay($day = null) { + + if (is_null($day)) { + $ret = array(); + foreach ($this->byday as $val) { + $num = (current($val) == 0) ? "" : current($val); + $ret[] = $num . key($val); + } + return $ret; + } + if (!is_array($day)) $day = array($day); + $days = array(); + foreach ($day as $d) { + // optional plus or minus followed by a series of digits as group 1 + // two-character week day as group 2 + if (preg_match('/^([+-]?[0-9]+)?(MO|TU|WE|TH|FR|SA|SU)$/', $d, $matches)) { + $num = ($matches[1] == "") ? "0" : $matches[1]; + $wday = $matches[2]; + if (substr($num, 0, 1) == "+") { + $num = substr($num, 1); + } + $days[] = array($wday => $num); + } + } + $this->byday = $days; + return $this; + + } + /** + * Specifies a rule which will happen on the month days specified. For instance, 23 would mean the 23rd of every month. + * This is a getter as well as a setter (if no arg is supplied, it is a getter) + * @param integer|array Must be between 1 and 31 or -31 to 1 (or an array of those values) + * @throws qCal_DateTime_Exception_InvalidRecur + * @return self + */ + public function byMonthDay($monthday = null) { + + if (is_null($monthday)) return $this->bymonthday; + if (!is_array($monthday)) $monthday = array($monthday); + $this->bymonthday = $monthday; + return $this; + + } + /** + * Specifies a rule which will happen on the nth day of the year + * This is a getter as well as a setter (if no arg is supplied, it is a getter) + * @param integer|array Must be between 1 and 366 or -366 to -1. + * @throws qCal_DateTime_Exception_InvalidRecur + * @return self + */ + public function byYearDay($yearday = null) { + + if (is_null($yearday)) return $this->byyearday; + if (!is_array($yearday)) $yearday = array($yearday); + $this->byyearday = $yearday; + return $this; + + } + /** + * Specifies a rule which will happen on the nth week of the year + * This is a getter as well as a setter (if no arg is supplied, it is a getter) + * @param integer|array Must be between 1 and 53 or -53 to -1. + * @throws qCal_DateTime_Exception_InvalidRecur + * @return self + */ + public function byWeekNo($weekno = null) { + + if (is_null($weekno)) return $this->byweekno; + if (!is_array($weekno)) $weekno = array($weekno); + $this->byweekno = $weekno; + return $this; + + } + /** + * Specifies a rule which will happen on the nth month of the year + * This is a getter as well as a setter (if no arg is supplied, it is a getter) + * @param integer|array Must be between 1 and 12 + * @throws qCal_DateTime_Exception_InvalidRecur + * @return self + */ + public function byMonth($month = null) { + + if (is_null($month)) return $this->bymonth; + if (!is_array($month)) $month = array($month); + $this->bymonth = $month; + return $this; + + } + /** + * Indicates the nth occurrence of the specific occurrence within the set of + * events specified by the rule. + * This is a getter as well as a setter (if no arg is supplied, it is a getter) + * @todo I don't really understand how this works... :( Figure out wtf it is for. + * @throws qCal_DateTime_Exception_InvalidRecur + * @return self + */ + public function bySetPos($setpos = null) { + + if (is_null($setpos)) return $this->bysetpos; + $this->bysetpos = (integer) $setpos; + return $this; + + } + /** + * Factory method generates the correct recur type based on the string it is passed: "yearly, weekly, etc." + * @param string The frequency type of recurrence rule you want to generate + * @param mixed The start date/time for the recurrence. Accepts anything qCal_Date accepts + */ + static public function factory($freq, $start) { + + $freq = ucfirst(strtolower($freq)); + $className = "qCal_DateTime_Recur_" . $freq; + $fileName = str_replace("_", DIRECTORY_SEPARATOR, $className) . ".php"; + qCal_Loader::loadFile($fileName); + $class = new $className($start); + return $class; + + } + /** + * Fetches instances of the recurrence rule in the given time period. Because recurrences + * could potentially go on forever, there is no way to fetch ALL instances of a recurrence rule + * other than providing a date range that spans the entire length of the recurrence. + * + * The way this will need to work is, depending on the frequency, I will find all possible + * occurrence of the rule. For instance, if this is a "monthly" rule, I'll find out which month + * to start in, then find all occurrence possible. Then narrow down by the other rules I guess. + * + * @idea Maybe I should build classes for each of the frequency types. That way I could loop over + * the object and get methods like qCal_DateTime_Recur_Monthly::isNthDay('SU') to find out what sunday + * of the month it is... stuff like that... I dunno... ? + * + * @throws qCal_DateTime_Exception_InvalidRecur + * @todo The giant switch in this method is a glaring code smell, but it works for now. I will refactor + * after version 0.1 and remove the switch (probably will implement qCal_DateTime_Recur_Yearly, qCal_DateTime_Recur_Monthly, etc.) + */ + public function getRecurrences($start, $end) { + + $start = qCal_DateTime::factory($start); + $end = qCal_DateTime::factory($end); + if ($start->getUnixTimestamp() > $end->getUnixTimestamp()) throw new qCal_DateTime_Exception_InvalidRecur('Start date must come before end date'); + if (!$this->interval) throw new qCal_DateTime_Exception_InvalidRecur('You must specify an interval'); + + $rules = array( + 'bymonth' => array(), + 'byweekno' => array(), + 'byyearday' => array(), + 'byday' => array(), + ); + + // byMonth rules + if (is_array($this->bymonth)) { + foreach ($this->bymonth as $bymonth) { + $rules['bymonth'][] = new qCal_DateTime_Recur_Rule_ByMonth($bymonth); + } + } + + // byWeekNo rules + if (is_array($this->byweekno)) { + foreach ($this->byweekno as $byweekno) { + $rules['byweekno'][] = new qCal_DateTime_Recur_Rule_ByWeekNo($byweekno); + } + } + + // byYearDay rules + if (is_array($this->byyearday)) { + foreach ($this->byyearday as $byyearday) { + $rules['byyearday'][] = new qCal_DateTime_Recur_Rule_ByYearDay($byyearday); + } + } + + // byMonthDay rules (these get applied to bymonth rules) + if (is_array($this->bymonthday)) { + foreach ($this->bymonthday as $bymonthday) { + $bmdrule = new qCal_DateTime_Recur_Rule_ByMonthDay($bymonthday); + foreach ($rules['bymonth'] as $bymonth) { + $bymonth->attach($bmdrule); + } + } + } + + // byDay rules (these get applied to bymonth rules if they exist, otherwise simply to year) + if (is_array($this->byday)) { + foreach ($this->byday as $byday) { + $bdrule = new qCal_DateTime_Recur_Rule_ByDay($byday); + if (is_array($rules['bymonth']) && !empty($rules['bymonth'])) { + foreach ($rules['bymonth'] as $bymonth) { + $bymonth->attach($bdrule); + } + } else { + $rules['byday'][] = $bdrule; + } + } + } + + // byHour rules (these get applied to each rule above) + if (is_array($this->byhour)) { + foreach ($this->byhour as $byhour) { + $bhrule = new qCal_DateTime_Recur_Rule_ByHour($byhour); + foreach ($rules as $type => $ruleset) { + foreach ($ruleset as $rule) { + $rule->attach($bhrule); + } + } + } + } + + // byMinute rules (these get applied to each rule above) + if (is_array($this->byminute)) { + foreach ($this->byminute as $byminute) { + $bmrule = new qCal_DateTime_Recur_Rule_ByMinute($byminute); + foreach ($rules as $type => $ruleset) { + foreach ($ruleset as $rule) { + $rule->attach($bmrule); + } + } + } + } + + // bySecond rules (these get applied to each rule above) + if (is_array($this->bysecond)) { + foreach ($this->bysecond as $bysecond) { + $bsrule = new qCal_DateTime_Recur_Rule_BySecond($bysecond); + foreach ($rules as $type => $ruleset) { + foreach ($ruleset as $rule) { + $rule->attach($bsrule); + } + } + } + } + + return $this->doGetRecurrences($rules, $start, $end); + + } + /** + * Each type of rule needs to determine its recurrences so this is left abstract + * to be implemented by children. + */ + abstract protected function doGetRecurrences($rules, $start, $end); + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur/Daily.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur/Daily.php new file mode 100644 index 00000000..5f6ce193 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur/Daily.php @@ -0,0 +1,10 @@ +value = $value; + + } + /** + * Attach rules to this rule. For instance, if this is a byMonth rule, then + * we can attach byDay rules like "-1SU" for the last Sunday of the month. + */ + public function attach(qCal_DateTime_Recur_Rule $rule) { + + $this->rules[] = $rule; + + } + /** + * Creates the recurrences for this rule. Left to children to do this. + */ + abstract public function getRecurrences(); + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur/Rule/ByDay.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur/Rule/ByDay.php new file mode 100644 index 00000000..0f92132c --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur/Rule/ByDay.php @@ -0,0 +1,10 @@ +format('d'); + $smonth = $start->format('m'); + $syear = $start->format('Y'); + + // end day, year, and month + $eday = $end->format('d'); + $emonth = $end->format('m'); + $eyear = $end->format('Y'); + + // loop over years, by increment + $year = $syear; + while ($year <= $eyear) { + + // if byMonth is specified... + if (count($this->byMonth())) { + // loop over each month + for ($month = 1; $month <= 12; $month++) { + // if this is the start year still and we haven't reached the start month, skip ahead + if ($year == $syear && $month < $smonth) { + continue; + } + // if this is the end year and we have passed the end month, break out of loop + if ($year == $eyear && $month > $emonth) { + break; + } + // if this is not one of the bymonths, continue as well + if (!in_array($month, $this->byMonth())) { + continue; + } + // now we need to loop over each day of the month to look for byday or bymonthday + $thismonth = new qCal_Date(); // used to determine total days in the current month + $thismonth->setDate($year, $month, 1); + $weekdays = array( + 'MO' => 0, + 'TU' => 0, + 'WE' => 0, + 'TH' => 0, + 'FR' => 0, + 'SA' => 0, + 'SU' => 0, + ); + // @todo For now this only allows 1SU, SU, but not -1SU (no negatives for now) + for ($day = 1; $day <= $thismonth->format('t'); $day++) { + $alreadyadded = false; + $date = new qCal_Date; + $date->setDate($year, $month, $day); + $date->setTime(0, 0, 0); + $wdname = strtoupper(substr($date->format('l'), 0, 2)); + // keep track of how many of each day of the week have gone by + $weekdays[$wdname]++; + // if byDay is specified... + // @todo this is inconsistent, I don't use the getter here because of its special functionality. + // I need to either remove the special functionality or not use getters elsewhere in this method + $byday = $this->byday; + if (count($byday)) { + // by day is broken into an array of arrays like array('TH' => 0), array('FR' => 1), array('MO' => -2) etc. + // with zero meaning every instance of that particular day should be included and number meaning the Nth of that day + foreach ($byday as $val) { + // if at least one of this wday has gone by... + $num = current($val); + if ($weekdays[$wdname] > 0) { + // check if it is the right week day and if a digit is specified (like 1SU) that it is checked as well + if ($wdname == key($val) && ($weekdays[$wdname] == $num || $num == 0)) { + $recurrences[] = $date; + $alreadyadded = true; + } + } + } + } + + // if byMonthDay is specified... + if (count($this->byMonthDay())) { + foreach ($this->byMonthDay() as $mday) { + // only add this day if it hasn't been added already + if ($mday == $day && !$alreadyadded) { + $recurrences[] = $date; + } + } + } + + // now loop over each hour and add hours + if (count($this->byHour())) { + $hourrecurrences = array(); + foreach ($this->byHour() as $hour) { + $new = new qCal_Date(); + $new = $new->copy($date); + $new->setTime($hour, 0, 0); + $hourrecurrences[] = $new; + } + } + + // now loop over byHours and add byMinutes + if (count($this->byMinute())) { + if (!isset($minuterecurrences)) $minuterecurrences = array(); + foreach ($this->byMinute() as $minute) { + $new = new qCal_Date(); + $new = $new->copy($date); + $new->setTime(0, $minute, 0); + } + } + + // now loop over byMinutes and add bySeconds + + } + } + } + + // if in the first year we don't find an instance, don't do the interval, just increment a year + if ($year == $syear && count($recurrences)) $year += $this->interval(); + else ($year++); + } + + // now loop over weeks to get byWeekNo + + foreach ($recurrences as $date) { + // pr($date->format("r")); + } + // exit; + + return $recurrences; + // for bymonth, it would make the most sense to loop over each month until the specified one + // is found. Then loop over each day to find its sub-rules. + + // for byweekno, it would make the most sense to loop over each week until the specified one + // is found. Then apply any sub-rules (actually I'm not sure how byhour and its ilk would be applied in this situation... need to read the rfc) + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Timezone.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Timezone.php new file mode 100644 index 00000000..ac7cb51b --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Timezone.php @@ -0,0 +1,62 @@ +format($this->format); + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Exception.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Exception.php new file mode 100644 index 00000000..b87fc565 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Exception.php @@ -0,0 +1,11 @@ +options = array( + 'searchpath' => get_include_path(), + ); + $this->options = array_merge($this->options, $options); + + } + /** + * @todo What should this accept? filename? actual string content? either? + * @todo Maybe even create a parse() for raw string and a parseFile() for a file name? + */ + public function parse($content, $lexer = null) { + + if (is_null($lexer)) { + $lexer = new qCal_Parser_Lexer_iCalendar($content); + } + $this->lexer = $lexer; + return $this->doParse($this->lexer->tokenize()); + + } + /** + * Parse a file. The searchpath defaults to the include path. Also, if the filename + * provided is an absolute path, the searchpath is not used. This is determined by + * either the file starting with a forward slash, or a drive letter (for Windows) + * @todo Throw an exception if file doesn't exist + * @todo I'm not really sure that it should default to the include path. That's not really what the include path is for, is it? + * @todo Test for path starting with a drive letter for windows (or find a better way to detect that) + */ + public function parseFile($filename) { + + // @todo This is hacky... but it works + if (substr($filename, 0, 1) == '/' || substr($filename, 0, 3) == 'C:\\') { + if (file_exists($filename)) { + $content = file_get_contents($filename); + return $this->parse($content); + } + } else { + $paths = explode(PATH_SEPARATOR, $this->options['searchpath']); + foreach ($paths as $path) { + $fname = $path . DIRECTORY_SEPARATOR . $filename; + if (file_exists($fname)) { + $content = file_get_contents($fname); + return $this->parse($content); + } + } + } + throw new qCal_Exception_FileNotFound('File cannot be found: "' . $filename . '"'); + + } + /** + * Override doParse in a child class if necessary + */ + protected function doParse($tokens) { + + $properties = array(); + foreach ($tokens['properties'] as $propertytoken) { + $params = array(); + foreach ($propertytoken['params'] as $paramtoken) { + $params[$paramtoken['param']] = $paramtoken['value']; + } + try { + $properties[] = qCal_Property::factory($propertytoken['property'], $propertytoken['value'], $params); + } catch (qCal_Exception $e) { + // @todo There should be a better way of determining what went wrong during parsing/lexing than this + // do nothing... + // pr($e); + } + } + $component = qCal_Component::factory($tokens['component'], $properties); + foreach ($tokens['children'] as $child) { + $childcmpnt = $this->doParse($child); + $component->attach($childcmpnt); + } + return $component; + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Parser/Lexer.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Parser/Lexer.php new file mode 100644 index 00000000..2de249a3 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Parser/Lexer.php @@ -0,0 +1,34 @@ +content = $content; + + } + /** + * Tokenize content into tokens that can be used to build iCalendar objects + */ + abstract public function tokenize(); + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Parser/Lexer/iCalendar.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Parser/Lexer/iCalendar.php new file mode 100644 index 00000000..7e2d9cb6 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Parser/Lexer/iCalendar.php @@ -0,0 +1,120 @@ +line_terminator = chr(13) . chr(10); + + } + /** + * Return a list of tokens (to be fed to the parser) + * @returns array tokens + */ + public function tokenize() { + + $lines = $this->unfold($this->content); + // loop through chunks of input text by separating by properties and components + // and create tokens for each one, creating a multi-dimensional array of tokens to return + $stack = array(); + foreach ($lines as $line) { + // begin a component + if (preg_match('#^BEGIN:([a-z]+)$#i', $line, $matches)) { + // create new array representing the new component + $array = array( + 'component' => $matches[1], + 'properties' => array(), + 'children' => array(), + ); + $stack[] = $array; + } elseif (strpos($line, "END:") === 0) { + // end component, pop the stack + $child = array_pop($stack); + if (empty($stack)) { + $tokens = $child; + } else { + $parent =& $stack[count($stack)-1]; + array_push($parent['children'], $child); + } + } else { + // continue component + if (preg_match('#^([^:]+):"?([^\n]+)?"?$#i', $line, $matches)) { + // @todo What do I do with empty values? + $value = isset($matches[2]) ? $matches[2] : ""; + $component =& $stack[count($stack)-1]; + // if line is a property line, start a new property, but first determine if there are any params + $property = $matches[1]; + $params = array(); + $propparts = explode(";", $matches[1]); + if (count($propparts) > 1) { + foreach ($propparts as $key => $part) { + // the first one is the property name + if ($key == 0) { + $property = $part; + } else { + // the rest are params + // @todo Quoted param values need to be taken care of... + list($paramname, $paramvalue) = explode("=", $part, 2); + $params[] = array( + 'param' => $paramname, + 'value' => $paramvalue, + ); + } + } + } + $proparray = array( + 'property' => $property, + 'value' => $value, + 'params' => $params, + ); + $component['properties'][] = $proparray; + } + } + } + return $tokens; + + } + /** + * Unfold the file before trying to parse it + */ + protected function unfold($content) { + + $return = array(); + $lines = explode($this->line_terminator, $content); + foreach ($lines as $line) { + $checkempty = trim($line); + if (empty($checkempty)) continue; + $chr1 = substr($line, 0, 1); + $therest = substr($line, 1); + // if character 1 is a whitespace character... (tab or space) + if ($chr1 == chr(9) || $chr1 == chr(32)) { + $return[count($return)-1] .= $therest; + } else { + $return[] = $line; + } + } + return $return; + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property.php new file mode 100644 index 00000000..28143973 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property.php @@ -0,0 +1,289 @@ +type) + * @todo Determine if there can be multiple params of the same name + */ + public function __construct($value = null, $params = array()) { + + if (is_null($this->name)) $this->name = $this->getPropertyNameFromClassName(get_class($this)); + foreach ($params as $pname => $pval) { + $this->setParam($pname, $pval); + } + // this must be set after parameters because the VALUE parameter can affect it + $this->setValue($value); + + } + /** + * Generates a qCal_Property class based on property name, params, and value + * which can come directly from an icalendar file + * @todo I need a way to detect INVALID properties as they are being parsed. This + * way there can be an option to NOT stop on errors. To just log and then continue. + */ + static public function factory($name, $value, $params = array()) { + + $className = self::getClassNameFromPropertyName($name); + $fileName = str_replace("_", DIRECTORY_SEPARATOR, $className) . ".php"; + try { + qCal_Loader::loadFile($fileName); + $class = new $className($value, $params); + } catch (qCal_Exception_InvalidFile $e) { + // if there is no class available for this property, check if it is non-standard + $xname = strtoupper(substr($name, 0, 2)); + // non-standard property + if ($xname == "X-") { + $class = new qCal_Property_NonStandard($value, $params, $name); + } else { + // if it's not a non-standard property, rethrow + throw $e; + } + } + return $class; + + } + /** + * Returns the property name (formatted and exactly to spec) + * @return string + */ + public function getName() { + + return $this->name; + + } + /** + * Returns the property value (as a string) + * If you want the actual object, use getValueObject() + * I wish I could just pass the object back and have php do some overloading magicness, but + * it doesn't know how :( + * @return string + */ + public function getValue() { + + return $this->value->__toString(); + + } + /** + * Just returns getValue() + */ + public function __toString() { + + return $this->getValue(); + + } + /** + * Returns raw value object (or for multi-value, an array) + * @return string + */ + public function getValueObject() { + + return $this->value; + + } + /** + * Sets the property value + * @param mixed + */ + public function setValue($value) { + + // if value sent is null and this property doesn't have a default value, + // the property can't be created, so throw an invalidpropertyvalue exception + if (is_null($value)) { + if ($this->default === false) { + // this is caught by factory and reported as a conformance error + throw new qCal_Exception_InvalidPropertyValue($this->getName() . ' property must have a value'); + } else { + $value = $this->default; + } + } + $this->value = $this->convertValue($value); + return $this; + + } + /** + * Converts a value into whatever internal storage mechanism the property uses + */ + protected function convertValue($value) { + + return qCal_Value::factory($this->getType(), $value); + + } + /** + * Returns the property type + * @return string + */ + public function getType() { + + return $this->type; + + } + /** + * Check if this is a property of a certain component. Some properties + * can only be set on certain Components. This method looks inside this + * property's $allowedComponents and returns true if $component is allowed + * + * @return boolean True if this is a property of $component, false otherwise + * @param qCal_Component The component we're evaluating + **/ + public function of(qCal_Component $component) { + + return in_array($component->getName(), $this->allowedComponents); + + } + /** + * Retreive the value of a parameter + * + * @return mixed parameter value + */ + public function getParam($name) { + + if (isset($this->params[strtoupper($name)])) { + return $this->params[strtoupper($name)]; + } + + } + /** + * Returns an array of all params + */ + public function getParams() { + + return $this->params; + + } + /** + * Set the value of a parameter + */ + public function setParam($name, $value) { + + $name = strtoupper($name); + // if value param has been passed in, change the type of this property to its value + if ($name == "VALUE") { + $value = strtoupper($value); + $this->type = $value; + } + $this->params[$name] = $value; + return $this; + + } + /** + * Determine's this property's name from the class name by adding a dash after + * every capital letter and upper-casing + * + * @return string The RFC property name + * @todo This method is flawed. The class name XLvFoo gets converted to X-L-VFOO when + * it should be X-LV-FOO + **/ + protected function getPropertyNameFromClassName($classname) { + + // determine the property name by class name + $parts = explode("_", $classname); + end($parts); + // find where capital letters are and insert dash + $chars = str_split(current($parts)); + // make a copy @todo Why make a copy? + $newchars = $chars; + foreach ($chars as $pos => $char) { + // don't add a dash for the first letter + if (!$pos) continue; + $num = ord($char); + // if character is a capital letter + if ($num >= 65 && $num <= 90) { + // insert dash + array_splice($newchars, $pos, 0, '-'); + } + } + return strtoupper(implode("", $newchars)); + + } + /** + * Determine's this property's class name from the property name + * + * @return string The property class name + **/ + protected function getClassNameFromPropertyName($name) { + + // remove dashes, capitalize properly + $parts = explode("-", $name); + $property = ""; + foreach ($parts as $part) $property .= trim(ucfirst(strtolower($part))); + // get the class, and instantiate + $className = "qCal_Property_" . $property; + return $className; + + } + /** + * Is this property allowed to be specified multiple times in a component? + * @return boolean + */ + public function allowMultiple() { + + return (boolean) $this->allowMultiple; + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Action.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Action.php new file mode 100644 index 00000000..3606d1ea --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Action.php @@ -0,0 +1,51 @@ +":Jim + * Dolittle\, ABC Industries\, +1-919-555-1234 + * + * The following is an example of this property referencing a network + * resource, such as a vCard [RFC 2426] object containing the contact + * information: + * + * CONTACT;ALTREP="http://host.com/pdi/jdoe.vcf":Jim + * Dolittle\, ABC Industries\, +1-919-555-1234 + */ +class qCal_Property_Contact extends qCal_Property { + + protected $type = 'TEXT'; + protected $allowedComponents = array('VEVENT','VTODO','VJOURNAL','VFREEBUSY'); + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Created.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Created.php new file mode 100644 index 00000000..7feb552b --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Created.php @@ -0,0 +1,45 @@ +value as $value) { + $return[] = $value->__toString(); + } + return implode(chr(44), $return); + + } + /** + * Sets the value of this property. Overwrites any previous values. Use addValue to + * add rather than overwrite. + * @todo I'm not sure I like how this is done. Eventually I will come back to it. + */ + public function setValue($value) { + + if (!is_array($value)) { + $value = array($value); + } + // parent::setValue($value); + $this->value = array(); + foreach ($value as $val) { + $this->value[] = $this->convertValue($val); + } + return $this; + + } + /** + * Add a value to the array of values (rather than overwrite) + */ + public function addValue($value) { + + $this->value[] = $this->convertValue($value); + return $this; + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/NonStandard.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/NonStandard.php new file mode 100644 index 00000000..a0b203a8 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/NonStandard.php @@ -0,0 +1,70 @@ +name = strtoupper($name); + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Organizer.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Organizer.php new file mode 100644 index 00000000..24ea907f --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Organizer.php @@ -0,0 +1,89 @@ + + * + * RELATED-TO:<19960401-080045-4000F192713-0052@host1.com> + */ +class qCal_Property_RecurrenceId extends qCal_Property { + + protected $type = 'TEXT'; + protected $allowedComponents = array('VEVENT','VTODO','VJOURNAL'); + protected $allowMultiple = true; + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Repeat.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Repeat.php new file mode 100644 index 00000000..f77d655f --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Repeat.php @@ -0,0 +1,47 @@ + (1997 9:00 AM EDT)September 2-11 + * + * Daily until December 24, 1997: + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=DAILY;UNTIL=19971224T000000Z + * + * ==> (1997 9:00 AM EDT)September 2-30;October 1-25 + * (1997 9:00 AM EST)October 26-31;November 1-30;December 1-23 + * + * Every other day - forever: + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=DAILY;INTERVAL=2 + * ==> (1997 9:00 AM EDT)September2,4,6,8...24,26,28,30; + * October 2,4,6...20,22,24 + * (1997 9:00 AM EST)October 26,28,30;November 1,3,5,7...25,27,29; + * Dec 1,3,... + * + * Every 10 days, 5 occurrences: + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5 + * + * ==> (1997 9:00 AM EDT)September 2,12,22;October 2,12 + * + * Everyday in January, for 3 years: + * + * DTSTART;TZID=US-Eastern:19980101T090000 + * RRULE:FREQ=YEARLY;UNTIL=20000131T090000Z; + * BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA + * or + * RRULE:FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1 + * + * ==> (1998 9:00 AM EDT)January 1-31 + * (1999 9:00 AM EDT)January 1-31 + * (2000 9:00 AM EDT)January 1-31 + * + * Weekly for 10 occurrences + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=WEEKLY;COUNT=10 + * + * ==> (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21 + * (1997 9:00 AM EST)October 28;November 4 + * + * Weekly until December 24, 1997 + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=WEEKLY;UNTIL=19971224T000000Z + * + * ==> (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21 + * (1997 9:00 AM EST)October 28;November 4,11,18,25; + * December 2,9,16,23 + * Every other week - forever: + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU + * + * ==> (1997 9:00 AM EDT)September 2,16,30;October 14 + * (1997 9:00 AM EST)October 28;November 11,25;December 9,23 + * (1998 9:00 AM EST)January 6,20;February + * ... + * + * Weekly on Tuesday and Thursday for 5 weeks: + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH + * or + * RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH + * + * ==> (1997 9:00 AM EDT)September 2,4,9,11,16,18,23,25,30;October 2 + * + * Every other week on Monday, Wednesday and Friday until December 24, + * 1997, but starting on Tuesday, September 2, 1997: + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU; + * BYDAY=MO,WE,FR + * ==> (1997 9:00 AM EDT)September 2,3,5,15,17,19,29;October + * 1,3,13,15,17 + * (1997 9:00 AM EST)October 27,29,31;November 10,12,14,24,26,28; + * December 8,10,12,22 + * + * Every other week on Tuesday and Thursday, for 8 occurrences: + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH + * + * ==> (1997 9:00 AM EDT)September 2,4,16,18,30;October 2,14,16 + * + * Monthly on the 1st Friday for ten occurrences: + * + * DTSTART;TZID=US-Eastern:19970905T090000 + * RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR + * + * ==> (1997 9:00 AM EDT)September 5;October 3 + * (1997 9:00 AM EST)November 7;Dec 5 + * (1998 9:00 AM EST)January 2;February 6;March 6;April 3 + * (1998 9:00 AM EDT)May 1;June 5 + * + * Monthly on the 1st Friday until December 24, 1997: + * + * DTSTART;TZID=US-Eastern:19970905T090000 + * RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR + * + * ==> (1997 9:00 AM EDT)September 5;October 3 + * (1997 9:00 AM EST)November 7;December 5 + * + * Every other month on the 1st and last Sunday of the month for 10 + * occurrences: + * + * DTSTART;TZID=US-Eastern:19970907T090000 + * RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU + * + * ==> (1997 9:00 AM EDT)September 7,28 + * (1997 9:00 AM EST)November 2,30 + * (1998 9:00 AM EST)January 4,25;March 1,29 + * (1998 9:00 AM EDT)May 3,31 + * + * Monthly on the second to last Monday of the month for 6 months: + * + * DTSTART;TZID=US-Eastern:19970922T090000 + * RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO + * + * ==> (1997 9:00 AM EDT)September 22;October 20 + * (1997 9:00 AM EST)November 17;December 22 + * (1998 9:00 AM EST)January 19;February 16 + * + * Monthly on the third to the last day of the month, forever: + * + * DTSTART;TZID=US-Eastern:19970928T090000 + * RRULE:FREQ=MONTHLY;BYMONTHDAY=-3 + * + * ==> (1997 9:00 AM EDT)September 28 + * (1997 9:00 AM EST)October 29;November 28;December 29 + * (1998 9:00 AM EST)January 29;February 26 + * ... + * + * Monthly on the 2nd and 15th of the month for 10 occurrences: + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15 + * + * ==> (1997 9:00 AM EDT)September 2,15;October 2,15 + * (1997 9:00 AM EST)November 2,15;December 2,15 + * (1998 9:00 AM EST)January 2,15 + * + * Monthly on the first and last day of the month for 10 occurrences: + * + * DTSTART;TZID=US-Eastern:19970930T090000 + * RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1 + * + * ==> (1997 9:00 AM EDT)September 30;October 1 + * (1997 9:00 AM EST)October 31;November 1,30;December 1,31 + * (1998 9:00 AM EST)January 1,31;February 1 + * + * Every 18 months on the 10th thru 15th of the month for 10 + * occurrences: + * + * DTSTART;TZID=US-Eastern:19970910T090000 + * RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14, + * 15 + * + * ==> (1997 9:00 AM EDT)September 10,11,12,13,14,15 + * (1999 9:00 AM EST)March 10,11,12,13 + * + * Every Tuesday, every other month: + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU + * + * ==> (1997 9:00 AM EDT)September 2,9,16,23,30 + * (1997 9:00 AM EST)November 4,11,18,25 + * (1998 9:00 AM EST)January 6,13,20,27;March 3,10,17,24,31 + * ... + * + * Yearly in June and July for 10 occurrences: + * + * DTSTART;TZID=US-Eastern:19970610T090000 + * RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7 + * ==> (1997 9:00 AM EDT)June 10;July 10 + * (1998 9:00 AM EDT)June 10;July 10 + * (1999 9:00 AM EDT)June 10;July 10 + * (2000 9:00 AM EDT)June 10;July 10 + * (2001 9:00 AM EDT)June 10;July 10 + * Note: Since none of the BYDAY, BYMONTHDAY or BYYEARDAY components + * are specified, the day is gotten from DTSTART + * + * Every other year on January, February, and March for 10 occurrences: + * + * DTSTART;TZID=US-Eastern:19970310T090000 + * RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3 + * + * ==> (1997 9:00 AM EST)March 10 + * (1999 9:00 AM EST)January 10;February 10;March 10 + * (2001 9:00 AM EST)January 10;February 10;March 10 + * (2003 9:00 AM EST)January 10;February 10;March 10 + * + * Every 3rd year on the 1st, 100th and 200th day for 10 occurrences: + * + * DTSTART;TZID=US-Eastern:19970101T090000 + * RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200 + * + * ==> (1997 9:00 AM EST)January 1 + * (1997 9:00 AM EDT)April 10;July 19 + * (2000 9:00 AM EST)January 1 + * (2000 9:00 AM EDT)April 9;July 18 + * (2003 9:00 AM EST)January 1 + * (2003 9:00 AM EDT)April 10;July 19 + * (2006 9:00 AM EST)January 1 + * + * Every 20th Monday of the year, forever: + * DTSTART;TZID=US-Eastern:19970519T090000 + * RRULE:FREQ=YEARLY;BYDAY=20MO + * + * ==> (1997 9:00 AM EDT)May 19 + * (1998 9:00 AM EDT)May 18 + * (1999 9:00 AM EDT)May 17 + * ... + * + * Monday of week number 20 (where the default start of the week is + * Monday), forever: + * + * DTSTART;TZID=US-Eastern:19970512T090000 + * RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO + * + * ==> (1997 9:00 AM EDT)May 12 + * (1998 9:00 AM EDT)May 11 + * (1999 9:00 AM EDT)May 17 + * ... + * + * Every Thursday in March, forever: + * + * DTSTART;TZID=US-Eastern:19970313T090000 + * RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH + * + * ==> (1997 9:00 AM EST)March 13,20,27 + * (1998 9:00 AM EST)March 5,12,19,26 + * (1999 9:00 AM EST)March 4,11,18,25 + * ... + * + * Every Thursday, but only during June, July, and August, forever: + * + * DTSTART;TZID=US-Eastern:19970605T090000 + * RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8 + * + * ==> (1997 9:00 AM EDT)June 5,12,19,26;July 3,10,17,24,31; + * August 7,14,21,28 + * (1998 9:00 AM EDT)June 4,11,18,25;July 2,9,16,23,30; + * August 6,13,20,27 + * (1999 9:00 AM EDT)June 3,10,17,24;July 1,8,15,22,29; + * August 5,12,19,26 + * ... + * + * Every Friday the 13th, forever: + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * EXDATE;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13 + * + * ==> (1998 9:00 AM EST)February 13;March 13;November 13 + * (1999 9:00 AM EDT)August 13 + * (2000 9:00 AM EDT)October 13 + * ... + * + * The first Saturday that follows the first Sunday of the month, + * forever: + * + * DTSTART;TZID=US-Eastern:19970913T090000 + * RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13 + * + * ==> (1997 9:00 AM EDT)September 13;October 11 + * (1997 9:00 AM EST)November 8;December 13 + * (1998 9:00 AM EST)January 10;February 7;March 7 + * (1998 9:00 AM EDT)April 11;May 9;June 13... + * ... + * + * Every four years, the first Tuesday after a Monday in November, + * forever (U.S. Presidential Election day): + * + * DTSTART;TZID=US-Eastern:19961105T090000 + * RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4, + * 5,6,7,8 + * + * ==> (1996 9:00 AM EST)November 5 + * (2000 9:00 AM EST)November 7 + * (2004 9:00 AM EST)November 2 + * ... + * + * The 3rd instance into the month of one of Tuesday, Wednesday or + * Thursday, for the next 3 months: + * + * DTSTART;TZID=US-Eastern:19970904T090000 + * RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3 + * + * ==> (1997 9:00 AM EDT)September 4;October 7 + * (1997 9:00 AM EST)November 6 + * + * The 2nd to last weekday of the month: + * + * DTSTART;TZID=US-Eastern:19970929T090000 + * RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2 + * + * ==> (1997 9:00 AM EDT)September 29 + * (1997 9:00 AM EST)October 30;November 27;December 30 + * (1998 9:00 AM EST)January 29;February 26;March 30 + * ... + * + * Every 3 hours from 9:00 AM to 5:00 PM on a specific day: + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z + * + * ==> (September 2, 1997 EDT)09:00,12:00,15:00 + * + * Every 15 minutes for 6 occurrences: + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=MINUTELY;INTERVAL=15;COUNT=6 + * + * ==> (September 2, 1997 EDT)09:00,09:15,09:30,09:45,10:00,10:15 + * + * Every hour and a half for 4 occurrences: + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=MINUTELY;INTERVAL=90;COUNT=4 + * + * ==> (September 2, 1997 EDT)09:00,10:30;12:00;13:30 + * + * Every 20 minutes from 9:00 AM to 4:40 PM every day: + * + * DTSTART;TZID=US-Eastern:19970902T090000 + * RRULE:FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40 + * or + * RRULE:FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16 + * + * ==> (September 2, 1997 EDT)9:00,9:20,9:40,10:00,10:20, + * ... 16:00,16:20,16:40 + * (September 3, 1997 EDT)9:00,9:20,9:40,10:00,10:20, + * ...16:00,16:20,16:40 + * ... + * + * An example where the days generated makes a difference because of + * WKST: + * + * DTSTART;TZID=US-Eastern:19970805T090000 + * RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO + * + * ==> (1997 EDT)Aug 5,10,19,24 + * + * changing only WKST from MO to SU, yields different results... + * + * DTSTART;TZID=US-Eastern:19970805T090000 + * RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU + * ==> (1997 EDT)August 5,17,19,31 + */ +class qCal_Property_Rrule extends qCal_Property { + + protected $type = 'RECUR'; + protected $allowedComponents = array('VEVENT','VTODO','VJOURNAL','VTIMEZONE','DAYLIGHT','STANDARD'); + protected $allowMultiple = true; + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Sequence.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Sequence.php new file mode 100644 index 00000000..f2380a99 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Sequence.php @@ -0,0 +1,94 @@ + + * ;Minimum iCalendar version needed to parse the iCalendar object + * + * maxver = + * ;Maximum iCalendar version needed to parse the iCalendar object + * + * Example: The following is an example of this property: + * + * VERSION:2.0 + */ +class qCal_Property_Version extends qCal_Property { + + protected $type = 'TEXT'; + protected $allowedComponents = array('VCALENDAR'); + protected $default = "2.0"; + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Renderer.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Renderer.php new file mode 100644 index 00000000..aedf7ac0 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Renderer.php @@ -0,0 +1,6 @@ +getName() . self::LINE_ENDING; + foreach ($component->getProperties() as $property) { + if (is_array($property)) { + foreach ($property as $prop) { + $return .= $this->renderProperty($prop); + } + } else { + $return .= $this->renderProperty($property); + } + } + foreach ($component->getChildren() as $children) { + if (is_array($children)) { + foreach ($children as $child) { + $return .= $this->render($child); + } + } else { + $return .= $this->render($children); + } + } + return $return . "END:" . $component->getName() . self::LINE_ENDING; + + } + /** + * Renders a property in accordance with rfc 2445 + * @todo $proptype is created below and never used... wtf? + */ + protected function renderProperty(qCal_Property $property) { + + $propval = $property->getValue(); + $params = $property->getParams(); + $paramreturn = ""; + foreach ($params as $paramname => $paramval) { + $paramreturn .= $this->renderParam($paramname, $paramval); + } + // if property has a "value" param, then use it as the type instead + $proptype = isset($params['VALUE']) ? $params['VALUE'] : $property->getType(); + if ($property instanceof qCal_Property_MultiValue) { + $values = array(); + foreach ($property->getValue() as $value) { + $values[] = $this->renderValue($property->getValue(), $proptype); + } + $value = implode(chr(44), $values); + } else { + $value = $this->renderValue($property->getValue(), $proptype); + } + $content = $property->getName() . $paramreturn . ":" . $value . self::LINE_ENDING; + return $this->fold($content); + + } + /** + * Renders a value + */ + protected function renderValue($value, $type) { + + switch(strtoupper($type)) { + case "TEXT": + $value = str_replace(",", "\,", $value); + break; + } + return $value; + + } + /** + * Renders a parameter + * RFC 2445 says if paramval contains COLON (US-ASCII decimal + * 58), SEMICOLON (US-ASCII decimal 59) or COMMA (US-ASCII decimal 44) + * character separators MUST be specified as quoted-string text values + */ + protected function renderParam($name, $value) { + + $invchars = array(chr(58),chr(59),chr(44)); + $quote = false; + foreach ($invchars as $char) { + if (strstr($value, $char)) { + $quote = true; + break; + } + } + if ($quote) $value = '"' . $value . '"'; + return ";" . $name . "=" . $value; + + } + + /** + * Text cannot exceed 75 octets. This method will "fold" long lines in accordance with RFC 2445 + * @todo Make sure this is multi-byte safe + * @todo The file I downloaded from google used this same folding method (long lines went to 76) + * so until I see any different, I'm going to keep it at 76. + */ + protected function fold($data) { + + if (strlen($data) == (self::FOLD_LENGTH + strlen(self::LINE_ENDING))) return $data; + $apart = str_split($data, self::FOLD_LENGTH); + return implode(self::LINE_ENDING . " ", $apart); + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Time.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Time.php new file mode 100644 index 00000000..0a4be942 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Time.php @@ -0,0 +1,198 @@ +setTimezone($timezone) + ->setTime($hour, $minute, $second, $rollover); + + } + /** + * Set the time + * @access protected This class is immutable, so this is protected. Only the constructor calls it. + */ + protected function setTime($hour = null, $minute = null, $second = null, $rollover = null) { + + if (is_null($hour)) { + $hour = gmdate("H"); + } + if (is_null($minute)) { + $minute = gmdate("i"); + } + if (is_null($second)) { + $second = gmdate("s"); + } + if (is_null($rollover)) $rollover = false; + if (!$rollover) { + if ($hour > 23 || $minute > 59 || $second > 59) { + throw new qCal_DateTime_Exception_InvalidTime(sprintf("Invalid time specified for qCal_Time: \"%02d:%02d:%02d\"", $hour, $minute, $second)); + } + } + // since PHP is incapable of storing a time without a date, we use the first day of + // the unix epoch so that we only have the amount of seconds since the zero of unix epoch + // we only use gm here because we don't want the server's timezone to interfere + $time = gmmktime($hour, $minute, $second, 1, 1, 1970); + $this->time = $time; + $formatString = "a|A|B|g|G|h|H|i|s|u"; + $keys = explode("|", $formatString); + $vals = explode("|", gmdate($formatString, $this->getTimestamp(false))); + $this->timeArray = array_merge($this->timeArray, array_combine($keys, $vals)); + return $this; + + } + /** + * Set the timezone + */ + protected function setTimezone($timezone) { + + if (is_null($timezone) || !($timezone instanceof qCal_Timezone)) { + $timezone = qCal_Timezone::factory($timezone); + } + $this->timezone = $timezone; + return $this; + + } + /** + * Get the timezone + */ + public function getTimezone() { + + return $this->timezone; + + } + /** + * Generate a qCal_Time object via a string or a number of other methods + */ + public static function factory($time, $timezone = null) { + + if (is_null($timezone) || !($timezone instanceof qCal_Timezone)) { + $timezone = qCal_Timezone::factory($timezone); + } + // get the default timezone so we can set it back to it later + $tz = date_default_timezone_get(); + // set the timezone to GMT temporarily + date_default_timezone_set("GMT"); + + if (is_integer($time)) { + // @todo Handle timestamps + // @maybe not... + } + if (is_string($time)) { + if ($time == "now") { + $time = new qCal_Time(null, null, null, $timezone); + } else { + $tstring = "01/01/1970 $time"; + if (!$timestamp = strtotime($tstring)) { + // if unix timestamp can't be created throw an exception + throw new qCal_DateTime_Exception_InvalidTime("Invalid or ambiguous time string passed to qCal_Time::factory()"); + } + list($hour, $minute, $second) = explode(":", gmdate("H:i:s", $timestamp)); + $time = new qCal_Time($hour, $minute, $second, $timezone); + } + } + + // set the timezone back to what it was + date_default_timezone_set($tz); + + return $time; + + } + /** + * Get the hour + */ + public function getHour() { + + return $this->timeArray['G']; + + } + /** + * Get the minute + */ + public function getMinute() { + + return $this->timeArray['i']; + + } + /** + * Get the second + */ + public function getSecond() { + + return $this->timeArray['s']; + + } + /** + * Get the timestamp + */ + public function getTimestamp($useOffset = true) { + + $time = ($useOffset) ? + $this->time - $this->getTimezone()->getOffsetSeconds() : + $this->time; + return $time; + + } + /** + * Set the format to use when outputting as a string + */ + public function setFormat($format) { + + $this->format = (string) $format; + return $this; + + } + /** + * Output the object using PHP's date() function's meta-characters + */ + public function format($format) { + + $escape = false; + $meta = str_split($format); + $output = array(); + foreach($meta as $char) { + if ($char == '\\') { + $escape = true; + continue; + } + if (!$escape && array_key_exists($char, $this->timeArray)) { + $output[] = $this->timeArray[$char]; + } else { + $output[] = $char; + } + // reset this to false after every iteration that wasn't "continued" + $escape = false; + } + return implode($output); + + } + /** + * Output the object as a string + */ + public function __toString() { + + return $this->format($this->format); + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Timezone.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Timezone.php new file mode 100644 index 00000000..a1d9c225 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Timezone.php @@ -0,0 +1,224 @@ +setName($name); + $this->setOffsetSeconds($offset); + if (is_null($abbreviation)) $abbreviation = $name; + $this->setAbbreviation($abbreviation); + $this->setIsDaylightSavings($daylightsavings); + $this->formatArray = array( + 'e' => $this->getName(), + 'I' => (integer) $this->isDaylightSavings(), + 'O' => $this->getOffsetHours(), + 'P' => $this->getOffset(), + 'T' => $this->getAbbreviation(), + 'Z' => $this->getOffsetSeconds(), + ); + + } + public function setName($name) { + + $this->name = (string) $name; + + } + public function setOffsetSeconds($offset) { + + $this->offsetSeconds = (integer) $offset; + + } + public function setAbbreviation($abbreviation) { + + $this->abbreviation = (string) $abbreviation; + + } + public function setIsDaylightSavings($daylightSavings = null) { + + $this->isDaylightSavings = (boolean) $daylightSavings; + + } + /** + * Generate a timezone from either an array of parameters, or a timezone + * name such as "America/Los_Angeles". + * @link http://php.net/manual/en/timezones.php A directory of valid timezones + * @todo This method is FUGLY. Rewrite it and make it make sense. This is sort of nonsensical. + */ + public static function factory($timezone = null) { + + if (is_array($timezone)) { + // remove anything irrelevant + $vals = array_intersect_key($timezone, array_flip(array('name','offsetSeconds','abbreviation','isDaylightSavings'))); + if (!array_key_exists("name", $vals)) { + // @todo throw an exception or something + } + if (!array_key_exists("offsetSeconds", $vals)) { + // @todo throw an exception or something + } + $name = $vals['name']; + $offsetSeconds = $vals['offsetSeconds']; + $abbreviation = (array_key_exists('abbreviation', $vals)) ? $vals['abbreviation'] : null; + $isDaylightSavings = (array_key_exists('isDaylightSavings', $vals)) ? $vals['isDaylightSavings'] : null; + $timezone = new qCal_Timezone($name, $offsetSeconds, $abbreviation, $isDaylightSavings); + } else { + // get the timezone information out of the string + $defaultTz = date_default_timezone_get(); + + if (is_null($timezone)) $timezone = $defaultTz; + + // if the timezone being set is invalid, we will get a PHP notice, so error is suppressed here + // @todo It would be more clean and probably more efficient to use php's error handling to throw an exception here... + if (is_string($timezone)) { + @date_default_timezone_set($timezone); + // if the function above didn't work, this will be true + if (date_default_timezone_get() != $timezone) { + // if the timezone requested is registered, use it + if (array_key_exists($timezone, self::$timezones)) { + $timezone = self::$timezones[$timezone]; + } else { + // otherwise, throw an exception + throw new qCal_DateTime_Exception_InvalidTimezone("'$timezone' is not a valid timezone."); + } + } else { + // if the timezone specified was a valid (native php) timezone, use it + $name = date("e"); + $offset = date("Z"); + $abbr = date("T"); + $ds = date("I"); + $timezone = new qCal_Timezone($name, $offset, $abbr, $ds); + } + } + + // now set it back to what it was... + date_default_timezone_set($defaultTz); + } + return $timezone; + + } + + public static function register(qCal_Timezone $timezone) { + + self::$timezones[$timezone->getName()] = $timezone; + + } + + public static function unregister($timezone) { + + unset(self::$timezones[(string) $timezone]); + + } + + public function getName() { + + return $this->name; + + } + + public function getOffset() { + + $seconds = $this->getOffsetSeconds(); + $negpos = "+"; + if ($seconds < 0) { + $negpos = "-"; + } + $hours = (integer) ($seconds / 60 / 60); + $minutes = $hours * 60; + $minutes = ($seconds / 60) - $minutes; + return sprintf("%s%02d:%02d", $negpos, abs($hours), abs($minutes)); + + } + + public function getOffsetHours() { + + $seconds = $this->getOffsetSeconds(); + $negpos = "+"; + if ($seconds < 0) { + $negpos = "-"; + } + $hours = (integer) ($seconds / 60 / 60); + $minutes = $hours * 60; + $minutes = ($seconds / 60) - $minutes; + return sprintf("%s%02d%02d", $negpos, abs($hours), abs($minutes)); + + } + + public function getOffsetSeconds() { + + return $this->offsetSeconds; + + } + + public function getAbbreviation() { + + return $this->abbreviation; + + } + + public function isDaylightSavings() { + + return $this->isDaylightSavings; + + } + + /** + * Set the format that should be used when calling either __toString() or format() without an argument. + * @param string $format + */ + public function setFormat($format) { + + $this->format = (string) $format; + return $this; + + } + + public function format($format) { + + $escape = false; + $meta = str_split($format); + $output = array(); + foreach($meta as $char) { + if ($char == '\\') { + $escape = true; + continue; + } + if (!$escape && array_key_exists($char, $this->formatArray)) { + $output[] = $this->formatArray[$char]; + } else { + $output[] = $char; + } + // reset this to false after every iteration that wasn't "continued" + $escape = false; + } + return implode($output); + + } + + public function __toString() { + + return $this->format($this->format); + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value.php new file mode 100644 index 00000000..3e3563cd --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value.php @@ -0,0 +1,98 @@ +setValue($value); + + } + /** + * A factory for data type objects. Pass in a type and a value, and it will return the value + * casted to the proper type + */ + public static function factory($type, $value) { + + // remove dashes, capitalize properly + $parts = explode("-", $type); + $type = ""; + foreach ($parts as $part) $type .= trim(ucfirst(strtolower($part))); + // get the class, and instantiate + $className = "qCal_Value_" . $type; + $class = new $className($value); + return $class; + + } + /** + * Sets the value of this object. The beauty of using inheritence here is that I can store + * the value however I want for any value type, and then on __toString() I can return it how + * iCalendar specifies :) + */ + public function setValue($value) { + + $this->value = $this->doCast($value); + return $this; + + } + /** + * Returns raw value (as it is stored) + */ + public function getValue() { + + return $this->value; + + } + /** + * Casts $value to this data type + */ + public function cast($value) { + + return $this->doCast($value); + + } + /** + * Returns the value as a string + */ + public function __toString() { + + return $this->toString($this->value); + + } + /** + * Converts from native format to a string, __toString() calls this internally + */ + protected function toString($value) { + + return (string) $value; + + } + /** + * This is left to be implemented by children classes, basically they + * implement this method to cast any input into their data type (from a string) + * @todo Change the name of this to something more appropriate, maybe toNative or something + */ + abstract protected function doCast($value); + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Binary.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Binary.php new file mode 100644 index 00000000..fb8b393c --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Binary.php @@ -0,0 +1,69 @@ + + * + * qCal_DataType_Binary + * This object defines any binary object that may be attached to an + * icalendar file. + */ +class qCal_Value_Binary extends qCal_Value { + + /** + * When the value of a binary property is requested, it will be returned as a base64 encoded string + * @todo Base64 is the only encoding supported by this standard, but the encoding=base64 parameter must be + * provided regardless. + */ + protected function toString($value) { + + return base64_encode($value); + + } + /** + * Binary can be store as-is I believe, so don't change it + */ + protected function doCast($value) { + + return $value; + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Boolean.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Boolean.php new file mode 100644 index 00000000..3d47a553 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Boolean.php @@ -0,0 +1,47 @@ +format('Ymd'); + + } + /** + * This converts to a qCal_Date for internal storage + */ + protected function doCast($value) { + + $date = qCal_Date::factory($value); + return $date; + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/DateTime.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/DateTime.php new file mode 100644 index 00000000..4f138ce0 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/DateTime.php @@ -0,0 +1,134 @@ +format('Ymd\THis'); + + } + /** + * This converts to a qCal_Date for internal storage + */ + protected function doCast($value) { + + // @todo This may be the wrong place to do this... + if ($value instanceof qCal_DateTime) { + return $value; + } + $date = qCal_DateTime::factory($value); + return $date; + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Duration.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Duration.php new file mode 100644 index 00000000..b0a86b08 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Duration.php @@ -0,0 +1,67 @@ +toICal(); + + } + /** + * Convert to internal representation + */ + protected function doCast($value) { + + return new qCal_DateTime_Duration($value); + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Float.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Float.php new file mode 100644 index 00000000..56474cac --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Float.php @@ -0,0 +1,44 @@ +getUnixTimestamp() + $duration->getSeconds()); // @todo This needs to be updated once qCal_DateTime accepts timestamps + } + return new qCal_DateTime_Period($start, $end); + + } + /** + * Convert to string - this converts to string into the UTC/UTC format + */ + protected function toString($value) { + + return $value->getStart()->getUtc() . "/" + . $value->getEnd()->getUtc(); + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Recur.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Recur.php new file mode 100644 index 00000000..97f6c5ce --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Recur.php @@ -0,0 +1,279 @@ +format('His'); + + } + /** + * This converts to a qCal_Date for internal storage + */ + protected function doCast($value) { + + $date = qCal_Time::factory($value); + return $date; + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Uri.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Uri.php new file mode 100644 index 00000000..03c3edc2 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Uri.php @@ -0,0 +1,56 @@ + + * + * Description: This data type might be used to reference binary + * information, for values that are large, or otherwise undesirable to + * include directly in the iCalendar object. + * + * The URI value formats in RFC 1738, RFC 2111 and any other IETF + * registered value format can be specified. + * + * Any IANA registered URI format can be used. These include, but are + * not limited to, those defined in RFC 1738 and RFC 2111. + * + * When a property parameter value is a URI value type, the URI MUST be + * specified as a quoted-string value. + * + * No additional content value encoding (i.e., BACKSLASH character + * encoding) is defined for this value type. + * + * Example: The following is a URI for a network file: + * + * http://host1.com/my-report.txt + */ +class qCal_Value_Uri extends qCal_Value { + + protected function toString($value) { + + return (string) $value; + + } + /** + * @todo: implement this + */ + protected function doCast($value) { + + return $value; + + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/UtcOffset.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/UtcOffset.php new file mode 100644 index 00000000..9d035533 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/UtcOffset.php @@ -0,0 +1,50 @@ +