Merge branch 'master' into dev/elastic

This commit is contained in:
Aleksander Machniak 2018-05-10 08:45:29 +02:00
commit 9124f11081
58 changed files with 1931 additions and 453 deletions

9
less-build.sh Executable file
View file

@ -0,0 +1,9 @@
#!/bin/sh
# First you have to link/copy /skins directory from Roundcube repo
# into ./skins here
# Note: You can remove -x option to generate non-minified file
# (remember to remove ".min" from the output file name)
lessc --relative-urls -x plugins/libkolab/skins/elastic/libkolab.less > plugins/libkolab/skins/elastic/libkolab.min.css

View file

@ -3006,13 +3006,13 @@ class calendar extends rcube_plugin
$calendar = $this->get_default_calendar($event['sensitivity'], $calendars);
$metadata = array(
'uid' => $event['uid'],
'uid' => $event['uid'],
'_instance' => $event['_instance'],
'changed' => is_object($event['changed']) ? $event['changed']->format('U') : 0,
'sequence' => intval($event['sequence']),
'fallback' => strtoupper($status),
'method' => $event['_method'],
'task' => 'calendar',
'changed' => is_object($event['changed']) ? $event['changed']->format('U') : 0,
'sequence' => intval($event['sequence']),
'fallback' => strtoupper($status),
'method' => $event['_method'],
'task' => 'calendar',
);
// update my attendee status according to submitted method
@ -3039,9 +3039,9 @@ class calendar extends rcube_plugin
if (!$reply_sender) {
$sender_identity = $this->rc->user->list_emails(true);
$event['attendees'][] = array(
'name' => $sender_identity['name'],
'email' => $sender_identity['email'],
'role' => 'OPT-PARTICIPANT',
'name' => $sender_identity['name'],
'email' => $sender_identity['email'],
'role' => 'OPT-PARTICIPANT',
'status' => strtoupper($status),
);
$metadata['attendee'] = $sender_identity['email'];
@ -3067,23 +3067,27 @@ class calendar extends rcube_plugin
// only update attendee status
if ($event['_method'] == 'REPLY') {
// try to identify the attendee using the email sender address
$existing_attendee = -1;
$existing_attendee = -1;
$existing_attendee_emails = array();
foreach ($existing['attendees'] as $i => $attendee) {
$existing_attendee_emails[] = $attendee['email'];
if ($this->itip->compare_email($attendee['email'], $event['_sender'], $event['_sender_utf'])) {
$existing_attendee = $i;
}
}
$event_attendee = null;
$event_attendee = null;
$update_attendees = array();
foreach ($event['attendees'] as $attendee) {
if ($this->itip->compare_email($attendee['email'], $event['_sender'], $event['_sender_utf'])) {
$event_attendee = $attendee;
$update_attendees[] = $attendee;
$event_attendee = $attendee;
$update_attendees[] = $attendee;
$metadata['fallback'] = $attendee['status'];
$metadata['attendee'] = $attendee['email'];
$metadata['rsvp'] = $attendee['rsvp'] || $attendee['role'] != 'NON-PARTICIPANT';
$metadata['rsvp'] = $attendee['rsvp'] || $attendee['role'] != 'NON-PARTICIPANT';
if ($attendee['status'] != 'DELEGATED') {
break;
}
@ -3109,6 +3113,23 @@ class calendar extends rcube_plugin
}
}
// Accept sender as a new participant (different email in From: and the iTip)
// Use ATTENDEE entry from the iTip with replaced email address
if (!$event_attendee) {
// remove the organizer
$itip_attendees = array_filter($event['attendees'], function($item) { return $item['role'] != 'ORGANIZER'; });
// there must be only one attendee
if (is_array($itip_attendees) && count($itip_attendees) == 1) {
$event_attendee = $itip_attendees[key($itip_attendees)];
$event_attendee['email'] = $event['_sender'];
$update_attendees[] = $event_attendee;
$metadata['fallback'] = $event_attendee['status'];
$metadata['attendee'] = $event_attendee['email'];
$metadata['rsvp'] = $event_attendee['rsvp'] || $event_attendee['role'] != 'NON-PARTICIPANT';
}
}
// found matching attendee entry in both existing and new events
if ($existing_attendee >= 0 && $event_attendee) {
$existing['attendees'][$existing_attendee] = $event_attendee;
@ -3119,6 +3140,9 @@ class calendar extends rcube_plugin
$existing['attendees'][] = $event_attendee;
$success = $this->driver->update_attendees($existing, $update_attendees);
}
else if (!$event_attendee) {
$error_msg = $this->gettext('errorunknownattendee');
}
else {
$error_msg = $this->gettext('newerversionexists');
}

View file

@ -770,7 +770,7 @@ function rcube_calendar_ui(settings)
// basic input validatetion
if (start.getTime() > end.getTime()) {
alert(rcmail.gettext('invalideventdates', 'calendar'));
rcmail.alert_dialog(rcmail.gettext('invalideventdates', 'calendar'));
return false;
}
@ -1757,7 +1757,7 @@ function rcube_calendar_ui(settings)
break;
}
}
// update event date/time display
if (success) {
update_freebusy_dates(event.start, event.end);
@ -1779,7 +1779,7 @@ function rcube_calendar_ui(settings)
rcmail.display_message(rcmail.gettext('suggestedslot', 'calendar') + ': ' + me.event_date_text(event, true), 'voice');
}
else {
alert(rcmail.gettext('noslotfound','calendar'));
rcmail.alert_dialog(rcmail.gettext('noslotfound','calendar'));
}
};
@ -1826,7 +1826,7 @@ function rcube_calendar_ui(settings)
success = true;
}
else {
alert(rcmail.gettext('noemailwarning'));
rcmail.alert_dialog(rcmail.gettext('noemailwarning'));
}
}
@ -2004,7 +2004,7 @@ function rcube_calendar_ui(settings)
{
text: rcmail.gettext('addresource', 'calendar'),
'class': 'mainaction save',
click: function() { rcmail.command('add-resource'); }
click: function() { rcmail.command('add-resource'); $dialog.dialog("close"); }
},
{
text: rcmail.gettext('close'),
@ -2013,23 +2013,33 @@ function rcube_calendar_ui(settings)
}
];
var resize = function() {
var container = $(rcmail.gui_objects.resourceinfocalendar);
container.fullCalendar('option', 'height', container.height() + 1);
};
// open jquery UI dialog
$dialog.dialog({
modal: true,
resizable: true,
resizable: false, // prevents from Availability tab reflow bugs on resize
closeOnEscape: true,
title: rcmail.gettext('findresources', 'calendar'),
classes: {'ui-dialog': 'selection-dialog resources-dialog'},
open: function() {
rcmail.ksearch_blur();
$dialog.attr('aria-hidden', 'false');
// for Elastic
if ($('html.layout-small,html.layout-phone').length) {
$('#eventresourcesdialog .resource-selection').css('display', 'flex');
$('#eventresourcesdialog .resource-content').css('display', 'none');
}
setTimeout(resize, 50);
},
close: function() {
$dialog.dialog('destroy').attr('aria-hidden', 'true').hide();
},
resize: function(e) {
var container = $(rcmail.gui_objects.resourceinfocalendar);
container.fullCalendar('option', 'height', container.height() + 4);
},
resize: resize,
buttons: buttons,
width: 900,
height: 500
@ -2055,6 +2065,14 @@ function rcube_calendar_ui(settings)
if (resources_data[node.id]) {
resource_showinfo(resources_data[node.id]);
rcmail.enable_command('add-resource', me.selected_event && $("#eventedit").is(':visible') ? true : false);
// on elastic mobile display resource info box
if ($('html.layout-small,html.layout-phone').length) {
$('#eventresourcesdialog .resource-selection').css('display', 'none');
$('#eventresourcesdialog .resource-content').css('display', 'flex');
$(window).resize();
resize();
}
}
else {
rcmail.enable_command('add-resource', false);
@ -2130,9 +2148,9 @@ function rcube_calendar_ui(settings)
for (var k in attribs) {
if (typeof attribs[k] == 'undefined')
continue;
table.append($('<tr>').addClass(k)
.append('<td class="title">' + Q(ucfirst(rcmail.get_label(k, 'calendar'))) + '</td>')
.append('<td class="value">' + text2html(render_attrib(attribs[k])) + '</td>')
table.append($('<tr>').addClass(k + ' form-group row')
.append('<td class="title col-sm-4"><label class="col-form-label">' + Q(ucfirst(rcmail.get_label(k, 'calendar'))) + '</label></td>')
.append('<td class="value col-sm-8 form-control-plaintext">' + text2html(render_attrib(attribs[k])) + '</td>')
);
}
@ -2388,7 +2406,7 @@ function rcube_calendar_ui(settings)
}
else {
me.saving_lock = rcmail.set_busy(true, 'calendar.savingdata');
rcmail.http_post('event', { action:'rsvp', e:submit_data, status:response, attendees:attendees, noreply:noreply });
rcmail.http_post('calendar/event', { action:'rsvp', e:submit_data, status:response, attendees:attendees, noreply:noreply });
}
event_show_dialog(me.selected_event);
@ -4050,12 +4068,17 @@ function rcube_calendar_ui(settings)
input.val('');
}
});
$('#edit-resource-find').click(function(){
event_resources_dialog();
return false;
});
$('#resource-content a.nav-link').on('click', function() {
e.preventDefault();
$(this).tab('show');
});
// handle change of "send invitations" checkbox
$('#edit-attendees-invite').change(function() {
$('#edit-attendees-donotify,input.edit-attendee-reply').prop('checked', this.checked);

View file

@ -4,7 +4,7 @@
"description": "Calendar plugin",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.5",
"version": "3.4.0",
"authors": [
{
"name": "Thomas Bruederli",
@ -26,7 +26,7 @@
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3",
"kolab/libcalendaring": ">=3.3.0",
"kolab/libkolab": ">=3.3.0"
"kolab/libcalendaring": ">=3.4.0",
"kolab/libkolab": ">=3.4.0"
}
}

View file

@ -234,7 +234,7 @@ class kolab_invitation_calendar
$events = array();
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
$cal = $this->_get_calendar($foldername);
if ($cal->get_namespace() == 'other')
if (!$cal || $cal->get_namespace() == 'other')
continue;
foreach ($cal->list_events($start, $end, $search, 1, $query, array(array($subquery, 'OR'))) as $event) {
@ -288,7 +288,7 @@ class kolab_invitation_calendar
$count = 0;
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
$cal = $this->_get_calendar($foldername);
if ($cal->get_namespace() == 'other')
if (!$cal || $cal->get_namespace() == 'other')
continue;
$count += $cal->count_events($start, $end, $filter);

View file

@ -38,7 +38,8 @@ class kolab_user_calendar extends kolab_calendar
*/
public function __construct($user_or_folder, $calendar)
{
$this->cal = $calendar;
$this->cal = $calendar;
$this->imap = $calendar->rc->get_storage();
// full user record is provided
if (is_array($user_or_folder)) {

View file

@ -805,20 +805,16 @@ class calendar_ui
*/
function resources_search_form($attrib)
{
$attrib += array('command' => 'search-resource', 'id' => 'rcmcalresqsearchbox', 'autocomplete' => 'off');
$attrib['name'] = '_q';
$input_q = new html_inputfield($attrib);
$out = $input_q->show();
$attrib += array(
'command' => 'search-resource',
'reset-command' => 'reset-resource-search',
'id' => 'rcmcalresqsearchbox',
'autocomplete' => 'off',
'form-name' => 'rcmcalresoursqsearchform',
);
// add form tag around text field
$out = $this->rc->output->form_tag(array(
'name' => "rcmcalresoursqsearchform",
'onsubmit' => rcmail_output::JS_OBJECT_NAME . ".command('" . $attrib['command'] . "'); return false",
'style' => "display:inline"),
$out);
return $out;
return $this->rc->output->search_form($attrib);
}
/**

View file

@ -222,6 +222,7 @@ $labels['addresource'] = 'Book resource';
$labels['findresources'] = 'Find resources';
$labels['resourcedetails'] = 'Details';
$labels['resourceavailability'] = 'Availability';
$labels['resourceprops'] = 'Resource properties';
$labels['resourceowner'] = 'Owner';
$labels['resourceadded'] = 'The resource was added to your event';
@ -254,6 +255,7 @@ $labels['nowritecalendarfound'] = 'No calendar found to save the event';
$labels['importedsuccessfully'] = 'The event was successfully added to \'$calendar\'';
$labels['updatedsuccessfully'] = 'The event was successfully updated in \'$calendar\'';
$labels['attendeupdateesuccess'] = 'Successfully updated the participant\'s status';
$labels['errorunknownattendee'] = 'Failed to find the participant information.';
$labels['itipsendsuccess'] = 'Invitation sent to participants.';
$labels['itipresponseerror'] = 'Failed to send the response to this event invitation';
$labels['itipinvalidrequest'] = 'This invitation is no longer valid';

View file

@ -8,18 +8,8 @@
<div class="header">
<a class="button icon back-content-button" href="#back" data-hidden="big"><span class="inner"><roundcube:label name="back" /></span></a>
<span id="aria-label-calendars" class="header-title"><roundcube:label name="calendar.calendars" /></span>
<div id="calendar-search" class="searchbar toolbar" role="search" aria-labelledby="aria-label-calendarsearchform">
<h2 id="aria-label-calendarsearchform" class="voice"><roundcube:label name="calendar.arialabelcalsearchform" /></h2>
<form name="foldersearchform" onsubmit="return false">
<input id="calendarlistsearch" 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="calendar.findcalendars" />" tabindex="0">
<span class="inner"><roundcube:label name="calendar.findcalendars" /></span>
</a>
</div>
<roundcube:object name="libkolab.folder_search_form" id="calendarlistsearch" wrapper="searchbar toolbar"
ariatag="h2" label="calsearchform" label-domain="calendar" buttontitle="findcalendars" />
</div>
<div id="calendars-content" class="scroller">
<roundcube:object name="plugin.calendar_list" id="calendarslist" class="treelist listing iconized" />
@ -136,6 +126,10 @@
<label class="col-sm-4 col-form-label"><roundcube:label name="calendar.links" /></label>
<span class="event-text col-sm-8"></span>
</div>
<div id="event-attachments" class="form-group row">
<label class="col-sm-4 col-form-label"><roundcube:label name="attachments" /></label>
<span class="event-text col-sm-8"></span>
</div>
<div id="event-created" class="form-group row faded">
<label class="col-sm-4 col-form-label"><roundcube:label name="calendar.created" /></label>
<span class="event-text event-created col-sm-8 form-control-plaintext"></span>
@ -144,9 +138,6 @@
<label class="col-sm-4 col-form-label"><roundcube:label name="calendar.changed" /></label>
<span class="event-text event-changed col-sm-8 form-control-plaintext"></span>
</div>
<div id="event-attachments" class="form-group row">
<div class="event-text"></div>
</div>
<roundcube:object name="plugin.event_rsvp_buttons" id="event-rsvp" class="calendar-invitebox invitebox boxinformation" style="display:none" />
</div>
@ -183,37 +174,52 @@
</div>
<div id="eventresourcesdialog" class="popupmenu">
<div class="resources-dialog">
<h3 class="voice" id="aria-label-resourceselection"><roundcube:label name="calendar.arialabelresourceselection" /></h3>
<div class="resource-selection uibox listbox" role="navigation" aria-labelledby="aria-label-resourceselection">
<h3 class="voice" id="aria-label-resourceselection"><roundcube:label name="calendar.arialabelresourceselection" /></h3>
<div class="resource-selection selection-list" role="navigation" aria-labelledby="aria-label-resourceselection">
<div class="header">
<span class="header-title"><roundcube:label name="calendar.tabresources" /></span>
<div id="resourcequicksearch" class="header">
<div class="searchbox" role="search" aria-labelledby="aria-label-resourcesearchform" aria-controls="resources-list">
<h3 id="aria-label-resourcesearchform" class="voice"><roundcube:label name="calendar.arialabelresourcesearchform" /></h3>
<label for="resourcesearchbox" class="voice"><roundcube:label name="calendar.searchterms" /></label>
<roundcube:object name="plugin.resources_searchform" id="resourcesearchbox" />
<a id="resourcesearchmenulink" class="iconbutton searchoptions"> </a>
<roundcube:button type="link" command="reset-resource-search" id="resourcesearchreset" class="iconbutton reset" title="resetsearch" label="resetsearch" />
</div>
</div>
<div class="scroller">
<roundcube:object name="plugin.resources_list" id="resources-list" class="listing treelist" />
</div>
<roundcube:object name="plugin.resources_searchform" id="resourcesearchbox"
wrapper="searchbar toolbar" ariatag="h4" buttontitle="calendar.findresources"
label="resourcesearchform" label-domain="calendar" />
</div>
<div class="resource-content">
<div id="resource-info" class="uibox contentbox" role="region" aria-labelledby="aria-label-resourcedetails">
<h2 class="boxtitle" id="aria-label-resourcedetails"><roundcube:label name="calendar.resourcedetails" /></h2>
<div class="scroller">
<roundcube:object name="plugin.resource_info" id="resource-details" class="propform" aria-live="polite" aria-relevant="text" aria-atomic="true" />
<div class="scroller">
<roundcube:object name="plugin.resources_list" id="resources-list" class="listing treelist" />
</div>
</div>
<div class="resource-content selection-content">
<div class="header" data-hidden="normal,big">
<a class="button icon back" href="#back" onclick="$('#eventresourcesdialog .resource-content').hide(); $('#eventresourcesdialog .resource-selection').show()">
<span class="inner"><roundcube:label name="back" /></span>
</a>
<span class="header-title"><roundcube:label name="calendar.resourceprops" /></span>
</div>
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item">
<a class="nav-link active" href="#resource-availability" data-toggle="tab" role="tab">
<roundcube:label name="calendar.resourceavailability" />
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#resource-info" data-toggle="tab" role="tab">
<roundcube:label name="calendar.resourcedetails" />
</a>
</li>
</ul>
<div class="tab-content">
<div id="resource-availability" class="tab-pane active" role="tabpanel" aria-labelledby="aria-label-resourceavailability">
<h3 class="voice" id="aria-label-resourceavailability"><roundcube:label name="calendar.resourceavailability" /></h3>
<roundcube:object name="plugin.resource_calendar" id="resource-freebusy-calendar" class="raw-tables" />
<div class="slot-nav">
<roundcube:button name="resource-cal-prev" id="resource-calendar-prev" type="link"
class="icon prevpage" title="calendar.prevslot" label="calendar.prevweek" />
<roundcube:button name="resource-cal-next" id="resource-calendar-next" type="link"
class="icon nextpage" title="calendar.nextslot" label="calendar.nextweek" />
</div>
</div>
<div id="resource-availability" class="uibox contentbox" role="region" aria-labelledby="aria-label-resourceavailability">
<h2 class="boxtitle" id="aria-label-resourceavailability"><roundcube:label name="calendar.resourceavailability" /></h2>
<roundcube:object name="plugin.resource_calendar" id="resource-freebusy-calendar" />
<div class="boxpagenav">
<roundcube:button name="resource-cal-prev" id="resource-calendar-prev" type="link" class="icon prevpage" title="calendar.prevslot" label="calendar.prevweek" />
<roundcube:button name="resource-cal-next" id="resource-calendar-next" type="link" class="icon nextpage" title="calendar.nextslot" label="calendar.nextweek" />
</div>
<div id="resource-info" class="tab-pane" role="tabpanel" aria-labelledby="aria-label-resourcedetails">
<h3 class="voice" id="aria-label-resourcedetails"><roundcube:label name="calendar.resourcedetails" /></h3>
<roundcube:object name="plugin.resource_info" id="resource-details" class="propform text-only"
aria-live="polite" aria-relevant="text" aria-atomic="true" />
</div>
</div>
</div>
@ -222,18 +228,18 @@
<div id="eventfreebusy" class="popupmenu calendar-scheduler formcontent">
<roundcube:object name="plugin.attendees_freebusy_table" id="attendees-freebusy-table"
class="schedule-table" data-h-margin="-1" data-v-margin="1" />
<div class="schedule-nav">
<button id="schedule-freebusy-prev" title="<roundcube:label name='previouspage' />">&#9668;</button>
<button id="schedule-freebusy-next" title="<roundcube:label name='nextpage' />">&#9658;</button>
</div>
<div class="slot-nav">
<div class="schedule-find-buttons">
<div class="nav">
<div class="schedule-buttons">
<button id="schedule-find-prev" class="btn btn-secondary prev-slot"><roundcube:label name="calendar.prevslot" /></button>
<button id="schedule-find-next" class="btn btn-secondary next-slot"><roundcube:label name="calendar.nextslot" /></button>
</div>
<div class="schedule-options">
<label><input type="checkbox" id="schedule-freebusy-workinghours" value="1" class="pretty-checkbox" /><roundcube:label name="calendar.onlyworkinghours" /></label>
</div>
<div class="schedule-nav">
<button id="schedule-freebusy-prev" title="<roundcube:label name='previouspage' />">&#9668;</button>
<button id="schedule-freebusy-next" title="<roundcube:label name='nextpage' />">&#9658;</button>
</div>
</div>
<div class="schedule-range">
<div class="form-group row">

View file

@ -117,7 +117,7 @@
</div>
</fieldset>
<!-- attendees list -->
<fieldset id="event-panel-attendees">
<fieldset id="event-panel-attendees" data-navlink-class="nav-icon attendees">
<legend><roundcube:label name="calendar.tabattendees" /></legend>
<h3 id="aria-label-attendeestable" class="voice"><roundcube:label name="calendar.arialabeleventattendees" /></h3>
<roundcube:object name="plugin.attendees_list" id="edit-attendees-table" class="edit-attendees-table no-img table table-sm"
@ -126,7 +126,7 @@
<roundcube:include file="/templates/freebusylegend.html" />
</fieldset>
<!-- resources list -->
<fieldset id="event-panel-resources">
<fieldset id="event-panel-resources" data-navlink-class="nav-icon resources">
<legend><roundcube:label name="calendar.tabresources" /></legend>
<h3 id="aria-label-resourcestable" class="voice"><roundcube:label name="calendar.arialabeleventresources" /></h3>
<roundcube:object name="plugin.attendees_list" id="edit-resources-table" class="edit-attendees-table no-img table table-sm"
@ -135,7 +135,7 @@
<roundcube:include file="/templates/freebusylegend.html" />
</fieldset>
<!-- attachments list (with upload form) -->
<fieldset id="event-panel-attachments">
<fieldset id="event-panel-attachments" data-navlink-class="nav-icon attachments">
<legend><roundcube:label name="calendar.tabattachments" /></legend>
<div id="edit-attachments-droparea" class="file-upload">
<h3 id="aria-label-attachmentuploadform" class="voice"><roundcube:label name="arialabelattachmentuploadform" /></h3>

View file

@ -4,7 +4,7 @@
"description": "Better HTML to Text converter that uses text-based browser (lynx) for this task",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.0",
"version": "3.4.0",
"authors": [
{
"name": "Aleksander Machniak",

View file

@ -4,7 +4,7 @@
"description": "Kolab 2-Factor Authentication",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.4",
"version": "3.4.0",
"authors": [
{
"name": "Thomas Bruederli",

View file

@ -4,7 +4,7 @@
"description": "ActiveSync configuration utility for Kolab accounts",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.2",
"version": "3.4.0",
"authors": [
{
"name": "Thomas Bruederli",
@ -26,6 +26,6 @@
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3",
"kolab/libkolab": ">=3.3.0"
"kolab/libkolab": ">=3.4.0"
}
}

View file

@ -4,7 +4,7 @@
"description": "Kolab addressbook",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.5",
"version": "3.4.0",
"authors": [
{
"name": "Thomas Bruederli",
@ -26,6 +26,6 @@
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3",
"kolab/libkolab": ">=3.3.0"
"kolab/libkolab": ">=3.4.0"
}
}

View file

@ -1,29 +1,16 @@
<div id="search-addon" class="searchbar toolbar" role="search" aria-labelledby="aria-labelfoldersearchform">
<h2 id="aria-label-labelfoldersearchform" class="voice"><roundcube:label name="kolab_addressbook.foldersearchform" /></h2>
<form name="foldersearchform" onsubmit="return false">
<input id="addressbooksearch" 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="kolab_addressbook.findaddressbooks" />" tabindex="0" >
<span class="inner"><roundcube:label name="kolab_addressbook.findaddressbooks" /></span>
</a>
</div>
<roundcube:object name="libkolab.folder_search_form" id="addressbooksearch" wrapper="searchbar toolbar"
ariatag="h2" label="foldersearchform" label-domain="kolab_addressbook" buttontitle="findaddressbooks" />
<script>
// append search form for address books
if (rcmail.gui_objects.folderlist) {
var searchbar = $('#search-addon')
var searchbar = $('#addressbooksearch').parent().parent()
.attr('aria-controls', rcmail.gui_objects.folderlist)
.insertAfter($('#layout > .sidebar > .header > .header-title'))
.get(0);
// Initialize searchbar
UI.searchbar_init(searchbar);
// Make checkboxes pretty
rcmail.addEventListener('init', function() {
// Make checkboxes pretty
rcmail.treelist.addEventListener('add-item', function(prop) {
UI.pretty_checkbox($(prop.li).find('input').addClass('flex-checkbox'));
});

View file

@ -4,7 +4,7 @@
"description": "Kolab authentication",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.5",
"version": "3.4.0",
"authors": [
{
"name": "Thomas Bruederli",

View file

@ -4,7 +4,7 @@
"description": "Kolab configuration storage",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.0",
"version": "3.4.0",
"authors": [
{
"name": "Aleksander Machniak",
@ -21,6 +21,6 @@
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3",
"kolab/libkolab": ">=3.3.0"
"kolab/libkolab": ">=3.4.0"
}
}

View file

@ -4,7 +4,7 @@
"description": "Kolab delegation feature",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.4",
"version": "3.4.0",
"authors": [
{
"name": "Aleksander Machniak",
@ -21,7 +21,7 @@
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3",
"kolab/libkolab": ">=3.3.0",
"kolab/kolab_auth": ">=3.3.0"
"kolab/libkolab": ">=3.4.0",
"kolab/kolab_auth": ">=3.4.0"
}
}

View file

@ -59,6 +59,9 @@ class kolab_delegation extends rcube_plugin
// delegation support in kolab_auth plugin
$this->add_hook('kolab_auth_emails', array($this, 'kolab_auth_emails'));
// delegation support in Enigma plugin
$this->add_hook('enigma_user_identities', array($this, 'user_identities'));
if ($this->rc->task == 'settings') {
// delegation management interface
$this->register_action('plugin.delegation', array($this, 'controller_ui'));
@ -314,6 +317,28 @@ class kolab_delegation extends rcube_plugin
return $args;
}
/**
* Delegation support in Enigma plugin
*/
public function user_identities($args)
{
// Remove delegators' identities from the key generation form
if (!empty($_SESSION['delegators'])) {
$args['identities'] = array_filter($args['identities'], function($ident) {
foreach ($_SESSION['delegators'] as $emails) {
if (in_array($ident['email'], $emails)) {
return false;
}
}
return true;
});
}
return $args;
}
/**
* Delegation UI handler
*/

View file

@ -4,7 +4,7 @@
"description": "User interface for Kolab File Manager (Chwala)",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.5",
"version": "3.4.0",
"authors": [
{
"name": "Aleksander Machniak",
@ -21,6 +21,6 @@
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3",
"kolab/libkolab": ">=3.3.0"
"kolab/libkolab": ">=3.4.0"
}
}

View file

@ -87,8 +87,7 @@ window.rcmail && window.files_api && rcmail.addEventListener('init', function()
.addEventListener('dragstart', function(e) { kolab_files_drag_start(e); })
.addEventListener('dragmove', function(e) { kolab_files_drag_move(e); })
.addEventListener('dragend', function(e) { kolab_files_drag_end(e); })
.addEventListener('column_replace', function(e) { kolab_files_set_coltypes(e, 'files'); })
.addEventListener('listupdate', function(e) { rcmail.triggerEvent('listupdate', e); });
.addEventListener('column_replace', function(e) { kolab_files_set_coltypes(e, 'files'); });
rcmail.enable_command('menu-open', 'menu-save', 'files-sort', 'files-search', 'files-search-reset', 'folder-create', true);
@ -107,8 +106,7 @@ window.rcmail && window.files_api && rcmail.addEventListener('init', function()
rcmail.sessionslist.addEventListener('dblclick', function(o) { kolab_files_sessions_list_dblclick(o); })
.addEventListener('select', function(o) { kolab_files_sessions_list_select(o); })
.addEventListener('keypress', function(o) { kolab_files_sessions_list_keypress(o); })
.addEventListener('column_replace', function(e) { kolab_files_set_coltypes(e, 'sessions'); })
.addEventListener('listupdate', function(e) { rcmail.triggerEvent('listupdate', e); });
.addEventListener('column_replace', function(e) { kolab_files_set_coltypes(e, 'sessions'); });
rcmail.sessionslist.init();
kolab_files_list_coltypes('sessions');
@ -251,8 +249,7 @@ function kolab_files_from_cloud_widget(elem)
column_movable: false,
dblclick_time: rcmail.dblclick_time
});
rcmail.fileslist.addEventListener('select', function(o) { kolab_files_list_select(o); })
.addEventListener('listupdate', function(e) { rcmail.triggerEvent('listupdate', e); });
rcmail.fileslist.addEventListener('select', function(o) { kolab_files_list_select(o); });
rcmail.enable_command('files-sort', 'files-search', 'files-search-reset', true);
@ -339,13 +336,22 @@ function kolab_directory_selector_dialog(id)
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.' + label),
buttons: buttons,
button_classes: ['mainaction'],
button_classes: ['mainaction save', 'cancel'],
classes: {'ui-dialog': 'selection-dialog files-dialog'},
minWidth: 250,
minHeight: 300,
height: 400,
width: 300
}, fn);
// add link for "more options" drop-down
if (!dialog.find('foldercreatelink').length)
$('<a>')
.attr({href: '#', 'class': 'btn btn-link options add-folder'})
.text(rcmail.gettext('kolab_files.addfolder'))
.click(function(e) { rcmail.command('folder-create', '', this, e); })
.prependTo(dialog.parent().parent().find('.ui-dialog-buttonset'));
// "enable" folder creation when dialog is displayed in parent window
if (rcmail.is_framed()) {
parent.rcmail.enable_command('folder-create', true);
@ -393,7 +399,8 @@ function kolab_files_selector_dialog()
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.selectfiles'),
buttons: buttons,
button_classes: ['mainaction'],
button_classes: ['mainaction save', 'cancel'],
classes: {'ui-dialog': 'selection-dialog files-dialog'},
minWidth: 500,
minHeight: 300,
width: 700,
@ -454,7 +461,8 @@ function kolab_files_folder_create_dialog()
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.foldercreate'),
buttons: buttons,
button_classes: ['mainaction']
button_classes: ['mainaction save', 'cancel'],
height: 200
});
// Fix submitting form with Enter
@ -499,7 +507,8 @@ function kolab_files_folder_edit_dialog()
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.folderedit'),
buttons: buttons,
button_classes: ['mainaction']
button_classes: ['mainaction save', 'cancel'],
height: 200
});
// Fix submitting form with Enter
@ -544,9 +553,6 @@ function kolab_files_folder_mount_dialog()
kolab_dialog_close(this);
};
// close folderoption menu
rcmail.hide_menu('folderoptions');
// initialize drivers list
if (!rcmail.drivers_list_initialized) {
rcmail.drivers_list_initialized = true;
@ -569,7 +575,7 @@ function kolab_files_folder_mount_dialog()
});
}
args.button_classes = ['mainaction'];
args.button_classes = ['mainaction save', 'cancel'];
// show dialog window
kolab_dialog_show(dialog, args, function() {
@ -664,7 +670,7 @@ function kolab_files_file_edit_dialog(file, sessions, readonly)
kolab_dialog_show(dialog, {
title: title,
buttons: buttons,
button_classes: ['mainaction'],
button_classes: ['mainaction save', 'cancel'],
minHeight: height - 100,
height: height
});
@ -701,9 +707,9 @@ function kolab_files_file_rename_dialog(file)
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.renamefile'),
buttons: buttons,
button_classes: ['mainaction'],
button_classes: ['mainaction save', 'cancel'],
minHeight: 100,
height: 200
height: 100
});
};
@ -711,6 +717,7 @@ function kolab_files_file_rename_dialog(file)
function kolab_files_file_create_dialog(file)
{
var buttons = {}, action = file ? 'copy' : 'create',
button_classes = ['mainaction save edit'],
dialog = $('#files-file-create-dialog'),
type_select = $('select[name="type"]', dialog),
select = $('select[name="parent"]', dialog).html(''),
@ -746,6 +753,7 @@ function kolab_files_file_create_dialog(file)
};
if (action == 'create') {
button_classes.push('save');
buttons[rcmail.gettext('kolab_files.create')] = function() {
create_func(this);
};
@ -756,6 +764,7 @@ function kolab_files_file_create_dialog(file)
type_select.parent('tr').hide();
}
button_classes.push('cancel');
buttons[rcmail.gettext('kolab_files.cancel')] = function() {
kolab_dialog_close(this);
};
@ -767,7 +776,7 @@ function kolab_files_file_create_dialog(file)
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.' + action + 'file'),
buttons: buttons,
button_classes: ['mainaction'],
button_classes: button_classes,
minHeight: 150,
height: 250
});
@ -779,13 +788,12 @@ function kolab_files_file_create_dialog(file)
// file session dialog
function kolab_files_session_dialog(session)
{
var buttons = {},
var buttons = {}, button_classes = [],
dialog = $('#files-session-dialog'),
filename = file_api.file_name(session.file),
owner = session.owner_name || session.owner,
title = rcmail.gettext('kolab_files.sessiondialog'),
content = rcmail.gettext('kolab_files.sessiondialogcontent'),
button_classes = ['mainaction'],
join_session = function(id) {
var viewer = file_api.file_type_supported('application/vnd.oasis.opendocument.text', rcmail.env.files_caps);
params = {action: 'edit', session: id};
@ -805,9 +813,10 @@ function kolab_files_session_dialog(session)
kolab_dialog_close(this);
file_api.document_delete(session.id);
};
button_classes.push('delete');
button_classes = ['mainaction edit', 'delete'];
}
else if (session.is_invited) {
button_classes = ['mainaction edit'];
// @TODO: check if not-accepted and provide "Decline invitation" button
// @TODO: Add "Accept button", add comment field to the dialog
buttons[rcmail.gettext('kolab_files.join')] = function() {
@ -816,6 +825,7 @@ function kolab_files_session_dialog(session)
};
}
else {
button_classes = ['mainaction session request'];
buttons[rcmail.gettext('kolab_files.request')] = function() {
kolab_dialog_close(this);
// @TODO: Add comment field to the dialog
@ -823,6 +833,7 @@ function kolab_files_session_dialog(session)
};
}
button_classes.push('cancel');
buttons[rcmail.gettext('kolab_files.cancel')] = function() {
kolab_dialog_close(this);
};
@ -1139,7 +1150,8 @@ function kolab_files_drag_end(e)
if (menu && modkey == SHIFT_KEY && rcmail.commands['files-copy']) {
var pos = rcube_event.get_mouse_pos(e);
$(menu).css({top: (pos.y-10)+'px', left: (pos.x-10)+'px'}).show();
rcmail.show_menu(menu.id, true, e);
$(menu).css({top: (pos.y-10)+'px', left: (pos.x-10)+'px'});
return;
}
@ -1202,6 +1214,10 @@ function kolab_files_frame_load(frame)
rcmail.enable_command('files-print', (rcmail.file_editor && rcmail.file_editor.printable)
|| (info && /^image\//i.test(info.type)));
// Enable image tools
rcmail.enable_command('image-scale', 'image-rotate', info && !!/^image\//.test(info.type));
rcmail.gui_objects.messagepartframe = frame;
// detect Print button and check if it can be accessed
try {
if ($('#fileframe').contents().find('#print').length)
@ -1312,17 +1328,12 @@ function document_editor_init()
// executed on editing session termination
function document_editor_close()
{
$('<div>').addClass('popupdialog').attr('role', 'alertdialog')
.html($('<span>').text(rcmail.gettext('kolab_files.sessionterminated')))
.dialog({
resizable: false,
closeOnEscape: true,
dialogClass: 'error',
title: rcmail.gettext('kolab_files.sessionterminatedtitle'),
close: function() { window.close(); },
width: 420,
minHeight: 90
}).show();
var dialog = $('<div>').addClass('popupdialog').attr('role', 'alertdialog')
.html($('<span>').text(rcmail.gettext('kolab_files.sessionterminated')));
rcmail.alert_dialog(dialog, function() { window.close(); }, {
title: rcmail.gettext('kolab_files.sessionterminatedtitle')
});
return false; // skip Chwala's error message
};
@ -1353,10 +1364,10 @@ rcube_webmail.prototype.document_editors = function()
rcube_webmail.prototype.document_close = function()
{
// check document "unsaved changes" state and display a warning
if (this.commands['document-save'] && !confirm(this.gettext('kolab_files.unsavedchanges')))
return;
file_api.document_delete(this.env.file_data.session.id);
if (this.commands['document-save'])
this.confirm_dialog(this.gettext('kolab_files.unsavedchanges'), 'kolab_files.terminate', function() {
file_api.document_delete(rcmail.env.file_data.session.id);
}, {button_class: 'delete'});
};
// document editors management dialog
@ -1389,7 +1400,7 @@ function kolab_files_editors_dialog(session)
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.manageeditors'),
buttons: buttons,
button_classes: ['mainaction']
button_classes: ['cancel']
});
if (!rcmail.env.editors_dialog) {
@ -1448,7 +1459,7 @@ function kolab_files_add_attendees(names, comment)
counter++;
}
else {
alert(rcmail.gettext('noemailwarning'));
rcmail.alert_dialog(rcmail.gettext('noemailwarning'));
}
}
@ -1524,7 +1535,7 @@ function document_editor_invitation_handler(invitation)
function kolab_files_invitation_dialog(invitation)
{
var text, records = [], content = [], buttons = {},
var text, records = [], content = [], buttons = {}, button_classes = [],
dialog = $('#document-invitation-dialog'),
data_map = {status: 'status', 'changed': 'when', filename: 'file', comment: 'comment'},
record_fn = function(type, label, value) {
@ -1548,6 +1559,7 @@ function kolab_files_invitation_dialog(invitation)
if (!invitation.is_session_owner) {
if (invitation.status == 'invited') {
text = document_editor.invitation_msg(invitation);
button_classes = ['session join', 'session accept', 'session decline delete'];
buttons[rcmail.gettext('kolab_files.join')] = function() {
join_session();
@ -1569,6 +1581,7 @@ function kolab_files_invitation_dialog(invitation)
else if (invitation.status == 'accepted-owner') {
text = document_editor.invitation_msg(invitation);
button_classes = ['mainaction session join'];
buttons[rcmail.gettext('kolab_files.join')] = function() {
join_session();
kolab_dialog_close(this);
@ -1586,6 +1599,7 @@ function kolab_files_invitation_dialog(invitation)
else if (invitation.status == 'requested') {
text = document_editor.invitation_msg(invitation);
button_classes = ['session accept', 'session decline delete'];
buttons[rcmail.gettext('kolab_files.accept')] = function() {
document_editor.invitation_accept(invitation);
kolab_dialog_close(this);
@ -1612,6 +1626,7 @@ function kolab_files_invitation_dialog(invitation)
content.push($('<table class="propform">').html(records));
}
button_classes.push('cancel');
buttons[rcmail.gettext('kolab_files.close')] = function() {
kolab_dialog_close(this);
};
@ -1621,7 +1636,8 @@ function kolab_files_invitation_dialog(invitation)
// show dialog window
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.invitationtitle').replace('$file', invitation.filename),
buttons: buttons
buttons: buttons,
button_classes: button_classes
});
};
@ -1677,33 +1693,75 @@ rcube_webmail.prototype.files_search_reset = function()
file_api.file_search_reset();
};
rcube_webmail.prototype.files_folder_delete = function()
rcube_webmail.prototype.files_folder_delete = function(prop, elem, event)
{
if (confirm(this.get_label('kolab_files.folderdeleteconfirm')))
this.hide_menu('folderoptions', event);
this.confirm_dialog(this.get_label('kolab_files.folderdeleteconfirm'), 'delete', function() {
file_api.folder_delete(file_api.env.folder);
});
};
rcube_webmail.prototype.files_delete = function()
{
if (!confirm(this.get_label('kolab_files.filedeleteconfirm')))
return;
var files = this.env.file ? [this.env.file] : kolab_files_selected();
file_api.file_delete(files);
this.confirm_dialog(this.get_label('kolab_files.filedeleteconfirm'), 'delete', function() {
var files = rcmail.env.file ? [rcmail.env.file] : kolab_files_selected();
file_api.file_delete(files);
});
};
rcube_webmail.prototype.files_move = function(folder)
rcube_webmail.prototype.files_move = function(folder, obj, event, files)
{
var files = kolab_files_selected();
if (!files)
files = kolab_files_selected();
if (!folder) {
var ref = this;
return this.files_folder_selector(event, function(folder) {
ref.files_move(folder, null, event, files);
});
}
file_api.file_move(files, folder);
};
rcube_webmail.prototype.files_copy = function(folder)
rcube_webmail.prototype.files_copy = function(folder, obj, event, files)
{
var files = kolab_files_selected();
if (!files)
files = kolab_files_selected();
if (!folder) {
var ref = this;
return this.files_folder_selector(event, function(folder) {
ref.files_copy(folder, null, event, files);
});
}
file_api.file_copy(files, folder);
};
// create folder selector popup
rcube_webmail.prototype.files_folder_selector = function(event, callback)
{
this.entity_selector('folder-selector', callback, file_api.env.folders, function(folder, a, folder_fullname) {
var n = folder.depth || 0,
id = folder.id,
row = $('<li>');
if (folder.virtual || folder.readonly)
a.addClass('virtual').attr({'aria-disabled': 'true', tabindex: '-1'});
else
a.addClass('active').data('id', folder_fullname);
a.css('padding-left', n ? (n * 16) + 'px' : 0);
// add folder name element
a.append($('<span>').text(folder.name));
return row.append(a);
}, event);
};
rcube_webmail.prototype.files_upload = function(form)
{
if (form)
@ -1881,13 +1939,15 @@ rcube_webmail.prototype.folder_create = function()
kolab_files_folder_create_dialog();
};
rcube_webmail.prototype.folder_rename = function()
rcube_webmail.prototype.folder_rename = function(prop, elem, event)
{
this.hide_menu('folderoptions', event);
kolab_files_folder_edit_dialog();
};
rcube_webmail.prototype.folder_mount = function()
rcube_webmail.prototype.folder_mount = function(prop, elem, event)
{
this.hide_menu('folderoptions', event);
kolab_files_folder_mount_dialog();
};
@ -1991,8 +2051,8 @@ function kolab_files_ui()
collections = ['audio', 'video', 'image', 'document'];
// try parent window if the list element does not exist
// i.e. called from dialog in parent window
if (!elem.length && rcmail.is_framed()) {
// i.e. called from a dialog in parent window
if (!elem.length && rcmail.is_framed() && (rcmail.env.task != 'files' || (rcmail.env.action != 'open' && rcmail.env.action != 'edit'))) {
body = window.parent.document.body;
elem = $(list_selector, body);
searchbox = $(search_selector, body);
@ -2097,6 +2157,14 @@ function kolab_files_ui()
// handle authentication errors on external sources
this.folder_list_auth_errors(response.result);
// Elastic: Set notree class on the folder list
var callback = function() {
list[list.find('.treetoggle').length > 0 ? 'removeClass' : 'addClass']('notree');
};
if (window.MutationObserver)
(new MutationObserver(callback)).observe(list.get(0), {childList: true, subtree: true});
callback();
};
this.folder_select = function(folder)
@ -2104,6 +2172,8 @@ function kolab_files_ui()
if (rcmail.busy)
return;
rcmail.triggerEvent('files-folder-select', {folder: folder});
var is_collection = folder.match(/^folder-collection-(.*)$/),
collection = RegExp.$1 || null;
@ -2111,7 +2181,7 @@ function kolab_files_ui()
rcmail.update_state(is_collection ? {collection: collection} : {folder: folder});
if (collection == 'sessions') {
rcmail.enable_command('files-list', 'files-folder-delete', 'folder-rename', 'files-upload', false);
rcmail.enable_command('files-list', 'files-folder-delete', 'folder-rename', 'files-upload', 'files-open', 'files-edit', false);
this.sessions_list();
return;
}
@ -2169,23 +2239,20 @@ function kolab_files_ui()
{
var toggle, sublist, collapsed, parent, parent_name, classes = ['mailbox'],
row = $('<li>'),
id = 'rcmli' + rcmail.html_identifier_encode(i);
row.attr('id', id).append($('<a class="name">').text(folder.name));
id = 'rcmli' + rcmail.html_identifier_encode(i),
content = $('<div>').append($('<a class="name">').text(folder.name));
if (folder.virtual)
classes.push('virtual');
else {
if (folder.subscribed !== undefined)
row.append(this.folder_list_subscription_button(folder.subscribed));
content.append(this.folder_list_subscription_button(folder.subscribed));
if (folder.readonly)
classes.push('readonly');
}
row.addClass(classes.join(' '));
folder.ref = row;
folder.ref = row.attr({id: id, 'class': classes.join(' ')}).append(content);
if (folder.depth) {
// find parent folder
@ -2228,7 +2295,7 @@ function kolab_files_ui()
// subscription button handler
this.folder_list_subscription_button_click = function(elem)
{
var folder = $(elem).parent('li').prop('id').replace(/^rcmli/, ''),
var folder = $(elem).parents('li:first').prop('id').replace(/^rcmli/, ''),
selected = $(elem).hasClass('subscribed');
folder = folder.replace(/--xsR$/, ''); // this might be a search result
@ -2240,7 +2307,7 @@ function kolab_files_ui()
// sets subscription button status
this.folder_list_subscription_state = function(elem, status)
{
$(elem).children('a.subscription')
$(elem).find('a.subscription:first')
.prop('aria-checked', status)[status ? 'addClass' : 'removeClass']('subscribed');
};
@ -2366,7 +2433,7 @@ function kolab_files_ui()
id: i,
classes: classes,
text: folder.name,
html: html,
html: $('<div>').append(html),
collapsed: false,
virtual: folder.virtual
}, path.length ? path.join(separator) : null);
@ -2429,7 +2496,7 @@ function kolab_files_ui()
if (!folder.virtual)
html.push(this.folder_list_subscription_button(true));
node.html = html;
node.html = $('<div>').append(html);
delete node.children;
rcmail.folder_list.insert(node, i > 0 ? path.slice(0, i).join(separator) : null);
@ -2708,6 +2775,7 @@ function kolab_files_ui()
// empty the list
this.env.file_list = [];
rcmail.fileslist.clear(true);
rcmail.triggerEvent('listupdate', {list: rcmail.fileslist, rowcount: 0});
// request
if (params.collection || params.all_folders)
@ -2739,6 +2807,7 @@ function kolab_files_ui()
this.env.file_list = list;
rcmail.fileslist.resize();
rcmail.triggerEvent('listupdate', {list: rcmail.fileslist, rowcount: rcmail.fileslist.rowcount});
// update document sessions info of this folder
if (list && list.length)
@ -2930,12 +2999,6 @@ function kolab_files_ui()
if (c == 'name')
col = '<td class="name filename ' + this.file_type_class(data.type) + '">'
+ '<span>' + escapeHTML(file_api.file_name(data.file)) + '</span></td>';
/*
else if (c == 'mtime')
col = '<td class="mtime">' + data.mtime + '</td>';
else if (c == 'size')
col = '<td class="size">' + this.file_size(data.size) + '</td>';
*/
else if (c == 'owner')
col = '<td class="owner">' + escapeHTML(data.owner_name || data.owner) + '</td>';
else if (c == 'options')
@ -3054,16 +3117,22 @@ function kolab_files_ui()
// check if opener window contains files list, if not we can just close current window
if (rco && rco.fileslist && (opener.file_api.env.folder == dir || !opener.file_api.env.folder))
self = opener.file_api;
else
window.close();
// also try parent for framed UI (Elastic)
else if (rcmail.is_framed() && parent.rcmail.fileslist && (parent.file_api.env.folder == dir || !parent.file_api.env.folder))
self = parent.file_api;
}
// @TODO: consider list modification "in-place" instead of full reload
self.file_list();
self.quota();
if (rcmail.env.file)
window.close();
if (rcmail.env.file) {
if (rcmail.is_framed()) {
parent.$('.ui-dialog:visible > .ui-dialog-buttonpane button.cancel').click();
}
else
window.close();
}
};
// file(s) move request
@ -3147,7 +3216,7 @@ function kolab_files_ui()
// or overwrite destination file(s)
this.file_move_ask_user = function(list, move)
{
var file = list[0], buttons = {},
var file = list[0], buttons = {}, button_classes = ['mainaction save file overwrite'],
text = rcmail.gettext('kolab_files.filemoveconfirm').replace('$file', file.dst),
dialog = $('<div></div>');
@ -3164,7 +3233,8 @@ function kolab_files_ui()
file_api.request(action, {file: f, overwrite: 1}, 'file_move_ask_user_response');
};
if (list.length > 1)
if (list.length > 1) {
button_classes.push('save file overwrite all');
buttons[rcmail.gettext('kolab_files.fileoverwriteall')] = function() {
var f = {}, action = move ? 'file_move' : 'file_copy';
@ -3174,6 +3244,7 @@ function kolab_files_ui()
file_api.req = file_api.set_busy(true, move ? 'kolab_files.filemoving' : 'kolab_files.filecopying');
file_api.request(action, {file: f, overwrite: 1}, action + '_response');
};
}
var skip_func = function() {
list.shift();
@ -3185,19 +3256,24 @@ function kolab_files_ui()
file_api.file_list();
};
button_classes.push('cancel file skip');
buttons[rcmail.gettext('kolab_files.fileskip')] = skip_func;
if (list.length > 1)
if (list.length > 1) {
button_classes.push('cancel file skip all');
buttons[rcmail.gettext('kolab_files.fileskipall')] = function() {
kolab_dialog_close(this, true);
if (move)
file_api.file_list();
};
}
// open jquery UI dialog
kolab_dialog_show(dialog.html(text), {
title: rcmail.gettext('confirmationtitle'),
close: skip_func,
buttons: buttons,
button_classes: button_classes,
height: 50,
minWidth: 400,
width: 400
@ -3323,7 +3399,7 @@ function kolab_files_ui()
size += files[i].size || files[i].fileSize;
if (size > maxsize) {
alert(rcmail.get_label('kolab_files.uploadsizeerror').replace('$size', kolab_files_file_size(maxsize)));
rcmail.alert_dialog(rcmail.get_label('kolab_files.uploadsizeerror').replace('$size', kolab_files_file_size(maxsize)));
return false;
}
}
@ -3538,7 +3614,8 @@ function kolab_files_ui()
_task: 'files',
_action: params && params.action ? params.action : 'open',
_file: file,
_viewer: viewer || 0
_viewer: viewer || 0,
_frame: 1
};
if (params && params.session)
@ -3610,7 +3687,7 @@ function kolab_files_ui()
var win = window, list = rcmail.sessionslist;
if (!list) {
win = window.opener;
win = window.opener || window.parent;
if (win && win.rcmail && win.file_api)
list = win.rcmail.sessionslist;
}
@ -3618,8 +3695,13 @@ function kolab_files_ui()
// remove session from the list (if sessions list exist)
if (list)
list.remove_row(this.deleted_session);
if (win.file_api && win.file_api.env.sessions_list)
if (win && win.file_api && win.file_api.env.sessions_list)
delete win.file_api.env.sessions_list[this.deleted_session];
// For Elastic: hide the parent dialog
if (rcmail.is_framed()) {
parent.$('.ui-dialog:visible button.cancel').click();
}
};
// Invite document session participants
@ -3729,6 +3811,7 @@ function kolab_files_ui()
};
args.title = this.t('kolab_files.folderauthtitle').replace('$title', label);
args.button_classes = ['mainaction save', 'cancel'];
// show dialog window
kolab_dialog_show(dialog, args, function() {

View file

@ -236,7 +236,7 @@ class kolab_files_engine
$out = $this->rc->output->form_tag($attrib, $out);
}
$this->plugin->add_label('foldercreating', 'foldercreatenotice', 'create', 'foldercreate', 'cancel');
$this->plugin->add_label('foldercreating', 'foldercreatenotice', 'create', 'foldercreate', 'cancel', 'addfolder');
$this->rc->output->add_gui_object('folder-create-form', $attrib['id']);
return $out;
@ -256,9 +256,9 @@ class kolab_files_engine
$select_parent = new html_select(array('id' => 'folder-edit-parent', 'name' => 'parent'));
$table = new html_table(array('cols' => 2, 'class' => 'propform'));
$table->add('title', html::label('folder-name', rcube::Q($this->plugin->gettext('foldername'))));
$table->add('title', html::label('folder-edit-name', rcube::Q($this->plugin->gettext('foldername'))));
$table->add(null, $input_name->show());
$table->add('title', html::label('folder-parent', rcube::Q($this->plugin->gettext('folderinside'))));
$table->add('title', html::label('folder-edit-parent', rcube::Q($this->plugin->gettext('folderinside'))));
$table->add(null, $select_parent->show());
$out = $table->show();
@ -318,7 +318,7 @@ class kolab_files_engine
. html::img(array('src' => $source['image'], 'alt' => $key, 'title' => $source['name']))
. html::div(null, html::span('name', rcube::Q($source['name']))
. html::br()
. html::span('description', rcube::Q($source['description']))
. html::span('description hint', rcube::Q($source['description']))
. $form->show()
);
@ -348,12 +348,13 @@ class kolab_files_engine
$checkbox = new html_checkbox(array(
'name' => 'store_passwords',
'value' => '1',
'class' => 'pretty-checkbox',
'id' => 'auth-pass-checkbox' . $attrib['suffix'],
));
return html::div('auth-options', $checkbox->show(). '&nbsp;'
. html::label('auth-pass-checkbox' . $attrib['suffix'], $this->plugin->gettext('storepasswords'))
. html::span('description', $this->plugin->gettext('storepasswordsdesc'))
. html::p('description hint', $this->plugin->gettext('storepasswordsdesc'))
);
}
@ -386,28 +387,29 @@ class kolab_files_engine
*/
public function document_editors_dialog($attrib)
{
$table = new html_table(array('cols' => 3, 'border' => 0, 'cellpadding' => 0, 'class' => 'records-table'));
$table = new html_table($attrib + array('cols' => 3, 'border' => 0, 'cellpadding' => 0));
$table->add_header('username', $this->plugin->gettext('participant'));
$table->add_header('status', $this->plugin->gettext('status'));
$table->add_header('options', null);
$input = new html_inputfield(array('name' => 'participant', 'id' => 'invitation-editor-name', 'size' => 30));
$input = new html_inputfield(array('name' => 'participant', 'id' => 'invitation-editor-name', 'size' => 30, 'class' => 'form-control'));
$textarea = new html_textarea(array('name' => 'comment', 'id' => 'invitation-comment',
'rows' => 4, 'cols' => 55, 'title' => $this->plugin->gettext('invitationtexttitle')));
$button = new html_inputfield(array('type' => 'button', 'class' => 'button', 'id' => 'invitation-editor-add', 'value' => $this->plugin->gettext('addparticipant')));
'rows' => 4, 'cols' => 55, 'class' => 'form-control', 'title' => $this->plugin->gettext('invitationtexttitle')));
$button = new html_inputfield(array('type' => 'button', 'class' => 'button', 'id' => 'invitation-editor-add',
'value' => $this->plugin->gettext('addparticipant')));
$this->plugin->add_label('manageeditors', 'statusorganizer');
$this->plugin->add_label('manageeditors', 'statusorganizer', 'addparticipant');
// initialize attendees autocompletion
$this->rc->autocomplete_init();
return '<div>' . $table->show() . html::div(null,
html::div(null, $input->show() . " " . $button->show())
return html::div(null, $table->show() . html::div(null,
html::div('form-searchbar', $input->show() . " " . $button->show())
. html::p('attendees-commentbox', html::label(null,
$this->plugin->gettext('invitationtextlabel') . $textarea->show())
)
) . '</div>';
));
}
/**
@ -465,7 +467,7 @@ class kolab_files_engine
$table->add(null, $input_name->show());
$table->add('title', html::label('file-create-type', rcube::Q($this->plugin->gettext('type'))));
$table->add(null, $select_type->show());
$table->add('title', html::label('folder-parent', rcube::Q($this->plugin->gettext('folderinside'))));
$table->add('title', html::label('file-create-parent', rcube::Q($this->plugin->gettext('folderinside'))));
$table->add(null, $select_parent->show());
$out = $table->show();
@ -488,32 +490,16 @@ class kolab_files_engine
*/
public function file_search_form($attrib)
{
$attrib['name'] = '_q';
if (empty($attrib['id'])) {
$attrib['id'] = 'filesearchbox';
}
if ($attrib['type'] == 'search' && !$this->rc->output->browser->khtml) {
unset($attrib['type'], $attrib['results']);
}
$input_q = new html_inputfield($attrib);
$out = $input_q->show();
// add some labels to client
$this->rc->output->add_label('searching');
$this->rc->output->add_gui_object('filesearchbox', $attrib['id']);
$attrib += array(
'name' => '_q',
'gui-object' => 'filesearchbox',
'form-name' => 'filesearchform',
'command' => 'files-search',
'reset-command' => 'files-search-reset',
);
// add form tag around text field
if (empty($attrib['form'])) {
$out = $this->rc->output->form_tag(array(
'action' => '?_task=files',
'name' => "filesearchform",
'onsubmit' => rcmail_output::JS_OBJECT_NAME . ".command('files-search'); return false",
), $out);
}
return $out;
return $this->rc->output->search_form($attrib);
}
/**
@ -717,20 +703,20 @@ class kolab_files_engine
$table = new html_table(array('cols' => 2, 'class' => $attrib['class']));
// file name
$table->add('label', $this->plugin->gettext('name').':');
$table->add('title', $this->plugin->gettext('name').':');
$table->add('data filename', $this->file_data['name']);
// file type
// @TODO: human-readable type name
$table->add('label', $this->plugin->gettext('type').':');
$table->add('title', $this->plugin->gettext('type').':');
$table->add('data filetype', $this->file_data['type']);
// file size
$table->add('label', $this->plugin->gettext('size').':');
$table->add('title', $this->plugin->gettext('size').':');
$table->add('data filesize', $this->rc->show_bytes($this->file_data['size']));
// file modification time
$table->add('label', $this->plugin->gettext('mtime').':');
$table->add('title', $this->plugin->gettext('mtime').':');
$table->add('data filemtime', $this->file_data['mtime']);
// @TODO: for images: width, height, color depth, etc.
@ -1444,7 +1430,7 @@ class kolab_files_engine
$this->file_data['filename'] = $file;
$this->plugin->add_label('filedeleteconfirm', 'filedeleting', 'filedeletenotice');
$this->plugin->add_label('filedeleteconfirm', 'filedeleting', 'filedeletenotice', 'terminate');
// register template objects for dialogs (and main interface)
$this->rc->output->add_handlers(array(
@ -1467,6 +1453,8 @@ class kolab_files_engine
$this->rc->output->set_env('extwin', true);
$this->rc->output->set_env('file', $file);
$this->rc->output->set_env('file_data', $this->file_data);
$this->rc->output->set_env('mimetype', $this->file_data['type']);
$this->rc->output->set_env('filename', pathinfo($file, PATHINFO_BASENAME));
$this->rc->output->set_env('editor_type', $editor_type);
$this->rc->output->set_env('photo_placeholder', $placeholder);
$this->rc->output->set_pagetitle(rcube::Q($file));

View file

@ -20,10 +20,13 @@ $labels['fromcloud'] = 'From cloud...';
$labels['selectfiles'] = 'Select file(s) to attach...';
$labels['attachsel'] = 'Attach selected';
$labels['foldercreate'] = 'Create folder';
$labels['addfolder'] = 'Add folder';
$labels['folderedit'] = 'Edit folder';
$labels['foldermount'] = 'Add storage';
$labels['folderdelete'] = 'Delete folder';
$labels['folderoptions'] = 'Folder options';
$labels['findfolders'] = 'Find folders';
$labels['findfiles'] = 'Find files';
$labels['folderinside'] = 'Insert inside';
$labels['foldername'] = 'Folder name';
@ -171,11 +174,13 @@ $labels['arialabelfileprops'] = 'File properties';
$labels['arialabelfilecontent'] = 'File content';
$labels['arialabelfileeditdialog'] = 'File editing dialog';
$labels['arialabelsessionslist'] = 'List of document editing sessions';
$labels['arialabelsessionslistoptions'] = 'Sessions list options';
$labels['arialabelfilerenameform'] = 'File rename form';
$labels['arialabelfilesessiondialog'] = 'Document editing session';
$labels['arialabelcollaborators'] = 'List of document editors';
$labels['arialabelexportoptions'] = 'Export options';
$labels['arialabeldoceditorsdialog'] = 'Document editors (invitations)';
$labels['arialabelmorefileactions'] = 'More file actions';
$labels['type.plain'] = 'Plain Text Document';
$labels['type.vndoasisopendocumenttext'] = 'Text Document (ODF)';

View file

@ -0,0 +1,39 @@
<div id="files-compose-dialog" class="popupmenu" role="dialog" aria-labelledby="aria-label-fileselect">
<h3 id="aria-label-fileselect" class="voice"><roundcube:label name="kolab_files.arialabelfileselectdialog" /></h3>
<div id="folderlistbox" class="selection-list" role="navigation" aria-labelledby="aria-label-folderlist">
<h4 id="aria-label-folderlist" class="voice"><roundcube:label name="kolab_files.arialabelfolderlist" /></h4>
<div class="header">
<a class="button icon back" href="#back" data-hidden="big" onclick="$('#folderlistbox').hide();$('#filelistcontainer').css('display','flex')"><span class="inner"><roundcube:label name="back" /></span></a>
<span class="header-title"><roundcube:label name="folders" /></span>
<roundcube:object name="libkolab.folder_search_form" id="foldersearch" wrapper="searchbar toolbar"
ariatag="h5" label="foldersearchform" buttontitle="kolab_files.findfolders" />
</div>
<div id="files-folder-list" class="scroller"></div>
</div>
<div id="filelistcontainer" class="selection-content content">
<div class="header">
<a class="button icon folders" href="#sidebar" data-hidden="big" onclick="$('#filelistcontainer').hide();$('#folderlistbox').css('display','flex')"><span class="inner"><roundcube:label name="folders" /></span></a>
<span class="header-title" data-hidden="big" ><roundcube:label name="kolab_files.selectfiles" /></span>
<roundcube:object name="file-search-form" id="filesearchbox" wrapper="searchbar toolbar"
label="searchform" buttontitle="kolab_files.findfiles" label-domain="kolab_files"
ariatag="h3" options="filesearchmenu" />
</div>
<h4 id="aria-label-filelist" class="voice"><roundcube:label name="kolab_files.arialabelfilelist" /></h4>
<div class="scroller">
<roundcube:object name="filelist" id="filelist" class="records-table filelist listing sortheader fixedheader"
aria-labelledby="aria-label-filelist" data-list="fileslist" data-label-msg="listempty" />
</div>
</div>
</div>
<div id="filesearchmenu" class="popupmenu form" data-editable="true">
<h3 id="aria-label-searchmenu" class="voice"><roundcube:label name="searchmod" /></h3>
<ul class="toolbarmenu" role="menu" aria-labelledby="aria-label-searchmenu">
<li role="menuitem" class="checkbox"><label><input type="checkbox" name="all_folders" value="1" id="search_all_folders" /><roundcube:label name="kolab_files.allfolders" /></label></li>
</ul>
</div>
<div id="files-folder-auth-dialog" class="popupmenu formcontent" role="dialog" aria-labelledby="aria-label-folderauthform">
<h3 id="aria-label-folderauthform" class="voice"><roundcube:label name="kolab_files.arialabelfolderauthform" /></h3>
<roundcube:object name="folder-auth-options">
</div>

View file

@ -0,0 +1,72 @@
<roundcube:include file="includes/layout.html" />
<h1 class="voice"><roundcube:var name="env:filename" /></h1>
<div class="content selected editor-<roundcube:var name="env:editor_type" />">
<h2 id="aria-label-toolbar" class="voice"><roundcube:label name="arialabeltoolbar" /></h2>
<div class="header document-editor-header" role="toolbar" aria-labelledby="aria-label-toolbar">
<a class="button icon members" href="#members" onclick="kolab_files_members_list(this)" data-hidden="big">
<span class="inner"><roundcube:label name="kolab_files.arialabelcollaborators"></span>
</a>
<span class="header-title constant"><roundcube:var name="env:filename" /></span>
<div class="toolbar">
<roundcube:button command="document-close" type="link"
class="button delete disabled" classAct="button delete"
label="kolab_files.terminate" title="kolab_files.terminatesession" innerClass="inner" />
<span class="dropbutton">
<roundcube:button command="document-export" type="link"
class="button export disabled" classAct="button export"
label="kolab_files.get" title="kolab_files.getfile" innerClass="inner" />
<a href="#export" class="button dropdown" id="exportmenulink" data-popup="export-menu" tabindex="0">
<span class="inner"><roundcubemail:label name="kolab_files.exportoptions" /><span>
</a>
</span>
<roundcube:if condition="env:editor_type == 'wopi'" />
<roundcube:button command="document-print" type="link"
class="button print disabled" classAct="button print"
label="print" title="kolab_files.printfile" innerClass="inner" data-hidden="small" />
<roundcube:endif />
<roundcube:button command="document-save" type="link"
class="button save disabled" classAct="button save"
label="kolab_files.save" title="kolab_files.savefile" innerClass="inner" data-hidden="small" />
</div>
<roundcube:if condition="env:editor_type != 'wopi'" />
<div id="doc-title" data-hidden="small">
<label for="document-title"><roundcube:label name="kolab_files.documenttitle" />&nbsp;<input id="document-title" type="text" class="form-control" /></label>
</div>
<roundcube:endif />
<div id="collaborators" role="toolbar" aria-labelledby="aria-label-collaborators" data-hidden="small">
<h3 id="aria-label-collaborators" class="voice"><roundcube:label name="kolab_files.arialabelcollaborators" /></h3>
<roundcube:button command="document-editors" type="link"
class="button add disabled" classAct="button add"
label="kolab_files.manageeditors" title="kolab_files.manageeditors" innerClass="inner" />
<div id="members" class="session-members"></div>
</div>
</div>
<h2 id="aria-label-filecontent" class="voice"><roundcube:label name="kolab_files.arialabelfilecontent" /></h2>
<div class="iframe-wrapper">
<roundcube:object name="filepreviewframe" id="fileframe" role="main"
title="kolab_files.arialabelfilecontent" aria-labelledby="aria-label-filecontent" />
</div>
<div class="footer toolbar hidden-big">
<roundcube:button command="document-save" type="link"
class="button save disabled" classAct="button save"
label="kolab_files.save" title="kolab_files.savefile" innerClass="inner" />
<roundcube:button name="close-window" type="link" onclick="parent.$('.ui-dialog:visible button.cancel').click()"
class="button cancel" label="cancel" title="cancel" innerClass="inner" />
</div>
</div>
<div id="export-menu" class="popupmenu">
<h3 id="aria-label-exportmenu" class="voice"><roundcube:label name="kolab_files.arialabelexportoptions" /></h3>
<ul id="exportmenu-menu" class="toolbarmenu listing" role="menu" aria-labelledby="aria-label-exportmenu"></ul>
</div>
<div id="document-editors-dialog" class="popupmenu editors-dialog" data-editable="true" role="dialog" aria-labelledby="aria-label-doceditorsdialog">
<h3 id="aria-label-doceditorsdialog" class="voice"><roundcube:label name="kolab_files.arialabeldoceditorsdialog" /></h3>
<roundcube:object name="document-editors-dialog" class="records-table table no-img" />
</div>
<script src="plugins/kolab_files/skins/elastic/ui.js" type="text/javascript"></script>
<roundcube:include file="includes/footer.html" />

View file

@ -0,0 +1,89 @@
<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>
<div class="scroller">
<roundcube:object name="fileinfobox" id="fileinfo" class="listing props-table" />
</div>
</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">
<a class="button icon properties" id="properties-button" href="#properties" onclick="UI.show_sidebar()" data-hidden="big">
<span class="inner"><roundcube:label name="properties"></span>
</a>
<span class="header-title constant"><roundcube:var name="env:filename" /></span>
<div id="filetoolbar" class="toolbar">
<roundcube:button command="files-get" type="link"
class="button download disabled" classAct="button download"
label="kolab_files.get" title="kolab_files.getfile" innerClass="inner" />
<roundcube:button command="files-edit" type="link"
class="button edit disabled" classAct="button edit"
label="kolab_files.edit" title="kolab_files.editfile" innerClass="inner" data-hidden="small" />
<roundcube:button command="files-save" type="link"
class="button save disabled" classAct="button save"
label="kolab_files.save" title="kolab_files.savefile" innerClass="inner" style="display:none" data-hidden="small" />
<roundcube:button command="files-delete" type="link"
class="button delete disabled" classAct="button delete"
label="delete" title="kolab_files.deletefile" innerClass="inner" />
<roundcube:button command="files-print" type="link"
class="button print disabled" classAct="button print"
label="print" title="kolab_files.printfile" 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-filecontent" class="voice"><roundcube:label name="kolab_files.arialabelfilecontent" /></h2>
<div class="iframe-wrapper file-type-<roundcube:exp expression="strtolower(preg_replace('|/.*$|', '', env:mimetype))" />">
<roundcube:object name="filepreviewframe" id="fileframe" role="main" aria-labelledby="aria-label-filecontent" />
</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 />
</div>
<div id="files-file-create-dialog" class="popupmenu" role="dialog" aria-labelledby="aria-label-filecreateform" aria-hidden="true">
<h3 id="aria-label-filecreateform" class="voice"><roundcube:label name="kolab_files.arialabelfilecreateform" /></h3>
<roundcube:object name="file-create-form" />
</div>
<div id="files-file-edit-dialog" class="popupmenu" role="dialog" aria-labelledby="aria-label-fileeditdialog" aria-hidden="true">
<h3 id="aria-label-fileeditdialog" class="voice"><roundcube:label name="kolab_files.arialabelfileeditdialog" /></h3>
<roundcube:object name="file-edit-dialog">
</div>
<script src="plugins/kolab_files/skins/elastic/ui.js" type="text/javascript"></script>
<roundcube:include file="includes/footer.html" />

View file

@ -0,0 +1,217 @@
<roundcube:include file="includes/layout.html" />
<roundcube:include file="includes/menu.html" />
<h1 class="voice"><roundcube:label name="kolab_files.files" /></h1>
<!-- folders list -->
<div class="sidebar listbox" role="navigation" aria-labelledby="arial-label-folders">
<div class="header">
<a class="button icon back-content-button" href="#back" data-hidden="big"><span class="inner"><roundcube:label name="back" /></span></a>
<span id="aria-label-folders" class="header-title"><roundcube:label name="folders" /></span>
<roundcube:object name="libkolab.folder_search_form" id="foldersearch" wrapper="searchbar toolbar"
ariatag="h2" label="foldersearchform" buttontitle="kolab_files.findfolders" />
</div>
<div id="files-folder-list" class="scroller"></div>
<div class="footer toolbar" role="toolbar">
<roundcube:button command="folder-create" type="link"
title="kolab_files.foldercreate" label="kolab_files.addfolder"
class="button create disabled" classAct="button create" innerClass="inner" />
<roundcube:button name="folderoptions" id="folderoptionslink" type="link"
title="moreactions" label="actions"
class="button actions" innerClass="inner" data-popup="folderoptions-menu" />
<roundcube:if condition="env:files_quota" />
<div id="quotadisplay" class="quota-widget">
<span class="voice"><roundcube:label name="quota"></span>
<roundcube:object name="filequotadisplay" class="count" display="text" />
</div>
<roundcube:endif />
</div>
</div>
<!-- files -->
<div class="content selected no-navbar" 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 menu-button" href="#menu"><span class="inner"><roundcube:label name="menu" /></span></a>
<a class="button icon back-sidebar-button folders" href="#sidebar" data-hidden="big"><span class="inner"><roundcube:label name="folders" /></span></a>
<span class="header-title"></span>
<!-- toolbar -->
<div id="filestoolbar" class="toolbar">
<roundcube:button command="files-upload" type="link" onclick="$('#filesuploadform').click()"
class="button upload disabled" classAct="button upload"
label="kolab_files.upload" title="kolab_files.uploadfile" innerClass="inner" />
<roundcube:button command="files-get" type="link"
class="button download disabled" classAct="button download"
label="kolab_files.get" title="kolab_files.getfile" innerClass="inner" />
<roundcube:button command="files-open" type="link"
class="button open disabled" classAct="button open"
label="kolab_files.view" title="kolab_files.viewfile" innerClass="inner" />
<roundcube:button command="files-edit" type="link"
class="button edit disabled" classAct="button edit"
label="kolab_files.edit" title="kolab_files.editfile" innerClass="inner" />
<roundcube:button command="files-create" type="link" data-fab="true"
class="button create disabled" classAct="button create"
label="kolab_files.create" title="kolab_files.createfile" innerClass="inner "/>
<roundcube:button command="files-delete" type="link"
class="button delete disabled" classAct="button delete"
label="delete" title="kolab_files.deletefile" innerClass="inner" />
<roundcube:button name="filemenulink" id="filemenulink" type="link"
class="button more" label="more" title="moreactions"
data-popup="file-menu" innerclass="inner" />
</div>
<roundcube:object name="file-search-form" id="searchform" wrapper="searchbar toolbar"
label="searchform" buttontitle="kolab_files.findfiles" label-domain="kolab_files"
ariatag="h3" options="filesearchmenu" />
</div>
<div id="filelistcontainer" class="content" role="main" aria-labelledby="aria-label-filelist" data-elastic-mode="true">
<h2 id="aria-label-filelist" class="voice"><roundcube:label name="kolab_files.arialabelfilelist" /></h2>
<div id="filelistbox" class="droptarget">
<roundcube:object name="filelist" id="filelist" class="records-table listing filelist sortheader fixedheader"
aria-labelledby="aria-label-filelist" data-list="fileslist"
data-label-msg="listempty" data-label-ext="listusebutton" data-create-command="files-create" />
</div>
<h2 id="aria-label-sessionslist" class="voice"><roundcube:label name="kolab_files.arialabelsessionslist" /></h2>
<div id="sessionslistbox" class="boxlistcontent" style="display:none">
<roundcube:object name="sessionslist" id="sessionslist" class="records-table listing filelist sortheader fixedheader"
optionsmenuIcon="true" aria-labelledby="aria-label-sessionslist" data-label-msg="listempty" />
</div>
</div>
<div class="footer toolbar" role="toolbar">
<div id="listcontrols" class="listselectors">
<roundcube:button id="fileslistmenu-link" name="fileslistmenu-link"
type="link" onclick="kolab_files_listoptions('files')"
label="options" class="button settings" innerClass="inner" />
<roundcube:button id="sessionslistmenu-link" name="sessionslistmenu-link"
type="link" onclick="kolab_files_listoptions('sessions')"
label="options" class="button hidden settings" innerClass="inner" />
</div>
</div>
</div>
<form id="filesuploadform" class="smart-upload"></form>
<div id="files-folder-create-dialog" class="popupmenu formcontent" role="dialog" aria-labelledby="aria-label-foldercreateform">
<h3 id="aria-label-foldercreateform" class="voice"><roundcube:label name="kolab_files.arialabelfoldercreateform" /></h3>
<roundcube:object name="folder-create-form" />
</div>
<div id="files-folder-edit-dialog" class="popupmenu formcontent" role="dialog" aria-labelledby="aria-label-foldereditform">
<h3 id="aria-label-foldereditform" class="voice"><roundcube:label name="kolab_files.arialabelfoldereditform" /></h3>
<roundcube:object name="folder-edit-form" />
</div>
<div id="files-folder-mount-dialog" class="popupmenu formcontent" role="dialog" aria-labelledby="aria-label-foldermountform">
<h3 id="aria-label-foldermountform" class="voice"><roundcube:label name="kolab_files.arialabelfoldermountform" /></h3>
<roundcube:object name="folder-mount-form" />
</div>
<div id="files-file-rename-dialog" class="popupmenu formcontent" role="dialog" aria-labelledby="aria-label-filerenameform">
<h3 id="aria-label-filerenameform" class="voice"><roundcube:label name="kolab_files.arialabelfilerenameform" /></h3>
<roundcube:object name="file-rename-form" />
</div>
<div id="files-file-create-dialog" class="popupmenu formcontent" role="dialog" aria-labelledby="aria-label-filecreateform">
<h3 id="aria-label-filecreateform" class="voice"><roundcube:label name="kolab_files.arialabelfilecreateform" /></h3>
<roundcube:object name="file-create-form" />
</div>
<div id="files-folder-auth-dialog" class="popupmenu formcontent" role="dialog" aria-labelledby="aria-label-folderauthform">
<h3 id="aria-label-folderauthform" class="voice"><roundcube:label name="kolab_files.arialabelfolderauthform" /></h3>
<roundcube:object name="folder-auth-options" />
</div>
<div id="files-file-edit-dialog" class="popupmenu formcontent" role="dialog" aria-labelledby="aria-label-fileeditdialog">
<h3 id="aria-label-fileeditdialog" class="voice"><roundcube:label name="kolab_files.arialabelfileeditdialog" /></h3>
<roundcube:object name="file-edit-dialog" />
</div>
<div id="files-session-dialog" class="popupmenu formcontent" role="dialog" aria-labelledby="aria-label-filesessiondialog">
<h3 id="aria-label-filesessiondialog" class="voice"><roundcube:label name="kolab_files.arialabelfilesessiondialog" /></h3>
<roundcube:object name="file-session-dialog" />
</div>
<div id="folderoptions-menu" class="popupmenu" data-editable="true">
<h3 id="aria-label-folderoptions" class="voice"><roundcube:label name="kolab_files.folderoptions" /></h3>
<ul class="toolbarmenu listing" role="menu" aria-labelledby="aria-label-folderoptions">
<roundcube:button type="link-menuitem" command="folder-rename" label="rename" class="rename" classAct="rename active" />
<roundcube:button type="link-menuitem" command="files-folder-delete" label="delete" class="delete" classAct="delete active" />
<roundcube:if condition="!empty(env:external_sources)" />
<roundcube:button type="link-menuitem" command="folder-mount" label="kolab_files.foldermount" class="mount storage" classAct="mount storage active" />
<roundcube:endif />
<roundcube:button type="link-menuitem" command="folders" task="settings" label="managefolders" class="folders" classAct="folders active" />
</ul>
</div>
<roundcube:add_label name="kolab_files.arialabellistoptions" />
<div id="fileslistoptions" class="propform popupmenu" role="dialog" aria-labelledby="aria-label-fileslistoptions">
<h3 id="aria-label-fileslistoptions" class="voice"><roundcube:label name="kolab_files.arialabellistoptions" /></h3>
<roundcube:if condition="!in_array('kolab_files_sort_col', (array)config:dont_override)" />
<div class="form-group row">
<label for="listoptions-sortcol" class="col-form-label col-sm-4"><roundcube:label name="listsorting" /></label>
<div class="col-sm-8">
<select name="sort_col" class="form-control">
<option value="name"><roundcube:label name="kolab_files.name" /></option>
<option value="mtime"><roundcube:label name="kolab_files.mtime" /></option>
<option value="size"><roundcube:label name="size" /></option>
</select>
</div>
</div>
<roundcube:endif />
<roundcube:if condition="!in_array('kolab_files_sort_order', (array)config:dont_override)" />
<div class="form-group row">
<label for="listoptions-sortcol" class="col-form-label col-sm-4"><roundcube:label name="listorder" /></label>
<div class="col-sm-8">
<select class="form-control">
<option value="ASC"><roundcube:label name="asc" /></option>
<option value="DESC"><roundcube:label name="desc" /></option>
</select>
</div>
</div>
<roundcube:endif />
</div>
<roundcube:add_label name="kolab_files.arialabelsessionslistoptions" />
<div id="sessionslistoptions" class="propform popupmenu" role="dialog" aria-labelledby="aria-label-sessionslistoptions">
<h3 id="aria-label-sessionslistoptions" class="voice"><roundcube:label name="kolab_files.arialabelsessionslistoptions" /></h3>
<roundcube:if condition="!in_array('kolab_files_sessions_sort_col', (array)config:dont_override)" />
<div class="form-group row">
<label for="listoptions-sortcol" class="col-form-label col-sm-4"><roundcube:label name="listsorting" /></label>
<div class="col-sm-8">
<select name="sort_col" class="form-control">
<option value="name"><roundcube:label name="kolab_files.name" /></option>
</select>
</div>
</div>
<roundcube:endif />
<roundcube:if condition="!in_array('kolab_files_sessions_sort_order', (array)config:dont_override)" />
<div class="form-group row">
<label for="listoptions-sortcol" class="col-form-label col-sm-4"><roundcube:label name="listorder" /></label>
<div class="col-sm-8">
<select class="form-control">
<option value="ASC"><roundcube:label name="asc" /></option>
<option value="DESC"><roundcube:label name="desc" /></option>
</select>
</div>
</div>
<roundcube:endif />
</div>
<div id="dragfilemenu" class="popupmenu">
<ul class="toolbarmenu listing" role="menu">
<roundcube:button type="link-menuitem" command="files-move" onclick="return kolab_files_drag_menu_action('files-move')" label="move" classAct="active" />
<roundcube:button type="link-menuitem" command="files-copy" onclick="return kolab_files_drag_menu_action('files-copy')" label="copy" classAct="active" />
</ul>
</div>
<div id="filesearchmenu" class="popupmenu form" data-editable="true">
<h3 id="aria-label-searchmenu" class="voice"><roundcube:label name="searchmod" /></h3>
<ul class="toolbarmenu" role="menu" aria-labelledby="aria-label-searchmenu">
<li role="menuitem" class="checkbox"><label><input type="checkbox" name="all_folders" value="1" id="search_all_folders" /><roundcube:label name="kolab_files.allfolders" /></label></li>
</ul>
</div>
<div id="file-menu" class="popupmenu">
<h3 id="aria-label-message-menu" class="voice"><roundcube:label name="kolab_files.arialabelmorefileactions" /></h3>
<ul class="toolbarmenu listing" role="menu" aria-labelledby="aria-label-file-menu">
<roundcube:button type="link-menuitem" command="files-rename" label="kolab_files.rename" class="rename" classAct="rename active" />
<roundcube:button type="link-menuitem" command="files-move" label="moveto" class="move" classAct="move active" innerclass="folder-selector-link" aria-haspopup="true" />
<roundcube:button type="link-menuitem" command="files-copy" label="copyto" class="copy" classAct="copy active" innerclass="folder-selector-link" aria-haspopup="true" />
</ul>
</div>
<script src="plugins/kolab_files/skins/elastic/ui.js" type="text/javascript"></script>
<roundcube:include file="includes/footer.html" />

View file

@ -0,0 +1,27 @@
<div id="files-dialog" class="popupmenu selection-list" data-editable="true" role="dialog" aria-labelledby="aria-label-filesavedialog">
<h3 id="aria-label-filesavedialog" class="voice"><roundcube:label name="kolab_files.arialabelfilesavedialog" /></h3>
<div class="header">
<span class="header-title" data-hidden="small"><roundcube:label name="folders" /></span>
<span class="header-title" data-hidden="big"><roundcube:label name="kolab_files.saveto" /></span>
<roundcube:object name="libkolab.folder_search_form" id="foldersearch" wrapper="searchbar toolbar"
ariatag="h2" label="foldersearchform" buttontitle="kolab_files.findfolders" />
</div>
<h3 id="aria-label-folderlist" class="voice"><roundcube:label name="kolab_files.arialabelfolderlist" /></h3>
<div id="files-folder-list" class="scroller" aria-labelledby="aria-label-folderlist" data-no-collections="true"></div>
<div id="file-save-as" class="form-addon form-group row">
<label for="file-save-as-input" class="col-form-label col-sm-4"><roundcube:label name="kolab_files.saveas" /></label>
<div class="col-sm-8">
<input id="file-save-as-input" type="text" value="" class="form-control">
</div>
</div>
</div>
<div id="files-folder-create-dialog" class="popupmenu formcontent" role="dialog" aria-labelledby="aria-label-foldercreateform">
<h3 id="aria-label-foldercreateform" class="voice"><roundcube:label name="kolab_files.arialabelfoldercreateform" /></h3>
<roundcube:object name="folder-create-form" />
</div>
<div id="files-folder-auth-dialog" class="popupmenu formcontent" role="dialog" aria-labelledby="aria-label-folderauthform">
<h3 id="aria-label-folderauthform" class="voice"><roundcube:label name="kolab_files.arialabelfolderauthform" /></h3>
<roundcube:object name="folder-auth-options">
</div>

View file

@ -0,0 +1,147 @@
function kolab_files_enable_command(p)
{
if (p.command == 'files-save') {
var toolbar = $('#toolbar-menu');
$('a.button.edit', toolbar).parent().hide();
$('a.button.save', toolbar).show().parent().show();
if (window.editor_edit_button)
window.editor_edit_button.addClass('hidden');
if (window.editor_save_button)
window.editor_save_button.removeClass('hidden');
}
else if (p.command == 'files-edit' && p.status) {
if (window.editor_edit_button)
window.editor_edit_button.removeClass('hidden');
}
};
function kolab_files_listoptions(type)
{
var content = $('#' + type + 'listoptions'),
width = content.width() + 25,
dialog = content.clone(),
title = rcmail.gettext('kolab_files.arialabel' + (type == 'sessions' ? 'sessions' : '') + 'listoptions'),
close_func = function() { rcmail[type + 'list'].focus(); },
save_func = function(e) {
if (rcube_event.is_keyboard(e.originalEvent)) {
$('#' + type + 'listmenu-link').focus();
}
var col = $('select[name="sort_col"]', dialog).val(),
ord = $('select[name="sort_ord"]', dialog).val();
kolab_files_set_list_options([], col, ord, type);
close_func();
return true;
};
// set form values
$('select[name="sort_col"]', dialog).val(rcmail.env[type + '_sort_col'] || 'name');
$('select[name="sort_ord"]', dialog).val(rcmail.env[type + '_sort_order'] == 'DESC' ? 'DESC' : 'ASC');
dialog = rcmail.simple_dialog(dialog, title, save_func, {
cancel_func: close_func,
closeOnEscape: true,
minWidth: 400,
width: width
});
};
function kolab_files_members_list(link)
{
var dialog = $('<div id="members-dialog" class="session-members"><ul></ul></div>'),
title = $(link).text(),
add_button = $('#collaborators a.button.add'),
save_func = function(e) {
add_button.click();
return true;
};
if (add_button.is('.disabled')) {
save_func = null;
}
$('#members img').each(function() {
var cloned = $(this).clone();
$('<li>').append(cloned).append($('<span>').text(this.title))
.appendTo(dialog.find('ul'));
});
dialog = rcmail.simple_dialog(dialog, title, save_func, {
closeOnEscape: true,
width: 400,
button: 'kolab_files.addparticipant',
button_class: 'participant add',
cancel_button: 'close'
});
};
if (rcmail.env.action == 'open') {
rcmail.addEventListener('enable-command', kolab_files_enable_command);
// center and scale the image in preview frame
if (rcmail.env.mimetype.startsWith('image/')) {
$('#fileframe').on('load', function() {
var css = 'img { max-width:100%; max-height:100%; } ' // scale
+ 'body { display:flex; align-items:center; justify-content:center; height:100%; margin:0; }'; // align
$(this).contents().find('head').append('<style type="text/css">'+ css + '</style>');
});
}
// Elastic mobile preview uses an iframe in a dialog
if (rcmail.is_framed()) {
var edit_button = $('#filetoolbar a.button.edit'),
save_button = $('#filetoolbar a.button.save');
parent.$('.ui-dialog:visible .ui-dialog-buttonpane .ui-dialog-buttonset').prepend(
window.editor_save_button = $('<button>')
.addClass('save btn btn-secondary' + (save_button.is('.disabled') ? ' hidden' : ''))
.text(save_button.text())
.on('click', function() { save_button.click(); })
);
parent.$('.ui-dialog:visible .ui-dialog-buttonpane .ui-dialog-buttonset').prepend(
window.editor_edit_button = $('<button>')
.addClass('edit btn btn-secondary' + (edit_button.is('.disabled') ? ' hidden' : ''))
.text(edit_button.text())
.on('click', function() { edit_button.click(); })
);
}
}
else if (rcmail.env.action == 'edit') {
rcmail.gui_object('exportmenu', 'export-menu');
}
else {
rcmail.addEventListener('files-folder-select', function(p) {
var is_sess = p.folder == 'folder-collection-sessions';
$('#fileslistmenu-link, #layout > .content > .header > a.toggleselect, #layout > .content > .header > .searchbar')[is_sess ? 'hide' : 'show']();
$('#sessionslistmenu-link')[is_sess ? 'removeClass' : 'addClass']('hidden');
// set list header title for mobile
// $('#layout > .content > .header > .header-title').text($('#files-folder-list li.selected a.name:first').text());
});
}
$(document).ready(function() {
if (rcmail.env.action == 'open') {
$('#toolbar-menu a.button.save').parent().hide();
}
else if (rcmail.env.action == 'edit') {
if (rcmail.is_framed()) {
parent.$('.ui-dialog:visible .ui-dialog-buttonpane').addClass('hidden');
}
}
if ($('#dragfilemenu').length) {
rcmail.gui_object('file_dragmenu', 'dragfilemenu');
}
if ($('#filesearchmenu').length) {
rcmail.gui_object('file_searchmenu', 'filesearchmenu');
}
});
kolab_files_upload_input('#filesuploadform');

View file

@ -377,7 +377,7 @@ ul.toolbarmenu li span.saveas {
table-layout: fixed;
}
#fileinfobox table td.label {
#fileinfobox table td.title {
width: 60px;
font-weight: bold;
padding-right: 0;

View file

@ -54,7 +54,7 @@
<div id="document-editors-dialog" class="uidialog" data-editable="true" role="dialog" aria-labelledby="aria-label-doceditorsdialog" aria-hidden="true">
<h3 id="aria-label-doceditorsdialog" class="voice"><roundcube:label name="kolab_files.arialabeldoceditorsdialog" /></h3>
<roundcube:object name="document-editors-dialog" />
<roundcube:object name="document-editors-dialog" class="records-table" />
</div>
<roundcube:include file="/includes/footer.html" />

View file

@ -4,7 +4,7 @@
"description": "Type-aware folder management/listing for Kolab",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.5",
"version": "3.4.0",
"authors": [
{
"name": "Aleksander Machniak",
@ -21,6 +21,6 @@
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3",
"kolab/libkolab": ">=3.3.0"
"kolab/libkolab": ">=3.4.0"
}
}

View file

@ -4,7 +4,7 @@
"description": "Notes module for Roundcube connecting to a Kolab server for storage",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.3",
"version": "3.4.0",
"authors": [
{
"name": "Thomas Bruederli",
@ -21,7 +21,7 @@
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3",
"kolab/libkolab": ">=3.3.0",
"kolab/libkolab": ">=3.4.0",
"roundcube/jqueryui": ">=1.10.4"
}
}

View file

@ -8,18 +8,8 @@
<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-notebooks" class="header-title"><roundcube:label name="kolab_notes.lists" /></span>
<div id="notebook-search" class="searchbar toolbar" role="search" aria-labelledby="aria-label-notebooksearchform">
<h2 id="aria-label-label-notebooksearchform" class="voice"><roundcube:label name="kolab_notes.arialabelfoldersearchform" /></h2>
<form name="foldersearchform" onsubmit="return false">
<input id="notebooksearch" 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="kolab_notes.findnotebooks" />" tabindex="0">
<span class="inner"><roundcube:label name="kolab_notes.findnotebooks" /></span>
</a>
</div>
<roundcube:object name="libkolab.folder_search_form" id="notebooksearch" wrapper="searchbar toolbar"
ariatag="h2" label="foldersearchform" label-domain="kolab_notes" buttontitle="findnotebooks" />
</div>
<div id="notebooks-content" class="scroller">
<roundcube:object name="plugin.notebooks" id="notebooks" class="listing treelist iconized" />

View file

@ -4,7 +4,7 @@
"description": "Keyboard shortcuts compatible with Kontact",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.0",
"version": "3.4.0",
"authors": [
{
"name": "Aleksander Machniak",

View file

@ -4,7 +4,7 @@
"description": "Email tags plugin",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.4",
"version": "3.4.0",
"authors": [
{
"name": "Aleksander Machniak",
@ -21,6 +21,6 @@
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3",
"kolab/libkolab": ">=3.3.0"
"kolab/libkolab": ">=3.4.0"
}
}

View file

@ -529,7 +529,7 @@ function remove_tags(tags, selection)
var taglist = $('#taglist li'),
list = main_list_widget(),
tagboxes = $((selection && list ? 'tr.selected ' : '') + 'span.tagbox'),
tagboxes = $('span.tagbox'),
win = rcmail.get_frame_window(rcmail.env.contentframe),
frame_tagboxes = win && win.jQuery ? win.jQuery('span.tagbox') : [],
update_filter = false;
@ -602,7 +602,7 @@ function reset_tags()
function tag_add(props, obj, event)
{
if (!props) {
return tag_selector(event, function(props) { rcmail.command('tag-add', props); });
return tag_selector(event, function(props) { tag_add(props); });
}
var tag, postdata = rcmail.selection_post_data();
@ -668,7 +668,7 @@ function tag_add_callback(tag)
function tag_remove(props, obj, event)
{
if (!props) {
return tag_selector(event, function(props) { rcmail.command('tag-remove', props); }, true);
return tag_selector(event, function(props) { tag_remove(props); }, true);
}
if (props.name) {
@ -689,8 +689,40 @@ function tag_remove(props, obj, event)
postdata._act = 'remove';
rc.http_post('plugin.kolab_tags', postdata, true);
// remove tags from message(s) without waiting to a response
// in case of an error the list will be refreshed
tag_remove_callback(this.tag_find(props));
}
// update messages list and message frame after removing tag assignments
function tag_remove_callback(tag)
{
if (!tag)
return;
var frame_window = rcmail.get_frame_window(rcmail.env.contentframe),
list = rcmail.message_list;
if (list) {
$.each(list.get_selection(), function (i, uid) {
var row = list.rows[uid];
if (row) {
$('span.tagbox', row.obj).each(function() {
if (!tag || $(this).data('tag') == tag.uid) {
$(this).remove();
}
});
}
});
message_list_select(list);
}
// TODO: remove tag(s) from the preview frame
}
// executes messages search according to selected messages
function apply_tags_filter()
{
@ -732,11 +764,22 @@ function search_request(url)
function message_list_select(list)
{
var has_tags_to_remove = (rcmail.env.tags.length
&& (rcmail.select_all_mode || $('tr.selected span.tagbox', list.list).length));
var selection = list.get_selection(),
has_tags = selection.length && rcmail.env.tags.length;
rcmail.enable_command('tag-remove', 'tag-remove-all', has_tags_to_remove);
rcmail.enable_command('tag-add', list.selection.length);
if (has_tags && !rcmail.select_all_mode) {
has_tags = false;
$.each(selection, function() {
var row = list.rows[this];
if (row && row.obj && $('span.tagbox', row.obj).length) {
has_tags = true;
return false;
}
});
}
rcmail.enable_command('tag-remove', 'tag-remove-all', has_tags);
rcmail.enable_command('tag-add', selection.length);
}
// add tags to message subject on message list
@ -897,7 +940,7 @@ function tag_selector(event, callback, remove_mode)
// temporarily show element to calculate its size
container.css({left: '-1000px', top: '-1000px'})
.appendTo(document.body).show()
.on('click', 'a.tag', function() {
.on('click', 'a.tag', function(e) {
container.data('callback')($(this).data('uid'));
return rcmail.hide_menu('tag-selector', e);
});

View file

@ -4,7 +4,7 @@
"description": "LDAP Authentication (obsoleted)",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.0",
"version": "3.4.0",
"authors": [
{
"name": "Aleksander Machniak",

View file

@ -4,7 +4,7 @@
"description": "Library providing common functions for calendaring plugins",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.5",
"version": "3.4.0",
"authors": [
{
"name": "Thomas Bruederli",

View file

@ -613,6 +613,20 @@ class libcalendaring_itip
}
}
// It may happen that sender's address is different in From: and the attached iTip
// In such case use the ATTENDEE entry with the address from From: header
if (empty($metadata['attendee']) && !empty($event['_sender'])) {
// remove the organizer
$itip_attendees = array_filter($event['attendees'], function($item) { return $item['role'] != 'ORGANIZER'; });
// there must be only one attendee
if (is_array($itip_attendees) && count($itip_attendees) == 1) {
$event_attendee = $itip_attendees[key($itip_attendees)];
$metadata['attendee'] = $event['_sender'];
$rsvp_status = strtoupper($event_attendee['status']);
}
}
// 1. update the attendee status on our copy
$update_button = html::tag('input', array(
'type' => 'button',

View file

@ -676,14 +676,6 @@ function rcube_libcalendaring(settings)
this.alarm_dialog = $('<div>').attr('id', 'alarm-display').append(records);
buttons.push({
text: rcmail.gettext('close'),
click: function() {
$(this).dialog('close');
},
'class': 'cancel'
});
buttons.push({
text: rcmail.gettext('dismissall','libcalendaring'),
click: function(e) {
@ -694,6 +686,14 @@ function rcube_libcalendaring(settings)
'class': 'delete'
});
buttons.push({
text: rcmail.gettext('close'),
click: function() {
$(this).dialog('close');
},
'class': 'cancel'
});
this.alarm_dialog.appendTo(document.body).dialog({
modal: true,
resizable: true,

View file

@ -4,7 +4,7 @@
"description": "Plugin to setup a basic environment for the interaction with a Kolab server.",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.5",
"version": "3.4.0",
"authors": [
{
"name": "Thomas Bruederli",

View file

@ -157,7 +157,8 @@ abstract class kolab_storage_folder_api
*/
public function get_foldername()
{
$parts = explode('/', $this->name);
$parts = explode($this->imap->get_hierarchy_delimiter(), $this->name);
return rcube_charset::convert(end($parts), 'UTF7-IMAP');
}
@ -168,7 +169,9 @@ abstract class kolab_storage_folder_api
*/
public function get_parent()
{
$path = explode('/', $this->name);
$delim = $this->imap->get_hierarchy_delimiter();
$path = explode($delim, $this->name);
array_pop($path);
// don't list top-level namespace folder
@ -176,7 +179,7 @@ abstract class kolab_storage_folder_api
$path = array();
}
return join('/', $path);
return join($delim, $path);
}
/**

View file

@ -58,6 +58,7 @@ class libkolab extends rcube_plugin
$this->add_texts('localization/', false);
if ($rcmail->output->type == 'html') {
$rcmail->output->add_handler('libkolab.folder_search_form', array($this, 'folder_search_form'));
$this->include_stylesheet($this->local_skin_path() . '/libkolab.css');
}
@ -342,4 +343,29 @@ class libkolab extends rcube_plugin
{
return $event['allday'] ? 'Ymd' : 'Ymd\THis';
}
/**
* Returns HTML code for folder search widget
*
* @param array $attrib Named parameters
*
* @return string HTML code for the gui object
*/
public function folder_search_form($attrib)
{
$rcmail = rcube::get_instance();
$attrib += array(
'gui-object' => false,
'wrapper' => true,
'form-name' => 'foldersearchform',
'command' => 'non-extsing-command',
'reset-command' => 'non-existing-command',
);
if ($attrib['label-domain'] && !strpos($attrib['buttontitle'], '.')) {
$attrib['buttontitle'] = $attrib['label-domain'] . '.' . $attrib['buttontitle'];
}
return $rcmail->output->search_form($attrib);
}
}

View file

@ -287,7 +287,6 @@ body.quickview-active .fc-content {
cursor: pointer;
}
// Button states
// borrowed from twitter bootstrap (http://twitter.github.com/bootstrap/)
@ -884,6 +883,32 @@ body.quickview-active .fc-content {
}
}
// Add scrollbar on iOS
html:matches(.ipad,.iphone) {
.ui-dialog-content:not(.iframe),
#fish-eye-view,
.fc-list-content,
.fc-view > div > div {
&::-webkit-scrollbar {
-webkit-appearance: none;
}
&::-webkit-scrollbar:vertical {
width: .6rem;
}
&::-webkit-scrollbar:horizontal {
height: .6rem;
}
&::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, .4);
border-radius: .3rem;
border: 2px solid #fff;
}
}
}
// fullcalendar style overrides for printing
.print-content {
.fc-header {
@ -917,6 +942,7 @@ body.quickview-active .fc-content {
color: black !important;
}
.fc-state-highlight,
.fc-event:not(.fc-event-row) .fc-event-skin {
background-color: #fff !important;
}
@ -944,7 +970,7 @@ body.quickview-active .fc-content {
padding-right: 0.3em;
}
.fc-event-cateories {
.fc-event-categories {
font-style: italic;
}
@ -981,30 +1007,40 @@ body.quickview-active .fc-content {
}
.fc-view-table {
tr.fc-event td,
tr.fc-event td.fc-event-handle {
border-color: @color-calendar-border;
padding-top: 0.5em;
padding-bottom: 0.5em;
tr.fc-event {
td,
td.fc-event-handle {
border-color: @color-calendar-border;
padding-top: .5em;
padding-bottom: .5em;
}
td.fc-event-handle {
padding-top: .8em;
}
.fc-event-description {
padding-left: 1.5em;
padding-top: 0;
}
}
tr.fc-last td {
border: 0;
}
tr.fc-event .fc-event-description {
padding-left: 2em;
padding-top: 0em;
}
col.fc-event-location {
width: 20%;
}
}
.fc-event-vert .fc-event-description {
font-size: 90%;
font-style: italic;
.fc-event-description {
white-space: pre-wrap;
.fc-event-vert & {
font-size: 90%;
font-style: italic;
}
}
.fc-day {
@ -1227,10 +1263,6 @@ body.task-calendar {
display: none;
}
}
.faded {
color: @color-black-shade-text;
}
}
#eventedit {
@ -1246,7 +1278,10 @@ body.task-calendar {
#fish-eye-view {
padding: 0;
border-bottom: 1px solid @color-calendar-border;
@media screen and (min-width: (@screen-width-small + 1px)) {
border-bottom: 1px solid @color-calendar-border;
}
.fc-header {
display: none;
@ -1339,20 +1374,79 @@ body.task-calendar {
input[type=checkbox] {
width: auto !important;
}
@media screen and (max-width: 420px) {
input {
width: 8em !important;
& + input {
width: 6em !important;
}
}
}
}
.calendar-scheduler {
.schedule-nav {
position: absolute;
right: 1rem;
top: 1rem;
.nav {
align-items: center;
html.layout-phone & {
top: 1.5rem;
button:first-child {
margin-right: .25rem;
}
& > div {
white-space: nowrap;
}
@media screen and (max-width: 420px) {
button {
padding-left: .5rem;
padding-right: .5rem;
}
}
}
.schedule-buttons {
.prev-slot:before {
content: @fa-var-chevron-left;
}
.next-slot:after {
&:extend(.font-icon-class);
content: @fa-var-chevron-right;
display: inline-block;
float: none;
margin-right: 0;
}
}
.schedule-options {
flex: 1;
margin-left: 1rem;
html.layout-phone & {
order: 100;
margin-left: 0;
}
label {
vertical-align: middle;
}
input.icon-checkbox + label:before {
margin-right:.25rem;
display: inline;
float: none;
}
}
.schedule-nav {
flex: 1;
margin-left: .3rem;
text-align: right;
button {
padding: 0 1rem;
line-height: 1.8;
}
}
@ -1371,48 +1465,6 @@ body.task-calendar {
}
}
.slot-nav {
display: flex;
align-items: center;
html.layout-phone & {
flex-wrap: wrap;
}
}
.schedule-find-buttons {
margin-right: 1rem;
float: left;
button:first-child {
margin-right: .5rem;
}
.prev-slot:before {
content: @fa-var-chevron-left;
}
.next-slot:after {
&:extend(.font-icon-class);
content: @fa-var-chevron-right;
display: inline-block;
float: none;
margin-right: 0;
}
}
.schedule-options {
label {
vertical-align: middle;
}
input.icon-checkbox + label:before {
margin-right:.25rem;
display: inline;
float: none;
}
}
.attendees-list {
position: relative;
@ -1527,14 +1579,23 @@ body.task-calendar {
height: 10px;
}
tr.dates th[colspan="1"] {
min-width: 48px;
max-width: 48px;
text-align: center;
font-size: .7rem;
line-height: 2.9;
}
tr.times td {
cursor: pointer;
min-width: 30px;
min-width: 48px;
max-width: 48px;
font-size: .7rem;
text-align: center;
color: @color-link;
height: 1.4rem;
padding: 0 .25rem;
padding: 0 .1rem;
vertical-align: middle;
border-top: 1px solid @color-table-border;
border-left: 1px solid @color-list-border;
@ -1578,11 +1639,11 @@ body.task-calendar {
// This span imitates a slanting line across the parent element
span {
display: block;
width: 200%;
height: 200%;
width: 300%;
height: 300%;
border: 1px solid #fff;
background: darken(@color-availability-busy, 10%);
transform: rotate(33deg) translate(10%);
transform: rotate(42deg) translate(2%);
}
&.w10 span {
@ -1591,32 +1652,36 @@ body.task-calendar {
&.w20 span,
&.w25 span {
transform: rotate(10deg) translate(10%);
transform: rotate(17deg) translate(-9%);
}
&.w30 span {
transform: rotate(15deg) translate(10%);
transform: rotate(28deg) translate(-7%);
}
&.w40 span {
transform: rotate(20deg) translate(10%);
transform: rotate(36deg) translate(-3%);
}
&.w60 span {
transform: rotate(48deg) translate(6%);
}
&.w70 span,
&.w75 span {
transform: rotate(42deg) translate(13%);
transform: rotate(55deg) translate(12%, 30%);
}
&.w80 span {
transform: rotate(48deg) translate(15%);
transform: rotate(56deg) translate(13%, 30%);
}
&.w90 span {
transform: rotate(52deg) translate(18%);
transform: rotate(59deg) translate(16%, 30%);
}
&.w100 span {
transform: rotate(55deg) translate(20%, 15%);
transform: rotate(62deg) translate(19%, 30%);
}
}
@ -1640,35 +1705,13 @@ body.task-calendar {
}
.resources-dialog {
display: flex;
height: 100%;
.resource-selection {
flex: 4;
border: 1px solid @color-layout-border;
min-width: 300px;
justify-content: center;
.header {
border-bottom: 1px solid @color-layout-border;
display: flex;
background-color: @color-layout-header-background;
font-size: @layout-header-font-size;
font-weight: bold;
line-height: @layout-header-height;
height: @layout-header-height;
min-height: @layout-header-height;
padding: 0 .25em;
position: relative; // for absolute positioning of searchbar
overflow: hidden;
white-space: nowrap;
}
}
.resource-content {
flex: 10;
display: flex;
flex-direction: column;
margin-left: 1em;
}
@ -1680,6 +1723,32 @@ body.task-calendar {
content: @fa-var-cube;
}
}
.slot-nav {
display: none; // TODO
}
}
#resource-availability {
height: 100%;
.fc {
height: 100%;
overflow: hidden;
position: relative;
}
.fc-content {
}
.fc-view {
border-left: 1px solid @color-calendar-border;
border-bottom: 1px solid @color-calendar-border;
}
table.fc-header {
display: none;
}
}
.standalone-invitebox {
@ -1687,7 +1756,7 @@ body.task-calendar {
padding-top: 36vh;
max-width: 500px;
width: 95%;
background: url(../../../../skins/elastic/images/watermark.jpg) center -20px no-repeat;
background: url("@{skin-path}/images/watermark.jpg") center -20px no-repeat;
background-size: auto 40%;
.invitebox {
@ -1785,23 +1854,22 @@ body.task-calendar {
.sidebar .calendar-datepicker {
display: none;
}
}
@media screen and (max-width: 420px) {
#eventedit {
.nav-link {
padding: .5rem;
.resources-dialog {
.resource-content {
display: none;
margin: 0;
}
.resource-content {
ul {
margin: 1em 1em 0 1em;
}
}
}
.datetime {
input {
width: 8em !important;
& + input {
width: 6em !important;
}
}
#resource-availability {
margin: 0 1em;
}
}

View file

@ -23,7 +23,450 @@
a.btn.fromcloud:before,
li > a.icon.saveas:before,
a.button.filesaveall:before {
a.button.filesaveall:before,
a.button.saveas:before {
&:extend(.font-icon-class);
content: @fa-var-folder;
}
.toolbarmenu.listing li a.mount.storage:before {
content: @fa-var-database;
}
.toolbar a.button.open:before {
content: @fa-var-eye;
}
.toolbar a.button.rename:before {
content: @fa-var-pen-square;
}
.toolbar a.button.cancel:before {
content: @fa-var-times;
}
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset a.btn-link.options.add-folder {
order: -1;
&:before {
content: @fa-var-plus;
}
}
.folderlist {
li.collection {
a.name {
padding-left: .5em;
}
&.audio a.name:before {
.font-icon-solid(@fa-var-volume-up);
}
&.video a.name:before {
.font-icon-solid(@fa-var-video);
}
&.image a.name:before {
content: @fa-var-image;
}
&.document a.name:before {
.font-icon-solid(@fa-var-book);
}
&.sessions a.name:before {
.font-icon-solid(@fa-var-users);
}
}
li.mailbox {
a.subscription {
cursor: pointer;
// reset listing's link style
padding: 0;
border-left: 0;
width: auto;
&:before {
.font-icon-regular(@fa-var-bookmark); // TODO: better icon
height: auto;
color: @color-link;
margin-right: .25rem;
}
&.subscribed:before {
&:extend(.font-icon-class);
.font-icon-solid(@fa-var-bookmark); // TODO: better icon
}
}
}
}
#filelistcontainer {
position: relative;
}
#filelistbox {
height: 100%;
&.droptarget {
&.active {
box-shadow: 0 0 2px 1px darken(@color-toolbar-button-background-hover, 20%) inset;
}
&.hover {
box-shadow: 0 0 2px 1px darken(@color-toolbar-button-background-hover, 20%) inset;
background-color: @color-toolbar-button-background-hover;
}
}
}
.filelist {
thead {
display: none;
}
td.options {
display: none;
}
td.name {
position: relative;
}
td.mtime {
width: 11em;
}
td.size {
text-align: right;
width: 6em;
}
tr.session > td.name {
padding-right: 2em;
&:after {
&:extend(.font-icon-class);
content: @fa-var-users;
color: @color-black-shade-border;
position: absolute;
right: .25em;
top: 0;
}
}
tr.session.invited > td.name:after {
color: @color-success;
}
tr.session.owner > td.name:after {
color: @color-black;
}
}
@media screen and (max-width: @screen-width-xs) {
.filelist {
td.mtime {
display: none;
}
}
}
.filelist tbody td.filename {
& span {
background: transparent !important;
}
& span:before {
&:extend(.font-icon-class);
.font-icon-regular(@fa-var-file);
}
&.pdf span:before,
&.application_pdf span:before {
content: @fa-var-file-pdf;
}
&.application_vnd_ms_word span:before,
&.application_msword span:before {
content: @fa-var-file-word;
}
&.application_vnd_ms_excel span:before,
&.application_vnd_oasis_opendocument_spreadsheet span:before,
&.application_vnd_oasis_opendocument_spreadsheet_template span:before {
content: @fa-var-file-excel;
}
&.application_vnd_ms_powerpoint span:before {
content: @fa-var-file-powerpoint;
}
&.application_vnd_oasis_opendocument_text span:before {
content: @fa-var-file-alt; // TODO
}
&.application_vnd_oasis_opendocument_presentation span:before,
&.application_vnd_oasis_opendocument_presentation_template span:before {
content: @fa-var-file-powerpoint; // TODO
}
&.tar span:before,
&.application_zip span:before,
&.application_x_7z_compressed span:before,
&.application_x_ace span:before,
&.application_x_arc span:before,
&.application_x_arj span:before,
&.application_x_bzip_compressed_tar span:before,
&.application_x_lha span:before,
&.application_x_rar span:before,
&.application_x_tarz span:before,
&.application_x_tzo span:before,
&.application_x_zip span:before,
&.application_x_zoo span:before {
content: @fa-var-file-archive;
}
&.image span:before,
&.image_png span:before,
&.image_svg_xml span:before,
&.image_jpeg span:before,
&.image_jpeg2000 span:before,
&.image_x_eps span:before,
&.application_vnd_stardivision_draw span:before,
&.application_vnd_sun_xml_draw span:before,
&.application_vnd_sun_xml_draw_template span:before {
content: @fa-var-file-image;
}
&.application_pgp_keys span:before,
&.application_pkcs7_mime span:before {
// TODO
}
&.audio span:before {
content: @fa-var-file-audio;
}
&.video span:before {
content: @fa-var-file-video;
}
&.ascii span:before,
&.text_plain span:before {
content: @fa-var-file-alt;
}
&.vcalendar span:before {
// TODO
}
&.vcard span:before,
&.text_x_vcard span:before {
// TODO
}
&.text_html span:before {
content: @fa-var-file-code;
}
&.style_css span:before {
// TODO
}
&.text_csv span:before {
// TODO
}
&.message_rfc822 span:before {
// TODO
}
}
#folder-mount-form {
td.source {
display: flex;
align-items: flex-start;
padding: .5rem;
&.selected {
background-color: @color-list-selected-background;
}
& > input {
height: 32px;
margin-right: .5rem;
}
& > div {
flex: 1;
}
img {
height: 32px;
margin-right: .5rem;
}
.name {
font-weight: bold;
}
.description {
font-size: 90%;
}
table {
margin-top: .5rem;
}
.form-group {
margin-bottom: 0;
}
}
.auth-options {
margin-top: .5rem;
& > label:before {
line-height: 1;
}
}
}
.files-dialog {
.selection-list {
flex: 1;
min-width: 220px;
}
.selection-content {
flex: 2;
@media screen and (min-width: (@screen-width-small + 1px)) {
border: 1px solid @color-layout-border;
border-left: 0;
}
}
a.subscription {
display: none;
}
@media screen and (max-width: @screen-width-small) {
.selection-list {
display: none;
}
}
}
.document-editor-header {
.toolbar {
text-align: left !important;
}
.header-title {
margin: 0 !important;
}
a.button.icon.members:before {
content: @fa-var-users;
}
#collaborators {
order: 9;
display: flex;
margin-right: .5rem;
a.button.add:before {
&:extend(.font-icon-class);
content: @fa-var-plus;
}
.inner {
display: none;
}
}
#doc-title {
flex: 1;
order: 8;
input {
display: inline;
}
}
}
.session-members {
img.photo {
width: 48px;
height: 48px;
min-width: 48px;
overflow: hidden;
background: url("@{skin-path}/images/contactpic.png") center center no-repeat #fff;
background-size: cover;
border-radius: 50%;
border: solid 3px #eee;
margin-left: .5rem;
}
ul {
padding: 0;
margin: 0;
list-style-type: none;
li {
display: flex;
align-items: center;
}
img.photo {
margin-left: 0;
margin-right: 1em;
}
}
}
.editors-dialog {
table {
th {
border: 0;
}
tr:last-child td {
border-bottom: 1px solid @color-layout-border;
}
td.options {
width: 1%;
a.delete:before {
&:extend(.font-icon-class);
content: @fa-var-trash-alt;
display: inline;
line-height: 1;
}
}
}
label {
display: block;
}
}
button.participant.add:before {
content: @fa-var-user-plus;
}
.iframe-wrapper.file-type-audio,
.iframe-wrapper.file-type-video {
display: flex;
align-items: center;
justify-content: center;
@media screen and (min-width: (@screen-width-small + 1px)) {
padding: 1em;
}
}

View file

@ -695,3 +695,31 @@ html.touch {
}
}
}
@media screen and (max-width: 420px) {
.nav-link.nav-icon {
width: 3em;
margin-right: 5px;
padding: .5rem .5rem .5rem .65rem;
&:before {
.font-icon-class;
margin: 0 1rem 0 0;
width: 1em;
line-height: 1.2;
}
&.resources:before {
content: @fa-var-cube;
margin-left: .1rem;
}
&.attachments:before {
content: @fa-var-paperclip;
}
&.attendees:before {
content: @fa-var-users;
}
}
}

View file

@ -304,10 +304,6 @@ html.touch #tasklist {
.attachmentslist li {
margin-right: 1em;
}
.faded {
color: @color-black-shade-text;
}
}
#taskedit-attachment-form {

View file

@ -21,8 +21,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@import (reference) "../../../../skins/elastic/styles/variables";
@import (reference) "../../../../skins/elastic/styles/mixins";
@skin: "elastic";
@skin-path: "../../../../skins/@{skin}";
@import (reference) "@{skin-path}/styles/variables";
@import (reference) "@{skin-path}/styles/mixins";
/*** Common folders list extensions ***/
@ -366,6 +369,10 @@ button.btn.print:before {
content: @fa-var-print;
}
.formcontent.text-only .faded * {
color: @color-black-shade-text;
}
.print-config {
position: fixed;
top: 0;
@ -408,6 +415,134 @@ button.btn.print:before {
}
}
.selection-dialog {
.ui-dialog-content {
display: flex !important;
overflow: hidden !important;
}
.selection-list {
display: flex;
flex-direction: column;
border: 1px solid @color-layout-border;
justify-content: center;
}
.ui-dialog-content .popupmenu {
display: flex !important; // overwrites .popupmenu style
width: 100%;
}
.scroller {
flex: 1;
overflow-y: auto;
}
.header {
border-bottom: 1px solid @color-layout-border;
display: flex;
background-color: @color-layout-header-background;
font-size: @layout-header-font-size;
font-weight: bold;
line-height: @layout-header-height;
height: @layout-header-height;
min-height: @layout-header-height;
padding: 0 .25em;
position: relative; // for absolute positioning of searchbar
overflow: hidden;
white-space: nowrap;
}
.header-title {
width: 100%;
text-align: center;
margin: 0 2em;
}
.selection-content {
display: flex;
flex-direction: column;
}
.tab-content {
margin-top: 1rem;
height: 100%;
overflow-y: auto;
}
.form-addon {
margin: 0;
padding: .5rem 0;
width: 100%;
background-color: @color-black-shade-bg;
border-top: 1px solid @color-layout-border;
}
// overwrite .popupmenu colors
.listing {
ul {
background-color: @color-layout-list-background;
}
li.selected {
color: @color-font;
}
}
@media screen and (max-width: @screen-width-small) {
.ui-dialog-content {
padding: 0 !important;
}
.selection-list {
border: 0;
}
.ui-dialog-titlebar {
display: none;
margin: 0;
}
.selection-content {
.header-title {
margin-left: 0;
}
}
.header {
a:before {
font-size: 1.75rem;
}
}
}
}
// Use icons-only on taskmenu with small screen height
@media screen and (max-height: 640px) and (min-width: (@screen-width-small + 1px)) {
body > #layout > .menu {
width: 2.5rem;
}
#taskmenu {
a {
height: auto;
width: 100%;
&:before {
height: 2.1rem;
width: 100%;
}
}
.special-buttons {
width: 2.5rem;
}
span.inner {
display: none;
}
}
}
@import "include/calendar";
@import "include/kolab_activesync";

View file

@ -7,18 +7,20 @@
<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 class="scroller">
<roundcube:object name="plugin.attachmentcontrols" class="listing props-table" role="contentinfo"
aria-labelledby="aria-label-contentinfo" />
</div>
</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">
<a class="button icon properties" id="properties-button" href="#properties" onclick="UI.show_sidebar()" data-hidden="big">
<span class="inner"><roundcube:label name="properties"></span>
</a>
<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"

View file

@ -4,7 +4,7 @@
"description": "Logon screen additions",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.0",
"version": "3.4.0",
"authors": [
{
"name": "Aleksander Machniak",

View file

@ -4,7 +4,7 @@
"description": "Open Document Viewer plugin",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.0",
"version": "3.4.0",
"authors": [
{
"name": "Thomas Bruederli",

View file

@ -4,7 +4,7 @@
"description": "Inline PDF viewer plugin",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.0",
"version": "3.4.0",
"authors": [
{
"name": "Thomas Bruederli",

View file

@ -4,7 +4,7 @@
"description": "Task management plugin",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.5",
"version": "3.4.0",
"authors": [
{
"name": "Thomas Bruederli",
@ -21,8 +21,8 @@
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3",
"kolab/libcalendaring": ">=3.3.0",
"kolab/libkolab": ">=3.3.0",
"kolab/libcalendaring": ">=3.4.0",
"kolab/libkolab": ">=3.4.0",
"roundcube/jqueryui": ">=1.10.4"
}
}

View file

@ -8,18 +8,8 @@
<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="tasklist.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>
<roundcube:object name="libkolab.folder_search_form" id="tasklistsearch" wrapper="searchbar toolbar"
ariatag="h2" label="foldersearchform" label-domain="tasklist" buttontitle="findlists" />
</div>
<div id="tasklists-content" class="scroller">
<roundcube:object name="plugin.tasklists" id="tasklists" class="treelist listing iconized" />
@ -105,7 +95,7 @@
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" />
label="tasklist.export" title="tasklist.exporttitle" innerClass="inner" />
<roundcube:container name="toolbar" id="taskstoolbar" />
</div>
</div>

View file

@ -95,7 +95,7 @@
</div>
</fieldset>
<!-- attendees list (assignments) -->
<fieldset id="taskedit-panel-attendees">
<fieldset id="taskedit-panel-attendees" data-navlink-class="nav-icon attendees">
<legend><roundcube:label name="tasklist.tabassignments" /></legend>
<div class="form-group row" id="taskedit-organizer">
<label for="edit-identities-list" class="col-form-label col-sm-2"><roundcube:label name="tasklist.roleorganizer" /></label>
@ -107,7 +107,7 @@
<roundcube:object name="plugin.attendees_form" id="edit-attendees-form" />
</fieldset>
<!-- attachments list (with upload form) -->
<fieldset id="taskedit-panel-attachments">
<fieldset id="taskedit-panel-attachments" data-navlink-class="nav-icon 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>

View file

@ -4,7 +4,7 @@
"description": "TinyMCE Editor Configurator",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.0",
"version": "3.4.0",
"authors": [
{
"name": "Aleksander Machniak",

View file

@ -4,7 +4,7 @@
"description": "Kolab Web Admin Client",
"homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3",
"version": "3.3.0",
"version": "3.4.0",
"authors": [
{
"name": "Aleksander Machniak",
@ -21,6 +21,6 @@
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3",
"kolab/libkolab": ">=3.3.0"
"kolab/libkolab": ">=3.4.0"
}
}