Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F4682569
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
30 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 01a19860..3198802d 100644
--- a/plugins/kolab_files/kolab_files.js
+++ b/plugins/kolab_files/kolab_files.js
@@ -1,318 +1,354 @@
/**
* Kolab files plugin
*
* @version @package_version@
* @author Aleksander Machniak <alec@alec.pl>
*/
window.rcmail && rcmail.addEventListener('init', function() {
- if (rcmail.env.task == 'mail') {
+ 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);
}
// 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="#">')
.text(rcmail.gettext('kolab_files.saveall'))
.click(function() { kolab_directory_selector_dialog(); })
.appendTo(attachment_list);
}
}
kolab_files_init();
}
});
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
});
};
function kolab_directory_selector_dialog()
{
var dialog = $('#files-dialog'), buttons = {};
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);
$('#files-dialog').dialog('destroy').hide();
};
buttons[rcmail.gettext('kolab_files.cancel')] = function () {
$('#files-dialog').dialog('destroy').hide();
};
// show dialog window
dialog.dialog({
modal: false,
resizable: !bw.ie6,
closeOnEscape: (!bw.ie6 && !bw.ie7), // disable for performance reasons
title: rcmail.gettext('kolab_files.saveall'),
// close: function() { rcmail.dialog_close(); },
buttons: buttons,
minWidth: 400,
minHeight: 300,
height: 300,
width: 350
}).show();
file_api.folder_selector();
};
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',
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: false,
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: 400,
minHeight: 300,
width: 600,
height: 400
}).show();
file_api.folder_selector();
};
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;
};
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.file_list = function(params)
+ {
+ if (rcmail.task != 'kolab_files')
+ this.file_selector(params);
+ };
+
+ this.folder_list = function(params)
+ {
+ if (rcmail.task != 'kolab_files')
+ this.folder_selector(params);
+ };
+
this.folder_selector = function()
{
this.req = this.set_busy(true, 'loading');
this.get('folder_list', {}, 'folder_selector_response');
};
// folder list response handler
this.folder_selector_response = function(response)
{
if (!this.response(response))
return;
var first, elem = $('#files-folder-selector'),
table = $('<table>');
elem.html('').append(table);
this.env.folders = this.folder_list_parse(response.result);
table.empty();
$.each(this.env.folders, function(i, f) {
var row = $('<tr><td><span class="branch"></span><span class="name"></span></td></tr>'),
span = $('span.name', row);
span.text(f.name);
row.attr('id', f.id);
if (f.depth)
$('span.branch', row).width(15 * f.depth);
if (f.virtual)
row.addClass('virtual');
else
span.click(function() { file_api.selector_select(i); });
// if (i == file_api.env.folder)
// row.addClass('selected');
table.append(row);
if (!first)
first = i;
});
// select first folder?
// if (first)
// this.selector_select(first);
// add tree icons
this.folder_list_tree(this.env.folders);
};
this.selector_select = function(i)
{
var list = $('#files-folder-selector > table');
$('tr.selected', list).removeClass('selected');
$('#' + this.env.folders[i].id, list).addClass('selected');
this.env.folder = i;
// list files in selected folder
if (rcmail.env.action == 'compose') {
this.file_selector();
}
};
this.file_selector = function(params)
{
+ if (!this.env.folder)
+ 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');
this.get('file_list', params, 'file_selector_response');
};
// file list response handler
this.file_selector_response = function(response)
{
if (!this.response(response))
return;
var table = $('#filelist');
$('tbody', table).empty();
$.each(response.result, function(key, data) {
var row = $('<tr><td class="filename"></td>'
+ /* '<td class="filemtime"></td>' */ '<td class="filesize"></td></tr>'),
link = $('<span></span>').text(key);
$('td.filename', row).addClass(file_api.file_type_class(data.type)).append(link);
// $('td.filemtime', row).text(data.mtime);
$('td.filesize', row).text(file_api.file_size(data.size));
row.attr('data-file', urlencode(key))
.click(function(e) { file_api.file_select(e, this); });
table.append(row);
});
};
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, 'creating');
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
if (rcmail.task == 'kolab_files')
this.folder_list();
else
this.folder_selector();
};
+ this.search = function()
+ {
+ var value = $(rcmail.gui_objects.filesearchbox).val();
+
+ if (value) {
+ this.env.search = {name: value};
+ this.file_list({search: this.env.search});
+ }
+ else
+ this.search_reset();
+ };
+
+ this.search_reset = function()
+ {
+ $(rcmail.gui_objects.filesearchbox).val('');
+
+ if (this.env.search) {
+ this.env.search = null;
+ this.file_list();
+ }
+ };
};
diff --git a/plugins/kolab_files/lib/kolab_files_engine.php b/plugins/kolab_files/lib/kolab_files_engine.php
index 84d34e8e..addd9d91 100644
--- a/plugins/kolab_files/lib/kolab_files_engine.php
+++ b/plugins/kolab_files/lib/kolab_files_engine.php
@@ -1,456 +1,491 @@
<?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;
/**
* 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);
$this->rc->output->set_env('files_url', $this->url . '/api/');
$this->rc->output->set_env('files_token', $this->get_api_token());
$this->plugin->include_stylesheet($this->plugin->local_skin_path().'/style.css');
$this->plugin->include_stylesheet($this->url . '/skins/default/images/mimetypes/style.css');
$this->plugin->include_script($this->url . '/js/files_api.js');
$this->plugin->include_script('kolab_files.js');
// add dialogs
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';
}
}
if (!empty($template)) {
// register template objects
$this->rc->output->add_handlers(array(
'folder-create-form' => array($this, 'folder_create_form'),
+ 'file-search-form' => array($this, 'file_search_form'),
));
// 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()
{
$action = $_POST['act'];
$method = 'action_' . $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($attr)
+ 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_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' => "file_api.search(); return false",
+ 'style' => "display:inline"),
+ $out);
+ }
+
+ return $out;
+ }
+
+
/**
* Get API token for current user session, authenticate if needed
*/
protected 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;
}
/**
* Handler for "save all attachments into cloud" action
*/
protected function action_saveall()
{
$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);
$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();
$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);
// @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');
// 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);
$response = $request->send();
$status = $response->getStatus();
$body = @json_decode($response->getBody(), true);
if ($status == 200 && $body['status'] == 'OK') {
$files[] = $filename;
}
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)) {
$this->rc->output->show_message($this->plugin->gettext('saveallnotice', array('n' => $count)), 'confirmation');
}
if ($count = count($errors)) {
$this->rc->output->show_message($this->plugin->gettext('saveallerror', array('n' => $count)), '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()
{
$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->rc->gettext('fileuploaderror');
}
}
if (!empty($errors)) {
$this->rc->output->command('display_message', "Failed to attach file(s) from cloud", '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/skins/larry/style.css b/plugins/kolab_files/skins/larry/style.css
index a983767e..463c220a 100644
--- a/plugins/kolab_files/skins/larry/style.css
+++ b/plugins/kolab_files/skins/larry/style.css
@@ -1,147 +1,161 @@
#files-dialog,
#files-compose-dialog,
#files-folder-create {
display: none;
}
#files-folder-selector {
position: absolute;
top: 5px;
bottom: 5px;
left: 0;
right: 0;
background-color: #f0f0f0;
padding: 2px;
}
#files-compose-dialog #files-folder-selector {
right: auto;
width: 190px;
+ top: 45px;
}
#files-dialog #files-folder-selector {
bottom: 40px;
}
#files-file-selector {
position: absolute;
- top: 5px;
+ top: 45px;
bottom: 5px;
left: 200px;
right: 0;
background-color: #f0f0f0;
padding: 2px;
overflow: auto;
}
#files-dialog #files-folder-footer {
position: absolute;
height: 30px;
bottom: 0;
}
#files-dialog #files-folder-footer form {
display: inline;
}
#files-folder-selector table {
width: 100%;
border-spacing: 0;
}
#files-folder-selector table td span.name {
background: url(images/folder.png) 0 0 no-repeat;
height: 18px;
padding-left: 20px;
margin-left: 3px;
cursor: pointer;
}
#files-folder-selector table tr.selected td span.name {
font-weight: bold;
}
#files-folder-selector table tr.virtual td span.name {
color: #bbb;
cursor: default;
}
+#files-compose-dialog #searchmenulink {
+ width: 15px;
+}
+
+#files-compose-dialog #quicksearchbar {
+ top: 10px;
+ right: 5px;
+}
+
+#files-compose-dialog #searchreset {
+ cursor: pointer;
+}
+
#filelist table {
width: 100%;
border-spacing: 0;
}
#filelist td {
white-space: nowrap;
cursor: default;
}
#filelist td.filename {
width: 98%;
height: 20px;
padding: 0 4px;
}
#filelist td.filesize {
text-align: right;
}
#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;
}
/*
#filelist thead td {
cursor: pointer;
}
#filelist thead td.sorted {
padding-right: 16px;
text-decoration: underline;
background: url(images/buttons.png) right -120px no-repeat;
}
#filelist thead td.sorted.reverse {
background-position: right -140px;
}
*/
#filelist tbody tr.selected td {
background-color: #d9ecf4;
}
/***** tree indicators *****/
td span.branch span
{
float: left;
height: 16px;
}
td span.branch span.tree
{
height: 17px;
width: 15px;
background: url(images/tree.gif) 0 0 no-repeat;
}
td span.branch span.l1
{
background-position: 0px 0px; /* L */
}
td span.branch span.l2
{
background-position: -30px 0px; /* | */
}
td span.branch span.l3
{
background-position: -15px 0px; /* |- */
}
diff --git a/plugins/kolab_files/skins/larry/templates/compose_plugin.html b/plugins/kolab_files/skins/larry/templates/compose_plugin.html
index 2a71e571..a94418f9 100644
--- a/plugins/kolab_files/skins/larry/templates/compose_plugin.html
+++ b/plugins/kolab_files/skins/larry/templates/compose_plugin.html
@@ -1,6 +1,14 @@
<div id="files-compose-dialog" class="uidialog">
+ <div id="quicksearchbar" class="searchbox">
+ <roundcube:object name="file-search-form" id="filesearchbox" />
+<!--
+ <roundcube:button name="searchmenulink" id="searchmenulink" class="iconbutton searchoptions" onclick="UI.show_popup('searchmenu');return false" title="searchmod" content=" " />
+-->
+ <a id="searchmenulink" class="iconbutton searchoptions"> </a>
+ <a id="searchreset" class="iconbutton reset" title="<roundcube:label name="resetsearch"/>" onclick="file_api.search_reset()"> </a>
+ </div>
<div id="files-folder-selector"></div>
<div id="files-file-selector">
<table id="filelist"><tbody></tbody></table>
</div>
</div>
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, May 1, 2:05 PM (3 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
661334
Default Alt Text
(30 KB)
Attached To
Mode
R14 roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline
Log In to Comment