2012-06-08 14:57:16 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Kolab Groupware driver for the Tasklist plugin
|
|
|
|
*
|
|
|
|
* @version @package_version@
|
|
|
|
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
|
|
|
*
|
2015-03-11 14:38:38 +01:00
|
|
|
* Copyright (C) 2012-2015, Kolab Systems AG <contact@kolabsys.com>
|
2012-06-08 14:57:16 +02:00
|
|
|
*
|
|
|
|
* 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 tasklist_kolab_driver extends tasklist_driver
|
|
|
|
{
|
|
|
|
// features supported by the backend
|
2014-07-25 14:08:41 +02:00
|
|
|
public $alarms = false;
|
2012-08-01 15:52:28 +02:00
|
|
|
public $attachments = true;
|
2014-07-25 14:08:41 +02:00
|
|
|
public $attendees = true;
|
|
|
|
public $undelete = false; // task undelete action
|
2024-01-24 11:24:41 +01:00
|
|
|
public $alarm_types = ['DISPLAY','AUDIO'];
|
2014-09-08 18:52:06 +02:00
|
|
|
public $search_more_results;
|
2012-06-08 14:57:16 +02:00
|
|
|
|
|
|
|
private $rc;
|
|
|
|
private $plugin;
|
|
|
|
private $lists;
|
2024-01-24 11:24:41 +01:00
|
|
|
private $folders = [];
|
|
|
|
private $tasks = [];
|
|
|
|
private $tags = [];
|
2015-03-25 11:59:10 +01:00
|
|
|
private $bonnie_api = false;
|
2012-06-08 14:57:16 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default constructor
|
|
|
|
*/
|
|
|
|
public function __construct($plugin)
|
|
|
|
{
|
2016-02-12 11:50:42 +01:00
|
|
|
$this->rc = $plugin->rc;
|
2012-06-08 14:57:16 +02:00
|
|
|
$this->plugin = $plugin;
|
|
|
|
|
2012-11-22 15:19:17 +01:00
|
|
|
if (kolab_storage::$version == '2.0') {
|
2012-11-15 15:03:00 +01:00
|
|
|
$this->alarm_absolute = false;
|
|
|
|
}
|
2014-04-09 11:12:50 +02:00
|
|
|
|
2014-05-21 13:04:18 +02:00
|
|
|
// tasklist use fully encoded identifiers
|
|
|
|
kolab_storage::$encode_ids = true;
|
|
|
|
|
2015-03-25 11:59:10 +01:00
|
|
|
// get configuration for the Bonnie API
|
2015-04-20 17:20:39 +02:00
|
|
|
$this->bonnie_api = libkolab::get_bonnie_api();
|
2015-03-25 11:59:10 +01:00
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
$this->plugin->register_action('folder-acl', [$this, 'folder_acl']);
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read available calendars for the current user and store them internally
|
|
|
|
*/
|
2012-11-21 12:25:14 +01:00
|
|
|
private function _read_lists($force = false)
|
2012-06-08 14:57:16 +02:00
|
|
|
{
|
|
|
|
// already read sources
|
2016-02-12 11:50:42 +01:00
|
|
|
if (isset($this->lists) && !$force) {
|
2012-06-08 14:57:16 +02:00
|
|
|
return $this->lists;
|
2016-02-12 11:50:42 +01:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
|
|
|
|
// get all folders that have type "task"
|
2013-07-18 17:47:49 +02:00
|
|
|
$folders = kolab_storage::sort_folders(kolab_storage::get_folders('task'));
|
2024-01-24 11:24:41 +01:00
|
|
|
$this->lists = $this->folders = [];
|
2013-07-18 17:47:49 +02:00
|
|
|
|
2014-08-13 18:02:48 +02:00
|
|
|
$delim = $this->rc->get_storage()->get_hierarchy_delimiter();
|
|
|
|
|
2013-07-18 17:47:49 +02:00
|
|
|
// find default folder
|
|
|
|
$default_index = 0;
|
|
|
|
foreach ($folders as $i => $folder) {
|
2024-01-24 11:24:41 +01:00
|
|
|
if ($folder->default && strpos($folder->name, $delim) === false) {
|
2013-07-18 17:47:49 +02:00
|
|
|
$default_index = $i;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|
|
|
|
|
2012-09-20 09:38:43 +02:00
|
|
|
// put default folder (aka INBOX) on top of the list
|
2013-07-18 17:47:49 +02:00
|
|
|
if ($default_index > 0) {
|
|
|
|
$default_folder = $folders[$default_index];
|
|
|
|
unset($folders[$default_index]);
|
|
|
|
array_unshift($folders, $default_folder);
|
2012-09-20 09:38:43 +02:00
|
|
|
}
|
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
$prefs = $this->rc->config->get('kolab_tasklists', []);
|
2014-05-21 13:04:18 +02:00
|
|
|
|
|
|
|
foreach ($folders as $folder) {
|
2014-11-21 10:03:18 +01:00
|
|
|
$tasklist = $this->folder_props($folder, $prefs);
|
2014-05-21 13:04:18 +02:00
|
|
|
|
|
|
|
$this->lists[$tasklist['id']] = $tasklist;
|
|
|
|
$this->folders[$tasklist['id']] = $folder;
|
|
|
|
$this->folders[$folder->name] = $folder;
|
|
|
|
}
|
2016-02-12 11:50:42 +01:00
|
|
|
|
|
|
|
return $this->lists;
|
2014-05-21 13:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Derive list properties from the given kolab_storage_folder object
|
|
|
|
*/
|
2014-11-21 10:03:18 +01:00
|
|
|
protected function folder_props($folder, $prefs)
|
2014-05-21 13:04:18 +02:00
|
|
|
{
|
|
|
|
if ($folder->get_namespace() == 'personal') {
|
|
|
|
$norename = false;
|
2015-03-11 14:38:38 +01:00
|
|
|
$editable = true;
|
|
|
|
$rights = 'lrswikxtea';
|
2014-05-21 13:04:18 +02:00
|
|
|
$alarms = true;
|
2024-01-24 11:24:41 +01:00
|
|
|
} else {
|
2014-05-21 13:04:18 +02:00
|
|
|
$alarms = false;
|
2015-03-11 14:38:38 +01:00
|
|
|
$rights = 'lr';
|
|
|
|
$editable = false;
|
2016-12-27 04:03:28 -05:00
|
|
|
if ($myrights = $folder->get_myrights()) {
|
2015-03-11 14:38:38 +01:00
|
|
|
$rights = $myrights;
|
2024-01-24 11:24:41 +01:00
|
|
|
if (strpos($rights, 't') !== false || strpos($rights, 'd') !== false) {
|
2016-12-27 04:03:28 -05:00
|
|
|
$editable = strpos($rights, 'i') !== false;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2014-05-21 13:04:18 +02:00
|
|
|
}
|
|
|
|
$info = $folder->get_folder_info();
|
2024-01-24 10:59:25 +01:00
|
|
|
$norename = !$editable || !empty($info['norename']) || !empty($info['protected']);
|
2014-05-21 13:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$list_id = $folder->id; #kolab_storage::folder_id($folder->name);
|
|
|
|
$old_id = kolab_storage::folder_id($folder->name, false);
|
|
|
|
|
|
|
|
if (!isset($prefs[$list_id]['showalarms']) && isset($prefs[$old_id]['showalarms'])) {
|
|
|
|
$prefs[$list_id]['showalarms'] = $prefs[$old_id]['showalarms'];
|
|
|
|
}
|
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
return [
|
2014-05-21 13:04:18 +02:00
|
|
|
'id' => $list_id,
|
|
|
|
'name' => $folder->get_name(),
|
|
|
|
'listname' => $folder->get_foldername(),
|
|
|
|
'editname' => $folder->get_foldername(),
|
|
|
|
'color' => $folder->get_color('0000CC'),
|
2024-01-24 11:24:41 +01:00
|
|
|
'showalarms' => $prefs[$list_id]['showalarms'] ?? $alarms,
|
2015-03-11 14:38:38 +01:00
|
|
|
'editable' => $editable,
|
|
|
|
'rights' => $rights,
|
2014-05-21 13:04:18 +02:00
|
|
|
'norename' => $norename,
|
|
|
|
'active' => $folder->is_active(),
|
2016-12-27 04:03:28 -05:00
|
|
|
'owner' => $folder->get_owner(),
|
2014-05-21 13:04:18 +02:00
|
|
|
'parentfolder' => $folder->get_parent(),
|
|
|
|
'default' => $folder->default,
|
2024-01-24 10:59:25 +01:00
|
|
|
'virtual' => $folder instanceof kolab_storage_folder_virtual,
|
2014-05-21 13:04:18 +02:00
|
|
|
'children' => true, // TODO: determine if that folder indeed has child folders
|
|
|
|
'subscribed' => (bool)$folder->is_subscribed(),
|
2014-09-09 13:29:28 +02:00
|
|
|
'removable' => !$folder->default,
|
2014-09-23 12:27:57 +02:00
|
|
|
'subtype' => $folder->subtype,
|
2014-09-09 13:29:28 +02:00
|
|
|
'group' => $folder->default ? 'default' : $folder->get_namespace(),
|
2014-05-21 13:04:18 +02:00
|
|
|
'class' => trim($folder->get_namespace() . ($folder->default ? ' default' : '')),
|
2015-03-17 10:43:03 +01:00
|
|
|
'caldavuid' => $folder->get_uid(),
|
2015-03-25 11:59:10 +01:00
|
|
|
'history' => !empty($this->bonnie_api),
|
2024-01-24 11:24:41 +01:00
|
|
|
];
|
2014-05-21 13:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a list of available task lists from this source
|
2016-12-27 04:03:28 -05:00
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param int $filter Bitmask defining filter criterias.
|
|
|
|
* See FILTER_* constants for possible values.
|
|
|
|
* @param array|null $tree Folders tree
|
2014-05-21 13:04:18 +02:00
|
|
|
*/
|
2016-12-27 04:03:28 -05:00
|
|
|
public function get_lists($filter = 0, &$tree = null)
|
2014-05-21 13:04:18 +02:00
|
|
|
{
|
2016-02-12 11:50:42 +01:00
|
|
|
$this->_read_lists();
|
|
|
|
|
2014-05-21 13:04:18 +02:00
|
|
|
// attempt to create a default list for this user
|
2014-09-08 18:52:06 +02:00
|
|
|
if (empty($this->lists) && !isset($this->search_more_results)) {
|
2024-01-24 11:24:41 +01:00
|
|
|
$prop = ['name' => 'Tasks', 'color' => '0000CC', 'default' => true];
|
|
|
|
if ($this->create_list($prop)) {
|
2014-05-21 13:04:18 +02:00
|
|
|
$this->_read_lists(true);
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2014-05-21 13:04:18 +02:00
|
|
|
}
|
|
|
|
|
2016-12-27 04:03:28 -05:00
|
|
|
$folders = $this->filter_folders($filter);
|
2012-06-21 21:59:47 +02:00
|
|
|
|
2013-10-10 17:27:24 +02:00
|
|
|
// include virtual folders for a full folder tree
|
2014-05-21 13:04:18 +02:00
|
|
|
if (!is_null($tree)) {
|
|
|
|
$folders = kolab_storage::folder_hierarchy($folders, $tree);
|
|
|
|
}
|
2012-08-04 17:19:03 +02:00
|
|
|
|
2014-05-21 13:04:18 +02:00
|
|
|
$delim = $this->rc->get_storage()->get_hierarchy_delimiter();
|
2024-01-24 11:24:41 +01:00
|
|
|
$prefs = $this->rc->config->get('kolab_tasklists', []);
|
2012-08-04 17:19:03 +02:00
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
$lists = [];
|
2013-07-18 17:47:49 +02:00
|
|
|
foreach ($folders as $folder) {
|
2014-11-21 10:03:18 +01:00
|
|
|
$list_id = $folder->id; // kolab_storage::folder_id($folder->name);
|
2014-05-21 13:04:18 +02:00
|
|
|
$imap_path = explode($delim, $folder->name);
|
2012-06-21 21:59:47 +02:00
|
|
|
|
2014-05-21 13:04:18 +02:00
|
|
|
// find parent
|
|
|
|
do {
|
2024-01-24 11:24:41 +01:00
|
|
|
array_pop($imap_path);
|
|
|
|
$parent_id = kolab_storage::folder_id(implode($delim, $imap_path));
|
|
|
|
} while (count($imap_path) > 1 && !$this->folders[$parent_id]);
|
2014-05-21 13:04:18 +02:00
|
|
|
|
|
|
|
// restore "real" parent ID
|
|
|
|
if ($parent_id && !$this->folders[$parent_id]) {
|
|
|
|
$parent_id = kolab_storage::folder_id($folder->get_parent());
|
|
|
|
}
|
2012-06-21 21:59:47 +02:00
|
|
|
|
2013-11-22 11:12:31 +01:00
|
|
|
$fullname = $folder->get_name();
|
2014-05-21 13:04:18 +02:00
|
|
|
$listname = $folder->get_foldername();
|
2013-10-10 17:27:24 +02:00
|
|
|
|
|
|
|
// special handling for virtual folders
|
2014-05-21 13:04:18 +02:00
|
|
|
if ($folder instanceof kolab_storage_folder_user) {
|
2024-01-24 11:24:41 +01:00
|
|
|
$lists[$list_id] = [
|
2014-05-21 13:04:18 +02:00
|
|
|
'id' => $list_id,
|
2016-02-12 13:45:58 +01:00
|
|
|
'name' => $fullname,
|
2013-10-16 16:35:56 +02:00
|
|
|
'listname' => $listname,
|
2014-08-18 13:34:10 +02:00
|
|
|
'title' => $folder->get_title(),
|
2014-05-21 13:04:18 +02:00
|
|
|
'virtual' => true,
|
2013-10-11 13:50:07 +02:00
|
|
|
'editable' => false,
|
2015-03-11 14:38:38 +01:00
|
|
|
'rights' => 'l',
|
2014-05-21 13:04:18 +02:00
|
|
|
'group' => 'other virtual',
|
|
|
|
'class' => 'user',
|
|
|
|
'parent' => $parent_id,
|
2024-01-24 11:24:41 +01:00
|
|
|
];
|
|
|
|
} elseif ($folder instanceof kolab_storage_folder_virtual) {
|
|
|
|
$lists[$list_id] = [
|
2014-05-21 13:04:18 +02:00
|
|
|
'id' => $list_id,
|
2016-02-12 13:45:58 +01:00
|
|
|
'name' => $fullname,
|
2014-05-21 13:04:18 +02:00
|
|
|
'listname' => $listname,
|
|
|
|
'virtual' => true,
|
|
|
|
'editable' => false,
|
2015-03-11 14:38:38 +01:00
|
|
|
'rights' => 'l',
|
2014-05-21 13:04:18 +02:00
|
|
|
'group' => $folder->get_namespace(),
|
|
|
|
'class' => 'folder',
|
|
|
|
'parent' => $parent_id,
|
2024-01-24 11:24:41 +01:00
|
|
|
];
|
|
|
|
} else {
|
2014-05-21 13:04:18 +02:00
|
|
|
if (!$this->lists[$list_id]) {
|
2014-11-21 10:03:18 +01:00
|
|
|
$this->lists[$list_id] = $this->folder_props($folder, $prefs);
|
2014-05-21 13:04:18 +02:00
|
|
|
$this->folders[$list_id] = $folder;
|
2012-09-06 17:52:14 +02:00
|
|
|
}
|
2014-05-21 13:04:18 +02:00
|
|
|
$this->lists[$list_id]['parent'] = $parent_id;
|
|
|
|
$lists[$list_id] = $this->lists[$list_id];
|
2012-09-06 17:52:14 +02:00
|
|
|
}
|
2013-10-10 17:27:24 +02:00
|
|
|
}
|
2014-05-21 13:04:18 +02:00
|
|
|
|
|
|
|
return $lists;
|
2013-10-10 17:27:24 +02:00
|
|
|
}
|
|
|
|
|
2016-12-27 04:03:28 -05:00
|
|
|
/**
|
|
|
|
* Get list of folders according to specified filters
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param int $filter Bitmask defining restrictions. See FILTER_* constants for possible values.
|
2016-12-27 04:03:28 -05:00
|
|
|
*
|
|
|
|
* @return array List of task folders
|
|
|
|
*/
|
|
|
|
protected function filter_folders($filter)
|
|
|
|
{
|
|
|
|
$this->_read_lists();
|
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
$folders = [];
|
2016-12-27 04:03:28 -05:00
|
|
|
foreach ($this->lists as $id => $list) {
|
|
|
|
if (!empty($this->folders[$id])) {
|
|
|
|
$folder = $this->folders[$id];
|
|
|
|
|
|
|
|
if ($folder->get_namespace() == 'personal') {
|
|
|
|
$folder->editable = true;
|
2024-01-24 11:24:41 +01:00
|
|
|
} elseif ($rights = $folder->get_myrights()) {
|
2016-12-27 04:03:28 -05:00
|
|
|
if (strpos($rights, 't') !== false || strpos($rights, 'd') !== false) {
|
|
|
|
$folder->editable = strpos($rights, 'i') !== false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$folders[] = $folder;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
$plugin = $this->rc->plugins->exec_hook('tasklist_list_filter', [
|
2016-12-27 04:03:28 -05:00
|
|
|
'list' => $folders,
|
|
|
|
'filter' => $filter,
|
|
|
|
'tasklists' => $folders,
|
2024-01-24 11:24:41 +01:00
|
|
|
]);
|
2016-12-27 04:03:28 -05:00
|
|
|
|
|
|
|
if ($plugin['abort'] || !$filter) {
|
|
|
|
return $plugin['tasklists'];
|
|
|
|
}
|
|
|
|
|
|
|
|
$personal = $filter & self::FILTER_PERSONAL;
|
|
|
|
$shared = $filter & self::FILTER_SHARED;
|
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
$tasklists = [];
|
2016-12-27 04:03:28 -05:00
|
|
|
foreach ($folders as $folder) {
|
|
|
|
if (($filter & self::FILTER_WRITEABLE) && !$folder->editable) {
|
|
|
|
continue;
|
|
|
|
}
|
2024-01-24 11:24:41 +01:00
|
|
|
/*
|
2024-01-25 13:47:41 +01:00
|
|
|
if (($filter & self::FILTER_INSERTABLE) && !$folder->insert) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (($filter & self::FILTER_ACTIVE) && !$folder->is_active()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (($filter & self::FILTER_PRIVATE) && $folder->subtype != 'private') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (($filter & self::FILTER_CONFIDENTIAL) && $folder->subtype != 'confidential') {
|
|
|
|
continue;
|
|
|
|
}
|
2024-01-24 11:24:41 +01:00
|
|
|
*/
|
2016-12-27 04:03:28 -05:00
|
|
|
if ($personal || $shared) {
|
|
|
|
$ns = $folder->get_namespace();
|
|
|
|
if (!(($personal && $ns == 'personal') || ($shared && $ns == 'shared'))) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$tasklists[$folder->id] = $folder;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $tasklists;
|
|
|
|
}
|
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
/**
|
2014-05-21 13:04:18 +02:00
|
|
|
* Get the kolab_calendar instance for the given calendar ID
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param string $id List identifier (encoded imap folder name)
|
|
|
|
*
|
|
|
|
* @return kolab_storage_folder|null Object nor null if list doesn't exist
|
2012-06-08 14:57:16 +02:00
|
|
|
*/
|
2014-05-21 13:04:18 +02:00
|
|
|
protected function get_folder($id)
|
2012-06-08 14:57:16 +02:00
|
|
|
{
|
2016-02-12 11:50:42 +01:00
|
|
|
$this->_read_lists();
|
|
|
|
|
2014-05-21 13:04:18 +02:00
|
|
|
// create list and folder instance if necesary
|
2023-06-06 14:52:01 +02:00
|
|
|
if (empty($this->lists[$id])) {
|
2014-05-21 13:04:18 +02:00
|
|
|
$folder = kolab_storage::get_folder(kolab_storage::id_decode($id));
|
2023-06-06 14:52:01 +02:00
|
|
|
if ($folder && $folder->type) {
|
2014-05-21 13:04:18 +02:00
|
|
|
$this->folders[$id] = $folder;
|
2024-01-24 11:24:41 +01:00
|
|
|
$this->lists[$id] = $this->folder_props($folder, $this->rc->config->get('kolab_tasklists', []));
|
2014-05-21 13:04:18 +02:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|
|
|
|
|
2023-06-06 14:52:01 +02:00
|
|
|
return $this->folders[$id] ?? null;
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|
|
|
|
|
2014-05-21 13:04:18 +02:00
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
/**
|
|
|
|
* Create a new list assigned to the current user
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array $prop Hash array with list properties
|
2012-06-08 14:57:16 +02:00
|
|
|
* name: List name
|
|
|
|
* color: The color of the list
|
|
|
|
* showalarms: True if alarms are enabled
|
2024-01-25 13:47:41 +01:00
|
|
|
*
|
2012-06-08 14:57:16 +02:00
|
|
|
* @return mixed ID of the new list on success, False on error
|
|
|
|
*/
|
2014-04-09 12:05:56 +02:00
|
|
|
public function create_list(&$prop)
|
2012-06-08 14:57:16 +02:00
|
|
|
{
|
2012-11-21 12:25:14 +01:00
|
|
|
$prop['type'] = 'task' . ($prop['default'] ? '.default' : '');
|
2012-12-10 12:17:41 +01:00
|
|
|
$prop['active'] = true; // activate folder by default
|
2014-01-08 09:16:49 +01:00
|
|
|
$prop['subscribed'] = true;
|
2012-06-21 21:59:47 +02:00
|
|
|
$folder = kolab_storage::folder_update($prop);
|
|
|
|
|
|
|
|
if ($folder === false) {
|
|
|
|
$this->last_error = kolab_storage::$last_error;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create ID
|
2012-08-04 17:19:03 +02:00
|
|
|
$id = kolab_storage::folder_id($folder);
|
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
$prefs['kolab_tasklists'] = $this->rc->config->get('kolab_tasklists', []);
|
2012-08-04 17:19:03 +02:00
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
if (isset($prop['showalarms'])) {
|
2012-08-04 17:19:03 +02:00
|
|
|
$prefs['kolab_tasklists'][$id]['showalarms'] = $prop['showalarms'] ? true : false;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-08-04 17:19:03 +02:00
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
if ($prefs['kolab_tasklists'][$id]) {
|
2012-08-04 17:19:03 +02:00
|
|
|
$this->rc->user->save_prefs($prefs);
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-08-04 17:19:03 +02:00
|
|
|
|
2014-04-09 12:05:56 +02:00
|
|
|
// force page reload to properly render folder hierarchy
|
2014-05-21 13:56:44 +02:00
|
|
|
if (!empty($prop['parent'])) {
|
2014-04-09 12:05:56 +02:00
|
|
|
$prop['_reload'] = true;
|
2024-01-24 11:24:41 +01:00
|
|
|
} else {
|
2014-05-21 13:56:44 +02:00
|
|
|
$folder = kolab_storage::get_folder($folder);
|
2024-01-24 11:24:41 +01:00
|
|
|
$prop += $this->folder_props($folder, []);
|
2014-05-21 13:56:44 +02:00
|
|
|
}
|
2014-04-09 12:05:56 +02:00
|
|
|
|
2012-08-04 17:19:03 +02:00
|
|
|
return $id;
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update properties of an existing tasklist
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array $prop Hash array with list properties
|
2012-06-08 14:57:16 +02:00
|
|
|
* id: List Identifier
|
|
|
|
* name: List name
|
|
|
|
* color: The color of the list
|
|
|
|
* showalarms: True if alarms are enabled (if supported)
|
2024-01-25 13:47:41 +01:00
|
|
|
*
|
|
|
|
* @return bool True on success, Fales on failure
|
2012-06-08 14:57:16 +02:00
|
|
|
*/
|
2014-04-09 12:05:56 +02:00
|
|
|
public function edit_list(&$prop)
|
2012-06-08 14:57:16 +02:00
|
|
|
{
|
2014-05-21 13:04:18 +02:00
|
|
|
if ($prop['id'] && ($folder = $this->get_folder($prop['id']))) {
|
2012-06-21 21:59:47 +02:00
|
|
|
$prop['oldname'] = $folder->name;
|
|
|
|
$prop['type'] = 'task';
|
|
|
|
$newfolder = kolab_storage::folder_update($prop);
|
|
|
|
|
|
|
|
if ($newfolder === false) {
|
|
|
|
$this->last_error = kolab_storage::$last_error;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create ID
|
2012-08-04 17:19:03 +02:00
|
|
|
$id = kolab_storage::folder_id($newfolder);
|
|
|
|
|
|
|
|
// fallback to local prefs
|
2024-01-24 11:24:41 +01:00
|
|
|
$prefs['kolab_tasklists'] = $this->rc->config->get('kolab_tasklists', []);
|
2012-08-04 17:19:03 +02:00
|
|
|
unset($prefs['kolab_tasklists'][$prop['id']]);
|
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
if (isset($prop['showalarms'])) {
|
2012-08-04 17:19:03 +02:00
|
|
|
$prefs['kolab_tasklists'][$id]['showalarms'] = $prop['showalarms'] ? true : false;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-08-04 17:19:03 +02:00
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
if ($prefs['kolab_tasklists'][$id]) {
|
2012-08-04 17:19:03 +02:00
|
|
|
$this->rc->user->save_prefs($prefs);
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-08-04 17:19:03 +02:00
|
|
|
|
2014-04-09 12:05:56 +02:00
|
|
|
// force page reload if folder name/hierarchy changed
|
2024-01-24 11:24:41 +01:00
|
|
|
if ($newfolder != $prop['oldname']) {
|
2014-04-09 12:05:56 +02:00
|
|
|
$prop['_reload'] = true;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2014-04-09 12:05:56 +02:00
|
|
|
|
2012-08-04 17:19:03 +02:00
|
|
|
return $id;
|
2012-06-21 21:59:47 +02:00
|
|
|
}
|
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set active/subscribed state of a list
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array $prop Hash array with list properties
|
2012-06-08 14:57:16 +02:00
|
|
|
* id: List Identifier
|
|
|
|
* active: True if list is active, false if not
|
2014-05-21 13:04:18 +02:00
|
|
|
* permanent: True if list is to be subscribed permanently
|
2024-01-25 13:47:41 +01:00
|
|
|
*
|
|
|
|
* @return bool True on success, Fales on failure
|
2012-06-08 14:57:16 +02:00
|
|
|
*/
|
|
|
|
public function subscribe_list($prop)
|
|
|
|
{
|
2014-05-21 13:04:18 +02:00
|
|
|
if ($prop['id'] && ($folder = $this->get_folder($prop['id']))) {
|
|
|
|
$ret = false;
|
2024-01-24 11:24:41 +01:00
|
|
|
if (isset($prop['permanent'])) {
|
2014-05-21 13:04:18 +02:00
|
|
|
$ret |= $folder->subscribe(intval($prop['permanent']));
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
|
|
|
if (isset($prop['active'])) {
|
2014-05-21 13:04:18 +02:00
|
|
|
$ret |= $folder->activate(intval($prop['active']));
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2014-09-09 13:29:28 +02:00
|
|
|
|
|
|
|
// apply to child folders, too
|
2023-06-06 14:52:01 +02:00
|
|
|
if (!empty($prop['recursive'])) {
|
|
|
|
foreach ((array) kolab_storage::list_folders($folder->name, '*', 'task') as $subfolder) {
|
2024-01-24 11:24:41 +01:00
|
|
|
if (isset($prop['permanent'])) {
|
2014-09-09 13:29:28 +02:00
|
|
|
($prop['permanent'] ? kolab_storage::folder_subscribe($subfolder) : kolab_storage::folder_unsubscribe($subfolder));
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
|
|
|
if (isset($prop['active'])) {
|
2014-09-09 13:29:28 +02:00
|
|
|
($prop['active'] ? kolab_storage::folder_activate($subfolder) : kolab_storage::folder_deactivate($subfolder));
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2014-09-09 13:29:28 +02:00
|
|
|
}
|
|
|
|
}
|
2016-02-12 11:50:42 +01:00
|
|
|
|
2014-05-21 13:04:18 +02:00
|
|
|
return $ret;
|
2012-06-21 21:59:47 +02:00
|
|
|
}
|
2016-02-12 11:50:42 +01:00
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete the given list with all its contents
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array $prop Hash array with list properties
|
2012-06-08 14:57:16 +02:00
|
|
|
* id: list Identifier
|
2024-01-25 13:47:41 +01:00
|
|
|
* @return bool True on success, Fales on failure
|
2012-06-08 14:57:16 +02:00
|
|
|
*/
|
2014-09-09 13:29:28 +02:00
|
|
|
public function delete_list($prop)
|
2012-06-08 14:57:16 +02:00
|
|
|
{
|
2014-05-21 13:04:18 +02:00
|
|
|
if ($prop['id'] && ($folder = $this->get_folder($prop['id']))) {
|
2016-02-12 11:50:42 +01:00
|
|
|
if (kolab_storage::folder_delete($folder->name)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->last_error = kolab_storage::$last_error;
|
2012-06-21 21:59:47 +02:00
|
|
|
}
|
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-05-21 13:04:18 +02:00
|
|
|
/**
|
|
|
|
* Search for shared or otherwise not listed tasklists the user has access
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param string $query Search string
|
|
|
|
* @param string $source Section/source to search
|
|
|
|
*
|
2014-05-21 13:04:18 +02:00
|
|
|
* @return array List of tasklists
|
|
|
|
*/
|
|
|
|
public function search_lists($query, $source)
|
|
|
|
{
|
|
|
|
if (!kolab_storage::setup()) {
|
2024-01-24 11:24:41 +01:00
|
|
|
return [];
|
2014-05-21 13:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$this->search_more_results = false;
|
2024-01-24 11:24:41 +01:00
|
|
|
$this->lists = $this->folders = [];
|
2014-05-21 13:04:18 +02:00
|
|
|
|
|
|
|
// find unsubscribed IMAP folders that have "event" type
|
|
|
|
if ($source == 'folders') {
|
2024-01-24 11:24:41 +01:00
|
|
|
foreach ((array)kolab_storage::search_folders('task', $query, ['other']) as $folder) {
|
2014-05-21 13:04:18 +02:00
|
|
|
$this->folders[$folder->id] = $folder;
|
2024-01-24 11:24:41 +01:00
|
|
|
$this->lists[$folder->id] = $this->folder_props($folder, []);
|
2014-05-21 13:04:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// search other user's namespace via LDAP
|
2024-01-24 11:24:41 +01:00
|
|
|
elseif ($source == 'users') {
|
2014-05-21 13:04:18 +02:00
|
|
|
$limit = $this->rc->config->get('autocomplete_max', 15) * 2; // we have slightly more space, so display twice the number
|
2024-01-24 11:24:41 +01:00
|
|
|
foreach (kolab_storage::search_users($query, 0, [], $limit * 10) as $user) {
|
|
|
|
$folders = [];
|
2014-05-21 13:04:18 +02:00
|
|
|
// search for tasks folders shared by this user
|
|
|
|
foreach (kolab_storage::list_user_folders($user, 'task', false) as $foldername) {
|
|
|
|
$folders[] = new kolab_storage_folder($foldername, 'task');
|
|
|
|
}
|
|
|
|
|
2024-01-24 10:59:25 +01:00
|
|
|
$count = 0;
|
2014-05-21 13:04:18 +02:00
|
|
|
if (count($folders)) {
|
|
|
|
$userfolder = new kolab_storage_folder_user($user['kolabtargetfolder'], '', $user);
|
|
|
|
$this->folders[$userfolder->id] = $userfolder;
|
2024-01-24 11:24:41 +01:00
|
|
|
$this->lists[$userfolder->id] = $this->folder_props($userfolder, []);
|
2014-05-21 13:04:18 +02:00
|
|
|
|
|
|
|
foreach ($folders as $folder) {
|
|
|
|
$this->folders[$folder->id] = $folder;
|
2024-01-24 11:24:41 +01:00
|
|
|
$this->lists[$folder->id] = $this->folder_props($folder, []);
|
2014-05-21 13:04:18 +02:00
|
|
|
$count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($count >= $limit) {
|
|
|
|
$this->search_more_results = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->get_lists();
|
|
|
|
}
|
|
|
|
|
2014-08-26 12:12:03 +02:00
|
|
|
/**
|
|
|
|
* Get a list of tags to assign tasks to
|
|
|
|
*
|
|
|
|
* @return array List of tags
|
|
|
|
*/
|
|
|
|
public function get_tags()
|
|
|
|
{
|
|
|
|
$config = kolab_storage_config::get_instance();
|
|
|
|
$tags = $config->get_tags();
|
2024-01-24 11:24:41 +01:00
|
|
|
$backend_tags = array_map(function ($v) { return $v['name']; }, $tags);
|
2014-08-26 12:12:03 +02:00
|
|
|
|
|
|
|
return array_values(array_unique(array_merge($this->tags, $backend_tags)));
|
|
|
|
}
|
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
/**
|
|
|
|
* Get number of tasks matching the given filter
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array $lists List of lists to count tasks of
|
|
|
|
*
|
2012-06-08 14:57:16 +02:00
|
|
|
* @return array Hash array with counts grouped by status (all|flagged|completed|today|tomorrow|nodate)
|
|
|
|
*/
|
|
|
|
public function count_tasks($lists = null)
|
|
|
|
{
|
2016-02-12 11:50:42 +01:00
|
|
|
if (empty($lists)) {
|
|
|
|
$lists = $this->_read_lists();
|
|
|
|
$lists = array_keys($lists);
|
2024-01-24 11:24:41 +01:00
|
|
|
} elseif (is_string($lists)) {
|
2012-06-08 14:57:16 +02:00
|
|
|
$lists = explode(',', $lists);
|
2016-02-12 11:50:42 +01:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
|
2016-02-12 11:50:42 +01:00
|
|
|
$today_date = new DateTime('now', $this->plugin->timezone);
|
|
|
|
$today = $today_date->format('Y-m-d');
|
2012-06-08 14:57:16 +02:00
|
|
|
$tomorrow_date = new DateTime('now + 1 day', $this->plugin->timezone);
|
2016-02-12 11:50:42 +01:00
|
|
|
$tomorrow = $tomorrow_date->format('Y-m-d');
|
2012-06-08 14:57:16 +02:00
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
$counts = ['all' => 0, 'today' => 0, 'tomorrow' => 0, 'later' => 0, 'overdue' => 0];
|
2016-02-12 11:50:42 +01:00
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
foreach ($lists as $list_id) {
|
2014-05-21 13:04:18 +02:00
|
|
|
if (!$folder = $this->get_folder($list_id)) {
|
|
|
|
continue;
|
|
|
|
}
|
2016-02-12 11:50:42 +01:00
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
foreach ($folder->select([['tags','!~','x-complete']], true) as $record) {
|
2014-11-27 07:11:54 -05:00
|
|
|
$rec = $this->_to_rcube_task($record, $list_id, false);
|
2012-06-08 14:57:16 +02:00
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
if ($this->is_complete($rec)) { // don't count complete tasks
|
2012-06-08 14:57:16 +02:00
|
|
|
continue;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
|
|
|
|
$counts['all']++;
|
2024-01-24 11:24:41 +01:00
|
|
|
if (empty($rec['date'])) {
|
2016-10-03 15:58:09 +02:00
|
|
|
$counts['later']++;
|
2024-01-24 11:24:41 +01:00
|
|
|
} elseif ($rec['date'] == $today) {
|
2012-06-08 14:57:16 +02:00
|
|
|
$counts['today']++;
|
2024-01-24 11:24:41 +01:00
|
|
|
} elseif ($rec['date'] == $tomorrow) {
|
2012-06-08 14:57:16 +02:00
|
|
|
$counts['tomorrow']++;
|
2024-01-24 11:24:41 +01:00
|
|
|
} elseif ($rec['date'] < $today) {
|
2012-06-08 14:57:16 +02:00
|
|
|
$counts['overdue']++;
|
2024-01-24 11:24:41 +01:00
|
|
|
} elseif ($rec['date'] > $tomorrow) {
|
2016-10-03 15:58:09 +02:00
|
|
|
$counts['later']++;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-22 19:00:49 +02:00
|
|
|
// avoid session race conditions that will loose temporary subscriptions
|
2014-05-27 19:52:03 +02:00
|
|
|
$this->plugin->rc->session->nowrite = true;
|
2014-05-22 19:00:49 +02:00
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
return $counts;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-12-17 15:57:29 +01:00
|
|
|
* Get all task records matching the given filter
|
2012-06-08 14:57:16 +02:00
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array $filter Hash array with filter criterias:
|
2012-06-08 14:57:16 +02:00
|
|
|
* - mask: Bitmask representing the filter selection (check against tasklist::FILTER_MASK_* constants)
|
|
|
|
* - from: Date range start as string (Y-m-d)
|
|
|
|
* - to: Date range end as string (Y-m-d)
|
|
|
|
* - search: Search query string
|
2016-06-20 10:09:36 +02:00
|
|
|
* - uid: Task UIDs
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array $lists List of lists to get tasks from
|
|
|
|
*
|
2012-06-08 14:57:16 +02:00
|
|
|
* @return array List of tasks records matchin the criteria
|
|
|
|
*/
|
|
|
|
public function list_tasks($filter, $lists = null)
|
|
|
|
{
|
2016-02-12 11:50:42 +01:00
|
|
|
if (empty($lists)) {
|
|
|
|
$lists = $this->_read_lists();
|
|
|
|
$lists = array_keys($lists);
|
2024-01-24 11:24:41 +01:00
|
|
|
} elseif (is_string($lists)) {
|
2012-06-08 14:57:16 +02:00
|
|
|
$lists = explode(',', $lists);
|
2016-02-12 11:50:42 +01:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
|
2016-02-13 17:35:29 +01:00
|
|
|
$config = kolab_storage_config::get_instance();
|
2024-01-24 11:24:41 +01:00
|
|
|
$results = [];
|
2012-06-08 14:57:16 +02:00
|
|
|
|
|
|
|
// query Kolab storage
|
2024-01-24 11:24:41 +01:00
|
|
|
$query = [];
|
|
|
|
if (!empty($filter['mask']) && $filter['mask'] & tasklist::FILTER_MASK_COMPLETE) {
|
|
|
|
$query[] = ['tags','~','x-complete'];
|
|
|
|
} elseif (empty($filter['since'])) {
|
|
|
|
$query[] = ['tags','!~','x-complete'];
|
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
|
|
|
|
// full text search (only works with cache enabled)
|
2023-06-06 14:52:01 +02:00
|
|
|
if (!empty($filter['search'])) {
|
2012-06-08 14:57:16 +02:00
|
|
|
$search = mb_strtolower($filter['search']);
|
|
|
|
foreach (rcube_utils::normalize_string($search, true) as $word) {
|
2024-01-24 11:24:41 +01:00
|
|
|
$query[] = ['words', '~', $word];
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-18 16:38:22 +01:00
|
|
|
if (!empty($filter['since'])) {
|
2024-01-24 11:24:41 +01:00
|
|
|
$query[] = ['changed', '>=', $filter['since']];
|
2013-10-21 16:38:22 +02:00
|
|
|
}
|
|
|
|
|
2023-01-18 16:38:22 +01:00
|
|
|
if (!empty($filter['uid'])) {
|
2024-01-24 11:24:41 +01:00
|
|
|
$query[] = ['uid', '=', (array) $filter['uid']];
|
2016-06-20 10:09:36 +02:00
|
|
|
}
|
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
foreach ($lists as $list_id) {
|
2014-05-21 13:04:18 +02:00
|
|
|
if (!$folder = $this->get_folder($list_id)) {
|
|
|
|
continue;
|
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
|
2016-02-13 17:35:29 +01:00
|
|
|
foreach ($folder->select($query) as $record) {
|
2012-06-08 14:57:16 +02:00
|
|
|
// TODO: post-filter tasks returned from storage
|
2016-02-13 17:35:29 +01:00
|
|
|
$record['list_id'] = $list_id;
|
|
|
|
$results[] = $record;
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-24 12:54:08 +01:00
|
|
|
$config->apply_tags($results, true);
|
2016-02-21 19:41:49 +01:00
|
|
|
$config->apply_links($results);
|
2016-02-13 17:35:29 +01:00
|
|
|
|
|
|
|
foreach (array_keys($results) as $idx) {
|
|
|
|
$results[$idx] = $this->_to_rcube_task($results[$idx], $results[$idx]['list_id']);
|
|
|
|
}
|
|
|
|
|
2014-05-22 19:00:49 +02:00
|
|
|
// avoid session race conditions that will loose temporary subscriptions
|
2014-05-27 19:52:03 +02:00
|
|
|
$this->plugin->rc->session->nowrite = true;
|
2014-05-22 19:00:49 +02:00
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return data of a specific task
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param mixed $prop Hash array with task properties or task UID
|
|
|
|
* @param int $filter Bitmask defining filter criterias for folders.
|
|
|
|
* See FILTER_* constants for possible values.
|
2016-12-27 04:03:28 -05:00
|
|
|
*
|
2012-06-08 14:57:16 +02:00
|
|
|
* @return array Hash array with task properties or false if not found
|
|
|
|
*/
|
2016-12-27 04:03:28 -05:00
|
|
|
public function get_task($prop, $filter = 0)
|
2012-06-08 14:57:16 +02:00
|
|
|
{
|
2014-11-04 10:56:19 +01:00
|
|
|
$this->_parse_id($prop);
|
2016-02-12 11:50:42 +01:00
|
|
|
|
2014-11-04 10:56:19 +01:00
|
|
|
$id = $prop['uid'];
|
|
|
|
$list_id = $prop['list'];
|
2024-01-24 11:24:41 +01:00
|
|
|
$folders = $list_id ? [$list_id => $this->get_folder($list_id)] : $this->get_lists($filter);
|
2012-06-08 14:57:16 +02:00
|
|
|
|
|
|
|
// find task in the available folders
|
2012-09-26 12:14:42 +02:00
|
|
|
foreach ($folders as $list_id => $folder) {
|
2024-01-24 11:24:41 +01:00
|
|
|
if (is_array($folder)) {
|
2016-12-27 04:03:28 -05:00
|
|
|
$folder = $this->folders[$list_id];
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
|
|
|
if (is_numeric($list_id) || !$folder) {
|
2012-09-26 12:14:42 +02:00
|
|
|
continue;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2023-01-18 16:38:22 +01:00
|
|
|
if (empty($this->tasks[$id]) && ($object = $folder->get_object($id))) {
|
2014-08-26 12:12:03 +02:00
|
|
|
$this->load_tags($object);
|
2014-11-04 10:56:19 +01:00
|
|
|
$this->tasks[$id] = $this->_to_rcube_task($object, $list_id);
|
2012-06-08 14:57:16 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->tasks[$id];
|
|
|
|
}
|
|
|
|
|
2012-09-26 12:14:42 +02:00
|
|
|
/**
|
|
|
|
* Get all decendents of the given task record
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param mixed $prop Hash array with task properties or task UID
|
|
|
|
* @param bool $recursive True if all childrens children should be fetched
|
|
|
|
*
|
2012-09-26 12:14:42 +02:00
|
|
|
* @return array List of all child task IDs
|
|
|
|
*/
|
|
|
|
public function get_childs($prop, $recursive = false)
|
|
|
|
{
|
|
|
|
if (is_string($prop)) {
|
|
|
|
$task = $this->get_task($prop);
|
2024-01-24 11:24:41 +01:00
|
|
|
$prop = ['uid' => $task['uid'], 'list' => $task['list']];
|
|
|
|
} else {
|
2014-11-04 10:56:19 +01:00
|
|
|
$this->_parse_id($prop);
|
2012-09-26 12:14:42 +02:00
|
|
|
}
|
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
$childs = [];
|
2012-09-26 12:14:42 +02:00
|
|
|
$list_id = $prop['list'];
|
2024-01-24 11:24:41 +01:00
|
|
|
$task_ids = [$prop['uid']];
|
2014-05-21 13:04:18 +02:00
|
|
|
$folder = $this->get_folder($list_id);
|
2012-09-26 12:14:42 +02:00
|
|
|
|
|
|
|
// query for childs (recursively)
|
|
|
|
while ($folder && !empty($task_ids)) {
|
2024-01-24 11:24:41 +01:00
|
|
|
$query_ids = [];
|
2012-09-26 12:14:42 +02:00
|
|
|
foreach ($task_ids as $task_id) {
|
2024-01-24 11:24:41 +01:00
|
|
|
$query = [['tags','=','x-parent:' . $task_id]];
|
2014-02-06 17:30:40 +01:00
|
|
|
foreach ($folder->select($query) as $record) {
|
2012-09-26 12:14:42 +02:00
|
|
|
// don't rely on kolab_storage_folder filtering
|
|
|
|
if ($record['parent_id'] == $task_id) {
|
2014-11-04 10:56:19 +01:00
|
|
|
$childs[] = $list_id . ':' . $record['uid'];
|
2012-09-26 12:14:42 +02:00
|
|
|
$query_ids[] = $record['uid'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
if (!$recursive) {
|
2012-09-26 12:14:42 +02:00
|
|
|
break;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-09-26 12:14:42 +02:00
|
|
|
|
|
|
|
$task_ids = $query_ids;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $childs;
|
|
|
|
}
|
|
|
|
|
2015-03-25 11:59:10 +01:00
|
|
|
/**
|
|
|
|
* Provide a list of revisions for the given task
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array $prop Hash array with task properties
|
|
|
|
*
|
2015-03-25 11:59:10 +01:00
|
|
|
* @return array List of changes, each as a hash array
|
|
|
|
* @see tasklist_driver::get_task_changelog()
|
|
|
|
*/
|
|
|
|
public function get_task_changelog($prop)
|
|
|
|
{
|
|
|
|
if (empty($this->bonnie_api)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
[$uid, $mailbox, $msguid] = $this->_resolve_task_identity($prop);
|
2015-03-25 11:59:10 +01:00
|
|
|
|
|
|
|
$result = $uid && $mailbox ? $this->bonnie_api->changelog('task', $uid, $mailbox, $msguid) : null;
|
|
|
|
if (is_array($result) && $result['uid'] == $uid) {
|
|
|
|
return $result['changes'];
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return full data of a specific revision of an event
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param mixed $prop UID string or hash array with task properties
|
|
|
|
* @param mixed $rev Revision number
|
2015-03-25 11:59:10 +01:00
|
|
|
*
|
|
|
|
* @return array Task object as hash array
|
|
|
|
* @see tasklist_driver::get_task_revision()
|
|
|
|
*/
|
|
|
|
public function get_task_revison($prop, $rev)
|
|
|
|
{
|
|
|
|
if (empty($this->bonnie_api)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->_parse_id($prop);
|
|
|
|
$uid = $prop['uid'];
|
|
|
|
$list_id = $prop['list'];
|
2024-01-24 11:24:41 +01:00
|
|
|
[$uid, $mailbox, $msguid] = $this->_resolve_task_identity($prop);
|
2015-03-25 11:59:10 +01:00
|
|
|
|
|
|
|
// call Bonnie API
|
|
|
|
$result = $this->bonnie_api->get('task', $uid, $rev, $mailbox, $msguid);
|
|
|
|
if (is_array($result) && $result['uid'] == $uid && !empty($result['xml'])) {
|
|
|
|
$format = kolab_format::factory('task');
|
|
|
|
$format->load($result['xml']);
|
|
|
|
$rec = $format->to_array();
|
|
|
|
$format->get_attachments($rec, true);
|
|
|
|
|
|
|
|
if ($format->is_valid()) {
|
2015-03-25 16:37:04 +01:00
|
|
|
$rec = self::_to_rcube_task($rec, $list_id, false);
|
2015-03-25 11:59:10 +01:00
|
|
|
$rec['rev'] = $result['rev'];
|
2015-03-25 16:37:04 +01:00
|
|
|
return $rec;
|
2015-03-25 11:59:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Command the backend to restore a certain revision of a task.
|
|
|
|
* This shall replace the current object with an older version.
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param mixed $prop UID string or hash array with task properties
|
|
|
|
* @param mixed $rev Revision number
|
2015-03-25 11:59:10 +01:00
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @return bool True on success, False on failure
|
2015-03-25 11:59:10 +01:00
|
|
|
* @see tasklist_driver::restore_task_revision()
|
|
|
|
*/
|
|
|
|
public function restore_task_revision($prop, $rev)
|
|
|
|
{
|
|
|
|
if (empty($this->bonnie_api)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->_parse_id($prop);
|
|
|
|
$uid = $prop['uid'];
|
|
|
|
$list_id = $prop['list'];
|
2024-01-24 11:24:41 +01:00
|
|
|
[$uid, $mailbox, $msguid] = $this->_resolve_task_identity($prop);
|
2015-03-25 11:59:10 +01:00
|
|
|
|
2016-02-12 11:50:42 +01:00
|
|
|
$folder = $this->get_folder($list_id);
|
2015-03-25 11:59:10 +01:00
|
|
|
$success = false;
|
|
|
|
|
|
|
|
if ($folder && ($raw_msg = $this->bonnie_api->rawdata('task', $uid, $rev, $mailbox))) {
|
|
|
|
$imap = $this->rc->get_storage();
|
|
|
|
|
|
|
|
// insert $raw_msg as new message
|
|
|
|
if ($imap->save_message($folder->name, $raw_msg, null, false)) {
|
|
|
|
$success = true;
|
|
|
|
|
|
|
|
// delete old revision from imap and cache
|
|
|
|
$imap->delete_message($msguid, $folder->name);
|
|
|
|
$folder->cache->set($msguid, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $success;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a list of property changes beteen two revisions of a task object
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array $prop Hash array with task properties
|
|
|
|
* @param mixed $rev1 Revision "from"
|
|
|
|
* @param mixed $rev2 Revision "to"
|
2015-03-25 11:59:10 +01:00
|
|
|
*
|
|
|
|
* @return array List of property changes, each as a hash array
|
|
|
|
* @see tasklist_driver::get_task_diff()
|
|
|
|
*/
|
|
|
|
public function get_task_diff($prop, $rev1, $rev2)
|
|
|
|
{
|
|
|
|
$this->_parse_id($prop);
|
|
|
|
$uid = $prop['uid'];
|
|
|
|
$list_id = $prop['list'];
|
2024-01-24 11:24:41 +01:00
|
|
|
[$uid, $mailbox, $msguid] = $this->_resolve_task_identity($prop);
|
2015-03-25 11:59:10 +01:00
|
|
|
|
|
|
|
// call Bonnie API
|
2024-01-24 10:59:25 +01:00
|
|
|
$result = $this->bonnie_api->diff('task', $uid, $rev1, $rev2, $mailbox, $msguid);
|
2015-03-25 11:59:10 +01:00
|
|
|
if (is_array($result) && $result['uid'] == $uid) {
|
|
|
|
$result['rev1'] = $rev1;
|
|
|
|
$result['rev2'] = $rev2;
|
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
$keymap = [
|
2015-03-25 11:59:10 +01:00
|
|
|
'start' => 'start',
|
|
|
|
'due' => 'date',
|
|
|
|
'dstamp' => 'changed',
|
|
|
|
'summary' => 'title',
|
|
|
|
'alarm' => 'alarms',
|
|
|
|
'attendee' => 'attendees',
|
|
|
|
'attach' => 'attachments',
|
|
|
|
'rrule' => 'recurrence',
|
2015-03-25 16:26:13 +01:00
|
|
|
'related-to' => 'parent_id',
|
2015-03-25 11:59:10 +01:00
|
|
|
'percent-complete' => 'complete',
|
|
|
|
'lastmodified-date' => 'changed',
|
2024-01-24 11:24:41 +01:00
|
|
|
];
|
|
|
|
$prop_keymaps = [
|
|
|
|
'attachments' => ['fmttype' => 'mimetype', 'label' => 'name'],
|
|
|
|
'attendees' => ['partstat' => 'status'],
|
|
|
|
];
|
|
|
|
$special_changes = [];
|
2015-03-25 11:59:10 +01:00
|
|
|
|
|
|
|
// map kolab event properties to keys the client expects
|
2024-01-24 11:24:41 +01:00
|
|
|
array_walk($result['changes'], function (&$change, $i) use ($keymap, $prop_keymaps, $special_changes) {
|
2015-03-25 11:59:10 +01:00
|
|
|
if (array_key_exists($change['property'], $keymap)) {
|
|
|
|
$change['property'] = $keymap[$change['property']];
|
|
|
|
}
|
|
|
|
if ($change['property'] == 'priority') {
|
|
|
|
$change['property'] = 'flagged';
|
|
|
|
$change['old'] = $change['old'] == 1 ? $this->plugin->gettext('yes') : null;
|
|
|
|
$change['new'] = $change['new'] == 1 ? $this->plugin->gettext('yes') : null;
|
|
|
|
}
|
|
|
|
// map alarms trigger value
|
|
|
|
if ($change['property'] == 'alarms') {
|
2024-01-24 11:24:41 +01:00
|
|
|
if (is_array($change['old']) && is_array($change['old']['trigger'])) {
|
2015-03-25 11:59:10 +01:00
|
|
|
$change['old']['trigger'] = $change['old']['trigger']['value'];
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
|
|
|
if (is_array($change['new']) && is_array($change['new']['trigger'])) {
|
2015-03-25 11:59:10 +01:00
|
|
|
$change['new']['trigger'] = $change['new']['trigger']['value'];
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2015-03-25 11:59:10 +01:00
|
|
|
}
|
|
|
|
// make all property keys uppercase
|
|
|
|
if ($change['property'] == 'recurrence') {
|
|
|
|
$special_changes['recurrence'] = $i;
|
2024-01-24 11:24:41 +01:00
|
|
|
foreach (['old','new'] as $m) {
|
2015-03-25 11:59:10 +01:00
|
|
|
if (is_array($change[$m])) {
|
2024-01-24 11:24:41 +01:00
|
|
|
$props = [];
|
2015-03-25 11:59:10 +01:00
|
|
|
foreach ($change[$m] as $k => $v) {
|
|
|
|
$props[strtoupper($k)] = $v;
|
|
|
|
}
|
|
|
|
$change[$m] = $props;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// map property keys names
|
|
|
|
if (is_array($prop_keymaps[$change['property']])) {
|
2024-01-24 11:24:41 +01:00
|
|
|
foreach ($prop_keymaps[$change['property']] as $k => $dest) {
|
|
|
|
if (is_array($change['old']) && array_key_exists($k, $change['old'])) {
|
|
|
|
$change['old'][$dest] = $change['old'][$k];
|
|
|
|
unset($change['old'][$k]);
|
|
|
|
}
|
|
|
|
if (is_array($change['new']) && array_key_exists($k, $change['new'])) {
|
|
|
|
$change['new'][$dest] = $change['new'][$k];
|
|
|
|
unset($change['new'][$k]);
|
|
|
|
}
|
2015-03-25 11:59:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($change['property'] == 'exdate') {
|
|
|
|
$special_changes['exdate'] = $i;
|
2024-01-24 11:24:41 +01:00
|
|
|
} elseif ($change['property'] == 'rdate') {
|
2015-03-25 11:59:10 +01:00
|
|
|
$special_changes['rdate'] = $i;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// merge some recurrence changes
|
2024-01-24 11:24:41 +01:00
|
|
|
foreach (['exdate','rdate'] as $prop) {
|
2015-03-25 11:59:10 +01:00
|
|
|
if (array_key_exists($prop, $special_changes)) {
|
|
|
|
$exdate = $result['changes'][$special_changes[$prop]];
|
|
|
|
if (array_key_exists('recurrence', $special_changes)) {
|
|
|
|
$recurrence = &$result['changes'][$special_changes['recurrence']];
|
2024-01-24 11:24:41 +01:00
|
|
|
} else {
|
2015-03-25 11:59:10 +01:00
|
|
|
$i = count($result['changes']);
|
2024-01-24 11:24:41 +01:00
|
|
|
$result['changes'][$i] = ['property' => 'recurrence', 'old' => [], 'new' => []];
|
2015-03-25 11:59:10 +01:00
|
|
|
$recurrence = &$result['changes'][$i]['recurrence'];
|
|
|
|
}
|
|
|
|
$key = strtoupper($prop);
|
|
|
|
$recurrence['old'][$key] = $exdate['old'];
|
|
|
|
$recurrence['new'][$key] = $exdate['new'];
|
|
|
|
unset($result['changes'][$special_changes[$prop]]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper method to resolved the given task identifier into uid and folder
|
|
|
|
*
|
|
|
|
* @return array (uid,folder,msguid) tuple
|
|
|
|
*/
|
|
|
|
private function _resolve_task_identity($prop)
|
|
|
|
{
|
2015-03-26 14:57:16 +01:00
|
|
|
$mailbox = $msguid = null;
|
2015-03-25 11:59:10 +01:00
|
|
|
|
2015-03-26 14:57:16 +01:00
|
|
|
$this->_parse_id($prop);
|
|
|
|
$uid = $prop['uid'];
|
|
|
|
$list_id = $prop['list'];
|
2015-03-25 11:59:10 +01:00
|
|
|
|
2015-03-26 14:57:16 +01:00
|
|
|
if ($folder = $this->get_folder($list_id)) {
|
|
|
|
$mailbox = $folder->get_mailbox_id();
|
2015-03-25 11:59:10 +01:00
|
|
|
|
2015-03-26 14:57:16 +01:00
|
|
|
// get task object from storage in order to get the real object uid an msguid
|
|
|
|
if ($rec = $folder->get_object($uid)) {
|
|
|
|
$msguid = $rec['_msguid'];
|
|
|
|
$uid = $rec['uid'];
|
|
|
|
}
|
|
|
|
}
|
2015-03-25 11:59:10 +01:00
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
return [$uid, $mailbox, $msguid];
|
2015-03-25 11:59:10 +01:00
|
|
|
}
|
|
|
|
|
2012-08-03 21:34:00 +02:00
|
|
|
/**
|
|
|
|
* Get a list of pending alarms to be displayed to the user
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param int $time Current time (unix timestamp)
|
|
|
|
* @param mixed $lists List of list IDs to show alarms for (either as array or comma-separated string)
|
|
|
|
*
|
|
|
|
* @return array A list of alarms, each encoded as hash array with task properties
|
2012-08-03 21:34:00 +02:00
|
|
|
* @see tasklist_driver::pending_alarms()
|
|
|
|
*/
|
|
|
|
public function pending_alarms($time, $lists = null)
|
|
|
|
{
|
2012-08-04 17:19:03 +02:00
|
|
|
$interval = 300;
|
|
|
|
$time -= $time % 60;
|
|
|
|
|
|
|
|
$slot = $time;
|
|
|
|
$slot -= $slot % $interval;
|
|
|
|
|
2012-11-17 09:49:57 +01:00
|
|
|
$last = $time - max(60, $this->rc->config->get('refresh_interval', 0));
|
2012-08-04 17:19:03 +02:00
|
|
|
$last -= $last % $interval;
|
|
|
|
|
|
|
|
// only check for alerts once in 5 minutes
|
2024-01-24 11:24:41 +01:00
|
|
|
if ($last == $slot) {
|
|
|
|
return [];
|
|
|
|
}
|
2012-08-04 17:19:03 +02:00
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
if ($lists && is_string($lists)) {
|
2012-08-04 17:19:03 +02:00
|
|
|
$lists = explode(',', $lists);
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-08-04 17:19:03 +02:00
|
|
|
|
|
|
|
$time = $slot + $interval;
|
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
$dbdata = [];
|
|
|
|
$candidates = [];
|
|
|
|
$query = [
|
|
|
|
['tags', '=', 'x-has-alarms'],
|
|
|
|
['tags', '!=', 'x-complete'],
|
|
|
|
];
|
2016-02-12 11:50:42 +01:00
|
|
|
|
|
|
|
$this->_read_lists();
|
|
|
|
|
2012-08-04 17:19:03 +02:00
|
|
|
foreach ($this->lists as $lid => $list) {
|
|
|
|
// skip lists with alarms disabled
|
2024-01-24 11:24:41 +01:00
|
|
|
if (!$list['showalarms'] || ($lists && !in_array($lid, $lists))) {
|
2012-08-04 17:19:03 +02:00
|
|
|
continue;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-08-04 17:19:03 +02:00
|
|
|
|
2014-05-21 13:04:18 +02:00
|
|
|
$folder = $this->get_folder($lid);
|
2014-02-06 17:30:40 +01:00
|
|
|
foreach ($folder->select($query) as $record) {
|
2024-01-24 11:24:41 +01:00
|
|
|
if (!($record['valarms'] || $record['alarms']) || $record['status'] == 'COMPLETED' || $record['complete'] == 100) { // don't trust query :-)
|
2012-08-04 17:19:03 +02:00
|
|
|
continue;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-08-04 17:19:03 +02:00
|
|
|
|
2014-11-27 07:11:54 -05:00
|
|
|
$task = $this->_to_rcube_task($record, $lid, false);
|
2012-08-04 17:19:03 +02:00
|
|
|
|
|
|
|
// add to list if alarm is set
|
2012-08-16 08:57:25 +02:00
|
|
|
$alarm = libcalendaring::get_next_alarm($task, 'task');
|
2014-04-17 17:49:00 +02:00
|
|
|
if ($alarm && $alarm['time'] && $alarm['time'] <= $time && in_array($alarm['action'], $this->alarm_types)) {
|
|
|
|
$id = $alarm['id']; // use alarm-id as primary identifier
|
2024-01-24 11:24:41 +01:00
|
|
|
$candidates[$id] = [
|
2014-04-17 17:49:00 +02:00
|
|
|
'id' => $id,
|
|
|
|
'title' => $task['title'],
|
|
|
|
'date' => $task['date'],
|
|
|
|
'time' => $task['time'],
|
|
|
|
'notifyat' => $alarm['time'],
|
|
|
|
'action' => $alarm['action'],
|
2024-01-24 11:24:41 +01:00
|
|
|
];
|
2012-08-04 17:19:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// get alarm information stored in local database
|
2014-04-17 17:49:00 +02:00
|
|
|
if (!empty($candidates)) {
|
2024-01-24 11:24:41 +01:00
|
|
|
$alarm_ids = array_map([$this->rc->db, 'quote'], array_keys($candidates));
|
|
|
|
$result = $this->rc->db->query(
|
|
|
|
"SELECT *"
|
2014-09-15 12:23:46 +02:00
|
|
|
. " FROM " . $this->rc->db->table_name('kolab_alarms', true)
|
2024-01-24 11:24:41 +01:00
|
|
|
. " WHERE `alarm_id` IN (" . implode(',', $alarm_ids) . ")"
|
2014-09-15 12:23:46 +02:00
|
|
|
. " AND `user_id` = ?",
|
2012-08-04 17:19:03 +02:00
|
|
|
$this->rc->user->ID
|
|
|
|
);
|
|
|
|
|
|
|
|
while ($result && ($rec = $this->rc->db->fetch_assoc($result))) {
|
2014-04-17 17:49:00 +02:00
|
|
|
$dbdata[$rec['alarm_id']] = $rec;
|
2012-08-04 17:19:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
$alarms = [];
|
2014-04-17 17:49:00 +02:00
|
|
|
foreach ($candidates as $id => $task) {
|
2024-01-24 11:24:41 +01:00
|
|
|
// skip dismissed
|
|
|
|
if (!empty($dbdata[$id]['dismissed'])) {
|
|
|
|
continue;
|
|
|
|
}
|
2012-08-04 17:19:03 +02:00
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
// snooze function may have shifted alarm time
|
|
|
|
$notifyat = !empty($dbdata[$id]['notifyat']) ? strtotime($dbdata[$id]['notifyat']) : $task['notifyat'];
|
|
|
|
if ($notifyat <= $time) {
|
|
|
|
$alarms[] = $task;
|
|
|
|
}
|
2012-08-04 17:19:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $alarms;
|
2012-08-03 21:34:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* (User) feedback after showing an alarm notification
|
|
|
|
* This should mark the alarm as 'shown' or snooze it for the given amount of time
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param string $id Task identifier
|
|
|
|
* @param int $snooze Suspend the alarm for this number of seconds
|
2012-08-03 21:34:00 +02:00
|
|
|
*/
|
|
|
|
public function dismiss_alarm($id, $snooze = 0)
|
|
|
|
{
|
2012-08-04 17:19:03 +02:00
|
|
|
// delete old alarm entry
|
|
|
|
$this->rc->db->query(
|
2014-09-15 12:23:46 +02:00
|
|
|
"DELETE FROM " . $this->rc->db->table_name('kolab_alarms', true) . "
|
|
|
|
WHERE `alarm_id` = ? AND `user_id` = ?",
|
2012-08-04 17:19:03 +02:00
|
|
|
$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(
|
2014-09-15 12:23:46 +02:00
|
|
|
"INSERT INTO " . $this->rc->db->table_name('kolab_alarms', true) . "
|
|
|
|
(`alarm_id`, `user_id`, `dismissed`, `notifyat`)
|
|
|
|
VALUES (?, ?, ?, ?)",
|
2012-08-04 17:19:03 +02:00
|
|
|
$id,
|
|
|
|
$this->rc->user->ID,
|
|
|
|
$snooze > 0 ? 0 : 1,
|
|
|
|
$notifyat
|
|
|
|
);
|
|
|
|
|
|
|
|
return $this->rc->db->affected_rows($query);
|
2012-08-03 21:34:00 +02:00
|
|
|
}
|
|
|
|
|
2014-04-24 20:26:56 +02:00
|
|
|
/**
|
|
|
|
* Remove alarm dismissal or snooze state
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param string $id Task identifier
|
2014-04-24 20:26:56 +02:00
|
|
|
*/
|
|
|
|
public function clear_alarms($id)
|
|
|
|
{
|
|
|
|
// delete alarm entry
|
|
|
|
$this->rc->db->query(
|
2014-09-15 12:23:46 +02:00
|
|
|
"DELETE FROM " . $this->rc->db->table_name('kolab_alarms', true) . "
|
|
|
|
WHERE `alarm_id` = ? AND `user_id` = ?",
|
2014-04-24 20:26:56 +02:00
|
|
|
$id,
|
|
|
|
$this->rc->user->ID
|
|
|
|
);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-08-19 04:06:45 -04:00
|
|
|
/**
|
|
|
|
* Get task tags
|
|
|
|
*/
|
2014-08-26 12:12:03 +02:00
|
|
|
private function load_tags(&$object)
|
2014-08-19 04:06:45 -04:00
|
|
|
{
|
2014-08-26 12:12:03 +02:00
|
|
|
// this task hasn't been migrated yet
|
|
|
|
if (!empty($object['categories'])) {
|
|
|
|
// OPTIONAL: call kolab_storage_config::apply_tags() to migrate the object
|
|
|
|
$object['tags'] = (array)$object['categories'];
|
|
|
|
if (!empty($object['tags'])) {
|
|
|
|
$this->tags = array_merge($this->tags, $object['tags']);
|
|
|
|
}
|
2024-01-24 11:24:41 +01:00
|
|
|
} else {
|
2014-08-26 12:12:03 +02:00
|
|
|
$config = kolab_storage_config::get_instance();
|
|
|
|
$tags = $config->get_tags($object['uid']);
|
2024-01-24 11:24:41 +01:00
|
|
|
$object['tags'] = array_map(function ($v) { return $v['name']; }, $tags);
|
2014-08-26 12:12:03 +02:00
|
|
|
}
|
2014-08-19 04:06:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update task tags
|
|
|
|
*/
|
|
|
|
private function save_tags($uid, $tags)
|
|
|
|
{
|
|
|
|
$config = kolab_storage_config::get_instance();
|
|
|
|
$config->save_tags($uid, $tags);
|
|
|
|
}
|
|
|
|
|
2014-10-13 15:33:39 +02:00
|
|
|
/**
|
|
|
|
* Find messages linked with a task record
|
|
|
|
*/
|
|
|
|
private function get_links($uid)
|
|
|
|
{
|
|
|
|
$config = kolab_storage_config::get_instance();
|
2015-01-13 22:19:52 +01:00
|
|
|
return $config->get_object_links($uid);
|
2014-10-13 15:33:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
private function save_links($uid, $links)
|
|
|
|
{
|
|
|
|
$config = kolab_storage_config::get_instance();
|
2016-02-13 13:09:59 +01:00
|
|
|
return $config->save_object_links($uid, (array) $links);
|
2014-10-13 15:33:39 +02:00
|
|
|
}
|
|
|
|
|
2014-11-04 10:56:19 +01:00
|
|
|
/**
|
|
|
|
* Extract uid + list identifiers from the given input
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array|string $prop Array or string with task identifier(s)
|
2014-11-04 10:56:19 +01:00
|
|
|
*/
|
|
|
|
private function _parse_id(&$prop)
|
|
|
|
{
|
|
|
|
$id_ = null;
|
|
|
|
if (is_array($prop)) {
|
|
|
|
// 'uid' + 'list' available, nothing to be done
|
|
|
|
if (!empty($prop['uid']) && !empty($prop['list'])) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 'id' is given
|
|
|
|
if (!empty($prop['id'])) {
|
|
|
|
if (!empty($prop['list'])) {
|
|
|
|
$list_id = $prop['_fromlist'] ?: $prop['list'];
|
2024-01-24 11:24:41 +01:00
|
|
|
if (strpos($prop['id'], $list_id . ':') === 0) {
|
|
|
|
$prop['uid'] = substr($prop['id'], strlen($list_id) + 1);
|
|
|
|
} else {
|
2014-11-04 10:56:19 +01:00
|
|
|
$prop['uid'] = $prop['id'];
|
|
|
|
}
|
2024-01-24 11:24:41 +01:00
|
|
|
} else {
|
2014-11-04 10:56:19 +01:00
|
|
|
$id_ = $prop['id'];
|
|
|
|
}
|
|
|
|
}
|
2024-01-24 11:24:41 +01:00
|
|
|
} else {
|
2014-11-04 10:56:19 +01:00
|
|
|
$id_ = strval($prop);
|
2024-01-24 11:24:41 +01:00
|
|
|
$prop = [];
|
2014-11-04 10:56:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// split 'id' into list + uid
|
|
|
|
if (!empty($id_)) {
|
2024-01-24 11:24:41 +01:00
|
|
|
[$list, $uid] = explode(':', $id_, 2);
|
2014-11-04 10:56:19 +01:00
|
|
|
if (!empty($uid)) {
|
|
|
|
$prop['uid'] = $uid;
|
|
|
|
$prop['list'] = $list;
|
2024-01-24 11:24:41 +01:00
|
|
|
} else {
|
2014-11-04 10:56:19 +01:00
|
|
|
$prop['uid'] = $id_;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
/**
|
|
|
|
* Convert from Kolab_Format to internal representation
|
|
|
|
*/
|
2014-11-27 07:11:54 -05:00
|
|
|
private function _to_rcube_task($record, $list_id, $all = true)
|
2012-06-08 14:57:16 +02:00
|
|
|
{
|
2014-11-04 10:56:19 +01:00
|
|
|
$id_prefix = $list_id . ':';
|
2024-01-24 11:24:41 +01:00
|
|
|
$task = [
|
2014-11-04 10:56:19 +01:00
|
|
|
'id' => $id_prefix . $record['uid'],
|
2012-06-08 14:57:16 +02:00
|
|
|
'uid' => $record['uid'],
|
2023-01-18 14:50:31 +01:00
|
|
|
'title' => $record['title'] ?? null,
|
2014-08-19 04:06:45 -04:00
|
|
|
// 'location' => $record['location'],
|
2023-01-18 14:50:31 +01:00
|
|
|
'description' => $record['description'] ?? null,
|
|
|
|
'flagged' => ($record['priority'] ?? null) == 1,
|
|
|
|
'complete' => floatval(($record['complete'] ?? null) / 100),
|
|
|
|
'status' => $record['status'] ?? null,
|
|
|
|
'parent_id' => ($record['parent_id'] ?? null) ? $id_prefix . $record['parent_id'] : null,
|
|
|
|
'recurrence' => $record['recurrence'] ?? null,
|
|
|
|
'attendees' => $record['attendees'] ?? null,
|
|
|
|
'organizer' => $record['organizer'] ?? null,
|
|
|
|
'sequence' => $record['sequence'] ?? null,
|
|
|
|
'tags' => $record['tags'] ?? null,
|
2014-11-04 10:56:19 +01:00
|
|
|
'list' => $list_id,
|
2023-01-18 14:50:31 +01:00
|
|
|
'links' => $record['links'] ?? null,
|
2024-01-24 11:24:41 +01:00
|
|
|
];
|
2012-06-08 14:57:16 +02:00
|
|
|
|
2014-11-27 07:11:54 -05:00
|
|
|
// we can sometimes skip this expensive operation
|
2016-02-13 17:35:29 +01:00
|
|
|
if ($all && !array_key_exists('links', $task)) {
|
2014-11-27 07:11:54 -05:00
|
|
|
$task['links'] = $this->get_links($task['uid']);
|
|
|
|
}
|
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
// convert from DateTime to internal date format
|
2023-01-18 14:50:31 +01:00
|
|
|
if (($record['due'] ?? null) instanceof DateTimeInterface) {
|
2014-04-24 19:44:21 +02:00
|
|
|
$due = $this->plugin->lib->adjust_timezone($record['due']);
|
|
|
|
$task['date'] = $due->format('Y-m-d');
|
2022-11-30 12:54:29 +01:00
|
|
|
if (empty($record['due']->_dateonly)) {
|
2014-04-24 19:44:21 +02:00
|
|
|
$task['time'] = $due->format('H:i');
|
2022-11-30 12:54:29 +01:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|
2012-07-29 13:36:16 +02:00
|
|
|
// convert from DateTime to internal date format
|
2023-01-18 14:50:31 +01:00
|
|
|
if (($record['start'] ?? null) instanceof DateTimeInterface) {
|
2014-04-24 19:44:21 +02:00
|
|
|
$start = $this->plugin->lib->adjust_timezone($record['start']);
|
|
|
|
$task['startdate'] = $start->format('Y-m-d');
|
2022-11-30 12:54:29 +01:00
|
|
|
if (empty($record['start']->_dateonly)) {
|
2014-04-24 19:44:21 +02:00
|
|
|
$task['starttime'] = $start->format('H:i');
|
2022-11-30 12:54:29 +01:00
|
|
|
}
|
2012-07-29 13:36:16 +02:00
|
|
|
}
|
2023-01-18 14:50:31 +01:00
|
|
|
if (($record['changed'] ?? null) instanceof DateTimeInterface) {
|
2014-08-13 11:07:06 +02:00
|
|
|
$task['changed'] = $record['changed'];
|
|
|
|
}
|
2023-01-18 14:50:31 +01:00
|
|
|
if (($record['created'] ?? null) instanceof DateTimeInterface) {
|
2014-08-13 11:07:06 +02:00
|
|
|
$task['created'] = $record['created'];
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|
|
|
|
|
2023-01-18 16:38:22 +01:00
|
|
|
if (!empty($record['valarms'])) {
|
2014-04-17 17:49:00 +02:00
|
|
|
$task['valarms'] = $record['valarms'];
|
2024-01-24 11:24:41 +01:00
|
|
|
} elseif (!empty($record['alarms'])) {
|
2012-08-03 14:07:58 +02:00
|
|
|
$task['alarms'] = $record['alarms'];
|
|
|
|
}
|
|
|
|
|
2014-08-21 10:28:25 +02:00
|
|
|
if (!empty($task['attendees'])) {
|
|
|
|
foreach ((array)$task['attendees'] as $i => $attendee) {
|
2022-11-30 12:54:29 +01:00
|
|
|
if (isset($attendee['delegated-from']) && is_array($attendee['delegated-from'])) {
|
2024-01-24 11:24:41 +01:00
|
|
|
$task['attendees'][$i]['delegated-from'] = implode(', ', $attendee['delegated-from']);
|
2014-08-21 10:28:25 +02:00
|
|
|
}
|
2022-11-30 12:54:29 +01:00
|
|
|
if (isset($attendee['delegated-to']) && is_array($attendee['delegated-to'])) {
|
2024-01-24 11:24:41 +01:00
|
|
|
$task['attendees'][$i]['delegated-to'] = implode(', ', $attendee['delegated-to']);
|
2014-08-21 10:28:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-18 16:38:22 +01:00
|
|
|
if (!empty($record['_attachments'])) {
|
2024-01-24 10:59:25 +01:00
|
|
|
$attachments = [];
|
2012-08-01 15:52:28 +02:00
|
|
|
foreach ($record['_attachments'] as $key => $attachment) {
|
|
|
|
if ($attachment !== false) {
|
2022-11-30 12:54:29 +01:00
|
|
|
if (empty($attachment['name'])) {
|
2012-08-01 15:52:28 +02:00
|
|
|
$attachment['name'] = $key;
|
2022-11-30 12:54:29 +01:00
|
|
|
}
|
2012-08-01 15:52:28 +02:00
|
|
|
$attachments[] = $attachment;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$task['attachments'] = $attachments;
|
|
|
|
}
|
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
return $task;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-07-30 17:40:53 +02:00
|
|
|
* Convert the given task record into a data structure that can be passed to kolab_storage backend for saving
|
|
|
|
* (opposite of self::_to_rcube_event())
|
2012-06-08 14:57:16 +02:00
|
|
|
*/
|
2022-11-30 12:54:29 +01:00
|
|
|
private function _from_rcube_task($task, $old = [])
|
2012-06-08 14:57:16 +02:00
|
|
|
{
|
2016-02-12 11:50:42 +01:00
|
|
|
$object = $task;
|
2014-11-04 10:56:19 +01:00
|
|
|
$id_prefix = $task['list'] . ':';
|
2012-06-08 14:57:16 +02:00
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
$toDT = function ($date) {
|
2022-11-30 12:54:29 +01:00
|
|
|
// Convert DateTime into libcalendaring_datetime
|
|
|
|
return libcalendaring_datetime::createFromFormat(
|
|
|
|
'Y-m-d\\TH:i:s',
|
|
|
|
$date->format('Y-m-d\\TH:i:s'),
|
|
|
|
$date->getTimezone()
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
if (!empty($task['date'])) {
|
2024-01-24 11:24:41 +01:00
|
|
|
$object['due'] = $toDT(rcube_utils::anytodatetime($task['date'] . ' ' . $task['time'], $this->plugin->timezone));
|
2022-11-30 12:54:29 +01:00
|
|
|
if (empty($task['time'])) {
|
2012-06-08 14:57:16 +02:00
|
|
|
$object['due']->_dateonly = true;
|
2022-11-30 12:54:29 +01:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
unset($object['date']);
|
|
|
|
}
|
|
|
|
|
2012-07-29 13:36:16 +02:00
|
|
|
if (!empty($task['startdate'])) {
|
2024-01-24 11:24:41 +01:00
|
|
|
$object['start'] = $toDT(rcube_utils::anytodatetime($task['startdate'] . ' ' . $task['starttime'], $this->plugin->timezone));
|
2022-11-30 12:54:29 +01:00
|
|
|
if (empty($task['starttime'])) {
|
2012-07-29 13:36:16 +02:00
|
|
|
$object['start']->_dateonly = true;
|
2022-11-30 12:54:29 +01:00
|
|
|
}
|
2012-07-29 13:36:16 +02:00
|
|
|
unset($object['startdate']);
|
|
|
|
}
|
|
|
|
|
2015-02-24 10:48:47 +01:00
|
|
|
// as per RFC (and the Kolab schema validation), start and due dates need to be of the same type (#3614)
|
|
|
|
// this should be catched in the client already but just make sure we don't write invalid objects
|
|
|
|
if (!empty($object['start']) && !empty($object['due']) && $object['due']->_dateonly != $object['start']->_dateonly) {
|
|
|
|
$object['start']->_dateonly = true;
|
|
|
|
$object['due']->_dateonly = true;
|
|
|
|
}
|
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
$object['complete'] = $task['complete'] * 100;
|
2024-01-24 11:24:41 +01:00
|
|
|
if ($task['complete'] == 1.0 && empty($task['complete'])) {
|
2012-06-08 14:57:16 +02:00
|
|
|
$object['status'] = 'COMPLETED';
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
if (!empty($task['flagged'])) {
|
2012-06-08 14:57:16 +02:00
|
|
|
$object['priority'] = 1;
|
2024-01-24 11:24:41 +01:00
|
|
|
} else {
|
2023-01-18 14:50:31 +01:00
|
|
|
$object['priority'] = ($old['priority'] ?? 0) > 1 ? $old['priority'] : 0;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
|
2014-11-04 10:56:19 +01:00
|
|
|
// remove list: prefix from parent_id
|
|
|
|
if (!empty($task['parent_id']) && strpos($task['parent_id'], $id_prefix) === 0) {
|
|
|
|
$object['parent_id'] = substr($task['parent_id'], strlen($id_prefix));
|
|
|
|
}
|
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
// copy meta data (starting with _) from old object
|
|
|
|
foreach ((array)$old as $key => $val) {
|
2024-01-24 11:24:41 +01:00
|
|
|
if (!isset($object[$key]) && $key[0] == '_') {
|
2014-04-24 19:44:21 +02:00
|
|
|
$object[$key] = $val;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|
2014-04-03 12:29:08 +02:00
|
|
|
|
2014-04-24 19:44:21 +02:00
|
|
|
// copy recurrence rules if the client didn't submit it (#2713)
|
|
|
|
if (!array_key_exists('recurrence', $object) && $old['recurrence']) {
|
2014-04-03 12:29:08 +02:00
|
|
|
$object['recurrence'] = $old['recurrence'];
|
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
|
2016-03-17 16:33:18 +01:00
|
|
|
unset($task['attachments']);
|
|
|
|
kolab_format::merge_attachments($object, $old);
|
2012-08-01 15:52:28 +02:00
|
|
|
|
2014-07-31 12:49:27 +02:00
|
|
|
// allow sequence increments if I'm the organizer
|
2015-02-20 17:35:09 +01:00
|
|
|
if ($this->plugin->is_organizer($object) && empty($object['_method'])) {
|
2014-07-31 12:49:27 +02:00
|
|
|
unset($object['sequence']);
|
2024-01-24 11:24:41 +01:00
|
|
|
} elseif (isset($old['sequence']) && empty($object['_method'])) {
|
2014-07-31 12:49:27 +02:00
|
|
|
$object['sequence'] = $old['sequence'];
|
|
|
|
}
|
2014-07-25 14:08:41 +02:00
|
|
|
|
2014-08-13 11:07:06 +02:00
|
|
|
unset($object['tempid'], $object['raw'], $object['list'], $object['flagged'], $object['tags'], $object['created']);
|
2012-06-08 14:57:16 +02:00
|
|
|
return $object;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a single task to the database
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array $task Hash array with task properties (see header of tasklist_driver.php)
|
|
|
|
*
|
2012-06-08 14:57:16 +02:00
|
|
|
* @return mixed New task ID on success, False on error
|
|
|
|
*/
|
|
|
|
public function create_task($task)
|
|
|
|
{
|
|
|
|
return $this->edit_task($task);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update an task entry with the given data
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array $task Hash array with task properties (see header of tasklist_driver.php)
|
|
|
|
*
|
|
|
|
* @return bool True on success, False on error
|
2012-06-08 14:57:16 +02:00
|
|
|
*/
|
|
|
|
public function edit_task($task)
|
|
|
|
{
|
2014-11-04 10:56:19 +01:00
|
|
|
$this->_parse_id($task);
|
2012-06-08 14:57:16 +02:00
|
|
|
$list_id = $task['list'];
|
2015-04-22 10:58:26 +02:00
|
|
|
if (!$list_id || !($folder = $this->get_folder($list_id))) {
|
2024-01-24 11:24:41 +01:00
|
|
|
rcube::raise_error(
|
|
|
|
[
|
2015-04-22 10:58:26 +02:00
|
|
|
'code' => 600, 'type' => 'php',
|
|
|
|
'file' => __FILE__, 'line' => __LINE__,
|
2024-01-24 11:24:41 +01:00
|
|
|
'message' => "Invalid list identifer to save task: " . print_r($list_id, true)],
|
|
|
|
true,
|
|
|
|
false
|
|
|
|
);
|
2012-06-08 14:57:16 +02:00
|
|
|
return false;
|
2015-04-22 10:58:26 +02:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
|
2014-10-13 15:33:39 +02:00
|
|
|
// email links and tags are stored separately
|
2023-01-18 14:50:31 +01:00
|
|
|
$links = $task['links'] ?? null;
|
|
|
|
$tags = $task['tags'] ?? null;
|
2014-10-13 15:33:39 +02:00
|
|
|
unset($task['tags'], $task['links']);
|
2014-08-19 04:06:45 -04:00
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
// moved from another folder
|
2023-01-18 14:50:31 +01:00
|
|
|
if (($task['_fromlist'] ?? false) && ($fromfolder = $this->get_folder($task['_fromlist']))) {
|
2024-01-24 11:24:41 +01:00
|
|
|
if (!$fromfolder->move($task['uid'], $folder)) {
|
2012-06-08 14:57:16 +02:00
|
|
|
return false;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
|
|
|
|
unset($task['_fromlist']);
|
|
|
|
}
|
|
|
|
|
|
|
|
// load previous version of this task to merge
|
2023-01-18 14:50:31 +01:00
|
|
|
$old = null;
|
2023-01-18 16:38:22 +01:00
|
|
|
if (!empty($task['id'])) {
|
2014-11-04 10:56:19 +01:00
|
|
|
$old = $folder->get_object($task['uid']);
|
2024-01-24 11:24:41 +01:00
|
|
|
if (!$old || PEAR::isError($old)) {
|
2012-06-08 14:57:16 +02:00
|
|
|
return false;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-09-26 12:14:42 +02:00
|
|
|
|
|
|
|
// merge existing properties if the update isn't complete
|
2024-01-24 11:24:41 +01:00
|
|
|
if (!isset($task['title']) || !isset($task['complete'])) {
|
2014-11-04 10:56:19 +01:00
|
|
|
$task += $this->_to_rcube_task($old, $list_id);
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// generate new task object from RC input
|
|
|
|
$object = $this->_from_rcube_task($task, $old);
|
2014-11-04 10:56:19 +01:00
|
|
|
$saved = $folder->save($object, 'task', $task['uid']);
|
2012-06-08 14:57:16 +02:00
|
|
|
|
|
|
|
if (!$saved) {
|
2024-01-24 11:24:41 +01:00
|
|
|
rcube::raise_error(
|
|
|
|
[
|
2012-06-08 14:57:16 +02:00
|
|
|
'code' => 600, 'type' => 'php',
|
|
|
|
'file' => __FILE__, 'line' => __LINE__,
|
2024-01-24 11:24:41 +01:00
|
|
|
'message' => "Error saving task object to Kolab server"],
|
|
|
|
true,
|
|
|
|
false
|
|
|
|
);
|
2012-06-08 14:57:16 +02:00
|
|
|
$saved = false;
|
2024-01-24 11:24:41 +01:00
|
|
|
} else {
|
2014-10-13 15:33:39 +02:00
|
|
|
// save links in configuration.relation object
|
|
|
|
$this->save_links($object['uid'], $links);
|
2014-08-19 04:06:45 -04:00
|
|
|
// save tags in configuration.relation object
|
|
|
|
$this->save_tags($object['uid'], $tags);
|
|
|
|
|
2014-11-04 10:56:19 +01:00
|
|
|
$task = $this->_to_rcube_task($object, $list_id);
|
2014-08-19 04:06:45 -04:00
|
|
|
$task['tags'] = (array) $tags;
|
2014-11-04 10:56:19 +01:00
|
|
|
$this->tasks[$task['uid']] = $task;
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $saved;
|
|
|
|
}
|
|
|
|
|
2012-09-18 08:53:24 +02:00
|
|
|
/**
|
|
|
|
* Move a single task to another list
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array $task Hash array with task properties:
|
|
|
|
*
|
|
|
|
* @return bool True on success, False on error
|
2012-09-18 08:53:24 +02:00
|
|
|
* @see tasklist_driver::move_task()
|
|
|
|
*/
|
|
|
|
public function move_task($task)
|
|
|
|
{
|
2014-11-04 10:56:19 +01:00
|
|
|
$this->_parse_id($task);
|
2012-09-18 08:53:24 +02:00
|
|
|
$list_id = $task['list'];
|
2024-01-24 11:24:41 +01:00
|
|
|
if (!$list_id || !($folder = $this->get_folder($list_id))) {
|
2012-09-18 08:53:24 +02:00
|
|
|
return false;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-09-18 08:53:24 +02:00
|
|
|
|
|
|
|
// execute move command
|
2014-05-21 13:04:18 +02:00
|
|
|
if ($task['_fromlist'] && ($fromfolder = $this->get_folder($task['_fromlist']))) {
|
2014-11-04 10:56:19 +01:00
|
|
|
return $fromfolder->move($task['uid'], $folder);
|
2012-09-18 08:53:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-06-08 14:57:16 +02:00
|
|
|
/**
|
|
|
|
* Remove a single task from the database
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array $task Hash array with task properties:
|
|
|
|
* id: Task identifier
|
|
|
|
* @param bool $force Remove record irreversible (mark as deleted otherwise, if supported by the backend)
|
|
|
|
*
|
|
|
|
* @return bool True on success, False on error
|
2012-06-08 14:57:16 +02:00
|
|
|
*/
|
|
|
|
public function delete_task($task, $force = true)
|
|
|
|
{
|
2014-11-04 10:56:19 +01:00
|
|
|
$this->_parse_id($task);
|
2012-06-08 14:57:16 +02:00
|
|
|
$list_id = $task['list'];
|
2024-01-24 11:24:41 +01:00
|
|
|
if (!$list_id || !($folder = $this->get_folder($list_id))) {
|
2012-06-08 14:57:16 +02:00
|
|
|
return false;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
|
2014-11-04 10:56:19 +01:00
|
|
|
$status = $folder->delete($task['uid']);
|
2014-08-19 04:06:45 -04:00
|
|
|
|
|
|
|
if ($status) {
|
|
|
|
// remove tag assignments
|
|
|
|
// @TODO: don't do this when undelete feature will be implemented
|
2014-11-04 10:56:19 +01:00
|
|
|
$this->save_tags($task['uid'], null);
|
2014-08-19 04:06:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return $status;
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Restores a single deleted task (if supported)
|
|
|
|
*
|
2024-01-25 13:47:41 +01:00
|
|
|
* @param array $prop Hash array with task properties:
|
2012-06-08 14:57:16 +02:00
|
|
|
* id: Task identifier
|
2024-01-25 13:47:41 +01:00
|
|
|
* @return bool True on success, False on error
|
2012-06-08 14:57:16 +02:00
|
|
|
*/
|
|
|
|
public function undelete_task($prop)
|
|
|
|
{
|
|
|
|
// TODO: implement this
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-01 15:52:28 +02:00
|
|
|
/**
|
|
|
|
* Get attachment properties
|
|
|
|
*
|
|
|
|
* @param string $id Attachment identifier
|
|
|
|
* @param array $task Hash array with event properties:
|
|
|
|
* id: Task identifier
|
|
|
|
* list: List identifier
|
2015-03-25 11:59:10 +01:00
|
|
|
* rev: Revision (optional)
|
2012-08-01 15:52:28 +02:00
|
|
|
*
|
2024-01-24 10:59:25 +01:00
|
|
|
* @return array|null Hash array with attachment properties:
|
2012-08-01 15:52:28 +02:00
|
|
|
* id: Attachment identifier
|
|
|
|
* name: Attachment name
|
|
|
|
* mimetype: MIME content type of the attachment
|
|
|
|
* size: Attachment size
|
|
|
|
*/
|
|
|
|
public function get_attachment($id, $task)
|
|
|
|
{
|
2015-03-25 11:59:10 +01:00
|
|
|
// get old revision of the object
|
|
|
|
if ($task['rev']) {
|
|
|
|
$task = $this->get_task_revison($task, $task['rev']);
|
2024-01-24 11:24:41 +01:00
|
|
|
} else {
|
2015-03-25 11:59:10 +01:00
|
|
|
$task = $this->get_task($task);
|
|
|
|
}
|
2012-08-01 15:52:28 +02:00
|
|
|
|
|
|
|
if ($task && !empty($task['attachments'])) {
|
|
|
|
foreach ($task['attachments'] as $att) {
|
2024-01-24 11:24:41 +01:00
|
|
|
if ($att['id'] == $id) {
|
2012-08-01 15:52:28 +02:00
|
|
|
return $att;
|
2024-01-24 11:24:41 +01:00
|
|
|
}
|
2012-08-01 15:52:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get attachment body
|
|
|
|
*
|
|
|
|
* @param string $id Attachment identifier
|
|
|
|
* @param array $task Hash array with event properties:
|
|
|
|
* id: Task identifier
|
|
|
|
* list: List identifier
|
2015-03-25 11:59:10 +01:00
|
|
|
* rev: Revision (optional)
|
2012-08-01 15:52:28 +02:00
|
|
|
*
|
2024-01-24 10:59:25 +01:00
|
|
|
* @return string|false Attachment body
|
2012-08-01 15:52:28 +02:00
|
|
|
*/
|
|
|
|
public function get_attachment_body($id, $task)
|
|
|
|
{
|
2014-11-04 10:56:19 +01:00
|
|
|
$this->_parse_id($task);
|
2015-03-25 11:59:10 +01:00
|
|
|
|
|
|
|
// get old revision of event
|
|
|
|
if ($task['rev']) {
|
|
|
|
if (empty($this->bonnie_api)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$cid = substr($id, 4);
|
|
|
|
|
|
|
|
// call Bonnie API and get the raw mime message
|
2024-01-24 11:24:41 +01:00
|
|
|
[$uid, $mailbox, $msguid] = $this->_resolve_task_identity($task);
|
2015-03-25 11:59:10 +01:00
|
|
|
if ($msg_raw = $this->bonnie_api->rawdata('task', $uid, $task['rev'], $mailbox, $msguid)) {
|
|
|
|
// parse the message and find the part with the matching content-id
|
|
|
|
$message = rcube_mime::parse_message($msg_raw);
|
|
|
|
foreach ((array)$message->parts as $part) {
|
|
|
|
if ($part->headers['content-id'] && trim($part->headers['content-id'], '<>') == $cid) {
|
|
|
|
return $part->body;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-05-21 13:04:18 +02:00
|
|
|
if ($storage = $this->get_folder($task['list'])) {
|
2014-11-04 10:56:19 +01:00
|
|
|
return $storage->get_attachment($task['uid'], $id);
|
2012-08-01 15:52:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-10-13 15:33:39 +02:00
|
|
|
/**
|
2015-01-13 22:19:52 +01:00
|
|
|
* Build a struct representing the given message reference
|
2014-10-13 15:33:39 +02:00
|
|
|
*
|
2015-01-13 22:19:52 +01:00
|
|
|
* @see tasklist_driver::get_message_reference()
|
2014-10-13 15:33:39 +02:00
|
|
|
*/
|
2015-01-13 22:19:52 +01:00
|
|
|
public function get_message_reference($uri_or_headers, $folder = null)
|
2014-10-13 15:33:39 +02:00
|
|
|
{
|
2015-01-13 22:19:52 +01:00
|
|
|
if (is_object($uri_or_headers)) {
|
|
|
|
$uri_or_headers = kolab_storage_config::get_message_uri($uri_or_headers, $folder);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_string($uri_or_headers)) {
|
|
|
|
return kolab_storage_config::get_message_reference($uri_or_headers, 'task');
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2014-10-13 15:33:39 +02:00
|
|
|
}
|
|
|
|
|
2014-10-13 18:40:39 +02:00
|
|
|
/**
|
|
|
|
* Find tasks assigned to a specified message
|
|
|
|
*
|
|
|
|
* @see tasklist_driver::get_message_related_tasks()
|
|
|
|
*/
|
|
|
|
public function get_message_related_tasks($headers, $folder)
|
|
|
|
{
|
|
|
|
$config = kolab_storage_config::get_instance();
|
|
|
|
$result = $config->get_message_relations($headers, $folder, 'task');
|
|
|
|
|
|
|
|
foreach ($result as $idx => $rec) {
|
2014-11-04 10:56:19 +01:00
|
|
|
$result[$idx] = $this->_to_rcube_task($rec, kolab_storage::folder_id($rec['_mailbox']));
|
2014-10-13 18:40:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2012-06-21 21:59:47 +02:00
|
|
|
/**
|
2024-01-24 11:24:41 +01:00
|
|
|
*
|
2012-06-21 21:59:47 +02:00
|
|
|
*/
|
2014-04-09 11:12:50 +02:00
|
|
|
public function tasklist_edit_form($action, $list, $fieldprop)
|
2012-06-21 21:59:47 +02:00
|
|
|
{
|
2016-02-12 11:50:42 +01:00
|
|
|
$this->_read_lists();
|
|
|
|
|
2014-04-09 11:12:50 +02:00
|
|
|
if ($list['id'] && ($list = $this->lists[$list['id']])) {
|
2014-05-21 13:04:18 +02:00
|
|
|
$folder_name = $this->get_folder($list['id'])->name; // UTF7
|
2024-01-24 11:24:41 +01:00
|
|
|
} else {
|
2014-04-09 11:12:50 +02:00
|
|
|
$folder_name = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
$storage = $this->rc->get_storage();
|
|
|
|
$delim = $storage->get_hierarchy_delimiter();
|
2024-01-24 11:24:41 +01:00
|
|
|
$form = [];
|
2024-01-24 10:59:25 +01:00
|
|
|
$options = [];
|
2014-04-09 11:12:50 +02:00
|
|
|
|
|
|
|
if (strlen($folder_name)) {
|
|
|
|
$path_imap = explode($delim, $folder_name);
|
|
|
|
array_pop($path_imap); // pop off name part
|
2021-01-27 10:19:57 +01:00
|
|
|
$path_imap = implode($delim, $path_imap);
|
2014-04-09 11:12:50 +02:00
|
|
|
|
|
|
|
$options = $storage->folder_info($folder_name);
|
2024-01-24 11:24:41 +01:00
|
|
|
} else {
|
2014-04-09 11:12:50 +02:00
|
|
|
$path_imap = '';
|
|
|
|
}
|
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
$hidden_fields[] = ['name' => 'oldname', 'value' => $folder_name];
|
2014-04-09 11:12:50 +02:00
|
|
|
|
|
|
|
// folder name (default field)
|
2024-01-24 11:24:41 +01:00
|
|
|
$input_name = new html_inputfield(['name' => 'name', 'id' => 'taskedit-tasklistname', 'size' => 20]);
|
2024-01-24 10:59:25 +01:00
|
|
|
$disabled = !empty($options['norename']) || !empty($options['protected']);
|
2024-01-24 11:24:41 +01:00
|
|
|
$fieldprop['name']['value'] = $input_name->show($list['editname'], ['disabled' => $disabled]);
|
2014-04-09 11:12:50 +02:00
|
|
|
|
|
|
|
// prevent user from moving folder
|
2024-01-24 10:59:25 +01:00
|
|
|
if ($disabled) {
|
2024-01-24 11:24:41 +01:00
|
|
|
$hidden_fields[] = ['name' => 'parent', 'value' => $path_imap];
|
|
|
|
} else {
|
|
|
|
$select = kolab_storage::folder_selector('task', ['name' => 'parent', 'id' => 'taskedit-parentfolder'], $folder_name);
|
|
|
|
$fieldprop['parent'] = [
|
2014-04-09 11:12:50 +02:00
|
|
|
'id' => 'taskedit-parentfolder',
|
|
|
|
'label' => $this->plugin->gettext('parentfolder'),
|
|
|
|
'value' => $select->show($path_imap),
|
2024-01-24 11:24:41 +01:00
|
|
|
];
|
2014-04-09 11:12:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// General tab
|
2024-01-24 11:24:41 +01:00
|
|
|
$form['properties'] = [
|
2014-04-09 11:12:50 +02:00
|
|
|
'name' => $this->rc->gettext('properties'),
|
2024-01-24 11:24:41 +01:00
|
|
|
'fields' => [],
|
|
|
|
];
|
2012-08-04 17:19:03 +02:00
|
|
|
|
2024-01-24 11:24:41 +01:00
|
|
|
foreach (['name','parent','showalarms'] as $f) {
|
2014-04-09 11:12:50 +02:00
|
|
|
$form['properties']['fields'][$f] = $fieldprop[$f];
|
|
|
|
}
|
|
|
|
|
2018-03-28 15:36:08 +00:00
|
|
|
return kolab_utils::folder_form($form, $folder_name, 'tasklist', $hidden_fields);
|
2014-04-09 11:12:50 +02:00
|
|
|
}
|
2012-06-08 14:57:16 +02:00
|
|
|
}
|