Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F6064786
tasklist_kolab_driver.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
40 KB
Referenced Files
None
Subscribers
None
tasklist_kolab_driver.php
View Options
<?php
/**
* Kolab Groupware driver for the Tasklist plugin
*
* @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
tasklist_kolab_driver
extends
tasklist_driver
{
// features supported by the backend
public
$alarms
=
false
;
public
$attachments
=
true
;
public
$undelete
=
false
;
// task undelete action
public
$alarm_types
=
array
(
'DISPLAY'
,
'AUDIO'
);
private
$rc
;
private
$plugin
;
private
$lists
;
private
$folders
=
array
();
private
$tasks
=
array
();
/**
* Default constructor
*/
public
function
__construct
(
$plugin
)
{
$this
->
rc
=
$plugin
->
rc
;
$this
->
plugin
=
$plugin
;
if
(
kolab_storage
::
$version
==
'2.0'
)
{
$this
->
alarm_absolute
=
false
;
}
// tasklist use fully encoded identifiers
kolab_storage
::
$encode_ids
=
true
;
$this
->
_read_lists
();
$this
->
plugin
->
register_action
(
'folder-acl'
,
array
(
$this
,
'folder_acl'
));
}
/**
* Read available calendars for the current user and store them internally
*/
private
function
_read_lists
(
$force
=
false
)
{
// already read sources
if
(
isset
(
$this
->
lists
)
&&
!
$force
)
return
$this
->
lists
;
// get all folders that have type "task"
$folders
=
kolab_storage
::
sort_folders
(
kolab_storage
::
get_folders
(
'task'
));
$this
->
lists
=
$this
->
folders
=
array
();
// find default folder
$default_index
=
0
;
foreach
(
$folders
as
$i
=>
$folder
)
{
if
(
$folder
->
default
)
$default_index
=
$i
;
}
// put default folder (aka INBOX) on top of the list
if
(
$default_index
>
0
)
{
$default_folder
=
$folders
[
$default_index
];
unset
(
$folders
[
$default_index
]);
array_unshift
(
$folders
,
$default_folder
);
}
$delim
=
$this
->
rc
->
get_storage
()->
get_hierarchy_delimiter
();
$prefs
=
$this
->
rc
->
config
->
get
(
'kolab_tasklists'
,
array
());
foreach
(
$folders
as
$folder
)
{
$tasklist
=
$this
->
folder_props
(
$folder
,
$delim
,
$prefs
);
$this
->
lists
[
$tasklist
[
'id'
]]
=
$tasklist
;
$this
->
folders
[
$tasklist
[
'id'
]]
=
$folder
;
$this
->
folders
[
$folder
->
name
]
=
$folder
;
}
}
/**
* Derive list properties from the given kolab_storage_folder object
*/
protected
function
folder_props
(
$folder
,
$delim
,
$prefs
)
{
if
(
$folder
->
get_namespace
()
==
'personal'
)
{
$norename
=
false
;
$readonly
=
false
;
$alarms
=
true
;
}
else
{
$alarms
=
false
;
$readonly
=
true
;
if
((
$rights
=
$folder
->
get_myrights
())
&&
!
PEAR
::
isError
(
$rights
))
{
if
(
strpos
(
$rights
,
'i'
)
!==
false
)
$readonly
=
false
;
}
$info
=
$folder
->
get_folder_info
();
$norename
=
$readonly
||
$info
[
'norename'
]
||
$info
[
'protected'
];
}
$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'
];
}
return
array
(
'id'
=>
$list_id
,
'name'
=>
$folder
->
get_name
(),
'listname'
=>
$folder
->
get_foldername
(),
'editname'
=>
$folder
->
get_foldername
(),
'color'
=>
$folder
->
get_color
(
'0000CC'
),
'showalarms'
=>
isset
(
$prefs
[
$list_id
][
'showalarms'
])
?
$prefs
[
$list_id
][
'showalarms'
]
:
$alarms
,
'editable'
=>
!
$readonly
,
'norename'
=>
$norename
,
'active'
=>
$folder
->
is_active
(),
'parentfolder'
=>
$folder
->
get_parent
(),
'default'
=>
$folder
->
default
,
'virtual'
=>
$folder
->
virtual
,
'children'
=>
true
,
// TODO: determine if that folder indeed has child folders
'subscribed'
=>
(
bool
)
$folder
->
is_subscribed
(),
'group'
=>
$folder
->
default
?
'default'
:
$folder
->
get_namespace
(),
'class'
=>
trim
(
$folder
->
get_namespace
()
.
(
$folder
->
default
?
' default'
:
''
)),
);
}
/**
* Get a list of available task lists from this source
*/
public
function
get_lists
(&
$tree
=
null
)
{
// attempt to create a default list for this user
if
(
empty
(
$this
->
lists
))
{
$prop
=
array
(
'name'
=>
'Tasks'
,
'color'
=>
'0000CC'
,
'default'
=>
true
);
if
(
$this
->
create_list
(
$prop
))
$this
->
_read_lists
(
true
);
}
$folders
=
array
();
foreach
(
$this
->
lists
as
$id
=>
$list
)
{
if
(!
empty
(
$this
->
folders
[
$id
]))
{
$folders
[]
=
$this
->
folders
[
$id
];
}
}
// include virtual folders for a full folder tree
if
(!
is_null
(
$tree
))
{
$folders
=
kolab_storage
::
folder_hierarchy
(
$folders
,
$tree
);
}
$delim
=
$this
->
rc
->
get_storage
()->
get_hierarchy_delimiter
();
$prefs
=
$this
->
rc
->
config
->
get
(
'kolab_tasklists'
,
array
());
$lists
=
array
();
foreach
(
$folders
as
$folder
)
{
$list_id
=
$folder
->
id
;
#kolab_storage::folder_id($folder->name);
$imap_path
=
explode
(
$delim
,
$folder
->
name
);
// find parent
do
{
array_pop
(
$imap_path
);
$parent_id
=
kolab_storage
::
folder_id
(
join
(
$delim
,
$imap_path
));
}
while
(
count
(
$imap_path
)
>
1
&&
!
$this
->
folders
[
$parent_id
]);
// restore "real" parent ID
if
(
$parent_id
&&
!
$this
->
folders
[
$parent_id
])
{
$parent_id
=
kolab_storage
::
folder_id
(
$folder
->
get_parent
());
}
$fullname
=
$folder
->
get_name
();
$listname
=
$folder
->
get_foldername
();
// special handling for virtual folders
if
(
$folder
instanceof
kolab_storage_folder_user
)
{
$lists
[
$list_id
]
=
array
(
'id'
=>
$list_id
,
'name'
=>
$folder
->
get_name
(),
'listname'
=>
$listname
,
'title'
=>
$folder
->
get_owner
(),
'virtual'
=>
true
,
'editable'
=>
false
,
'group'
=>
'other virtual'
,
'class'
=>
'user'
,
'parent'
=>
$parent_id
,
);
}
else
if
(
$folder
->
virtual
)
{
$lists
[
$list_id
]
=
array
(
'id'
=>
$list_id
,
'name'
=>
kolab_storage
::
object_name
(
$fullname
),
'listname'
=>
$listname
,
'virtual'
=>
true
,
'editable'
=>
false
,
'group'
=>
$folder
->
get_namespace
(),
'class'
=>
'folder'
,
'parent'
=>
$parent_id
,
);
}
else
{
if
(!
$this
->
lists
[
$list_id
])
{
$this
->
lists
[
$list_id
]
=
$this
->
folder_props
(
$folder
,
$delim
,
$prefs
);
$this
->
folders
[
$list_id
]
=
$folder
;
}
$this
->
lists
[
$list_id
][
'parent'
]
=
$parent_id
;
$lists
[
$list_id
]
=
$this
->
lists
[
$list_id
];
}
}
return
$lists
;
}
/**
* Get the kolab_calendar instance for the given calendar ID
*
* @param string List identifier (encoded imap folder name)
* @return object kolab_storage_folder Object nor null if list doesn't exist
*/
protected
function
get_folder
(
$id
)
{
// create list and folder instance if necesary
if
(!
$this
->
lists
[
$id
])
{
$folder
=
kolab_storage
::
get_folder
(
kolab_storage
::
id_decode
(
$id
));
if
(
$folder
->
type
)
{
$this
->
folders
[
$id
]
=
$folder
;
$this
->
lists
[
$id
]
=
$this
->
folder_props
(
$folder
,
$this
->
rc
->
get_storage
()->
get_hierarchy_delimiter
(),
$this
->
rc
->
config
->
get
(
'kolab_tasklists'
,
array
()));
}
}
return
$this
->
folders
[
$id
];
}
/**
* Create a new list assigned to the current user
*
* @param array Hash array with list properties
* name: List name
* color: The color of the list
* showalarms: True if alarms are enabled
* @return mixed ID of the new list on success, False on error
*/
public
function
create_list
(&
$prop
)
{
$prop
[
'type'
]
=
'task'
.
(
$prop
[
'default'
]
?
'.default'
:
''
);
$prop
[
'active'
]
=
true
;
// activate folder by default
$prop
[
'subscribed'
]
=
true
;
$folder
=
kolab_storage
::
folder_update
(
$prop
);
if
(
$folder
===
false
)
{
$this
->
last_error
=
kolab_storage
::
$last_error
;
return
false
;
}
// create ID
$id
=
kolab_storage
::
folder_id
(
$folder
);
$prefs
[
'kolab_tasklists'
]
=
$this
->
rc
->
config
->
get
(
'kolab_tasklists'
,
array
());
if
(
isset
(
$prop
[
'showalarms'
]))
$prefs
[
'kolab_tasklists'
][
$id
][
'showalarms'
]
=
$prop
[
'showalarms'
]
?
true
:
false
;
if
(
$prefs
[
'kolab_tasklists'
][
$id
])
$this
->
rc
->
user
->
save_prefs
(
$prefs
);
// force page reload to properly render folder hierarchy
if
(!
empty
(
$prop
[
'parent'
]))
{
$prop
[
'_reload'
]
=
true
;
}
else
{
$folder
=
kolab_storage
::
get_folder
(
$folder
);
$prop
+=
$this
->
folder_props
(
$folder
,
$this
->
rc
->
get_storage
()->
get_hierarchy_delimiter
(),
array
());
}
return
$id
;
}
/**
* Update properties of an existing tasklist
*
* @param array Hash array with list properties
* id: List Identifier
* name: List name
* color: The color of the list
* showalarms: True if alarms are enabled (if supported)
* @return boolean True on success, Fales on failure
*/
public
function
edit_list
(&
$prop
)
{
if
(
$prop
[
'id'
]
&&
(
$folder
=
$this
->
get_folder
(
$prop
[
'id'
])))
{
$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
$id
=
kolab_storage
::
folder_id
(
$newfolder
);
// fallback to local prefs
$prefs
[
'kolab_tasklists'
]
=
$this
->
rc
->
config
->
get
(
'kolab_tasklists'
,
array
());
unset
(
$prefs
[
'kolab_tasklists'
][
$prop
[
'id'
]]);
if
(
isset
(
$prop
[
'showalarms'
]))
$prefs
[
'kolab_tasklists'
][
$id
][
'showalarms'
]
=
$prop
[
'showalarms'
]
?
true
:
false
;
if
(
$prefs
[
'kolab_tasklists'
][
$id
])
$this
->
rc
->
user
->
save_prefs
(
$prefs
);
// force page reload if folder name/hierarchy changed
if
(
$newfolder
!=
$prop
[
'oldname'
])
$prop
[
'_reload'
]
=
true
;
return
$id
;
}
return
false
;
}
/**
* Set active/subscribed state of a list
*
* @param array Hash array with list properties
* id: List Identifier
* active: True if list is active, false if not
* permanent: True if list is to be subscribed permanently
* @return boolean True on success, Fales on failure
*/
public
function
subscribe_list
(
$prop
)
{
if
(
$prop
[
'id'
]
&&
(
$folder
=
$this
->
get_folder
(
$prop
[
'id'
])))
{
$ret
=
false
;
if
(
isset
(
$prop
[
'permanent'
]))
$ret
|=
$folder
->
subscribe
(
intval
(
$prop
[
'permanent'
]));
if
(
isset
(
$prop
[
'active'
]))
$ret
|=
$folder
->
activate
(
intval
(
$prop
[
'active'
]));
return
$ret
;
}
return
false
;
}
/**
* Delete the given list with all its contents
*
* @param array Hash array with list properties
* id: list Identifier
* @return boolean True on success, Fales on failure
*/
public
function
remove_list
(
$prop
)
{
if
(
$prop
[
'id'
]
&&
(
$folder
=
$this
->
get_folder
(
$prop
[
'id'
])))
{
if
(
kolab_storage
::
folder_delete
(
$folder
->
name
))
return
true
;
else
$this
->
last_error
=
kolab_storage
::
$last_error
;
}
return
false
;
}
/**
* Search for shared or otherwise not listed tasklists the user has access
*
* @param string Search string
* @param string Section/source to search
* @return array List of tasklists
*/
public
function
search_lists
(
$query
,
$source
)
{
if
(!
kolab_storage
::
setup
())
{
return
array
();
}
$this
->
search_more_results
=
false
;
$this
->
lists
=
$this
->
folders
=
array
();
$delim
=
$this
->
rc
->
get_storage
()->
get_hierarchy_delimiter
();
// find unsubscribed IMAP folders that have "event" type
if
(
$source
==
'folders'
)
{
foreach
((
array
)
kolab_storage
::
search_folders
(
'task'
,
$query
,
array
(
'other'
))
as
$folder
)
{
$this
->
folders
[
$folder
->
id
]
=
$folder
;
$this
->
lists
[
$folder
->
id
]
=
$this
->
folder_props
(
$folder
,
$delim
,
array
());
}
}
// search other user's namespace via LDAP
else
if
(
$source
==
'users'
)
{
$limit
=
$this
->
rc
->
config
->
get
(
'autocomplete_max'
,
15
)
*
2
;
// we have slightly more space, so display twice the number
foreach
(
kolab_storage
::
search_users
(
$query
,
0
,
array
(),
$limit
*
10
)
as
$user
)
{
$folders
=
array
();
// 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'
);
}
if
(
count
(
$folders
))
{
$userfolder
=
new
kolab_storage_folder_user
(
$user
[
'kolabtargetfolder'
],
''
,
$user
);
$this
->
folders
[
$userfolder
->
id
]
=
$userfolder
;
$this
->
lists
[
$userfolder
->
id
]
=
$this
->
folder_props
(
$userfolder
,
$delim
,
array
());
foreach
(
$folders
as
$folder
)
{
$this
->
folders
[
$folder
->
id
]
=
$folder
;
$this
->
lists
[
$folder
->
id
]
=
$this
->
folder_props
(
$folder
,
$delim
,
array
());
$count
++;
}
}
if
(
$count
>=
$limit
)
{
$this
->
search_more_results
=
true
;
break
;
}
}
}
return
$this
->
get_lists
();
}
/**
* Get number of tasks matching the given filter
*
* @param array List of lists to count tasks of
* @return array Hash array with counts grouped by status (all|flagged|completed|today|tomorrow|nodate)
*/
public
function
count_tasks
(
$lists
=
null
)
{
if
(
empty
(
$lists
))
$lists
=
array_keys
(
$this
->
lists
);
else
if
(
is_string
(
$lists
))
$lists
=
explode
(
','
,
$lists
);
$today_date
=
new
DateTime
(
'now'
,
$this
->
plugin
->
timezone
);
$today
=
$today_date
->
format
(
'Y-m-d'
);
$tomorrow_date
=
new
DateTime
(
'now + 1 day'
,
$this
->
plugin
->
timezone
);
$tomorrow
=
$tomorrow_date
->
format
(
'Y-m-d'
);
$counts
=
array
(
'all'
=>
0
,
'flagged'
=>
0
,
'today'
=>
0
,
'tomorrow'
=>
0
,
'overdue'
=>
0
,
'nodate'
=>
0
);
foreach
(
$lists
as
$list_id
)
{
if
(!
$folder
=
$this
->
get_folder
(
$list_id
))
{
continue
;
}
foreach
(
$folder
->
select
(
array
(
array
(
'tags'
,
'!~'
,
'x-complete'
)))
as
$record
)
{
$rec
=
$this
->
_to_rcube_task
(
$record
);
if
(
$this
->
is_complete
(
$rec
))
// don't count complete tasks
continue
;
$counts
[
'all'
]++;
if
(
$rec
[
'flagged'
])
$counts
[
'flagged'
]++;
if
(
empty
(
$rec
[
'date'
]))
$counts
[
'nodate'
]++;
else
if
(
$rec
[
'date'
]
==
$today
)
$counts
[
'today'
]++;
else
if
(
$rec
[
'date'
]
==
$tomorrow
)
$counts
[
'tomorrow'
]++;
else
if
(
$rec
[
'date'
]
<
$today
)
$counts
[
'overdue'
]++;
}
}
// avoid session race conditions that will loose temporary subscriptions
$this
->
plugin
->
rc
->
session
->
nowrite
=
true
;
return
$counts
;
}
/**
* Get all taks records matching the given filter
*
* @param array Hash array with filter criterias:
* - 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
* @param array List of lists to get tasks from
* @return array List of tasks records matchin the criteria
*/
public
function
list_tasks
(
$filter
,
$lists
=
null
)
{
if
(
empty
(
$lists
))
$lists
=
array_keys
(
$this
->
lists
);
else
if
(
is_string
(
$lists
))
$lists
=
explode
(
','
,
$lists
);
$results
=
array
();
// query Kolab storage
$query
=
array
();
if
(
$filter
[
'mask'
]
&
tasklist
::
FILTER_MASK_COMPLETE
)
$query
[]
=
array
(
'tags'
,
'~'
,
'x-complete'
);
else
if
(
empty
(
$filter
[
'since'
]))
$query
[]
=
array
(
'tags'
,
'!~'
,
'x-complete'
);
// full text search (only works with cache enabled)
if
(
$filter
[
'search'
])
{
$search
=
mb_strtolower
(
$filter
[
'search'
]);
foreach
(
rcube_utils
::
normalize_string
(
$search
,
true
)
as
$word
)
{
$query
[]
=
array
(
'words'
,
'~'
,
$word
);
}
}
if
(
$filter
[
'since'
])
{
$query
[]
=
array
(
'changed'
,
'>='
,
$filter
[
'since'
]);
}
foreach
(
$lists
as
$list_id
)
{
if
(!
$folder
=
$this
->
get_folder
(
$list_id
))
{
continue
;
}
foreach
(
$folder
->
select
(
$query
)
as
$record
)
{
$task
=
$this
->
_to_rcube_task
(
$record
);
$task
[
'list'
]
=
$list_id
;
// TODO: post-filter tasks returned from storage
$results
[]
=
$task
;
}
}
// avoid session race conditions that will loose temporary subscriptions
$this
->
plugin
->
rc
->
session
->
nowrite
=
true
;
return
$results
;
}
/**
* Return data of a specific task
*
* @param mixed Hash array with task properties or task UID
* @return array Hash array with task properties or false if not found
*/
public
function
get_task
(
$prop
)
{
$id
=
is_array
(
$prop
)
?
(
$prop
[
'uid'
]
?:
$prop
[
'id'
])
:
$prop
;
$list_id
=
is_array
(
$prop
)
?
$prop
[
'list'
]
:
null
;
$folders
=
$list_id
?
array
(
$list_id
=>
$this
->
get_folder
(
$list_id
))
:
$this
->
folders
;
// find task in the available folders
foreach
(
$folders
as
$list_id
=>
$folder
)
{
if
(
is_numeric
(
$list_id
)
||
!
$folder
)
continue
;
if
(!
$this
->
tasks
[
$id
]
&&
(
$object
=
$folder
->
get_object
(
$id
)))
{
$this
->
tasks
[
$id
]
=
$this
->
_to_rcube_task
(
$object
);
$this
->
tasks
[
$id
][
'list'
]
=
$list_id
;
break
;
}
}
return
$this
->
tasks
[
$id
];
}
/**
* Get all decendents of the given task record
*
* @param mixed Hash array with task properties or task UID
* @param boolean True if all childrens children should be fetched
* @return array List of all child task IDs
*/
public
function
get_childs
(
$prop
,
$recursive
=
false
)
{
if
(
is_string
(
$prop
))
{
$task
=
$this
->
get_task
(
$prop
);
$prop
=
array
(
'id'
=>
$task
[
'id'
],
'list'
=>
$task
[
'list'
]);
}
$childs
=
array
();
$list_id
=
$prop
[
'list'
];
$task_ids
=
array
(
$prop
[
'id'
]);
$folder
=
$this
->
get_folder
(
$list_id
);
// query for childs (recursively)
while
(
$folder
&&
!
empty
(
$task_ids
))
{
$query_ids
=
array
();
foreach
(
$task_ids
as
$task_id
)
{
$query
=
array
(
array
(
'tags'
,
'='
,
'x-parent:'
.
$task_id
));
foreach
(
$folder
->
select
(
$query
)
as
$record
)
{
// don't rely on kolab_storage_folder filtering
if
(
$record
[
'parent_id'
]
==
$task_id
)
{
$childs
[]
=
$record
[
'uid'
];
$query_ids
[]
=
$record
[
'uid'
];
}
}
}
if
(!
$recursive
)
break
;
$task_ids
=
$query_ids
;
}
return
$childs
;
}
/**
* Get a list of pending alarms to be displayed to the user
*
* @param integer Current time (unix timestamp)
* @param mixed 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
* @see tasklist_driver::pending_alarms()
*/
public
function
pending_alarms
(
$time
,
$lists
=
null
)
{
$interval
=
300
;
$time
-=
$time
%
60
;
$slot
=
$time
;
$slot
-=
$slot
%
$interval
;
$last
=
$time
-
max
(
60
,
$this
->
rc
->
config
->
get
(
'refresh_interval'
,
0
));
$last
-=
$last
%
$interval
;
// only check for alerts once in 5 minutes
if
(
$last
==
$slot
)
return
array
();
if
(
$lists
&&
is_string
(
$lists
))
$lists
=
explode
(
','
,
$lists
);
$time
=
$slot
+
$interval
;
$candidates
=
array
();
$query
=
array
(
array
(
'tags'
,
'='
,
'x-has-alarms'
),
array
(
'tags'
,
'!='
,
'x-complete'
));
foreach
(
$this
->
lists
as
$lid
=>
$list
)
{
// skip lists with alarms disabled
if
(!
$list
[
'showalarms'
]
||
(
$lists
&&
!
in_array
(
$lid
,
$lists
)))
continue
;
$folder
=
$this
->
get_folder
(
$lid
);
foreach
(
$folder
->
select
(
$query
)
as
$record
)
{
if
(!(
$record
[
'valarms'
]
||
$record
[
'alarms'
])
||
$record
[
'status'
]
==
'COMPLETED'
||
$record
[
'complete'
]
==
100
)
// don't trust query :-)
continue
;
$task
=
$this
->
_to_rcube_task
(
$record
);
// add to list if alarm is set
$alarm
=
libcalendaring
::
get_next_alarm
(
$task
,
'task'
);
if
(
$alarm
&&
$alarm
[
'time'
]
&&
$alarm
[
'time'
]
<=
$time
&&
in_array
(
$alarm
[
'action'
],
$this
->
alarm_types
))
{
$id
=
$alarm
[
'id'
];
// use alarm-id as primary identifier
$candidates
[
$id
]
=
array
(
'id'
=>
$id
,
'title'
=>
$task
[
'title'
],
'date'
=>
$task
[
'date'
],
'time'
=>
$task
[
'time'
],
'notifyat'
=>
$alarm
[
'time'
],
'action'
=>
$alarm
[
'action'
],
);
}
}
}
// get alarm information stored in local database
if
(!
empty
(
$candidates
))
{
$alarm_ids
=
array_map
(
array
(
$this
->
rc
->
db
,
'quote'
),
array_keys
(
$candidates
));
$result
=
$this
->
rc
->
db
->
query
(
sprintf
(
"SELECT * FROM kolab_alarms
WHERE alarm_id IN (%s) AND user_id=?"
,
join
(
','
,
$alarm_ids
),
$this
->
rc
->
db
->
now
()
),
$this
->
rc
->
user
->
ID
);
while
(
$result
&&
(
$rec
=
$this
->
rc
->
db
->
fetch_assoc
(
$result
)))
{
$dbdata
[
$rec
[
'alarm_id'
]]
=
$rec
;
}
}
$alarms
=
array
();
foreach
(
$candidates
as
$id
=>
$task
)
{
// skip dismissed
if
(
$dbdata
[
$id
][
'dismissed'
])
continue
;
// snooze function may have shifted alarm time
$notifyat
=
$dbdata
[
$id
][
'notifyat'
]
?
strtotime
(
$dbdata
[
$id
][
'notifyat'
])
:
$task
[
'notifyat'
];
if
(
$notifyat
<=
$time
)
$alarms
[]
=
$task
;
}
return
$alarms
;
}
/**
* (User) feedback after showing an alarm notification
* This should mark the alarm as 'shown' or snooze it for the given amount of time
*
* @param string Task identifier
* @param integer Suspend the alarm for this number of seconds
*/
public
function
dismiss_alarm
(
$id
,
$snooze
=
0
)
{
// delete old alarm entry
$this
->
rc
->
db
->
query
(
"DELETE FROM kolab_alarms
WHERE alarm_id=? AND user_id=?"
,
$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
(
"INSERT INTO kolab_alarms
(alarm_id, user_id, dismissed, notifyat)
VALUES(?, ?, ?, ?)"
,
$id
,
$this
->
rc
->
user
->
ID
,
$snooze
>
0
?
0
:
1
,
$notifyat
);
return
$this
->
rc
->
db
->
affected_rows
(
$query
);
}
/**
* Remove alarm dismissal or snooze state
*
* @param string Task identifier
*/
public
function
clear_alarms
(
$id
)
{
// delete alarm entry
$this
->
rc
->
db
->
query
(
"DELETE FROM kolab_alarms
WHERE alarm_id=? AND user_id=?"
,
$id
,
$this
->
rc
->
user
->
ID
);
return
true
;
}
/**
* Convert from Kolab_Format to internal representation
*/
private
function
_to_rcube_task
(
$record
)
{
$task
=
array
(
'id'
=>
$record
[
'uid'
],
'uid'
=>
$record
[
'uid'
],
'title'
=>
$record
[
'title'
],
# 'location' => $record['location'],
'description'
=>
$record
[
'description'
],
'tags'
=>
array_filter
((
array
)
$record
[
'categories'
]),
'flagged'
=>
$record
[
'priority'
]
==
1
,
'complete'
=>
floatval
(
$record
[
'complete'
]
/
100
),
'status'
=>
$record
[
'status'
],
'parent_id'
=>
$record
[
'parent_id'
],
'recurrence'
=>
$record
[
'recurrence'
],
);
// convert from DateTime to internal date format
if
(
is_a
(
$record
[
'due'
],
'DateTime'
))
{
$due
=
$this
->
plugin
->
lib
->
adjust_timezone
(
$record
[
'due'
]);
$task
[
'date'
]
=
$due
->
format
(
'Y-m-d'
);
if
(!
$record
[
'due'
]->
_dateonly
)
$task
[
'time'
]
=
$due
->
format
(
'H:i'
);
}
// convert from DateTime to internal date format
if
(
is_a
(
$record
[
'start'
],
'DateTime'
))
{
$start
=
$this
->
plugin
->
lib
->
adjust_timezone
(
$record
[
'start'
]);
$task
[
'startdate'
]
=
$start
->
format
(
'Y-m-d'
);
if
(!
$record
[
'start'
]->
_dateonly
)
$task
[
'starttime'
]
=
$start
->
format
(
'H:i'
);
}
if
(
is_a
(
$record
[
'dtstamp'
],
'DateTime'
))
{
$task
[
'changed'
]
=
$record
[
'dtstamp'
];
}
if
(
$record
[
'valarms'
])
{
$task
[
'valarms'
]
=
$record
[
'valarms'
];
}
else
if
(
$record
[
'alarms'
])
{
$task
[
'alarms'
]
=
$record
[
'alarms'
];
}
if
(!
empty
(
$record
[
'_attachments'
]))
{
foreach
(
$record
[
'_attachments'
]
as
$key
=>
$attachment
)
{
if
(
$attachment
!==
false
)
{
if
(!
$attachment
[
'name'
])
$attachment
[
'name'
]
=
$key
;
$attachments
[]
=
$attachment
;
}
}
$task
[
'attachments'
]
=
$attachments
;
}
return
$task
;
}
/**
* 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())
*/
private
function
_from_rcube_task
(
$task
,
$old
=
array
())
{
$object
=
$task
;
$object
[
'categories'
]
=
(
array
)
$task
[
'tags'
];
if
(!
empty
(
$task
[
'date'
]))
{
$object
[
'due'
]
=
new
DateTime
(
$task
[
'date'
].
' '
.
$task
[
'time'
],
$this
->
plugin
->
timezone
);
if
(
empty
(
$task
[
'time'
]))
$object
[
'due'
]->
_dateonly
=
true
;
unset
(
$object
[
'date'
]);
}
if
(!
empty
(
$task
[
'startdate'
]))
{
$object
[
'start'
]
=
new
DateTime
(
$task
[
'startdate'
].
' '
.
$task
[
'starttime'
],
$this
->
plugin
->
timezone
);
if
(
empty
(
$task
[
'starttime'
]))
$object
[
'start'
]->
_dateonly
=
true
;
unset
(
$object
[
'startdate'
]);
}
$object
[
'complete'
]
=
$task
[
'complete'
]
*
100
;
if
(
$task
[
'complete'
]
==
1.0
&&
empty
(
$task
[
'complete'
]))
$object
[
'status'
]
=
'COMPLETED'
;
if
(
$task
[
'flagged'
])
$object
[
'priority'
]
=
1
;
else
$object
[
'priority'
]
=
$old
[
'priority'
]
>
1
?
$old
[
'priority'
]
:
0
;
// copy meta data (starting with _) from old object
foreach
((
array
)
$old
as
$key
=>
$val
)
{
if
(!
isset
(
$object
[
$key
])
&&
$key
[
0
]
==
'_'
)
$object
[
$key
]
=
$val
;
}
// copy recurrence rules if the client didn't submit it (#2713)
if
(!
array_key_exists
(
'recurrence'
,
$object
)
&&
$old
[
'recurrence'
])
{
$object
[
'recurrence'
]
=
$old
[
'recurrence'
];
}
// delete existing attachment(s)
if
(!
empty
(
$task
[
'deleted_attachments'
]))
{
foreach
(
$task
[
'deleted_attachments'
]
as
$attachment
)
{
if
(
is_array
(
$object
[
'_attachments'
]))
{
foreach
(
$object
[
'_attachments'
]
as
$idx
=>
$att
)
{
if
(
$att
[
'id'
]
==
$attachment
)
$object
[
'_attachments'
][
$idx
]
=
false
;
}
}
}
unset
(
$task
[
'deleted_attachments'
]);
}
// in kolab_storage attachments are indexed by content-id
if
(
is_array
(
$task
[
'attachments'
]))
{
foreach
(
$task
[
'attachments'
]
as
$idx
=>
$attachment
)
{
$key
=
null
;
// Roundcube ID has nothing to do with the storage ID, remove it
if
(
$attachment
[
'content'
])
{
unset
(
$attachment
[
'id'
]);
}
else
{
foreach
((
array
)
$old
[
'_attachments'
]
as
$cid
=>
$oldatt
)
{
if
(
$oldatt
&&
$attachment
[
'id'
]
==
$oldatt
[
'id'
])
$key
=
$cid
;
}
}
// replace existing entry
if
(
$key
)
{
$object
[
'_attachments'
][
$key
]
=
$attachment
;
}
// append as new attachment
else
{
$object
[
'_attachments'
][]
=
$attachment
;
}
}
unset
(
$object
[
'attachments'
]);
}
unset
(
$object
[
'tempid'
],
$object
[
'raw'
],
$object
[
'list'
],
$object
[
'flagged'
],
$object
[
'tags'
]);
return
$object
;
}
/**
* Add a single task to the database
*
* @param array Hash array with task properties (see header of tasklist_driver.php)
* @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
*
* @param array Hash array with task properties (see header of tasklist_driver.php)
* @return boolean True on success, False on error
*/
public
function
edit_task
(
$task
)
{
$list_id
=
$task
[
'list'
];
if
(!
$list_id
||
!(
$folder
=
$this
->
get_folder
(
$list_id
)))
return
false
;
// moved from another folder
if
(
$task
[
'_fromlist'
]
&&
(
$fromfolder
=
$this
->
get_folder
(
$task
[
'_fromlist'
])))
{
if
(!
$fromfolder
->
move
(
$task
[
'id'
],
$folder
->
name
))
return
false
;
unset
(
$task
[
'_fromlist'
]);
}
// load previous version of this task to merge
if
(
$task
[
'id'
])
{
$old
=
$folder
->
get_object
(
$task
[
'id'
]);
if
(!
$old
||
PEAR
::
isError
(
$old
))
return
false
;
// merge existing properties if the update isn't complete
if
(!
isset
(
$task
[
'title'
])
||
!
isset
(
$task
[
'complete'
]))
$task
+=
$this
->
_to_rcube_task
(
$old
);
}
// generate new task object from RC input
$object
=
$this
->
_from_rcube_task
(
$task
,
$old
);
$saved
=
$folder
->
save
(
$object
,
'task'
,
$task
[
'id'
]);
if
(!
$saved
)
{
raise_error
(
array
(
'code'
=>
600
,
'type'
=>
'php'
,
'file'
=>
__FILE__
,
'line'
=>
__LINE__
,
'message'
=>
"Error saving task object to Kolab server"
),
true
,
false
);
$saved
=
false
;
}
else
{
$task
=
$this
->
_to_rcube_task
(
$object
);
$task
[
'list'
]
=
$list_id
;
$this
->
tasks
[
$task
[
'id'
]]
=
$task
;
}
return
$saved
;
}
/**
* Move a single task to another list
*
* @param array Hash array with task properties:
* @return boolean True on success, False on error
* @see tasklist_driver::move_task()
*/
public
function
move_task
(
$task
)
{
$list_id
=
$task
[
'list'
];
if
(!
$list_id
||
!(
$folder
=
$this
->
get_folder
(
$list_id
)))
return
false
;
// execute move command
if
(
$task
[
'_fromlist'
]
&&
(
$fromfolder
=
$this
->
get_folder
(
$task
[
'_fromlist'
])))
{
return
$fromfolder
->
move
(
$task
[
'id'
],
$folder
->
name
);
}
return
false
;
}
/**
* Remove a single task from the database
*
* @param array Hash array with task properties:
* id: Task identifier
* @param boolean Remove record irreversible (mark as deleted otherwise, if supported by the backend)
* @return boolean True on success, False on error
*/
public
function
delete_task
(
$task
,
$force
=
true
)
{
$list_id
=
$task
[
'list'
];
if
(!
$list_id
||
!(
$folder
=
$this
->
get_folder
(
$list_id
)))
return
false
;
return
$folder
->
delete
(
$task
[
'id'
]);
}
/**
* Restores a single deleted task (if supported)
*
* @param array Hash array with task properties:
* id: Task identifier
* @return boolean True on success, False on error
*/
public
function
undelete_task
(
$prop
)
{
// TODO: implement this
return
false
;
}
/**
* Get attachment properties
*
* @param string $id Attachment identifier
* @param array $task Hash array with event properties:
* id: Task identifier
* list: List identifier
*
* @return array Hash array with attachment properties:
* id: Attachment identifier
* name: Attachment name
* mimetype: MIME content type of the attachment
* size: Attachment size
*/
public
function
get_attachment
(
$id
,
$task
)
{
$task
[
'uid'
]
=
$task
[
'id'
];
$task
=
$this
->
get_task
(
$task
);
if
(
$task
&&
!
empty
(
$task
[
'attachments'
]))
{
foreach
(
$task
[
'attachments'
]
as
$att
)
{
if
(
$att
[
'id'
]
==
$id
)
return
$att
;
}
}
return
null
;
}
/**
* Get attachment body
*
* @param string $id Attachment identifier
* @param array $task Hash array with event properties:
* id: Task identifier
* list: List identifier
*
* @return string Attachment body
*/
public
function
get_attachment_body
(
$id
,
$task
)
{
if
(
$storage
=
$this
->
get_folder
(
$task
[
'list'
]))
{
return
$storage
->
get_attachment
(
$task
[
'id'
],
$id
);
}
return
false
;
}
/**
*
*/
public
function
tasklist_edit_form
(
$action
,
$list
,
$fieldprop
)
{
if
(
$list
[
'id'
]
&&
(
$list
=
$this
->
lists
[
$list
[
'id'
]]))
{
$folder_name
=
$this
->
get_folder
(
$list
[
'id'
])->
name
;
// UTF7
}
else
{
$folder_name
=
''
;
}
$storage
=
$this
->
rc
->
get_storage
();
$delim
=
$storage
->
get_hierarchy_delimiter
();
$form
=
array
();
if
(
strlen
(
$folder_name
))
{
$path_imap
=
explode
(
$delim
,
$folder_name
);
array_pop
(
$path_imap
);
// pop off name part
$path_imap
=
implode
(
$path_imap
,
$delim
);
$options
=
$storage
->
folder_info
(
$folder_name
);
}
else
{
$path_imap
=
''
;
}
$hidden_fields
[]
=
array
(
'name'
=>
'oldname'
,
'value'
=>
$folder_name
);
// folder name (default field)
$input_name
=
new
html_inputfield
(
array
(
'name'
=>
'name'
,
'id'
=>
'taskedit-tasklistame'
,
'size'
=>
20
));
$fieldprop
[
'name'
][
'value'
]
=
$input_name
->
show
(
$list
[
'editname'
],
array
(
'disabled'
=>
(
$options
[
'norename'
]
||
$options
[
'protected'
])));
// prevent user from moving folder
if
(!
empty
(
$options
)
&&
(
$options
[
'norename'
]
||
$options
[
'protected'
]))
{
$hidden_fields
[]
=
array
(
'name'
=>
'parent'
,
'value'
=>
$path_imap
);
}
else
{
$select
=
kolab_storage
::
folder_selector
(
'task'
,
array
(
'name'
=>
'parent'
,
'id'
=>
'taskedit-parentfolder'
),
$folder_name
);
$fieldprop
[
'parent'
]
=
array
(
'id'
=>
'taskedit-parentfolder'
,
'label'
=>
$this
->
plugin
->
gettext
(
'parentfolder'
),
'value'
=>
$select
->
show
(
$path_imap
),
);
}
// General tab
$form
[
'properties'
]
=
array
(
'name'
=>
$this
->
rc
->
gettext
(
'properties'
),
'fields'
=>
array
(),
);
foreach
(
array
(
'name'
,
'parent'
,
'showalarms'
)
as
$f
)
{
$form
[
'properties'
][
'fields'
][
$f
]
=
$fieldprop
[
$f
];
}
// add folder ACL tab
if
(
$action
!=
'form-new'
)
{
$form
[
'sharing'
]
=
array
(
'name'
=>
Q
(
$this
->
plugin
->
gettext
(
'tabsharing'
)),
'content'
=>
html
::
tag
(
'iframe'
,
array
(
'src'
=>
$this
->
rc
->
url
(
array
(
'_action'
=>
'folder-acl'
,
'_folder'
=>
$folder_name
,
'framed'
=>
1
)),
'width'
=>
'100%'
,
'height'
=>
280
,
'border'
=>
0
,
'style'
=>
'border:0'
),
''
)
);
}
$form_html
=
''
;
if
(
is_array
(
$hidden_fields
))
{
foreach
(
$hidden_fields
as
$field
)
{
$hiddenfield
=
new
html_hiddenfield
(
$field
);
$form_html
.=
$hiddenfield
->
show
()
.
"
\n
"
;
}
}
// create form output
foreach
(
$form
as
$tab
)
{
if
(
is_array
(
$tab
[
'fields'
])
&&
empty
(
$tab
[
'content'
]))
{
$table
=
new
html_table
(
array
(
'cols'
=>
2
));
foreach
(
$tab
[
'fields'
]
as
$col
=>
$colprop
)
{
$label
=
!
empty
(
$colprop
[
'label'
])
?
$colprop
[
'label'
]
:
$this
->
plugin
->
gettext
(
$col
);
$table
->
add
(
'title'
,
html
::
label
(
$colprop
[
'id'
],
Q
(
$label
)));
$table
->
add
(
null
,
$colprop
[
'value'
]);
}
$content
=
$table
->
show
();
}
else
{
$content
=
$tab
[
'content'
];
}
if
(!
empty
(
$content
))
{
$form_html
.=
html
::
tag
(
'fieldset'
,
null
,
html
::
tag
(
'legend'
,
null
,
Q
(
$tab
[
'name'
]))
.
$content
)
.
"
\n
"
;
}
}
return
$form_html
;
}
/**
* Handler to render ACL form for a notes folder
*/
public
function
folder_acl
()
{
$this
->
plugin
->
require_plugin
(
'acl'
);
$this
->
rc
->
output
->
add_handler
(
'folderacl'
,
array
(
$this
,
'folder_acl_form'
));
$this
->
rc
->
output
->
send
(
'tasklist.kolabacl'
);
}
/**
* Handler for ACL form template object
*/
public
function
folder_acl_form
()
{
$folder
=
rcube_utils
::
get_input_value
(
'_folder'
,
RCUBE_INPUT_GPC
);
if
(
strlen
(
$folder
))
{
$storage
=
$this
->
rc
->
get_storage
();
$options
=
$storage
->
folder_info
(
$folder
);
// get sharing UI from acl plugin
$acl
=
$this
->
rc
->
plugins
->
exec_hook
(
'folder_form'
,
array
(
'form'
=>
array
(),
'options'
=>
$options
,
'name'
=>
$folder
));
}
return
$acl
[
'form'
][
'sharing'
][
'content'
]
?:
html
::
div
(
'hint'
,
$this
->
plugin
->
gettext
(
'aclnorights'
));
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Fri, May 22, 4:27 AM (1 d, 21 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
761004
Default Alt Text
tasklist_kolab_driver.php (40 KB)
Attached To
Mode
R14 roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline
Log In to Comment