Added fallback to accept/decline invitations by URL if recipient doesn't understand iTIP + small refactoring if itip code
This commit is contained in:
parent
6e904e0d88
commit
37cb27f2f3
13 changed files with 680 additions and 224 deletions
|
@ -33,7 +33,7 @@ class calendar extends rcube_plugin
|
|||
const FREEBUSY_TENTATIVE = 3;
|
||||
const FREEBUSY_OOF = 4;
|
||||
|
||||
public $task = '?(?!login|logout).*';
|
||||
public $task = '?(?!logout).*';
|
||||
public $rc;
|
||||
public $driver;
|
||||
public $home; // declare public to be used in other classes
|
||||
|
@ -98,11 +98,16 @@ class calendar extends rcube_plugin
|
|||
|
||||
$this->ui->init();
|
||||
|
||||
// settings are required in every GUI step
|
||||
$this->rc->output->set_env('calendar_settings', $this->load_settings());
|
||||
// settings are required in (almost) every GUI step
|
||||
if ($this->rc->action != 'attend')
|
||||
$this->rc->output->set_env('calendar_settings', $this->load_settings());
|
||||
}
|
||||
|
||||
if ($this->rc->task == 'calendar' && $this->rc->action != 'save-pref') {
|
||||
// catch iTIP confirmation requests that don're require a valid session
|
||||
if ($this->rc->action == 'attend' && !empty($_REQUEST['_t'])) {
|
||||
$this->add_hook('startup', array($this, 'itip_attend_response'));
|
||||
}
|
||||
else if ($this->rc->task == 'calendar' && $this->rc->action != 'save-pref') {
|
||||
if ($this->rc->action != 'upload') {
|
||||
$this->load_driver();
|
||||
}
|
||||
|
@ -172,13 +177,26 @@ class calendar extends rcube_plugin
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load iTIP functions
|
||||
*/
|
||||
private function load_itip()
|
||||
{
|
||||
if (!$this->itip) {
|
||||
require_once($this->home . '/lib/calendar_itip.php');
|
||||
$this->itip = new calendar_itip($this);
|
||||
}
|
||||
|
||||
return $this->itip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load iCalendar functions
|
||||
*/
|
||||
private function load_ical()
|
||||
public function get_ical()
|
||||
{
|
||||
if (!$this->ical) {
|
||||
require($this->home . '/lib/calendar_ical.php');
|
||||
require_once($this->home . '/lib/calendar_ical.php');
|
||||
$this->ical = new calendar_ical($this);
|
||||
}
|
||||
|
||||
|
@ -618,8 +636,9 @@ class calendar extends rcube_plugin
|
|||
}
|
||||
}
|
||||
|
||||
if ($organizer && $this->send_itip_message($old, 'REPLY', $organizer, 'itipsubjectdeclined', 'itipmailbodydeclined'))
|
||||
$this->rc->output->command('display_message', $this->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name']))), 'confirmation');
|
||||
$itip = $this->load_itip();
|
||||
if ($organizer && $itip->send_itip_message($old, 'REPLY', $organizer, 'itipsubjectdeclined', 'itipmailbodydeclined'))
|
||||
$this->rc->output->command('display_message', $this->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name'] ? $organizer['name'] : $organizer['email']))), 'confirmation');
|
||||
else
|
||||
$this->rc->output->command('display_message', $this->gettext('itipresponseerror'), 'error');
|
||||
}
|
||||
|
@ -688,8 +707,9 @@ class calendar extends rcube_plugin
|
|||
break;
|
||||
}
|
||||
}
|
||||
if ($organizer && $this->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status))
|
||||
$this->rc->output->command('display_message', $this->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name']))), 'confirmation');
|
||||
$itip = $this->load_itip();
|
||||
if ($organizer && $itip->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status))
|
||||
$this->rc->output->command('display_message', $this->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name'] ? $organizer['name'] : $organizer['email']))), 'confirmation');
|
||||
else
|
||||
$this->rc->output->command('display_message', $this->gettext('itipresponseerror'), 'error');
|
||||
}
|
||||
|
@ -784,8 +804,7 @@ class calendar extends rcube_plugin
|
|||
header("Content-Type: text/calendar");
|
||||
header("Content-Disposition: inline; filename=".$calendar_name.'.ics');
|
||||
|
||||
$this->load_ical();
|
||||
$this->ical->export($events, '', true);
|
||||
$this->get_ical()->export($events, '', true);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
@ -1405,11 +1424,12 @@ class calendar extends rcube_plugin
|
|||
$is_cancelled = true;
|
||||
}
|
||||
|
||||
$itip = $this->load_itip();
|
||||
$emails = $this->get_user_emails();
|
||||
|
||||
// compose multipart message using PEAR:Mail_Mime
|
||||
$method = $action == 'remove' ? 'CANCEL' : 'REQUEST';
|
||||
$message = $this->compose_itip_message($event, $method);
|
||||
$message = $itip->compose_itip_message($event, $method);
|
||||
|
||||
// list existing attendees from $old event
|
||||
$old_attendees = array();
|
||||
|
@ -1430,7 +1450,7 @@ class calendar extends rcube_plugin
|
|||
$subject = $is_cancelled ? 'eventcancelsubject' : ($is_new ? 'invitationsubject' : ($event['title'] ? 'eventupdatesubject':'eventupdatesubjectempty'));
|
||||
|
||||
// finally send the message
|
||||
if ($this->send_itip_message($event, $method, $attendee, $subject, $bodytext, $message))
|
||||
if ($itip->send_itip_message($event, $method, $attendee, $subject, $bodytext, $message))
|
||||
$sent++;
|
||||
else
|
||||
$sent = -100;
|
||||
|
@ -1614,7 +1634,71 @@ class calendar extends rcube_plugin
|
|||
|
||||
|
||||
/**** Event invitation plugin hooks ****/
|
||||
|
||||
/**
|
||||
* Handler for URLs that allow an invitee to respond on his invitation mail
|
||||
*/
|
||||
public function itip_attend_response($p)
|
||||
{
|
||||
if ($p['action'] == 'attend') {
|
||||
$this->rc->output->set_env('task', 'calendar'); // override some env vars
|
||||
$this->rc->output->set_env('keep_alive', 0);
|
||||
$this->rc->output->set_pagetitle($this->gettext('calendar'));
|
||||
|
||||
$itip = $this->load_itip();
|
||||
$token = get_input_value('_t', RCUBE_INPUT_GPC);
|
||||
|
||||
// read event info stored under the given token
|
||||
if ($invitation = $itip->get_invitation($token)) {
|
||||
$this->token = $token;
|
||||
$this->event = $invitation['event'];
|
||||
|
||||
// show message about cancellation
|
||||
if ($invitation['cancelled']) {
|
||||
$this->invitestatus = html::div('rsvp-status declined', $this->gettext('eventcancelled'));
|
||||
}
|
||||
// save submitted RSVP status
|
||||
else if (!empty($_POST['rsvp'])) {
|
||||
$status = null;
|
||||
foreach (array('accepted','tentative','declined') as $method) {
|
||||
if ($_POST['rsvp'] == $this->gettext('itip' . $method)) {
|
||||
$status = $method;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// send itip reply to organizer
|
||||
if ($status && $itip->update_invitation($invitation, $invitation['attendee'], strtoupper($status))) {
|
||||
$this->invitestatus = html::div('rsvp-status ' . strtolower($status), $this->gettext('youhave'.strtolower($status)));
|
||||
}
|
||||
else
|
||||
$this->rc->output->command('display_message', $this->gettext('errorsaving'), 'error', -1);
|
||||
}
|
||||
|
||||
$this->register_handler('plugin.event_inviteform', array($this, 'itip_event_inviteform'));
|
||||
$this->register_handler('plugin.event_invitebox', array($this->ui, 'event_invitebox'));
|
||||
|
||||
if (!$this->invitestatus)
|
||||
$this->register_handler('plugin.event_rsvp_buttons', array($this->ui, 'event_rsvp_buttons'));
|
||||
|
||||
$this->rc->output->set_pagetitle($this->gettext('itipinvitation') . ' ' . $this->event['title']);
|
||||
}
|
||||
else
|
||||
$this->rc->output->command('display_message', $this->gettext('itipinvalidrequest'), 'error', -1);
|
||||
|
||||
$this->rc->output->send('calendar.itipattend');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function itip_event_inviteform($p)
|
||||
{
|
||||
$hidden = new html_hiddenfield(array('name' => "_t", 'value' => $this->token));
|
||||
return html::tag('form', array('action' => $this->rc->url(array('task' => 'calendar', 'action' => 'attend')), 'method' => 'post', 'noclose' => true)) . $hidden->show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check mail message structure of there are .ics files attached
|
||||
*/
|
||||
|
@ -1637,12 +1721,11 @@ class calendar extends rcube_plugin
|
|||
{
|
||||
// load iCalendar functions (if necessary)
|
||||
if (!empty($this->ics_parts)) {
|
||||
$this->load_ical();
|
||||
$this->get_ical();
|
||||
}
|
||||
|
||||
$html = '';
|
||||
foreach ($this->ics_parts as $mime_id) {
|
||||
$this->load_ical();
|
||||
$part = $this->message->mime_parts[$mime_id];
|
||||
$charset = $part->ctype_parameters['charset'] ? $part->ctype_parameters['charset'] : RCMAIL_CHARSET;
|
||||
$events = $this->ical->import($this->message->get_part_content($mime_id), $charset);
|
||||
|
@ -1732,19 +1815,8 @@ class calendar extends rcube_plugin
|
|||
));
|
||||
}
|
||||
|
||||
// show event details in a table
|
||||
$table = new html_table(array('cols' => 2, 'border' => 0, 'class' => 'calendar-eventdetails'));
|
||||
$table->add('ititle', $title);
|
||||
$table->add('title', Q($event['title']));
|
||||
$table->add('label', $this->gettext('date'));
|
||||
$table->add('location', Q($this->event_date_text($event)));
|
||||
if ($event['location']) {
|
||||
$table->add('label', $this->gettext('location'));
|
||||
$table->add('location', Q($event['location']));
|
||||
}
|
||||
|
||||
// add box below messsage body
|
||||
$html .= html::div('calendar-invitebox', $table->show() . $buttons_pre . html::div('rsvp-buttons', $buttons));
|
||||
// show event details with buttons
|
||||
$html .= html::div('calendar-invitebox', $this->ui->event_details_table($event, $title) . $buttons_pre . html::div('rsvp-buttons', $buttons));
|
||||
|
||||
// limit listing
|
||||
if ($idx >= 3)
|
||||
|
@ -1786,8 +1858,7 @@ class calendar extends rcube_plugin
|
|||
$headers = $this->rc->imap->get_headers($uid);
|
||||
}
|
||||
|
||||
$this->load_ical();
|
||||
$events = $this->ical->import($part, $charset);
|
||||
$events = $this->get_ical()->import($part, $charset);
|
||||
|
||||
$error_msg = $this->gettext('errorimportingevent');
|
||||
$success = false;
|
||||
|
@ -1900,8 +1971,9 @@ class calendar extends rcube_plugin
|
|||
|
||||
// send iTip reply
|
||||
if ($this->ical->method == 'REQUEST' && $organizer && !in_array($organizer['email'], $emails) && !$error_msg) {
|
||||
if ($this->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status))
|
||||
$this->rc->output->command('display_message', $this->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name']))), 'confirmation');
|
||||
$itip = $this->load_itip();
|
||||
if ($itip->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status))
|
||||
$this->rc->output->command('display_message', $this->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name'] ? $organizer['name'] : $organizer['email']))), 'confirmation');
|
||||
else
|
||||
$this->rc->output->command('display_message', $this->gettext('itipresponseerror'), 'error');
|
||||
}
|
||||
|
@ -1910,99 +1982,6 @@ class calendar extends rcube_plugin
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an iTip mail message
|
||||
*
|
||||
* @param array Event object to send
|
||||
* @param string iTip method (REQUEST|REPLY|CANCEL)
|
||||
* @param array Hash array with recipient data (name, email)
|
||||
* @param string Mail subject
|
||||
* @param string Mail body text label
|
||||
* @param object Mail_mime object with message data
|
||||
* @return boolean True on success, false on failure
|
||||
*/
|
||||
public function send_itip_message($event, $method, $recipient, $subject, $bodytext, $message = null)
|
||||
{
|
||||
if (!$message)
|
||||
$message = $this->compose_itip_message($event, $method);
|
||||
|
||||
$myself = $this->rc->user->get_identity();
|
||||
$mailto = rcube_idn_to_ascii($recipient['email']);
|
||||
|
||||
$headers = $message->headers();
|
||||
$headers['To'] = format_email_recipient($mailto, $recipient['name']);
|
||||
$headers['Subject'] = $this->gettext(array(
|
||||
'name' => $subject,
|
||||
'vars' => array('title' => $event['title'], 'name' => ($myself['name'] ? $myself['name'] : $myself['email']))
|
||||
));
|
||||
|
||||
// compose a list of all event attendees
|
||||
$attendees_list = array();
|
||||
foreach ((array)$event['attendees'] as $attendee) {
|
||||
$attendees_list[] = ($attendee['name'] && $attendee['email']) ?
|
||||
$attendee['name'] . ' <' . $attendee['email'] . '>' :
|
||||
($attendee['name'] ? $attendee['name'] : $attendee['email']);
|
||||
}
|
||||
|
||||
$mailbody = $this->gettext(array(
|
||||
'name' => $bodytext,
|
||||
'vars' => array(
|
||||
'title' => $event['title'],
|
||||
'date' => $this->event_date_text($event),
|
||||
'attendees' => join(', ', $attendees_list),
|
||||
'sender' => $myself['name'] ? $myself['name'] : $myself['email'],
|
||||
'organizer' => $myself['name'],
|
||||
)
|
||||
));
|
||||
|
||||
$message->headers($headers);
|
||||
$message->setTXTBody(rcube_message::format_flowed($mailbody, 79));
|
||||
|
||||
// finally send the message
|
||||
return rcmail_deliver_message($message, $headers['X-Sender'], $mailto, $smtp_error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to build a Mail_mime object to send an iTip message
|
||||
*
|
||||
* @param array Event object to send
|
||||
* @param string iTip method (REQUEST|REPLY|CANCEL)
|
||||
* @return object Mail_mime object with message data
|
||||
*/
|
||||
private function compose_itip_message($event, $method)
|
||||
{
|
||||
$myself = $this->rc->user->get_identity();
|
||||
$from = rcube_idn_to_ascii($myself['email']);
|
||||
$sender = format_email_recipient($from, $myself['name']);
|
||||
|
||||
// compose multipart message using PEAR:Mail_Mime
|
||||
$message = new Mail_mime("\r\n");
|
||||
$message->setParam('text_encoding', 'quoted-printable');
|
||||
$message->setParam('head_encoding', 'quoted-printable');
|
||||
$message->setParam('head_charset', RCMAIL_CHARSET);
|
||||
$message->setParam('text_charset', RCMAIL_CHARSET . ";\r\n format=flowed");
|
||||
|
||||
// compose common headers array
|
||||
$headers = array(
|
||||
'From' => $sender,
|
||||
'Date' => rcmail_user_date(),
|
||||
'Message-ID' => rcmail_gen_message_id(),
|
||||
'X-Sender' => $from,
|
||||
);
|
||||
if ($agent = $this->rc->config->get('useragent'))
|
||||
$headers['User-Agent'] = $agent;
|
||||
|
||||
$message->headers($headers);
|
||||
|
||||
// attach ics file for this event
|
||||
$this->load_ical();
|
||||
$vcal = $this->ical->export(array($event), $method);
|
||||
$message->addAttachment($vcal, 'text/calendar', 'event.ics', false, '8bit', 'attachment', RCMAIL_CHARSET . "; method=" . $metod);
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if specified message part is a vcalendar data
|
||||
*
|
||||
|
@ -2031,5 +2010,29 @@ class calendar extends rcube_plugin
|
|||
return array_unique($emails);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build an absolute URL with the given parameters
|
||||
*/
|
||||
public function get_url($param = array())
|
||||
{
|
||||
$param += array('task' => 'calendar');
|
||||
|
||||
$schema = 'http';
|
||||
$default_port = 80;
|
||||
if (rcube_https_check()) {
|
||||
$schema = 'https';
|
||||
$default_port = 143;
|
||||
}
|
||||
$url = $schema . '://' . $_SERVER['HTTP_HOST'];
|
||||
if ($_SERVER['SERVER_PORT'] != $default_port)
|
||||
$url .= ':' . $_SERVER['SERVER_PORT'];
|
||||
if (dirname($_SERVER['SCRIPT_NAME']) != '/')
|
||||
$url .= dirname($_SERVER['SCRIPT_NAME']);
|
||||
$url .= preg_replace('!^\./!', '/', $this->rc->url($param));
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -62,3 +62,16 @@ CREATE TABLE `attachments` (
|
|||
CONSTRAINT `fk_attachments_event_id` FOREIGN KEY (`event_id`)
|
||||
REFERENCES `events`(`event_id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
CREATE TABLE `itipinvitations` (
|
||||
`token` VARCHAR(64) NOT NULL,
|
||||
`event_uid` VARCHAR(255) NOT NULL,
|
||||
`user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`event` TEXT NOT NULL,
|
||||
`expires` DATETIME DEFAULT NULL,
|
||||
`cancelled` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY(`token`),
|
||||
INDEX `uid_idx` (`event_uid`,`user_id`),
|
||||
CONSTRAINT `fk_itipinvitations_user_id` FOREIGN KEY (`user_id`)
|
||||
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
|
|
@ -59,3 +59,16 @@ CREATE TABLE attachments (
|
|||
REFERENCES events(event_id)
|
||||
);
|
||||
|
||||
CREATE TABLE itipinvitations (
|
||||
token varchar(64) NOT NULL PRIMARY KEY,
|
||||
event_uid varchar(255) NOT NULL,
|
||||
user_id integer NOT NULL default '0',
|
||||
event text NOT NULL,
|
||||
expires datetime NOT NULL default '1000-01-01 00:00:00',
|
||||
cancelled tinyint(1) NOT NULL default '0',
|
||||
CONSTRAINT fk_itipinvitations_user_id FOREIGN KEY (user_id)
|
||||
REFERENCES users(user_id)
|
||||
);
|
||||
|
||||
CREATE INDEX ix_itipinvitations_uid ON itipinvitations(event_uid,user_id);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Roundcube Calendar Kolab backend
|
||||
*
|
||||
* @version 0.3 beta
|
||||
* @version 0.6 beta
|
||||
* @author Thomas Bruederli
|
||||
* @licence GNU GPL
|
||||
**/
|
||||
|
@ -12,3 +12,17 @@ CREATE TABLE `kolab_alarms` (
|
|||
`dismissed` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY(`event_id`)
|
||||
) /*!40000 ENGINE=INNODB */;
|
||||
|
||||
CREATE TABLE `itipinvitations` (
|
||||
`token` VARCHAR(64) NOT NULL,
|
||||
`event_uid` VARCHAR(255) NOT NULL,
|
||||
`user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`event` TEXT NOT NULL,
|
||||
`expires` DATETIME DEFAULT NULL,
|
||||
`cancelled` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY(`token`),
|
||||
INDEX `uid_idx` (`event_uid`,`user_id`),
|
||||
CONSTRAINT `fk_itipinvitations_user_id` FOREIGN KEY (`user_id`)
|
||||
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
|
|
|
@ -245,7 +245,6 @@ class calendar_ical
|
|||
*/
|
||||
public function export($events, $method = null, $write = false)
|
||||
{
|
||||
if (!empty($this->rc->user->ID)) {
|
||||
$ical = "BEGIN:VCALENDAR" . self::EOL;
|
||||
$ical .= "VERSION:2.0" . self::EOL;
|
||||
$ical .= "PRODID:-//Roundcube Webmail " . RCMAIL_VERSION . "//NONSGML Calendar//EN" . self::EOL;
|
||||
|
@ -332,7 +331,6 @@ class calendar_ical
|
|||
|
||||
// fold lines to 75 chars
|
||||
return rcube_vcard::rfc2425_fold($ical);
|
||||
}
|
||||
}
|
||||
|
||||
private function escpape($str)
|
||||
|
|
322
plugins/calendar/lib/calendar_itip.php
Normal file
322
plugins/calendar/lib/calendar_itip.php
Normal file
|
@ -0,0 +1,322 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* iTIP functions for the Calendar plugin
|
||||
*
|
||||
* Class providing functionality to manage iTIP invitations
|
||||
*
|
||||
* @version 0.6-beta
|
||||
* @author Thomas Bruederli <roundcube@gmail.com>
|
||||
* @package calendar
|
||||
*
|
||||
* Copyright (C) 2011, Kolab Systems AG
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
class calendar_itip
|
||||
{
|
||||
private $rc;
|
||||
private $cal;
|
||||
private $event;
|
||||
|
||||
function __construct($cal)
|
||||
{
|
||||
$this->cal = $cal;
|
||||
$this->rc = $cal->rc;
|
||||
$this->sender = $this->rc->user->get_identity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an iTip mail message
|
||||
*
|
||||
* @param array Event object to send
|
||||
* @param string iTip method (REQUEST|REPLY|CANCEL)
|
||||
* @param array Hash array with recipient data (name, email)
|
||||
* @param string Mail subject
|
||||
* @param string Mail body text label
|
||||
* @param object Mail_mime object with message data
|
||||
* @return boolean True on success, false on failure
|
||||
*/
|
||||
public function send_itip_message($event, $method, $recipient, $subject, $bodytext, $message = null)
|
||||
{
|
||||
if (!$this->sender['name'])
|
||||
$this->sender['name'] = $this->sender['email'];
|
||||
|
||||
if (!$message)
|
||||
$message = $this->compose_itip_message($event, $method);
|
||||
|
||||
$mailto = rcube_idn_to_ascii($recipient['email']);
|
||||
|
||||
$headers = $message->headers();
|
||||
$headers['To'] = format_email_recipient($mailto, $recipient['name']);
|
||||
$headers['Subject'] = $this->cal->gettext(array(
|
||||
'name' => $subject,
|
||||
'vars' => array('title' => $event['title'], 'name' => $this->sender['name'])
|
||||
));
|
||||
|
||||
// compose a list of all event attendees
|
||||
$attendees_list = array();
|
||||
foreach ((array)$event['attendees'] as $attendee) {
|
||||
$attendees_list[] = ($attendee['name'] && $attendee['email']) ?
|
||||
$attendee['name'] . ' <' . $attendee['email'] . '>' :
|
||||
($attendee['name'] ? $attendee['name'] : $attendee['email']);
|
||||
}
|
||||
|
||||
$mailbody = $this->cal->gettext(array(
|
||||
'name' => $bodytext,
|
||||
'vars' => array(
|
||||
'title' => $event['title'],
|
||||
'date' => $this->cal->event_date_text($event),
|
||||
'attendees' => join(', ', $attendees_list),
|
||||
'sender' => $this->sender['name'],
|
||||
'organizer' => $this->sender['name'],
|
||||
)
|
||||
));
|
||||
|
||||
// append links for direct invitation replies
|
||||
if ($method == 'REQUEST' && ($token = $this->store_invitation($event, $recipient['email']))) {
|
||||
$mailbody .= "\n\n" . $this->cal->gettext(array(
|
||||
'name' => 'invitationattendlinks',
|
||||
'vars' => array('url' => $this->cal->get_url(array('action' => 'attend', 't' => $token))),
|
||||
));
|
||||
}
|
||||
else if ($method == 'CANCEL') {
|
||||
$this->cancel_itip_invitation($event);
|
||||
}
|
||||
|
||||
$message->headers($headers);
|
||||
$message->setTXTBody(rcube_message::format_flowed($mailbody, 79));
|
||||
|
||||
// finally send the message
|
||||
return rcmail_deliver_message($message, $headers['X-Sender'], $mailto, $smtp_error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to build a Mail_mime object to send an iTip message
|
||||
*
|
||||
* @param array Event object to send
|
||||
* @param string iTip method (REQUEST|REPLY|CANCEL)
|
||||
* @return object Mail_mime object with message data
|
||||
*/
|
||||
public function compose_itip_message($event, $method)
|
||||
{
|
||||
$from = rcube_idn_to_ascii($this->sender['email']);
|
||||
$sender = format_email_recipient($from, $this->sender['name']);
|
||||
|
||||
// compose multipart message using PEAR:Mail_Mime
|
||||
$message = new Mail_mime("\r\n");
|
||||
$message->setParam('text_encoding', 'quoted-printable');
|
||||
$message->setParam('head_encoding', 'quoted-printable');
|
||||
$message->setParam('head_charset', RCMAIL_CHARSET);
|
||||
$message->setParam('text_charset', RCMAIL_CHARSET . ";\r\n format=flowed");
|
||||
|
||||
// compose common headers array
|
||||
$headers = array(
|
||||
'From' => $sender,
|
||||
'Date' => rcmail_user_date(),
|
||||
'Message-ID' => rcmail_gen_message_id(),
|
||||
'X-Sender' => $from,
|
||||
);
|
||||
if ($agent = $this->rc->config->get('useragent'))
|
||||
$headers['User-Agent'] = $agent;
|
||||
|
||||
$message->headers($headers);
|
||||
|
||||
// attach ics file for this event
|
||||
$ical = $this->cal->get_ical();
|
||||
$ics = $ical->export(array($event), $method);
|
||||
$message->addAttachment($ics, 'text/calendar', 'event.ics', false, '8bit', 'attachment', RCMAIL_CHARSET . "; method=" . $method);
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find invitation record by token
|
||||
*
|
||||
* @param string Invitation token
|
||||
* @return mixed Invitation record as hash array or False if not found
|
||||
*/
|
||||
public function get_invitation($token)
|
||||
{
|
||||
if ($parts = $this->decode_token($token)) {
|
||||
$result = $this->rc->db->query("SELECT * FROM itipinvitations WHERE token=?", $parts['base']);
|
||||
if ($result && ($rec = $this->rc->db->fetch_assoc($result))) {
|
||||
$rec['event'] = unserialize($rec['event']);
|
||||
$rec['attendee'] = $parts['attendee'];
|
||||
return $rec;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the attendee status of the given invitation record
|
||||
*
|
||||
* @param array Invitation record as fetched with calendar_itip::get_invitation()
|
||||
* @param string Attendee email address
|
||||
* @param string New attendee status
|
||||
*/
|
||||
public function update_invitation($invitation, $email, $newstatus)
|
||||
{
|
||||
if (is_string($invitation))
|
||||
$invitation = $this->get_invitation($invitation);
|
||||
|
||||
if ($invitation['token'] && $invitation['event']) {
|
||||
// update attendee record in event data
|
||||
foreach ($invitation['event']['attendees'] as $i => $attendee) {
|
||||
if ($attendee['role'] == 'ORGANIZER') {
|
||||
$organizer = $attendee;
|
||||
}
|
||||
else if ($attendee['email'] == $email) {
|
||||
// nothing to be done here
|
||||
if ($attendee['status'] == $newstatus)
|
||||
return true;
|
||||
|
||||
$invitation['event']['attendees'][$i]['status'] = $newstatus;
|
||||
$this->sender = $attendee;
|
||||
}
|
||||
}
|
||||
$invitation['event']['changed'] = time();
|
||||
|
||||
// send iTIP REPLY message to organizer
|
||||
if ($organizer) {
|
||||
$status = strtolower($newstatus);
|
||||
if ($this->send_itip_message($invitation['event'], 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status))
|
||||
$this->rc->output->command('display_message', $this->cal->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name'] ? $organizer['name'] : $organizer['email']))), 'confirmation');
|
||||
else
|
||||
$this->rc->output->command('display_message', $this->cal->gettext('itipresponseerror'), 'error');
|
||||
}
|
||||
|
||||
// update record in DB
|
||||
$query = $this->rc->db->query(
|
||||
"UPDATE itipinvitations
|
||||
SET event=?
|
||||
WHERE token=?",
|
||||
self::serialize_event($invitation['event']),
|
||||
$invitation['token']
|
||||
);
|
||||
|
||||
if ($this->rc->db->affected_rows($query))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create iTIP invitation token for later replies via URL
|
||||
*
|
||||
* @param array Hash array with event properties
|
||||
* @param string Attendee email address
|
||||
* @return string Invitation token
|
||||
*/
|
||||
public function store_invitation($event, $attendee)
|
||||
{
|
||||
static $stored = array();
|
||||
|
||||
if (!$event['uid'] || !$attendee)
|
||||
return false;
|
||||
|
||||
// generate token for this invitation
|
||||
$token = $this->generate_token($event, $attendee);
|
||||
$base = substr($token, 0, 40);
|
||||
|
||||
// already stored this
|
||||
if ($stored[$base])
|
||||
return $token;
|
||||
|
||||
$query = $this->rc->db->query(
|
||||
"REPLACE INTO itipinvitations
|
||||
(token, event_uid, user_id, event, expires)
|
||||
VALUES(?, ?, ?, ?, ?)",
|
||||
$base,
|
||||
$event['uid'],
|
||||
$this->rc->user->ID,
|
||||
self::serialize_event($event),
|
||||
date('Y-m-d H:i:s', $event['end'] + 86400 * 2)
|
||||
);
|
||||
|
||||
if ($this->rc->db->affected_rows($query)) {
|
||||
$stored[$base] = 1;
|
||||
return $token;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark invitations for the given event as cancelled
|
||||
*
|
||||
* @param array Hash array with event properties
|
||||
*/
|
||||
public function cancel_itip_invitation($event)
|
||||
{
|
||||
// flag invitation record as cancelled
|
||||
$this->rc->db->query(
|
||||
"UPDATE itipinvitations
|
||||
SET cancelled=1
|
||||
WHERE event_uid=? AND user_id=?",
|
||||
$event['uid'],
|
||||
$this->rc->user->ID
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an invitation request token for the given event and attendee
|
||||
*
|
||||
* @param array Event hash array
|
||||
* @param string Attendee email address
|
||||
*/
|
||||
public function generate_token($event, $attendee)
|
||||
{
|
||||
$base = sha1($event['uid'] . ';' . $this->rc->user->ID);
|
||||
$mail = base64_encode($attendee);
|
||||
$hash = substr(md5($base . $mail . $this->rc->config->get('des_key')), 0, 6);
|
||||
|
||||
return "$base.$mail.$hash";
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given iTIP request token and return its parts
|
||||
*
|
||||
* @param string Request token to decode
|
||||
* @return mixed Hash array with parts or False if invalid
|
||||
*/
|
||||
public function decode_token($token)
|
||||
{
|
||||
list($base, $mail, $hash) = explode('.', $token);
|
||||
|
||||
// validate and return parts
|
||||
if ($mail && $hash && $hash == substr(md5($base . $mail . $this->rc->config->get('des_key')), 0, 6)) {
|
||||
return array('base' => $base, 'attendee' => base64_decode($mail));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to serialize the given event for storing in invitations table
|
||||
*/
|
||||
private static function serialize_event($event)
|
||||
{
|
||||
$ev = $event;
|
||||
$ev['description'] = abbreviate_string($ev['description'], 100);
|
||||
unset($ev['attachments']);
|
||||
return serialize($ev);
|
||||
}
|
||||
|
||||
}
|
|
@ -27,14 +27,14 @@
|
|||
class calendar_ui
|
||||
{
|
||||
private $rc;
|
||||
private $calendar;
|
||||
private $cal;
|
||||
private $ready = false;
|
||||
public $screen;
|
||||
|
||||
function __construct($calendar)
|
||||
function __construct($cal)
|
||||
{
|
||||
$this->calendar = $calendar;
|
||||
$this->rc = $calendar->rc;
|
||||
$this->cal = $cal;
|
||||
$this->rc = $cal->rc;
|
||||
$this->screen = $this->rc->task == 'calendar' ? ($this->rc->action ? $this->rc->action: 'calendar') : 'other';
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ class calendar_ui
|
|||
return;
|
||||
|
||||
// add taskbar button
|
||||
$this->calendar->add_button(array(
|
||||
$this->cal->add_button(array(
|
||||
'name' => 'calendar',
|
||||
'class' => 'button-calendar',
|
||||
'label' => 'calendar.calendar',
|
||||
|
@ -55,11 +55,11 @@ class calendar_ui
|
|||
), 'taskbar');
|
||||
|
||||
// load basic client script (which - unfortunately - requires fullcalendar)
|
||||
$this->calendar->include_script('lib/js/fullcalendar.js');
|
||||
$this->calendar->include_script('calendar_base.js');
|
||||
$this->cal->include_script('lib/js/fullcalendar.js');
|
||||
$this->cal->include_script('calendar_base.js');
|
||||
|
||||
$skin = $this->rc->config->get('skin');
|
||||
$this->calendar->include_stylesheet('skins/' . $skin . '/calendar.css');
|
||||
$this->cal->include_stylesheet('skins/' . $skin . '/calendar.css');
|
||||
|
||||
$this->ready = true;
|
||||
}
|
||||
|
@ -70,8 +70,8 @@ class calendar_ui
|
|||
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');
|
||||
$this->cal->include_stylesheet('skins/' . $skin . '/fullcalendar.css');
|
||||
$this->cal->include_stylesheet('skins/' . $skin . '/jquery.miniColors.css');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,8 +79,8 @@ class calendar_ui
|
|||
*/
|
||||
public function addJS()
|
||||
{
|
||||
$this->calendar->include_script('calendar_ui.js');
|
||||
$this->calendar->include_script('lib/js/jquery.miniColors.min.js');
|
||||
$this->cal->include_script('calendar_ui.js');
|
||||
$this->cal->include_script('lib/js/jquery.miniColors.min.js');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,8 +88,8 @@ class calendar_ui
|
|||
*/
|
||||
function calendar_css($attrib = array())
|
||||
{
|
||||
$mode = $this->rc->config->get('calendar_event_coloring', $this->calendar->defaults['calendar_event_coloring']);
|
||||
$categories = $this->calendar->driver->list_categories();
|
||||
$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) {
|
||||
|
@ -113,7 +113,7 @@ class calendar_ui
|
|||
}
|
||||
}
|
||||
|
||||
$calendars = $this->calendar->driver->list_calendars();
|
||||
$calendars = $this->cal->driver->list_calendars();
|
||||
foreach ((array)$calendars as $id => $prop) {
|
||||
if (!$prop['color'])
|
||||
continue;
|
||||
|
@ -146,7 +146,7 @@ class calendar_ui
|
|||
*/
|
||||
function calendar_list($attrib = array())
|
||||
{
|
||||
$calendars = $this->calendar->driver->list_calendars();
|
||||
$calendars = $this->cal->driver->list_calendars();
|
||||
|
||||
$li = '';
|
||||
foreach ((array)$calendars as $id => $prop) {
|
||||
|
@ -154,10 +154,10 @@ class calendar_ui
|
|||
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;
|
||||
$prop['alarms'] = $this->cal->driver->alarms;
|
||||
$prop['attendees'] = $this->cal->driver->attendees;
|
||||
$prop['freebusy'] = $this->cal->driver->freebusy;
|
||||
$prop['attachments'] = $this->cal->driver->attachments;
|
||||
$jsenv[$id] = $prop;
|
||||
|
||||
$html_id = html_identifier($id);
|
||||
|
@ -185,7 +185,7 @@ class calendar_ui
|
|||
{
|
||||
$attrib['name'] = 'calendar';
|
||||
$select = new html_select($attrib);
|
||||
foreach ((array)$this->calendar->driver->list_calendars() as $id => $prop) {
|
||||
foreach ((array)$this->cal->driver->list_calendars() as $id => $prop) {
|
||||
if (!$prop['readonly'])
|
||||
$select->add($prop['name'], $id);
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ class calendar_ui
|
|||
$attrib['name'] = 'categories';
|
||||
$select = new html_select($attrib);
|
||||
$select->add('---', '');
|
||||
foreach ((array)$this->calendar->driver->list_categories() as $cat => $color) {
|
||||
foreach ((array)$this->cal->driver->list_categories() as $cat => $color) {
|
||||
$select->add($cat, $cat);
|
||||
}
|
||||
|
||||
|
@ -215,10 +215,10 @@ class calendar_ui
|
|||
{
|
||||
$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');
|
||||
$select->add($this->cal->gettext('free'), 'free');
|
||||
$select->add($this->cal->gettext('busy'), 'busy');
|
||||
$select->add($this->cal->gettext('outofoffice'), 'outofoffice');
|
||||
$select->add($this->cal->gettext('tentative'), 'tentative');
|
||||
return $select->show(null);
|
||||
}
|
||||
|
||||
|
@ -229,9 +229,9 @@ class calendar_ui
|
|||
{
|
||||
$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');
|
||||
$select->add($this->cal->gettext('normal'), '1');
|
||||
$select->add($this->cal->gettext('low'), '0');
|
||||
$select->add($this->cal->gettext('high'), '2');
|
||||
return $select->show(null);
|
||||
}
|
||||
|
||||
|
@ -242,9 +242,9 @@ class calendar_ui
|
|||
{
|
||||
$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');
|
||||
$select->add($this->cal->gettext('public'), '0');
|
||||
$select->add($this->cal->gettext('private'), '1');
|
||||
$select->add($this->cal->gettext('confidential'), '2');
|
||||
return $select->show(null);
|
||||
}
|
||||
|
||||
|
@ -255,9 +255,9 @@ class calendar_ui
|
|||
{
|
||||
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);
|
||||
$select_type->add($this->cal->gettext('none'), '');
|
||||
foreach ($this->cal->driver->alarm_types as $type)
|
||||
$select_type->add($this->cal->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));
|
||||
|
@ -265,7 +265,7 @@ class calendar_ui
|
|||
|
||||
$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);
|
||||
$select_offset->add($this->cal->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'));
|
||||
|
@ -281,7 +281,7 @@ class calendar_ui
|
|||
);
|
||||
|
||||
// TODO: support adding more alarms
|
||||
#$html .= html::a(array('href' => '#', 'id' => 'edit-alam-add', 'title' => $this->calendar->gettext('addalarm')),
|
||||
#$html .= html::a(array('href' => '#', 'id' => 'edit-alam-add', 'title' => $this->cal->gettext('addalarm')),
|
||||
# $attrib['addicon'] ? html::img(array('src' => $attrib['addicon'], 'alt' => 'add')) : '(+)');
|
||||
|
||||
return $html;
|
||||
|
@ -304,7 +304,7 @@ class calendar_ui
|
|||
$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))))));
|
||||
$this->cal->gettext(array('name' => $label, 'vars' => array('min' => $n % 60, 'hrs' => intval($n / 60))))));
|
||||
}
|
||||
|
||||
return html::tag('ul', $attrib, join("\n", $items), html::$common_attrib);
|
||||
|
@ -316,7 +316,7 @@ class calendar_ui
|
|||
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->calendar->gettext('sendnotifications')));
|
||||
return html::div($attrib, html::label(null, $checkbox->show(1) . ' ' . $this->cal->gettext('sendnotifications')));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -327,12 +327,12 @@ class calendar_ui
|
|||
$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'));
|
||||
$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->calendar->gettext('changerecurringeventwarning')) . html::div('savemode', $form));
|
||||
return html::div($attrib, html::div('message', html::span('ui-icon ui-icon-alert', '') . $this->cal->gettext('changerecurringeventwarning')) . html::div('savemode', $form));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -344,39 +344,39 @@ class calendar_ui
|
|||
// 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('');
|
||||
$select->add($this->cal->gettext('never'), '');
|
||||
$select->add($this->cal->gettext('daily'), 'DAILY');
|
||||
$select->add($this->cal->gettext('weekly'), 'WEEKLY');
|
||||
$select->add($this->cal->gettext('monthly'), 'MONTHLY');
|
||||
$select->add($this->cal->gettext('yearly'), 'YEARLY');
|
||||
$html = html::label('edit-frequency', $this->cal->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')));
|
||||
$html = html::div($attrib, html::label(null, $this->cal->gettext('every')) . $select->show(1) . html::span('label-after', $this->cal->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')));
|
||||
$html = html::div($attrib, html::label(null, $this->cal->gettext('every')) . $select->show(1) . html::span('label-after', $this->cal->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])) . ' ';
|
||||
$weekdays .= html::label(array('class' => 'weekday'), $checkbox->show('', array('value' => strtoupper(substr($daymap[$d], 0, 2)))) . $this->cal->gettext($daymap[$d])) . ' ';
|
||||
}
|
||||
$html .= html::div($attrib, html::label(null, $this->calendar->gettext('bydays')) . $weekdays);
|
||||
$html .= html::div($attrib, html::label(null, $this->cal->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')));
|
||||
$html = html::div($attrib, html::label(null, $this->cal->gettext('every')) . $select->show(1) . html::span('label-after', $this->cal->gettext('months')));
|
||||
|
||||
/* multiple month selection is not supported by Kolab
|
||||
$checkbox = new html_radiobutton(array('name' => 'bymonthday', 'class' => 'edit-recurrence-monthly-bymonthday'));
|
||||
|
@ -388,9 +388,9 @@ class calendar_ui
|
|||
// 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('label', html::label(null, $radio->show('BYMONTHDAY', array('value' => 'BYMONTHDAY')) . ' ' . $this->cal->gettext('onsamedate'))); // $this->cal->gettext('each')
|
||||
$table->add(null, $monthdays);
|
||||
$table->add('label', html::label(null, $radio->show('', array('value' => 'BYDAY')) . ' ' . $this->calendar->gettext('onevery')));
|
||||
$table->add('label', html::label(null, $radio->show('', array('value' => 'BYDAY')) . ' ' . $this->cal->gettext('onevery')));
|
||||
$table->add(null, $this->rrule_selectors($attrib['part']));
|
||||
|
||||
$html .= html::div($attrib, $table->show());
|
||||
|
@ -400,19 +400,19 @@ class calendar_ui
|
|||
// 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')));
|
||||
$html = html::div($attrib, html::label(null, $this->cal->gettext('every')) . $select->show(1) . html::span('label-after', $this->cal->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';
|
||||
$boxtype = is_a($this->cal->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 .= html::label(array('class' => 'month'), $checkbox->show(null, array('value' => $m)) . $this->cal->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'], '---'));
|
||||
$html .= html::div($attrib, html::label(null, $this->cal->gettext('onevery')) . $this->rrule_selectors($attrib['part'], '---'));
|
||||
break;
|
||||
|
||||
// end of recurrence form
|
||||
|
@ -423,20 +423,20 @@ class calendar_ui
|
|||
|
||||
$table = new html_table(array('cols' => 2, 'border' => 0, 'cellpadding' => 0, 'class' => 'formtable'));
|
||||
|
||||
$table->add('label', ucfirst($this->calendar->gettext('recurrencend')));
|
||||
$table->add('label', ucfirst($this->cal->gettext('recurrencend')));
|
||||
$table->add(null, html::label(null, $radio->show('', array('value' => '', 'id' => 'edit-recurrence-repeat-forever')) . ' ' .
|
||||
$this->calendar->gettext('forever')));
|
||||
$this->cal->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(
|
||||
$this->cal->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(''));
|
||||
$this->cal->gettext('until') . ' ' . $input->show(''));
|
||||
$html = $table->show();
|
||||
break;
|
||||
}
|
||||
|
@ -463,16 +463,16 @@ class calendar_ui
|
|||
$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')
|
||||
$this->cal->gettext('first'),
|
||||
$this->cal->gettext('second'),
|
||||
$this->cal->gettext('third'),
|
||||
$this->cal->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);
|
||||
if (!is_a($this->cal->driver, 'kolab_driver'))
|
||||
$select_prefix->add($this->cal->gettext('last'), -1);
|
||||
|
||||
$select_wday = new html_select(array('name' => 'byday', 'id' => "edit-recurrence-$part-byday"));
|
||||
if ($noselect) $select_wday->add($noselect, '');
|
||||
|
@ -481,10 +481,10 @@ class calendar_ui
|
|||
$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)));
|
||||
$select_wday->add($this->cal->gettext($daymap[$d]), strtoupper(substr($daymap[$d], 0, 2)));
|
||||
}
|
||||
if ($part == 'monthly')
|
||||
$select_wday->add($this->calendar->gettext('dayofmonth'), '');
|
||||
$select_wday->add($this->cal->gettext('dayofmonth'), '');
|
||||
|
||||
return $select_prefix->show() . ' ' . $select_wday->show();
|
||||
}
|
||||
|
@ -541,15 +541,15 @@ class calendar_ui
|
|||
{
|
||||
$table = new html_table(array('cols' => 3));
|
||||
|
||||
if (!empty($this->calendar->attachment['name'])) {
|
||||
if (!empty($this->cal->attachment['name'])) {
|
||||
$table->add('title', Q(rcube_label('filename')));
|
||||
$table->add(null, Q($this->calendar->attachment['name']));
|
||||
$table->add(null, Q($this->cal->attachment['name']));
|
||||
$table->add(null, '[' . html::a('?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), Q(rcube_label('download'))) . ']');
|
||||
}
|
||||
|
||||
if (!empty($this->calendar->attachment['size'])) {
|
||||
if (!empty($this->cal->attachment['size'])) {
|
||||
$table->add('title', Q(rcube_label('filesize')));
|
||||
$table->add(null, Q(show_bytes($this->calendar->attachment['size'])));
|
||||
$table->add(null, Q(show_bytes($this->cal->attachment['size'])));
|
||||
}
|
||||
|
||||
return $table->show($attrib);
|
||||
|
@ -567,21 +567,21 @@ class calendar_ui
|
|||
|
||||
$formfields = array(
|
||||
'name' => array(
|
||||
'label' => $this->calendar->gettext('name'),
|
||||
'label' => $this->cal->gettext('name'),
|
||||
'value' => $input_name->show($name),
|
||||
'id' => 'calendar-name',
|
||||
),
|
||||
'color' => array(
|
||||
'label' => $this->calendar->gettext('color'),
|
||||
'label' => $this->cal->gettext('color'),
|
||||
'value' => $input_color->show($calendar['color']),
|
||||
'id' => 'calendar-color',
|
||||
),
|
||||
);
|
||||
|
||||
if ($this->calendar->driver->alarms) {
|
||||
if ($this->cal->driver->alarms) {
|
||||
$checkbox = new html_checkbox(array('name' => 'showalarms', 'id' => 'calendar-showalarms', 'value' => 1));
|
||||
$formfields['showalarms'] = array(
|
||||
'label' => $this->calendar->gettext('showalarms'),
|
||||
'label' => $this->cal->gettext('showalarms'),
|
||||
'value' => $checkbox->show($calendar['showalarms']?1:0),
|
||||
'id' => 'calendar-showalarms',
|
||||
);
|
||||
|
@ -589,7 +589,7 @@ class calendar_ui
|
|||
|
||||
// allow driver to extend or replace the form content
|
||||
return html::tag('form', array('action' => "#", 'method' => "get", 'id' => 'calendarpropform'),
|
||||
$this->calendar->driver->calendar_form($action, $calendar, $formfields)
|
||||
$this->cal->driver->calendar_form($action, $calendar, $formfields)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -599,10 +599,10 @@ class calendar_ui
|
|||
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('role', $this->cal->gettext('role'));
|
||||
$table->add_header('name', $this->cal->gettext('attendee'));
|
||||
$table->add_header('availability', $this->cal->gettext('availability'));
|
||||
$table->add_header('confirmstate', $this->cal->gettext('confirmstate'));
|
||||
$table->add_header('options', '');
|
||||
|
||||
return $table->show($attrib);
|
||||
|
@ -618,9 +618,9 @@ class calendar_ui
|
|||
|
||||
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-invitebox', html::label(null, $checkbox->show(1) . $this->calendar->gettext('sendinvitations')))
|
||||
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-invitebox', html::label(null, $checkbox->show(1) . $this->cal->gettext('sendinvitations')))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -631,7 +631,7 @@ class calendar_ui
|
|||
{
|
||||
$table = new html_table(array('cols' => 2, 'border' => 0, 'cellspacing' => 0));
|
||||
$table->add('attendees',
|
||||
html::tag('h3', 'boxtitle', $this->calendar->gettext('tabattendees')) .
|
||||
html::tag('h3', 'boxtitle', $this->cal->gettext('tabattendees')) .
|
||||
html::div('timesheader', ' ') .
|
||||
html::div(array('id' => 'schedule-attendees-list', 'class' => 'attendees-list'), '')
|
||||
);
|
||||
|
@ -644,21 +644,55 @@ class calendar_ui
|
|||
|
||||
return $table->show($attrib);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Render event details in a table
|
||||
*/
|
||||
function event_details_table($event, $title)
|
||||
{
|
||||
$table = new html_table(array('cols' => 2, 'border' => 0, 'class' => 'calendar-eventdetails'));
|
||||
$table->add('ititle', $title);
|
||||
$table->add('title', Q($event['title']));
|
||||
$table->add('label', $this->cal->gettext('date'));
|
||||
$table->add('location', Q($this->cal->event_date_text($event)));
|
||||
if ($event['location']) {
|
||||
$table->add('label', $this->cal->gettext('location'));
|
||||
$table->add('location', Q($event['location']));
|
||||
}
|
||||
|
||||
return $table->show();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function event_invitebox($attrib = array())
|
||||
{
|
||||
if ($this->cal->event) {
|
||||
return html::div($attrib,
|
||||
$this->event_details_table($this->cal->event, $this->cal->gettext('itipinvitation')) .
|
||||
$this->cal->invitestatus
|
||||
);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function event_rsvp_buttons($attrib = array())
|
||||
{
|
||||
$attrib += array('type' => 'button');
|
||||
foreach (array('accepted','tentative','declined') as $method) {
|
||||
$buttons .= html::tag('input', array(
|
||||
'type' => 'button',
|
||||
'type' => $attrib['type'],
|
||||
'name' => $attrib['iname'],
|
||||
'class' => 'button',
|
||||
'rel' => $method,
|
||||
'value' => $this->calendar->gettext('itip' . $method),
|
||||
'value' => $this->cal->gettext('itip' . $method),
|
||||
));
|
||||
}
|
||||
|
||||
return html::div($attrib,
|
||||
html::div('label', $this->calendar->gettext('acceptinvitation')) .
|
||||
html::div('label', $this->cal->gettext('acceptinvitation')) .
|
||||
html::div('rsvp-buttons', $buttons));
|
||||
}
|
||||
|
||||
|
|
|
@ -120,6 +120,7 @@ $labels['nextslot'] = 'Nächster Vorschlag';
|
|||
$labels['noslotfound'] = 'Es konnten keine freien Zeiten gefunden werden';
|
||||
$labels['invitationsubject'] = 'Sie wurden zu "$title" eingeladen';
|
||||
$labels['invitationmailbody'] = "*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees\n\nIm Anhang finden Sie eine iCalendar-Datei mit allen Details des Termins. Diese können Sie in Ihre Kalenderanwendung importieren.";
|
||||
$labels['invitationattendlinks'] = "Falls Ihr E-Mail-Programm keine iTip-Anfragen unterstützt, können Sie den folgenden Link verwenden, um den Termin zu bestätigen oder abzulehnen:\n\$url";
|
||||
$labels['eventupdatesubject'] = '"$title" wurde aktualisiert';
|
||||
$labels['eventupdatesubjectempty'] = 'An event that concerns you has been updated';
|
||||
$labels['eventupdatemailbody'] = "*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees\n\nIm Anhang finden Sie eine iCalendar-Datei mit den aktualisiereten Termindaten. Diese können Sie in Ihre Kalenderanwendung importieren";
|
||||
|
@ -148,6 +149,7 @@ $labels['acceptinvitation'] = 'Möchten Sie die Einladung zu diesem Termin anneh
|
|||
$labels['youhaveaccepted'] = 'Sie haben die Einladung angenommen';
|
||||
$labels['youhavetentative'] = 'Sie haben die Einladung mit Vorbehalt angenommen';
|
||||
$labels['youhavedeclined'] = 'Sie haben die Einladung abgelehnt';
|
||||
$labels['eventcancelled'] = 'Der Termin wurde vom Organisator abgesagt';
|
||||
|
||||
// event dialog tabs
|
||||
$labels['tabsummary'] = 'Übersicht';
|
||||
|
|
|
@ -119,6 +119,7 @@ $labels['nextslot'] = 'Nächster Vorschlag';
|
|||
$labels['noslotfound'] = 'Es konnten keine freien Zeiten gefunden werden';
|
||||
$labels['invitationsubject'] = 'Sie wurden zu "$title" eingeladen';
|
||||
$labels['invitationmailbody'] = "*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees\n\nIm Anhang finden Sie eine iCalendar-Datei mit allen Details des Termins. Diese können Sie in Ihre Kalenderanwendung importieren.";
|
||||
$labels['invitationattendlinks'] = "Falls Ihr E-Mail-Programm keine iTip-Anfragen unterstützt, können Sie den folgenden Link verwenden, um den Termin zu bestätigen oder abzulehnen:\n\$url";
|
||||
$labels['eventupdatesubject'] = '"$title" wurde aktualisiert';
|
||||
$labels['eventupdatesubjectempty'] = 'An event that concerns you has been updated';
|
||||
$labels['eventupdatemailbody'] = "*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees\n\nIm Anhang finden Sie eine iCalendar-Datei mit den aktualisiereten Termindaten. Diese können Sie in Ihre Kalenderanwendung importieren";
|
||||
|
@ -147,6 +148,7 @@ $labels['acceptinvitation'] = 'Möchten Sie die Einladung zu diesem Termin anneh
|
|||
$labels['youhaveaccepted'] = 'Sie haben die Einladung angenommen';
|
||||
$labels['youhavetentative'] = 'Sie haben die Einladung mit Vorbehalt angenommen';
|
||||
$labels['youhavedeclined'] = 'Sie haben die Einladung abgelehnt';
|
||||
$labels['eventcancelled'] = 'Der Termin wurde vom Organisator abgesagt';
|
||||
|
||||
// event dialog tabs
|
||||
$labels['tabsummary'] = 'Übersicht';
|
||||
|
|
|
@ -120,6 +120,7 @@ $labels['nextslot'] = 'Next Slot';
|
|||
$labels['noslotfound'] = 'Unable to find a free time slot';
|
||||
$labels['invitationsubject'] = 'You\'ve been invited to "$title"';
|
||||
$labels['invitationmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nPlease find attached an iCalendar file with all the event details which you can import to your calendar application.";
|
||||
$labels['invitationattendlinks'] = "In case your email client doesn't support iTip requests you can use the following link to either accept or decline this invitation:\n\$url";
|
||||
$labels['eventupdatesubject'] = '"$title" has been updated';
|
||||
$labels['eventupdatesubjectempty'] = 'An event that concerns you has been updated';
|
||||
$labels['eventupdatemailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nPlease find attached an iCalendar file with the updated event details which you can import to your calendar application.";
|
||||
|
@ -148,6 +149,7 @@ $labels['acceptinvitation'] = 'Do you accept this invitation?';
|
|||
$labels['youhaveaccepted'] = 'You have accepted this invitation';
|
||||
$labels['youhavetentative'] = 'You have tentatively accepted this invitation';
|
||||
$labels['youhavedeclined'] = 'You have declined this invitation';
|
||||
$labels['eventcancelled'] = 'The event has been cancelled';
|
||||
|
||||
// event dialog tabs
|
||||
$labels['tabsummary'] = 'Summary';
|
||||
|
@ -174,6 +176,7 @@ $labels['nowritecalendarfound'] = 'No calendar found to save the event';
|
|||
$labels['importedsuccessfully'] = 'The event was successfully added to \'$calendar\'';
|
||||
$labels['attendeupdateesuccess'] = 'Successfully updated the participant\'s status';
|
||||
$labels['itipresponseerror'] = 'Failed to send the response to this event invitation';
|
||||
$labels['itipinvalidrequest'] = 'This invitation is no longer valid';
|
||||
$labels['sentresponseto'] = 'Successfully sent invitation response to $mailto';
|
||||
$labels['localchangeswarning'] = 'You are about to make changes that will only be reflected on your personal calendar';
|
||||
|
||||
|
|
|
@ -1169,3 +1169,34 @@ div.calendar-invitebox .rsvp-status.tentative {
|
|||
background-position: 2px -60px;
|
||||
}
|
||||
|
||||
/* iTIP attend reply page */
|
||||
|
||||
.calendaritipattend .centerbox {
|
||||
width: 40em;
|
||||
margin: 80px auto;
|
||||
padding: 10px 10px 10px 90px;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 1px 1px 24px #ccc;
|
||||
-moz-box-shadow: 1px 1px 18px #ccc;
|
||||
-webkit-box-shadow: #ccc 1px 1px 18px;
|
||||
background: url(images/invitation.png) 10px 10px no-repeat #fbfbfb;
|
||||
}
|
||||
|
||||
.calendaritipattend .calendar-invitebox {
|
||||
background: none;
|
||||
padding-left: 0;
|
||||
border: 0;
|
||||
margin: 0 0 2em 0;
|
||||
}
|
||||
|
||||
.calendaritipattend .calendar-invitebox .rsvp-status {
|
||||
margin-top: 2.5em;
|
||||
font-size: 110%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.calendaritipattend .calendar-invitebox td.title,
|
||||
.calendaritipattend .calendar-invitebox td.ititle {
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
|
|
BIN
plugins/calendar/skins/default/images/invitation.png
Normal file
BIN
plugins/calendar/skins/default/images/invitation.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
21
plugins/calendar/skins/default/templates/itipattend.html
Normal file
21
plugins/calendar/skins/default/templates/itipattend.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title><roundcube:object name="pagetitle" /></title>
|
||||
<roundcube:include file="/includes/links.html" />
|
||||
</head>
|
||||
<body class="calendaritipattend">
|
||||
|
||||
<roundcube:object name="logo" src="/images/roundcube_logo.png" id="logo" border="0" style="margin:0 11px" />
|
||||
|
||||
<roundcube:object name="message" id="message" />
|
||||
|
||||
<div class="centerbox">
|
||||
<roundcube:object name="plugin.event_inviteform" />
|
||||
<roundcube:object name="plugin.event_invitebox" class="calendar-invitebox" />
|
||||
<roundcube:object name="plugin.event_rsvp_buttons" type="submit" iname="rsvp" id="event-rsvp" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Reference in a new issue