Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117799788
D165.1775266899.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
17 KB
Referenced Files
None
Subscribers
None
D165.1775266899.diff
View Options
diff --git a/plugins/tasklist/drivers/database/tasklist_database_driver.php b/plugins/tasklist/drivers/database/tasklist_database_driver.php
--- a/plugins/tasklist/drivers/database/tasklist_database_driver.php
+++ b/plugins/tasklist/drivers/database/tasklist_database_driver.php
@@ -332,6 +332,10 @@
$sql_add .= ' AND changed >= ' . $this->rc->db->quote(date('Y-m-d H:i:s', $filter['since']));
}
+ if ($filter['uid']) {
+ $sql_add .= ' AND `uid` IN (' . implode(',', array_map(array($this->rc->db, 'quote'), $filter['uid'])) . ')');
+ }
+
$tasks = array();
if (!empty($list_ids)) {
$result = $this->rc->db->query(sprintf(
diff --git a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
--- a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
+++ b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
@@ -550,6 +550,7 @@
* - from: Date range start as string (Y-m-d)
* - to: Date range end as string (Y-m-d)
* - search: Search query string
+ * - uid: Task UIDs
* @param array List of lists to get tasks from
* @return array List of tasks records matchin the criteria
*/
@@ -585,6 +586,10 @@
$query[] = array('changed', '>=', $filter['since']);
}
+ if ($filter['uid']) {
+ $query[] = array('uid', '=', (array) $filter['uid']);
+ }
+
foreach ($lists as $list_id) {
if (!$folder = $this->get_folder($list_id)) {
continue;
diff --git a/plugins/tasklist/localization/en_US.inc b/plugins/tasklist/localization/en_US.inc
--- a/plugins/tasklist/localization/en_US.inc
+++ b/plugins/tasklist/localization/en_US.inc
@@ -13,6 +13,10 @@
$labels['lists'] = 'Tasklists';
$labels['list'] = 'Tasklist';
$labels['tags'] = 'Tags';
+$labels['export'] = 'Export';
+$labels['exporttitle'] = 'Export to iCalendar';
+$labels['exportattachments'] = 'With attachments';
+$labels['currentview'] = 'current view';
$labels['tasklistsubscribe'] = 'List permanently';
$labels['listsearchresults'] = 'Available Tasklists';
$labels['findlists'] = 'Find tasklists...';
diff --git a/plugins/tasklist/skins/larry/buttons.png b/plugins/tasklist/skins/larry/buttons.png
deleted file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 0
Hc$@<O00001
diff --git a/plugins/tasklist/skins/larry/images/buttons.png b/plugins/tasklist/skins/larry/images/buttons.png
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 0
Hc$@<O00001
diff --git a/plugins/tasklist/skins/larry/sprites.png b/plugins/tasklist/skins/larry/images/sprites.png
rename from plugins/tasklist/skins/larry/sprites.png
rename to plugins/tasklist/skins/larry/images/sprites.png
diff --git a/plugins/tasklist/skins/larry/tasklist.css b/plugins/tasklist/skins/larry/tasklist.css
--- a/plugins/tasklist/skins/larry/tasklist.css
+++ b/plugins/tasklist/skins/larry/tasklist.css
@@ -11,7 +11,7 @@
*/
#taskbar a.button-tasklist span.button-inner {
- background-image: url(buttons.png);
+ background-image: url(images/buttons.png);
background-position: 0 0;
}
@@ -22,10 +22,22 @@
ul.toolbarmenu li span.icon.taskadd,
#attachmentmenu li a.tasklistlink span.icon.taskadd {
- background-image: url(buttons.png);
+ background-image: url(images/buttons.png);
background-position: -4px -90px;
}
+#taskstoolbar a.button.export {
+ background-image: url(images/buttons.png);
+ background-position: center -179px;
+ min-width: 50px;
+ max-width: 70px;
+}
+
+#taskstoolbar a.button.import {
+ background-image: url(images/buttons.png);
+ background-position: center -139px;
+}
+
#taskedit.uidialog,
.tasklistview div.uidialog {
display: none;
@@ -296,7 +308,7 @@
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- background: url(sprites.png) right 20px no-repeat;
+ background: url(images/sprites.png) right 20px no-repeat;
}
.quickview-active #tasklistsbox .treelist li input,
@@ -341,7 +353,7 @@
height: 16px;
padding: 0;
margin-right: 4px;
- background: url(sprites.png) -200px 0 no-repeat;
+ background: url(images/sprites.png) -200px 0 no-repeat;
overflow: hidden;
text-indent: -5000px;
cursor: pointer;
@@ -461,7 +473,7 @@
}
#taskstoolbar a.button.newtask {
- background-image: url(buttons.png);
+ background-image: url(images/buttons.png);
background-position: center -53px;
}
@@ -547,7 +559,7 @@
.buttonbar-right a.iconbutton {
padding: 0;
- background-image: url(sprites.png);
+ background-image: url(images/sprites.png);
background-position: 0 -238px;
}
@@ -599,7 +611,7 @@
width: 14px;
height: 14px;
- background: url(sprites.png) -2px -80px no-repeat;
+ background: url(images/sprites.png) -2px -80px no-repeat;
text-indent: -1000px;
overflow: hidden;
}
@@ -648,7 +660,7 @@
display: inline-block;
width: 16px;
height: 16px;
- background: url(sprites.png) 1000px -3px no-repeat;
+ background: url(images/sprites.png) 1000px -3px no-repeat;
margin: -3px 1em 0 0;
vertical-align: middle;
cursor: pointer;
@@ -730,7 +742,7 @@
right: 6px;
width: 18px;
height: 18px;
- background: url(sprites.png) 1000px -80px no-repeat;
+ background: url(images/sprites.png) 1000px -80px no-repeat;
text-indent: -5000px;
overflow: hidden;
cursor: pointer;
@@ -772,7 +784,7 @@
ul.toolbarmenu li span.collapse,
ul.toolbarmenu li span.history,
ul.toolbarmenu.iconized .selected span.icon {
- background-image: url(sprites.png);
+ background-image: url(images/sprites.png);
}
ul.toolbarmenu li span.add {
@@ -1162,7 +1174,7 @@
#taskedit-links .attachmentslist li.message.eml,
#task-links .attachmentslist li.message.eml {
- background-image: url(sprites.png);
+ background-image: url(images/sprites.png);
background-position: -2px -388px;
}
@@ -1411,7 +1423,7 @@
left: 8px;
width: 18px;
height: 18px;
- background: url(buttons.png) -6px -115px no-repeat;
+ background: url(images/buttons.png) -6px -115px no-repeat;
}
div.messagetasklinks ul.tasklist {
diff --git a/plugins/tasklist/skins/larry/templates/mainview.html b/plugins/tasklist/skins/larry/templates/mainview.html
--- a/plugins/tasklist/skins/larry/templates/mainview.html
+++ b/plugins/tasklist/skins/larry/templates/mainview.html
@@ -17,6 +17,7 @@
<div id="taskstoolbar" class="toolbar" role="toolbar" aria-labelledby="aria-label-toolbar">
<roundcube:button command="newtask" type="link" class="button newtask disabled" classAct="button newtask" classSel="button newtask pressed" label="tasklist.newtask" title="tasklist.newtask" />
<roundcube:button command="print" type="link" class="button print disabled" classAct="button print" classSel="button print pressed" label="print" title="tasklist.printtitle" />
+ <roundcube:button command="export" type="link" class="button export disabled" classAct="button export" classSel="button export pressed" label="tasklist.export" title="tasklist.exporttitle" />
<roundcube:container name="toolbar" id="taskstoolbar" />
</div>
@@ -320,6 +321,10 @@
<roundcube:container name="tasklistform" id="tasklistform" />
</div>
+<div id="tasksexport" class="uidialog">
+ <roundcube:object name="plugin.tasks_export_form" id="tasks-export-form" />
+</div>
+
<script type="text/javascript">
// UI startup
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -820,6 +820,92 @@
loadstate.lists = active_lists();
}
+ // open a tasks export dialog
+ this.export_tasks = function()
+ {
+ // close show dialog first
+ var $dialog = $("#tasksexport"),
+ form = rcmail.gui_objects.exportform,
+ buttons = {};
+
+ if (!form)
+ return;
+
+ if ($dialog.is(':ui-dialog'))
+ $dialog.dialog('close');
+
+ $("#task-export-list").val('');
+
+ buttons[rcmail.gettext('export', 'tasklist')] = function() {
+ var source = $('#task-export-list option:selected').val();
+
+ // "current view" export, use hidden form to POST task IDs
+ if (source === '') {
+ var cache = {}, tasks = [], inputs = [],
+ postform = $('#tasks-export-form-post');
+
+ $.each(listindex || [], function() {
+ var rec = listdata[this];
+ if (match_filter(rec, cache)) {
+ tasks.push(rec.id);
+ }
+ });
+
+ // copy form inputs, there may be controls added by other plugins
+ $('#tasksexport select, #tasksexport input').each(function() {
+ if (this.type != 'checkbox' || this.checked)
+ inputs.push($('<input>').attr({type: 'hidden', name: this.name, value: this.value}));
+ });
+
+ inputs.push($('<input>').attr({type: 'hidden', name: '_token', value: rcmail.env.request_token}));
+ inputs.push($('<input>').attr({type: 'hidden', name: 'id', value: tasks.join(',')}));
+
+ if (!postform.length)
+ postform = $('<form>')
+ .attr({style: 'display: none', method: 'POST', action: '?_task=tasks&_action=export'})
+ .appendTo('body');
+
+ postform.html('').append(inputs).submit();
+ }
+ // otherwise we can use simple GET
+ else {
+ rcmail.goto_url('export', {source: source, attachments: attach});
+ }
+
+ $dialog.dialog("close");
+ };
+
+ buttons[rcmail.gettext('cancel', 'tasklist')] = function() {
+ $dialog.dialog("close");
+ };
+
+ // open jquery UI dialog
+ $dialog.dialog({
+ modal: true,
+ resizable: false,
+ closeOnEscape: false,
+ title: rcmail.gettext('exporttitle', 'tasklist'),
+ open: function() {
+ $dialog.parent().find('.ui-dialog-buttonset .ui-button').first().addClass('mainaction');
+ },
+ close: function() {
+ $('.ui-dialog-buttonpane button', $dialog.parent()).button('enable');
+ $dialog.dialog("destroy").hide();
+ },
+ buttons: buttons,
+ width: 520
+ }).show();
+ };
+/*
+ // download the selected task as iCal
+ this.task_download = function(task)
+ {
+ if (task && task.id) {
+ rcmail.goto_url('export', {source: task.list, id: task.id, attachments: 1});
+ }
+ };
+*/
+
/**
* Modify query parameters for refresh requests
*/
@@ -3367,6 +3453,7 @@
rcmail.register_command('list-remove', function(){ rctasks.list_remove(rctasks.selected_list); }, false);
rcmail.register_command('list-showurl', function(){ rctasks.list_showurl(rctasks.selected_list); }, false);
+ rcmail.register_command('export', function(){ rctasks.export_tasks(); }, true);
rcmail.register_command('search', function(){ rctasks.quicksearch(); }, true);
rcmail.register_command('reset-search', function(){ rctasks.reset_search(); }, true);
rcmail.register_command('expand-all', function(){ rctasks.expand_collapse(true); }, true);
diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php
--- a/plugins/tasklist/tasklist.php
+++ b/plugins/tasklist/tasklist.php
@@ -119,6 +119,7 @@
$this->register_action('mail2task', array($this, 'mail_message2task'));
$this->register_action('get-attachment', array($this, 'attachment_get'));
$this->register_action('upload', array($this, 'attachment_upload'));
+ $this->register_action('export', array($this, 'export_tasks'));
$this->register_action('mailimportitip', array($this, 'mail_import_itip'));
$this->register_action('mailimportattach', array($this, 'mail_import_attachment'));
$this->register_action('itip-status', array($this, 'task_itip_status'));
@@ -1578,6 +1579,79 @@
return $p;
}
+ /**
+ * Construct the ics file for exporting tasks to iCalendar format
+ */
+ function export_tasks()
+ {
+ $source = rcube_utils::get_input_value('source', rcube_utils::INPUT_GPC);
+ $task_id = rcube_utils::get_input_value('id', rcube_utils::INPUT_GPC);
+ $attachments = (bool) rcube_utils::get_input_value('attachments', rcube_utils::INPUT_GPC);
+
+ $this->load_driver();
+
+ $browser = new rcube_browser;
+ $lists = $this->driver->get_lists();
+ $tasks = array();
+ $filter = array();
+
+ // get message UIDs for filter
+ if ($source && ($list = $lists[$source])) {
+ $filename = html_entity_decode($list['name']) ?: $sorce;
+ $filter = array($source => true);
+ }
+ else if ($task_id) {
+ $filename = 'tasks';
+ foreach (explode(',', $task_id) as $id) {
+ list($list_id, $task_id) = explode(':', $id, 2);
+ if ($list_id && $task_id) {
+ $filter[$list_id][] = $task_id;
+ }
+ }
+ }
+
+ // Get tasks
+ foreach ($filter as $list_id => $uids) {
+ $_filter = is_array($uids) ? array('uid' => $uids) : null;
+ $_tasks = $this->driver->list_tasks($_filter, $list_id);
+ if (!empty($_tasks)) {
+ $tasks = array_merge($tasks, $_tasks);
+ }
+ }
+
+ // Set file name
+ if ($source && count($tasks) == 1) {
+ $filename = $tasks[0]['title'] ?: 'task';
+ }
+ $filename .= '.ics';
+ $filename = $browser->ie ? rawurlencode($filename) : addcslashes($filename, '"');
+
+ $tasks = array_map(array($this, 'to_libcal'), $tasks);
+
+ // Give plugins a possibility to implement other output formats or modify the result
+ $plugin = $this->rc->plugins->exec_hook('tasks_export', array(
+ 'result' => $tasks,
+ 'attachments' => $attachments,
+ 'filename' => $filename,
+ 'plugin' => $this,
+ ));
+
+ if ($plugin['abort']) {
+ exit;
+ }
+
+ $this->rc->output->nocacheing_headers();
+
+ // don't kill the connection if download takes more than 30 sec.
+ @set_time_limit(0);
+ header("Content-Type: text/calendar");
+ header("Content-Disposition: inline; filename=\"". $plugin['filename'] ."\"");
+
+ $this->get_ical()->export($plugin['tasks'], '', true,
+ $plugins['attachments'] ? array($this->driver, 'get_attachment_body') : null);
+ exit;
+ }
+
/******* Attachment handling *******/
diff --git a/plugins/tasklist/tasklist_ui.php b/plugins/tasklist/tasklist_ui.php
--- a/plugins/tasklist/tasklist_ui.php
+++ b/plugins/tasklist/tasklist_ui.php
@@ -161,6 +161,7 @@
$this->plugin->register_handler('plugin.edit_attendees_notify', array($this, 'edit_attendees_notify'));
$this->plugin->register_handler('plugin.task_rsvp_buttons', array($this->plugin->itip, 'itip_rsvp_buttons'));
$this->plugin->register_handler('plugin.object_changelog_table', array('libkolab', 'object_changelog_table'));
+ $this->plugin->register_handler('plugin.tasks_export_form', array($this, 'tasks_export_form'));
jqueryui::tagedit();
@@ -310,11 +311,18 @@
*/
function tasklist_select($attrib = array())
{
- $attrib['name'] = 'list';
+ if (empty($attrib['name'])) {
+ $attrib['name'] = 'list';
+ }
+
$attrib['is_escaped'] = true;
$select = new html_select($attrib);
$default = null;
+ foreach ((array) $attrib['extra'] as $id => $name) {
+ $select->add($name, $id);
+ }
+
foreach ((array)$this->plugin->driver->get_lists() as $id => $prop) {
if ($prop['editable'] || strpos($prop['rights'], 'i') !== false) {
$select->add($prop['name'], $id);
@@ -526,6 +534,38 @@
}
/**
+ * Form to select options for exporting tasks
+ */
+ function tasks_export_form($attrib = array())
+ {
+ if (!$attrib['id']) {
+ $attrib['id'] = 'rcmTaskExportForm';
+ }
+
+ $html .= html::div('form-section',
+ html::label('task-export-list', $this->plugin->gettext('list')) .
+ $this->tasklist_select(array(
+ 'name' => 'source',
+ 'id' => 'task-export-list',
+ 'extra' => array('' => '- ' . $this->plugin->gettext('currentview') . ' -'),
+ ))
+ );
+
+ $checkbox = new html_checkbox(array('name' => 'attachments', 'id' => 'task-export-attachments', 'value' => 1));
+ $html .= html::div('form-section',
+ html::label('task-export-attachments', $this->plugin->gettext('exportattachments')) .
+ $checkbox->show(1)
+ );
+
+ $this->register_gui_object('exportform', $attrib['id']);
+
+ return html::tag('form', array('action' => $this->rc->url(array('task' => 'tasklist', 'action' => 'export')),
+ 'method' => "post", 'id' => $attrib['id']),
+ $html
+ );
+ }
+
+ /**
* Wrapper for rcube_output_html::add_gui_object()
*/
function register_gui_object($name, $id)
diff --git a/source/tasklist_buttons.psd b/source/tasklist_buttons.psd
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 0
Hc$@<O00001
diff --git a/source/tasklist_sprites.psd b/source/tasklist_sprites.psd
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 0
Hc$@<O00001
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Apr 4, 1:41 AM (2 h, 15 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18824330
Default Alt Text
D165.1775266899.diff (17 KB)
Attached To
Mode
D165: Implemented Tasks Export (T147, #3861)
Attached
Detach File
Event Timeline