anewt-developers team mailing list archive
-
anewt-developers team
-
Mailing list archive
-
Message #00129
[Branch ~uws/anewt/anewt.uws] Rev 1724: [calendar] Completely reworked calendar module
------------------------------------------------------------
revno: 1724
committer: Wouter Bolsterlee <uws@xxxxxxxxx>
branch nick: anewt.uws
timestamp: Sun 2009-08-02 20:49:43 +0200
message:
[calendar] Completely reworked calendar module
The API and code is modernised to conform to current Anewt
coding practice, e.g. the iCalendar class is now named
AnewtCalendar and has a cleaner API. The API documentation
is 100% complete. Also added some simple test/demo code.
added:
calendar/calendar.test.php
renamed:
calendar/ical.lib.php => calendar/calendar.lib.php
modified:
calendar/main.lib.php
calendar/calendar.lib.php
--
lp:anewt
https://code.launchpad.net/~uws/anewt/anewt.uws
Your team Anewt developers is subscribed to branch lp:anewt.
To unsubscribe from this branch go to https://code.launchpad.net/~uws/anewt/anewt.uws/+edit-subscription.
=== renamed file 'calendar/ical.lib.php' => 'calendar/calendar.lib.php'
--- calendar/ical.lib.php 2009-08-02 16:32:09 +0000
+++ calendar/calendar.lib.php 2009-08-02 18:49:43 +0000
@@ -1,30 +1,15 @@
<?php
-/* vim:set fdm=indent: */
-
/*
* Anewt, Almost No Effort Web Toolkit, calendar module
*
- * Copyright (C) 2006 Wouter Bolsterlee <uws@xxxxxxxxx>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA+
+ * This code is copyrighted and distributed under the terms of the GNU LGPL.
+ * See the README file for more information.
*/
/*
- * Content types not (yet) supported (not really needed for the basic
+ * XXX: Content types not (yet) supported (not really needed for the basic
* functionality):
*
* - VTODO
@@ -36,266 +21,286 @@
/**
- * A class representing an iCalendar calendar object. It has some basic
- * properties and may contain one or more events. An instance of this class can
- * be serialized to a text/calendar (.ics) file, which can be imported into
- * calendering applications such as Evolution, iCal or Outlook.
+ * A class representing an iCalendar calendar object.
+ *
+ * It has some basic properties and may contain one or more events (represented
+ * by AnewtCalendarEvent). An instance of this class can be serialized to
+ * a <code>text/calendar</code> (<code>.ics</code>) file, which can be imported
+ * into calendering applications such as Evolution, iCal or Outlook.
+ *
+ * The available properties are:
+ *
+ * - \c filename contains the filename for this calendar
+ * - \c method defines the \c METHOD for the calendar. This is \c PUBLISH by
+ * default.
+ * - \c generator contains the product identifier (optional, defaults to an
+ * Anewt-specific string)
+ *
+ * A filename is optional, but it is recommended to set one for better
+ * client compatibility, since some platforms ignore the MIME type information
+ * and rely on the file name (extension) to determine what actions to perform.
+ *
+ * \see AnewtCalendarEvent
*/
-class iCalendar extends AnewtContainer
+class AnewtCalendar extends AnewtContainer
{
- var $events = array(); /**< \private Array holding all events */
+ /* Static members */
/**
- * \static
- *
* Escape a string so that it can be used for TEXT fields. Newlines and
* other characters are escaped to match the iCalendar specification.
*
* \param $str
* A string to escape.
*
+ * \param $multiline
+ * Boolean to indicate whether this is a multiline string. This affects
+ * how the string is escaped.
+ *
* \return
* The escaped string
*/
- function escape_string($str)
+ private static function escape_string($str, $multiline)
{
assert('is_string($str)');
+ assert('is_bool($multiline)');
/* Escape comma's, semicolons, backslashes */
$str = str_replace('\\', '\\\\', $str);
$str = str_replace(',', '\,', $str);
$str = str_replace(';', '\;', $str);
- /* Newlines need escaping too: literal \N should be put in the output */
- $str = preg_replace('/\r?\n/', '\N', $str);
+ /* Remove or escape newlines. Newlines are escaped as a literal \N */
+
+ $newline_replacement = $multiline ? '\N' : ' ';
+ $str = preg_replace('/\r?\n/', $newline_replacement, $str);
return $str;
}
- /**
- * Create a new calendar instance.
- */
- function iCalendar()
+
+ /* Instance members */
+
+ /**
+ * Array holding all events
+ */
+ private $events = array();
+
+ /**
+ * Construct a new AnewtCalendar instance.
+ */
+ public function __construct()
{
- /* Do nothing */
+ parent::__construct();
+ $this->_seed(array (
+ 'filename' => null,
+ 'generator' => '-//Almost No Effort Web Toolkit//Calendar module//EN',
+ 'method' => 'PUBLISH',
+ ));
}
/**
* Add an event to the calendar.
*
* \param $event
- * An iCalendarEvent instance
+ * An AnewtCalendarEvent instance
*/
- function add_event(&$event)
+ function add_event($event)
{
- assert('is_object($event)');
- assert('method_exists($event, "to_ical")');
- $this->events[] = &$event;
+ assert('$event instanceof AnewtCalendarEvent');
+ $this->events[] = $event;
}
/**
- * Returns a string representing this iCalender.
+ * Render this calendar to a string in iCal format.
*
* \return
- * String with iCalender data.
+ * String representation of this calendar
*/
- function to_ical()
+ function render()
{
- $r = array();
-
- /* Generic calendar data */
- $r[] = 'BEGIN:VCALENDAR';
- $r[] = 'VERSION:2.0';
- $r[] = sprintf('METHOD:%s', $this->getdefault('method', 'PUBLISH'));
- $r[] = sprintf('PRODID:%s', $this->getdefault('generator', '-//Almost No Effort Web Toolkit//Calendar module//EN'));
-
- $r[] = '';
-
- /* Loop over all events in the calendar */
- foreach (array_keys($this->events) as $key)
+ $out = array();
+
+ $out[] = 'BEGIN:VCALENDAR';
+ $out[] = 'VERSION:2.0';
+ $out[] = sprintf('METHOD:%s', $this->method);
+ $out[] = sprintf('PRODID:%s', $this->generator);
+ $out[] = '';
+
+ foreach ($this->events as $event)
{
- $event = &$this->events[$key];
- $r[] = $event->to_ical();
+ $out[] = 'BEGIN:VEVENT';
+
+ /* Generation date */
+
+ $out[] = sprintf('DTSTAMP;VALUE=DATE:%s', AnewtDateTime::iso8601_compact(AnewtDateTime::now()));
+
+
+ /* Summary and description */
+
+ assert('!is_null($event->summary)');
+
+ $description = $event->description;
+ if (is_null($description))
+ $description = $event->summary;
+
+ $out[] = sprintf('SUMMARY:%s', AnewtCalendar::escape_string($event->summary, false));
+ $out[] = sprintf('DESCRIPTION:%s', AnewtCalendar::escape_string($description, true));
+
+
+ /* Unique ID */
+
+ $uid = $event->uid;
+ if (is_null($uid))
+ {
+ /* Generate a unique id */
+ $uid = strtoupper(sprintf('%s-%s',
+ md5($event->summary),
+ md5(AnewtDateTime::iso8601_compact($event->date_start))
+ ));
+ }
+ $out[] = sprintf('UID:%s', $uid);
+
+
+ /* Dates */
+
+ assert('$event->date_start instanceof AnewtDateTimeAtom');
+ $datestart_str = $event->all_day
+ ? AnewtDateTime::iso8601_date_compact($event->date_start) /* Without time */
+ : AnewtDateTime::iso8601_compact($event->date_start); /* With time */
+ $out[] = sprintf('DTSTART;VALUE=DATE:%s', $datestart_str);
+
+ if (!is_null($event->date_end))
+ {
+ assert('$event->date_end instanceof AnewtDateTimeAtom');
+ $date_end_str = $event->all_day
+ ? AnewtDateTime::iso8601_date_compact($event->date_end) /* Without time */
+ : AnewtDateTime::iso8601_compact($event->date_end); /* With time */
+ $out[] = sprintf('DTEND;VALUE=DATE:%s', $date_end_str);
+ }
+
+ $out[] = sprintf('TRANSP:%s', $event->transparent ? 'TRANSPARENT' : 'OPAQUE');
+
+
+ /* Misc */
+
+ if (!is_null($event->location))
+ $out[] = sprintf('LOCATION:%s', $event->location);
+
+ if (!is_null($event->url))
+ $out[] = sprintf('URL:%s', $event->url);
+
+ $out[] = 'END:VEVENT';
+ $out[] = '';
}
- $r[] = 'END:VCALENDAR';
+ $out[] = 'END:VCALENDAR';
- return implode("\r\n", $r);
+ return implode(CRLF, $out);
}
/**
- * Outputs the iCalendar to a browser.
+ * Output the calendar to a browser.
*
- * \param $exit_after_flush
- * Boolean value indicating whether to stop execution after flushing to
- * the client (default false). If true, make sure the call to flush() is
- * the last call in your code, because statements below the call to
- * flush() will never be executed.
+ * This renders the calendar and all its asssociated events, and sends the
+ * output to the browser with the correct HTTP headers for the MIME type and
+ * (optionally) the downoad filename.
*/
- function flush($exit_after_flush=false)
+ function flush()
{
- /* MIME type */
header('Content-Type: text/calendar');
- /* Easier debugging (without a browser "save as" dialog) */
- if ($this->getdefault('debug', false))
- header('Content-Type: text/plain');
-
- /* A filename is optional, but it is recommended to set one for better
- * client compatibility (some platforms ignore the MIME type information
- * and use the file name (extension) to determine what actions to
- * perform. */
- if ($this->is_set('filename'))
+ $filename = $this->filename;
+ if (!is_null($filename))
{
- $filename = $this->get('filename');
- $filename = str_strip_suffix($filename, '.ics');
- header(sprintf('Content-Disposition: inline; filename=%s.ics', $filename));
+ /* Make sure the filename ends with .ics */
+ if (!str_has_prefix($filename, 'ics'))
+ $filename = sprintf('%s.ics', $filename);
+
+ header(sprintf('Content-Disposition: inline; filename=%s', $filename));
}
- echo $this->to_ical();
-
- assert('is_bool($exit_after_flush)');
- if ($exit_after_flush)
- exit(0);
+ echo to_string($this), NL;
}
}
/**
- * Represents an event on a calendar. Several properties can be set: summary,
- * description, date-start, date-end, all-day (boolean), location, url.
+ * Calendar event for AnewtCalendar calendars.
+ *
+ * AnewtCalendarEvent instances can be added to an AnewtCalendar using
+ * AnewtCalendar::add_event().
+ *
+ * The properties that can be set on AnewtCalendar instances are:
+ *
+ * - <code>uid</code>: A unique ID for this event (optional, defaults to an
+ * autogenerated, deterministic ID based on the summary and start date)
+ * - <code>summary</code>: The summary line for this event
+ * - <code>description</code>: A longer (multiline) description for this event
+ * (optional)
+ * - <code>location</code>: The location of this event (optional)
+ * - <code>url</code>: An associated URL for this event (optional)
+ * - <code>date-start</code>: The start date for this event (AnewtDateTimeAtom
+ * instance)
+ * - <code>date-end</code>: The end date for this event (AnewtDateTimeAtom
+ * instance, optional)
+ * - <code>all-day</code>: Whether this is an all-day event (boolean, defaults
+ * to \c false)
+ * - <code>transparent</code>: Whether this event is transparent and hence
+ * should not conflict with other appointments (boolean, defaults to \c false)
+ *
+ * Note that the \c summary and \c date-start properties are required by the
+ * iCalendar specification.
+ *
+ * \see AnewtCalendar
*/
-class iCalendarEvent extends AnewtContainer {
+class AnewtCalendarEvent extends AnewtContainer
+{
/**
- * Construct creates a new iCalendar event.
+ * Construct a new AnewtCalendarEvent instance.
+ *
+ * If you don't specify \c $summary or \c $date_start these values need to
+ * be set later.
*
* \param $summary
* A summary line for this event (optional).
*
- * \param $datestart
+ * \param $date_start
* A AnewtDateTimeAtom instance describing the start date for this event
* (optional).
*/
- function iCalendarEvent($summary=null, $datestart=null)
+ function __construct($summary=null, $date_start=null)
{
- $this->set('all-day', false);
+ parent::__construct();
+
+ $now = AnewtDateTime::now();
+
+ $this->_seed(array(
+
+ 'uid' => null,
+
+ 'summary' => null,
+ 'description' => null,
+ 'location' => null,
+ 'url' => null,
+
+ 'date-start' => $now,
+ 'date-end' => $now,
+ 'all-day' => false,
+ 'transparent' => false,
+ ));
if (!is_null($summary))
{
assert('is_string($summary)');
- $this->set('summary', $summary);
- }
-
- if (!is_null($datestart))
- {
- assert('$datestart instanceof AnewtDateTimeAtom');
- $this->set('datestart', $datestart);
- }
- }
-
- /**
- * Setter for the event summary. This string must not contain newlines, so
- * they will be replaced with space characters.
- *
- * \param $str
- * The summary to set
- */
- function set_summary($str)
- {
- $str = preg_replace('/\r?\n/', '', $str);
- $this->_set('summary', $str);
- }
-
- /**
- * Setter for the event description. This value needs to be escaped.
- *
- * \param $str
- * The description to set
- */
- function set_description($str)
- {
- $this->_set('description', iCalendar::escape_string($str));
- }
-
- /**
- * Return a string representing this event.
- *
- * \return
- * String with iCalender data.
- */
- function to_ical()
- {
- $r = array();
- $r[] = 'BEGIN:VEVENT';
-
- /* Generation date */
- $r[] = sprintf('DTSTAMP;VALUE=DATE:%s', AnewtDateTime::iso8601_compact(AnewtDateTime::now()));
-
- /* Summary */
- $r[] = sprintf('SUMMARY:%s', $this->get('summary'));
-
- /* Description */
- if (!$this->is_set('description'))
- {
- $this->set('description', $this->get('summary'));
- }
- $r[] = sprintf('DESCRIPTION:%s', $this->get('description'));
-
- /* Unique ID */
- if (!$this->is_set('uid'))
- {
- /* Generate a unique id */
- $uid = strtoupper(sprintf('%s-%s',
- md5($this->get('summary')),
- md5(AnewtDateTime::iso8601_compact($this->get('datestart')))));
- $this->set('uid', $uid);
- }
- $r[] = sprintf('UID:%s', $this->get('uid'));
-
-
- /* All day event? */
- $all_day = $this->get('all-day');
- assert('is_bool($all_day)');
-
- /* Start date */
- $datestart = $this->get('datestart');
- $datestart_str = $all_day
- ? AnewtDateTime::iso8601_date_compact($datestart) /* without time */
- : AnewtDateTime::iso8601_compact($datestart); /* with time */
- $r[] = sprintf('DTSTART;VALUE=DATE:%s', $datestart_str);
-
- /* End date */
- if ($this->is_set('dateend'))
- {
- $dateend = $this->get('dateend');
- assert('$dateend instanceof AnewtDateTimeAtom');
- $dateend_str = $all_day
- ? AnewtDateTime::iso8601_date_compact($dateend) /* without time */
- : AnewtDateTime::iso8601_compact($dateend); /* with time */
- $r[] = sprintf('DTEND;VALUE=DATE:%s', $dateend_str);
- }
-
- /* Transparency */
- $transparent = $this->getdefault('transparent', false);
- assert('is_bool($transparent)');
- $r[] = sprintf('TRANSP:%s', $transparent ? 'TRANSPARENT' : 'OPAQUE');
-
- /* Location */
- if ($this->is_set('location'))
- {
- $r[] = sprintf('LOCATION:%s', $this->get('location'));
- }
-
- /* URL */
- if ($this->is_set('url'))
- {
- $r[] = sprintf('URL:%s', $this->get('url'));
- }
-
- $r[] = 'END:VEVENT';
- $r[] = '';
-
- return implode("\r\n", $r);
+ $this->summary = $summary;
+ }
+
+ if (!is_null($date_start))
+ {
+ assert('$date_start instanceof AnewtDateTimeAtom');
+ $this->date_start = $date_start;
+ }
}
}
=== added file 'calendar/calendar.test.php'
--- calendar/calendar.test.php 1970-01-01 00:00:00 +0000
+++ calendar/calendar.test.php 2009-08-02 18:49:43 +0000
@@ -0,0 +1,26 @@
+<?php
+
+error_reporting(E_ALL | E_STRICT);
+require_once dirname(__FILE__) . '/../anewt.lib.php';
+
+anewt_include('calendar');
+
+$calendar = new AnewtCalendar();
+
+$event = new AnewtCalendarEvent('Title');
+$event->date_start = AnewtDateTime::parse_string('2009-01-01 12:00');
+$event->date_end = AnewtDateTime::parse_string('2009-01-01 14:00');
+$event->summary = 'This is the summary';
+$event->location = 'This is the location';
+$event->url = 'http://example.org/foo';
+$event->uid = 'abc1234567890';
+$calendar->add_event($event);
+
+$event = new AnewtCalendarEvent('Another event', AnewtDateTime::now());
+$event->summary = "This is a multiline\nsummary";
+$event->summary = "This is a multiline\ndescription";
+$calendar->add_event($event);
+
+$calendar->flush();
+
+?>
=== modified file 'calendar/main.lib.php'
--- calendar/main.lib.php 2006-07-28 14:56:22 +0000
+++ calendar/main.lib.php 2009-08-02 18:49:43 +0000
@@ -3,24 +3,11 @@
/*
* Anewt, Almost No Effort Web Toolkit, calendar module
*
- * Copyright (C) 2006 Wouter Bolsterlee <uws@xxxxxxxxx>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA+
+ * This code is copyrighted and distributed under the terms of the GNU LGPL.
+ * See the README file for more information.
*/
-anewt_include('calendar/ical');
+anewt_include('calendar/calendar');
?>