Page MenuHomePhorge

No OneTemporary

Size
30 KB
Referenced Files
None
Subscribers
None
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

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)

Event Timeline