Merge branch 'dev/kolab3'
Conflicts: plugins/calendar/drivers/kolab/kolab_driver.php
This commit is contained in:
commit
2cf2cbcca8
49 changed files with 8719 additions and 2141 deletions
3
plugins/calendar/.gitignore
vendored
3
plugins/calendar/.gitignore
vendored
|
@ -4,4 +4,5 @@
|
|||
*~
|
||||
config.inc.php
|
||||
skins/*
|
||||
!skins/default
|
||||
!skins/default
|
||||
!skins/larry
|
18
plugins/calendar/README
Normal file
18
plugins/calendar/README
Normal file
|
@ -0,0 +1,18 @@
|
|||
A calendar module for Roundcube
|
||||
-------------------------------
|
||||
|
||||
This plugin currently supports a local database as well as a Kolab groupware
|
||||
server as backends for calendar and event storage. For both drivers, some
|
||||
initialization of the local database is necessary. To do so, execute the
|
||||
SQL commands in drivers/<yourchoice>/SQL/<yourdatabase>.sql
|
||||
|
||||
The client-side calendar UI relies on the 'fullcalenda'r project by Adam Arshaw
|
||||
with extensions made for the use in Roundcube. All changes are published in
|
||||
an official fork at https://github.com/roundcube/fullcalendar
|
||||
|
||||
For recurring event computation, some utility classes from the Horde project
|
||||
are used. They are packaged in a slightly modified version with this plugin.
|
||||
|
||||
iCalendar parsing is done with the help of the Horde_iCalendar class. A copy
|
||||
of that class with all its dependencies is part of this package. In order
|
||||
to update it, execute lib/get_horde_icalendar.sh > lib/Horde_iCalendar.php
|
|
@ -13,7 +13,7 @@
|
|||
+ View: 3.3: Display modes (agenda / day / week / month)
|
||||
+ Day / Week / Month
|
||||
+ List (Agenda) view
|
||||
- Add selection for date range
|
||||
+ Add selection for date range
|
||||
- Individual days selection
|
||||
+ Show list of calendars in a (hideable) drawer
|
||||
+ View: 3.1: Folder list
|
||||
|
@ -25,13 +25,13 @@
|
|||
+ View: 3.9: Alter event with drag/drop
|
||||
+ Option: 4.12: Set default reminder time
|
||||
+ Option: 3.23: Specify folder for new event (prefs)
|
||||
- Option: Set date/time format in prefs
|
||||
+ Option: Set date/time format in prefs
|
||||
+ Receive: 1.20: Invitation handling
|
||||
- Jump to calendar view from mail ("Show event")
|
||||
- Allow to re-send invitations
|
||||
- Implement iTIP delegation
|
||||
|
||||
- View: 3.4: Fish-Eye View For Busy Days
|
||||
+ View: 3.4: Fish-Eye View For Busy Days
|
||||
+ View: 3.8: Color according to calendar and category (similar to Kontact)
|
||||
|
||||
+ Support for multiple calendars (replace categories)
|
||||
|
@ -39,9 +39,10 @@
|
|||
+ Colors for calendars should be user-configurable
|
||||
+ ICS parser/generator (http://code.google.com/p/qcal/)
|
||||
|
||||
- Script to send event alarms by email (in cronjob)
|
||||
- Export *with* attachments
|
||||
- Importing ICS files (upload, drag & drop)
|
||||
- Remember last visited view
|
||||
- Create/manage invdividual views
|
||||
- Support for tasks/todos with task list view (ordered by date/time)
|
||||
+ Importing ICS files (upload, drag & drop)
|
||||
|
||||
|
||||
|
|
|
@ -99,9 +99,9 @@ class calendar extends rcube_plugin
|
|||
// set user's timezone
|
||||
$this->timezone = new DateTimeZone($this->rc->config->get('timezone', 'GMT'));
|
||||
$now = new DateTime('now', $this->timezone);
|
||||
$this->timezone_offset = $now->format('Z') / 3600;
|
||||
$this->dst_active = $now->format('I');
|
||||
$this->gmt_offset = $now->getOffset();
|
||||
$this->dst_active = $now->format('I');
|
||||
$this->timezone_offset = $this->gmt_offset / 3600 - $this->dst_active;
|
||||
|
||||
require($this->home . '/lib/calendar_ui.php');
|
||||
$this->ui = new calendar_ui($this);
|
||||
|
@ -205,7 +205,7 @@ class calendar extends rcube_plugin
|
|||
|
||||
switch ($driver_name) {
|
||||
case "kolab":
|
||||
$this->require_plugin('kolab_core');
|
||||
$this->require_plugin('libkolab');
|
||||
default:
|
||||
$this->driver = new $driver_class($this);
|
||||
break;
|
||||
|
@ -1069,6 +1069,11 @@ class calendar extends rcube_plugin
|
|||
$settings['identity'] = array('name' => $identity['name'], 'email' => $identity['email'], 'emails' => ';' . join(';', $identity['emails']));
|
||||
}
|
||||
|
||||
// define list of file types which can be displayed inline
|
||||
// same as in program/steps/mail/show.inc
|
||||
$mimetypes = $this->rc->config->get('client_mimetypes', 'text/plain,text/html,text/xml,image/jpeg,image/gif,image/png,application/x-javascript,application/pdf,application/x-shockwave-flash');
|
||||
$settings['mimetypes'] = is_string($mimetypes) ? explode(',', $mimetypes) : (array)$mimetypes;
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
|
@ -1259,7 +1264,47 @@ class calendar extends rcube_plugin
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next alarm (time & action) for the given event
|
||||
*
|
||||
* @param array Event data
|
||||
* @return array Hash array with alarm time/type or null if no alarms are configured
|
||||
*/
|
||||
public static function get_next_alarm($event)
|
||||
{
|
||||
if (!$event['alarms'])
|
||||
return null;
|
||||
|
||||
// TODO: handle multiple alarms (currently not supported)
|
||||
list($trigger, $action) = explode(':', $event['alarms'], 2);
|
||||
|
||||
$notify = self::parse_alaram_value($trigger);
|
||||
if (!empty($notify[1])){ // offset
|
||||
$mult = 1;
|
||||
switch ($notify[1]) {
|
||||
case '-S': $mult = -1; break;
|
||||
case '+S': $mult = 1; break;
|
||||
case '-M': $mult = -60; break;
|
||||
case '+M': $mult = 60; break;
|
||||
case '-H': $mult = -3600; break;
|
||||
case '+H': $mult = 3600; break;
|
||||
case '-D': $mult = -86400; break;
|
||||
case '+D': $mult = 86400; break;
|
||||
case '-W': $mult = -604800; break;
|
||||
case '+W': $mult = 604800; break;
|
||||
}
|
||||
$offset = $notify[0] * $mult;
|
||||
$refdate = $mult > 0 ? $event['end'] : $event['start'];
|
||||
$notify_at = $refdate + $offset;
|
||||
}
|
||||
else { // absolute timestamp
|
||||
$notify_at = $notify[0];
|
||||
}
|
||||
|
||||
return array('time' => $notify_at, 'action' => $action ? strtoupper($action) : 'DISPLAY');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the internal structured data into a vcalendar rrule 2.0 string
|
||||
*/
|
||||
|
@ -1278,7 +1323,7 @@ class calendar extends rcube_plugin
|
|||
case 'EXDATE':
|
||||
foreach ((array)$val as $i => $ex)
|
||||
$val[$i] = gmdate('Ymd\THis', $ex);
|
||||
$val = join(',', $val);
|
||||
$val = join(',', (array)$val);
|
||||
break;
|
||||
}
|
||||
$rrule .= $k . '=' . $val . ';';
|
||||
|
@ -1534,12 +1579,8 @@ class calendar extends rcube_plugin
|
|||
}
|
||||
|
||||
ob_end_clean();
|
||||
send_nocacheing_headers();
|
||||
|
||||
if (isset($_SESSION['calendar_attachment']))
|
||||
$attachment = $_SESSION['calendar_attachment'];
|
||||
else
|
||||
$attachment = $_SESSION['calendar_attachment'] = $this->driver->get_attachment($id, $event);
|
||||
$attachment = $GLOBALS['calendar_attachment'] = $this->driver->get_attachment($id, $event);
|
||||
|
||||
// show part page
|
||||
if (!empty($_GET['_frame'])) {
|
||||
|
@ -1550,16 +1591,30 @@ class calendar extends rcube_plugin
|
|||
exit;
|
||||
}
|
||||
|
||||
$this->rc->session->remove('calendar_attachment');
|
||||
|
||||
if ($attachment) {
|
||||
$mimetype = strtolower($attachment['mimetype']);
|
||||
// allow post-processing of the attachment body
|
||||
$part = new rcube_message_part;
|
||||
$part->filename = $attachment['name'];
|
||||
$part->size = $attachment['size'];
|
||||
$part->mimetype = $attachment['mimetype'];
|
||||
|
||||
$plugin = $this->rc->plugins->exec_hook('message_part_get', array(
|
||||
'body' => $this->driver->get_attachment_body($id, $event),
|
||||
'mimetype' => strtolower($attachment['mimetype']),
|
||||
'download' => !empty($_GET['_download']),
|
||||
'part' => $part,
|
||||
));
|
||||
|
||||
if ($plugin['abort'])
|
||||
exit;
|
||||
|
||||
$mimetype = $plugin['mimetype'];
|
||||
list($ctype_primary, $ctype_secondary) = explode('/', $mimetype);
|
||||
|
||||
$browser = $this->rc->output->browser;
|
||||
|
||||
// send download headers
|
||||
if ($_GET['_download']) {
|
||||
if ($plugin['download']) {
|
||||
header("Content-Type: application/octet-stream");
|
||||
if ($browser->ie)
|
||||
header("Content-Type: application/force-download");
|
||||
|
@ -1573,13 +1628,11 @@ class calendar extends rcube_plugin
|
|||
header("Content-Transfer-Encoding: binary");
|
||||
}
|
||||
|
||||
$body = $this->driver->get_attachment_body($id, $event);
|
||||
|
||||
// display page, @TODO: support text/plain (and maybe some other text formats)
|
||||
if ($mimetype == 'text/html' && empty($_GET['_download'])) {
|
||||
$OUTPUT = new rcube_html_page();
|
||||
// @TODO: use washtml on $body
|
||||
$OUTPUT->write($body);
|
||||
$OUTPUT->write($plugin['body']);
|
||||
}
|
||||
else {
|
||||
// don't kill the connection if download takes more than 30 sec.
|
||||
|
@ -1598,7 +1651,7 @@ class calendar extends rcube_plugin
|
|||
$disposition = !empty($_GET['_download']) ? 'attachment' : 'inline';
|
||||
header("Content-Disposition: $disposition; filename=\"$filename\"");
|
||||
|
||||
echo $body;
|
||||
echo $plugin['body'];
|
||||
}
|
||||
|
||||
exit;
|
||||
|
@ -1614,7 +1667,7 @@ class calendar extends rcube_plugin
|
|||
*/
|
||||
public function attachment_frame($attrib)
|
||||
{
|
||||
$attachment = $_SESSION['calendar_attachment'];
|
||||
$attachment = $GLOBALS['calendar_attachment'];
|
||||
|
||||
$mimetype = strtolower($attachment['mimetype']);
|
||||
list($ctype_primary, $ctype_secondary) = explode('/', $mimetype);
|
||||
|
@ -2167,15 +2220,15 @@ class calendar extends rcube_plugin
|
|||
$charset = RCMAIL_CHARSET;
|
||||
|
||||
// establish imap connection
|
||||
$this->rc->imap_connect();
|
||||
$this->rc->imap->set_mailbox($mbox);
|
||||
$imap = $this->rc->get_storage();
|
||||
$imap->set_mailbox($mbox);
|
||||
|
||||
if ($uid && $mime_id) {
|
||||
list($mime_id, $index) = explode(':', $mime_id);
|
||||
$part = $this->rc->imap->get_message_part($uid, $mime_id);
|
||||
$part = $imap->get_message_part($uid, $mime_id);
|
||||
if ($part->ctype_parameters['charset'])
|
||||
$charset = $part->ctype_parameters['charset'];
|
||||
$headers = $this->rc->imap->get_headers($uid);
|
||||
$headers = $imap->get_message_headers($uid);
|
||||
}
|
||||
|
||||
$events = $this->get_ical()->import($part, $charset);
|
||||
|
@ -2312,8 +2365,8 @@ class calendar extends rcube_plugin
|
|||
$event = array();
|
||||
|
||||
// establish imap connection
|
||||
$this->rc->imap_connect();
|
||||
$this->rc->imap->set_mailbox($mbox);
|
||||
$imap = $this->rc->get_storage();
|
||||
$imap->set_mailbox($mbox);
|
||||
$message = new rcube_message($uid);
|
||||
|
||||
if ($message->headers) {
|
||||
|
@ -2331,7 +2384,7 @@ class calendar extends rcube_plugin
|
|||
|
||||
foreach ((array)$message->attachments as $part) {
|
||||
$attachment = array(
|
||||
'data' => $this->rc->imap->get_message_part($uid, $part->mime_id, $part),
|
||||
'data' => $imap->get_message_part($uid, $part->mime_id, $part),
|
||||
'size' => $part->size,
|
||||
'name' => $part->filename,
|
||||
'mimetype' => $part->mimetype,
|
||||
|
|
|
@ -260,7 +260,7 @@ function rcube_calendar_ui(settings)
|
|||
var qstring = '_id='+urlencode(att.id)+'&_event='+urlencode(event.recurrence_id||event.id)+'&_cal='+urlencode(event.calendar);
|
||||
|
||||
// open attachment in frame if it's of a supported mimetype
|
||||
if (id && att.mimetype && $.inArray(att.mimetype, rcmail.mimetypes)>=0) {
|
||||
if (id && att.mimetype && $.inArray(att.mimetype, settings.mimetypes)>=0) {
|
||||
rcmail.attachment_win = window.open(rcmail.env.comm_path+'&_action=get-attachment&'+qstring+'&_frame=1', 'rcubeeventattachment');
|
||||
if (rcmail.attachment_win) {
|
||||
window.setTimeout(function() { rcmail.attachment_win.focus(); }, 10);
|
||||
|
@ -337,7 +337,7 @@ function rcube_calendar_ui(settings)
|
|||
var $dialog = $("#eventshow").dialog('close').removeClass().addClass('uidialog');
|
||||
var calendar = event.calendar && me.calendars[event.calendar] ? me.calendars[event.calendar] : { editable:false };
|
||||
me.selected_event = event;
|
||||
|
||||
|
||||
$dialog.find('div.event-section, div.event-line').hide();
|
||||
$('#event-title').html(Q(event.title)).show();
|
||||
|
||||
|
@ -363,9 +363,10 @@ function rcube_calendar_ui(settings)
|
|||
if (event.free_busy)
|
||||
$('#event-free-busy').show().children('.event-text').html(Q(rcmail.gettext(event.free_busy, 'calendar')));
|
||||
if (event.priority > 0) {
|
||||
var priolabels = [ '', rcmail.gettext('high'), rcmail.gettext('highest'), '', '', rcmail.gettext('normal'), '', '', rcmail.gettext('low'), rcmail.gettext('lowest') ];
|
||||
var priolabels = [ '', rcmail.gettext('highest'), rcmail.gettext('high'), '', '', rcmail.gettext('normal'), '', '', rcmail.gettext('low'), rcmail.gettext('lowest') ];
|
||||
$('#event-priority').show().children('.event-text').html(Q(event.priority+' '+priolabels[event.priority]));
|
||||
}
|
||||
|
||||
if (event.sensitivity != 0) {
|
||||
var sensitivityclasses = { 0:'public', 1:'private', 2:'confidential' };
|
||||
$('#event-sensitivity').show().children('.event-text').html(Q(sensitivitylabels[event.sensitivity]));
|
||||
|
@ -415,7 +416,7 @@ function rcube_calendar_ui(settings)
|
|||
$('#event-rsvp')[(rsvp?'show':'hide')]();
|
||||
$('#event-rsvp .rsvp-buttons input').prop('disabled', false).filter('input[rel='+rsvp+']').prop('disabled', true);
|
||||
}
|
||||
|
||||
|
||||
var buttons = {};
|
||||
if (calendar.editable && event.editable !== false) {
|
||||
buttons[rcmail.gettext('edit', 'calendar')] = function() {
|
||||
|
@ -2348,7 +2349,7 @@ function rcube_calendar_ui(settings)
|
|||
event.end = new Date(event.start.getTime() + (allDay ? DAY_MS : HOUR_MS));
|
||||
}
|
||||
// moved to all-day section: set times to 12:00 - 13:00
|
||||
if (allDay && !event.allday) {
|
||||
if (allDay && !event.allDay) {
|
||||
event.start.setHours(12);
|
||||
event.start.setMinutes(0);
|
||||
event.start.setSeconds(0);
|
||||
|
@ -2357,7 +2358,7 @@ function rcube_calendar_ui(settings)
|
|||
event.end.setSeconds(0);
|
||||
}
|
||||
// moved from all-day section: set times to working hours
|
||||
else if (event.allday && !allDay) {
|
||||
else if (event.allDay && !allDay) {
|
||||
var newstart = event.start.getTime();
|
||||
revertFunc(); // revert to get original duration
|
||||
var numdays = Math.max(1, Math.round((event.end.getTime() - event.start.getTime()) / DAY_MS)) - 1;
|
||||
|
@ -2407,7 +2408,7 @@ function rcube_calendar_ui(settings)
|
|||
}
|
||||
},
|
||||
viewRender: function(view) {
|
||||
if (view.name == 'month')
|
||||
if (fc && view.name == 'month')
|
||||
fc.fullCalendar('option', 'maxHeight', Math.floor((view.element.parent().height()-18) / 6) - 35);
|
||||
}
|
||||
});
|
||||
|
@ -2436,7 +2437,7 @@ function rcube_calendar_ui(settings)
|
|||
/* Time completions */
|
||||
var result = [];
|
||||
var now = new Date();
|
||||
var st, start = (this.element.attr('id').indexOf('endtime') > 0
|
||||
var st, start = (String(this.element.attr('id')).indexOf('endtime') > 0
|
||||
&& (st = $('#edit-starttime').val())
|
||||
&& $('#edit-startdate').val() == $('#edit-enddate').val())
|
||||
? parse_datetime(st, '') : null;
|
||||
|
|
|
@ -396,31 +396,13 @@ class database_driver extends calendar_driver
|
|||
*/
|
||||
private function _get_notification($event)
|
||||
{
|
||||
if ($event['alarms']) {
|
||||
list($trigger, $action) = explode(':', $event['alarms']);
|
||||
$notify = calendar::parse_alaram_value($trigger);
|
||||
if (!empty($notify[1])){ // offset
|
||||
$mult = 1;
|
||||
switch ($notify[1]) {
|
||||
case '-M': $mult = -60; break;
|
||||
case '+M': $mult = 60; break;
|
||||
case '-H': $mult = -3600; break;
|
||||
case '+H': $mult = 3600; break;
|
||||
case '-D': $mult = -86400; break;
|
||||
case '+D': $mult = 86400; break;
|
||||
}
|
||||
$offset = $notify[0] * $mult;
|
||||
$refdate = $mult > 0 ? $event['end'] : $event['start'];
|
||||
$notify_at = $refdate + $offset;
|
||||
}
|
||||
else { // absolute timestamp
|
||||
$notify_at = $notify[0];
|
||||
}
|
||||
if ($event['alarms'] && $event['start'] > time()) {
|
||||
$alarm = calendar::get_next_alarm($event);
|
||||
|
||||
if ($event['start'] > time())
|
||||
return date('Y-m-d H:i:s', $notify_at);
|
||||
if ($alarm['time'] && $alarm['action'] == 'DISPLAY')
|
||||
return date('Y-m-d H:i:s', $alarm['time']);
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,12 @@
|
|||
|
||||
CREATE TABLE IF NOT EXISTS `kolab_alarms` (
|
||||
`event_id` VARCHAR(255) NOT NULL,
|
||||
`user_id` int(10) UNSIGNED NOT NULL,
|
||||
`notifyat` DATETIME DEFAULT NULL,
|
||||
`dismissed` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY(`event_id`)
|
||||
PRIMARY KEY(`event_id`),
|
||||
CONSTRAINT `fk_kolab_alarms_user_id` FOREIGN KEY (`user_id`)
|
||||
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) /*!40000 ENGINE=INNODB */;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `itipinvitations` (
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
* @author Aleksander Machniak <machniak@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2011, Kolab Systems AG <contact@kolabsys.com>
|
||||
* Copyright (C) 2012, 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
|
||||
|
@ -26,6 +26,9 @@
|
|||
|
||||
class kolab_calendar
|
||||
{
|
||||
const COLOR_KEY_SHARED = '/shared/vendor/kolab/color';
|
||||
const COLOR_KEY_PRIVATE = '/shared/vendor/kolab/color';
|
||||
|
||||
public $id;
|
||||
public $ready = false;
|
||||
public $readonly = true;
|
||||
|
@ -35,17 +38,10 @@ class kolab_calendar
|
|||
public $storage;
|
||||
|
||||
private $cal;
|
||||
private $events;
|
||||
private $id2uid;
|
||||
private $events = array();
|
||||
private $imap_folder = 'INBOX/Calendar';
|
||||
private $namespace;
|
||||
private $search_fields = array('title', 'description', 'location', '_attendees');
|
||||
private $sensitivity_map = array('public', 'private', 'confidential');
|
||||
private $priority_map = array('low' => 9, 'normal' => 5, 'high' => 1);
|
||||
private $role_map = array('REQ-PARTICIPANT' => 'required', 'OPT-PARTICIPANT' => 'optional', 'CHAIR' => 'resource');
|
||||
private $status_map = array('NEEDS-ACTION' => 'none', 'TENTATIVE' => 'tentative', 'CONFIRMED' => 'accepted', 'ACCEPTED' => 'accepted', 'DECLINED' => 'declined');
|
||||
private $month_map = array('', 'january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december');
|
||||
private $weekday_map = array('MO'=>'monday', 'TU'=>'tuesday', 'WE'=>'wednesday', 'TH'=>'thursday', 'FR'=>'friday', 'SA'=>'saturday', 'SU'=>'sunday');
|
||||
|
||||
|
||||
/**
|
||||
|
@ -59,12 +55,11 @@ class kolab_calendar
|
|||
$this->imap_folder = $imap_folder;
|
||||
|
||||
// ID is derrived from folder name
|
||||
$this->id = rcube_kolab::folder_id($this->imap_folder);
|
||||
$this->id = kolab_storage::folder_id($this->imap_folder);
|
||||
|
||||
// fetch objects from the given IMAP folder
|
||||
$this->storage = rcube_kolab::get_storage($this->imap_folder);
|
||||
|
||||
$this->ready = !PEAR::isError($this->storage);
|
||||
$this->storage = kolab_storage::get_folder($this->imap_folder);
|
||||
$this->ready = $this->storage && !PEAR::isError($this->storage);
|
||||
|
||||
// Set readonly and alarms flags according to folder permissions
|
||||
if ($this->ready) {
|
||||
|
@ -73,8 +68,8 @@ class kolab_calendar
|
|||
$this->alarms = true;
|
||||
}
|
||||
else {
|
||||
$rights = $this->storage->_folder->getMyRights();
|
||||
if (!PEAR::isError($rights)) {
|
||||
$rights = $this->storage->get_myrights();
|
||||
if ($rights && !PEAR::isError($rights)) {
|
||||
if (strpos($rights, 'i') !== false)
|
||||
$this->readonly = false;
|
||||
}
|
||||
|
@ -96,7 +91,7 @@ class kolab_calendar
|
|||
*/
|
||||
public function get_name()
|
||||
{
|
||||
$folder = rcube_kolab::object_name($this->imap_folder, $this->namespace);
|
||||
$folder = kolab_storage::object_name($this->imap_folder, $this->namespace);
|
||||
return $folder;
|
||||
}
|
||||
|
||||
|
@ -119,7 +114,7 @@ class kolab_calendar
|
|||
*/
|
||||
public function get_owner()
|
||||
{
|
||||
return $this->storage->_folder->getOwner();
|
||||
return $this->storage->get_owner();
|
||||
}
|
||||
|
||||
|
||||
|
@ -130,10 +125,7 @@ class kolab_calendar
|
|||
*/
|
||||
public function get_namespace()
|
||||
{
|
||||
if ($this->namespace === null) {
|
||||
$this->namespace = rcube_kolab::folder_namespace($this->imap_folder);
|
||||
}
|
||||
return $this->namespace;
|
||||
return $this->storage->get_namespace();
|
||||
}
|
||||
|
||||
|
||||
|
@ -154,7 +146,8 @@ class kolab_calendar
|
|||
public function get_color()
|
||||
{
|
||||
// color is defined in folder METADATA
|
||||
if ($color = $this->storage->_folder->getKolabAttribute('color', HORDE_ANNOT_READ_PRIVATE_SHARED)) {
|
||||
$metadata = $this->storage->get_metadata(array(self::COLOR_KEY_PRIVATE, self::COLOR_KEY_SHARED));
|
||||
if (($color = $metadata[self::COLOR_KEY_PRIVATE]) || ($color = $metadata[self::COLOR_KEY_SHARED])) {
|
||||
return $color;
|
||||
}
|
||||
|
||||
|
@ -168,19 +161,11 @@ class kolab_calendar
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the corresponding Kolab_Folder instance
|
||||
* Return the corresponding kolab_storage_folder instance
|
||||
*/
|
||||
public function get_folder()
|
||||
{
|
||||
return $this->storage->_folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the attachment body
|
||||
*/
|
||||
public function get_attachment_body($id)
|
||||
{
|
||||
return $this->storage->getAttachment($id);
|
||||
return $this->storage;
|
||||
}
|
||||
|
||||
|
||||
|
@ -189,17 +174,21 @@ class kolab_calendar
|
|||
*/
|
||||
public function get_event($id)
|
||||
{
|
||||
$this->_fetch_events();
|
||||
|
||||
// directly access storage object
|
||||
if (!$this->events[$id] && ($record = $this->storage->get_object($id)))
|
||||
$this->events[$id] = $this->_to_rcube_event($record);
|
||||
|
||||
// event not found, maybe a recurring instance is requested
|
||||
if (!$this->events[$id]) {
|
||||
$master_id = preg_replace('/-\d+$/', '', $id);
|
||||
if ($this->events[$master_id] && $this->events[$master_id]['recurrence']) {
|
||||
$master = $this->events[$master_id];
|
||||
if ($record = $this->storage->get_object($master_id))
|
||||
$this->events[$master_id] = $this->_to_rcube_event($record);
|
||||
|
||||
if (($master = $this->events[$master_id]) && $master['recurrence']) {
|
||||
$this->_get_recurring_events($master, $master['start'], $master['start'] + 86400 * 365 * 10, $id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $this->events[$id];
|
||||
}
|
||||
|
||||
|
@ -208,13 +197,21 @@ class kolab_calendar
|
|||
* @param integer Event's new start (unix timestamp)
|
||||
* @param integer Event's new end (unix timestamp)
|
||||
* @param string Search query (optional)
|
||||
* @param boolean Strip virtual events (optional)
|
||||
* @param boolean Include virtual events (optional)
|
||||
* @param array Additional parameters to query storage
|
||||
* @return array A list of event records
|
||||
*/
|
||||
public function list_events($start, $end, $search = null, $virtual = 1)
|
||||
public function list_events($start, $end, $search = null, $virtual = 1, $query = array())
|
||||
{
|
||||
$this->_fetch_events();
|
||||
|
||||
// query Kolab storage
|
||||
$query[] = array('dtstart', '<=', $end);
|
||||
$query[] = array('dtend', '>=', $start);
|
||||
|
||||
foreach ((array)$this->storage->select($query) as $record) {
|
||||
$event = $this->_to_rcube_event($record);
|
||||
$this->events[$event['id']] = $event;
|
||||
}
|
||||
|
||||
if (!empty($search))
|
||||
$search = mb_strtolower($search);
|
||||
|
||||
|
@ -275,9 +272,9 @@ class kolab_calendar
|
|||
|
||||
//generate new event from RC input
|
||||
$object = $this->_from_rcube_event($event);
|
||||
$saved = $this->storage->save($object);
|
||||
$saved = $this->storage->save($object, 'event');
|
||||
|
||||
if (PEAR::isError($saved)) {
|
||||
if (!$saved || PEAR::isError($saved)) {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
|
@ -303,15 +300,15 @@ class kolab_calendar
|
|||
public function update_event($event)
|
||||
{
|
||||
$updated = false;
|
||||
$old = $this->storage->getObject($event['id']);
|
||||
if (PEAR::isError($old))
|
||||
$old = $this->storage->get_object($event['id']);
|
||||
if (!$old || PEAR::isError($old))
|
||||
return false;
|
||||
|
||||
$old['recurrence'] = ''; # clear old field, could have been removed in new, too
|
||||
$object = array_merge($old, $this->_from_rcube_event($event));
|
||||
$saved = $this->storage->save($object, $event['id']);
|
||||
$object = $this->_from_rcube_event($event, $old);
|
||||
$saved = $this->storage->save($object, 'event', $event['id']);
|
||||
|
||||
if (PEAR::isError($saved)) {
|
||||
if (!$saved || PEAR::isError($saved)) {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
|
@ -334,29 +331,15 @@ class kolab_calendar
|
|||
*/
|
||||
public function delete_event($event, $force = true)
|
||||
{
|
||||
$deleted = false;
|
||||
$deleted = $this->storage->delete($event['id'], $force);
|
||||
|
||||
if (!$force) {
|
||||
// Get IMAP object ID
|
||||
$imap_uid = $this->storage->_getStorageId($event['id']);
|
||||
}
|
||||
|
||||
$deleteme = $this->storage->delete($event['id'], $force);
|
||||
|
||||
if (PEAR::isError($deleteme)) {
|
||||
if (!$deleted || PEAR::isError($deleted)) {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Error deleting event object from Kolab server:" . $deleteme->getMessage()),
|
||||
'message' => "Error deleting event object from Kolab server"),
|
||||
true, false);
|
||||
}
|
||||
else {
|
||||
// Save IMAP object ID in session, will be used for restore action
|
||||
if ($imap_uid)
|
||||
$_SESSION['kolab_delete_uids'][$event['id']] = $imap_uid;
|
||||
|
||||
$deleted = true;
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
@ -369,63 +352,18 @@ class kolab_calendar
|
|||
*/
|
||||
public function restore_event($event)
|
||||
{
|
||||
$imap_uid = $_SESSION['kolab_delete_uids'][$event['id']];
|
||||
|
||||
if (!$imap_uid)
|
||||
return false;
|
||||
|
||||
$session = &Horde_Kolab_Session::singleton();
|
||||
$imap = &$session->getImap();
|
||||
|
||||
if (is_object($imap) && is_a($imap, 'PEAR_Error')) {
|
||||
$error = $imap;
|
||||
if ($this->storage->undelete($event['id'])) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
$result = $imap->select($this->imap_folder);
|
||||
if (is_object($result) && is_a($result, 'PEAR_Error')) {
|
||||
$error = $result;
|
||||
}
|
||||
else {
|
||||
$result = $imap->undeleteMessages($imap_uid);
|
||||
if (is_object($result) && is_a($result, 'PEAR_Error')) {
|
||||
$error = $result;
|
||||
}
|
||||
else {
|
||||
// re-sync the cache
|
||||
$this->storage->synchronize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Error undeleting an event object(s) from the Kolab server:" . $error->getMessage()),
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Error undeleting a contact object $uid from the Kolab server"),
|
||||
true, false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$rcmail->session->remove('kolab_delete_uids');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply fetch all records and store them in private member vars
|
||||
* We thereby rely on cahcing done by the Horde classes
|
||||
*/
|
||||
private function _fetch_events()
|
||||
{
|
||||
if (!isset($this->events)) {
|
||||
$this->events = array();
|
||||
foreach ((array)$this->storage->getObjects() as $record) {
|
||||
$event = $this->_to_rcube_event($record);
|
||||
$this->events[$event['id']] = $event;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -471,308 +409,99 @@ class kolab_calendar
|
|||
/**
|
||||
* Convert from Kolab_Format to internal representation
|
||||
*/
|
||||
private function _to_rcube_event($rec)
|
||||
private function _to_rcube_event($record)
|
||||
{
|
||||
$start_time = date('H:i:s', $rec['start-date']);
|
||||
$allday = $rec['_is_all_day'] || ($start_time == '00:00:00' && $start_time == date('H:i:s', $rec['end-date']));
|
||||
if ($allday) { // in Roundcube all-day events only go from 12:00 to 13:00
|
||||
$rec['start-date'] += 12 * 3600;
|
||||
$rec['end-date'] -= 11 * 3600;
|
||||
$rec['end-date'] -= $this->cal->gmt_offset - date('Z', $rec['end-date']); // shift times from server's timezone to user's timezone
|
||||
$rec['start-date'] -= $this->cal->gmt_offset - date('Z', $rec['start-date']); // because generated with mktime() in Horde_Kolab_Format_Date::decodeDate()
|
||||
// sanity check
|
||||
if ($rec['end-date'] <= $rec['start-date'])
|
||||
$rec['end-date'] += 86400;
|
||||
}
|
||||
|
||||
// convert alarm time into internal format
|
||||
if ($rec['alarm']) {
|
||||
$alarm_value = $rec['alarm'];
|
||||
$alarm_unit = 'M';
|
||||
if ($rec['alarm'] % 1440 == 0) {
|
||||
$alarm_value /= 1440;
|
||||
$alarm_unit = 'D';
|
||||
}
|
||||
else if ($rec['alarm'] % 60 == 0) {
|
||||
$alarm_value /= 60;
|
||||
$alarm_unit = 'H';
|
||||
}
|
||||
$alarm_value *= -1;
|
||||
}
|
||||
|
||||
// convert recurrence rules into internal pseudo-vcalendar format
|
||||
if ($recurrence = $rec['recurrence']) {
|
||||
$rrule = array(
|
||||
'FREQ' => strtoupper($recurrence['cycle']),
|
||||
'INTERVAL' => intval($recurrence['interval']),
|
||||
);
|
||||
|
||||
if ($recurrence['range-type'] == 'number')
|
||||
$rrule['COUNT'] = intval($recurrence['range']);
|
||||
else if ($recurrence['range-type'] == 'date')
|
||||
$rrule['UNTIL'] = $recurrence['range'];
|
||||
|
||||
if ($recurrence['day']) {
|
||||
$byday = array();
|
||||
$prefix = ($rrule['FREQ'] == 'MONTHLY' || $rrule['FREQ'] == 'YEARLY') ? intval($recurrence['daynumber'] ? $recurrence['daynumber'] : 1) : '';
|
||||
foreach ($recurrence['day'] as $day)
|
||||
$byday[] = $prefix . substr(strtoupper($day), 0, 2);
|
||||
$rrule['BYDAY'] = join(',', $byday);
|
||||
}
|
||||
if ($recurrence['daynumber']) {
|
||||
if ($recurrence['type'] == 'monthday' || $recurrence['type'] == 'daynumber')
|
||||
$rrule['BYMONTHDAY'] = $recurrence['daynumber'];
|
||||
else if ($recurrence['type'] == 'yearday')
|
||||
$rrule['BYYEARDAY'] = $recurrence['daynumber'];
|
||||
}
|
||||
if ($recurrence['month']) {
|
||||
$monthmap = array_flip($this->month_map);
|
||||
$rrule['BYMONTH'] = strtolower($monthmap[$recurrence['month']]);
|
||||
}
|
||||
|
||||
if ($recurrence['exclusion']) {
|
||||
foreach ((array)$recurrence['exclusion'] as $excl)
|
||||
$rrule['EXDATE'][] = strtotime($excl . date(' H:i:s', $rec['start-date'])); // use time of event start
|
||||
$record['id'] = $record['uid'];
|
||||
$record['calendar'] = $this->id;
|
||||
|
||||
// convert from DateTime to unix timestamp
|
||||
if (is_a($record['start'], 'DateTime'))
|
||||
$record['start'] = $record['start']->format('U');
|
||||
if (is_a($record['end'], 'DateTime'))
|
||||
$record['end'] = $record['end']->format('U');
|
||||
|
||||
// all-day events go from 12:00 - 13:00
|
||||
if ($record['end'] <= $record['start'] && $record['allday'])
|
||||
$record['end'] = $record['start'] + 3600;
|
||||
|
||||
if (!empty($record['_attachments'])) {
|
||||
foreach ($record['_attachments'] as $name => $attachment) {
|
||||
if ($attachment !== false) {
|
||||
$attachment['name'] = $name;
|
||||
$attachments[] = $attachment;
|
||||
}
|
||||
}
|
||||
|
||||
$record['attachments'] = $attachments;
|
||||
}
|
||||
|
||||
$sensitivity_map = array_flip($this->sensitivity_map);
|
||||
$status_map = array_flip($this->status_map);
|
||||
$role_map = array_flip($this->role_map);
|
||||
$record['sensitivity'] = intval($sensitivity_map[$record['sensitivity']]);
|
||||
|
||||
if (!empty($rec['_attachments'])) {
|
||||
foreach ($rec['_attachments'] as $name => $attachment) {
|
||||
// @TODO: 'type' and 'key' are the only supported (no 'size')
|
||||
$attachments[] = array(
|
||||
'id' => $attachment['key'],
|
||||
'mimetype' => $attachment['type'],
|
||||
'name' => $name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($rec['organizer']) {
|
||||
$attendees[] = array(
|
||||
'role' => 'ORGANIZER',
|
||||
'name' => $rec['organizer']['display-name'],
|
||||
'email' => $rec['organizer']['smtp-address'],
|
||||
'status' => 'ACCEPTED',
|
||||
);
|
||||
$_attendees .= $rec['organizer']['display-name'] . ' ' . $rec['organizer']['smtp-address'] . ' ';
|
||||
}
|
||||
|
||||
foreach ((array)$rec['attendee'] as $attendee) {
|
||||
$attendees[] = array(
|
||||
'role' => $role_map[$attendee['role']],
|
||||
'name' => $attendee['display-name'],
|
||||
'email' => $attendee['smtp-address'],
|
||||
'status' => $status_map[$attendee['status']],
|
||||
'rsvp' => $attendee['request-response'],
|
||||
);
|
||||
$_attendees .= $rec['organizer']['display-name'] . ' ' . $rec['organizer']['smtp-address'] . ' ';
|
||||
}
|
||||
|
||||
// Roundcube only supports one category assignment
|
||||
$categories = explode(',', $rec['categories']);
|
||||
|
||||
return array(
|
||||
'id' => $rec['uid'],
|
||||
'uid' => $rec['uid'],
|
||||
'title' => $rec['summary'],
|
||||
'location' => $rec['location'],
|
||||
'description' => $rec['body'],
|
||||
'start' => $rec['start-date'],
|
||||
'end' => $rec['end-date'],
|
||||
'allday' => $allday,
|
||||
'recurrence' => $rrule,
|
||||
'alarms' => $alarm_value . $alarm_unit,
|
||||
'_alarm' => intval($rec['alarm']),
|
||||
'categories' => $categories[0],
|
||||
'attachments' => $attachments,
|
||||
'attendees' => $attendees,
|
||||
'_attendees' => $_attendees,
|
||||
'free_busy' => $rec['show-time-as'],
|
||||
'priority' => is_numeric($rec['priority']) ? intval($rec['priority']) : (isset($this->priority_map[$rec['priority']]) ? $this->priority_map[$rec['priority']] : 0),
|
||||
'sensitivity' => $sensitivity_map[$rec['sensitivity']],
|
||||
'changed' => $rec['last-modification-date'],
|
||||
'calendar' => $this->id,
|
||||
);
|
||||
if (is_array($record['categories']))
|
||||
$record['categories'] = $record['categories'][0];
|
||||
|
||||
// remove internals
|
||||
unset($record['_mailbox'], $record['_msguid'], $record['_formatobj'], $record['_attachments']);
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given event record into a data structure that can be passed to Kolab_Storage backend for saving
|
||||
* (opposite of self::_to_rcube_event())
|
||||
*/
|
||||
private function _from_rcube_event($event)
|
||||
private function _from_rcube_event($event, $old = array())
|
||||
{
|
||||
$priority_map = $this->priority_map;
|
||||
$tz_offset = $this->cal->gmt_offset;
|
||||
|
||||
$object = array(
|
||||
// kolab => roundcube
|
||||
'uid' => $event['uid'],
|
||||
'summary' => $event['title'],
|
||||
'location' => $event['location'],
|
||||
'body' => $event['description'],
|
||||
'categories' => $event['categories'],
|
||||
'start-date' => $event['start'],
|
||||
'end-date' => $event['end'],
|
||||
'sensitivity' =>$this->sensitivity_map[$event['sensitivity']],
|
||||
'show-time-as' => $event['free_busy'],
|
||||
'priority' => $event['priority'],
|
||||
);
|
||||
|
||||
//handle alarms
|
||||
if ($event['alarms']) {
|
||||
//get the value
|
||||
$alarmbase = explode(":", $event['alarms']);
|
||||
|
||||
//get number only
|
||||
$avalue = preg_replace('/[^0-9]/', '', $alarmbase[0]);
|
||||
|
||||
if (preg_match("/H/",$alarmbase[0])) {
|
||||
$object['alarm'] = $avalue*60;
|
||||
} else if (preg_match("/D/",$alarmbase[0])) {
|
||||
$object['alarm'] = $avalue*24*60;
|
||||
} else {
|
||||
$object['alarm'] = $avalue;
|
||||
}
|
||||
}
|
||||
|
||||
//recurr object/array
|
||||
if (count($event['recurrence']) > 1) {
|
||||
$ra = $event['recurrence'];
|
||||
|
||||
//Frequency abd interval
|
||||
$object['recurrence']['cycle'] = strtolower($ra['FREQ']);
|
||||
$object['recurrence']['interval'] = intval($ra['INTERVAL']);
|
||||
|
||||
//Range Type
|
||||
if ($ra['UNTIL']) {
|
||||
$object['recurrence']['range-type'] = 'date';
|
||||
$object['recurrence']['range'] = $ra['UNTIL'];
|
||||
}
|
||||
if ($ra['COUNT']) {
|
||||
$object['recurrence']['range-type'] = 'number';
|
||||
$object['recurrence']['range'] = $ra['COUNT'];
|
||||
}
|
||||
|
||||
//weekly
|
||||
if ($ra['FREQ'] == 'WEEKLY') {
|
||||
if ($ra['BYDAY']) {
|
||||
foreach (split(",", $ra['BYDAY']) as $day)
|
||||
$object['recurrence']['day'][] = $this->weekday_map[$day];
|
||||
}
|
||||
else {
|
||||
// use weekday of start date if empty
|
||||
$object['recurrence']['day'][] = strtolower(gmdate('l', $event['start'] + $tz_offset));
|
||||
}
|
||||
}
|
||||
|
||||
//monthly (temporary hack to follow current Horde logic)
|
||||
if ($ra['FREQ'] == 'MONTHLY') {
|
||||
if ($ra['BYDAY'] && preg_match('/(-?[1-4])([A-Z]+)/', $ra['BYDAY'], $m)) {
|
||||
$object['recurrence']['daynumber'] = $m[1];
|
||||
$object['recurrence']['day'] = array($this->weekday_map[$m[2]]);
|
||||
$object['recurrence']['cycle'] = 'monthly';
|
||||
$object['recurrence']['type'] = 'weekday';
|
||||
}
|
||||
else {
|
||||
$object['recurrence']['daynumber'] = date('j', $event['start']);
|
||||
$object['recurrence']['cycle'] = 'monthly';
|
||||
$object['recurrence']['type'] = 'daynumber';
|
||||
}
|
||||
}
|
||||
|
||||
//yearly
|
||||
if ($ra['FREQ'] == 'YEARLY') {
|
||||
if (!$ra['BYMONTH'])
|
||||
$ra['BYMONTH'] = gmdate('n', $event['start'] + $tz_offset);
|
||||
|
||||
$object['recurrence']['cycle'] = 'yearly';
|
||||
$object['recurrence']['month'] = $this->month_map[intval($ra['BYMONTH'])];
|
||||
|
||||
if ($ra['BYDAY'] && preg_match('/(-?[1-4])([A-Z]+)/', $ra['BYDAY'], $m)) {
|
||||
$object['recurrence']['type'] = 'weekday';
|
||||
$object['recurrence']['daynumber'] = $m[1];
|
||||
$object['recurrence']['day'] = array($this->weekday_map[$m[2]]);
|
||||
}
|
||||
else {
|
||||
$object['recurrence']['type'] = 'monthday';
|
||||
$object['recurrence']['daynumber'] = gmdate('j', $event['start'] + $tz_offset);
|
||||
}
|
||||
}
|
||||
|
||||
//exclusions
|
||||
foreach ((array)$ra['EXDATE'] as $excl) {
|
||||
$object['recurrence']['exclusion'][] = gmdate('Y-m-d', $excl + $tz_offset);
|
||||
}
|
||||
}
|
||||
|
||||
// whole day event
|
||||
if ($event['allday']) {
|
||||
$object['end-date'] += 12 * 3600; // end is at 13:00 => jump to the next day
|
||||
$object['end-date'] += $tz_offset - date('Z'); // shift 00 times from user's timezone to server's timezone
|
||||
$object['start-date'] += $tz_offset - date('Z'); // because Horde_Kolab_Format_Date::encodeDate() uses strftime()
|
||||
|
||||
// create timestamps at exactly 00:00. This is also needed for proper re-interpretation in _to_rcube_event() after updating an event
|
||||
$object['start-date'] = mktime(0,0,0, date('n', $object['start-date']), date('j', $object['start-date']), date('Y', $object['start-date']));
|
||||
$object['end-date'] = mktime(0,0,0, date('n', $object['end-date']), date('j', $object['end-date']), date('Y', $object['end-date']));
|
||||
|
||||
// sanity check: end date is same or smaller than start
|
||||
if (date('Y-m-d', $object['end-date']) <= date('Y-m-d', $object['start-date']))
|
||||
$object['end-date'] = mktime(13,0,0, date('n', $object['start-date']), date('j', $object['start-date']), date('Y', $object['start-date'])) + 86400;
|
||||
|
||||
$object['_is_all_day'] = 1;
|
||||
}
|
||||
$object = &$event;
|
||||
|
||||
// in Horde attachments are indexed by name
|
||||
$object['_attachments'] = array();
|
||||
if (!empty($event['attachments'])) {
|
||||
if (is_array($event['attachments'])) {
|
||||
$collisions = array();
|
||||
foreach ($event['attachments'] as $idx => $attachment) {
|
||||
// Roundcube ID has nothing to do with Horde ID, remove it
|
||||
if ($attachment['content'])
|
||||
unset($attachment['id']);
|
||||
|
||||
// Horde code assumes that there will be no more than
|
||||
// one file with the same name: make filenames unique
|
||||
$filename = $attachment['name'];
|
||||
if ($collisions[$filename]++) {
|
||||
$ext = preg_match('/(\.[a-z0-9]{1,6})$/i', $filename, $m) ? $m[1] : null;
|
||||
$attachment['name'] = basename($filename, $ext) . '-' . $collisions[$filename] . $ext;
|
||||
// flagged for deletion => set to false
|
||||
if ($attachment['_deleted']) {
|
||||
$object['_attachments'][$attachment['name']] = false;
|
||||
}
|
||||
else {
|
||||
// Horde code assumes that there will be no more than
|
||||
// one file with the same name: make filenames unique
|
||||
$filename = $attachment['name'];
|
||||
if ($collisions[$filename]++) {
|
||||
$ext = preg_match('/(\.[a-z0-9]{1,6})$/i', $filename, $m) ? $m[1] : null;
|
||||
$attachment['name'] = basename($filename, $ext) . '-' . $collisions[$filename] . $ext;
|
||||
}
|
||||
|
||||
// set type parameter
|
||||
if ($attachment['mimetype'])
|
||||
$attachment['type'] = $attachment['mimetype'];
|
||||
|
||||
$object['_attachments'][$attachment['name']] = $attachment;
|
||||
unset($event['attachments'][$idx]);
|
||||
$object['_attachments'][$attachment['name']] = $attachment;
|
||||
}
|
||||
}
|
||||
|
||||
unset($event['attachments']);
|
||||
}
|
||||
|
||||
// process event attendees
|
||||
foreach ((array)$event['attendees'] as $attendee) {
|
||||
$role = $attendee['role'];
|
||||
if ($role == 'ORGANIZER') {
|
||||
$object['organizer'] = array(
|
||||
'display-name' => $attendee['name'],
|
||||
'smtp-address' => $attendee['email'],
|
||||
);
|
||||
}
|
||||
else {
|
||||
$object['attendee'][] = array(
|
||||
'display-name' => $attendee['name'],
|
||||
'smtp-address' => $attendee['email'],
|
||||
'status' => $this->status_map[$attendee['status']],
|
||||
'role' => $this->role_map[$role],
|
||||
'request-response' => $attendee['rsvp'],
|
||||
);
|
||||
}
|
||||
// translate sensitivity property
|
||||
$event['sensitivity'] = $this->sensitivity_map[$event['sensitivity']];
|
||||
|
||||
// set current user as ORGANIZER
|
||||
$identity = $this->cal->rc->user->get_identity();
|
||||
if (empty($event['attendees']) && $identity['email'])
|
||||
$event['attendees'] = array(array('role' => 'ORGANIZER', 'name' => $identity['name'], 'email' => $identity['email']));
|
||||
|
||||
$event['_owner'] = $identity['email'];
|
||||
|
||||
// copy meta data (starting with _) from old object
|
||||
foreach ((array)$old as $key => $val) {
|
||||
if (!isset($event[$key]) && $key[0] == '_')
|
||||
$event[$key] = $val;
|
||||
}
|
||||
|
||||
return $object;
|
||||
return $event;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
* @author Aleksander Machniak <machniak@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2011, Kolab Systems AG <contact@kolabsys.com>
|
||||
* Copyright (C) 2012, 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
|
||||
|
@ -33,6 +33,7 @@ class kolab_driver extends calendar_driver
|
|||
public $freebusy = true;
|
||||
public $attachments = true;
|
||||
public $undelete = true;
|
||||
public $alarm_types = array('DISPLAY','EMAIL');
|
||||
public $categoriesimmutable = true;
|
||||
|
||||
private $rc;
|
||||
|
@ -64,7 +65,7 @@ class kolab_driver extends calendar_driver
|
|||
return $this->calendars;
|
||||
|
||||
// get all folders that have "event" type
|
||||
$folders = rcube_kolab::get_folders('event');
|
||||
$folders = kolab_storage::get_folders('event');
|
||||
$this->calendars = array();
|
||||
|
||||
if (PEAR::isError($folders)) {
|
||||
|
@ -134,7 +135,7 @@ class kolab_driver extends calendar_driver
|
|||
'readonly' => $cal->readonly,
|
||||
'showalarms' => $cal->alarms,
|
||||
'class_name' => $cal->get_namespace(),
|
||||
'active' => rcube_kolab::is_subscribed($cal->get_realname()),
|
||||
'active' => $cal->storage->is_subscribed(kolab_storage::SERVERSIDE_SUBSCRIPTION),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -160,11 +161,11 @@ class kolab_driver extends calendar_driver
|
|||
}
|
||||
|
||||
// subscribe to new calendar by default
|
||||
$storage = $this->rc->get_storage();
|
||||
$storage->subscribe($folder);
|
||||
$storage = kolab_storage::get_folder($folder);
|
||||
$storage->subscribe($prop['active'], kolab_storage::SERVERSIDE_SUBSCRIPTION);
|
||||
|
||||
// create ID
|
||||
$id = rcube_kolab::folder_id($folder);
|
||||
$id = kolab_storage::folder_id($folder);
|
||||
|
||||
// save color in user prefs (temp. solution)
|
||||
$prefs['kolab_calendars'] = $this->rc->config->get('kolab_calendars', array());
|
||||
|
@ -197,7 +198,7 @@ class kolab_driver extends calendar_driver
|
|||
}
|
||||
|
||||
// create ID
|
||||
$id = rcube_kolab::folder_id($newfolder);
|
||||
$id = kolab_storage::folder_id($newfolder);
|
||||
|
||||
// fallback to local prefs
|
||||
$prefs['kolab_calendars'] = $this->rc->config->get('kolab_calendars', array());
|
||||
|
@ -226,13 +227,9 @@ class kolab_driver extends calendar_driver
|
|||
public function subscribe_calendar($prop)
|
||||
{
|
||||
if ($prop['id'] && ($cal = $this->calendars[$prop['id']])) {
|
||||
$storage = $this->rc->get_storage();
|
||||
if ($prop['active'])
|
||||
return $storage->subscribe($cal->get_realname());
|
||||
else
|
||||
return $storage->unsubscribe($cal->get_realname());
|
||||
return $cal->storage->subscribe($prop['active'], kolab_storage::SERVERSIDE_SUBSCRIPTION);
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -302,24 +299,24 @@ class kolab_driver extends calendar_driver
|
|||
// update the folder name
|
||||
if (strlen($oldfolder)) {
|
||||
if ($oldfolder != $folder) {
|
||||
if (!($result = rcube_kolab::folder_rename($oldfolder, $folder)))
|
||||
$this->last_error = rcube_kolab::$last_error;
|
||||
if (!($result = kolab_storage::folder_rename($oldfolder, $folder)))
|
||||
$this->last_error = kolab_storage::$last_error;
|
||||
}
|
||||
else
|
||||
$result = true;
|
||||
}
|
||||
// create new folder
|
||||
else {
|
||||
if (!($result = rcube_kolab::folder_create($folder, 'event', false)))
|
||||
$this->last_error = rcube_kolab::$last_error;
|
||||
if (!($result = kolab_storage::folder_create($folder, 'event')))
|
||||
$this->last_error = kolab_storage::$last_error;
|
||||
}
|
||||
|
||||
// save color in METADATA
|
||||
// TODO: also save 'showalarams' and other properties here
|
||||
|
||||
if ($result && $prop['color']) {
|
||||
if (!($meta_saved = $storage->set_metadata($folder, array('/shared/vendor/kolab/color' => $prop['color'])))) // try in shared namespace
|
||||
$meta_saved = $storage->set_metadata($folder, array('/private/vendor/kolab/color' => $prop['color'])); // try in private namespace
|
||||
if (!($meta_saved = $storage->set_metadata(array(kolab_calendar::COLOR_KEY_SHARED => $prop['color'])))) // try in shared namespace
|
||||
$meta_saved = $storage->set_metadata(array(kolab_calendar::COLOR_KEY_PRIVATE => $prop['color'])); // try in private namespace
|
||||
if ($meta_saved)
|
||||
unset($prop['color']); // unsetting will prevent fallback to local user prefs
|
||||
}
|
||||
|
@ -337,7 +334,7 @@ class kolab_driver extends calendar_driver
|
|||
{
|
||||
if ($prop['id'] && ($cal = $this->calendars[$prop['id']])) {
|
||||
$folder = $cal->get_realname();
|
||||
if (rcube_kolab::folder_delete($folder)) {
|
||||
if (kolab_storage::folder_delete($folder)) {
|
||||
// remove color in user prefs (temp. solution)
|
||||
$prefs['kolab_calendars'] = $this->rc->config->get('kolab_calendars', array());
|
||||
unset($prefs['kolab_calendars'][$prop['id']]);
|
||||
|
@ -346,7 +343,7 @@ class kolab_driver extends calendar_driver
|
|||
return true;
|
||||
}
|
||||
else
|
||||
$this->last_error = rcube_kolab::$last_error;
|
||||
$this->last_error = kolab_storage::$last_error;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -404,7 +401,6 @@ class kolab_driver extends calendar_driver
|
|||
}
|
||||
}
|
||||
|
||||
$GLOBALS['conf']['kolab']['no_triggering'] = true;
|
||||
$success = $storage->insert_event($event);
|
||||
|
||||
if ($success)
|
||||
|
@ -475,7 +471,6 @@ class kolab_driver extends calendar_driver
|
|||
$master = $event;
|
||||
|
||||
$this->rc->session->remove('calendar_restore_event_data');
|
||||
$GLOBALS['conf']['kolab']['no_triggering'] = true;
|
||||
|
||||
// read master if deleting a recurring event
|
||||
if ($event['recurrence'] || $event['recurrence_id']) {
|
||||
|
@ -566,7 +561,6 @@ class kolab_driver extends calendar_driver
|
|||
return false;
|
||||
|
||||
$fromcalendar = $storage;
|
||||
$storage->storage->synchronize();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -583,7 +577,7 @@ class kolab_driver extends calendar_driver
|
|||
if (!empty($old['attachments'])) {
|
||||
foreach ($old['attachments'] as $idx => $att) {
|
||||
if ($att['id'] == $attachment) {
|
||||
unset($old['attachments'][$idx]);
|
||||
$old['attachments'][$idx]['_deleted'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -596,12 +590,12 @@ class kolab_driver extends calendar_driver
|
|||
// skip entries without content (could be existing ones)
|
||||
if (!$attachment['data'] && !$attachment['path'])
|
||||
continue;
|
||||
// we'll read file contacts into memory, Horde/Kolab classes does the same
|
||||
// So we cannot save memory, rcube_imap class can do this better
|
||||
|
||||
$attachments[] = array(
|
||||
'name' => $attachment['name'],
|
||||
'type' => $attachment['mimetype'],
|
||||
'content' => $attachment['data'] ? $attachment['data'] : file_get_contents($attachment['path']),
|
||||
'mimetype' => $attachment['mimetype'],
|
||||
'content' => $attachment['data'],
|
||||
'path' => $attachment['path'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -618,8 +612,6 @@ class kolab_driver extends calendar_driver
|
|||
if ($old['recurrence']['EXDATE'])
|
||||
$event['recurrence']['EXDATE'] = $old['recurrence']['EXDATE'];
|
||||
|
||||
$GLOBALS['conf']['kolab']['no_triggering'] = true;
|
||||
|
||||
switch ($savemode) {
|
||||
case 'new':
|
||||
// save submitted data as new (non-recurring) event
|
||||
|
@ -629,7 +621,7 @@ class kolab_driver extends calendar_driver
|
|||
// copy attachment data to new event
|
||||
foreach ((array)$event['attachments'] as $idx => $attachment) {
|
||||
if (!$attachment['data'])
|
||||
$attachment['data'] = $fromcalendar->get_attachment_body($attachment['id']);
|
||||
$attachment['data'] = $fromcalendar->get_attachment_body($attachment['id'], $event);
|
||||
}
|
||||
|
||||
$success = $storage->insert_event($event);
|
||||
|
@ -773,17 +765,19 @@ class kolab_driver extends calendar_driver
|
|||
$time = $slot + $interval;
|
||||
|
||||
$events = array();
|
||||
$query = array(array('tags', 'LIKE', '% x-has-alarms %'));
|
||||
foreach ($this->calendars as $cid => $calendar) {
|
||||
// skip calendars with alarms disabled
|
||||
if (!$calendar->alarms || ($calendars && !in_array($cid, $calendars)))
|
||||
continue;
|
||||
|
||||
foreach ($calendar->list_events($time, $time + 86400 * 365) as $e) {
|
||||
foreach ($calendar->list_events($time, $time + 86400 * 365, null, 1, $query) as $e) {
|
||||
// add to list if alarm is set
|
||||
if ($e['_alarm'] && ($notifyat = $e['start'] - $e['_alarm'] * 60) <= $time) {
|
||||
$alarm = calendar::get_next_alarm($e);
|
||||
if ($alarm && $alarm['time'] && $alarm['time'] <= $time && $alarm['action'] == 'DISPLAY') {
|
||||
$id = $e['id'];
|
||||
$events[$id] = $e;
|
||||
$events[$id]['notifyat'] = $notifyat;
|
||||
$events[$id]['notifyat'] = $alarm['time'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -792,11 +786,13 @@ class kolab_driver extends calendar_driver
|
|||
if (!empty($events)) {
|
||||
$event_ids = array_map(array($this->rc->db, 'quote'), array_keys($events));
|
||||
$result = $this->rc->db->query(sprintf(
|
||||
"SELECT * FROM kolab_alarms
|
||||
WHERE event_id IN (%s)",
|
||||
join(',', $event_ids),
|
||||
$this->rc->db->now()
|
||||
));
|
||||
"SELECT * FROM kolab_alarms
|
||||
WHERE event_id IN (%s) AND user_id=?",
|
||||
join(',', $event_ids),
|
||||
$this->rc->db->now()
|
||||
),
|
||||
$this->rc->user->ID
|
||||
);
|
||||
|
||||
while ($result && ($e = $this->rc->db->fetch_assoc($result))) {
|
||||
$dbdata[$e['event_id']] = $e;
|
||||
|
@ -825,15 +821,24 @@ class kolab_driver extends calendar_driver
|
|||
*/
|
||||
public function dismiss_alarm($event_id, $snooze = 0)
|
||||
{
|
||||
// delete old alarm entry
|
||||
$this->rc->db->query(
|
||||
"DELETE FROM kolab_alarms
|
||||
WHERE event_id=? AND user_id=?",
|
||||
$event_id,
|
||||
$this->rc->user->ID
|
||||
);
|
||||
|
||||
// set new notifyat time or unset if not snoozed
|
||||
$notifyat = $snooze > 0 ? date('Y-m-d H:i:s', time() + $snooze) : null;
|
||||
|
||||
|
||||
$query = $this->rc->db->query(
|
||||
"REPLACE INTO kolab_alarms
|
||||
(event_id, dismissed, notifyat)
|
||||
VALUES(?, ?, ?)",
|
||||
"INSERT INTO kolab_alarms
|
||||
(event_id, user_id, dismissed, notifyat)
|
||||
VALUES(?, ?, ?, ?)",
|
||||
$event_id,
|
||||
$snooze > 0 ? 0 : 1,
|
||||
$this->rc->user->ID,
|
||||
$snooze > 0 ? 0 : 1,
|
||||
$notifyat
|
||||
);
|
||||
|
||||
|
@ -876,13 +881,14 @@ class kolab_driver extends calendar_driver
|
|||
|
||||
/**
|
||||
* Get attachment body
|
||||
* @see calendar_driver::get_attachment_body()
|
||||
*/
|
||||
public function get_attachment_body($id, $event)
|
||||
{
|
||||
if (!($storage = $this->calendars[$event['calendar']]))
|
||||
if (!($cal = $this->calendars[$event['calendar']]))
|
||||
return false;
|
||||
|
||||
return $storage->get_attachment_body($id);
|
||||
return $cal->storage->get_attachment($event['id'], $id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -986,12 +992,11 @@ class kolab_driver extends calendar_driver
|
|||
ignore_user_abort(true);
|
||||
|
||||
$cal = get_input_value('source', RCUBE_INPUT_GPC);
|
||||
if (!($storage = $this->calendars[$cal]))
|
||||
if (!($cal = $this->calendars[$cal]))
|
||||
return false;
|
||||
|
||||
// trigger updates on folder
|
||||
$folder = $storage->get_folder();
|
||||
$trigger = $folder->trigger();
|
||||
$trigger = $cal->storage->trigger();
|
||||
if (is_object($trigger) && is_a($trigger, 'PEAR_Error')) {
|
||||
raise_error(array(
|
||||
'code' => 900, 'type' => 'php',
|
||||
|
@ -1048,7 +1053,7 @@ class kolab_driver extends calendar_driver
|
|||
// Disable folder name input
|
||||
if (!empty($options) && ($options['norename'] || $options['protected'])) {
|
||||
$input_name = new html_hiddenfield(array('name' => 'name', 'id' => 'calendar-name'));
|
||||
$formfields['name']['value'] = Q(str_replace($delimiter, ' » ', rcube_kolab::object_name($folder)))
|
||||
$formfields['name']['value'] = Q(str_replace($delimiter, ' » ', kolab_storage::object_name($folder)))
|
||||
. $input_name->show($folder);
|
||||
}
|
||||
|
||||
|
@ -1065,7 +1070,7 @@ class kolab_driver extends calendar_driver
|
|||
$hidden_fields[] = array('name' => 'parent', 'value' => $path_imap);
|
||||
}
|
||||
else {
|
||||
$select = rcube_kolab::folder_selector('event', array('name' => 'parent'), $folder);
|
||||
$select = kolab_storage::folder_selector('event', array('name' => 'parent'), $folder);
|
||||
$form['props']['fieldsets']['location']['content']['path'] = array(
|
||||
'label' => $this->cal->gettext('parentcalendar'),
|
||||
'value' => $select->show(strlen($folder) ? $path_imap : ''),
|
||||
|
|
774
plugins/calendar/lib/Horde_Date.php
Normal file
774
plugins/calendar/lib/Horde_Date.php
Normal file
|
@ -0,0 +1,774 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This is a copy of the Horde/Date.php class from the Horde framework
|
||||
*/
|
||||
|
||||
define('HORDE_DATE_SUNDAY', 0);
|
||||
define('HORDE_DATE_MONDAY', 1);
|
||||
define('HORDE_DATE_TUESDAY', 2);
|
||||
define('HORDE_DATE_WEDNESDAY', 3);
|
||||
define('HORDE_DATE_THURSDAY', 4);
|
||||
define('HORDE_DATE_FRIDAY', 5);
|
||||
define('HORDE_DATE_SATURDAY', 6);
|
||||
|
||||
define('HORDE_DATE_MASK_SUNDAY', 1);
|
||||
define('HORDE_DATE_MASK_MONDAY', 2);
|
||||
define('HORDE_DATE_MASK_TUESDAY', 4);
|
||||
define('HORDE_DATE_MASK_WEDNESDAY', 8);
|
||||
define('HORDE_DATE_MASK_THURSDAY', 16);
|
||||
define('HORDE_DATE_MASK_FRIDAY', 32);
|
||||
define('HORDE_DATE_MASK_SATURDAY', 64);
|
||||
define('HORDE_DATE_MASK_WEEKDAYS', 62);
|
||||
define('HORDE_DATE_MASK_WEEKEND', 65);
|
||||
define('HORDE_DATE_MASK_ALLDAYS', 127);
|
||||
|
||||
define('HORDE_DATE_MASK_SECOND', 1);
|
||||
define('HORDE_DATE_MASK_MINUTE', 2);
|
||||
define('HORDE_DATE_MASK_HOUR', 4);
|
||||
define('HORDE_DATE_MASK_DAY', 8);
|
||||
define('HORDE_DATE_MASK_MONTH', 16);
|
||||
define('HORDE_DATE_MASK_YEAR', 32);
|
||||
define('HORDE_DATE_MASK_ALLPARTS', 63);
|
||||
|
||||
/**
|
||||
* Horde Date wrapper/logic class, including some calculation
|
||||
* functions.
|
||||
*
|
||||
* $Horde: framework/Date/Date.php,v 1.8.10.18 2008/09/17 08:46:04 jan Exp $
|
||||
*
|
||||
* @package Horde_Date
|
||||
*/
|
||||
class Horde_Date {
|
||||
|
||||
/**
|
||||
* Year
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $year;
|
||||
|
||||
/**
|
||||
* Month
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $month;
|
||||
|
||||
/**
|
||||
* Day
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $mday;
|
||||
|
||||
/**
|
||||
* Hour
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $hour = 0;
|
||||
|
||||
/**
|
||||
* Minute
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $min = 0;
|
||||
|
||||
/**
|
||||
* Second
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $sec = 0;
|
||||
|
||||
/**
|
||||
* Internally supported strftime() specifiers.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
var $_supportedSpecs = '%CdDeHImMnRStTyY';
|
||||
|
||||
/**
|
||||
* Build a new date object. If $date contains date parts, use them to
|
||||
* initialize the object.
|
||||
*
|
||||
* Recognized formats:
|
||||
* - arrays with keys 'year', 'month', 'mday', 'day' (since Horde 3.2),
|
||||
* 'hour', 'min', 'minute' (since Horde 3.2), 'sec'
|
||||
* - objects with properties 'year', 'month', 'mday', 'hour', 'min', 'sec'
|
||||
* - yyyy-mm-dd hh:mm:ss (since Horde 3.1)
|
||||
* - yyyymmddhhmmss (since Horde 3.1)
|
||||
* - yyyymmddThhmmssZ (since Horde 3.1.4)
|
||||
* - unix timestamps
|
||||
*/
|
||||
function Horde_Date($date = null)
|
||||
{
|
||||
if (function_exists('nl_langinfo')) {
|
||||
$this->_supportedSpecs .= 'bBpxX';
|
||||
}
|
||||
|
||||
if (is_array($date) || is_object($date)) {
|
||||
foreach ($date as $key => $val) {
|
||||
if (in_array($key, array('year', 'month', 'mday', 'hour', 'min', 'sec'))) {
|
||||
$this->$key = (int)$val;
|
||||
}
|
||||
}
|
||||
|
||||
// If $date['day'] is present and numeric we may have been passed
|
||||
// a Horde_Form_datetime array.
|
||||
if (is_array($date) && isset($date['day']) &&
|
||||
is_numeric($date['day'])) {
|
||||
$this->mday = (int)$date['day'];
|
||||
}
|
||||
// 'minute' key also from Horde_Form_datetime
|
||||
if (is_array($date) && isset($date['minute'])) {
|
||||
$this->min = $date['minute'];
|
||||
}
|
||||
} elseif (!is_null($date)) {
|
||||
// Match YYYY-MM-DD HH:MM:SS, YYYYMMDDHHMMSS and YYYYMMDD'T'HHMMSS'Z'.
|
||||
if (preg_match('/(\d{4})-?(\d{2})-?(\d{2})T? ?(\d{2}):?(\d{2}):?(\d{2})Z?/', $date, $parts)) {
|
||||
$this->year = (int)$parts[1];
|
||||
$this->month = (int)$parts[2];
|
||||
$this->mday = (int)$parts[3];
|
||||
$this->hour = (int)$parts[4];
|
||||
$this->min = (int)$parts[5];
|
||||
$this->sec = (int)$parts[6];
|
||||
} else {
|
||||
// Try as a timestamp.
|
||||
$parts = @getdate($date);
|
||||
if ($parts) {
|
||||
$this->year = $parts['year'];
|
||||
$this->month = $parts['mon'];
|
||||
$this->mday = $parts['mday'];
|
||||
$this->hour = $parts['hours'];
|
||||
$this->min = $parts['minutes'];
|
||||
$this->sec = $parts['seconds'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
*/
|
||||
function isLeapYear($year)
|
||||
{
|
||||
if (strlen($year) != 4 || preg_match('/\D/', $year)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (($year % 4 == 0 && $year % 100 != 0) || $year % 400 == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the day of the year (1-366) that corresponds to the
|
||||
* first day of the given week.
|
||||
*
|
||||
* TODO: with PHP 5.1+, see http://derickrethans.nl/calculating_start_and_end_dates_of_a_week.php
|
||||
*
|
||||
* @param integer $week The week of the year to find the first day of.
|
||||
* @param integer $year The year to calculate for.
|
||||
*
|
||||
* @return integer The day of the year of the first day of the given week.
|
||||
*/
|
||||
function firstDayOfWeek($week, $year)
|
||||
{
|
||||
$jan1 = new Horde_Date(array('year' => $year, 'month' => 1, 'mday' => 1));
|
||||
$start = $jan1->dayOfWeek();
|
||||
if ($start > HORDE_DATE_THURSDAY) {
|
||||
$start -= 7;
|
||||
}
|
||||
return (($week * 7) - (7 + $start)) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
*/
|
||||
function daysInMonth($month, $year)
|
||||
{
|
||||
if ($month == 2) {
|
||||
if (Horde_Date::isLeapYear($year)) {
|
||||
return 29;
|
||||
} else {
|
||||
return 28;
|
||||
}
|
||||
} elseif ($month == 4 || $month == 6 || $month == 9 || $month == 11) {
|
||||
return 30;
|
||||
} else {
|
||||
return 31;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the day of the week (0 = Sunday, 6 = Saturday) of this
|
||||
* object's date.
|
||||
*
|
||||
* @return integer The day of the week.
|
||||
*/
|
||||
function dayOfWeek()
|
||||
{
|
||||
if ($this->month > 2) {
|
||||
$month = $this->month - 2;
|
||||
$year = $this->year;
|
||||
} else {
|
||||
$month = $this->month + 10;
|
||||
$year = $this->year - 1;
|
||||
}
|
||||
|
||||
$day = (floor((13 * $month - 1) / 5) +
|
||||
$this->mday + ($year % 100) +
|
||||
floor(($year % 100) / 4) +
|
||||
floor(($year / 100) / 4) - 2 *
|
||||
floor($year / 100) + 77);
|
||||
|
||||
return (int)($day - 7 * floor($day / 7));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the day number of the year (1 to 365/366).
|
||||
*
|
||||
* @return integer The day of the year.
|
||||
*/
|
||||
function dayOfYear()
|
||||
{
|
||||
$monthTotals = array(0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334);
|
||||
$dayOfYear = $this->mday + $monthTotals[$this->month - 1];
|
||||
if (Horde_Date::isLeapYear($this->year) && $this->month > 2) {
|
||||
++$dayOfYear;
|
||||
}
|
||||
|
||||
return $dayOfYear;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the week of the month.
|
||||
*
|
||||
* @since Horde 3.2
|
||||
*
|
||||
* @return integer The week number.
|
||||
*/
|
||||
function weekOfMonth()
|
||||
{
|
||||
return ceil($this->mday / 7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the week of the year, first Monday is first day of first week.
|
||||
*
|
||||
* @return integer The week number.
|
||||
*/
|
||||
function weekOfYear()
|
||||
{
|
||||
return $this->format('W');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of weeks in the given year (52 or 53).
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param integer $year The year to count the number of weeks in.
|
||||
*
|
||||
* @return integer $numWeeks The number of weeks in $year.
|
||||
*/
|
||||
function weeksInYear($year)
|
||||
{
|
||||
// Find the last Thursday of the year.
|
||||
$day = 31;
|
||||
$date = new Horde_Date(array('year' => $year, 'month' => 12, 'mday' => $day, 'hour' => 0, 'min' => 0, 'sec' => 0));
|
||||
while ($date->dayOfWeek() != HORDE_DATE_THURSDAY) {
|
||||
--$date->mday;
|
||||
}
|
||||
return $date->weekOfYear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the date of this object to the $nth weekday of $weekday.
|
||||
*
|
||||
* @param integer $weekday The day of the week (0 = Sunday, etc).
|
||||
* @param integer $nth The $nth $weekday to set to (defaults to 1).
|
||||
*/
|
||||
function setNthWeekday($weekday, $nth = 1)
|
||||
{
|
||||
if ($weekday < HORDE_DATE_SUNDAY || $weekday > HORDE_DATE_SATURDAY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->mday = 1;
|
||||
$first = $this->dayOfWeek();
|
||||
if ($weekday < $first) {
|
||||
$this->mday = 8 + $weekday - $first;
|
||||
} else {
|
||||
$this->mday = $weekday - $first + 1;
|
||||
}
|
||||
$this->mday += 7 * $nth - 7;
|
||||
|
||||
$this->correct();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function dump($prefix = '')
|
||||
{
|
||||
echo ($prefix ? $prefix . ': ' : '') . $this->year . '-' . $this->month . '-' . $this->mday . "<br />\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the date currently represented by this object a valid date?
|
||||
*
|
||||
* @return boolean Validity, counting leap years, etc.
|
||||
*/
|
||||
function isValid()
|
||||
{
|
||||
if ($this->year < 0 || $this->year > 9999) {
|
||||
return false;
|
||||
}
|
||||
return checkdate($this->month, $this->mday, $this->year);
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct any over- or underflows in any of the date's members.
|
||||
*
|
||||
* @param integer $mask We may not want to correct some overflows.
|
||||
*/
|
||||
function correct($mask = HORDE_DATE_MASK_ALLPARTS)
|
||||
{
|
||||
if ($mask & HORDE_DATE_MASK_SECOND) {
|
||||
while ($this->sec < 0) {
|
||||
--$this->min;
|
||||
$this->sec += 60;
|
||||
}
|
||||
while ($this->sec > 59) {
|
||||
++$this->min;
|
||||
$this->sec -= 60;
|
||||
}
|
||||
}
|
||||
|
||||
if ($mask & HORDE_DATE_MASK_MINUTE) {
|
||||
while ($this->min < 0) {
|
||||
--$this->hour;
|
||||
$this->min += 60;
|
||||
}
|
||||
while ($this->min > 59) {
|
||||
++$this->hour;
|
||||
$this->min -= 60;
|
||||
}
|
||||
}
|
||||
|
||||
if ($mask & HORDE_DATE_MASK_HOUR) {
|
||||
while ($this->hour < 0) {
|
||||
--$this->mday;
|
||||
$this->hour += 24;
|
||||
}
|
||||
while ($this->hour > 23) {
|
||||
++$this->mday;
|
||||
$this->hour -= 24;
|
||||
}
|
||||
}
|
||||
|
||||
if ($mask & HORDE_DATE_MASK_MONTH) {
|
||||
while ($this->month > 12) {
|
||||
++$this->year;
|
||||
$this->month -= 12;
|
||||
}
|
||||
while ($this->month < 1) {
|
||||
--$this->year;
|
||||
$this->month += 12;
|
||||
}
|
||||
}
|
||||
|
||||
if ($mask & HORDE_DATE_MASK_DAY) {
|
||||
while ($this->mday > Horde_Date::daysInMonth($this->month, $this->year)) {
|
||||
$this->mday -= Horde_Date::daysInMonth($this->month, $this->year);
|
||||
++$this->month;
|
||||
$this->correct(HORDE_DATE_MASK_MONTH);
|
||||
}
|
||||
while ($this->mday < 1) {
|
||||
--$this->month;
|
||||
$this->correct(HORDE_DATE_MASK_MONTH);
|
||||
$this->mday += Horde_Date::daysInMonth($this->month, $this->year);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare this date to another date object to see which one is
|
||||
* greater (later). Assumes that the dates are in the same
|
||||
* timezone.
|
||||
*
|
||||
* @param mixed $date The date to compare to.
|
||||
*
|
||||
* @return integer == 0 if the dates are equal
|
||||
* >= 1 if this date is greater (later)
|
||||
* <= -1 if the other date is greater (later)
|
||||
*/
|
||||
function compareDate($date)
|
||||
{
|
||||
if (!is_object($date) || !is_a($date, 'Horde_Date')) {
|
||||
$date = new Horde_Date($date);
|
||||
}
|
||||
|
||||
if ($this->year != $date->year) {
|
||||
return $this->year - $date->year;
|
||||
}
|
||||
if ($this->month != $date->month) {
|
||||
return $this->month - $date->month;
|
||||
}
|
||||
|
||||
return $this->mday - $date->mday;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare this to another date object by time, to see which one
|
||||
* is greater (later). Assumes that the dates are in the same
|
||||
* timezone.
|
||||
*
|
||||
* @param mixed $date The date to compare to.
|
||||
*
|
||||
* @return integer == 0 if the dates are equal
|
||||
* >= 1 if this date is greater (later)
|
||||
* <= -1 if the other date is greater (later)
|
||||
*/
|
||||
function compareTime($date)
|
||||
{
|
||||
if (!is_object($date) || !is_a($date, 'Horde_Date')) {
|
||||
$date = new Horde_Date($date);
|
||||
}
|
||||
|
||||
if ($this->hour != $date->hour) {
|
||||
return $this->hour - $date->hour;
|
||||
}
|
||||
if ($this->min != $date->min) {
|
||||
return $this->min - $date->min;
|
||||
}
|
||||
|
||||
return $this->sec - $date->sec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare this to another date object, including times, to see
|
||||
* which one is greater (later). Assumes that the dates are in the
|
||||
* same timezone.
|
||||
*
|
||||
* @param mixed $date The date to compare to.
|
||||
*
|
||||
* @return integer == 0 if the dates are equal
|
||||
* >= 1 if this date is greater (later)
|
||||
* <= -1 if the other date is greater (later)
|
||||
*/
|
||||
function compareDateTime($date)
|
||||
{
|
||||
if (!is_object($date) || !is_a($date, 'Horde_Date')) {
|
||||
$date = new Horde_Date($date);
|
||||
}
|
||||
|
||||
if ($diff = $this->compareDate($date)) {
|
||||
return $diff;
|
||||
}
|
||||
|
||||
return $this->compareTime($date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time offset for local time zone.
|
||||
*
|
||||
* @param boolean $colon Place a colon between hours and minutes?
|
||||
*
|
||||
* @return string Timezone offset as a string in the format +HH:MM.
|
||||
*/
|
||||
function tzOffset($colon = true)
|
||||
{
|
||||
$secs = $this->format('Z');
|
||||
|
||||
if ($secs < 0) {
|
||||
$sign = '-';
|
||||
$secs = -$secs;
|
||||
} else {
|
||||
$sign = '+';
|
||||
}
|
||||
$colon = $colon ? ':' : '';
|
||||
$mins = intval(($secs + 30) / 60);
|
||||
return sprintf('%s%02d%s%02d',
|
||||
$sign, $mins / 60, $colon, $mins % 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the unix timestamp representation of this date.
|
||||
*
|
||||
* @return integer A unix timestamp.
|
||||
*/
|
||||
function timestamp()
|
||||
{
|
||||
if (class_exists('DateTime')) {
|
||||
return $this->format('U');
|
||||
} else {
|
||||
return Horde_Date::_mktime($this->hour, $this->min, $this->sec, $this->month, $this->mday, $this->year);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the unix timestamp representation of this date, 12:00am.
|
||||
*
|
||||
* @return integer A unix timestamp.
|
||||
*/
|
||||
function datestamp()
|
||||
{
|
||||
if (class_exists('DateTime')) {
|
||||
$dt = new DateTime();
|
||||
$dt->setDate($this->year, $this->month, $this->mday);
|
||||
$dt->setTime(0, 0, 0);
|
||||
return $dt->format('U');
|
||||
} else {
|
||||
return Horde_Date::_mktime(0, 0, 0, $this->month, $this->mday, $this->year);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time using the specifiers available in date() or in the DateTime
|
||||
* class' format() method.
|
||||
*
|
||||
* @since Horde 3.3
|
||||
*
|
||||
* @param string $format
|
||||
*
|
||||
* @return string Formatted time.
|
||||
*/
|
||||
function format($format)
|
||||
{
|
||||
if (class_exists('DateTime')) {
|
||||
$dt = new DateTime();
|
||||
$dt->setDate($this->year, $this->month, $this->mday);
|
||||
$dt->setTime($this->hour, $this->min, $this->sec);
|
||||
return $dt->format($format);
|
||||
} else {
|
||||
return date($format, $this->timestamp());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time in ISO-8601 format. Works correctly since Horde 3.2.
|
||||
*
|
||||
* @return string Date and time in ISO-8601 format.
|
||||
*/
|
||||
function iso8601DateTime()
|
||||
{
|
||||
return $this->rfc3339DateTime() . $this->tzOffset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time in RFC 2822 format.
|
||||
*
|
||||
* @return string Date and time in RFC 2822 format.
|
||||
*/
|
||||
function rfc2822DateTime()
|
||||
{
|
||||
return $this->format('D, j M Y H:i:s') . ' ' . $this->tzOffset(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time in RFC 3339 format.
|
||||
*
|
||||
* @since Horde 3.1
|
||||
*
|
||||
* @return string Date and time in RFC 3339 format. The seconds part has
|
||||
* been added with Horde 3.2.
|
||||
*/
|
||||
function rfc3339DateTime()
|
||||
{
|
||||
return $this->format('Y-m-d\TH:i:s');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time to standard 'ctime' format.
|
||||
*
|
||||
* @return string Date and time.
|
||||
*/
|
||||
function cTime()
|
||||
{
|
||||
return $this->format('D M j H:i:s Y');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format date and time using strftime() format.
|
||||
*
|
||||
* @since Horde 3.1
|
||||
*
|
||||
* @return string strftime() formatted date and time.
|
||||
*/
|
||||
function strftime($format)
|
||||
{
|
||||
if (preg_match('/%[^' . $this->_supportedSpecs . ']/', $format)) {
|
||||
return strftime($format, $this->timestamp());
|
||||
} else {
|
||||
return $this->_strftime($format);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format date and time using a limited set of the strftime() format.
|
||||
*
|
||||
* @return string strftime() formatted date and time.
|
||||
*/
|
||||
function _strftime($format)
|
||||
{
|
||||
if (preg_match('/%[bBpxX]/', $format)) {
|
||||
require_once 'Horde/NLS.php';
|
||||
}
|
||||
|
||||
return preg_replace(
|
||||
array('/%b/e',
|
||||
'/%B/e',
|
||||
'/%C/e',
|
||||
'/%d/e',
|
||||
'/%D/e',
|
||||
'/%e/e',
|
||||
'/%H/e',
|
||||
'/%I/e',
|
||||
'/%m/e',
|
||||
'/%M/e',
|
||||
'/%n/',
|
||||
'/%p/e',
|
||||
'/%R/e',
|
||||
'/%S/e',
|
||||
'/%t/',
|
||||
'/%T/e',
|
||||
'/%x/e',
|
||||
'/%X/e',
|
||||
'/%y/e',
|
||||
'/%Y/',
|
||||
'/%%/'),
|
||||
array('$this->_strftime(NLS::getLangInfo(constant(\'ABMON_\' . (int)$this->month)))',
|
||||
'$this->_strftime(NLS::getLangInfo(constant(\'MON_\' . (int)$this->month)))',
|
||||
'(int)($this->year / 100)',
|
||||
'sprintf(\'%02d\', $this->mday)',
|
||||
'$this->_strftime(\'%m/%d/%y\')',
|
||||
'sprintf(\'%2d\', $this->mday)',
|
||||
'sprintf(\'%02d\', $this->hour)',
|
||||
'sprintf(\'%02d\', $this->hour == 0 ? 12 : ($this->hour > 12 ? $this->hour - 12 : $this->hour))',
|
||||
'sprintf(\'%02d\', $this->month)',
|
||||
'sprintf(\'%02d\', $this->min)',
|
||||
"\n",
|
||||
'$this->_strftime(NLS::getLangInfo($this->hour < 12 ? AM_STR : PM_STR))',
|
||||
'$this->_strftime(\'%H:%M\')',
|
||||
'sprintf(\'%02d\', $this->sec)',
|
||||
"\t",
|
||||
'$this->_strftime(\'%H:%M:%S\')',
|
||||
'$this->_strftime(NLS::getLangInfo(D_FMT))',
|
||||
'$this->_strftime(NLS::getLangInfo(T_FMT))',
|
||||
'substr(sprintf(\'%04d\', $this->year), -2)',
|
||||
(int)$this->year,
|
||||
'%'),
|
||||
$format);
|
||||
}
|
||||
|
||||
/**
|
||||
* mktime() implementation that supports dates outside of 1970-2038,
|
||||
* from http://phplens.com/phpeverywhere/adodb_date_library.
|
||||
*
|
||||
* @TODO remove in Horde 4
|
||||
*
|
||||
* This does NOT work with pre-1970 daylight saving times.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
function _mktime($hr, $min, $sec, $mon = false, $day = false,
|
||||
$year = false, $is_dst = false, $is_gmt = false)
|
||||
{
|
||||
if ($mon === false) {
|
||||
return $is_gmt
|
||||
? @gmmktime($hr, $min, $sec)
|
||||
: @mktime($hr, $min, $sec);
|
||||
}
|
||||
|
||||
if ($year > 1901 && $year < 2038 &&
|
||||
($year >= 1970 || version_compare(PHP_VERSION, '5.0.0', '>='))) {
|
||||
return $is_gmt
|
||||
? @gmmktime($hr, $min, $sec, $mon, $day, $year)
|
||||
: @mktime($hr, $min, $sec, $mon, $day, $year);
|
||||
}
|
||||
|
||||
$gmt_different = $is_gmt
|
||||
? 0
|
||||
: (mktime(0, 0, 0, 1, 2, 1970, 0) - gmmktime(0, 0, 0, 1, 2, 1970, 0));
|
||||
|
||||
$mon = intval($mon);
|
||||
$day = intval($day);
|
||||
$year = intval($year);
|
||||
|
||||
if ($mon > 12) {
|
||||
$y = floor($mon / 12);
|
||||
$year += $y;
|
||||
$mon -= $y * 12;
|
||||
} elseif ($mon < 1) {
|
||||
$y = ceil((1 - $mon) / 12);
|
||||
$year -= $y;
|
||||
$mon += $y * 12;
|
||||
}
|
||||
|
||||
$_day_power = 86400;
|
||||
$_hour_power = 3600;
|
||||
$_min_power = 60;
|
||||
|
||||
$_month_table_normal = array('', 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
|
||||
$_month_table_leaf = array('', 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
|
||||
|
||||
$_total_date = 0;
|
||||
if ($year >= 1970) {
|
||||
for ($a = 1970; $a <= $year; $a++) {
|
||||
$leaf = Horde_Date::isLeapYear($a);
|
||||
if ($leaf == true) {
|
||||
$loop_table = $_month_table_leaf;
|
||||
$_add_date = 366;
|
||||
} else {
|
||||
$loop_table = $_month_table_normal;
|
||||
$_add_date = 365;
|
||||
}
|
||||
if ($a < $year) {
|
||||
$_total_date += $_add_date;
|
||||
} else {
|
||||
for ($b = 1; $b < $mon; $b++) {
|
||||
$_total_date += $loop_table[$b];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ($_total_date + $day - 1) * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different;
|
||||
}
|
||||
|
||||
for ($a = 1969 ; $a >= $year; $a--) {
|
||||
$leaf = Horde_Date::isLeapYear($a);
|
||||
if ($leaf == true) {
|
||||
$loop_table = $_month_table_leaf;
|
||||
$_add_date = 366;
|
||||
} else {
|
||||
$loop_table = $_month_table_normal;
|
||||
$_add_date = 365;
|
||||
}
|
||||
if ($a > $year) {
|
||||
$_total_date += $_add_date;
|
||||
} else {
|
||||
for ($b = 12; $b > $mon; $b--) {
|
||||
$_total_date += $loop_table[$b];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$_total_date += $loop_table[$mon] - $day;
|
||||
$_day_time = $hr * $_hour_power + $min * $_min_power + $sec;
|
||||
$_day_time = $_day_power - $_day_time;
|
||||
$ret = -($_total_date * $_day_power + $_day_time - $gmt_different);
|
||||
if ($ret < -12220185600) {
|
||||
// If earlier than 5 Oct 1582 - gregorian correction.
|
||||
return $ret + 10 * 86400;
|
||||
} elseif ($ret < -12219321600) {
|
||||
// If in limbo, reset to 15 Oct 1582.
|
||||
return -12219321600;
|
||||
} else {
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,779 +2,10 @@
|
|||
|
||||
/**
|
||||
* This is a concatenated copy of the following files:
|
||||
* Horde/Date.php, PEAR/Date/Calc.php, Horde/Date/Recurrence.php
|
||||
* PEAR/Date/Calc.php, Horde/Date/Recurrence.php
|
||||
*/
|
||||
|
||||
define('HORDE_DATE_SUNDAY', 0);
|
||||
define('HORDE_DATE_MONDAY', 1);
|
||||
define('HORDE_DATE_TUESDAY', 2);
|
||||
define('HORDE_DATE_WEDNESDAY', 3);
|
||||
define('HORDE_DATE_THURSDAY', 4);
|
||||
define('HORDE_DATE_FRIDAY', 5);
|
||||
define('HORDE_DATE_SATURDAY', 6);
|
||||
|
||||
define('HORDE_DATE_MASK_SUNDAY', 1);
|
||||
define('HORDE_DATE_MASK_MONDAY', 2);
|
||||
define('HORDE_DATE_MASK_TUESDAY', 4);
|
||||
define('HORDE_DATE_MASK_WEDNESDAY', 8);
|
||||
define('HORDE_DATE_MASK_THURSDAY', 16);
|
||||
define('HORDE_DATE_MASK_FRIDAY', 32);
|
||||
define('HORDE_DATE_MASK_SATURDAY', 64);
|
||||
define('HORDE_DATE_MASK_WEEKDAYS', 62);
|
||||
define('HORDE_DATE_MASK_WEEKEND', 65);
|
||||
define('HORDE_DATE_MASK_ALLDAYS', 127);
|
||||
|
||||
define('HORDE_DATE_MASK_SECOND', 1);
|
||||
define('HORDE_DATE_MASK_MINUTE', 2);
|
||||
define('HORDE_DATE_MASK_HOUR', 4);
|
||||
define('HORDE_DATE_MASK_DAY', 8);
|
||||
define('HORDE_DATE_MASK_MONTH', 16);
|
||||
define('HORDE_DATE_MASK_YEAR', 32);
|
||||
define('HORDE_DATE_MASK_ALLPARTS', 63);
|
||||
|
||||
/**
|
||||
* Horde Date wrapper/logic class, including some calculation
|
||||
* functions.
|
||||
*
|
||||
* $Horde: framework/Date/Date.php,v 1.8.10.18 2008/09/17 08:46:04 jan Exp $
|
||||
*
|
||||
* @package Horde_Date
|
||||
*/
|
||||
class Horde_Date {
|
||||
|
||||
/**
|
||||
* Year
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $year;
|
||||
|
||||
/**
|
||||
* Month
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $month;
|
||||
|
||||
/**
|
||||
* Day
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $mday;
|
||||
|
||||
/**
|
||||
* Hour
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $hour = 0;
|
||||
|
||||
/**
|
||||
* Minute
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $min = 0;
|
||||
|
||||
/**
|
||||
* Second
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $sec = 0;
|
||||
|
||||
/**
|
||||
* Internally supported strftime() specifiers.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
var $_supportedSpecs = '%CdDeHImMnRStTyY';
|
||||
|
||||
/**
|
||||
* Build a new date object. If $date contains date parts, use them to
|
||||
* initialize the object.
|
||||
*
|
||||
* Recognized formats:
|
||||
* - arrays with keys 'year', 'month', 'mday', 'day' (since Horde 3.2),
|
||||
* 'hour', 'min', 'minute' (since Horde 3.2), 'sec'
|
||||
* - objects with properties 'year', 'month', 'mday', 'hour', 'min', 'sec'
|
||||
* - yyyy-mm-dd hh:mm:ss (since Horde 3.1)
|
||||
* - yyyymmddhhmmss (since Horde 3.1)
|
||||
* - yyyymmddThhmmssZ (since Horde 3.1.4)
|
||||
* - unix timestamps
|
||||
*/
|
||||
function Horde_Date($date = null)
|
||||
{
|
||||
if (function_exists('nl_langinfo')) {
|
||||
$this->_supportedSpecs .= 'bBpxX';
|
||||
}
|
||||
|
||||
if (is_array($date) || is_object($date)) {
|
||||
foreach ($date as $key => $val) {
|
||||
if (in_array($key, array('year', 'month', 'mday', 'hour', 'min', 'sec'))) {
|
||||
$this->$key = (int)$val;
|
||||
}
|
||||
}
|
||||
|
||||
// If $date['day'] is present and numeric we may have been passed
|
||||
// a Horde_Form_datetime array.
|
||||
if (is_array($date) && isset($date['day']) &&
|
||||
is_numeric($date['day'])) {
|
||||
$this->mday = (int)$date['day'];
|
||||
}
|
||||
// 'minute' key also from Horde_Form_datetime
|
||||
if (is_array($date) && isset($date['minute'])) {
|
||||
$this->min = $date['minute'];
|
||||
}
|
||||
} elseif (!is_null($date)) {
|
||||
// Match YYYY-MM-DD HH:MM:SS, YYYYMMDDHHMMSS and YYYYMMDD'T'HHMMSS'Z'.
|
||||
if (preg_match('/(\d{4})-?(\d{2})-?(\d{2})T? ?(\d{2}):?(\d{2}):?(\d{2})Z?/', $date, $parts)) {
|
||||
$this->year = (int)$parts[1];
|
||||
$this->month = (int)$parts[2];
|
||||
$this->mday = (int)$parts[3];
|
||||
$this->hour = (int)$parts[4];
|
||||
$this->min = (int)$parts[5];
|
||||
$this->sec = (int)$parts[6];
|
||||
} else {
|
||||
// Try as a timestamp.
|
||||
$parts = @getdate($date);
|
||||
if ($parts) {
|
||||
$this->year = $parts['year'];
|
||||
$this->month = $parts['mon'];
|
||||
$this->mday = $parts['mday'];
|
||||
$this->hour = $parts['hours'];
|
||||
$this->min = $parts['minutes'];
|
||||
$this->sec = $parts['seconds'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
*/
|
||||
function isLeapYear($year)
|
||||
{
|
||||
if (strlen($year) != 4 || preg_match('/\D/', $year)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (($year % 4 == 0 && $year % 100 != 0) || $year % 400 == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the day of the year (1-366) that corresponds to the
|
||||
* first day of the given week.
|
||||
*
|
||||
* TODO: with PHP 5.1+, see http://derickrethans.nl/calculating_start_and_end_dates_of_a_week.php
|
||||
*
|
||||
* @param integer $week The week of the year to find the first day of.
|
||||
* @param integer $year The year to calculate for.
|
||||
*
|
||||
* @return integer The day of the year of the first day of the given week.
|
||||
*/
|
||||
function firstDayOfWeek($week, $year)
|
||||
{
|
||||
$jan1 = new Horde_Date(array('year' => $year, 'month' => 1, 'mday' => 1));
|
||||
$start = $jan1->dayOfWeek();
|
||||
if ($start > HORDE_DATE_THURSDAY) {
|
||||
$start -= 7;
|
||||
}
|
||||
return (($week * 7) - (7 + $start)) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
*/
|
||||
function daysInMonth($month, $year)
|
||||
{
|
||||
if ($month == 2) {
|
||||
if (Horde_Date::isLeapYear($year)) {
|
||||
return 29;
|
||||
} else {
|
||||
return 28;
|
||||
}
|
||||
} elseif ($month == 4 || $month == 6 || $month == 9 || $month == 11) {
|
||||
return 30;
|
||||
} else {
|
||||
return 31;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the day of the week (0 = Sunday, 6 = Saturday) of this
|
||||
* object's date.
|
||||
*
|
||||
* @return integer The day of the week.
|
||||
*/
|
||||
function dayOfWeek()
|
||||
{
|
||||
if ($this->month > 2) {
|
||||
$month = $this->month - 2;
|
||||
$year = $this->year;
|
||||
} else {
|
||||
$month = $this->month + 10;
|
||||
$year = $this->year - 1;
|
||||
}
|
||||
|
||||
$day = (floor((13 * $month - 1) / 5) +
|
||||
$this->mday + ($year % 100) +
|
||||
floor(($year % 100) / 4) +
|
||||
floor(($year / 100) / 4) - 2 *
|
||||
floor($year / 100) + 77);
|
||||
|
||||
return (int)($day - 7 * floor($day / 7));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the day number of the year (1 to 365/366).
|
||||
*
|
||||
* @return integer The day of the year.
|
||||
*/
|
||||
function dayOfYear()
|
||||
{
|
||||
$monthTotals = array(0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334);
|
||||
$dayOfYear = $this->mday + $monthTotals[$this->month - 1];
|
||||
if (Horde_Date::isLeapYear($this->year) && $this->month > 2) {
|
||||
++$dayOfYear;
|
||||
}
|
||||
|
||||
return $dayOfYear;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the week of the month.
|
||||
*
|
||||
* @since Horde 3.2
|
||||
*
|
||||
* @return integer The week number.
|
||||
*/
|
||||
function weekOfMonth()
|
||||
{
|
||||
return ceil($this->mday / 7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the week of the year, first Monday is first day of first week.
|
||||
*
|
||||
* @return integer The week number.
|
||||
*/
|
||||
function weekOfYear()
|
||||
{
|
||||
return $this->format('W');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of weeks in the given year (52 or 53).
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param integer $year The year to count the number of weeks in.
|
||||
*
|
||||
* @return integer $numWeeks The number of weeks in $year.
|
||||
*/
|
||||
function weeksInYear($year)
|
||||
{
|
||||
// Find the last Thursday of the year.
|
||||
$day = 31;
|
||||
$date = new Horde_Date(array('year' => $year, 'month' => 12, 'mday' => $day, 'hour' => 0, 'min' => 0, 'sec' => 0));
|
||||
while ($date->dayOfWeek() != HORDE_DATE_THURSDAY) {
|
||||
--$date->mday;
|
||||
}
|
||||
return $date->weekOfYear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the date of this object to the $nth weekday of $weekday.
|
||||
*
|
||||
* @param integer $weekday The day of the week (0 = Sunday, etc).
|
||||
* @param integer $nth The $nth $weekday to set to (defaults to 1).
|
||||
*/
|
||||
function setNthWeekday($weekday, $nth = 1)
|
||||
{
|
||||
if ($weekday < HORDE_DATE_SUNDAY || $weekday > HORDE_DATE_SATURDAY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->mday = 1;
|
||||
$first = $this->dayOfWeek();
|
||||
if ($weekday < $first) {
|
||||
$this->mday = 8 + $weekday - $first;
|
||||
} else {
|
||||
$this->mday = $weekday - $first + 1;
|
||||
}
|
||||
$this->mday += 7 * $nth - 7;
|
||||
|
||||
$this->correct();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function dump($prefix = '')
|
||||
{
|
||||
echo ($prefix ? $prefix . ': ' : '') . $this->year . '-' . $this->month . '-' . $this->mday . "<br />\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the date currently represented by this object a valid date?
|
||||
*
|
||||
* @return boolean Validity, counting leap years, etc.
|
||||
*/
|
||||
function isValid()
|
||||
{
|
||||
if ($this->year < 0 || $this->year > 9999) {
|
||||
return false;
|
||||
}
|
||||
return checkdate($this->month, $this->mday, $this->year);
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct any over- or underflows in any of the date's members.
|
||||
*
|
||||
* @param integer $mask We may not want to correct some overflows.
|
||||
*/
|
||||
function correct($mask = HORDE_DATE_MASK_ALLPARTS)
|
||||
{
|
||||
if ($mask & HORDE_DATE_MASK_SECOND) {
|
||||
while ($this->sec < 0) {
|
||||
--$this->min;
|
||||
$this->sec += 60;
|
||||
}
|
||||
while ($this->sec > 59) {
|
||||
++$this->min;
|
||||
$this->sec -= 60;
|
||||
}
|
||||
}
|
||||
|
||||
if ($mask & HORDE_DATE_MASK_MINUTE) {
|
||||
while ($this->min < 0) {
|
||||
--$this->hour;
|
||||
$this->min += 60;
|
||||
}
|
||||
while ($this->min > 59) {
|
||||
++$this->hour;
|
||||
$this->min -= 60;
|
||||
}
|
||||
}
|
||||
|
||||
if ($mask & HORDE_DATE_MASK_HOUR) {
|
||||
while ($this->hour < 0) {
|
||||
--$this->mday;
|
||||
$this->hour += 24;
|
||||
}
|
||||
while ($this->hour > 23) {
|
||||
++$this->mday;
|
||||
$this->hour -= 24;
|
||||
}
|
||||
}
|
||||
|
||||
if ($mask & HORDE_DATE_MASK_MONTH) {
|
||||
while ($this->month > 12) {
|
||||
++$this->year;
|
||||
$this->month -= 12;
|
||||
}
|
||||
while ($this->month < 1) {
|
||||
--$this->year;
|
||||
$this->month += 12;
|
||||
}
|
||||
}
|
||||
|
||||
if ($mask & HORDE_DATE_MASK_DAY) {
|
||||
while ($this->mday > Horde_Date::daysInMonth($this->month, $this->year)) {
|
||||
$this->mday -= Horde_Date::daysInMonth($this->month, $this->year);
|
||||
++$this->month;
|
||||
$this->correct(HORDE_DATE_MASK_MONTH);
|
||||
}
|
||||
while ($this->mday < 1) {
|
||||
--$this->month;
|
||||
$this->correct(HORDE_DATE_MASK_MONTH);
|
||||
$this->mday += Horde_Date::daysInMonth($this->month, $this->year);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare this date to another date object to see which one is
|
||||
* greater (later). Assumes that the dates are in the same
|
||||
* timezone.
|
||||
*
|
||||
* @param mixed $date The date to compare to.
|
||||
*
|
||||
* @return integer == 0 if the dates are equal
|
||||
* >= 1 if this date is greater (later)
|
||||
* <= -1 if the other date is greater (later)
|
||||
*/
|
||||
function compareDate($date)
|
||||
{
|
||||
if (!is_object($date) || !is_a($date, 'Horde_Date')) {
|
||||
$date = new Horde_Date($date);
|
||||
}
|
||||
|
||||
if ($this->year != $date->year) {
|
||||
return $this->year - $date->year;
|
||||
}
|
||||
if ($this->month != $date->month) {
|
||||
return $this->month - $date->month;
|
||||
}
|
||||
|
||||
return $this->mday - $date->mday;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare this to another date object by time, to see which one
|
||||
* is greater (later). Assumes that the dates are in the same
|
||||
* timezone.
|
||||
*
|
||||
* @param mixed $date The date to compare to.
|
||||
*
|
||||
* @return integer == 0 if the dates are equal
|
||||
* >= 1 if this date is greater (later)
|
||||
* <= -1 if the other date is greater (later)
|
||||
*/
|
||||
function compareTime($date)
|
||||
{
|
||||
if (!is_object($date) || !is_a($date, 'Horde_Date')) {
|
||||
$date = new Horde_Date($date);
|
||||
}
|
||||
|
||||
if ($this->hour != $date->hour) {
|
||||
return $this->hour - $date->hour;
|
||||
}
|
||||
if ($this->min != $date->min) {
|
||||
return $this->min - $date->min;
|
||||
}
|
||||
|
||||
return $this->sec - $date->sec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare this to another date object, including times, to see
|
||||
* which one is greater (later). Assumes that the dates are in the
|
||||
* same timezone.
|
||||
*
|
||||
* @param mixed $date The date to compare to.
|
||||
*
|
||||
* @return integer == 0 if the dates are equal
|
||||
* >= 1 if this date is greater (later)
|
||||
* <= -1 if the other date is greater (later)
|
||||
*/
|
||||
function compareDateTime($date)
|
||||
{
|
||||
if (!is_object($date) || !is_a($date, 'Horde_Date')) {
|
||||
$date = new Horde_Date($date);
|
||||
}
|
||||
|
||||
if ($diff = $this->compareDate($date)) {
|
||||
return $diff;
|
||||
}
|
||||
|
||||
return $this->compareTime($date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time offset for local time zone.
|
||||
*
|
||||
* @param boolean $colon Place a colon between hours and minutes?
|
||||
*
|
||||
* @return string Timezone offset as a string in the format +HH:MM.
|
||||
*/
|
||||
function tzOffset($colon = true)
|
||||
{
|
||||
$secs = $this->format('Z');
|
||||
|
||||
if ($secs < 0) {
|
||||
$sign = '-';
|
||||
$secs = -$secs;
|
||||
} else {
|
||||
$sign = '+';
|
||||
}
|
||||
$colon = $colon ? ':' : '';
|
||||
$mins = intval(($secs + 30) / 60);
|
||||
return sprintf('%s%02d%s%02d',
|
||||
$sign, $mins / 60, $colon, $mins % 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the unix timestamp representation of this date.
|
||||
*
|
||||
* @return integer A unix timestamp.
|
||||
*/
|
||||
function timestamp()
|
||||
{
|
||||
if (class_exists('DateTime')) {
|
||||
return $this->format('U');
|
||||
} else {
|
||||
return Horde_Date::_mktime($this->hour, $this->min, $this->sec, $this->month, $this->mday, $this->year);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the unix timestamp representation of this date, 12:00am.
|
||||
*
|
||||
* @return integer A unix timestamp.
|
||||
*/
|
||||
function datestamp()
|
||||
{
|
||||
if (class_exists('DateTime')) {
|
||||
$dt = new DateTime();
|
||||
$dt->setDate($this->year, $this->month, $this->mday);
|
||||
$dt->setTime(0, 0, 0);
|
||||
return $dt->format('U');
|
||||
} else {
|
||||
return Horde_Date::_mktime(0, 0, 0, $this->month, $this->mday, $this->year);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time using the specifiers available in date() or in the DateTime
|
||||
* class' format() method.
|
||||
*
|
||||
* @since Horde 3.3
|
||||
*
|
||||
* @param string $format
|
||||
*
|
||||
* @return string Formatted time.
|
||||
*/
|
||||
function format($format)
|
||||
{
|
||||
if (class_exists('DateTime')) {
|
||||
$dt = new DateTime();
|
||||
$dt->setDate($this->year, $this->month, $this->mday);
|
||||
$dt->setTime($this->hour, $this->min, $this->sec);
|
||||
return $dt->format($format);
|
||||
} else {
|
||||
return date($format, $this->timestamp());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time in ISO-8601 format. Works correctly since Horde 3.2.
|
||||
*
|
||||
* @return string Date and time in ISO-8601 format.
|
||||
*/
|
||||
function iso8601DateTime()
|
||||
{
|
||||
return $this->rfc3339DateTime() . $this->tzOffset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time in RFC 2822 format.
|
||||
*
|
||||
* @return string Date and time in RFC 2822 format.
|
||||
*/
|
||||
function rfc2822DateTime()
|
||||
{
|
||||
return $this->format('D, j M Y H:i:s') . ' ' . $this->tzOffset(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time in RFC 3339 format.
|
||||
*
|
||||
* @since Horde 3.1
|
||||
*
|
||||
* @return string Date and time in RFC 3339 format. The seconds part has
|
||||
* been added with Horde 3.2.
|
||||
*/
|
||||
function rfc3339DateTime()
|
||||
{
|
||||
return $this->format('Y-m-d\TH:i:s');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time to standard 'ctime' format.
|
||||
*
|
||||
* @return string Date and time.
|
||||
*/
|
||||
function cTime()
|
||||
{
|
||||
return $this->format('D M j H:i:s Y');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format date and time using strftime() format.
|
||||
*
|
||||
* @since Horde 3.1
|
||||
*
|
||||
* @return string strftime() formatted date and time.
|
||||
*/
|
||||
function strftime($format)
|
||||
{
|
||||
if (preg_match('/%[^' . $this->_supportedSpecs . ']/', $format)) {
|
||||
return strftime($format, $this->timestamp());
|
||||
} else {
|
||||
return $this->_strftime($format);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format date and time using a limited set of the strftime() format.
|
||||
*
|
||||
* @return string strftime() formatted date and time.
|
||||
*/
|
||||
function _strftime($format)
|
||||
{
|
||||
if (preg_match('/%[bBpxX]/', $format)) {
|
||||
require_once 'Horde/NLS.php';
|
||||
}
|
||||
|
||||
return preg_replace(
|
||||
array('/%b/e',
|
||||
'/%B/e',
|
||||
'/%C/e',
|
||||
'/%d/e',
|
||||
'/%D/e',
|
||||
'/%e/e',
|
||||
'/%H/e',
|
||||
'/%I/e',
|
||||
'/%m/e',
|
||||
'/%M/e',
|
||||
'/%n/',
|
||||
'/%p/e',
|
||||
'/%R/e',
|
||||
'/%S/e',
|
||||
'/%t/',
|
||||
'/%T/e',
|
||||
'/%x/e',
|
||||
'/%X/e',
|
||||
'/%y/e',
|
||||
'/%Y/',
|
||||
'/%%/'),
|
||||
array('$this->_strftime(NLS::getLangInfo(constant(\'ABMON_\' . (int)$this->month)))',
|
||||
'$this->_strftime(NLS::getLangInfo(constant(\'MON_\' . (int)$this->month)))',
|
||||
'(int)($this->year / 100)',
|
||||
'sprintf(\'%02d\', $this->mday)',
|
||||
'$this->_strftime(\'%m/%d/%y\')',
|
||||
'sprintf(\'%2d\', $this->mday)',
|
||||
'sprintf(\'%02d\', $this->hour)',
|
||||
'sprintf(\'%02d\', $this->hour == 0 ? 12 : ($this->hour > 12 ? $this->hour - 12 : $this->hour))',
|
||||
'sprintf(\'%02d\', $this->month)',
|
||||
'sprintf(\'%02d\', $this->min)',
|
||||
"\n",
|
||||
'$this->_strftime(NLS::getLangInfo($this->hour < 12 ? AM_STR : PM_STR))',
|
||||
'$this->_strftime(\'%H:%M\')',
|
||||
'sprintf(\'%02d\', $this->sec)',
|
||||
"\t",
|
||||
'$this->_strftime(\'%H:%M:%S\')',
|
||||
'$this->_strftime(NLS::getLangInfo(D_FMT))',
|
||||
'$this->_strftime(NLS::getLangInfo(T_FMT))',
|
||||
'substr(sprintf(\'%04d\', $this->year), -2)',
|
||||
(int)$this->year,
|
||||
'%'),
|
||||
$format);
|
||||
}
|
||||
|
||||
/**
|
||||
* mktime() implementation that supports dates outside of 1970-2038,
|
||||
* from http://phplens.com/phpeverywhere/adodb_date_library.
|
||||
*
|
||||
* @TODO remove in Horde 4
|
||||
*
|
||||
* This does NOT work with pre-1970 daylight saving times.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
function _mktime($hr, $min, $sec, $mon = false, $day = false,
|
||||
$year = false, $is_dst = false, $is_gmt = false)
|
||||
{
|
||||
if ($mon === false) {
|
||||
return $is_gmt
|
||||
? @gmmktime($hr, $min, $sec)
|
||||
: @mktime($hr, $min, $sec);
|
||||
}
|
||||
|
||||
if ($year > 1901 && $year < 2038 &&
|
||||
($year >= 1970 || version_compare(PHP_VERSION, '5.0.0', '>='))) {
|
||||
return $is_gmt
|
||||
? @gmmktime($hr, $min, $sec, $mon, $day, $year)
|
||||
: @mktime($hr, $min, $sec, $mon, $day, $year);
|
||||
}
|
||||
|
||||
$gmt_different = $is_gmt
|
||||
? 0
|
||||
: (mktime(0, 0, 0, 1, 2, 1970, 0) - gmmktime(0, 0, 0, 1, 2, 1970, 0));
|
||||
|
||||
$mon = intval($mon);
|
||||
$day = intval($day);
|
||||
$year = intval($year);
|
||||
|
||||
if ($mon > 12) {
|
||||
$y = floor($mon / 12);
|
||||
$year += $y;
|
||||
$mon -= $y * 12;
|
||||
} elseif ($mon < 1) {
|
||||
$y = ceil((1 - $mon) / 12);
|
||||
$year -= $y;
|
||||
$mon += $y * 12;
|
||||
}
|
||||
|
||||
$_day_power = 86400;
|
||||
$_hour_power = 3600;
|
||||
$_min_power = 60;
|
||||
|
||||
$_month_table_normal = array('', 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
|
||||
$_month_table_leaf = array('', 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
|
||||
|
||||
$_total_date = 0;
|
||||
if ($year >= 1970) {
|
||||
for ($a = 1970; $a <= $year; $a++) {
|
||||
$leaf = Horde_Date::isLeapYear($a);
|
||||
if ($leaf == true) {
|
||||
$loop_table = $_month_table_leaf;
|
||||
$_add_date = 366;
|
||||
} else {
|
||||
$loop_table = $_month_table_normal;
|
||||
$_add_date = 365;
|
||||
}
|
||||
if ($a < $year) {
|
||||
$_total_date += $_add_date;
|
||||
} else {
|
||||
for ($b = 1; $b < $mon; $b++) {
|
||||
$_total_date += $loop_table[$b];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ($_total_date + $day - 1) * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different;
|
||||
}
|
||||
|
||||
for ($a = 1969 ; $a >= $year; $a--) {
|
||||
$leaf = Horde_Date::isLeapYear($a);
|
||||
if ($leaf == true) {
|
||||
$loop_table = $_month_table_leaf;
|
||||
$_add_date = 366;
|
||||
} else {
|
||||
$loop_table = $_month_table_normal;
|
||||
$_add_date = 365;
|
||||
}
|
||||
if ($a > $year) {
|
||||
$_total_date += $_add_date;
|
||||
} else {
|
||||
for ($b = 12; $b > $mon; $b--) {
|
||||
$_total_date += $loop_table[$b];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$_total_date += $loop_table[$mon] - $day;
|
||||
$_day_time = $hr * $_hour_power + $min * $_min_power + $sec;
|
||||
$_day_time = $_day_power - $_day_time;
|
||||
$ret = -($_total_date * $_day_power + $_day_time - $gmt_different);
|
||||
if ($ret < -12220185600) {
|
||||
// If earlier than 5 Oct 1582 - gregorian correction.
|
||||
return $ret + 10 * 86400;
|
||||
} elseif ($ret < -12219321600) {
|
||||
// If in limbo, reset to 15 Oct 1582.
|
||||
return -12219321600;
|
||||
} else {
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
|
||||
require_once(dirname(__FILE__) . '/Horde_Date.php');
|
||||
|
||||
// {{{ Header
|
||||
|
||||
|
|
3289
plugins/calendar/lib/Horde_iCalendar.php
Normal file
3289
plugins/calendar/lib/Horde_iCalendar.php
Normal file
File diff suppressed because it is too large
Load diff
|
@ -124,7 +124,10 @@ class calendar_ical
|
|||
private function get_parser()
|
||||
{
|
||||
// use Horde:iCalendar to parse vcalendar file format
|
||||
require_once 'Horde/iCalendar.php';
|
||||
@include_once('Horde/iCalendar.php');
|
||||
|
||||
if (!class_exists('Horde_iCalendar'))
|
||||
require_once($this->cal->home . '/lib/Horde_iCalendar.php');
|
||||
|
||||
// set target charset for parsed events
|
||||
$GLOBALS['_HORDE_STRING_CHARSET'] = RCMAIL_CHARSET;
|
||||
|
|
|
@ -239,10 +239,11 @@ class calendar_itip
|
|||
if ($stored[$base])
|
||||
return $token;
|
||||
|
||||
// @TODO: REPLACE works only with MySQL
|
||||
// delete old entry
|
||||
$this->rc->db->query("DELETE FROM itipinvitations WHERE token=?", $base);
|
||||
|
||||
$query = $this->rc->db->query(
|
||||
"REPLACE INTO itipinvitations
|
||||
"INSERT INTO itipinvitations
|
||||
(token, event_uid, user_id, event, expires)
|
||||
VALUES(?, ?, ?, ?, ?)",
|
||||
$base,
|
||||
|
|
|
@ -450,17 +450,16 @@ class calendar_ui
|
|||
$select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-monthly'));
|
||||
$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'));
|
||||
$checkbox = new html_checkbox(array('name' => 'bymonthday', 'class' => 'edit-recurrence-monthly-bymonthday'));
|
||||
for ($monthdays = '', $d = 1; $d <= 31; $d++) {
|
||||
$monthdays .= html::label(array('class' => 'monthday'), $checkbox->show('', array('value' => $d)) . $d);
|
||||
$monthdays .= $d % 7 ? ' ' : html::br();
|
||||
}
|
||||
*/
|
||||
|
||||
// rule selectors
|
||||
$radio = new html_radiobutton(array('name' => 'repeatmode', 'class' => 'edit-recurrence-monthly-mode'));
|
||||
$table = new html_table(array('cols' => 2, 'border' => 0, 'cellpadding' => 0, 'class' => 'formtable'));
|
||||
$table->add('label', html::label(null, $radio->show('BYMONTHDAY', array('value' => 'BYMONTHDAY')) . ' ' . $this->cal->gettext('onsamedate'))); // $this->cal->gettext('each')
|
||||
$table->add('label', html::label(null, $radio->show('BYMONTHDAY', array('value' => 'BYMONTHDAY')) . ' ' . $this->cal->gettext('each')));
|
||||
$table->add(null, $monthdays);
|
||||
$table->add('label', html::label(null, $radio->show('', array('value' => 'BYDAY')) . ' ' . $this->cal->gettext('onevery')));
|
||||
$table->add(null, $this->rrule_selectors($attrib['part']));
|
||||
|
@ -475,8 +474,7 @@ class calendar_ui
|
|||
$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->cal->driver, 'kolab_driver') ? 'radio' : 'checkbox';
|
||||
$checkbox = new html_inputfield(array('type' => $boxtype, 'name' => 'bymonth', 'class' => 'edit-recurrence-yearly-bymonth'));
|
||||
$checkbox = new html_checkbox(array('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->cal->gettext($monthmap[$m]));
|
||||
$months .= $m % 4 ? ' ' : html::br();
|
||||
|
@ -538,13 +536,10 @@ class calendar_ui
|
|||
$this->cal->gettext('first'),
|
||||
$this->cal->gettext('second'),
|
||||
$this->cal->gettext('third'),
|
||||
$this->cal->gettext('fourth')
|
||||
$this->cal->gettext('fourth'),
|
||||
$this->cal->gettext('last')
|
||||
),
|
||||
array(1, 2, 3, 4));
|
||||
|
||||
// Kolab doesn't support 'last' but others do.
|
||||
if (!is_a($this->cal->driver, 'kolab_driver'))
|
||||
$select_prefix->add($this->cal->gettext('last'), -1);
|
||||
array(1, 2, 3, 4, -1));
|
||||
|
||||
$select_wday = new html_select(array('name' => 'byday', 'id' => "edit-recurrence-$part-byday"));
|
||||
if ($noselect) $select_wday->add($noselect, '');
|
||||
|
@ -555,8 +550,6 @@ class calendar_ui
|
|||
$d = $j % 7;
|
||||
$select_wday->add($this->cal->gettext($daymap[$d]), strtoupper(substr($daymap[$d], 0, 2)));
|
||||
}
|
||||
if ($part == 'monthly')
|
||||
$select_wday->add($this->cal->gettext('dayofmonth'), '');
|
||||
|
||||
return $select_prefix->show() . ' ' . $select_wday->show();
|
||||
}
|
||||
|
@ -664,13 +657,13 @@ class calendar_ui
|
|||
|
||||
if (!empty($this->cal->attachment['name'])) {
|
||||
$table->add('title', Q(rcube_label('filename')));
|
||||
$table->add(null, Q($this->cal->attachment['name']));
|
||||
$table->add(null, '[' . html::a('?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), Q(rcube_label('download'))) . ']');
|
||||
$table->add('header', Q($this->cal->attachment['name']));
|
||||
$table->add('download-link', html::a('?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), Q(rcube_label('download'))));
|
||||
}
|
||||
|
||||
if (!empty($this->cal->attachment['size'])) {
|
||||
$table->add('title', Q(rcube_label('filesize')));
|
||||
$table->add(null, Q(show_bytes($this->cal->attachment['size'])));
|
||||
$table->add('header', Q(show_bytes($this->cal->attachment['size'])));
|
||||
}
|
||||
|
||||
return $table->show($attrib);
|
||||
|
|
31
plugins/calendar/lib/get_horde_icalendar.sh
Executable file
31
plugins/calendar/lib/get_horde_icalendar.sh
Executable file
|
@ -0,0 +1,31 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copy Horde_iCalendar classes and dependencies to stdout.
|
||||
# This will create a standalone copy of the classes requried for iCal parsing.
|
||||
|
||||
SRCDIR=$1
|
||||
|
||||
if [ ! -d "$SRCDIR" ]; then
|
||||
echo "Usage: get_horde_icalendar.sh SRCDIR"
|
||||
echo "Please enter a valid source directory of the Horde lib"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "<?php
|
||||
|
||||
/**
|
||||
* This is a concatenated copy of the following files:
|
||||
* Horde/String.php, Horde/iCalendar.php, Horde/iCalendar/*.php
|
||||
* Pull the latest version of these file from the PEAR channel of the Horde project at http://pear.horde.org
|
||||
*/
|
||||
|
||||
require_once(dirname(__FILE__) . '/Horde_Date.php');"
|
||||
|
||||
sed 's/<?php//; s/?>//' $SRCDIR/String.php
|
||||
echo "\n"
|
||||
sed 's/<?php//; s/?>//' $SRCDIR/iCalendar.php | sed -E "s/include_once.+//; s/NLS::getCharset\(\)/'UTF-8'/"
|
||||
echo "\n"
|
||||
|
||||
for fn in `ls $SRCDIR/iCalendar/*.php | grep -v 'vcard.php'`; do
|
||||
sed 's/<?php//; s/?>//' $fn | sed -E "s/(include|require)_once.+//"
|
||||
done;
|
|
@ -4,7 +4,7 @@
|
|||
http://pear.php.net/dtd/package-2.0
|
||||
http://pear.php.net/dtd/package-2.0.xsd">
|
||||
<name>calendar</name>
|
||||
<uri>http://git.kolab.org/roundcube-plugins-kolab/</uri>
|
||||
<uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri>
|
||||
<summary>Calendar plugin</summary>
|
||||
<description>-</description>
|
||||
<lead>
|
||||
|
@ -64,10 +64,9 @@
|
|||
<tasks:replace from="@name@" to="name" type="package-info"/>
|
||||
<tasks:replace from="@package_version@" to="version" type="package-info"/>
|
||||
</file>
|
||||
<file name="lib/Horde_Date_Recurrence.php" role="php">
|
||||
<tasks:replace from="@name@" to="name" type="package-info"/>
|
||||
<tasks:replace from="@package_version@" to="version" type="package-info"/>
|
||||
</file>
|
||||
<file name="lib/Horde_Date.php" role="php"></file>
|
||||
<file name="lib/Horde_Date_Recurrence.php" role="php"></file>
|
||||
<file name="lib/Horde_iCalendar.php" role="php"></file>
|
||||
<file name="lib/fullcalendar-rc.patch" role="data">
|
||||
<tasks:replace from="@name@" to="name" type="package-info"/>
|
||||
<tasks:replace from="@package_version@" to="version" type="package-info"/>
|
||||
|
@ -157,6 +156,7 @@
|
|||
|
||||
<file name="config.inc.php.dist" role="data"></file>
|
||||
<file name="LICENSE" role="data"></file>
|
||||
<file name="README" role="data"></file>
|
||||
<file name="TODO" role="data"></file>
|
||||
|
||||
<file name="localization/bg_BG.inc" role="data"></file>
|
||||
|
|
|
@ -286,39 +286,45 @@ a.miniColors-trigger {
|
|||
|
||||
#attachmentcontainer {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
top: 60px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
#attachmentframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid #999999;
|
||||
background-color: #F9F9F9;
|
||||
border: 0;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#partheader {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 220px;
|
||||
right: 20px;
|
||||
height: 40px;
|
||||
position: relative;
|
||||
padding: 3px 0;
|
||||
background: #f9f9f9;
|
||||
background: -moz-linear-gradient(top, #fff 0%, #e9e9e9 100%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#fff), color-stop(100%,#e9e9e9));
|
||||
background: -o-linear-gradient(top, #fff 0%, #e9e9e9 100%);
|
||||
background: -ms-linear-gradient(top, #fff 0%, #e9e9e9 100%);
|
||||
background: linear-gradient(top, #fff 0%, #e9e9e9 100%);
|
||||
}
|
||||
|
||||
#partheader table td {
|
||||
padding-left: 2px;
|
||||
padding-right: 4px;
|
||||
vertical-align: middle;
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
#partheader table td.title {
|
||||
color: #666;
|
||||
#partheader table td.header {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#partheader table td.title a {
|
||||
color: #666;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#edit-attachments {
|
||||
margin-top: 0.6em;
|
||||
}
|
||||
|
@ -502,8 +508,8 @@ td.topalign {
|
|||
.event-update-confirm .message {
|
||||
margin-top: 0.5em;
|
||||
padding: 0.8em;
|
||||
background-color: #F7FDCB;
|
||||
border: 1px solid #C2D071;
|
||||
border: 1px solid #ffdf0e;
|
||||
background-color: #fef893;
|
||||
}
|
||||
|
||||
.event-dialog-message .message,
|
||||
|
@ -540,8 +546,6 @@ td.topalign {
|
|||
#edit-attendees-notify {
|
||||
margin: 0.3em 0;
|
||||
padding: 0.5em;
|
||||
border: 1px solid #ffdf0e;
|
||||
background-color: #fef893;
|
||||
}
|
||||
|
||||
#edit-attendees-table {
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
</div>
|
||||
|
||||
<div id="attachmentcontainer" class="uibox">
|
||||
<roundcube:object name="plugin.attachmentframe" id="attachmentframe" style="width:100%; height:100%" />
|
||||
<roundcube:object name="plugin.attachmentframe" id="attachmentframe" class="header-table" style="width:100%" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -51,7 +51,7 @@ class kolab_addressbook extends rcube_plugin
|
|||
$this->rc = rcmail::get_instance();
|
||||
|
||||
// load required plugin
|
||||
$this->require_plugin('kolab_core');
|
||||
$this->require_plugin('libkolab');
|
||||
|
||||
// register hooks
|
||||
$this->add_hook('addressbooks_list', array($this, 'address_sources'));
|
||||
|
@ -245,7 +245,7 @@ class kolab_addressbook extends rcube_plugin
|
|||
}
|
||||
|
||||
// get all folders that have "contact" type
|
||||
$this->folders = rcube_kolab::get_folders('contact');
|
||||
$this->folders = kolab_storage::get_folders('contact');
|
||||
|
||||
if (PEAR::isError($this->folders)) {
|
||||
raise_error(array(
|
||||
|
@ -264,7 +264,7 @@ class kolab_addressbook extends rcube_plugin
|
|||
|
||||
foreach ($names as $utf7name => $name) {
|
||||
// create instance of rcube_contacts
|
||||
$abook_id = rcube_kolab::folder_id($utf7name);
|
||||
$abook_id = kolab_storage::folder_id($utf7name);
|
||||
$abook = new rcube_kolab_contacts($utf7name);
|
||||
$this->sources[$abook_id] = $abook;
|
||||
}
|
||||
|
@ -289,12 +289,11 @@ class kolab_addressbook extends rcube_plugin
|
|||
|
||||
// extend the list of contact fields to be displayed in the 'personal' section
|
||||
if (is_array($p['form']['personal'])) {
|
||||
$p['form']['contact']['content']['officelocation'] = array('size' => 40);
|
||||
$p['form']['personal']['content']['initials'] = array('size' => 6);
|
||||
$p['form']['personal']['content']['profession'] = array('size' => 40);
|
||||
$p['form']['personal']['content']['children'] = array('size' => 40);
|
||||
$p['form']['personal']['content']['pgppublickey'] = array('size' => 40);
|
||||
$p['form']['personal']['content']['freebusyurl'] = array('size' => 40);
|
||||
$p['form']['personal']['content']['pgppublickey'] = array('size' => 70);
|
||||
$p['form']['personal']['content']['pkcs7publickey'] = array('size' => 70);
|
||||
|
||||
// re-order fields according to the coltypes list
|
||||
$p['form']['contact']['content'] = $this->_sort_form_fields($p['form']['contact']['content']);
|
||||
|
@ -304,8 +303,9 @@ class kolab_addressbook extends rcube_plugin
|
|||
$p['form']['settings'] = array(
|
||||
'name' => $this->gettext('settings'),
|
||||
'content' => array(
|
||||
'pgppublickey' => array('size' => 40, 'visible' => true),
|
||||
'freebusyurl' => array('size' => 40, 'visible' => true),
|
||||
'pgppublickey' => array('size' => 70, 'visible' => true),
|
||||
'pkcs7publickey' => array('size' => 70, 'visible' => false),
|
||||
)
|
||||
);
|
||||
*/
|
||||
|
@ -481,7 +481,7 @@ class kolab_addressbook extends rcube_plugin
|
|||
|
||||
if (!$plugin['abort']) {
|
||||
if ($oldfolder != $folder)
|
||||
$result = rcube_kolab::folder_rename($oldfolder, $folder);
|
||||
$result = kolab_storage::folder_rename($oldfolder, $folder);
|
||||
else
|
||||
$result = true;
|
||||
}
|
||||
|
@ -497,7 +497,7 @@ class kolab_addressbook extends rcube_plugin
|
|||
$folder = $plugin['name'];
|
||||
|
||||
if (!$plugin['abort']) {
|
||||
$result = rcube_kolab::folder_create($folder, 'contact', false);
|
||||
$result = kolab_storage::folder_create($folder, 'contact');
|
||||
}
|
||||
else {
|
||||
$result = $plugin['result'];
|
||||
|
@ -545,7 +545,7 @@ class kolab_addressbook extends rcube_plugin
|
|||
$this->rc->output->show_message('kolab_addressbook.book'.$type.'d', 'confirmation');
|
||||
$this->rc->output->command('set_env', 'delimiter', $delimiter);
|
||||
$this->rc->output->command('book_update', array(
|
||||
'id' => rcube_kolab::folder_id($folder),
|
||||
'id' => kolab_storage::folder_id($folder),
|
||||
'name' => $name,
|
||||
'readonly' => false,
|
||||
'editable' => true,
|
||||
|
@ -553,7 +553,7 @@ class kolab_addressbook extends rcube_plugin
|
|||
'realname' => rcube_charset::convert($folder, 'UTF7-IMAP'), // IMAP folder name
|
||||
'class_name' => $kolab_folder->get_namespace(),
|
||||
'kolab' => true,
|
||||
), rcube_kolab::folder_id($oldfolder));
|
||||
), kolab_storage::folder_id($oldfolder));
|
||||
|
||||
$this->rc->output->send('iframe');
|
||||
}
|
||||
|
@ -574,12 +574,12 @@ class kolab_addressbook extends rcube_plugin
|
|||
{
|
||||
$folder = trim(get_input_value('_source', RCUBE_INPUT_GPC, true, 'UTF7-IMAP'));
|
||||
|
||||
if (rcube_kolab::folder_delete($folder)) {
|
||||
if (kolab_storage::folder_delete($folder)) {
|
||||
$this->rc->output->show_message('kolab_addressbook.bookdeleted', 'confirmation');
|
||||
$this->rc->output->set_env('pagecount', 0);
|
||||
$this->rc->output->command('set_rowcount', rcmail_get_rowcount_text(new rcube_result_set()));
|
||||
$this->rc->output->command('list_contacts_clear');
|
||||
$this->rc->output->command('book_delete_done', rcube_kolab::folder_id($folder));
|
||||
$this->rc->output->command('book_delete_done', kolab_storage::folder_id($folder));
|
||||
}
|
||||
else {
|
||||
$this->rc->output->show_message('kolab_addressbook.bookdeleteerror', 'error');
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* @author Aleksander Machniak <machniak@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2011, Kolab Systems AG <contact@kolabsys.com>
|
||||
* Copyright (C) 2012, 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
|
||||
|
@ -144,8 +144,8 @@ class kolab_addressbook_ui
|
|||
if (strlen($folder)) {
|
||||
$hidden_fields[] = array('name' => '_oldname', 'value' => $folder);
|
||||
|
||||
$this->rc->imap_connect();
|
||||
$options = $this->rc->imap->mailbox_info($folder);
|
||||
$this->rc->storage_connect();
|
||||
$options = $this->rc->get_storage()->mailbox_info($folder);
|
||||
}
|
||||
|
||||
$form = array();
|
||||
|
@ -156,7 +156,7 @@ class kolab_addressbook_ui
|
|||
);
|
||||
|
||||
if (!empty($options) && ($options['norename'] || $options['protected'])) {
|
||||
$foldername = Q(str_replace($delimiter, ' » ', rcube_kolab::object_name($folder)));
|
||||
$foldername = Q(str_replace($delimiter, ' » ', kolab_storage::object_name($folder)));
|
||||
}
|
||||
else {
|
||||
$foldername = new html_inputfield(array('name' => '_name', 'id' => '_name', 'size' => 30));
|
||||
|
@ -178,7 +178,7 @@ class kolab_addressbook_ui
|
|||
$hidden_fields[] = array('name' => '_parent', 'value' => $path_imap);
|
||||
}
|
||||
else {
|
||||
$select = rcube_kolab::folder_selector('contact', array('name' => '_parent'), $folder);
|
||||
$select = kolab_storage::folder_selector('contact', array('name' => '_parent'), $folder);
|
||||
|
||||
$form['props']['fieldsets']['location']['content']['path'] = array(
|
||||
'label' => $this->plugin->gettext('parentbook'),
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* Backend class for a custom address book
|
||||
*
|
||||
* This part of the Roundcube+Kolab integration and connects the
|
||||
* rcube_addressbook interface with the rcube_kolab wrapper for Kolab_Storage
|
||||
* rcube_addressbook interface with the kolab_storage wrapper from libkolab
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
* @author Aleksander Machniak <machniak@kolabsys.com>
|
||||
|
@ -46,30 +46,28 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
'department' => array('limit' => 1),
|
||||
'email' => array('subtypes' => null),
|
||||
'phone' => array(),
|
||||
'address' => array('limit' => 2, 'subtypes' => array('home','business')),
|
||||
'officelocation' => array('type' => 'text', 'size' => 40, 'maxlength' => 50, 'limit' => 1,
|
||||
'label' => 'kolab_addressbook.officelocation', 'category' => 'main'),
|
||||
'website' => array('limit' => 1, 'subtypes' => null),
|
||||
'im' => array('limit' => 1, 'subtypes' => null),
|
||||
'address' => array('subtypes' => array('home','work','office')),
|
||||
'website' => array('subtypes' => array('homepage','blog')),
|
||||
'im' => array('subtypes' => null),
|
||||
'gender' => array('limit' => 1),
|
||||
'initials' => array('type' => 'text', 'size' => 6, 'maxlength' => 10, 'limit' => 1,
|
||||
'label' => 'kolab_addressbook.initials', 'category' => 'personal'),
|
||||
'birthday' => array('limit' => 1),
|
||||
'anniversary' => array('limit' => 1),
|
||||
'profession' => array('type' => 'text', 'size' => 40, 'maxlength' => 80, 'limit' => 1,
|
||||
'label' => 'kolab_addressbook.profession', 'category' => 'personal'),
|
||||
'manager' => array('limit' => 1),
|
||||
'assistant' => array('limit' => 1),
|
||||
'manager' => array('limit' => null),
|
||||
'assistant' => array('limit' => null),
|
||||
'spouse' => array('limit' => 1),
|
||||
'children' => array('type' => 'text', 'size' => 40, 'maxlength' => 80, 'limit' => 1,
|
||||
'children' => array('type' => 'text', 'size' => 40, 'maxlength' => 80, 'limit' => null,
|
||||
'label' => 'kolab_addressbook.children', 'category' => 'personal'),
|
||||
'pgppublickey' => array('type' => 'text', 'size' => 40, 'limit' => 1,
|
||||
'label' => 'kolab_addressbook.pgppublickey'),
|
||||
'freebusyurl' => array('type' => 'text', 'size' => 40, 'limit' => 1,
|
||||
'label' => 'kolab_addressbook.freebusyurl'),
|
||||
'pgppublickey' => array('type' => 'textarea', 'size' => 70, 'rows' => 10, 'limit' => 1,
|
||||
'label' => 'kolab_addressbook.pgppublickey'),
|
||||
'pkcs7publickey' => array('type' => 'textarea', 'size' => 70, 'rows' => 10, 'limit' => 1,
|
||||
'label' => 'kolab_addressbook.pkcs7publickey'),
|
||||
'notes' => array(),
|
||||
'photo' => array(),
|
||||
// TODO: define more Kolab-specific fields such as: language, latitude, longitude
|
||||
// TODO: define more Kolab-specific fields such as: language, latitude, longitude, crypto settings
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -86,47 +84,14 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
|
||||
private $gid;
|
||||
private $storagefolder;
|
||||
private $contactstorage;
|
||||
private $liststorage;
|
||||
private $contacts;
|
||||
private $distlists;
|
||||
private $groupmembers;
|
||||
private $id2uid;
|
||||
private $filter;
|
||||
private $result;
|
||||
private $namespace;
|
||||
private $imap_folder = 'INBOX/Contacts';
|
||||
private $gender_map = array(0 => 'male', 1 => 'female');
|
||||
private $phonetypemap = array('home' => 'home1', 'work' => 'business1', 'work2' => 'business2', 'workfax' => 'businessfax');
|
||||
private $addresstypemap = array('work' => 'business');
|
||||
private $fieldmap = array(
|
||||
// kolab => roundcube
|
||||
'full-name' => 'name',
|
||||
'given-name' => 'firstname',
|
||||
'middle-names' => 'middlename',
|
||||
'last-name' => 'surname',
|
||||
'prefix' => 'prefix',
|
||||
'suffix' => 'suffix',
|
||||
'nick-name' => 'nickname',
|
||||
'organization' => 'organization',
|
||||
'department' => 'department',
|
||||
'job-title' => 'jobtitle',
|
||||
'initials' => 'initials',
|
||||
'birthday' => 'birthday',
|
||||
'anniversary' => 'anniversary',
|
||||
'im-address' => 'im',
|
||||
'web-page' => 'website',
|
||||
'office-location' => 'officelocation',
|
||||
'profession' => 'profession',
|
||||
'manager-name' => 'manager',
|
||||
'assistant' => 'assistant',
|
||||
'spouse-name' => 'spouse',
|
||||
'children' => 'children',
|
||||
'body' => 'notes',
|
||||
'pgp-publickey' => 'pgppublickey',
|
||||
'free-busy-url' => 'freebusyurl',
|
||||
'gender' => 'gender',
|
||||
);
|
||||
private $action;
|
||||
|
||||
|
||||
public function __construct($imap_folder = null)
|
||||
|
@ -136,9 +101,9 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
}
|
||||
|
||||
// extend coltypes configuration
|
||||
$format = rcube_kolab::get_format('contact');
|
||||
$this->coltypes['phone']['subtypes'] = $format->_phone_types;
|
||||
$this->coltypes['address']['subtypes'] = $format->_address_types;
|
||||
$format = kolab_format::factory('contact');
|
||||
$this->coltypes['phone']['subtypes'] = array_keys($format->phonetypes);
|
||||
$this->coltypes['address']['subtypes'] = array_keys($format->addresstypes);
|
||||
|
||||
// set localized labels for proprietary cols
|
||||
foreach ($this->coltypes as $col => $prop) {
|
||||
|
@ -147,17 +112,17 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
}
|
||||
|
||||
// fetch objects from the given IMAP folder
|
||||
$this->storagefolder = rcube_kolab::get_folder($this->imap_folder);
|
||||
$this->ready = !PEAR::isError($this->storagefolder);
|
||||
$this->storagefolder = kolab_storage::get_folder($this->imap_folder);
|
||||
$this->ready = $this->storagefolder && !PEAR::isError($this->storagefolder);
|
||||
|
||||
// Set readonly and editable flags according to folder permissions
|
||||
if ($this->ready) {
|
||||
if ($this->get_owner() == $_SESSION['username']) {
|
||||
if ($this->storagefolder->get_owner() == $_SESSION['username']) {
|
||||
$this->editable = true;
|
||||
$this->readonly = false;
|
||||
}
|
||||
else {
|
||||
$rights = $this->storagefolder->getMyRights();
|
||||
$rights = $this->storagefolder->get_myrights();
|
||||
if (!PEAR::isError($rights)) {
|
||||
if (strpos($rights, 'i') !== false)
|
||||
$this->readonly = false;
|
||||
|
@ -166,6 +131,8 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->action = rcmail::get_instance()->action;
|
||||
}
|
||||
|
||||
|
||||
|
@ -176,7 +143,7 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
*/
|
||||
public function get_name()
|
||||
{
|
||||
$folder = rcube_kolab::object_name($this->imap_folder, $this->namespace);
|
||||
$folder = kolab_storage::object_name($this->imap_folder, $this->namespace);
|
||||
return $folder;
|
||||
}
|
||||
|
||||
|
@ -192,17 +159,6 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for the IMAP folder owner
|
||||
*
|
||||
* @return string Name of the folder owner
|
||||
*/
|
||||
public function get_owner()
|
||||
{
|
||||
return $this->storagefolder->getOwner();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for the name of the namespace to which the IMAP folder belongs
|
||||
*
|
||||
|
@ -210,8 +166,8 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
*/
|
||||
public function get_namespace()
|
||||
{
|
||||
if ($this->namespace === null) {
|
||||
$this->namespace = rcube_kolab::folder_namespace($this->imap_folder);
|
||||
if ($this->namespace === null && $this->ready) {
|
||||
$this->namespace = $this->storagefolder->get_namespace();
|
||||
}
|
||||
|
||||
return $this->namespace;
|
||||
|
@ -270,8 +226,8 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
$this->_fetch_groups();
|
||||
$groups = array();
|
||||
foreach ((array)$this->distlists as $group) {
|
||||
if (!$search || strstr(strtolower($group['last-name']), strtolower($search)))
|
||||
$groups[$group['last-name']] = array('ID' => $group['ID'], 'name' => $group['last-name']);
|
||||
if (!$search || strstr(strtolower($group['name']), strtolower($search)))
|
||||
$groups[$group['name']] = array('ID' => $group['ID'], 'name' => $group['name']);
|
||||
}
|
||||
|
||||
// sort groups
|
||||
|
@ -290,23 +246,38 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
*/
|
||||
public function list_records($cols=null, $subset=0)
|
||||
{
|
||||
$this->result = $this->count();
|
||||
$this->result = new rcube_result_set(0, ($this->list_page-1) * $this->page_size);;
|
||||
|
||||
// list member of the selected group
|
||||
if ($this->gid) {
|
||||
$this->_fetch_groups();
|
||||
$seen = array();
|
||||
$this->result->count = 0;
|
||||
foreach ((array)$this->distlists[$this->gid]['member'] as $member) {
|
||||
// skip member that don't match the search filter
|
||||
if (is_array($this->filter['ids']) && array_search($member['ID'], $this->filter['ids']) === false)
|
||||
continue;
|
||||
|
||||
if ($this->contacts[$member['ID']] && !$seen[$member['ID']]++)
|
||||
if ($member['uid'] && ($contact = $this->storagefolder->get_object($member['uid'])) && !$seen[$member['ID']]++) {
|
||||
$this->contacts[$member['ID']] = $this->_to_rcube_contact($contact);
|
||||
$this->result->count++;
|
||||
}
|
||||
else if ($member['email'] && !$seen[$member['ID']]++) {
|
||||
$this->contacts[$member['ID']] = $member;
|
||||
$this->result->count++;
|
||||
}
|
||||
}
|
||||
$ids = array_keys($seen);
|
||||
}
|
||||
else
|
||||
$ids = is_array($this->filter['ids']) ? $this->filter['ids'] : array_keys($this->contacts);
|
||||
else if (is_array($this->filter['ids'])) {
|
||||
$ids = $this->filter['ids'];
|
||||
if ($this->result->count = count($ids))
|
||||
$this->_fetch_contacts(array(array('uid', '=', $ids)));
|
||||
}
|
||||
else {
|
||||
$this->_fetch_contacts();
|
||||
$ids = array_keys($this->contacts);
|
||||
$this->result->count = count($ids);
|
||||
}
|
||||
|
||||
// sort data arrays according to desired list sorting
|
||||
if ($count = count($ids)) {
|
||||
|
@ -348,8 +319,6 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
*/
|
||||
public function search($fields, $value, $mode=0, $select=true, $nocount=false, $required=array())
|
||||
{
|
||||
$this->_fetch_contacts();
|
||||
|
||||
// search by ID
|
||||
if ($fields == $this->primary_key) {
|
||||
$ids = !is_array($value) ? explode(',', $value) : $value;
|
||||
|
@ -384,6 +353,25 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
// build key name regexp
|
||||
$regexp = '/^(' . implode($fields, '|') . ')(?:.*)$/';
|
||||
|
||||
// pass query to storage if only indexed cols are involved
|
||||
// NOTE: this is only some rough pre-filtering but probably includes false positives
|
||||
$squery = array();
|
||||
if (count(array_intersect(kolab_format_contact::$fulltext_cols, $fields)) == $scount) {
|
||||
switch ($mode) {
|
||||
case 1: $prefix = ' '; $suffix = ' '; break; // strict
|
||||
case 2: $prefix = ' '; $suffix = ''; break; // prefix
|
||||
default: $prefix = ''; $suffix = ''; break; // substring
|
||||
}
|
||||
|
||||
$search_string = is_array($value) ? join(' ', $value) : $value;
|
||||
foreach (rcube_utils::normalize_string($search_string, true) as $word) {
|
||||
$squery[] = array('words', 'LIKE', '%' . $prefix . $word . $suffix . '%');
|
||||
}
|
||||
}
|
||||
|
||||
// get all/matching records
|
||||
$this->_fetch_contacts($squery);
|
||||
|
||||
// save searching conditions
|
||||
$this->filter = array('fields' => $fields, 'value' => $value, 'mode' => $mode, 'ids' => array());
|
||||
|
||||
|
@ -408,17 +396,19 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
}
|
||||
|
||||
foreach ((array)$contact[$col] as $val) {
|
||||
$val = mb_strtolower($val);
|
||||
switch ($mode) {
|
||||
case 1:
|
||||
$got = ($val == $search);
|
||||
break;
|
||||
case 2:
|
||||
$got = ($search == substr($val, 0, strlen($search)));
|
||||
break;
|
||||
default:
|
||||
$got = (strpos($val, $search) !== false);
|
||||
break;
|
||||
foreach ((array)$val as $str) {
|
||||
$str = mb_strtolower($str);
|
||||
switch ($mode) {
|
||||
case 1:
|
||||
$got = ($str == $search);
|
||||
break;
|
||||
case 2:
|
||||
$got = ($search == substr($str, 0, strlen($search)));
|
||||
break;
|
||||
default:
|
||||
$got = (strpos($str, $search) !== false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($got) {
|
||||
|
@ -461,9 +451,17 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
*/
|
||||
public function count()
|
||||
{
|
||||
$this->_fetch_contacts();
|
||||
$this->_fetch_groups();
|
||||
$count = $this->gid ? count($this->distlists[$this->gid]['member']) : (is_array($this->filter['ids']) ? count($this->filter['ids']) : count($this->contacts));
|
||||
if ($this->gid) {
|
||||
$this->_fetch_groups();
|
||||
$count = count($this->distlists[$this->gid]['member']);
|
||||
}
|
||||
else if (is_array($this->filter['ids'])) {
|
||||
$count = count($this->filter['ids']);
|
||||
}
|
||||
else {
|
||||
$count = $this->storagefolder->count();
|
||||
}
|
||||
|
||||
return new rcube_result_set($count, ($this->list_page-1) * $this->page_size);
|
||||
}
|
||||
|
||||
|
@ -488,11 +486,21 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
*/
|
||||
public function get_record($id, $assoc=false)
|
||||
{
|
||||
$this->_fetch_contacts();
|
||||
if ($this->contacts[$id]) {
|
||||
$rec = null;
|
||||
$uid = $this->_id2uid($id);
|
||||
if (strpos($uid, 'mailto:') === 0) {
|
||||
$this->_fetch_groups(true);
|
||||
$rec = $this->contacts[$id];
|
||||
$this->readonly = true; // set source to read-only
|
||||
}
|
||||
else if ($object = $this->storagefolder->get_object($uid)) {
|
||||
$rec = $this->_to_rcube_contact($object);
|
||||
}
|
||||
|
||||
if ($rec) {
|
||||
$this->result = new rcube_result_set(1);
|
||||
$this->result->add($this->contacts[$id]);
|
||||
return $assoc ? $this->contacts[$id] : $this->result;
|
||||
$this->result->add($rec);
|
||||
return $assoc ? $rec : $this->result;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -512,7 +520,7 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
|
||||
foreach ((array)$this->groupmembers[$id] as $gid) {
|
||||
if ($group = $this->distlists[$gid])
|
||||
$out[$gid] = $group['last-name'];
|
||||
$out[$gid] = $group['name'];
|
||||
}
|
||||
|
||||
return $out;
|
||||
|
@ -546,26 +554,21 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
}
|
||||
|
||||
if (!$existing) {
|
||||
$this->_connect();
|
||||
|
||||
// generate new Kolab contact item
|
||||
$object = $this->_from_rcube_contact($save_data);
|
||||
$object['uid'] = $this->contactstorage->generateUID();
|
||||
$saved = $this->storagefolder->save($object, 'contact');
|
||||
|
||||
$saved = $this->contactstorage->save($object);
|
||||
|
||||
if (PEAR::isError($saved)) {
|
||||
if (!$saved) {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Error saving contact object to Kolab server:" . $saved->getMessage()),
|
||||
'message' => "Error saving contact object to Kolab server"),
|
||||
true, false);
|
||||
}
|
||||
else {
|
||||
$contact = $this->_to_rcube_contact($object);
|
||||
$id = $contact['ID'];
|
||||
$this->contacts[$id] = $contact;
|
||||
$this->id2uid[$id] = $object['uid'];
|
||||
$insert_id = $id;
|
||||
}
|
||||
}
|
||||
|
@ -586,22 +589,21 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
public function update($id, $save_data)
|
||||
{
|
||||
$updated = false;
|
||||
$this->_fetch_contacts();
|
||||
if ($this->contacts[$id] && ($uid = $this->id2uid[$id])) {
|
||||
$old = $this->contactstorage->getObject($uid);
|
||||
$object = array_merge($old, $this->_from_rcube_contact($save_data));
|
||||
if ($old = $this->storagefolder->get_object($this->_id2uid($id))) {
|
||||
$object = $this->_from_rcube_contact($save_data, $old);
|
||||
|
||||
$saved = $this->contactstorage->save($object, $uid);
|
||||
if (PEAR::isError($saved)) {
|
||||
if (!$this->storagefolder->save($object, 'contact', $old['uid'])) {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Error saving contact object to Kolab server:" . $saved->getMessage()),
|
||||
'message' => "Error saving contact object to Kolab server"),
|
||||
true, false);
|
||||
}
|
||||
else {
|
||||
$this->contacts[$id] = $this->_to_rcube_contact($object);
|
||||
$updated = true;
|
||||
|
||||
// TODO: update data in groups this contact is member of
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -619,45 +621,38 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
*/
|
||||
public function delete($ids, $force=true)
|
||||
{
|
||||
$this->_fetch_contacts();
|
||||
$this->_fetch_groups();
|
||||
|
||||
if (!is_array($ids))
|
||||
$ids = explode(',', $ids);
|
||||
|
||||
$count = 0;
|
||||
$imap_uids = array();
|
||||
|
||||
foreach ($ids as $id) {
|
||||
if ($uid = $this->id2uid[$id]) {
|
||||
$imap_uid = $this->contactstorage->_getStorageId($uid);
|
||||
$deleted = $this->contactstorage->delete($uid, $force);
|
||||
if ($uid = $this->_id2uid($id)) {
|
||||
$is_mailto = strpos($uid, 'mailto:') === 0;
|
||||
$deleted = $is_mailto || $this->storagefolder->delete($uid, $force);
|
||||
|
||||
if (PEAR::isError($deleted)) {
|
||||
if (!$deleted) {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Error deleting a contact object from the Kolab server:" . $deleted->getMessage()),
|
||||
'message' => "Error deleting a contact object $uid from the Kolab server"),
|
||||
true, false);
|
||||
}
|
||||
else {
|
||||
// remove from distribution lists
|
||||
foreach ((array)$this->groupmembers[$id] as $gid)
|
||||
$this->remove_from_group($gid, $id);
|
||||
foreach ((array)$this->groupmembers[$id] as $gid) {
|
||||
if (!$is_mailto || $gid == $this->gid)
|
||||
$this->remove_from_group($gid, $id);
|
||||
}
|
||||
|
||||
$imap_uids[$id] = $imap_uid;
|
||||
// clear internal cache
|
||||
unset($this->contacts[$id], $this->id2uid[$id], $this->groupmembers[$id]);
|
||||
unset($this->contacts[$id], $this->groupmembers[$id]);
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// store IMAP uids for undelete()
|
||||
if (!$force) {
|
||||
$_SESSION['kolab_delete_uids'] = $imap_uids;
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
|
@ -675,52 +670,22 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
if (!is_array($ids))
|
||||
$ids = explode(',', $ids);
|
||||
|
||||
$count = 0;
|
||||
$uids = array();
|
||||
$imap_uids = $_SESSION['kolab_delete_uids'];
|
||||
|
||||
// convert contact IDs into IMAP UIDs
|
||||
foreach ($ids as $id)
|
||||
if ($uid = $imap_uids[$id])
|
||||
$uids[] = $uid;
|
||||
|
||||
if (!empty($uids)) {
|
||||
$session = &Horde_Kolab_Session::singleton();
|
||||
$imap = &$session->getImap();
|
||||
|
||||
if (is_object($imap) && is_a($imap, 'PEAR_Error')) {
|
||||
$error = $imap;
|
||||
$count = 0;
|
||||
foreach ($ids as $id) {
|
||||
$uid = $this->_id2uid($id);
|
||||
if ($this->storagefolder->undelete($uid)) {
|
||||
$count++;
|
||||
}
|
||||
else {
|
||||
$result = $imap->select($this->imap_folder);
|
||||
if (is_object($result) && is_a($result, 'PEAR_Error')) {
|
||||
$error = $result;
|
||||
}
|
||||
else {
|
||||
$result = $imap->undeleteMessages(implode(',', $uids));
|
||||
if (is_object($result) && is_a($result, 'PEAR_Error')) {
|
||||
$error = $result;
|
||||
}
|
||||
else {
|
||||
$this->_connect();
|
||||
$this->contactstorage->synchronize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Error undeleting a contact object(s) from the Kolab server:" . $error->getMessage()),
|
||||
'message' => "Error undeleting a contact object $uid from the Kolab server"),
|
||||
true, false);
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$rcmail->session->remove('kolab_delete_uids');
|
||||
}
|
||||
|
||||
return count($uids);
|
||||
return $count;
|
||||
}
|
||||
|
||||
|
||||
|
@ -729,11 +694,8 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
*/
|
||||
public function delete_all()
|
||||
{
|
||||
$this->_connect();
|
||||
|
||||
if (!PEAR::isError($this->contactstorage->deleteAll())) {
|
||||
if ($this->storagefolder->delete_all()) {
|
||||
$this->contacts = array();
|
||||
$this->id2uid = array();
|
||||
$this->result = null;
|
||||
}
|
||||
}
|
||||
|
@ -760,22 +722,22 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
$result = false;
|
||||
|
||||
$list = array(
|
||||
'uid' => $this->liststorage->generateUID(),
|
||||
'last-name' => $name,
|
||||
'uid' => kolab_format::generate_uid(),
|
||||
'name' => $name,
|
||||
'member' => array(),
|
||||
);
|
||||
$saved = $this->liststorage->save($list);
|
||||
$saved = $this->storagefolder->save($list, 'distribution-list');
|
||||
|
||||
if (PEAR::isError($saved)) {
|
||||
if (!$saved) {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Error saving distribution-list object to Kolab server:" . $saved->getMessage()),
|
||||
'message' => "Error saving distribution-list object to Kolab server"),
|
||||
true, false);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
$id = md5($list['uid']);
|
||||
$id = $this->_uid2id($list['uid']);
|
||||
$this->distlists[$id] = $list;
|
||||
$result = array('id' => $id, 'name' => $name);
|
||||
}
|
||||
|
@ -795,13 +757,13 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
$result = false;
|
||||
|
||||
if ($list = $this->distlists[$gid])
|
||||
$deleted = $this->liststorage->delete($list['uid']);
|
||||
$deleted = $this->storagefolder->delete($list['uid']);
|
||||
|
||||
if (PEAR::isError($deleted)) {
|
||||
if (!$deleted) {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Error deleting distribution-list object from the Kolab server:" . $deleted->getMessage()),
|
||||
'message' => "Error deleting distribution-list object from the Kolab server"),
|
||||
true, false);
|
||||
}
|
||||
else
|
||||
|
@ -822,16 +784,16 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
$this->_fetch_groups();
|
||||
$list = $this->distlists[$gid];
|
||||
|
||||
if ($newname != $list['last-name']) {
|
||||
$list['last-name'] = $newname;
|
||||
$saved = $this->liststorage->save($list, $list['uid']);
|
||||
if ($newname != $list['name']) {
|
||||
$list['name'] = $newname;
|
||||
$saved = $this->storagefolder->save($list, 'distribution-list', $list['uid']);
|
||||
}
|
||||
|
||||
if (PEAR::isError($saved)) {
|
||||
if (!$saved) {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Error saving distribution-list object to Kolab server:" . $saved->getMessage()),
|
||||
'message' => "Error saving distribution-list object to Kolab server"),
|
||||
true, false);
|
||||
return false;
|
||||
}
|
||||
|
@ -854,8 +816,7 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
$added = 0;
|
||||
$exists = array();
|
||||
|
||||
$this->_fetch_groups();
|
||||
$this->_fetch_contacts();
|
||||
$this->_fetch_groups(true);
|
||||
$list = $this->distlists[$gid];
|
||||
|
||||
foreach ((array)$list['member'] as $i => $member)
|
||||
|
@ -865,28 +826,37 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
$ids = array_diff($ids, $exists);
|
||||
|
||||
foreach ($ids as $contact_id) {
|
||||
if ($uid = $this->id2uid[$contact_id]) {
|
||||
$contact = $this->contacts[$contact_id];
|
||||
foreach ($this->get_col_values('email', $contact, true) as $email) {
|
||||
$list['member'][] = array(
|
||||
'uid' => $uid,
|
||||
'display-name' => $contact['name'],
|
||||
'smtp-address' => $email,
|
||||
);
|
||||
}
|
||||
$uid = $this->_id2uid($contact_id);
|
||||
if ($contact = $this->storagefolder->get_object($uid)) {
|
||||
foreach ($this->get_col_values('email', $contact, true) as $email)
|
||||
break;
|
||||
|
||||
$list['member'][] = array(
|
||||
'uid' => $uid,
|
||||
'email' => $email,
|
||||
'name' => $contact['name'],
|
||||
);
|
||||
$this->groupmembers[$contact_id][] = $gid;
|
||||
$added++;
|
||||
}
|
||||
else if (strpos($uid, 'mailto:') === 0 && ($contact = $this->contacts[$contact_id])) {
|
||||
$list['member'][] = array(
|
||||
'email' => $contact['email'],
|
||||
'name' => $contact['name'],
|
||||
);
|
||||
$this->groupmembers[$contact_id][] = $gid;
|
||||
$added++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($added)
|
||||
$saved = $this->liststorage->save($list, $list['uid']);
|
||||
$saved = $this->storagefolder->save($list, 'distribution-list', $list['uid']);
|
||||
|
||||
if (PEAR::isError($saved)) {
|
||||
if (!$saved) {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Error saving distribution-list to Kolab server:" . $saved->getMessage()),
|
||||
'message' => "Error saving distribution-list to Kolab server"),
|
||||
true, false);
|
||||
$added = false;
|
||||
}
|
||||
|
@ -921,13 +891,13 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
|
||||
// write distribution list back to server
|
||||
$list['member'] = $new_member;
|
||||
$saved = $this->liststorage->save($list, $list['uid']);
|
||||
$saved = $this->storagefolder->save($list, 'distribution-list', $list['uid']);
|
||||
|
||||
if (PEAR::isError($saved)) {
|
||||
if (!$saved) {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Error saving distribution-list object to Kolab server:" . $saved->getMessage()),
|
||||
'message' => "Error saving distribution-list object to Kolab server"),
|
||||
true, false);
|
||||
}
|
||||
else {
|
||||
|
@ -970,44 +940,16 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
}
|
||||
|
||||
/**
|
||||
* Establishes a connection to the Kolab_Data object for accessing contact data
|
||||
* Query storage layer and store records in private member var
|
||||
*/
|
||||
private function _connect()
|
||||
{
|
||||
if (!isset($this->contactstorage)) {
|
||||
$this->contactstorage = $this->storagefolder->getData(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a connection to the Kolab_Data object for accessing groups data
|
||||
*/
|
||||
private function _connect_groups()
|
||||
{
|
||||
if (!isset($this->liststorage)) {
|
||||
$this->liststorage = $this->storagefolder->getData('distributionlist');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply fetch all records and store them in private member vars
|
||||
*/
|
||||
private function _fetch_contacts()
|
||||
private function _fetch_contacts($query = array())
|
||||
{
|
||||
if (!isset($this->contacts)) {
|
||||
$this->_connect();
|
||||
|
||||
// read contacts
|
||||
$this->contacts = $this->id2uid = array();
|
||||
foreach ((array)$this->contactstorage->getObjects() as $record) {
|
||||
// Because of a bug, sometimes group records are returned
|
||||
if ($record['__type'] == 'Group')
|
||||
continue;
|
||||
|
||||
$this->contacts = array();
|
||||
foreach ((array)$this->storagefolder->select($query) as $record) {
|
||||
$contact = $this->_to_rcube_contact($record);
|
||||
$id = $contact['ID'];
|
||||
$this->contacts[$id] = $contact;
|
||||
$this->id2uid[$id] = $record['uid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1055,178 +997,158 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
/**
|
||||
* Read distribution-lists AKA groups from server
|
||||
*/
|
||||
private function _fetch_groups()
|
||||
private function _fetch_groups($with_contacts = false)
|
||||
{
|
||||
if (!isset($this->distlists)) {
|
||||
$this->_connect_groups();
|
||||
|
||||
$this->distlists = $this->groupmembers = array();
|
||||
foreach ((array)$this->liststorage->getObjects() as $record) {
|
||||
// FIXME: folders without any distribution-list objects return contacts instead ?!
|
||||
if ($record['__type'] != 'Group')
|
||||
continue;
|
||||
|
||||
$record['ID'] = md5($record['uid']);
|
||||
foreach ((array)$this->storagefolder->get_objects('distribution-list') as $record) {
|
||||
$record['ID'] = $this->_uid2id($record['uid']);
|
||||
foreach ((array)$record['member'] as $i => $member) {
|
||||
$mid = md5($member['uid']);
|
||||
$mid = $this->_uid2id($member['uid'] ? $member['uid'] : 'mailto:' . $member['email']);
|
||||
$record['member'][$i]['ID'] = $mid;
|
||||
$record['member'][$i]['readonly'] = empty($member['uid']);
|
||||
$this->groupmembers[$mid][] = $record['ID'];
|
||||
|
||||
if ($with_contacts && empty($member['uid']))
|
||||
$this->contacts[$mid] = $record['member'][$i];
|
||||
}
|
||||
$this->distlists[$record['ID']] = $record;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode object UID into a safe identifier
|
||||
*/
|
||||
private function _uid2id($uid)
|
||||
{
|
||||
return rtrim(strtr(base64_encode($uid), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Roundcube object identifier back into the original UID
|
||||
*/
|
||||
private function _id2uid($id)
|
||||
{
|
||||
return base64_decode(str_pad(strtr($id, '-_', '+/'), strlen($id) % 4, '=', STR_PAD_RIGHT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Map fields from internal Kolab_Format to Roundcube contact format
|
||||
*/
|
||||
private function _to_rcube_contact($record)
|
||||
{
|
||||
$out = array(
|
||||
'ID' => md5($record['uid']),
|
||||
'email' => array(),
|
||||
'phone' => array(),
|
||||
);
|
||||
$record['ID'] = $this->_uid2id($record['uid']);
|
||||
|
||||
foreach ($this->fieldmap as $kolab => $rcube) {
|
||||
if (strlen($record[$kolab]))
|
||||
$out[$rcube] = $record[$kolab];
|
||||
if (is_array($record['phone'])) {
|
||||
$phones = $record['phone'];
|
||||
unset($record['phone']);
|
||||
foreach ((array)$phones as $i => $phone) {
|
||||
$key = 'phone' . ($phone['type'] ? ':' . $phone['type'] : '');
|
||||
$record[$key][] = $phone['number'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($record['gender']))
|
||||
$out['gender'] = $this->gender_map[$record['gender']];
|
||||
|
||||
foreach ((array)$record['email'] as $i => $email)
|
||||
$out['email'][] = $email['smtp-address'];
|
||||
|
||||
if (!$record['email'] && $record['emails'])
|
||||
$out['email'] = preg_split('/,\s*/', $record['emails']);
|
||||
|
||||
foreach ((array)$record['phone'] as $i => $phone)
|
||||
$out['phone:'.$phone['type']][] = $phone['number'];
|
||||
if (is_array($record['website'])) {
|
||||
$urls = $record['website'];
|
||||
unset($record['website']);
|
||||
foreach ((array)$urls as $i => $url) {
|
||||
$key = 'website' . ($url['type'] ? ':' . $url['type'] : '');
|
||||
$record[$key][] = $url['url'];
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($record['address'])) {
|
||||
foreach ($record['address'] as $i => $adr) {
|
||||
$key = 'address:' . $adr['type'];
|
||||
$out[$key][] = array(
|
||||
'street' => $adr['street'],
|
||||
$addresses = $record['address'];
|
||||
unset($record['address']);
|
||||
foreach ($addresses as $i => $adr) {
|
||||
$key = 'address' . ($adr['type'] ? ':' . $adr['type'] : '');
|
||||
$record[$key][] = array(
|
||||
'street' => $adr['street'],
|
||||
'locality' => $adr['locality'],
|
||||
'zipcode' => $adr['postal-code'],
|
||||
'region' => $adr['region'],
|
||||
'country' => $adr['country'],
|
||||
'zipcode' => $adr['code'],
|
||||
'region' => $adr['region'],
|
||||
'country' => $adr['country'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// photo is stored as separate attachment
|
||||
if ($record['picture'] && ($att = $record['_attachments'][$record['picture']])) {
|
||||
$out['photo'] = $att['content'] ? $att['content'] : $this->contactstorage->getAttachment($att['key']);
|
||||
if ($record['photo'] && strlen($record['photo']) < 255 && ($att = $record['_attachments'][$record['photo']])) {
|
||||
// only fetch photo content if requested
|
||||
if ($this->action == 'photo')
|
||||
$record['photo'] = $att['content'] ? $att['content'] : $this->storagefolder->get_attachment($record['uid'], $att['id']);
|
||||
}
|
||||
|
||||
// truncate publickey value for display
|
||||
if ($record['pgppublickey'] && $this->action == 'show')
|
||||
$record['pgppublickey'] = substr($record['pgppublickey'], 0, 140) . '...';
|
||||
|
||||
// remove empty fields
|
||||
return array_filter($out);
|
||||
return array_filter($record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map fields from Roundcube format to internal Kolab_Format
|
||||
* Map fields from Roundcube format to internal kolab_format_contact properties
|
||||
*/
|
||||
private function _from_rcube_contact($contact)
|
||||
private function _from_rcube_contact($contact, $old = array())
|
||||
{
|
||||
$object = array();
|
||||
if (!$contact['uid'] && $contact['ID'])
|
||||
$contact['uid'] = $this->_id2uid($contact['ID']);
|
||||
else if (!$contact['uid'] && $old['uid'])
|
||||
$contact['uid'] = $old['uid'];
|
||||
|
||||
foreach (array_flip($this->fieldmap) as $rcube => $kolab) {
|
||||
if (isset($contact[$rcube]))
|
||||
$object[$kolab] = is_array($contact[$rcube]) ? $contact[$rcube][0] : $contact[$rcube];
|
||||
else if ($values = $this->get_col_values($rcube, $contact, true))
|
||||
$object[$kolab] = is_array($values) ? $values[0] : $values;
|
||||
}
|
||||
|
||||
// format dates
|
||||
if ($object['birthday'] && ($date = @strtotime($object['birthday'])))
|
||||
$object['birthday'] = date('Y-m-d', $date);
|
||||
if ($object['anniversary'] && ($date = @strtotime($object['anniversary'])))
|
||||
$object['anniversary'] = date('Y-m-d', $date);
|
||||
|
||||
$gendermap = array_flip($this->gender_map);
|
||||
if (isset($object['gender']))
|
||||
$object['gender'] = $gendermap[$object['gender']];
|
||||
|
||||
$emails = $this->get_col_values('email', $contact, true);
|
||||
$object['emails'] = join(', ', array_filter($emails));
|
||||
// overwrite 'email' field
|
||||
$object['email'] = null;
|
||||
|
||||
foreach ($this->get_col_values('phone', $contact) as $type => $values) {
|
||||
if ($this->phonetypemap[$type])
|
||||
$type = $this->phonetypemap[$type];
|
||||
foreach ((array)$values as $phone) {
|
||||
if (!empty($phone)) {
|
||||
$object['phone-' . $type] = $phone;
|
||||
$object['phone'][] = array('number' => $phone, 'type' => $type);
|
||||
$contact['email'] = array_filter($this->get_col_values('email', $contact, true));
|
||||
$contact['im'] = array_filter($this->get_col_values('im', $contact, true));
|
||||
|
||||
foreach ($this->get_col_values('website', $contact) as $type => $values) {
|
||||
foreach ((array)$values as $url) {
|
||||
if (!empty($url)) {
|
||||
$contact['website'][] = array('url' => $url, 'type' => $type);
|
||||
}
|
||||
}
|
||||
unset($contact['website:'.$type]);
|
||||
}
|
||||
|
||||
$object['address'] = array();
|
||||
foreach ($this->get_col_values('phone', $contact) as $type => $values) {
|
||||
foreach ((array)$values as $phone) {
|
||||
if (!empty($phone)) {
|
||||
$contact['phone'][] = array('number' => $phone, 'type' => $type);
|
||||
}
|
||||
}
|
||||
unset($contact['phone:'.$type]);
|
||||
}
|
||||
|
||||
$addresses = array();
|
||||
foreach ($this->get_col_values('address', $contact) as $type => $values) {
|
||||
if ($this->addresstypemap[$type])
|
||||
$type = $this->addresstypemap[$type];
|
||||
|
||||
$updated = false;
|
||||
$basekey = 'addr-' . $type . '-';
|
||||
foreach ((array)$values as $adr) {
|
||||
// skip empty address
|
||||
$adr = array_filter($adr);
|
||||
if (empty($adr))
|
||||
continue;
|
||||
|
||||
// switch type if slot is already taken
|
||||
if (isset($object[$basekey . 'type'])) {
|
||||
$type = $type == 'home' ? 'business' : 'home';
|
||||
$basekey = 'addr-' . $type . '-';
|
||||
}
|
||||
|
||||
if (!isset($object[$basekey . 'type'])) {
|
||||
$object[$basekey . 'type'] = $type;
|
||||
$object[$basekey . 'street'] = $adr['street'];
|
||||
$object[$basekey . 'locality'] = $adr['locality'];
|
||||
$object[$basekey . 'postal-code'] = $adr['zipcode'];
|
||||
$object[$basekey . 'region'] = $adr['region'];
|
||||
$object[$basekey . 'country'] = $adr['country'];
|
||||
|
||||
// Update existing address entry of this type
|
||||
foreach($object['address'] as $index => $address) {
|
||||
if ($address['type'] == $type) {
|
||||
$object['address'][$index] = $new_address;
|
||||
$updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$updated) {
|
||||
$object['address'][] = array(
|
||||
'type' => $type,
|
||||
'street' => $adr['street'],
|
||||
'locality' => $adr['locality'],
|
||||
'postal-code' => $adr['zipcode'],
|
||||
'region' => $adr['region'],
|
||||
'country' => $adr['country'],
|
||||
);
|
||||
}
|
||||
$addresses[] = array(
|
||||
'type' => $type,
|
||||
'street' => $adr['street'],
|
||||
'locality' => $adr['locality'],
|
||||
'code' => $adr['zipcode'],
|
||||
'region' => $adr['region'],
|
||||
'country' => $adr['country'],
|
||||
);
|
||||
}
|
||||
|
||||
unset($contact['address:'.$type]);
|
||||
}
|
||||
$contact['address'] = $addresses;
|
||||
|
||||
// copy meta data (starting with _) from old object
|
||||
foreach ((array)$old as $key => $val) {
|
||||
if (!isset($contact[$key]) && $key[0] == '_')
|
||||
$contact[$key] = $val;
|
||||
}
|
||||
|
||||
// save new photo as attachment
|
||||
if ($contact['photo']) {
|
||||
$attkey = 'photo.attachment';
|
||||
$object['_attachments'][$attkey] = array(
|
||||
'type' => rc_image_content_type($contact['photo']),
|
||||
'content' => preg_match('![^a-z0-9/=+-]!i', $contact['photo']) ? $contact['photo'] : base64_decode($contact['photo']),
|
||||
);
|
||||
$object['picture'] = $attkey;
|
||||
}
|
||||
|
||||
return $object;
|
||||
// add empty values for some fields which can be removed in the UI
|
||||
return array_filter($contact) + array('nickname' => '', 'birthday' => '', 'anniversary' => '', 'freebusyurl' => '');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ $labels['initials'] = 'Initialen';
|
|||
$labels['profession'] = 'Berufsbezeichnung';
|
||||
$labels['officelocation'] = 'Büro Adresse';
|
||||
$labels['children'] = 'Kinder';
|
||||
$labels['pgppublickey'] = 'Öffentlicher PGP-Schlüssel';
|
||||
$labels['pgppublickey'] = 'PGP-Schlüssel';
|
||||
$labels['pkcs7publickey'] = 'S/MIME-Schlüssel';
|
||||
$labels['freebusyurl'] = 'Frei/Belegt URL';
|
||||
$labels['typebusiness'] = 'Dienstlich';
|
||||
$labels['typebusinessfax'] = 'Dienst';
|
||||
|
|
|
@ -5,7 +5,8 @@ $labels['initials'] = 'Initialen';
|
|||
$labels['profession'] = 'Berufsbezeichnung';
|
||||
$labels['officelocation'] = 'Büro Adresse';
|
||||
$labels['children'] = 'Kinder';
|
||||
$labels['pgppublickey'] = 'Öffentlicher PGP-Schlüssel';
|
||||
$labels['pgppublickey'] = 'PGP-Schlüssel';
|
||||
$labels['pkcs7publickey'] = 'S/MIME-Schlüssel';
|
||||
$labels['freebusyurl'] = 'Frei/Belegt URL';
|
||||
$labels['typebusiness'] = 'Dienstlich';
|
||||
$labels['typebusinessfax'] = 'Dienst';
|
||||
|
|
|
@ -5,7 +5,8 @@ $labels['initials'] = 'Initials';
|
|||
$labels['profession'] = 'Profession';
|
||||
$labels['officelocation'] = 'Office location';
|
||||
$labels['children'] = 'Children';
|
||||
$labels['pgppublickey'] = 'PGP publickey';
|
||||
$labels['pgppublickey'] = 'PGP public key';
|
||||
$labels['pkcs7publickey'] = 'S/MIME public key';
|
||||
$labels['freebusyurl'] = 'Free-busy URL';
|
||||
$labels['typebusiness'] = 'Business';
|
||||
$labels['typebusinessfax'] = 'Business Fax';
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
http://pear.php.net/dtd/package-2.0
|
||||
http://pear.php.net/dtd/package-2.0.xsd">
|
||||
<name>kolab_addressbook</name>
|
||||
<uri>http://git.kolab.org/roundcube-plugins-kolab/</uri>
|
||||
<uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri>
|
||||
<summary>Kolab addressbook</summary>
|
||||
<description>
|
||||
Sample plugin to add a new address book source with data from Kolab storage.
|
||||
|
|
28
plugins/kolab_addressbook/skins/larry/kolab_addressbook.css
Normal file
28
plugins/kolab_addressbook/skins/larry/kolab_addressbook.css
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
#directorylist li.addressbook.readonly,
|
||||
#directorylist li.addressbook.shared,
|
||||
#directorylist li.addressbook.other {
|
||||
/* background-image: url(kolab_folders.png); */
|
||||
background-position: 5px -1000px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#directorylist li.addressbook.readonly {
|
||||
background-position: 5px 0px;
|
||||
}
|
||||
|
||||
#directorylist li.addressbook.shared {
|
||||
background-position: 5px -54px;
|
||||
}
|
||||
|
||||
#directorylist li.addressbook.shared.readonly {
|
||||
background-position: 5px -72px;
|
||||
}
|
||||
|
||||
#directorylist li.addressbook.other {
|
||||
background-position: 5px -18px;
|
||||
}
|
||||
|
||||
#directorylist li.addressbook.other.readonly {
|
||||
background-position: 5px -36px;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<roundcube:object name="doctype" value="html5" />
|
||||
<html>
|
||||
<head>
|
||||
<title><roundcube:object name="pagetitle" /></title>
|
||||
<roundcube:include file="/includes/links.html" />
|
||||
</head>
|
||||
<body class="iframe">
|
||||
|
||||
<h1 class="boxtitle"><roundcube:label name="kolab_addressbook.bookproperties" /></h1>
|
||||
|
||||
<div class="boxcontent">
|
||||
<roundcube:object name="bookdetails" class="propform" />
|
||||
</div>
|
||||
|
||||
<div id="formfooter">
|
||||
<div class="footerleft formbuttons">
|
||||
<roundcube:button command="book-save" type="input" class="button mainaction" label="save" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<roundcube:include file="/includes/footer.html" />
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -4,7 +4,7 @@
|
|||
http://pear.php.net/dtd/package-2.0
|
||||
http://pear.php.net/dtd/package-2.0.xsd">
|
||||
<name>kolab_auth</name>
|
||||
<uri>http://git.kolab.org/roundcube-plugins-kolab/</uri>
|
||||
<uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri>
|
||||
<summary>Kolab Authentication</summary>
|
||||
<description>
|
||||
Authenticates on LDAP server, finds canonized authentication ID for IMAP
|
||||
|
|
|
@ -61,16 +61,9 @@ class kolab_config extends rcube_plugin
|
|||
if ($this->config)
|
||||
return;
|
||||
|
||||
$this->require_plugin('kolab_folders');
|
||||
return; // CURRENTLY DISABLED until libkolabxml has support for config objects
|
||||
|
||||
// load dependencies
|
||||
require_once 'Horde/Util.php';
|
||||
require_once 'Horde/Kolab/Format.php';
|
||||
require_once 'Horde/Kolab/Format/XML.php';
|
||||
require_once $this->home . '/lib/configuration.php';
|
||||
require_once $this->home . '/lib/kolab_configuration.php';
|
||||
|
||||
String::setDefaultCharset('UTF-8');
|
||||
$this->require_plugin('libkolab');
|
||||
|
||||
$this->config = new kolab_configuration();
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
http://pear.php.net/dtd/package-2.0
|
||||
http://pear.php.net/dtd/package-2.0.xsd">
|
||||
<name>kolab_config</name>
|
||||
<uri>http://git.kolab.org/roundcube-plugins-kolab/</uri>
|
||||
<uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri>
|
||||
<summary>Kolab configuration storage</summary>
|
||||
<description>
|
||||
Plugin to use Kolab server as a configuration storage. Provides an API to handle
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
http://pear.php.net/dtd/package-2.0
|
||||
http://pear.php.net/dtd/package-2.0.xsd">
|
||||
<name>kolab_core</name>
|
||||
<uri>http://git.kolab.org/roundcube-plugins-kolab/</uri>
|
||||
<uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri>
|
||||
<summary>Kolab API</summary>
|
||||
<description>
|
||||
Plugin to setup a basic environment for interaction with a Kolab server.
|
||||
|
|
|
@ -30,8 +30,6 @@ class kolab_folders extends rcube_plugin
|
|||
public $mail_types = array('inbox', 'drafts', 'sentitems', 'outbox', 'wastebasket', 'junkemail');
|
||||
private $rc;
|
||||
|
||||
const CTYPE_KEY = '/shared/vendor/kolab/folder-type';
|
||||
|
||||
|
||||
/**
|
||||
* Plugin initialization.
|
||||
|
@ -40,6 +38,9 @@ class kolab_folders extends rcube_plugin
|
|||
{
|
||||
$this->rc = rcmail::get_instance();
|
||||
|
||||
// load required plugin
|
||||
$this->require_plugin('libkolab');
|
||||
|
||||
// Folder listing hooks
|
||||
$this->add_hook('storage_folders', array($this, 'mailboxes_list'));
|
||||
|
||||
|
@ -57,68 +58,32 @@ class kolab_folders extends rcube_plugin
|
|||
*/
|
||||
function mailboxes_list($args)
|
||||
{
|
||||
// infinite loop prevention
|
||||
if ($this->is_processing) {
|
||||
return $args;
|
||||
}
|
||||
|
||||
if (!$this->metadata_support()) {
|
||||
return $args;
|
||||
}
|
||||
|
||||
$filter = $args['filter'];
|
||||
$this->is_processing = true;
|
||||
|
||||
// all-folders request, use core method
|
||||
if (!$filter) {
|
||||
// get folders
|
||||
$folders = kolab_storage::list_folders($args['root'], $args['name'], $args['filter'], $args['mode'] == 'LSUB');
|
||||
|
||||
$this->is_processing = false;
|
||||
|
||||
if (!is_array($folders)) {
|
||||
return $args;
|
||||
}
|
||||
|
||||
// get folders types
|
||||
$folderdata = $this->get_folder_type_list($args['root'].$args['name'], true);
|
||||
|
||||
if (!is_array($folderdata)) {
|
||||
return $args;
|
||||
// Create default folders
|
||||
if ($args['root'] == '' && $args['name'] = '*') {
|
||||
$this->create_default_folders($folders, $args['filter']);
|
||||
}
|
||||
|
||||
$regexp = '/^' . preg_quote($filter, '/') . '(\..+)?$/';
|
||||
|
||||
// In some conditions we can skip LIST command (?)
|
||||
if ($args['mode'] == 'LIST' && $filter != 'mail'
|
||||
&& $args['root'] == '' && $args['name'] == '*'
|
||||
) {
|
||||
foreach ($folderdata as $folder => $type) {
|
||||
if (!preg_match($regexp, $type)) {
|
||||
unset($folderdata[$folder]);
|
||||
}
|
||||
}
|
||||
$args['folders'] = array_keys($folderdata);
|
||||
return $args;
|
||||
}
|
||||
|
||||
$storage = $this->rc->get_storage();
|
||||
|
||||
// Get folders list
|
||||
if ($args['mode'] == 'LIST') {
|
||||
if (!$storage->check_connection()) {
|
||||
return $args;
|
||||
}
|
||||
$args['folders'] = $storage->conn->listMailboxes($args['root'], $args['name']);
|
||||
}
|
||||
else {
|
||||
$args['folders'] = $this->list_subscribed($args['root'], $args['name']);
|
||||
}
|
||||
|
||||
// In case of an error, return empty list
|
||||
if (!is_array($args['folders'])) {
|
||||
$args['folders'] = array();
|
||||
return $args;
|
||||
}
|
||||
|
||||
// Filter folders list
|
||||
foreach ($args['folders'] as $idx => $folder) {
|
||||
$type = $folderdata[$folder];
|
||||
if ($filter == 'mail' && empty($type)) {
|
||||
continue;
|
||||
}
|
||||
if (empty($type) || !preg_match($regexp, $type)) {
|
||||
unset($args['folders'][$idx]);
|
||||
}
|
||||
}
|
||||
$args['folders'] = $folders;
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
@ -132,10 +97,11 @@ class kolab_folders extends rcube_plugin
|
|||
return $args;
|
||||
}
|
||||
|
||||
$table = $args['table'];
|
||||
$table = $args['table'];
|
||||
$storage = $this->rc->get_storage();
|
||||
|
||||
// get folders types
|
||||
$folderdata = $this->get_folder_type_list('*');
|
||||
$folderdata = $storage->get_metadata('*', kolab_storage::CTYPE_KEY);
|
||||
|
||||
if (!is_array($folderdata)) {
|
||||
return $args;
|
||||
|
@ -146,7 +112,7 @@ class kolab_folders extends rcube_plugin
|
|||
for ($i=1, $cnt=$table->size(); $i<=$cnt; $i++) {
|
||||
$attrib = $table->get_row_attribs($i);
|
||||
$folder = $attrib['foldername']; // UTF7-IMAP
|
||||
$type = $folderdata[$folder];
|
||||
$type = !empty($folderdata[$folder]) ? $folderdata[$folder][kolab_storage::CTYPE_KEY] : null;
|
||||
|
||||
if (!$type)
|
||||
$type = 'mail';
|
||||
|
@ -266,8 +232,6 @@ class kolab_folders extends rcube_plugin
|
|||
{
|
||||
// Folder actions from folders list
|
||||
if (empty($args['record'])) {
|
||||
// Just clear Horde folders cache and return
|
||||
$this->clear_folders_cache();
|
||||
return $args;
|
||||
}
|
||||
|
||||
|
@ -340,11 +304,6 @@ class kolab_folders extends rcube_plugin
|
|||
}
|
||||
}
|
||||
|
||||
// Clear Horde folders cache
|
||||
if ($result) {
|
||||
$this->clear_folders_cache();
|
||||
}
|
||||
|
||||
$args['record']['class'] = self::folder_class_name($ctype);
|
||||
$args['record']['subscribe'] = $subscribe;
|
||||
$args['result'] = $result;
|
||||
|
@ -355,7 +314,7 @@ class kolab_folders extends rcube_plugin
|
|||
/**
|
||||
* Checks if IMAP server supports any of METADATA, ANNOTATEMORE, ANNOTATEMORE2
|
||||
*
|
||||
* @return boolean
|
||||
* @return boolean
|
||||
*/
|
||||
function metadata_support()
|
||||
{
|
||||
|
@ -376,9 +335,9 @@ class kolab_folders extends rcube_plugin
|
|||
function get_folder_type($folder)
|
||||
{
|
||||
$storage = $this->rc->get_storage();
|
||||
$folderdata = $storage->get_metadata($folder, array(kolab_folders::CTYPE_KEY));
|
||||
$folderdata = $storage->get_metadata($folder, kolab_storage::CTYPE_KEY);
|
||||
|
||||
return explode('.', $folderdata[$folder][kolab_folders::CTYPE_KEY]);
|
||||
return explode('.', $folderdata[$folder][kolab_storage::CTYPE_KEY]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -393,112 +352,7 @@ class kolab_folders extends rcube_plugin
|
|||
{
|
||||
$storage = $this->rc->get_storage();
|
||||
|
||||
return $storage->set_metadata($folder, array(kolab_folders::CTYPE_KEY => $type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of subscribed folders (directly from IMAP server)
|
||||
*
|
||||
* @param string $root Optional root folder
|
||||
* @param string $name Optional name pattern
|
||||
*
|
||||
* @return array List of mailboxes/folders
|
||||
*/
|
||||
private function list_subscribed($root='', $name='*')
|
||||
{
|
||||
$storage = $this->rc->get_storage();
|
||||
|
||||
if (!$storage->check_connection()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Code copied from rcube_imap::_list_mailboxes()
|
||||
// Server supports LIST-EXTENDED, we can use selection options
|
||||
// #1486225: Some dovecot versions returns wrong result using LIST-EXTENDED
|
||||
if (!$this->rc->config->get('imap_force_lsub') && $imap->get_capability('LIST-EXTENDED')) {
|
||||
// This will also set mailbox options, LSUB doesn't do that
|
||||
$a_folders = $storage->conn->listMailboxes($root, $name,
|
||||
NULL, array('SUBSCRIBED'));
|
||||
|
||||
// remove non-existent folders
|
||||
if (is_array($a_folders) && $name = '*' && !empty($storage->conn->data['LIST'])) {
|
||||
foreach ($a_folders as $idx => $folder) {
|
||||
if (($opts = $storage->conn->data['LIST'][$folder])
|
||||
&& in_array('\\NonExistent', $opts)
|
||||
) {
|
||||
$storage->conn->unsubscribe($folder);
|
||||
unset($a_folders[$idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// retrieve list of folders from IMAP server using LSUB
|
||||
else {
|
||||
$a_folders = $storage->conn->listSubscribed($root, $name);
|
||||
|
||||
// unsubscribe non-existent folders, remove from the list
|
||||
if (is_array($a_folders) && $name == '*' && !empty($storage->conn->data['LIST'])) {
|
||||
foreach ($a_folders as $idx => $folder) {
|
||||
if (!isset($storage->conn->data['LIST'][$folder])
|
||||
|| in_array('\\Noselect', $storage->conn->data['LIST'][$folder])
|
||||
) {
|
||||
// Some servers returns \Noselect for existing folders
|
||||
if (!$storage->folder_exists($folder)) {
|
||||
$storage->conn->unsubscribe($folder);
|
||||
unset($a_folders[$idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $a_folders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of folder(s) type(s)
|
||||
*
|
||||
* @param string $mbox Folder name or pattern
|
||||
* @param bool $defaults Enables creation of configured default folders
|
||||
*
|
||||
* @return array List of folders data, indexed by folder name
|
||||
*/
|
||||
function get_folder_type_list($mbox, $create_defaults = false)
|
||||
{
|
||||
$storage = $this->rc->get_storage();
|
||||
|
||||
// Use mailboxes. prefix so the cache will be cleared by core
|
||||
// together with other mailboxes-related cache data
|
||||
$cache_key = 'mailboxes.folder-type.'.$mbox;
|
||||
|
||||
// get cached metadata
|
||||
$metadata = $storage->get_cache($cache_key);
|
||||
|
||||
if (!is_array($metadata)) {
|
||||
$metadata = $storage->get_metadata($mbox, kolab_folders::CTYPE_KEY);
|
||||
$need_update = true;
|
||||
}
|
||||
|
||||
if (!is_array($metadata)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// make the result more flat
|
||||
if ($need_update) {
|
||||
$metadata = array_map('implode', $metadata);
|
||||
}
|
||||
|
||||
// create default folders if needed
|
||||
if ($create_defaults) {
|
||||
$this->create_default_folders($metadata, $cache_key);
|
||||
}
|
||||
|
||||
// write mailboxlist to cache
|
||||
if ($need_update) {
|
||||
$storage->update_cache($cache_key, $metadata);
|
||||
}
|
||||
|
||||
return $metadata;
|
||||
return $storage->set_metadata($folder, array(kolab_storage::CTYPE_KEY => $type));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -511,7 +365,7 @@ class kolab_folders extends rcube_plugin
|
|||
function get_default_folder($type)
|
||||
{
|
||||
$storage = $this->rc->get_storage();
|
||||
$folderdata = $this->get_folder_type_list('*');
|
||||
$folderdata = $storage->get_metadata('*', kolab_storage::CTYPE_KEY);
|
||||
|
||||
if (!is_array($folderdata)) {
|
||||
return null;
|
||||
|
@ -521,7 +375,8 @@ class kolab_folders extends rcube_plugin
|
|||
$namespace = $storage->get_namespace();
|
||||
|
||||
// get all folders of specified type
|
||||
$folderdata = array_intersect($folderdata, array($type));
|
||||
$folderdata = array_map('implode', $folderdata);
|
||||
$folderdata = array_intersect($folderdata, array($type));
|
||||
unset($folders[0]);
|
||||
|
||||
foreach ($folderdata as $folder => $data) {
|
||||
|
@ -562,24 +417,24 @@ class kolab_folders extends rcube_plugin
|
|||
return implode(' ', $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear Horde's folder cache. See Kolab_List::singleton().
|
||||
*/
|
||||
private function clear_folders_cache()
|
||||
{
|
||||
unset($_SESSION['horde_session_objects']['kolab_folderlist']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates default folders if they doesn't exist
|
||||
*/
|
||||
private function create_default_folders(&$folderdata, $cache_key = null)
|
||||
private function create_default_folders(&$folders, $filter)
|
||||
{
|
||||
$storage = $this->rc->get_storage();
|
||||
$namespace = $storage->get_namespace();
|
||||
$folderdata = $storage->get_metadata('*', kolab_storage::CTYPE_KEY);
|
||||
$defaults = array();
|
||||
$need_update = false;
|
||||
|
||||
if (!is_array($folderdata)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// "Flattenize" metadata array to become a name->type hash
|
||||
$folderdata = array_map('implode', $folderdata);
|
||||
|
||||
// Find personal namespace prefix
|
||||
if (is_array($namespace['personal']) && count($namespace['personal']) == 1) {
|
||||
$prefix = $namespace['personal'][0][0];
|
||||
|
@ -621,7 +476,7 @@ class kolab_folders extends rcube_plugin
|
|||
}
|
||||
|
||||
// get all folders of specified type
|
||||
$folders = array_intersect($folderdata, array($type));
|
||||
$folders = array_intersect($folderdata, array($type));
|
||||
unset($folders[0]);
|
||||
|
||||
// find folders in personal namespace
|
||||
|
@ -653,16 +508,10 @@ class kolab_folders extends rcube_plugin
|
|||
$result = $this->set_folder_type($foldername, $type);
|
||||
|
||||
// add new folder to the result
|
||||
if ($result) {
|
||||
$folderdata[$foldername] = $type;
|
||||
$need_update = true;
|
||||
if ($result && (!$filter || $filter == $type1)) {
|
||||
$folders[] = $foldername;
|
||||
}
|
||||
}
|
||||
|
||||
// update cache
|
||||
if ($need_update && $cache_key) {
|
||||
$storage->update_cache($cache_key, $folderdata);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
http://pear.php.net/dtd/package-2.0
|
||||
http://pear.php.net/dtd/package-2.0.xsd">
|
||||
<name>kolab_folders</name>
|
||||
<uri>http://git.kolab.org/roundcube-plugins-kolab/</uri>
|
||||
<uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri>
|
||||
<summary>Type-aware folder management/listing for Kolab</summary>
|
||||
<description>
|
||||
The plugin extends folders handling with features of the Kolab Suite
|
||||
|
@ -21,10 +21,10 @@
|
|||
<email>machniak@kolabsys.com</email>
|
||||
<active>yes</active>
|
||||
</lead>
|
||||
<date>2011-11-01</date>
|
||||
<date>2012-05-14</date>
|
||||
<version>
|
||||
<release>1.0</release>
|
||||
<api>1.0</api>
|
||||
<release>2.0</release>
|
||||
<api>2.0</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>stable</release>
|
||||
|
|
|
@ -26,7 +26,7 @@ class kolab_zpush extends rcube_plugin
|
|||
{
|
||||
public $task = 'settings';
|
||||
public $urlbase;
|
||||
|
||||
|
||||
private $rc;
|
||||
private $ui;
|
||||
private $cache;
|
||||
|
@ -34,7 +34,7 @@ class kolab_zpush extends rcube_plugin
|
|||
private $folders;
|
||||
private $folders_meta;
|
||||
private $root_meta;
|
||||
|
||||
|
||||
const ROOT_MAILBOX = 'INBOX';
|
||||
const CTYPE_KEY = '/shared/vendor/kolab/folder-type';
|
||||
const ACTIVESYNC_KEY = '/private/vendor/kolab/activesync';
|
||||
|
@ -45,17 +45,18 @@ class kolab_zpush extends rcube_plugin
|
|||
public function init()
|
||||
{
|
||||
$this->rc = rcmail::get_instance();
|
||||
|
||||
|
||||
$this->require_plugin('jqueryui');
|
||||
$this->add_texts('localization/', true);
|
||||
|
||||
|
||||
$this->include_script('kolab_zpush.js');
|
||||
|
||||
|
||||
$this->register_action('plugin.zpushconfig', array($this, 'config_view'));
|
||||
$this->register_action('plugin.zpushjson', array($this, 'json_command'));
|
||||
|
||||
if ($this->rc->action == 'plugin.zpushconfig')
|
||||
$this->require_plugin('kolab_core');
|
||||
|
||||
if ($this->rc->action == 'plugin.zpushconfig') {
|
||||
$this->require_plugin('libkolab');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -66,6 +67,8 @@ class kolab_zpush extends rcube_plugin
|
|||
{
|
||||
$storage = $this->rc->get_storage();
|
||||
|
||||
// @TODO: Metadata is already cached by rcube storage, get rid of cache here
|
||||
|
||||
$this->cache = $this->rc->get_cache('zpush', 'db', 900);
|
||||
$this->cache->expunge();
|
||||
|
||||
|
@ -120,7 +123,7 @@ class kolab_zpush extends rcube_plugin
|
|||
$laxpic = intval(get_input_value('laxpic', RCUBE_INPUT_POST));
|
||||
$subsciptions = get_input_value('subscribed', RCUBE_INPUT_POST);
|
||||
$err = false;
|
||||
|
||||
|
||||
if ($device = $devices[$imei]) {
|
||||
// update device config if changed
|
||||
if ($devicealias != $this->root_meta['DEVICE'][$imei]['ALIAS'] ||
|
||||
|
@ -146,12 +149,12 @@ class kolab_zpush extends rcube_plugin
|
|||
// skip root folder (already handled above)
|
||||
if ($folder == self::ROOT_MAILBOX)
|
||||
continue;
|
||||
|
||||
|
||||
if ($subsciptions[$folder] != $meta[$imei]['S']) {
|
||||
$meta[$imei]['S'] = intval($subsciptions[$folder]);
|
||||
$this->folders_meta[$folder] = $meta;
|
||||
unset($meta['TYPE']);
|
||||
|
||||
|
||||
// read metadata first
|
||||
$folderdata = $storage->get_metadata($folder, array(self::ACTIVESYNC_KEY));
|
||||
if ($asyncdata = $folderdata[$folder][self::ACTIVESYNC_KEY])
|
||||
|
@ -161,25 +164,24 @@ class kolab_zpush extends rcube_plugin
|
|||
$err |= !$storage->set_metadata($folder, array(self::ACTIVESYNC_KEY => $this->serialize_metadata($metadata)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// update cache
|
||||
$this->cache->remove('folders');
|
||||
$this->cache->write('folders', $this->folders_meta);
|
||||
|
||||
|
||||
$this->rc->output->command('plugin.zpush_save_complete', array('success' => !$err, 'id' => $imei, 'devicealias' => Q($devicealias)));
|
||||
}
|
||||
|
||||
|
||||
if ($err)
|
||||
$this->rc->output->show_message($this->gettext('savingerror'), 'error');
|
||||
else
|
||||
$this->rc->output->show_message($this->gettext('successfullysaved'), 'confirmation');
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$this->init_imap();
|
||||
$devices = $this->list_devices();
|
||||
|
||||
|
||||
if ($device = $devices[$imei]) {
|
||||
unset($this->root_meta['DEVICE'][$imei], $this->root_meta['FOLDER'][$imei]);
|
||||
|
||||
|
@ -237,20 +239,20 @@ class kolab_zpush extends rcube_plugin
|
|||
public function config_view()
|
||||
{
|
||||
require_once $this->home . '/kolab_zpush_ui.php';
|
||||
|
||||
|
||||
$storage = $this->rc->get_storage();
|
||||
|
||||
|
||||
// checks if IMAP server supports any of METADATA, ANNOTATEMORE, ANNOTATEMORE2
|
||||
if (!($storage->get_capability('METADATA') || $storage->get_capability('ANNOTATEMORE') || $storage->get_capability('ANNOTATEMORE2'))) {
|
||||
$this->rc->output->show_message($this->gettext('notsupported'), 'error');
|
||||
}
|
||||
|
||||
|
||||
$this->ui = new kolab_zpush_ui($this);
|
||||
|
||||
|
||||
$this->register_handler('plugin.devicelist', array($this->ui, 'device_list'));
|
||||
$this->register_handler('plugin.deviceconfigform', array($this->ui, 'device_config_form'));
|
||||
$this->register_handler('plugin.foldersubscriptions', array($this->ui, 'folder_subscriptions'));
|
||||
|
||||
|
||||
$this->rc->output->set_env('devicecount', count($this->list_devices()));
|
||||
$this->rc->output->send('kolab_zpush.config');
|
||||
}
|
||||
|
@ -264,9 +266,10 @@ class kolab_zpush extends rcube_plugin
|
|||
public function list_devices()
|
||||
{
|
||||
if (!isset($this->devices)) {
|
||||
$this->init_imap();
|
||||
$this->devices = (array)$this->root_meta['DEVICE'];
|
||||
}
|
||||
|
||||
|
||||
return $this->devices;
|
||||
}
|
||||
|
||||
|
@ -299,7 +302,7 @@ class kolab_zpush extends rcube_plugin
|
|||
}
|
||||
$this->folders_meta[$folder]['TYPE'] = !empty($foldertype[0]) ? $foldertype[0] : 'mail';
|
||||
}
|
||||
|
||||
|
||||
// cache it!
|
||||
$this->cache->write('folders', $this->folders_meta);
|
||||
}
|
||||
|
@ -317,7 +320,7 @@ class kolab_zpush extends rcube_plugin
|
|||
{
|
||||
if (!isset($this->folders_meta))
|
||||
$this->list_folders();
|
||||
|
||||
|
||||
return $this->folders_meta;
|
||||
}
|
||||
|
||||
|
|
|
@ -67,18 +67,18 @@ class kolab_zpush_ui
|
|||
$input = new html_inputfield(array('name' => 'devicealias', 'id' => $field_id, 'size' => 40));
|
||||
$table->add('title', html::label($field_id, $this->config->gettext('devicealias')));
|
||||
$table->add(null, $input->show());
|
||||
|
||||
|
||||
$field_id = 'config-device-mode';
|
||||
$select = new html_select(array('name' => 'syncmode', 'id' => $field_id));
|
||||
$select->add(array($this->config->gettext('modeauto'), $this->config->gettext('modeflat'), $this->config->gettext('modefolder')), array('-1', '0', '1'));
|
||||
$table->add('title', html::label($field_id, $this->config->gettext('syncmode')));
|
||||
$table->add(null, $select->show('-1'));
|
||||
|
||||
|
||||
$field_id = 'config-device-laxpic';
|
||||
$checkbox = new html_checkbox(array('name' => 'laxpic', 'value' => '1', 'id' => $field_id));
|
||||
$table->add('title', $this->config->gettext('imageformat'));
|
||||
$table->add(null, html::label($field_id, $checkbox->show() . ' ' . $this->config->gettext('laxpiclabel')));
|
||||
|
||||
|
||||
if ($attrib['form'])
|
||||
$this->rc->output->add_gui_object('editform', $attrib['form']);
|
||||
|
||||
|
@ -90,7 +90,7 @@ class kolab_zpush_ui
|
|||
{
|
||||
if (!$attrib['id'])
|
||||
$attrib['id'] = 'foldersubscriptions';
|
||||
|
||||
|
||||
// group folders by type (show only known types)
|
||||
$folder_groups = array('mail' => array(), 'contact' => array(), 'event' => array(), 'task' => array());
|
||||
$folder_meta = $this->config->folders_meta();
|
||||
|
@ -99,7 +99,7 @@ class kolab_zpush_ui
|
|||
if (is_array($folder_groups[$type]))
|
||||
$folder_groups[$type][] = $folder;
|
||||
}
|
||||
|
||||
|
||||
// build block for every folder type
|
||||
foreach ($folder_groups as $type => $group) {
|
||||
if (empty($group))
|
||||
|
@ -111,14 +111,14 @@ class kolab_zpush_ui
|
|||
}
|
||||
|
||||
$this->rc->output->add_gui_object('subscriptionslist', $attrib['id']);
|
||||
|
||||
|
||||
return html::div($attrib, $html);
|
||||
}
|
||||
|
||||
public function folder_subscriptions_block($a_folders, $attrib)
|
||||
{
|
||||
$alarms = ($attrib['type'] == 'event' || $attrib['type'] == 'task');
|
||||
|
||||
|
||||
$table = new html_table(array('cellspacing' => 0));
|
||||
$table->add_header('subscription', $attrib['syncicon'] ? html::img(array('src' => $this->skin_path . $attrib['syncicon'], 'title' => $this->config->gettext('synchronize'))) : '');
|
||||
$table->add_header('alarm', $alarms && $attrib['alarmicon'] ? html::img(array('src' => $this->skin_path . $attrib['alarmicon'], 'title' => $this->config->gettext('withalarms'))) : '');
|
||||
|
@ -129,7 +129,7 @@ class kolab_zpush_ui
|
|||
|
||||
$names = array();
|
||||
foreach ($a_folders as $folder) {
|
||||
$foldername = $origname = preg_replace('/^INBOX »\s+/', '', rcube_kolab::object_name($folder));
|
||||
$foldername = $origname = preg_replace('/^INBOX »\s+/', '', kolab_storage::object_name($folder));
|
||||
|
||||
// find folder prefix to truncate (the same code as in kolab_addressbook plugin)
|
||||
for ($i = count($names)-1; $i >= 0; $i--) {
|
||||
|
@ -161,7 +161,7 @@ class kolab_zpush_ui
|
|||
$table->add('alarm', $checkbox_alarm->show('', array('value' => $folder, 'id' => $folder_id.'_alarm')));
|
||||
else
|
||||
$table->add('alarm', '');
|
||||
|
||||
|
||||
$table->add(join(' ', $classes), html::label($folder_id, $padding . Q($foldername)));
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
http://pear.php.net/dtd/package-2.0
|
||||
http://pear.php.net/dtd/package-2.0.xsd">
|
||||
<name>kolab_zpush</name>
|
||||
<uri>http://git.kolab.org/roundcube-plugins-kolab/</uri>
|
||||
<uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri>
|
||||
<summary>Z-Push configuration utility for Kolab accounts</summary>
|
||||
<description></description>
|
||||
<lead>
|
||||
|
@ -13,10 +13,10 @@
|
|||
<email>bruederli@kolabsys.com</email>
|
||||
<active>yes</active>
|
||||
</lead>
|
||||
<date>2011-11-14</date>
|
||||
<time>12:12:00</time>
|
||||
<date>2012-05-14</date>
|
||||
<version>
|
||||
<release>0.3</release>
|
||||
<release>1.0</release>
|
||||
<api>1.0</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>stable</release>
|
||||
|
@ -29,6 +29,7 @@
|
|||
<file name="kolab_zpush_ui.php" role="php"></file>
|
||||
<file name="kolab_zpush.js" role="data"></file>
|
||||
<file name="localization/de_CH.inc" role="data"></file>
|
||||
<file name="localization/de_DE.inc" role="data"></file>
|
||||
<file name="localization/en_US.inc" role="data"></file>
|
||||
<file name="localization/pl_PL.inc" role="data"></file>
|
||||
<file name="skins/default/templates/config.html" role="data"></file>
|
||||
|
|
43
plugins/libkolab/README
Normal file
43
plugins/libkolab/README
Normal file
|
@ -0,0 +1,43 @@
|
|||
libkolab plugin to access to Kolab groupware data
|
||||
=================================================
|
||||
|
||||
The contained library classes establish a connection to the Kolab server
|
||||
and manage the access to the Kolab groupware objects stored in various
|
||||
IMAP folders. For reading and writing these objects, the PHP bindings of
|
||||
the libkolabxml library are used.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
* libkolabxml PHP bindings
|
||||
- kolabformat.so loaded into PHP
|
||||
- kolabformat.php placed somewhere in the include_path
|
||||
* PEAR: HTTP/Request2
|
||||
* PEAR: Net/URL2
|
||||
|
||||
* Optional for old format support:
|
||||
Horde Kolab_Format package and all of its dependencies
|
||||
which are at least Horde_(Browser,DOM,NLS,String,Utils)
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
To use local cache you need to create a dedicated table in Roundcube's database.
|
||||
To do so, execute the SQL commands in SQL/<yourdatabase>.sql
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
The following options can be configured in Roundcube's main config file
|
||||
or a local config file (config.inc.php) located in the plugin folder.
|
||||
|
||||
// Enable caching of Kolab objects in local database
|
||||
$rcmail_config['kolab_cache'] = true;
|
||||
|
||||
// Optional override of the URL to read and trigger Free/Busy information of Kolab users
|
||||
// Defaults to https://<imap-server->/freebusy
|
||||
$rcmail_config['kolab_freebusy_server'] = 'https://<some-host>/<freebusy-path>';
|
||||
|
||||
// Set this option to disable SSL certificate checks when triggering Free/Busy (enabled by default)
|
||||
$rcmail_config['kolab_ssl_verify_peer'] = false;
|
||||
|
22
plugins/libkolab/SQL/mysql.sql
Normal file
22
plugins/libkolab/SQL/mysql.sql
Normal file
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* libkolab database schema
|
||||
*
|
||||
* @version @package_version@
|
||||
* @author Thomas Bruederli
|
||||
* @licence GNU AGPL
|
||||
**/
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `kolab_cache` (
|
||||
`resource` VARCHAR(255) CHARACTER SET ascii NOT NULL,
|
||||
`type` VARCHAR(32) CHARACTER SET ascii NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`dtstart` DATETIME,
|
||||
`dtend` DATETIME,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
PRIMARY KEY(`resource`,`type`,`msguid`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
289
plugins/libkolab/lib/kolab_format.php
Normal file
289
plugins/libkolab/lib/kolab_format.php
Normal file
|
@ -0,0 +1,289 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab format model class wrapping libkolabxml bindings
|
||||
*
|
||||
* Abstract base class for different Kolab groupware objects read from/written
|
||||
* to the new Kolab 3 format using the PHP bindings of libkolabxml.
|
||||
*
|
||||
* @version @package_version@
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012, 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/>.
|
||||
*/
|
||||
|
||||
abstract class kolab_format
|
||||
{
|
||||
public static $timezone;
|
||||
|
||||
protected $obj;
|
||||
protected $data;
|
||||
protected $xmldata;
|
||||
protected $loaded = false;
|
||||
|
||||
/**
|
||||
* Factory method to instantiate a kolab_format object of the given type
|
||||
*
|
||||
* @param string Object type to instantiate
|
||||
* @param string Cached xml data to initialize with
|
||||
* @return object kolab_format
|
||||
*/
|
||||
public static function factory($type, $xmldata = null)
|
||||
{
|
||||
if (!isset(self::$timezone))
|
||||
self::$timezone = new DateTimeZone('UTC');
|
||||
|
||||
$suffix = preg_replace('/[^a-z]+/', '', $type);
|
||||
$classname = 'kolab_format_' . $suffix;
|
||||
if (class_exists($classname))
|
||||
return new $classname($xmldata);
|
||||
|
||||
return PEAR::raiseError(sprintf("Failed to load Kolab Format wrapper for type %s", $type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given date/time value into a cDateTime object
|
||||
*
|
||||
* @param mixed Date/Time value either as unix timestamp, date string or PHP DateTime object
|
||||
* @param DateTimeZone The timezone the date/time is in. Use global default if empty
|
||||
* @param boolean True of the given date has no time component
|
||||
* @return object The libkolabxml date/time object
|
||||
*/
|
||||
public static function get_datetime($datetime, $tz = null, $dateonly = false)
|
||||
{
|
||||
if (!$tz) $tz = self::$timezone;
|
||||
$result = new cDateTime();
|
||||
|
||||
// got a unix timestamp (in UTC)
|
||||
if (is_numeric($datetime)) {
|
||||
$datetime = new DateTime('@'.$datetime, new DateTimeZone('UTC'));
|
||||
if ($tz) $datetime->setTimezone($tz);
|
||||
}
|
||||
else if (is_string($datetime) && strlen($datetime))
|
||||
$datetime = new DateTime($datetime, $tz);
|
||||
|
||||
if (is_a($datetime, 'DateTime')) {
|
||||
$result->setDate($datetime->format('Y'), $datetime->format('n'), $datetime->format('j'));
|
||||
|
||||
if (!$dateonly)
|
||||
$result->setTime($datetime->format('G'), $datetime->format('i'), $datetime->format('s'));
|
||||
|
||||
if ($tz && $tz->getName() == 'UTC')
|
||||
$result->setUTC(true);
|
||||
else if ($tz)
|
||||
$result->setTimezone($tz->getName());
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given cDateTime into a PHP DateTime object
|
||||
*
|
||||
* @param object cDateTime The libkolabxml datetime object
|
||||
* @return object DateTime PHP datetime instance
|
||||
*/
|
||||
public static function php_datetime($cdt)
|
||||
{
|
||||
if (!is_object($cdt) || !$cdt->isValid())
|
||||
return null;
|
||||
|
||||
$d = new DateTime;
|
||||
$d->setTimezone(self::$timezone);
|
||||
|
||||
try {
|
||||
if ($tzs = $cdt->timezone()) {
|
||||
$tz = new DateTimeZone($tzs);
|
||||
$d->setTimezone($tz);
|
||||
}
|
||||
else if ($cdt->isUTC()) {
|
||||
$d->setTimezone(new DateTimeZone('UTC'));
|
||||
}
|
||||
}
|
||||
catch (Exception $e) { }
|
||||
|
||||
$d->setDate($cdt->year(), $cdt->month(), $cdt->day());
|
||||
|
||||
if ($cdt->isDateOnly()) {
|
||||
$d->_dateonly = true;
|
||||
$d->setTime(12, 0, 0); // set time to noon to avoid timezone troubles
|
||||
}
|
||||
else {
|
||||
$d->setTime($cdt->hour(), $cdt->minute(), $cdt->second());
|
||||
}
|
||||
|
||||
return $d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a libkolabxml vector to a PHP array
|
||||
*
|
||||
* @param object vector Object
|
||||
* @return array Indexed array contaning vector elements
|
||||
*/
|
||||
public static function vector2array($vec, $max = PHP_INT_MAX)
|
||||
{
|
||||
$arr = array();
|
||||
for ($i=0; $i < $vec->size() && $i < $max; $i++)
|
||||
$arr[] = $vec->get($i);
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a libkolabxml vector (string) from a PHP array
|
||||
*
|
||||
* @param array Array with vector elements
|
||||
* @return object vectors
|
||||
*/
|
||||
public static function array2vector($arr)
|
||||
{
|
||||
$vec = new vectors;
|
||||
foreach ((array)$arr as $val) {
|
||||
if (strlen($val))
|
||||
$vec->push($val);
|
||||
}
|
||||
return $vec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for format errors after calling kolabformat::write*()
|
||||
*
|
||||
* @return boolean True if there were errors, False if OK
|
||||
*/
|
||||
protected function format_errors()
|
||||
{
|
||||
$ret = $log = false;
|
||||
switch (kolabformat::error()) {
|
||||
case kolabformat.NoError:
|
||||
$ret = false;
|
||||
break;
|
||||
case kolabformat.Warning:
|
||||
$ret = false;
|
||||
$log = "Warning";
|
||||
break;
|
||||
default:
|
||||
$ret = true;
|
||||
$log = "Error";
|
||||
}
|
||||
|
||||
if ($log) {
|
||||
raise_error(array(
|
||||
'code' => 660,
|
||||
'type' => 'php',
|
||||
'file' => __FILE__,
|
||||
'line' => __LINE__,
|
||||
'message' => "kolabformat write $log: " . kolabformat::errorMessage(),
|
||||
), true);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the last generated UID to the object properties.
|
||||
* Should be called after kolabformat::writeXXXX();
|
||||
*/
|
||||
protected function update_uid()
|
||||
{
|
||||
// get generated UID
|
||||
if (!$this->data['uid']) {
|
||||
$this->data['uid'] = kolabformat::getSerializedUID();
|
||||
$this->obj->setUid($this->data['uid']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize libkolabxml object with cached xml data
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
if (!$this->loaded) {
|
||||
if ($this->xmldata) {
|
||||
$this->load($this->xmldata);
|
||||
$this->xmldata = null;
|
||||
}
|
||||
$this->loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Direct getter for object properties
|
||||
*/
|
||||
public function __get($var)
|
||||
{
|
||||
return $this->data[$var];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Kolab object data from the given XML block
|
||||
*
|
||||
* @param string XML data
|
||||
*/
|
||||
abstract public function load($xml);
|
||||
|
||||
/**
|
||||
* Set properties to the kolabformat object
|
||||
*
|
||||
* @param array Object data as hash array
|
||||
*/
|
||||
abstract public function set(&$object);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
abstract public function is_valid();
|
||||
|
||||
/**
|
||||
* Write object data to XML format
|
||||
*
|
||||
* @return string XML data
|
||||
*/
|
||||
abstract public function write();
|
||||
|
||||
/**
|
||||
* Convert the Kolab object into a hash array data structure
|
||||
*
|
||||
* @return array Kolab object data as hash array
|
||||
*/
|
||||
abstract public function to_array();
|
||||
|
||||
/**
|
||||
* Load object data from Kolab2 format
|
||||
*
|
||||
* @param array Hash array with object properties (produced by Horde Kolab_Format classes)
|
||||
*/
|
||||
abstract public function fromkolab2($object);
|
||||
|
||||
/**
|
||||
* Callback for kolab_storage_cache to get object specific tags to cache
|
||||
*
|
||||
* @return array List of tags to save in cache
|
||||
*/
|
||||
public function get_tags()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for kolab_storage_cache to get words to index for fulltext search
|
||||
*
|
||||
* @return array List of words to save in cache
|
||||
*/
|
||||
public function get_words()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
532
plugins/libkolab/lib/kolab_format_contact.php
Normal file
532
plugins/libkolab/lib/kolab_format_contact.php
Normal file
|
@ -0,0 +1,532 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab Contact model class
|
||||
*
|
||||
* @version @package_version@
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012, 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 kolab_format_contact extends kolab_format
|
||||
{
|
||||
public $CTYPE = 'application/vcard+xml';
|
||||
|
||||
public static $fulltext_cols = array('name', 'firstname', 'surname', 'middlename', 'email');
|
||||
|
||||
public $phonetypes = array(
|
||||
'home' => Telephone::Home,
|
||||
'work' => Telephone::Work,
|
||||
'text' => Telephone::Text,
|
||||
'main' => Telephone::Voice,
|
||||
'homefax' => Telephone::Fax,
|
||||
'workfax' => Telephone::Fax,
|
||||
'mobile' => Telephone::Cell,
|
||||
'video' => Telephone::Video,
|
||||
'pager' => Telephone::Pager,
|
||||
'car' => Telephone::Car,
|
||||
'other' => Telephone::Textphone,
|
||||
);
|
||||
|
||||
public $addresstypes = array(
|
||||
'home' => Address::Home,
|
||||
'work' => Address::Work,
|
||||
'office' => 0,
|
||||
);
|
||||
|
||||
private $gendermap = array(
|
||||
'female' => Contact::Female,
|
||||
'male' => Contact::Male,
|
||||
);
|
||||
|
||||
private $relatedmap = array(
|
||||
'manager' => Related::Manager,
|
||||
'assistant' => Related::Assistant,
|
||||
'spouse' => Related::Spouse,
|
||||
'children' => Related::Child,
|
||||
);
|
||||
|
||||
// old Kolab 2 format field map
|
||||
private $kolab2_fieldmap = array(
|
||||
// kolab => roundcube
|
||||
'full-name' => 'name',
|
||||
'given-name' => 'firstname',
|
||||
'middle-names' => 'middlename',
|
||||
'last-name' => 'surname',
|
||||
'prefix' => 'prefix',
|
||||
'suffix' => 'suffix',
|
||||
'nick-name' => 'nickname',
|
||||
'organization' => 'organization',
|
||||
'department' => 'department',
|
||||
'job-title' => 'jobtitle',
|
||||
'birthday' => 'birthday',
|
||||
'anniversary' => 'anniversary',
|
||||
'phone' => 'phone',
|
||||
'im-address' => 'im',
|
||||
'web-page' => 'website',
|
||||
'profession' => 'profession',
|
||||
'manager-name' => 'manager',
|
||||
'assistant' => 'assistant',
|
||||
'spouse-name' => 'spouse',
|
||||
'children' => 'children',
|
||||
'body' => 'notes',
|
||||
'pgp-publickey' => 'pgppublickey',
|
||||
'free-busy-url' => 'freebusyurl',
|
||||
'picture' => 'photo',
|
||||
);
|
||||
private $kolab2_phonetypes = array(
|
||||
'home1' => 'home',
|
||||
'business1' => 'work',
|
||||
'business2' => 'work',
|
||||
'businessfax' => 'workfax',
|
||||
);
|
||||
private $kolab2_addresstypes = array(
|
||||
'business' => 'work'
|
||||
);
|
||||
private $kolab2_gender = array(0 => 'male', 1 => 'female');
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
function __construct($xmldata = null)
|
||||
{
|
||||
$this->obj = new Contact;
|
||||
$this->xmldata = $xmldata;
|
||||
|
||||
// complete phone types
|
||||
$this->phonetypes['homefax'] |= Telephone::Home;
|
||||
$this->phonetypes['workfax'] |= Telephone::Work;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Contact object data from the given XML block
|
||||
*
|
||||
* @param string XML data
|
||||
*/
|
||||
public function load($xml)
|
||||
{
|
||||
$this->obj = kolabformat::readContact($xml, false);
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write Contact object data to XML format
|
||||
*
|
||||
* @return string XML data
|
||||
*/
|
||||
public function write()
|
||||
{
|
||||
$this->init();
|
||||
$this->xmldata = kolabformat::writeContact($this->obj);
|
||||
|
||||
if (!parent::format_errors())
|
||||
parent::update_uid();
|
||||
else
|
||||
$this->xmldata = null;
|
||||
|
||||
return $this->xmldata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set contact properties to the kolabformat object
|
||||
*
|
||||
* @param array Contact data as hash array
|
||||
*/
|
||||
public function set(&$object)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
// set some automatic values if missing
|
||||
if (false && !$this->obj->created()) {
|
||||
if (!empty($object['created']))
|
||||
$object['created'] = new DateTime('now', self::$timezone);
|
||||
$this->obj->setCreated(self::get_datetime($object['created']));
|
||||
}
|
||||
|
||||
if (!empty($object['uid']))
|
||||
$this->obj->setUid($object['uid']);
|
||||
|
||||
// do the hard work of setting object values
|
||||
$nc = new NameComponents;
|
||||
$nc->setSurnames(self::array2vector($object['surname']));
|
||||
$nc->setGiven(self::array2vector($object['firstname']));
|
||||
$nc->setAdditional(self::array2vector($object['middlename']));
|
||||
$nc->setPrefixes(self::array2vector($object['prefix']));
|
||||
$nc->setSuffixes(self::array2vector($object['suffix']));
|
||||
$this->obj->setNameComponents($nc);
|
||||
$this->obj->setName($object['name']);
|
||||
|
||||
if (isset($object['nickname']))
|
||||
$this->obj->setNickNames(self::array2vector($object['nickname']));
|
||||
if (isset($object['profession']))
|
||||
$this->obj->setTitles(self::array2vector($object['profession']));
|
||||
|
||||
// organisation related properties (affiliation)
|
||||
$org = new Affiliation;
|
||||
$offices = new vectoraddress;
|
||||
if ($object['organization'])
|
||||
$org->setOrganisation($object['organization']);
|
||||
if ($object['department'])
|
||||
$org->setOrganisationalUnits(self::array2vector($object['department']));
|
||||
if ($object['jobtitle'])
|
||||
$org->setRoles(self::array2vector($object['jobtitle']));
|
||||
|
||||
$rels = new vectorrelated;
|
||||
if ($object['manager']) {
|
||||
foreach ((array)$object['manager'] as $manager)
|
||||
$rels->push(new Related(Related::Text, $manager, Related::Manager));
|
||||
}
|
||||
if ($object['assistant']) {
|
||||
foreach ((array)$object['assistant'] as $assistant)
|
||||
$rels->push(new Related(Related::Text, $assistant, Related::Assistant));
|
||||
}
|
||||
$org->setRelateds($rels);
|
||||
|
||||
// email, im, url
|
||||
$this->obj->setEmailAddresses(self::array2vector($object['email']));
|
||||
$this->obj->setIMaddresses(self::array2vector($object['im']));
|
||||
|
||||
$vurls = new vectorurl;
|
||||
foreach ((array)$object['website'] as $url) {
|
||||
$type = $url['type'] == 'blog' ? Url::Blog : Url::None;
|
||||
$vurls->push(new Url($url['url'], $type));
|
||||
}
|
||||
$this->obj->setUrls($vurls);
|
||||
|
||||
// addresses
|
||||
$adrs = new vectoraddress;
|
||||
foreach ((array)$object['address'] as $address) {
|
||||
$adr = new Address;
|
||||
$type = $this->addresstypes[$address['type']];
|
||||
if (isset($type))
|
||||
$adr->setTypes($type);
|
||||
else if ($address['type'])
|
||||
$adr->setLabel($address['type']);
|
||||
if ($address['street'])
|
||||
$adr->setStreet($address['street']);
|
||||
if ($address['locality'])
|
||||
$adr->setLocality($address['locality']);
|
||||
if ($address['code'])
|
||||
$adr->setCode($address['code']);
|
||||
if ($address['region'])
|
||||
$adr->setRegion($address['region']);
|
||||
if ($address['country'])
|
||||
$adr->setCountry($address['country']);
|
||||
|
||||
if ($address['type'] == 'office')
|
||||
$offices->push($adr);
|
||||
else
|
||||
$adrs->push($adr);
|
||||
}
|
||||
$this->obj->setAddresses($adrs);
|
||||
$org->setAddresses($offices);
|
||||
|
||||
// add org affiliation after addresses are set
|
||||
$orgs = new vectoraffiliation;
|
||||
$orgs->push($org);
|
||||
$this->obj->setAffiliations($orgs);
|
||||
|
||||
// telephones
|
||||
$tels = new vectortelephone;
|
||||
foreach ((array)$object['phone'] as $phone) {
|
||||
$tel = new Telephone;
|
||||
if (isset($this->phonetypes[$phone['type']]))
|
||||
$tel->setTypes($this->phonetypes[$phone['type']]);
|
||||
$tel->setNumber($phone['number']);
|
||||
$tels->push($tel);
|
||||
}
|
||||
$this->obj->setTelephones($tels);
|
||||
|
||||
if (isset($object['gender']))
|
||||
$this->obj->setGender($this->gendermap[$object['gender']] ? $this->gendermap[$object['gender']] : Contact::NotSet);
|
||||
if (isset($object['notes']))
|
||||
$this->obj->setNote($object['notes']);
|
||||
if (isset($object['freebusyurl']))
|
||||
$this->obj->setFreeBusyUrl($object['freebusyurl']);
|
||||
if (isset($object['birthday']))
|
||||
$this->obj->setBDay(self::get_datetime($object['birthday'], null, true));
|
||||
if (isset($object['anniversary']))
|
||||
$this->obj->setAnniversary(self::get_datetime($object['anniversary'], null, true));
|
||||
|
||||
if (!empty($object['photo'])) {
|
||||
if ($type = rc_image_content_type($object['photo']))
|
||||
$this->obj->setPhoto($object['photo'], $type);
|
||||
}
|
||||
else if (isset($object['photo']))
|
||||
$this->obj->setPhoto('','');
|
||||
else if ($this->obj->photoMimetype()) // load saved photo for caching
|
||||
$object['photo'] = $this->obj->photo();
|
||||
|
||||
// spouse and children are relateds
|
||||
$rels = new vectorrelated;
|
||||
if ($object['spouse']) {
|
||||
$rels->push(new Related(Related::Text, $object['spouse'], Related::Spouse));
|
||||
}
|
||||
if ($object['children']) {
|
||||
foreach ((array)$object['children'] as $child)
|
||||
$rels->push(new Related(Related::Text, $child, Related::Child));
|
||||
}
|
||||
$this->obj->setRelateds($rels);
|
||||
|
||||
// insert/replace crypto keys
|
||||
$pgp_index = $pkcs7_index = -1;
|
||||
$keys = $this->obj->keys();
|
||||
for ($i=0; $i < $keys->size(); $i++) {
|
||||
$key = $keys->get($i);
|
||||
if ($pgp_index < 0 && $key->type() == Key::PGP)
|
||||
$pgp_index = $i;
|
||||
else if ($pkcs7_index < 0 && $key->type() == Key::PKCS7_MIME)
|
||||
$pkcs7_index = $i;
|
||||
}
|
||||
|
||||
$pgpkey = $object['pgppublickey'] ? new Key($object['pgppublickey'], Key::PGP) : new Key();
|
||||
$pkcs7key = $object['pkcs7publickey'] ? new Key($object['pkcs7publickey'], Key::PKCS7_MIME) : new Key();
|
||||
|
||||
if ($pgp_index >= 0)
|
||||
$keys->set($pgp_index, $pgpkey);
|
||||
else if (!empty($object['pgppublickey']))
|
||||
$keys->push($pgpkey);
|
||||
if ($pkcs7_index >= 0)
|
||||
$keys->set($pkcs7_index, $pkcs7key);
|
||||
else if (!empty($object['pkcs7publickey']))
|
||||
$keys->push($pkcs7key);
|
||||
|
||||
$this->obj->setKeys($keys);
|
||||
|
||||
// TODO: handle language, gpslocation, etc.
|
||||
|
||||
|
||||
// cache this data
|
||||
$this->data = $object;
|
||||
unset($this->data['_formatobj']);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function is_valid()
|
||||
{
|
||||
return $this->data || (is_object($this->obj) && $this->obj->uid() /*$this->obj->isValid()*/);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the Contact object into a hash array data structure
|
||||
*
|
||||
* @return array Contact data as hash array
|
||||
*/
|
||||
public function to_array()
|
||||
{
|
||||
// return cached result
|
||||
if (!empty($this->data))
|
||||
return $this->data;
|
||||
|
||||
$this->init();
|
||||
|
||||
// read object properties into local data object
|
||||
$object = array(
|
||||
'uid' => $this->obj->uid(),
|
||||
# 'changed' => $this->obj->lastModified(),
|
||||
'name' => $this->obj->name(),
|
||||
);
|
||||
|
||||
$nc = $this->obj->nameComponents();
|
||||
$object['surname'] = join(' ', self::vector2array($nc->surnames()));
|
||||
$object['firstname'] = join(' ', self::vector2array($nc->given()));
|
||||
$object['middlename'] = join(' ', self::vector2array($nc->additional()));
|
||||
$object['prefix'] = join(' ', self::vector2array($nc->prefixes()));
|
||||
$object['suffix'] = join(' ', self::vector2array($nc->suffixes()));
|
||||
$object['nickname'] = join(' ', self::vector2array($this->obj->nickNames()));
|
||||
$object['profession'] = join(' ', self::vector2array($this->obj->titles()));
|
||||
|
||||
// organisation related properties (affiliation)
|
||||
$orgs = $this->obj->affiliations();
|
||||
if ($orgs->size()) {
|
||||
$org = $orgs->get(0);
|
||||
$object['organization'] = $org->organisation();
|
||||
$object['jobtitle'] = join(' ', self::vector2array($org->roles()));
|
||||
$object['department'] = join(' ', self::vector2array($org->organisationalUnits()));
|
||||
$this->read_relateds($org->relateds(), $object);
|
||||
}
|
||||
|
||||
$object['email'] = self::vector2array($this->obj->emailAddresses());
|
||||
$object['im'] = self::vector2array($this->obj->imAddresses());
|
||||
|
||||
$urls = $this->obj->urls();
|
||||
for ($i=0; $i < $urls->size(); $i++) {
|
||||
$url = $urls->get($i);
|
||||
$subtype = $url->type() == Url::Blog ? 'blog' : 'homepage';
|
||||
$object['website'][] = array('url' => $url->url(), 'type' => $subtype);
|
||||
}
|
||||
|
||||
// addresses
|
||||
$this->read_addresses($this->obj->addresses(), $object);
|
||||
if ($org && ($offices = $org->addresses()))
|
||||
$this->read_addresses($offices, $object, 'office');
|
||||
|
||||
// telehones
|
||||
$tels = $this->obj->telephones();
|
||||
$teltypes = array_flip($this->phonetypes);
|
||||
for ($i=0; $i < $tels->size(); $i++) {
|
||||
$tel = $tels->get($i);
|
||||
$object['phone'][] = array('number' => $tel->number(), 'type' => $teltypes[$tel->types()]);
|
||||
}
|
||||
|
||||
$object['notes'] = $this->obj->note();
|
||||
$object['freebusyurl'] = $this->obj->freeBusyUrl();
|
||||
|
||||
if ($bday = self::php_datetime($this->obj->bDay()))
|
||||
$object['birthday'] = $bday->format('c');
|
||||
|
||||
if ($anniversary = self::php_datetime($this->obj->anniversary()))
|
||||
$object['anniversary'] = $anniversary->format('c');
|
||||
|
||||
$gendermap = array_flip($this->gendermap);
|
||||
if (($g = $this->obj->gender()) && $gendermap[$g])
|
||||
$object['gender'] = $gendermap[$g];
|
||||
|
||||
if ($this->obj->photoMimetype())
|
||||
$object['photo'] = $this->obj->photo();
|
||||
|
||||
// relateds -> spouse, children
|
||||
$this->read_relateds($this->obj->relateds(), $object);
|
||||
|
||||
// crypto settings: currently only key values are supported
|
||||
$keys = $this->obj->keys();
|
||||
for ($i=0; is_object($keys) && $i < $keys->size(); $i++) {
|
||||
$key = $keys->get($i);
|
||||
if ($key->type() == Key::PGP)
|
||||
$object['pgppublickey'] = $key->key();
|
||||
else if ($key->type() == Key::PKCS7_MIME)
|
||||
$object['pkcs7publickey'] = $key->key();
|
||||
}
|
||||
|
||||
$this->data = $object;
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for kolab_storage_cache to get words to index for fulltext search
|
||||
*
|
||||
* @return array List of words to save in cache
|
||||
*/
|
||||
public function get_words()
|
||||
{
|
||||
$data = '';
|
||||
foreach (self::$fulltext_cols as $col) {
|
||||
$val = is_array($this->data[$col]) ? join(' ', $this->data[$col]) : $this->data[$col];
|
||||
if (strlen($val))
|
||||
$data .= $val . ' ';
|
||||
}
|
||||
|
||||
return array_unique(rcube_utils::normalize_string($data, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load data from old Kolab2 format
|
||||
*
|
||||
* @param array Hash array with object properties
|
||||
*/
|
||||
public function fromkolab2($record)
|
||||
{
|
||||
$object = array(
|
||||
'uid' => $record['uid'],
|
||||
'email' => array(),
|
||||
'phone' => array(),
|
||||
);
|
||||
|
||||
foreach ($this->kolab2_fieldmap as $kolab => $rcube) {
|
||||
if (is_array($record[$kolab]) || strlen($record[$kolab]))
|
||||
$object[$rcube] = $record[$kolab];
|
||||
}
|
||||
|
||||
if (isset($record['gender']))
|
||||
$object['gender'] = $this->kolab2_gender[$record['gender']];
|
||||
|
||||
foreach ((array)$record['email'] as $i => $email)
|
||||
$object['email'][] = $email['smtp-address'];
|
||||
|
||||
if (!$record['email'] && $record['emails'])
|
||||
$object['email'] = preg_split('/,\s*/', $record['emails']);
|
||||
|
||||
if (is_array($record['address'])) {
|
||||
foreach ($record['address'] as $i => $adr) {
|
||||
$object['address'][] = array(
|
||||
'type' => $this->kolab2_addresstypes[$adr['type']] ? $this->kolab2_addresstypes[$adr['type']] : $adr['type'],
|
||||
'street' => $adr['street'],
|
||||
'locality' => $adr['locality'],
|
||||
'code' => $adr['postal-code'],
|
||||
'region' => $adr['region'],
|
||||
'country' => $adr['country'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// office location goes into an address block
|
||||
if ($record['office-location'])
|
||||
$object['address'][] = array('type' => 'office', 'locality' => $record['office-location']);
|
||||
|
||||
// merge initials into nickname
|
||||
if ($record['initials'])
|
||||
$object['nickname'] = trim($object['nickname'] . ', ' . $record['initials'], ', ');
|
||||
|
||||
// remove empty fields
|
||||
$this->data = array_filter($object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to copy contents of an Address vector to the contact data object
|
||||
*/
|
||||
private function read_addresses($addresses, &$object, $type = null)
|
||||
{
|
||||
$adrtypes = array_flip($this->addresstypes);
|
||||
|
||||
for ($i=0; $i < $addresses->size(); $i++) {
|
||||
$adr = $addresses->get($i);
|
||||
$object['address'][] = array(
|
||||
'type' => $type ? $type : ($adrtypes[$adr->types()] ? $adrtypes[$adr->types()] : ''), /*$adr->label()),*/
|
||||
'street' => $adr->street(),
|
||||
'code' => $adr->code(),
|
||||
'locality' => $adr->locality(),
|
||||
'region' => $adr->region(),
|
||||
'country' => $adr->country()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to map contents of a Related vector to the contact data object
|
||||
*/
|
||||
private function read_relateds($rels, &$object)
|
||||
{
|
||||
$typemap = array_flip($this->relatedmap);
|
||||
|
||||
for ($i=0; $i < $rels->size(); $i++) {
|
||||
$rel = $rels->get($i);
|
||||
if ($rel->type() != Related::Text) // we can't handle UID relations yet
|
||||
continue;
|
||||
|
||||
$types = $rel->relationTypes();
|
||||
foreach ($typemap as $t => $field) {
|
||||
if ($types & $t) {
|
||||
$object[$field][] = $rel->text();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
160
plugins/libkolab/lib/kolab_format_distributionlist.php
Normal file
160
plugins/libkolab/lib/kolab_format_distributionlist.php
Normal file
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab Distribution List model class
|
||||
*
|
||||
* @version @package_version@
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012, 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 kolab_format_distributionlist extends kolab_format
|
||||
{
|
||||
public $CTYPE = 'application/vcard+xml';
|
||||
|
||||
function __construct($xmldata = null)
|
||||
{
|
||||
$this->obj = new DistList;
|
||||
$this->xmldata = $xmldata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Kolab object data from the given XML block
|
||||
*
|
||||
* @param string XML data
|
||||
*/
|
||||
public function load($xml)
|
||||
{
|
||||
$this->obj = kolabformat::readDistlist($xml, false);
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write object data to XML format
|
||||
*
|
||||
* @return string XML data
|
||||
*/
|
||||
public function write()
|
||||
{
|
||||
$this->init();
|
||||
$this->xmldata = kolabformat::writeDistlist($this->obj);
|
||||
|
||||
if (!parent::format_errors())
|
||||
parent::update_uid();
|
||||
else
|
||||
$this->xmldata = null;
|
||||
|
||||
return $this->xmldata;
|
||||
}
|
||||
|
||||
public function set(&$object)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
// set some automatic values if missing
|
||||
if (!empty($object['uid']))
|
||||
$this->obj->setUid($object['uid']);
|
||||
|
||||
$this->obj->setName($object['name']);
|
||||
|
||||
$seen = array();
|
||||
$members = new vectorcontactref;
|
||||
foreach ($object['member'] as $member) {
|
||||
if ($member['uid'])
|
||||
$m = new ContactReference(ContactReference::UidReference, $member['uid']);
|
||||
else if ($member['email'])
|
||||
$m = new ContactReference(ContactReference::EmailReference, $member['email']);
|
||||
else
|
||||
continue;
|
||||
|
||||
$m->setName($member['name']);
|
||||
$members->push($m);
|
||||
$seen[$member['email']]++;
|
||||
}
|
||||
|
||||
$this->obj->setMembers($members);
|
||||
|
||||
// cache this data
|
||||
$this->data = $object;
|
||||
unset($this->data['_formatobj']);
|
||||
}
|
||||
|
||||
public function is_valid()
|
||||
{
|
||||
return $this->data || (is_object($this->obj) && $this->obj->isValid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Load data from old Kolab2 format
|
||||
*/
|
||||
public function fromkolab2($record)
|
||||
{
|
||||
$object = array(
|
||||
'uid' => $record['uid'],
|
||||
'changed' => $record['last-modification-date'],
|
||||
'name' => $record['last-name'],
|
||||
'member' => array(),
|
||||
);
|
||||
|
||||
foreach ($record['member'] as $member) {
|
||||
$object['member'][] = array(
|
||||
'email' => $member['smtp-address'],
|
||||
'name' => $member['display-name'],
|
||||
'uid' => $member['uid'],
|
||||
);
|
||||
}
|
||||
|
||||
$this->data = $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the Distlist object into a hash array data structure
|
||||
*
|
||||
* @return array Distribution list data as hash array
|
||||
*/
|
||||
public function to_array()
|
||||
{
|
||||
// return cached result
|
||||
if (!empty($this->data))
|
||||
return $this->data;
|
||||
|
||||
$this->init();
|
||||
|
||||
// read object properties
|
||||
$object = array(
|
||||
'uid' => $this->obj->uid(),
|
||||
# 'changed' => $this->obj->lastModified(),
|
||||
'name' => $this->obj->name(),
|
||||
'member' => array(),
|
||||
);
|
||||
|
||||
$members = $this->obj->members();
|
||||
for ($i=0; $i < $members->size(); $i++) {
|
||||
$member = $members->get($i);
|
||||
# if ($member->type() == ContactReference::UidReference && ($uid = $member->uid()))
|
||||
$object['member'][] = array(
|
||||
'uid' => $member->uid(),
|
||||
'email' => $member->email(),
|
||||
'name' => $member->name(),
|
||||
);
|
||||
}
|
||||
|
||||
$this->data = $object;
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
}
|
638
plugins/libkolab/lib/kolab_format_event.php
Normal file
638
plugins/libkolab/lib/kolab_format_event.php
Normal file
|
@ -0,0 +1,638 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab Event model class
|
||||
*
|
||||
* @version @package_version@
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012, 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 kolab_format_event extends kolab_format
|
||||
{
|
||||
public $CTYPE = 'application/calendar+xml';
|
||||
|
||||
public static $fulltext_cols = array('title', 'description', 'location', 'attendees:name', 'attendees:email');
|
||||
|
||||
private $sensitivity_map = array(
|
||||
'public' => kolabformat::ClassPublic,
|
||||
'private' => kolabformat::ClassPrivate,
|
||||
'confidential' => kolabformat::ClassConfidential,
|
||||
);
|
||||
|
||||
private $role_map = array(
|
||||
'REQ-PARTICIPANT' => kolabformat::Required,
|
||||
'OPT-PARTICIPANT' => kolabformat::Optional,
|
||||
'NON-PARTICIPANT' => kolabformat::NonParticipant,
|
||||
'CHAIR' => kolabformat::Chair,
|
||||
);
|
||||
|
||||
private $rrule_type_map = array(
|
||||
'MINUTELY' => RecurrenceRule::Minutely,
|
||||
'HOURLY' => RecurrenceRule::Hourly,
|
||||
'DAILY' => RecurrenceRule::Daily,
|
||||
'WEEKLY' => RecurrenceRule::Weekly,
|
||||
'MONTHLY' => RecurrenceRule::Monthly,
|
||||
'YEARLY' => RecurrenceRule::Yearly,
|
||||
);
|
||||
|
||||
private $weekday_map = array(
|
||||
'MO' => kolabformat::Monday,
|
||||
'TU' => kolabformat::Tuesday,
|
||||
'WE' => kolabformat::Wednesday,
|
||||
'TH' => kolabformat::Thursday,
|
||||
'FR' => kolabformat::Friday,
|
||||
'SA' => kolabformat::Saturday,
|
||||
'SU' => kolabformat::Sunday,
|
||||
);
|
||||
|
||||
private $alarm_type_map = array(
|
||||
'DISPLAY' => Alarm::DisplayAlarm,
|
||||
'EMAIL' => Alarm::EMailAlarm,
|
||||
'AUDIO' => Alarm::AudioAlarm,
|
||||
);
|
||||
|
||||
private $status_map = array(
|
||||
'UNKNOWN' => kolabformat::PartNeedsAction,
|
||||
'NEEDS-ACTION' => kolabformat::PartNeedsAction,
|
||||
'TENTATIVE' => kolabformat::PartTentative,
|
||||
'ACCEPTED' => kolabformat::PartAccepted,
|
||||
'DECLINED' => kolabformat::PartDeclined,
|
||||
'DELEGATED' => kolabformat::PartDelegated,
|
||||
);
|
||||
|
||||
private $kolab2_rolemap = array(
|
||||
'required' => 'REQ-PARTICIPANT',
|
||||
'optional' => 'OPT-PARTICIPANT',
|
||||
'resource' => 'CHAIR',
|
||||
);
|
||||
private $kolab2_statusmap = array(
|
||||
'none' => 'NEEDS-ACTION',
|
||||
'tentative' => 'TENTATIVE',
|
||||
'accepted' => 'CONFIRMED',
|
||||
'accepted' => 'ACCEPTED',
|
||||
'declined' => 'DECLINED',
|
||||
);
|
||||
private $kolab2_monthmap = array('', 'january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december');
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
function __construct($xmldata = null)
|
||||
{
|
||||
$this->obj = new Event;
|
||||
$this->xmldata = $xmldata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Contact object data from the given XML block
|
||||
*
|
||||
* @param string XML data
|
||||
*/
|
||||
public function load($xml)
|
||||
{
|
||||
$this->obj = kolabformat::readEvent($xml, false);
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write Contact object data to XML format
|
||||
*
|
||||
* @return string XML data
|
||||
*/
|
||||
public function write()
|
||||
{
|
||||
$this->init();
|
||||
$this->xmldata = kolabformat::writeEvent($this->obj);
|
||||
|
||||
if (!parent::format_errors())
|
||||
parent::update_uid();
|
||||
else
|
||||
$this->xmldata = null;
|
||||
|
||||
return $this->xmldata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set contact properties to the kolabformat object
|
||||
*
|
||||
* @param array Contact data as hash array
|
||||
*/
|
||||
public function set(&$object)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
// set some automatic values if missing
|
||||
if (!$this->obj->created()) {
|
||||
if (!empty($object['created']))
|
||||
$object['created'] = new DateTime('now', self::$timezone);
|
||||
$this->obj->setCreated(self::get_datetime($object['created']));
|
||||
}
|
||||
|
||||
if (!empty($object['uid']))
|
||||
$this->obj->setUid($object['uid']);
|
||||
|
||||
// increment sequence
|
||||
$this->obj->setSequence($this->obj->sequence()+1);
|
||||
|
||||
// do the hard work of setting object values
|
||||
$this->obj->setStart(self::get_datetime($object['start'], null, $object['allday']));
|
||||
$this->obj->setEnd(self::get_datetime($object['end'], null, $object['allday']));
|
||||
$this->obj->setSummary($object['title']);
|
||||
$this->obj->setLocation($object['location']);
|
||||
$this->obj->setDescription($object['description']);
|
||||
$this->obj->setPriority($object['priority']);
|
||||
$this->obj->setClassification($this->sensitivity_map[$object['sensitivity']]);
|
||||
$this->obj->setCategories(self::array2vector($object['categories']));
|
||||
$this->obj->setTransparency($object['free_busy'] == 'free');
|
||||
|
||||
$status = kolabformat::StatusUndefined;
|
||||
if ($object['free_busy'] == 'tentative')
|
||||
$status = kolabformat::StatusTentative;
|
||||
if ($object['cancelled'])
|
||||
$status = kolabformat::StatusCancelled;
|
||||
$this->obj->setStatus($status);
|
||||
|
||||
// process event attendees
|
||||
$organizer = new ContactReference;
|
||||
$attendees = new vectorattendee;
|
||||
foreach ((array)$object['attendees'] as $attendee) {
|
||||
$cr = new ContactReference(ContactReference::EmailReference, $attendee['email']);
|
||||
$cr->setName($attendee['name']);
|
||||
|
||||
if ($attendee['role'] == 'ORGANIZER') {
|
||||
$organizer = $cr;
|
||||
}
|
||||
else {
|
||||
$att = new Attendee;
|
||||
$att->setContact($cr);
|
||||
$att->setPartStat($this->status_map[$attendee['status']]);
|
||||
$att->setRole($this->role_map[$attendee['role']] ? $this->role_map[$attendee['role']] : kolabformat::Required);
|
||||
$att->setRSVP((bool)$attendee['rsvp']);
|
||||
|
||||
if ($att->isValid()) {
|
||||
$attendees->push($att);
|
||||
}
|
||||
else {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Invalid event attendee: " . json_encode($attendee),
|
||||
), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->obj->setOrganizer($organizer);
|
||||
$this->obj->setAttendees($attendees);
|
||||
|
||||
// save recurrence rule
|
||||
if ($object['recurrence']) {
|
||||
$rr = new RecurrenceRule;
|
||||
$rr->setFrequency($this->rrule_type_map[$object['recurrence']['FREQ']]);
|
||||
|
||||
if ($object['recurrence']['INTERVAL'])
|
||||
$rr->setInterval(intval($object['recurrence']['INTERVAL']));
|
||||
|
||||
if ($object['recurrence']['BYDAY']) {
|
||||
$byday = new vectordaypos;
|
||||
foreach (explode(',', $object['recurrence']['BYDAY']) as $day) {
|
||||
$occurrence = 0;
|
||||
if (preg_match('/^([\d-]+)([A-Z]+)$/', $day, $m)) {
|
||||
$occurrence = intval($m[1]);
|
||||
$day = $m[2];
|
||||
}
|
||||
if (isset($this->weekday_map[$day]))
|
||||
$byday->push(new DayPos($occurrence, $this->weekday_map[$day]));
|
||||
}
|
||||
$rr->setByday($byday);
|
||||
}
|
||||
|
||||
if ($object['recurrence']['BYMONTHDAY']) {
|
||||
$bymday = new vectori;
|
||||
foreach (explode(',', $object['recurrence']['BYMONTHDAY']) as $day)
|
||||
$bymday->push(intval($day));
|
||||
$rr->setBymonthday($bymday);
|
||||
}
|
||||
|
||||
if ($object['recurrence']['BYMONTH']) {
|
||||
$bymonth = new vectori;
|
||||
foreach (explode(',', $object['recurrence']['BYMONTH']) as $month)
|
||||
$bymonth->push(intval($month));
|
||||
$rr->setBymonth($bymonth);
|
||||
}
|
||||
|
||||
if ($object['recurrence']['COUNT'])
|
||||
$rr->setCount(intval($object['recurrence']['COUNT']));
|
||||
else if ($object['recurrence']['UNTIL'])
|
||||
$rr->setEnd(self::get_datetime($object['recurrence']['UNTIL'], null, true));
|
||||
|
||||
if ($rr->isValid()) {
|
||||
$this->obj->setRecurrenceRule($rr);
|
||||
|
||||
// add exception dates (only if recurrence rule is valid)
|
||||
$exdates = new vectordatetime;
|
||||
foreach ((array)$object['recurrence']['EXDATE'] as $exdate)
|
||||
$exdates->push(self::get_datetime($exdate, null, true));
|
||||
$this->obj->setExceptionDates($exdates);
|
||||
}
|
||||
else {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Invalid event recurrence rule: " . json_encode($object['recurrence']),
|
||||
), true);
|
||||
}
|
||||
}
|
||||
|
||||
// save alarm
|
||||
$valarms = new vectoralarm;
|
||||
if ($object['alarms']) {
|
||||
list($offset, $type) = explode(":", $object['alarms']);
|
||||
|
||||
if ($type == 'EMAIL') { // email alarms implicitly go to event owner
|
||||
$recipients = new vectorcontactref;
|
||||
$recipients->push(new ContactReference(ContactReference::EmailReference, $object['_owner']));
|
||||
$alarm = new Alarm($object['title'], strval($object['description']), $recipients);
|
||||
}
|
||||
else { // default: display alarm
|
||||
$alarm = new Alarm($object['title']);
|
||||
}
|
||||
|
||||
if (preg_match('/^@(\d+)/', $offset, $d)) {
|
||||
$alarm->setStart(self::get_datetime($d[1], new DateTimeZone('UTC')));
|
||||
}
|
||||
else if (preg_match('/^([-+]?)(\d+)([SMHDW])/', $offset, $d)) {
|
||||
$days = $hours = $minutes = $seconds = 0;
|
||||
switch ($d[3]) {
|
||||
case 'W': $days = 7*intval($d[2]); break;
|
||||
case 'D': $days = intval($d[2]); break;
|
||||
case 'H': $hours = intval($d[2]); break;
|
||||
case 'M': $minutes = intval($d[2]); break;
|
||||
case 'S': $seconds = intval($d[2]); break;
|
||||
}
|
||||
$alarm->setRelativeStart(new Duration($days, $hours, $minutes, $seconds, $d[1] == '-'), $d[1] == '-' ? kolabformat::Start : kolabformat::End);
|
||||
}
|
||||
|
||||
$valarms->push($alarm);
|
||||
}
|
||||
$this->obj->setAlarms($valarms);
|
||||
|
||||
// save attachments
|
||||
$vattach = new vectorattachment;
|
||||
foreach ((array)$object['_attachments'] as $name => $attr) {
|
||||
if (empty($attr))
|
||||
continue;
|
||||
$attach = new Attachment;
|
||||
$attach->setLabel($name);
|
||||
$attach->setUri('cid:' . $name, $attr['mimetype']);
|
||||
$vattach->push($attach);
|
||||
}
|
||||
$this->obj->setAttachments($vattach);
|
||||
|
||||
// cache this data
|
||||
$this->data = $object;
|
||||
unset($this->data['_formatobj']);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function is_valid()
|
||||
{
|
||||
return $this->data || (is_object($this->obj) && $this->obj->isValid() && $this->obj->uid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the Contact object into a hash array data structure
|
||||
*
|
||||
* @return array Contact data as hash array
|
||||
*/
|
||||
public function to_array()
|
||||
{
|
||||
// return cached result
|
||||
if (!empty($this->data))
|
||||
return $this->data;
|
||||
|
||||
$this->init();
|
||||
|
||||
$sensitivity_map = array_flip($this->sensitivity_map);
|
||||
|
||||
// read object properties
|
||||
$object = array(
|
||||
'uid' => $this->obj->uid(),
|
||||
'changed' => self::php_datetime($this->obj->lastModified()),
|
||||
'title' => $this->obj->summary(),
|
||||
'location' => $this->obj->location(),
|
||||
'description' => $this->obj->description(),
|
||||
'allday' => $this->obj->start()->isDateOnly(),
|
||||
'start' => self::php_datetime($this->obj->start()),
|
||||
'end' => self::php_datetime($this->obj->end()),
|
||||
'categories' => self::vector2array($this->obj->categories()),
|
||||
'free_busy' => $this->obj->transparency() ? 'free' : 'busy', // TODO: transparency is only boolean
|
||||
'sensitivity' => $sensitivity_map[$this->obj->classification()],
|
||||
'priority' => $this->obj->priority(),
|
||||
);
|
||||
|
||||
// status defines different event properties...
|
||||
$status = $this->obj->status();
|
||||
if ($status == kolabformat::StatusTentative)
|
||||
$object['free_busy'] = 'tentative';
|
||||
else if ($status == kolabformat::StatusCancelled)
|
||||
$objec['cancelled'] = true;
|
||||
|
||||
// read organizer and attendees
|
||||
if ($organizer = $this->obj->organizer()) {
|
||||
$object['attendees'][] = array(
|
||||
'role' => 'ORGANIZER',
|
||||
'email' => $organizer->email(),
|
||||
'name' => $organizer->name(),
|
||||
);
|
||||
}
|
||||
|
||||
$role_map = array_flip($this->role_map);
|
||||
$status_map = array_flip($this->status_map);
|
||||
$attvec = $this->obj->attendees();
|
||||
for ($i=0; $i < $attvec->size(); $i++) {
|
||||
$attendee = $attvec->get($i);
|
||||
$cr = $attendee->contact();
|
||||
$object['attendees'][] = array(
|
||||
'role' => $role_map[$attendee->role()],
|
||||
'status' => $status_map[$attendee->partStat()],
|
||||
'rsvp' => $attendee->rsvp(),
|
||||
'email' => $cr->email(),
|
||||
'name' => $cr->name(),
|
||||
);
|
||||
}
|
||||
|
||||
// read recurrence rule
|
||||
if (($rr = $this->obj->recurrenceRule()) && $rr->isValid()) {
|
||||
$rrule_type_map = array_flip($this->rrule_type_map);
|
||||
$object['recurrence'] = array('FREQ' => $rrule_type_map[$rr->frequency()]);
|
||||
|
||||
if ($intvl = $rr->interval())
|
||||
$object['recurrence']['INTERVAL'] = $intvl;
|
||||
|
||||
if (($count = $rr->count()) && $count > 0) {
|
||||
$object['recurrence']['COUNT'] = $count;
|
||||
}
|
||||
else if ($until = self::php_datetime($rr->end())) {
|
||||
$until->setTime($object['start']->format('G'), $object['start']->format('i'), 0);
|
||||
$object['recurrence']['UNTIL'] = $until->format('U');
|
||||
}
|
||||
|
||||
if (($byday = $rr->byday()) && $byday->size()) {
|
||||
$weekday_map = array_flip($this->weekday_map);
|
||||
$weekdays = array();
|
||||
for ($i=0; $i < $byday->size(); $i++) {
|
||||
$daypos = $byday->get($i);
|
||||
$prefix = $daypos->occurence();
|
||||
$weekdays[] = ($prefix ? $prefix : '') . $weekday_map[$daypos->weekday()];
|
||||
}
|
||||
$object['recurrence']['BYDAY'] = join(',', $weekdays);
|
||||
}
|
||||
|
||||
if (($bymday = $rr->bymonthday()) && $bymday->size()) {
|
||||
$object['recurrence']['BYMONTHDAY'] = join(',', self::vector2array($bymday));
|
||||
}
|
||||
|
||||
if (($bymonth = $rr->bymonth()) && $bymonth->size()) {
|
||||
$object['recurrence']['BYMONTH'] = join(',', self::vector2array($bymonth));
|
||||
}
|
||||
|
||||
if ($exceptions = $this->obj->exceptionDates()) {
|
||||
for ($i=0; $i < $exceptions->size(); $i++) {
|
||||
if ($exdate = self::php_datetime($exceptions->get($i)))
|
||||
$object['recurrence']['EXDATE'][] = $exdate->format('U');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// read alarm
|
||||
$valarms = $this->obj->alarms();
|
||||
$alarm_types = array_flip($this->alarm_type_map);
|
||||
for ($i=0; $i < $valarms->size(); $i++) {
|
||||
$alarm = $valarms->get($i);
|
||||
$type = $alarm_types[$alarm->type()];
|
||||
|
||||
if ($type == 'DISPLAY' || $type == 'EMAIL') { // only DISPLAY and EMAIL alarms are supported
|
||||
if ($start = self::php_datetime($alarm->start())) {
|
||||
$object['alarms'] = '@' . $start->format('U');
|
||||
}
|
||||
else if ($offset = $alarm->relativeStart()) {
|
||||
$value = $alarm->relativeTo() == kolabformat::End ? '+' : '-';
|
||||
if ($w = $offset->weeks()) $value .= $w . 'W';
|
||||
else if ($d = $offset->days()) $value .= $d . 'D';
|
||||
else if ($h = $offset->hours()) $value .= $h . 'H';
|
||||
else if ($m = $offset->minutes()) $value .= $m . 'M';
|
||||
else if ($s = $offset->seconds()) $value .= $s . 'S';
|
||||
else continue;
|
||||
|
||||
$object['alarms'] = $value;
|
||||
}
|
||||
$object['alarms'] .= ':' . $type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// handle attachments
|
||||
$vattach = $this->obj->attachments();
|
||||
for ($i=0; $i < $vattach->size(); $i++) {
|
||||
$attach = $vattach->get($i);
|
||||
|
||||
// skip cid: attachments which are mime message parts handled by kolab_storage_folder
|
||||
if (substr($attach->uri(), 0, 4) != 'cid') {
|
||||
$name = $attach->label();
|
||||
$data = $attach->data();
|
||||
$object['_attachments'][$name] = array(
|
||||
'mimetype' => $attach->mimetype(),
|
||||
'size' => strlen($data),
|
||||
'content' => $data,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->data = $object;
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for kolab_storage_cache to get object specific tags to cache
|
||||
*
|
||||
* @return array List of tags to save in cache
|
||||
*/
|
||||
public function get_tags()
|
||||
{
|
||||
$tags = array();
|
||||
|
||||
foreach ((array)$this->data['categories'] as $cat) {
|
||||
$tags[] = rcube_utils::normalize_string($cat);
|
||||
}
|
||||
|
||||
if (!empty($this->data['alarms'])) {
|
||||
$tags[] = 'x-has-alarms';
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for kolab_storage_cache to get words to index for fulltext search
|
||||
*
|
||||
* @return array List of words to save in cache
|
||||
*/
|
||||
public function get_words()
|
||||
{
|
||||
$data = '';
|
||||
foreach (self::$fulltext_cols as $colname) {
|
||||
list($col, $field) = explode(':', $colname);
|
||||
|
||||
if ($field) {
|
||||
$a = array();
|
||||
foreach ((array)$this->data[$col] as $attr)
|
||||
$a[] = $attr[$field];
|
||||
$val = join(' ', $a);
|
||||
}
|
||||
else {
|
||||
$val = is_array($this->data[$col]) ? join(' ', $this->data[$col]) : $this->data[$col];
|
||||
}
|
||||
|
||||
if (strlen($val))
|
||||
$data .= $val . ' ';
|
||||
}
|
||||
|
||||
return array_unique(rcube_utils::normalize_string($data, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load data from old Kolab2 format
|
||||
*/
|
||||
public function fromkolab2($rec)
|
||||
{
|
||||
if (PEAR::isError($rec))
|
||||
return;
|
||||
|
||||
$start_time = date('H:i:s', $rec['start-date']);
|
||||
$allday = $rec['_is_all_day'] || ($start_time == '00:00:00' && $start_time == date('H:i:s', $rec['end-date']));
|
||||
|
||||
// in Roundcube all-day events go from 12:00 to 13:00
|
||||
if ($allday) {
|
||||
$now = new DateTime('now', self::$timezone);
|
||||
$gmt_offset = $now->getOffset();
|
||||
|
||||
$rec['start-date'] += 12 * 3600;
|
||||
$rec['end-date'] -= 11 * 3600;
|
||||
$rec['end-date'] -= $gmt_offset - date('Z', $rec['end-date']); // shift times from server's timezone to user's timezone
|
||||
$rec['start-date'] -= $gmt_offset - date('Z', $rec['start-date']); // because generated with mktime() in Horde_Kolab_Format_Date::decodeDate()
|
||||
// sanity check
|
||||
if ($rec['end-date'] <= $rec['start-date'])
|
||||
$rec['end-date'] += 86400;
|
||||
}
|
||||
|
||||
// convert alarm time into internal format
|
||||
if ($rec['alarm']) {
|
||||
$alarm_value = $rec['alarm'];
|
||||
$alarm_unit = 'M';
|
||||
if ($rec['alarm'] % 1440 == 0) {
|
||||
$alarm_value /= 1440;
|
||||
$alarm_unit = 'D';
|
||||
}
|
||||
else if ($rec['alarm'] % 60 == 0) {
|
||||
$alarm_value /= 60;
|
||||
$alarm_unit = 'H';
|
||||
}
|
||||
$alarm_value *= -1;
|
||||
}
|
||||
|
||||
// convert recurrence rules into internal pseudo-vcalendar format
|
||||
if ($recurrence = $rec['recurrence']) {
|
||||
$rrule = array(
|
||||
'FREQ' => strtoupper($recurrence['cycle']),
|
||||
'INTERVAL' => intval($recurrence['interval']),
|
||||
);
|
||||
|
||||
if ($recurrence['range-type'] == 'number')
|
||||
$rrule['COUNT'] = intval($recurrence['range']);
|
||||
else if ($recurrence['range-type'] == 'date')
|
||||
$rrule['UNTIL'] = $recurrence['range'];
|
||||
|
||||
if ($recurrence['day']) {
|
||||
$byday = array();
|
||||
$prefix = ($rrule['FREQ'] == 'MONTHLY' || $rrule['FREQ'] == 'YEARLY') ? intval($recurrence['daynumber'] ? $recurrence['daynumber'] : 1) : '';
|
||||
foreach ($recurrence['day'] as $day)
|
||||
$byday[] = $prefix . substr(strtoupper($day), 0, 2);
|
||||
$rrule['BYDAY'] = join(',', $byday);
|
||||
}
|
||||
if ($recurrence['daynumber']) {
|
||||
if ($recurrence['type'] == 'monthday' || $recurrence['type'] == 'daynumber')
|
||||
$rrule['BYMONTHDAY'] = $recurrence['daynumber'];
|
||||
else if ($recurrence['type'] == 'yearday')
|
||||
$rrule['BYYEARDAY'] = $recurrence['daynumber'];
|
||||
}
|
||||
if ($recurrence['month']) {
|
||||
$monthmap = array_flip($this->kolab2_monthmap);
|
||||
$rrule['BYMONTH'] = strtolower($monthmap[$recurrence['month']]);
|
||||
}
|
||||
|
||||
if ($recurrence['exclusion']) {
|
||||
foreach ((array)$recurrence['exclusion'] as $excl)
|
||||
$rrule['EXDATE'][] = strtotime($excl . date(' H:i:s', $rec['start-date'])); // use time of event start
|
||||
}
|
||||
}
|
||||
|
||||
$attendees = array();
|
||||
if ($rec['organizer']) {
|
||||
$attendees[] = array(
|
||||
'role' => 'ORGANIZER',
|
||||
'name' => $rec['organizer']['display-name'],
|
||||
'email' => $rec['organizer']['smtp-address'],
|
||||
'status' => 'ACCEPTED',
|
||||
);
|
||||
$_attendees .= $rec['organizer']['display-name'] . ' ' . $rec['organizer']['smtp-address'] . ' ';
|
||||
}
|
||||
|
||||
foreach ((array)$rec['attendee'] as $attendee) {
|
||||
$attendees[] = array(
|
||||
'role' => $this->kolab2_rolemap[$attendee['role']],
|
||||
'name' => $attendee['display-name'],
|
||||
'email' => $attendee['smtp-address'],
|
||||
'status' => $this->kolab2_statusmap[$attendee['status']],
|
||||
'rsvp' => $attendee['request-response'],
|
||||
);
|
||||
$_attendees .= $rec['organizer']['display-name'] . ' ' . $rec['organizer']['smtp-address'] . ' ';
|
||||
}
|
||||
|
||||
$this->data = array(
|
||||
'uid' => $rec['uid'],
|
||||
'title' => $rec['summary'],
|
||||
'location' => $rec['location'],
|
||||
'description' => $rec['body'],
|
||||
'start' => $rec['start-date'],
|
||||
'end' => $rec['end-date'],
|
||||
'allday' => $allday,
|
||||
'recurrence' => $rrule,
|
||||
'alarms' => $alarm_value . $alarm_unit,
|
||||
'categories' => explode(',', $rec['categories']),
|
||||
'attachments' => $attachments,
|
||||
'attendees' => $attendees,
|
||||
'free_busy' => $rec['show-time-as'],
|
||||
'priority' => $rec['priority'],
|
||||
'sensitivity' => $rec['sensitivity'],
|
||||
'changed' => $rec['last-modification-date'],
|
||||
);
|
||||
}
|
||||
}
|
462
plugins/libkolab/lib/kolab_storage.php
Normal file
462
plugins/libkolab/lib/kolab_storage.php
Normal file
|
@ -0,0 +1,462 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab storage class providing static methods to access groupware objects on a Kolab server.
|
||||
*
|
||||
* @version @package_version@
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012, 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 kolab_storage
|
||||
{
|
||||
const CTYPE_KEY = '/shared/vendor/kolab/folder-type';
|
||||
const SERVERSIDE_SUBSCRIPTION = 0;
|
||||
const CLIENTSIDE_SUBSCRIPTION = 1;
|
||||
|
||||
public static $last_error;
|
||||
|
||||
private static $ready = false;
|
||||
private static $config;
|
||||
private static $cache;
|
||||
private static $imap;
|
||||
|
||||
|
||||
/**
|
||||
* Setup the environment needed by the libs
|
||||
*/
|
||||
public static function setup()
|
||||
{
|
||||
if (self::$ready)
|
||||
return true;
|
||||
|
||||
$rcmail = rcube::get_instance();
|
||||
self::$config = $rcmail->config;
|
||||
self::$imap = $rcmail->get_storage();
|
||||
self::$ready = class_exists('kolabformat') &&
|
||||
(self::$imap->get_capability('METADATA') || self::$imap->get_capability('ANNOTATEMORE') || self::$imap->get_capability('ANNOTATEMORE2'));
|
||||
|
||||
if (self::$ready) {
|
||||
// set imap options
|
||||
self::$imap->set_options(array(
|
||||
'skip_deleted' => true,
|
||||
'threading' => false,
|
||||
));
|
||||
self::$imap->set_pagesize(9999);
|
||||
}
|
||||
|
||||
return self::$ready;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a list of storage folders for the given data type
|
||||
*
|
||||
* @param string Data type to list folders for (contact,distribution-list,event,task,note)
|
||||
*
|
||||
* @return array List of Kolab_Folder objects (folder names in UTF7-IMAP)
|
||||
*/
|
||||
public static function get_folders($type)
|
||||
{
|
||||
$folders = $folderdata = array();
|
||||
|
||||
if (self::setup()) {
|
||||
foreach ((array)self::list_folders('', '*', $type, false, $folderdata) as $foldername) {
|
||||
$folders[$foldername] = new kolab_storage_folder($foldername, $folderdata[$foldername]);
|
||||
}
|
||||
}
|
||||
|
||||
return $folders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for a specific storage folder
|
||||
*
|
||||
* @param string IMAP folder to access (UTF7-IMAP)
|
||||
* @return object kolab_storage_folder The folder object
|
||||
*/
|
||||
public static function get_folder($folder)
|
||||
{
|
||||
return self::setup() ? new kolab_storage_folder($folder) : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for a single Kolab object, identified by its UID.
|
||||
* This will search all folders storing objects of the given type.
|
||||
*
|
||||
* @param string Object UID
|
||||
* @param string Object type (contact,distribution-list,event,task,note)
|
||||
* @return array The Kolab object represented as hash array or false if not found
|
||||
*/
|
||||
public static function get_object($uid, $type)
|
||||
{
|
||||
self::setup();
|
||||
$folder = null;
|
||||
foreach ((array)self::list_folders('', '*', $type) as $foldername) {
|
||||
if (!$folder)
|
||||
$folder = new kolab_storage_folder($foldername);
|
||||
else
|
||||
$folder->set_folder($foldername);
|
||||
|
||||
if ($object = $folder->get_object($uid))
|
||||
return $object;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static function get_freebusy_server()
|
||||
{
|
||||
return unslashify(self::$config->get('kolab_freebusy_server', 'https://' . $_SESSION['imap_host'] . '/freebusy'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compose an URL to query the free/busy status for the given user
|
||||
*/
|
||||
public static function get_freebusy_url($email)
|
||||
{
|
||||
return self::get_freebusy_server() . '/' . $email . '.ifb';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates folder ID from folder name
|
||||
*
|
||||
* @param string $folder Folder name (UTF7-IMAP)
|
||||
*
|
||||
* @return string Folder ID string
|
||||
*/
|
||||
public static function folder_id($folder)
|
||||
{
|
||||
return asciiwords(strtr($folder, '/.-', '___'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deletes IMAP folder
|
||||
*
|
||||
* @param string $name Folder name (UTF7-IMAP)
|
||||
*
|
||||
* @return bool True on success, false on failure
|
||||
*/
|
||||
public static function folder_delete($name)
|
||||
{
|
||||
self::setup();
|
||||
|
||||
$success = self::$imap->delete_folder($name);
|
||||
self::$last_error = self::$imap->get_error_str();
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates IMAP folder
|
||||
*
|
||||
* @param string $name Folder name (UTF7-IMAP)
|
||||
* @param string $type Folder type
|
||||
* @param bool $subscribed Sets folder subscription
|
||||
*
|
||||
* @return bool True on success, false on failure
|
||||
*/
|
||||
public static function folder_create($name, $type = null, $subscribed = false)
|
||||
{
|
||||
self::setup();
|
||||
|
||||
if ($saved = self::$imap->create_folder($name, $subscribed)) {
|
||||
// set metadata for folder type
|
||||
if ($type) {
|
||||
$saved = self::$imap->set_metadata($name, array(self::CTYPE_KEY => $type));
|
||||
|
||||
// revert if metadata could not be set
|
||||
if (!$saved) {
|
||||
self::$imap->delete_folder($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($saved) {
|
||||
return true;
|
||||
}
|
||||
|
||||
self::$last_error = self::$imap->get_error_str();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames IMAP folder
|
||||
*
|
||||
* @param string $oldname Old folder name (UTF7-IMAP)
|
||||
* @param string $newname New folder name (UTF7-IMAP)
|
||||
*
|
||||
* @return bool True on success, false on failure
|
||||
*/
|
||||
public static function folder_rename($oldname, $newname)
|
||||
{
|
||||
self::setup();
|
||||
|
||||
$success = self::$imap->rename_folder($oldname, $newname);
|
||||
self::$last_error = self::$imap->get_error_str();
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for human-readable name of Kolab object (folder)
|
||||
* See http://wiki.kolab.org/UI-Concepts/Folder-Listing for reference
|
||||
*
|
||||
* @param string $folder IMAP folder name (UTF7-IMAP)
|
||||
* @param string $folder_ns Will be set to namespace name of the folder
|
||||
*
|
||||
* @return string Name of the folder-object
|
||||
*/
|
||||
public static function object_name($folder, &$folder_ns=null)
|
||||
{
|
||||
self::setup();
|
||||
|
||||
$found = false;
|
||||
$namespace = self::$imap->get_namespace();
|
||||
|
||||
if (!empty($namespace['shared'])) {
|
||||
foreach ($namespace['shared'] as $ns) {
|
||||
if (strlen($ns[0]) && strpos($folder, $ns[0]) === 0) {
|
||||
$prefix = '';
|
||||
$folder = substr($folder, strlen($ns[0]));
|
||||
$delim = $ns[1];
|
||||
$found = true;
|
||||
$folder_ns = 'shared';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$found && !empty($namespace['other'])) {
|
||||
foreach ($namespace['other'] as $ns) {
|
||||
if (strlen($ns[0]) && strpos($folder, $ns[0]) === 0) {
|
||||
// remove namespace prefix
|
||||
$folder = substr($folder, strlen($ns[0]));
|
||||
$delim = $ns[1];
|
||||
// get username
|
||||
$pos = strpos($folder, $delim);
|
||||
if ($pos) {
|
||||
$prefix = '('.substr($folder, 0, $pos).') ';
|
||||
$folder = substr($folder, $pos+1);
|
||||
}
|
||||
else {
|
||||
$prefix = '('.$folder.')';
|
||||
$folder = '';
|
||||
}
|
||||
$found = true;
|
||||
$folder_ns = 'other';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$found && !empty($namespace['personal'])) {
|
||||
foreach ($namespace['personal'] as $ns) {
|
||||
if (strlen($ns[0]) && strpos($folder, $ns[0]) === 0) {
|
||||
// remove namespace prefix
|
||||
$folder = substr($folder, strlen($ns[0]));
|
||||
$prefix = '';
|
||||
$delim = $ns[1];
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($delim))
|
||||
$delim = self::$imap->get_hierarchy_delimiter();
|
||||
|
||||
$folder = rcube_charset::convert($folder, 'UTF7-IMAP');
|
||||
$folder = str_replace($delim, ' » ', $folder);
|
||||
|
||||
if ($prefix)
|
||||
$folder = $prefix . ' ' . $folder;
|
||||
|
||||
if (!$folder_ns)
|
||||
$folder_ns = 'personal';
|
||||
|
||||
return $folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SELECT field with folders list
|
||||
*
|
||||
* @param string $type Folder type
|
||||
* @param array $attrs SELECT field attributes (e.g. name)
|
||||
* @param string $current The name of current folder (to skip it)
|
||||
*
|
||||
* @return html_select SELECT object
|
||||
*/
|
||||
public static function folder_selector($type, $attrs, $current = '')
|
||||
{
|
||||
// get all folders of specified type
|
||||
$folders = self::get_folders($type);
|
||||
|
||||
$delim = self::$imap->get_hierarchy_delimiter();
|
||||
$names = array();
|
||||
$len = strlen($current);
|
||||
|
||||
if ($len && ($rpos = strrpos($current, $delim))) {
|
||||
$parent = substr($current, 0, $rpos);
|
||||
$p_len = strlen($parent);
|
||||
}
|
||||
|
||||
// Filter folders list
|
||||
foreach ($folders as $c_folder) {
|
||||
$name = $c_folder->name;
|
||||
// skip current folder and it's subfolders
|
||||
if ($len && ($name == $current || strpos($name, $current.$delim) === 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// always show the parent of current folder
|
||||
if ($p_len && $name == $parent) { }
|
||||
// skip folders where user have no rights to create subfolders
|
||||
else if ($c_folder->get_owner() != $_SESSION['username']) {
|
||||
$rights = $c_folder->get_myrights();
|
||||
if (!preg_match('/[ck]/', $rights)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$names[$name] = rcube_charset::convert($name, 'UTF7-IMAP');
|
||||
}
|
||||
|
||||
// Make sure parent folder is listed (might be skipped e.g. if it's namespace root)
|
||||
if ($p_len && !isset($names[$parent])) {
|
||||
$names[$parent] = rcube_charset::convert($parent, 'UTF7-IMAP');
|
||||
}
|
||||
|
||||
// Sort folders list
|
||||
asort($names, SORT_LOCALE_STRING);
|
||||
|
||||
$folders = array_keys($names);
|
||||
$names = array();
|
||||
|
||||
// Build SELECT field of parent folder
|
||||
$select = new html_select($attrs);
|
||||
$select->add('---', '');
|
||||
|
||||
foreach ($folders as $name) {
|
||||
$imap_name = $name;
|
||||
$name = $origname = self::object_name($name);
|
||||
|
||||
// find folder prefix to truncate
|
||||
for ($i = count($names)-1; $i >= 0; $i--) {
|
||||
if (strpos($name, $names[$i].' » ') === 0) {
|
||||
$length = strlen($names[$i].' » ');
|
||||
$prefix = substr($name, 0, $length);
|
||||
$count = count(explode(' » ', $prefix));
|
||||
$name = str_repeat(' ', $count-1) . '» ' . substr($name, $length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$names[] = $origname;
|
||||
$select->add($name, $imap_name);
|
||||
}
|
||||
|
||||
return $select;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of folder names
|
||||
*
|
||||
* @param string Optional root folder
|
||||
* @param string Optional name pattern
|
||||
* @param string Data type to list folders for (contact,distribution-list,event,task,note,mail)
|
||||
* @param string Enable to return subscribed folders only
|
||||
* @param array Will be filled with folder-types data
|
||||
*
|
||||
* @return array List of folders
|
||||
*/
|
||||
public static function list_folders($root = '', $mbox = '*', $filter = null, $subscribed = false, &$folderdata = array())
|
||||
{
|
||||
if (!self::setup()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$filter) {
|
||||
// Get ALL folders list, standard way
|
||||
if ($subscribed) {
|
||||
return self::$imap->list_folders_subscribed($root, $mbox);
|
||||
}
|
||||
else {
|
||||
return self::$imap->list_folders($root, $mbox);
|
||||
}
|
||||
}
|
||||
|
||||
$prefix = $root . $mbox;
|
||||
|
||||
// get folders types
|
||||
$folderdata = self::$imap->get_metadata($prefix, self::CTYPE_KEY);
|
||||
|
||||
if (!is_array($folderdata)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$folderdata = array_map('implode', $folderdata);
|
||||
$regexp = '/^' . preg_quote($filter, '/') . '(\..+)?$/';
|
||||
|
||||
// In some conditions we can skip LIST command (?)
|
||||
if ($subscribed == false && $filter != 'mail' && $prefix == '*') {
|
||||
foreach ($folderdata as $folder => $type) {
|
||||
if (!preg_match($regexp, $type)) {
|
||||
unset($folderdata[$folder]);
|
||||
}
|
||||
}
|
||||
return array_keys($folderdata);
|
||||
}
|
||||
|
||||
// Get folders list
|
||||
if ($subscribed) {
|
||||
$folders = self::$imap->list_folders_subscribed_direct($root, $mbox);
|
||||
}
|
||||
else {
|
||||
$folders = self::$imap->list_folders_direct($root, $mbox);
|
||||
}
|
||||
|
||||
// In case of an error, return empty list (?)
|
||||
if (!is_array($folders)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Filter folders list
|
||||
foreach ($folders as $idx => $folder) {
|
||||
$type = $folderdata[$folder];
|
||||
|
||||
if ($filter == 'mail' && empty($type)) {
|
||||
continue;
|
||||
}
|
||||
if (empty($type) || !preg_match($regexp, $type)) {
|
||||
unset($folders[$idx]);
|
||||
}
|
||||
}
|
||||
|
||||
return $folders;
|
||||
}
|
||||
|
||||
}
|
568
plugins/libkolab/lib/kolab_storage_cache.php
Normal file
568
plugins/libkolab/lib/kolab_storage_cache.php
Normal file
|
@ -0,0 +1,568 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab storage cache class providing a local caching layer for Kolab groupware objects.
|
||||
*
|
||||
* @version @package_version@
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012, 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 kolab_storage_cache
|
||||
{
|
||||
private $db;
|
||||
private $imap;
|
||||
private $folder;
|
||||
private $uid2msg;
|
||||
private $objects;
|
||||
private $index = array();
|
||||
private $resource_uri;
|
||||
private $enabled = true;
|
||||
private $synched = false;
|
||||
private $ready = false;
|
||||
|
||||
private $binary_cols = array('photo','pgppublickey','pkcs7publickey');
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public function __construct(kolab_storage_folder $storage_folder = null)
|
||||
{
|
||||
$rcmail = rcube::get_instance();
|
||||
$this->db = $rcmail->get_dbh();
|
||||
$this->imap = $rcmail->get_storage();
|
||||
$this->enabled = $rcmail->config->get('kolab_cache', false);
|
||||
|
||||
if ($storage_folder)
|
||||
$this->set_folder($storage_folder);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connect cache with a storage folder
|
||||
*
|
||||
* @param kolab_storage_folder The storage folder instance to connect with
|
||||
*/
|
||||
public function set_folder(kolab_storage_folder $storage_folder)
|
||||
{
|
||||
$this->folder = $storage_folder;
|
||||
|
||||
if (empty($this->folder->name)) {
|
||||
$this->ready = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// compose fully qualified ressource uri for this instance
|
||||
$this->resource_uri = $this->folder->get_resource_uri();
|
||||
$this->ready = $this->enabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Synchronize local cache data with remote
|
||||
*/
|
||||
public function synchronize()
|
||||
{
|
||||
// only sync once per request cycle
|
||||
if ($this->synched)
|
||||
return;
|
||||
|
||||
// lock synchronization for this folder or wait if locked
|
||||
$this->_sync_lock();
|
||||
|
||||
// synchronize IMAP mailbox cache
|
||||
$this->imap->folder_sync($this->folder->name);
|
||||
|
||||
// compare IMAP index with object cache index
|
||||
$imap_index = $this->imap->index($this->folder->name);
|
||||
$this->index = $imap_index->get();
|
||||
|
||||
// determine objects to fetch or to invalidate
|
||||
if ($this->ready) {
|
||||
// read cache index
|
||||
$sql_result = $this->db->query(
|
||||
"SELECT msguid, uid FROM kolab_cache WHERE resource=? AND type<>?",
|
||||
$this->resource_uri,
|
||||
'lock'
|
||||
);
|
||||
|
||||
$old_index = array();
|
||||
while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
|
||||
$old_index[] = $sql_arr['msguid'];
|
||||
$this->uid2msg[$sql_arr['uid']] = $sql_arr['msguid'];
|
||||
}
|
||||
|
||||
// fetch new objects from imap
|
||||
$fetch_index = array_diff($this->index, $old_index);
|
||||
foreach ($this->_fetch($fetch_index, '*') as $object) {
|
||||
$msguid = $object['_msguid'];
|
||||
$this->set($msguid, $object);
|
||||
}
|
||||
|
||||
// delete invalid entries from local DB
|
||||
$del_index = array_diff($old_index, $this->index);
|
||||
if (!empty($del_index)) {
|
||||
$quoted_ids = join(',', array_map(array($this->db, 'quote'), $del_index));
|
||||
$this->db->query(
|
||||
"DELETE FROM kolab_cache WHERE resource=? AND msguid IN ($quoted_ids)",
|
||||
$this->resource_uri
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// remove lock
|
||||
$this->_sync_unlock();
|
||||
|
||||
$this->synched = time();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a single entry from cache or
|
||||
*
|
||||
* @param string Related IMAP message UID
|
||||
* @param string Object type to read
|
||||
* @param string IMAP folder name the entry relates to
|
||||
* @param array Hash array with object properties or null if not found
|
||||
*/
|
||||
public function get($msguid, $type = null, $foldername = null)
|
||||
{
|
||||
// delegate to another cache instance
|
||||
if ($foldername && $foldername != $this->folder->name) {
|
||||
return kolab_storage::get_folder($foldername)->cache->get($msguid, $object);
|
||||
}
|
||||
|
||||
// load object if not in memory
|
||||
if (!isset($this->objects[$msguid])) {
|
||||
if ($this->ready) {
|
||||
$sql_result = $this->db->query(
|
||||
"SELECT * FROM kolab_cache ".
|
||||
"WHERE resource=? AND msguid=?",
|
||||
$this->resource_uri,
|
||||
$msguid
|
||||
);
|
||||
|
||||
if ($sql_arr = $this->db->fetch_assoc($sql_result)) {
|
||||
$this->objects[$msguid] = $this->_unserialize($sql_arr);
|
||||
}
|
||||
}
|
||||
|
||||
// fetch from IMAP if not present in cache
|
||||
if (empty($this->objects[$msguid])) {
|
||||
$result = $this->_fetch(array($msguid), $type, $foldername);
|
||||
$this->objects[$msguid] = $result[0];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->objects[$msguid];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Insert/Update a cache entry
|
||||
*
|
||||
* @param string Related IMAP message UID
|
||||
* @param mixed Hash array with object properties to save or false to delete the cache entry
|
||||
* @param string IMAP folder name the entry relates to
|
||||
*/
|
||||
public function set($msguid, $object, $foldername = null)
|
||||
{
|
||||
// delegate to another cache instance
|
||||
if ($foldername && $foldername != $this->folder->name) {
|
||||
kolab_storage::get_folder($foldername)->cache->set($msguid, $object);
|
||||
return;
|
||||
}
|
||||
|
||||
// write to cache
|
||||
if ($this->ready) {
|
||||
// remove old entry
|
||||
$this->db->query("DELETE FROM kolab_cache WHERE resource=? AND msguid=?",
|
||||
$this->resource_uri, $msguid);
|
||||
|
||||
// write new object data if not false (wich means deleted)
|
||||
if ($object) {
|
||||
$sql_data = $this->_serialize($object);
|
||||
$objtype = $object['_type'] ? $object['_type'] : $this->folder->type;
|
||||
|
||||
$result = $this->db->query(
|
||||
"INSERT INTO kolab_cache ".
|
||||
" (resource, type, msguid, uid, data, xml, dtstart, dtend, tags, words)".
|
||||
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
$this->resource_uri,
|
||||
$objtype,
|
||||
$msguid,
|
||||
$object['uid'],
|
||||
$sql_data['data'],
|
||||
$sql_data['xml'],
|
||||
$sql_data['dtstart'],
|
||||
$sql_data['dtend'],
|
||||
$sql_data['tags'],
|
||||
$sql_data['words']
|
||||
);
|
||||
|
||||
if (!$this->db->affected_rows($result)) {
|
||||
rcmail::raise_error(array(
|
||||
'code' => 900, 'type' => 'php',
|
||||
'message' => "Failed to write to kolab cache"
|
||||
), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// keep a copy in memory for fast access
|
||||
$this->objects[$msguid] = $object;
|
||||
|
||||
if ($object)
|
||||
$this->uid2msg[$object['uid']] = $msguid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move an existing cache entry to a new resource
|
||||
*
|
||||
* @param string Entry's IMAP message UID
|
||||
* @param string Entry's Object UID
|
||||
* @param string Target IMAP folder to move it to
|
||||
*/
|
||||
public function move($msguid, $objuid, $target_folder)
|
||||
{
|
||||
$target = kolab_storage::get_folder($target_folder);
|
||||
|
||||
// resolve new message UID in target folder
|
||||
if ($new_msguid = $target->cache->uid2msguid($objuid)) {
|
||||
$this->db->query(
|
||||
"UPDATE kolab_cache SET resource=?, msguid=? ".
|
||||
"WHERE resource=? AND msguid=?",
|
||||
$target->get_resource_uri(),
|
||||
$new_msguid,
|
||||
$this->resource_uri,
|
||||
$msguid
|
||||
);
|
||||
}
|
||||
else {
|
||||
// just clear cache entry
|
||||
$this->set($msguid, false);
|
||||
}
|
||||
|
||||
unset($this->uid2msg[$uid]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove all objects from local cache
|
||||
*/
|
||||
public function purge($type = null)
|
||||
{
|
||||
$result = $this->db->query(
|
||||
"DELETE FROM kolab_cache WHERE resource=?".
|
||||
($type ? ' AND type=?' : ''),
|
||||
$this->resource_uri,
|
||||
$type
|
||||
);
|
||||
return $this->db->affected_rows($result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Select Kolab objects filtered by the given query
|
||||
*
|
||||
* @param array Pseudo-SQL query as list of filter parameter triplets
|
||||
* triplet: array('<colname>', '<comparator>', '<value>')
|
||||
* @return array List of Kolab data objects (each represented as hash array)
|
||||
*/
|
||||
public function select($query = array())
|
||||
{
|
||||
$result = array();
|
||||
|
||||
// read from local cache DB (assume it to be synchronized)
|
||||
if ($this->ready) {
|
||||
$sql_result = $this->db->query(
|
||||
"SELECT * FROM kolab_cache ".
|
||||
"WHERE resource=? " . $this->_sql_where($query),
|
||||
$this->resource_uri
|
||||
);
|
||||
|
||||
while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
|
||||
if ($object = $this->_unserialize($sql_arr))
|
||||
$result[] = $object;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// extract object type from query parameter
|
||||
$filter = $this->_query2assoc($query);
|
||||
|
||||
// use 'list' for folder's default objects
|
||||
if ($filter['type'] == $this->type) {
|
||||
$index = $this->index;
|
||||
}
|
||||
else { // search by object type
|
||||
$search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_storage_folder::KTYPE_PREFIX . $filter['type'];
|
||||
$index = $this->imap->search_once($this->folder->name, $search)->get();
|
||||
}
|
||||
|
||||
// fetch all messages in $index from IMAP
|
||||
$result = $this->_fetch($index, $filter['type']);
|
||||
|
||||
// TODO: post-filter result according to query
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get number of objects mathing the given query
|
||||
*
|
||||
* @param array $query Pseudo-SQL query as list of filter parameter triplets
|
||||
* @return integer The number of objects of the given type
|
||||
*/
|
||||
public function count($query = array())
|
||||
{
|
||||
$count = 0;
|
||||
|
||||
// cache is in sync, we can count records in local DB
|
||||
if ($this->synched) {
|
||||
$sql_result = $this->db->query(
|
||||
"SELECT COUNT(*) AS NUMROWS FROM kolab_cache ".
|
||||
"WHERE resource=? " . $this->_sql_where($query),
|
||||
$this->resource_uri
|
||||
);
|
||||
|
||||
$sql_arr = $this->db->fetch_assoc($sql_result);
|
||||
$count = intval($sql_arr['NUMROWS']);
|
||||
}
|
||||
else {
|
||||
// search IMAP by object type
|
||||
$filter = $this->_query2assoc($query);
|
||||
$ctype = kolab_storage_folder::KTYPE_PREFIX . $filter['type'];
|
||||
$index = $this->imap->search_once($this->folder->name, 'UNDELETED HEADER X-Kolab-Type ' . $ctype);
|
||||
$count = $index->count();
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper method to compose a valid SQL query from pseudo filter triplets
|
||||
*/
|
||||
private function _sql_where($query)
|
||||
{
|
||||
$sql_where = '';
|
||||
foreach ($query as $param) {
|
||||
if ($param[1] == '=' && is_array($param[2])) {
|
||||
$qvalue = '(' . join(',', array_map(array($this->db, 'quote'), $param[2])) . ')';
|
||||
$param[1] = 'IN';
|
||||
}
|
||||
else {
|
||||
$qvalue = $this->db->quote($param[2]);
|
||||
}
|
||||
|
||||
$sql_where .= sprintf(' AND %s %s %s',
|
||||
$this->db->quote_identifier($param[0]),
|
||||
$param[1],
|
||||
$qvalue
|
||||
);
|
||||
}
|
||||
|
||||
return $sql_where;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to convert the given pseudo-query triplets into
|
||||
* an associative filter array with 'equals' values only
|
||||
*/
|
||||
private function _query2assoc($query)
|
||||
{
|
||||
// extract object type from query parameter
|
||||
$filter = array();
|
||||
foreach ($query as $param) {
|
||||
if ($param[1] == '=')
|
||||
$filter[$param[0]] = $param[2];
|
||||
}
|
||||
return $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch messages from IMAP
|
||||
*
|
||||
* @param array List of message UIDs to fetch
|
||||
* @return array List of parsed Kolab objects
|
||||
*/
|
||||
private function _fetch($index, $type = null, $folder = null)
|
||||
{
|
||||
$results = array();
|
||||
foreach ((array)$index as $msguid) {
|
||||
if ($object = $this->folder->read_object($msguid, $type, $folder)) {
|
||||
$results[] = $object;
|
||||
$this->uid2msg[$object['uid']] = $msguid;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper method to convert the given Kolab object into a dataset to be written to cache
|
||||
*/
|
||||
private function _serialize($object)
|
||||
{
|
||||
$bincols = array_flip($this->binary_cols);
|
||||
$sql_data = array('dtstart' => null, 'dtend' => null, 'xml' => '', 'tags' => '', 'words' => '');
|
||||
|
||||
// set type specific values
|
||||
if ($this->folder->type == 'event') {
|
||||
// database runs in server's timezone so using date() is what we want
|
||||
$sql_data['dtstart'] = date('Y-m-d H:i:s', is_object($object['start']) ? $object['start']->format('U') : $object['start']);
|
||||
$sql_data['dtend'] = date('Y-m-d H:i:s', is_object($object['end']) ? $object['end']->format('U') : $object['end']);
|
||||
|
||||
// extend date range for recurring events
|
||||
if ($object['recurrence']) {
|
||||
$sql_data['dtend'] = date('Y-m-d H:i:s', $object['recurrence']['UNTIL'] ?: strtotime('now + 2 years'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($object['_formatobj']) {
|
||||
$sql_data['xml'] = (string)$object['_formatobj']->write();
|
||||
$sql_data['tags'] = ' ' . join(' ', $object['_formatobj']->get_tags()) . ' '; // pad with spaces for strict/prefix search
|
||||
$sql_data['words'] = ' ' . join(' ', $object['_formatobj']->get_words()) . ' ';
|
||||
}
|
||||
|
||||
// extract object data
|
||||
$data = array();
|
||||
foreach ($object as $key => $val) {
|
||||
if ($val === "" || $val === null) {
|
||||
// skip empty properties
|
||||
continue;
|
||||
}
|
||||
if (isset($bincols[$key])) {
|
||||
$data[$key] = base64_encode($val);
|
||||
}
|
||||
else if ($key[0] != '_') {
|
||||
$data[$key] = $val;
|
||||
}
|
||||
else if ($key == '_attachments') {
|
||||
foreach ($val as $k => $att) {
|
||||
unset($att['content'], $att['path']);
|
||||
if ($att['id'])
|
||||
$data[$key][$k] = $att;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sql_data['data'] = serialize($data);
|
||||
return $sql_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to turn stored cache data into a valid storage object
|
||||
*/
|
||||
private function _unserialize($sql_arr)
|
||||
{
|
||||
$object = unserialize($sql_arr['data']);
|
||||
|
||||
// decode binary properties
|
||||
foreach ($this->binary_cols as $key) {
|
||||
if (!empty($object[$key]))
|
||||
$object[$key] = base64_decode($object[$key]);
|
||||
}
|
||||
|
||||
// add meta data
|
||||
$object['_type'] = $sql_arr['type'];
|
||||
$object['_msguid'] = $sql_arr['msguid'];
|
||||
$object['_mailbox'] = $this->folder->name;
|
||||
$object['_formatobj'] = kolab_format::factory($sql_arr['type'], $sql_arr['xml']);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check lock record for this folder and wait if locked or set lock
|
||||
*/
|
||||
private function _sync_lock()
|
||||
{
|
||||
if (!$this->ready)
|
||||
return;
|
||||
|
||||
$sql_arr = $this->db->fetch_assoc($this->db->query(
|
||||
"SELECT msguid AS locked, ".$this->db->unixtimestamp('created')." AS created FROM kolab_cache ".
|
||||
"WHERE resource=? AND type=?",
|
||||
$this->resource_uri,
|
||||
'lock'
|
||||
));
|
||||
|
||||
// create lock record if not exists
|
||||
if (!$sql_arr) {
|
||||
$this->db->query(
|
||||
"INSERT INTO kolab_cache (resource, type, msguid, created, uid, data, xml)".
|
||||
" VALUES (?, ?, 1, ?, '', '', '')",
|
||||
$this->resource_uri,
|
||||
'lock',
|
||||
date('Y-m-d H:i:s')
|
||||
);
|
||||
}
|
||||
// wait if locked (expire locks after 10 minutes)
|
||||
else if (intval($sql_arr['locked']) > 0 && (time() - $sql_arr['created']) < 600) {
|
||||
usleep(500000);
|
||||
return $this->_sync_lock();
|
||||
}
|
||||
// set lock
|
||||
else {
|
||||
$this->db->query(
|
||||
"UPDATE kolab_cache SET msguid=1, created=? ".
|
||||
"WHERE resource=? AND type=?",
|
||||
date('Y-m-d H:i:s'),
|
||||
$this->resource_uri,
|
||||
'lock'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove lock for this folder
|
||||
*/
|
||||
private function _sync_unlock()
|
||||
{
|
||||
$this->db->query(
|
||||
"UPDATE kolab_cache SET msguid=0, created='' ".
|
||||
"WHERE resource=? AND type=?",
|
||||
$this->resource_uri,
|
||||
'lock'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an object UID into an IMAP message UID
|
||||
*
|
||||
* @param string Kolab object UID
|
||||
* @param boolean Include deleted objects
|
||||
* @return int The resolved IMAP message UID
|
||||
*/
|
||||
public function uid2msguid($uid, $deleted = false)
|
||||
{
|
||||
if (!isset($this->uid2msg[$uid])) {
|
||||
// use IMAP SEARCH to get the right message
|
||||
$index = $this->imap->search_once($this->folder->name, ($deleted ? '' : 'UNDELETED ') . 'HEADER SUBJECT ' . $uid);
|
||||
$results = $index->get();
|
||||
$this->uid2msg[$uid] = $results[0];
|
||||
}
|
||||
|
||||
return $this->uid2msg[$uid];
|
||||
}
|
||||
|
||||
}
|
835
plugins/libkolab/lib/kolab_storage_folder.php
Normal file
835
plugins/libkolab/lib/kolab_storage_folder.php
Normal file
|
@ -0,0 +1,835 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The kolab_storage_folder class represents an IMAP folder on the Kolab server.
|
||||
*
|
||||
* @version @package_version@
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012, 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 kolab_storage_folder
|
||||
{
|
||||
const KTYPE_PREFIX = 'application/x-vnd.kolab.';
|
||||
|
||||
/**
|
||||
* The folder name.
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* The type of this folder.
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* The attached cache object
|
||||
* @var kolab_storage_cache
|
||||
*/
|
||||
public $cache;
|
||||
|
||||
private $type_annotation;
|
||||
private $imap;
|
||||
private $info;
|
||||
private $owner;
|
||||
private $resource_uri;
|
||||
private $uid2msg = array();
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
function __construct($name, $type = null)
|
||||
{
|
||||
$this->imap = rcube::get_instance()->get_storage();
|
||||
$this->imap->set_options(array('skip_deleted' => true));
|
||||
$this->cache = new kolab_storage_cache($this);
|
||||
$this->set_folder($name, $type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the IMAP folder this instance connects to
|
||||
*
|
||||
* @param string The folder name/path
|
||||
* @param string Optional folder type if known
|
||||
*/
|
||||
public function set_folder($name, $type = null)
|
||||
{
|
||||
if (!$type) {
|
||||
$metadata = $this->imap->get_metadata($name, array(kolab_storage::CTYPE_KEY));
|
||||
$type = $metadata[$name][kolab_storage::CTYPE_KEY];
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
$this->type_annotation = $type;
|
||||
$this->type = reset(explode('.', $type));
|
||||
$this->resource_uri = null;
|
||||
|
||||
$this->imap->set_folder($this->name);
|
||||
$this->cache->set_folder($this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function get_folder_info()
|
||||
{
|
||||
if (!isset($this->info))
|
||||
$this->info = $this->imap->folder_info($this->name);
|
||||
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns IMAP metadata/annotations (GETMETADATA/GETANNOTATION)
|
||||
*
|
||||
* @param array List of metadata keys to read
|
||||
* @return array Metadata entry-value hash array on success, NULL on error
|
||||
*/
|
||||
public function get_metadata($keys)
|
||||
{
|
||||
$metadata = $this->imap->get_metadata($this->name, (array)$keys);
|
||||
return $metadata[$this->name];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets IMAP metadata/annotations (SETMETADATA/SETANNOTATION)
|
||||
*
|
||||
* @param array $entries Entry-value array (use NULL value as NIL)
|
||||
* @return boolean True on success, False on failure
|
||||
*/
|
||||
public function set_metadata($entries)
|
||||
{
|
||||
return $this->imap->set_metadata($this->name, $entries);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the owner of the folder.
|
||||
*
|
||||
* @return string The owner of this folder.
|
||||
*/
|
||||
public function get_owner()
|
||||
{
|
||||
// return cached value
|
||||
if (isset($this->owner))
|
||||
return $this->owner;
|
||||
|
||||
$info = $this->get_folder_info();
|
||||
$rcmail = rcube::get_instance();
|
||||
|
||||
switch ($info['namespace']) {
|
||||
case 'personal':
|
||||
$this->owner = $rcmail->user->get_username();
|
||||
break;
|
||||
|
||||
case 'shared':
|
||||
$this->owner = 'anonymous';
|
||||
break;
|
||||
|
||||
default:
|
||||
$owner = '';
|
||||
list($prefix, $user) = explode($this->imap->get_hierarchy_delimiter(), $info['name']);
|
||||
if (strpos($user, '@') === false) {
|
||||
$domain = strstr($rcmail->user->get_username(), '@');
|
||||
if (!empty($domain))
|
||||
$user .= $domain;
|
||||
}
|
||||
$this->owner = $user;
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->owner;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for the name of the namespace to which the IMAP folder belongs
|
||||
*
|
||||
* @return string Name of the namespace (personal, other, shared)
|
||||
*/
|
||||
public function get_namespace()
|
||||
{
|
||||
return $this->imap->folder_namespace($this->name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get IMAP ACL information for this folder
|
||||
*
|
||||
* @return string Permissions as string
|
||||
*/
|
||||
public function get_myrights()
|
||||
{
|
||||
$rights = $this->info['rights'];
|
||||
|
||||
if (!is_array($rights))
|
||||
$rights = $this->imap->my_rights($this->name);
|
||||
|
||||
return join('', (array)$rights);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compose a unique resource URI for this IMAP folder
|
||||
*/
|
||||
public function get_resource_uri()
|
||||
{
|
||||
if (!empty($this->resource_uri))
|
||||
return $this->resource_uri;
|
||||
|
||||
// strip namespace prefix from folder name
|
||||
$ns = $this->get_namespace();
|
||||
$nsdata = $this->imap->get_namespace($ns);
|
||||
if (is_array($nsdata[0]) && strlen($nsdata[0][0]) && strpos($this->name, $nsdata[0][0]) === 0) {
|
||||
$subpath = substr($this->name, strlen($nsdata[0][0]));
|
||||
if ($ns == 'other') {
|
||||
list($user, $suffix) = explode($nsdata[0][1], $subpath);
|
||||
$subpath = $suffix;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$subpath = $this->name;
|
||||
}
|
||||
|
||||
// compose fully qualified ressource uri for this instance
|
||||
$this->resource_uri = 'imap://' . urlencode($this->get_owner()) . '@' . $this->imap->options['host'] . '/' . $subpath;
|
||||
return $this->resource_uri;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check subscription status of this folder
|
||||
*
|
||||
* @param string Subscription type (kolab_storage::SERVERSIDE_SUBSCRIPTION or kolab_storage::CLIENTSIDE_SUBSCRIPTION)
|
||||
* @return boolean True if subscribed, false if not
|
||||
*/
|
||||
public function is_subscribed($type = 0)
|
||||
{
|
||||
static $subscribed; // local cache
|
||||
|
||||
if ($type == kolab_storage::SERVERSIDE_SUBSCRIPTION) {
|
||||
if (!$subscribed)
|
||||
$subscribed = $this->imap->list_folders_subscribed();
|
||||
|
||||
return in_array($this->name, $subscribed);
|
||||
}
|
||||
else if (kolab_storage::CLIENTSIDE_SUBSCRIPTION) {
|
||||
// TODO: implement this
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change subscription status of this folder
|
||||
*
|
||||
* @param boolean The desired subscription status: true = subscribed, false = not subscribed
|
||||
* @param string Subscription type (kolab_storage::SERVERSIDE_SUBSCRIPTION or kolab_storage::CLIENTSIDE_SUBSCRIPTION)
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
public function subscribe($subscribed, $type = 0)
|
||||
{
|
||||
if ($type == kolab_storage::SERVERSIDE_SUBSCRIPTION) {
|
||||
return $subscribed ? $this->imap->subscribe($this->name) : $this->imap->unsubscribe($this->name);
|
||||
}
|
||||
else {
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get number of objects stored in this folder
|
||||
*
|
||||
* @param string $type Object type (e.g. contact, event, todo, journal, note, configuration)
|
||||
* @return integer The number of objects of the given type
|
||||
*/
|
||||
public function count($type = null)
|
||||
{
|
||||
if (!$type) $type = $this->type;
|
||||
|
||||
// synchronize cache first
|
||||
$this->cache->synchronize();
|
||||
|
||||
return $this->cache->count(array(array('type','=',$type)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List all Kolab objects of the given type
|
||||
*
|
||||
* @param string $type Object type (e.g. contact, event, todo, journal, note, configuration)
|
||||
* @return array List of Kolab data objects (each represented as hash array)
|
||||
*/
|
||||
public function get_objects($type = null)
|
||||
{
|
||||
if (!$type) $type = $this->type;
|
||||
|
||||
// synchronize caches
|
||||
$this->cache->synchronize();
|
||||
|
||||
// fetch objects from cache
|
||||
return $this->cache->select(array(array('type','=',$type)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Select *some* Kolab objects matching the given query
|
||||
*
|
||||
* @param array Pseudo-SQL query as list of filter parameter triplets
|
||||
* triplet: array('<colname>', '<comparator>', '<value>')
|
||||
* @return array List of Kolab data objects (each represented as hash array)
|
||||
*/
|
||||
public function select($query = array())
|
||||
{
|
||||
// check query argument
|
||||
if (empty($query))
|
||||
return $this->get_objects();
|
||||
|
||||
$type = null;
|
||||
foreach ($query as $i => $param) {
|
||||
if ($param[0] == 'type') {
|
||||
$type = $param[2];
|
||||
}
|
||||
else if (($param[0] == 'dtstart' || $param[0] == 'dtend') && is_numeric($param[2])) {
|
||||
$query[$i][2] = date('Y-m-d H:i:s', $param[2]);
|
||||
}
|
||||
}
|
||||
|
||||
// add type selector if not in $query
|
||||
if (!$type)
|
||||
$query[] = array('type','=',$this->type);
|
||||
|
||||
// synchronize caches
|
||||
$this->cache->synchronize();
|
||||
|
||||
// fetch objects from cache
|
||||
return $this->cache->select($query);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for a single Kolab object, identified by its UID
|
||||
*
|
||||
* @param string Object UID
|
||||
* @return array The Kolab object represented as hash array
|
||||
*/
|
||||
public function get_object($uid)
|
||||
{
|
||||
// synchronize caches
|
||||
$this->cache->synchronize();
|
||||
|
||||
$msguid = $this->cache->uid2msguid($uid);
|
||||
if ($msguid && ($object = $this->cache->get($msguid)))
|
||||
return $object;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetch a Kolab object attachment which is stored in a separate part
|
||||
* of the mail MIME message that represents the Kolab record.
|
||||
*
|
||||
* @param string Object's UID
|
||||
* @param string The attachment's mime number
|
||||
* @param string IMAP folder where message is stored;
|
||||
* If set, that also implies that the given UID is an IMAP UID
|
||||
* @return mixed The attachment content as binary string
|
||||
*/
|
||||
public function get_attachment($uid, $part, $mailbox = null)
|
||||
{
|
||||
if ($msguid = ($mailbox ? $uid : $this->cache->uid2msguid($uid))) {
|
||||
$this->imap->set_folder($mailbox ? $mailbox : $this->name);
|
||||
return $this->imap->get_message_part($msguid, $part);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetch the mime message from the storage server and extract
|
||||
* the Kolab groupware object from it
|
||||
*
|
||||
* @param string The IMAP message UID to fetch
|
||||
* @param string The object type expected (use wildcard '*' to accept all types)
|
||||
* @param string The folder name where the message is stored
|
||||
* @return mixed Hash array representing the Kolab object, a kolab_format instance or false if not found
|
||||
*/
|
||||
public function read_object($msguid, $type = null, $folder = null)
|
||||
{
|
||||
if (!$type) $type = $this->type;
|
||||
if (!$folder) $folder = $this->name;
|
||||
|
||||
$this->imap->set_folder($folder);
|
||||
|
||||
$headers = $this->imap->get_message_headers($msguid);
|
||||
$object_type = substr($headers->others['x-kolab-type'], strlen(self::KTYPE_PREFIX));
|
||||
$content_type = self::KTYPE_PREFIX . $object_type;
|
||||
|
||||
// check object type header and abort on mismatch
|
||||
if ($type != '*' && $object_type != $type)
|
||||
return false;
|
||||
|
||||
$message = new rcube_message($msguid);
|
||||
$attachments = array();
|
||||
|
||||
// get XML part
|
||||
foreach ((array)$message->attachments as $part) {
|
||||
if (!$xml && ($part->mimetype == $content_type || preg_match('!application/([a-z]+\+)?xml!', $part->mimetype))) {
|
||||
$xml = $part->body ? $part->body : $message->get_part_content($part->mime_id);
|
||||
}
|
||||
else if ($part->filename || $part->content_id) {
|
||||
$key = $part->content_id ? trim($part->content_id, '<>') : $part->filename;
|
||||
$attachments[$key] = array(
|
||||
'id' => $part->mime_id,
|
||||
'mimetype' => $part->mimetype,
|
||||
'size' => $part->size,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$xml) {
|
||||
raise_error(array(
|
||||
'code' => 600,
|
||||
'type' => 'php',
|
||||
'file' => __FILE__,
|
||||
'line' => __LINE__,
|
||||
'message' => "Could not find Kolab data part in message $msguid ($this->name).",
|
||||
), true);
|
||||
return false;
|
||||
}
|
||||
|
||||
$format = kolab_format::factory($object_type);
|
||||
|
||||
if (is_a($format, 'PEAR_Error'))
|
||||
return false;
|
||||
|
||||
// check kolab format version
|
||||
if (strpos($xml, '<' . $object_type) !== false) {
|
||||
// old Kolab 2.0 format detected
|
||||
$handler = class_exists('Horde_Kolab_Format') ? Horde_Kolab_Format::factory('XML', $object_type) : null;
|
||||
if (!is_object($handler) || is_a($handler, 'PEAR_Error')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// XML-to-array
|
||||
$object = $handler->load($xml);
|
||||
$format->fromkolab2($object);
|
||||
}
|
||||
else {
|
||||
// load Kolab 3 format using libkolabxml
|
||||
$format->load($xml);
|
||||
}
|
||||
|
||||
if ($format->is_valid()) {
|
||||
$object = $format->to_array();
|
||||
$object['_type'] = $object_type;
|
||||
$object['_msguid'] = $msguid;
|
||||
$object['_mailbox'] = $this->name;
|
||||
$object['_attachments'] = array_merge((array)$object['_attachments'], $attachments);
|
||||
$object['_formatobj'] = $format;
|
||||
|
||||
return $object;
|
||||
}
|
||||
else {
|
||||
// try to extract object UID from XML block
|
||||
if (preg_match('!<uid>(.+)</uid>!Uims', $xml, $m))
|
||||
$msgadd = " UID = " . trim(strip_tags($m[1]));
|
||||
|
||||
raise_error(array(
|
||||
'code' => 600,
|
||||
'type' => 'php',
|
||||
'file' => __FILE__,
|
||||
'line' => __LINE__,
|
||||
'message' => "Could not parse Kolab object data in message $msguid ($this->name)." . $msgadd,
|
||||
), true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save an object in this folder.
|
||||
*
|
||||
* @param array $object The array that holds the data of the object.
|
||||
* @param string $type The type of the kolab object.
|
||||
* @param string $uid The UID of the old object if it existed before
|
||||
* @return boolean True on success, false on error
|
||||
*/
|
||||
public function save(&$object, $type = null, $uid = null)
|
||||
{
|
||||
if (!$type)
|
||||
$type = $this->type;
|
||||
|
||||
// copy attachments from old message
|
||||
if (!empty($object['_msguid']) && ($old = $this->cache->get($object['_msguid'], $type, $object['_mailbox']))) {
|
||||
foreach ((array)$old['_attachments'] as $name => $att) {
|
||||
if (!isset($object['_attachments'][$name])) {
|
||||
$object['_attachments'][$name] = $old['_attachments'][$name];
|
||||
}
|
||||
// load photo.attachment from old Kolab2 format to be directly embedded in xcard block
|
||||
if ($name == 'photo.attachment' && !isset($object['photo']) && !$object['_attachments'][$name]['content'] && $att['id']) {
|
||||
$object['photo'] = $this->get_attachment($object['_msguid'], $att['id'], $object['_mailbox']);
|
||||
unset($object['_attachments'][$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($raw_msg = $this->build_message($object, $type)) {
|
||||
$result = $this->imap->save_message($this->name, $raw_msg, '', false);
|
||||
|
||||
// delete old message
|
||||
if ($result && !empty($object['_msguid']) && !empty($object['_mailbox'])) {
|
||||
$this->imap->delete_message($object['_msguid'], $object['_mailbox']);
|
||||
$this->cache->set($object['_msguid'], false, $object['_mailbox']);
|
||||
}
|
||||
else if ($result && $uid && ($msguid = $this->cache->uid2msguid($uid))) {
|
||||
$this->imap->delete_message($msguid, $this->name);
|
||||
$this->cache->set($object['_msguid'], false);
|
||||
}
|
||||
|
||||
// update cache with new UID
|
||||
if ($result) {
|
||||
$object['_msguid'] = $result;
|
||||
$this->cache->set($result, $object);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete the specified object from this folder.
|
||||
*
|
||||
* @param mixed $object The Kolab object to delete or object UID
|
||||
* @param boolean $expunge Should the folder be expunged?
|
||||
* @param boolean $trigger Should the folder update be triggered?
|
||||
*
|
||||
* @return boolean True if successful, false on error
|
||||
*/
|
||||
public function delete($object, $expunge = true, $trigger = true)
|
||||
{
|
||||
$msguid = is_array($object) ? $object['_msguid'] : $this->cache->uid2msguid($object);
|
||||
$success = false;
|
||||
|
||||
if ($msguid && $expunge) {
|
||||
$success = $this->imap->delete_message($msguid, $this->name);
|
||||
}
|
||||
else if ($msguid) {
|
||||
$success = $this->imap->set_flag($msguid, 'DELETED', $this->name);
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
$this->cache->set($result, false);
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function delete_all()
|
||||
{
|
||||
$this->cache->purge();
|
||||
return $this->imap->clear_folder($this->name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Restore a previously deleted object
|
||||
*
|
||||
* @param string Object UID
|
||||
* @return mixed Message UID on success, false on error
|
||||
*/
|
||||
public function undelete($uid)
|
||||
{
|
||||
if ($msguid = $this->cache->uid2msguid($uid, true)) {
|
||||
if ($this->imap->set_flag($msguid, 'UNDELETED', $this->name)) {
|
||||
return $msguid;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Move a Kolab object message to another IMAP folder
|
||||
*
|
||||
* @param string Object UID
|
||||
* @param string IMAP folder to move object to
|
||||
* @return boolean True on success, false on failure
|
||||
*/
|
||||
public function move($uid, $target_folder)
|
||||
{
|
||||
if ($msguid = $this->cache->uid2msguid($uid)) {
|
||||
if ($success = $this->imap->move_message($msguid, $target_folder, $this->name)) {
|
||||
$this->cache->move($msguid, $uid, $target_folder);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Failed to move message $msguid to $target_folder: " . $this->imap->get_error_str(),
|
||||
), true);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates source of the configuration object message
|
||||
*/
|
||||
private function build_message(&$object, $type)
|
||||
{
|
||||
// load old object to preserve data we don't understand/process
|
||||
if (is_object($object['_formatobj']))
|
||||
$format = $object['_formatobj'];
|
||||
else if ($object['_msguid'] && ($old = $this->cache->get($object['_msguid'], $type, $object['_mailbox'])))
|
||||
$format = $old['_formatobj'];
|
||||
|
||||
// create new kolab_format instance
|
||||
if (!$format)
|
||||
$format = kolab_format::factory($type);
|
||||
|
||||
$format->set($object);
|
||||
$xml = $format->write();
|
||||
$object['uid'] = $format->uid; // read UID from format
|
||||
$object['_formatobj'] = $format;
|
||||
|
||||
if (!$format->is_valid() || empty($object['uid'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$mime = new Mail_mime("\r\n");
|
||||
$rcmail = rcube::get_instance();
|
||||
$headers = array();
|
||||
$part_id = 1;
|
||||
|
||||
if ($ident = $rcmail->user->get_identity()) {
|
||||
$headers['From'] = $ident['email'];
|
||||
$headers['To'] = $ident['email'];
|
||||
}
|
||||
$headers['Date'] = date('r');
|
||||
$headers['X-Kolab-Type'] = self::KTYPE_PREFIX . $type;
|
||||
$headers['Subject'] = $object['uid'];
|
||||
// $headers['Message-ID'] = $rcmail->gen_message_id();
|
||||
$headers['User-Agent'] = $rcmail->config->get('useragent');
|
||||
|
||||
$mime->headers($headers);
|
||||
$mime->setTXTBody('This is a Kolab Groupware object. '
|
||||
. 'To view this object you will need an email client that understands the Kolab Groupware format. '
|
||||
. "For a list of such email clients please visit http://www.kolab.org/\n\n");
|
||||
|
||||
$mime->addAttachment($xml, // file
|
||||
$format->CTYPE, // content-type
|
||||
'kolab.xml', // filename
|
||||
false, // is_file
|
||||
'8bit', // encoding
|
||||
'attachment', // disposition
|
||||
RCMAIL_CHARSET // charset
|
||||
);
|
||||
$part_id++;
|
||||
|
||||
// save object attachments as separate parts
|
||||
// TODO: optimize memory consumption by using tempfiles for transfer
|
||||
foreach ((array)$object['_attachments'] as $name => $att) {
|
||||
if (empty($att['content']) && !empty($att['id'])) {
|
||||
$msguid = !empty($object['_msguid']) ? $object['_msguid'] : $object['uid'];
|
||||
$att['content'] = $this->get_attachment($msguid, $att['id'], $object['_mailbox']);
|
||||
}
|
||||
|
||||
$headers = array('Content-ID' => Mail_mimePart::encodeHeader('Content-ID', '<' . $name . '>', RCMAIL_CHARSET, 'quoted-printable'));
|
||||
|
||||
if (!empty($att['content'])) {
|
||||
$mime->addAttachment($att['content'], $att['mimetype'], $name, false, 'base64', 'attachment', '', '', '', null, null, '', null, $headers);
|
||||
$part_id++;
|
||||
}
|
||||
else if (!empty($att['path'])) {
|
||||
$mime->addAttachment($att['path'], $att['mimetype'], $name, true, 'base64', 'attachment', '', '', '', null, null, '', null, $headers);
|
||||
$part_id++;
|
||||
}
|
||||
|
||||
$object['_attachments'][$name]['id'] = $part_id;
|
||||
}
|
||||
|
||||
return $mime->getMessage();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Triggers any required updates after changes within the
|
||||
* folder. This is currently only required for handling free/busy
|
||||
* information with Kolab.
|
||||
*
|
||||
* @return boolean|PEAR_Error True if successfull.
|
||||
*/
|
||||
public function trigger()
|
||||
{
|
||||
$owner = $this->get_owner();
|
||||
$result = false;
|
||||
|
||||
switch($this->type) {
|
||||
case 'event':
|
||||
if ($this->get_namespace() == 'personal') {
|
||||
$result = $this->trigger_url(
|
||||
sprintf('%s/trigger/%s/%s.pfb', kolab_storage::get_freebusy_server(), $owner, $this->imap->mod_folder($this->name)),
|
||||
$this->imap->options['user'],
|
||||
$this->imap->options['password']
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($result && is_object($result) && is_a($result, 'PEAR_Error')) {
|
||||
return PEAR::raiseError(sprintf("Failed triggering folder %s. Error was: %s",
|
||||
$this->name, $result->getMessage()));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a URL.
|
||||
*
|
||||
* @param string $url The URL to be triggered.
|
||||
* @param string $auth_user Username to authenticate with
|
||||
* @param string $auth_passwd Password for basic auth
|
||||
* @return boolean|PEAR_Error True if successfull.
|
||||
*/
|
||||
private function trigger_url($url, $auth_user = null, $auth_passwd = null)
|
||||
{
|
||||
require_once('HTTP/Request2.php');
|
||||
|
||||
try {
|
||||
$rcmail = rcube::get_instance();
|
||||
$request = new HTTP_Request2($url);
|
||||
$request->setConfig(array('ssl_verify_peer' => $rcmail->config->get('kolab_ssl_verify_peer', true)));
|
||||
|
||||
// set authentication credentials
|
||||
if ($auth_user && $auth_passwd)
|
||||
$request->setAuth($auth_user, $auth_passwd);
|
||||
|
||||
$result = $request->send();
|
||||
// rcube::write_log('trigger', $result->getBody());
|
||||
}
|
||||
catch (Exception $e) {
|
||||
return PEAR::raiseError($e->getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Legacy methods to keep compatibility with the old Horde Kolab_Storage classes */
|
||||
|
||||
/**
|
||||
* Compatibility method
|
||||
*/
|
||||
public function getOwner()
|
||||
{
|
||||
PEAR::raiseError("Call to deprecated method kolab_storage_folder::getOwner()");
|
||||
return $this->get_owner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IMAP ACL information for this folder
|
||||
*/
|
||||
public function getMyRights()
|
||||
{
|
||||
PEAR::raiseError("Call to deprecated method kolab_storage_folder::getMyRights()");
|
||||
return $this->get_myrights();
|
||||
}
|
||||
|
||||
/**
|
||||
* NOP to stay compatible with the formerly used Horde classes
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
PEAR::raiseError("Call to deprecated method kolab_storage_folder::getData()");
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* List all Kolab objects of the given type
|
||||
*/
|
||||
public function getObjects($type = null)
|
||||
{
|
||||
PEAR::raiseError("Call to deprecated method kolab_storage_folder::getObjects()");
|
||||
return $this->get_objects($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for a single Kolab object, identified by its UID
|
||||
*/
|
||||
public function getObject($uid)
|
||||
{
|
||||
PEAR::raiseError("Call to deprecated method kolab_storage_folder::getObject()");
|
||||
return $this->get_object($uid);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function getAttachment($key)
|
||||
{
|
||||
PEAR::raiseError("Call to deprecated method not returning anything.");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias function of delete()
|
||||
*/
|
||||
public function deleteMessage($id, $trigger = true, $expunge = true)
|
||||
{
|
||||
PEAR::raiseError("Call to deprecated method kolab_storage_folder::deleteMessage()");
|
||||
return $this->delete(array('_msguid' => $id), $trigger, $expunge);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function deleteAll()
|
||||
{
|
||||
PEAR::raiseError("Call to deprecated method kolab_storage_folder::deleteAll()");
|
||||
return $this->delete_all();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
74
plugins/libkolab/libkolab.php
Normal file
74
plugins/libkolab/libkolab.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab core library
|
||||
*
|
||||
* Plugin to setup a basic environment for the interaction with a Kolab server.
|
||||
* Other Kolab-related plugins will depend on it and can use the library classes
|
||||
*
|
||||
* @version @package_version@
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012, 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 libkolab extends rcube_plugin
|
||||
{
|
||||
/**
|
||||
* Required startup method of a Roundcube plugin
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
// load local config
|
||||
$this->load_config();
|
||||
|
||||
$this->add_hook('storage_init', array($this, 'storage_init'));
|
||||
|
||||
// extend include path to load bundled lib classes
|
||||
$include_path = $this->home . '/lib' . PATH_SEPARATOR . ini_get('include_path');
|
||||
set_include_path($include_path);
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
try {
|
||||
kolab_format::$timezone = new DateTimeZone($rcmail->config->get('timezone', 'GMT'));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
raise_error($e, true);
|
||||
kolab_format::$timezone = new DateTimeZone('GMT');
|
||||
}
|
||||
|
||||
// load (old) dependencies if available
|
||||
if (@include_once('Horde/Util.php')) {
|
||||
include_once 'Horde/Kolab/Format.php';
|
||||
include_once 'Horde/Kolab/Format/XML.php';
|
||||
include_once 'Horde/Kolab/Format/XML/contact.php';
|
||||
include_once 'Horde/Kolab/Format/XML/event.php';
|
||||
|
||||
String::setDefaultCharset('UTF-8');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook into IMAP FETCH HEADER.FIELDS command and request Kolab-specific headers
|
||||
*/
|
||||
function storage_init($p)
|
||||
{
|
||||
$p['fetch_headers'] = trim($p['fetch_headers'] .' X-KOLAB-TYPE');
|
||||
return $p;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -26,7 +26,7 @@
|
|||
*/
|
||||
class odfviewer extends rcube_plugin
|
||||
{
|
||||
public $task = 'mail|logout';
|
||||
public $task = 'mail|calendar|logout';
|
||||
|
||||
private $tempdir = 'plugins/odfviewer/files/';
|
||||
private $tempbase = 'plugins/odfviewer/files/';
|
||||
|
@ -56,10 +56,9 @@ class odfviewer extends rcube_plugin
|
|||
$ua = new rcube_browser;
|
||||
if ($ua->ie && $ua->ver < 9)
|
||||
return;
|
||||
|
||||
// extend list of mimetypes that should open in preview
|
||||
$rcmail = rcmail::get_instance();
|
||||
if ($rcmail->action == 'preview' || $rcmail->action == 'show') {
|
||||
if ($rcmail->action == 'preview' || $rcmail->action == 'show' || $rcmail->task == 'calendar') {
|
||||
$mimetypes = $rcmail->config->get('client_mimetypes', 'text/plain,text/html,text/xml,image/jpeg,image/gif,image/png,application/x-javascript,application/pdf,application/x-shockwave-flash');
|
||||
if (!is_array($mimetypes))
|
||||
$mimetypes = explode(',', $mimetypes);
|
||||
|
@ -75,20 +74,24 @@ class odfviewer extends rcube_plugin
|
|||
*/
|
||||
function get_part($args)
|
||||
{
|
||||
global $IMAP, $MESSAGE;
|
||||
|
||||
if (!$args['download'] && $args['mimetype'] && in_array($args['mimetype'], $this->odf_mimetypes)) {
|
||||
if (empty($_GET['_load'])) {
|
||||
$suffix = preg_match('/(\.\w+)$/', $args['part']->filename, $m) ? $m[1] : '.odt';
|
||||
$fn = md5(session_id() . $_SERVER['REQUEST_URI']) . $suffix;
|
||||
|
||||
|
||||
// FIXME: copy file to disk because only apache can send the file correctly
|
||||
$tempfn = $this->tempdir . $fn;
|
||||
if (!file_exists($tempfn)) {
|
||||
$fp = fopen($tempfn, 'w');
|
||||
$IMAP->get_message_part($MESSAGE->uid, $args['part']->mime_id, $args['part'], false, $fp);
|
||||
fclose($fp);
|
||||
|
||||
if ($args['body']) {
|
||||
file_put_contents($tempfn, $args['body']);
|
||||
}
|
||||
else {
|
||||
$fp = fopen($tempfn, 'w');
|
||||
$imap = rcmail::get_instance()->get_storage();
|
||||
$imap->get_message_part($args['uid'], $args['id'], $args['part'], false, $fp);
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
// remember tempfiles in session to clean up on logout
|
||||
$_SESSION['odfviewer']['tempfiles'][] = $fn;
|
||||
}
|
||||
|
|
|
@ -34,161 +34,168 @@
|
|||
var core={},gui={},xmldom={},odf={};
|
||||
// Input 1
|
||||
function Runtime(){}Runtime.ByteArray=function(){};Runtime.ByteArray.prototype.slice=function(){};Runtime.prototype.byteArrayFromArray=function(){};Runtime.prototype.byteArrayFromString=function(){};Runtime.prototype.byteArrayToString=function(){};Runtime.prototype.concatByteArrays=function(){};Runtime.prototype.read=function(){};Runtime.prototype.readFile=function(){};Runtime.prototype.readFileSync=function(){};Runtime.prototype.loadXML=function(){};Runtime.prototype.writeFile=function(){};
|
||||
Runtime.prototype.isFile=function(){};Runtime.prototype.getFileSize=function(){};Runtime.prototype.deleteFile=function(){};Runtime.prototype.log=function(){};Runtime.prototype.setTimeout=function(){};Runtime.prototype.libraryPaths=function(){};Runtime.prototype.type=function(){};Runtime.prototype.getDOMImplementation=function(){};Runtime.prototype.getWindow=function(){};var IS_COMPILED_CODE=true;
|
||||
Runtime.byteArrayToString=function(i,k){function e(e){var a="",b,h=e.length,c,d,f;for(b=0;b<h;b+=1)c=e[b],c<128?a+=String.fromCharCode(c):(b+=1,d=e[b],c<224?a+=String.fromCharCode((c&31)<<6|d&63):(b+=1,f=e[b],a+=String.fromCharCode((c&15)<<12|(d&63)<<6|f&63)));return a}if(k==="utf8")return e(i);else k!=="binary"&&this.log("Unsupported encoding: "+k);return function(e){var a="",b,h=e.length;for(b=0;b<h;b+=1)a+=String.fromCharCode(e[b]&255);return a}(i)};
|
||||
Runtime.getFunctionName=function(i){return i.name===void 0?(i=/function\s+(\w+)/.exec(i))&&i[1]:i.name};
|
||||
function BrowserRuntime(i){function k(b,a){var c,d,f;a?f=b:a=b;if(i){d=i.ownerDocument;if(f)c=d.createElement("span"),c.className=f,c.appendChild(d.createTextNode(f)),i.appendChild(c),i.appendChild(d.createTextNode(" "));c=d.createElement("span");c.appendChild(d.createTextNode(a));i.appendChild(c);i.appendChild(d.createElement("br"))}else console&&console.log(a)}var e=this,g={},a=window.ArrayBuffer&&window.Uint8Array;this.ByteArray=a?function(b){Uint8Array.prototype.slice=function(b,c){if(c===void 0)b===
|
||||
void 0&&(b=0),c=this.length;var a=this.subarray(b,c),f,j;c-=b;f=new Uint8Array(new ArrayBuffer(c));for(j=0;j<c;j+=1)f[j]=a[j];return f};return new Uint8Array(new ArrayBuffer(b))}:function(b){var a=[];a.length=b;return a};this.concatByteArrays=a?function(b,a){var c,d=b.length,f=a.length,j=new this.ByteArray(d+f);for(c=0;c<d;c+=1)j[c]=b[c];for(c=0;c<f;c+=1)j[c+d]=a[c];return j}:function(b,a){return b.concat(a)};this.byteArrayFromArray=function(b){return b.slice()};this.byteArrayFromString=function(b,
|
||||
a){if(a==="utf8"){var c=b.length,d,f,j,g=0;for(f=0;f<c;f+=1)j=b.charCodeAt(f),g+=1+(j>128)+(j>2048);d=new e.ByteArray(g);for(f=g=0;f<c;f+=1)j=b.charCodeAt(f),j<128?(d[g]=j,g+=1):j<2048?(d[g]=192|j>>>6,d[g+1]=128|j&63,g+=2):(d[g]=224|j>>>12&15,d[g+1]=128|j>>>6&63,d[g+2]=128|j&63,g+=3);return d}else a!=="binary"&&e.log("unknown encoding: "+a);c=b.length;d=new e.ByteArray(c);for(f=0;f<c;f+=1)d[f]=b.charCodeAt(f)&255;return d};this.byteArrayToString=Runtime.byteArrayToString;this.readFile=function(b,
|
||||
a,c){if(g.hasOwnProperty(b))c(null,g[b]);else{var d=new XMLHttpRequest;d.open("GET",b,true);d.onreadystatechange=function(){var f;d.readyState===4&&(d.status===0&&!d.responseText?c("File "+b+" is empty."):d.status===200||d.status===0?(f=a==="binary"?typeof VBArray!=="undefined"?(new VBArray(d.responseBody)).toArray():e.byteArrayFromString(d.responseText,"binary"):d.responseText,g[b]=f,c(null,f)):c(d.responseText||d.statusText))};d.overrideMimeType&&(a!=="binary"?d.overrideMimeType("text/plain; charset="+
|
||||
a):d.overrideMimeType("text/plain; charset=x-user-defined"));try{d.send(null)}catch(f){c(f.message)}}};this.read=function(b,a,c,d){if(g.hasOwnProperty(b))d(null,g[b].slice(a,a+c));else{var f=new XMLHttpRequest;f.open("GET",b,true);f.onreadystatechange=function(){var j;f.readyState===4&&(f.status===0&&!f.responseText?d("File "+b+" is empty."):f.status===200||f.status===0?(j=typeof VBArray!=="undefined"?(new VBArray(f.responseBody)).toArray():e.byteArrayFromString(f.responseText,"binary"),g[b]=j,d(null,
|
||||
j.slice(a,a+c))):d(f.responseText||f.statusText))};f.overrideMimeType&&f.overrideMimeType("text/plain; charset=x-user-defined");try{f.send(null)}catch(j){d(j.message)}}};this.readFileSync=function(b,a){var c=new XMLHttpRequest,d;c.open("GET",b,false);c.overrideMimeType&&(a!=="binary"?c.overrideMimeType("text/plain; charset="+a):c.overrideMimeType("text/plain; charset=x-user-defined"));try{if(c.send(null),c.status===200||c.status===0)d=c.responseText}catch(f){}return d};this.writeFile=function(b,a,
|
||||
c){g[b]=a;var d=new XMLHttpRequest;d.open("PUT",b,true);d.onreadystatechange=function(){d.readyState===4&&(d.status===0&&!d.responseText?c("File "+b+" is empty."):d.status>=200&&d.status<300||d.status===0?c(null):c("Status "+String(d.status)+": "+d.responseText||d.statusText))};a=a.buffer&&!d.sendAsBinary?a.buffer:e.byteArrayToString(a,"binary");try{d.sendAsBinary?d.sendAsBinary(a):d.send(a)}catch(f){e.log("HUH? "+f+" "+a),c(f.message)}};this.deleteFile=function(b,a){var c=new XMLHttpRequest;c.open("DELETE",
|
||||
b,true);c.onreadystatechange=function(){c.readyState===4&&(c.status<200&&c.status>=300?a(c.responseText):a(null))};c.send(null)};this.loadXML=function(b,a){var c=new XMLHttpRequest;c.open("GET",b,true);c.overrideMimeType("text/xml");c.onreadystatechange=function(){c.readyState===4&&(c.status===0&&!c.responseText?a("File "+b+" is empty."):c.status===200||c.status===0?a(null,c.responseXML):a(c.responseText))};try{c.send(null)}catch(d){a(d.message)}};this.isFile=function(b,a){e.getFileSize(b,function(b){a(b!==
|
||||
-1)})};this.getFileSize=function(b,a){var c=new XMLHttpRequest;c.open("HEAD",b,true);c.onreadystatechange=function(){if(c.readyState===4){var b=c.getResponseHeader("Content-Length");b?a(parseInt(b,10)):a(-1)}};c.send(null)};this.log=k;this.setTimeout=function(b,a){setTimeout(function(){b()},a)};this.libraryPaths=function(){return["lib"]};this.setCurrentDirectory=function(){};this.type=function(){return"BrowserRuntime"};this.getDOMImplementation=function(){return window.document.implementation};this.exit=
|
||||
function(b){k("Calling exit with code "+String(b)+", but exit() is not implemented.")};this.getWindow=function(){return window}}
|
||||
function NodeJSRuntime(){var i=require("fs"),k="";this.ByteArray=function(e){return new Buffer(e)};this.byteArrayFromArray=function(e){var g=new Buffer(e.length),a,b=e.length;for(a=0;a<b;a+=1)g[a]=e[a];return g};this.concatByteArrays=function(e,g){var a=new Buffer(e.length+g.length);e.copy(a,0,0);g.copy(a,e.length,0);return a};this.byteArrayFromString=function(e,g){return new Buffer(e,g)};this.byteArrayToString=function(e,g){return e.toString(g)};this.readFile=function(e,g,a){g!=="binary"?i.readFile(e,
|
||||
g,a):i.readFile(e,null,a)};this.writeFile=function(e,g,a){i.writeFile(e,g,"binary",function(b){a(b||null)})};this.deleteFile=i.unlink;this.read=function(e,g,a,b){k&&(e=k+"/"+e);i.open(e,"r+",666,function(h,c){if(h)b(h);else{var d=new Buffer(a);i.read(c,d,0,a,g,function(a){i.close(c);b(a,d)})}})};this.readFileSync=function(e,g){return!g?"":i.readFileSync(e,g)};this.loadXML=function(){throw"Not implemented.";};this.isFile=function(e,g){k&&(e=k+"/"+e);i.stat(e,function(a,b){g(!a&&b.isFile())})};this.getFileSize=
|
||||
function(e,g){k&&(e=k+"/"+e);i.stat(e,function(a,b){a?g(-1):g(b.size)})};this.log=function(e){process.stderr.write(e+"\n")};this.setTimeout=function(e,g){setTimeout(function(){e()},g)};this.libraryPaths=function(){return[__dirname]};this.setCurrentDirectory=function(e){k=e};this.currentDirectory=function(){return k};this.type=function(){return"NodeJSRuntime"};this.getDOMImplementation=function(){return null};this.exit=process.exit;this.getWindow=function(){return null}}
|
||||
function RhinoRuntime(){var i=this,k=Packages.javax.xml.parsers.DocumentBuilderFactory.newInstance(),e,g,a="";k.setValidating(false);k.setNamespaceAware(true);k.setExpandEntityReferences(false);k.setSchema(null);g=Packages.org.xml.sax.EntityResolver({resolveEntity:function(a,h){var c=new Packages.java.io.FileReader(h);return new Packages.org.xml.sax.InputSource(c)}});e=k.newDocumentBuilder();e.setEntityResolver(g);this.ByteArray=function(a){return[a]};this.byteArrayFromArray=function(a){return a};
|
||||
this.byteArrayFromString=function(a){var h=[],c,d=a.length;for(c=0;c<d;c+=1)h[c]=a.charCodeAt(c)&255;return h};this.byteArrayToString=Runtime.byteArrayToString;this.concatByteArrays=function(a,h){return a.concat(h)};this.loadXML=function(a,h){var c=new Packages.java.io.File(a),d;try{d=e.parse(c)}catch(f){print(f);h(f);return}h(null,d)};this.readFile=function(a,h,c){var d=new Packages.java.io.File(a),f=h==="binary"?"latin1":h;d.isFile()?(a=readFile(a,f),h==="binary"&&(a=i.byteArrayFromString(a,"binary")),
|
||||
c(null,a)):c(a+" is not a file.")};this.writeFile=function(a,h,c){var a=new Packages.java.io.FileOutputStream(a),d,f=h.length;for(d=0;d<f;d+=1)a.write(h[d]);a.close();c(null)};this.deleteFile=function(a,h){(new Packages.java.io.File(a))["delete"]()?h(null):h("Could not delete "+a)};this.read=function(b,h,c,d){a&&(b=a+"/"+b);var f;f=b;var j="binary";(new Packages.java.io.File(f)).isFile()?(j==="binary"&&(j="latin1"),f=readFile(f,j)):f=null;f?d(null,this.byteArrayFromString(f.substring(h,h+c),"binary")):
|
||||
d("Cannot read "+b)};this.readFileSync=function(a,h){return!h?"":readFile(a,h)};this.isFile=function(b,h){a&&(b=a+"/"+b);var c=new Packages.java.io.File(b);h(c.isFile())};this.getFileSize=function(b,h){a&&(b=a+"/"+b);var c=new Packages.java.io.File(b);h(c.length())};this.log=print;this.setTimeout=function(a){a()};this.libraryPaths=function(){return["lib"]};this.setCurrentDirectory=function(b){a=b};this.currentDirectory=function(){return a};this.type=function(){return"RhinoRuntime"};this.getDOMImplementation=
|
||||
function(){return e.getDOMImplementation()};this.exit=quit;this.getWindow=function(){return null}}var runtime=function(){return typeof window!=="undefined"?new BrowserRuntime(window.document.getElementById("logoutput")):typeof require!=="undefined"?new NodeJSRuntime:new RhinoRuntime}();
|
||||
(function(){function i(e){var a=e[0],b;b=eval("if (typeof "+a+" === 'undefined') {eval('"+a+" = {};');}"+a);for(a=1;a<e.length-1;a+=1)b.hasOwnProperty(e[a])||(b=b[e[a]]={});return b[e[e.length-1]]}var k={},e={};runtime.loadClass=function(g){if(!IS_COMPILED_CODE&&!k.hasOwnProperty(g)){var a=g.split("."),b;b=i(a);if(!b&&(b=function(a){var b,d,f,j,g;d=a.replace(".","/")+".js";j=runtime.libraryPaths();runtime.currentDirectory&&j.push(runtime.currentDirectory());for(g=0;!b&&g<j.length;g+=1){f=j[g];if(!e.hasOwnProperty(f))if((b=
|
||||
runtime.readFileSync(j[g]+"/manifest.js","utf8"))&&b.length)try{e[f]=eval(b)}catch(i){e[f]=null,runtime.log("Cannot load manifest for "+f+".")}else e[f]=null;b=null;if((f=e[f])&&f.indexOf&&f.indexOf(d)!==-1)try{b=runtime.readFileSync(j[g]+"/"+d,"utf8")}catch(l){throw runtime.log("Error loading "+a+" "+l),l;}}if(b===void 0)throw"Cannot load class "+a;try{b=eval(a+" = eval(code);")}catch(k){throw runtime.log("Error loading "+a+" "+k),k;}return b}(g),!b||Runtime.getFunctionName(b)!==a[a.length-1]))throw runtime.log("Loaded code is not for "+
|
||||
a[a.length-1]),"Loaded code is not for "+a[a.length-1];k[g]=true}}})();
|
||||
(function(i){function k(e){if(e.length){var g=e[0];runtime.readFile(g,"utf8",function(a,b){function h(){var a;(a=eval(b))&&runtime.exit(a)}var c="";runtime.libraryPaths();g.indexOf("/")!==-1&&(c=g.substring(0,g.indexOf("/")));runtime.setCurrentDirectory(c);a?(runtime.log(a),runtime.exit(1)):h.apply(null,e)})}}i=Array.prototype.slice.call(i);runtime.type()==="NodeJSRuntime"?k(process.argv.slice(2)):runtime.type()==="RhinoRuntime"?k(i):k(i.slice(1))})(typeof arguments!=="undefined"&&arguments);
|
||||
Runtime.prototype.isFile=function(){};Runtime.prototype.getFileSize=function(){};Runtime.prototype.deleteFile=function(){};Runtime.prototype.log=function(){};Runtime.prototype.setTimeout=function(){};Runtime.prototype.libraryPaths=function(){};Runtime.prototype.type=function(){};Runtime.prototype.getDOMImplementation=function(){};Runtime.prototype.getWindow=function(){};var IS_COMPILED_CODE=!0;
|
||||
Runtime.byteArrayToString=function(g,m){function e(e){var a="",c,b=e.length,d,o,f;for(c=0;c<b;c+=1)d=e[c],128>d?a+=String.fromCharCode(d):(c+=1,o=e[c],224>d?a+=String.fromCharCode((d&31)<<6|o&63):(c+=1,f=e[c],a+=String.fromCharCode((d&15)<<12|(o&63)<<6|f&63)));return a}if("utf8"===m)return e(g);"binary"!==m&&this.log("Unsupported encoding: "+m);return function(e){var a="",c,b=e.length;for(c=0;c<b;c+=1)a+=String.fromCharCode(e[c]&255);return a}(g)};
|
||||
Runtime.getFunctionName=function(g){return void 0===g.name?(g=/function\s+(\w+)/.exec(g))&&g[1]:g.name};
|
||||
function BrowserRuntime(g){function m(c,b){var d,a,f;b?f=c:b=c;g?(a=g.ownerDocument,f&&(d=a.createElement("span"),d.className=f,d.appendChild(a.createTextNode(f)),g.appendChild(d),g.appendChild(a.createTextNode(" "))),d=a.createElement("span"),d.appendChild(a.createTextNode(b)),g.appendChild(d),g.appendChild(a.createElement("br"))):console&&console.log(b)}var e=this,k={},a=window.ArrayBuffer&&window.Uint8Array;this.ByteArray=a?function(c){Uint8Array.prototype.slice=function(c,d){void 0===d&&(void 0===
|
||||
c&&(c=0),d=this.length);var a=this.subarray(c,d),f,h,d=d-c;f=new Uint8Array(new ArrayBuffer(d));for(h=0;h<d;h+=1)f[h]=a[h];return f};return new Uint8Array(new ArrayBuffer(c))}:function(c){var b=[];b.length=c;return b};this.concatByteArrays=a?function(c,b){var d,a=c.length,f=b.length,h=new this.ByteArray(a+f);for(d=0;d<a;d+=1)h[d]=c[d];for(d=0;d<f;d+=1)h[d+a]=b[d];return h}:function(c,b){return c.concat(b)};this.byteArrayFromArray=function(c){return c.slice()};this.byteArrayFromString=function(c,b){if("utf8"===
|
||||
b){var d=c.length,a,f,h,i=0;for(f=0;f<d;f+=1)h=c.charCodeAt(f),i+=1+(128<h)+(2048<h);a=new e.ByteArray(i);for(f=i=0;f<d;f+=1)h=c.charCodeAt(f),128>h?(a[i]=h,i+=1):2048>h?(a[i]=192|h>>>6,a[i+1]=128|h&63,i+=2):(a[i]=224|h>>>12&15,a[i+1]=128|h>>>6&63,a[i+2]=128|h&63,i+=3);return a}"binary"!==b&&e.log("unknown encoding: "+b);d=c.length;a=new e.ByteArray(d);for(f=0;f<d;f+=1)a[f]=c.charCodeAt(f)&255;return a};this.byteArrayToString=Runtime.byteArrayToString;this.readFile=function(c,b,d){if(k.hasOwnProperty(c))d(null,
|
||||
k[c]);else{var a=new XMLHttpRequest;a.open("GET",c,!0);a.onreadystatechange=function(){var f;4===a.readyState&&(0===a.status&&!a.responseText?d("File "+c+" is empty."):200===a.status||0===a.status?(f="binary"===b?"undefined"!==typeof VBArray?(new VBArray(a.responseBody)).toArray():e.byteArrayFromString(a.responseText,"binary"):a.responseText,k[c]=f,d(null,f)):d(a.responseText||a.statusText))};a.overrideMimeType&&("binary"!==b?a.overrideMimeType("text/plain; charset="+b):a.overrideMimeType("text/plain; charset=x-user-defined"));
|
||||
try{a.send(null)}catch(f){d(f.message)}}};this.read=function(c,a,d,o){if(k.hasOwnProperty(c))o(null,k[c].slice(a,a+d));else{var f=new XMLHttpRequest;f.open("GET",c,!0);f.onreadystatechange=function(){var i;4===f.readyState&&(0===f.status&&!f.responseText?o("File "+c+" is empty."):200===f.status||0===f.status?(i="undefined"!==typeof VBArray?(new VBArray(f.responseBody)).toArray():e.byteArrayFromString(f.responseText,"binary"),k[c]=i,o(null,i.slice(a,a+d))):o(f.responseText||f.statusText))};f.overrideMimeType&&
|
||||
f.overrideMimeType("text/plain; charset=x-user-defined");try{f.send(null)}catch(h){o(h.message)}}};this.readFileSync=function(c,a){var d=new XMLHttpRequest,o;d.open("GET",c,!1);d.overrideMimeType&&("binary"!==a?d.overrideMimeType("text/plain; charset="+a):d.overrideMimeType("text/plain; charset=x-user-defined"));try{if(d.send(null),200===d.status||0===d.status)o=d.responseText}catch(f){}return o};this.writeFile=function(c,a,d){k[c]=a;var o=new XMLHttpRequest;o.open("PUT",c,!0);o.onreadystatechange=
|
||||
function(){4===o.readyState&&(0===o.status&&!o.responseText?d("File "+c+" is empty."):200<=o.status&&300>o.status||0===o.status?d(null):d("Status "+o.status+": "+o.responseText||o.statusText))};a=a.buffer&&!o.sendAsBinary?a.buffer:e.byteArrayToString(a,"binary");try{o.sendAsBinary?o.sendAsBinary(a):o.send(a)}catch(f){e.log("HUH? "+f+" "+a),d(f.message)}};this.deleteFile=function(c,a){var d=new XMLHttpRequest;d.open("DELETE",c,!0);d.onreadystatechange=function(){4===d.readyState&&(200>d.status&&300<=
|
||||
d.status?a(d.responseText):a(null))};d.send(null)};this.loadXML=function(a,b){var d=new XMLHttpRequest;d.open("GET",a,!0);d.overrideMimeType&&d.overrideMimeType("text/xml");d.onreadystatechange=function(){4===d.readyState&&(0===d.status&&!d.responseText?b("File "+a+" is empty."):200===d.status||0===d.status?b(null,d.responseXML):b(d.responseText))};try{d.send(null)}catch(o){b(o.message)}};this.isFile=function(a,b){e.getFileSize(a,function(a){b(-1!==a)})};this.getFileSize=function(a,b){var d=new XMLHttpRequest;
|
||||
d.open("HEAD",a,!0);d.onreadystatechange=function(){if(4===d.readyState){var a=d.getResponseHeader("Content-Length");a?b(parseInt(a,10)):b(-1)}};d.send(null)};this.log=m;this.setTimeout=function(a,b){setTimeout(function(){a()},b)};this.libraryPaths=function(){return["lib"]};this.setCurrentDirectory=function(){};this.type=function(){return"BrowserRuntime"};this.getDOMImplementation=function(){return window.document.implementation};this.exit=function(a){m("Calling exit with code "+a+", but exit() is not implemented.")};
|
||||
this.getWindow=function(){return window}}
|
||||
function NodeJSRuntime(){var g=require("fs"),m="";this.ByteArray=function(e){return new Buffer(e)};this.byteArrayFromArray=function(e){var k=new Buffer(e.length),a,c=e.length;for(a=0;a<c;a+=1)k[a]=e[a];return k};this.concatByteArrays=function(e,k){var a=new Buffer(e.length+k.length);e.copy(a,0,0);k.copy(a,e.length,0);return a};this.byteArrayFromString=function(e,k){return new Buffer(e,k)};this.byteArrayToString=function(e,k){return e.toString(k)};this.readFile=function(e,k,a){"binary"!==k?g.readFile(e,
|
||||
k,a):g.readFile(e,null,a)};this.writeFile=function(e,k,a){g.writeFile(e,k,"binary",function(c){a(c||null)})};this.deleteFile=g.unlink;this.read=function(e,k,a,c){m&&(e=m+"/"+e);g.open(e,"r+",666,function(b,d){if(b)c(b);else{var o=new Buffer(a);g.read(d,o,0,a,k,function(a){g.close(d);c(a,o)})}})};this.readFileSync=function(e,k){return!k?"":g.readFileSync(e,k)};this.loadXML=function(){throw"Not implemented.";};this.isFile=function(e,k){m&&(e=m+"/"+e);g.stat(e,function(a,c){k(!a&&c.isFile())})};this.getFileSize=
|
||||
function(e,k){m&&(e=m+"/"+e);g.stat(e,function(a,c){a?k(-1):k(c.size)})};this.log=function(e){process.stderr.write(e+"\n")};this.setTimeout=function(e,k){setTimeout(function(){e()},k)};this.libraryPaths=function(){return[__dirname]};this.setCurrentDirectory=function(e){m=e};this.currentDirectory=function(){return m};this.type=function(){return"NodeJSRuntime"};this.getDOMImplementation=function(){return null};this.exit=process.exit;this.getWindow=function(){return null}}
|
||||
function RhinoRuntime(){var g=this,m=Packages.javax.xml.parsers.DocumentBuilderFactory.newInstance(),e,k,a="";m.setValidating(!1);m.setNamespaceAware(!0);m.setExpandEntityReferences(!1);m.setSchema(null);k=Packages.org.xml.sax.EntityResolver({resolveEntity:function(a,b){var d=new Packages.java.io.FileReader(b);return new Packages.org.xml.sax.InputSource(d)}});e=m.newDocumentBuilder();e.setEntityResolver(k);this.ByteArray=function(a){return[a]};this.byteArrayFromArray=function(a){return a};this.byteArrayFromString=
|
||||
function(a){var b=[],d,o=a.length;for(d=0;d<o;d+=1)b[d]=a.charCodeAt(d)&255;return b};this.byteArrayToString=Runtime.byteArrayToString;this.concatByteArrays=function(a,b){return a.concat(b)};this.loadXML=function(a,b){var d=new Packages.java.io.File(a),o;try{o=e.parse(d)}catch(f){print(f);b(f);return}b(null,o)};this.readFile=function(a,b,d){var o=new Packages.java.io.File(a),f="binary"===b?"latin1":b;o.isFile()?(a=readFile(a,f),"binary"===b&&(a=g.byteArrayFromString(a,"binary")),d(null,a)):d(a+" is not a file.")};
|
||||
this.writeFile=function(a,b,d){var a=new Packages.java.io.FileOutputStream(a),o,f=b.length;for(o=0;o<f;o+=1)a.write(b[o]);a.close();d(null)};this.deleteFile=function(a,b){(new Packages.java.io.File(a))["delete"]()?b(null):b("Could not delete "+a)};this.read=function(c,b,d,o){a&&(c=a+"/"+c);var f;f=c;var h="binary";(new Packages.java.io.File(f)).isFile()?("binary"===h&&(h="latin1"),f=readFile(f,h)):f=null;f?o(null,this.byteArrayFromString(f.substring(b,b+d),"binary")):o("Cannot read "+c)};this.readFileSync=
|
||||
function(a,b){return!b?"":readFile(a,b)};this.isFile=function(c,b){a&&(c=a+"/"+c);var d=new Packages.java.io.File(c);b(d.isFile())};this.getFileSize=function(c,b){a&&(c=a+"/"+c);var d=new Packages.java.io.File(c);b(d.length())};this.log=print;this.setTimeout=function(a){a()};this.libraryPaths=function(){return["lib"]};this.setCurrentDirectory=function(c){a=c};this.currentDirectory=function(){return a};this.type=function(){return"RhinoRuntime"};this.getDOMImplementation=function(){return e.getDOMImplementation()};
|
||||
this.exit=quit;this.getWindow=function(){return null}}var runtime=function(){return"undefined"!==typeof window?new BrowserRuntime(window.document.getElementById("logoutput")):"undefined"!==typeof require?new NodeJSRuntime:new RhinoRuntime}();
|
||||
(function(){function g(e){var a=e[0],c;c=eval("if (typeof "+a+" === 'undefined') {eval('"+a+" = {};');}"+a);for(a=1;a<e.length-1;a+=1)c.hasOwnProperty(e[a])||(c=c[e[a]]={});return c[e[e.length-1]]}var m={},e={};runtime.loadClass=function(k){function a(a){var a=a.replace(".","/")+".js",b=runtime.libraryPaths(),c,h,i;runtime.currentDirectory&&b.push(runtime.currentDirectory());for(c=0;c<b.length;c+=1){h=b[c];if(!e.hasOwnProperty(h))if((i=runtime.readFileSync(b[c]+"/manifest.js","utf8"))&&i.length)try{e[h]=
|
||||
eval(i)}catch(j){e[h]=null,runtime.log("Cannot load manifest for "+h+".")}else e[h]=null;if((h=e[h])&&h.indexOf&&-1!==h.indexOf(a))return b[c]+"/"+a}return null}if(!IS_COMPILED_CODE&&!m.hasOwnProperty(k)){var c=k.split("."),b;b=g(c);if(!b&&(b=function(c){var b,f;f=a(c);if(!f)throw c+" is not listed in any manifest.js.";try{b=runtime.readFileSync(f,"utf8")}catch(e){throw runtime.log("Error loading "+c+" "+e),e;}if(void 0===b)throw"Cannot load class "+c;try{b=eval(c+" = eval(code);")}catch(i){throw runtime.log("Error loading "+
|
||||
c+" "+i),i;}return b}(k),!b||Runtime.getFunctionName(b)!==c[c.length-1]))throw runtime.log("Loaded code is not for "+c[c.length-1]),"Loaded code is not for "+c[c.length-1];m[k]=!0}}})();
|
||||
(function(g){function m(e){if(e.length){var g=e[0];runtime.readFile(g,"utf8",function(a,c){function b(){var a;(a=eval(c))&&runtime.exit(a)}var d="";runtime.libraryPaths();-1!==g.indexOf("/")&&(d=g.substring(0,g.indexOf("/")));runtime.setCurrentDirectory(d);a?(runtime.log(a),runtime.exit(1)):b.apply(null,e)})}}g=Array.prototype.slice.call(g);"NodeJSRuntime"===runtime.type()?m(process.argv.slice(2)):"RhinoRuntime"===runtime.type()?m(g):m(g.slice(1))})("undefined"!==typeof arguments&&arguments);
|
||||
// Input 2
|
||||
core.Base64=function(){function i(a){var b=[],f,c=a.length;for(f=0;f<c;f+=1)b[f]=a.charCodeAt(f)&255;return b}function k(a){var b,f="",c,d=a.length-2;for(c=0;c<d;c+=3)b=a[c]<<16|a[c+1]<<8|a[c+2],f+=u[b>>>18],f+=u[b>>>12&63],f+=u[b>>>6&63],f+=u[b&63];c===d+1?(b=a[c]<<4,f+=u[b>>>6],f+=u[b&63],f+="=="):c===d&&(b=a[c]<<10|a[c+1]<<2,f+=u[b>>>12],f+=u[b>>>6&63],f+=u[b&63],f+="=");return f}function e(a){var a=a.replace(/[^A-Za-z0-9+\/]+/g,""),b=[],f=a.length%4,c,d=a.length,h;for(c=0;c<d;c+=4)h=(n[a.charAt(c)]||
|
||||
0)<<18|(n[a.charAt(c+1)]||0)<<12|(n[a.charAt(c+2)]||0)<<6|(n[a.charAt(c+3)]||0),b.push(h>>16,h>>8&255,h&255);b.length-=[0,0,2,1][f];return b}function g(a){var b=[],f,c=a.length,d;for(f=0;f<c;f+=1)d=a[f],d<128?b.push(d):d<2048?b.push(192|d>>>6,128|d&63):b.push(224|d>>>12&15,128|d>>>6&63,128|d&63);return b}function a(a){var b=[],f,c=a.length,d,h,j;for(f=0;f<c;f+=1)d=a[f],d<128?b.push(d):(f+=1,h=a[f],d<224?b.push((d&31)<<6|h&63):(f+=1,j=a[f],b.push((d&15)<<12|(h&63)<<6|j&63)));return b}function b(a){return k(i(a))}
|
||||
function h(a){return String.fromCharCode.apply(String,e(a))}function c(b){return a(i(b))}function d(b){return String.fromCharCode.apply(String,a(b))}function f(a,b,f){for(var c="",d,h,j;b<f;b+=1)d=a.charCodeAt(b)&255,d<128?c+=String.fromCharCode(d):(b+=1,h=a.charCodeAt(b)&255,d<224?c+=String.fromCharCode((d&31)<<6|h&63):(b+=1,j=a.charCodeAt(b)&255,c+=String.fromCharCode((d&15)<<12|(h&63)<<6|j&63)));return c}function j(a,b){function c(){var e=j+d;if(e>a.length)e=a.length;h+=f(a,j,e);j=e;e=j===a.length;
|
||||
b(h,e)&&!e&&runtime.setTimeout(c,0)}var d=1E5,h="",j=0;a.length<d?b(f(a,0,a.length),true):(typeof a!=="string"&&(a=a.slice()),c())}function p(a){return g(i(a))}function m(a){return String.fromCharCode.apply(String,g(a))}function l(a){return String.fromCharCode.apply(String,g(i(a)))}var u="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";(function(){var a=[],b,f="A".charCodeAt(0),c="a".charCodeAt(0),d="0".charCodeAt(0);for(b=0;b<26;b+=1)a.push(f+b);for(b=0;b<26;b+=1)a.push(c+b);for(b=
|
||||
0;b<10;b+=1)a.push(d+b);a.push("+".charCodeAt(0));a.push("/".charCodeAt(0));return a})();var n=function(a){var b={},f,c;for(f=0,c=a.length;f<c;f+=1)b[a.charAt(f)]=f;return b}(u),q,r,y,C;(y=runtime.getWindow()&&runtime.getWindow().btoa)?q=function(a){return y(l(a))}:(y=b,q=function(a){return k(p(a))});(C=runtime.getWindow()&&runtime.getWindow().atob)?r=function(a){a=C(a);return f(a,0,a.length)}:(C=h,r=function(a){return d(e(a))});return function(){this.convertByteArrayToBase64=this.convertUTF8ArrayToBase64=
|
||||
k;this.convertBase64ToByteArray=this.convertBase64ToUTF8Array=e;this.convertUTF16ArrayToByteArray=this.convertUTF16ArrayToUTF8Array=g;this.convertByteArrayToUTF16Array=this.convertUTF8ArrayToUTF16Array=a;this.convertUTF8StringToBase64=b;this.convertBase64ToUTF8String=h;this.convertUTF8StringToUTF16Array=c;this.convertByteArrayToUTF16String=this.convertUTF8ArrayToUTF16String=d;this.convertUTF8StringToUTF16String=j;this.convertUTF16StringToByteArray=this.convertUTF16StringToUTF8Array=p;this.convertUTF16ArrayToUTF8String=
|
||||
m;this.convertUTF16StringToUTF8String=l;this.convertUTF16StringToBase64=q;this.convertBase64ToUTF16String=r;this.fromBase64=h;this.toBase64=b;this.atob=C;this.btoa=y;this.utob=l;this.btou=j;this.encode=q;this.encodeURI=function(a){return q(a).replace(/[+\/]/g,function(a){return a==="+"?"-":"_"}).replace(/\\=+$/,"")};this.decode=function(a){return r(a.replace(/[\-_]/g,function(a){return a==="-"?"+":"/"}))}}}();
|
||||
core.Base64=function(){function g(a){var c=[],b,d=a.length;for(b=0;b<d;b+=1)c[b]=a.charCodeAt(b)&255;return c}function m(a){var b,c="",d,f=a.length-2;for(d=0;d<f;d+=3)b=a[d]<<16|a[d+1]<<8|a[d+2],c+=x[b>>>18],c+=x[b>>>12&63],c+=x[b>>>6&63],c+=x[b&63];d===f+1?(b=a[d]<<4,c+=x[b>>>6],c+=x[b&63],c+="=="):d===f&&(b=a[d]<<10|a[d+1]<<2,c+=x[b>>>12],c+=x[b>>>6&63],c+=x[b&63],c+="=");return c}function e(a){var a=a.replace(/[^A-Za-z0-9+\/]+/g,""),c=[],b=a.length%4,d,f=a.length,e;for(d=0;d<f;d+=4)e=(p[a.charAt(d)]||
|
||||
0)<<18|(p[a.charAt(d+1)]||0)<<12|(p[a.charAt(d+2)]||0)<<6|(p[a.charAt(d+3)]||0),c.push(e>>16,e>>8&255,e&255);c.length-=[0,0,2,1][b];return c}function k(a){var c=[],b,d=a.length,f;for(b=0;b<d;b+=1)f=a[b],128>f?c.push(f):2048>f?c.push(192|f>>>6,128|f&63):c.push(224|f>>>12&15,128|f>>>6&63,128|f&63);return c}function a(a){var c=[],b,d=a.length,f,e,l;for(b=0;b<d;b+=1)f=a[b],128>f?c.push(f):(b+=1,e=a[b],224>f?c.push((f&31)<<6|e&63):(b+=1,l=a[b],c.push((f&15)<<12|(e&63)<<6|l&63)));return c}function c(a){return m(g(a))}
|
||||
function b(a){return String.fromCharCode.apply(String,e(a))}function d(c){return a(g(c))}function o(c){for(var c=a(c),b="",d=0;d<c.length;)b+=String.fromCharCode.apply(String,c.slice(d,d+45E3)),d+=45E3;return b}function f(a,c,b){var d="",f,e,l;for(l=c;l<b;l+=1)c=a.charCodeAt(l)&255,128>c?d+=String.fromCharCode(c):(l+=1,f=a.charCodeAt(l)&255,224>c?d+=String.fromCharCode((c&31)<<6|f&63):(l+=1,e=a.charCodeAt(l)&255,d+=String.fromCharCode((c&15)<<12|(f&63)<<6|e&63)));return d}function h(a,c){function b(){var l=
|
||||
j+d;l>a.length&&(l=a.length);e+=f(a,j,l);j=l;l=j===a.length;c(e,l)&&!l&&runtime.setTimeout(b,0)}var d=1E5,e="",j=0;a.length<d?c(f(a,0,a.length),!0):("string"!==typeof a&&(a=a.slice()),b())}function i(a){return k(g(a))}function j(a){return String.fromCharCode.apply(String,k(a))}function n(a){return String.fromCharCode.apply(String,k(g(a)))}var x="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";(function(){var a=[],c;for(c=0;26>c;c+=1)a.push(65+c);for(c=0;26>c;c+=1)a.push(97+c);for(c=
|
||||
0;10>c;c+=1)a.push(48+c);a.push(43);a.push(47);return a})();var p=function(a){var c={},b,d;for(b=0,d=a.length;b<d;b+=1)c[a.charAt(b)]=b;return c}(x),t,r,z,s;(z=runtime.getWindow()&&runtime.getWindow().btoa)?t=function(a){return z(n(a))}:(z=c,t=function(a){return m(i(a))});(s=runtime.getWindow()&&runtime.getWindow().atob)?r=function(a){a=s(a);return f(a,0,a.length)}:(s=b,r=function(a){return o(e(a))});return function(){this.convertByteArrayToBase64=this.convertUTF8ArrayToBase64=m;this.convertBase64ToByteArray=
|
||||
this.convertBase64ToUTF8Array=e;this.convertUTF16ArrayToByteArray=this.convertUTF16ArrayToUTF8Array=k;this.convertByteArrayToUTF16Array=this.convertUTF8ArrayToUTF16Array=a;this.convertUTF8StringToBase64=c;this.convertBase64ToUTF8String=b;this.convertUTF8StringToUTF16Array=d;this.convertByteArrayToUTF16String=this.convertUTF8ArrayToUTF16String=o;this.convertUTF8StringToUTF16String=h;this.convertUTF16StringToByteArray=this.convertUTF16StringToUTF8Array=i;this.convertUTF16ArrayToUTF8String=j;this.convertUTF16StringToUTF8String=
|
||||
n;this.convertUTF16StringToBase64=t;this.convertBase64ToUTF16String=r;this.fromBase64=b;this.toBase64=c;this.atob=s;this.btoa=z;this.utob=n;this.btou=h;this.encode=t;this.encodeURI=function(a){return t(a).replace(/[+\/]/g,function(a){return"+"===a?"-":"_"}).replace(/\\=+$/,"")};this.decode=function(a){return r(a.replace(/[\-_]/g,function(a){return"-"===a?"+":"/"}))}}}();
|
||||
// Input 3
|
||||
core.RawDeflate=function(){function i(){this.dl=this.fc=0}function k(){this.extra_bits=this.static_tree=this.dyn_tree=null;this.max_code=this.max_length=this.elems=this.extra_base=0}function e(a,b,f,c){this.good_length=a;this.max_lazy=b;this.nice_length=f;this.max_chain=c}function g(){this.next=null;this.len=0;this.ptr=Array(a);this.off=0}var a=8192,b,h,c,d,f=null,j,p,m,l,u,n,q,r,y,C,s,v,E,B,z,G,o,x,t,w,L,Q,R,X,A,J,H,S,K,F,D,U,M,I,O,T,N,$,Y,oa,da,ea,V,fa,pa,aa,ga,Z,ha,ia,qa,ra=[0,0,0,0,0,0,0,0,1,
|
||||
1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],ba=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],Ha=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],va=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],ja;ja=[new e(0,0,0,0),new e(4,4,8,4),new e(4,5,16,8),new e(4,6,32,32),new e(4,4,16,16),new e(8,16,32,32),new e(8,16,128,128),new e(8,32,128,256),new e(32,128,258,1024),new e(32,258,258,4096)];var ka=function(d){f[p+j++]=d;if(p+j==a&&j!=0){var e;b!=null?(d=b,b=b.next):d=new g;d.next=null;d.len=
|
||||
d.off=0;h==null?h=c=d:c=c.next=d;d.len=j-p;for(e=0;e<d.len;e++)d.ptr[e]=f[p+e];j=p=0}},la=function(b){b&=65535;p+j<a-2?(f[p+j++]=b&255,f[p+j++]=b>>>8):(ka(b&255),ka(b>>>8))},ma=function(){s=(s<<5^l[o+3-1]&255)&8191;v=q[32768+s];q[o&32767]=v;q[32768+s]=o},W=function(a,b){P(b[a].fc,b[a].dl)},wa=function(a,b,f){return a[b].fc<a[f].fc||a[b].fc==a[f].fc&&N[b]<=N[f]},xa=function(a,b,f){var c;for(c=0;c<f&&qa<ia.length;c++)a[b+c]=ia.charCodeAt(qa++)&255;return c},ya=function(a){var b=L,f=o,c,d=G,h=o>32506?
|
||||
o-32506:0,j=o+258,e=l[f+d-1],g=l[f+d];G>=X&&(b>>=2);do if(c=a,!(l[c+d]!=g||l[c+d-1]!=e||l[c]!=l[f]||l[++c]!=l[f+1])){f+=2;c++;do;while(l[++f]==l[++c]&&l[++f]==l[++c]&&l[++f]==l[++c]&&l[++f]==l[++c]&&l[++f]==l[++c]&&l[++f]==l[++c]&&l[++f]==l[++c]&&l[++f]==l[++c]&&f<j);c=258-(j-f);f=j-258;if(c>d){x=a;d=c;if(c>=258)break;e=l[f+d-1];g=l[f+d]}}while((a=q[a&32767])>h&&--b!=0);return d},sa=function(){var a,b,f=65536-w-o;if(f==-1)f--;else if(o>=65274){for(a=0;a<32768;a++)l[a]=l[a+32768];x-=32768;o-=32768;
|
||||
C-=32768;for(a=0;a<8192;a++)b=q[32768+a],q[32768+a]=b>=32768?b-32768:0;for(a=0;a<32768;a++)b=q[a],q[a]=b>=32768?b-32768:0;f+=32768}t||(a=xa(l,o+w,f),a<=0?t=true:w+=a)},Ia=function(a,b,f){var c;if(!d){if(!t){y=r=0;var e,g;if(S[0].dl==0){F.dyn_tree=A;F.static_tree=H;F.extra_bits=ra;F.extra_base=257;F.elems=286;F.max_length=15;F.max_code=0;D.dyn_tree=J;D.static_tree=S;D.extra_bits=ba;D.extra_base=0;D.elems=30;D.max_length=15;D.max_code=0;U.dyn_tree=K;U.static_tree=null;U.extra_bits=Ha;U.extra_base=0;
|
||||
U.elems=19;U.max_length=7;for(g=e=U.max_code=0;g<28;g++){oa[g]=e;for(c=0;c<1<<ra[g];c++)$[e++]=g}$[e-1]=g;for(g=e=0;g<16;g++){da[g]=e;for(c=0;c<1<<ba[g];c++)Y[e++]=g}for(e>>=7;g<30;g++){da[g]=e<<7;for(c=0;c<1<<ba[g]-7;c++)Y[256+e++]=g}for(c=0;c<=15;c++)M[c]=0;for(c=0;c<=143;)H[c++].dl=8,M[8]++;for(;c<=255;)H[c++].dl=9,M[9]++;for(;c<=279;)H[c++].dl=7,M[7]++;for(;c<=287;)H[c++].dl=8,M[8]++;za(H,287);for(c=0;c<30;c++)S[c].dl=5,S[c].fc=Aa(c,5);Ba()}for(c=0;c<8192;c++)q[32768+c]=0;Q=ja[R].max_lazy;X=ja[R].good_length;
|
||||
L=ja[R].max_chain;C=o=0;w=xa(l,0,65536);if(w<=0)t=true,w=0;else{for(t=false;w<262&&!t;)sa();for(c=s=0;c<2;c++)s=(s<<5^l[c]&255)&8191}h=null;p=j=0;R<=3?(G=2,z=0):(z=2,B=0);m=false}d=true;if(w==0)return m=true,0}if((c=Ca(a,b,f))==f)return f;if(m)return c;if(R<=3)for(;w!=0&&h==null;){ma();v!=0&&o-v<=32506&&(z=ya(v),z>w&&(z=w));if(z>=3)if(g=ca(o-x,z-3),w-=z,z<=Q){z--;do o++,ma();while(--z!=0);o++}else o+=z,z=0,s=l[o]&255,s=(s<<5^l[o+1]&255)&8191;else g=ca(0,l[o]&255),w--,o++;g&&(na(0),C=o);for(;w<262&&
|
||||
!t;)sa()}else for(;w!=0&&h==null;){ma();G=z;E=x;z=2;v!=0&&G<Q&&o-v<=32506&&(z=ya(v),z>w&&(z=w),z==3&&o-x>4096&&z--);if(G>=3&&z<=G){g=ca(o-1-E,G-3);w-=G-1;G-=2;do o++,ma();while(--G!=0);B=0;z=2;o++;g&&(na(0),C=o)}else B!=0?ca(0,l[o-1]&255)&&(na(0),C=o):B=1,o++,w--;for(;w<262&&!t;)sa()}w==0&&(B!=0&&ca(0,l[o-1]&255),na(1),m=true);return c+Ca(a,c+b,f-c)},Ca=function(a,c,d){var e,g,q;for(e=0;h!=null&&e<d;){g=d-e;if(g>h.len)g=h.len;for(q=0;q<g;q++)a[c+e+q]=h.ptr[h.off+q];h.off+=g;h.len-=g;e+=g;if(h.len==
|
||||
0)g=h,h=h.next,g.next=b,b=g}if(e==d)return e;if(p<j){g=d-e;g>j-p&&(g=j-p);for(q=0;q<g;q++)a[c+e+q]=f[p+q];p+=g;e+=g;j==p&&(j=p=0)}return e},Ba=function(){var a;for(a=0;a<286;a++)A[a].fc=0;for(a=0;a<30;a++)J[a].fc=0;for(a=0;a<19;a++)K[a].fc=0;A[256].fc=1;aa=V=fa=pa=Z=ha=0;ga=1},ta=function(a,b){for(var c=I[b],f=b<<1;f<=O;){f<O&&wa(a,I[f+1],I[f])&&f++;if(wa(a,c,I[f]))break;I[b]=I[f];b=f;f<<=1}I[b]=c},za=function(a,b){var c=Array(16),f=0,d;for(d=1;d<=15;d++)f=f+M[d-1]<<1,c[d]=f;for(f=0;f<=b;f++)if(d=
|
||||
a[f].dl,d!=0)a[f].fc=Aa(c[d]++,d)},ua=function(a){var b=a.dyn_tree,c=a.static_tree,f=a.elems,d,e=-1,h=f;O=0;T=573;for(d=0;d<f;d++)b[d].fc!=0?(I[++O]=e=d,N[d]=0):b[d].dl=0;for(;O<2;)d=I[++O]=e<2?++e:0,b[d].fc=1,N[d]=0,Z--,c!=null&&(ha-=c[d].dl);a.max_code=e;for(d=O>>1;d>=1;d--)ta(b,d);do d=I[1],I[1]=I[O--],ta(b,1),c=I[1],I[--T]=d,I[--T]=c,b[h].fc=b[d].fc+b[c].fc,N[h]=N[d]>N[c]+1?N[d]:N[c]+1,b[d].dl=b[c].dl=h,I[1]=h++,ta(b,1);while(O>=2);I[--T]=I[1];h=a.dyn_tree;d=a.extra_bits;var f=a.extra_base,c=
|
||||
a.max_code,g=a.max_length,j=a.static_tree,q,t,o,r,i=0;for(t=0;t<=15;t++)M[t]=0;h[I[T]].dl=0;for(a=T+1;a<573;a++)if(q=I[a],t=h[h[q].dl].dl+1,t>g&&(t=g,i++),h[q].dl=t,!(q>c))M[t]++,o=0,q>=f&&(o=d[q-f]),r=h[q].fc,Z+=r*(t+o),j!=null&&(ha+=r*(j[q].dl+o));if(i!=0){do{for(t=g-1;M[t]==0;)t--;M[t]--;M[t+1]+=2;M[g]--;i-=2}while(i>0);for(t=g;t!=0;t--)for(q=M[t];q!=0;)if(d=I[--a],!(d>c)){if(h[d].dl!=t)Z+=(t-h[d].dl)*h[d].fc,h[d].fc=t;q--}}za(b,e)},Da=function(a,b){var c,f=-1,d,e=a[0].dl,h=0,g=7,j=4;e==0&&(g=
|
||||
138,j=3);a[b+1].dl=65535;for(c=0;c<=b;c++)d=e,e=a[c+1].dl,++h<g&&d==e||(h<j?K[d].fc+=h:d!=0?(d!=f&&K[d].fc++,K[16].fc++):h<=10?K[17].fc++:K[18].fc++,h=0,f=d,e==0?(g=138,j=3):d==e?(g=6,j=3):(g=7,j=4))},Ea=function(a,b){var c,f=-1,d,e=a[0].dl,h=0,g=7,j=4;e==0&&(g=138,j=3);for(c=0;c<=b;c++)if(d=e,e=a[c+1].dl,!(++h<g&&d==e)){if(h<j){do W(d,K);while(--h!=0)}else d!=0?(d!=f&&(W(d,K),h--),W(16,K),P(h-3,2)):h<=10?(W(17,K),P(h-3,3)):(W(18,K),P(h-11,7));h=0;f=d;e==0?(g=138,j=3):d==e?(g=6,j=3):(g=7,j=4)}},na=
|
||||
function(a){var b,c,f,d;d=o-C;ea[pa]=aa;ua(F);ua(D);Da(A,F.max_code);Da(J,D.max_code);ua(U);for(f=18;f>=3;f--)if(K[va[f]].dl!=0)break;Z+=3*(f+1)+14;b=Z+3+7>>3;c=ha+3+7>>3;c<=b&&(b=c);if(d+4<=b&&C>=0){P(0+a,3);Fa();la(d);la(~d);for(f=0;f<d;f++)ka(l[C+f])}else if(c==b)P(2+a,3),Ga(H,S);else{P(4+a,3);d=F.max_code+1;b=D.max_code+1;f+=1;P(d-257,5);P(b-1,5);P(f-4,4);for(c=0;c<f;c++)P(K[va[c]].dl,3);Ea(A,d-1);Ea(J,b-1);Ga(A,J)}Ba();a!=0&&Fa()},ca=function(a,b){n[V++]=b;a==0?A[b].fc++:(a--,A[$[b]+256+1].fc++,
|
||||
J[(a<256?Y[a]:Y[256+(a>>7)])&255].fc++,u[fa++]=a,aa|=ga);ga<<=1;(V&7)==0&&(ea[pa++]=aa,aa=0,ga=1);if(R>2&&(V&4095)==0){var c=V*8,f=o-C,d;for(d=0;d<30;d++)c+=J[d].fc*(5+ba[d]);c>>=3;if(fa<parseInt(V/2,10)&&c<parseInt(f/2,10))return true}return V==8191||fa==8192},Ga=function(a,b){var c,f=0,d=0,h=0,e=0,g,j;if(V!=0){do(f&7)==0&&(e=ea[h++]),c=n[f++]&255,(e&1)==0?W(c,a):(g=$[c],W(g+256+1,a),j=ra[g],j!=0&&(c-=oa[g],P(c,j)),c=u[d++],g=(c<256?Y[c]:Y[256+(c>>7)])&255,W(g,b),j=ba[g],j!=0&&(c-=da[g],P(c,j))),
|
||||
e>>=1;while(f<V)}W(256,a)},P=function(a,c){y>16-c?(r|=a<<y,la(r),r=a>>16-y,y+=c-16):(r|=a<<y,y+=c)},Aa=function(a,c){var b=0;do b|=a&1,a>>=1,b<<=1;while(--c>0);return b>>1},Fa=function(){y>8?la(r):y>0&&ka(r);y=r=0};this.deflate=function(e,g){var j,o;ia=e;qa=0;typeof g=="undefined"&&(g=6);(j=g)?j<1?j=1:j>9&&(j=9):j=6;R=j;t=d=false;if(f==null){b=h=c=null;f=Array(a);l=Array(65536);u=Array(8192);n=Array(32832);q=Array(65536);A=Array(573);for(j=0;j<573;j++)A[j]=new i;J=Array(61);for(j=0;j<61;j++)J[j]=
|
||||
new i;H=Array(288);for(j=0;j<288;j++)H[j]=new i;S=Array(30);for(j=0;j<30;j++)S[j]=new i;K=Array(39);for(j=0;j<39;j++)K[j]=new i;F=new k;D=new k;U=new k;M=Array(16);I=Array(573);N=Array(573);$=Array(256);Y=Array(512);oa=Array(29);da=Array(30);ea=Array(1024)}for(var r=Array(1024),w=[];(j=Ia(r,0,r.length))>0;){var x=Array(j);for(o=0;o<j;o++)x[o]=String.fromCharCode(r[o]);w[w.length]=x.join("")}ia=null;return w.join("")}};
|
||||
core.RawDeflate=function(){function g(){this.dl=this.fc=0}function m(){this.extra_bits=this.static_tree=this.dyn_tree=null;this.max_code=this.max_length=this.elems=this.extra_base=0}function e(a,c,b,d){this.good_length=a;this.max_lazy=c;this.nice_length=b;this.max_chain=d}function k(){this.next=null;this.len=0;this.ptr=[];this.ptr.length=a;this.off=0}var a=8192,c,b,d,o,f=null,h,i,j,n,x,p,t,r,z,s,q,u,E,C,w,G,l,v,y,B,M,F,R,S,A,K,I,P,L,H,D,U,N,J,V,aa,T,ba,Q,$,W,ea,X,ia,pa,ca,fa,Y,da,ja,qa,ra=[0,0,0,
|
||||
0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],ga=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],Ha=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],va=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],ka;ka=[new e(0,0,0,0),new e(4,4,8,4),new e(4,5,16,8),new e(4,6,32,32),new e(4,4,16,16),new e(8,16,32,32),new e(8,16,128,128),new e(8,32,128,256),new e(32,128,258,1024),new e(32,258,258,4096)];var la=function(l){f[i+h++]=l;if(i+h===a){var e;if(0!==h){null!==c?(l=c,c=c.next):l=new k;
|
||||
l.next=null;l.len=l.off=0;null===b?b=d=l:d=d.next=l;l.len=h-i;for(e=0;e<l.len;e++)l.ptr[e]=f[i+e];h=i=0}}},ma=function(c){c&=65535;i+h<a-2?(f[i+h++]=c&255,f[i+h++]=c>>>8):(la(c&255),la(c>>>8))},na=function(){q=(q<<5^n[l+3-1]&255)&8191;u=t[32768+q];t[l&32767]=u;t[32768+q]=l},O=function(a,c){z>16-c?(r|=a<<z,ma(r),r=a>>16-z,z+=c-16):(r|=a<<z,z+=c)},Z=function(a,c){O(c[a].fc,c[a].dl)},wa=function(a,c,b){return a[c].fc<a[b].fc||a[c].fc===a[b].fc&&T[c]<=T[b]},xa=function(a,c,b){var d;for(d=0;d<b&&qa<ja.length;d++)a[c+
|
||||
d]=ja.charCodeAt(qa++)&255;return d},sa=function(){var a,c,b=65536-B-l;if(-1===b)b--;else if(65274<=l){for(a=0;32768>a;a++)n[a]=n[a+32768];v-=32768;l-=32768;s-=32768;for(a=0;8192>a;a++)c=t[32768+a],t[32768+a]=32768<=c?c-32768:0;for(a=0;32768>a;a++)c=t[a],t[a]=32768<=c?c-32768:0;b+=32768}y||(a=xa(n,l+B,b),0>=a?y=!0:B+=a)},ya=function(a){var c=M,b=l,d,f=G,e=32506<l?l-32506:0,j=l+258,i=n[b+f-1],q=n[b+f];G>=S&&(c>>=2);do if(d=a,!(n[d+f]!==q||n[d+f-1]!==i||n[d]!==n[b]||n[++d]!==n[b+1])){b+=2;d++;do++b;
|
||||
while(n[b]===n[++d]&&n[++b]===n[++d]&&n[++b]===n[++d]&&n[++b]===n[++d]&&n[++b]===n[++d]&&n[++b]===n[++d]&&n[++b]===n[++d]&&n[++b]===n[++d]&&b<j);d=258-(j-b);b=j-258;if(d>f){v=a;f=d;if(258<=d)break;i=n[b+f-1];q=n[b+f]}}while((a=t[a&32767])>e&&0!==--c);return f},ha=function(a,c){p[X++]=c;0===a?A[c].fc++:(a--,A[ba[c]+256+1].fc++,K[(256>a?Q[a]:Q[256+(a>>7)])&255].fc++,x[ia++]=a,ca|=fa);fa<<=1;0===(X&7)&&(ea[pa++]=ca,ca=0,fa=1);if(2<R&&0===(X&4095)){var b=8*X,d=l-s,f;for(f=0;30>f;f++)b+=K[f].fc*(5+ga[f]);
|
||||
b>>=3;if(ia<parseInt(X/2,10)&&b<parseInt(d/2,10))return!0}return 8191===X||8192===ia},ta=function(a,c){for(var b=J[c],d=c<<1;d<=V;){d<V&&wa(a,J[d+1],J[d])&&d++;if(wa(a,b,J[d]))break;J[c]=J[d];c=d;d<<=1}J[c]=b},za=function(a,c){var b=0;do b|=a&1,a>>=1,b<<=1;while(0<--c);return b>>1},Aa=function(a,c){var b=[];b.length=16;var d=0,f;for(f=1;15>=f;f++)d=d+N[f-1]<<1,b[f]=d;for(d=0;d<=c;d++)f=a[d].dl,0!==f&&(a[d].fc=za(b[f]++,f))},ua=function(a){var c=a.dyn_tree,b=a.static_tree,d=a.elems,f,l=-1,e=d;V=0;
|
||||
aa=573;for(f=0;f<d;f++)0!==c[f].fc?(J[++V]=l=f,T[f]=0):c[f].dl=0;for(;2>V;)f=J[++V]=2>l?++l:0,c[f].fc=1,T[f]=0,Y--,null!==b&&(da-=b[f].dl);a.max_code=l;for(f=V>>1;1<=f;f--)ta(c,f);do f=J[1],J[1]=J[V--],ta(c,1),b=J[1],J[--aa]=f,J[--aa]=b,c[e].fc=c[f].fc+c[b].fc,T[e]=T[f]>T[b]+1?T[f]:T[b]+1,c[f].dl=c[b].dl=e,J[1]=e++,ta(c,1);while(2<=V);J[--aa]=J[1];e=a.dyn_tree;f=a.extra_bits;var d=a.extra_base,b=a.max_code,j=a.max_length,i=a.static_tree,q,h,o,s,v=0;for(h=0;15>=h;h++)N[h]=0;e[J[aa]].dl=0;for(a=aa+
|
||||
1;573>a;a++)q=J[a],h=e[e[q].dl].dl+1,h>j&&(h=j,v++),e[q].dl=h,q>b||(N[h]++,o=0,q>=d&&(o=f[q-d]),s=e[q].fc,Y+=s*(h+o),null!==i&&(da+=s*(i[q].dl+o)));if(0!==v){do{for(h=j-1;0===N[h];)h--;N[h]--;N[h+1]+=2;N[j]--;v-=2}while(0<v);for(h=j;0!==h;h--)for(q=N[h];0!==q;)f=J[--a],f>b||(e[f].dl!==h&&(Y+=(h-e[f].dl)*e[f].fc,e[f].fc=h),q--)}Aa(c,l)},Ba=function(a,c){var b,d=-1,f,l=a[0].dl,e=0,h=7,j=4;0===l&&(h=138,j=3);a[c+1].dl=65535;for(b=0;b<=c;b++)f=l,l=a[b+1].dl,++e<h&&f===l||(e<j?L[f].fc+=e:0!==f?(f!==d&&
|
||||
L[f].fc++,L[16].fc++):10>=e?L[17].fc++:L[18].fc++,e=0,d=f,0===l?(h=138,j=3):f===l?(h=6,j=3):(h=7,j=4))},Ca=function(){8<z?ma(r):0<z&&la(r);z=r=0},Da=function(a,c){var b,d=0,f=0,l=0,e=0,h,j;if(0!==X){do 0===(d&7)&&(e=ea[l++]),b=p[d++]&255,0===(e&1)?Z(b,a):(h=ba[b],Z(h+256+1,a),j=ra[h],0!==j&&(b-=$[h],O(b,j)),b=x[f++],h=(256>b?Q[b]:Q[256+(b>>7)])&255,Z(h,c),j=ga[h],0!==j&&(b-=W[h],O(b,j))),e>>=1;while(d<X)}Z(256,a)},Ea=function(a,c){var b,d=-1,f,l=a[0].dl,e=0,h=7,j=4;0===l&&(h=138,j=3);for(b=0;b<=c;b++)if(f=
|
||||
l,l=a[b+1].dl,!(++e<h&&f===l)){if(e<j){do Z(f,L);while(0!==--e)}else 0!==f?(f!==d&&(Z(f,L),e--),Z(16,L),O(e-3,2)):10>=e?(Z(17,L),O(e-3,3)):(Z(18,L),O(e-11,7));e=0;d=f;0===l?(h=138,j=3):f===l?(h=6,j=3):(h=7,j=4)}},Fa=function(){var a;for(a=0;286>a;a++)A[a].fc=0;for(a=0;30>a;a++)K[a].fc=0;for(a=0;19>a;a++)L[a].fc=0;A[256].fc=1;ca=X=ia=pa=Y=da=0;fa=1},oa=function(a){var c,b,d,f;f=l-s;ea[pa]=ca;ua(H);ua(D);Ba(A,H.max_code);Ba(K,D.max_code);ua(U);for(d=18;3<=d&&!(0!==L[va[d]].dl);d--);Y+=3*(d+1)+14;c=
|
||||
Y+3+7>>3;b=da+3+7>>3;b<=c&&(c=b);if(f+4<=c&&0<=s){O(0+a,3);Ca();ma(f);ma(~f);for(d=0;d<f;d++)la(n[s+d])}else if(b===c)O(2+a,3),Da(I,P);else{O(4+a,3);f=H.max_code+1;c=D.max_code+1;d+=1;O(f-257,5);O(c-1,5);O(d-4,4);for(b=0;b<d;b++)O(L[va[b]].dl,3);Ea(A,f-1);Ea(K,c-1);Da(A,K)}Fa();0!==a&&Ca()},Ga=function(a,d,l){var e,j,q;for(e=0;null!==b&&e<l;){j=l-e;j>b.len&&(j=b.len);for(q=0;q<j;q++)a[d+e+q]=b.ptr[b.off+q];b.off+=j;b.len-=j;e+=j;0===b.len&&(j=b,b=b.next,j.next=c,c=j)}if(e===l)return e;if(i<h){j=l-
|
||||
e;j>h-i&&(j=h-i);for(q=0;q<j;q++)a[d+e+q]=f[i+q];i+=j;e+=j;h===i&&(h=i=0)}return e},Ia=function(a,c,d){var f;if(!o){if(!y){z=r=0;var e,g;if(0===P[0].dl){H.dyn_tree=A;H.static_tree=I;H.extra_bits=ra;H.extra_base=257;H.elems=286;H.max_length=15;H.max_code=0;D.dyn_tree=K;D.static_tree=P;D.extra_bits=ga;D.extra_base=0;D.elems=30;D.max_length=15;D.max_code=0;U.dyn_tree=L;U.static_tree=null;U.extra_bits=Ha;U.extra_base=0;U.elems=19;U.max_length=7;for(g=e=U.max_code=0;28>g;g++){$[g]=e;for(f=0;f<1<<ra[g];f++)ba[e++]=
|
||||
g}ba[e-1]=g;for(g=e=0;16>g;g++){W[g]=e;for(f=0;f<1<<ga[g];f++)Q[e++]=g}for(e>>=7;30>g;g++){W[g]=e<<7;for(f=0;f<1<<ga[g]-7;f++)Q[256+e++]=g}for(f=0;15>=f;f++)N[f]=0;for(f=0;143>=f;)I[f++].dl=8,N[8]++;for(;255>=f;)I[f++].dl=9,N[9]++;for(;279>=f;)I[f++].dl=7,N[7]++;for(;287>=f;)I[f++].dl=8,N[8]++;Aa(I,287);for(f=0;30>f;f++)P[f].dl=5,P[f].fc=za(f,5);Fa()}for(f=0;8192>f;f++)t[32768+f]=0;F=ka[R].max_lazy;S=ka[R].good_length;M=ka[R].max_chain;s=l=0;B=xa(n,0,65536);if(0>=B)y=!0,B=0;else{for(y=!1;262>B&&!y;)sa();
|
||||
for(f=q=0;2>f;f++)q=(q<<5^n[f]&255)&8191}b=null;i=h=0;3>=R?(G=2,w=0):(w=2,C=0);j=!1}o=!0;if(0===B)return j=!0,0}if((f=Ga(a,c,d))===d)return d;if(j)return f;if(3>=R)for(;0!==B&&null===b;){na();0!==u&&32506>=l-u&&(w=ya(u),w>B&&(w=B));if(3<=w)if(g=ha(l-v,w-3),B-=w,w<=F){w--;do l++,na();while(0!==--w);l++}else l+=w,w=0,q=n[l]&255,q=(q<<5^n[l+1]&255)&8191;else g=ha(0,n[l]&255),B--,l++;g&&(oa(0),s=l);for(;262>B&&!y;)sa()}else for(;0!==B&&null===b;){na();G=w;E=v;w=2;0!==u&&G<F&&32506>=l-u&&(w=ya(u),w>B&&
|
||||
(w=B),3===w&&4096<l-v&&w--);if(3<=G&&w<=G){g=ha(l-1-E,G-3);B-=G-1;G-=2;do l++,na();while(0!==--G);C=0;w=2;l++;g&&(oa(0),s=l)}else 0!==C?ha(0,n[l-1]&255)&&(oa(0),s=l):C=1,l++,B--;for(;262>B&&!y;)sa()}0===B&&(0!==C&&ha(0,n[l-1]&255),oa(1),j=!0);return f+Ga(a,f+c,d-f)};this.deflate=function(e,l){var j,h;ja=e;qa=0;"undefined"===typeof l&&(l=6);(j=l)?1>j?j=1:9<j&&(j=9):j=6;R=j;y=o=!1;if(null===f){c=b=d=null;f=[];f.length=a;n=[];n.length=65536;x=[];x.length=8192;p=[];p.length=32832;t=[];t.length=65536;
|
||||
A=[];A.length=573;for(j=0;573>j;j++)A[j]=new g;K=[];K.length=61;for(j=0;61>j;j++)K[j]=new g;I=[];I.length=288;for(j=0;288>j;j++)I[j]=new g;P=[];P.length=30;for(j=0;30>j;j++)P[j]=new g;L=[];L.length=39;for(j=0;39>j;j++)L[j]=new g;H=new m;D=new m;U=new m;N=[];N.length=16;J=[];J.length=573;T=[];T.length=573;ba=[];ba.length=256;Q=[];Q.length=512;$=[];$.length=29;W=[];W.length=30;ea=[];ea.length=1024}for(var q=Array(1024),i=[];0<(j=Ia(q,0,q.length));){var s=[];s.length=j;for(h=0;h<j;h++)s[h]=String.fromCharCode(q[h]);
|
||||
i[i.length]=s.join("")}ja=null;return i.join("")}};
|
||||
// Input 4
|
||||
core.ByteArray=function(i){this.pos=0;this.data=i;this.readUInt32LE=function(){var i=this.data,e=this.pos+=4;return i[--e]<<24|i[--e]<<16|i[--e]<<8|i[--e]};this.readUInt16LE=function(){var i=this.data,e=this.pos+=2;return i[--e]<<8|i[--e]}};
|
||||
core.ByteArray=function(g){this.pos=0;this.data=g;this.readUInt32LE=function(){var g=this.data,e=this.pos+=4;return g[--e]<<24|g[--e]<<16|g[--e]<<8|g[--e]};this.readUInt16LE=function(){var g=this.data,e=this.pos+=2;return g[--e]<<8|g[--e]}};
|
||||
// Input 5
|
||||
core.ByteArrayWriter=function(i){var k=this,e=new runtime.ByteArray(0);this.appendByteArrayWriter=function(g){e=runtime.concatByteArrays(e,g.getByteArray())};this.appendByteArray=function(g){e=runtime.concatByteArrays(e,g)};this.appendArray=function(g){e=runtime.concatByteArrays(e,runtime.byteArrayFromArray(g))};this.appendUInt16LE=function(e){k.appendArray([e&255,e>>8&255])};this.appendUInt32LE=function(e){k.appendArray([e&255,e>>8&255,e>>16&255,e>>24&255])};this.appendString=function(g){e=runtime.concatByteArrays(e,
|
||||
runtime.byteArrayFromString(g,i))};this.getLength=function(){return e.length};this.getByteArray=function(){return e}};
|
||||
core.ByteArrayWriter=function(g){var m=this,e=new runtime.ByteArray(0);this.appendByteArrayWriter=function(g){e=runtime.concatByteArrays(e,g.getByteArray())};this.appendByteArray=function(g){e=runtime.concatByteArrays(e,g)};this.appendArray=function(g){e=runtime.concatByteArrays(e,runtime.byteArrayFromArray(g))};this.appendUInt16LE=function(e){m.appendArray([e&255,e>>8&255])};this.appendUInt32LE=function(e){m.appendArray([e&255,e>>8&255,e>>16&255,e>>24&255])};this.appendString=function(k){e=runtime.concatByteArrays(e,
|
||||
runtime.byteArrayFromString(k,g))};this.getLength=function(){return e.length};this.getByteArray=function(){return e}};
|
||||
// Input 6
|
||||
core.RawInflate=function(){var i,k,e=null,g,a,b,h,c,d,f,j,p,m,l,u,n,q,r=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535],y=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],C=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,99,99],s=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],v=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],E=[16,17,18,
|
||||
0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],B=function(){this.list=this.next=null},z=function(){this.n=this.b=this.e=0;this.t=null},G=function(a,c,b,f,d,j){this.BMAX=16;this.N_MAX=288;this.status=0;this.root=null;this.m=0;var e=Array(this.BMAX+1),h,g,q,t,o,r,i,w=Array(this.BMAX+1),x,m,n,p=new z,l=Array(this.BMAX);t=Array(this.N_MAX);var v,y=Array(this.BMAX+1),k,s,C;C=this.root=null;for(o=0;o<e.length;o++)e[o]=0;for(o=0;o<w.length;o++)w[o]=0;for(o=0;o<l.length;o++)l[o]=null;for(o=0;o<t.length;o++)t[o]=
|
||||
0;for(o=0;o<y.length;o++)y[o]=0;h=c>256?a[256]:this.BMAX;x=a;m=0;o=c;do e[x[m]]++,m++;while(--o>0);if(e[0]==c)this.root=null,this.status=this.m=0;else{for(r=1;r<=this.BMAX;r++)if(e[r]!=0)break;i=r;j<r&&(j=r);for(o=this.BMAX;o!=0;o--)if(e[o]!=0)break;q=o;j>o&&(j=o);for(k=1<<r;r<o;r++,k<<=1)if((k-=e[r])<0){this.status=2;this.m=j;return}if((k-=e[o])<0)this.status=2,this.m=j;else{e[o]+=k;y[1]=r=0;x=e;m=1;for(n=2;--o>0;)y[n++]=r+=x[m++];x=a;o=m=0;do if((r=x[m++])!=0)t[y[r]++]=o;while(++o<c);c=y[q];y[0]=
|
||||
o=0;x=t;m=0;t=-1;v=w[0]=0;n=null;for(s=0;i<=q;i++)for(a=e[i];a-- >0;){for(;i>v+w[1+t];){v+=w[1+t];t++;s=(s=q-v)>j?j:s;if((g=1<<(r=i-v))>a+1){g-=a+1;for(n=i;++r<s;){if((g<<=1)<=e[++n])break;g-=e[n]}}v+r>h&&v<h&&(r=h-v);s=1<<r;w[1+t]=r;n=Array(s);for(g=0;g<s;g++)n[g]=new z;C=C==null?this.root=new B:C.next=new B;C.next=null;C.list=n;l[t]=n;if(t>0)y[t]=o,p.b=w[t],p.e=16+r,p.t=n,r=(o&(1<<v)-1)>>v-w[t],l[t-1][r].e=p.e,l[t-1][r].b=p.b,l[t-1][r].n=p.n,l[t-1][r].t=p.t}p.b=i-v;m>=c?p.e=99:x[m]<b?(p.e=x[m]<
|
||||
256?16:15,p.n=x[m++]):(p.e=d[x[m]-b],p.n=f[x[m++]-b]);g=1<<i-v;for(r=o>>v;r<s;r+=g)n[r].e=p.e,n[r].b=p.b,n[r].n=p.n,n[r].t=p.t;for(r=1<<i-1;(o&r)!=0;r>>=1)o^=r;for(o^=r;(o&(1<<v)-1)!=y[t];)v-=w[t],t--}this.m=w[1];this.status=k!=0&&q!=1?1:0}}},o=function(a){for(;h<a;)b|=(n.length==q?-1:n[q++])<<h,h+=8},x=function(a){return b&r[a]},t=function(a){b>>=a;h-=a},w=function(a,b,d){var e,h,g;if(d==0)return 0;for(g=0;;){o(l);h=p.list[x(l)];for(e=h.e;e>16;){if(e==99)return-1;t(h.b);e-=16;o(e);h=h.t[x(e)];e=
|
||||
h.e}t(h.b);if(e==16)k&=32767,a[b+g++]=i[k++]=h.n;else{if(e==15)break;o(e);f=h.n+x(e);t(e);o(u);h=m.list[x(u)];for(e=h.e;e>16;){if(e==99)return-1;t(h.b);e-=16;o(e);h=h.t[x(e)];e=h.e}t(h.b);o(e);j=k-h.n-x(e);for(t(e);f>0&&g<d;)f--,j&=32767,k&=32767,a[b+g++]=i[k++]=i[j++]}if(g==d)return d}c=-1;return g},L,Q=function(a,c,b){var f,d,e,h,j,g,q,r=Array(316);for(f=0;f<r.length;f++)r[f]=0;o(5);g=257+x(5);t(5);o(5);q=1+x(5);t(5);o(4);f=4+x(4);t(4);if(g>286||q>30)return-1;for(d=0;d<f;d++)o(3),r[E[d]]=x(3),t(3);
|
||||
for(;d<19;d++)r[E[d]]=0;l=7;d=new G(r,19,19,null,null,l);if(d.status!=0)return-1;p=d.root;l=d.m;h=g+q;for(f=e=0;f<h;)if(o(l),j=p.list[x(l)],d=j.b,t(d),d=j.n,d<16)r[f++]=e=d;else if(d==16){o(2);d=3+x(2);t(2);if(f+d>h)return-1;for(;d-- >0;)r[f++]=e}else{d==17?(o(3),d=3+x(3),t(3)):(o(7),d=11+x(7),t(7));if(f+d>h)return-1;for(;d-- >0;)r[f++]=0;e=0}l=9;d=new G(r,g,257,y,C,l);if(l==0)d.status=1;if(d.status!=0)return-1;p=d.root;l=d.m;for(f=0;f<q;f++)r[f]=r[f+g];u=6;d=new G(r,q,0,s,v,u);m=d.root;u=d.m;return u==
|
||||
0&&g>257?-1:d.status!=0?-1:w(a,c,b)};this.inflate=function(r,z){i==null&&(i=Array(65536));h=b=k=0;c=-1;d=false;f=j=0;p=null;n=r;q=0;var B=new runtime.ByteArray(z);a:{var E,H;for(E=0;E<z;){if(d&&c==-1)break;if(f>0){if(c!=0)for(;f>0&&E<z;)f--,j&=32767,k&=32767,B[0+E++]=i[k++]=i[j++];else{for(;f>0&&E<z;)f--,k&=32767,o(8),B[0+E++]=i[k++]=x(8),t(8);f==0&&(c=-1)}if(E==z)break}if(c==-1){if(d)break;o(1);x(1)!=0&&(d=true);t(1);o(2);c=x(2);t(2);p=null;f=0}switch(c){case 0:H=B;var S=0+E,K=z-E,F=void 0,F=h&7;
|
||||
t(F);o(16);F=x(16);t(16);o(16);if(F!=(~b&65535))H=-1;else{t(16);f=F;for(F=0;f>0&&F<K;)f--,k&=32767,o(8),H[S+F++]=i[k++]=x(8),t(8);f==0&&(c=-1);H=F}break;case 1:if(p!=null)H=w(B,0+E,z-E);else b:{H=B;S=0+E;K=z-E;if(e==null){for(var D=void 0,F=Array(288),D=void 0,D=0;D<144;D++)F[D]=8;for(;D<256;D++)F[D]=9;for(;D<280;D++)F[D]=7;for(;D<288;D++)F[D]=8;a=7;D=new G(F,288,257,y,C,a);if(D.status!=0){alert("HufBuild error: "+D.status);H=-1;break b}e=D.root;a=D.m;for(D=0;D<30;D++)F[D]=5;L=5;D=new G(F,30,0,s,
|
||||
v,L);if(D.status>1){e=null;alert("HufBuild error: "+D.status);H=-1;break b}g=D.root;L=D.m}p=e;m=g;l=a;u=L;H=w(H,S,K)}break;case 2:H=p!=null?w(B,0+E,z-E):Q(B,0+E,z-E);break;default:H=-1}if(H==-1)break a;E+=H}}n=null;return B}};
|
||||
core.RawInflate=function(){var g,m,e=null,k,a,c,b,d,o,f,h,i,j,n,x,p,t,r=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535],z=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],s=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,99,99],q=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],u=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],E=[16,17,18,
|
||||
0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],C=function(){this.list=this.next=null},w=function(){this.n=this.b=this.e=0;this.t=null},G=function(a,c,b,f,d,e){this.BMAX=16;this.N_MAX=288;this.status=0;this.root=null;this.m=0;var j=Array(this.BMAX+1),l,h,q,i,g,s,o,v=Array(this.BMAX+1),n,y,u,t=new w,k=Array(this.BMAX);i=Array(this.N_MAX);var p,z=Array(this.BMAX+1),B,r,x;x=this.root=null;for(g=0;g<j.length;g++)j[g]=0;for(g=0;g<v.length;g++)v[g]=0;for(g=0;g<k.length;g++)k[g]=null;for(g=0;g<i.length;g++)i[g]=
|
||||
0;for(g=0;g<z.length;g++)z[g]=0;l=256<c?a[256]:this.BMAX;n=a;y=0;g=c;do j[n[y]]++,y++;while(0<--g);if(j[0]==c)this.root=null,this.status=this.m=0;else{for(s=1;s<=this.BMAX&&!(0!=j[s]);s++);o=s;e<s&&(e=s);for(g=this.BMAX;0!=g&&!(0!=j[g]);g--);q=g;e>g&&(e=g);for(B=1<<s;s<g;s++,B<<=1)if(0>(B-=j[s])){this.status=2;this.m=e;return}if(0>(B-=j[g]))this.status=2,this.m=e;else{j[g]+=B;z[1]=s=0;n=j;y=1;for(u=2;0<--g;)z[u++]=s+=n[y++];n=a;g=y=0;do if(0!=(s=n[y++]))i[z[s]++]=g;while(++g<c);c=z[q];z[0]=g=0;n=
|
||||
i;y=0;i=-1;p=v[0]=0;u=null;for(r=0;o<=q;o++)for(a=j[o];0<a--;){for(;o>p+v[1+i];){p+=v[1+i];i++;r=(r=q-p)>e?e:r;if((h=1<<(s=o-p))>a+1){h-=a+1;for(u=o;++s<r&&!((h<<=1)<=j[++u]);)h-=j[u]}p+s>l&&p<l&&(s=l-p);r=1<<s;v[1+i]=s;u=Array(r);for(h=0;h<r;h++)u[h]=new w;x=null==x?this.root=new C:x.next=new C;x.next=null;x.list=u;k[i]=u;0<i&&(z[i]=g,t.b=v[i],t.e=16+s,t.t=u,s=(g&(1<<p)-1)>>p-v[i],k[i-1][s].e=t.e,k[i-1][s].b=t.b,k[i-1][s].n=t.n,k[i-1][s].t=t.t)}t.b=o-p;y>=c?t.e=99:n[y]<b?(t.e=256>n[y]?16:15,t.n=
|
||||
n[y++]):(t.e=d[n[y]-b],t.n=f[n[y++]-b]);h=1<<o-p;for(s=g>>p;s<r;s+=h)u[s].e=t.e,u[s].b=t.b,u[s].n=t.n,u[s].t=t.t;for(s=1<<o-1;0!=(g&s);s>>=1)g^=s;for(g^=s;(g&(1<<p)-1)!=z[i];)p-=v[i],i--}this.m=v[1];this.status=0!=B&&1!=q?1:0}}},l=function(a){for(;b<a;)c|=(p.length==t?-1:p[t++])<<b,b+=8},v=function(a){return c&r[a]},y=function(a){c>>=a;b-=a},B=function(a,c,b){var e,s,q;if(0==b)return 0;for(q=0;;){l(n);s=i.list[v(n)];for(e=s.e;16<e;){if(99==e)return-1;y(s.b);e-=16;l(e);s=s.t[v(e)];e=s.e}y(s.b);if(16==
|
||||
e)m&=32767,a[c+q++]=g[m++]=s.n;else{if(15==e)break;l(e);f=s.n+v(e);y(e);l(x);s=j.list[v(x)];for(e=s.e;16<e;){if(99==e)return-1;y(s.b);e-=16;l(e);s=s.t[v(e)];e=s.e}y(s.b);l(e);h=m-s.n-v(e);for(y(e);0<f&&q<b;)f--,h&=32767,m&=32767,a[c+q++]=g[m++]=g[h++]}if(q==b)return b}d=-1;return q},M,F=function(a,c,b){var f,d,e,h,g,o,t,p=Array(316);for(f=0;f<p.length;f++)p[f]=0;l(5);o=257+v(5);y(5);l(5);t=1+v(5);y(5);l(4);f=4+v(4);y(4);if(286<o||30<t)return-1;for(d=0;d<f;d++)l(3),p[E[d]]=v(3),y(3);for(;19>d;d++)p[E[d]]=
|
||||
0;n=7;d=new G(p,19,19,null,null,n);if(0!=d.status)return-1;i=d.root;n=d.m;h=o+t;for(f=e=0;f<h;)if(l(n),g=i.list[v(n)],d=g.b,y(d),d=g.n,16>d)p[f++]=e=d;else if(16==d){l(2);d=3+v(2);y(2);if(f+d>h)return-1;for(;0<d--;)p[f++]=e}else{17==d?(l(3),d=3+v(3),y(3)):(l(7),d=11+v(7),y(7));if(f+d>h)return-1;for(;0<d--;)p[f++]=0;e=0}n=9;d=new G(p,o,257,z,s,n);0==n&&(d.status=1);if(0!=d.status)return-1;i=d.root;n=d.m;for(f=0;f<t;f++)p[f]=p[f+o];x=6;d=new G(p,t,0,q,u,x);j=d.root;x=d.m;return 0==x&&257<o||0!=d.status?
|
||||
-1:B(a,c,b)};this.inflate=function(r,w){null==g&&(g=Array(65536));b=c=m=0;d=-1;o=!1;f=h=0;i=null;p=r;t=0;var C=new runtime.ByteArray(w);a:{var E,I;for(E=0;E<w&&!(o&&-1==d);){if(0<f){if(0!=d)for(;0<f&&E<w;)f--,h&=32767,m&=32767,C[0+E++]=g[m++]=g[h++];else{for(;0<f&&E<w;)f--,m&=32767,l(8),C[0+E++]=g[m++]=v(8),y(8);0==f&&(d=-1)}if(E==w)break}if(-1==d){if(o)break;l(1);0!=v(1)&&(o=!0);y(1);l(2);d=v(2);y(2);i=null;f=0}switch(d){case 0:I=C;var P=0+E,L=w-E,H=void 0,H=b&7;y(H);l(16);H=v(16);y(16);l(16);if(H!=
|
||||
(~c&65535))I=-1;else{y(16);f=H;for(H=0;0<f&&H<L;)f--,m&=32767,l(8),I[P+H++]=g[m++]=v(8),y(8);0==f&&(d=-1);I=H}break;case 1:if(null!=i)I=B(C,0+E,w-E);else b:{I=C;P=0+E;L=w-E;if(null==e){for(var D=void 0,H=Array(288),D=void 0,D=0;144>D;D++)H[D]=8;for(;256>D;D++)H[D]=9;for(;280>D;D++)H[D]=7;for(;288>D;D++)H[D]=8;a=7;D=new G(H,288,257,z,s,a);if(0!=D.status){alert("HufBuild error: "+D.status);I=-1;break b}e=D.root;a=D.m;for(D=0;30>D;D++)H[D]=5;M=5;D=new G(H,30,0,q,u,M);if(1<D.status){e=null;alert("HufBuild error: "+
|
||||
D.status);I=-1;break b}k=D.root;M=D.m}i=e;j=k;n=a;x=M;I=B(I,P,L)}break;case 2:I=null!=i?B(C,0+E,w-E):F(C,0+E,w-E);break;default:I=-1}if(-1==I)break a;E+=I}}p=null;return C}};
|
||||
// Input 7
|
||||
core.Cursor=function(i,k){function e(a,e){for(var c=e;c&&c!==a;)c=c.parentNode;return c||e}function g(){var b,h,c;if(a.parentNode){h=0;for(b=a.parentNode.firstChild;b&&b!==a;)h+=1,b=b.nextSibling;if(a.previousSibling&&a.previousSibling.nodeType===3&&a.nextSibling&&a.nextSibling.nodeType===3)c=a.nextSibling,a.previousSibling.appendData(c.nodeValue);for(b=0;b<i.rangeCount;b+=1){var d=i.getRangeAt(b),f=h,j=void 0,g=void 0,j=a.parentNode,g=e(a,d.startContainer);e(a,d.endContainer);g===a?d.setStart(j,
|
||||
f):g===j&&d.startOffset>f&&d.setStart(j,d.startOffset-1);d.endContainer===a?d.setEnd(j,f):d.endContainer===j&&d.endOffset>f&&d.setEnd(j,d.endOffset-1)}if(c){for(b=0;b<i.rangeCount;b+=1){var d=i.getRangeAt(b),f=a.previousSibling,j=c,g=h,m=f.length-j.length;d.startContainer===j?d.setStart(f,m+d.startOffset):d.startContainer===f.parentNode&&d.startOffset===g&&d.setStart(f,m);d.endContainer===j?d.setEnd(f,m+d.endOffset):d.endContainer===f.parentNode&&d.endOffset===g&&d.setEnd(f,m)}c.parentNode.removeChild(c)}a.parentNode.removeChild(a)}}
|
||||
var a;a=k.createElementNS("urn:webodf:names:cursor","cursor");this.getNode=function(){return a};this.updateToSelection=function(){g();if(i.focusNode){var b=i.focusNode,e=i.focusOffset;if(b.nodeType===3){var c,d,f,j;j=b.parentNode;e===0?j.insertBefore(a,b):e===b.length?j.appendChild(a):(c=b.length,d=b.nextSibling,f=k.createTextNode(b.substringData(e,c)),b.deleteData(e,c),d?j.insertBefore(f,d):j.appendChild(f),j.insertBefore(a,f))}else if(b.nodeType!==9){for(c=b.firstChild;c&&e;)c=c.nextSibling,e-=
|
||||
1;b.insertBefore(a,c)}}};this.remove=function(){g()}};
|
||||
core.Cursor=function(g,m){function e(a,b){for(var d=b;d&&d!==a;)d=d.parentNode;return d||b}function k(){var c,b,d;if(a.parentNode){b=0;for(c=a.parentNode.firstChild;c&&c!==a;)b+=1,c=c.nextSibling;a.previousSibling&&3===a.previousSibling.nodeType&&a.nextSibling&&3===a.nextSibling.nodeType&&(d=a.nextSibling,a.previousSibling.appendData(d.nodeValue));for(c=0;c<g.rangeCount;c+=1){var o=g.getRangeAt(c),f=b,h=void 0,i=void 0,h=a.parentNode,i=e(a,o.startContainer);e(a,o.endContainer);i===a?o.setStart(h,
|
||||
f):i===h&&o.startOffset>f&&o.setStart(h,o.startOffset-1);o.endContainer===a?o.setEnd(h,f):o.endContainer===h&&o.endOffset>f&&o.setEnd(h,o.endOffset-1)}if(d){for(c=0;c<g.rangeCount;c+=1){var o=g.getRangeAt(c),f=a.previousSibling,h=d,i=b,j=f.length-h.length;o.startContainer===h?o.setStart(f,j+o.startOffset):o.startContainer===f.parentNode&&o.startOffset===i&&o.setStart(f,j);o.endContainer===h?o.setEnd(f,j+o.endOffset):o.endContainer===f.parentNode&&o.endOffset===i&&o.setEnd(f,j)}d.parentNode.removeChild(d)}a.parentNode.removeChild(a)}}
|
||||
var a;a=m.createElementNS("urn:webodf:names:cursor","cursor");this.getNode=function(){return a};this.updateToSelection=function(){k();if(g.focusNode){var c=g.focusNode,b=g.focusOffset;if(3===c.nodeType){var d,e,f,h;h=c.parentNode;0===b?h.insertBefore(a,c):b===c.length?h.appendChild(a):(d=c.length,e=c.nextSibling,f=m.createTextNode(c.substringData(b,d)),c.deleteData(b,d),e?h.insertBefore(f,e):h.appendChild(f),h.insertBefore(a,f))}else if(9!==c.nodeType){for(d=c.firstChild;d&&b;)d=d.nextSibling,b-=
|
||||
1;c.insertBefore(a,d)}}};this.remove=function(){k()}};
|
||||
// Input 8
|
||||
core.UnitTest=function(){};core.UnitTest.prototype.setUp=function(){};core.UnitTest.prototype.tearDown=function(){};core.UnitTest.prototype.description=function(){};core.UnitTest.prototype.tests=function(){};core.UnitTest.prototype.asyncTests=function(){};
|
||||
core.UnitTestRunner=function(){function i(a){g+=1;runtime.log("fail",a)}function k(a,b){var e;try{if(a.length!==b.length)return false;for(e=0;e<a.length;e+=1)if(a[e]!==b[e])return false}catch(c){return false}return true}function e(a,b,e){(typeof b!=="string"||typeof e!=="string")&&runtime.log("WARN: shouldBe() expects string arguments");var c,d;try{d=eval(b)}catch(f){c=f}a=eval(e);c?i(b+" should be "+a+". Threw exception "+c):(a===0?d===a&&1/d===1/a:d===a||(typeof a==="number"&&isNaN(a)?typeof d===
|
||||
"number"&&isNaN(d):Object.prototype.toString.call(a)===Object.prototype.toString.call([])&&k(d,a)))?runtime.log("pass",b+" is "+e):typeof d===typeof a?i(b+" should be "+a+". Was "+(d===0&&1/d<0?"-0":String(d))+"."):i(b+" should be "+a+" (of type "+typeof a+"). Was "+d+" (of type "+typeof d+").")}var g=0;this.shouldBeNull=function(a,b){e(a,b,"null")};this.shouldBeNonNull=function(a,b){var e,c;try{c=eval(b)}catch(d){e=d}e?i(b+" should be non-null. Threw exception "+e):c!==null?runtime.log("pass",b+
|
||||
" is non-null."):i(b+" should be non-null. Was "+c)};this.shouldBe=e;this.countFailedTests=function(){return g}};
|
||||
core.UnitTester=function(){var i=0,k={};this.runTests=function(e,g){function a(e){if(e.length===0)k[b]=f,i+=c.countFailedTests(),g();else{p=e[0];var j=Runtime.getFunctionName(p);runtime.log("Running "+j);l=c.countFailedTests();d.setUp();p(function(){d.tearDown();f[j]=l===c.countFailedTests();a(e.slice(1))})}}var b=Runtime.getFunctionName(e),h,c=new core.UnitTestRunner,d=new e(c),f={},j,p,m,l;if(b.hasOwnProperty(k))runtime.log("Test "+b+" has already run.");else{runtime.log("Running "+b+": "+d.description());
|
||||
m=d.tests();for(j=0;j<m.length;j+=1)p=m[j],h=Runtime.getFunctionName(p),runtime.log("Running "+h),l=c.countFailedTests(),d.setUp(),p(),d.tearDown(),f[h]=l===c.countFailedTests();a(d.asyncTests())}};this.countFailedTests=function(){return i};this.results=function(){return k}};
|
||||
core.UnitTestRunner=function(){function g(a){k+=1;runtime.log("fail",a)}function m(a,c){var b;try{if(a.length!==c.length)return!1;for(b=0;b<a.length;b+=1)if(a[b]!==c[b])return!1}catch(d){return!1}return!0}function e(a,c,b){("string"!==typeof c||"string"!==typeof b)&&runtime.log("WARN: shouldBe() expects string arguments");var d,e;try{e=eval(c)}catch(f){d=f}a=eval(b);d?g(c+" should be "+a+". Threw exception "+d):(0===a?e===a&&1/e===1/a:e===a||("number"===typeof a&&isNaN(a)?"number"===typeof e&&isNaN(e):
|
||||
Object.prototype.toString.call(a)===Object.prototype.toString.call([])&&m(e,a)))?runtime.log("pass",c+" is "+b):typeof e===typeof a?g(c+" should be "+a+". Was "+(0===e&&0>1/e?"-0":""+e)+"."):g(c+" should be "+a+" (of type "+typeof a+"). Was "+e+" (of type "+typeof e+").")}var k=0;this.shouldBeNull=function(a,c){e(a,c,"null")};this.shouldBeNonNull=function(a,c){var b,d;try{d=eval(c)}catch(e){b=e}b?g(c+" should be non-null. Threw exception "+b):null!==d?runtime.log("pass",c+" is non-null."):g(c+" should be non-null. Was "+
|
||||
d)};this.shouldBe=e;this.countFailedTests=function(){return k}};
|
||||
core.UnitTester=function(){var g=0,m={};this.runTests=function(e,k){function a(b){if(0===b.length)m[c]=f,g+=d.countFailedTests(),k();else{i=b[0];var e=Runtime.getFunctionName(i);runtime.log("Running "+e);n=d.countFailedTests();o.setUp();i(function(){o.tearDown();f[e]=n===d.countFailedTests();a(b.slice(1))})}}var c=Runtime.getFunctionName(e),b,d=new core.UnitTestRunner,o=new e(d),f={},h,i,j,n;if(c.hasOwnProperty(m))runtime.log("Test "+c+" has already run.");else{runtime.log("Running "+c+": "+o.description());
|
||||
j=o.tests();for(h=0;h<j.length;h+=1)i=j[h],b=Runtime.getFunctionName(i),runtime.log("Running "+b),n=d.countFailedTests(),o.setUp(),i(),o.tearDown(),f[b]=n===d.countFailedTests();a(o.asyncTests())}};this.countFailedTests=function(){return g};this.results=function(){return m}};
|
||||
// Input 9
|
||||
core.PointWalker=function(i){function k(a){for(var c=-1;a;)a=a.previousSibling,c+=1;return c}var e=i,g=null,a=i&&i.firstChild,b=0;this.setPoint=function(h,c){e=h;b=c;if(e.nodeType===3)g=a=null;else{for(a=e.firstChild;c;)c-=1,a=a.nextSibling;g=a?a.previousSibling:e.lastChild}};this.stepForward=function(){var h;if(e.nodeType===3&&(h=typeof e.nodeValue.length==="number"?e.nodeValue.length:e.nodeValue.length(),b<h))return b+=1,true;if(a)return a.nodeType===1?(e=a,g=null,a=e.firstChild,b=0):a.nodeType===
|
||||
3?(e=a,a=g=null,b=0):(g=a,a=a.nextSibling,b+=1),true;return e!==i?(g=e,a=g.nextSibling,e=e.parentNode,b=k(g)+1,true):false};this.stepBackward=function(){if(e.nodeType===3&&b>0)return b-=1,true;if(g)return g.nodeType===1?(e=g,g=e.lastChild,a=null,b=k(g)+1):g.nodeType===3?(e=g,a=g=null,b=typeof e.nodeValue.length==="number"?e.nodeValue.length:e.nodeValue.length()):(a=g,g=g.previousSibling,b-=1),true;return e!==i?(a=e,g=a.previousSibling,e=e.parentNode,b=k(a),true):false};this.node=function(){return e};
|
||||
this.position=function(){return b};this.precedingSibling=function(){return g};this.followingSibling=function(){return a}};
|
||||
core.PointWalker=function(g){function m(a){for(var c=-1;a;)a=a.previousSibling,c+=1;return c}var e=g,k=null,a=g&&g.firstChild,c=0;this.setPoint=function(b,d){e=b;c=d;if(3===e.nodeType)k=a=null;else{for(a=e.firstChild;d;)d-=1,a=a.nextSibling;k=a?a.previousSibling:e.lastChild}};this.stepForward=function(){var b;if(3===e.nodeType&&(b="number"===typeof e.nodeValue.length?e.nodeValue.length:e.nodeValue.length(),c<b))return c+=1,!0;if(a)return 1===a.nodeType?(e=a,k=null,a=e.firstChild,c=0):3===a.nodeType?
|
||||
(e=a,a=k=null,c=0):(k=a,a=a.nextSibling,c+=1),!0;return e!==g?(k=e,a=k.nextSibling,e=e.parentNode,c=m(k)+1,!0):!1};this.stepBackward=function(){if(3===e.nodeType&&0<c)return c-=1,!0;if(k)return 1===k.nodeType?(e=k,k=e.lastChild,a=null,c=m(k)+1):3===k.nodeType?(e=k,a=k=null,c="number"===typeof e.nodeValue.length?e.nodeValue.length:e.nodeValue.length()):(a=k,k=k.previousSibling,c-=1),!0;return e!==g?(a=e,k=a.previousSibling,e=e.parentNode,c=m(a),!0):!1};this.node=function(){return e};this.position=
|
||||
function(){return c};this.precedingSibling=function(){return k};this.followingSibling=function(){return a}};
|
||||
// Input 10
|
||||
core.Async=function(){this.forEach=function(i,k,e){function g(a){h!==b&&(a?(h=b,e(a)):(h+=1,h===b&&e(null)))}var a,b=i.length,h=0;for(a=0;a<b;a+=1)k(i[a],g)}};
|
||||
core.Async=function(){this.forEach=function(g,m,e){function k(a){b!==c&&(a?(b=c,e(a)):(b+=1,b===c&&e(null)))}var a,c=g.length,b=0;for(a=0;a<c;a+=1)m(g[a],k)}};
|
||||
// Input 11
|
||||
runtime.loadClass("core.RawInflate");runtime.loadClass("core.ByteArray");runtime.loadClass("core.ByteArrayWriter");
|
||||
core.Zip=function(i,k){function e(a){var c=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,
|
||||
runtime.loadClass("core.RawInflate");runtime.loadClass("core.ByteArray");runtime.loadClass("core.ByteArrayWriter");runtime.loadClass("core.Base64");
|
||||
core.Zip=function(g,m){function e(a){var c=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,
|
||||
853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,
|
||||
4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,
|
||||
225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,
|
||||
2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,
|
||||
2932959818,3654703836,1088359270,936918E3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117],b=0,f,d=a.length,e=0,e=0;b^=-1;for(f=0;f<d;f+=1)e=(b^a[f])&255,e=c[e],b=b>>>8^e;return b^-1}function g(a){return new Date((a>>25&127)+1980,(a>>21&15)-1,a>>16&31,a>>11&15,a>>5&63,(a&31)<<1)}function a(a){var c=a.getFullYear();return c<1980?0:c-
|
||||
1980<<25|a.getMonth()+1<<21|a.getDate()<<16|a.getHours()<<11|a.getMinutes()<<5|a.getSeconds()>>1}function b(a,c){var b,f,d,e,j,h,i,m=this;this.load=function(c){if(m.data!==void 0)c(null,m.data);else{var d=j+34+b+f+256;d+i>p&&(d=p-i);runtime.read(a,i,d,function(b,f){if(b)c(b,f);else a:{var d=f,g=new core.ByteArray(d),o=g.readUInt32LE(),r;if(o!==67324752)c("File entry signature is wrong."+o.toString()+" "+d.length.toString(),null);else{g.pos+=22;o=g.readUInt16LE();r=g.readUInt16LE();g.pos+=o+r;if(e){d=
|
||||
d.slice(g.pos,g.pos+j);if(j!==d.length){c("The amount of compressed bytes read was "+d.length.toString()+" instead of "+j.toString()+" for "+m.filename+" in "+a+".",null);break a}d=l(d,h)}else d=d.slice(g.pos,g.pos+h);h!==d.length?c("The amount of bytes read was "+d.length.toString()+" instead of "+h.toString()+" for "+m.filename+" in "+a+".",null):(m.data=d,c(null,d))}}})}};this.set=function(a,c,b,f){m.filename=a;m.data=c;m.compressed=b;m.date=f};this.error=null;if(c)c.readUInt32LE()!==33639248?
|
||||
this.error="Central directory entry has wrong signature at position "+(c.pos-4).toString()+' for file "'+a+'": '+c.data.length.toString():(c.pos+=6,e=c.readUInt16LE(),this.date=g(c.readUInt32LE()),c.readUInt32LE(),j=c.readUInt32LE(),h=c.readUInt32LE(),b=c.readUInt16LE(),f=c.readUInt16LE(),d=c.readUInt16LE(),c.pos+=8,i=c.readUInt32LE(),this.filename=runtime.byteArrayToString(c.data.slice(c.pos,c.pos+b),"utf8"),c.pos+=b+f+d)}function h(a,c){if(a.length!==22)c("Central directory length should be 22.",
|
||||
u);else{var f=new core.ByteArray(a),d;d=f.readUInt32LE();d!==101010256?c("Central directory signature is wrong: "+d.toString(),u):f.readUInt16LE()!==0?c("Zip files with non-zero disk numbers are not supported.",u):f.readUInt16LE()!==0?c("Zip files with non-zero disk numbers are not supported.",u):(d=f.readUInt16LE(),m=f.readUInt16LE(),d!==m?c("Number of entries is inconsistent.",u):(d=f.readUInt32LE(),f=f.readUInt16LE(),f=p-22-d,runtime.read(i,f,p-f,function(a,f){a:{var d=new core.ByteArray(f),e,
|
||||
g;j=[];for(e=0;e<m;e+=1){g=new b(i,d);if(g.error){c(g.error,u);break a}j[j.length]=g}c(null,u)}})))}}function c(c){var b=new core.ByteArrayWriter("utf8"),f=0;b.appendArray([80,75,3,4,20,0,0,0,0,0]);if(c.data)f=c.data.length;b.appendUInt32LE(a(c.date));b.appendUInt32LE(e(c.data));b.appendUInt32LE(f);b.appendUInt32LE(f);b.appendUInt16LE(c.filename.length);b.appendUInt16LE(0);b.appendString(c.filename);c.data&&b.appendByteArray(c.data);return b}function d(c,b){var f=new core.ByteArrayWriter("utf8"),
|
||||
d=0;f.appendArray([80,75,1,2,20,0,20,0,0,0,0,0]);if(c.data)d=c.data.length;f.appendUInt32LE(a(c.date));f.appendUInt32LE(e(c.data));f.appendUInt32LE(d);f.appendUInt32LE(d);f.appendUInt16LE(c.filename.length);f.appendArray([0,0,0,0,0,0,0,0,0,0,0,0]);f.appendUInt32LE(b);f.appendString(c.filename);return f}function f(a,c){if(a===j.length)c(null);else{var b=j[a];b.data!==void 0?f(a+1,c):b.load(function(b){b?c(b):f(a+1,c)})}}var j,p,m,l=(new core.RawInflate).inflate,u=this;this.load=function(a,c){var b=
|
||||
null,f,d;for(d=0;d<j.length;d+=1)if(f=j[d],f.filename===a){b=f;break}b?b.data?c(null,b.data):b.load(c):c(a+" not found.",null)};this.save=function(a,c,f,d){var e,g;for(e=0;e<j.length;e+=1)if(g=j[e],g.filename===a){g.set(a,c,f,d);return}g=new b(i);g.set(a,c,f,d);j.push(g)};this.write=function(a){f(0,function(b){if(b)a(b);else{var b=new core.ByteArrayWriter("utf8"),f,e,g,h=[0];for(f=0;f<j.length;f+=1)b.appendByteArrayWriter(c(j[f])),h.push(b.getLength());g=b.getLength();for(f=0;f<j.length;f+=1)e=j[f],
|
||||
b.appendByteArrayWriter(d(e,h[f]));f=b.getLength()-g;b.appendArray([80,75,5,6,0,0,0,0]);b.appendUInt16LE(j.length);b.appendUInt16LE(j.length);b.appendUInt32LE(f);b.appendUInt32LE(g);b.appendArray([0,0]);runtime.writeFile(i,b.getByteArray(),a)}})};this.getEntries=function(){return j.slice()};p=-1;k===null?j=[]:runtime.getFileSize(i,function(a){p=a;p<0?k("File '"+i+"' cannot be read.",u):runtime.read(i,p-22,22,function(a,c){a||k===null?k(a,u):h(c,k)})})};
|
||||
2932959818,3654703836,1088359270,936918E3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117],b,f,d=a.length,e=0,e=0;b=-1;for(f=0;f<d;f+=1)e=(b^a[f])&255,e=c[e],b=b>>>8^e;return b^-1}function k(a){return new Date((a>>25&127)+1980,(a>>21&15)-1,a>>16&31,a>>11&15,a>>5&63,(a&31)<<1)}function a(a){var c=a.getFullYear();return 1980>c?0:c-1980<<
|
||||
25|a.getMonth()+1<<21|a.getDate()<<16|a.getHours()<<11|a.getMinutes()<<5|a.getSeconds()>>1}function c(a,c){var b,f,d,e,j,h,l,g=this;this.load=function(c){if(void 0!==g.data)c(null,g.data);else{var d=j+34+b+f+256;d+l>n&&(d=n-l);runtime.read(a,l,d,function(b,f){if(b)c(b,f);else a:{var d=f,l=new core.ByteArray(d),i=l.readUInt32LE(),q;if(67324752!==i)c("File entry signature is wrong."+i.toString()+" "+d.length.toString(),null);else{l.pos+=22;i=l.readUInt16LE();q=l.readUInt16LE();l.pos+=i+q;if(e){d=d.slice(l.pos,
|
||||
l.pos+j);if(j!==d.length){c("The amount of compressed bytes read was "+d.length.toString()+" instead of "+j.toString()+" for "+g.filename+" in "+a+".",null);break a}d=p(d,h)}else d=d.slice(l.pos,l.pos+h);h!==d.length?c("The amount of bytes read was "+d.length.toString()+" instead of "+h.toString()+" for "+g.filename+" in "+a+".",null):(g.data=d,c(null,d))}}})}};this.set=function(a,c,b,d){g.filename=a;g.data=c;g.compressed=b;g.date=d};this.error=null;c&&(33639248!==c.readUInt32LE()?this.error="Central directory entry has wrong signature at position "+
|
||||
(c.pos-4).toString()+' for file "'+a+'": '+c.data.length.toString():(c.pos+=6,e=c.readUInt16LE(),this.date=k(c.readUInt32LE()),c.readUInt32LE(),j=c.readUInt32LE(),h=c.readUInt32LE(),b=c.readUInt16LE(),f=c.readUInt16LE(),d=c.readUInt16LE(),c.pos+=8,l=c.readUInt32LE(),this.filename=runtime.byteArrayToString(c.data.slice(c.pos,c.pos+b),"utf8"),c.pos+=b+f+d))}function b(a,b){if(22!==a.length)b("Central directory length should be 22.",t);else{var d=new core.ByteArray(a),f;f=d.readUInt32LE();101010256!==
|
||||
f?b("Central directory signature is wrong: "+f.toString(),t):0!==d.readUInt16LE()?b("Zip files with non-zero disk numbers are not supported.",t):0!==d.readUInt16LE()?b("Zip files with non-zero disk numbers are not supported.",t):(f=d.readUInt16LE(),x=d.readUInt16LE(),f!==x?b("Number of entries is inconsistent.",t):(f=d.readUInt32LE(),d=d.readUInt16LE(),d=n-22-f,runtime.read(g,d,n-d,function(a,d){a:{var f=new core.ByteArray(d),e,l;j=[];for(e=0;e<x;e+=1){l=new c(g,f);if(l.error){b(l.error,t);break a}j[j.length]=
|
||||
l}b(null,t)}})))}}function d(a,c){var b=null,d,f;for(f=0;f<j.length;f+=1)if(d=j[f],d.filename===a){b=d;break}b?b.data?c(null,b.data):b.load(c):c(a+" not found.",null)}function o(a,c){d(a,function(a,b){if(a)return c(a,null);b=runtime.byteArrayToString(b,"utf8");c(null,b)})}function f(c){var b=new core.ByteArrayWriter("utf8"),d=0;b.appendArray([80,75,3,4,20,0,0,0,0,0]);c.data&&(d=c.data.length);b.appendUInt32LE(a(c.date));b.appendUInt32LE(e(c.data));b.appendUInt32LE(d);b.appendUInt32LE(d);b.appendUInt16LE(c.filename.length);
|
||||
b.appendUInt16LE(0);b.appendString(c.filename);c.data&&b.appendByteArray(c.data);return b}function h(c,b){var d=new core.ByteArrayWriter("utf8"),f=0;d.appendArray([80,75,1,2,20,0,20,0,0,0,0,0]);c.data&&(f=c.data.length);d.appendUInt32LE(a(c.date));d.appendUInt32LE(e(c.data));d.appendUInt32LE(f);d.appendUInt32LE(f);d.appendUInt16LE(c.filename.length);d.appendArray([0,0,0,0,0,0,0,0,0,0,0,0]);d.appendUInt32LE(b);d.appendString(c.filename);return d}function i(a,c){if(a===j.length)c(null);else{var b=j[a];
|
||||
void 0!==b.data?i(a+1,c):b.load(function(b){b?c(b):i(a+1,c)})}}var j,n,x,p=(new core.RawInflate).inflate,t=this,r=new core.Base64;this.load=d;this.save=function(a,b,d,f){var e,h;for(e=0;e<j.length;e+=1)if(h=j[e],h.filename===a){h.set(a,b,d,f);return}h=new c(g);h.set(a,b,d,f);j.push(h)};this.write=function(a){i(0,function(c){if(c)a(c);else{var c=new core.ByteArrayWriter("utf8"),b,d,e,i=[0];for(b=0;b<j.length;b+=1)c.appendByteArrayWriter(f(j[b])),i.push(c.getLength());e=c.getLength();for(b=0;b<j.length;b+=
|
||||
1)d=j[b],c.appendByteArrayWriter(h(d,i[b]));b=c.getLength()-e;c.appendArray([80,75,5,6,0,0,0,0]);c.appendUInt16LE(j.length);c.appendUInt16LE(j.length);c.appendUInt32LE(b);c.appendUInt32LE(e);c.appendArray([0,0]);runtime.writeFile(g,c.getByteArray(),a)}})};this.loadContentXmlAsFragments=function(a,c){o(a,function(a,b){if(a)return c.rootElementReady(a);c.rootElementReady(null,b,!0)})};this.loadAsString=o;this.loadAsDOM=function(a,c){o(a,function(a,b){a?c(a,null):(b=(new DOMParser).parseFromString(b,
|
||||
"text/xml"),c(null,b))})};this.loadAsDataURL=function(a,c,b){d(a,function(a,d){if(a)return b(a,null);var f=0,e;c||(c=80===d[1]&&78===d[2]&&71===d[3]?"image/png":255===d[0]&&216===d[1]&&255===d[2]?"image/jpeg":71===d[0]&&73===d[1]&&70===d[2]?"image/gif":"");for(e="data:"+c+";base64,";f<d.length;)e+=r.convertUTF8ArrayToBase64(d.slice(f,Math.min(f+45E3,d.length))),f+=45E3;b(null,e)})};this.getEntries=function(){return j.slice()};n=-1;null===m?j=[]:runtime.getFileSize(g,function(a){n=a;0>n?m("File '"+
|
||||
g+"' cannot be read.",t):runtime.read(g,n-22,22,function(a,c){a||null===m?m(a,t):b(c,m)})})};
|
||||
// Input 12
|
||||
xmldom.LSSerializerFilter=function(){};
|
||||
// Input 13
|
||||
typeof Object.create!=="function"&&(Object.create=function(i){var k=function(){};k.prototype=i;return new k});
|
||||
xmldom.LSSerializer=function(){function i(e,g){var a="",b=Object.create(e),h=k.filter?k.filter.acceptNode(g):1,c;if(h===1){c="";var d=g.attributes,f,j,p,m="",l;if(d){if(b[g.namespaceURI]!==g.prefix)b[g.namespaceURI]=g.prefix;c+="<"+g.nodeName;f=d.length;for(j=0;j<f;j+=1)if(p=d.item(j),p.namespaceURI!=="http://www.w3.org/2000/xmlns/"&&(l=k.filter?k.filter.acceptNode(p):1,l===1)){if(p.namespaceURI){l=p.prefix;var u=p.namespaceURI;b.hasOwnProperty(u)?l=b[u]+":":(b[u]!==l&&(b[u]=l),l+=":")}else l="";
|
||||
m+=" "+(l+p.localName+'="'+p.nodeValue+'"')}for(j in b)b.hasOwnProperty(j)&&((l=b[j])?l!=="xmlns"&&(c+=" xmlns:"+b[j]+'="'+j+'"'):c+=' xmlns="'+j+'"');c+=m+">"}a+=c}if(h===1||h===3){for(c=g.firstChild;c;)a+=i(b,c),c=c.nextSibling;g.nodeValue&&(a+=g.nodeValue)}h===1&&(b="",g.nodeType===1&&(b+="</"+g.nodeName+">"),a+=b);return a}var k=this;this.filter=null;this.writeToString=function(e,g){if(!e)return"";var a;if(g){a=g;var b={},h;for(h in a)a.hasOwnProperty(h)&&(b[a[h]]=h);a=b}else a={};return i(a,
|
||||
"function"!==typeof Object.create&&(Object.create=function(g){var m=function(){};m.prototype=g;return new m});
|
||||
xmldom.LSSerializer=function(){function g(e,k){var a="",c=Object.create(e),b=m.filter?m.filter.acceptNode(k):1,d;if(1===b){d="";var o=k.attributes,f,h,i,j="",n;if(o){c[k.namespaceURI]!==k.prefix&&(c[k.namespaceURI]=k.prefix);d+="<"+k.nodeName;f=o.length;for(h=0;h<f;h+=1)if(i=o.item(h),"http://www.w3.org/2000/xmlns/"!==i.namespaceURI&&(n=m.filter?m.filter.acceptNode(i):1,1===n)){if(i.namespaceURI){n=i.prefix;var x=i.namespaceURI;c.hasOwnProperty(x)?n=c[x]+":":(c[x]!==n&&(c[x]=n),n+=":")}else n="";
|
||||
j+=" "+(n+i.localName+'="'+i.nodeValue+'"')}for(h in c)c.hasOwnProperty(h)&&((n=c[h])?"xmlns"!==n&&(d+=" xmlns:"+c[h]+'="'+h+'"'):d+=' xmlns="'+h+'"');d+=j+">"}a+=d}if(1===b||3===b){for(d=k.firstChild;d;)a+=g(c,d),d=d.nextSibling;k.nodeValue&&(a+=k.nodeValue)}1===b&&(c="",1===k.nodeType&&(c+="</"+k.nodeName+">"),a+=c);return a}var m=this;this.filter=null;this.writeToString=function(e,k){if(!e)return"";var a;if(k){a=k;var c={},b;for(b in a)a.hasOwnProperty(b)&&(c[a[b]]=b);a=c}else a={};return g(a,
|
||||
e)}};
|
||||
// Input 14
|
||||
xmldom.RelaxNGParser=function(){function i(a,c){this.message=function(){c&&(a+=c.nodeType===1?" Element ":" Node ",a+=c.nodeName,c.nodeValue&&(a+=" with value '"+c.nodeValue+"'"),a+=".");return a}}function k(a){if(a.e.length<=2)return a;var c={name:a.name,e:a.e.slice(0,2)};return k({name:a.name,e:[c].concat(a.e.slice(2))})}function e(a){var a=a.split(":",2),b="",d;a.length===1?a=["",a[0]]:b=a[0];for(d in c)c[d]===b&&(a[0]=d);return a}function g(a,c){var j;var f;for(var b=0,d,h,i=a.name;a.e&&b<a.e.length;)if(d=
|
||||
a.e[b],d.name==="ref"){h=c[d.a.name];if(!h)throw d.a.name+" was not defined.";d=a.e.slice(b+1);a.e=a.e.slice(0,b);a.e=a.e.concat(h.e);a.e=a.e.concat(d)}else b+=1,g(d,c);d=a.e;if(i==="choice"&&(!d||!d[1]||d[1].name==="empty"))!d||!d[0]||d[0].name==="empty"?(delete a.e,a.name="empty"):(d[1]=d[0],d[0]={name:"empty"});if(i==="group"||i==="interleave")if(d[0].name==="empty")d[1].name==="empty"?(delete a.e,a.name="empty"):(i=a.name=d[1].name,a.names=d[1].names,f=a.e=d[1].e,d=f);else if(d[1].name==="empty")i=
|
||||
a.name=d[0].name,a.names=d[0].names,j=a.e=d[0].e,d=j;if(i==="oneOrMore"&&d[0].name==="empty")delete a.e,a.name="empty";if(i==="attribute"){h=a.names?a.names.length:0;for(var k,q=a.localnames=[h],r=a.namespaces=[h],b=0;b<h;b+=1)k=e(a.names[b]),r[b]=k[0],q[b]=k[1]}if(i==="interleave")if(d[0].name==="interleave")d[1].name==="interleave"?a.e=d[0].e.concat(d[1].e):a.e=[d[1]].concat(d[0].e);else if(d[1].name==="interleave")a.e=[d[0]].concat(d[1].e)}function a(c,b){for(var d=0,e;c.e&&d<c.e.length;)e=c.e[d],
|
||||
e.name==="elementref"?(e.id=e.id||0,c.e[d]=b[e.id]):e.name!=="element"&&a(e,b),d+=1}var b=this,h,c={"http://www.w3.org/XML/1998/namespace":"xml"},d;d=function(a,b,g){var h=[],i,u,n=a.localName,q=[];i=a.attributes;var r=n,y=q,C={},s,v;for(s=0;s<i.length;s+=1)if(v=i.item(s),v.namespaceURI){if(v.namespaceURI==="http://www.w3.org/2000/xmlns/")c[v.value]=v.localName}else{v.localName==="name"&&(r==="element"||r==="attribute")&&y.push(v.value);if(v.localName==="name"||v.localName==="combine"||v.localName===
|
||||
"type"){var E=v,B;B=v.value;B=B.replace(/^\s\s*/,"");for(var z=/\s/,G=B.length-1;z.test(B.charAt(G));)G-=1;B=B.slice(0,G+1);E.value=B}C[v.localName]=v.value}i=C;i.combine=i.combine||void 0;a=a.firstChild;r=h;y=q;for(C="";a;){if(a.nodeType===1&&a.namespaceURI==="http://relaxng.org/ns/structure/1.0"){if(s=d(a,b,r))s.name==="name"?y.push(c[s.a.ns]+":"+s.text):s.name==="choice"&&s.names&&s.names.length&&(y=y.concat(s.names),delete s.names),r.push(s)}else a.nodeType===3&&(C+=a.nodeValue);a=a.nextSibling}a=
|
||||
C;n!=="value"&&n!=="param"&&(a=/^\s*([\s\S]*\S)?\s*$/.exec(a)[1]);if(n==="value"&&i.type===void 0)i.type="token",i.datatypeLibrary="";if((n==="attribute"||n==="element")&&i.name!==void 0)u=e(i.name),h=[{name:"name",text:u[1],a:{ns:u[0]}}].concat(h),delete i.name;if(n==="name"||n==="nsName"||n==="value"){if(i.ns===void 0)i.ns=""}else delete i.ns;if(n==="name")u=e(a),i.ns=u[0],a=u[1];if(h.length>1&&(n==="define"||n==="oneOrMore"||n==="zeroOrMore"||n==="optional"||n==="list"||n==="mixed"))h=[{name:"group",
|
||||
e:k({name:"group",e:h}).e}];h.length>2&&n==="element"&&(h=[h[0]].concat({name:"group",e:k({name:"group",e:h.slice(1)}).e}));h.length===1&&n==="attribute"&&h.push({name:"text",text:a});if(h.length===1&&(n==="choice"||n==="group"||n==="interleave"))n=h[0].name,q=h[0].names,i=h[0].a,a=h[0].text,h=h[0].e;else if(h.length>2&&(n==="choice"||n==="group"||n==="interleave"))h=k({name:n,e:h}).e;n==="mixed"&&(n="interleave",h=[h[0],{name:"text"}]);n==="optional"&&(n="choice",h=[h[0],{name:"empty"}]);n==="zeroOrMore"&&
|
||||
(n="choice",h=[{name:"oneOrMore",e:[h[0]]},{name:"empty"}]);if(n==="define"&&i.combine){a:{r=i.combine;y=i.name;C=h;for(s=0;g&&s<g.length;s+=1)if(v=g[s],v.name==="define"&&v.a&&v.a.name===y){v.e=[{name:r,e:v.e.concat(C)}];g=v;break a}g=null}if(g)return}g={name:n};if(h&&h.length>0)g.e=h;for(u in i)if(i.hasOwnProperty(u)){g.a=i;break}if(a!==void 0)g.text=a;if(q&&q.length>0)g.names=q;if(n==="element")g.id=b.length,b.push(g),g={name:"elementref",id:g.id};return g};this.parseRelaxNGDOM=function(f,e){var k=
|
||||
[],m=d(f&&f.documentElement,k,void 0),l,u,n={};for(l=0;l<m.e.length;l+=1)u=m.e[l],u.name==="define"?n[u.a.name]=u:u.name==="start"&&(h=u);if(!h)return[new i("No Relax NG start element was found.")];g(h,n);for(l in n)n.hasOwnProperty(l)&&g(n[l],n);for(l=0;l<k.length;l+=1)g(k[l],n);if(e)b.rootPattern=e(h.e[0],k);a(h,k);for(l=0;l<k.length;l+=1)a(k[l],k);b.start=h;b.elements=k;b.nsmap=c;return null}};
|
||||
xmldom.RelaxNGParser=function(){function g(a,c){this.message=function(){c&&(a+=1===c.nodeType?" Element ":" Node ",a+=c.nodeName,c.nodeValue&&(a+=" with value '"+c.nodeValue+"'"),a+=".");return a}}function m(a){if(2>=a.e.length)return a;var c={name:a.name,e:a.e.slice(0,2)};return m({name:a.name,e:[c].concat(a.e.slice(2))})}function e(a){var a=a.split(":",2),c="",b;1===a.length?a=["",a[0]]:c=a[0];for(b in d)d[b]===c&&(a[0]=b);return a}function k(a,c){for(var b=0,d,g,o=a.name;a.e&&b<a.e.length;)if(d=
|
||||
a.e[b],"ref"===d.name){g=c[d.a.name];if(!g)throw d.a.name+" was not defined.";d=a.e.slice(b+1);a.e=a.e.slice(0,b);a.e=a.e.concat(g.e);a.e=a.e.concat(d)}else b+=1,k(d,c);d=a.e;if("choice"===o&&(!d||!d[1]||"empty"===d[1].name))!d||!d[0]||"empty"===d[0].name?(delete a.e,a.name="empty"):(d[1]=d[0],d[0]={name:"empty"});if("group"===o||"interleave"===o)"empty"===d[0].name?"empty"===d[1].name?(delete a.e,a.name="empty"):(o=a.name=d[1].name,a.names=d[1].names,d=a.e=d[1].e):"empty"===d[1].name&&(o=a.name=
|
||||
d[0].name,a.names=d[0].names,d=a.e=d[0].e);"oneOrMore"===o&&"empty"===d[0].name&&(delete a.e,a.name="empty");if("attribute"===o){g=a.names?a.names.length:0;for(var p,t=a.localnames=[g],r=a.namespaces=[g],b=0;b<g;b+=1)p=e(a.names[b]),r[b]=p[0],t[b]=p[1]}"interleave"===o&&("interleave"===d[0].name?"interleave"===d[1].name?a.e=d[0].e.concat(d[1].e):a.e=[d[1]].concat(d[0].e):"interleave"===d[1].name&&(a.e=[d[0]].concat(d[1].e)))}function a(c,d){for(var b=0,e;c.e&&b<c.e.length;)e=c.e[b],"elementref"===
|
||||
e.name?(e.id=e.id||0,c.e[b]=d[e.id]):"element"!==e.name&&a(e,d),b+=1}var c=this,b,d={"http://www.w3.org/XML/1998/namespace":"xml"},o;o=function(a,c,b){var g=[],n,k,p=a.localName,t=[];n=a.attributes;var r=p,z=t,s={},q,u;for(q=0;q<n.length;q+=1)if(u=n.item(q),u.namespaceURI)"http://www.w3.org/2000/xmlns/"===u.namespaceURI&&(d[u.value]=u.localName);else{"name"===u.localName&&("element"===r||"attribute"===r)&&z.push(u.value);if("name"===u.localName||"combine"===u.localName||"type"===u.localName){var E=
|
||||
u,C;C=u.value;C=C.replace(/^\s\s*/,"");for(var w=/\s/,G=C.length-1;w.test(C.charAt(G));)G-=1;C=C.slice(0,G+1);E.value=C}s[u.localName]=u.value}n=s;n.combine=n.combine||void 0;a=a.firstChild;r=g;z=t;for(s="";a;){if(1===a.nodeType&&"http://relaxng.org/ns/structure/1.0"===a.namespaceURI){if(q=o(a,c,r))"name"===q.name?z.push(d[q.a.ns]+":"+q.text):"choice"===q.name&&q.names&&q.names.length&&(z=z.concat(q.names),delete q.names),r.push(q)}else 3===a.nodeType&&(s+=a.nodeValue);a=a.nextSibling}a=s;"value"!==
|
||||
p&&"param"!==p&&(a=/^\s*([\s\S]*\S)?\s*$/.exec(a)[1]);"value"===p&&void 0===n.type&&(n.type="token",n.datatypeLibrary="");if(("attribute"===p||"element"===p)&&void 0!==n.name)k=e(n.name),g=[{name:"name",text:k[1],a:{ns:k[0]}}].concat(g),delete n.name;"name"===p||"nsName"===p||"value"===p?void 0===n.ns&&(n.ns=""):delete n.ns;"name"===p&&(k=e(a),n.ns=k[0],a=k[1]);if(1<g.length&&("define"===p||"oneOrMore"===p||"zeroOrMore"===p||"optional"===p||"list"===p||"mixed"===p))g=[{name:"group",e:m({name:"group",
|
||||
e:g}).e}];2<g.length&&"element"===p&&(g=[g[0]].concat({name:"group",e:m({name:"group",e:g.slice(1)}).e}));1===g.length&&"attribute"===p&&g.push({name:"text",text:a});if(1===g.length&&("choice"===p||"group"===p||"interleave"===p))p=g[0].name,t=g[0].names,n=g[0].a,a=g[0].text,g=g[0].e;else if(2<g.length&&("choice"===p||"group"===p||"interleave"===p))g=m({name:p,e:g}).e;"mixed"===p&&(p="interleave",g=[g[0],{name:"text"}]);"optional"===p&&(p="choice",g=[g[0],{name:"empty"}]);"zeroOrMore"===p&&(p="choice",
|
||||
g=[{name:"oneOrMore",e:[g[0]]},{name:"empty"}]);if("define"===p&&n.combine){a:{r=n.combine;z=n.name;s=g;for(q=0;b&&q<b.length;q+=1)if(u=b[q],"define"===u.name&&u.a&&u.a.name===z){u.e=[{name:r,e:u.e.concat(s)}];b=u;break a}b=null}if(b)return}b={name:p};g&&0<g.length&&(b.e=g);for(k in n)if(n.hasOwnProperty(k)){b.a=n;break}void 0!==a&&(b.text=a);t&&0<t.length&&(b.names=t);"element"===p&&(b.id=c.length,c.push(b),b={name:"elementref",id:b.id});return b};this.parseRelaxNGDOM=function(f,e){var i=[],j=o(f&&
|
||||
f.documentElement,i,void 0),n,m,p={};for(n=0;n<j.e.length;n+=1)m=j.e[n],"define"===m.name?p[m.a.name]=m:"start"===m.name&&(b=m);if(!b)return[new g("No Relax NG start element was found.")];k(b,p);for(n in p)p.hasOwnProperty(n)&&k(p[n],p);for(n=0;n<i.length;n+=1)k(i[n],p);e&&(c.rootPattern=e(b.e[0],i));a(b,i);for(n=0;n<i.length;n+=1)a(i[n],i);c.start=b;c.elements=i;c.nsmap=d;return null}};
|
||||
// Input 15
|
||||
runtime.loadClass("xmldom.RelaxNGParser");
|
||||
xmldom.RelaxNG=function(){function i(a){return function(){var c;return function(){c===void 0&&(c=a());return c}}()}function k(a,c){return function(){var b={},d=0;return function(f){var e=f.hash||f.toString(),g;g=b[e];if(g!==void 0)return g;b[e]=g=c(f);g.hash=a+d.toString();d+=1;return g}}()}function e(a){return function(){var c={};return function(b){var d,f;f=c[b.localName];if(f===void 0)c[b.localName]=f={};else if(d=f[b.namespaceURI],d!==void 0)return d;return f[b.namespaceURI]=d=a(b)}}()}function g(a,
|
||||
c,b){return function(){var d={},f=0;return function(e,g){var h=c&&c(e,g),j,i;if(h!==void 0)return h;h=e.hash||e.toString();j=g.hash||g.toString();i=d[h];if(i===void 0)d[h]=i={};else if(h=i[j],h!==void 0)return h;i[j]=h=b(e,g);h.hash=a+f.toString();f+=1;return h}}()}function a(c,b){b.p1.type==="choice"?a(c,b.p1):c[b.p1.hash]=b.p1;b.p2.type==="choice"?a(c,b.p2):c[b.p2.hash]=b.p2}function b(a,c){return{type:"element",nc:a,nullable:false,textDeriv:function(){return s},startTagOpenDeriv:function(b){return a.contains(b)?
|
||||
l(c,v):s},attDeriv:function(){return s},startTagCloseDeriv:function(){return this}}}function h(){return{type:"list",nullable:false,hash:"list",textDeriv:function(){return v}}}function c(a,b,d,e){if(b===s)return s;if(e>=d.length)return b;e===0&&(e=0);for(var g=d.item(e);g.namespaceURI===f;){e+=1;if(e>=d.length)return b;g=d.item(e)}return g=c(a,b.attDeriv(a,d.item(e)),d,e+1)}function d(a,c,b){b.e[0].a?(a.push(b.e[0].text),c.push(b.e[0].a.ns)):d(a,c,b.e[0]);b.e[1].a?(a.push(b.e[1].text),c.push(b.e[1].a.ns)):
|
||||
d(a,c,b.e[1])}var f="http://www.w3.org/2000/xmlns/",j,p,m,l,u,n,q,r,y,C,s={type:"notAllowed",nullable:false,hash:"notAllowed",textDeriv:function(){return s},startTagOpenDeriv:function(){return s},attDeriv:function(){return s},startTagCloseDeriv:function(){return s},endTagDeriv:function(){return s}},v={type:"empty",nullable:true,hash:"empty",textDeriv:function(){return s},startTagOpenDeriv:function(){return s},attDeriv:function(){return s},startTagCloseDeriv:function(){return v},endTagDeriv:function(){return s}},
|
||||
E={type:"text",nullable:true,hash:"text",textDeriv:function(){return E},startTagOpenDeriv:function(){return s},attDeriv:function(){return s},startTagCloseDeriv:function(){return E},endTagDeriv:function(){return s}},B,z,G;j=g("choice",function(a,c){if(a===s)return c;if(c===s)return a;if(a===c)return a},function(c,b){var d={},f;a(d,{p1:c,p2:b});b=c=void 0;for(f in d)d.hasOwnProperty(f)&&(c===void 0?c=d[f]:b=b===void 0?d[f]:j(b,d[f]));return function(a,c){return{type:"choice",p1:a,p2:c,nullable:a.nullable||
|
||||
c.nullable,textDeriv:function(b,d){return j(a.textDeriv(b,d),c.textDeriv(b,d))},startTagOpenDeriv:e(function(b){return j(a.startTagOpenDeriv(b),c.startTagOpenDeriv(b))}),attDeriv:function(b,d){return j(a.attDeriv(b,d),c.attDeriv(b,d))},startTagCloseDeriv:i(function(){return j(a.startTagCloseDeriv(),c.startTagCloseDeriv())}),endTagDeriv:i(function(){return j(a.endTagDeriv(),c.endTagDeriv())})}}(c,b)});p=function(a,c,b){return function(){var d={},f=0;return function(e,g){var h=c&&c(e,g),j,i;if(h!==
|
||||
void 0)return h;h=e.hash||e.toString();j=g.hash||g.toString();h<j&&(i=h,h=j,j=i,i=e,e=g,g=i);i=d[h];if(i===void 0)d[h]=i={};else if(h=i[j],h!==void 0)return h;i[j]=h=b(e,g);h.hash=a+f.toString();f+=1;return h}}()}("interleave",function(a,c){if(a===s||c===s)return s;if(a===v)return c;if(c===v)return a},function(a,c){return{type:"interleave",p1:a,p2:c,nullable:a.nullable&&c.nullable,textDeriv:function(b,d){return j(p(a.textDeriv(b,d),c),p(a,c.textDeriv(b,d)))},startTagOpenDeriv:e(function(b){return j(B(function(a){return p(a,
|
||||
c)},a.startTagOpenDeriv(b)),B(function(c){return p(a,c)},c.startTagOpenDeriv(b)))}),attDeriv:function(b,d){return j(p(a.attDeriv(b,d),c),p(a,c.attDeriv(b,d)))},startTagCloseDeriv:i(function(){return p(a.startTagCloseDeriv(),c.startTagCloseDeriv())})}});m=g("group",function(a,c){if(a===s||c===s)return s;if(a===v)return c;if(c===v)return a},function(a,c){return{type:"group",p1:a,p2:c,nullable:a.nullable&&c.nullable,textDeriv:function(b,d){var f=m(a.textDeriv(b,d),c);return a.nullable?j(f,c.textDeriv(b,
|
||||
d)):f},startTagOpenDeriv:function(b){var d=B(function(a){return m(a,c)},a.startTagOpenDeriv(b));return a.nullable?j(d,c.startTagOpenDeriv(b)):d},attDeriv:function(b,d){return j(m(a.attDeriv(b,d),c),m(a,c.attDeriv(b,d)))},startTagCloseDeriv:i(function(){return m(a.startTagCloseDeriv(),c.startTagCloseDeriv())})}});l=g("after",function(a,c){if(a===s||c===s)return s},function(a,c){return{type:"after",p1:a,p2:c,nullable:false,textDeriv:function(b,d){return l(a.textDeriv(b,d),c)},startTagOpenDeriv:e(function(b){return B(function(a){return l(a,
|
||||
c)},a.startTagOpenDeriv(b))}),attDeriv:function(b,d){return l(a.attDeriv(b,d),c)},startTagCloseDeriv:i(function(){return l(a.startTagCloseDeriv(),c)}),endTagDeriv:i(function(){return a.nullable?c:s})}});u=k("oneormore",function(a){return a===s?s:{type:"oneOrMore",p:a,nullable:a.nullable,textDeriv:function(c,b){return m(a.textDeriv(c,b),j(this,v))},startTagOpenDeriv:function(c){var b=this;return B(function(a){return m(a,j(b,v))},a.startTagOpenDeriv(c))},attDeriv:function(c,b){return m(a.attDeriv(c,
|
||||
b),j(this,v))},startTagCloseDeriv:i(function(){return u(a.startTagCloseDeriv())})}});q=g("attribute",void 0,function(a,c){return{type:"attribute",nullable:false,nc:a,p:c,attDeriv:function(b,d){return a.contains(d)&&(c.nullable&&/^\s+$/.test(d.nodeValue)||c.textDeriv(b,d.nodeValue).nullable)?v:s},startTagCloseDeriv:function(){return s}}});n=k("value",function(a){return{type:"value",nullable:false,value:a,textDeriv:function(c,b){return b===a?v:s},attDeriv:function(){return s},startTagCloseDeriv:function(){return this}}});
|
||||
y=k("data",function(a){return{type:"data",nullable:false,dataType:a,textDeriv:function(){return v},attDeriv:function(){return s},startTagCloseDeriv:function(){return this}}});B=function x(a,c){if(c.type==="after")return l(c.p1,a(c.p2));else if(c.type==="choice")return j(x(a,c.p1),x(a,c.p2));return c};z=function(a,b,d){for(var f=d.currentNode,b=b.startTagOpenDeriv(f),b=c(a,b,f.attributes,0),e=b=b.startTagCloseDeriv(),f=d.currentNode,b=d.firstChild(),g=0,h=[];b;)b.nodeType===1?h.push(b):b.nodeType===
|
||||
3&&!/^\s*$/.test(b.nodeValue)&&(h.push(b.nodeValue),g+=1),b=d.nextSibling();h.length===0&&(h=[""]);g=e;for(e=0;g!==s&&e<h.length;e+=1)b=h[e],typeof b==="string"?g=/^\s*$/.test(b)?j(g,g.textDeriv(a,b)):g.textDeriv(a,b):(d.currentNode=b,g=z(a,g,d));d.currentNode=f;return b=g.endTagDeriv()};r=function(a){var c,b,f;if(a.name==="name")return c=a.text,b=a.a.ns,{name:c,ns:b,hash:"{"+b+"}"+c,contains:function(a){return a.namespaceURI===b&&a.localName===c}};else if(a.name==="choice"){c=[];b=[];d(c,b,a);a=
|
||||
"";for(f=0;f<c.length;f+=1)a+="{"+b[f]+"}"+c[f]+",";return{hash:a,contains:function(a){var d;for(d=0;d<c.length;d+=1)if(c[d]===a.localName&&b[d]===a.namespaceURI)return true;return false}}}return{hash:"anyName",contains:function(){return true}}};C=function t(a,c){var d,f;if(a.name==="elementref"){d=a.id||0;a=c[d];if(a.name!==void 0){var e=a;d=c[e.id]={hash:"element"+e.id.toString()};e=b(r(e.e[0]),C(e.e[1],c));for(f in e)e.hasOwnProperty(f)&&(d[f]=e[f]);f=d}else f=a;return f}switch(a.name){case "empty":return v;
|
||||
case "notAllowed":return s;case "text":return E;case "choice":return j(t(a.e[0],c),t(a.e[1],c));case "interleave":d=t(a.e[0],c);for(f=1;f<a.e.length;f+=1)d=p(d,t(a.e[f],c));return d;case "group":return m(t(a.e[0],c),t(a.e[1],c));case "oneOrMore":return u(t(a.e[0],c));case "attribute":return q(r(a.e[0]),t(a.e[1],c));case "value":return n(a.text);case "data":return d=a.a&&a.a.type,d===void 0&&(d=""),y(d);case "list":return h()}throw"No support for "+a.name;};this.makePattern=function(a,c){var b={},
|
||||
d;for(d in c)c.hasOwnProperty(d)&&(b[d]=c[d]);return d=C(a,b)};this.validate=function(a,c){var b;a.currentNode=a.root;b=z(null,G,a);b.nullable?c(null):(runtime.log("Error in Relax NG validation: "+b),c(["Error in Relax NG validation: "+b]))};this.init=function(a){G=a}};
|
||||
xmldom.RelaxNG=function(){function g(a){return function(){var c;return function(){void 0===c&&(c=a());return c}}()}function m(a,c){return function(){var b={},d=0;return function(f){var e=f.hash||f.toString(),g;g=b[e];if(void 0!==g)return g;b[e]=g=c(f);g.hash=a+d.toString();d+=1;return g}}()}function e(a){return function(){var c={};return function(b){var d,f;f=c[b.localName];if(void 0===f)c[b.localName]=f={};else if(d=f[b.namespaceURI],void 0!==d)return d;return f[b.namespaceURI]=d=a(b)}}()}function k(a,
|
||||
c,b){return function(){var d={},f=0;return function(e,g){var h=c&&c(e,g),j,i;if(void 0!==h)return h;h=e.hash||e.toString();j=g.hash||g.toString();i=d[h];if(void 0===i)d[h]=i={};else if(h=i[j],void 0!==h)return h;i[j]=h=b(e,g);h.hash=a+f.toString();f+=1;return h}}()}function a(c,b){"choice"===b.p1.type?a(c,b.p1):c[b.p1.hash]=b.p1;"choice"===b.p2.type?a(c,b.p2):c[b.p2.hash]=b.p2}function c(a,c){return{type:"element",nc:a,nullable:!1,textDeriv:function(){return q},startTagOpenDeriv:function(b){return a.contains(b)?
|
||||
n(c,u):q},attDeriv:function(){return q},startTagCloseDeriv:function(){return this}}}function b(){return{type:"list",nullable:!1,hash:"list",textDeriv:function(){return u}}}function d(a,c,b,e){if(c===q)return q;if(e>=b.length)return c;0===e&&(e=0);for(var g=b.item(e);g.namespaceURI===f;){e+=1;if(e>=b.length)return c;g=b.item(e)}return g=d(a,c.attDeriv(a,b.item(e)),b,e+1)}function o(a,c,b){b.e[0].a?(a.push(b.e[0].text),c.push(b.e[0].a.ns)):o(a,c,b.e[0]);b.e[1].a?(a.push(b.e[1].text),c.push(b.e[1].a.ns)):
|
||||
o(a,c,b.e[1])}var f="http://www.w3.org/2000/xmlns/",h,i,j,n,x,p,t,r,z,s,q={type:"notAllowed",nullable:!1,hash:"notAllowed",textDeriv:function(){return q},startTagOpenDeriv:function(){return q},attDeriv:function(){return q},startTagCloseDeriv:function(){return q},endTagDeriv:function(){return q}},u={type:"empty",nullable:!0,hash:"empty",textDeriv:function(){return q},startTagOpenDeriv:function(){return q},attDeriv:function(){return q},startTagCloseDeriv:function(){return u},endTagDeriv:function(){return q}},
|
||||
E={type:"text",nullable:!0,hash:"text",textDeriv:function(){return E},startTagOpenDeriv:function(){return q},attDeriv:function(){return q},startTagCloseDeriv:function(){return E},endTagDeriv:function(){return q}},C,w,G;h=k("choice",function(a,b){if(a===q)return b;if(b===q||a===b)return a},function(b,c){var d={},f;a(d,{p1:b,p2:c});c=b=void 0;for(f in d)d.hasOwnProperty(f)&&(void 0===b?b=d[f]:c=void 0===c?d[f]:h(c,d[f]));return function(a,b){return{type:"choice",p1:a,p2:b,nullable:a.nullable||b.nullable,
|
||||
textDeriv:function(c,d){return h(a.textDeriv(c,d),b.textDeriv(c,d))},startTagOpenDeriv:e(function(c){return h(a.startTagOpenDeriv(c),b.startTagOpenDeriv(c))}),attDeriv:function(c,d){return h(a.attDeriv(c,d),b.attDeriv(c,d))},startTagCloseDeriv:g(function(){return h(a.startTagCloseDeriv(),b.startTagCloseDeriv())}),endTagDeriv:g(function(){return h(a.endTagDeriv(),b.endTagDeriv())})}}(b,c)});i=function(a,b,c){return function(){var d={},f=0;return function(e,g){var h=b&&b(e,g),j,i;if(void 0!==h)return h;
|
||||
h=e.hash||e.toString();j=g.hash||g.toString();h<j&&(i=h,h=j,j=i,i=e,e=g,g=i);i=d[h];if(void 0===i)d[h]=i={};else if(h=i[j],void 0!==h)return h;i[j]=h=c(e,g);h.hash=a+f.toString();f+=1;return h}}()}("interleave",function(a,b){if(a===q||b===q)return q;if(a===u)return b;if(b===u)return a},function(a,b){return{type:"interleave",p1:a,p2:b,nullable:a.nullable&&b.nullable,textDeriv:function(c,d){return h(i(a.textDeriv(c,d),b),i(a,b.textDeriv(c,d)))},startTagOpenDeriv:e(function(c){return h(C(function(a){return i(a,
|
||||
b)},a.startTagOpenDeriv(c)),C(function(b){return i(a,b)},b.startTagOpenDeriv(c)))}),attDeriv:function(c,d){return h(i(a.attDeriv(c,d),b),i(a,b.attDeriv(c,d)))},startTagCloseDeriv:g(function(){return i(a.startTagCloseDeriv(),b.startTagCloseDeriv())})}});j=k("group",function(a,b){if(a===q||b===q)return q;if(a===u)return b;if(b===u)return a},function(a,b){return{type:"group",p1:a,p2:b,nullable:a.nullable&&b.nullable,textDeriv:function(c,d){var f=j(a.textDeriv(c,d),b);return a.nullable?h(f,b.textDeriv(c,
|
||||
d)):f},startTagOpenDeriv:function(c){var d=C(function(a){return j(a,b)},a.startTagOpenDeriv(c));return a.nullable?h(d,b.startTagOpenDeriv(c)):d},attDeriv:function(c,d){return h(j(a.attDeriv(c,d),b),j(a,b.attDeriv(c,d)))},startTagCloseDeriv:g(function(){return j(a.startTagCloseDeriv(),b.startTagCloseDeriv())})}});n=k("after",function(a,b){if(a===q||b===q)return q},function(a,b){return{type:"after",p1:a,p2:b,nullable:!1,textDeriv:function(c,d){return n(a.textDeriv(c,d),b)},startTagOpenDeriv:e(function(c){return C(function(a){return n(a,
|
||||
b)},a.startTagOpenDeriv(c))}),attDeriv:function(c,d){return n(a.attDeriv(c,d),b)},startTagCloseDeriv:g(function(){return n(a.startTagCloseDeriv(),b)}),endTagDeriv:g(function(){return a.nullable?b:q})}});x=m("oneormore",function(a){return a===q?q:{type:"oneOrMore",p:a,nullable:a.nullable,textDeriv:function(b,c){return j(a.textDeriv(b,c),h(this,u))},startTagOpenDeriv:function(b){var c=this;return C(function(a){return j(a,h(c,u))},a.startTagOpenDeriv(b))},attDeriv:function(b,c){return j(a.attDeriv(b,
|
||||
c),h(this,u))},startTagCloseDeriv:g(function(){return x(a.startTagCloseDeriv())})}});t=k("attribute",void 0,function(a,b){return{type:"attribute",nullable:!1,nc:a,p:b,attDeriv:function(c,d){return a.contains(d)&&(b.nullable&&/^\s+$/.test(d.nodeValue)||b.textDeriv(c,d.nodeValue).nullable)?u:q},startTagCloseDeriv:function(){return q}}});p=m("value",function(a){return{type:"value",nullable:!1,value:a,textDeriv:function(b,c){return c===a?u:q},attDeriv:function(){return q},startTagCloseDeriv:function(){return this}}});
|
||||
z=m("data",function(a){return{type:"data",nullable:!1,dataType:a,textDeriv:function(){return u},attDeriv:function(){return q},startTagCloseDeriv:function(){return this}}});C=function v(a,b){return"after"===b.type?n(b.p1,a(b.p2)):"choice"===b.type?h(v(a,b.p1),v(a,b.p2)):b};w=function(a,b,c){for(var f=c.currentNode,b=b.startTagOpenDeriv(f),b=d(a,b,f.attributes,0),e=b=b.startTagCloseDeriv(),f=c.currentNode,b=c.firstChild(),g=[],j;b;)1===b.nodeType?g.push(b):3===b.nodeType&&!/^\s*$/.test(b.nodeValue)&&
|
||||
g.push(b.nodeValue),b=c.nextSibling();0===g.length&&(g=[""]);j=e;for(e=0;j!==q&&e<g.length;e+=1)b=g[e],"string"===typeof b?j=/^\s*$/.test(b)?h(j,j.textDeriv(a,b)):j.textDeriv(a,b):(c.currentNode=b,j=w(a,j,c));c.currentNode=f;return b=j.endTagDeriv()};r=function(a){var b,c,d;if("name"===a.name)return b=a.text,c=a.a.ns,{name:b,ns:c,hash:"{"+c+"}"+b,contains:function(a){return a.namespaceURI===c&&a.localName===b}};if("choice"===a.name){b=[];c=[];o(b,c,a);a="";for(d=0;d<b.length;d+=1)a+="{"+c[d]+"}"+
|
||||
b[d]+",";return{hash:a,contains:function(a){var d;for(d=0;d<b.length;d+=1)if(b[d]===a.localName&&c[d]===a.namespaceURI)return!0;return!1}}}return{hash:"anyName",contains:function(){return!0}}};s=function y(a,d){var f,e;if("elementref"===a.name){f=a.id||0;a=d[f];if(void 0!==a.name){var g=a;f=d[g.id]={hash:"element"+g.id.toString()};g=c(r(g.e[0]),s(g.e[1],d));for(e in g)g.hasOwnProperty(e)&&(f[e]=g[e]);e=f}else e=a;return e}switch(a.name){case "empty":return u;case "notAllowed":return q;case "text":return E;
|
||||
case "choice":return h(y(a.e[0],d),y(a.e[1],d));case "interleave":f=y(a.e[0],d);for(e=1;e<a.e.length;e+=1)f=i(f,y(a.e[e],d));return f;case "group":return j(y(a.e[0],d),y(a.e[1],d));case "oneOrMore":return x(y(a.e[0],d));case "attribute":return t(r(a.e[0]),y(a.e[1],d));case "value":return p(a.text);case "data":return f=a.a&&a.a.type,void 0===f&&(f=""),z(f);case "list":return b()}throw"No support for "+a.name;};this.makePattern=function(a,b){var c={},d;for(d in b)b.hasOwnProperty(d)&&(c[d]=b[d]);return d=
|
||||
s(a,c)};this.validate=function(a,b){var c;a.currentNode=a.root;c=w(null,G,a);c.nullable?b(null):(runtime.log("Error in Relax NG validation: "+c),b(["Error in Relax NG validation: "+c]))};this.init=function(a){G=a}};
|
||||
// Input 16
|
||||
runtime.loadClass("xmldom.RelaxNGParser");
|
||||
xmldom.RelaxNG2=function(){function i(a,b){this.message=function(){b&&(a+=b.nodeType===1?" Element ":" Node ",a+=b.nodeName,b.nodeValue&&(a+=" with value '"+b.nodeValue+"'"),a+=".");return a}}function k(c,b,f,e){return c.name==="empty"?null:a(c,b,f,e)}function e(a,d){if(a.e.length!==2)throw"Element with wrong # of elements: "+a.e.length;h+=1;for(var f=d.currentNode,e=f?f.nodeType:0,g=null;e>1;){if(e!==8&&(e!==3||!/^\s+$/.test(d.currentNode.nodeValue)))return h-=1,[new i("Not allowed node of type "+
|
||||
e+".")];e=(f=d.nextSibling())?f.nodeType:0}if(!f)return h-=1,[new i("Missing element "+a.names)];if(a.names&&a.names.indexOf(b[f.namespaceURI]+":"+f.localName)===-1)return h-=1,[new i("Found "+f.nodeName+" instead of "+a.names+".",f)];if(d.firstChild()){for(g=k(a.e[1],d,f);d.nextSibling();)if(e=d.currentNode.nodeType,(!d.currentNode||!(d.currentNode.nodeType===3&&/^\s+$/.test(d.currentNode.nodeValue)))&&e!==8)return h-=1,[new i("Spurious content.",d.currentNode)];if(d.parentNode()!==f)return h-=1,
|
||||
[new i("Implementation error.")]}else g=k(a.e[1],d,f);h-=1;d.nextSibling();return g}var g,a,b,h=0;a=function(b,d,f,g){var h=b.name,m=null;if(h==="text")a:{for(var l=(b=d.currentNode)?b.nodeType:0;b!==f&&l!==3;){if(l===1){m=[new i("Element not allowed here.",b)];break a}l=(b=d.nextSibling())?b.nodeType:0}d.nextSibling();m=null}else if(h==="data")m=null;else if(h==="value")g!==b.text&&(m=[new i("Wrong value, should be '"+b.text+"', not '"+g+"'",f)]);else if(h==="list")m=null;else if(h==="attribute")a:{if(b.e.length!==
|
||||
2)throw"Attribute with wrong # of elements: "+b.e.length;h=b.localnames.length;for(m=0;m<h;m+=1){g=f.getAttributeNS(b.namespaces[m],b.localnames[m]);g===""&&!f.hasAttributeNS(b.namespaces[m],b.localnames[m])&&(g=void 0);if(l!==void 0&&g!==void 0){m=[new i("Attribute defined too often.",f)];break a}l=g}m=l===void 0?[new i("Attribute not found: "+b.names,f)]:k(b.e[1],d,f,l)}else if(h==="element")m=e(b,d,f);else if(h==="oneOrMore"){g=0;do l=d.currentNode,h=a(b.e[0],d,f),g+=1;while(!h&&l!==d.currentNode);
|
||||
g>1?(d.currentNode=l,m=null):m=h}else if(h==="choice"){if(b.e.length!==2)throw"Choice with wrong # of options: "+b.e.length;l=d.currentNode;if(b.e[0].name==="empty"){if(h=a(b.e[1],d,f,g))d.currentNode=l;m=null}else{if(h=k(b.e[0],d,f,g))d.currentNode=l,h=a(b.e[1],d,f,g);m=h}}else if(h==="group"){if(b.e.length!==2)throw"Group with wrong # of members: "+b.e.length;m=a(b.e[0],d,f)||a(b.e[1],d,f)}else if(h==="interleave")a:{for(var l=b.e.length,g=[l],u=l,n,q,r,y;u>0;){n=0;q=d.currentNode;for(m=0;m<l;m+=
|
||||
1)if(r=d.currentNode,g[m]!==true&&g[m]!==r)y=b.e[m],(h=a(y,d,f))?(d.currentNode=r,g[m]===void 0&&(g[m]=false)):r===d.currentNode||y.name==="oneOrMore"||y.name==="choice"&&(y.e[0].name==="oneOrMore"||y.e[1].name==="oneOrMore")?(n+=1,g[m]=r):(n+=1,g[m]=true);if(q===d.currentNode&&n===u)break;if(n===0){for(m=0;m<l;m+=1)if(g[m]===false){m=[new i("Interleave does not match.",f)];break a}break}for(m=u=0;m<l;m+=1)g[m]!==true&&(u+=1)}m=null}else throw h+" not allowed in nonEmptyPattern.";return m};this.validate=
|
||||
function(a,b){a.currentNode=a.root;var f=k(g.e[0],a,a.root);b(f)};this.init=function(a,d){g=a;b=d}};
|
||||
xmldom.RelaxNG2=function(){function g(a,c){this.message=function(){c&&(a+=1===c.nodeType?" Element ":" Node ",a+=c.nodeName,c.nodeValue&&(a+=" with value '"+c.nodeValue+"'"),a+=".");return a}}function m(b,c,e,f){return"empty"===b.name?null:a(b,c,e,f)}function e(a,d){if(2!==a.e.length)throw"Element with wrong # of elements: "+a.e.length;for(var e=d.currentNode,f=e?e.nodeType:0,h=null;1<f;){if(8!==f&&(3!==f||!/^\s+$/.test(d.currentNode.nodeValue)))return[new g("Not allowed node of type "+f+".")];f=
|
||||
(e=d.nextSibling())?e.nodeType:0}if(!e)return[new g("Missing element "+a.names)];if(a.names&&-1===a.names.indexOf(c[e.namespaceURI]+":"+e.localName))return[new g("Found "+e.nodeName+" instead of "+a.names+".",e)];if(d.firstChild()){for(h=m(a.e[1],d,e);d.nextSibling();)if(f=d.currentNode.nodeType,(!d.currentNode||!(3===d.currentNode.nodeType&&/^\s+$/.test(d.currentNode.nodeValue)))&&8!==f)return[new g("Spurious content.",d.currentNode)];if(d.parentNode()!==e)return[new g("Implementation error.")]}else h=
|
||||
m(a.e[1],d,e);d.nextSibling();return h}var k,a,c;a=function(b,c,o,f){var h=b.name,i=null;if("text"===h)a:{for(var j=(b=c.currentNode)?b.nodeType:0;b!==o&&3!==j;){if(1===j){i=[new g("Element not allowed here.",b)];break a}j=(b=c.nextSibling())?b.nodeType:0}c.nextSibling();i=null}else if("data"===h)i=null;else if("value"===h)f!==b.text&&(i=[new g("Wrong value, should be '"+b.text+"', not '"+f+"'",o)]);else if("list"===h)i=null;else if("attribute"===h)a:{if(2!==b.e.length)throw"Attribute with wrong # of elements: "+
|
||||
b.e.length;h=b.localnames.length;for(i=0;i<h;i+=1){f=o.getAttributeNS(b.namespaces[i],b.localnames[i]);""===f&&!o.hasAttributeNS(b.namespaces[i],b.localnames[i])&&(f=void 0);if(void 0!==j&&void 0!==f){i=[new g("Attribute defined too often.",o)];break a}j=f}i=void 0===j?[new g("Attribute not found: "+b.names,o)]:m(b.e[1],c,o,j)}else if("element"===h)i=e(b,c,o);else if("oneOrMore"===h){f=0;do j=c.currentNode,h=a(b.e[0],c,o),f+=1;while(!h&&j!==c.currentNode);1<f?(c.currentNode=j,i=null):i=h}else if("choice"===
|
||||
h){if(2!==b.e.length)throw"Choice with wrong # of options: "+b.e.length;j=c.currentNode;if("empty"===b.e[0].name){if(h=a(b.e[1],c,o,f))c.currentNode=j;i=null}else{if(h=m(b.e[0],c,o,f))c.currentNode=j,h=a(b.e[1],c,o,f);i=h}}else if("group"===h){if(2!==b.e.length)throw"Group with wrong # of members: "+b.e.length;i=a(b.e[0],c,o)||a(b.e[1],c,o)}else if("interleave"===h)a:{for(var j=b.e.length,f=[j],n=j,k,p,t,r;0<n;){k=0;p=c.currentNode;for(i=0;i<j;i+=1)t=c.currentNode,!0!==f[i]&&f[i]!==t&&(r=b.e[i],(h=
|
||||
a(r,c,o))?(c.currentNode=t,void 0===f[i]&&(f[i]=!1)):t===c.currentNode||"oneOrMore"===r.name||"choice"===r.name&&("oneOrMore"===r.e[0].name||"oneOrMore"===r.e[1].name)?(k+=1,f[i]=t):(k+=1,f[i]=!0));if(p===c.currentNode&&k===n)break;if(0===k){for(i=0;i<j;i+=1)if(!1===f[i]){i=[new g("Interleave does not match.",o)];break a}break}for(i=n=0;i<j;i+=1)!0!==f[i]&&(n+=1)}i=null}else throw h+" not allowed in nonEmptyPattern.";return i};this.validate=function(a,c){a.currentNode=a.root;var e=m(k.e[0],a,a.root);
|
||||
c(e)};this.init=function(a,d){k=a;c=d}};
|
||||
// Input 17
|
||||
xmldom.OperationalTransformInterface=function(){};xmldom.OperationalTransformInterface.prototype.retain=function(){};xmldom.OperationalTransformInterface.prototype.insertCharacters=function(){};xmldom.OperationalTransformInterface.prototype.insertElementStart=function(){};xmldom.OperationalTransformInterface.prototype.insertElementEnd=function(){};xmldom.OperationalTransformInterface.prototype.deleteCharacters=function(){};xmldom.OperationalTransformInterface.prototype.deleteElementStart=function(){};
|
||||
xmldom.OperationalTransformInterface.prototype.deleteElementEnd=function(){};xmldom.OperationalTransformInterface.prototype.replaceAttributes=function(){};xmldom.OperationalTransformInterface.prototype.updateAttributes=function(){};
|
||||
// Input 18
|
||||
xmldom.OperationalTransformDOM=function(){this.retain=function(){};this.insertCharacters=function(){};this.insertElementStart=function(){};this.insertElementEnd=function(){};this.deleteCharacters=function(){};this.deleteElementStart=function(){};this.deleteElementEnd=function(){};this.replaceAttributes=function(){};this.updateAttributes=function(){};this.atEnd=function(){return true}};
|
||||
xmldom.OperationalTransformDOM=function(){this.retain=function(){};this.insertCharacters=function(){};this.insertElementStart=function(){};this.insertElementEnd=function(){};this.deleteCharacters=function(){};this.deleteElementStart=function(){};this.deleteElementEnd=function(){};this.replaceAttributes=function(){};this.updateAttributes=function(){};this.atEnd=function(){return!0}};
|
||||
// Input 19
|
||||
xmldom.XPath=function(){function i(i,e,g){i=i.ownerDocument.evaluate(e,i,g,XPathResult.UNORDERED_NODE_ITERATOR_TYPE,null);e=[];for(g=i.iterateNext();g!==null;)g.nodeType===1&&e.push(g),g=i.iterateNext();return e}xmldom.XPath=function(){this.getODFElementsWithXPath=i};return xmldom.XPath}();
|
||||
xmldom.XPath=function(){function g(a,c,b){return-1!==a&&(a<c||-1===c)&&(a<b||-1===b)}function m(a){for(var c=[],b=0,d=a.length,f;b<d;){var e=a,h=d,o=c,k="",u=[],m=e.indexOf("[",b),C=e.indexOf("/",b),w=e.indexOf("=",b);g(C,m,w)?(k=e.substring(b,C),b=C+1):g(m,C,w)?(k=e.substring(b,m),b=i(e,m,u)):g(w,C,m)?(k=e.substring(b,w),b=w):(k=e.substring(b,h),b=h);o.push({location:k,predicates:u});if(b<d&&"="===a[b]){f=a.substring(b+1,d);if(2<f.length&&("'"===f[0]||'"'===f[0]))f=f.slice(1,f.length-1);else try{f=
|
||||
parseInt(f,10)}catch(G){}b=d}}return{steps:c,value:f}}function e(){}function k(){var a,c=!1;this.setNode=function(c){a=c};this.reset=function(){c=!1};this.next=function(){var b=c?null:a;c=!0;return b}}function a(a,c,b){this.reset=function(){a.reset()};this.next=function(){for(var d=a.next();d&&!(d=d.getAttributeNodeNS(c,b));)d=a.next();return d}}function c(a,c){var b=a.next(),d=null;this.reset=function(){a.reset();b=a.next();d=null};this.next=function(){for(;b;){if(d)if(c&&d.firstChild)d=d.firstChild;
|
||||
else{for(;!d.nextSibling&&d!==b;)d=d.parentNode;d===b?b=a.next():d=d.nextSibling}else{do(d=b.firstChild)||(b=a.next());while(b&&!d)}if(d&&1===d.nodeType)return d}return null}}function b(a,b){this.reset=function(){a.reset()};this.next=function(){for(var c=a.next();c&&!b(c);)c=a.next();return c}}function d(a,c,d){var c=c.split(":",2),f=d(c[0]),e=c[1];return new b(a,function(a){return a.localName===e&&a.namespaceURI===f})}function o(a,c,d){var f=new k,e=h(f,c,d),g=c.value;return void 0===g?new b(a,function(a){f.setNode(a);
|
||||
e.reset();return e.next()}):new b(a,function(a){f.setNode(a);e.reset();return(a=e.next())&&a.nodeValue===g})}function f(a,c,b){var d=a.ownerDocument,f=[],f=new k;f.setNode(a);a=m(c);f=h(f,a,b);a=[];for(b=f.next();b;)a.push(b),b=f.next();return f=a}var h,i;i=function(a,c,b){for(var d=c,f=a.length,e=0;d<f;)"]"===a[d]?(e-=1,0>=e&&b.push(m(a.substring(c,d)))):"["===a[d]&&(0>=e&&(c=d+1),e+=1),d+=1;return d};e.prototype.next=function(){};e.prototype.reset=function(){};h=function(b,f,e){var g,h,i,k;for(g=
|
||||
0;g<f.steps.length;g+=1){i=f.steps[g];h=i.location;""===h?b=new c(b,!1):"@"===h[0]?(k=h.slice(1).split(":",2),b=new a(b,e(k[0]),k[1])):"."!==h&&(b=new c(b,!1),-1!==h.indexOf(":")&&(b=d(b,h,e)));for(h=0;h<i.predicates.length;h+=1)k=i.predicates[h],b=o(b,k,e)}return b};xmldom.XPath=function(){this.getODFElementsWithXPath=f};return xmldom.XPath}();
|
||||
// Input 20
|
||||
odf.StyleInfo=function(){function i(e,g){for(var a=k[e.localName],b=a&&a[e.namespaceURI],h=b?b.length:0,c,d,f,a=0;a<h;a+=1)if(c=e.getAttributeNS(b[a].ns,b[a].localname))d=b[a].keygroup,(f=g[d])||(f=g[d]={}),f[c]=1;for(a=e.firstChild;a;)a.nodeType===1&&(b=a,i(b,g)),a=a.nextSibling}var k;this.UsedKeysList=function(e){var g={};this.uses=function(a){var b=a.localName,e=a.getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:drawing:1.0","name")||a.getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:style:1.0",
|
||||
"name"),a=b==="style"?a.getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:style:1.0","family"):a.namespaceURI==="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"?"data":b;return(a=g[a])?a[e]>0:false};i(e,g)};this.canElementHaveStyle=function(e,g){var a=k[g.localName];return(a=a&&a[g.namespaceURI])&&a.length>0};k=function(e){var j;var g,a,b,h,c,d={},f;for(g in e)if(e.hasOwnProperty(g)){b=e[g];c=b.length;for(a=0;a<c;a+=1)h=b[a],f=d[h.en]=d[h.en]||{},j=f[h.ens]=f[h.ens]||[],f=j,f.push({ns:h.ans,
|
||||
localname:h.a,keygroup:g})}return d}({text:[{ens:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",en:"tab-stop",ans:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",a:"leader-text-style"},{ens:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",en:"drop-cap",ans:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",a:"style-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",en:"notes-configuration",ans:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",a:"citation-body-style-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",
|
||||
odf.StyleInfo=function(){function g(e,k){for(var a=m[e.localName],c=a&&a[e.namespaceURI],b=c?c.length:0,d,o,f,a=0;a<b;a+=1)if(d=e.getAttributeNS(c[a].ns,c[a].localname))o=c[a].keygroup,(f=k[o])||(f=k[o]={}),f[d]=1;for(a=e.firstChild;a;)1===a.nodeType&&(c=a,g(c,k)),a=a.nextSibling}var m;this.UsedKeysList=function(e){var k={};this.uses=function(a){var c=a.localName,b=a.getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:drawing:1.0","name")||a.getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:style:1.0",
|
||||
"name"),a="style"===c?a.getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:style:1.0","family"):"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"===a.namespaceURI?"data":c;return(a=k[a])?0<a[b]:!1};g(e,k)};this.canElementHaveStyle=function(e,g){var a=m[g.localName];return(a=a&&a[g.namespaceURI])&&0<a.length};m=function(e){var g,a,c,b,d,o={},f;for(g in e)if(e.hasOwnProperty(g)){c=e[g];d=c.length;for(a=0;a<d;a+=1)b=c[a],f=o[b.en]=o[b.en]||{},f=f[b.ens]=f[b.ens]||[],f.push({ns:b.ans,localname:b.a,
|
||||
keygroup:g})}return o}({text:[{ens:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",en:"tab-stop",ans:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",a:"leader-text-style"},{ens:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",en:"drop-cap",ans:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",a:"style-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",en:"notes-configuration",ans:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",a:"citation-body-style-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",
|
||||
en:"notes-configuration",ans:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",a:"citation-style-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",en:"a",ans:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",a:"style-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",en:"alphabetical-index",ans:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",a:"style-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",en:"linenumbering-configuration",ans:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",
|
||||
a:"style-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",en:"list-level-style-number",ans:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",a:"style-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",en:"ruby-text",ans:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",a:"style-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",en:"span",ans:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",a:"style-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",en:"a",ans:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",
|
||||
a:"visited-style-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",en:"text-properties",ans:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",a:"text-line-through-text-style"},{ens:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",en:"alphabetical-index-source",ans:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",a:"main-entry-style-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",en:"index-entry-bibliography",ans:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",a:"style-name"},
|
||||
|
@ -251,81 +258,84 @@ en:"user-field-get",ans:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",a:"dat
|
|||
a:"data-style-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",en:"variable-set",ans:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",a:"data-style-name"}],"page-layout":[{ens:"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0",en:"notes",ans:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",a:"page-layout-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",en:"handout-master",ans:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",a:"page-layout-name"},{ens:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",
|
||||
en:"master-page",ans:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",a:"page-layout-name"}]})};
|
||||
// Input 21
|
||||
odf.Style2CSS=function(){function i(a,b){var c={},d,f,e;if(!b)return c;for(d=b.firstChild;d;){d.namespaceURI===p&&d.localName==="style"?e=d.getAttributeNS(p,"family"):d.namespaceURI===m&&d.localName==="list-style"&&(e="list");if(f=e&&d.getAttributeNS&&d.getAttributeNS(p,"name"))c[e]||(c[e]={}),c[e][f]=d;d=d.nextSibling}return c}function k(a,b){if(!b||!a)return null;if(a[b])return a[b];var c,d;for(c in a)if(a.hasOwnProperty(c)&&(d=k(a[c].derivedStyles,b)))return d;return null}function e(a,b,c){var d=
|
||||
b[a],f,g;if(d)if(f=d.getAttributeNS(p,"parent-style-name"),g=null,f&&(g=k(c,f),!g&&b[f]&&(e(f,b,c),g=b[f],b[f]=null)),g){if(!g.derivedStyles)g.derivedStyles={};g.derivedStyles[a]=d}else c[a]=d}function g(a,b){for(var c in a)a.hasOwnProperty(c)&&(e(c,a,b),a[c]=null)}function a(a,b){var c=u[a],d;if(c===null)return null;d="["+c+'|style-name="'+b+'"]';c==="presentation"&&(c="draw",d='[presentation|style-name="'+b+'"]');return c+"|"+n[a].join(d+","+c+"|")+d}function b(c,d,f){var e=[],g,h;e.push(a(c,d));
|
||||
for(g in f.derivedStyles)if(f.derivedStyles.hasOwnProperty(g))for(h in d=b(c,g,f.derivedStyles[g]),d)d.hasOwnProperty(h)&&e.push(d[h]);return e}function h(a,b,c){if(!a)return null;for(a=a.firstChild;a;){if(a.namespaceURI===b&&a.localName===c)return b=a;a=a.nextSibling}return null}function c(a,b){var c="",d,f;for(d in b)b.hasOwnProperty(d)&&(d=b[d],(f=a.getAttributeNS(d[0],d[1]))&&(c+=d[2]+":"+f+";"));return c}function d(a,b,c,d){for(var b='text|list[text|style-name="'+b+'"]',c=c.getAttributeNS(m,
|
||||
"level"),f="",c=c&&parseInt(c,10);c>1;)b+=" > text|list-item > text|list",c-=1;b+=" > list-item:before";try{a.insertRule(b+"{"+d+"}",a.cssRules.length)}catch(e){throw e;}}function f(a,e,g,i){if(e==="list")for(var k=i.firstChild,l,n;k;){if(k.namespaceURI===m)if(l=k,k.localName==="list-level-style-number"){n=l;var t=n.getAttributeNS(p,"num-format"),u=n.getAttributeNS(p,"num-suffix"),L="",L={1:"decimal",a:"lower-latin",A:"upper-latin",i:"lower-roman",I:"upper-roman"},Q="",Q=n.getAttributeNS(p,"num-prefix")||
|
||||
"";Q+=L.hasOwnProperty(t)?" counter(list, "+L[t]+")":t?"'"+t+"';":" ''";u&&(Q+=" '"+u+"'");n=L="content: "+Q+";";d(a,g,l,n)}else k.localName==="list-level-style-image"?(n="content: none;",d(a,g,l,n)):k.localName==="list-level-style-bullet"&&(n="content: '"+l.getAttributeNS(m,"bullet-char")+"';",d(a,g,l,n));k=k.nextSibling}else{g=b(e,g,i).join(",");l="";if(k=h(i,p,"text-properties")){n="";n+=c(k,q);t=k.getAttributeNS(p,"text-underline-style");t==="solid"&&(n+="text-decoration: underline;");if(t=k.getAttributeNS(p,
|
||||
"font-name"))(t='"'+t+'"')&&(n+="font-family: "+t+";");l+=n}if(k=h(i,p,"paragraph-properties")){n=k;k="";k+=c(n,y);n=n.getElementsByTagNameNS(p,"background-image");if(n.length>0&&(t=n.item(0).getAttributeNS(j,"href")))k+="background-image: url('odfkit:"+t+"');",n=n.item(0),k+=c(n,r);l+=k}if(k=h(i,p,"graphic-properties"))n="",n+=c(k,C),l+=n;if(k=h(i,p,"table-cell-properties"))n="",n+=c(k,s),l+=n;if(l.length!==0)try{a.insertRule(g+"{"+l+"}",a.cssRules.length)}catch(R){throw R;}}for(var X in i.derivedStyles)i.derivedStyles.hasOwnProperty(X)&&
|
||||
f(a,e,X,i.derivedStyles[X])}var j="http://www.w3.org/1999/xlink",p="urn:oasis:names:tc:opendocument:xmlns:style:1.0",m="urn:oasis:names:tc:opendocument:xmlns:text:1.0",l={draw:"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0",fo:"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",office:"urn:oasis:names:tc:opendocument:xmlns:office:1.0",presentation:"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0",style:p,svg:"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0",table:"urn:oasis:names:tc:opendocument:xmlns:table:1.0",
|
||||
text:m,xlink:j},u={graphic:"draw",paragraph:"text",presentation:"presentation",ruby:"text",section:"text",table:"table","table-cell":"table","table-column":"table","table-row":"table",text:"text",list:"text"},n={graphic:"circle,connected,control,custom-shape,ellipse,frame,g,line,measure,page,page-thumbnail,path,polygon,polyline,rect,regular-polygon".split(","),paragraph:"alphabetical-index-entry-template,h,illustration-index-entry-template,index-source-style,object-index-entry-template,p,table-index-entry-template,table-of-content-entry-template,user-index-entry-template".split(","),
|
||||
odf.Style2CSS=function(){function g(a,b){var c={},d,f,e;if(!b)return c;for(d=b.firstChild;d;){d.namespaceURI===i&&"style"===d.localName?e=d.getAttributeNS(i,"family"):d.namespaceURI===j&&"list-style"===d.localName&&(e="list");if(f=e&&d.getAttributeNS&&d.getAttributeNS(i,"name"))c[e]||(c[e]={}),c[e][f]=d;d=d.nextSibling}return c}function m(a,b){if(!b||!a)return null;if(a[b])return a[b];var c,d;for(c in a)if(a.hasOwnProperty(c)&&(d=m(a[c].derivedStyles,b)))return d;return null}function e(a,b,c){var d=
|
||||
b[a],f,g;d&&(f=d.getAttributeNS(i,"parent-style-name"),g=null,f&&(g=m(c,f),!g&&b[f]&&(e(f,b,c),g=b[f],b[f]=null)),g?(g.derivedStyles||(g.derivedStyles={}),g.derivedStyles[a]=d):c[a]=d)}function k(a,b){for(var c in a)a.hasOwnProperty(c)&&(e(c,a,b),a[c]=null)}function a(a,b){var c=x[a],d;if(null===c)return null;d="["+c+'|style-name="'+b+'"]';"presentation"===c&&(c="draw",d='[presentation|style-name="'+b+'"]');return c+"|"+p[a].join(d+","+c+"|")+d}function c(b,d,f){var e=[],g,h;e.push(a(b,d));for(g in f.derivedStyles)if(f.derivedStyles.hasOwnProperty(g))for(h in d=
|
||||
c(b,g,f.derivedStyles[g]),d)d.hasOwnProperty(h)&&e.push(d[h]);return e}function b(a,b,c){if(!a)return null;for(a=a.firstChild;a;){if(a.namespaceURI===b&&a.localName===c)return b=a;a=a.nextSibling}return null}function d(a,b){var c="",d,f;for(d in b)b.hasOwnProperty(d)&&(d=b[d],(f=a.getAttributeNS(d[0],d[1]))&&(c+=d[2]+":"+f+";"));return c}function o(a,b,c,d){b='text|list[text|style-name="'+b+'"]';for(c=(c=c.getAttributeNS(j,"level"))&&parseInt(c,10);1<c;)b+=" > text|list-item > text|list",c-=1;try{a.insertRule(b+
|
||||
" > list-item:before{"+d+"}",a.cssRules.length)}catch(f){throw f;}}function f(a,e,g,k){if("list"===e)for(var n=k.firstChild,l,m;n;){if(n.namespaceURI===j)if(l=n,"list-level-style-number"===n.localName){m=l;var p=m.getAttributeNS(i,"num-format"),x=m.getAttributeNS(i,"num-suffix"),M="",M={1:"decimal",a:"lower-latin",A:"upper-latin",i:"lower-roman",I:"upper-roman"},F="",F=m.getAttributeNS(i,"num-prefix")||"",F=M.hasOwnProperty(p)?F+(" counter(list, "+M[p]+")"):p?F+("'"+p+"';"):F+" ''";x&&(F+=" '"+x+
|
||||
"'");m=M="content: "+F+";";o(a,g,l,m)}else"list-level-style-image"===n.localName?(m="content: none;",o(a,g,l,m)):"list-level-style-bullet"===n.localName&&(m="content: '"+l.getAttributeNS(j,"bullet-char")+"';",o(a,g,l,m));n=n.nextSibling}else{g=c(e,g,k).join(",");n="";if(l=b(k,i,"text-properties")){m=""+d(l,t);p=l.getAttributeNS(i,"text-underline-style");"solid"===p&&(m+="text-decoration: underline;");if(p=l.getAttributeNS(i,"font-name"))(p='"'+p+'"')&&(m+="font-family: "+p+";");n+=m}if(l=b(k,i,"paragraph-properties")){m=
|
||||
l;l=""+d(m,z);m=m.getElementsByTagNameNS(i,"background-image");if(0<m.length&&(p=m.item(0).getAttributeNS(h,"href")))l+="background-image: url('odfkit:"+p+"');",m=m.item(0),l+=d(m,r);n+=l}if(l=b(k,i,"graphic-properties"))l=""+d(l,s),n+=l;if(l=b(k,i,"table-cell-properties"))l=""+d(l,q),n+=l;if(0!==n.length)try{a.insertRule(g+"{"+n+"}",a.cssRules.length)}catch(R){throw R;}}for(var S in k.derivedStyles)k.derivedStyles.hasOwnProperty(S)&&f(a,e,S,k.derivedStyles[S])}var h="http://www.w3.org/1999/xlink",
|
||||
i="urn:oasis:names:tc:opendocument:xmlns:style:1.0",j="urn:oasis:names:tc:opendocument:xmlns:text:1.0",n={draw:"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0",fo:"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",office:"urn:oasis:names:tc:opendocument:xmlns:office:1.0",presentation:"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0",style:i,svg:"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0",table:"urn:oasis:names:tc:opendocument:xmlns:table:1.0",text:j,xlink:h},x=
|
||||
{graphic:"draw",paragraph:"text",presentation:"presentation",ruby:"text",section:"text",table:"table","table-cell":"table","table-column":"table","table-row":"table",text:"text",list:"text"},p={graphic:"circle,connected,control,custom-shape,ellipse,frame,g,line,measure,page,page-thumbnail,path,polygon,polyline,rect,regular-polygon".split(","),paragraph:"alphabetical-index-entry-template,h,illustration-index-entry-template,index-source-style,object-index-entry-template,p,table-index-entry-template,table-of-content-entry-template,user-index-entry-template".split(","),
|
||||
presentation:"caption,circle,connector,control,custom-shape,ellipse,frame,g,line,measure,page-thumbnail,path,polygon,polyline,rect,regular-polygon".split(","),ruby:["ruby","ruby-text"],section:"alphabetical-index,bibliography,illustration-index,index-title,object-index,section,table-of-content,table-index,user-index".split(","),table:["background","table"],"table-cell":"body,covered-table-cell,even-columns,even-rows,first-column,first-row,last-column,last-row,odd-columns,odd-rows,table-cell".split(","),
|
||||
"table-column":["table-column"],"table-row":["table-row"],text:"a,index-entry-chapter,index-entry-link-end,index-entry-link-start,index-entry-page-number,index-entry-span,index-entry-tab-stop,index-entry-text,index-title-template,linenumbering-configuration,list-level-style-number,list-level-style-bullet,outline-level-style,span".split(","),list:["list-item"]},q=[["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","color","color"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
|
||||
"background-color","background-color"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","font-weight","font-weight"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","font-style","font-style"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","font-size","font-size"]],r=[[p,"repeat","background-repeat"]],y=[["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","background-color","background-color"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
|
||||
"table-column":["table-column"],"table-row":["table-row"],text:"a,index-entry-chapter,index-entry-link-end,index-entry-link-start,index-entry-page-number,index-entry-span,index-entry-tab-stop,index-entry-text,index-title-template,linenumbering-configuration,list-level-style-number,list-level-style-bullet,outline-level-style,span".split(","),list:["list-item"]},t=[["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","color","color"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
|
||||
"background-color","background-color"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","font-weight","font-weight"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","font-style","font-style"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","font-size","font-size"]],r=[[i,"repeat","background-repeat"]],z=[["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","background-color","background-color"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
|
||||
"text-align","text-align"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","padding-left","padding-left"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","padding-right","padding-right"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","padding-top","padding-top"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","padding-bottom","padding-bottom"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","border-left","border-left"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
|
||||
"border-right","border-right"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","border-top","border-top"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","border-bottom","border-bottom"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","margin-left","margin-left"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","margin-right","margin-right"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","margin-top","margin-top"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
|
||||
"margin-bottom","margin-bottom"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","border","border"]],C=[["urn:oasis:names:tc:opendocument:xmlns:drawing:1.0","fill-color","background-color"],["urn:oasis:names:tc:opendocument:xmlns:drawing:1.0","fill","background"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","min-height","min-height"],["urn:oasis:names:tc:opendocument:xmlns:drawing:1.0","stroke","border"],["urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0",
|
||||
"stroke-color","border-color"]],s=[["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","background-color","background-color"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","border-left","border-left"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","border-right","border-right"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","border-top","border-top"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","border-bottom","border-bottom"]];
|
||||
this.namespaces=l;this.namespaceResolver=function(a){return l[a]||null};this.namespaceResolver.lookupNamespaceURI=this.namespaceResolver;this.style2css=function(a,b,c){for(var d,e,h,j,r;a.cssRules.length;)a.deleteRule(a.cssRules.length-1);d=null;if(b)d=b.ownerDocument;if(c)d=c.ownerDocument;if(d){for(e in l)if(l.hasOwnProperty(e)){j="@namespace "+e+" url("+l[e]+");";try{a.insertRule(j,a.cssRules.length)}catch(q){}}b=i(d,b);d=i(d,c);for(r in u)if(u.hasOwnProperty(r))for(h in c={},g(b[r],c),g(d[r],
|
||||
c),c)c.hasOwnProperty(h)&&f(a,r,h,c[h])}}};
|
||||
"margin-bottom","margin-bottom"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","border","border"]],s=[["urn:oasis:names:tc:opendocument:xmlns:drawing:1.0","fill-color","background-color"],["urn:oasis:names:tc:opendocument:xmlns:drawing:1.0","fill","background"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","min-height","min-height"],["urn:oasis:names:tc:opendocument:xmlns:drawing:1.0","stroke","border"],["urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0",
|
||||
"stroke-color","border-color"]],q=[["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","background-color","background-color"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","border-left","border-left"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","border-right","border-right"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","border-top","border-top"],["urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0","border-bottom","border-bottom"]];
|
||||
this.namespaces=n;this.namespaceResolver=function(a){return n[a]||null};this.namespaceResolver.lookupNamespaceURI=this.namespaceResolver;this.style2css=function(a,b,c){for(var d,e,h,i,j;a.cssRules.length;)a.deleteRule(a.cssRules.length-1);d=null;b&&(d=b.ownerDocument);c&&(d=c.ownerDocument);if(d){for(e in n)if(n.hasOwnProperty(e)){i="@namespace "+e+" url("+n[e]+");";try{a.insertRule(i,a.cssRules.length)}catch(o){}}b=g(d,b);e=g(d,c);c={};for(j in x)if(x.hasOwnProperty(j))for(h in d=c[j]={},k(b[j],
|
||||
d),k(e[j],d),d)d.hasOwnProperty(h)&&f(a,j,h,d[h])}}};
|
||||
// Input 22
|
||||
runtime.loadClass("core.Base64");runtime.loadClass("xmldom.XPath");runtime.loadClass("odf.Style2CSS");
|
||||
odf.FontLoader=function(){function i(b,e,c,d,f){var g,k=0,m;for(m in b)b.hasOwnProperty(m)&&(k===c&&(g=m),k+=1);if(!g)return f();e.load(b[g].href,function(k,m){if(k)runtime.log(k);else{var n=d,n=document.styleSheets[0],q='@font-face { font-family: "'+g+'"; src: url(data:application/x-font-ttf;charset=binary;base64,'+a.convertUTF8ArrayToBase64(m)+') format("truetype"); }';try{n.insertRule(q,n.cssRules.length)}catch(r){runtime.log("Problem inserting rule in CSS: "+q)}}return i(b,e,c+1,d,f)})}function k(a,
|
||||
e,c){i(a,e,0,c,function(){})}var e=new odf.Style2CSS,g=new xmldom.XPath,a=new core.Base64;odf.FontLoader=function(){this.loadFonts=function(a,h,c){var d={},f,i,p;if(a){a=g.getODFElementsWithXPath(a,"style:font-face[svg:font-face-src]",e.namespaceResolver);for(f=0;f<a.length;f+=1)i=a[f],p=i.getAttributeNS(e.namespaces.style,"name"),i=g.getODFElementsWithXPath(i,"svg:font-face-src/svg:font-face-uri",e.namespaceResolver),i.length>0&&(i=i[0].getAttributeNS(e.namespaces.xlink,"href"),d[p]={href:i})}k(d,
|
||||
h,c)}};return odf.FontLoader}();
|
||||
odf.FontLoader=function(){function g(c,b,d,e,f){var h,i=0,j;for(j in c)c.hasOwnProperty(j)&&(i===d&&(h=j),i+=1);if(!h)return f();b.load(c[h].href,function(i,j){if(i)runtime.log(i);else{var k=e,k=document.styleSheets[0],m='@font-face { font-family: "'+h+'"; src: url(data:application/x-font-ttf;charset=binary;base64,'+a.convertUTF8ArrayToBase64(j)+') format("truetype"); }';try{k.insertRule(m,k.cssRules.length)}catch(r){runtime.log("Problem inserting rule in CSS: "+m)}}return g(c,b,d+1,e,f)})}function m(a,
|
||||
b,d){g(a,b,0,d,function(){})}var e=new odf.Style2CSS,k=new xmldom.XPath,a=new core.Base64;odf.FontLoader=function(){this.loadFonts=function(a,b,d){var g={},f,h,i;if(a){a=k.getODFElementsWithXPath(a,"style:font-face[svg:font-face-src]",e.namespaceResolver);for(f=0;f<a.length;f+=1)h=a[f],i=h.getAttributeNS(e.namespaces.style,"name"),h=k.getODFElementsWithXPath(h,"svg:font-face-src/svg:font-face-uri",e.namespaceResolver),0<h.length&&(h=h[0].getAttributeNS(e.namespaces.xlink,"href"),g[i]={href:h})}m(g,
|
||||
b,d)}};return odf.FontLoader}();
|
||||
// Input 23
|
||||
runtime.loadClass("core.Base64");runtime.loadClass("core.Zip");runtime.loadClass("xmldom.LSSerializer");runtime.loadClass("odf.StyleInfo");runtime.loadClass("odf.Style2CSS");runtime.loadClass("odf.FontLoader");
|
||||
odf.OdfContainer=function(){function i(a,b,c){for(a=a?a.firstChild:null;a;){if(a.localName===c&&a.namespaceURI===b)return a;a=a.nextSibling}return null}function k(a){var b,c=p.length;for(b=0;b<c;b+=1)if(a.namespaceURI===f&&a.localName===p[b])return b;return-1}function e(a,b){var d=a.automaticStyles,f;b&&(f=new c.UsedKeysList(b));this.acceptNode=function(a){if(a.namespaceURI==="http://www.w3.org/1999/xhtml")return 3;else if(f&&a.parentNode===d&&a.nodeType===1)return f.uses(a)?1:2;return 1}}function g(a,
|
||||
b){if(b){var c=k(b),d,f=a.firstChild;if(c!==-1){for(;f;){d=k(f);if(d!==-1&&d>c)break;f=f.nextSibling}a.insertBefore(b,f)}}}function a(a){this.OdfContainer=a}function b(a,b,c){var d=this,f;this.size=0;this.type=null;this.name=a;this.container=b;this.onchange=this.onreadystatechange=this.document=this.url=null;this.EMPTY=0;this.LOADING=1;this.DONE=2;this.state=this.EMPTY;this.load=function(){c.load(a,function(b,c){f=c;d.url=null;if(f){var e=0,g=u[a];g||(g=f[1]===80&&f[2]===78&&f[3]===71?"image/png":
|
||||
f[0]===255&&f[1]===216&&f[2]===255?"image/jpeg":f[0]===71&&f[1]===73&&f[2]===70?"image/gif":"");for(d.url="data:"+g+";base64,";e<f.length;)d.url+=m.convertUTF8ArrayToBase64(f.slice(e,Math.min(e+45E3,f.length))),e+=45E3}if(d.onchange)d.onchange(d);if(d.onstatereadychange)d.onstatereadychange(d)})};this.abort=function(){}}function h(){this.length=0;this.item=function(){}}var c=new odf.StyleInfo,d=new odf.Style2CSS,f="urn:oasis:names:tc:opendocument:xmlns:office:1.0",j="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0",
|
||||
p="meta,settings,scripts,font-face-decls,styles,automatic-styles,master-styles,body".split(","),m=new core.Base64,l=new odf.FontLoader,u={};a.prototype=new function(){};a.prototype.constructor=a;a.namespaceURI=f;a.localName="document";b.prototype.load=function(){};b.prototype.getUrl=function(){return this.data?"data:;base64,"+m.toBase64(this.data):null};odf.OdfContainer=function q(c,k){function m(a){for(var b=a.firstChild,c;b;)c=b.nextSibling,b.nodeType===1?m(b):b.nodeType===7&&a.removeChild(b),b=
|
||||
c}function s(a){var b=A.rootElement.ownerDocument,c;if(a){m(a.documentElement);try{c=b.importNode(a.documentElement,true)}catch(d){}}return c}function p(a){A.state=a;if(A.onchange)A.onchange(A);if(A.onstatereadychange)A.onstatereadychange(A)}function E(a){var a=s(a),b=A.rootElement;!a||a.localName!=="document-styles"||a.namespaceURI!==f?p(q.INVALID):(b.fontFaceDecls=i(a,f,"font-face-decls"),g(b,b.fontFaceDecls),b.styles=i(a,f,"styles"),g(b,b.styles),b.automaticStyles=i(a,f,"automatic-styles"),g(b,
|
||||
b.automaticStyles),b.masterStyles=i(a,f,"master-styles"),g(b,b.masterStyles),l.loadFonts(b.fontFaceDecls,J,null))}function B(a){var a=s(a),b,c,d;if(!a||a.localName!=="document-content"||a.namespaceURI!==f)p(q.INVALID);else{b=A.rootElement;c=i(a,f,"font-face-decls");if(b.fontFaceDecls&&c)for(d=c.firstChild;d;)b.fontFaceDecls.appendChild(d),d=c.firstChild;else if(c)b.fontFaceDecls=c,g(b,c);c=i(a,f,"automatic-styles");if(b.automaticStyles&&c)for(d=c.firstChild;d;)b.automaticStyles.appendChild(d),d=c.firstChild;
|
||||
else if(c)b.automaticStyles=c,g(b,c);b.body=i(a,f,"body");g(b,b.body)}}function z(a){var a=s(a),b;if(a&&!(a.localName!=="document-meta"||a.namespaceURI!==f))b=A.rootElement,b.meta=i(a,f,"meta"),g(b,b.meta)}function G(a){var a=s(a),b;if(a&&!(a.localName!=="document-settings"||a.namespaceURI!==f))b=A.rootElement,b.settings=i(a,f,"settings"),g(b,b.settings)}function o(a,b){J.load(a,function(a,c){if(a)b(a,null);else{var d=runtime.byteArrayToString(c,"utf8"),d=(new DOMParser).parseFromString(d,"text/xml");
|
||||
b(null,d)}})}function x(){o("styles.xml",function(a,b){E(b);A.state!==q.INVALID&&o("content.xml",function(a,b){B(b);A.state!==q.INVALID&&o("meta.xml",function(a,b){z(b);A.state!==q.INVALID&&o("settings.xml",function(a,b){b&&G(b);o("META-INF/manifest.xml",function(a,b){if(b){var c=s(b),d;if(c&&!(c.localName!=="manifest"||c.namespaceURI!==j)){d=A.rootElement;d.manifest=c;for(c=d.manifest.firstChild;c;)c.nodeType===1&&c.localName==="file-entry"&&c.namespaceURI===j&&(u[c.getAttributeNS(j,"full-path")]=
|
||||
c.getAttributeNS(j,"media-type")),c=c.nextSibling}}A.state!==q.INVALID&&p(q.DONE)})})})})})}function t(a,b){var c="",d;for(d in b)b.hasOwnProperty(d)&&(c+=" xmlns:"+d+'="'+b[d]+'"');return'<?xml version="1.0" encoding="UTF-8"?><office:'+a+" "+c+' office:version="1.2">'}function w(){var a=d.namespaces,b=new xmldom.LSSerializer,c=t("document-meta",a);b.filter=new e(A.rootElement);c+=b.writeToString(A.rootElement.meta,a);c+="</office:document-meta>";return c}function L(){var a=d.namespaces,b=new xmldom.LSSerializer,
|
||||
c=t("document-settings",a);b.filter=new e(A.rootElement);c+=b.writeToString(A.rootElement.settings,a);c+="</office:document-settings>";return c}function Q(){var a=d.namespaces,b=new xmldom.LSSerializer,c=t("document-styles",a);b.filter=new e(A.rootElement,A.rootElement.masterStyles);c+=b.writeToString(A.rootElement.fontFaceDecls,a);c+=b.writeToString(A.rootElement.styles,a);c+=b.writeToString(A.rootElement.automaticStyles,a);c+=b.writeToString(A.rootElement.masterStyles,a);c+="</office:document-styles>";
|
||||
return c}function R(){var a=d.namespaces,b=new xmldom.LSSerializer,c=t("document-content",a);b.filter=new e(A.rootElement,A.rootElement.body);c+=b.writeToString(A.rootElement.automaticStyles,a);c+=b.writeToString(A.rootElement.body,a);c+="</office:document-content>";return c}function X(a,b){runtime.loadXML(a,function(a,c){if(a)b(a);else{var d=s(c);!d||d.localName!=="document"||d.namespaceURI!==f?p(q.INVALID):(A.rootElement=d,d.fontFaceDecls=i(d,f,"font-face-decls"),d.styles=i(d,f,"styles"),d.automaticStyles=
|
||||
i(d,f,"automatic-styles"),d.masterStyles=i(d,f,"master-styles"),d.body=i(d,f,"body"),d.meta=i(d,f,"meta"),p(q.DONE))}})}var A=this,J=null;this.onstatereadychange=k;this.parts=this.rootElement=this.state=this.onchange=null;this.getPart=function(a){return new b(a,A,J)};this.save=function(a){var b;b=runtime.byteArrayFromString(L(),"utf8");J.save("settings.xml",b,true,new Date);b=runtime.byteArrayFromString(w(),"utf8");J.save("meta.xml",b,true,new Date);b=runtime.byteArrayFromString(Q(),"utf8");J.save("styles.xml",
|
||||
b,true,new Date);b=runtime.byteArrayFromString(R(),"utf8");J.save("content.xml",b,true,new Date);J.write(function(b){a(b)})};this.state=q.LOADING;this.rootElement=function(a){var b=document.createElementNS(a.namespaceURI,a.localName),c,a=new a;for(c in a)a.hasOwnProperty(c)&&(b[c]=a[c]);return b}(a);this.parts=new h(this);J=new core.Zip(c,function(a,b){J=b;a?X(c,function(b){if(a)J.error=a+"\n"+b,p(q.INVALID)}):x()})};odf.OdfContainer.EMPTY=0;odf.OdfContainer.LOADING=1;odf.OdfContainer.DONE=2;odf.OdfContainer.INVALID=
|
||||
3;odf.OdfContainer.SAVING=4;odf.OdfContainer.MODIFIED=5;odf.OdfContainer.getContainer=function(a){return new odf.OdfContainer(a,null)};return odf.OdfContainer}();
|
||||
odf.OdfContainer=function(){function g(a,b,c){for(a=a?a.firstChild:null;a;){if(a.localName===c&&a.namespaceURI===b)return a;a=a.nextSibling}return null}function m(a){var b,c=i.length;for(b=0;b<c;b+=1)if(a.namespaceURI===f&&a.localName===i[b])return b;return-1}function e(a,b){var c=a.automaticStyles,f;b&&(f=new d.UsedKeysList(b));this.acceptNode=function(a){return"http://www.w3.org/1999/xhtml"===a.namespaceURI?3:f&&a.parentNode===c&&1===a.nodeType?f.uses(a)?1:2:1}}function k(a,b){if(b){var c=m(b),
|
||||
d,f=a.firstChild;if(-1!==c){for(;f;){d=m(f);if(-1!==d&&d>c)break;f=f.nextSibling}a.insertBefore(b,f)}}}function a(a){this.OdfContainer=a}function c(a,b,c){var d=this;this.size=0;this.type=null;this.name=a;this.container=b;this.onchange=this.onreadystatechange=this.document=this.url=null;this.EMPTY=0;this.LOADING=1;this.DONE=2;this.state=this.EMPTY;this.load=function(){c.loadAsDataURL(a,x[a],function(a,b){d.url=b;if(d.onchange)d.onchange(d);if(d.onstatereadychange)d.onstatereadychange(d)})};this.abort=
|
||||
function(){}}function b(){this.length=0;this.item=function(){}}var d=new odf.StyleInfo,o=new odf.Style2CSS,f="urn:oasis:names:tc:opendocument:xmlns:office:1.0",h="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0",i="meta,settings,scripts,font-face-decls,styles,automatic-styles,master-styles,body".split(","),j=new core.Base64,n=new odf.FontLoader,x={};a.prototype=new function(){};a.prototype.constructor=a;a.namespaceURI=f;a.localName="document";c.prototype.load=function(){};c.prototype.getUrl=function(){return this.data?
|
||||
"data:;base64,"+j.toBase64(this.data):null};odf.OdfContainer=function t(d,i){function j(a){for(var b=a.firstChild,c;b;)c=b.nextSibling,1===b.nodeType?j(b):7===b.nodeType&&a.removeChild(b),b=c}function q(a){var b=A.rootElement.ownerDocument,c;if(a){j(a.documentElement);try{c=b.importNode(a.documentElement,!0)}catch(d){}}return c}function m(a){A.state=a;if(A.onchange)A.onchange(A);if(A.onstatereadychange)A.onstatereadychange(A)}function E(a){var a=q(a),b=A.rootElement;!a||"document-styles"!==a.localName||
|
||||
a.namespaceURI!==f?m(t.INVALID):(b.fontFaceDecls=g(a,f,"font-face-decls"),k(b,b.fontFaceDecls),b.styles=g(a,f,"styles"),k(b,b.styles),b.automaticStyles=g(a,f,"automatic-styles"),k(b,b.automaticStyles),b.masterStyles=g(a,f,"master-styles"),k(b,b.masterStyles),n.loadFonts(b.fontFaceDecls,K,null))}function C(a){var a=q(a),b,c,d;if(!a||"document-content"!==a.localName||a.namespaceURI!==f)m(t.INVALID);else{b=A.rootElement;c=g(a,f,"font-face-decls");if(b.fontFaceDecls&&c)for(d=c.firstChild;d;)b.fontFaceDecls.appendChild(d),
|
||||
d=c.firstChild;else c&&(b.fontFaceDecls=c,k(b,c));c=g(a,f,"automatic-styles");if(b.automaticStyles&&c)for(d=c.firstChild;d;)b.automaticStyles.appendChild(d),d=c.firstChild;else c&&(b.automaticStyles=c,k(b,c));b.body=g(a,f,"body");k(b,b.body)}}function w(a){var a=q(a),b;if(a&&!("document-meta"!==a.localName||a.namespaceURI!==f))b=A.rootElement,b.meta=g(a,f,"meta"),k(b,b.meta)}function G(a){var a=q(a),b;if(a&&!("document-settings"!==a.localName||a.namespaceURI!==f))b=A.rootElement,b.settings=g(a,f,
|
||||
"settings"),k(b,b.settings)}function l(a,b){K.loadAsDOM(a,b)}function v(){l("styles.xml",function(a,b){E(b);A.state!==t.INVALID&&l("content.xml",function(a,b){C(b);A.state!==t.INVALID&&l("meta.xml",function(a,b){w(b);A.state!==t.INVALID&&l("settings.xml",function(a,b){b&&G(b);l("META-INF/manifest.xml",function(a,b){if(b){var c=q(b),d;if(c&&!("manifest"!==c.localName||c.namespaceURI!==h)){d=A.rootElement;d.manifest=c;for(c=d.manifest.firstChild;c;)1===c.nodeType&&"file-entry"===c.localName&&c.namespaceURI===
|
||||
h&&(x[c.getAttributeNS(h,"full-path")]=c.getAttributeNS(h,"media-type")),c=c.nextSibling}}A.state!==t.INVALID&&m(t.DONE)})})})})})}function y(a,b){var c="",d;for(d in b)b.hasOwnProperty(d)&&(c+=" xmlns:"+d+'="'+b[d]+'"');return'<?xml version="1.0" encoding="UTF-8"?><office:'+a+" "+c+' office:version="1.2">'}function B(){var a=o.namespaces,b=new xmldom.LSSerializer,c=y("document-meta",a);b.filter=new e(A.rootElement);c+=b.writeToString(A.rootElement.meta,a);return c+"</office:document-meta>"}function M(){var a=
|
||||
o.namespaces,b=new xmldom.LSSerializer,c=y("document-settings",a);b.filter=new e(A.rootElement);c+=b.writeToString(A.rootElement.settings,a);return c+"</office:document-settings>"}function F(){var a=o.namespaces,b=new xmldom.LSSerializer,c=y("document-styles",a);b.filter=new e(A.rootElement,A.rootElement.masterStyles);c+=b.writeToString(A.rootElement.fontFaceDecls,a);c+=b.writeToString(A.rootElement.styles,a);c+=b.writeToString(A.rootElement.automaticStyles,a);c+=b.writeToString(A.rootElement.masterStyles,
|
||||
a);return c+"</office:document-styles>"}function R(){var a=o.namespaces,b=new xmldom.LSSerializer,c=y("document-content",a);b.filter=new e(A.rootElement,A.rootElement.body);c+=b.writeToString(A.rootElement.automaticStyles,a);c+=b.writeToString(A.rootElement.body,a);return c+"</office:document-content>"}function S(a,b){runtime.loadXML(a,function(a,c){if(a)b(a);else{var d=q(c);!d||"document"!==d.localName||d.namespaceURI!==f?m(t.INVALID):(A.rootElement=d,d.fontFaceDecls=g(d,f,"font-face-decls"),d.styles=
|
||||
g(d,f,"styles"),d.automaticStyles=g(d,f,"automatic-styles"),d.masterStyles=g(d,f,"master-styles"),d.body=g(d,f,"body"),d.meta=g(d,f,"meta"),m(t.DONE))}})}var A=this,K=null;this.onstatereadychange=i;this.parts=this.rootElement=this.state=this.onchange=null;this.getPart=function(a){return new c(a,A,K)};this.save=function(a){var b;b=runtime.byteArrayFromString(M(),"utf8");K.save("settings.xml",b,!0,new Date);b=runtime.byteArrayFromString(B(),"utf8");K.save("meta.xml",b,!0,new Date);b=runtime.byteArrayFromString(F(),
|
||||
"utf8");K.save("styles.xml",b,!0,new Date);b=runtime.byteArrayFromString(R(),"utf8");K.save("content.xml",b,!0,new Date);K.write(function(b){a(b)})};this.state=t.LOADING;this.rootElement=function(a){var b=document.createElementNS(a.namespaceURI,a.localName),c,a=new a;for(c in a)a.hasOwnProperty(c)&&(b[c]=a[c]);return b}(a);this.parts=new b(this);K=new core.Zip(d,function(a,b){K=b;a?S(d,function(b){a&&(K.error=a+"\n"+b,m(t.INVALID))}):v()})};odf.OdfContainer.EMPTY=0;odf.OdfContainer.LOADING=1;odf.OdfContainer.DONE=
|
||||
2;odf.OdfContainer.INVALID=3;odf.OdfContainer.SAVING=4;odf.OdfContainer.MODIFIED=5;odf.OdfContainer.getContainer=function(a){return new odf.OdfContainer(a,null)};return odf.OdfContainer}();
|
||||
// Input 24
|
||||
odf.Formatting=function(){function i(e){function g(a,e){for(var c=a&&a.firstChild;c&&e;)c=c.nextSibling,e-=1;return c}var a=g(e.startContainer,e.startOffset);g(e.endContainer,e.endOffset);this.next=function(){return a===null?a:null}}var k=new odf.StyleInfo;this.setOdfContainer=function(){};this.isCompletelyBold=function(){return false};this.getAlignment=function(e){this.getParagraphStyles(e)};this.getParagraphStyles=function(e){var g,a,b,h=[];for(g=0;g<e.length;g+=0){a=void 0;b=[];for(a=(new i(e[g])).next();a;)k.canElementHaveStyle("paragraph",
|
||||
a)&&b.push(a);for(a=0;a<b.length;a+=1)h.indexOf(b[a])===-1&&h.push(b[a])}return h};this.getTextStyles=function(){return[]}};
|
||||
odf.Formatting=function(){function g(e){function g(a,b){for(var d=a&&a.firstChild;d&&b;)d=d.nextSibling,b-=1;return d}var a=g(e.startContainer,e.startOffset);g(e.endContainer,e.endOffset);this.next=function(){return null===a?a:null}}var m=new odf.StyleInfo;this.setOdfContainer=function(){};this.isCompletelyBold=function(){return!1};this.getAlignment=function(e){this.getParagraphStyles(e)};this.getParagraphStyles=function(e){var k,a,c,b=[];for(k=0;k<e.length;k+=0){a=void 0;c=[];for(a=(new g(e[k])).next();a;)m.canElementHaveStyle("paragraph",
|
||||
a)&&c.push(a);for(a=0;a<c.length;a+=1)-1===b.indexOf(c[a])&&b.push(c[a])}return b};this.getTextStyles=function(){return[]}};
|
||||
// Input 25
|
||||
runtime.loadClass("odf.OdfContainer");runtime.loadClass("odf.Formatting");runtime.loadClass("xmldom.XPath");
|
||||
odf.OdfCanvas=function(){function i(a,b,c){a.addEventListener?a.addEventListener(b,c,false):a.attachEvent?a.attachEvent("on"+b,c):a["on"+b]=c}function k(a){function b(a,c){for(;c;){if(c===a)return true;c=c.parentNode}return false}function c(){var e=[],g=runtime.getWindow().getSelection(),h,i;for(h=0;h<g.rangeCount;h+=1)i=g.getRangeAt(h),i!==null&&b(a,i.startContainer)&&b(a,i.endContainer)&&e.push(i);if(e.length===d.length){for(g=0;g<e.length;g+=1)if(h=e[g],i=d[g],h=h===i?false:h===null||i===null?
|
||||
true:h.startContainer!==i.startContainer||h.startOffset!==i.startOffset||h.endContainer!==i.endContainer||h.endOffset!==i.endOffset,h)break;if(g===e.length)return}d=e;var g=Array(e.length),j,k=a.ownerDocument;for(h=0;h<e.length;h+=1)i=e[h],j=k.createRange(),j.setStart(i.startContainer,i.startOffset),j.setEnd(i.endContainer,i.endOffset),g[h]=j;d=g;g=f.length;for(e=0;e<g;e+=1)f[e](a,d)}var d=[],f=[];this.addListener=function(a,b){var c,d=f.length;for(c=0;c<d;c+=1)if(f[c]===b)return;f.push(b)};i(a,"mouseup",
|
||||
c);i(a,"keyup",c);i(a,"keydown",c)}function e(a){for(a=a.firstChild;a;){if(a.namespaceURI===f&&a.localName==="binary-data")return"data:image/png;base64,"+a.textContent;a=a.nextSibling}return""}function g(a,b,c,d){function f(b){b='draw|image[styleid="'+a+'"] {'+("background-image: url("+b+");")+"}";d.insertRule(b,d.cssRules.length)}c.setAttribute("styleid",a);var g=c.getAttributeNS(m,"href"),h;if(g)try{b.getPartUrl?(g=b.getPartUrl(g),f(g)):(h=b.getPart(g),h.onchange=function(a){f(a.url)},h.load())}catch(i){runtime.log("slight problem: "+
|
||||
i)}else g=e(c),f(g)}function a(a){var b=a.getElementsByTagName("style"),c=a.getElementsByTagName("head")[0],d="",f,b=b&&b.length>0?b[0].cloneNode(false):a.createElement("style");for(f in h)h.hasOwnProperty(f)&&f&&(d+="@namespace "+f+" url("+h[f]+");\n");b.appendChild(a.createTextNode(d));c.appendChild(b);return b}var b=new odf.Style2CSS,h=b.namespaces,c=h.draw,d=h.fo,f=h.office,j=h.svg,p=h.text,m=h.xlink,l=runtime.getWindow(),u=new xmldom.XPath,n={},q;odf.OdfCanvas=function(f){function e(a){function h(){for(var e=
|
||||
f;e.firstChild;)e.removeChild(e.firstChild);f.style.display="inline-block";f.style.background="white";e=a.rootElement;f.ownerDocument.importNode(e,true);E.setOdfContainer(a);var i=G;(new odf.Style2CSS).style2css(i.sheet,e.styles,e.automaticStyles);var i=o.sheet,k=a,q=e.body,l,m,s;m=[];for(l=q.firstChild;l&&l!==q;)if(l.namespaceURI===c&&(m[m.length]=l),l.firstChild)l=l.firstChild;else{for(;l&&l!==q&&!l.nextSibling;)l=l.parentNode;if(l&&l.nextSibling)l=l.nextSibling}for(s=0;s<m.length;s+=1){l=m[s];
|
||||
var v="frame"+String(s),y=i;l.setAttribute("styleid",v);var w=void 0,C=l.getAttributeNS(p,"anchor-type"),x=l.getAttributeNS(j,"x"),z=l.getAttributeNS(j,"y"),B=l.getAttributeNS(j,"width"),O=l.getAttributeNS(j,"height"),T=l.getAttributeNS(d,"min-height"),N=l.getAttributeNS(d,"min-width");if(C==="as-char")w="display: inline-block;";else if(C||x||z)w="position: absolute;";else if(B||O||T||N)w="display: block;";x&&(w+="left: "+x+";");z&&(w+="top: "+z+";");B&&(w+="width: "+B+";");O&&(w+="height: "+O+";");
|
||||
T&&(w+="min-height: "+T+";");N&&(w+="min-width: "+N+";");w&&(w="draw|"+l.localName+'[styleid="'+v+'"] {'+w+"}",y.insertRule(w,y.cssRules.length))}m=q.getElementsByTagNameNS(c,"image");for(s=0;s<m.length;s+=1)l=m.item(s),g("image"+String(s),k,l,i);s=u.getODFElementsWithXPath(q,".//*[*[@text:anchor-type='paragraph']]",b.namespaceResolver);for(q=0;q<s.length;q+=1)k=s[q],k.setAttributeNS&&k.setAttributeNS("urn:webodf","containsparagraphanchor",true);i.insertRule("office|presentation draw|page:nth-child(1n) { display:block; }",
|
||||
i.cssRules.length);i.insertRule("draw|page { background-color:#fff; }",i.cssRules.length);for(i=f;i.firstChild;)i.removeChild(i.firstChild);f.appendChild(e);if(n.hasOwnProperty("statereadychange")){e=n.statereadychange;for(i=0;i<e.length;i+=1)e[i](void 0)}}if(v===a)v.state===odf.OdfContainer.DONE?h():v.onchange=h}function h(){if(q){for(var a=q.ownerDocument.createDocumentFragment();q.firstChild;)a.insertBefore(q.firstChild,null);q.parentNode.replaceChild(a,q)}}var m=f.ownerDocument,v,E=new odf.Formatting,
|
||||
B=new k(f),z=a(m),G=a(m),o=a(m),x=false;this.odfContainer=function(){return v};this.slidevisibilitycss=function(){return z};this.load=this.load=function(a){f.innerHTML="loading "+a;v=new odf.OdfContainer(a,function(a){v=a;e(a)});v.onstatereadychange=e};this.save=function(a){h();v.save(a)};this.setEditable=function(a){(x=a)||h()};this.addListener=function(a,b){if(a==="selectionchange")B.addListener(a,b);else{var c=n[a];c===void 0&&(c=n[a]=[]);c.push(b)}};this.getFormatting=function(){return E};i(f,
|
||||
"click",function(a){for(var a=a||l.event,b=a.target,c=l.getSelection(),d=c.getRangeAt(0),f=d&&d.startContainer,e=d&&d.startOffset,g=d&&d.endContainer,i=d&&d.endOffset;b&&!((b.localName==="p"||b.localName==="h")&&b.namespaceURI===p);)b=b.parentNode;if(x&&b&&b.parentNode!==q)q?q.parentNode&&h():(q=b.ownerDocument.createElement("p"),q.style||(q=b.ownerDocument.createElementNS("http://www.w3.org/1999/xhtml","p")),q.style.margin="0px",q.style.padding="0px",q.style.border="0px",q.setAttribute("contenteditable",
|
||||
true)),b.parentNode.replaceChild(q,b),q.appendChild(b),q.focus(),d&&(c.removeAllRanges(),d=b.ownerDocument.createRange(),d.setStart(f,e),d.setEnd(g,i),c.addRange(d)),a.preventDefault?(a.preventDefault(),a.stopPropagation()):(a.returnValue=false,a.cancelBubble=true)})};return odf.OdfCanvas}();
|
||||
odf.OdfCanvas=function(){function g(a,b,c){a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent?a.attachEvent("on"+b,c):a["on"+b]=c}function m(a){function b(a,c){for(;c;){if(c===a)return!0;c=c.parentNode}return!1}function c(){var e=[],g=runtime.getWindow().getSelection(),h,i;for(h=0;h<g.rangeCount;h+=1)i=g.getRangeAt(h),null!==i&&b(a,i.startContainer)&&b(a,i.endContainer)&&e.push(i);if(e.length===d.length){for(g=0;g<e.length&&!(h=e[g],i=d[g],h=h===i?!1:null===h||null===i?!0:h.startContainer!==
|
||||
i.startContainer||h.startOffset!==i.startOffset||h.endContainer!==i.endContainer||h.endOffset!==i.endOffset,h);g+=1);if(g===e.length)return}d=e;var g=[e.length],j,k=a.ownerDocument;for(h=0;h<e.length;h+=1)i=e[h],j=k.createRange(),j.setStart(i.startContainer,i.startOffset),j.setEnd(i.endContainer,i.endOffset),g[h]=j;d=g;g=f.length;for(e=0;e<g;e+=1)f[e](a,d)}var d=[],f=[];this.addListener=function(a,b){var c,d=f.length;for(c=0;c<d;c+=1)if(f[c]===b)return;f.push(b)};g(a,"mouseup",c);g(a,"keyup",c);g(a,
|
||||
"keydown",c)}function e(a){for(a=a.firstChild;a;){if(a.namespaceURI===h&&"binary-data"===a.localName)return"data:image/png;base64,"+a.textContent;a=a.nextSibling}return""}function k(a,b,c,d){function f(b){b='draw|image[styleid="'+a+'"] {'+("background-image: url("+b+");")+"}";d.insertRule(b,d.cssRules.length)}c.setAttribute("styleid",a);var g=c.getAttributeNS(n,"href"),h;if(g)try{b.getPartUrl?(g=b.getPartUrl(g),f(g)):(h=b.getPart(g),h.onchange=function(a){f(a.url)},h.load())}catch(i){runtime.log("slight problem: "+
|
||||
i)}else g=e(c),f(g)}function a(a,b,c){function d(a,b,c,f){z.addToQueue(function(){k(a,b,c,f)})}var f,e;f=b.getElementsByTagNameNS(o,"image");for(b=0;b<f.length;b+=1)e=f.item(b),d("image"+b,a,e,c)}function c(a){var b=a.getElementsByTagName("style"),c=a.getElementsByTagName("head")[0],f="",e,b=b&&0<b.length?b[0].cloneNode(!1):a.createElement("style");for(e in d)d.hasOwnProperty(e)&&e&&(f+="@namespace "+e+" url("+d[e]+");\n");b.appendChild(a.createTextNode(f));c.appendChild(b);return b}var b=new odf.Style2CSS,
|
||||
d=b.namespaces,o=d.draw,f=d.fo,h=d.office,i=d.svg,j=d.text,n=d.xlink,x=runtime.getWindow(),p=new xmldom.XPath,t={},r,z=new function(){function a(d){c=!0;runtime.setTimeout(function(){try{d()}catch(f){runtime.log(f)}c=!1;0<b.length&&a(b.pop())},10)}var b=[],c=!1;this.clearQueue=function(){b.length=0};this.addToQueue=function(d){if(0===b.length&&!c)return a(d);b.push(d)}};odf.OdfCanvas=function(d){function e(){var a=d.firstChild.firstChild;a&&(d.style.WebkitTransform="scale("+F+")",d.style.WebkitTransformOrigin=
|
||||
"left top",d.style.width=Math.round(F*a.offsetWidth)+"px",d.style.height=Math.round(F*a.offsetHeight)+"px")}function h(c){function g(){for(var h=d;h.firstChild;)h.removeChild(h.firstChild);d.style.display="inline-block";h=c.rootElement;d.ownerDocument.importNode(h,!0);G.setOdfContainer(c);var k=y;(new odf.Style2CSS).style2css(k.sheet,h.styles,h.automaticStyles);var k=c,m=B.sheet,l;l=h.body;var r,z,u;z=[];for(r=l.firstChild;r&&r!==l;)if(r.namespaceURI===o&&(z[z.length]=r),r.firstChild)r=r.firstChild;
|
||||
else{for(;r&&r!==l&&!r.nextSibling;)r=r.parentNode;r&&r.nextSibling&&(r=r.nextSibling)}for(u=0;u<z.length;u+=1){r=z[u];var w="frame"+u,x=m;r.setAttribute("styleid",w);var v=void 0,S=r.getAttributeNS(j,"anchor-type"),E=r.getAttributeNS(i,"x"),F=r.getAttributeNS(i,"y"),M=r.getAttributeNS(i,"width"),Q=r.getAttributeNS(i,"height"),$=r.getAttributeNS(f,"min-height"),W=r.getAttributeNS(f,"min-width");if("as-char"===S)v="display: inline-block;";else if(S||E||F)v="position: absolute;";else if(M||Q||$||W)v=
|
||||
"display: block;";E&&(v+="left: "+E+";");F&&(v+="top: "+F+";");M&&(v+="width: "+M+";");Q&&(v+="height: "+Q+";");$&&(v+="min-height: "+$+";");W&&(v+="min-width: "+W+";");v&&(v="draw|"+r.localName+'[styleid="'+w+'"] {'+v+"}",x.insertRule(v,x.cssRules.length))}u=p.getODFElementsWithXPath(l,".//*[*[@text:anchor-type='paragraph']]",b.namespaceResolver);for(z=0;z<u.length;z+=1)l=u[z],l.setAttributeNS&&l.setAttributeNS("urn:webodf","containsparagraphanchor",!0);m.insertRule("office|presentation draw|page:nth-child(1n) {display:block;}",
|
||||
m.cssRules.length);m.insertRule("draw|page { background-color:#fff; }",m.cssRules.length);for(l=d;l.firstChild;)l.removeChild(l.firstChild);l=n.createElement("div");l.style.display="inline-block";l.style.background="white";l.appendChild(h);d.appendChild(l);a(k,h.body,m);e();if(t.hasOwnProperty("statereadychange")){h=t.statereadychange;for(k=0;k<h.length;k+=1)h[k](void 0)}}w===c&&(w.state===odf.OdfContainer.DONE?g():w.onchange=g)}function k(){if(r){for(var a=r.ownerDocument.createDocumentFragment();r.firstChild;)a.insertBefore(r.firstChild,
|
||||
null);r.parentNode.replaceChild(a,r)}}var n=d.ownerDocument,w,G=new odf.Formatting,l=new m(d),v=c(n),y=c(n),B=c(n),M=!1,F=1;this.odfContainer=function(){return w};this.slidevisibilitycss=function(){return v};this.load=this.load=function(a){z.clearQueue();d.innerHTML="loading "+a;w=new odf.OdfContainer(a,function(a){w=a;h(a)});w.onstatereadychange=h};this.save=function(a){k();w.save(a)};this.setEditable=function(a){(M=a)||k()};this.addListener=function(a,b){if("selectionchange"===a)l.addListener(a,
|
||||
b);else{var c=t[a];void 0===c&&(c=t[a]=[]);b&&-1===c.indexOf(b)&&c.push(b)}};this.getFormatting=function(){return G};this.setZoomLevel=function(a){F=a;e()};this.getZoomLevel=function(){return F};this.fitToContainingElement=function(a,b){var c=d.offsetHeight/F;F=a/(d.offsetWidth/F);b/c<F&&(F=b/c);e()};this.fitToWidth=function(a){F=a/(d.offsetWidth/F);e()};this.fitToHeight=function(a){F=a/(d.offsetHeight/F);e()};g(d,"click",function(a){for(var a=a||x.event,b=a.target,c=x.getSelection(),d=0<c.rangeCount?
|
||||
c.getRangeAt(0):null,f=d&&d.startContainer,e=d&&d.startOffset,g=d&&d.endContainer,h=d&&d.endOffset;b&&!(("p"===b.localName||"h"===b.localName)&&b.namespaceURI===j);)b=b.parentNode;M&&b&&b.parentNode!==r&&(r?r.parentNode&&k():(r=b.ownerDocument.createElement("p"),r.style||(r=b.ownerDocument.createElementNS("http://www.w3.org/1999/xhtml","p")),r.style.margin="0px",r.style.padding="0px",r.style.border="0px",r.setAttribute("contenteditable",!0)),b.parentNode.replaceChild(r,b),r.appendChild(b),r.focus(),
|
||||
d&&(c.removeAllRanges(),d=b.ownerDocument.createRange(),d.setStart(f,e),d.setEnd(g,h),c.addRange(d)),a.preventDefault?(a.preventDefault(),a.stopPropagation()):(a.returnValue=!1,a.cancelBubble=!0))})};return odf.OdfCanvas}();
|
||||
// Input 26
|
||||
runtime.loadClass("xmldom.XPath");runtime.loadClass("odf.Style2CSS");
|
||||
gui.PresenterUI=function(){var i=new odf.Style2CSS,k=new xmldom.XPath,e=i.namespaceResolver;return function(g){var a=this;a.setInitialSlideMode=function(){a.startSlideMode("single")};a.keyDownHandler=function(b){if(!b.target.isContentEditable&&b.target.nodeName!=="input")switch(b.keyCode){case 84:a.toggleToolbar();break;case 37:case 8:a.prevSlide();break;case 39:case 32:a.nextSlide();break;case 36:a.firstSlide();break;case 35:a.lastSlide()}};a.root=function(){return a.odf_canvas.odfContainer().rootElement};
|
||||
a.firstSlide=function(){a.slideChange(function(){return 0})};a.lastSlide=function(){a.slideChange(function(a,e){return e-1})};a.nextSlide=function(){a.slideChange(function(a,e){return a+1<e?a+1:-1})};a.prevSlide=function(){a.slideChange(function(a){return a<1?-1:a-1})};a.slideChange=function(b){var e=a.getPages(a.odf_canvas.odfContainer().rootElement),c=-1,d=0;e.forEach(function(a){a=a[1];a.hasAttribute("slide_current")&&(c=d,a.removeAttribute("slide_current"));d+=1});b=b(c,e.length);b===-1&&(b=c);
|
||||
e[b][1].setAttribute("slide_current","1");document.getElementById("pagelist").selectedIndex=b;a.slide_mode==="cont"&&window.scrollBy(0,e[b][1].getBoundingClientRect().top-30)};a.selectSlide=function(b){a.slideChange(function(a,c){return b>=c?-1:b<0?-1:b})};a.scrollIntoContView=function(b){var e=a.getPages(a.odf_canvas.odfContainer().rootElement);e.length!==0&&window.scrollBy(0,e[b][1].getBoundingClientRect().top-30)};a.getPages=function(a){var a=a.getElementsByTagNameNS(e("draw"),"page"),g=[],c;for(c=
|
||||
0;c<a.length;c+=1)g.push([a[c].getAttribute("draw:name"),a[c]]);return g};a.fillPageList=function(b,e){for(var c=a.getPages(b),d,f,g;e.firstChild;)e.removeChild(e.firstChild);for(d=0;d<c.length;d+=1)f=document.createElement("option"),g=k.getODFElementsWithXPath(c[d][1],'./draw:frame[@presentation:class="title"]//draw:text-box/text:p',xmldom.XPath),g=g.length>0?g[0].textContent:c[d][0],f.textContent=d+1+": "+g,e.appendChild(f)};a.startSlideMode=function(b){var e=document.getElementById("pagelist"),
|
||||
c=a.odf_canvas.slidevisibilitycss().sheet;for(a.slide_mode=b;c.cssRules.length>0;)c.deleteRule(0);a.selectSlide(0);a.slide_mode==="single"?(c.insertRule("draw|page { position:fixed; left:0px;top:30px; z-index:1; }",0),c.insertRule("draw|page[slide_current] { z-index:2;}",1),c.insertRule("draw|page { -webkit-transform: scale(1);}",2),a.fitToWindow(),window.addEventListener("resize",a.fitToWindow,false)):a.slide_mode==="cont"&&window.removeEventListener("resize",a.fitToWindow,false);a.fillPageList(a.odf_canvas.odfContainer().rootElement,
|
||||
e)};a.toggleToolbar=function(){var b,e,c;b=a.odf_canvas.slidevisibilitycss().sheet;e=-1;for(c=0;c<b.cssRules.length;c+=1)if(b.cssRules[c].cssText.substring(0,8)===".toolbar"){e=c;break}e>-1?b.deleteRule(e):b.insertRule(".toolbar { position:fixed; left:0px;top:-200px; z-index:0; }",0)};a.fitToWindow=function(){var b=a.getPages(a.root()),e=(window.innerHeight-40)/b[0][1].clientHeight,b=(window.innerWidth-10)/b[0][1].clientWidth,e=e<b?e:b,b=a.odf_canvas.slidevisibilitycss().sheet;b.deleteRule(2);b.insertRule("draw|page { \n-moz-transform: scale("+
|
||||
e+"); \n-moz-transform-origin: 0% 0%; -webkit-transform-origin: 0% 0%; -webkit-transform: scale("+e+"); -o-transform-origin: 0% 0%; -o-transform: scale("+e+"); -ms-transform-origin: 0% 0%; -ms-transform: scale("+e+"); }",2)};a.load=function(b){a.odf_canvas.load(b)};a.odf_element=g;a.odf_canvas=new odf.OdfCanvas(a.odf_element);a.odf_canvas.addListener("statereadychange",a.setInitialSlideMode);a.slide_mode="undefined";document.addEventListener("keydown",a.keyDownHandler,false)}}();
|
||||
gui.PresenterUI=function(){var g=new odf.Style2CSS,m=new xmldom.XPath,e=g.namespaceResolver;return function(g){var a=this;a.setInitialSlideMode=function(){a.startSlideMode("single")};a.keyDownHandler=function(c){if(!(c.target.isContentEditable||"input"===c.target.nodeName))switch(c.keyCode){case 84:a.toggleToolbar();break;case 37:case 8:a.prevSlide();break;case 39:case 32:a.nextSlide();break;case 36:a.firstSlide();break;case 35:a.lastSlide()}};a.root=function(){return a.odf_canvas.odfContainer().rootElement};
|
||||
a.firstSlide=function(){a.slideChange(function(){return 0})};a.lastSlide=function(){a.slideChange(function(a,b){return b-1})};a.nextSlide=function(){a.slideChange(function(a,b){return a+1<b?a+1:-1})};a.prevSlide=function(){a.slideChange(function(a){return 1>a?-1:a-1})};a.slideChange=function(c){var b=a.getPages(a.odf_canvas.odfContainer().rootElement),d=-1,e=0;b.forEach(function(a){a=a[1];a.hasAttribute("slide_current")&&(d=e,a.removeAttribute("slide_current"));e+=1});c=c(d,b.length);-1===c&&(c=d);
|
||||
b[c][1].setAttribute("slide_current","1");document.getElementById("pagelist").selectedIndex=c;"cont"===a.slide_mode&&window.scrollBy(0,b[c][1].getBoundingClientRect().top-30)};a.selectSlide=function(c){a.slideChange(function(a,d){return c>=d||0>c?-1:c})};a.scrollIntoContView=function(c){var b=a.getPages(a.odf_canvas.odfContainer().rootElement);0!==b.length&&window.scrollBy(0,b[c][1].getBoundingClientRect().top-30)};a.getPages=function(a){var a=a.getElementsByTagNameNS(e("draw"),"page"),b=[],d;for(d=
|
||||
0;d<a.length;d+=1)b.push([a[d].getAttribute("draw:name"),a[d]]);return b};a.fillPageList=function(c,b){for(var d=a.getPages(c),e,f,g;b.firstChild;)b.removeChild(b.firstChild);for(e=0;e<d.length;e+=1)f=document.createElement("option"),g=m.getODFElementsWithXPath(d[e][1],'./draw:frame[@presentation:class="title"]//draw:text-box/text:p',xmldom.XPath),g=0<g.length?g[0].textContent:d[e][0],f.textContent=e+1+": "+g,b.appendChild(f)};a.startSlideMode=function(c){var b=document.getElementById("pagelist"),
|
||||
d=a.odf_canvas.slidevisibilitycss().sheet;for(a.slide_mode=c;0<d.cssRules.length;)d.deleteRule(0);a.selectSlide(0);"single"===a.slide_mode?(d.insertRule("draw|page { position:fixed; left:0px;top:30px; z-index:1; }",0),d.insertRule("draw|page[slide_current] { z-index:2;}",1),d.insertRule("draw|page { -webkit-transform: scale(1);}",2),a.fitToWindow(),window.addEventListener("resize",a.fitToWindow,!1)):"cont"===a.slide_mode&&window.removeEventListener("resize",a.fitToWindow,!1);a.fillPageList(a.odf_canvas.odfContainer().rootElement,
|
||||
b)};a.toggleToolbar=function(){var c,b,d;c=a.odf_canvas.slidevisibilitycss().sheet;b=-1;for(d=0;d<c.cssRules.length;d+=1)if(".toolbar"===c.cssRules[d].cssText.substring(0,8)){b=d;break}-1<b?c.deleteRule(b):c.insertRule(".toolbar { position:fixed; left:0px;top:-200px; z-index:0; }",0)};a.fitToWindow=function(){var c=a.getPages(a.root()),b=(window.innerHeight-40)/c[0][1].clientHeight,c=(window.innerWidth-10)/c[0][1].clientWidth,b=b<c?b:c,c=a.odf_canvas.slidevisibilitycss().sheet;c.deleteRule(2);c.insertRule("draw|page { \n-moz-transform: scale("+
|
||||
b+"); \n-moz-transform-origin: 0% 0%; -webkit-transform-origin: 0% 0%; -webkit-transform: scale("+b+"); -o-transform-origin: 0% 0%; -o-transform: scale("+b+"); -ms-transform-origin: 0% 0%; -ms-transform: scale("+b+"); }",2)};a.load=function(c){a.odf_canvas.load(c)};a.odf_element=g;a.odf_canvas=new odf.OdfCanvas(a.odf_element);a.odf_canvas.addListener("statereadychange",a.setInitialSlideMode);a.slide_mode="undefined";document.addEventListener("keydown",a.keyDownHandler,!1)}}();
|
||||
// Input 27
|
||||
gui.Caret=function(i,k){k.ownerDocument.createElementNS("urn:webodf:names:cursor","cursor");this.updateToSelection=function(){i.rangeCount===1&&i.getRangeAt(0)}};
|
||||
gui.Caret=function(g,m){m.ownerDocument.createElementNS("urn:webodf:names:cursor","cursor");this.updateToSelection=function(){1===g.rangeCount&&g.getRangeAt(0)}};
|
||||
// Input 28
|
||||
runtime.loadClass("core.Cursor");
|
||||
gui.SelectionMover=function(i,k){function e(a,b){if(i.rangeCount!==0){var d=i.getRangeAt(0);if(d.startContainer&&d.startContainer.nodeType===1){k.setPoint(d.startContainer,d.startOffset);b();d=k.node();k.position();var f=[],e;for(e=0;e<i.rangeCount;e+=1)f[e]=i.getRangeAt(e);i.removeAllRanges();f.length===0&&(f[0]=d.ownerDocument.createRange());f[f.length-1].setStart(k.node(),k.position());for(e=0;e<f.length;e+=1)i.addRange(f[e])}}}function g(){b.updateToSelection();for(var a=b.getNode().getBoundingClientRect(),
|
||||
c=a.left,d=a.top,a=false,f=200;!a;){f-=1;b.remove();if(i.focusNode&&i.focusNode.nodeType===1){k.setPoint(i.focusNode,i.focusOffset);k.stepForward();var a=k.node(),e=k.position();i.collapse(a,e);b.updateToSelection()}a=b.getNode().getBoundingClientRect();a=a.top!==d&&a.left>c}}var a=k.node().ownerDocument,b=new core.Cursor(i,a);this.movePointForward=function(a){e(a,k.stepForward)};this.movePointBackward=function(a){e(a,k.stepBackward)};this.moveLineForward=function(a){i.modify?i.modify(a?"extend":
|
||||
"move","forward","line"):e(a,g)};this.moveLineBackward=function(a){i.modify?i.modify(a?"extend":"move","backward","line"):e(a,function(){})};return this};
|
||||
gui.SelectionMover=function(g,m){function e(a,c){if(0!==g.rangeCount){var e=g.getRangeAt(0);if(e.startContainer&&1===e.startContainer.nodeType){m.setPoint(e.startContainer,e.startOffset);c();e=m.node();m.position();var f=[],h;for(h=0;h<g.rangeCount;h+=1)f[h]=g.getRangeAt(h);g.removeAllRanges();0===f.length&&(f[0]=e.ownerDocument.createRange());f[f.length-1].setStart(m.node(),m.position());for(h=0;h<f.length;h+=1)g.addRange(f[h])}}}function k(){c.updateToSelection();for(var a=c.getNode().getBoundingClientRect(),
|
||||
d=a.left,e=a.top,a=!1;!a;){c.remove();if(g.focusNode&&1===g.focusNode.nodeType){m.setPoint(g.focusNode,g.focusOffset);m.stepForward();var a=m.node(),f=m.position();g.collapse(a,f);c.updateToSelection()}a=c.getNode().getBoundingClientRect();a=a.top!==e&&a.left>d}}var a=m.node().ownerDocument,c=new core.Cursor(g,a);this.movePointForward=function(a){e(a,m.stepForward)};this.movePointBackward=function(a){e(a,m.stepBackward)};this.moveLineForward=function(a){g.modify?g.modify(a?"extend":"move","forward",
|
||||
"line"):e(a,k)};this.moveLineBackward=function(a){g.modify?g.modify(a?"extend":"move","backward","line"):e(a,function(){})};return this};
|
||||
// Input 29
|
||||
runtime.loadClass("core.PointWalker");runtime.loadClass("core.Cursor");
|
||||
gui.XMLEdit=function(i,k){function e(a,b,c){a.addEventListener?a.addEventListener(b,c,false):a.attachEvent?a.attachEvent("on"+b,c):a["on"+b]=c}function g(a){a.preventDefault?a.preventDefault():a.returnValue=false}function a(){var a=i.ownerDocument.defaultView.getSelection();a&&!(a.rangeCount<=0)&&n&&(a=a.getRangeAt(0),n.setPoint(a.startContainer,a.startOffset))}function b(){var a=i.ownerDocument.defaultView.getSelection(),b,c;a.removeAllRanges();n&&n.node()&&(b=n.node(),c=b.ownerDocument.createRange(),
|
||||
c.setStart(b,n.position()),c.collapse(true),a.addRange(c))}function h(c){var d=c.charCode||c.keyCode;if(n=null,n&&d===37)a(),n.stepBackward(),b();else if(d>=16&&d<=20||d>=33&&d<=40)return;g(c)}function c(){}function d(a){i.ownerDocument.defaultView.getSelection().getRangeAt(0);g(a)}function f(a){for(var b=a.firstChild;b&&b!==a;)b.nodeType===1&&f(b),b=b.nextSibling||b.parentNode;var c,d,e,b=a.attributes;c="";for(e=b.length-1;e>=0;e-=1)d=b.item(e),c=c+" "+d.nodeName+'="'+d.nodeValue+'"';a.setAttribute("customns_name",
|
||||
a.nodeName);a.setAttribute("customns_atts",c);b=a.firstChild;for(d=/^\s*$/;b&&b!==a;)c=b,b=b.nextSibling||b.parentNode,c.nodeType===3&&d.test(c.nodeValue)&&c.parentNode.removeChild(c)}function j(a,b){for(var c=a.firstChild,d,e,f;c&&c!==a;){if(c.nodeType===1){j(c,b);d=c.attributes;for(f=d.length-1;f>=0;f-=1)if(e=d.item(f),e.namespaceURI==="http://www.w3.org/2000/xmlns/"&&!b[e.nodeValue])b[e.nodeValue]=e.localName}c=c.nextSibling||c.parentNode}}function p(){var a=i.ownerDocument.createElement("style"),
|
||||
b;b={};j(i,b);var c={},d,e,f=0;for(d in b)if(b.hasOwnProperty(d)&&d){e=b[d];if(!e||c.hasOwnProperty(e)||e==="xmlns"){do e="ns"+f,f+=1;while(c.hasOwnProperty(e));b[d]=e}c[e]=true}b="@namespace customns url(customns);\n";a.type="text/css";b+=m;a.appendChild(i.ownerDocument.createTextNode(b));k=k.parentNode.replaceChild(a,k)}var m,l,u,n=null;if(!i.id)i.id="xml"+String(Math.random()).substring(2);l="#"+i.id+" ";m=l+"*,"+l+":visited, "+l+":link {display:block; margin: 0px; margin-left: 10px; font-size: medium; color: black; background: white; font-variant: normal; font-weight: normal; font-style: normal; font-family: sans-serif; text-decoration: none; white-space: pre-wrap; height: auto; width: auto}\n"+
|
||||
l+":before {color: blue; content: '<' attr(customns_name) attr(customns_atts) '>';}\n"+l+":after {color: blue; content: '</' attr(customns_name) '>';}\n"+l+"{overflow: auto;}\n";(function(a){e(a,"click",d);e(a,"keydown",h);e(a,"keypress",c);e(a,"drop",g);e(a,"dragend",g);e(a,"beforepaste",g);e(a,"paste",g)})(i);this.updateCSS=p;this.setXML=function(a){a=a.documentElement||a;u=a=i.ownerDocument.importNode(a,true);for(f(a);i.lastChild;)i.removeChild(i.lastChild);i.appendChild(a);p();n=new core.PointWalker(a)};
|
||||
this.getXML=function(){return u}};
|
||||
gui.XMLEdit=function(g,m){function e(a,b,c){a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent?a.attachEvent("on"+b,c):a["on"+b]=c}function k(a){a.preventDefault?a.preventDefault():a.returnValue=!1}function a(){var a=g.ownerDocument.defaultView.getSelection();a&&!(0>=a.rangeCount)&&p&&(a=a.getRangeAt(0),p.setPoint(a.startContainer,a.startOffset))}function c(){var a=g.ownerDocument.defaultView.getSelection(),b,c;a.removeAllRanges();p&&p.node()&&(b=p.node(),c=b.ownerDocument.createRange(),
|
||||
c.setStart(b,p.position()),c.collapse(!0),a.addRange(c))}function b(b){var d=b.charCode||b.keyCode;if(p=null,p&&37===d)a(),p.stepBackward(),c();else if(16<=d&&20>=d||33<=d&&40>=d)return;k(b)}function d(){}function o(a){g.ownerDocument.defaultView.getSelection().getRangeAt(0);k(a)}function f(a){for(var b=a.firstChild;b&&b!==a;)1===b.nodeType&&f(b),b=b.nextSibling||b.parentNode;var c,d,e,b=a.attributes;c="";for(e=b.length-1;0<=e;e-=1)d=b.item(e),c=c+" "+d.nodeName+'="'+d.nodeValue+'"';a.setAttribute("customns_name",
|
||||
a.nodeName);a.setAttribute("customns_atts",c);b=a.firstChild;for(d=/^\s*$/;b&&b!==a;)c=b,b=b.nextSibling||b.parentNode,3===c.nodeType&&d.test(c.nodeValue)&&c.parentNode.removeChild(c)}function h(a,b){for(var c=a.firstChild,d,e,f;c&&c!==a;){if(1===c.nodeType){h(c,b);d=c.attributes;for(f=d.length-1;0<=f;f-=1)e=d.item(f),"http://www.w3.org/2000/xmlns/"===e.namespaceURI&&!b[e.nodeValue]&&(b[e.nodeValue]=e.localName)}c=c.nextSibling||c.parentNode}}function i(){var a=g.ownerDocument.createElement("style"),
|
||||
b;b={};h(g,b);var c={},d,e,f=0;for(d in b)if(b.hasOwnProperty(d)&&d){e=b[d];if(!e||c.hasOwnProperty(e)||"xmlns"===e){do e="ns"+f,f+=1;while(c.hasOwnProperty(e));b[d]=e}c[e]=!0}a.type="text/css";b="@namespace customns url(customns);\n"+j;a.appendChild(g.ownerDocument.createTextNode(b));m=m.parentNode.replaceChild(a,m)}var j,n,x,p=null;g.id||(g.id="xml"+(""+Math.random()).substring(2));n="#"+g.id+" ";j=n+"*,"+n+":visited, "+n+":link {display:block; margin: 0px; margin-left: 10px; font-size: medium; color: black; background: white; font-variant: normal; font-weight: normal; font-style: normal; font-family: sans-serif; text-decoration: none; white-space: pre-wrap; height: auto; width: auto}\n"+
|
||||
n+":before {color: blue; content: '<' attr(customns_name) attr(customns_atts) '>';}\n"+n+":after {color: blue; content: '</' attr(customns_name) '>';}\n"+n+"{overflow: auto;}\n";(function(a){e(a,"click",o);e(a,"keydown",b);e(a,"keypress",d);e(a,"drop",k);e(a,"dragend",k);e(a,"beforepaste",k);e(a,"paste",k)})(g);this.updateCSS=i;this.setXML=function(a){a=a.documentElement||a;x=a=g.ownerDocument.importNode(a,!0);for(f(a);g.lastChild;)g.removeChild(g.lastChild);g.appendChild(a);i();p=new core.PointWalker(a)};
|
||||
this.getXML=function(){return x}};
|
||||
// Input 30
|
||||
(function(){return"core/Async.js,core/Base64.js,core/ByteArray.js,core/ByteArrayWriter.js,core/Cursor.js,core/JSLint.js,core/PointWalker.js,core/RawDeflate.js,core/RawInflate.js,core/UnitTester.js,core/Zip.js,gui/Caret.js,gui/SelectionMover.js,gui/XMLEdit.js,gui/PresenterUI.js,odf/FontLoader.js,odf/Formatting.js,odf/OdfCanvas.js,odf/OdfContainer.js,odf/Style2CSS.js,odf/StyleInfo.js,xmldom/LSSerializer.js,xmldom/LSSerializerFilter.js,xmldom/OperationalTransformDOM.js,xmldom/OperationalTransformInterface.js,xmldom/RelaxNG.js,xmldom/RelaxNG2.js,xmldom/RelaxNGParser.js,xmldom/XPath.js".split(",")})();
|
||||
|
|
Loading…
Add table
Reference in a new issue