Page MenuHomePhorge

No OneTemporary

Size
76 KB
Referenced Files
None
Subscribers
None
diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index ae75a937..aa4d7eac 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -1,1317 +1,1312 @@
<?php
/*
+-------------------------------------------------------------------------+
| Calendar plugin for Roundcube |
| Version 0.3 beta |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License version 2 |
| as published by the Free Software Foundation. |
| |
| This program 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 General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License along |
| with this program; if not, write to the Free Software Foundation, Inc., |
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| |
+-------------------------------------------------------------------------+
| Author: Lazlo Westerhof <hello@lazlo.me> |
| Thomas Bruederli <roundcube@gmail.com> |
+-------------------------------------------------------------------------+
*/
class calendar extends rcube_plugin
{
const FREEBUSY_UNKNOWN = 0;
const FREEBUSY_FREE = 1;
const FREEBUSY_BUSY = 2;
const FREEBUSY_TENTATIVE = 3;
const FREEBUSY_OOF = 4;
public $task = '?(?!login|logout).*';
public $rc;
public $driver;
public $home; // declare public to be used in other classes
public $urlbase;
public $timezone;
public $ical;
public $ui;
public $defaults = array(
'calendar_default_view' => "agendaWeek",
'calendar_date_format' => "yyyy-MM-dd",
'calendar_date_short' => "M-d",
'calendar_date_long' => "MMM d yyyy",
'calendar_date_agenda' => "ddd MM-dd",
'calendar_time_format' => "HH:mm",
'calendar_timeslots' => 2,
'calendar_first_day' => 1,
'calendar_first_hour' => 6,
'calendar_work_start' => 6,
'calendar_work_end' => 18,
);
private $default_categories = array(
'Personal' => 'c0c0c0',
'Work' => 'ff0000',
'Family' => '00ff00',
'Holiday' => 'ff6600',
);
/**
* Plugin initialization.
*/
function init()
{
$this->rc = rcmail::get_instance();
$this->register_task('calendar', 'calendar');
// load calendar configuration
$this->load_config();
// load localizations
$this->add_texts('localization/', $this->rc->task == 'calendar' && (!$this->rc->action || $this->rc->action == 'print'));
// set user's timezone
if ($this->rc->config->get('timezone') === 'auto')
$this->timezone = isset($_SESSION['timezone']) ? $_SESSION['timezone'] : date('Z');
else
$this->timezone = ($this->rc->config->get('timezone') + intval($this->rc->config->get('dst_active')));
$this->gmt_offset = $this->timezone * 3600;
require($this->home . '/lib/calendar_ui.php');
$this->ui = new calendar_ui($this);
// load Calendar user interface which includes jquery-ui
if (!$this->rc->output->ajax_call && !$this->rc->output->env['framed']) {
$this->require_plugin('jqueryui');
$this->ui->init();
// settings are required in every GUI step
$this->rc->output->set_env('calendar_settings', $this->load_settings());
}
if ($this->rc->task == 'calendar' && $this->rc->action != 'save-pref') {
if ($this->rc->action != 'upload') {
$this->load_driver();
// load iCalendar functions
require($this->home . '/lib/calendar_ical.php');
$this->ical = new calendar_ical($this->rc, $this->driver);
}
// register calendar actions
$this->register_action('index', array($this, 'calendar_view'));
$this->register_action('event', array($this, 'event_action'));
$this->register_action('calendar', array($this, 'calendar_action'));
$this->register_action('load_events', array($this, 'load_events'));
$this->register_action('export_events', array($this, 'export_events'));
$this->register_action('upload', array($this, 'attachment_upload'));
$this->register_action('get-attachment', array($this, 'attachment_get'));
$this->register_action('freebusy-status', array($this, 'freebusy_status'));
$this->register_action('freebusy-times', array($this, 'freebusy_times'));
$this->register_action('randomdata', array($this, 'generate_randomdata'));
$this->register_action('print', array($this,'print_view'));
// remove undo information...
if ($undo = $_SESSION['calendar_event_undo']) {
// ...after timeout
$undo_time = $this->rc->config->get('undo_timeout', 0);
if ($undo['ts'] < time() - $undo_time) {
$this->rc->session->remove('calendar_event_undo');
// @TODO: do EXPUNGE on kolab objects?
}
}
}
else if ($this->rc->task == 'settings') {
// add hooks for Calendar settings
$this->add_hook('preferences_sections_list', array($this, 'preferences_sections_list'));
$this->add_hook('preferences_list', array($this, 'preferences_list'));
$this->add_hook('preferences_save', array($this, 'preferences_save'));
}
// add hook to display alarms
$this->add_hook('keep_alive', array($this, 'keep_alive'));
}
/**
* Helper method to load the backend driver according to local config
*/
private function load_driver()
{
if (is_object($this->driver))
return;
$driver_name = $this->rc->config->get('calendar_driver', 'database');
$driver_class = $driver_name . '_driver';
require_once($this->home . '/drivers/calendar_driver.php');
require_once($this->home . '/drivers/' . $driver_name . '/' . $driver_class . '.php');
switch ($driver_name) {
case "kolab":
$this->require_plugin('kolab_core');
default:
$this->driver = new $driver_class($this);
break;
}
}
/**
* Render the main calendar view from skin template
*/
function calendar_view()
{
$this->rc->output->set_pagetitle($this->gettext('calendar'));
// Add CSS stylesheets to the page header
$this->ui->addCSS();
// Add JS files to the page header
$this->ui->addJS();
$this->register_handler('plugin.calendar_css', array($this->ui, 'calendar_css'));
$this->register_handler('plugin.calendar_list', array($this->ui, 'calendar_list'));
$this->register_handler('plugin.calendar_select', array($this->ui, 'calendar_select'));
$this->register_handler('plugin.category_select', array($this->ui, 'category_select'));
$this->register_handler('plugin.freebusy_select', array($this->ui, 'freebusy_select'));
$this->register_handler('plugin.priority_select', array($this->ui, 'priority_select'));
$this->register_handler('plugin.sensitivity_select', array($this->ui, 'sensitivity_select'));
$this->register_handler('plugin.alarm_select', array($this->ui, 'alarm_select'));
$this->register_handler('plugin.snooze_select', array($this->ui, 'snooze_select'));
$this->register_handler('plugin.recurrence_form', array($this->ui, 'recurrence_form'));
$this->register_handler('plugin.attachments_form', array($this->ui, 'attachments_form'));
$this->register_handler('plugin.attachments_list', array($this->ui, 'attachments_list'));
$this->register_handler('plugin.attendees_list', array($this->ui, 'attendees_list'));
$this->register_handler('plugin.attendees_form', array($this->ui, 'attendees_form'));
$this->register_handler('plugin.attendees_freebusy_table', array($this->ui, 'attendees_freebusy_table'));
$this->register_handler('plugin.edit_recurring_warning', array($this->ui, 'recurring_event_warning'));
$this->register_handler('plugin.searchform', array($this->rc->output, 'search_form')); // use generic method from rcube_template
$this->rc->output->add_label('low','normal','high','delete','cancel','uploading','noemailwarning');
$this->rc->output->send("calendar.calendar");
}
/**
* Handler for preferences_sections_list hook.
* Adds Calendar settings sections into preferences sections list.
*
* @param array Original parameters
* @return array Modified parameters
*/
function preferences_sections_list($p)
{
$p['list']['calendar'] = array(
'id' => 'calendar', 'section' => $this->gettext('calendar'),
);
return $p;
}
/**
* Handler for preferences_list hook.
* Adds options blocks into Calendar settings sections in Preferences.
*
* @param array Original parameters
* @return array Modified parameters
*/
function preferences_list($p)
{
if ($p['section'] == 'calendar') {
$this->load_driver();
$p['blocks']['view']['name'] = $this->gettext('mainoptions');
$field_id = 'rcmfd_default_view';
$select = new html_select(array('name' => '_default_view', 'id' => $field_id));
$select->add($this->gettext('day'), "agendaDay");
$select->add($this->gettext('week'), "agendaWeek");
$select->add($this->gettext('month'), "month");
$select->add($this->gettext('agenda'), "table");
$p['blocks']['view']['options']['default_view'] = array(
'title' => html::label($field_id, Q($this->gettext('default_view'))),
'content' => $select->show($this->rc->config->get('calendar_default_view', "agendaWeek")),
);
/*
$field_id = 'rcmfd_time_format';
$choices = array('HH:mm', 'H:mm', 'h:mmt');
$select = new html_select(array('name' => '_time_format', 'id' => $field_id));
$select->add($choices);
$p['blocks']['view']['options']['time_format'] = array(
'title' => html::label($field_id, Q($this->gettext('time_format'))),
'content' => $select->show($this->rc->config->get('calendar_time_format', "HH:mm")),
);
*/
$field_id = 'rcmfd_timeslot';
$choices = array('1', '2', '3', '4', '6');
$select = new html_select(array('name' => '_timeslots', 'id' => $field_id));
$select->add($choices);
$p['blocks']['view']['options']['timeslots'] = array(
'title' => html::label($field_id, Q($this->gettext('timeslots'))),
'content' => $select->show($this->rc->config->get('calendar_timeslots', 2)),
);
$field_id = 'rcmfd_firstday';
$select = new html_select(array('name' => '_first_day', 'id' => $field_id));
$select->add(rcube_label('sunday'), '0');
$select->add(rcube_label('monday'), '1');
$select->add(rcube_label('tuesday'), '2');
$select->add(rcube_label('wednesday'), '3');
$select->add(rcube_label('thursday'), '4');
$select->add(rcube_label('friday'), '5');
$select->add(rcube_label('saturday'), '6');
$p['blocks']['view']['options']['first_day'] = array(
'title' => html::label($field_id, Q($this->gettext('first_day'))),
'content' => $select->show($this->rc->config->get('calendar_first_day', 1)),
);
$field_id = 'rcmfd_alarm';
$select_type = new html_select(array('name' => '_alarm_type', 'id' => $field_id));
$select_type->add($this->gettext('none'), '');
foreach ($this->driver->alarm_types as $type)
$select_type->add($this->gettext(strtolower("alarm{$type}option")), $type);
$input_value = new html_inputfield(array('name' => '_alarm_value', 'id' => $field_id . 'value', 'size' => 3));
$select_offset = new html_select(array('name' => '_alarm_offset', 'id' => $field_id . 'offset'));
foreach (array('-M','-H','-D','+M','+H','+D') as $trigger)
$select_offset->add($this->gettext('trigger' . $trigger), $trigger);
$p['blocks']['view']['options']['alarmtype'] = array(
'title' => html::label($field_id, Q($this->gettext('defaultalarmtype'))),
'content' => $select_type->show($this->rc->config->get('calendar_default_alarm_type', '')),
);
$preset = self::parse_alaram_value($this->rc->config->get('calendar_default_alarm_offset', '-15M'));
$p['blocks']['view']['options']['alarmoffset'] = array(
'title' => html::label($field_id . 'value', Q($this->gettext('defaultalarmoffset'))),
'content' => $input_value->show($preset[0]) . ' ' . $select_offset->show($preset[1]),
);
// default calendar selection
$field_id = 'rcmfd_default_calendar';
$select_cal = new html_select(array('name' => '_default_calendar', 'id' => $field_id));
foreach ((array)$this->driver->list_calendars() as $id => $prop) {
if (!$prop['readononly'])
$select_cal->add($prop['name'], strval($id));
}
$p['blocks']['view']['options']['defaultcalendar'] = array(
'title' => html::label($field_id . 'value', Q($this->gettext('defaultcalendar'))),
'content' => $select_cal->show($this->rc->config->get('calendar_default_calendar', '')),
);
// category definitions
if (!$this->driver->categoriesimmutable) {
$p['blocks']['categories']['name'] = $this->gettext('categories');
$categories = (array) $this->rc->config->get('calendar_categories', $this->default_categories);
$categories_list = '';
foreach ($categories as $name => $color) {
$key = md5($name);
$field_class = 'rcmfd_category_' . str_replace(' ', '_', $name);
$category_remove = new html_inputfield(array('type' => 'button', 'value' => 'X', 'class' => 'button', 'onclick' => '$(this).parent().remove()', 'title' => $this->gettext('remove_category')));
$category_name = new html_inputfield(array('name' => "_categories[$key]", 'class' => $field_class, 'size' => 30));
$category_color = new html_inputfield(array('name' => "_colors[$key]", 'class' => "$field_class colors", 'size' => 6));
$categories_list .= html::div(null, $category_name->show($name) . '&nbsp;' . $category_color->show($color) . '&nbsp;' . $category_remove->show());
}
$p['blocks']['categories']['options']['category_' . $name] = array(
'content' => html::div(array('id' => 'calendarcategories'), $categories_list),
);
$field_id = 'rcmfd_new_category';
$new_category = new html_inputfield(array('name' => '_new_category', 'id' => $field_id, 'size' => 30));
$add_category = new html_inputfield(array('type' => 'button', 'class' => 'button', 'value' => $this->gettext('add_category'), 'onclick' => "rcube_calendar_add_category()"));
$p['blocks']['categories']['options']['categories'] = array(
'content' => $new_category->show('') . '&nbsp;' . $add_category->show(),
);
$this->rc->output->add_script('function rcube_calendar_add_category(){
var name = $("#rcmfd_new_category").val();
if (name.length) {
var input = $("<input>").attr("type", "text").attr("name", "_categories[]").attr("size", 30).val(name);
var color = $("<input>").attr("type", "text").attr("name", "_colors[]").attr("size", 6).addClass("colors").val("000000");
var button = $("<input>").attr("type", "button").attr("value", "X").addClass("button").click(function(){ $(this).parent().remove() });
$("<div>").append(input).append("&nbsp;").append(color).append("&nbsp;").append(button).appendTo("#calendarcategories");
color.miniColors();
}
}');
// include color picker
$this->include_script('lib/js/jquery.miniColors.min.js');
$this->include_stylesheet('skins/' .$this->rc->config->get('skin') . '/jquery.miniColors.css');
$this->rc->output->add_script('$("input.colors").miniColors()', 'docready');
}
}
return $p;
}
/**
* Handler for preferences_save hook.
* Executed on Calendar settings form submit.
*
* @param array Original parameters
* @return array Modified parameters
*/
function preferences_save($p)
{
if ($p['section'] == 'calendar') {
$this->load_driver();
// compose default alarm preset value
$alarm_offset = get_input_value('_alarm_offset', RCUBE_INPUT_POST);
$default_alam = $alarm_offset[0] . intval(get_input_value('_alarm_value', RCUBE_INPUT_POST)) . $alarm_offset[1];
$p['prefs'] = array(
'calendar_default_view' => get_input_value('_default_view', RCUBE_INPUT_POST),
'calendar_time_format' => get_input_value('_time_format', RCUBE_INPUT_POST),
'calendar_timeslots' => get_input_value('_timeslots', RCUBE_INPUT_POST),
'calendar_first_day' => get_input_value('_first_day', RCUBE_INPUT_POST),
'calendar_default_alarm_type' => get_input_value('_alarm_type', RCUBE_INPUT_POST),
'calendar_default_alarm_offset' => $default_alam,
'calendar_default_calendar' => get_input_value('_default_calendar', RCUBE_INPUT_POST),
);
// categories
if (!$this->driver->categoriesimmutable) {
$old_categories = $new_categories = array();
foreach ($this->driver->list_categories() as $name => $color) {
$old_categories[md5($name)] = $name;
}
$categories = get_input_value('_categories', RCUBE_INPUT_POST);
$colors = get_input_value('_colors', RCUBE_INPUT_POST);
foreach ($categories as $key => $name) {
$color = preg_replace('/^#/', '', strval($colors[$key]));
// rename categories in existing events -> driver's job
if ($oldname = $old_categories[$key]) {
$this->driver->replace_category($oldname, $name, $color);
unset($old_categories[$key]);
}
else
$this->driver->add_category($name, $color);
$new_categories[$name] = $color;
}
// these old categories have been removed, alter events accordingly -> driver's job
foreach ((array)$old_categories[$key] as $key => $name) {
$this->driver->remove_category($name);
}
$p['prefs']['calendar_categories'] = $new_categories;
}
}
return $p;
}
/**
* Dispatcher for calendar actions initiated by the client
*/
function calendar_action()
{
$action = get_input_value('action', RCUBE_INPUT_GPC);
$cal = get_input_value('c', RCUBE_INPUT_POST);
$success = $reload = false;
switch ($action) {
case "form-new":
case "form-edit":
echo $this->ui->calendar_editform($action, $cal);
exit;
case "new":
$success = $this->driver->create_calendar($cal);
$reload = true;
break;
case "edit":
$success = $this->driver->edit_calendar($cal);
$reload = true;
break;
case "remove":
if ($success = $this->driver->remove_calendar($cal))
$this->rc->output->command('plugin.destroy_source', array('id' => $cal['id']));
break;
}
if ($success)
$this->rc->output->show_message('successfullysaved', 'confirmation');
else
$this->rc->output->show_message('calendar.errorsaving', 'error');
// TODO: keep view and date selection
if ($success && $reload)
$this->rc->output->redirect('');
}
/**
* Dispatcher for event actions initiated by the client
*/
function event_action()
{
$action = get_input_value('action', RCUBE_INPUT_GPC);
$event = get_input_value('e', RCUBE_INPUT_POST);
$success = $reload = $got_msg = false;
switch ($action) {
case "new":
// create UID for new event
$event['uid'] = $this->generate_uid();
-
- // set current user as organizer
- if (FALSE && !$event['attendees']) {
- $identity = $this->rc->user->get_identity();
- $event['attendees'][] = array('role' => 'ORGANIZER', 'name' => $identity['name'], 'email' => $identity['email']);
- }
-
$this->prepare_event($event, $action);
if ($success = $this->driver->new_event($event))
$this->cleanup_event($event);
$reload = true;
break;
case "edit":
$this->prepare_event($event, $action);
if ($success = $this->driver->edit_event($event))
$this->cleanup_event($event);
$reload = true;
break;
case "resize":
$success = $this->driver->resize_event($event);
$reload = true;
break;
case "move":
$success = $this->driver->move_event($event);
$reload = true;
break;
case "remove":
// remove previous deletes
$undo_time = $this->driver->undelete ? $this->rc->config->get('undo_timeout', 0) : 0;
$this->rc->session->remove('calendar_event_undo');
$success = $this->driver->remove_event($event, $undo_time < 1);
$reload = true;
if ($undo_time > 0 && $success) {
$_SESSION['calendar_event_undo'] = array('ts' => time(), 'data' => $event);
// display message with Undo link.
$msg = html::span(null, $this->gettext('successremoval'))
. ' ' . html::a(array('onclick' => sprintf("%s.http_request('event', 'action=undo', %s.display_message('', 'loading'))",
JS_OBJECT_NAME, JS_OBJECT_NAME)), rcube_label('undo'));
$this->rc->output->show_message($msg, 'confirmation', null, true, $undo_time);
$got_msg = true;
}
else if ($success) {
$this->rc->output->show_message('calendar.successremoval', 'confirmation');
$got_msg = true;
}
break;
case "undo":
// Restore deleted event
$event = $_SESSION['calendar_event_undo']['data'];
$reload = true;
if ($event)
$success = $this->driver->restore_event($event);
if ($success) {
$this->rc->session->remove('calendar_event_undo');
$this->rc->output->show_message('calendar.successrestore', 'confirmation');
$got_msg = true;
}
break;
case "dismiss":
foreach (explode(',', $event['id']) as $id)
$success |= $this->driver->dismiss_alarm($id, $event['snooze']);
break;
}
// show confirmation/error message
if (!$got_msg) {
if ($success)
$this->rc->output->show_message('successfullysaved', 'confirmation');
else
$this->rc->output->show_message('calendar.errorsaving', 'error');
}
// unlock client
$this->rc->output->command('plugin.unlock_saving');
// FIXME: update a single event object on the client instead of reloading the entire source
if ($success && $reload)
$this->rc->output->command('plugin.reload_calendar', array('source' => $event['calendar']));
}
/**
* Handler for load-requests from fullcalendar
* This will return pure JSON formatted output
*/
function load_events()
{
$events = $this->driver->load_events(
get_input_value('start', RCUBE_INPUT_GET),
get_input_value('end', RCUBE_INPUT_GET),
($query = get_input_value('q', RCUBE_INPUT_GET)),
get_input_value('source', RCUBE_INPUT_GET)
);
echo $this->encode($events, !empty($query));
exit;
}
/**
* Handler for keep-alive requests
* This will check for pending notifications and pass them to the client
*/
function keep_alive($attr)
{
$this->load_driver();
$alarms = $this->driver->pending_alarms(time());
if ($alarms) {
// make sure texts and env vars are available on client
if ($this->rc->task != 'calendar') {
$this->add_texts('localization/', true);
$this->rc->output->set_env('snooze_select', $this->ui->snooze_select());
}
$this->rc->output->command('plugin.display_alarms', $this->_alarms_output($alarms));
}
}
/**
* Construct the ics file for exporting events to iCalendar format;
*/
function export_events()
{
$start = get_input_value('start', RCUBE_INPUT_GET);
$end = get_input_value('end', RCUBE_INPUT_GET);
if (!$start) $start = mktime(0, 0, 0, 1, date('n'), date('Y')-1);
if (!$end) $end = mktime(0, 0, 0, 31, 12, date('Y')+10);
- $calendar_name = get_input_value('source', RCUBE_INPUT_GET);
+ $calendar_name = get_input_value('source', RCUBE_INPUT_GET);
$events = $this->driver->load_events($start, $end, null, $calendar_name, 0);
header("Content-Type: text/calendar");
header("Content-Disposition: inline; filename=".$calendar_name);
echo $this->ical->export($events);
exit;
}
/**
*
*/
function load_settings()
{
$settings = array();
// configuration
$settings['default_calendar'] = $this->rc->config->get('calendar_default_calendar');
$settings['default_view'] = (string)$this->rc->config->get('calendar_default_view', $this->defaults['calendar_default_view']);
$settings['date_format'] = (string)$this->rc->config->get('calendar_date_format', $this->defaults['calendar_date_format']);
$settings['date_short'] = (string)$this->rc->config->get('calendar_date_short', $this->defaults['calendar_date_short']);
$settings['date_long'] = (string)$this->rc->config->get('calendar_date_long', $this->defaults['calendar_date_long']);
$settings['date_agenda'] = (string)$this->rc->config->get('calendar_date_agenda', $this->defaults['calendar_date_agenda']);
$settings['time_format'] = (string)$this->rc->config->get('calendar_time_format', $this->defaults['calendar_time_format']);
$settings['timeslots'] = (int)$this->rc->config->get('calendar_timeslots', $this->defaults['calendar_timeslots']);
$settings['first_day'] = (int)$this->rc->config->get('calendar_first_day', $this->defaults['calendar_first_day']);
$settings['first_hour'] = (int)$this->rc->config->get('calendar_first_hour', $this->defaults['calendar_first_hour']);
$settings['work_start'] = (int)$this->rc->config->get('calendar_work_start', $this->defaults['calendar_work_start']);
$settings['work_end'] = (int)$this->rc->config->get('calendar_work_end', $this->defaults['calendar_work_end']);
$settings['timezone'] = $this->timezone;
// localization
$settings['days'] = array(
rcube_label('sunday'), rcube_label('monday'),
rcube_label('tuesday'), rcube_label('wednesday'),
rcube_label('thursday'), rcube_label('friday'),
rcube_label('saturday')
);
$settings['days_short'] = array(
rcube_label('sun'), rcube_label('mon'),
rcube_label('tue'), rcube_label('wed'),
rcube_label('thu'), rcube_label('fri'),
rcube_label('sat')
);
$settings['months'] = array(
$this->rc->gettext('longjan'), $this->rc->gettext('longfeb'),
$this->rc->gettext('longmar'), $this->rc->gettext('longapr'),
$this->rc->gettext('longmay'), $this->rc->gettext('longjun'),
$this->rc->gettext('longjul'), $this->rc->gettext('longaug'),
$this->rc->gettext('longsep'), $this->rc->gettext('longoct'),
$this->rc->gettext('longnov'), $this->rc->gettext('longdec')
);
$settings['months_short'] = array(
$this->rc->gettext('jan'), $this->rc->gettext('feb'),
$this->rc->gettext('mar'), $this->rc->gettext('apr'),
$this->rc->gettext('may'), $this->rc->gettext('jun'),
$this->rc->gettext('jul'), $this->rc->gettext('aug'),
$this->rc->gettext('sep'), $this->rc->gettext('oct'),
$this->rc->gettext('nov'), $this->rc->gettext('dec')
);
$settings['today'] = rcube_label('today');
// user prefs
$settings['hidden_calendars'] = array_filter(explode(',', $this->rc->config->get('hidden_calendars', '')));
// get user identity to create default attendee
- $identity = $this->rc->user->get_identity();
- $settings['event_owner'] = array('name' => $identity['name'], 'email' => $identity['email']);
+ if ($this->ui->screen == 'calendar') {
+ $identity = $this->rc->user->get_identity();
+ $settings['event_owner'] = array('name' => $identity['name'], 'email' => $identity['email']);
+ }
return $settings;
}
/**
* Convert the given date string into a GMT-based time stamp
*/
function fromGMT($datetime)
{
$ts = is_numeric($datetime) ? $datetime : strtotime($datetime);
return $ts + $this->gmt_offset;
}
/**
* Encode events as JSON
*
* @param array Events as array
* @param boolean Add CSS class names according to calendar and categories
* @return string JSON encoded events
*/
function encode($events, $addcss = false)
{
$json = array();
foreach ($events as $event) {
// compose a human readable strings for alarms_text and recurrence_text
if ($event['alarms'])
$event['alarms_text'] = $this->_alarms_text($event['alarms']);
if ($event['recurrence'])
$event['recurrence_text'] = $this->_recurrence_text($event['recurrence']);
$json[] = array(
'start' => gmdate('c', $this->fromGMT($event['start'])), // client treats date strings as they were in users's timezone
'end' => gmdate('c', $this->fromGMT($event['end'])), // so shift timestamps to users's timezone and render a date string
'description' => strval($event['description']),
'location' => strval($event['location']),
'className' => ($addcss ? 'fc-event-cal-'.asciiwords($event['calendar'], true).' ' : '') . 'cat-' . asciiwords($event['categories'], true),
'allDay' => ($event['allday'] == 1),
) + $event;
}
return json_encode($json);
}
/**
* Generate reduced and streamlined output for pending alarms
*/
private function _alarms_output($alarms)
{
$out = array();
foreach ($alarms as $alarm) {
$out[] = array(
'id' => $alarm['id'],
'start' => gmdate('c', $this->fromGMT($alarm['start'])),
'end' => gmdate('c', $this->fromGMT($alarm['end'])),
'allDay' => ($event['allday'] == 1)?true:false,
'title' => $alarm['title'],
'location' => $alarm['location'],
'calendar' => $alarm['calendar'],
);
}
return $out;
}
/**
* Render localized text for alarm settings
*/
private function _alarms_text($alarm)
{
list($trigger, $action) = explode(':', $alarm);
$text = '';
switch ($action) {
case 'EMAIL':
$text = $this->gettext('alarmemail');
break;
case 'DISPLAY':
$text = $this->gettext('alarmdisplay');
break;
}
if (preg_match('/@(\d+)/', $trigger, $m)) {
$text .= ' ' . $this->gettext(array('name' => 'alarmat', 'vars' => array('datetime' => format_date($m[1]))));
}
else if ($val = self::parse_alaram_value($trigger)) {
$text .= ' ' . intval($val[0]) . ' ' . $this->gettext('trigger' . $val[1]);
}
else
return false;
return $text;
}
/**
* Render localized text describing the recurrence rule of an event
*/
private function _recurrence_text($rrule)
{
// TODO: finish this
$freq = sprintf('%s %d ', $this->gettext('every'), $rrule['INTERVAL']);
$details = '';
switch ($rrule['FREQ']) {
case 'DAILY':
$freq .= $this->gettext('days');
break;
case 'WEEKLY':
$freq .= $this->gettext('weeks');
break;
case 'MONTHLY':
$freq .= $this->gettext('months');
break;
case 'YEARY':
$freq .= $this->gettext('years');
break;
}
if ($rrule['INTERVAL'] == 1)
$freq = $this->gettext(strtolower($rrule['FREQ']));
if ($rrule['COUNT'])
$until = $this->gettext(array('name' => 'forntimes', 'vars' => array('nr' => $rrule['COUNT'])));
else if ($rrule['UNTIL'])
$until = $this->gettext('recurrencend') . ' ' . format_date($rrule['UNTIL'], self::to_php_date_format($this->rc->config->get('calendar_date_format', $this->defaults['calendar_date_format'])));
else
$until = $this->gettext('forever');
return rtrim($freq . $details . ', ' . $until);
}
/**
* Generate a unique identifier for an event
*/
public function generate_uid()
{
return strtoupper(md5(time() . uniqid(rand())) . '-' . substr(md5($this->rc->user->get_username()), 0, 16));
}
/**
* Helper function to convert alarm trigger strings
* into two-field values (e.g. "-45M" => 45, "-M")
*/
public static function parse_alaram_value($val)
{
if ($val[0] == '@')
return array(substr($val, 1));
else if (preg_match('/([+-])(\d+)([HMD])/', $val, $m))
return array($m[2], $m[1].$m[3]);
return false;
}
/**
* Convert the internal structured data into a vcalendar rrule 2.0 string
*/
public static function to_rrule($recurrence)
{
if (is_string($recurrence))
return $recurrence;
$rrule = '';
foreach ((array)$recurrence as $k => $val) {
$k = strtoupper($k);
switch ($k) {
case 'UNTIL':
$val = gmdate('Ymd\THis', $val);
break;
case 'EXDATE':
foreach ((array)$val as $i => $ex)
$val[$i] = gmdate('Ymd\THis', $ex);
$val = join(',', $val);
break;
}
$rrule .= $k . '=' . $val . ';';
}
return $rrule;
}
/**
* Convert from fullcalendar date format to PHP date() format string
*/
private static function to_php_date_format($from)
{
// "dd.MM.yyyy HH:mm:ss" => "d.m.Y H:i:s"
return strtr($from, array(
'yyyy' => 'Y',
'yy' => 'y',
'MMMM' => 'F',
'MMM' => 'M',
'MM' => 'm',
'M' => 'n',
'dddd' => 'l',
'ddd' => 'D',
'dd' => 'd',
'HH' => 'H',
'hh' => 'h',
'mm' => 'i',
'ss' => 's',
'TT' => 'A',
'tt' => 'a',
'T' => 'A',
't' => 'a',
'u' => 'c',
));
}
/**
* TEMPORARY: generate random event data for testing
* Create events by opening http://<roundcubeurl>/?_task=calendar&_action=randomdata&_num=500
*/
public function generate_randomdata()
{
$cats = array_keys($this->driver->list_categories());
$cals = $this->driver->list_calendars();
$num = $_REQUEST['_num'] ? intval($_REQUEST['_num']) : 100;
while ($count++ < $num) {
$start = round((time() + rand(-2600, 2600) * 1000) / 300) * 300;
$duration = round(rand(30, 360) / 30) * 30 * 60;
$allday = rand(0,20) > 18;
$alarm = rand(-30,12) * 5;
$fb = rand(0,2);
if (date('G', $start) > 23)
$start -= 3600;
if ($allday) {
$start = strtotime(date('Y-m-d 00:00:00', $start));
$duration = 86399;
}
$title = '';
$len = rand(2, 12);
$words = explode(" ", "The Hough transform is named after Paul Hough who patented the method in 1962. It is a technique which can be used to isolate features of a particular shape within an image. Because it requires that the desired features be specified in some parametric form, the classical Hough transform is most commonly used for the de- tection of regular curves such as lines, circles, ellipses, etc. A generalized Hough transform can be employed in applications where a simple analytic description of a feature(s) is not possible. Due to the computational complexity of the generalized Hough algorithm, we restrict the main focus of this discussion to the classical Hough transform. Despite its domain restrictions, the classical Hough transform (hereafter referred to without the classical prefix ) retains many applications, as most manufac- tured parts (and many anatomical parts investigated in medical imagery) contain feature boundaries which can be described by regular curves. The main advantage of the Hough transform technique is that it is tolerant of gaps in feature boundary descriptions and is relatively unaffected by image noise.");
$chars = "!# abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890";
for ($i = 0; $i < $len; $i++)
$title .= $words[rand(0,count($words)-1)] . " ";
$this->driver->new_event(array(
'uid' => $this->generate_uid(),
'start' => $start,
'end' => $start + $duration,
'allday' => $allday,
'title' => rtrim($title),
'free_busy' => $fb == 2 ? 'outofoffice' : ($fb ? 'busy' : 'free'),
'categories' => $cats[array_rand($cats)],
'calendar' => array_rand($cals),
'alarms' => $alarm > 0 ? "-{$alarm}M:DISPLAY" : '',
'priority' => 1,
));
}
$this->rc->output->redirect('');
}
/**
* Handler for attachments upload
*/
public function attachment_upload()
{
// Upload progress update
if (!empty($_GET['_progress'])) {
rcube_upload_progress();
}
$event = get_input_value('_id', RCUBE_INPUT_GPC);
$calendar = get_input_value('calendar', RCUBE_INPUT_GPC);
$uploadid = get_input_value('_uploadid', RCUBE_INPUT_GPC);
$eventid = $calendar.':'.$event;
if (!is_array($_SESSION['event_session']) || $_SESSION['event_session']['id'] != $eventid) {
$_SESSION['event_session'] = array();
$_SESSION['event_session']['id'] = $eventid;
$_SESSION['event_session']['attachments'] = array();
}
// clear all stored output properties (like scripts and env vars)
$this->rc->output->reset();
if (is_array($_FILES['_attachments']['tmp_name'])) {
foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath) {
// Process uploaded attachment if there is no error
$err = $_FILES['_attachments']['error'][$i];
if (!$err) {
$attachment = array(
'path' => $filepath,
'size' => $_FILES['_attachments']['size'][$i],
'name' => $_FILES['_attachments']['name'][$i],
'mimetype' => rc_mime_content_type($filepath, $_FILES['_attachments']['name'][$i], $_FILES['_attachments']['type'][$i]),
'group' => $eventid,
);
$attachment = $this->rc->plugins->exec_hook('attachment_upload', $attachment);
}
if (!$err && $attachment['status'] && !$attachment['abort']) {
$id = $attachment['id'];
// store new attachment in session
unset($attachment['status'], $attachment['abort']);
$_SESSION['event_session']['attachments'][$id] = $attachment;
if (($icon = $_SESSION['calendar_deleteicon']) && is_file($icon)) {
$button = html::img(array(
'src' => $icon,
'alt' => rcube_label('delete')
));
}
else {
$button = Q(rcube_label('delete'));
}
$content = html::a(array(
'href' => "#delete",
'onclick' => sprintf("return %s.remove_from_attachment_list('rcmfile%s')", JS_OBJECT_NAME, $id),
'title' => rcube_label('delete'),
), $button);
$content .= Q($attachment['name']);
$this->rc->output->command('add2attachment_list', "rcmfile$id", array(
'html' => $content,
'name' => $attachment['name'],
'mimetype' => $attachment['mimetype'],
'complete' => true), $uploadid);
}
else { // upload failed
if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
$msg = rcube_label(array('name' => 'filesizeerror', 'vars' => array(
'size' => show_bytes(parse_bytes(ini_get('upload_max_filesize'))))));
}
else if ($attachment['error']) {
$msg = $attachment['error'];
}
else {
$msg = rcube_label('fileuploaderror');
}
$this->rc->output->command('display_message', $msg, 'error');
$this->rc->output->command('remove_from_attachment_list', $uploadid);
}
}
}
else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// if filesize exceeds post_max_size then $_FILES array is empty,
// show filesizeerror instead of fileuploaderror
if ($maxsize = ini_get('post_max_size'))
$msg = rcube_label(array('name' => 'filesizeerror', 'vars' => array(
'size' => show_bytes(parse_bytes($maxsize)))));
else
$msg = rcube_label('fileuploaderror');
$this->rc->output->command('display_message', $msg, 'error');
$this->rc->output->command('remove_from_attachment_list', $uploadid);
}
$this->rc->output->send('iframe');
}
/**
* Handler for attachments download/displaying
*/
public function attachment_get()
{
$event = get_input_value('_event', RCUBE_INPUT_GPC);
$calendar = get_input_value('_cal', RCUBE_INPUT_GPC);
$id = get_input_value('_id', RCUBE_INPUT_GPC);
$event = array('id' => $event, 'calendar' => $calendar);
// show loading page
if (!empty($_GET['_preload'])) {
$url = str_replace('&_preload=1', '', $_SERVER['REQUEST_URI']);
$message = rcube_label('loadingdata');
header('Content-Type: text/html; charset=' . RCMAIL_CHARSET);
print "<html>\n<head>\n"
. '<meta http-equiv="refresh" content="0; url='.Q($url).'">' . "\n"
. '<meta http-equiv="content-type" content="text/html; charset='.RCMAIL_CHARSET.'">' . "\n"
. "</head>\n<body>\n$message\n</body>\n</html>";
exit;
}
ob_end_clean();
send_nocacheing_headers();
if (isset($_SESSION['calendar_attachment']))
$attachment = $_SESSION['calendar_attachment'];
else
$attachment = $_SESSION['calendar_attachment'] = $this->driver->get_attachment($id, $event);
// show part page
if (!empty($_GET['_frame'])) {
$this->attachment = $attachment;
$this->register_handler('plugin.attachmentframe', array($this, 'attachment_frame'));
$this->register_handler('plugin.attachmentcontrols', array($this->ui, 'attachment_controls'));
$this->rc->output->send('calendar.attachment');
exit;
}
$this->rc->session->remove('calendar_attachment');
if ($attachment) {
$mimetype = strtolower($attachment['mimetype']);
list($ctype_primary, $ctype_secondary) = explode('/', $mimetype);
$browser = $this->rc->output->browser;
// send download headers
if ($_GET['_download']) {
header("Content-Type: application/octet-stream");
if ($browser->ie)
header("Content-Type: application/force-download");
}
else if ($ctype_primary == 'text') {
header("Content-Type: text/$ctype_secondary");
}
else {
// $mimetype = rcmail_fix_mimetype($mimetype);
header("Content-Type: $mimetype");
header("Content-Transfer-Encoding: binary");
}
$body = $this->driver->get_attachment_body($id, $event);
// display page, @TODO: support text/plain (and maybe some other text formats)
if ($mimetype == 'text/html' && empty($_GET['_download'])) {
$OUTPUT = new rcube_html_page();
// @TODO: use washtml on $body
$OUTPUT->write($body);
}
else {
// don't kill the connection if download takes more than 30 sec.
@set_time_limit(0);
$filename = $attachment['name'];
$filename = preg_replace('[\r\n]', '', $filename);
if ($browser->ie && $browser->ver < 7)
$filename = rawurlencode(abbreviate_string($filename, 55));
else if ($browser->ie)
$filename = rawurlencode($filename);
else
$filename = addcslashes($filename, '"');
$disposition = !empty($_GET['_download']) ? 'attachment' : 'inline';
header("Content-Disposition: $disposition; filename=\"$filename\"");
echo $body;
}
exit;
}
// if we arrive here, the requested part was not found
header('HTTP/1.1 404 Not Found');
exit;
}
/**
* Template object for attachment display frame
*/
public function attachment_frame($attrib)
{
$attachment = $_SESSION['calendar_attachment'];
$mimetype = strtolower($attachment['mimetype']);
list($ctype_primary, $ctype_secondary) = explode('/', $mimetype);
$attrib['src'] = './?' . str_replace('_frame=', ($ctype_primary == 'text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']);
return html::iframe($attrib);
}
/**
* Prepares new/edited event properties before save
*/
private function prepare_event(&$event, $action)
{
$eventid = $event['calendar'].':'.$event['id'];
$attachments = array();
if (is_array($_SESSION['event_session']) && $_SESSION['event_session']['id'] == $eventid) {
if (!empty($_SESSION['event_session']['attachments'])) {
foreach ($_SESSION['event_session']['attachments'] as $id => $attachment) {
if (is_array($event['attachments']) && in_array($id, $event['attachments'])) {
$attachments[$id] = $this->rc->plugins->exec_hook('attachment_get', $attachment);
}
}
}
}
$event['attachments'] = $attachments;
// check for organizer in attendees
if ($event['attendees']) {
$identity = $this->rc->user->get_identity();
$organizer = $owner = false;
foreach ($event['attendees'] as $i => $attendee) {
if ($attendee['role'] == 'ORGANIZER')
$organizer = true;
if ($attendee['email'] == $identity['email'])
$owner = $i;
}
// set owner as organizer if yet missing
if (!$organizer && $owner !== false) {
$event['attendees'][$i]['role'] = 'ORGANIZER';
}
else if (!$organizer && $identity['email'] && $action == 'new') {
array_unshift($event['attendees'], array('role' => 'ORGANIZER', 'name' => $identity['name'], 'email' => $identity['email'], 'status' => 'ACCEPTED'));
}
}
}
/**
* Releases some resources after successful event save
*/
private function cleanup_event(&$event)
{
// remove temp. attachment files
if (!empty($_SESSION['event_session']) && ($eventid = $_SESSION['event_session']['id'])) {
$this->rc->plugins->exec_hook('attachments_cleanup', array('group' => $eventid));
unset($_SESSION['event_session']);
}
}
/**
* Echo simple free/busy status text for the given user and time range
*/
public function freebusy_status()
{
$email = get_input_value('email', RCUBE_INPUT_GPC);
$start = get_input_value('start', RCUBE_INPUT_GET);
$end = get_input_value('end', RCUBE_INPUT_GET);
if (!$start) $start = time();
if (!$end) $end = $start + 3600;
$fbtypemap = array(calendar::FREEBUSY_FREE => 'FREE', calendar::FREEBUSY_BUSY => 'BUSY', calendar::FREEBUSY_TENTATIVE => 'TENTATIVE', calendar::FREEBUSY_OOF => 'OUT-OF-OFFICE');
$status = 'UNKNOWN';
// if the backend has free-busy information
$fblist = $this->driver->get_freebusy_list($email, $start, $end);
if (is_array($fblist)) {
$status = 'FREE';
foreach ($fblist as $slot) {
list($from, $to, $type) = $slot;
if ($from <= $end && $to > $start) {
$status = $type && $fbtypemap[$type] ? $fbtypemap[$type] : 'BUSY';
break;
}
}
}
// let this information be cached for 15min
send_future_expire_header(900);
echo $status;
exit;
}
/**
* Return a list of free/busy time slots within the given period
* Echo data in JSON encoding
*/
public function freebusy_times()
{
$email = get_input_value('email', RCUBE_INPUT_GPC);
$start = get_input_value('start', RCUBE_INPUT_GET);
$end = get_input_value('end', RCUBE_INPUT_GET);
$interval = intval(get_input_value('interval', RCUBE_INPUT_GET));
if (!$start) $start = time();
if (!$end) $end = $start + 86400 * 30;
if (!$interval) $interval = 60; // 1 hour
$fblist = $this->driver->get_freebusy_list($email, $start, $end);
$slots = array();
// build a list from $start till $end with blocks representing the fb-status
for ($s = 0, $t = $start; $t <= $end; $s++) {
$status = self::FREEBUSY_UNKNOWN;
$t_end = $t + $interval * 60;
// determine attendee's status
if (is_array($fblist)) {
$status = self::FREEBUSY_FREE;
foreach ($fblist as $slot) {
list($from, $to, $type) = $slot;
if ($from <= $t_end && $to > $t) {
$status = isset($type) ? $type : self::FREEBUSY_BUSY;
break;
}
}
}
$slots[$s] = $status;
$t = $t_end;
}
echo json_encode(array('email' => $email, 'start' => intval($start), 'interval' => $interval, 'slots' => $slots));
exit;
}
/**
* Handler for printing calendars
*/
public function print_view()
{
$title = $this->gettext('print');
$view = get_input_value('view', RCUBE_INPUT_GPC);
if (!in_array($view, array('agendaWeek', 'agendaDay', 'month', 'table')))
$view = 'agendaDay';
$this->rc->output->set_env('view',$view);
if ($date = get_input_value('date', RCUBE_INPUT_GPC))
$this->rc->output->set_env('date', $date);
if ($search = get_input_value('search', RCUBE_INPUT_GPC)) {
$this->rc->output->set_env('search', $search);
$title .= ' "' . $search . '"';
}
// Add CSS stylesheets to the page header
$skin = $this->rc->config->get('skin');
$this->include_stylesheet('skins/' . $skin . '/fullcalendar.css');
$this->include_stylesheet('skins/' . $skin . '/print.css');
// Add JS files to the page header
$this->include_script('print.js');
$this->register_handler('plugin.calendar_css', array($this->ui, 'calendar_css'));
$this->register_handler('plugin.calendar_list', array($this->ui, 'calendar_list'));
$this->rc->output->set_pagetitle($title);
$this->rc->output->send("calendar.print");
}
}
diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php
index 30d44ea5..061e71d5 100644
--- a/plugins/calendar/lib/calendar_ui.php
+++ b/plugins/calendar/lib/calendar_ui.php
@@ -1,607 +1,609 @@
<?php
/*
+-------------------------------------------------------------------------+
| User Interface for the Calendar Plugin |
| Version 0.3 beta |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License version 2 |
| as published by the Free Software Foundation. |
| |
| This program 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 General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License along |
| with this program; if not, write to the Free Software Foundation, Inc., |
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| |
+-------------------------------------------------------------------------+
| Author: Lazlo Westerhof <hello@lazlo.me> |
+-------------------------------------------------------------------------+
*/
class calendar_ui
{
private $rc;
private $calendar;
+ public $screen;
function __construct($calendar)
{
$this->calendar = $calendar;
$this->rc = $calendar->rc;
+ $this->screen = $this->rc->task == 'calendar' ? ($this->rc->action ? $this->rc->action: 'calendar') : 'other';
}
/**
* Calendar UI initialization and requests handlers
*/
public function init()
{
// add taskbar button
$this->calendar->add_button(array(
'name' => 'calendar',
'class' => 'button-calendar',
'label' => 'calendar.calendar',
'href' => './?_task=calendar',
), 'taskbar');
// load basic client script (which - unfortunately - requires fullcalendar)
$this->calendar->include_script('lib/js/fullcalendar.js');
$this->calendar->include_script('calendar_base.js');
$skin = $this->rc->config->get('skin');
$this->calendar->include_stylesheet('skins/' . $skin . '/calendar.css');
}
/**
* Adds CSS stylesheets to the page header
*/
public function addCSS()
{
$skin = $this->rc->config->get('skin');
$this->calendar->include_stylesheet('skins/' . $skin . '/fullcalendar.css');
$this->calendar->include_stylesheet('skins/' . $skin . '/jquery.miniColors.css');
}
/**
* Adds JS files to the page header
*/
public function addJS()
{
$this->calendar->include_script('calendar_ui.js');
$this->calendar->include_script('lib/js/jquery.miniColors.min.js');
}
/**
*
*/
function calendar_css($attrib = array())
{
$categories = $this->rc->config->get('calendar_categories', array());
$css = "\n";
foreach ((array)$categories as $class => $color) {
$class = 'cat-' . asciiwords($class, true);
$css .= "." . $class . ",\n";
$css .= ".fc-event-" . $class . ",\n";
$css .= "." . $class . " a {\n";
$css .= "color: #" . $color . ";\n";
$css .= "border-color: #" . $color . ";\n";
$css .= "}\n";
}
$calendars = $this->calendar->driver->list_calendars();
foreach ((array)$calendars as $id => $prop) {
if (!$prop['color'])
continue;
$color = $prop['color'];
$class = 'cal-' . asciiwords($id, true);
$css .= "li." . $class . ", ";
$css .= "#eventshow ." . $class . " { ";
$css .= "color: #" . $color . " }\n";
$css .= ".fc-event-" . $class . ", ";
$css .= ".fc-event-" . $class . " .fc-event-inner, ";
$css .= ".fc-event-" . $class . " .fc-event-time {\n";
if (!$attrib['printmode'])
$css .= "background-color: #" . $color . ";\n";
$css .= "border-color: #" . $color . ";\n";
$css .= "}\n";
}
return html::tag('style', array('type' => 'text/css'), $css);
}
/**
*
*/
function calendar_list($attrib = array())
{
$calendars = $this->calendar->driver->list_calendars();
$hidden = explode(',', $this->rc->config->get('hidden_calendars', ''));
$li = '';
foreach ((array)$calendars as $id => $prop) {
if ($attrib['activeonly'] && in_array($id, $hidden))
continue;
unset($prop['user_id']);
$prop['alarms'] = $this->calendar->driver->alarms;
$prop['attendees'] = $this->calendar->driver->attendees;
$prop['freebusy'] = $this->calendar->driver->freebusy;
$prop['attachments'] = $this->calendar->driver->attachments;
$jsenv[$id] = $prop;
$html_id = html_identifier($id);
$class = 'cal-' . asciiwords($id, true);
if ($prop['readonly'])
$class .= ' readonly';
if ($prop['class_name'])
$class .= ' '.$prop['class_name'];
$li .= html::tag('li', array('id' => 'rcmlical' . $html_id, 'class' => $class),
html::tag('input', array('type' => 'checkbox', 'name' => '_cal[]', 'value' => $id, 'checked' => !in_array($id, $hidden)), '') . html::span(null, Q($prop['name'])));
}
$this->rc->output->set_env('calendars', $jsenv);
$this->rc->output->add_gui_object('folderlist', $attrib['id']);
return html::tag('ul', $attrib, $li, html::$common_attrib);
}
/**
* Render a HTML select box for calendar selection
*/
function calendar_select($attrib = array())
{
$attrib['name'] = 'calendar';
$select = new html_select($attrib);
foreach ((array)$this->calendar->driver->list_calendars() as $id => $prop) {
if (!$prop['readonly'])
$select->add($prop['name'], $id);
}
return $select->show(null);
}
/**
* Render a HTML select box to select an event category
*/
function category_select($attrib = array())
{
$attrib['name'] = 'categories';
$select = new html_select($attrib);
$select->add('---', '');
foreach ((array)$this->calendar->driver->list_categories() as $cat => $color) {
$select->add($cat, $cat);
}
return $select->show(null);
}
/**
* Render a HTML select box for free/busy/out-of-office property
*/
function freebusy_select($attrib = array())
{
$attrib['name'] = 'freebusy';
$select = new html_select($attrib);
$select->add($this->calendar->gettext('free'), 'free');
$select->add($this->calendar->gettext('busy'), 'busy');
$select->add($this->calendar->gettext('outofoffice'), 'outofoffice');
$select->add($this->calendar->gettext('tentative'), 'tentative');
return $select->show(null);
}
/**
* Render a HTML select for event priorities
*/
function priority_select($attrib = array())
{
$attrib['name'] = 'priority';
$select = new html_select($attrib);
$select->add($this->calendar->gettext('normal'), '1');
$select->add($this->calendar->gettext('low'), '0');
$select->add($this->calendar->gettext('high'), '2');
return $select->show(null);
}
/**
* Render HTML input for sensitivity selection
*/
function sensitivity_select($attrib = array())
{
$attrib['name'] = 'sensitivity';
$select = new html_select($attrib);
$select->add($this->calendar->gettext('public'), '0');
$select->add($this->calendar->gettext('private'), '1');
$select->add($this->calendar->gettext('confidential'), '2');
return $select->show(null);
}
/**
* Render HTML form for alarm configuration
*/
function alarm_select($attrib = array())
{
unset($attrib['name']);
$select_type = new html_select(array('name' => 'alarmtype[]', 'class' => 'edit-alarm-type'));
$select_type->add($this->calendar->gettext('none'), '');
foreach ($this->calendar->driver->alarm_types as $type)
$select_type->add($this->calendar->gettext(strtolower("alarm{$type}option")), $type);
$input_value = new html_inputfield(array('name' => 'alarmvalue[]', 'class' => 'edit-alarm-value', 'size' => 3));
$input_date = new html_inputfield(array('name' => 'alarmdate[]', 'class' => 'edit-alarm-date', 'size' => 10));
$input_time = new html_inputfield(array('name' => 'alarmtime[]', 'class' => 'edit-alarm-time', 'size' => 6));
$select_offset = new html_select(array('name' => 'alarmoffset[]', 'class' => 'edit-alarm-offset'));
foreach (array('-M','-H','-D','+M','+H','+D','@') as $trigger)
$select_offset->add($this->calendar->gettext('trigger' . $trigger), $trigger);
// pre-set with default values from user settings
$preset = calendar::parse_alaram_value($this->rc->config->get('calendar_default_alarm_offset', '-15M'));
$hidden = array('style' => 'display:none');
$html = html::span('edit-alarm-set',
$select_type->show($this->rc->config->get('calendar_default_alarm_type', '')) . ' ' .
html::span(array('class' => 'edit-alarm-values', 'style' => 'display:none'),
$input_value->show($preset[0]) . ' ' .
$select_offset->show($preset[1]) . ' ' .
$input_date->show('', $hidden) . ' ' .
$input_time->show('', $hidden)
)
);
// TODO: support adding more alarms
#$html .= html::a(array('href' => '#', 'id' => 'edit-alam-add', 'title' => $this->calendar->gettext('addalarm')),
# $attrib['addicon'] ? html::img(array('src' => $attrib['addicon'], 'alt' => 'add')) : '(+)');
return $html;
}
function snooze_select($attrib = array())
{
$steps = array(
5 => 'repeatinmin',
10 => 'repeatinmin',
15 => 'repeatinmin',
20 => 'repeatinmin',
30 => 'repeatinmin',
60 => 'repeatinhr',
120 => 'repeatinhrs',
1440 => 'repeattomorrow',
10080 => 'repeatinweek',
);
$items = array();
foreach ($steps as $n => $label) {
$items[] = html::tag('li', null, html::a(array('href' => "#" . ($n * 60), 'class' => 'active'),
$this->calendar->gettext(array('name' => $label, 'vars' => array('min' => $n % 60, 'hrs' => intval($n / 60))))));
}
return html::tag('ul', $attrib, join("\n", $items), html::$common_attrib);
}
/**
* Generate the form for recurrence settings
*/
function recurring_event_warning($attrib = array())
{
$attrib['id'] = 'edit-recurring-warning';
$radio = new html_radiobutton(array('name' => 'savemode', 'class' => 'edit-recurring-savemode'));
$form = html::label(null, $radio->show('', array('value' => 'current')) . $this->calendar->gettext('currentevent')) . ' ' .
html::label(null, $radio->show('', array('value' => 'future')) . $this->calendar->gettext('futurevents')) . ' ' .
html::label(null, $radio->show('all', array('value' => 'all')) . $this->calendar->gettext('allevents')) . ' ' .
html::label(null, $radio->show('', array('value' => 'new')) . $this->calendar->gettext('saveasnew'));
return html::div($attrib, html::div('message', html::span('ui-icon ui-icon-alert', '') . $this->calendar->gettext('changerecurringeventwarning')) . html::div('savemode', $form));
}
/**
* Generate the form for recurrence settings
*/
function recurrence_form($attrib = array())
{
switch ($attrib['part']) {
// frequency selector
case 'frequency':
$select = new html_select(array('name' => 'frequency', 'id' => 'edit-recurrence-frequency'));
$select->add($this->calendar->gettext('never'), '');
$select->add($this->calendar->gettext('daily'), 'DAILY');
$select->add($this->calendar->gettext('weekly'), 'WEEKLY');
$select->add($this->calendar->gettext('monthly'), 'MONTHLY');
$select->add($this->calendar->gettext('yearly'), 'YEARLY');
$html = html::label('edit-frequency', $this->calendar->gettext('frequency')) . $select->show('');
break;
// daily recurrence
case 'daily':
$select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-daily'));
$html = html::div($attrib, html::label(null, $this->calendar->gettext('every')) . $select->show(1) . html::span('label-after', $this->calendar->gettext('days')));
break;
// weekly recurrence form
case 'weekly':
$select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-weekly'));
$html = html::div($attrib, html::label(null, $this->calendar->gettext('every')) . $select->show(1) . html::span('label-after', $this->calendar->gettext('weeks')));
// weekday selection
$daymap = array('sun','mon','tue','wed','thu','fri','sat');
$checkbox = new html_checkbox(array('name' => 'byday', 'class' => 'edit-recurrence-weekly-byday'));
$first = $this->rc->config->get('calendar_first_day', 1);
for ($weekdays = '', $j = $first; $j <= $first+6; $j++) {
$d = $j % 7;
$weekdays .= html::label(array('class' => 'weekday'), $checkbox->show('', array('value' => strtoupper(substr($daymap[$d], 0, 2)))) . $this->calendar->gettext($daymap[$d])) . ' ';
}
$html .= html::div($attrib, html::label(null, $this->calendar->gettext('bydays')) . $weekdays);
break;
// monthly recurrence form
case 'monthly':
$select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-monthly'));
$html = html::div($attrib, html::label(null, $this->calendar->gettext('every')) . $select->show(1) . html::span('label-after', $this->calendar->gettext('months')));
/* multiple month selection is not supported by Kolab
$checkbox = new html_radiobutton(array('name' => 'bymonthday', 'class' => 'edit-recurrence-monthly-bymonthday'));
for ($monthdays = '', $d = 1; $d <= 31; $d++) {
$monthdays .= html::label(array('class' => 'monthday'), $checkbox->show('', array('value' => $d)) . $d);
$monthdays .= $d % 7 ? ' ' : html::br();
}
*/
// rule selectors
$radio = new html_radiobutton(array('name' => 'repeatmode', 'class' => 'edit-recurrence-monthly-mode'));
$table = new html_table(array('cols' => 2, 'border' => 0, 'cellpadding' => 0, 'class' => 'formtable'));
$table->add('label', html::label(null, $radio->show('BYMONTHDAY', array('value' => 'BYMONTHDAY')) . ' ' . $this->calendar->gettext('onsamedate'))); // $this->calendar->gettext('each')
$table->add(null, $monthdays);
$table->add('label', html::label(null, $radio->show('', array('value' => 'BYDAY')) . ' ' . $this->calendar->gettext('onevery')));
$table->add(null, $this->rrule_selectors($attrib['part']));
$html .= html::div($attrib, $table->show());
break;
// annually recurrence form
case 'yearly':
$select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-yearly'));
$html = html::div($attrib, html::label(null, $this->calendar->gettext('every')) . $select->show(1) . html::span('label-after', $this->calendar->gettext('years')));
// month selector
$monthmap = array('','jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec');
$boxtype = is_a($this->calendar->driver, 'kolab_driver') ? 'radio' : 'checkbox';
$checkbox = new html_inputfield(array('type' => $boxtype, 'name' => 'bymonth', 'class' => 'edit-recurrence-yearly-bymonth'));
for ($months = '', $m = 1; $m <= 12; $m++) {
$months .= html::label(array('class' => 'month'), $checkbox->show(null, array('value' => $m)) . $this->calendar->gettext($monthmap[$m]));
$months .= $m % 4 ? ' ' : html::br();
}
$html .= html::div($attrib + array('id' => 'edit-recurrence-yearly-bymonthblock'), $months);
// day rule selection
$html .= html::div($attrib, html::label(null, $this->calendar->gettext('onevery')) . $this->rrule_selectors($attrib['part'], '---'));
break;
// end of recurrence form
case 'until':
$radio = new html_radiobutton(array('name' => 'repeat', 'class' => 'edit-recurrence-until'));
$select = $this->interval_selector(array('name' => 'times', 'id' => 'edit-recurrence-repeat-times'));
$input = new html_inputfield(array('name' => 'untildate', 'id' => 'edit-recurrence-enddate', 'size' => "10"));
$table = new html_table(array('cols' => 2, 'border' => 0, 'cellpadding' => 0, 'class' => 'formtable'));
$table->add('label', ucfirst($this->calendar->gettext('recurrencend')));
$table->add(null, html::label(null, $radio->show('', array('value' => '', 'id' => 'edit-recurrence-repeat-forever')) . ' ' .
$this->calendar->gettext('forever')));
$table->add('label', '');
$table->add(null, html::label(null, $radio->show('', array('value' => 'count', 'id' => 'edit-recurrence-repeat-count')) . ' ' .
$this->calendar->gettext(array(
'name' => 'forntimes',
'vars' => array('nr' => $select->show(1)))
)));
$table->add('label', '');
$table->add(null, $radio->show('', array('value' => 'until', 'id' => 'edit-recurrence-repeat-until')) . ' ' .
$this->calendar->gettext('until') . ' ' . $input->show(''));
$html = $table->show();
break;
}
return $html;
}
/**
* Input field for interval selection
*/
private function interval_selector($attrib)
{
$select = new html_select($attrib);
$select->add(range(1,30), range(1,30));
return $select;
}
/**
* Drop-down menus for recurrence rules like "each last sunday of"
*/
private function rrule_selectors($part, $noselect = null)
{
// rule selectors
$select_prefix = new html_select(array('name' => 'bydayprefix', 'id' => "edit-recurrence-$part-prefix"));
if ($noselect) $select_prefix->add($noselect, '');
$select_prefix->add(array(
$this->calendar->gettext('first'),
$this->calendar->gettext('second'),
$this->calendar->gettext('third'),
$this->calendar->gettext('fourth')
),
array(1, 2, 3, 4));
// Kolab doesn't support 'last' but others do.
if (!is_a($this->calendar->driver, 'kolab_driver'))
$select_prefix->add($this->calendar->gettext('last'), -1);
$select_wday = new html_select(array('name' => 'byday', 'id' => "edit-recurrence-$part-byday"));
if ($noselect) $select_wday->add($noselect, '');
$daymap = array('sunday','monday','tuesday','wednesday','thursday','friday','saturday');
$first = $this->rc->config->get('calendar_first_day', 1);
for ($j = $first; $j <= $first+6; $j++) {
$d = $j % 7;
$select_wday->add($this->calendar->gettext($daymap[$d]), strtoupper(substr($daymap[$d], 0, 2)));
}
if ($part == 'monthly')
$select_wday->add($this->calendar->gettext('dayofmonth'), '');
return $select_prefix->show() . '&nbsp;' . $select_wday->show();
}
/**
* Generate the form for event attachments upload
*/
function attachments_form($attrib = array())
{
// add ID if not given
if (!$attrib['id'])
$attrib['id'] = 'rcmUploadForm';
// Enable upload progress bar
rcube_upload_progress_init();
// find max filesize value
$max_filesize = parse_bytes(ini_get('upload_max_filesize'));
$max_postsize = parse_bytes(ini_get('post_max_size'));
if ($max_postsize && $max_postsize < $max_filesize)
$max_filesize = $max_postsize;
$this->rc->output->set_env('max_filesize', $max_filesize);
$max_filesize = show_bytes($max_filesize);
$button = new html_inputfield(array('type' => 'button'));
$input = new html_inputfield(array(
'type' => 'file', 'name' => '_attachments[]',
'multiple' => 'multiple', 'size' => $attrib['attachmentfieldsize']));
return html::div($attrib,
html::div(null, $input->show()) .
html::div('buttons', $button->show(rcube_label('upload'), array('class' => 'button mainaction',
'onclick' => JS_OBJECT_NAME . ".upload_file(this.form)"))) .
html::div('hint', rcube_label(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize))))
);
}
/**
* Generate HTML element for attachments list
*/
function attachments_list($attrib = array())
{
if (!$attrib['id'])
$attrib['id'] = 'rcmAttachmentList';
$skin_path = $this->rc->config->get('skin_path');
if ($attrib['deleteicon']) {
$_SESSION['calendar_deleteicon'] = $skin_path . $attrib['deleteicon'];
$this->rc->output->set_env('deleteicon', $skin_path . $attrib['deleteicon']);
}
if ($attrib['cancelicon'])
$this->rc->output->set_env('cancelicon', $skin_path . $attrib['cancelicon']);
if ($attrib['loadingicon'])
$this->rc->output->set_env('loadingicon', $skin_path . $attrib['loadingicon']);
$this->rc->output->add_gui_object('attachmentlist', $attrib['id']);
return html::tag('ul', $attrib, '', html::$common_attrib);
}
function attachment_controls($attrib = array())
{
$table = new html_table(array('cols' => 3));
if (!empty($this->calendar->attachment['name'])) {
$table->add('title', Q(rcube_label('filename')));
$table->add(null, Q($this->calendar->attachment['name']));
$table->add(null, '[' . html::a('?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), Q(rcube_label('download'))) . ']');
}
if (!empty($this->calendar->attachment['size'])) {
$table->add('title', Q(rcube_label('filesize')));
$table->add(null, Q(show_bytes($this->calendar->attachment['size'])));
}
return $table->show($attrib);
}
/**
* Handler for calendar form template object.
* Will get additional form fields from driver class
*/
function calendar_editform($action, $calendar = array())
{
// compose default calendar form
$input_name = new html_inputfield(array('name' => 'name', 'id' => 'calendar-name', 'size' => 20));
$html = html::div('form-section',
html::label('calendar-name', $this->calendar->gettext('name')) .
$input_name->show($calendar['name']));
$input_color = new html_inputfield(array('name' => 'color', 'id' => 'calendar-color', 'size' => 6));
$html .= html::div('form-section',
html::label('calendar-color', $this->calendar->gettext('color')) .
$input_color->show($calendar['color']));
// allow driver to extend the form
$html = $this->calendar->driver->calendar_form($action, $calendar, $html);
return html::tag('form', array('action' => "#", 'method' => "get"), $html);
}
/**
*
*/
function attendees_list($attrib = array())
{
$table = new html_table(array('cols' => 5, 'border' => 0, 'cellpadding' => 0, 'class' => 'rectable'));
$table->add_header('role', $this->calendar->gettext('role'));
$table->add_header('name', $this->calendar->gettext('attendee'));
$table->add_header('availability', $this->calendar->gettext('availability'));
$table->add_header('confirmstate', $this->calendar->gettext('confirmstate'));
$table->add_header('options', '');
return $table->show($attrib);
}
/**
*
*/
function attendees_form($attrib = array())
{
$input = new html_inputfield(array('name' => 'participant', 'id' => 'edit-attendee-name', 'size' => 30));
$checkbox = new html_checkbox(array('name' => 'notify', 'id' => 'edit-attendees-notify', 'value' => 1, 'disabled' => true)); // disabled for now
return html::div($attrib,
html::div(null, $input->show() . " " .
html::tag('input', array('type' => 'button', 'class' => 'button', 'id' => 'edit-attendee-add', 'value' => $this->calendar->gettext('addattendee'))) . " " .
html::tag('input', array('type' => 'button', 'class' => 'button', 'id' => 'edit-attendee-schedule', 'value' => $this->calendar->gettext('scheduletime').'...'))) .
html::p('attendees-notifybox', html::label(null, $checkbox->show(1) . $this->calendar->gettext('sendnotifications')))
);
}
/**
*
*/
function attendees_freebusy_table($attrib = array())
{
$table = new html_table(array('cols' => 2, 'border' => 0, 'cellspacing' => 0));
$table->add('attendees',
html::tag('h3', 'boxtitle', $this->calendar->gettext('tabattendees')) .
html::div('timesheader', '&nbsp;') .
html::div(array('id' => 'schedule-attendees-list', 'class' => 'attendees-list'), '')
);
$table->add('times',
html::div('scroll',
html::tag('table', array('id' => 'schedule-freebusy-times', 'border' => 0, 'cellspacing' => 0), html::tag('thead') . html::tag('tbody')) .
html::div(array('id' => 'schedule-event-time', 'style' => 'display:none'), '&nbsp;')
)
);
return $table->show($attrib);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Apr 9, 1:02 PM (1 d, 22 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
540450
Default Alt Text
(76 KB)

Event Timeline