Many changes to get user maintenance basically working, make the layout

all look somewhat nicer, and start to try and work with Mulberry, including
implementing MKCALENDAR and PROPFIND in at least a basic manner.
This commit is contained in:
Andrew McMillan 2006-10-01 20:46:28 +13:00
parent db963fadf6
commit f649e1b49d
17 changed files with 273 additions and 344 deletions

View File

@ -109,6 +109,18 @@ CREATE TABLE todo (
GRANT SELECT,INSERT,UPDATE,DELETE ON todo TO general;
-- Something that can look like a filesystem hierarchy where we store stuff
CREATE TABLE calendar (
user_no INT references usr(user_no),
dav_name TEXT,
dav_etag TEXT,
created TIMESTAMP WITH TIME ZONE,
PRIMARY KEY ( user_no, dav_name )
);
GRANT SELECT,INSERT,UPDATE,DELETE ON calendar TO general;
-- Each user can be related to each other user. This mechanism can also
-- be used to define groups of users, since some relationships are transitive.
CREATE TABLE relationship_type (

View File

@ -1,10 +1,16 @@
-- Some sample data to prime the database...
INSERT INTO roles ( role_no, role_name ) VALUES( 1, 'Admin');
SELECT setval('roles_role_no_seq', 1);
INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
VALUES( 1, TRUE, current_date, current_date, 'admin', '**nimda', 'Calendar Administrator', 'calendars@example.net' );
INSERT INTO role_member (user_no, role_no) VALUES( 1, 1);
INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
VALUES( 2, TRUE, current_date, current_date, 'andrew', '**x', 'Andrew McMillan', 'andrew@catalyst.net.nz' );
INSERT INTO role_member (user_no, role_no) VALUES( 2, 1);
INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
VALUES( 10, TRUE, current_date, current_date, 'user1', '**user1', 'User 1', 'user1@example.net' );
@ -30,11 +36,6 @@ INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullna
SELECT setval('usr_user_no_seq', 1000);
INSERT INTO roles ( role_no, role_name ) VALUES( 1, 'Admin');
SELECT setval('roles_role_no_seq', 1);
INSERT INTO role_member (user_no, role_no) VALUES( 1, 1);
INSERT INTO relationship_type ( rt_id, rt_name, rt_isgroup, rt_inverse, confers, prefix_match )
VALUES( 1, 'Meeting Admin', TRUE, NULL, 'RW', '' );
@ -46,7 +47,7 @@ INSERT INTO relationship_type ( rt_id, rt_name, rt_isgroup, rt_inverse, confers,
VALUES( 3, 'Assistant to', FALSE, 2, 'RW', '' );
INSERT INTO relationship_type ( rt_id, rt_name, rt_isgroup, rt_inverse, confers, prefix_match )
VALUES( 4, 'Team Member', FALSE, 4, 'R', '' );
VALUES( 4, 'Member of team', FALSE, 4, 'R', '' );
INSERT INTO relationship_type ( rt_id, rt_name, rt_isgroup, rt_inverse, confers, prefix_match )
VALUES( 5, 'Meeting Resource', TRUE, NULL, 'RW', '' );

2
debian/control vendored
View File

@ -7,7 +7,7 @@ Build-Depends: debhelper
Package: rscds
Architecture: all
Depends: debconf (>= 1.0.32), php4 (>= 4:4.3) | php5, php4-pgsql(>= 3:4.3.0) | php5-pgsql, postgresql-client (>= 7.4) | postgresql-client-8.0 | postgresql-client-8.1, libawl-php (>=0.3-1)
Depends: debconf (>= 1.0.32), php4 (>= 4:4.3) | php5, php4-pgsql(>= 3:4.3.0) | php5-pgsql, postgresql-client (>= 7.4) | postgresql-client-8.0 | postgresql-client-8.1, libawl-php (>=0.4-1)
Description: Really Simple CalDAV Server
The Really Simple CalDAV Server is designed to trivially store
CalDAV calendars, such as those from Evolution, in a central

View File

@ -23,6 +23,10 @@ switch ( $_SERVER['REQUEST_METHOD'] ) {
include_once("caldav-PROPFIND.php");
break;
case 'MKCALENDAR':
include_once("caldav-MKCALENDAR.php");
break;
case 'PUT':
include_once("caldav-PUT.php");
break;

43
htdocs/css/browse.css Normal file
View File

@ -0,0 +1,43 @@
/* CSS for browse pages in RSCDS */
tr.header th, td {
padding: 1px 4px;
}
tr.header th {
font-family: Arial Narrow, sans-serif;
background-color: #a0f0b0;
color: #003010;
}
tr.header a {
color: inherit;
text-decoration:none;
border:0;
}
tr.r0:hover, tr.r1:hover {
background-color:#ffffc0;
}
.r0 {
background-color: #e0fff0;
}
.r1 {
background-color: #c0ffd0;
}
img.order {
border:0;
}
.right {
text-align:right;
}
.left {
text-align:left;
}

46
htdocs/js/browse.js Normal file
View File

@ -0,0 +1,46 @@
/**
* Simple function to send the browser to a given URL
*/
function Go( url ) {
window.location=url;
return true;
}
/**
* Make this tag into a Link to a given URL
*/
function LinkTo( tag, url ) {
tag.style.cursor = "pointer";
tag.setAttribute('onClick', "Go('" + url + "')");
tag.setAttribute('onMouseOut', "window.status='';return true;");
window.status = window.location.protocol + '//' + document.domain + url;
tag.setAttribute('onMouseover', "window.status = window.location.protocol + '//' + document.domain + '" + url + "';return true;");
tag.setAttribute('href', url);
return true;
}
/**
* Make this tag and all of it's contents into a clickable link, using the link target from an
* existing link target somewhere within the tag. Setting 'which1' to '1' will make the target
* match the 1st href target within the HTML of the tag.
* @param objectref tag A reference to the object which will become clickable.
* @param int which1 A one-based index to select which internal href attribute will become the target.
*/
function LinkHref( tag, which1 ) {
var urls = tag.innerHTML.match( / href="([^"]*)"/ig );
// alert(show_props(urls,'urls', 1));
try {
var url = urls[which1 - 1];
urls = url.match( /="([^"]*)"/ );
}
catch (e) {
//alert("Here are the URLs found:\nYou appear to need to choose a different index for your LinkHref call (the second parameter). Add 1 to the index below for the correct URL shown and use that.\n\n" + show_props(urls,'urls', 0));
return false;
}
// alert(show_props(urls,'urls', 1));
url = urls[1];
// alert("Linking to >>>" + url + "<<<");
LinkTo(tag,url);
return true;
}

View File

@ -63,6 +63,40 @@ label {
padding: 2px;
}
.prompt {
font-family: Arial Narrow, sans-serif;
background-color: #a0f0b0;
color: #003010;
}
.ph {
font-family:Bitstream Vera Sans, sans-serif;
color: #003010;
font-size: 120%;
background-color: #60e080;
text-align:left;
border-top: 2px white solid;
padding-top:3px;
}
#messages {
background-color: #602000;
color:white;
border:0;
padding:2px 6px;
}
ul.messages, li.messages {
font-family:Bitstream Vera Sans, sans-serif;
font-weight:700;
font-size: 1.1em;
}
li.messages {
display:inherit;
}
#menu {
background-color: #084010;
color: white;

View File

@ -8,12 +8,15 @@ require_once("interactive-page.php");
require_once("classBrowser.php");
$c->stylesheets[] = "css/browse.css";
$c->scripts[] = "js/browse.js";
$browser = new Browser("Calendar Users");
$browser->AddColumn( 'user_no', 'No.', '', '##user_link##' );
$browser->AddColumn( 'user_no', 'No.', 'right', '##user_link##' );
$browser->AddColumn( 'username', 'Name' );
$browser->AddHidden( 'user_link', "'<a href=\"/user.php?user_no=' || user_no || '\">' || user_no || '</a>'" );
$browser->AddColumn( 'fullname', 'Full Name' );
$browser->AddColumn( 'email', 'EMail' );
$browser->SetJoins( 'usr' );
@ -23,7 +26,7 @@ require_once("interactive-page.php");
else
$browser->AddOrder( 'user_no', 'A' );
$browser->RowFormat( "<tr onMouseover=\"LinkHref(this,1);\" title=\"Click to Display Role Detail\" class=\"r%d\">\n", "</tr>\n", '#even' );
$browser->RowFormat( "<tr onMouseover=\"LinkHref(this,1);\" title=\"Click to Display User Detail\" class=\"r%d\">\n", "</tr>\n", '#even' );
$browser->DoQuery();
$c->page_title = "Calendar Users";

View File

@ -10,6 +10,10 @@
*/
require_once("User.php");
require_once("classBrowser.php");
$c->stylesheets[] = "css/browse.css";
$c->scripts[] = "js/browse.js";
/**
* A class for viewing and maintaining RSCDS User records
@ -53,6 +57,8 @@ class RSCDSUser extends User
$html .= $this->RenderRoles($ef);
$html .= $this->RenderRelationships($ef);
$html .= "</table>\n";
$html .= "</div>";
@ -66,6 +72,46 @@ class RSCDSUser extends User
return $html;
}
/**
* Render the user's relationships to other users & resources
*
* @return string The string of html to be output
*/
function RenderRelationships( $ef, $title = "User Relationships" ) {
global $session, $c;
$browser = new Browser("");
$browser->AddHidden( 'user_link', "'<a href=\"/user.php?user_no=' || user_no || '\">' || fullname || '</a>'" );
$browser->AddColumn( 'rt_name', 'Relationship' );
$browser->AddColumn( 'fullname', 'Linked To', 'left', '##user_link##' );
$browser->AddColumn( 'rt_isgroup', 'Group?' );
$browser->AddHidden( 'confers', 'Confers' );
$browser->AddColumn( 'email', 'EMail' );
$browser->SetJoins( 'relationship NATURAL JOIN relationship_type rt LEFT JOIN usr ON (to_user = user_no)' );
$browser->SetWhere( "from_user = $this->user_no" );
$browser->SetUnion("SELECT rt.rt_name, fullname, rt.rt_isgroup, email, '<a href=\"/user.php?user_no=' || user_no || '\">' || fullname || '</a>' AS user_link, rt.confers AS confers FROM relationship NATURAL JOIN relationship_type rt1 LEFT JOIN relationship_type rt ON (rt.rt_id = rt1.rt_inverse) LEFT JOIN usr ON (from_user = user_no) WHERE to_user = $this->user_no ");
if ( isset( $_GET['o']) && isset($_GET['d']) ) {
$browser->AddOrder( $_GET['o'], $_GET['d'] );
}
else
$browser->AddOrder( 'rt_name', 'A' );
$browser->RowFormat( "<tr onMouseover=\"LinkHref(this,1);\" title=\"Click to display that relationship\" class=\"r%d\">\n", "</tr>\n", '#even' );
$browser->DoQuery();
$html = ( $title == "" ? "" : $ef->BreakLine($title) );
$html .= "<tr><td>&nbsp;</td><td>\n";
$html .= $browser->Render();
$html .= "</td></tr>\n";
return $html;
}
}
?>

View File

@ -23,6 +23,10 @@ class XMLElement {
/**
* Constructor - nothing fancy as yet.
*
* @param string The tag name of the new element
* @param mixed Either a string of content, or an array of sub-elements
* @param array An array of attribute name/value pairs
*/
function XMLElement( $tagname, $content=false, $attributes=false ) {
$this->tagname=$tagname;
@ -60,6 +64,18 @@ class XMLElement {
$this->content[] = $v;
}
/**
* Add a new sub-element
*
* @param string The tag name of the new element
* @param mixed Either a string of content, or an array of sub-elements
* @param array An array of attribute name/value pairs
*/
function NewElement( $tagname, $content=false, $attributes=false ) {
if ( gettype($this->content) != "array" ) $this->content = array();
$this->content[] = new XMLElement($tagname,$content,$attributes);
}
/**
* Render the document tree into (nicely formatted) XML
*
@ -72,7 +88,7 @@ class XMLElement {
* Render the element attribute values
*/
foreach( $this->attributes AS $k => $v ) {
$r .= sprintf( ' %s="%s"', $k, $v );
$r .= sprintf( ' %s="%s"', $k, htmlspecialchars($v) );
}
}
if ( (is_array($this->content) && count($this->content) > 0) || strlen($this->content) > 0 ) {
@ -95,7 +111,7 @@ class XMLElement {
*
* FIXME This should switch to CDATA in some situations.
*/
$r .= htmlspecialchars($this->content);
$r .= htmlspecialchars($this->content, ENT_NOQUOTES );
}
$r .= '</' . $this->tagname.">\n";
}

View File

@ -2,31 +2,14 @@
dbg_error_log("MKCALENDAR", "method handler");
$attributes = array();
$parser = xml_parser_create_ns('UTF-8');
xml_parser_set_option ( $parser, XML_OPTION_SKIP_WHITE, 1 );
function xml_start_callback( $parser, $el_name, $el_attrs ) {
dbg_error_log( "PROPFIND", "Parsing $el_name" );
dbg_log_array( "PROPFIND", "$el_name::attrs", $el_attrs, true );
$attributes[$el_name] = $el_attrs;
}
function xml_end_callback( $parser, $el_name ) {
dbg_error_log( "PROPFIND", "Finished Parsing $el_name" );
}
xml_set_element_handler ( $parser, 'xml_start_callback', 'xml_end_callback' );
$rpt_request = array();
xml_parse_into_struct( $parser, $raw_post, $rpt_request );
xml_parser_free($parser);
$make_path = $_SERVER['PATH_INFO'];
/**
* FIXME We kind of lie, at this point
*/
header("HTTP/1.1 200 Created");
$sql = "INSERT INTO calendar ( user_no, dav_name, dav_etag, created ) VALUES( ?, ?, ?, current_timestamp );";
$qry = new PgQuery( $sql, $session->user_no, $make_path, md5($session->user_no. $make_path) );
if ( $qry->Exec("MKCALENDAR",__LINE__,__FILE__) )
header("HTTP/1.1 200 Created");
else
header("HTTP/1.1 500 Infernal Server Error");
?>

View File

@ -2,7 +2,8 @@
dbg_error_log("OPTIONS", "method handler");
header( "Content-type: text/plain");
// header( "Allow: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE, PROPFIND, PROPPATCH, LOCK, UNLOCK, REPORT, ACL");
header( "Allow: OPTIONS, GET, PUT, DELETE, REPORT, PROPFIND, COPY, MOVE");
header( "Allow: ACL, COPY, DELETE, GET, HEAD, LOCK, MKCALENDAR, MKCOL, MOVE, OPTIONS, POST, PROPFIND, PROPPATCH, PUT, REPORT, SCHEDULE, TRACE, UNLOCK");
// header( "DAV: 1, 2, 3, access-control, calendar-access");
header( "DAV: 1, 2, calendar-access");
// header( "DAV: 1, 2, 3, calendar-access, calendar-schedule");
header( "DAV: 1, 2, access-control, calendar-access, calendar-schedule");
?>

View File

@ -23,6 +23,8 @@ xml_parse_into_struct( $parser, $raw_post, $rpt_request );
xml_parser_free($parser);
$find_path = $_SERVER['PATH_INFO'];
list( $blank, $username, $calpath ) = split( '/', $find_path, 3);
$calpath = "/".$calpath;
$href_list = array();
$attribute_list = array();
@ -107,24 +109,42 @@ else {
$url = sprintf("http://%s:%d%s%s", $_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT'], $_SERVER['SCRIPT_NAME'], $find_path );
$url = $_SERVER['SCRIPT_NAME'] . $find_path ;
$url = preg_replace( '#/$#', '', $url);
for ( $i=0; $i < 2; $i++ ) {
$props = array();
$sql = "SELECT * FROM calendar WHERE user_no = ? AND dav_name ~ ?;";
if ( $calpath == '' ) {
$sql = "SELECT user_no, '/' || username || '/' AS dav_name, md5( '/' || username || '/') AS dav_etag, updated AS created FROM usr WHERE user_no = $session->user_no UNION ".$sql;
}
$qry = new PgQuery($sql, $session->user_no, '^/'.$username.$calpath );
$qry->Exec("PROPFIND",__LINE,__FILE__);
while( $calendar = $qry->Fetch() ) {
$url = $_SERVER['SCRIPT_NAME'] . $calendar->dav_name;
$resourcetypes = array( new XMLElement("collection") );
$contentlength = false;
if ( $calendar->dav_name != "/$username/" ) {
$resourcetypes[] = new XMLElement("calendar", false, array("xmlns" => "urn:ietf:params:xml:ns:caldav"));
$lqry = new PgQuery("SELECT sum(length(caldav_data)) FROM caldav_data WHERE user_no = ? AND dav_name ~ ?;", $session->user_no, '^/'.$username.$calpath.'[^/]+$' );
if ( $lqry->Exec("PROPFIND",__LINE,__FILE__) && $row = $lqry->Fetch() ) {
$contentlength = $row->sum;
}
}
$prop = new XMLElement("prop");
if ( isset($attribute_list['GETCONTENTLENGTH']) ) {
$props[] = new XMLElement("getcontentlength" );
$prop->NewElement("getcontentlength", $contentlength );
}
if ( isset($attribute_list['GETCONTENTTYPE']) ) {
$props[] = new XMLElement("getcontenttype", "httpd/unix-directory" );
// $prop->NewElement("getcontenttype", "text/calendar" );
$prop->NewElement("getcontenttype", "httpd/unix-directory" );
}
if ( isset($attribute_list['RESOURCETYPE']) ) {
$resourcetypes = array( new XMLElement("collection") );
if ( $i == 1 ) $resourcetypes[] = new XMLElement("calendar", false, array("xmlns" => "urn:ietf:params:xml:ns:caldav"));
$props[] = new XMLElement("resourcetype", $resourcetypes );
$prop->NewElement("resourcetype", $resourcetypes );
}
if ( isset($attribute_list['GETETAG']) ) {
$prop->NewElement("getetag", '"'.$calendar->dav_etag.'"' );
}
$prop = new XMLElement("prop", $props );
$status = new XMLElement("status", "HTTP/1.1 200 OK" );
$propstat = new XMLElement( "propstat", array( $prop, $status) );
if ( $i == 1 ) $url .= "/calendar";
$href = new XMLElement("href", $url );
$responses[] = new XMLElement( "response", array($href,$propstat));
@ -133,10 +153,16 @@ else {
$multistatus = new XMLElement( "multistatus", $responses, array('xmlns'=>'DAV:') );
}
dbg_log_array( "PROPFIND", "XML", $multistatus, true );
$xmldoc = $multistatus->Render();
$etag = md5($xmldoc);
header("HTTP/1.1 207 Multi-Status");
header("Content-type: text/xml;charset=UTF-8");
header("DAV: 1, 2, calendar-access, calendar-schedule");
header("ETag: \"$etag\"");
echo'<?xml version="1.0" encoding="UTF-8" ?>'."\n";
echo $multistatus->Render();
echo $xmldoc;
?>

View File

@ -6,9 +6,9 @@ $page_menu->AddOption("Home","/","Browse all users", false, 3900 );
$page_menu->AddOption("Help","/help.php","Help on something or other", false, 4500 );
$page_menu->AddOption("Logout","/?logout","Log out of the $c->system_name", false, 5400 );
$relationship_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
// $relationship_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
$user_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
$role_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
// $role_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
$user_menu->AddOption("My Details","/user.php?user_no=$session->user_no","View my own user record", false, 700);

View File

@ -65,9 +65,9 @@ EOHDR;
echo "<div id=\"pageheader\">\n";
if ( isset($page_menu) && is_object($page_menu) ) {
$page_menu->AddSubMenu( $relationship_menu, "Relationships", "/relationships.php", "Browse all relationships", false, 4050 );
// $page_menu->AddSubMenu( $relationship_menu, "Relationships", "/relationships.php", "Browse all relationships", false, 4050 );
$page_menu->AddSubMenu( $user_menu, "Users", "/users.php", "Browse all users", false, 4100 );
$page_menu->AddSubMenu( $role_menu, "Roles", "/roles.php", "Browse all roles", false, 4300 );
// $page_menu->AddSubMenu( $role_menu, "Roles", "/roles.php", "Browse all roles", false, 4300 );
$page_menu->MakeSomethingActive($active_menu_pattern);
echo $page_menu->Render();
}

View File

@ -1,288 +0,0 @@
<?php
/**
* A Class for handling vEvent data
*
* @package awl
* @subpackage iCalendar
* @author Andrew McMillan <andrew@catalyst.net.nz>
* @copyright Catalyst IT Ltd
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
*/
/**
* A Class for handling Events on a calendar
*
* @package awl
*/
class vEvent {
/**#@+
* @access private
*/
/**
* List of participants in this event
* @var participants array
*/
var $participants = array();
/**
* An array of arbitrary properties
* @var properties array
*/
var $properties;
/**
* The typical location name for the standard timezone such as "Pacific/Auckland"
* @var tz_locn string
*/
var $tz_locn;
/**
* The type of iCalendar data VEVENT/VTODO
* @var type string
*/
var $type;
/**#@-*/
/**
* The constructor takes an array of args. If there is an element called 'vevent'
* then that will be parsed into the vEvent object. Otherwise the array elements
* are converted into properties of the vEvent object directly.
*/
function vEvent( $args ) {
global $c;
// Probably a good idea to always have values for these things...
if ( isset($c->local_tzid ) ) $this->properties['tz_id'] = $c->local_tzid;
$this->properties['dtstamp'] = date('Ymd\THis');
$this->properties['sequence'] = 1;
$this->properties['uid'] = sprintf( "%s@%s", time() * 1000 + rand(0,1000), $c->domain_name);
if ( !isset($args) || !is_array($args) ) return;
if ( isset($args['vevent']) ) {
$this->BuildFromText($args['vevent']);
$this->DealWithTimeZones();
return;
}
foreach( $args AS $k => $v ) {
$this->properties[strtoupper($k)] = $v;
}
}
/**
* Build the vEvent object from a text string which is a single VEVENT
*
* @var vevent string
*/
function BuildFromText( $vevent ) {
$vevent = preg_replace('/[\r\n]+ /', ' ', $vevent );
$lines = preg_split('/[\r\n]+/', $vevent );
$properties = array();
$vtimezone = "";
$state = 0;
foreach( $lines AS $k => $v ) {
dbg_error_log( "vEvent", "LINE %03d: >>>%s<<<", $k, $v );
switch( $state ) {
case 0:
if ( $v == 'BEGIN:VEVENT' ) {
$state = $v;
$this->type = 'VEVENT';
}
else if ( $v == 'BEGIN:VTODO' ) {
$state = $v;
$this->type = 'VTODO';
}
else if ( $v == 'BEGIN:VTIMEZONE' ) $state = $v;
break;
case 'BEGIN:VEVENT':
if ( $v == 'END:VEVENT' ) $state = 0;
break;
case 'BEGIN:VTODO':
if ( $v == 'END:VTODO' ) $state = 0;
break;
case 'BEGIN:VTIMEZONE':
if ( $v == 'END:VTIMEZONE' ) {
$state = 0;
$vtimezone .= $v;
}
break;
}
if ( ($state == 'BEGIN:VEVENT' || $state == 'BEGIN:VTODO') && $state != $v ) {
list( $parameter, $value ) = preg_split('/:/', $v );
if ( preg_match('/^DT[A-Z]+;TZID=/', $parameter) ) {
list( $parameter, $tz_id ) = preg_split('/;/', $parameter );
$properties['TZID'] = $tz_id;
}
$properties[strtoupper($parameter)] = $value;
}
if ( $state == 'BEGIN:VTIMEZONE' ) {
$vtimezone .= $v . "\n";
list( $parameter, $value ) = preg_split('/:/', $v );
if ( !isset($this->tz_locn) && $parameter == 'X-LIC-LOCATION' ) {
$this->tz_locn = $value;
}
}
}
if ( $vtimezone != "" ) {
$properties['VTIMEZONE'] = $vtimezone;
}
$this->properties = &$properties;
}
/**
* Do what must be done with time zones from on file. Attempt to turn
* them into something that PostgreSQL can understand...
*/
function DealWithTimeZones() {
if ( isset($c->save_time_zone_defs) ) {
$qry = new PgQuery( "SELECT tz_locn FROM time_zone WHERE tz_id = ?;", $this->properties['TZID'] );
if ( $qry->Exec('vEvent') && $qry->rows == 1 ) {
$row = $qry->Fetch();
$this->tz_locn = $row->tz_locn;
}
}
if ( !isset($this->tz_locn) ) {
// In case there was no X-LIC-LOCATION defined, let's hope there is something in the TZID
$this->tz_locn = preg_replace('/^.*([a-z]+\/[a-z]+)$/i','$1',$this->properties['TZID'] );
}
if ( isset($c->save_time_zone_defs) && $qry->rows != 1 ) {
$qry2 = new PgQuery( "INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES( ?, ?, ? );",
$this->properties['TZID'], $this->tz_locn, $this->properties['VTIMEZONE'] );
$qry2->Exec("vEvent");
}
}
/**
* Get the value of a property
*/
function Get( $key ) {
return $this->properties[strtoupper($key)];
}
/**
* Put the value of a property
*/
function Put( $key, $value ) {
return $this->properties[strtoupper($key)] = $value;
}
/**
* Returns a PostgreSQL Date Format string suitable for returning iCal dates
*/
function SqlDateFormat() {
return "'IYYYMMDD\"T\"HH24MISS'";
}
/**
* Returns a PostgreSQL Date Format string suitable for returning iCal durations
* - this doesn't work for negative intervals, but events should not have such!
*/
function SqlDurationFormat() {
return "'\"PT\"HH24\"H\"MI\"M\"'";
}
/*
BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
VERSION:2.0
BEGIN:VEVENT
UID:20060918T005755Z-21151-1000-1-7@ubu
DTSTAMP:20060918T005755Z
DTSTART;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
20060918T153000
DTEND;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
20060918T160000
SUMMARY:Lunch
X-EVOLUTION-CALDAV-HREF:http:
//andrew@mycaldav/caldav.php/andrew/20060918T005757Z.ics
BEGIN:VALARM
X-EVOLUTION-ALARM-UID:20060918T005755Z-21149-1000-1-12@ubu
ACTION:DISPLAY
TRIGGER;VALUE=DURATION;RELATED=START:-PT15M
DESCRIPTION:Lunch
END:VALARM
END:VEVENT
BEGIN:VTIMEZONE
TZID:/softwarestudio.org/Olson_20011030_5/Pacific/Auckland
X-LIC-LOCATION:Pacific/Auckland
BEGIN:STANDARD
TZOFFSETFROM:+1300
TZOFFSETTO:+1200
TZNAME:NZST
DTSTART:19700315T030000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SU;BYMONTH=3
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+1200
TZOFFSETTO:+1300
TZNAME:NZDT
DTSTART:19701004T020000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=10
END:DAYLIGHT
END:VTIMEZONE
END:VCALENDAR
*/
/**
* Render the vEvent object as a text string which is a single VEVENT
*/
function Render( ) {
$interesting = array( "uid", "dtstamp", "dtstart", "dtend", "duration", "summary",
"location", "description", "action", "class", "transp", "sequence");
$result = <<<EOTXT
BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Catalyst.Net.NZ//NONSGML AWL Calendar//EN
VERSION:2.0
BEGIN:VEVENT
EOTXT;
foreach( $interesting AS $k => $v ) {
$v = strtoupper($v);
if ( isset($this->properties[$v]) )
$result .= sprintf("%s:%s\n", $v, $this->properties[$v]);
}
$result .= <<<EOTXT
END:VEVENT
END:VCALENDAR
EOTXT;
/*
$result = sprintf( $format,
$this->properties['UID'],
$this->properties['DTSTART'],
$this->properties['DURATION'],
$this->properties['SUMMARY'],
$this->properties['LOCATION']
);
*/
return $result;
}
}
?>

View File

@ -43,11 +43,13 @@
<item url="htdocs/relationships.php" uploadstatus="1" />
<item url="dba/caldav_functions.sql" uploadstatus="1" />
<item url="htdocs/user.php" uploadstatus="1" />
<item url="htdocs/role.php" uploadstatus="1" />
<item url="inc/Role.php" uploadstatus="1" />
<item url="inc/RSCDSUser.php" uploadstatus="1" />
<item url="inc/caldav-PROPFIND.php" />
<item url="inc/caldav-MKCALENDAR.php" />
<item url="inc/XMLElement.php" />
<item url="inc/caldav-PROPFIND.php" uploadstatus="1" />
<item url="inc/caldav-MKCALENDAR.php" uploadstatus="1" />
<item url="inc/XMLElement.php" uploadstatus="1" />
<item url="htdocs/js/" />
<item url="htdocs/js/browse.js" />
<item url="htdocs/css/browse.css" />
<item url="htdocs/css/" />
</project>
</webproject>