Elastic: Initial (partial) support for tasklist plugin

This commit is contained in:
Aleksander Machniak 2018-03-15 13:27:22 +00:00
parent 7ba3605df9
commit 9400095642
28 changed files with 1595 additions and 938 deletions

View file

@ -1991,6 +1991,8 @@ class calendar extends rcube_plugin
$this->lib->attachment = $attachment;
$this->register_handler('plugin.attachmentframe', array($this->lib, 'attachment_frame'));
$this->register_handler('plugin.attachmentcontrols', array($this->lib, 'attachment_header'));
$this->rc->output->set_env('filename', $attachment['name']);
$this->rc->output->set_env('mimetype', $attachment['mimetype']);
$this->rc->output->send('calendar.attachment');
}
// deliver attachment content

View file

@ -26,7 +26,7 @@
*/
window.rcmail && rcmail.addEventListener('init', function() {
if (rcmail.task == 'mail' || rcmail.task == 'notes') {
if (/^(mail|notes|tasks)$/.test(rcmail.task)) {
var msg_view = rcmail.env.action == 'show' || rcmail.env.action == 'preview';
if (!msg_view && rcmail.env.action) {
@ -124,6 +124,9 @@ function main_list_widget()
if (rcmail.task == 'notes' && rcmail.noteslist)
return rcmail.noteslist;
if (rcmail.task == 'tasks' && rcmail.gui_objects.resultlist)
return rcmail.gui_objects.resultlist;
}
// fills tag cloud with tags list
@ -1157,3 +1160,22 @@ function tag_droppable_accept(draggable)
return true;
}
// Convert list of tag names into "blocks" and add to the specified element
function kolab_tags_text_block(tags, element, del_btn)
{
if (tags && tags.length) {
var items = [];
tags.sort();
$.each(tags, function(i,val) {
var tag = tag_find(val, 'name');
if (tag) {
items.push(tag_box_element(tag, del_btn));
}
});
$(element).append(items);
}
};

View file

@ -23,7 +23,7 @@
class kolab_tags extends rcube_plugin
{
public $task = 'mail|notes';
public $task = 'mail|notes|tasks';
public $rc;
public $home;

View file

@ -28,7 +28,7 @@
$(document).ready(function(e) {
// put tags cloud under folders list
var tagcloud = $('#tagcloud').detach();
$('#folderlist-content,#notebooks-content').children('ul:first').after(tagcloud.show());
$('#folderlist-content,#notebooks-content,#tasklists-content').children('ul:first').after(tagcloud.show());
// add tag message menu positions to Mark menu
var menu = $('#tagmessagemenu li').detach();
@ -36,7 +36,7 @@ $(document).ready(function(e) {
// add tags management menu positions to folder actions menu
menu = $('#tagsmenu li').detach();
$('#mailboxoptions-menu,#notebookactions-menu').find('ul').append(menu);
$('#mailboxoptions-menu,#notebookactions-menu,#tasklistactions-menu').find('ul').append(menu);
// Apply tags colors in Elastic-way
rcmail.addEventListener('kolab-tags-update', function() {

View file

@ -26,7 +26,6 @@
</ul>
</div>
<script type="text/javascript">
$(document).ready(function(e) {
@ -43,6 +42,11 @@ $(document).ready(function(e) {
sidebar: '#sidebar',
list: '#notebooksbox'
};
else if (rcmail.env.task == 'tasks')
containers = {
sidebar: '#sidebar',
list: '#tasklistsbox'
};
if (containers) {
$(containers.sidebar).append(tagcloud.show());

View file

@ -815,6 +815,7 @@ class libcalendaring_itip
'name' => '_comment',
'cols' => 40,
'rows' => 6,
'class' => 'form-control',
'style' => 'display:none',
'placeholder' => $this->gettext('itipcomment')
);

View file

@ -1451,9 +1451,11 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
.addEventListener('plugin.fetch_itip_object_status', rcube_libcalendaring.fetch_itip_object_status)
.addEventListener('plugin.itip_message_processed', rcube_libcalendaring.itip_message_processed);
if (rcmail.env.action == 'get-attachment' && rcmail.gui_objects['attachmentframe']) {
if (rcmail.env.action == 'get-attachment' && rcmail.gui_objects.attachmentframe) {
rcmail.gui_objects.messagepartframe = rcmail.gui_objects.attachmentframe;
rcmail.enable_command('image-scale', 'image-rotate', !!/^image\//.test(rcmail.env.mimetype));
rcmail.register_command('print-attachment', function() {
var frame = rcmail.get_frame_window(rcmail.gui_objects['attachmentframe'].id);
var frame = rcmail.get_frame_window(rcmail.gui_objects.attachmentframe.id);
if (frame) frame.print();
}, true);
}

View file

@ -348,12 +348,12 @@ class libcalendaring extends rcube_plugin
{
unset($attrib['name']);
$input_value = new html_inputfield(array('name' => 'alarmvalue[]', 'class' => 'edit-alarm-value', 'size' => 3));
$input_date = new html_inputfield(array('name' => 'alarmdate[]', 'class' => 'edit-alarm-date', 'size' => 10));
$input_time = new html_inputfield(array('name' => 'alarmtime[]', 'class' => 'edit-alarm-time', 'size' => 6));
$select_type = new html_select(array('name' => 'alarmtype[]', 'class' => 'edit-alarm-type', 'id' => $attrib['id']));
$select_offset = new html_select(array('name' => 'alarmoffset[]', 'class' => 'edit-alarm-offset'));
$select_related = new html_select(array('name' => 'alarmrelated[]', 'class' => 'edit-alarm-related'));
$input_value = new html_inputfield(array('name' => 'alarmvalue[]', 'class' => 'edit-alarm-value form-control', 'size' => 3));
$input_date = new html_inputfield(array('name' => 'alarmdate[]', 'class' => 'edit-alarm-date form-control', 'size' => 10));
$input_time = new html_inputfield(array('name' => 'alarmtime[]', 'class' => 'edit-alarm-time form-control', 'size' => 6));
$select_type = new html_select(array('name' => 'alarmtype[]', 'class' => 'edit-alarm-type form-control', 'id' => $attrib['id']));
$select_offset = new html_select(array('name' => 'alarmoffset[]', 'class' => 'edit-alarm-offset form-control'));
$select_related = new html_select(array('name' => 'alarmrelated[]', 'class' => 'edit-alarm-related form-control'));
$object_type = $attrib['_type'] ?: 'event';
$select_type->add($this->gettext('none'), '');
@ -859,30 +859,35 @@ class libcalendaring extends rcube_plugin
switch ($attrib['part']) {
// frequency selector
case 'frequency':
$select = new html_select(array('name' => 'frequency', 'id' => 'edit-recurrence-frequency'));
$select = new html_select(array('name' => 'frequency', 'id' => 'edit-recurrence-frequency', 'class' => 'form-control'));
$select->add($this->gettext('never'), '');
$select->add($this->gettext('daily'), 'DAILY');
$select->add($this->gettext('weekly'), 'WEEKLY');
$select->add($this->gettext('monthly'), 'MONTHLY');
$select->add($this->gettext('yearly'), 'YEARLY');
$select->add($this->gettext('rdate'), 'RDATE');
$html = html::label('edit-recurrence-frequency', $this->gettext('frequency')) . $select->show('');
$html = html::label(array('for' => 'edit-recurrence-frequency', 'class' => 'col-form-label col-sm-2'), $this->gettext('frequency'))
. html::div('col-sm-10', $select->show(''));
break;
// daily recurrence
case 'daily':
$select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-daily'));
$html = html::div($attrib, html::label('edit-recurrence-interval-daily', $this->gettext('every')) . $select->show(1) . html::span('label-after', $this->gettext('days')));
$select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval form-control', 'id' => 'edit-recurrence-interval-daily'));
$html = html::div($attrib, html::label(array('for' => 'edit-recurrence-interval-daily', 'class' => 'col-form-label col-sm-2'),
$this->gettext('every')) . html::div('col-sm-10', $select->show(1) . html::span('label-after', $this->gettext('days'))));
break;
// weekly recurrence form
case 'weekly':
$select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-weekly'));
$html = html::div($attrib, html::label('edit-recurrence-interval-weekly', $this->gettext('every')) . $select->show(1) . html::span('label-after', $this->gettext('weeks')));
$select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval form-control', 'id' => 'edit-recurrence-interval-weekly'));
$html = html::div($attrib, html::label(array('for' => 'edit-recurrence-interval-weekly', 'class' => 'col-form-label col-sm-2'),
$this->gettext('every')) . html::div('col-sm-10', $select->show(1) . html::span('label-after', $this->gettext('weeks'))));
// weekday selection
$daymap = array('sun','mon','tue','wed','thu','fri','sat');
$daymap = array('sun','mon','tue','wed','thu','fri','sat');
$checkbox = new html_checkbox(array('name' => 'byday', 'class' => 'edit-recurrence-weekly-byday'));
$first = $this->rc->config->get('calendar_first_day', 1);
$first = $this->rc->config->get('calendar_first_day', 1);
for ($weekdays = '', $j = $first; $j <= $first+6; $j++) {
$d = $j % 7;
$weekdays .= html::label(array('class' => 'weekday'),
@ -890,13 +895,16 @@ class libcalendaring extends rcube_plugin
$this->gettext($daymap[$d])
) . ' ';
}
$html .= html::div($attrib, html::label(null, $this->gettext('bydays')) . $weekdays);
$html .= html::div($attrib, html::label(array('class' => 'col-form-label col-sm-2'), $this->gettext('bydays'))
. html::div('col-sm-10', $weekdays));
break;
// monthly recurrence form
case 'monthly':
$select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-monthly'));
$html = html::div($attrib, html::label('edit-recurrence-interval-monthly', $this->gettext('every')) . $select->show(1) . html::span('label-after', $this->gettext('months')));
$select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval form-control', 'id' => 'edit-recurrence-interval-monthly'));
$html = html::div($attrib, html::label(array('for' => 'edit-recurrence-interval-monthly', 'class' => 'col-form-label col-sm-2'), $this->gettext('every'))
. html::div('col-sm-10', $select->show(1) . html::span('label-after', $this->gettext('months'))));
$checkbox = new html_checkbox(array('name' => 'bymonthday', 'class' => 'edit-recurrence-monthly-bymonthday'));
for ($monthdays = '', $d = 1; $d <= 31; $d++) {
@ -917,26 +925,31 @@ class libcalendaring extends rcube_plugin
// annually recurrence form
case 'yearly':
$select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval', 'id' => 'edit-recurrence-interval-yearly'));
$html = html::div($attrib, html::label('edit-recurrence-interval-yearly', $this->gettext('every')) . $select->show(1) . html::span('label-after', $this->gettext('years')));
$select = $this->interval_selector(array('name' => 'interval', 'class' => 'edit-recurrence-interval form-control', 'id' => 'edit-recurrence-interval-yearly'));
$html = html::div($attrib, html::label(array('for' => 'edit-recurrence-interval-yearly', 'class' => 'col-form-label col-sm-2'), $this->gettext('every'))
. html::div('col-sm-10', $select->show(1) . html::span('label-after', $this->gettext('years'))));
// month selector
$monthmap = array('','jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec');
$checkbox = new html_checkbox(array('name' => 'bymonth', 'class' => 'edit-recurrence-yearly-bymonth'));
for ($months = '', $m = 1; $m <= 12; $m++) {
$months .= html::label(array('class' => 'month'), $checkbox->show(null, array('value' => $m)) . $this->gettext($monthmap[$m]));
$months .= $m % 4 ? ' ' : html::br();
}
$html .= html::div($attrib + array('id' => 'edit-recurrence-yearly-bymonthblock'), $months);
// day rule selection
$html .= html::div($attrib, html::label(null, $this->gettext('onevery')) . $this->rrule_selectors($attrib['part'], '---'));
$html .= html::div($attrib, html::label(array('col-form-label col-sm-2'), $this->gettext('onevery'))
. html::div('col-sm-10', $this->rrule_selectors($attrib['part'], '---')));
break;
// end of recurrence form
case 'until':
$radio = new html_radiobutton(array('name' => 'repeat', 'class' => 'edit-recurrence-until'));
$select = $this->interval_selector(array('name' => 'times', 'id' => 'edit-recurrence-repeat-times'));
$input = new html_inputfield(array('name' => 'untildate', 'id' => 'edit-recurrence-enddate', 'size' => "10"));
$radio = new html_radiobutton(array('name' => 'repeat', 'class' => 'edit-recurrence-until'));
$select = $this->interval_selector(array('name' => 'times', 'id' => 'edit-recurrence-repeat-times', 'class' => 'form-control'));
$input = new html_inputfield(array('name' => 'untildate', 'id' => 'edit-recurrence-enddate', 'size' => "10", 'class' => 'form-control'));
$html = html::div('line first',
html::label(null, $radio->show('', array('value' => '', 'id' => 'edit-recurrence-repeat-forever')) . ' ' .
@ -957,12 +970,12 @@ class libcalendaring extends rcube_plugin
$this->gettext('untildate') . ' ' . $input->show('', array('aria-label' => $this->gettext('untilenddate')))
);
$html = html::div($attrib, html::label(null, ucfirst($this->gettext('recurrencend'))) . $html);
$html = html::div($attrib, html::label(array('class' => 'col-form-label col-sm-2'), ucfirst($this->gettext('recurrencend'))) . $html);
break;
case 'rdate':
$ul = html::tag('ul', array('id' => 'edit-recurrence-rdates'), '');
$input = new html_inputfield(array('name' => 'rdate', 'id' => 'edit-recurrence-rdate-input', 'size' => "10"));
$ul = html::tag('ul', array('id' => 'edit-recurrence-rdates'), '');
$input = new html_inputfield(array('name' => 'rdate', 'id' => 'edit-recurrence-rdate-input', 'size' => "10", 'class' => 'form-control'));
$button = new html_inputfield(array('type' => 'button', 'class' => 'button add', 'value' => $this->gettext('addrdate')));
$html .= html::div($attrib, $ul . html::div('inputform', $input->show() . $button->show()));
break;
@ -987,7 +1000,7 @@ class libcalendaring extends rcube_plugin
private function rrule_selectors($part, $noselect = null)
{
// rule selectors
$select_prefix = new html_select(array('name' => 'bydayprefix', 'id' => "edit-recurrence-$part-prefix"));
$select_prefix = new html_select(array('name' => 'bydayprefix', 'id' => "edit-recurrence-$part-prefix", 'class' => 'form-control'));
if ($noselect) $select_prefix->add($noselect, '');
$select_prefix->add(array(
$this->gettext('first'),
@ -998,7 +1011,7 @@ class libcalendaring extends rcube_plugin
),
array(1, 2, 3, 4, -1));
$select_wday = new html_select(array('name' => 'byday', 'id' => "edit-recurrence-$part-byday"));
$select_wday = new html_select(array('name' => 'byday', 'id' => "edit-recurrence-$part-byday", 'class' => 'form-control'));
if ($noselect) $select_wday->add($noselect, '');
$daymap = array('sunday','monday','tuesday','wednesday','thursday','friday','saturday');
@ -1016,8 +1029,9 @@ class libcalendaring extends rcube_plugin
*/
public function to_client_recurrence($recurrence, $allday = false)
{
if ($recurrence['UNTIL'])
if ($recurrence['UNTIL']) {
$recurrence['UNTIL'] = $this->adjust_timezone($recurrence['UNTIL'], $allday)->format('c');
}
// format RDATE values
if (is_array($recurrence['RDATE'])) {
@ -1072,7 +1086,7 @@ class libcalendaring extends rcube_plugin
$this->rc->upload_progress();
}
$recid = $id_prefix . rcube_utils::get_input_value('_id', rcube_utils::INPUT_GPC);
$recid = $id_prefix . rcube_utils::get_input_value('_id', rcube_utils::INPUT_GPC);
$uploadid = rcube_utils::get_input_value('_uploadid', rcube_utils::INPUT_GPC);
if (!is_array($_SESSION[$session_key]) || $_SESSION[$session_key]['id'] != $recid) {
@ -1086,69 +1100,83 @@ class libcalendaring extends rcube_plugin
if (is_array($_FILES['_attachments']['tmp_name'])) {
foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath) {
// Process uploaded attachment if there is no error
$err = $_FILES['_attachments']['error'][$i];
// Process uploaded attachment if there is no error
$err = $_FILES['_attachments']['error'][$i];
if (!$err) {
$attachment = array(
'path' => $filepath,
'size' => $_FILES['_attachments']['size'][$i],
'name' => $_FILES['_attachments']['name'][$i],
'mimetype' => rcube_mime::file_content_type($filepath, $_FILES['_attachments']['name'][$i], $_FILES['_attachments']['type'][$i]),
'group' => $recid,
);
if (!$err) {
$filename = $_FILES['_attachments']['name'][$i];
$attachment = array(
'path' => $filepath,
'size' => $_FILES['_attachments']['size'][$i],
'name' => $filename,
'mimetype' => rcube_mime::file_content_type($filepath, $filename, $_FILES['_attachments']['type'][$i]),
'group' => $recid,
);
$attachment = $this->rc->plugins->exec_hook('attachment_upload', $attachment);
}
$attachment = $this->rc->plugins->exec_hook('attachment_upload', $attachment);
}
if (!$err && $attachment['status'] && !$attachment['abort']) {
$id = $attachment['id'];
if (!$err && $attachment['status'] && !$attachment['abort']) {
$id = $attachment['id'];
// store new attachment in session
unset($attachment['status'], $attachment['abort']);
$_SESSION[$session_key]['attachments'][$id] = $attachment;
// store new attachment in session
unset($attachment['status'], $attachment['abort']);
$this->rc->session->append($session_key . '.attachments', $id, $attachment);
if (($icon = $_SESSION[$session_key . '_deleteicon']) && is_file($icon)) {
$button = html::img(array(
'src' => $icon,
'alt' => $this->rc->gettext('delete')
));
}
else {
$button = rcube::Q($this->rc->gettext('delete'));
}
if (($icon = $_SESSION[$session_key . '_deleteicon']) && is_file($icon)) {
$button = html::img(array(
'src' => $icon,
'alt' => $this->rc->gettext('delete')
));
}
else if ($_SESSION[$session_key . '_textbuttons']) {
$button = rcube::Q($this->rc->gettext('delete'));
}
else {
$button = '';
}
$content = html::a(array(
'href' => "#delete",
'class' => 'delete',
'onclick' => sprintf("return %s.remove_from_attachment_list('rcmfile%s')", rcmail_output::JS_OBJECT_NAME, $id),
'title' => $this->rc->gettext('delete'),
'aria-label' => $this->rc->gettext('delete') . ' ' . $attachment['name'],
), $button);
$link_content = sprintf('<span class="attachment-name">%s</span><span class="attachment-size">(%s)</span>',
rcube::Q($attachment['name']), $this->rc->show_bytes($attachment['size']));
$content .= rcube::Q($attachment['name']);
$delete_link = html::a(array(
'href' => "#delete",
'class' => 'delete',
'onclick' => sprintf("return %s.remove_from_attachment_list('rcmfile%s')", rcmail_output::JS_OBJECT_NAME, $id),
'title' => $this->rc->gettext('delete'),
'aria-label' => $this->rc->gettext('delete') . ' ' . $attachment['name'],
), $button);
$this->rc->output->command('add2attachment_list', "rcmfile$id", array(
'html' => $content,
'name' => $attachment['name'],
'mimetype' => $attachment['mimetype'],
'classname' => rcube_utils::file2class($attachment['mimetype'], $attachment['name']),
'complete' => true), $uploadid);
}
else { // upload failed
if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
$msg = $this->rc->gettext(array('name' => 'filesizeerror', 'vars' => array(
'size' => $this->rc->show_bytes(parse_bytes(ini_get('upload_max_filesize'))))));
}
else if ($attachment['error']) {
$msg = $attachment['error'];
}
else {
$msg = $this->rc->gettext('fileuploaderror');
}
$content_link = html::a(array(
'href' => "#load",
'class' => 'filename',
'onclick' => 'return false', // sprintf("return %s.command('load-attachment','rcmfile%s', this, event)", rcmail_output::JS_OBJECT_NAME, $id),
), $link_content);
$this->rc->output->command('display_message', $msg, 'error');
$this->rc->output->command('remove_from_attachment_list', $uploadid);
$content .= $_SESSION[$session_key . '_icon_pos'] == 'left' ? $delete_link.$content_link : $content_link.$delete_link;
$this->rc->output->command('add2attachment_list', "rcmfile$id", array(
'html' => $content,
'name' => $attachment['name'],
'mimetype' => $attachment['mimetype'],
'classname' => 'no-menu ' . rcube_utils::file2class($attachment['mimetype'], $attachment['name']),
'complete' => true
), $uploadid);
}
else { // upload failed
if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
$msg = $this->rc->gettext(array('name' => 'filesizeerror', 'vars' => array(
'size' => $this->rc->show_bytes(parse_bytes(ini_get('upload_max_filesize'))))));
}
else if ($attachment['error']) {
$msg = $attachment['error'];
}
else {
$msg = $this->rc->gettext('fileuploaderror');
}
$this->rc->output->command('display_message', $msg, 'error');
$this->rc->output->command('remove_from_attachment_list', $uploadid);
}
}
}

View file

@ -42,26 +42,6 @@
margin: 0 .5rem 0 .2rem;
}
}
.count {
position: absolute;
top: 0;
right: 0;
min-width: 2em;
line-height: 1.4rem;
margin: (@listing-line-height - 1.4rem)/2;
padding: 0 .3em;
border-radius: .4em;
background: @color-list-secondary;
color: #fff;
text-align: center;
font-weight: bold;
html.touch & {
line-height: 2rem;
margin: (@listing-touch-line-height - 2rem)/2;
}
}
}
#tagsform option:before,

View file

@ -33,3 +33,211 @@
content: @fa-var-tasks !important;
}
}
@tasklist-record-height: 3.8rem;
#tasklist {
li.taskitem {
border-bottom: 0;
div.taskhead {
position: relative;
display: block;
height: @tasklist-record-height;
padding-left: (1 * @listing-treetoggle-width);
outline: 0;
&.flagged {
color: @color-list-flagged !important;
}
&.selected {
background-color: @color-list-selected-background;
}
}
& > div,
& > span {
border-bottom: 1px solid @color-list-border;
}
ul {
padding: 0;
border: 0;
li {
padding-left: 0;
.taskhead, .title { padding-left: (2 * @listing-treetoggle-width + .25rem); }
.childtoggle { left: @listing-treetoggle-width; }
li {
.taskhead, .title { padding-left: (3 * @listing-treetoggle-width + .25rem); }
.childtoggle { left: (2 * @listing-treetoggle-width); }
li {
.taskhead, .title { padding-left: (4 * @listing-treetoggle-width + .25rem); }
.childtoggle { left: (3 * @listing-treetoggle-width); }
li {
.taskhead, .title { padding-left: (5 * @listing-treetoggle-width + .25rem); }
.childtoggle { left: (4 * @listing-treetoggle-width); }
li {
.taskhead, .title { padding-left: (6 * @listing-treetoggle-width + .25rem); }
.childtoggle { left: (5 * @listing-treetoggle-width); }
}
}
}
}
}
}
}
.childtoggle {
position: absolute;
left: 0;
top: 0;
width: @listing-treetoggle-width;
padding: 0 0 0 .25rem;
height: @tasklist-record-height;
cursor: pointer;
z-index: 1;
.inner {
display: none;
}
&:before {
&:extend(.font-icon-class);
content: @fa-var-angle-right;
margin: 0;
width: 1em;
line-height: @tasklist-record-height;
}
&.expanded:before {
content: @fa-var-angle-down;
}
&.collapsed:before {
content: @fa-var-angle-right;
}
}
span.title {
position: absolute;
left: 0;
right: 0;
bottom: 0;
line-height: 2em;
padding: 0 0 0 .2em;
padding-left: (1 * @listing-treetoggle-width + .25rem);
margin-right: 2em;
.overflow-ellipsis;
}
span.tags {
position: absolute;
right: 2em;
top: 0;
line-height: 2em;
text-align: right;
}
span.date {
font-size: 90%;
line-height: 1.8em;
color: @color-list-secondary;
padding-left: .2em;
}
span.flagged {
position: absolute;
right: 0;
top: 0;
line-height: 1.8em;
cursor: pointer;
}
.taskhead:not(.flagged):hover span.flagged:before {
&:extend(.font-icon-class);
.font-icon-regular(@fa-var-flag);
}
.taskhead.flagged span.flagged:before {
&:extend(.font-icon-class);
content: @fa-var-flag;
}
.progressbar {
position: absolute;
bottom: 1px;
left: .2em;
right: .2em;
.progressvalue {
border-top: 2px solid @color-warning;
}
}
.taskhead.complete .progressbar {
display: none;
}
input + label,
input.complete {
position: absolute;
right: .25rem;
bottom: 0;
top: auto;
line-height: 2em;
padding: 0 .2em 0 0;
cursor: pointer;
z-index: 1;
line-height: 1.8;
}
.actions {
display: none;
}
// Focus indicator
@media screen and (min-width: @screen-width-large) {
.taskhead {
border-left: 2px solid transparent;
}
.taskhead.focused {
border-left: 2px solid @color-list-focus-indicator;
outline: 0;
}
}
}
#taskshow {
& + .formbuttons {
button.disabled {
display: none;
}
}
.task-parent-title {
color: @color-black-shade-text;
font-size: 90%;
margin-bottom: .5rem;
}
.task-title {
font-size: 1.5rem;
font-weight: bold;
}
.task-description {
margin: 1rem 0;
}
}
#taskedit-attachment-form {
// fixes redundant scrolling and height issue on task edit form
position: absolute;
}

View file

@ -30,10 +30,10 @@
.listing {
// Fix focus indicator, because we add div element in the list record
@media screen and (min-width: @screen-width-large) {
li > div a {
li > div > a {
border-left: 2px solid transparent;
}
li > div a:focus {
li > div > a:focus {
border-left: 2px solid @color-list-focus-indicator;
outline: 0;
}
@ -56,14 +56,19 @@
height: @listing-line-height !important;
font-size: 1.1em !important;
}
& + input {
position: initial;
}
}
&.tasklist .listname:before {
// TODO: tasklist icon
&:extend(.font-icon-class);
content: @fa-var-tasks;
}
&.calendar .calname:before {
// TODO: clendar icon
.font-icon-regular(@fa-var-calendar);
}
&.folder .listname:before {
@ -77,20 +82,38 @@
}
span.actions {
a.remove {
display: none;
display: flex;
input + & {
padding-right: 2em;
}
a {
padding: 0;
margin: 0;
margin-right: .2rem;
cursor: pointer;
&:before {
margin: 0;
}
&.remove {
display: none;
}
}
}
a.quickview:before {
content: @fa-var-eye; // TODO: disabled button state
content: @fa-var-eye;
opacity: .2;
color: @color-link;
}
&.focusview {
span.actions {
a.quickview:before {
content: @fa-var-eye;
}
a.quickview:before {
content: @fa-var-eye;
opacity: 1;
}
}
@ -145,6 +168,7 @@
}
}
& > div.readonly a:first-child,
&.readonly:not(.virtual) > div a:first-child {
padding-right: 1.6em;
@ -154,6 +178,17 @@
}
}
& > div.virtual.user > .listname,
&.virtual.user > div a:first-child {
&:before {
.font-icon-solid(@fa-var-user);
}
&:after {
content: "";
}
}
&.other.user > div {
span.calname {
// TODO @fa-var-user;
@ -166,12 +201,6 @@
}
}
& > div.readonly {
span.calname {
// TODO: @fa-var-lock;
}
}
&.virtual > div > a {
opacity: .5;
}
@ -198,6 +227,26 @@
li.droptarget > div {
background-color: @color-list-droptarget-background;
}
li .count {
position: absolute;
top: 0;
right: 0;
min-width: 2em;
line-height: 1.4rem;
margin: (@listing-line-height - 1.4rem)/2;
padding: 0 .3em;
border-radius: .4em;
background: @color-list-secondary;
color: #fff;
text-align: center;
font-weight: bold;
html.touch & {
line-height: 2rem;
margin: (@listing-touch-line-height - 2rem)/2;
}
}
}
html.touch .listing {
@ -276,6 +325,31 @@ a.history {
}
}
.form-group > .datetime {
display: flex;
white-space: nowrap;
input {
width: 10em;
}
input:first-child {
margin-right: .5rem;
width: 15em;
}
a {
margin-left: 1em;
text-align: right;
flex: 1;
line-height: 2.4;
}
}
button.btn.save.notify:before {
content: @fa-var-envelope;
}
@import "include/calendar";
@import "include/kolab_activesync";
@import "include/kolab_delegation";

View file

@ -12,9 +12,9 @@ necessary. To do so, execute the SQL commands in
drivers/database/SQL/<yourdatabase>.initial.sql
For some general calendar-based operations such as alarms handling, this
plugin requires the `libcalendaring` plugin which is also part of the
Kolab Roundcube Plugins repository. Make sure that plugin is installed
and configured correctly.
plugin requires the `libcalendaring` plugin, as well as libkolab plugin
for more skins support, which are also part of the Kolab Roundcube Plugins
repository. Make sure these plugins are installed and configured correctly.
IMPORTANT
---------

View file

@ -22,6 +22,7 @@
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3",
"kolab/libcalendaring": ">=3.3.0",
"kolab/libkolab": ">=3.3.0",
"roundcube/jqueryui": ">=1.10.4"
}
}

View file

@ -1693,7 +1693,7 @@ class tasklist_kolab_driver extends tasklist_driver
$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));
$input_name = new html_inputfield(array('name' => 'name', 'id' => 'taskedit-tasklistname', 'size' => 20));
$fieldprop['name']['value'] = $input_name->show($list['editname'], array('disabled' => ($options['norename'] || $options['protected'])));
// prevent user from moving folder
@ -1723,13 +1723,7 @@ class tasklist_kolab_driver extends tasklist_driver
if ($action != 'form-new') {
$form['sharing'] = array(
'name' => rcube::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'),
'')
'content' => $this->folder_acl_form($folder_name)
);
}
@ -1744,7 +1738,7 @@ class tasklist_kolab_driver extends tasklist_driver
// create form output
foreach ($form as $tab) {
if (is_array($tab['fields']) && empty($tab['content'])) {
$table = new html_table(array('cols' => 2));
$table = new html_table(array('cols' => 2, 'class' => 'propform'));
foreach ($tab['fields'] as $col => $colprop) {
$label = !empty($colprop['label']) ? $colprop['label'] : $this->plugin->gettext($col);
@ -1765,24 +1759,13 @@ class tasklist_kolab_driver extends tasklist_driver
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()
public function folder_acl_form($folder)
{
$folder = rcube_utils::get_input_value('_folder', rcube_utils::INPUT_GPC);
if (strlen($folder)) {
$this->plugin->require_plugin('acl');
$storage = $this->rc->get_storage();
$options = $storage->folder_info($folder);

View file

@ -20,6 +20,7 @@ $labels['currentview'] = 'current view';
$labels['tasklistsubscribe'] = 'List permanently';
$labels['listsearchresults'] = 'Available Tasklists';
$labels['findlists'] = 'Find tasklists...';
$labels['findtasks'] = 'Find tasks...';
$labels['searchterms'] = 'Search terms';
$labels['notasklistsfound'] = 'No tasklists found';
$labels['nrtasklistsfound'] = '$nr tasklists found';
@ -98,6 +99,7 @@ $labels['listactions'] = 'List options...';
$labels['listname'] = 'Name';
$labels['showalarms'] = 'Show reminders';
$labels['import'] = 'Import';
$labels['importtasks'] = 'Import Tasks';
$labels['viewactions'] = 'View actions';
$labels['focusview'] = 'View only this list';
@ -196,12 +198,15 @@ $labels['errornotifying'] = 'Failed to send notifications to task assignees';
$labels['removefromcalendar'] = 'Remove from my tasks';
$labels['delegateinvitation'] = 'Delegate assignment';
$labels['importtext'] = 'You can upload tasks in <a href="https://wikipedia.org/wiki/ICalendar">iCalendar</a> format (.ics).';
$labels['andnmore'] = '$nr more...';
$labels['savetotasklist'] = 'Save to tasks';
$labels['comment'] = 'Comment';
$labels['errorimportingtask'] = 'Failed to import task(s)';
$labels['importwarningexists'] = 'A copy of this task already exists in your tasklist.';
$labels['importsuccess'] = 'Successfully imported $nr tasks';
$labels['importnone'] = 'No tasks found to be imported';
$labels['importerror'] = 'An error occured while importing';
$labels['newerversionexists'] = 'A newer version of this task already exists! Aborted.';
$labels['nowritetasklistfound'] = 'No tasklist found to save the task';
$labels['importedsuccessfully'] = 'The task was successfully added to \'$list\'';
@ -213,3 +218,4 @@ $labels['sentresponseto'] = 'Successfully sent assignment response to $mailto';
$labels['successremoval'] = 'The task has been deleted successfully.';
$labels['invalidlistproperties'] = 'Invalid list properties! Please set a valid name.';
$labels['arialabelsortmenu'] = 'Tasks sorting options';
$labels['arialabeltasklistform'] = 'Tasks list form';

View file

@ -0,0 +1,67 @@
<roundcube:include file="includes/layout.html" />
<h1 class="voice"><roundcube:var name="env:filename" /></h1>
<div class="sidebar listbox">
<div class="header">
<a class="button icon back-content-button" href="#content" data-hidden="big"><span class="inner"><roundcube:label name="back" /></span></a>
<span class="header-title" id="aria-label-contentinfo"><roundcube:label name="properties" /></span>
</div>
<roundcube:object name="plugin.attachmentcontrols" class="listing" role="contentinfo"
aria-labelledby="aria-label-contentinfo" />
</div>
<div class="content selected">
<h2 id="aria-label-toolbar" class="voice"><roundcube:label name="arialabeltoolbar" /></h2>
<div class="header" role="toolbar" aria-labelledby="aria-label-toolbar">
<span class="header-title constant"><roundcube:var name="env:filename" /></span>
<div id="messagetoolbar" class="toolbar">
<a class="button properties" id="properties-button" href="#properties" onclick="UI.show_sidebar()" data-hidden="big">
<span class="inner"><roundcube:label name="properties"></span>
</a>
<roundcube:button command="download-attachment" type="link" label="download" title="download"
class="button download disabled" classAct="button download" innerclass="inner" />
<roundcube:button command="print-attachment" type="link" label="print" title="print"
class="button print disabled" classAct="button print" innerclass="inner" data-hidden="small" />
<roundcube:if condition="stripos(env:mimetype, 'image/') === 0" />
<roundcube:button command="image-scale" type="link" prop="+" data-hidden="small"
class="button zoomin disabled" classAct="button zoomin"
label="zoomin" title="increaseimage" innerclass="inner" />
<roundcube:button command="image-scale" type="link" prop="-" data-hidden="small"
class="button zoomout disabled" classAct="button zoomout"
label="zoomout" title="decreaseimage" innerclass="inner" />
<roundcube:button command="image-rotate" type="link"
class="button rotate disabled" classAct="button rotate" data-hidden="small"
label="rotate" title="rotateimage" innerclass="inner" />
<roundcube:endif />
</div>
</div>
<h2 id="aria-label-messagepart" class="voice"><roundcube:label name="arialabelattachmentpreview" /></h2>
<div class="iframe-wrapper">
<roundcube:object name="plugin.attachmentframe" id="attachmentframe" title="arialabelattachmentpreview"
role="main" aria-labelledby="aria-label-messagepart" />
</div>
</div>
<roundcube:if condition="stripos(env:mimetype, 'image/') === 0" />
<div id="image-tools" class="image-tools" data-hidden="big">
<h3 id="aria-label-imagetools" class="voice"><roundcube:label name="arialabelimagetools" /></h3>
<div class="toolbar" role="menu" aria-labelledby="aria-label-imagetools">
<roundcube:button command="image-scale" type="link" prop="+"
class="button zoomin disabled" classAct="button zoomin"
label="zoomin" title="increaseimage" innerclass="inner" />
<roundcube:button command="image-scale" type="link" prop="-"
class="button zoomout disabled" classAct="button zoomout"
label="zoomout" title="decreaseimage" innerclass="inner" />
<roundcube:button command="image-rotate" type="link"
class="button rotate disabled" classAct="button rotate"
label="rotate" title="rotateimage" innerclass="inner" />
</div>
<a href="#" class="button icon tools" onclick="$(this).attr('title', $(this).data('label-' + ($('#image-tools').toggleClass('open').is('.open') ? 'hide' : 'show')))"
data-label-show="<roundcube:label name="showtools" />" data-label-hide="<roundcube:label name="hidetools" />" title="<roundcube:label name="showtools" />">
<span class="inner"><roundcube:label name="showtools" /></span>
</a>
</div>
<roundcube:endif />
<roundcube:include file="includes/footer.html" />

View file

@ -0,0 +1,9 @@
<roundcube:include file="includes/layout.html" />
<h1 class="voice"><roundcube:label name="tasklist.navtitle" /> : <roundcube:label name="kolab_notes.arialabeltasklistform" /></h1>
<div class="formcontent">
<roundcube:object name="tasklistform" class="tabbed propform" />
</div>
<roundcube:include file="includes/footer.html" />

View file

@ -0,0 +1,339 @@
<roundcube:include file="includes/layout.html" />
<roundcube:include file="includes/menu.html" />
<h1 class="voice"><roundcube:label name="tasklist.navtitle" /></h1>
<!-- task folders list -->
<div class="sidebar listbox" role="navigation" aria-labelledby="arial-label-notebooks">
<div class="header">
<a class="button icon back-list-button" href="#back"><span class="inner"><roundcube:label name="back" /></span></a>
<span id="aria-label-tasklists" class="header-title"><roundcube:label name="tasklist.lists" /></span>
<div id="tasklist-search" class="searchbar toolbar" role="search" aria-labelledby="aria-label-tasklistsearchform">
<h2 id="aria-label-label-tasklistsearchform" class="voice"><roundcube:label name="kolab_notes.arialabelfoldersearchform" /></h2>
<form name="foldersearchform" onsubmit="return false">
<input id="tasklistsearch" type="text" name="q" placeholder="<roundcube:label name="searchplaceholder" />" />
<a class="button reset" href="#" onclick="return rcmail.command(\'reset-listsearch\',null,this,event)" title="<roundcube:label name="resetsearch" />" tabindex="0">
<span class="inner"><roundcube:label name="resetsearch" /></span>
</a>
</form>
<a class="button search" href="#" title="<roundcube:label name="tasklist.findlists" />" tabindex="0">
<span class="inner"><roundcube:label name="tasklist.findlists" /></span>
</a>
</div>
</div>
<div id="tasklists-content" class="scroller">
<roundcube:object name="plugin.tasklists" id="tasklists" class="treelist listing iconized" />
</div>
<div class="footer toolbar" role="toolbar">
<roundcube:button command="list-create" type="link" title="tasklist.createlist"
class="button create disabled" classAct="button create" innerClass="inner" label="tasklist.createlist" />
<roundcube:button name="tasklistactionsmenu" id="tasklistoptionsmenulink" type="link"
title="tasklist.listactions" class="button actions" data-popup="tasklistactions-menu"
innerClass="inner" label="actions" />
</div>
</div>
<!-- tasks list -->
<div class="list listbox selected" aria-labelledby="aria-label-taskslist">
<div class="header">
<a class="button icon menu-button" href="#menu"><span class="inner"><roundcube:label name="menu" /></span></a>
<a class="button icon back-sidebar-button folders" href="#sidebar"><span class="inner"><roundcube:label name="tasklist.lists" /></span></a>
<span id="aria-label-taskslist" class="header-title"><roundcube:label name="tasklist.navtitle" /></span>
<roundcube:object name="plugin.searchform" id="searchform" wrapper="searchbar toolbar"
label="tasksearchform" label-domain="tasklist" buttontitle="tasklist.findtasks" ariatag="h2" />
<a class="button icon toolbar-menu-button" href="#list-menu"><span class="inner"><roundcube:label name="menu" /></span></a>
</div>
<div class="pagenav pagenav-list toolbar" onclick="UI.switch_nav_list(this)">
<a class="button icon expand"><span class="inner"></span></a>
<span id="taskselector-name" class="pagenav-text"><roundcube:label name="tasklist.all" /></span>
</div>
<div id="taskselector-content" class="navlist scroller" aria-labelledby="aria-label-taskselector">
<h2 class="voice" id="aria-label-taskselector"><roundcube:label name="tasklist.arialabeltaskselector" /></h2>
<ul id="taskselector" class="listing" role="listbox" aria-controls="tasklist">
<li class="all selected" role="radio" aria-checked="true" aria-labelledby="aria-radio-all"><a href="#all" id="aria-radio-all"><roundcube:label name="tasklist.all" /></a></li>
<li class="overdue inactive" role="radio" aria-checked="false" aria-labelledby="aria-radio-overdue"><a href="#overdue" id="aria-radio-overdue"><roundcube:label name="tasklist.overdue" /><span class="count"></span></a></li>
<li class="today" role="radio" aria-checked="false" aria-labelledby="aria-radio-today"><a href="#today" id="aria-radio-today"><roundcube:label name="tasklist.today" /><span class="count"></span></a></li>
<li class="tomorrow" role="radio" aria-checked="false" aria-labelledby="aria-radio-tomorrow"><a href="#tomorrow" id="aria-radio-tomorrow"><roundcube:label name="tasklist.tomorrow" /><span class="count"></span></a></li>
<roundcube:if condition="env:tasklist_driver != 'kolab'" />
<li class="week" role="radio" aria-checked="false" aria-labelledby="aria-radio-week"><a href="#week" id="aria-radio-week"><roundcube:label name="tasklist.next7days" /></a></li>
<roundcube:endif />
<li class="later" role="radio" aria-checked="false" aria-labelledby="aria-radio-later"><a href="#later" id="aria-radio-later"><roundcube:label name="tasklist.later" /><span class="count"></span></a></li>
<li class="nodate" role="radio" aria-checked="false" aria-labelledby="aria-radio-nodate"><a href="#nodate" id="aria-radio-nodate"><roundcube:label name="tasklist.nodate" ucfirst="true" /></a></li>
<li class="flagged" role="radio" aria-checked="false" aria-labelledby="aria-radio-flagged"><a href="#flagged" id="aria-radio-flagged"><roundcube:label name="tasklist.flagged" /></a></li>
<roundcube:if condition="env:tasklist_driver == 'kolab'" />
<li class="mytasks" role="radio" aria-checked="false" aria-labelledby="aria-radio-mytasks"><a href="#mytasks" id="aria-radio-mytasks" title="<roundcube:label name='tasklist.mytaskstitle'/>"><roundcube:label name="tasklist.mytasks" /></a></li>
<li class="assigned" role="radio" aria-checked="false" aria-labelledby="aria-radio-assigned"><a href="#assigned" id="aria-radio-assigned" title="<roundcube:label name='tasklist.assignedtitle'/>"><roundcube:label name="tasklist.assigned" /></a></li>
<roundcube:endif />
<li class="complete" role="radio" aria-checked="false" aria-labelledby="aria-radio-complete"><a href="#complete" id="aria-radio-complete"><roundcube:label name="tasklist.complete" /><span class="count"></span></a></li>
</ul>
</div>
<div class="scroller">
<h2 id="aria-label-taskslist" class="voice"><roundcube:label name="tasklist.navtitle" /></h2>
<roundcube:object name="plugin.tasks" id="tasklist" role="tree"
class="listing" role="listbox" data-list="tasklist"
data-label-msg="listempty" data-label-ext="listusebutton" data-create-command="newtask" />
</div>
<div class="footer toolbar">
<div id="listcontrols" class="listselectors">
<a href="#threads" class="button threads" data-popup="threadselect-menu" title="<roundcube:label name="threads" />"><span class="inner"><roundcube:label name="threads" /></span></a>
<roundcube:button name="optionsmenu" id="listmenulink" type="link" class="button settings"
label="options" innerClass="inner" onclick="tasklist_options_menu()" />
</div>
</div>
</div>
<!-- task details frame -->
<div class="content" role="main">
<h2 id="aria-label-toolbar" class="voice"><roundcube:label name="arialabeltoolbar" /></h2>
<div class="header" role="toolbar" aria-labelledby="aria-label-toolbar">
<a class="button icon back-list-button" href="#back"><span class="inner"><roundcube:label name="back" /></span></a>
<span class="header-title"></span>
<!-- toolbar -->
<div id="taskstoolbar" class="toolbar">
<roundcube:button command="newtask" type="link"
class="button create disabled" classAct="button create"
label="create" title="tasklist.newtask" innerClass="inner" data-fab="true" />
<roundcube:button command="print" type="link"
class="button print disabled" classAct="button print"
label="print" title="tasklist.printtitle" innerClass="inner" />
<roundcube:button command="delete-task" type="link"
label="delete" title="tasklist.removetask"
class="button delete disabled" classAct="button delete" innerclass="inner" />
<span class="spacer"></span>
<roundcube:button command="import" type="link"
class="button import disabled" classAct="button import"
label="import" title="tasklist.importtasks" innerClass="inner" />
<roundcube:button command="export" type="link"
class="button export disabled" classAct="button export"
label="tasklist.export" title="tasklist.exporttitle" />
<roundcube:container name="toolbar" id="taskstoolbar" />
</div>
</div>
<!-- task content frame -->
<h2 id="aria-label-taskform" class="voice"><roundcube:label name="kolab_notes.arialabeltaskform" /></h2>
<div class="content scroller watermark" role="main" aria-labelledby="aria-label-taskform">
<roundcube:include file="/templates/taskedit.html" />
<div id="taskshow" class="hidden formcontent propform text-only" data-nodialog="true">
<div id="task-parent-title" class="task-parent-title"></div>
<h2 id="task-title" class="task-title"></h2>
<div id="task-description" class="task-description"></div>
<div id="task-attendees" class="form-group row task-attendees">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.assignedto" /></label>
<span class="task-text col-sm-10 form-control-plaintext"></span>
</div>
<div id="task-tags" class="form-group row">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.tags" /></label>
<span class="task-text tagedit col-sm-10 form-control-plaintext"></span>
</div>
<div id="task-start" class="form-group row">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.start" /></label>
<span class="col-sm-10 form-control-plaintext"><span class="task-text"></span> <span id="task-starttime"></span></span>
</div>
<div id="task-date" class="form-group row">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.datetime" /></label>
<span class="col-sm-10 form-control-plaintext"><span class="task-text"></span> <span id="task-time"></span></span>
</div>
<div id="task-recurrence" class="form-group row">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.repeat" /></label>
<span class="task-text col-sm-10 form-control-plaintext"></span>
</div>
<div id="task-alarm" class="form-group row">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.alarms" /></label>
<span class="task-text col-sm-10 form-control-plaintext"></span>
</div>
<div id="task-organizer" class="form-group row task-attendees">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.roleorganizer" /></label>
<span class="task-text col-sm-10 form-control-plaintext"></span>
</div>
<div id="task-list" class="form-group row">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.list" /></label>
<span class="task-text col-sm-10 form-control-plaintext"></span>
</div>
<div id="task-completeness" class="form-group row">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.complete" /></label>
<span class="task-text col-sm-10 form-control-plaintext"></span>
</div>
<div id="task-status" class="form-group row">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.status" /></label>
<span class="task-text col-sm-10 form-control-plaintext"></span>
</div>
<div id="task-links" class="form-group row">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.links" /></label>
<span class="task-text col-sm-10 form-control-plaintext"></span>
</div>
<div id="task-attachments" class="form-group row">
<label class="col-sm-2 col-form-label"><roundcube:label name="attachments" /></label>
<div class="task-text col-sm-10"></div>
</div>
<div id="task-created" class="form-group row">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.created" /></label>
<span class="task-text task-created col-sm-10 form-control-plaintext"></span>
</div>
<div id="task-changed" class="form-group row">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.changed" /></label>
<span class="task-text task-changed col-sm-10 form-control-plaintext"></span>
</div>
<div id="task-rsvp-comment" class="form-group row">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.rsvpcomment" /></label>
<span class="task-text col-sm-10 form-control-plaintext"></span>
</div>
<roundcube:object name="plugin.task_rsvp_buttons" id="task-rsvp" style="display:none" />
</div>
<div class="formbuttons">
<roundcube:button command="save-task" label="save"
class="btn btn-primary submit disabled" classAct="btn btn-primary submit" innerCLass="inner" />
<roundcube:button command="edit-task" label="edit"
class="btn btn-primary edit disabled" classAct="btn btn-primary edit" innerclass="inner" />
<roundcube:button command="add-child-task" label="tasklist.addsubtask"
class="create btn btn-secondary disabled" classAct="create btn btn-secondary" innerclass="inner" />
<roundcube:if condition="config:kolab_bonnie_api" />
<roundcube:button command="history" type="link" label="libkolab.showhistory"
class="btn btn-secondary history task-history disabled" classAct="btn btn-secondary history task-history" />
<roundcube:endif />
</div>
</div>
</div>
<roundcube:if condition="env:tasklist_driver == 'kolab' && config:kolab_bonnie_api" />
<div id="taskhistory" class="popupmenu" aria-hidden="true">
<roundcube:object name="plugin.object_changelog_table" class="records-table changelog-table" />
<div class="compare-button"><input type="button" class="button" value="↳ <roundcube:label name='libkolab.compare' />" /></div>
</div>
<div id="taskdiff" class="popupmenu" aria-hidden="true">
<div class="form-group row task-parent-title">
<span class="task-text-old"></span> &#8674;
<span class="task-text-new"></span> &raquo;
</div>
<h2 class="task-title">Task Title</h2>
<h2 class="task-title-new task-text-new"></h2>
<div class="form-group row task-description">
<label><roundcube:label name="calendar.description" /></label>
<div class="task-text-diff" style="white-space:pre-wrap"></div>
<div class="task-text-old"></div>
<div class="task-text-new"></div>
</div>
<div class="form-group row task-flagged">
<label><roundcube:label name="tasklist.flagged" /></label>
<span class="task-text-old"></span> &#8674;
<span class="task-text-new"></span>
</div>
<div class="form-group row task-start">
<label><roundcube:label name="tasklist.start" /></label>
<span class="task-text-old"></span> &#8674;
<span class="task-text-new"></span>
</div>
<div class="form-group row task-date">
<label><roundcube:label name="tasklist.datetime" /></label>
<span class="task-text-old"></span> &#8674;
<span class="task-text-new"></span>
</div>
<div class="form-group row task-recurrence">
<label><roundcube:label name="tasklist.repeat" /></label>
<span class="task-text-old"></span> &#8674;
<span class="task-text-new"></span>
</div>
<div class="form-group row task-alarms">
<label><roundcube:label name="tasklist.alarms" /></label>
<span class="task-text-old"></span> &#8674;
<span class="task-text-new"></span>
</div>
<div class="form-group row task-attendees">
<label><roundcube:label name="tasklist.assignedto" /><span class="index"></span></label>
<span class="task-text-old"></span> &#8674;
<span class="task-text-new"></span>
</div>
<div class="form-group row task-complete">
<label><roundcube:label name="tasklist.complete" /></label>
<span class="task-text-old"></span> &#8674;
<span class="task-text-new"></span>
</div>
<div class="form-group row task-status">
<label><roundcube:label name="tasklist.status" /></label>
<span class="task-text-old"></span> &#8674;
<span class="task-text-new"></span>
</div>
<div class="form-group row task-links">
<label><roundcube:label name="tasklist.links" /><span class="index"></span></label>
<span class="task-text"></span>
</div>
<div class="form-group row task-attachments">
<label><roundcube:label name="attachments" /><span class="index"></span></label>
<div class="task-text-old"></div>
<div class="task-text-new"></div>
</div>
</div>
<roundcube:endif />
<div id="tasklistactions-menu" class="popupmenu">
<h3 id="aria-label-tasklistoptions" class="voice"><roundcube:label name="tasklist.listactions" /></h3>
<ul class="toolbarmenu listing" role="menu" aria-labelledby="aria-label-tasklistoptions">
<roundcube:button type="link-menuitem" command="list-edit" label="edit" class="edit disabled" classAct="edit active" />
<roundcube:button type="link-menuitem" command="list-delete" label="delete" class="delete disabled" classAct="delete active" />
<roundcube:if condition="env:tasklist_driver == 'kolab'" />
<roundcube:button type="link-menuitem" command="list-remove" label="tasklist.removelist" class="remove disabled" classAct="remove active" />
<roundcube:endif />
<roundcube:if condition="config:calendar_caldav_url" />
<roundcube:button type="link-menuitem" command="list-showurl" label="tasklist.showcaldavurl" class="showurl disabled" classAct="showurl active" />
<roundcube:endif />
<roundcube:if condition="env:tasklist_driver == 'kolab'" />
<roundcube:button type="link-menuitem" command="folders" task="settings" label="managefolders" class="folders disabled" classAct="folders active" />
<roundcube:endif />
</ul>
</div>
<div id="listoptions-menu" class="popupmenu propform">
<h3 id="aria-label-taskviewsortmenu" class="voice"><roundcube:label name="tasklist.arialabelsortmenu" /></h3>
<div class="form-group row">
<label for="options-sortcol" class="col-form-label col-sm-4"><roundcube:label name="listsorting" /></label>
<div class="col-sm-8">
<select id="options-sortcol" class="form-control">
<option value="auto"><roundcube:label name="tasklist.auto" /></option>
<option value="datetime"><roundcube:label name="tasklist.datetime" /></option>
<option value="startdatetime"><roundcube:label name="tasklist.start" /></option>
<option value="flagged"><roundcube:label name="tasklist.flagged" /></option>
<option value="complete"><roundcube:label name="tasklist.completeness" /></option>
<option value="changed"><roundcube:label name="tasklist.changed" /></option>
</select>
</div>
</div>
<div class="form-group row">
<label for="options-ord" class="col-form-label col-sm-4"><roundcube:label name="listorder" /></label>
<div class="col-sm-8">
<select id="options-ord" class="form-control">
<option value="asc"><roundcube:label name="sortasc" /></option>
<option value="desc"><roundcube:label name="sortdesc" /></option>
</select>
</div>
</div>
<roundcube:add_label name="listoptionstitle" />
</div>
<div id="threadselect-menu" class="popupmenu">
<h3 id="aria-label-threadselectmenu" class="voice"><roundcube:label name="tasklist.viewactions" /></h3>
<ul class="toolbarmenu listing" role="menu" aria-labelledby="aria-label-threadselectmenu">
<roundcube:button command="expand-all" type="link-menuitem" label="expand-all" class="expand all" classAct="expand all active" />
<roundcube:button command="collapse-all" type="link-menuitem" label="collapse-all" class="expand none" classAct="expand none active" />
</ul>
</div>
<div id="tasklistform" class="popupmenu formcontent">
<roundcube:label name="loading" />
<roundcube:container name="tasklistform" id="tasklistform" />
</div>
<div id="tasksimport" class="popupmenu formcontent">
<roundcube:object name="plugin.tasks_import_form" id="tasks-import-form" />
</div>
<div id="tasksexport" class="popupmenu formcontent">
<roundcube:object name="plugin.tasks_export_form" id="tasks-export-form" />
</div>
<script>
rcmail.addEventListener('tasks-insertrow', function(o) {
UI.pretty_checkbox($(o.div).find('input'));
});
</script>
<roundcube:include file="includes/footer.html" />

View file

@ -0,0 +1,15 @@
<roundcube:include file="includes/layout.html" />
<div id="printconfig" class="noprint">
<div class="pagewidth">
<a href="#close" onclick="window.close()" class="rightalign"><roundcube:label name="close" /></a>
<span class="prop"><input type="button" id="printme" value="<roundcube:label name='print' />" onclick="window.print()"></span>
<span class="prop"><label><input type="checkbox" id="propdescription" checked="checked" onclick="$('#tasklist .description')[this.checked ? 'show' : 'hide']()" /> <roundcube:label name="tasklist.printdescriptions" /></label></span>
</div>
</div>
<div id="tasks" class="pagewidth">
<roundcube:object name="plugin.tasklist_print" id="tasklist" />
</div>
<roundcube:include file="includes/footer.html" />

View file

@ -0,0 +1,127 @@
<div id="taskedit" class="hidden formcontent" data-nodialog="true" data-notabs="true">
<form id="taskeditform" class="tabbed" action="#" method="post" enctype="multipart/form-data">
<!-- basic info -->
<fieldset id="taskedit-panel-main">
<legend><roundcube:label name="tasklist.tabsummary" /></legend>
<div class="form-group">
<label for="taskedit-title"><roundcube:label name="tasklist.title" /></label>
<input type="text" class="form-control" name="title" id="taskedit-title" size="60" />
</div>
<div class="form-group">
<label for="taskedit-description"><roundcube:label name="tasklist.description" /></label>
<textarea name="description" id="taskedit-description" class="form-control" rows="5" cols="60"></textarea>
</div>
<div class="form-group">
<label for="tagedit-input"><roundcube:label name="tasklist.tags" /></label>
<roundcube:object name="plugin.tags_editline" id="taskedit-tagline" class="tagedit" tabindex="0" />
</div>
<div class="form-group row">
<label for="taskedit-startdate" class="col-sm-2 col-form-label"><roundcube:label name="tasklist.start" /></label>
<div class="col-sm-10 datetime">
<input type="text" name="startdate" size="10" id="taskedit-startdate" class="form-control" /> &nbsp;
<input type="text" name="starttime" size="6" id="taskedit-starttime" class="form-control" aria-label="<roundcube:label name='tasklist.starttime' />" />
<a href="#nodate" class="edit-nodate" rel="#taskedit-startdate,#taskedit-starttime"><roundcube:label name="tasklist.nodate" /></a>
</div>
</div>
<div class="form-group row">
<label for="taskedit-date" class="col-sm-2 col-form-label"><roundcube:label name="tasklist.datetime" /></label>
<div class="col-sm-10 datetime">
<input type="text" name="date" size="10" id="taskedit-date" class="form-control" /> &nbsp;
<input type="text" name="time" size="6" id="taskedit-time" class="form-control" aria-label="<roundcube:label name='tasklist.duetime' />" />
<a href="#nodate" class="edit-nodate" rel="#taskedit-date,#taskedit-time"><roundcube:label name="tasklist.nodate" /></a>
</div>
</div>
<div class="form-group row" id="taskedit-alarms">
<label for="edit-alarm-item" class="col-sm-2 col-form-label"><roundcube:label name="tasklist.alarms" /></label>
<div class="col-sm-10">
<div class="edit-alarm-item first">
<roundcube:object name="plugin.alarm_select" id="edit-alarm-item" />
<span class="edit-alarm-buttons">
<a href="#add" class="iconbutton add add-alarm"><roundcube:label name="libcalendaring.addalarm" /></a>
<a href="#delete" class="iconbutton remove delete-alarm"><roundcube:label name="libcalendaring.removealarm" /></a>
</span>
</div>
</div>
</div>
<div class="form-group row">
<label for="taskedit-completeness" class="col-sm-2 col-form-label"><roundcube:label name="tasklist.complete" /></label>
<div class="col-sm-10 input-percent-slider">
<input type="text" name="title" id="taskedit-completeness" size="3" autocomplete="off" class="form-control" />
<span class="label">%</span><div id="taskedit-completeness-slider"></div>
</div>
</div>
<div class="form-group row">
<label for="taskedit-status" class="col-sm-2 col-form-label"><roundcube:label name="tasklist.status" /></label>
<div class="col-sm-10">
<roundcube:object name="plugin.status_select" id="taskedit-status" class="form-control" />
</div>
</div>
<div class="form-group row" id="tasklist-select">
<label for="taskedit-tasklist" class="col-sm-2 col-form-label"><roundcube:label name="tasklist.list" /></label>
<div class="col-sm-10">
<roundcube:object name="plugin.tasklist_select" id="taskedit-tasklist" class="form-control" />
</div>
</div>
<div class="form-group row" id="taskedit-links">
<label class="col-sm-2 col-form-label"><roundcube:label name="tasklist.links" /></label>
<div class="col-sm-10">
<div class="task-text"></div>
</div>
</div>
</fieldset>
<!-- recurrence settings -->
<fieldset id="taskedit-panel-recurrence">
<legend><roundcube:label name="tasklist.tabrecurrence" /></legend>
<div class="form-group row">
<roundcube:object name="plugin.recurrence_form" part="frequency" />
</div>
<div class="recurrence-form" id="recurrence-form-daily">
<roundcube:object name="plugin.recurrence_form" part="daily" class="form-group row" />
</div>
<div class="recurrence-form" id="recurrence-form-weekly">
<roundcube:object name="plugin.recurrence_form" part="weekly" class="form-group row" />
</div>
<div class="recurrence-form" id="recurrence-form-monthly">
<roundcube:object name="plugin.recurrence_form" part="monthly" class="form-group row" />
</div>
<div class="recurrence-form" id="recurrence-form-yearly">
<roundcube:object name="plugin.recurrence_form" part="yearly" class="form-group row" />
</div>
<div class="recurrence-form" id="recurrence-form-until">
<roundcube:object name="plugin.recurrence_form" part="until" class="form-group row" />
</div>
<div class="recurrence-form" id="recurrence-form-rdate">
<roundcube:object name="plugin.recurrence_form" part="rdate" class="form-group row" />
</div>
</fieldset>
<!-- attendees list (assignments) -->
<fieldset id="taskedit-panel-attendees">
<legend><roundcube:label name="tasklist.tabassignments" /></legend>
<div class="form-group" id="taskedit-organizer">
<label for="edit-identities-list"><roundcube:label name="tasklist.roleorganizer" /></label>
<roundcube:object name="plugin.identity_select" id="edit-identities-list" />
</div>
<h3 id="aria-label-attendeestable" class="voice"><roundcube:label name="tasklist.arialabeleventassignments" /></h3>
<roundcube:object name="plugin.attendees_list" id="edit-attendees-table" class="records-table edit-attendees-table" coltitle="attendee" aria-labelledby="aria-label-attendeestable" />
<roundcube:object name="plugin.attendees_form" id="edit-attendees-form" />
<roundcube:include file="/templates/freebusylegend.html" />
</fieldset>
<!-- attachments list (with upload form) -->
<fieldset id="taskedit-panel-attachments">
<legend><roundcube:label name="tasklist.tabattachments" /></legend>
<div id="taskedit-attachments-droparea" class="file-upload">
<h3 id="aria-label-attachmentuploadform" class="voice"><roundcube:label name="arialabelattachmentuploadform" /></h3>
<div id="taskedit-attachments-form" class="upload-form" role="region" aria-labelledby="aria-label-attachmentuploadform">
<roundcube:object name="plugin.attachments_form" mode="hint" />
<button class="btn btn-secondary attach" href="#" onclick="rcmail.upload_input('taskedit-attachment-form'); return false"><roundcube:label name="addattachment" /></button>
</div>
<div id="taskedit-attachments">
<roundcube:object name="plugin.attachments_list" id="taskedit-attachment-list" class="attachmentslist" />
</div>
</div>
<roundcube:object name="plugin.filedroparea" id="taskedit-attachments-droparea" />
</fieldset>
</form>
<roundcube:object name="plugin.edit_attendees_notify" id="edit-attendees-notify" class="task-dialog-message" style="display:none" />
<roundcube:object name="plugin.attachments_form" id="taskedit-attachment-form" mode="smart" />
</div>

View file

@ -64,17 +64,9 @@ ul.toolbarmenu li span.icon.taskadd,
width: 15px;
}
#tagsbox {
position: absolute;
top: 42px;
left: 0;
width: 100%;
height: 242px;
}
#tasklistsbox {
position: absolute;
top: 300px;
top: 42px;
left: 0;
width: 100%;
bottom: 0px;
@ -105,7 +97,6 @@ ul.toolbarmenu li span.icon.taskadd,
top: 68px;
}
#taskselector {
margin: -1px 40px 0 0;
padding: 0;
@ -118,7 +109,6 @@ ul.toolbarmenu li span.icon.taskadd,
padding-right: 0.3em;
}
.tagcloud li,
#taskselector li a {
display: inline-block;
color: #004458;
@ -130,12 +120,10 @@ ul.toolbarmenu li span.icon.taskadd,
border-color: transparent;
}
.webkit .tagcloud li,
.webkit #taskselector li a {
padding-bottom: 0.25em;
}
#taskselector li:first-child {
border-top: 0;
border-radius: 4px 4px 0 0;
@ -155,7 +143,6 @@ ul.toolbarmenu li span.icon.taskadd,
color: #97b3bf;
}
.tagcloud li.selected,
#taskselector li.selected a {
color: #fff;
background: #005d76;
@ -201,51 +188,6 @@ ul.toolbarmenu li span.icon.taskadd,
border-color: #ff3800 transparent;
}
.tagcloud {
padding: 0;
margin: 6px;
list-style: none;
}
.tagcloud li {
display: inline-block;
color: #004458;
padding-right: 0.2em;
margin-right: 0.3em;
margin-bottom: 0.4em;
min-width: 1.2em;
cursor: pointer;
}
.tagcloud li.inactive {
color: #89b3be;
padding-right: 0.6em;
font-size: 80%;
/* display: none; */
}
.tagcloud li .count {
position: relative;
top: -1px;
margin-left: 5px;
padding: 0.2em 0.5em;
font-size: 80%;
font-weight: bold;
color: #59838e;
background: #c7e3ef;
border: 1px solid #b0ccd7;
border-radius: 8px;
}
.tagcloud li.selected .count {
border: none;
}
.tag-draghelper .tag .count,
.tagcloud li.inactive .count {
display: none;
}
#tasklistsbox .treelist li {
margin: 0;
display: block;
@ -280,7 +222,7 @@ ul.toolbarmenu li span.icon.taskadd,
display: block;
}
#tasklistsbox .treelist li span.listname {
#tasklistsbox .treelist li a.listname {
display: block;
position: absolute;
top: 7px;
@ -296,11 +238,11 @@ ul.toolbarmenu li span.icon.taskadd,
}
.quickview-active #tasklistsbox .treelist li input,
.quickview-active #tasklistsbox .treelist li span.listname {
.quickview-active #tasklistsbox .treelist li a.listname {
opacity: 0.35;
}
.quickview-active #tasklistsbox .treelist div.focusview span.listname {
.quickview-active #tasklistsbox .treelist div.focusview a.listname {
opacity: 1.0;
}
@ -381,19 +323,19 @@ ul.toolbarmenu li span.icon.taskadd,
outline: 2px solid rgba(30,150,192, 0.5);
}
#tasklistsbox .treelist li.selected > div > span.listname {
#tasklistsbox .treelist li.selected > div > a.listname {
font-weight: bold;
}
#tasklistsbox .treelist .readonly > span.listname {
#tasklistsbox .treelist .readonly > a.listname {
background-position: right -142px;
}
#tasklistsbox .treelist .user > span.listname {
#tasklistsbox .treelist .user > a.listname {
background-position: right -160px;
}
#tasklistsbox .treelist .virtual > span.listname {
#tasklistsbox .treelist .virtual > a.listname {
color: #aaa;
top: 4px;
left: 20px;
@ -1019,14 +961,14 @@ a.morelink:hover {
display: block;
color: #333;
font-weight: bold;
padding: 3px 4px 3px 30px;
padding: 2px 4px 2px 30px;
text-shadow: 0px 1px 1px #fff;
text-decoration: none;
white-space: nowrap;
line-height: 20px;
}
#taskedit-attachments ul li a.file {
#taskedit-attachments ul li a.filename {
padding: 0;
}
@ -1243,7 +1185,7 @@ label.block {
min-height: 290px;
}
.tasklistview .uidialog .propform #taskedit-tasklistame {
.tasklistview .uidialog .propform #taskedit-tasklistname {
width: 20em;
}
@ -1486,10 +1428,11 @@ div.messagetasklinks .messagetaskref input.complete {
padding-right: 0;
}
/** Special hacks for IE7 **/
/** They need to be in this file to also affect the task-create dialog embedded in mail view **/
html.ie7 #taskedit-completeness-slider {
display: inline;
.form-section label + div {
line-height: 2;
vertical-align: top;
}
.form-section label + div {
display: inline-block;
}

View file

@ -1,26 +0,0 @@
<roundcube:object name="doctype" value="html5" />
<html>
<head>
<title><roundcube:object name="pagetitle" /></title>
<roundcube:include file="/includes/links.html" />
<style type="text/css" media="screen">
body.aclform {
background: #efefef;
margin: 0;
}
body.aclform .hint {
margin: 1em;
}
</style>
</head>
<body class="iframe aclform">
<roundcube:object name="folderacl" />
<roundcube:include file="/includes/footer.html" />
</body>
</html>

View file

@ -0,0 +1,18 @@
<roundcube:object name="doctype" value="html5" />
<html>
<head>
<title><roundcube:object name="pagetitle" /></title>
<roundcube:include file="/includes/links.html" />
</head>
<body class="iframe fullheight">
<h1 class="voice"><roundcube:label name="tasklist.navtitle" /> : <roundcube:label name="kolab_notes.arialabeltasklistform" /></h1>
<div class="boxcontent">
<roundcube:object name="tasklistform" class="tabbed propform" />
</div>
<roundcube:include file="/includes/footer.html" />
</body>
</html>

View file

@ -22,13 +22,6 @@
<roundcube:container name="toolbar" id="taskstoolbar" />
</div>
<div id="tagsbox" class="uibox listbox">
<h2 class="boxtitle" id="aria-label-tagsbox"><roundcube:label name="tasklist.tags" id="taglist" /></h2>
<div class="scroller">
<roundcube:object name="plugin.tagslist" id="tagslist" class="tagcloud" role="region" aria-labelledby="aria-label-tagsbox" aria-controls="thelist" />
</div>
</div>
<div id="tasklistsbox" class="uibox listbox" role="navigation" aria-labelledby="aria-label-tasklists">
<h2 class="boxtitle" id="aria-label-tasklists"><roundcube:label name="tasklist.lists" />
<a href="#tasklists" class="iconbutton search" title="<roundcube:label name='tasklist.findlists' />" tabindex="0"><roundcube:label name="tasklist.findlists" /></a>
@ -343,8 +336,6 @@ $(document).ready(function(e){
new rcube_splitter({ id:'taskviewsplitter', p1:'#sidebar', p2:'#mainview-right',
orientation:'v', relative:true, start:240, min:180, size:12 }).init();
new rcube_splitter({ id:'taskviewsplitterv', p1:'#tagsbox', p2:'#tasklistsbox',
orientation:'h', relative:true, start:242, min:120, size:12, offset:4 }).init();
// animation to unfold list search box
$('#tasklistsbox .boxtitle a.search').click(function(e){

View file

@ -100,12 +100,14 @@
<div id="taskedit-attachments">
<roundcube:object name="plugin.attachments_list" id="taskedit-attachment-list" class="attachmentslist" />
</div>
<div id="taskedit-attachments-form" role="region" aria-labelledby="aria-label-attachmentuploadform">
<div id="taskedit-attachments-form" role="region" aria-labelledby="aria-label-attachmentuploadform" style="text-align:center">
<h3 id="aria-label-attachmentuploadform" class="voice"><roundcube:label name="arialabelattachmentuploadform" /></h3>
<roundcube:object name="plugin.attachments_form" id="taskedit-attachment-form" attachmentFieldSize="30" />
<roundcube:object name="plugin.attachments_form" id="taskedit-attachment-form" mode="hint" />
<a class="button" tabindex="1" href="#" onclick="rcmail.upload_input('taskedit-attachment-form')"><roundcube:label name="addattachment" /></a>
</div>
<roundcube:object name="plugin.filedroparea" id="taskedit-panel-attachments" />
</div>
</form>
<roundcube:object name="plugin.edit_attendees_notify" id="edit-attendees-notify" class="task-dialog-message" style="display:none" />
<roundcube:object name="plugin.attachments_form" id="taskedit-attachment-form" mode="smart" />
</div>

File diff suppressed because it is too large Load diff

View file

@ -74,6 +74,7 @@ class tasklist extends rcube_plugin
function init()
{
$this->require_plugin('libcalendaring');
$this->require_plugin('libkolab');
$this->require_plugin('jqueryui');
$this->rc = rcube::get_instance();
@ -182,8 +183,9 @@ class tasklist extends rcube_plugin
*/
private function load_driver()
{
if (is_object($this->driver))
if (is_object($this->driver)) {
return;
}
$driver_name = $this->rc->config->get('tasklist_driver', 'database');
$driver_class = 'tasklist_' . $driver_name . '_driver';
@ -191,18 +193,11 @@ class tasklist extends rcube_plugin
require_once($this->home . '/drivers/tasklist_driver.php');
require_once($this->home . '/drivers/' . $driver_name . '/' . $driver_class . '.php');
switch ($driver_name) {
case "kolab":
$this->require_plugin('libkolab');
default:
$this->driver = new $driver_class($this);
break;
}
$this->driver = new $driver_class($this);
$this->rc->output->set_env('tasklist_driver', $driver_name);
}
/**
* Dispatcher for task-related actions initiated by the client
*/
@ -996,6 +991,7 @@ class tasklist extends rcube_plugin
switch ($action) {
case 'form-new':
case 'form-edit':
$this->load_ui();
echo $this->ui->tasklist_editform($action, $list);
exit;
@ -1779,6 +1775,8 @@ class tasklist extends rcube_plugin
$this->lib->attachment = $attachment;
$this->register_handler('plugin.attachmentframe', array($this->lib, 'attachment_frame'));
$this->register_handler('plugin.attachmentcontrols', array($this->lib, 'attachment_header'));
$this->rc->output->set_env('filename', $attachment['name']);
$this->rc->output->set_env('mimetype', $attachment['mimetype']);
$this->rc->output->send('tasklist.attachment');
}
// deliver attachment content

View file

@ -57,8 +57,6 @@ class tasklist_ui
$this->plugin->include_stylesheet($this->plugin->local_skin_path() . '/tasklist.css');
if ($this->rc->task == 'mail' || $this->rc->task == 'tasks') {
jqueryui::tagedit();
$this->plugin->include_script('tasklist_base.js');
// copy config to client
@ -145,7 +143,6 @@ class tasklist_ui
$this->plugin->register_handler('plugin.searchform', array($this->rc->output, 'search_form'));
$this->plugin->register_handler('plugin.quickaddform', array($this, 'quickadd_form'));
$this->plugin->register_handler('plugin.tasks', array($this, 'tasks_resultview'));
$this->plugin->register_handler('plugin.tagslist', array($this, 'tagslist'));
$this->plugin->register_handler('plugin.tags_editline', array($this, 'tags_editline'));
$this->plugin->register_handler('plugin.alarm_select', array($this, 'alarm_select'));
$this->plugin->register_handler('plugin.recurrence_form', array($this->plugin->lib, 'recurrence_form'));
@ -161,8 +158,6 @@ class tasklist_ui
$this->plugin->register_handler('plugin.tasks_export_form', array($this, 'tasks_export_form'));
$this->plugin->register_handler('plugin.tasks_import_form', array($this, 'tasks_import_form'));
jqueryui::tagedit();
$this->plugin->include_script('tasklist.js');
$this->rc->output->include_script('treelist.js');
@ -278,7 +273,7 @@ class tasklist_ui
if (!$activeonly || $prop['active']) {
$label_id = 'tl:' . $id;
return html::div(join(' ', $classes),
html::span(array('class' => 'listname', 'title' => $title, 'id' => $label_id), $prop['listname'] ?: $prop['name']) .
html::a(array('class' => 'listname', 'title' => $title, 'href' => '#', 'id' => $label_id), $prop['listname'] ?: $prop['name']) .
($prop['virtual'] ? '' :
html::tag('input', array('type' => 'checkbox', 'name' => '_list[]', 'value' => $id, 'checked' => $prop['active'], 'aria-labelledby' => $label_id)) .
html::span('actions',
@ -337,31 +332,38 @@ class tasklist_ui
return $select->show($default);
}
function tasklist_editform($action, $list = array())
{
$this->action = $action;
$this->list = $list;
$this->rc->output->add_handler('tasklistform', array($this, 'tasklistform'));
$this->rc->output->send('tasklist.listform');
}
function tasklistform($attrib)
{
$fields = array(
'name' => array(
'id' => 'taskedit-tasklistame',
'id' => 'taskedit-tasklistname',
'label' => $this->plugin->gettext('listname'),
'value' => html::tag('input', array('id' => 'taskedit-tasklistame', 'name' => 'name', 'type' => 'text', 'class' => 'text', 'size' => 40)),
'value' => html::tag('input', array('id' => 'taskedit-tasklistname', 'name' => 'name', 'type' => 'text', 'class' => 'text', 'size' => 40)),
),
/*
'color' => array(
'id' => 'taskedit-color',
'id' => 'taskedit-color',
'label' => $this->plugin->gettext('color'),
'value' => html::tag('input', array('id' => 'taskedit-color', 'name' => 'color', 'type' => 'text', 'class' => 'text colorpicker', 'size' => 6)),
),
*/
'showalarms' => array(
'id' => 'taskedit-showalarms',
'id' => 'taskedit-showalarms',
'label' => $this->plugin->gettext('showalarms'),
'value' => html::tag('input', array('id' => 'taskedit-showalarms', 'name' => 'color', 'type' => 'checkbox')),
),
);
return html::tag('form', array('action' => "#", 'method' => "post", 'id' => 'tasklisteditform'),
$this->plugin->driver->tasklist_edit_form($action, $list, $fields)
return html::tag('form', $attrib + array('action' => "#", 'method' => "post", 'id' => 'tasklisteditform'),
$this->plugin->driver->tasklist_edit_form($this->action, $this->list, $fields)
);
}
@ -402,18 +404,6 @@ class tasklist_ui
return html::tag('ul', $attrib, '');
}
/**
* Container for a tags cloud
*/
function tagslist($attrib)
{
$attrib += array('id' => 'rcmtasktagslist');
unset($attrib['name']);
$this->register_gui_object('tagslist', $attrib['id']);
return html::tag('ul', $attrib, '');
}
/**
* Interactive UI element to add/remove tags
*/
@ -432,8 +422,9 @@ class tasklist_ui
*/
function attachments_list($attrib = array())
{
if (!$attrib['id'])
if (!$attrib['id']) {
$attrib['id'] = 'rcmtaskattachmentlist';
}
$this->register_gui_object('attachmentlist', $attrib['id']);
@ -446,26 +437,11 @@ class tasklist_ui
function attachments_form($attrib = array())
{
// add ID if not given
if (!$attrib['id'])
if (!$attrib['id']) {
$attrib['id'] = 'rcmtaskuploadform';
}
// Get max filesize, enable upload progress bar
$max_filesize = $this->rc->upload_init();
$button = new html_inputfield(array('type' => 'button'));
$input = new html_inputfield(array(
'type' => 'file',
'name' => '_attachments[]',
'multiple' => 'multiple',
'size' => $attrib['attachmentfieldsize'],
));
return html::div($attrib,
html::div(null, $input->show()) .
html::div('buttons', $button->show($this->rc->gettext('upload'), array('class' => 'button mainaction',
'onclick' => rcmail_output::JS_OBJECT_NAME . ".upload_file(this.form)"))) .
html::div('hint', $this->rc->gettext(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize))))
);
return $this->rc->upload_form($attrib, 'uploadform', 'upload-file', array('multiple' => true));
}
/**
@ -547,8 +523,7 @@ class tasklist_ui
// Get max filesize, enable upload progress bar
$max_filesize = $this->rc->upload_init();
$accept = '.ics, text/calendar, text/x-vcalendar, application/ics';
$accept = '.ics, text/calendar, text/x-vcalendar, application/ics';
if (class_exists('ZipArchive', false)) {
$accept .= ', .zip, application/zip';
}
@ -560,24 +535,26 @@ class tasklist_ui
'accept' => $accept
));
$html = html::div('form-section',
html::div(null, $input->show())
. html::div('hint', $this->rc->gettext(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize))))
$html = html::div('form-section form-group row',
html::label(array('class' => 'col-sm-4 col-form-label', 'for' => 'importfile'), rcube::Q($this->rc->gettext('importfromfile')))
. html::div('col-sm-8', $input->show()
. html::div('hint', $this->rc->gettext(array('id' => 'importfile', 'name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize)))))
);
$html .= html::div('form-section',
html::label('task-import-list', $this->plugin->gettext('list'))
. $this->tasklist_select(array('name' => 'source', 'id' => 'task-import-list', 'editable' => true))
$html .= html::div('form-section form-group row',
html::label(array('for' => 'task-import-list', 'class' => 'col-sm-4 col-form-label'), $this->plugin->gettext('list'))
. html::div('col-sm-8', $this->tasklist_select(array('name' => 'source', 'id' => 'task-import-list', 'editable' => true)))
);
$this->rc->output->add_gui_object('importform', $attrib['id']);
$this->rc->output->add_label('import', 'importerror');
return html::tag('form', array(
return html::tag('p', null, $this->plugin->gettext('importtext'))
.html::tag('form', array(
'action' => $this->rc->url(array('task' => 'tasklist', 'action' => 'import')),
'method' => 'post',
'enctype' => 'multipart/form-data',
'id' => $attrib['id']
'id' => $attrib['id'],
),
$html
);
@ -592,25 +569,27 @@ class tasklist_ui
$attrib['id'] = 'rcmTaskExportForm';
}
$html .= html::div('form-section',
html::label('task-export-list', $this->plugin->gettext('list')) .
$this->tasklist_select(array(
$html .= html::div('form-section form-group row',
html::label(array('for' => 'task-export-list', 'class' => 'col-sm-4 col-form-label'), $this->plugin->gettext('list'))
. html::div('col-sm-8', $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)
$checkbox = new html_checkbox(array('name' => 'attachments', 'id' => 'task-export-attachments', 'value' => 1, 'class' => 'form-check-input'));
$html .= html::div('form-section form-group row form-check',
html::label(array('for' => 'task-export-attachments', 'class' => 'col-sm-4 col-form-label'), $this->plugin->gettext('exportattachments'))
. html::div('col-sm-8', $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']),
return html::tag('form', array(
'action' => $this->rc->url(array('task' => 'tasklist', 'action' => 'export')),
'method' => 'post', 'id' => $attrib['id']
),
$html
);
}