Page MenuHomePhorge

No OneTemporary

diff --git a/lib/file_api.php b/lib/file_api.php
index 03ace94..b0e8131 100644
--- a/lib/file_api.php
+++ b/lib/file_api.php
@@ -1,362 +1,366 @@
<?php
class file_api
{
private $app_name = 'Kolab File API';
private $api;
private $config = array(
'date_format' => 'Y-m-d H:i',
'language' => 'en_US',
);
const ERROR_CODE = 500;
public function __construct()
{
}
/**
* Initialise backend class
*/
protected function api_init()
{
if ($this->api) {
return;
}
// @TODO: config
$driver = 'kolab';
$class = $driver . '_file_storage';
require_once $driver . '/' . $class . '.php';
$this->api = new $class;
}
/**
* Process the request and dispatch it to the requested service
*/
public function run()
{
- $request = $_GET['method'];
+ $request = strtolower($_GET['method']);
// Check the session, authenticate the user
if (!$this->session_validate()) {
@session_destroy();
if ($request == 'authenticate') {
session_start();
if ($username = $this->authenticate()) {
$_SESSION['user'] = $username;
$_SESSION['time'] = time();
$_SESSION['config'] = $this->config;
$this->output_success(array('token' => session_id()));
}
}
throw new Exception("Invalid session", 403);
}
// Call service method
$result = $this->request($request);
// Send success response, errors should be handled by driver class
// by throwing exceptions or sending output by itself
$this->output_success($result);
}
/**
* Session validation check
*/
private function session_validate()
{
$sess_id = rcube_utils::request_header('X-Session-Token') ?: $_REQUEST['token'];
if (empty($sess_id)) {
return false;
}
session_id($sess_id);
session_start();
if (empty($_SESSION['user'])) {
return false;
}
// Session timeout
// $timeout = $this->config->get('kolab_wap', 'session_timeout');
$timeout = 3600;
if ($timeout && $_SESSION['time'] && $_SESSION['time'] < time() - $timeout) {
return false;
}
// update session time
$_SESSION['time'] = time();
return true;
}
/**
* Authentication request handler (HTTP Auth)
*/
private function authenticate()
{
if (isset($_POST['username'])) {
$username = $_POST['username'];
$password = $_POST['password'];
}
else if (!empty($_SERVER['PHP_AUTH_USER'])) {
$username = $_SERVER['PHP_AUTH_USER'];
$password = $_SERVER['PHP_AUTH_PW'];
}
// when used with (f)cgi no PHP_AUTH* variables are available without defining a special rewrite rule
else if (!isset($_SERVER['PHP_AUTH_USER'])) {
// "Basic didhfiefdhfu4fjfjdsa34drsdfterrde..."
if (isset($_SERVER["REMOTE_USER"])) {
$basicAuthData = base64_decode(substr($_SERVER["REMOTE_USER"], 6));
}
else if (isset($_SERVER["REDIRECT_REMOTE_USER"])) {
$basicAuthData = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6));
}
else if (isset($_SERVER["Authorization"])) {
$basicAuthData = base64_decode(substr($_SERVER["Authorization"], 6));
}
else if (isset($_SERVER["HTTP_AUTHORIZATION"])) {
$basicAuthData = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6));
}
if (isset($basicAuthData) && !empty($basicAuthData)) {
list($username, $password) = explode(":", $basicAuthData);
}
}
if (!empty($username)) {
$this->api_init();
$result = $this->api->authenticate($username, $password);
}
if (empty($result)) {
/*
header('WWW-Authenticate: Basic realm="' . $this->app_name .'"');
header('HTTP/1.1 401 Unauthorized');
exit;
*/
throw new Exception("Invalid password or username", file_api::ERROR_CODE);
}
return $username;
}
/**
* Storage driver method caller
*/
private function request($request)
{
+ if ($request == 'ping') {
+ return array();
+ }
+
$this->api_init();
switch ($request) {
case 'file_list':
if (!isset($_GET['folder']) || $_GET['folder'] === '') {
throw new Exception("Missing folder name", file_api::ERROR_CODE);
}
$params = array('reverse' => !empty($_GET['reverse']) && rcube_utils::get_boolean($_GET['reverse']));
if (!empty($_GET['sort'])) {
$params['sort'] = strtolower($_GET['sort']);
}
if (!empty($_GET['search'])) {
$params['search'] = $_GET['search'];
if (!is_array($params['search'])) {
$params['search'] = array('name' => $params['search']);
}
}
return $this->api->file_list($_GET['folder'], $params);
case 'file_create':
if (!isset($_GET['folder']) || $_GET['folder'] === '') {
throw new Exception("Missing folder name", file_api::ERROR_CODE);
}
$uploads = $this->upload();
$result = array();
foreach ($uploads as $file) {
$this->api->file_create($_GET['folder'], $file['name'], $file);
unset($file['path']);
$result[$file['name']] = array(
'type' => $file['type'],
'size' => $file['size'],
);
}
return $result;
case 'file_delete':
if (!isset($_GET['folder']) || $_GET['folder'] === '') {
throw new Exception("Missing folder name", file_api::ERROR_CODE);
}
if (!isset($_GET['file']) || $_GET['file'] === '') {
throw new Exception("Missing file name", file_api::ERROR_CODE);
}
return $this->api->file_delete($_GET['folder'], $_GET['file']);
case 'file_get':
if (!isset($_GET['folder']) || $_GET['folder'] === '') {
header("HTTP/1.0 ".file_api::ERROR_CODE." Missing folder name");
}
if (!isset($_GET['file']) || $_GET['file'] === '') {
header("HTTP/1.0 ".file_api::ERROR_CODE." Missing file name");
}
try {
$this->api->file_get($_GET['folder'], $_GET['file']);
}
catch (Exception $e) {
header("HTTP/1.0 ".file_api::ERROR_CODE." " . $e->getMessage());
}
exit;
case 'file_rename':
if (!isset($_GET['folder']) || $_GET['folder'] === '') {
throw new Exception("Missing folder name", file_api::ERROR_CODE);
}
if (!isset($_GET['file']) || $_GET['file'] === '') {
throw new Exception("Missing file name", file_api::ERROR_CODE);
}
if (!isset($_GET['new']) || $_GET['new'] === '') {
throw new Exception("Missing new file name", file_api::ERROR_CODE);
}
if ($_GET['new'] === $_GET['file']) {
throw new Exception("Old and new file name is the same", file_api::ERROR_CODE);
}
return $this->api->file_rename($_GET['folder'], $_GET['file'], $_GET['new']);
case 'folder_create':
if (!isset($_GET['folder']) || $_GET['folder'] === '') {
throw new Exception("Missing folder name", file_api::ERROR_CODE);
}
return $this->api->folder_create($_GET['folder']);
case 'folder_delete':
if (!isset($_GET['folder']) || $_GET['folder'] === '') {
throw new Exception("Missing folder name", file_api::ERROR_CODE);
}
return $this->api->folder_delete($_GET['folder']);
case 'folder_rename':
if (!isset($_GET['folder']) || $_GET['folder'] === '') {
throw new Exception("Missing source folder name", file_api::ERROR_CODE);
}
if (!isset($_GET['new']) || $_GET['new'] === '') {
throw new Exception("Missing destination folder name", file_api::ERROR_CODE);
}
if ($_GET['new'] === $_GET['folder']) {
return;
}
return $this->api->folder_rename($_GET['folder'], $_GET['new']);
case 'folder_list':
return $this->api->folder_list();
case 'quit':
@session_destroy();
return array();
case 'configure':
foreach (array_keys($this->config) as $name) {
if (isset($_GET[$name])) {
$this->config[$name] = $_GET[$name];
}
}
$_SESSION['config'] = $this->config;
return $this->config;
}
if ($request) {
throw new Exception("Unknown method", 501);
}
}
/**
* File uploads handler
*/
protected function upload()
{
$files = array();
if (is_array($_FILES['file']['tmp_name'])) {
foreach ($_FILES['file']['tmp_name'] as $i => $filepath) {
if ($err = $_FILES['file']['error'][$i]) {
if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
throw new Exception("Maximum file size exceeded", file_api::ERROR_CODE);
}
throw new Exception("File upload failed", file_api::ERROR_CODE);
}
$files[] = array(
'path' => $filepath,
'name' => $_FILES['file']['name'][$i],
'size' => filesize($filepath),
'type' => rcube_mime::file_content_type($filepath, $_FILES['file']['name'][$i], $_FILES['file']['type']),
);
}
}
else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
throw new Exception("File upload failed", file_api::ERROR_CODE);
}
return $files;
}
/**
* Send success response
*
* @param mixed $data Data
*/
public function output_success($data)
{
if (!is_array($data)) {
$data = array();
}
$this->output_send(array('status' => 'OK', 'result' => $data));
}
/**
* Send error response
*
* @param mixed $data Data
*/
public function output_error($errdata, $code = null)
{
if (is_string($errdata)) {
$errdata = array('reason' => $errdata);
}
if (!$code) {
$code = file_api::ERROR_CODE;
}
$this->output_send(array('status' => 'ERROR', 'code' => $code) + $errdata);
}
/**
* Send response
*
* @param mixed $data Data
*/
protected function output_send($data)
{
// Send response
header("Content-Type: application/json; charset=utf-8");
echo json_encode($data);
exit;
}
}
diff --git a/public_html/js/files_api.js b/public_html/js/files_api.js
new file mode 100644
index 0000000..1d3b5eb
--- /dev/null
+++ b/public_html/js/files_api.js
@@ -0,0 +1,328 @@
+/*
+ +--------------------------------------------------------------------------+
+ | This file is part of the Kolab File API |
+ | |
+ | Copyright (C) 2012-2013, Kolab Systems AG |
+ | |
+ | 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/> |
+ +--------------------------------------------------------------------------+
+ | Author: Aleksander Machniak <machniak@kolabsys.com> |
+ +--------------------------------------------------------------------------+
+*/
+
+function files_api()
+{
+ var ref = this;
+
+ // default config
+ this.translations = {};
+ this.env = {
+ url: 'api/',
+ directory_separator: '/'
+ };
+
+
+ /*********************************************************/
+ /********* Basic utilities *********/
+ /*********************************************************/
+
+ // set environment variable(s)
+ this.set_env = function(p, value)
+ {
+ if (p != null && typeof p === 'object' && !value)
+ for (var n in p)
+ this.env[n] = p[n];
+ else
+ this.env[p] = value;
+ };
+
+ // add a localized label(s) to the client environment
+ this.tdef = function(p, value)
+ {
+ if (typeof p == 'string')
+ this.translations[p] = value;
+ else if (typeof p == 'object')
+ $.extend(this.translations, p);
+ };
+
+ // return a localized string
+ this.t = function(label)
+ {
+ if (this.translations[label])
+ return this.translations[label];
+ else
+ return label;
+ };
+
+ // print a message into browser console
+ this.log = function(msg)
+ {
+ if (window.console && console.log)
+ console.log(msg);
+ };
+
+ /********************************************************/
+ /********* Remote request methods *********/
+ /********************************************************/
+
+ // send a http POST request to the API service
+ this.post = function(action, postdata, func)
+ {
+ var url = this.env.url + action, ref = this;
+
+ if (!func) func = 'response';
+
+ this.set_request_time();
+
+ return $.ajax({
+ type: 'POST', url: url, data: postdata, dataType: 'json',
+ contentType: 'application/json; charset=utf-8',
+ success: function(response) { ref[func](response); },
+ error: function(o, status, err) { ref.http_error(o, status, err); },
+ cache: false,
+ beforeSend: function(xmlhttp) { xmlhttp.setRequestHeader('X-Session-Token', ref.env.token); }
+ });
+ };
+
+ // send a http GET request to the API service
+ this.get = function(action, data, func)
+ {
+ var url = this.env.url;
+
+ if (!func) func = 'response';
+
+ this.set_request_time();
+ data.method = action;
+
+ return $.ajax({
+ type: 'GET', url: url, data: data,
+ success: function(response) { ref[func](response); },
+ error: function(o, status, err) { ref.http_error(o, status, err); },
+ cache: false,
+ beforeSend: function(xmlhttp) { xmlhttp.setRequestHeader('X-Session-Token', ref.env.token); }
+ });
+ };
+
+ // handle HTTP request errors
+ this.http_error = function(request, status, err)
+ {
+ var errmsg = request.statusText;
+
+ this.set_busy(false);
+ request.abort();
+
+ if (request.status && errmsg)
+ this.display_message(this.t('servererror') + ' (' + errmsg + ')', 'error');
+ };
+
+ this.response = function(response)
+ {
+ this.update_request_time();
+ this.set_busy(false);
+
+ return this.response_parse(response);
+ };
+
+ this.response_parse = function(response)
+ {
+ if (!response || response.status != 'OK') {
+ // Logout on invalid-session error
+ if (response && response.code == 403)
+ this.logout(response);
+ else
+ this.display_message(response && response.reason ? response.reason : this.t('servererror'), 'error');
+
+ return false;
+ }
+
+ return true;
+ };
+
+
+ /*********************************************************/
+ /********* Utilities *********/
+ /*********************************************************/
+
+ // Called on "session expired" session
+ this.logout = function(response) {};
+
+ // set state
+ this.set_busy = function(a, message) {};
+
+ // displays error message
+ this.display_message = function(label) {};
+
+ // 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() {};
+
+
+ /*********************************************************/
+ /********* Helpers *********/
+ /*********************************************************/
+
+ // compose a valid url with the given parameters
+ this.url = function(action, query)
+ {
+ var k, param = {},
+ querystring = typeof query === 'string' ? '&' + query : '';
+
+ if (typeof action !== 'string')
+ query = action;
+ else if (!query || typeof query !== 'object')
+ query = {};
+
+ // overwrite task name
+ if (action)
+ query.method = action;
+
+ // remove undefined values
+ for (k in query) {
+ if (query[k] !== undefined && query[k] !== null)
+ param[k] = query[k];
+ }
+
+ return '?' + $.param(param) + querystring;
+ };
+
+ // Folder list parser, converts it into structure
+ this.folder_list_parse = function(list)
+ {
+ var i, n, items, items_len, f, tmp, folder, num = 1,
+ len = list.length, folders = {};
+
+ for (i=0; i<len; i++) {
+ folder = list[i];
+ items = folder.split(this.env.directory_separator);
+ items_len = items.length;
+
+ for (n=0; n<items_len-1; n++) {
+ tmp = items.slice(0,n+1);
+ f = tmp.join(this.env.directory_separator);
+ if (!folders[f])
+ folders[f] = {name: tmp.pop(), depth: n, id: 'f'+num++, virtual: 1};
+ }
+
+ folders[folder] = {name: items.pop(), depth: items_len-1, id: 'f'+num++};
+ }
+
+ return folders;
+ };
+
+ // folder structure presentation (structure icons)
+ this.folder_list_tree = function(folders)
+ {
+ var i, n, diff, tree = [], folder;
+
+ for (i in folders) {
+ items = i.split(this.env.directory_separator);
+ items_len = items.length;
+
+ // skip root
+ if (items_len < 2) {
+ tree = [];
+ continue;
+ }
+
+ folders[i].tree = [1];
+
+ for (n=0; n<tree.length; n++) {
+ folder = tree[n];
+ diff = folders[folder].depth - (items_len - 1);
+ if (diff >= 0)
+ folders[folder].tree[diff] = folders[folder].tree[diff] ? folders[folder].tree[diff] + 2 : 2;
+ }
+
+ tree.push(i);
+ }
+
+ for (i in folders) {
+ if (tree = folders[i].tree) {
+ var html = '', divs = [];
+ for (n=0; n<folders[i].depth; n++) {
+ if (tree[n] > 2)
+ divs.push({'class': 'l3', width: 15});
+ else if (tree[n] > 1)
+ divs.push({'class': 'l2', width: 15});
+ else if (tree[n] > 0)
+ divs.push({'class': 'l1', width: 15});
+ // separator
+ else if (divs.length && !divs[divs.length-1]['class'])
+ divs[divs.length-1].width += 15;
+ else
+ divs.push({'class': null, width: 15});
+ }
+
+ for (n=divs.length-1; n>=0; n--) {
+ if (divs[n]['class'])
+ html += '<span class="tree '+divs[n]['class']+'" />';
+ else
+ html += '<span style="width:'+divs[n].width+'px" />';
+ }
+
+ if (html)
+ $('#' + folders[i].id + ' span.branch').html(html);
+ }
+ }
+ };
+
+ // convert content-type string into class name
+ this.file_type_class = function(type)
+ {
+ if (!type)
+ return '';
+
+ type = type.replace(/[^a-z0-9]/g, '_');
+
+ return type;
+ };
+
+ // convert bytes into number with size unit
+ this.file_size = function(size)
+ {
+ if (size >= 1073741824)
+ return parseFloat(size/1073741824).toFixed(2) + ' GB';
+ if (size >= 1048576)
+ return parseFloat(size/1048576).toFixed(2) + ' MB';
+ if (size >= 1024)
+ return parseInt(size/1024) + ' kB';
+
+ return parseInt(size || 0)+ ' B';
+ };
+};
+
+// Add escape() method to RegExp object
+// http://dev.rubyonrails.org/changeset/7271
+RegExp.escape = function(str)
+{
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+
+// make a string URL safe (and compatible with PHP's rawurlencode())
+function urlencode(str)
+{
+ if (window.encodeURIComponent)
+ return encodeURIComponent(str).replace('*', '%2A');
+
+ return escape(str)
+ .replace('+', '%2B')
+ .replace('*', '%2A')
+ .replace('/', '%2F')
+ .replace('@', '%40');
+};
diff --git a/public_html/js/app.js b/public_html/js/files_ui.js
similarity index 74%
rename from public_html/js/app.js
rename to public_html/js/files_ui.js
index 30d1ea8..131905f 100644
--- a/public_html/js/app.js
+++ b/public_html/js/files_ui.js
@@ -1,1000 +1,769 @@
/*
+--------------------------------------------------------------------------+
| This file is part of the Kolab File API |
| |
- | Copyright (C) 2011-2012, Kolab Systems AG |
+ | Copyright (C) 2012-2013, Kolab Systems AG |
| |
| 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/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
-function file_ui()
+function files_ui()
{
var ref = this;
- this.translations = {};
this.request_timeout = 300;
this.message_time = 3000;
this.events = {};
this.env = {
+ url: 'api/',
sort_column: 'name',
sort_reverse: 0,
directory_separator: '/'
};
// set jQuery ajax options
$.ajaxSetup({
cache: false,
error: function(request, status, err) { ref.http_error(request, status, err); },
beforeSend: function(xmlhttp) { xmlhttp.setRequestHeader('X-Session-Token', ref.env.token); }
});
/*********************************************************/
/********* basic utilities *********/
/*********************************************************/
// set environment variable(s)
this.set_env = function(p, value)
{
if (p != null && typeof p === 'object' && !value)
for (var n in p)
this.env[n] = p[n];
else
this.env[p] = value;
};
- // add a localized label(s) to the client environment
- this.tdef = function(p, value)
- {
- if (typeof p == 'string')
- this.translations[p] = value;
- else if (typeof p == 'object')
- $.extend(this.translations, p);
- };
-
- // return a localized string
- this.t = function(label)
- {
- if (this.translations[label])
- return this.translations[label];
- else
- return label;
- };
-
- // print a message into browser console
- this.log = function(msg)
- {
- if (window.console && console.log)
- console.log(msg);
- };
-
// execute a specific command on the web client
this.command = function(command, props, obj)
{
if (obj && obj.blur)
obj.blur();
if (this.busy)
return false;
this.set_busy(true, 'loading');
var ret = undefined,
func = command.replace(/[^a-z]/g, '_'),
task = command.replace(/\.[a-z-_]+$/g, '');
if (this[func] && typeof this[func] === 'function') {
ret = this[func](props);
}
else {
this.http_post(command, props);
}
// update menu state
// $('li', $('#navigation')).removeClass('active');
// $('li.'+task, ('#navigation')).addClass('active');
return ret === false ? false : obj ? false : true;
};
this.set_busy = function(a, message)
{
if (a && this.busy)
return;
if (a && message) {
var msg = this.t(message);
if (msg == message)
msg = 'Loading...';
this.display_message(msg, 'loading');
}
else if (!a) {
this.hide_message('loading');
}
this.busy = a;
// if (this.gui_objects.editform)
// this.lock_form(this.gui_objects.editform, a);
// clear pending timer
if (this.request_timer)
clearTimeout(this.request_timer);
// set timer for requests
if (a && this.request_timeout)
this.request_timer = window.setTimeout(function() { ref.request_timed_out(); }, this.request_timeout * 1000);
};
// called when a request timed out
this.request_timed_out = function()
{
this.set_busy(false);
this.display_message('Request timed out!', 'error');
};
// Add variable to GET string, replace old value if exists
this.add_url = function(url, name, value)
{
value = urlencode(value);
if (/(\?.*)$/.test(url)) {
var urldata = RegExp.$1,
datax = RegExp('((\\?|&)'+RegExp.escape(name)+'=[^&]*)');
if (datax.test(urldata))
urldata = urldata.replace(datax, RegExp.$2 + name + '=' + value);
else
urldata += '&' + name + '=' + value
return url.replace(/(\?.*)$/, urldata);
}
- else
- return url + '?' + name + '=' + value;
+
+ return url + '?' + name + '=' + value;
};
this.trigger_event = function(event, data)
{
if (this.events[event])
for (var i in this.events[event])
this.events[event][i](data);
};
this.add_event_listener = function(event, func)
{
if (!this.events[event])
this.events[event] = [];
this.events[event].push(func);
};
/*********************************************************/
/********* GUI functionality *********/
/*********************************************************/
// write to the document/window title
this.set_pagetitle = function(title)
{
if (title && document.title)
document.title = title;
};
// display a system message (types: loading, notice, error)
this.display_message = function(msg, type, timeout)
{
var obj, ref = this;
if (!type)
type = 'notice';
if (msg)
msg = this.t(msg);
if (type == 'loading') {
timeout = this.request_timeout * 1000;
if (!msg)
msg = this.t('loading');
}
else if (!timeout)
timeout = this.message_time * (type == 'error' || type == 'warning' ? 2 : 1);
obj = $('<div>');
if (type != 'loading') {
msg = '<div><span>' + msg + '</span></div>';
obj.addClass(type).click(function() { return ref.hide_message(); });
}
if (timeout > 0)
window.setTimeout(function() { ref.hide_message(type, type != 'loading'); }, timeout);
obj.attr('id', type == 'loading' ? 'loading' : 'message')
.appendTo('body').html(msg).show();
};
// make a message to disapear
this.hide_message = function(type, fade)
{
if (type == 'loading')
$('#loading').remove();
else
$('#message').fadeOut('normal', function() { $(this).remove(); });
};
this.set_watermark = function(id)
{
if (this.env.watermark)
$('#'+id).html(this.env.watermark);
};
/********************************************************/
/********* Remote request methods *********/
/********************************************************/
- // compose a valid url with the given parameters
- this.url = function(action, query)
- {
- var k, param = {},
- querystring = typeof query === 'string' ? '&' + query : '';
-
- if (typeof action !== 'string')
- query = action;
- else if (!query || typeof query !== 'object')
- query = {};
-
- // overwrite task name
- if (action)
- query.method = action;
-
- // remove undefined values
- for (k in query) {
- if (query[k] !== undefined && query[k] !== null)
- param[k] = query[k];
- }
-
- return '?' + $.param(param) + querystring;
- };
-
// send a http POST request to the server
this.http_post = function(action, postdata)
{
var url = this.url(action);
if (postdata && typeof postdata === 'object')
postdata.remote = 1;
else {
if (!postdata)
postdata = '';
postdata += '&remote=1';
}
this.set_request_time();
return $.ajax({
type: 'POST', url: url, data: postdata, dataType: 'json',
success: function(response) { ui.http_response(response); },
error: function(o, status, err) { ui.http_error(o, status, err); }
});
};
- // send a http POST request to the API service
- this.api_post = function(action, postdata, func)
- {
- var url = 'api/' + action;
-
- if (!func) func = 'api_response';
-
- this.set_request_time();
-
- return $.ajax({
- type: 'POST', url: url, data: postdata, dataType: 'json',
- contentType: 'application/json; charset=utf-8',
- success: function(response) { ui[func](response); },
- error: function(o, status, err) { ui.http_error(o, status, err); }
- });
- };
-
- // send a http GET request to the API service
- this.api_get = function(action, data, func)
- {
- var url = 'api/';
-
- if (!func) func = 'api_response';
-
- this.set_request_time();
- data.method = action;
-
- return $.ajax({
- type: 'GET', url: url, data: data,
- success: function(response) { ui[func](response); },
- error: function(o, status, err) { ui.http_error(o, status, err); }
- });
- };
-
// handle HTTP response
this.http_response = function(response)
{
var i;
if (!response)
return;
// set env vars
if (response.env)
this.set_env(response.env);
// we have translation labels to add
if (typeof response.labels === 'object')
this.tdef(response.labels);
// HTML page elements
if (response.objects)
for (i in response.objects)
$('#'+i).html(response.objects[i]);
this.update_request_time();
this.set_busy(false);
// if we get javascript code from server -> execute it
if (response.exec)
eval(response.exec);
this.trigger_event('http-response', response);
};
// handle HTTP request errors
this.http_error = function(request, status, err)
{
var errmsg = request.statusText;
this.set_busy(false);
request.abort();
if (request.status && errmsg)
this.display_message(this.t('servererror') + ' (' + errmsg + ')', 'error');
};
- this.api_response = function(response)
- {
- this.update_request_time();
- this.set_busy(false);
-
- return this.api_response_parse(response);
- };
-
- this.api_response_parse = function(response)
- {
- if (!response || response.status != 'OK') {
- // Logout on invalid-session error
- if (response && response.code == 403)
- this.main_logout();
- else
- this.display_message(response && response.reason ? response.reason : this.t('servererror'), 'error');
-
- return false;
- }
-
- return true;
- };
-
/********************************************************/
/********* Helper methods *********/
/********************************************************/
// disable/enable all fields of a form
this.lock_form = function(form, lock)
{
if (!form || !form.elements)
return;
var n, len, elm;
if (lock)
this.disabled_form_elements = [];
for (n=0, len=form.elements.length; n<len; n++) {
elm = form.elements[n];
if (elm.type == 'hidden')
continue;
// remember which elem was disabled before lock
if (lock && elm.disabled)
this.disabled_form_elements.push(elm);
// check this.disabled_form_elements before inArray() as a workaround for FF5 bug
// http://bugs.jquery.com/ticket/9873
else if (lock || (this.disabled_form_elements && $.inArray(elm, this.disabled_form_elements)<0))
elm.disabled = lock;
}
};
this.set_request_time = function()
{
this.env.request_time = (new Date()).getTime();
};
// Update request time element
this.update_request_time = function()
{
if (this.env.request_time) {
var t = ((new Date()).getTime() - this.env.request_time)/1000,
el = $('#reqtime');
el.text(el.text().replace(/[0-9.,]+/, t));
}
};
// position and display popup
this.popup_show = function(e, popup)
{
var popup = $(popup),
pos = this.mouse_pos(e),
win = $(window),
w = popup.width(),
h = popup.height(),
left = pos.left - w,
top = pos.top;
if (top + h > win.height())
top -= h;
if (left + w > win.width())
left -= w;
popup.css({left: left + 'px', top: top + 'px'})
.click(function(e) { e.stopPropagation(); $(this).hide(); }).show();
e.stopPropagation();
};
// Return absolute mouse position of an event
this.mouse_pos = function(e)
{
if (!e) e = window.event;
var mX = (e.pageX) ? e.pageX : e.clientX,
mY = (e.pageY) ? e.pageY : e.clientY;
if (document.body && document.all) {
mX += document.body.scrollLeft;
mY += document.body.scrollTop;
}
if (e._offset) {
mX += e._offset.left;
mY += e._offset.top;
}
return { left:mX, top:mY };
};
this.serialize_form = function(id)
{
var i, v, json = {},
form = $(id),
query = form.serializeArray();
for (i in query)
json[query[i].name] = query[i].value;
// serializeArray() doesn't work properly for multi-select
$('select[multiple="multiple"]', form).each(function() {
var name = this.name;
json[name] = [];
$(':selected', this).each(function() {
json[name].push(this.value);
});
});
return json;
};
/*********************************************************/
/********* Commands *********/
/*********************************************************/
this.main_logout = function(params)
{
location.href = '?task=main&action=logout' + (params ? '&' + $.param(params) : '');
return false;
};
// folder list request
this.folder_list = function()
{
this.set_busy(true, 'loading');
- this.api_get('folder_list', {}, 'folder_list_response');
+ this.get('folder_list', {}, 'folder_list_response');
};
// folder list response handler
this.folder_list_response = function(response)
{
- if (!this.api_response(response))
+ if (!this.response(response))
return;
var elem = $('#folderlist'), table = $('table', elem);
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() { ui.folder_select(i); });
if (i == ui.env.folder)
row.addClass('selected');
table.append(row);
});
// add tree icons
- this.folder_list_tree();
+ this.folder_list_tree(this.env.folders);
};
this.folder_select = function(folder)
{
this.env.search = null;
this.file_search_stop();
this.command('file.list', folder);
};
// folder create request
this.folder_create = function(folder)
{
this.set_busy(true, 'saving');
- this.api_get('folder_create', {folder: folder}, 'folder_create_response');
+ this.get('folder_create', {folder: folder}, 'folder_create_response');
};
// folder create response handler
this.folder_create_response = function(response)
{
- if (!this.api_response(response))
+ if (!this.response(response))
return;
this.folder_list();
};
// file list request
this.file_list = function(folder, params)
{
if (!params)
params = {};
params.folder = folder ? 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.folder = params.folder;
this.env.sort_col = params.sort;
this.env.sort_reverse = params.reverse;
this.set_busy(true, 'loading');
- this.api_get('file_list', params, 'file_list_response');
+ this.get('file_list', params, 'file_list_response');
var list = $('#folderlist');
$('tr.selected', list).removeClass('selected');
$('#' + this.env.folders[params.folder].id, list).addClass('selected');
};
// file list response handler
this.file_list_response = function(response)
{
- if (!this.api_response(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).click(function(e) { ui.file_menu(e, key); });
$('td.filename', row).addClass(ui.file_type_class(data.type)).append(link);
$('td.filemtime', row).text(data.mtime);
$('td.filesize', row).text(ui.file_size(data.size));
row.attr('data-file', urlencode(key));
table.append(row);
});
};
// file delete request
this.file_delete = function(file)
{
this.set_busy(true, 'deleting');
- this.api_get('file_delete', {folder: this.env.folder, file: file}, 'file_delete_response');
+ this.get('file_delete', {folder: this.env.folder, file: file}, 'file_delete_response');
};
// file delete response handler
this.file_delete_response = function(response)
{
- if (!this.api_response(response))
+ if (!this.response(response))
return;
this.file_list(this.env.folder);
};
// file rename request
this.file_rename = function(file, newname)
{
if (file === newname)
return;
this.set_busy(true, 'saving');
- this.api_get('file_rename', {folder: this.env.folder, file: file, 'new': newname}, 'file_rename_response');
+ this.get('file_rename', {folder: this.env.folder, file: file, 'new': newname}, 'file_rename_response');
};
// file delete response handler
this.file_rename_response = function(response)
{
- if (!this.api_response(response))
+ if (!this.response(response))
return;
this.file_list(this.env.folder);
};
this.file_menu = function(e, file)
{
var menu = $('#file-menu');
$('li.file-open > a', menu)
- .attr({target: '_blank', href: 'api/' + ui.url('file_get', {folder: this.env.folder, token: this.env.token, file: file})});
+ .attr({target: '_blank', href: 'api/' + this.url('file_get', {folder: this.env.folder, token: this.env.token, file: file})});
$('li.file-delete > a', menu).off('click').click(function() { ui.file_delete(file); });
$('li.file-rename > a', menu).off('click').click(function() { ui.file_rename_start(file); });
this.popup_show(e, menu);
};
this.file_rename_start = function(file)
{
var list = $('#filelist'),
tr = $('tr[data-file="' + urlencode(file) + '"]', list),
td = $('td.filename', tr),
input = $('<input>').attr({type: 'text', name: 'filename', 'class': 'filerename'})
.val(file).data('filename', file)
.click(function(e) { e.stopPropagation(); })
.keydown(function(e) {
switch (e.which) {
case 27: // ESC
ui.file_rename_stop();
break;
case 13: // Enter
var elem = $(this), newname = elem.val();
ui.file_rename(elem.data('filename'), newname);
elem.parent().text(newname);
break;
}
});
$('span', td).text('').append(input);
input.focus();
};
this.file_rename_stop = function()
{
$('input.filerename').each(function() {
var elem = $(this);
elem.parent().text(elem.data('filename'));
});
};
// file upload request
this.file_upload = function()
{
var form = $('#uploadform'),
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() {
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'};
}
- if (ui.api_response_parse(response))
+ if (ui.response_parse(response))
ui.file_list(ui.env.folder);
});
}
};
// post the given form to a hidden iframe
this.async_upload_form = function(form, action, onload)
{
var ts = new Date().getTime(),
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).bind('load', {ts:ts}, onload);
$(form).attr({
target: frame_name,
action: 'api/' + this.url(action, {folder: this.env.folder, token: this.env.token, uploadid:ts}),
method: 'POST'
}).attr(form.encoding ? 'encoding' : 'enctype', 'multipart/form-data')
.submit();
};
// Display file search form
this.file_search_start = function()
{
var form = this.form_show('file-search');
$('input[name="name"]', form).val('').focus();
};
// Hide file search form
this.file_search_stop = function()
{
if (this.env.search)
this.file_list(null, {search: null});
this.form_hide('file-search');
this.env.search = null;
};
// Execute file search
this.file_search_submit = function()
{
var form = this.form_show('file-search'),
value = $('input[name="name"]', form).val();
if (value) {
this.env.search = {name: value};
this.file_list(null, {search: this.env.search});
}
else
this.file_search_stop();
};
// Display folder creation form
this.folder_create_start = function()
{
var form = this.form_show('folder-create');
$('input[name="name"]', form).val('').focus();
$('input[name="parent"]', form).prop('checked', this.env.folder);
};
// Hide folder creation form
this.folder_create_stop = function()
{
this.form_hide('folder-create');
};
// Submit folder creation form
this.folder_create_submit = function()
{
var folder = '', data = this.serialize_form('#folder-create-form');
if (!data.name)
return;
if (data.parent && this.env.folder)
folder = this.env.folder + this.env.directory_separator;
folder += data.name;
this.folder_create_stop();
this.command('folder_create', folder);
};
// Display folder creation form
this.form_show = function(name)
{
var form = $('#' + name + '-form');
$('#forms > form').hide();
form.show();
$('#taskcontent').css('top', form.height() + 20);
return form;
};
// Display folder creation form
this.form_hide = function(name)
{
var form = $('#' + name + '-form');
form.hide();
$('#taskcontent').css('top', 10);
};
-
- // Folder list parser, converts it into structure
- this.folder_list_parse = function(list)
- {
- var i, n, items, items_len, f, tmp, folder, num = 1,
- len = list.length, folders = {};
-
- for (i=0; i<len; i++) {
- folder = list[i];
- items = folder.split(this.env.directory_separator);
- items_len = items.length;
-
- for (n=0; n<items_len-1; n++) {
- tmp = items.slice(0,n+1);
- f = tmp.join(this.env.directory_separator);
- if (!folders[f])
- folders[f] = {name: tmp.pop(), depth: n, id: 'f'+num++, virtual: 1};
- }
-
- folders[folder] = {name: items.pop(), depth: items_len-1, id: 'f'+num++};
- }
-
- return folders;
- };
-
- // folder structure presentation (structure icons)
- this.folder_list_tree = function()
- {
- var i, n, diff, tree = [], folder, folders = this.env.folders;
-
- for (i in folders) {
- items = i.split(this.env.directory_separator);
- items_len = items.length;
-
- // skip root
- if (items_len < 2) {
- tree = [];
- continue;
- }
-
- folders[i].tree = [1];
-
- for (n=0; n<tree.length; n++) {
- folder = tree[n];
- diff = folders[folder].depth - (items_len - 1);
- if (diff >= 0)
- folders[folder].tree[diff] = folders[folder].tree[diff] ? folders[folder].tree[diff] + 2 : 2;
- }
-
- tree.push(i);
- }
-
- for (i in folders) {
- if (tree = folders[i].tree) {
- var html = '', divs = [];
- for (n=0; n<folders[i].depth; n++) {
- if (tree[n] > 2)
- divs.push({'class': 'l3', width: 15});
- else if (tree[n] > 1)
- divs.push({'class': 'l2', width: 15});
- else if (tree[n] > 0)
- divs.push({'class': 'l1', width: 15});
- // separator
- else if (divs.length && !divs[divs.length-1]['class'])
- divs[divs.length-1].width += 15;
- else
- divs.push({'class': null, width: 15});
- }
-
- for (n=divs.length-1; n>=0; n--) {
- if (divs[n]['class'])
- html += '<span class="tree '+divs[n]['class']+'" />';
- else
- html += '<span style="width:'+divs[n].width+'px" />';
- }
-
- if (html)
- $('#' + folders[i].id + ' span.branch').html(html);
- }
- }
- };
-
- // convert content-type string into class name
- this.file_type_class = function(type)
- {
- if (!type)
- return '';
-
- type = type.replace(/[^a-z0-9]/g, '_');
-
- return type;
- };
-
- // convert bytes into number with size unit
- this.file_size = function(size)
- {
- if (size >= 1073741824)
- return parseFloat(size/1073741824).toFixed(2) + ' GB';
- if (size >= 1048576)
- return parseFloat(size/1048576).toFixed(2) + ' MB';
- if (size >= 1024)
- return parseInt(size/1024) + ' kB';
-
- return parseInt(size || 0)+ ' B';
- };
-};
-
-// Add escape() method to RegExp object
-// http://dev.rubyonrails.org/changeset/7271
-RegExp.escape = function(str)
-{
- return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
-};
-
-// make a string URL safe (and compatible with PHP's rawurlencode())
-function urlencode(str)
-{
- if (window.encodeURIComponent)
- return encodeURIComponent(str).replace('*', '%2A');
-
- return escape(str)
- .replace('+', '%2B')
- .replace('*', '%2A')
- .replace('/', '%2F')
- .replace('@', '%40');
};
// Initialize application object (don't change var name!)
-var ui = new file_ui();
+var ui = $.extend(new files_api(), new files_ui());
// general click handler
$(document).click(function() {
$('.popup').hide();
ui.file_rename_stop();
});
diff --git a/public_html/skins/default/templates/login.html b/public_html/skins/default/templates/login.html
index d1f76b8..ef609ac 100644
--- a/public_html/skins/default/templates/login.html
+++ b/public_html/skins/default/templates/login.html
@@ -1,22 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>{$pagetitle}</title>
<link rel="stylesheet" href="{$skin_path}style.css" />
<link rel="shortcut icon" type="image/png" href="{$skin_path}images/favicon.png" />
<script src="js/jquery.min.js"></script>
- <script src="js/app.js"></script>
+ <script src="js/files_api.js"></script>
+ <script src="js/files_ui.js"></script>
</head>
<body>
<div id="logo"></div>
<div id="topmenu"></div>
<div id="navigation"></div>
<div id="task_navigation"></div>
<div id="content">{$engine->login_form()}</div>
<div id="footer">
{include file="footer.html"}
</div>
{$script}
</body>
</html>

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jun 10, 3:54 AM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
196942
Default Alt Text
(51 KB)

Event Timeline