Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2528998
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
44 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php
index 6fcdb3c3..33ff37a5 100644
--- a/plugins/calendar/lib/calendar_ui.php
+++ b/plugins/calendar/lib/calendar_ui.php
@@ -1,896 +1,896 @@
<?php
/**
* User Interface class for the Calendar plugin
*
* @author Lazlo Westerhof <hello@lazlo.me>
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2010, Lazlo Westerhof <hello@lazlo.me>
* Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class calendar_ui
{
private $rc;
private $cal;
private $ready = false;
public $screen;
function __construct($cal)
{
$this->cal = $cal;
$this->rc = $cal->rc;
$this->screen = $this->rc->task == 'calendar' ? ($this->rc->action ? $this->rc->action: 'calendar') : 'other';
}
/**
* Calendar UI initialization and requests handlers
*/
public function init()
{
if ($this->ready) // already done
return;
// add taskbar button
$this->cal->add_button(array(
'command' => 'calendar',
'class' => 'button-calendar',
'classsel' => 'button-calendar button-selected',
'innerclass' => 'button-inner',
'label' => 'calendar.calendar',
), 'taskbar');
// load basic client script
$this->cal->include_script('calendar_base.js');
$skin_path = $this->cal->local_skin_path();
$this->cal->include_stylesheet($skin_path . '/calendar.css');
$this->ready = true;
}
/**
* Register handler methods for the template engine
*/
public function init_templates()
{
$this->cal->register_handler('plugin.calendar_css', array($this, 'calendar_css'));
$this->cal->register_handler('plugin.calendar_list', array($this, 'calendar_list'));
$this->cal->register_handler('plugin.calendar_select', array($this, 'calendar_select'));
$this->cal->register_handler('plugin.identity_select', array($this, 'identity_select'));
$this->cal->register_handler('plugin.category_select', array($this, 'category_select'));
$this->cal->register_handler('plugin.status_select', array($this, 'status_select'));
$this->cal->register_handler('plugin.freebusy_select', array($this, 'freebusy_select'));
$this->cal->register_handler('plugin.priority_select', array($this, 'priority_select'));
$this->cal->register_handler('plugin.sensitivity_select', array($this, 'sensitivity_select'));
$this->cal->register_handler('plugin.alarm_select', array($this, 'alarm_select'));
$this->cal->register_handler('plugin.recurrence_form', array($this->cal->lib, 'recurrence_form'));
$this->cal->register_handler('plugin.attachments_form', array($this, 'attachments_form'));
$this->cal->register_handler('plugin.attachments_list', array($this, 'attachments_list'));
$this->cal->register_handler('plugin.filedroparea', array($this, 'file_drop_area'));
$this->cal->register_handler('plugin.attendees_list', array($this, 'attendees_list'));
$this->cal->register_handler('plugin.attendees_form', array($this, 'attendees_form'));
$this->cal->register_handler('plugin.resources_form', array($this, 'resources_form'));
$this->cal->register_handler('plugin.resources_list', array($this, 'resources_list'));
$this->cal->register_handler('plugin.resources_searchform', array($this, 'resources_search_form'));
$this->cal->register_handler('plugin.resource_info', array($this, 'resource_info'));
$this->cal->register_handler('plugin.resource_calendar', array($this, 'resource_calendar'));
$this->cal->register_handler('plugin.attendees_freebusy_table', array($this, 'attendees_freebusy_table'));
$this->cal->register_handler('plugin.edit_attendees_notify', array($this, 'edit_attendees_notify'));
$this->cal->register_handler('plugin.edit_recurring_warning', array($this, 'recurring_event_warning'));
$this->cal->register_handler('plugin.event_rsvp_buttons', array($this, 'event_rsvp_buttons'));
$this->cal->register_handler('plugin.angenda_options', array($this, 'angenda_options'));
$this->cal->register_handler('plugin.events_import_form', array($this, 'events_import_form'));
$this->cal->register_handler('plugin.events_export_form', array($this, 'events_export_form'));
$this->cal->register_handler('plugin.event_changelog_table', array($this, 'event_changelog_table'));
$this->cal->register_handler('plugin.searchform', array($this->rc->output, 'search_form')); // use generic method from rcube_template
}
/**
* Adds CSS stylesheets to the page header
*/
public function addCSS()
{
$skin_path = $this->cal->local_skin_path();
$this->cal->include_stylesheet($skin_path . '/fullcalendar.css');
}
/**
* Adds JS files to the page header
*/
public function addJS()
{
$this->cal->include_script('calendar_ui.js');
$this->cal->include_script('lib/js/fullcalendar.js');
$this->rc->output->include_script('treelist.js');
// include kolab folderlist widget if available
if (in_array('libkolab', $this->cal->api->loaded_plugins())) {
$this->cal->api->include_script('libkolab/js/folderlist.js');
}
jqueryui::miniColors();
}
/**
*
*/
function calendar_css($attrib = array())
{
$mode = $this->rc->config->get('calendar_event_coloring', $this->cal->defaults['calendar_event_coloring']);
$categories = $this->cal->driver->list_categories();
$css = "\n";
foreach ((array)$categories as $class => $color) {
if (empty($color))
continue;
$class = 'cat-' . asciiwords(strtolower($class), true);
$css .= ".$class { color: #$color }\n";
if ($mode > 0) {
if ($mode == 2) {
$css .= ".fc-event-$class .fc-event-bg {";
$css .= " opacity: 0.9;";
$css .= " filter: alpha(opacity=90);";
}
else {
$css .= ".fc-event-$class.fc-event-skin, ";
$css .= ".fc-event-$class .fc-event-skin, ";
$css .= ".fc-event-$class .fc-event-inner {";
}
$css .= " background-color: #" . $color . ";";
if ($mode % 2)
$css .= " border-color: #$color;";
$css .= "}\n";
}
}
$calendars = $this->cal->driver->list_calendars();
foreach ((array)$calendars as $id => $prop) {
if (!$prop['color'])
continue;
$css .= $this->calendar_css_classes($id, $prop, $mode);
}
return html::tag('style', array('type' => 'text/css'), $css);
}
/**
*
*/
public function calendar_css_classes($id, $prop, $mode)
{
$color = $prop['color'];
$class = 'cal-' . asciiwords($id, true);
- $css .= "li.$class, #eventshow .$class { color: #$color }\n";
+ $css .= "li .$class, #eventshow .$class { color: #$color }\n";
if ($mode != 1) {
if ($mode == 3) {
$css .= ".fc-event-$class .fc-event-bg {";
$css .= " opacity: 0.9;";
$css .= " filter: alpha(opacity=90);";
}
else {
$css .= ".fc-event-$class, ";
$css .= ".fc-event-$class .fc-event-inner {";
}
if (!$attrib['printmode'])
$css .= " background-color: #$color;";
if ($mode % 2 == 0)
$css .= " border-color: #$color;";
$css .= "}\n";
}
return $css . ".$class .handle { background-color: #$color; }\n";
}
/**
*
*/
function calendar_list($attrib = array())
{
$html = '';
$jsenv = array();
$tree = true;
$calendars = $this->cal->driver->list_calendars(false, false, $tree);
// walk folder tree
if (is_object($tree)) {
$html = $this->list_tree_html($tree, $calendars, $jsenv, $attrib);
// append birthdays calendar which isn't part of $tree
if ($bdaycal = $calendars[calendar_driver::BIRTHDAY_CALENDAR_ID]) {
$calendars = array(calendar_driver::BIRTHDAY_CALENDAR_ID => $bdaycal);
}
else {
$calendars = array(); // clear array for flat listing
}
}
else {
// fall-back to flat folder listing
$attrib['class'] .= ' flat';
}
foreach ((array)$calendars as $id => $prop) {
if ($attrib['activeonly'] && !$prop['active'])
continue;
$html .= html::tag('li', array('id' => 'rcmlical' . $id, 'class' => $prop['group']),
$content = $this->calendar_list_item($id, $prop, $jsenv, $attrib['activeonly'])
);
}
$this->rc->output->set_env('calendars', $jsenv);
$this->rc->output->add_gui_object('calendarslist', $attrib['id']);
return html::tag('ul', $attrib, $html, html::$common_attrib);
}
/**
* Return html for a structured list <ul> for the folder tree
*/
public function list_tree_html($node, $data, &$jsenv, $attrib)
{
$out = '';
foreach ($node->children as $folder) {
$id = $folder->id;
$prop = $data[$id];
$is_collapsed = false; // TODO: determine this somehow?
$content = $this->calendar_list_item($id, $prop, $jsenv, $attrib['activeonly']);
if (!empty($folder->children)) {
$content .= html::tag('ul', array('style' => ($is_collapsed ? "display:none;" : null)),
$this->list_tree_html($folder, $data, $jsenv, $attrib));
}
if (strlen($content)) {
$out .= html::tag('li', array(
'id' => 'rcmlical' . rcube_utils::html_identifier($id),
'class' => $prop['group'] . ($prop['virtual'] ? ' virtual' : ''),
),
$content);
}
}
return $out;
}
/**
* Helper method to build a calendar list item (HTML content and js data)
*/
public function calendar_list_item($id, $prop, &$jsenv, $activeonly = false)
{
// enrich calendar properties with settings from the driver
if (!$prop['virtual']) {
unset($prop['user_id']);
$prop['alarms'] = $this->cal->driver->alarms;
$prop['attendees'] = $this->cal->driver->attendees;
$prop['freebusy'] = $this->cal->driver->freebusy;
$prop['attachments'] = $this->cal->driver->attachments;
$prop['undelete'] = $this->cal->driver->undelete;
$prop['feedurl'] = $this->cal->get_url(array('_cal' => $this->cal->ical_feed_hash($id) . '.ics', 'action' => 'feed'));
$jsenv[$id] = $prop;
}
$classes = array('calendar', 'cal-' . asciiwords($id, true));
$title = $prop['title'] ?: ($prop['name'] != $prop['listname'] || strlen($prop['name']) > 25 ?
html_entity_decode($prop['name'], ENT_COMPAT, RCMAIL_CHARSET) : '');
if ($prop['virtual'])
$classes[] = 'virtual';
else if ($prop['readonly'])
$classes[] = 'readonly';
if ($prop['subscribed'])
$classes[] = 'subscribed';
if ($prop['subscribed'] === 2)
$classes[] = 'partial';
if ($prop['class'])
$classes[] = $prop['class'];
$content = '';
if (!$activeonly || $prop['active']) {
$label_id = 'cl:' . $id;
$content = html::div(join(' ', $classes),
html::span(array('class' => 'calname', 'id' => $label_id, 'title' => $title), $prop['editname'] ? Q($prop['editname']) : $prop['listname']) .
($prop['virtual'] ? '' :
html::tag('input', array('type' => 'checkbox', 'name' => '_cal[]', 'value' => $id, 'checked' => $prop['active'], 'aria-labelledby' => $label_id), '') .
html::span('actions',
($prop['removable'] ? html::a(array('href' => '#', 'class' => 'remove', 'title' => $this->cal->gettext('removelist')), ' ') : '') .
html::a(array('href' => '#', 'class' => 'quickview', 'title' => $this->cal->gettext('quickview'), 'role' => 'checkbox', 'aria-checked' => 'false'), '') .
(isset($prop['subscribed']) ? html::a(array('href' => '#', 'class' => 'subscribed', 'title' => $this->cal->gettext('calendarsubscribe'), 'role' => 'checkbox', 'aria-checked' => $prop['subscribed'] ? 'true' : 'false'), ' ') : '')
) .
html::span(array('class' => 'handle', 'style' => "background-color: #" . ($prop['color'] ?: 'f00')), ' ')
)
);
}
return $content;
}
/**
*
*/
function angenda_options($attrib = array())
{
$attrib += array('id' => 'agendaoptions');
$attrib['style'] .= 'display:none';
$select_range = new html_select(array('name' => 'listrange', 'id' => 'agenda-listrange'));
$select_range->add(1 . ' ' . preg_replace('/\(.+\)/', '', $this->cal->lib->gettext('days')), $days);
foreach (array(2,5,7,14,30,60,90,180,365) as $days)
$select_range->add($days . ' ' . preg_replace('/\(|\)/', '', $this->cal->lib->gettext('days')), $days);
$html .= html::label('agenda-listrange', $this->cal->gettext('listrange'));
$html .= $select_range->show($this->rc->config->get('calendar_agenda_range', $this->cal->defaults['calendar_agenda_range']));
$select_sections = new html_select(array('name' => 'listsections', 'id' => 'agenda-listsections'));
$select_sections->add('---', '');
foreach (array('day' => 'libcalendaring.days', 'week' => 'libcalendaring.weeks', 'month' => 'libcalendaring.months', 'smart' => 'calendar.smartsections') as $val => $label)
$select_sections->add(preg_replace('/\(|\)/', '', ucfirst($this->rc->gettext($label))), $val);
$html .= html::span('spacer', ' ');
$html .= html::label('agenda-listsections', $this->cal->gettext('listsections'));
$html .= $select_sections->show($this->rc->config->get('calendar_agenda_sections', $this->cal->defaults['calendar_agenda_sections']));
return html::div($attrib, $html);
}
/**
* Render a HTML select box for calendar selection
*/
function calendar_select($attrib = array())
{
$attrib['name'] = 'calendar';
$attrib['is_escaped'] = true;
$select = new html_select($attrib);
foreach ((array)$this->cal->driver->list_calendars() as $id => $prop) {
if (!$prop['readonly'])
$select->add($prop['name'], $id);
}
return $select->show(null);
}
/**
* Render a HTML select box for user identity selection
*/
function identity_select($attrib = array())
{
$attrib['name'] = 'identity';
$select = new html_select($attrib);
$identities = $this->rc->user->list_emails();
foreach ($identities as $ident) {
$select->add(format_email_recipient($ident['email'], $ident['name']), $ident['identity_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_keys((array)$this->cal->driver->list_categories()) as $cat) {
$select->add($cat, $cat);
}
return $select->show(null);
}
/**
* Render a HTML select box for status property
*/
function status_select($attrib = array())
{
$attrib['name'] = 'status';
$select = new html_select($attrib);
$select->add('---', '');
$select->add($this->cal->gettext('status-confirmed'), 'CONFIRMED');
$select->add($this->cal->gettext('status-cancelled'), 'CANCELLED');
//$select->add($this->cal->gettext('tentative'), 'TENTATIVE');
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->cal->gettext('free'), 'free');
$select->add($this->cal->gettext('busy'), 'busy');
// out-of-office is not supported by libkolabxml (#3220)
// $select->add($this->cal->gettext('outofoffice'), 'outofoffice');
$select->add($this->cal->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('---', '0');
$select->add('1 '.$this->cal->gettext('highest'), '1');
$select->add('2 '.$this->cal->gettext('high'), '2');
$select->add('3 ', '3');
$select->add('4 ', '4');
$select->add('5 '.$this->cal->gettext('normal'), '5');
$select->add('6 ', '6');
$select->add('7 ', '7');
$select->add('8 '.$this->cal->gettext('low'), '8');
$select->add('9 '.$this->cal->gettext('lowest'), '9');
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->cal->gettext('public'), 'public');
$select->add($this->cal->gettext('private'), 'private');
$select->add($this->cal->gettext('confidential'), 'confidential');
return $select->show(null);
}
/**
* Render HTML form for alarm configuration
*/
function alarm_select($attrib = array())
{
return $this->cal->lib->alarm_select($attrib, $this->cal->driver->alarm_types, $this->cal->driver->alarm_absolute);
}
/**
*
*/
function edit_attendees_notify($attrib = array())
{
$checkbox = new html_checkbox(array('name' => '_notify', 'id' => 'edit-attendees-donotify', 'value' => 1));
return html::div($attrib, html::label(null, $checkbox->show(1) . ' ' . $this->cal->gettext('sendnotifications')));
}
/**
* 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->cal->gettext('currentevent')) . ' ' .
html::label(null, $radio->show('', array('value' => 'future')) . $this->cal->gettext('futurevents')) . ' ' .
html::label(null, $radio->show('all', array('value' => 'all')) . $this->cal->gettext('allevents')) . ' ' .
html::label(null, $radio->show('', array('value' => 'new')) . $this->cal->gettext('saveasnew'));
return html::div($attrib, html::div('message', html::span('ui-icon ui-icon-alert', '') . $this->cal->gettext('changerecurringeventwarning')) . html::div('savemode', $form));
}
/**
* Form for uploading and importing events
*/
function events_import_form($attrib = array())
{
if (!$attrib['id'])
$attrib['id'] = 'rcmImportForm';
// Get max filesize, enable upload progress bar
$max_filesize = rcube_upload_init();
$accept = '.ics, text/calendar, text/x-vcalendar, application/ics';
if (class_exists('ZipArchive', false)) {
$accept .= ', .zip, application/zip';
}
$input = new html_inputfield(array(
'type' => 'file', 'name' => '_data', 'size' => $attrib['uploadfieldsize'],
'accept' => $accept));
$select = new html_select(array('name' => '_range', 'id' => 'event-import-range'));
$select->add(array(
$this->cal->gettext('onemonthback'),
$this->cal->gettext(array('name' => 'nmonthsback', 'vars' => array('nr'=>2))),
$this->cal->gettext(array('name' => 'nmonthsback', 'vars' => array('nr'=>3))),
$this->cal->gettext(array('name' => 'nmonthsback', 'vars' => array('nr'=>6))),
$this->cal->gettext(array('name' => 'nmonthsback', 'vars' => array('nr'=>12))),
$this->cal->gettext('all'),
),
array('1','2','3','6','12',0));
$html .= html::div('form-section',
html::div(null, $input->show()) .
html::div('hint', rcube_label(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize))))
);
$html .= html::div('form-section',
html::label('event-import-calendar', $this->cal->gettext('calendar')) .
$this->calendar_select(array('name' => 'calendar', 'id' => 'event-import-calendar'))
);
$html .= html::div('form-section',
html::label('event-import-range', $this->cal->gettext('importrange')) .
$select->show(1)
);
$this->rc->output->add_gui_object('importform', $attrib['id']);
$this->rc->output->add_label('import');
return html::tag('form', array('action' => $this->rc->url(array('task' => 'calendar', 'action' => 'import_events')),
'method' => "post", 'enctype' => 'multipart/form-data', 'id' => $attrib['id']),
$html
);
}
/**
* Form to select options for exporting events
*/
function events_export_form($attrib = array())
{
if (!$attrib['id'])
$attrib['id'] = 'rcmExportForm';
$html .= html::div('form-section',
html::label('event-export-calendar', $this->cal->gettext('calendar')) .
$this->calendar_select(array('name' => 'calendar', 'id' => 'event-export-calendar'))
);
$select = new html_select(array('name' => 'range', 'id' => 'event-export-range'));
$select->add(array(
$this->cal->gettext('all'),
$this->cal->gettext('onemonthback'),
$this->cal->gettext(array('name' => 'nmonthsback', 'vars' => array('nr'=>2))),
$this->cal->gettext(array('name' => 'nmonthsback', 'vars' => array('nr'=>3))),
$this->cal->gettext(array('name' => 'nmonthsback', 'vars' => array('nr'=>6))),
$this->cal->gettext(array('name' => 'nmonthsback', 'vars' => array('nr'=>12))),
$this->cal->gettext('customdate'),
),
array(0,'1','2','3','6','12','custom'));
$startdate = new html_inputfield(array('name' => 'start', 'size' => 11, 'id' => 'event-export-startdate'));
$html .= html::div('form-section',
html::label('event-export-range', $this->cal->gettext('exportrange')) .
$select->show(0) .
html::span(array('style'=>'display:none'), $startdate->show())
);
$checkbox = new html_checkbox(array('name' => 'attachments', 'id' => 'event-export-attachments', 'value' => 1));
$html .= html::div('form-section',
html::label('event-export-range', $this->cal->gettext('exportattachments')) .
$checkbox->show(1)
);
$this->rc->output->add_gui_object('exportform', $attrib['id']);
return html::tag('form', array('action' => $this->rc->url(array('task' => 'calendar', 'action' => 'export_events')),
'method' => "post", 'id' => $attrib['id']),
$html
);
}
/**
* Generate the form for event attachments upload
*/
function attachments_form($attrib = array())
{
// add ID if not given
if (!$attrib['id'])
$attrib['id'] = 'rcmUploadForm';
// Get max filesize, enable upload progress bar
$max_filesize = rcube_upload_init();
$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('formbuttons', $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))))
);
}
/**
* Register UI object for HTML5 drag & drop file upload
*/
function file_drop_area($attrib = array())
{
if ($attrib['id']) {
$this->rc->output->add_gui_object('filedrop', $attrib['id']);
$this->rc->output->set_env('filedrop', array('action' => 'upload', 'fieldname' => '_attachments'));
}
}
/**
* Generate HTML element for attachments list
*/
function attachments_list($attrib = array())
{
if (!$attrib['id'])
$attrib['id'] = 'rcmAttachmentList';
$skin_path = $this->cal->local_skin_path();
if ($attrib['deleteicon']) {
$_SESSION[calendar::SESSION_KEY . '_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']);
$this->attachmentlist_id = $attrib['id'];
return html::tag('ul', $attrib, '', html::$common_attrib);
}
/**
* Handler for calendar form template.
* The form content could be overriden by the driver
*/
function calendar_editform($action, $calendar = array())
{
// compose default calendar form fields
$input_name = new html_inputfield(array('name' => 'name', 'id' => 'calendar-name', 'size' => 20));
$input_color = new html_inputfield(array('name' => 'color', 'id' => 'calendar-color', 'size' => 6));
$formfields = array(
'name' => array(
'label' => $this->cal->gettext('name'),
'value' => $input_name->show($calendar['name']),
'id' => 'calendar-name',
),
'color' => array(
'label' => $this->cal->gettext('color'),
'value' => $input_color->show($calendar['color']),
'id' => 'calendar-color',
),
);
if ($this->cal->driver->alarms) {
$checkbox = new html_checkbox(array('name' => 'showalarms', 'id' => 'calendar-showalarms', 'value' => 1));
$formfields['showalarms'] = array(
'label' => $this->cal->gettext('showalarms'),
'value' => $checkbox->show($calendar['showalarms']?1:0),
'id' => 'calendar-showalarms',
);
}
// allow driver to extend or replace the form content
return html::tag('form', array('action' => "#", 'method' => "get", 'id' => 'calendarpropform'),
$this->cal->driver->calendar_form($action, $calendar, $formfields)
);
}
/**
*
*/
function attendees_list($attrib = array())
{
// add "noreply" checkbox to attendees table only
$invitations = strpos($attrib['id'], 'attend') !== false;
$invite = new html_checkbox(array('value' => 1, 'id' => 'edit-attendees-invite'));
$table = new html_table(array('cols' => 5 + intval($invitations), 'border' => 0, 'cellpadding' => 0, 'class' => 'rectable'));
$table->add_header('role', $this->cal->gettext('role'));
$table->add_header('name', $this->cal->gettext($attrib['coltitle'] ?: 'attendee'));
$table->add_header('availability', $this->cal->gettext('availability'));
$table->add_header('confirmstate', $this->cal->gettext('confirmstate'));
if ($invitations) {
$table->add_header(array('class' => 'invite', 'title' => $this->cal->gettext('sendinvitations')),
$invite->show(1) . html::label('edit-attendees-invite', $this->cal->gettext('sendinvitations')));
}
$table->add_header('options', '');
// hide invite column if disabled by config
$itip_notify = (int)$this->rc->config->get('calendar_itip_send_option', $this->cal->defaults['calendar_itip_send_option']);
if ($invitations && !($itip_notify & 2)) {
$css = sprintf('#%s td.invite, #%s th.invite { display:none !important }', $attrib['id'], $attrib['id']);
$this->rc->output->add_footer(html::tag('style', array('type' => 'text/css'), $css));
}
return $table->show($attrib);
}
/**
*
*/
function attendees_form($attrib = array())
{
$input = new html_inputfield(array('name' => 'participant', 'id' => 'edit-attendee-name', 'size' => 30));
$textarea = new html_textarea(array('name' => 'comment', 'id' => 'edit-attendees-comment',
'rows' => 4, 'cols' => 55, 'title' => $this->cal->gettext('itipcommenttitle')));
return html::div($attrib,
html::div(null, $input->show() . " " .
html::tag('input', array('type' => 'button', 'class' => 'button', 'id' => 'edit-attendee-add', 'value' => $this->cal->gettext('addattendee'))) . " " .
html::tag('input', array('type' => 'button', 'class' => 'button', 'id' => 'edit-attendee-schedule', 'value' => $this->cal->gettext('scheduletime').'...'))) .
html::p('attendees-commentbox', html::label(null, $this->cal->gettext('itipcomment') . $textarea->show()))
);
}
/**
*
*/
function resources_form($attrib = array())
{
$input = new html_inputfield(array('name' => 'resource', 'id' => 'edit-resource-name', 'size' => 30));
return html::div($attrib,
html::div(null, $input->show() . " " .
html::tag('input', array('type' => 'button', 'class' => 'button', 'id' => 'edit-resource-add', 'value' => $this->cal->gettext('addresource'))) . " " .
html::tag('input', array('type' => 'button', 'class' => 'button', 'id' => 'edit-resource-find', 'value' => $this->cal->gettext('findresources').'...')))
);
}
/**
*
*/
function resources_list($attrib = array())
{
$attrib += array('id' => 'calendar-resources-list');
$this->rc->output->add_gui_object('resourceslist', $attrib['id']);
return html::tag('ul', $attrib, '', html::$common_attrib);
}
/**
*
*/
public function resource_info($attrib = array())
{
$attrib += array('id' => 'calendar-resources-info');
$this->rc->output->add_gui_object('resourceinfo', $attrib['id']);
$this->rc->output->add_gui_object('resourceownerinfo', $attrib['id'] . '-owner');
// copy address book labels for owner details to client
$this->rc->output->add_label('name','firstname','surname','department','jobtitle','email','phone','address');
$table_attrib = array('id','class','style','width','summary','cellpadding','cellspacing','border');
return html::tag('table', $attrib,
html::tag('tbody', null, ''), $table_attrib) .
html::tag('table', array('id' => $attrib['id'] . '-owner', 'style' => 'display:none') + $attrib,
html::tag('thead', null,
html::tag('tr', null,
html::tag('td', array('colspan' => 2), Q($this->cal->gettext('resourceowner')))
)
) .
html::tag('tbody', null, ''),
$table_attrib);
}
/**
*
*/
public function resource_calendar($attrib = array())
{
$attrib += array('id' => 'calendar-resources-calendar');
$this->rc->output->add_gui_object('resourceinfocalendar', $attrib['id']);
return html::div($attrib, '');
}
/**
* GUI object 'searchform' for the resource finder dialog
*
* @param array Named parameters
* @return string HTML code for the gui object
*/
function resources_search_form($attrib)
{
$attrib += array('command' => 'search-resource', 'id' => 'rcmcalresqsearchbox', 'autocomplete' => 'off');
$attrib['name'] = '_q';
$input_q = new html_inputfield($attrib);
$out = $input_q->show();
// add form tag around text field
$out = $this->rc->output->form_tag(array(
'name' => "rcmcalresoursqsearchform",
'onsubmit' => rcmail_output::JS_OBJECT_NAME . ".command('" . $attrib['command'] . "'); return false",
'style' => "display:inline"),
$out);
return $out;
}
/**
*
*/
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->cal->gettext('tabattendees')) .
html::div('timesheader', ' ') .
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'), ' ')
)
);
return $table->show($attrib);
}
/**
* Table oultine for event changelog display
*/
function event_changelog_table($attrib = array())
{
$table = new html_table(array('cols' => 5, 'border' => 0, 'cellspacing' => 0));
$table->add_header('diff', '');
$table->add_header('revision', $this->cal->gettext('revision'));
$table->add_header('date', $this->cal->gettext('date'));
$table->add_header('user', $this->cal->gettext('user'));
$table->add_header('operation', $this->cal->gettext('operation'));
$table->add_header('actions', ' ');
return $table->show($attrib);
}
/**
*
*/
function event_invitebox($attrib = array())
{
if ($this->cal->event) {
return html::div($attrib,
$this->cal->itip->itip_object_details_table($this->cal->event, $this->cal->itip->gettext('itipinvitation')) .
$this->cal->invitestatus
);
}
return '';
}
function event_rsvp_buttons($attrib = array())
{
$actions = array('accepted','tentative','declined');
if ($attrib['delegate'] !== 'false')
$actions[] = 'delegated';
return $this->cal->itip->itip_rsvp_buttons($attrib, $actions);
}
}
diff --git a/plugins/calendar/print.js b/plugins/calendar/print.js
index 731a7d21..c3c647ce 100644
--- a/plugins/calendar/print.js
+++ b/plugins/calendar/print.js
@@ -1,173 +1,173 @@
/**
* Print view for the Calendar plugin
*
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* @licstart The following is the entire license notice for the
* JavaScript code in this file.
*
* Copyright (C) 2011, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @licend The above is the entire license notice
* for the JavaScript code in this file.
*/
/* calendar plugin printing code */
window.rcmail && rcmail.addEventListener('init', function(evt) {
// quote html entities
var Q = function(str)
{
return String(str).replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
};
var rc_loading;
var showdesc = true;
var settings = $.extend(rcmail.env.calendar_settings, rcmail.env.libcal_settings);
// create list of event sources AKA calendars
var src, event_sources = [];
var add_url = (rcmail.env.search ? '&q='+escape(rcmail.env.search) : '');
for (var id in rcmail.env.calendars) {
source = $.extend({
url: "./?_task=calendar&_action=load_events&source=" + escape(id) + add_url,
className: 'fc-event-cal-'+id,
id: id
}, rcmail.env.calendars[id]);
event_sources.push(source);
}
var viewdate = new Date();
if (rcmail.env.date)
viewdate.setTime(rcmail.env.date * 1000);
// initalize the fullCalendar plugin
var fc = $('#calendar').fullCalendar({
header: {
left: '',
center: 'title',
right: 'agendaDay,agendaWeek,month,table'
},
aspectRatio: 0.85,
ignoreTimezone: true, // will treat the given date strings as in local (browser's) timezone
date: viewdate.getDate(),
month: viewdate.getMonth(),
year: viewdate.getFullYear(),
defaultView: rcmail.env.view,
eventSources: event_sources,
monthNames : settings['months'],
monthNamesShort : settings['months_short'],
dayNames : settings['days'],
dayNamesShort : settings['days_short'],
firstDay : settings['first_day'],
firstHour : settings['first_hour'],
slotMinutes : 60/settings['timeslots'],
timeFormat: {
'': settings['time_format'],
agenda: settings['time_format'] + '{ - ' + settings['time_format'] + '}',
list: settings['time_format'] + '{ - ' + settings['time_format'] + '}',
table: settings['time_format'] + '{ - ' + settings['time_format'] + '}'
},
axisFormat : settings['time_format'],
columnFormat: {
month: 'ddd', // Mon
week: 'ddd ' + settings['date_short'], // Mon 9/7
day: 'dddd ' + settings['date_short'], // Monday 9/7
list: settings['date_agenda'],
table: settings['date_agenda']
},
titleFormat: {
month: 'MMMM yyyy',
week: settings['dates_long'],
day: 'dddd ' + settings['date_long'],
list: settings['dates_long'],
table: settings['dates_long']
},
listSections: rcmail.env.listSections !== undefined ? rcmail.env.listSections : settings['agenda_sections'],
listRange: rcmail.env.listRange || settings['agenda_range'],
tableCols: ['handle', 'date', 'time', 'title', 'location'],
allDayText: rcmail.gettext('all-day', 'calendar'),
buttonText: {
day: rcmail.gettext('day', 'calendar'),
week: rcmail.gettext('week', 'calendar'),
month: rcmail.gettext('month', 'calendar'),
table: rcmail.gettext('agenda', 'calendar')
},
listTexts: {
until: rcmail.gettext('until', 'calendar'),
past: rcmail.gettext('pastevents', 'calendar'),
today: rcmail.gettext('today', 'calendar'),
tomorrow: rcmail.gettext('tomorrow', 'calendar'),
thisWeek: rcmail.gettext('thisweek', 'calendar'),
nextWeek: rcmail.gettext('nextweek', 'calendar'),
thisMonth: rcmail.gettext('thismonth', 'calendar'),
nextMonth: rcmail.gettext('nextmonth', 'calendar'),
future: rcmail.gettext('futureevents', 'calendar'),
week: rcmail.gettext('weekofyear', 'calendar')
},
loading: function(isLoading) {
rc_loading = rcmail.set_busy(isLoading, 'loading', rc_loading);
},
// event rendering
eventRender: function(event, element, view) {
- if (view.name != 'month') {
+ if (view.name != 'month' && view.name != 'table') {
var cont = element.find('.fc-event-title');
if (event.location) {
cont.after('<div class="fc-event-location">@ ' + Q(event.location) + '</div>');
cont = cont.next();
}
if (event.description && showdesc) {
cont.after('<div class="fc-event-description">' + Q(event.description) + '</div>');
}
/* TODO: create icons black on white
if (event.recurrence)
element.find('.fc-event-time').append('<i class="fc-icon-recurring"></i>');
if (event.alarms)
element.find('.fc-event-time').append('<i class="fc-icon-alarms"></i>');
*/
}
if (view.name == 'table' && event.description && showdesc) {
var cols = element.children().css('border', 0).length;
element.after('<tr class="fc-event-row-secondary fc-event"><td colspan="'+cols+'" class="fc-event-description">' + Q(event.description) + '</td></tr>');
}
},
viewDisplay: function(view) {
// remove hard-coded hight and make contents visible
window.setTimeout(function(){
if (view.name == 'table') {
$('div.fc-list-content').css('overflow', 'visible').height('auto');
}
else {
$('div.fc-agenda-divider')
.next().css('overflow', 'visible').height('auto')
.children('div').css('overflow', 'visible').height('auto');
}
// adjust fixed height if vertical day slots
var h = $('table.fc-agenda-slots:visible').height() + $('table.fc-agenda-allday:visible').height() + 4;
if (h) $('table.fc-agenda-days td.fc-widget-content').children('div').height(h);
}, 20);
}
});
// activate settings form
$('#propdescription').change(function(){
showdesc = this.checked;
fc.fullCalendar('render');
});
});
diff --git a/plugins/calendar/skins/larry/print.css b/plugins/calendar/skins/larry/print.css
index 908d7600..5d190f5b 100644
--- a/plugins/calendar/skins/larry/print.css
+++ b/plugins/calendar/skins/larry/print.css
@@ -1,209 +1,213 @@
/*** Printing styles for Calendar plugin ***/
body {
margin: 0;
color: #000;
background: #fff;
}
body, td, th, div, p, h3, select, input, textarea {
font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
font-size: 8pt;
}
#calendar {
position: relative;
top: 0;
left: 0;
height: auto;
margin: 5em auto 0 auto;
overflow: visible;
}
#calendar .fc-header-right {
padding-right: 0;
}
#printconfig {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 10000;
padding: 0.5em;
background: #ebebeb;
border-bottom: 1px solid #999;
box-shadow: 0 3px 4px #ccc;
-moz-box-shadow: 0 3px 4px #ccc;
-webkit-box-shadow: 0 3px 4px #ccc;
}
#printconfig .prop {
padding-right: 2em;
}
#message {
position: absolute;
top: 5.5em;
left: 1em;
}
#message div.loading {
color: #666;
font-style: italic;
}
#calendarlist {
list-style-type: square;
margin: 2em 0;
padding-left: 1em;
}
#calendarlist li {
- float: left;
padding-left: 0;
padding-right: 3em;
margin-left: 0;
font-weight: bold;
}
#calendarlist input,
#calendarlist .handle {
display: none;
}
+#calendarlist li.x-invitations div {
+ color: #999;
+ font-style: italic;
+}
+
.calwidth {
width: 700px;
margin: 0 auto;
}
.rightalign {
float: right;
padding-top: 0.3em;
}
@media print {
.noprint,
.fc-header-right span {
display: none;
}
#calendar {
margin-top: 0;
}
}
/* fullcalendar style overrides */
.fc-view {
overflow: visible;
}
.fc-event-skin,
.fc-event-inner .fc-event-skin {
color: black;
background-color: #fff !important;
}
.fc-event-title {
font-weight: bold;
}
.fc-event-hori .fc-event-title {
font-weight: normal;
white-space: nowrap;
}
.fc-event-hori .fc-event-time {
white-space: nowrap;
font-weight: normal !important;
font-size: 10px;
padding-right: 0.6em;
}
.fc-grid .fc-event-time {
font-weight: normal !important;
padding-right: 0.3em;
}
.fc-event-cateories {
font-style: italic;
}
.fc-event-location {
font-size: 90%;
}
.fc-agenda-slots td div {
height: 1.4em;
}
.fc-widget-header,
.fc-mon, .fc-tue, .fc-wed, .fc-thu, .fc-fri {
background-color: #fff;
}
.fc-widget-header, .fc-widget-content {
border-color: #ccc;
}
.fc-icon-alarms,
.fc-icon-recurring {
display: inline-block;
width: 11px;
height: 11px;
background: url('images/eventicons.gif') 0 0 no-repeat;
margin-left: 3px;
line-height: 10px;
}
.fc-icon-alarms {
background-position: 0 -13px;
}
.fc-view-list, .fc-view-table {
border: 0;
}
.fc-view-list div.fc-list-header,
.fc-view-table td.fc-list-header {
padding: 0.3em;
background: #fff;
font-weight: bold;
font-size: 1.2em;
color: #333;
border-color: #333;
border-style: solid;
border-width: 1px 0;
filter: none;
}
.fc-list-section .fc-event {
cursor: auto;
}
.fc-view-table tr.fc-event td,
.fc-view-table tr.fc-event td.fc-event-handle {
border-color: #999;
padding-top: 0.5em;
padding-bottom: 0.5em;
}
.fc-view-table tr.fc-last td {
border: 0;
}
.fc-view-table tr.fc-event .fc-event-description {
padding-left: 2em;
padding-top: 0em;
}
.fc-event-vert .fc-event-description {
font-size: 90%;
font-style: italic;
}
.fc-view-table col.fc-event-location {
width: 20%;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Feb 2, 5:13 AM (1 d, 19 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
426825
Default Alt Text
(44 KB)
Attached To
Mode
R14 roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline
Log In to Comment