Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F9121960
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
57 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/plugins/kolab_files/kolab_files.js b/plugins/kolab_files/kolab_files.js
index 451847c0..a1c72fa0 100644
--- a/plugins/kolab_files/kolab_files.js
+++ b/plugins/kolab_files/kolab_files.js
@@ -1,707 +1,742 @@
/**
* Kolab files plugin
*
* @version @package_version@
* @author Aleksander Machniak <alec@alec.pl>
*/
window.rcmail && rcmail.addEventListener('init', function() {
if (rcmail.task == 'mail') {
// mail compose
if (rcmail.env.action == 'compose') {
var elem = $('#compose-attachments > div'),
input = $('<input class="button" type="button">');
input.val(rcmail.gettext('kolab_files.fromcloud'))
.click(function() { kolab_files_selector_dialog(); })
.appendTo(elem);
if (rcmail.gui_objects.filelist) {
rcmail.file_list = new rcube_list_widget(rcmail.gui_objects.filelist, {
multiselect: true,
// draggable: true,
keyboard: true,
column_movable: false,
dblclick_time: rcmail.dblclick_time
});
rcmail.file_list.addEventListener('select', function(o) { kolab_files_list_select(o); });
rcmail.file_list.addEventListener('listupdate', function(e) { rcmail.triggerEvent('listupdate', e); });
rcmail.gui_objects.filelist.parentNode.onmousedown = function(e){ return kolab_files_click_on_list(e); };
rcmail.enable_command('files-sort', 'files-search', 'files-search-reset', true);
rcmail.file_list.init();
kolab_files_list_coltypes();
}
}
// mail preview
else if (rcmail.env.action == 'show' || rcmail.env.action == 'preview') {
var attachment_list = $('#attachment-list');
if ($('li', attachment_list).length) {
var link = $('<a href="#" class="button filesaveall">')
.text(rcmail.gettext('kolab_files.saveall'))
.click(function() { kolab_directory_selector_dialog(); })
.appendTo(attachment_list);
}
+
+ rcmail.addEventListener('menu-open', kolab_files_attach_menu_open);
}
kolab_files_init();
}
else if (rcmail.task == 'files') {
if (rcmail.gui_objects.filelist) {
rcmail.file_list = new rcube_list_widget(rcmail.gui_objects.filelist, {
multiselect: true,
draggable: true,
keyboard: true,
column_movable: rcmail.env.col_movable,
dblclick_time: rcmail.dblclick_time
});
/*
rcmail.file_list.row_init = function(o){ kolab_files_init_file_row(o); };
rcmail.file_list.addEventListener('dblclick', function(o){ p.msglist_dbl_click(o); });
rcmail.file_list.addEventListener('click', function(o){ p.msglist_click(o); });
rcmail.file_list.addEventListener('keypress', function(o){ p.msglist_keypress(o); });
*/
rcmail.file_list.addEventListener('select', function(o){ kolab_files_list_select(o); });
/*
rcmail.file_list.addEventListener('dragstart', function(o){ p.drag_start(o); });
rcmail.file_list.addEventListener('dragmove', function(e){ p.drag_move(e); });
rcmail.file_list.addEventListener('dragend', function(e){ p.drag_end(e); });
*/
rcmail.file_list.addEventListener('column_replace', function(e){ kolab_files_set_coltypes(e); });
rcmail.file_list.addEventListener('listupdate', function(e){ rcmail.triggerEvent('listupdate', e); });
// document.onmouseup = function(e){ return p.doc_mouse_up(e); };
rcmail.gui_objects.filelist.parentNode.onmousedown = function(e){ return kolab_files_click_on_list(e); };
rcmail.enable_command('menu-open', 'menu-save', 'files-sort', 'files-search', 'files-search-reset', true);
rcmail.file_list.init();
kolab_files_list_coltypes();
}
rcmail.env.file_commands = ['files-get'];
kolab_files_init();
file_api.folder_list();
}
});
function kolab_files_init()
{
if (window.file_api)
return;
// Initialize application object (don't change var name!)
file_api = $.extend(new files_api(), new kolab_files_ui());
file_api.set_env({
token: kolab_files_token(),
url: rcmail.env.files_url,
sort_column: 'name',
sort_reverse: 0
});
file_api.translations = rcmail.labels;
};
function kolab_files_token()
{
// consider the token from parent window more reliable (fresher) than in framed window
// it's because keep-alive is not requested in frames
return (window.parent && parent.rcmail && parent.rcmail.env.files_token) || rcmail.env.files_token;
};
/**********************************************************/
/********* Plugin functionality in other tasks **********/
/**********************************************************/
-function kolab_directory_selector_dialog()
+function kolab_directory_selector_dialog(id)
{
- var dialog = $('#files-dialog'), buttons = {};
+ var dialog = $('#files-dialog'), buttons = {},
+ input = $('#file-save-as-input'),
+ form = $('#file-save-as'),
+ list = $('#folderlistbox');
+
+ // attachment is specified
+ if (id) {
+ var attach = $('#attach'+id), filename = attach.attr('title') || attach.text();
+ form.show();
+ dialog.addClass('saveas');
+ input.val(filename);
+ }
+ else {
+ form.hide();
+ dialog.removeClass('saveas');
+ }
buttons[rcmail.gettext('kolab_files.save')] = function () {
- var lock = rcmail.set_busy(true, 'saving');
- rcmail.http_post('plugin.kolab_files', {
- act: 'saveall',
- source: rcmail.env.mailbox,
- uid: rcmail.env.uid,
- dest: file_api.env.folder
- }, lock);
+ var lock = rcmail.set_busy(true, 'saving'),
+ request = {
+ act: 'save-file',
+ source: rcmail.env.mailbox,
+ uid: rcmail.env.uid,
+ dest: file_api.env.folder
+ };
+
+ if (id) {
+ request.id = id;
+ request.name = input.val();
+ }
+
+ rcmail.http_post('plugin.kolab_files', request, lock);
$('#files-dialog').dialog('destroy').hide();
};
buttons[rcmail.gettext('kolab_files.cancel')] = function () {
$('#files-dialog').dialog('destroy').hide();
};
// show dialog window
dialog.dialog({
modal: true,
resizable: !bw.ie6,
closeOnEscape: (!bw.ie6 && !bw.ie7), // disable for performance reasons
- title: rcmail.gettext('kolab_files.saveall'),
+ title: rcmail.gettext('kolab_files.' + (id ? 'saveto' : 'saveall')),
// close: function() { rcmail.dialog_close(); },
buttons: buttons,
minWidth: 250,
minHeight: 300,
height: 350,
width: 300
}).show();
if (!rcmail.env.folders_loaded) {
file_api.folder_list();
rcmail.env.folders_loaded = true;
}
};
function kolab_files_selector_dialog()
{
var dialog = $('#files-compose-dialog'), buttons = {};
buttons[rcmail.gettext('kolab_files.attachsel')] = function () {
var list = [];
$('#filelist tr.selected').each(function() {
list.push($(this).data('file'));
});
$('#files-compose-dialog').dialog('destroy').hide();
if (list.length) {
// display upload indicator and cancel button
var content = '<span>' + rcmail.get_label('uploading' + (list.length > 1 ? 'many' : '')) + '</span>',
id = new Date().getTime();
rcmail.add2attachment_list(id, { name:'', html:content, classname:'uploading', complete:false });
// send request
rcmail.http_post('plugin.kolab_files', {
- act: 'attach',
+ act: 'attach-file',
folder: file_api.env.folder,
files: list,
id: rcmail.env.compose_id,
uploadid: id
});
}
};
buttons[rcmail.gettext('kolab_files.cancel')] = function () {
$('#files-compose-dialog').dialog('destroy').hide();
};
// show dialog window
dialog.dialog({
modal: true,
resizable: !bw.ie6,
closeOnEscape: (!bw.ie6 && !bw.ie7), // disable for performance reasons
title: rcmail.gettext('kolab_files.selectfiles'),
// close: function() { rcmail.dialog_close(); },
buttons: buttons,
minWidth: 500,
minHeight: 300,
width: 700,
height: 500
}).show();
if (!rcmail.env.files_loaded) {
file_api.folder_list();
rcmail.env.files_loaded = true;
}
else
rcmail.file_list.clear_selection();
};
+function kolab_files_attach_menu_open(p)
+{
+ if (!p || !p.props || p.props.menu != 'attachmentmenu')
+ return;
+
+ var id = p.props.id;
+
+ $('#attachmenusaveas').unbind('click').attr('onclick', '').click(function(e) {
+ return kolab_directory_selector_dialog(id);
+ });
+};
/***********************************************************/
/********** Main functionality **********/
/***********************************************************/
// for reordering column array (Konqueror workaround)
// and for setting some message list global variables
kolab_files_list_coltypes = function()
{
var n, list = rcmail.file_list;
rcmail.env.subject_col = null;
if ((n = $.inArray('name', rcmail.env.coltypes)) >= 0) {
rcmail.env.subject_col = n;
list.subject_col = n;
}
list.init_header();
};
kolab_files_set_list_options = function(cols, sort_col, sort_order)
{
var update = 0, i, idx, name, newcols = [], oldcols = rcmail.env.coltypes;
if (sort_col === undefined)
sort_col = rcmail.env.sort_col;
if (!sort_order)
sort_order = rcmail.env.sort_order;
if (rcmail.env.sort_col != sort_col || rcmail.env.sort_order != sort_order) {
update = 1;
rcmail.set_list_sorting(sort_col, sort_order);
}
if (cols && cols.length) {
// make sure new columns are added at the end of the list
for (i=0; i<oldcols.length; i++) {
name = oldcols[i];
idx = $.inArray(name, cols);
if (idx != -1) {
newcols.push(name);
delete cols[idx];
}
}
for (i=0; i<cols.length; i++)
if (cols[i])
newcols.push(cols[i]);
if (newcols.join() != oldcols.join()) {
update += 2;
oldcols = newcols;
}
}
if (update == 1)
file_api.file_list({sort: sort_col, reverse: sort_order == 'DESC'});
else if (update) {
rcmail.http_post('files/prefs', {
kolab_files_list_cols: oldcols,
kolab_files_sort_col: sort_col,
kolab_files_sort_order: sort_order
}, rcmail.set_busy(true, 'loading'));
}
};
kolab_files_set_coltypes = function(list)
{
var i, found, name, cols = list.list.tHead.rows[0].cells;
rcmail.env.coltypes = [];
for (i=0; i<cols.length; i++)
if (cols[i].id && cols[i].id.match(/^rcm/)) {
name = cols[i].id.replace(/^rcm/, '');
rcmail.env.coltypes.push(name);
}
// if ((found = $.inArray('name', rcmail.env.coltypes)) >= 0)
// rcmail.env.subject_col = found;
rcmail.env.subject_col = list.subject_col;
rcmail.http_post('files/prefs', {kolab_files_list_cols: rcmail.env.coltypes});
};
kolab_files_click_on_list = function(e)
{
if (rcmail.gui_objects.qsearchbox)
rcmail.gui_objects.qsearchbox.blur();
if (rcmail.file_list)
rcmail.file_list.focus();
return true;
};
kolab_files_list_select = function(list)
{
var selected = list.selection.length;
rcmail.enable_command(rcmail.env.file_commands, selected == 1);
// reset all-pages-selection
// if (list.selection.length && list.selection.length != list.rowcount)
// rcmail.select_all_mode = false;
};
rcube_webmail.prototype.files_sort = function(props)
{
var params = {},
sort_order = this.env.sort_order,
sort_col = !this.env.disabled_sort_col ? props : this.env.sort_col;
if (!this.env.disabled_sort_order)
sort_order = this.env.sort_col == sort_col && sort_order == 'ASC' ? 'DESC' : 'ASC';
// set table header and update env
this.set_list_sorting(sort_col, sort_order);
this.http_post('files/prefs', {kolab_files_sort_col: sort_col, kolab_files_sort_order: sort_order});
params.sort = sort_col;
params.reverse = sort_order == 'DESC';
file_api.file_list(params);
};
rcube_webmail.prototype.files_search = function()
{
var value = $(this.gui_objects.filesearchbox).val();
if (value)
file_api.file_search(value);
else
file_api.file_search_reset();
};
rcube_webmail.prototype.files_search_reset = function()
{
$(this.gui_objects.filesearchbox).val('');
file_api.file_search_reset();
};
rcube_webmail.prototype.files_folder_delete = function()
{
if (confirm(this.get_label('deletefolderconfirm')))
file_api.folder_delete(file_api.env.folder);
};
rcube_webmail.prototype.files_upload = function(form)
{
if (form)
file_api.file_upload(form);
};
rcube_webmail.prototype.files_list_update = function(head)
{
var list = this.file_list;
list.clear();
$('thead', list.list).html(head);
kolab_files_list_coltypes();
file_api.file_list();
};
rcube_webmail.prototype.files_get = function()
{
var id = this.file_list.get_selection();
if (id = $('#rcmrow'+id).data('file')) {
file_api.file_get(id, {'force-download': true});
}
};
/**********************************************************/
/********* Files API handler **********/
/**********************************************************/
function kolab_files_ui()
{
/*
// Called on "session expired" session
this.logout = function(response) {};
// called when a request timed out
this.request_timed_out = function() {};
// called on start of the request
this.set_request_time = function() {};
// called on request response
this.update_request_time = function() {};
*/
// set state
this.set_busy = function(a, message)
{
if (this.req)
rcmail.hide_message(this.req);
return rcmail.set_busy(a, message);
};
// displays error message
this.display_message = function(label)
{
return rcmail.display_message(this.t(label));
};
this.http_error = function(request, status, err)
{
rcmail.http_error(request, status, err);
};
this.folder_list = function()
{
this.req = this.set_busy(true, 'loading');
this.get('folder_list', {}, 'folder_list_response');
};
// folder list response handler
this.folder_list_response = function(response)
{
if (!this.response(response))
return;
var first, elem = $('#files-folder-list'),
list = $('<ul class="listing"></ul>');
elem.html('').append(list);
this.env.folders = this.folder_list_parse(response.result);
$.each(this.env.folders, function(i, f) {
var row = $('<li class="mailbox"><span class="branch"></span></li>');
row.attr('id', f.id)
.append($('<span class="name">').text(f.name))
.click(function() { file_api.folder_select(i); });
if (f.depth)
$('span.branch', row).width(15 * f.depth);
if (f.virtual)
row.addClass('virtual');
list.append(row);
if (!first)
first = i;
});
// select first folder?
if (this.env.folder || first)
this.folder_select(this.env.folder ? this.env.folder : first);
// add tree icons
this.folder_list_tree(this.env.folders);
};
this.folder_select = function(i)
{
var list = $('#files-folder-list > ul');
$('li.selected', list).removeClass('selected');
$('#' + this.env.folders[i].id, list).addClass('selected');
this.env.folder = i;
rcmail.enable_command('files-folder-delete', 'files-upload', true);
// list files in selected folder
this.file_list();
};
this.file_list = function(params)
{
if (!this.env.folder || !rcmail.gui_objects.filelist)
return;
if (!params)
params = {};
params.folder = this.env.folder;
if (params.sort == undefined)
params.sort = this.env.sort_col;
if (params.reverse == undefined)
params.reverse = this.env.sort_reverse;
if (params.search == undefined)
params.search = this.env.search;
this.env.sort_col = params.sort;
this.env.sort_reverse = params.reverse;
this.req = this.set_busy(true, 'loading');
rcmail.enable_command(rcmail.env.file_commands, false);
rcmail.file_list.clear();
this.get('file_list', params, 'file_list_response');
};
// file list response handler
this.file_list_response = function(response)
{
if (!this.response(response))
return;
var i = 0, table = $('#filelist');
$('tbody', table).empty();
$.each(response.result, function(key, data) {
var c, row = '', col;
i++;
for (c in rcmail.env.coltypes) {
c = rcmail.env.coltypes[c];
if (c == 'name')
col = '<td class="name filename ' + file_api.file_type_class(data.type) + '">'
+ '<span>' + key + '</span></td>';
else if (c == 'mtime')
col = '<td class="mtime">' + data.mtime + '</td>';
else if (c == 'size')
col = '<td class="size">' + file_api.file_size(data.size) + '</td>';
else if (c == 'options')
col = '<td class="options"></td>'; // @TODO
else
col = '<td class="' + c + '"></td>';
row += col;
}
row = $('<tr>')
.html(row)
.attr({id: 'rcmrow' + i, 'data-file': urlencode(key)});
// table.append(row);
rcmail.file_list.insert_row(row.get([0]));
});
};
this.file_select = function(e, row)
{
var table = $('#filelist');
// $('tr.selected', table).removeClass('selected');
$(row).addClass('selected');
};
// folder create request
this.folder_create = function(folder)
{
this.req = this.set_busy(true, 'kolab_files.foldercreating');
this.get('folder_create', {folder: folder}, 'folder_create_response');
};
// folder create response handler
this.folder_create_response = function(response)
{
if (!this.response(response))
return;
// refresh folders list
this.folder_list();
};
// folder delete request
this.folder_delete = function(folder)
{
this.req = this.set_busy(true, 'folderdeleting');
this.get('folder_delete', {folder: folder}, 'folder_delete_response');
};
// folder delete response handler
this.folder_delete_response = function(response)
{
if (!this.response(response))
return;
this.env.folder = null;
rcmail.enable_command('files-folder-delete', 'files-folder-rename', false);
// refresh folders list
this.folder_list();
};
this.file_search = function(value)
{
if (value) {
this.env.search = {name: value};
this.file_list({search: this.env.search});
}
else
this.search_reset();
};
this.file_search_reset = function()
{
if (this.env.search) {
this.env.search = null;
this.file_list();
}
};
this.file_get = function(file, params)
{
if (!params)
params = {};
params.folder = this.env.folder;
params.token = this.env.token;
params.file = file;
rcmail.redirect(this.env.url + this.url('file_get', params));
};
// file upload request
this.file_upload = function(form)
{
var form = $(form),
field = $('input[type=file]', form).get(0),
files = field.files ? field.files.length : field.value ? 1 : 0;
if (files) {
// submit form and read server response
this.async_upload_form(form, 'file_create', function(event) {
var doc, response;
try {
doc = this.contentDocument ? this.contentDocument : this.contentWindow.document;
response = doc.body.innerHTML;
// response may be wrapped in <pre> tag
if (response.slice(0, 5).toLowerCase() == '<pre>' && response.slice(-6).toLowerCase() == '</pre>') {
response = doc.body.firstChild.firstChild.nodeValue;
}
response = eval('(' + response + ')');
} catch (err) {
response = {status: 'ERROR'};
}
rcmail.hide_message(event.data.ts);
// refresh the list on upload success
if (file_api.response_parse(response))
file_api.file_list();
});
}
};
// post the given form to a hidden iframe
this.async_upload_form = function(form, action, onload)
{
var ts = rcmail.display_message(rcmail.get_label('kolab_files.uploading'), 'loading', 1000),
frame_name = 'fileupload'+ts;
/*
// upload progress support
if (this.env.upload_progress_name) {
var fname = this.env.upload_progress_name,
field = $('input[name='+fname+']', form);
if (!field.length) {
field = $('<input>').attr({type: 'hidden', name: fname});
field.prependTo(form);
}
field.val(ts);
}
*/
// have to do it this way for IE
// otherwise the form will be posted to a new window
if (document.all) {
var html = '<iframe id="'+frame_name+'" name="'+frame_name+'"'
+ ' src="program/resources/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>';
document.body.insertAdjacentHTML('BeforeEnd', html);
}
// for standards-compliant browsers
else
$('<iframe>')
.attr({name: frame_name, id: frame_name})
.css({border: 'none', width: 0, height: 0, visibility: 'hidden'})
.appendTo(document.body);
// handle upload errors, parsing iframe content in onload
$('#'+frame_name).on('load', {ts:ts}, onload);
$(form).attr({
target: frame_name,
action: this.env.url + this.url(action, {folder: this.env.folder, token: this.env.token, uploadid:ts}),
method: 'POST'
}).attr(form.encoding ? 'encoding' : 'enctype', 'multipart/form-data')
.submit();
};
};
diff --git a/plugins/kolab_files/lib/kolab_files_engine.php b/plugins/kolab_files/lib/kolab_files_engine.php
index 47b8299f..fa46ac8a 100644
--- a/plugins/kolab_files/lib/kolab_files_engine.php
+++ b/plugins/kolab_files/lib/kolab_files_engine.php
@@ -1,737 +1,759 @@
<?php
/**
* Kolab files storage engine
*
* @version @package_version@
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class kolab_files_engine
{
private $plugin;
private $rc;
private $timeout = 60;
private $sort_cols = array('name', 'mtime', 'size');
/**
* Class constructor
*/
public function __construct($plugin, $url)
{
$this->url = $url;
$this->plugin = $plugin;
$this->rc = $plugin->rc;
}
/**
* User interface initialization
*/
public function ui()
{
$this->plugin->add_texts('localization/', true);
// set templates of Files UI and widgets
if ($this->rc->task == 'mail') {
if ($this->rc->action == 'compose') {
$template = 'compose_plugin';
}
else if ($this->rc->action == 'show' || $this->rc->action == 'preview') {
$template = 'message_plugin';
+
+ // add "Save as" button into attachment menu
+ $this->plugin->add_button(array(
+ 'id' => 'attachmenusaveas',
+ 'name' => 'attachmenusaveas',
+ 'type' => 'link',
+ 'wrapper' => 'li',
+ 'onclick' => 'return false',
+ 'class' => 'icon active saveas',
+ 'classact' => 'icon active saveas',
+ 'innerclass' => 'icon active saveas',
+ 'label' => 'kolab_files.saveto',
+ ), 'attachmentmenu');
}
}
else if ($this->rc->task == 'files') {
$template = 'files';
}
// add taskbar button
if (empty($_REQUEST['framed'])) {
$this->plugin->add_button(array(
'command' => 'files',
'class' => 'button-files',
'classsel' => 'button-files button-selected',
'innerclass' => 'button-inner',
'label' => 'kolab_files.files',
), 'taskbar');
}
$this->plugin->include_stylesheet($this->plugin->local_skin_path().'/style.css');
if (!empty($template)) {
$this->plugin->include_script($this->url . '/js/files_api.js');
$this->plugin->include_script('kolab_files.js');
$this->rc->output->set_env('files_url', $this->url . '/api/');
$this->rc->output->set_env('files_token', $this->get_api_token());
if ($this->rc->task != 'files') {
// register template objects for dialogs
$this->rc->output->add_handlers(array(
'folder-create-form' => array($this, 'folder_create_form'),
'file-search-form' => array($this, 'file_search_form'),
'filelist' => array($this, 'file_list'),
));
// add dialog content at the end of page body
$this->rc->output->add_footer(
$this->rc->output->parse('kolab_files.' . $template, false, false));
}
}
}
/**
* Engine actions handler
*/
public function actions()
{
if ($this->rc->task == 'files' && $this->rc->action) {
$action = $this->rc->action;
}
else if ($this->rc->task != 'files' && $_POST['act']) {
$action = $_POST['act'];
}
else {
$action = 'index';
}
$method = 'action_' . str_replace('-', '_', $action);
if (method_exists($this, $method)) {
$this->plugin->add_texts('localization/');
$this->{$method}();
}
}
/**
* Template object for folder creation form in "Save as" dialog
*/
public function folder_create_form($attrib)
{
$attrib['name'] = 'folder-create-form';
if (empty($attrib['id'])) {
$attrib['id'] = 'folder-create-form';
}
$input_name = new html_inputfield(array('name' => 'folder_name'));
$out = $input_name->show();
// $input_parent = new html_checkbox(array('name' => 'folder_parent', 'checked' => true, 'value' => 1));
// $out .= html::label(null, $input_parent->show() . $this->plugin->gettext('assubfolder'));
// add form tag around text field
if (empty($attrib['form'])) {
$out = $this->rc->output->form_tag($attrib, $out);
}
$this->rc->output->add_label('kolab_files.foldercreating');
$this->rc->output->add_gui_object('folder-create-form', $attrib['id']);
return $out;
}
/**
* Template object for file search form in "From cloud" dialog
*/
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']);
// add form tag around text field
if (empty($attrib['form'])) {
$out = $this->rc->output->form_tag(array(
'name' => "filesearchform",
'onsubmit' => rcmail_output::JS_OBJECT_NAME . ".command('files-search'); return false",
), $out);
}
return $out;
}
/**
* Template object for files list
*/
public function file_list($attrib)
{
// $this->rc->output->add_label('');
// define list of cols to be displayed based on parameter or config
if (empty($attrib['columns'])) {
$list_cols = $this->rc->config->get('kolab_files_list_cols');
$dont_override = $this->rc->config->get('dont_override');
$a_show_cols = is_array($list_cols) ? $list_cols : array('name');
$this->rc->output->set_env('col_movable', !in_array('kolab_files_list_cols', (array)$dont_override));
}
else {
$a_show_cols = preg_split('/[\s,;]+/', strip_quotes($attrib['columns']));
}
// make sure 'name' and 'options' column is present
if (!in_array('name', $a_show_cols)) {
array_unshift($a_show_cols, 'name');
}
if (!in_array('options', $a_show_cols)) {
array_unshift($a_show_cols, 'options');
}
$attrib['columns'] = $a_show_cols;
// save some variables for use in ajax list
$_SESSION['kolab_files_list_attrib'] = $attrib;
// For list in dialog(s) remove all option-like columns
if ($this->rc->task != 'files') {
$a_show_cols = array_intersect($a_show_cols, $this->sort_cols);
}
// set default sort col/order to session
if (!isset($_SESSION['kolab_files_sort_col']))
$_SESSION['kolab_files_sort_col'] = $this->rc->config->get('kolab_files_sort_col') ?: 'name';
if (!isset($_SESSION['kolab_files_sort_order']))
$_SESSION['kolab_files_sort_order'] = strtoupper($this->rc->config->get('kolab_files_sort_order') ?: 'asc');
// set client env
$this->rc->output->add_gui_object('filelist', $attrib['id']);
$this->rc->output->set_env('sort_col', $_SESSION['kolab_files_sort_col']);
$this->rc->output->set_env('sort_order', $_SESSION['kolab_files_sort_order']);
$this->rc->output->set_env('coltypes', $a_show_cols);
$this->rc->output->include_script('list.js');
// attach css rules for mimetype icons
$this->plugin->include_stylesheet($this->url . '/skins/default/images/mimetypes/style.css');
$thead = '';
foreach ($this->file_list_head($attrib, $a_show_cols) as $cell) {
$thead .= html::tag('td', array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']);
}
return html::tag('table', $attrib,
html::tag('thead', null, html::tag('tr', null, $thead)) . html::tag('tbody', null, ''),
array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
}
/**
* Creates <THEAD> for message list table
*/
protected function file_list_head($attrib, $a_show_cols)
{
$skin_path = $_SESSION['skin_path'];
$image_tag = html::img(array('src' => "%s%s", 'alt' => "%s"));
// check to see if we have some settings for sorting
$sort_col = $_SESSION['kolab_files_sort_col'];
$sort_order = $_SESSION['kolab_files_sort_order'];
$dont_override = (array)$this->rc->config->get('dont_override');
$disabled_sort = in_array('message_sort_col', $dont_override);
$disabled_order = in_array('message_sort_order', $dont_override);
$this->rc->output->set_env('disabled_sort_col', $disabled_sort);
$this->rc->output->set_env('disabled_sort_order', $disabled_order);
// define sortable columns
if ($disabled_sort)
$a_sort_cols = $sort_col && !$disabled_order ? array($sort_col) : array();
else
$a_sort_cols = $this->sort_cols;
if (!empty($attrib['optionsmenuicon'])) {
$onclick = 'return ' . JS_OBJECT_NAME . ".command('menu-open', 'filelistmenu')";
if ($attrib['optionsmenuicon'] === true || $attrib['optionsmenuicon'] == 'true')
$list_menu = html::div(array('onclick' => $onclick, 'class' => 'listmenu',
'id' => 'listmenulink', 'title' => $this->rc->gettext('listoptions')));
else
$list_menu = html::a(array('href' => '#', 'onclick' => $onclick),
html::img(array('src' => $skin_path . $attrib['optionsmenuicon'],
'id' => 'listmenulink', 'title' => $this->rc->gettext('listoptions'))));
}
else {
$list_menu = '';
}
$cells = array();
foreach ($a_show_cols as $col) {
// get column name
switch ($col) {
/*
case 'status':
$col_name = '<span class="' . $col .'"> </span>';
break;
*/
case 'options':
$col_name = $list_menu;
break;
default:
$col_name = Q($this->plugin->gettext($col));
}
// make sort links
if (in_array($col, $a_sort_cols))
$col_name = html::a(array('href'=>"#sort", 'onclick' => 'return '.JS_OBJECT_NAME.".command('files-sort','".$col."',this)", 'title' => rcube_label('sortby')), $col_name);
else if ($col_name[0] != '<')
$col_name = '<span class="' . $col .'">' . $col_name . '</span>';
$sort_class = $col == $sort_col && !$disabled_order ? " sorted$sort_order" : '';
$class_name = $col.$sort_class;
// put it all together
$cells[] = array('className' => $class_name, 'id' => "rcm$col", 'html' => $col_name);
}
return $cells;
}
/**
* Update files list object
*/
protected function file_list_update($prefs)
{
$attrib = $_SESSION['kolab_files_list_attrib'];
if (!empty($prefs['kolab_files_list_cols'])) {
$attrib['columns'] = $prefs['kolab_files_list_cols'];
$_SESSION['kolab_files_list_attrib'] = $attrib;
}
$a_show_cols = $attrib['columns'];
$head = '';
foreach ($this->file_list_head($attrib, $a_show_cols) as $cell) {
$head .= html::tag('td', array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']);
}
$head = html::tag('tr', null, $head);
$this->rc->output->set_env('coltypes', $a_show_cols);
$this->rc->output->command('files_list_update', $head);
}
/**
* Get API token for current user session, authenticate if needed
*/
public function get_api_token()
{
$token = $_SESSION['kolab_files_token'];
$time = $_SESSION['kolab_files_time'];
if ($token && time() - $this->timeout < $time) {
return $token;
}
if (!($request = $this->get_request())) {
return $token;
}
try {
$url = $request->getUrl();
// Send ping request
if ($token) {
$url->setQueryVariables(array('method' => 'ping'));
$request->setUrl($url);
$response = $request->send();
$status = $response->getStatus();
if ($status == 200 && ($body = json_decode($response->getBody(), true))) {
if ($body['status'] == 'OK') {
$_SESSION['kolab_files_time'] = time();
return $token;
}
}
}
// Go with authenticate request
$url->setQueryVariables(array('method' => 'authenticate'));
$request->setUrl($url);
$request->setAuth($this->rc->user->get_username(), $this->rc->decrypt($_SESSION['password']));
$response = $request->send();
$status = $response->getStatus();
if ($status == 200 && ($body = json_decode($response->getBody(), true))) {
$token = $body['result']['token'];
if ($token) {
$_SESSION['kolab_files_token'] = $token;
$_SESSION['kolab_files_time'] = time();
}
}
else {
throw new Exception(sprintf("Authenticate error (Status: %d)", $status));
}
}
catch (Exception $e) {
rcube::raise_error($e, true, false);
}
return $token;
}
/**
* Initialize HTTP_Request object
*/
protected function get_request()
{
$url = $this->url . '/api/';
if (!$this->request) {
require_once 'HTTP/Request2.php';
try {
$request = new HTTP_Request2();
$request->setConfig(array(
'store_body' => true,
'follow_redirects' => true,
'ssl_verify_peer' => $this->rc->config->get('kolab_ssl_verify_peer', true),
));
$this->request = $request;
}
catch (Exception $e) {
rcube::raise_error($e, true, true);
}
}
if ($this->request) {
// cleanup
try {
$this->request->setBody('');
$this->request->setUrl($url);
$this->request->setMethod(HTTP_Request2::METHOD_GET);
}
catch (Exception $e) {
rcube::raise_error($e, true, true);
}
}
return $this->request;
}
protected function action_index()
{
// register template objects
$this->rc->output->add_handlers(array(
// 'folderlist' => array($this, 'folder_list'),
'filelist' => array($this, 'file_list'),
'file-search-form' => array($this, 'file_search_form'),
));
$this->rc->output->add_label('deletefolderconfirm', 'folderdeleting',
'kolab_files.foldercreating', 'kolab_files.uploading');
$this->rc->output->set_pagetitle($this->plugin->gettext('files'));
$this->rc->output->send('kolab_files.files');
}
/**
* Handler for preferences save action
*/
protected function action_prefs()
{
$dont_override = (array)$this->rc->config->get('dont_override');
$prefs = array();
$opts = array(
'kolab_files_sort_col' => true,
'kolab_files_sort_order' => true,
'kolab_files_list_cols' => false,
);
foreach ($opts as $o => $sess) {
if (isset($_POST[$o]) && !in_array($o, $dont_override)) {
$prefs[$o] = rcube_utils::get_input_value($o, rcube_utils::INPUT_POST);
if ($sess) {
$_SESSION[$o] = $prefs[$o];
}
if ($o == 'kolab_files_list_cols') {
$update_list = true;
}
}
}
// save preference values
if (!empty($prefs)) {
$this->rc->user->save_prefs($prefs);
}
if (!empty($update_list)) {
$this->file_list_update($prefs);
}
$this->rc->output->send();
}
/**
* Handler for "save all attachments into cloud" action
*/
- protected function action_saveall()
+ protected function action_save_file()
{
$source = rcube_utils::get_input_value('source', rcube_utils::INPUT_POST);
$uid = rcube_utils::get_input_value('uid', rcube_utils::INPUT_POST);
$dest = rcube_utils::get_input_value('dest', rcube_utils::INPUT_POST);
+ $id = rcube_utils::get_input_value('id', rcube_utils::INPUT_POST);
+ $name = rcube_utils::get_input_value('name', rcube_utils::INPUT_POST);
$temp_dir = unslashify($this->rc->config->get('temp_dir'));
$storage = $this->rc->get_storage();
$message = new rcube_message($uid);
$request = $this->get_request();
$url = $request->getUrl();
$files = array();
$errors = array();
+ $attachments = array();
$request->setMethod(HTTP_Request2::METHOD_POST);
$request->setHeader('X-Session-Token', $this->get_api_token());
$url->setQueryVariables(array('method' => 'file_create', 'folder' => $dest));
$request->setUrl($url);
+ foreach ($message->attachments as $attach_prop) {
+ if (empty($id) || $id == $attach_prop->mime_id) {
+ $filename = strlen($name) ? $name : rcmail_attachment_name($attach_prop, true);
+ $attachments[$filename] = $attach_prop;
+ }
+ }
+
// @TODO: handle error
// @TODO: implement file upload using file URI instead of body upload
- foreach ($message->attachments as $attach_prop) {
- $filename = rcmail_attachment_name($attach_prop, true);
- $path = tempnam($temp_dir, 'rcmAttmnt');
+ foreach ($attachments as $attach_name => $attach_prop) {
+ $path = tempnam($temp_dir, 'rcmAttmnt');
// save attachment to file
if ($fp = fopen($path, 'w+')) {
$message->get_part_content($attach_prop->mime_id, $fp, true);
}
else {
$errors[] = true;
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => "Unable to save attachment into file $path"),
true, false);
continue;
}
fclose($fp);
// send request to the API
try {
$request->setBody('');
- $request->addUpload('file[]', $path, $filename, $attach_prop->mimetype);
+ $request->addUpload('file[]', $path, $attach_name, $attach_prop->mimetype);
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
- $files[] = $filename;
+ $files[] = $attach_name;
}
else {
throw new Exception($body['reason']);
}
}
catch (Exception $e) {
unlink($path);
$errors[] = $e->getMessage();
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => $e->getMessage()),
true, false);
continue;
}
// clean up
unlink($path);
$request->setBody('');
}
if ($count = count($files)) {
$msg = $this->plugin->gettext(array('name' => 'saveallnotice', 'vars' => array('n' => $count)));
$this->rc->output->show_message($msg, 'confirmation');
}
if ($count = count($errors)) {
$msg = $this->plugin->gettext(array('name' => 'saveallerror', 'vars' => array('n' => $count)));
$this->rc->output->show_message($msg, 'error');
}
// @TODO: update quota indicator, make this optional in case files aren't stored in IMAP
$this->rc->output->send();
}
/**
* Handler for "add attachments from the cloud" action
*/
- protected function action_attach()
+ protected function action_attach_file()
{
$folder = rcube_utils::get_input_value('folder', rcube_utils::INPUT_POST);
$files = rcube_utils::get_input_value('files', rcube_utils::INPUT_POST);
$uploadid = rcube_utils::get_input_value('uploadid', rcube_utils::INPUT_POST);
$COMPOSE_ID = rcube_utils::get_input_value('id', rcube_utils::INPUT_POST);
$COMPOSE = null;
$errors = array();
if ($COMPOSE_ID && $_SESSION['compose_data_'.$COMPOSE_ID]) {
$COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
}
if (!$COMPOSE) {
die("Invalid session var!");
}
// attachment upload action
if (!is_array($COMPOSE['attachments'])) {
$COMPOSE['attachments'] = array();
}
// clear all stored output properties (like scripts and env vars)
$this->rc->output->reset();
$temp_dir = unslashify($this->rc->config->get('temp_dir'));
$request = $this->get_request();
$url = $request->getUrl();
// Use observer object to store HTTP response into a file
require_once $this->plugin->home . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'kolab_files_observer.php';
$observer = new kolab_files_observer();
$request->setHeader('X-Session-Token', $this->get_api_token());
// download files from the API and attach them
foreach ($files as $file) {
// decode filename
$file = urldecode($file);
// get file information
try {
$url->setQueryVariables(array('method' => 'file_info', 'folder' => $folder, 'file' => $file));
$request->setUrl($url);
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$file_params = $body['result'];
}
else {
throw new Exception($body['reason']);
}
}
catch (Exception $e) {
$errors[] = $e->getMessage();
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => $e->getMessage()),
true, false);
continue;
}
// set location of downloaded file
$path = tempnam($temp_dir, 'rcmAttmnt');
$observer->set_file($path);
// download file
try {
$url->setQueryVariables(array('method' => 'file_get', 'folder' => $folder, 'file' => $file));
$request->setUrl($url);
$request->attach($observer);
$response = $request->send();
$status = $response->getStatus();
$response->getBody(); // returns nothing
$request->detach($observer);
if ($status != 200 || !file_exists($path)) {
throw new Exception("Unable to save file");
}
}
catch (Exception $e) {
$errors[] = $e->getMessage();
rcube::raise_error(array(
'code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__,
'message' => $e->getMessage()),
true, false);
continue;
}
$attachment = array(
'path' => $path,
'size' => $file_params['size'],
'name' => $file_params['name'],
'mimetype' => $file_params['type'],
'group' => $COMPOSE_ID,
);
$attachment = $this->rc->plugins->exec_hook('attachment_save', $attachment);
if ($attachment['status'] && !$attachment['abort']) {
$id = $attachment['id'];
// store new attachment in session
unset($attachment['status'], $attachment['abort']);
$COMPOSE['attachments'][$id] = $attachment;
if (($icon = $COMPOSE['deleteicon']) && is_file($icon)) {
$button = html::img(array(
'src' => $icon,
'alt' => $this->rc->gettext('delete')
));
}
else {
$button = Q($this->rc->gettext('delete'));
}
$content = html::a(array(
'href' => "#delete",
'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", JS_OBJECT_NAME, $id),
'title' => $this->rc->gettext('delete'),
'class' => 'delete',
), $button);
$content .= Q($attachment['name']);
$this->rc->output->command('add2attachment_list', "rcmfile$id", array(
'html' => $content,
'name' => $attachment['name'],
'mimetype' => $attachment['mimetype'],
'classname' => rcmail_filetype2classname($attachment['mimetype'], $attachment['name']),
'complete' => true), $uploadid);
}
else if ($attachment['error']) {
$errors[] = $attachment['error'];
}
else {
$errors[] = $this->plugin->gettext('attacherror');
}
}
if (!empty($errors)) {
$this->rc->output->command('display_message', $this->plugin->gettext('attacherror'), 'error');
$this->rc->output->command('remove_from_attachment_list', $uploadid);
}
// send html page with JS calls as response
$this->rc->output->command('auto_save_start', false);
$this->rc->output->send();
}
}
diff --git a/plugins/kolab_files/localization/en_US.inc b/plugins/kolab_files/localization/en_US.inc
index 20e4284c..2233f304 100644
--- a/plugins/kolab_files/localization/en_US.inc
+++ b/plugins/kolab_files/localization/en_US.inc
@@ -1,31 +1,33 @@
<?php
$labels['files'] = 'Files';
$labels['saveall'] = 'Save all to cloud...';
+$labels['saveto'] = 'Save to cloud...';
+$labels['saveas'] = 'Save as:';
$labels['save'] = 'Save';
$labels['cancel'] = 'Cancel';
$labels['fromcloud'] = 'From cloud...';
$labels['selectfiles'] = 'Select file(s) to attach...';
$labels['attachsel'] = 'Attach selected';
$labels['foldercreate'] = 'Create folder';
$labels['folderrename'] = 'Rename folder';
$labels['folderdelete'] = 'Delete folder';
$labels['name'] = 'Name';
$labels['mtime'] = 'Modified';
$labels['upload'] = 'Upload';
$labels['uploadfile'] = 'Upload file(s)';
$labels['get'] = 'Download';
$labels['getfile'] = 'Download file';
$labels['view'] = 'View';
$labels['viewfile'] = 'View file';
$labels['deletefile'] = 'Delete selected file(s)';
$labels['uploading'] = 'Uploading file(s)...';
$labels['foldercreating'] = 'Creating folder...';
$labels['saveallnotice'] = 'Successfully saved $n file(s).';
$labels['saveallerror'] = 'Saving $n file(s) failed.';
$labels['attacherror'] = 'Failed to attach file(s) from the cloud';
?>
diff --git a/plugins/kolab_files/skins/larry/images/buttons.png b/plugins/kolab_files/skins/larry/images/buttons.png
index 58c0a293..661dbbcd 100644
Binary files a/plugins/kolab_files/skins/larry/images/buttons.png and b/plugins/kolab_files/skins/larry/images/buttons.png differ
diff --git a/plugins/kolab_files/skins/larry/style.css b/plugins/kolab_files/skins/larry/style.css
index 0a56dbdd..d4c86285 100644
--- a/plugins/kolab_files/skins/larry/style.css
+++ b/plugins/kolab_files/skins/larry/style.css
@@ -1,242 +1,263 @@
/* Taskbar button */
#taskbar a.button-files span.button-inner
{
background: url(images/buttons.png) 0 0 no-repeat;
height: 22px;
}
#taskbar a.button-files:hover span.button-inner,
#taskbar a.button-files.button-selected span.button-inner
{
background: url(images/buttons.png) 0 -26px no-repeat;
height: 22px;
}
/* Files main interface */
#filestoolbar {
position: absolute;
height: 40px;
left: 0;
top: -6px;
z-index: 10;
}
#filestoolbar a.button {
background-image: url(images/buttons.png);
}
#filestoolbar a.button.upload {
background-position: center -52px;
}
#filestoolbar a.button.get {
background-position: center -94px;
}
#filestoolbar a.button.view {
background-position: center -131px;
}
#filestoolbar a.button.delete {
background-image: url(../../../../skins/larry/images/buttons.png);
}
#filestoolbar form {
display: inline;
}
#folderlistbox {
position: absolute;
top: 42px;
left: 0;
width: 220px;
bottom: 0;
}
#filelistcontainer {
position: absolute;
top: 42px;
left: 232px;
right: 0;
bottom: 0;
overflow: auto;
}
#files-folder-list ul li span.name {
background: url(../../../../skins/larry/images/listicons.png) 6px 3px no-repeat;
padding: 6px 8px 2px 32px;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
width: auto;
overflow: hidden;
color: #376572;
}
#files-folder-list ul li span.branch {
display: inline-block;
}
#files-folder-list ul li:first-child {
border-radius: 4px 4px 0 0;
border-top: 0;
}
#files-folder-list ul li.virtual {
color: #aaa;
cursor: default;
}
#filelist thead tr td {
padding: 0;
}
#filelist tbody tr td {
padding: 2px 7px;
height: 18px;
}
#filelist tr td.size {
width: 60px;
text-align: right;
}
#filelist thead tr td.size {
text-align: left;
}
#filelist tr td.mtime {
width: 125px;
}
#filelist tr td.options {
width: 26px;
}
#filelist thead tr td.filename,
#filelist tbody tr td.filename {
width: 99%;
white-space: nowrap;
}
#filelist thead tr td.sortedASC a,
#filelist thead tr td.sortedDESC a {
color: #004458;
text-decoration: underline;
background: url(../../../../skins/larry/images/listicons.png) right -912px no-repeat;
}
#filelist thead tr td.sortedASC a {
background-position: right -944px;
}
#filelist td img {
vertical-align: middle;
display: inline-block;
}
#filelist tr td.options div.listmenu,
#filelist tr td.flag span.flagged,
#filelist tr td.flag span.unflagged,
#filelist tr td.flag span.unflagged:hover {
display: inline-block;
vertical-align: middle;
height: 18px;
width: 20px;
padding: 0;
background: url(../../../../skins/larry/images/listicons.png) -100px 0 no-repeat;
}
#filelist thead tr td.options div.listmenu {
background-position: 0 -976px;
cursor: pointer;
width: 26px;
}
#filelist thead tr td.options {
padding: 0 3px;
}
#filelist td.filename {
padding: 0 4px;
}
#filelist tbody td.filename span {
background: url(images/unknown.png) 0 0 no-repeat;
padding: 0 0 0 20px;
height: 16px;
}
/*
#filelist tbody td.filename span input {
padding: 0 2px;
height: 18px;
}
*/
/* plugin dialogs */
#files-dialog,
#files-compose-dialog {
display: none;
}
#files-compose-dialog #folderlistbox {
right: auto;
width: 190px;
top: 45px;
bottom: 5px;
box-shadow: none;
}
#files-dialog #folderlistbox {
- bottom: 5px;
top: 5px;
+ bottom: 5px;
left: 0;
right: 0;
width: auto;
box-shadow: none;
}
+#files-dialog.saveas #folderlistbox {
+ top: 50px;
+}
+
#files-compose-dialog #filelistcontainer {
position: absolute;
top: 45px;
bottom: 5px;
left: 200px;
right: 0;
box-shadow: none;
}
#files-folder-create {
background-color: white;
padding: 10px;
bottom: 10px;
left: 5px;
top: auto;
z-index: 1001; /* for use in modal dialog window */
}
#folder-create-form {
margin-bottom: 10px;
}
#files-compose-dialog #searchmenulink {
width: 15px;
}
+#files-compose-dialog .searchbox input {
+ background-color: white;
+}
+
#files-compose-dialog #quicksearchbar {
top: 10px;
right: 5px;
}
#files-compose-dialog #searchreset {
cursor: pointer;
}
a.filesaveall {
display: inline-block;
margin-top: .5em;
padding: 3px 5px 4px 5px;
}
+
+#file-save-as {
+ height: 25px;
+ white-space: nowrap;
+}
+
+#file-save-as input {
+ width: 200px;
+}
+
+ul.toolbarmenu li span.saveas {
+ background: url(images/buttons.png) -5px -180px no-repeat;
+}
diff --git a/plugins/kolab_files/skins/larry/templates/message_plugin.html b/plugins/kolab_files/skins/larry/templates/message_plugin.html
index 50cf7cf6..08cb71a8 100644
--- a/plugins/kolab_files/skins/larry/templates/message_plugin.html
+++ b/plugins/kolab_files/skins/larry/templates/message_plugin.html
@@ -1,14 +1,18 @@
<div id="files-dialog" class="uidialog">
+ <div id="file-save-as">
+ <label for="file-save-as-input"><roundcube:label name="kolab_files.saveas" /></label>
+ <input id="file-save-as-input" type="text" value="">
+ </div>
<div id="folderlistbox" class="uibox listbox">
<div id="files-folder-list" class="scroller withfooter"></div>
<div id="folderlist-footer" class="boxfooter">
<roundcube:button name="foldercreatelink" id="foldercreatelink" type="link" onclick="kolab_files_folder_form()" title="createfolder" class="listbutton add" classAct="listbutton add" innerClass="inner" content="+" />
</div>
</div>
<div id="files-folder-create" class="popupmenu">
<roundcube:object name="folder-create-form" />
<input id="folder-create-save-button" onclick="kolab_directory_create()" type="button" class="button mainaction" value="<roundcube:label name="create" />">
<input id="folder-create-cancel-button" onclick="kolab_directory_cancel()" type="button" class="button" value="<roundcube:label name="cancel" />">
</div>
</div>
<script src="plugins/kolab_files/skins/larry/ui.js" type="text/javascript"></script>
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Jul 30, 5:23 AM (17 h, 2 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1179918
Default Alt Text
(57 KB)
Attached To
Mode
R14 roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline
Log In to Comment