mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-05-26 02:44:29 +00:00
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:
parent
db963fadf6
commit
f649e1b49d
@ -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 (
|
||||
|
||||
@ -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
2
debian/control
vendored
@ -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
|
||||
|
||||
@ -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
43
htdocs/css/browse.css
Normal 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
46
htdocs/js/browse.js
Normal 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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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> </td><td>\n";
|
||||
$html .= $browser->Render();
|
||||
$html .= "</td></tr>\n";
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
@ -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";
|
||||
}
|
||||
|
||||
@ -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");
|
||||
|
||||
?>
|
||||
@ -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");
|
||||
?>
|
||||
@ -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;
|
||||
|
||||
?>
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
288
inc/vEvent.php
288
inc/vEvent.php
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
12
rscds.webprj
12
rscds.webprj
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user