From f00f07eb8fd88fb563c621384c670c3237087d68 Mon Sep 17 00:00:00 2001 From: Maxime Delorme Date: Sat, 19 May 2007 11:40:37 +1200 Subject: [PATCH] Changes to allow processing of a server-based directory of calendars to load for many people. Moving this away from the user maintenance screen. --- htdocs/tools.php | 156 +++++++++++++++++++++++++++ htdocs/user.php | 42 ++++---- inc/caldav-PUT-collection.php | 120 --------------------- inc/caldav-PUT-functions.php | 196 ++++++++++++++++++++++++++++++++++ inc/caldav-PUT.php | 51 ++------- inc/interactive-page.php | 6 +- 6 files changed, 381 insertions(+), 190 deletions(-) create mode 100644 htdocs/tools.php delete mode 100644 inc/caldav-PUT-collection.php create mode 100644 inc/caldav-PUT-functions.php diff --git a/htdocs/tools.php b/htdocs/tools.php new file mode 100644 index 00000000..f8b199d4 --- /dev/null +++ b/htdocs/tools.php @@ -0,0 +1,156 @@ + +* @copyright Maxime Delorme +* @license http://gnu.org/copyleft/gpl.html GNU GPL v2 +*/ + +require_once("../inc/always.php"); +require_once("RSCDSSession.php"); +$session->LoginRequired(); + +require_once("DataEntry.php"); +require_once("interactive-page.php"); +require_once("classBrowser.php"); + +if ( !$session->AllowedTo("Admin" ) ) + exit; + +if(isset($_POST['Sync_LDAP'])){ + sync_LDAP(); +} + +if(isset($_POST['import_from_directory'])){ + Tools::importFromDirectory(); +} + + +include("page-header.php"); +$Tools = new Tools(); + +$Tools->render(); +class Tools { + + function render(){ + global $c; + echo $this->renderImportFromDirectory(); + if($c->authenticate_hook['call'] == 'LDAP_check'){ + echo $this->renderSyncLDAP(); + } + } + + function renderSyncLDAP(){ + $html = '
'; + $html .= '

'.translate('Sync LDAP with RSCDS') .'

'; + + $data = (object) array('directory_path' => '/path/to/your/ics/files','calendar_path' => 'home'); + $ef = new EntryForm( $_SERVER['REQUEST_URI'],$data , true,true ); + $html .= "\n"; + $html .= $ef->StartForm( array("autocomplete" => "off" ) ); + $html .= sprintf( "\n", + translate("This operation does the following: then + ")); + $html .= "
%s
\n"; + + $html .= $ef->SubmitButton( "Sync_LDAP", 'submit'); + $html .= $ef->EndForm(); + + $html .= "
"; + return $html; + } + + function renderImportFromDirectory(){ + $html = '
'; + $html .= '

'.translate('Import all .ics files of a directory') .'

'; + + $data = (object) array('directory_path' => '/path/to/your/ics/files','calendar_path' => 'home'); + $ef = new EntryForm( $_SERVER['REQUEST_URI'],$data , true,true ); + $html .= "\n"; + $html .= $ef->StartForm( array("autocomplete" => "off" ) ); + + $html .= $ef->DataEntryLine( translate("path to store your ics"), "%s", "text", "calendar_path", + array( "size" => 20, + "title" => translate("Set the path to store your ics e.g. 'home' will be referenced as /caldav.php/me/home/"), + "help" => translate("WARNING: all events in this path will be deleted before inserting allof the ics file") + ) + , '' ); + + $html .= $ef->DataEntryLine( translate("Directory on the server"), "%s", "text", "directory_path", + array( "size" => 20, "title" => translate("The path on the server where your .ics files are."))); + + $html .= "
\n"; + $html .= $ef->SubmitButton( "import_from_directory", 'submit'); + $html .= $ef->EndForm(); + + $html .= "
"; + return $html; + } + + function importFromDirectory(){ + global $c; + if(!isset($_POST["calendar_path"])){ + dbg_error_log( "importFromDirectory", "calendar path not given"); + return ; + } + $path_ics = $_POST["calendar_path"]; + if ( substr($path_ics,-1,1) != '/' ) $path_ics .= '/'; // ensure that we target a collection + if ( substr($path_ics,0,1) != '/' ) $path_ics = '/'.$path_ics; // ensure that we target a collection + + if(!isset($_POST["directory_path"])){ + dbg_error_log( "importFromDirectory", "directory path not given"); + return ; + } + $dir = $_POST["directory_path"]; + if(!is_readable($dir)){ + $c->messages[] = sprintf(i18n('directory %s is not readable'),htmlentities($dir)); + dbg_error_log( "importFromDirectory", "directory is not readable"); + return ; + } + if ($handle = opendir($dir)) { + while (false !== ($file = readdir($handle))) { + if ($file != "." && $file != ".." && substr($file,-4) != '.ics') { + continue; + } + if(!is_readable($dir.'/'.$file)){ + dbg_error_log( "importFromDirectory", "ics file '%s' is not readable",$dir .'/'.$file); + continue; + } + $ics = file_get_contents($dir.'/'.$file); + $ics = trim($ics); + + + if ( $ics != '' ) { + include_once('check_UTF8.php'); + if ( check_string($ics) ) { + $path = "/".substr($file,0,-4).$path_ics; + dbg_error_log( "importFromDirectory", "importing to $path"); + include_once("caldav-PUT-functions.php"); + $qry = new PgQuery( "SELECT * FROM usr WHERE username = ?;", substr($file,0,-4) ); + if ( $qry->Exec("importFromDirectory") && $user = $qry->Fetch() ) { + $user_no = $user->user_no; + } + controlRequestContainer(substr($file,0,-4),$user_no, $path,false); + import_collection($ics,$user_no,$path,false); + $c->messages[] = sprintf(i18n("all events of user %s were deleted and replaced by those from file %s"),substr($file,0,-4),$dir.'/'.$file); + } + else { + $c->messages[] = sprintf(i18n("the file %s is not UTF-8 encoded, please check error for more details"),$dir.'/'.$file); + } + } + } + closedir($handle); + } + } +} + +include("page-footer.php"); + +?> \ No newline at end of file diff --git a/htdocs/user.php b/htdocs/user.php index f949e5d0..b36a71ff 100644 --- a/htdocs/user.php +++ b/htdocs/user.php @@ -11,7 +11,7 @@ $user_no = intval(isset($_POST['user_no']) ? $_POST['user_no'] : $_GET['user_no'] ); $user = new RSCDSUser($user_no); if ( $user->user_no == 0 ) { - $c->page_title = ( $user_no != "" ? i18n("User Unavailable") : i18n("New User") ); + $c->page_title = ( $user_no != "" ? translate("User Unavailable") : translate("New User") ); } else { $c->page_title = sprintf("%s (%s)", $user->Get("fullname"), $user->Get("username")); @@ -29,7 +29,7 @@ $user = new RSCDSUser($user->user_no); $user->EditMode = true; if ( $user->user_no == 0 ) { - $c->page_title = ( $user_no != "" ? "User Unavailable" : "New User" ); + $c->page_title = ( $user_no != "" ? translate("User Unavailable") : translate("New User") ); } else { $c->page_title = $user->Get("user_no"). " - " . $user->Get("fullname"); @@ -42,6 +42,23 @@ if ( substr($path_ics,-1,1) != '/' ) $path_ics .= '/'; // ensure that we target a collection if ( substr($path_ics,0,1) != '/' ) $path_ics = '/'.$path_ics; // ensure that we target a collection + + if ( $ics !='' ) { + /** + * If the user has uploaded a .ics file as a calendar, we fake this out + * as if it were a "PUT" request against a collection. This is something + * of a hack. It works though :-) + */ + include('check_UTF8.php'); + if ( check_string($ics) ) { + $path = "/".$user->Get("username").$path_ics; + include("caldav-PUT-functions.php"); + controlRequestContainer($user->Get("username"),$user->user_no, $path,false); + import_collection($ics,$user->user_no,$path,false); + $c->messages[] = sprintf(translate("All events of user %s were deleted and replaced by those from the file."),$user->Get("username")); + } else + $c->messages[] = sprintf(translate("The file %s is not UTF-8 encoded, please check the error for more details."),$dir.'/'.$file); + } } else { unset($ics); @@ -74,25 +91,4 @@ include("page-header.php"); echo $user->Render($c->page_title); include("page-footer.php"); - if ( $ics !='' ) { - /** - * If the user has uploaded a .ics file as a calendar, we fake this out - * as if it were a "PUT" request against a collection. This is something - * of a hack, especially since it doesn't provide quality feedback to the - * user about what is happening. It works though :-) - * - * TODO: Extract the important code from caldav-PUT-collection.php into a - * function which can be included here and shared. That way we can perhaps - * show the user a useful error message, if needed. - */ - include('check_UTF8.php'); - if ( check_string($ics) ) { - $_SERVER['REQUEST_METHOD'] = 'PUT'; - $_SERVER['PATH_INFO'] = "/".$user->Get("username").$path_ics; - require_once("CalDAVRequest.php"); - $request = new CalDAVRequest(); - $request->raw_post = $ics; - include_once("caldav-PUT.php"); - } - } ?> diff --git a/inc/caldav-PUT-collection.php b/inc/caldav-PUT-collection.php deleted file mode 100644 index 4ede3464..00000000 --- a/inc/caldav-PUT-collection.php +++ /dev/null @@ -1,120 +0,0 @@ -raw_post ); -$fh = fopen('/tmp/PUT-2.txt','w'); -fwrite($fh,$icalendar); -fclose($fh); - -$lines = preg_split('/\r?\n/', $icalendar ); - -$events = array(); -$timezones = array(); - -$current = ""; -$state = ""; -$tzid = 'unknown'; -foreach( $lines AS $lno => $line ) { - dbg_error_log( "PUT", "CalendarLine[%04d] - %s: %s", $lno, $state, $line ); - if ( $state == "" ) { - if ( preg_match( '/^BEGIN:(VEVENT|VTIMEZONE|VTODO)$/', $line, $matches ) ) { - $current .= $line."\n"; - $state = $matches[1]; - } - } - else { - $current .= $line."\n"; - if ( $line == "END:$state" ) { - switch ( $state ) { - case 'VEVENT': - $events[] = array( 'data' => $current, 'tzid' => $tzid ); - break; - case 'VTODO': - $events[] = array( 'data' => $current, 'tzid' => $tzid ); - break; - case 'VTIMEZONE': - $timezones[$tzid] = $current; - break; - } - $state = ""; - $current = ""; - $tzid = 'unknown'; - } - else if ( preg_match( '/TZID=([^:]+)(:|$)/', $line, $matches ) ) { - $tzid = $matches[1]; - } - } -} - -function rollback_on_error() { - global $request; - $qry = new PgQuery("ROLLBACK;"); $qry->Exec("PUT"); - $request->DoResponse( 500, translate("Database error") ); -} - -$qry = new PgQuery("BEGIN; DELETE FROM calendar_item WHERE user_no=? AND dav_name ~ ?; DELETE FROM caldav_data WHERE user_no=? AND dav_name ~ ?;", $request->user_no, $request->path.'[^/]+$', $request->user_no, $request->path.'[^/]+$'); -if ( !$qry->Exec("PUT") ) rollback_on_error(); - -foreach( $events AS $k => $event ) { - dbg_error_log( "PUT", "Putting event %d with data: %s", $k, $event['data'] ); - $icalendar = iCalendar::iCalHeader() . $event['data'] . $timezones[$event['tzid']] . iCalendar::iCalFooter(); - $ic = new iCalendar( array( 'icalendar' => $icalendar ) ); - $etag = md5($icalendar); - $event_path = sprintf( "%s%d.ics", $request->path, $k); - $qry = new PgQuery( "INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified ) VALUES( ?, ?, ?, ?, ?, ?, current_timestamp, current_timestamp )", - $request->user_no, $event_path, $etag, $icalendar, $ic->type, $session->user_no ); - if ( !$qry->Exec("PUT") ) rollback_on_error(); - - $sql = ""; - if ( preg_match(':^(Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Brazil|Canada|Chile|Etc|Europe|Indian|Mexico|Mideast|Pacific|US)/[a-z]+$:i', $ic->tz_locn ) ) { - // We only set the timezone if it looks reasonable enough for us - $sql = ( $ic->tz_locn == '' ? '' : "SET TIMEZONE TO ".qpg($ic->tz_locn).";" ); - } - - $dtstart = $ic->Get('dtstart'); - if ( (!isset($dtstart) || $dtstart == "") && $ic->Get('due') != "" ) { - $dtstart = $ic->Get('due'); - } - - $dtend = $ic->Get('dtend'); - if ( (!isset($dtend) || "$dtend" == "") && $ic->Get('duration') != "" AND $dtstart != "" ) { - $duration = preg_replace( '#[PT]#', ' ', $ic->Get('duration') ); - $dtend = '('.qpg($dtstart).'::timestamp with time zone + '.qpg($duration).'::interval)'; - } - else { - dbg_error_log( "PUT", " DTEND: '%s', DTSTART: '%s', DURATION: '%s'", $dtend, $dtstart, $ic->Get('duration') ); - $dtend = qpg($dtend); - } - - $last_modified = $ic->Get("last-modified"); - if ( !isset($last_modified) || $last_modified == '' ) { - $last_modified = gmdate( 'Ymd\THis\Z' ); - } - - $dtstamp = $ic->Get("dtstamp"); - if ( !isset($dtstamp) || $dtstamp == '' ) { - $dtstamp = $last_modified; - } - - $sql .= <<user_no, $event_path, $etag, $ic->Get('uid'), $dtstamp, - $ic->Get('dtstart'), $ic->Get('summary'), $ic->Get('location'), - $ic->Get('class'), $ic->Get('transp'), $ic->Get('description'), $ic->Get('rrule'), $ic->Get('tz_id'), - $last_modified, $ic->Get('url'), $ic->Get('priority'), $ic->Get('created'), - $ic->Get('due'), $ic->Get('percent-complete') - ); - if ( !$qry->Exec("PUT") ) rollback_on_error(); -} - -$qry = new PgQuery("COMMIT;"); -if ( !$qry->Exec("PUT") ) rollback_on_error(); - -$request->DoResponse( 200 ); - -?> \ No newline at end of file diff --git a/inc/caldav-PUT-functions.php b/inc/caldav-PUT-functions.php new file mode 100644 index 00000000..87887011 --- /dev/null +++ b/inc/caldav-PUT-functions.php @@ -0,0 +1,196 @@ +Exec("PUT") ) { + if($context){ + global $request; + $request->DoResponse( 500, translate("Error querying database.") ); + } + else { + global $c; + $c->messages[] = sprintf("Status: %d, Message: %s, User: %d, Path: %s", 500, translate("Error querying database."),$user_no,$path); + } + } + if ( $qry->rows == 0 ) { + if ( preg_match( '#^(.*/)([^/]+/)$#', $request_container, $matches ) ) {//( + $parent_container = $matches[1]; + $displayname = $matches[2]; + } + $sql = "INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar, created, modified ) VALUES( ?, ?, ?, ?, ?, TRUE, current_timestamp, current_timestamp );"; + $qry = new PgQuery( $sql, $user_no, $parent_container, $request_container, md5($user_no. $request_container), $displayname ); + $qry->Exec("PUT"); + } + } + //I don't think useful because there is already a $request->IsCollection() that does the job + //Andrew If you think it's unuseful then remove $is_collection in this function + return $is_collection; +} + +/** +* This function launches an error +* @param int $user_no the user wich will receive this ics file +* @param string $path the $path where it will be store such as /user_foo/home/ +* @param boolean $context is true if this function is called from a way where $request is defined +*/ +function rollback_on_error($context,$user_no,$path) { + $qry = new PgQuery("ROLLBACK;"); $qry->Exec("PUT-collection"); + if($context){ + global $request; + $request->DoResponse( 500, translate("Database error") ); + } + else { + global $c; + $c->messages[] = sprintf("Status: %d, Message: %s, User: %d, Path: %s", 500, translate("Database error"),$user_no,$path); + } +} + + +/** +* This function will import a whole calendar +* @param string $ics_content the ics file to import +* @param int $user_no the user wich will receive this ics file +* @param string $path the $path where it will be store such as /user_foo/home/ +* @param boolean $context is true if this function is called from a way where $request is defined +*/ +function import_collection($ics_content, $user_no, $path,$context){ + // According to RFC2445 we should always end with CRLF, but the CalDAV spec says + // that normalising XML parses often muck with it and may remove the CR. + $icalendar = preg_replace('/\r?\n /', '', $ics_content ); + $fh = fopen('/tmp/PUT-2.txt','w'); + fwrite($fh,$icalendar); + fclose($fh); + + $lines = preg_split('/\r?\n/', $icalendar ); + + $events = array(); + $timezones = array(); + + $current = ""; + $state = ""; + $tzid = 'unknown'; + foreach( $lines AS $lno => $line ) { + dbg_error_log( "PUT", "CalendarLine[%04d] - %s: %s", $lno, $state, $line ); + if ( $state == "" ) { + if ( preg_match( '/^BEGIN:(VEVENT|VTIMEZONE|VTODO)$/', $line, $matches ) ) { + $current .= $line."\n"; + $state = $matches[1]; + } + } + else { + $current .= $line."\n"; + if ( $line == "END:$state" ) { + switch ( $state ) { + case 'VEVENT': + $events[] = array( 'data' => $current, 'tzid' => $tzid ); + break; + case 'VTODO': + $events[] = array( 'data' => $current, 'tzid' => $tzid ); + break; + case 'VTIMEZONE': + $timezones[$tzid] = $current; + break; + } + $state = ""; + $current = ""; + $tzid = 'unknown'; + } + else if ( preg_match( '/TZID=([^:]+)(:|$)/', $line, $matches ) ) { + $tzid = $matches[1]; + } + } + } + $qry = new PgQuery("BEGIN; DELETE FROM calendar_item WHERE user_no=? AND dav_name ~ ?; DELETE FROM caldav_data WHERE user_no=? AND dav_name ~ ?;", $user_no, $path.'[^/]+$', $user_no, $path.'[^/]+$'); + if ( !$qry->Exec("PUT") ) rollback_on_error(); + + foreach( $events AS $k => $event ) { + dbg_error_log( "PUT", "Putting event %d with data: %s", $k, $event['data'] ); + $icalendar = iCalendar::iCalHeader() . $event['data'] . $timezones[$event['tzid']] . iCalendar::iCalFooter(); + $ic = new iCalendar( array( 'icalendar' => $icalendar ) ); + $etag = md5($icalendar); + $event_path = sprintf( "%s%d.ics", $path, $k); + $qry = new PgQuery( "INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified ) VALUES( ?, ?, ?, ?, ?, ?, current_timestamp, current_timestamp )", + $user_no, $event_path, $etag, $icalendar, $ic->type, $session->user_no ); + if ( !$qry->Exec("PUT") ) rollback_on_error(); + + $sql = ""; + if ( preg_match(':^(Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Brazil|Canada|Chile|Etc|Europe|Indian|Mexico|Mideast|Pacific|US)/[a-z]+$:i', $ic->tz_locn ) ) { + // We only set the timezone if it looks reasonable enough for us + $sql = ( $ic->tz_locn == '' ? '' : "SET TIMEZONE TO ".qpg($ic->tz_locn).";" ); + } + + $dtstart = $ic->Get('dtstart'); + if ( (!isset($dtstart) || $dtstart == "") && $ic->Get('due') != "" ) { + $dtstart = $ic->Get('due'); + } + + $dtend = $ic->Get('dtend'); + if ( (!isset($dtend) || "$dtend" == "") && $ic->Get('duration') != "" AND $dtstart != "" ) { + $duration = preg_replace( '#[PT]#', ' ', $ic->Get('duration') ); + $dtend = '('.qpg($dtstart).'::timestamp with time zone + '.qpg($duration).'::interval)'; + } + else { + dbg_error_log( "PUT", " DTEND: '%s', DTSTART: '%s', DURATION: '%s'", $dtend, $dtstart, $ic->Get('duration') ); + $dtend = qpg($dtend); + } + + $last_modified = $ic->Get("last-modified"); + if ( !isset($last_modified) || $last_modified == '' ) { + $last_modified = gmdate( 'Ymd\THis\Z' ); + } + + $dtstamp = $ic->Get("dtstamp"); + if ( !isset($dtstamp) || $dtstamp == '' ) { + $dtstamp = $last_modified; + } + + $sql .= <<Get('uid'), $dtstamp, + $ic->Get('dtstart'), $ic->Get('summary'), $ic->Get('location'), + $ic->Get('class'), $ic->Get('transp'), $ic->Get('description'), $ic->Get('rrule'), $ic->Get('tz_id'), + $last_modified, $ic->Get('url'), $ic->Get('priority'), $ic->Get('created'), + $ic->Get('due'), $ic->Get('percent-complete') + ); + if ( !$qry->Exec("PUT") ) rollback_on_error($context,$user_no,$path); + } + + $qry = new PgQuery("COMMIT;"); + if ( !$qry->Exec("PUT") ) rollback_on_error($context,$user_no,$path); +} +?> \ No newline at end of file diff --git a/inc/caldav-PUT.php b/inc/caldav-PUT.php index dd73eb1b..d16d3f7d 100644 --- a/inc/caldav-PUT.php +++ b/inc/caldav-PUT.php @@ -18,60 +18,23 @@ $fh = fopen('/tmp/PUT.txt','w'); fwrite($fh,$request->raw_post); fclose($fh); -if ( isset($request_container) ) unset($request_container); -if ( isset($request_name) ) unset($request_name); -// Check to see if the path is like /foo /foo/bar or /foo/bar/baz etc. (not ending in a '/', but contains at least one) -if ( preg_match( '#^(.*/)([^/]+)$#', $request->path, $matches ) ) { - $request_container = $matches[1]; // get everything up to the last '/' - $request_name = $matches[2]; // get after the last '/' -} -else { - // In this case we must have a URL with a trailing '/', so it must be a collection. - $request_container = $request->path; - $request_name = ""; -} +include_once('caldav-PUT-functions.php'); +$is_collection = controlRequestContainer($request->username,$request->user_no, $request->path,true); $lock_opener = $request->FailIfLocked(); -/** -* Before we write the event, we check the container exists, creating it if it doesn't -*/ -if ( $request_container == "/$request->username/" ) { - /** - * Well, it exists, and we support it, but it is against the CalDAV spec - */ - dbg_error_log( "WARN", " Storing events directly in user's base folders is not recommended!"); -} -else { - $sql = "SELECT * FROM collection WHERE user_no = ? AND dav_name = ?;"; - $qry = new PgQuery( $sql, $request->user_no, $request_container ); - if ( ! $qry->Exec("PUT") ) { - $request->DoResponse( 500, translate("Error querying database.") ); - } - if ( $qry->rows == 0 ) { - if ( preg_match( '#^(.*/)([^/]+/)$#', $request_container, $matches ) ) { - $parent_container = $matches[1]; - $displayname = $matches[2]; - } - $sql = "INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar, created, modified ) VALUES( ?, ?, ?, ?, ?, TRUE, current_timestamp, current_timestamp );"; - $qry = new PgQuery( $sql, $request->user_no, $parent_container, $request_container, md5($request->user_no. $request_container), $displayname ); - $qry->Exec("PUT"); - } -} - -$etag = md5($request->raw_post); -include_once("iCalendar.php"); - -if ( $request_name == "" ) { +if ( $is_collection ) { /** * CalDAV does not define the result of a PUT on a collection. We treat that - * as an import, but hide the code somewhere completely separate + * as an import. The code is in caldav-PUT-functions.php */ - include_once("caldav-PUT-collection.php"); + import_collection($request->raw_post,$request->user_no,$request->path,true); + $request->DoResponse( 200 ); return; } +$etag = md5($request->raw_post); $ic = new iCalendar(array( 'icalendar' => $request->raw_post )); dbg_log_array( "PUT", 'EVENT', $ic->properties['VCALENDAR'][0], true ); diff --git a/inc/interactive-page.php b/inc/interactive-page.php index ec30942f..fba152df 100644 --- a/inc/interactive-page.php +++ b/inc/interactive-page.php @@ -2,10 +2,10 @@ require_once("MenuSet.php"); $page_menu = new MenuSet('menu', 'menu', 'menu_active'); $page_menu->AddOption(translate("Home"),"$c->base_url/",translate("Browse all users"), false, 3900 ); -//if ( $session->AllowedTo("Admin" ) { +if ( $session->AllowedTo("Admin" )) { // $page_menu->AddOption(translate("Setup"),"$c->base_url/setup.php",translate("Setup RSCDS"), false, 5000 ); -// $page_menu->AddOption(translate("Operations"),"$c->base_url/tools.php",translate("Operations on your calendar"), false, 5200 ); -//} + $page_menu->AddOption(translate("Operations"),"$c->base_url/tools.php",translate("Operations on your calendar"), false, 5200 ); +} $page_menu->AddOption(translate("Logout"),"$c->base_url/?logout",translate("Log out of the").$c->system_name, false, 5400 ); $page_menu->AddOption(translate("Help"),"$c->base_url/help.php",translate("Help on something or other"), false, 8500 ); $page_menu->AddOption(translate("Report Bug"),"http://sourceforge.net/tracker/?func=add&group_id=179845&atid=890785",translate("Report a bug in the system"), false, 9000 );