Merge pull request #6260 from owncloud/jan-navigation-filesidebar
Files app navigation sidebar
This commit is contained in:
commit
1fb0d5412c
46 changed files with 4409 additions and 3118 deletions
|
@ -26,6 +26,7 @@
|
|||
"fakeServer": true,
|
||||
"_": true,
|
||||
"OC": true,
|
||||
"OCA": true,
|
||||
"t": true,
|
||||
"n": true
|
||||
}
|
||||
|
|
|
@ -19,3 +19,13 @@ $templateManager->registerTemplate('text/html', 'core/templates/filetemplates/te
|
|||
$templateManager->registerTemplate('application/vnd.oasis.opendocument.presentation', 'core/templates/filetemplates/template.odp');
|
||||
$templateManager->registerTemplate('application/vnd.oasis.opendocument.text', 'core/templates/filetemplates/template.odt');
|
||||
$templateManager->registerTemplate('application/vnd.oasis.opendocument.spreadsheet', 'core/templates/filetemplates/template.ods');
|
||||
|
||||
\OCA\Files\App::getNavigationManager()->add(
|
||||
array(
|
||||
"id" => 'files',
|
||||
"appname" => 'files',
|
||||
"script" => 'list.php',
|
||||
"order" => 0,
|
||||
"name" => $l->t('All files')
|
||||
)
|
||||
);
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
See the COPYING-README file. */
|
||||
|
||||
/* FILE MENU */
|
||||
.actions { padding:5px; height:32px; width: 100%; }
|
||||
.actions { padding:5px; height:32px; display: inline-block; float: left; }
|
||||
.actions input, .actions button, .actions .button { margin:0; float:left; }
|
||||
.actions .button a { color: #555; }
|
||||
.actions .button a:hover, .actions .button a:active { color: #333; }
|
||||
.actions.hidden { display: none; }
|
||||
|
||||
#new {
|
||||
z-index: 1010;
|
||||
|
@ -75,6 +76,7 @@
|
|||
top: 44px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* make sure there's enough room for the file actions */
|
||||
#body-user #filestable {
|
||||
min-width: 688px; /* 768 (mobile break) - 80 (nav width) */
|
||||
|
@ -84,6 +86,40 @@
|
|||
}
|
||||
|
||||
#filestable tbody tr { background-color:#fff; height:51px; }
|
||||
|
||||
.app-files #app-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override global #controls styles
|
||||
* to be more flexible / relative
|
||||
*/
|
||||
#body-user .app-files #controls {
|
||||
left: 310px; /* main nav bar + sidebar */
|
||||
position: fixed;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
/* this is mostly for file viewer apps, text editor, etc */
|
||||
#body-user .app-files.no-sidebar #controls {
|
||||
left: 0px;
|
||||
padding-left: 80px; /* main nav bar */
|
||||
}
|
||||
|
||||
.app-files #app-navigation {
|
||||
width: 230px;
|
||||
}
|
||||
|
||||
.app-files #app-settings {
|
||||
width: 229px; /* DUH */
|
||||
}
|
||||
|
||||
.app-files #app-settings input {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
#filestable tbody tr { background-color:#fff; height:40px; }
|
||||
#filestable tbody tr:hover, tbody tr:active {
|
||||
background-color: rgb(240,240,240);
|
||||
}
|
||||
|
@ -183,9 +219,7 @@ table.multiselect thead {
|
|||
z-index: 10;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
left: 0;
|
||||
padding-left: 80px;
|
||||
width: 100%;
|
||||
left: 310px; /* main nav bar + sidebar */
|
||||
}
|
||||
|
||||
table.multiselect thead th {
|
||||
|
@ -272,7 +306,7 @@ table td.filename form { font-size:14px; margin-left:48px; margin-right:48px; }
|
|||
|
||||
/* Use label to have bigger clickable size for checkbox */
|
||||
#fileList tr td.filename>input[type="checkbox"] + label,
|
||||
#select_all + label {
|
||||
.select-all + label {
|
||||
height: 50px;
|
||||
position: absolute;
|
||||
width: 50px;
|
||||
|
@ -286,10 +320,10 @@ table td.filename form { font-size:14px; margin-left:48px; margin-right:48px; }
|
|||
#fileList tr td.filename>input[type="checkbox"] + label {
|
||||
left: 0;
|
||||
}
|
||||
#select_all + label {
|
||||
.select-all + label {
|
||||
top: 0;
|
||||
}
|
||||
#select_all {
|
||||
.select-all {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: 18px;
|
||||
|
@ -337,6 +371,9 @@ a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; }
|
|||
display: inline;
|
||||
padding: 17px 5px;
|
||||
}
|
||||
.selectedActions a.hidden {
|
||||
display: none;
|
||||
}
|
||||
.selectedActions a img {
|
||||
position:relative;
|
||||
top:5px;
|
||||
|
@ -412,7 +449,7 @@ table.dragshadow td.size {
|
|||
}
|
||||
.mask {
|
||||
z-index: 50;
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
|
|
@ -28,6 +28,7 @@ OCP\User::checkLoggedIn();
|
|||
OCP\Util::addStyle('files', 'files');
|
||||
OCP\Util::addStyle('files', 'upload');
|
||||
OCP\Util::addStyle('files', 'mobile');
|
||||
OCP\Util::addscript('files', 'app');
|
||||
OCP\Util::addscript('files', 'file-upload');
|
||||
OCP\Util::addscript('files', 'jquery.iframe-transport');
|
||||
OCP\Util::addscript('files', 'jquery.fileupload');
|
||||
|
@ -37,28 +38,23 @@ OCP\Util::addscript('files', 'breadcrumb');
|
|||
OCP\Util::addscript('files', 'filelist');
|
||||
|
||||
OCP\App::setActiveNavigationEntry('files_index');
|
||||
// Load the files
|
||||
$dir = isset($_GET['dir']) ? stripslashes($_GET['dir']) : '';
|
||||
$dir = \OC\Files\Filesystem::normalizePath($dir);
|
||||
$dirInfo = \OC\Files\Filesystem::getFileInfo($dir, false);
|
||||
// Redirect if directory does not exist
|
||||
if (!$dirInfo || !$dirInfo->getType() === 'dir') {
|
||||
header('Location: ' . OCP\Util::getScriptName() . '');
|
||||
exit();
|
||||
}
|
||||
|
||||
$isIE8 = false;
|
||||
preg_match('/MSIE (.*?);/', $_SERVER['HTTP_USER_AGENT'], $matches);
|
||||
if (count($matches) > 0 && $matches[1] <= 8){
|
||||
if (count($matches) > 0 && $matches[1] <= 9) {
|
||||
$isIE8 = true;
|
||||
}
|
||||
|
||||
// if IE8 and "?dir=path" was specified, reformat the URL to use a hash like "#?dir=path"
|
||||
if ($isIE8 && isset($_GET['dir'])){
|
||||
if ($dir === ''){
|
||||
$dir = '/';
|
||||
// if IE8 and "?dir=path&view=someview" was specified, reformat the URL to use a hash like "#?dir=path&view=someview"
|
||||
if ($isIE8 && (isset($_GET['dir']) || isset($_GET['view']))) {
|
||||
$hash = '#?';
|
||||
$dir = isset($_GET['dir']) ? $_GET['dir'] : '/';
|
||||
$view = isset($_GET['view']) ? $_GET['view'] : 'files';
|
||||
$hash = '#?dir=' . \OCP\Util::encodePath($dir);
|
||||
if ($view !== 'files') {
|
||||
$hash .= '&view=' . urlencode($view);
|
||||
}
|
||||
header('Location: ' . OCP\Util::linkTo('files', 'index.php') . '#?dir=' . \OCP\Util::encodePath($dir));
|
||||
header('Location: ' . OCP\Util::linkTo('files', 'index.php') . $hash);
|
||||
exit();
|
||||
}
|
||||
|
||||
|
@ -66,16 +62,9 @@ $user = OC_User::getUser();
|
|||
|
||||
$config = \OC::$server->getConfig();
|
||||
|
||||
// needed for share init, permissions will be reloaded
|
||||
// anyway with ajax load
|
||||
$permissions = $dirInfo->getPermissions();
|
||||
|
||||
// information about storage capacities
|
||||
$storageInfo=OC_Helper::getStorageInfo($dir, $dirInfo);
|
||||
$freeSpace=$storageInfo['free'];
|
||||
$uploadLimit=OCP\Util::uploadLimit();
|
||||
$maxUploadFilesize=OCP\Util::maxUploadFilesize($dir, $freeSpace);
|
||||
$publicUploadEnabled = $config->getAppValue('core', 'shareapi_allow_public_upload', 'yes');
|
||||
// mostly for the home storage's free space
|
||||
$dirInfo = \OC\Files\Filesystem::getFileInfo('/', false);
|
||||
$storageInfo=OC_Helper::getStorageInfo('/', $dirInfo);
|
||||
// if the encryption app is disabled, than everything is fine (INIT_SUCCESSFUL status code)
|
||||
$encryptionInitStatus = 2;
|
||||
if (OC_App::isEnabled('files_encryption')) {
|
||||
|
@ -83,32 +72,52 @@ if (OC_App::isEnabled('files_encryption')) {
|
|||
$encryptionInitStatus = $session->getInitialized();
|
||||
}
|
||||
|
||||
$trashEnabled = \OCP\App::isEnabled('files_trashbin');
|
||||
$trashEmpty = true;
|
||||
if ($trashEnabled) {
|
||||
$trashEmpty = \OCA\Files_Trashbin\Trashbin::isEmpty($user);
|
||||
$nav = new OCP\Template('files', 'appnavigation', '');
|
||||
|
||||
$navItems = \OCA\Files\App::getNavigationManager()->getAll();
|
||||
$nav->assign('navigationItems', $navItems);
|
||||
|
||||
$contentItems = array();
|
||||
|
||||
function renderScript($appName, $scriptName) {
|
||||
$content = '';
|
||||
$appPath = OC_App::getAppPath($appName);
|
||||
$scriptPath = $appPath . '/' . $scriptName;
|
||||
if (file_exists($scriptPath)) {
|
||||
// TODO: sanitize path / script name ?
|
||||
ob_start();
|
||||
include $scriptPath;
|
||||
$content = ob_get_contents();
|
||||
@ob_end_clean();
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
// render the container content for every navigation item
|
||||
foreach ($navItems as $item) {
|
||||
$content = '';
|
||||
if (isset($item['script'])) {
|
||||
$content = renderScript($item['appname'], $item['script']);
|
||||
}
|
||||
$contentItem = array();
|
||||
$contentItem['id'] = $item['id'];
|
||||
$contentItem['content'] = $content;
|
||||
$contentItems[] = $contentItem;
|
||||
}
|
||||
|
||||
OCP\Util::addscript('files', 'fileactions');
|
||||
OCP\Util::addscript('files', 'files');
|
||||
OCP\Util::addscript('files', 'navigation');
|
||||
OCP\Util::addscript('files', 'keyboardshortcuts');
|
||||
$tmpl = new OCP\Template('files', 'index', 'user');
|
||||
$tmpl->assign('dir', $dir);
|
||||
$tmpl->assign('permissions', $permissions);
|
||||
$tmpl->assign('trash', $trashEnabled);
|
||||
$tmpl->assign('trashEmpty', $trashEmpty);
|
||||
$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); // minimium of freeSpace and uploadLimit
|
||||
$tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize));
|
||||
$tmpl->assign('freeSpace', $freeSpace);
|
||||
$tmpl->assign('uploadLimit', $uploadLimit); // PHP upload limit
|
||||
$tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true)));
|
||||
$tmpl->assign('usedSpacePercent', (int)$storageInfo['relative']);
|
||||
$tmpl->assign('isPublic', false);
|
||||
$tmpl->assign('publicUploadEnabled', $publicUploadEnabled);
|
||||
$tmpl->assign("encryptedFiles", \OCP\Util::encryptedFiles());
|
||||
$tmpl->assign("mailNotificationEnabled", $config->getAppValue('core', 'shareapi_allow_mail_notification', 'yes'));
|
||||
$tmpl->assign("allowShareWithLink", $config->getAppValue('core', 'shareapi_allow_links', 'yes'));
|
||||
$tmpl->assign("encryptionInitStatus", $encryptionInitStatus);
|
||||
$tmpl->assign('disableSharing', false);
|
||||
$tmpl->assign('appNavigation', $nav);
|
||||
$tmpl->assign('appContents', $contentItems);
|
||||
$tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true)));
|
||||
|
||||
$tmpl->printPage();
|
||||
|
|
147
apps/files/js/app.js
Normal file
147
apps/files/js/app.js
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright (c) 2014
|
||||
*
|
||||
* @author Vincent Petry
|
||||
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
|
||||
/* global dragOptions, folderDropOptions */
|
||||
(function() {
|
||||
|
||||
if (!OCA.Files) {
|
||||
OCA.Files = {};
|
||||
}
|
||||
|
||||
var App = {
|
||||
navigation: null,
|
||||
|
||||
initialize: function() {
|
||||
this.navigation = new OCA.Files.Navigation($('#app-navigation'));
|
||||
|
||||
// TODO: ideally these should be in a separate class / app (the embedded "all files" app)
|
||||
this.fileActions = OCA.Files.FileActions;
|
||||
this.files = OCA.Files.Files;
|
||||
|
||||
this.fileList = new OCA.Files.FileList(
|
||||
$('#app-content-files'), {
|
||||
scrollContainer: $('#app-content'),
|
||||
dragOptions: dragOptions,
|
||||
folderDropOptions: folderDropOptions
|
||||
}
|
||||
);
|
||||
this.files.initialize();
|
||||
this.fileActions.registerDefaultActions(this.fileList);
|
||||
this.fileList.setFileActions(this.fileActions);
|
||||
|
||||
// for backward compatibility, the global FileList will
|
||||
// refer to the one of the "files" view
|
||||
window.FileList = this.fileList;
|
||||
|
||||
this._setupEvents();
|
||||
// trigger URL change event handlers
|
||||
this._onPopState(OC.Util.History.parseUrlQuery());
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the container of the currently visible app.
|
||||
*
|
||||
* @return app container
|
||||
*/
|
||||
getCurrentAppContainer: function() {
|
||||
return this.navigation.getActiveContainer();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup events based on URL changes
|
||||
*/
|
||||
_setupEvents: function() {
|
||||
OC.Util.History.addOnPopStateHandler(_.bind(this._onPopState, this));
|
||||
|
||||
// detect when app changed their current directory
|
||||
$('#app-content').delegate('>div', 'changeDirectory', _.bind(this._onDirectoryChanged, this));
|
||||
$('#app-content').delegate('>div', 'changeViewerMode', _.bind(this._onChangeViewerMode, this));
|
||||
|
||||
$('#app-navigation').on('itemChanged', _.bind(this._onNavigationChanged, this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for when the current navigation item has changed
|
||||
*/
|
||||
_onNavigationChanged: function(e) {
|
||||
var params;
|
||||
if (e && e.itemId) {
|
||||
params = {
|
||||
view: e.itemId,
|
||||
dir: '/'
|
||||
};
|
||||
this._changeUrl(params.view, params.dir);
|
||||
this.navigation.getActiveContainer().trigger(new $.Event('urlChanged', params));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for when an app notified that its directory changed
|
||||
*/
|
||||
_onDirectoryChanged: function(e) {
|
||||
if (e.dir) {
|
||||
this._changeUrl(this.navigation.getActiveItem(), e.dir);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for when an app notifies that it needs space
|
||||
* for viewer mode.
|
||||
*/
|
||||
_onChangeViewerMode: function(e) {
|
||||
var state = !!e.viewerModeEnabled;
|
||||
$('#app-navigation').toggleClass('hidden', state);
|
||||
$('.app-files').toggleClass('viewer-mode no-sidebar', state);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for when the URL changed
|
||||
*/
|
||||
_onPopState: function(params) {
|
||||
params = _.extend({
|
||||
dir: '/',
|
||||
view: 'files'
|
||||
}, params);
|
||||
var lastId = this.navigation.getActiveItem();
|
||||
if (!this.navigation.itemExists(params.view)) {
|
||||
params.view = 'files';
|
||||
}
|
||||
this.navigation.setActiveItem(params.view, {silent: true});
|
||||
if (lastId !== this.navigation.getActiveItem()) {
|
||||
this.navigation.getActiveContainer().trigger(new $.Event('show'));
|
||||
}
|
||||
this.navigation.getActiveContainer().trigger(new $.Event('urlChanged', params));
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the URL to point to the given dir and view
|
||||
*/
|
||||
_changeUrl: function(view, dir) {
|
||||
var params = {dir: dir};
|
||||
if (view !== 'files') {
|
||||
params.view = view;
|
||||
}
|
||||
OC.Util.History.pushState(params);
|
||||
}
|
||||
};
|
||||
OCA.Files.App = App;
|
||||
})();
|
||||
|
||||
$(document).ready(function() {
|
||||
// wait for other apps/extensions to register their event handlers
|
||||
// in the "ready" clause
|
||||
_.defer(function() {
|
||||
OCA.Files.App.initialize();
|
||||
});
|
||||
});
|
||||
|
|
@ -159,7 +159,11 @@
|
|||
this.totalWidth = 64;
|
||||
// FIXME: this class should not know about global elements
|
||||
if ( $('#navigation').length ) {
|
||||
this.totalWidth += $('#navigation').get(0).offsetWidth;
|
||||
this.totalWidth += $('#navigation').outerWidth();
|
||||
}
|
||||
|
||||
if ( $('#app-navigation').length && !$('#app-navigation').hasClass('hidden')) {
|
||||
this.totalWidth += $('#app-navigation').outerWidth();
|
||||
}
|
||||
this.hiddenBreadcrumbs = 0;
|
||||
|
||||
|
@ -167,8 +171,8 @@
|
|||
this.totalWidth += $(this.breadcrumbs[i]).get(0).offsetWidth;
|
||||
}
|
||||
|
||||
$.each($('#controls .actions>div'), function(index, action) {
|
||||
self.totalWidth += $(action).get(0).offsetWidth;
|
||||
$.each($('#controls .actions'), function(index, action) {
|
||||
self.totalWidth += $(action).outerWidth();
|
||||
});
|
||||
|
||||
},
|
||||
|
@ -236,6 +240,6 @@
|
|||
}
|
||||
};
|
||||
|
||||
window.BreadCrumb = BreadCrumb;
|
||||
OCA.Files.BreadCrumb = BreadCrumb;
|
||||
})();
|
||||
|
||||
|
|
|
@ -346,7 +346,7 @@ OC.Upload = {
|
|||
// noone set update parameters, we set the minimum
|
||||
data.formData = {
|
||||
requesttoken: oc_requesttoken,
|
||||
dir: $('#dir').val(),
|
||||
dir: FileList.getCurrentDirectory(),
|
||||
file_directory: fileDirectory
|
||||
};
|
||||
}
|
||||
|
@ -460,7 +460,6 @@ OC.Upload = {
|
|||
|
||||
$('#uploadprogresswrapper input.stop').fadeOut();
|
||||
$('#uploadprogressbar').fadeOut();
|
||||
Files.updateStorageStatistics();
|
||||
});
|
||||
fileupload.on('fileuploadfail', function(e, data) {
|
||||
OC.Upload.log('progress handle fileuploadfail', e, data);
|
||||
|
@ -471,8 +470,6 @@ OC.Upload = {
|
|||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
console.log('skipping file progress because your browser is broken');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -595,7 +592,7 @@ OC.Upload = {
|
|||
if (FileList.lastAction) {
|
||||
FileList.lastAction();
|
||||
}
|
||||
var name = getUniqueName(newname);
|
||||
var name = FileList.getUniqueName(newname);
|
||||
if (newname !== name) {
|
||||
FileList.checkName(name, newname, true);
|
||||
var hidden = true;
|
||||
|
@ -607,7 +604,7 @@ OC.Upload = {
|
|||
$.post(
|
||||
OC.filePath('files', 'ajax', 'newfile.php'),
|
||||
{
|
||||
dir: $('#dir').val(),
|
||||
dir: FileList.getCurrentDirectory(),
|
||||
filename: name
|
||||
},
|
||||
function(result) {
|
||||
|
@ -623,7 +620,7 @@ OC.Upload = {
|
|||
$.post(
|
||||
OC.filePath('files','ajax','newfolder.php'),
|
||||
{
|
||||
dir: $('#dir').val(),
|
||||
dir: FileList.getCurrentDirectory(),
|
||||
foldername: name
|
||||
},
|
||||
function(result) {
|
||||
|
@ -648,7 +645,7 @@ OC.Upload = {
|
|||
} else { //or the domain
|
||||
localName = (localName.match(/:\/\/(.[^\/]+)/)[1]).replace('www.', '');
|
||||
}
|
||||
localName = getUniqueName(localName);
|
||||
localName = FileList.getUniqueName(localName);
|
||||
//IE < 10 does not fire the necessary events for the progress bar.
|
||||
if ($('html.lte9').length === 0) {
|
||||
$('#uploadprogressbar').progressbar({value: 0});
|
||||
|
@ -658,7 +655,7 @@ OC.Upload = {
|
|||
var eventSource = new OC.EventSource(
|
||||
OC.filePath('files', 'ajax', 'newfile.php'),
|
||||
{
|
||||
dir: $('#dir').val(),
|
||||
dir: FileList.getCurrentDirectory(),
|
||||
source: name,
|
||||
filename: localName
|
||||
}
|
||||
|
|
|
@ -8,242 +8,255 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/* global OC, FileList, Files */
|
||||
/* global trashBinApp */
|
||||
var FileActions = {
|
||||
actions: {},
|
||||
defaults: {},
|
||||
icons: {},
|
||||
currentFile: null,
|
||||
register: function (mime, name, permissions, icon, action, displayName) {
|
||||
if (!FileActions.actions[mime]) {
|
||||
FileActions.actions[mime] = {};
|
||||
}
|
||||
if (!FileActions.actions[mime][name]) {
|
||||
FileActions.actions[mime][name] = {};
|
||||
}
|
||||
if (!displayName) {
|
||||
displayName = t('files', name);
|
||||
}
|
||||
FileActions.actions[mime][name]['action'] = action;
|
||||
FileActions.actions[mime][name]['permissions'] = permissions;
|
||||
FileActions.actions[mime][name]['displayName'] = displayName;
|
||||
FileActions.icons[name] = icon;
|
||||
},
|
||||
setDefault: function (mime, name) {
|
||||
FileActions.defaults[mime] = name;
|
||||
},
|
||||
get: function (mime, type, permissions) {
|
||||
var actions = this.getActions(mime, type, permissions);
|
||||
var filteredActions = {};
|
||||
$.each(actions, function (name, action) {
|
||||
filteredActions[name] = action.action;
|
||||
});
|
||||
return filteredActions;
|
||||
},
|
||||
getActions: function (mime, type, permissions) {
|
||||
var actions = {};
|
||||
if (FileActions.actions.all) {
|
||||
actions = $.extend(actions, FileActions.actions.all);
|
||||
}
|
||||
if (type) {//type is 'dir' or 'file'
|
||||
if (FileActions.actions[type]) {
|
||||
actions = $.extend(actions, FileActions.actions[type]);
|
||||
(function() {
|
||||
|
||||
var FileActions = {
|
||||
actions: {},
|
||||
defaults: {},
|
||||
icons: {},
|
||||
currentFile: null,
|
||||
register: function (mime, name, permissions, icon, action, displayName) {
|
||||
if (!this.actions[mime]) {
|
||||
this.actions[mime] = {};
|
||||
}
|
||||
}
|
||||
if (mime) {
|
||||
var mimePart = mime.substr(0, mime.indexOf('/'));
|
||||
if (FileActions.actions[mimePart]) {
|
||||
actions = $.extend(actions, FileActions.actions[mimePart]);
|
||||
if (!this.actions[mime][name]) {
|
||||
this.actions[mime][name] = {};
|
||||
}
|
||||
if (FileActions.actions[mime]) {
|
||||
actions = $.extend(actions, FileActions.actions[mime]);
|
||||
if (!displayName) {
|
||||
displayName = t('files', name);
|
||||
}
|
||||
}
|
||||
var filteredActions = {};
|
||||
$.each(actions, function (name, action) {
|
||||
if (action.permissions & permissions) {
|
||||
filteredActions[name] = action;
|
||||
this.actions[mime][name]['action'] = action;
|
||||
this.actions[mime][name]['permissions'] = permissions;
|
||||
this.actions[mime][name]['displayName'] = displayName;
|
||||
this.icons[name] = icon;
|
||||
},
|
||||
clear: function() {
|
||||
this.actions = {};
|
||||
this.defaults = {};
|
||||
this.icons = {};
|
||||
this.currentFile = null;
|
||||
},
|
||||
setDefault: function (mime, name) {
|
||||
this.defaults[mime] = name;
|
||||
},
|
||||
get: function (mime, type, permissions) {
|
||||
var actions = this.getActions(mime, type, permissions);
|
||||
var filteredActions = {};
|
||||
$.each(actions, function (name, action) {
|
||||
filteredActions[name] = action.action;
|
||||
});
|
||||
return filteredActions;
|
||||
},
|
||||
getActions: function (mime, type, permissions) {
|
||||
var actions = {};
|
||||
if (this.actions.all) {
|
||||
actions = $.extend(actions, this.actions.all);
|
||||
}
|
||||
});
|
||||
return filteredActions;
|
||||
},
|
||||
getDefault: function (mime, type, permissions) {
|
||||
var mimePart;
|
||||
if (mime) {
|
||||
mimePart = mime.substr(0, mime.indexOf('/'));
|
||||
}
|
||||
var name = false;
|
||||
if (mime && FileActions.defaults[mime]) {
|
||||
name = FileActions.defaults[mime];
|
||||
} else if (mime && FileActions.defaults[mimePart]) {
|
||||
name = FileActions.defaults[mimePart];
|
||||
} else if (type && FileActions.defaults[type]) {
|
||||
name = FileActions.defaults[type];
|
||||
} else {
|
||||
name = FileActions.defaults.all;
|
||||
}
|
||||
var actions = this.get(mime, type, permissions);
|
||||
return actions[name];
|
||||
},
|
||||
/**
|
||||
* Display file actions for the given element
|
||||
* @param parent "td" element of the file for which to display actions
|
||||
* @param triggerEvent if true, triggers the fileActionsReady on the file
|
||||
* list afterwards (false by default)
|
||||
*/
|
||||
display: function (parent, triggerEvent) {
|
||||
FileActions.currentFile = parent;
|
||||
var actions = FileActions.getActions(FileActions.getCurrentMimeType(), FileActions.getCurrentType(), FileActions.getCurrentPermissions());
|
||||
var file = FileActions.getCurrentFile();
|
||||
var nameLinks;
|
||||
if (FileList.findFileEl(file).data('renaming')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// recreate fileactions
|
||||
nameLinks = parent.children('a.name');
|
||||
nameLinks.find('.fileactions, .nametext .action').remove();
|
||||
nameLinks.append('<span class="fileactions" />');
|
||||
var defaultAction = FileActions.getDefault(FileActions.getCurrentMimeType(), FileActions.getCurrentType(), FileActions.getCurrentPermissions());
|
||||
|
||||
var actionHandler = function (event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
FileActions.currentFile = event.data.elem;
|
||||
var file = FileActions.getCurrentFile();
|
||||
|
||||
event.data.actionFunc(file);
|
||||
};
|
||||
|
||||
var addAction = function (name, action, displayName) {
|
||||
|
||||
if ((name === 'Download' || action !== defaultAction) && name !== 'Delete') {
|
||||
|
||||
var img = FileActions.icons[name],
|
||||
actionText = displayName,
|
||||
actionContainer = 'a.name>span.fileactions';
|
||||
|
||||
if (name === 'Rename') {
|
||||
// rename has only an icon which appears behind
|
||||
// the file name
|
||||
actionText = '';
|
||||
actionContainer = 'a.name span.nametext';
|
||||
if (type) {//type is 'dir' or 'file'
|
||||
if (this.actions[type]) {
|
||||
actions = $.extend(actions, this.actions[type]);
|
||||
}
|
||||
}
|
||||
if (mime) {
|
||||
var mimePart = mime.substr(0, mime.indexOf('/'));
|
||||
if (this.actions[mimePart]) {
|
||||
actions = $.extend(actions, this.actions[mimePart]);
|
||||
}
|
||||
if (this.actions[mime]) {
|
||||
actions = $.extend(actions, this.actions[mime]);
|
||||
}
|
||||
}
|
||||
var filteredActions = {};
|
||||
$.each(actions, function (name, action) {
|
||||
if (action.permissions & permissions) {
|
||||
filteredActions[name] = action;
|
||||
}
|
||||
});
|
||||
return filteredActions;
|
||||
},
|
||||
getDefault: function (mime, type, permissions) {
|
||||
var mimePart;
|
||||
if (mime) {
|
||||
mimePart = mime.substr(0, mime.indexOf('/'));
|
||||
}
|
||||
var name = false;
|
||||
if (mime && this.defaults[mime]) {
|
||||
name = this.defaults[mime];
|
||||
} else if (mime && this.defaults[mimePart]) {
|
||||
name = this.defaults[mimePart];
|
||||
} else if (type && this.defaults[type]) {
|
||||
name = this.defaults[type];
|
||||
} else {
|
||||
name = this.defaults.all;
|
||||
}
|
||||
var actions = this.get(mime, type, permissions);
|
||||
return actions[name];
|
||||
},
|
||||
/**
|
||||
* Display file actions for the given element
|
||||
* @param parent "td" element of the file for which to display actions
|
||||
* @param triggerEvent if true, triggers the fileActionsReady on the file
|
||||
* list afterwards (false by default)
|
||||
*/
|
||||
display: function (parent, triggerEvent) {
|
||||
this.currentFile = parent;
|
||||
var self = this;
|
||||
var actions = this.getActions(this.getCurrentMimeType(), this.getCurrentType(), this.getCurrentPermissions());
|
||||
var file = this.getCurrentFile();
|
||||
var nameLinks;
|
||||
if (parent.closest('tr').data('renaming')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// recreate fileactions
|
||||
nameLinks = parent.children('a.name');
|
||||
nameLinks.find('.fileactions, .nametext .action').remove();
|
||||
nameLinks.append('<span class="fileactions" />');
|
||||
var defaultAction = this.getDefault(this.getCurrentMimeType(), this.getCurrentType(), this.getCurrentPermissions());
|
||||
|
||||
var actionHandler = function (event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
self.currentFile = event.data.elem;
|
||||
var file = self.getCurrentFile();
|
||||
|
||||
event.data.actionFunc(file);
|
||||
};
|
||||
|
||||
var addAction = function (name, action, displayName) {
|
||||
|
||||
if ((name === 'Download' || action !== defaultAction) && name !== 'Delete') {
|
||||
|
||||
var img = self.icons[name],
|
||||
actionText = displayName,
|
||||
actionContainer = 'a.name>span.fileactions';
|
||||
|
||||
if (name === 'Rename') {
|
||||
// rename has only an icon which appears behind
|
||||
// the file name
|
||||
actionText = '';
|
||||
actionContainer = 'a.name span.nametext';
|
||||
}
|
||||
if (img.call) {
|
||||
img = img(file);
|
||||
}
|
||||
var html = '<a href="#" class="action action-' + name.toLowerCase() + '" data-action="' + name + '">';
|
||||
if (img) {
|
||||
html += '<img class ="svg" src="' + img + '" />';
|
||||
}
|
||||
html += '<span> ' + actionText + '</span></a>';
|
||||
|
||||
var element = $(html);
|
||||
element.data('action', name);
|
||||
element.on('click', {a: null, elem: parent, actionFunc: actions[name].action}, actionHandler);
|
||||
parent.find(actionContainer).append(element);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$.each(actions, function (name, action) {
|
||||
if (name !== 'Share') {
|
||||
displayName = action.displayName;
|
||||
ah = action.action;
|
||||
|
||||
addAction(name, ah, displayName);
|
||||
}
|
||||
});
|
||||
if(actions.Share){
|
||||
displayName = t('files', 'Share');
|
||||
addAction('Share', actions.Share, displayName);
|
||||
}
|
||||
|
||||
// remove the existing delete action
|
||||
parent.parent().children().last().find('.action.delete').remove();
|
||||
if (actions['Delete']) {
|
||||
var img = self.icons['Delete'];
|
||||
var html;
|
||||
if (img.call) {
|
||||
img = img(file);
|
||||
}
|
||||
var html = '<a href="#" class="action action-' + name.toLowerCase() + '" data-action="' + name + '">';
|
||||
if (img) {
|
||||
html += '<img class ="svg" src="' + img + '" />';
|
||||
if (typeof trashBinApp !== 'undefined' && trashBinApp) {
|
||||
html = '<a href="#" original-title="' + t('files', 'Delete permanently') + '" class="action delete delete-icon" />';
|
||||
} else {
|
||||
html = '<a href="#" class="action delete delete-icon" />';
|
||||
}
|
||||
html += '<span> ' + actionText + '</span></a>';
|
||||
|
||||
var element = $(html);
|
||||
element.data('action', name);
|
||||
element.on('click', {a: null, elem: parent, actionFunc: actions[name].action}, actionHandler);
|
||||
parent.find(actionContainer).append(element);
|
||||
element.data('action', actions['Delete']);
|
||||
element.on('click', {a: null, elem: parent, actionFunc: actions['Delete'].action}, actionHandler);
|
||||
parent.parent().children().last().append(element);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$.each(actions, function (name, action) {
|
||||
if (name !== 'Share') {
|
||||
displayName = action.displayName;
|
||||
ah = action.action;
|
||||
|
||||
addAction(name, ah, displayName);
|
||||
if (triggerEvent){
|
||||
$('#fileList').trigger(jQuery.Event("fileActionsReady"));
|
||||
}
|
||||
});
|
||||
if(actions.Share){
|
||||
displayName = t('files', 'Share');
|
||||
addAction('Share', actions.Share, displayName);
|
||||
}
|
||||
},
|
||||
getCurrentFile: function () {
|
||||
return this.currentFile.parent().attr('data-file');
|
||||
},
|
||||
getCurrentMimeType: function () {
|
||||
return this.currentFile.parent().attr('data-mime');
|
||||
},
|
||||
getCurrentType: function () {
|
||||
return this.currentFile.parent().attr('data-type');
|
||||
},
|
||||
getCurrentPermissions: function () {
|
||||
return this.currentFile.parent().data('permissions');
|
||||
},
|
||||
|
||||
// remove the existing delete action
|
||||
parent.parent().children().last().find('.action.delete').remove();
|
||||
if (actions['Delete']) {
|
||||
var img = FileActions.icons['Delete'];
|
||||
var html;
|
||||
if (img.call) {
|
||||
img = img(file);
|
||||
}
|
||||
if (typeof trashBinApp !== 'undefined' && trashBinApp) {
|
||||
html = '<a href="#" original-title="' + t('files', 'Delete permanently') + '" class="action delete delete-icon" />';
|
||||
/**
|
||||
* Register the actions that are used by default for the files app.
|
||||
*/
|
||||
registerDefaultActions: function(fileList) {
|
||||
// TODO: try to find a way to not make it depend on fileList,
|
||||
// maybe get a handler or listener to trigger events on
|
||||
this.register('all', 'Delete', OC.PERMISSION_DELETE, function () {
|
||||
return OC.imagePath('core', 'actions/delete');
|
||||
}, function (filename) {
|
||||
fileList.do_delete(filename);
|
||||
$('.tipsy').remove();
|
||||
});
|
||||
|
||||
// t('files', 'Rename')
|
||||
this.register('all', 'Rename', OC.PERMISSION_UPDATE, function () {
|
||||
return OC.imagePath('core', 'actions/rename');
|
||||
}, function (filename) {
|
||||
fileList.rename(filename);
|
||||
});
|
||||
|
||||
this.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename) {
|
||||
var dir = fileList.getCurrentDirectory();
|
||||
if (dir !== '/') {
|
||||
dir = dir + '/';
|
||||
}
|
||||
fileList.changeDirectory(dir + filename);
|
||||
});
|
||||
|
||||
this.setDefault('dir', 'Open');
|
||||
var downloadScope;
|
||||
if ($('#allowZipDownload').val() == 1) {
|
||||
downloadScope = 'all';
|
||||
} else {
|
||||
html = '<a href="#" class="action delete delete-icon" />';
|
||||
downloadScope = 'file';
|
||||
}
|
||||
var element = $(html);
|
||||
element.data('action', actions['Delete']);
|
||||
element.on('click', {a: null, elem: parent, actionFunc: actions['Delete'].action}, actionHandler);
|
||||
parent.parent().children().last().append(element);
|
||||
|
||||
this.register(downloadScope, 'Download', OC.PERMISSION_READ, function () {
|
||||
return OC.imagePath('core', 'actions/download');
|
||||
}, function (filename) {
|
||||
var url = OCA.Files.Files.getDownloadUrl(filename, fileList.getCurrentDirectory());
|
||||
if (url) {
|
||||
OC.redirect(url);
|
||||
}
|
||||
});
|
||||
|
||||
fileList.$fileList.trigger(jQuery.Event("fileActionsReady"));
|
||||
}
|
||||
};
|
||||
|
||||
if (triggerEvent){
|
||||
$('#fileList').trigger(jQuery.Event("fileActionsReady"));
|
||||
}
|
||||
},
|
||||
getCurrentFile: function () {
|
||||
return FileActions.currentFile.parent().attr('data-file');
|
||||
},
|
||||
getCurrentMimeType: function () {
|
||||
return FileActions.currentFile.parent().attr('data-mime');
|
||||
},
|
||||
getCurrentType: function () {
|
||||
return FileActions.currentFile.parent().attr('data-type');
|
||||
},
|
||||
getCurrentPermissions: function () {
|
||||
return FileActions.currentFile.parent().data('permissions');
|
||||
}
|
||||
};
|
||||
OCA.Files.FileActions = FileActions;
|
||||
})();
|
||||
|
||||
$(document).ready(function () {
|
||||
var downloadScope;
|
||||
if ($('#allowZipDownload').val() == 1) {
|
||||
downloadScope = 'all';
|
||||
} else {
|
||||
downloadScope = 'file';
|
||||
}
|
||||
// for backward compatibility
|
||||
window.FileActions = OCA.Files.FileActions;
|
||||
|
||||
if (typeof disableDownloadActions == 'undefined' || !disableDownloadActions) {
|
||||
FileActions.register(downloadScope, 'Download', OC.PERMISSION_READ, function () {
|
||||
return OC.imagePath('core', 'actions/download');
|
||||
}, function (filename) {
|
||||
var url = Files.getDownloadUrl(filename);
|
||||
if (url) {
|
||||
OC.redirect(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
$('#fileList tr').each(function () {
|
||||
FileActions.display($(this).children('td.filename'));
|
||||
});
|
||||
|
||||
$('#fileList').trigger(jQuery.Event("fileActionsReady"));
|
||||
|
||||
});
|
||||
|
||||
FileActions.register('all', 'Delete', OC.PERMISSION_DELETE, function () {
|
||||
return OC.imagePath('core', 'actions/delete');
|
||||
}, function (filename) {
|
||||
FileList.do_delete(filename);
|
||||
$('.tipsy').remove();
|
||||
});
|
||||
|
||||
// t('files', 'Rename')
|
||||
FileActions.register('all', 'Rename', OC.PERMISSION_UPDATE, function () {
|
||||
return OC.imagePath('core', 'actions/rename');
|
||||
}, function (filename) {
|
||||
FileList.rename(filename);
|
||||
});
|
||||
|
||||
FileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename) {
|
||||
var dir = $('#dir').val() || '/';
|
||||
if (dir !== '/') {
|
||||
dir = dir + '/';
|
||||
}
|
||||
FileList.changeDirectory(dir + filename);
|
||||
});
|
||||
|
||||
FileActions.setDefault('dir', 'Open');
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,257 +8,296 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/* global OC, t, FileList */
|
||||
/* global getURLParameter */
|
||||
var Files = {
|
||||
// file space size sync
|
||||
_updateStorageStatistics: function() {
|
||||
Files._updateStorageStatisticsTimeout = null;
|
||||
var currentDir = FileList.getCurrentDirectory(),
|
||||
state = Files.updateStorageStatistics;
|
||||
if (state.dir){
|
||||
if (state.dir === currentDir) {
|
||||
/**
|
||||
* Utility class for file related operations
|
||||
*/
|
||||
(function() {
|
||||
var Files = {
|
||||
// file space size sync
|
||||
_updateStorageStatistics: function(currentDir) {
|
||||
var state = Files.updateStorageStatistics;
|
||||
if (state.dir){
|
||||
if (state.dir === currentDir) {
|
||||
return;
|
||||
}
|
||||
// cancel previous call, as it was for another dir
|
||||
state.call.abort();
|
||||
}
|
||||
state.dir = currentDir;
|
||||
state.call = $.getJSON(OC.filePath('files','ajax','getstoragestats.php') + '?dir=' + encodeURIComponent(currentDir),function(response) {
|
||||
state.dir = null;
|
||||
state.call = null;
|
||||
Files.updateMaxUploadFilesize(response);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Update storage statistics such as free space, max upload,
|
||||
* etc based on the given directory.
|
||||
*
|
||||
* Note this function is debounced to avoid making too
|
||||
* many ajax calls in a row.
|
||||
*
|
||||
* @param dir directory
|
||||
* @param force whether to force retrieving
|
||||
*/
|
||||
updateStorageStatistics: function(dir, force) {
|
||||
if (!OC.currentUser) {
|
||||
return;
|
||||
}
|
||||
// cancel previous call, as it was for another dir
|
||||
state.call.abort();
|
||||
}
|
||||
state.dir = currentDir;
|
||||
state.call = $.getJSON(OC.filePath('files','ajax','getstoragestats.php') + '?dir=' + encodeURIComponent(currentDir),function(response) {
|
||||
state.dir = null;
|
||||
state.call = null;
|
||||
Files.updateMaxUploadFilesize(response);
|
||||
});
|
||||
},
|
||||
updateStorageStatistics: function(force) {
|
||||
if (!OC.currentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
// debounce to prevent calling too often
|
||||
if (Files._updateStorageStatisticsTimeout) {
|
||||
clearTimeout(Files._updateStorageStatisticsTimeout);
|
||||
}
|
||||
if (force) {
|
||||
Files._updateStorageStatistics();
|
||||
}
|
||||
else {
|
||||
Files._updateStorageStatisticsTimeout = setTimeout(Files._updateStorageStatistics, 250);
|
||||
}
|
||||
},
|
||||
|
||||
updateMaxUploadFilesize:function(response) {
|
||||
if (response === undefined) {
|
||||
return;
|
||||
}
|
||||
if (response.data !== undefined && response.data.uploadMaxFilesize !== undefined) {
|
||||
$('#max_upload').val(response.data.uploadMaxFilesize);
|
||||
$('#free_space').val(response.data.freeSpace);
|
||||
$('#upload.button').attr('original-title', response.data.maxHumanFilesize);
|
||||
$('#usedSpacePercent').val(response.data.usedSpacePercent);
|
||||
Files.displayStorageWarnings();
|
||||
}
|
||||
if (response[0] === undefined) {
|
||||
return;
|
||||
}
|
||||
if (response[0].uploadMaxFilesize !== undefined) {
|
||||
$('#max_upload').val(response[0].uploadMaxFilesize);
|
||||
$('#upload.button').attr('original-title', response[0].maxHumanFilesize);
|
||||
$('#usedSpacePercent').val(response[0].usedSpacePercent);
|
||||
Files.displayStorageWarnings();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Fix path name by removing double slash at the beginning, if any
|
||||
*/
|
||||
fixPath: function(fileName) {
|
||||
if (fileName.substr(0, 2) == '//') {
|
||||
return fileName.substr(1);
|
||||
}
|
||||
return fileName;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether the given file name is valid.
|
||||
* @param name file name to check
|
||||
* @return true if the file name is valid.
|
||||
* Throws a string exception with an error message if
|
||||
* the file name is not valid
|
||||
*/
|
||||
isFileNameValid: function (name) {
|
||||
var trimmedName = name.trim();
|
||||
if (trimmedName === '.' || trimmedName === '..')
|
||||
{
|
||||
throw t('files', '"{name}" is an invalid file name.', {name: name});
|
||||
} else if (trimmedName.length === 0) {
|
||||
throw t('files', 'File name cannot be empty.');
|
||||
}
|
||||
// check for invalid characters
|
||||
var invalidCharacters =
|
||||
['\\', '/', '<', '>', ':', '"', '|', '?', '*', '\n'];
|
||||
for (var i = 0; i < invalidCharacters.length; i++) {
|
||||
if (trimmedName.indexOf(invalidCharacters[i]) !== -1) {
|
||||
throw t('files', "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed.");
|
||||
if (force) {
|
||||
Files._updateStorageStatistics(dir);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
displayStorageWarnings: function() {
|
||||
if (!OC.Notification.isHidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var usedSpacePercent = $('#usedSpacePercent').val();
|
||||
if (usedSpacePercent > 98) {
|
||||
OC.Notification.show(t('files', 'Your storage is full, files can not be updated or synced anymore!'));
|
||||
return;
|
||||
}
|
||||
if (usedSpacePercent > 90) {
|
||||
OC.Notification.show(t('files', 'Your storage is almost full ({usedSpacePercent}%)',
|
||||
{usedSpacePercent: usedSpacePercent}));
|
||||
}
|
||||
},
|
||||
|
||||
displayEncryptionWarning: function() {
|
||||
|
||||
if (!OC.Notification.isHidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var encryptedFiles = $('#encryptedFiles').val();
|
||||
var initStatus = $('#encryptionInitStatus').val();
|
||||
if (initStatus === '0') { // enc not initialized, but should be
|
||||
OC.Notification.show(t('files', 'Encryption App is enabled but your keys are not initialized, please log-out and log-in again'));
|
||||
return;
|
||||
}
|
||||
if (initStatus === '1') { // encryption tried to init but failed
|
||||
OC.Notification.show(t('files', 'Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files.'));
|
||||
return;
|
||||
}
|
||||
if (encryptedFiles === '1') {
|
||||
OC.Notification.show(t('files', 'Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files.'));
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: move to FileList class
|
||||
setupDragAndDrop: function() {
|
||||
var $fileList = $('#fileList');
|
||||
|
||||
//drag/drop of files
|
||||
$fileList.find('tr td.filename').each(function(i,e) {
|
||||
if ($(e).parent().data('permissions') & OC.PERMISSION_DELETE) {
|
||||
$(e).draggable(dragOptions);
|
||||
else {
|
||||
Files._updateStorageStatisticsDebounced(dir);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
$fileList.find('tr[data-type="dir"] td.filename').each(function(i,e) {
|
||||
if ($(e).parent().data('permissions') & OC.PERMISSION_CREATE) {
|
||||
$(e).droppable(folderDropOptions);
|
||||
updateMaxUploadFilesize:function(response) {
|
||||
if (response === undefined) {
|
||||
return;
|
||||
}
|
||||
if (response.data !== undefined && response.data.uploadMaxFilesize !== undefined) {
|
||||
$('#max_upload').val(response.data.uploadMaxFilesize);
|
||||
$('#free_space').val(response.data.freeSpace);
|
||||
$('#upload.button').attr('original-title', response.data.maxHumanFilesize);
|
||||
$('#usedSpacePercent').val(response.data.usedSpacePercent);
|
||||
Files.displayStorageWarnings();
|
||||
}
|
||||
if (response[0] === undefined) {
|
||||
return;
|
||||
}
|
||||
if (response[0].uploadMaxFilesize !== undefined) {
|
||||
$('#max_upload').val(response[0].uploadMaxFilesize);
|
||||
$('#upload.button').attr('original-title', response[0].maxHumanFilesize);
|
||||
$('#usedSpacePercent').val(response[0].usedSpacePercent);
|
||||
Files.displayStorageWarnings();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the download URL of the given file(s)
|
||||
* @param filename string or array of file names to download
|
||||
* @param dir optional directory in which the file name is, defaults to the current directory
|
||||
*/
|
||||
getDownloadUrl: function(filename, dir) {
|
||||
if ($.isArray(filename)) {
|
||||
filename = JSON.stringify(filename);
|
||||
}
|
||||
var params = {
|
||||
dir: dir || FileList.getCurrentDirectory(),
|
||||
files: filename
|
||||
};
|
||||
return this.getAjaxUrl('download', params);
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the ajax URL for a given action
|
||||
* @param action action string
|
||||
* @param params optional params map
|
||||
*/
|
||||
getAjaxUrl: function(action, params) {
|
||||
var q = '';
|
||||
if (params) {
|
||||
q = '?' + OC.buildQueryString(params);
|
||||
}
|
||||
return OC.filePath('files', 'ajax', action + '.php') + q;
|
||||
}
|
||||
};
|
||||
$(document).ready(function() {
|
||||
// FIXME: workaround for trashbin app
|
||||
if (window.trashBinApp) {
|
||||
return;
|
||||
}
|
||||
Files.displayEncryptionWarning();
|
||||
Files.bindKeyboardShortcuts(document, jQuery);
|
||||
/**
|
||||
* Fix path name by removing double slash at the beginning, if any
|
||||
*/
|
||||
fixPath: function(fileName) {
|
||||
if (fileName.substr(0, 2) == '//') {
|
||||
return fileName.substr(1);
|
||||
}
|
||||
return fileName;
|
||||
},
|
||||
|
||||
Files.setupDragAndDrop();
|
||||
/**
|
||||
* Checks whether the given file name is valid.
|
||||
* @param name file name to check
|
||||
* @return true if the file name is valid.
|
||||
* Throws a string exception with an error message if
|
||||
* the file name is not valid
|
||||
*/
|
||||
isFileNameValid: function (name) {
|
||||
var trimmedName = name.trim();
|
||||
if (trimmedName === '.' || trimmedName === '..')
|
||||
{
|
||||
throw t('files', '"{name}" is an invalid file name.', {name: name});
|
||||
} else if (trimmedName.length === 0) {
|
||||
throw t('files', 'File name cannot be empty.');
|
||||
}
|
||||
// check for invalid characters
|
||||
var invalidCharacters =
|
||||
['\\', '/', '<', '>', ':', '"', '|', '?', '*', '\n'];
|
||||
for (var i = 0; i < invalidCharacters.length; i++) {
|
||||
if (trimmedName.indexOf(invalidCharacters[i]) !== -1) {
|
||||
throw t('files', "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed.");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
displayStorageWarnings: function() {
|
||||
if (!OC.Notification.isHidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$('#file_action_panel').attr('activeAction', false);
|
||||
var usedSpacePercent = $('#usedSpacePercent').val();
|
||||
if (usedSpacePercent > 98) {
|
||||
OC.Notification.show(t('files', 'Your storage is full, files can not be updated or synced anymore!'));
|
||||
return;
|
||||
}
|
||||
if (usedSpacePercent > 90) {
|
||||
OC.Notification.show(t('files', 'Your storage is almost full ({usedSpacePercent}%)',
|
||||
{usedSpacePercent: usedSpacePercent}));
|
||||
}
|
||||
},
|
||||
|
||||
// Triggers invisible file input
|
||||
$('#upload a').on('click', function() {
|
||||
$(this).parent().children('#file_upload_start').trigger('click');
|
||||
return false;
|
||||
});
|
||||
displayEncryptionWarning: function() {
|
||||
|
||||
// Trigger cancelling of file upload
|
||||
$('#uploadprogresswrapper .stop').on('click', function() {
|
||||
OC.Upload.cancelUploads();
|
||||
FileList.updateSelectionSummary();
|
||||
});
|
||||
if (!OC.Notification.isHidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show trash bin
|
||||
$('#trash').on('click', function() {
|
||||
window.location=OC.filePath('files_trashbin', '', 'index.php');
|
||||
});
|
||||
var encryptedFiles = $('#encryptedFiles').val();
|
||||
var initStatus = $('#encryptionInitStatus').val();
|
||||
if (initStatus === '0') { // enc not initialized, but should be
|
||||
OC.Notification.show(t('files', 'Encryption App is enabled but your keys are not initialized, please log-out and log-in again'));
|
||||
return;
|
||||
}
|
||||
if (initStatus === '1') { // encryption tried to init but failed
|
||||
OC.Notification.show(t('files', 'Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files.'));
|
||||
return;
|
||||
}
|
||||
if (encryptedFiles === '1') {
|
||||
OC.Notification.show(t('files', 'Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files.'));
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
// drag&drop support using jquery.fileupload
|
||||
// TODO use OC.dialogs
|
||||
$(document).bind('drop dragover', function (e) {
|
||||
e.preventDefault(); // prevent browser from doing anything, if file isn't dropped in dropZone
|
||||
});
|
||||
/**
|
||||
* Returns the download URL of the given file(s)
|
||||
* @param filename string or array of file names to download
|
||||
* @param dir optional directory in which the file name is, defaults to the current directory
|
||||
*/
|
||||
getDownloadUrl: function(filename, dir) {
|
||||
if ($.isArray(filename)) {
|
||||
filename = JSON.stringify(filename);
|
||||
}
|
||||
var params = {
|
||||
dir: dir,
|
||||
files: filename
|
||||
};
|
||||
return this.getAjaxUrl('download', params);
|
||||
},
|
||||
|
||||
//do a background scan if needed
|
||||
scanFiles();
|
||||
/**
|
||||
* Returns the ajax URL for a given action
|
||||
* @param action action string
|
||||
* @param params optional params map
|
||||
*/
|
||||
getAjaxUrl: function(action, params) {
|
||||
var q = '';
|
||||
if (params) {
|
||||
q = '?' + OC.buildQueryString(params);
|
||||
}
|
||||
return OC.filePath('files', 'ajax', action + '.php') + q;
|
||||
},
|
||||
|
||||
// display storage warnings
|
||||
setTimeout(Files.displayStorageWarnings, 100);
|
||||
OC.Notification.setDefault(Files.displayStorageWarnings);
|
||||
|
||||
// only possible at the moment if user is logged in
|
||||
if (OC.currentUser) {
|
||||
// start on load - we ask the server every 5 minutes
|
||||
var updateStorageStatisticsInterval = 5*60*1000;
|
||||
var updateStorageStatisticsIntervalId = setInterval(Files.updateStorageStatistics, updateStorageStatisticsInterval);
|
||||
|
||||
// Use jquery-visibility to de-/re-activate file stats sync
|
||||
if ($.support.pageVisibility) {
|
||||
$(document).on({
|
||||
'show.visibility': function() {
|
||||
if (!updateStorageStatisticsIntervalId) {
|
||||
updateStorageStatisticsIntervalId = setInterval(Files.updateStorageStatistics, updateStorageStatisticsInterval);
|
||||
getMimeIcon: function(mime, ready) {
|
||||
if (Files.getMimeIcon.cache[mime]) {
|
||||
ready(Files.getMimeIcon.cache[mime]);
|
||||
} else {
|
||||
$.get( OC.filePath('files','ajax','mimeicon.php'), {mime: mime}, function(path) {
|
||||
if(OC.Util.hasSVGSupport()){
|
||||
path = path.substr(0, path.length-4) + '.svg';
|
||||
}
|
||||
},
|
||||
'hide.visibility': function() {
|
||||
clearInterval(updateStorageStatisticsIntervalId);
|
||||
updateStorageStatisticsIntervalId = 0;
|
||||
Files.getMimeIcon.cache[mime]=path;
|
||||
ready(Files.getMimeIcon.cache[mime]);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Generates a preview URL based on the URL space.
|
||||
* @param urlSpec map with {x: width, y: height, file: file path}
|
||||
* @return preview URL
|
||||
* @deprecated used OCA.Files.FileList.generatePreviewUrl instead
|
||||
*/
|
||||
generatePreviewUrl: function(urlSpec) {
|
||||
console.warn('DEPRECATED: please use generatePreviewUrl() from an OCA.Files.FileList instance');
|
||||
return OCA.Files.App.fileList.generatePreviewUrl(urlSpec);
|
||||
},
|
||||
|
||||
/**
|
||||
* Lazy load preview
|
||||
* @deprecated used OCA.Files.FileList.lazyLoadPreview instead
|
||||
*/
|
||||
lazyLoadPreview : function(path, mime, ready, width, height, etag) {
|
||||
console.warn('DEPRECATED: please use lazyLoadPreview() from an OCA.Files.FileList instance');
|
||||
return OCA.Files.App.fileList.lazyLoadPreview({
|
||||
path: path,
|
||||
mime: mime,
|
||||
callback: ready,
|
||||
width: width,
|
||||
height: height,
|
||||
etag: etag
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the files view
|
||||
*/
|
||||
initialize: function() {
|
||||
Files.getMimeIcon.cache = {};
|
||||
Files.displayEncryptionWarning();
|
||||
Files.bindKeyboardShortcuts(document, $);
|
||||
|
||||
// TODO: move file list related code (upload) to OCA.Files.FileList
|
||||
$('#file_action_panel').attr('activeAction', false);
|
||||
|
||||
// Triggers invisible file input
|
||||
$('#upload a').on('click', function() {
|
||||
$(this).parent().children('#file_upload_start').trigger('click');
|
||||
return false;
|
||||
});
|
||||
|
||||
// Trigger cancelling of file upload
|
||||
$('#uploadprogresswrapper .stop').on('click', function() {
|
||||
OC.Upload.cancelUploads();
|
||||
});
|
||||
|
||||
// drag&drop support using jquery.fileupload
|
||||
// TODO use OC.dialogs
|
||||
$(document).bind('drop dragover', function (e) {
|
||||
e.preventDefault(); // prevent browser from doing anything, if file isn't dropped in dropZone
|
||||
});
|
||||
|
||||
//do a background scan if needed
|
||||
scanFiles();
|
||||
|
||||
// display storage warnings
|
||||
setTimeout(Files.displayStorageWarnings, 100);
|
||||
OC.Notification.setDefault(Files.displayStorageWarnings);
|
||||
|
||||
// only possible at the moment if user is logged in or the files app is loaded
|
||||
if (OC.currentUser && OCA.Files.App) {
|
||||
// start on load - we ask the server every 5 minutes
|
||||
var updateStorageStatisticsInterval = 5*60*1000;
|
||||
var updateStorageStatisticsIntervalId = setInterval(OCA.Files.App.fileList.updateStorageStatistics, updateStorageStatisticsInterval);
|
||||
|
||||
// TODO: this should also stop when switching to another view
|
||||
// Use jquery-visibility to de-/re-activate file stats sync
|
||||
if ($.support.pageVisibility) {
|
||||
$(document).on({
|
||||
'show.visibility': function() {
|
||||
if (!updateStorageStatisticsIntervalId) {
|
||||
updateStorageStatisticsIntervalId = setInterval(OCA.Files.App.fileList.updateStorageStatistics, updateStorageStatisticsInterval);
|
||||
}
|
||||
},
|
||||
'hide.visibility': function() {
|
||||
clearInterval(updateStorageStatisticsIntervalId);
|
||||
updateStorageStatisticsIntervalId = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$('#app-settings-header').on('click', function() {
|
||||
var $settings = $('#app-settings');
|
||||
$settings.toggleClass('opened');
|
||||
if ($settings.hasClass('opened')) {
|
||||
$settings.find('input').focus();
|
||||
}
|
||||
});
|
||||
|
||||
//scroll to and highlight preselected file
|
||||
/*
|
||||
if (getURLParameter('scrollto')) {
|
||||
FileList.scrollTo(getURLParameter('scrollto'));
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
//scroll to and highlight preselected file
|
||||
if (getURLParameter('scrollto')) {
|
||||
FileList.scrollTo(getURLParameter('scrollto'));
|
||||
}
|
||||
});
|
||||
Files._updateStorageStatisticsDebounced = _.debounce(Files._updateStorageStatistics, 250);
|
||||
OCA.Files.Files = Files;
|
||||
})();
|
||||
|
||||
function scanFiles(force, dir, users) {
|
||||
if (!OC.currentUser) {
|
||||
|
@ -292,7 +331,9 @@ function scanFiles(force, dir, users) {
|
|||
scannerEventSource.listen('done',function(count) {
|
||||
scanFiles.scanning=false;
|
||||
console.log('done after ' + count + ' files');
|
||||
Files.updateStorageStatistics();
|
||||
if (OCA.Files.App) {
|
||||
OCA.Files.App.fileList.updateStorageStatistics(true);
|
||||
}
|
||||
});
|
||||
scannerEventSource.listen('user',function(user) {
|
||||
console.log('scanning files for ' + user);
|
||||
|
@ -303,6 +344,7 @@ scanFiles.scanning=false;
|
|||
// TODO: move to FileList
|
||||
var createDragShadow = function(event) {
|
||||
//select dragged file
|
||||
var FileList = OCA.Files.App.fileList;
|
||||
var isDragSelected = $(event.target).parents('tr').find('td input:first').prop('checked');
|
||||
if (!isDragSelected) {
|
||||
//select dragged file
|
||||
|
@ -323,7 +365,7 @@ var createDragShadow = function(event) {
|
|||
var tbody = $('<tbody></tbody>');
|
||||
dragshadow.append(tbody);
|
||||
|
||||
var dir=$('#dir').val();
|
||||
var dir = FileList.getCurrentDirectory();
|
||||
|
||||
$(selectedFiles).each(function(i,elem) {
|
||||
var newtr = $('<tr/>')
|
||||
|
@ -336,8 +378,8 @@ var createDragShadow = function(event) {
|
|||
if (elem.type === 'dir') {
|
||||
newtr.find('td.filename').attr('style','background-image:url('+OC.imagePath('core', 'filetypes/folder.png')+')');
|
||||
} else {
|
||||
var path = getPathForPreview(elem.name);
|
||||
Files.lazyLoadPreview(path, elem.mime, function(previewpath) {
|
||||
var path = dir + '/' + elem.name;
|
||||
OCA.Files.App.files.lazyLoadPreview(path, elem.mime, function(previewpath) {
|
||||
newtr.find('td.filename').attr('style','background-image:url('+previewpath+')');
|
||||
}, null, null, elem.etag);
|
||||
}
|
||||
|
@ -350,9 +392,14 @@ var createDragShadow = function(event) {
|
|||
//start&stop handlers needs some cleaning up
|
||||
// TODO: move to FileList class
|
||||
var dragOptions={
|
||||
revert: 'invalid', revertDuration: 300,
|
||||
opacity: 0.7, zIndex: 100, appendTo: 'body', cursorAt: { left: 24, top: 18 },
|
||||
helper: createDragShadow, cursor: 'move',
|
||||
revert: 'invalid',
|
||||
revertDuration: 300,
|
||||
opacity: 0.7,
|
||||
zIndex: 100,
|
||||
appendTo: 'body',
|
||||
cursorAt: { left: 24, top: 18 },
|
||||
helper: createDragShadow,
|
||||
cursor: 'move',
|
||||
start: function(event, ui){
|
||||
var $selectedFiles = $('td.filename input:checkbox:checked');
|
||||
if($selectedFiles.length > 1){
|
||||
|
@ -383,6 +430,7 @@ var folderDropOptions = {
|
|||
hoverClass: "canDrop",
|
||||
drop: function( event, ui ) {
|
||||
// don't allow moving a file into a selected folder
|
||||
var FileList = OCA.Files.App.fileList;
|
||||
if ($(event.target).parents('tr').find('td input:first').prop('checked') === true) {
|
||||
return false;
|
||||
}
|
||||
|
@ -400,115 +448,11 @@ var folderDropOptions = {
|
|||
tolerance: 'pointer'
|
||||
};
|
||||
|
||||
Files.getMimeIcon = function(mime, ready) {
|
||||
if (Files.getMimeIcon.cache[mime]) {
|
||||
ready(Files.getMimeIcon.cache[mime]);
|
||||
} else {
|
||||
$.get( OC.filePath('files','ajax','mimeicon.php'), {mime: mime}, function(path) {
|
||||
if(OC.Util.hasSVGSupport()){
|
||||
path = path.substr(0, path.length-4) + '.svg';
|
||||
}
|
||||
Files.getMimeIcon.cache[mime]=path;
|
||||
ready(Files.getMimeIcon.cache[mime]);
|
||||
});
|
||||
}
|
||||
}
|
||||
Files.getMimeIcon.cache={};
|
||||
|
||||
function getPathForPreview(name) {
|
||||
var path = $('#dir').val() + '/' + name;
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a preview URL based on the URL space.
|
||||
* @param urlSpec map with {x: width, y: height, file: file path}
|
||||
* @return preview URL
|
||||
*/
|
||||
Files.generatePreviewUrl = function(urlSpec) {
|
||||
urlSpec = urlSpec || {};
|
||||
if (!urlSpec.x) {
|
||||
urlSpec.x = $('#filestable').data('preview-x');
|
||||
}
|
||||
if (!urlSpec.y) {
|
||||
urlSpec.y = $('#filestable').data('preview-y');
|
||||
}
|
||||
urlSpec.y *= window.devicePixelRatio;
|
||||
urlSpec.x *= window.devicePixelRatio;
|
||||
urlSpec.forceIcon = 0;
|
||||
return OC.generateUrl('/core/preview.png?') + $.param(urlSpec);
|
||||
};
|
||||
|
||||
Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) {
|
||||
// get mime icon url
|
||||
Files.getMimeIcon(mime, function(iconURL) {
|
||||
var previewURL,
|
||||
urlSpec = {};
|
||||
ready(iconURL); // set mimeicon URL
|
||||
|
||||
urlSpec.file = Files.fixPath(path);
|
||||
|
||||
if (etag){
|
||||
// use etag as cache buster
|
||||
urlSpec.c = etag;
|
||||
}
|
||||
else {
|
||||
console.warn('Files.lazyLoadPreview(): missing etag argument');
|
||||
}
|
||||
|
||||
previewURL = Files.generatePreviewUrl(urlSpec);
|
||||
previewURL = previewURL.replace('(', '%28');
|
||||
previewURL = previewURL.replace(')', '%29');
|
||||
|
||||
// preload image to prevent delay
|
||||
// this will make the browser cache the image
|
||||
var img = new Image();
|
||||
img.onload = function(){
|
||||
// if loading the preview image failed (no preview for the mimetype) then img.width will < 5
|
||||
if (img.width > 5) {
|
||||
ready(previewURL);
|
||||
}
|
||||
};
|
||||
img.src = previewURL;
|
||||
});
|
||||
};
|
||||
|
||||
function getUniqueName(name) {
|
||||
if (FileList.findFileEl(name).exists()) {
|
||||
var numMatch;
|
||||
var parts=name.split('.');
|
||||
var extension = "";
|
||||
if (parts.length > 1) {
|
||||
extension=parts.pop();
|
||||
}
|
||||
var base=parts.join('.');
|
||||
numMatch=base.match(/\((\d+)\)/);
|
||||
var num=2;
|
||||
if (numMatch && numMatch.length>0) {
|
||||
num=parseInt(numMatch[numMatch.length-1])+1;
|
||||
base=base.split('(');
|
||||
base.pop();
|
||||
base=$.trim(base.join('('));
|
||||
}
|
||||
name=base+' ('+num+')';
|
||||
if (extension) {
|
||||
name = name+'.'+extension;
|
||||
}
|
||||
return getUniqueName(name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
function checkTrashStatus() {
|
||||
$.post(OC.filePath('files_trashbin', 'ajax', 'isEmpty.php'), function(result) {
|
||||
if (result.data.isEmpty === false) {
|
||||
$("input[type=button][id=trash]").removeAttr("disabled");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// override core's fileDownloadPath (legacy)
|
||||
function fileDownloadPath(dir, file) {
|
||||
return Files.getDownloadUrl(file, dir);
|
||||
return OCA.Files.Files.getDownloadUrl(file, dir);
|
||||
}
|
||||
|
||||
// for backward compatibility
|
||||
window.Files = OCA.Files.Files;
|
||||
|
||||
|
|
|
@ -190,6 +190,6 @@
|
|||
this.$el.append($summary);
|
||||
}
|
||||
};
|
||||
window.FileSummary = FileSummary;
|
||||
OCA.Files.FileSummary = FileSummary;
|
||||
})();
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
* enter: open file/folder
|
||||
* delete/backspace: delete file/folder
|
||||
*****************************/
|
||||
var Files = Files || {};
|
||||
(function(Files) {
|
||||
var keys = [];
|
||||
var keyCodes = {
|
||||
|
@ -167,4 +166,4 @@ var Files = Files || {};
|
|||
removeA(keys, event.keyCode);
|
||||
});
|
||||
};
|
||||
})(Files);
|
||||
})((OCA.Files && OCA.Files.Files) || {});
|
||||
|
|
122
apps/files/js/navigation.js
Normal file
122
apps/files/js/navigation.js
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright (c) 2014
|
||||
*
|
||||
* @author Vincent Petry
|
||||
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
var Navigation = function($el) {
|
||||
this.initialize($el);
|
||||
};
|
||||
|
||||
Navigation.prototype = {
|
||||
|
||||
/**
|
||||
* Currently selected item in the list
|
||||
*/
|
||||
_activeItem: null,
|
||||
|
||||
/**
|
||||
* Currently selected container
|
||||
*/
|
||||
$currentContent: null,
|
||||
|
||||
/**
|
||||
* Initializes the navigation from the given container
|
||||
* @param $el element containing the navigation
|
||||
*/
|
||||
initialize: function($el) {
|
||||
this.$el = $el;
|
||||
this._activeItem = null;
|
||||
this.$currentContent = null;
|
||||
this._setupEvents();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup UI events
|
||||
*/
|
||||
_setupEvents: function() {
|
||||
this.$el.on('click', 'li a', _.bind(this._onClickItem, this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the container of the currently active app.
|
||||
*
|
||||
* @return app container
|
||||
*/
|
||||
getActiveContainer: function() {
|
||||
return this.$currentContent;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the currently active item
|
||||
*
|
||||
* @return item ID
|
||||
*/
|
||||
getActiveItem: function() {
|
||||
return this._activeItem;
|
||||
},
|
||||
|
||||
/**
|
||||
* Switch the currently selected item, mark it as selected and
|
||||
* make the content container visible, if any.
|
||||
*
|
||||
* @param string itemId id of the navigation item to select
|
||||
* @param array options "silent" to not trigger event
|
||||
*/
|
||||
setActiveItem: function(itemId, options) {
|
||||
var oldItemId = this._activeItem;
|
||||
if (itemId === this._activeItem) {
|
||||
if (!options || !options.silent) {
|
||||
this.$el.trigger(
|
||||
new $.Event('itemChanged', {itemId: itemId, previousItemId: oldItemId})
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.$el.find('li').removeClass('selected');
|
||||
if (this.$currentContent) {
|
||||
this.$currentContent.addClass('hidden');
|
||||
this.$currentContent.trigger(jQuery.Event('hide'));
|
||||
}
|
||||
this._activeItem = itemId;
|
||||
this.$el.find('li[data-id=' + itemId + ']').addClass('selected');
|
||||
this.$currentContent = $('#app-content-' + itemId);
|
||||
this.$currentContent.removeClass('hidden');
|
||||
if (!options || !options.silent) {
|
||||
this.$currentContent.trigger(jQuery.Event('show'));
|
||||
this.$el.trigger(
|
||||
new $.Event('itemChanged', {itemId: itemId, previousItemId: oldItemId})
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether a given item exists
|
||||
*/
|
||||
itemExists: function(itemId) {
|
||||
return this.$el.find('li[data-id=' + itemId + ']').length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for when clicking on an item.
|
||||
*/
|
||||
_onClickItem: function(ev) {
|
||||
var $target = $(ev.target);
|
||||
var itemId = $target.closest('li').attr('data-id');
|
||||
this.setActiveItem(itemId);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
OCA.Files.Navigation = Navigation;
|
||||
|
||||
})();
|
|
@ -30,6 +30,11 @@ class App {
|
|||
*/
|
||||
private $l10n;
|
||||
|
||||
/**
|
||||
* @var \OCP\INavigationManager
|
||||
*/
|
||||
private static $navigationManager;
|
||||
|
||||
/**
|
||||
* @var \OC\Files\View
|
||||
*/
|
||||
|
@ -40,6 +45,18 @@ class App {
|
|||
$this->l10n = $l10n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the app's navigation manager
|
||||
*
|
||||
* @return \OCP\INavigationManager
|
||||
*/
|
||||
public static function getNavigationManager() {
|
||||
if (self::$navigationManager === null) {
|
||||
self::$navigationManager = new \OC\NavigationManager();
|
||||
}
|
||||
return self::$navigationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* rename a file
|
||||
*
|
||||
|
|
38
apps/files/list.php
Normal file
38
apps/files/list.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - Files list
|
||||
*
|
||||
* @author Vincent Petry
|
||||
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Check if we are a user
|
||||
OCP\User::checkLoggedIn();
|
||||
|
||||
$config = \OC::$server->getConfig();
|
||||
// TODO: move this to the generated config.js
|
||||
$publicUploadEnabled = $config->getAppValue('core', 'shareapi_allow_public_upload', 'yes');
|
||||
$uploadLimit=OCP\Util::uploadLimit();
|
||||
|
||||
// renders the controls and table headers template
|
||||
$tmpl = new OCP\Template('files', 'list', '');
|
||||
$tmpl->assign('uploadLimit', $uploadLimit); // PHP upload limit
|
||||
$tmpl->assign('publicUploadEnabled', $publicUploadEnabled);
|
||||
$tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true)));
|
||||
$tmpl->printPage();
|
||||
|
17
apps/files/templates/appnavigation.php
Normal file
17
apps/files/templates/appnavigation.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<div id="app-navigation">
|
||||
<ul>
|
||||
<?php foreach ($_['navigationItems'] as $item) { ?>
|
||||
<li data-id="<?php p($item['id']) ?>" class="nav-<?php p($item['id']) ?>"><a href="<?php p(isset($item['href']) ? $item['href'] : '#') ?>"><?php p($item['name']);?></a></li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
<div id="app-settings">
|
||||
<div id="app-settings-header">
|
||||
<button class="settings-button"></button>
|
||||
</div>
|
||||
<div id="app-settings-content">
|
||||
<h2><?php p($l->t('WebDAV'));?></h2>
|
||||
<div><input id="webdavurl" type="text" readonly="readonly" value="<?php p(OC_Helper::linkToRemote('webdav')); ?>"></input></div>
|
||||
<em><?php print_unescaped($l->t('Use this address to <a href="%s" target="_blank">access your Files via WebDAV</a>', array(link_to_docs('user-webdav'))));?></em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,120 +1,15 @@
|
|||
<?php /** @var $l OC_L10N */ ?>
|
||||
<div id="controls">
|
||||
<div class="actions creatable hidden">
|
||||
<?php if(!isset($_['dirToken'])):?>
|
||||
<div id="new" class="button">
|
||||
<a><?php p($l->t('New'));?></a>
|
||||
<ul>
|
||||
<li class="icon-filetype-text svg"
|
||||
data-type="file" data-newname="<?php p($l->t('New text file')) ?>.txt">
|
||||
<p><?php p($l->t('Text file'));?></p>
|
||||
</li>
|
||||
<li class="icon-filetype-folder svg"
|
||||
data-type="folder" data-newname="<?php p($l->t('New folder')) ?>">
|
||||
<p><?php p($l->t('Folder'));?></p>
|
||||
</li>
|
||||
<li class="icon-link svg" data-type="web">
|
||||
<p><?php p($l->t('From link'));?></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
<div id="upload" class="button"
|
||||
title="<?php p($l->t('Upload (max. %s)', array($_['uploadMaxHumanFilesize']))) ?>">
|
||||
<?php if($_['uploadMaxFilesize'] >= 0):?>
|
||||
<input type="hidden" id="max_upload" name="MAX_FILE_SIZE" value="<?php p($_['uploadMaxFilesize']) ?>">
|
||||
<?php endif;?>
|
||||
<input type="hidden" id="upload_limit" value="<?php p($_['uploadLimit']) ?>">
|
||||
<input type="hidden" id="free_space" value="<?php p($_['freeSpace']) ?>">
|
||||
<?php if(isset($_['dirToken'])):?>
|
||||
<input type="hidden" id="publicUploadRequestToken" name="requesttoken" value="<?php p($_['requesttoken']) ?>" />
|
||||
<input type="hidden" id="dirToken" name="dirToken" value="<?php p($_['dirToken']) ?>" />
|
||||
<?php endif;?>
|
||||
<input type="hidden" class="max_human_file_size"
|
||||
value="(max <?php p($_['uploadMaxHumanFilesize']); ?>)">
|
||||
<input type="hidden" name="dir" value="<?php p($_['dir']) ?>" id="dir">
|
||||
<input type="file" id="file_upload_start" name='files[]'
|
||||
data-url="<?php print_unescaped(OCP\Util::linkTo('files', 'ajax/upload.php')); ?>" />
|
||||
<a href="#" class="svg icon-upload"></a>
|
||||
</div>
|
||||
<?php if ($_['trash']): ?>
|
||||
<input id="trash" type="button" value="<?php p($l->t('Deleted files'));?>" class="button" <?php $_['trashEmpty'] ? p('disabled') : '' ?> />
|
||||
<?php endif; ?>
|
||||
<div id="uploadprogresswrapper">
|
||||
<div id="uploadprogressbar"></div>
|
||||
<input type="button" class="stop" style="display:none"
|
||||
value="<?php p($l->t('Cancel upload'));?>"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="file_action_panel"></div>
|
||||
<div class="notCreatable notPublic hidden">
|
||||
<?php p($l->t('You don’t have permission to upload or create files here'))?>
|
||||
</div>
|
||||
<input type="hidden" name="permissions" value="<?php p($_['permissions']); ?>" id="permissions">
|
||||
</div>
|
||||
|
||||
<div id="emptycontent" class="hidden"><?php p($l->t('Nothing in here. Upload something!'))?></div>
|
||||
|
||||
<input type="hidden" id="disableSharing" data-status="<?php p($_['disableSharing']); ?>" />
|
||||
|
||||
<table id="filestable" data-allow-public-upload="<?php p($_['publicUploadEnabled'])?>" data-preview-x="36" data-preview-y="36">
|
||||
<thead>
|
||||
<tr>
|
||||
<th id='headerName' class="hidden column-name">
|
||||
<div id="headerName-container">
|
||||
<input type="checkbox" id="select_all" />
|
||||
<label for="select_all"></label>
|
||||
<a class="name sort columntitle" data-sort="name"><span><?php p($l->t( 'Name' )); ?></span><span class="sort-indicator"></span></a>
|
||||
<span id="selectedActionsList" class="selectedActions">
|
||||
<?php if($_['allowZipDownload']) : ?>
|
||||
<a href="" class="download">
|
||||
<img class="svg" alt="Download"
|
||||
src="<?php print_unescaped(OCP\image_path("core", "actions/download.svg")); ?>" />
|
||||
<?php p($l->t('Download'))?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
</div>
|
||||
</th>
|
||||
<th id="headerSize" class="hidden column-size">
|
||||
<a class="size sort columntitle" data-sort="size"><span><?php p($l->t('Size')); ?></span><span class="sort-indicator"></span></a>
|
||||
</th>
|
||||
<th id="headerDate" class="hidden column-mtime">
|
||||
<a id="modified" class="columntitle" data-sort="mtime"><span><?php p($l->t( 'Modified' )); ?></span><span class="sort-indicator"></span></a>
|
||||
<?php if ($_['permissions'] & OCP\PERMISSION_DELETE): ?>
|
||||
<span class="selectedActions"><a href="" class="delete-selected">
|
||||
<?php p($l->t('Delete'))?>
|
||||
<img class="svg" alt="<?php p($l->t('Delete'))?>"
|
||||
src="<?php print_unescaped(OCP\image_path("core", "actions/delete.svg")); ?>" />
|
||||
</a></span>
|
||||
<?php endif; ?>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="fileList">
|
||||
</tbody>
|
||||
<tfoot>
|
||||
</tfoot>
|
||||
</table>
|
||||
<div id="editor"></div><!-- FIXME Do not use this div in your app! It is deprecated and will be removed in the future! -->
|
||||
<div id="uploadsize-message" title="<?php p($l->t('Upload too large'))?>">
|
||||
<p>
|
||||
<?php p($l->t('The files you are trying to upload exceed the maximum size for file uploads on this server.'));?>
|
||||
</p>
|
||||
</div>
|
||||
<div id="scanning-message">
|
||||
<h3>
|
||||
<?php p($l->t('Files are being scanned, please wait.'));?> <span id='scan-count'></span>
|
||||
</h3>
|
||||
<p>
|
||||
<?php p($l->t('Current scanning'));?> <span id='scan-current'></span>
|
||||
</p>
|
||||
</div>
|
||||
<?php $_['appNavigation']->printPage(); ?>
|
||||
<div id="app-content">
|
||||
<?php foreach ($_['appContents'] as $content) { ?>
|
||||
<div id="app-content-<?php p($content['id']) ?>" class="hidden">
|
||||
<?php print_unescaped($content['content']) ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div><!-- closing app-content -->
|
||||
|
||||
<!-- config hints for javascript -->
|
||||
<input type="hidden" name="filesApp" id="filesApp" value="1" />
|
||||
<input type="hidden" name="allowZipDownload" id="allowZipDownload" value="<?php p($_['allowZipDownload']); ?>" />
|
||||
<input type="hidden" name="usedSpacePercent" id="usedSpacePercent" value="<?php p($_['usedSpacePercent']); ?>" />
|
||||
<?php if (!$_['isPublic']) :?>
|
||||
<input type="hidden" name="encryptedFiles" id="encryptedFiles" value="<?php $_['encryptedFiles'] ? p('1') : p('0'); ?>" />
|
||||
|
|
107
apps/files/templates/list.php
Normal file
107
apps/files/templates/list.php
Normal file
|
@ -0,0 +1,107 @@
|
|||
<div id="controls">
|
||||
<div class="actions creatable hidden">
|
||||
<?php if(!isset($_['dirToken'])):?>
|
||||
<div id="new" class="button">
|
||||
<a><?php p($l->t('New'));?></a>
|
||||
<ul>
|
||||
<li class="icon-filetype-text svg"
|
||||
data-type="file" data-newname="<?php p($l->t('New text file')) ?>.txt">
|
||||
<p><?php p($l->t('Text file'));?></p>
|
||||
</li>
|
||||
<li class="icon-filetype-folder svg"
|
||||
data-type="folder" data-newname="<?php p($l->t('New folder')) ?>">
|
||||
<p><?php p($l->t('Folder'));?></p>
|
||||
</li>
|
||||
<li class="icon-link svg" data-type="web">
|
||||
<p><?php p($l->t('From link'));?></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
<?php /* Note: the template attributes are here only for the public page. These are normally loaded
|
||||
through ajax instead (updateStorageStatistics).
|
||||
*/ ?>
|
||||
<div id="upload" class="button"
|
||||
title="<?php isset($_['uploadMaxHumanFilesize']) ? p($l->t('Upload (max. %s)', array($_['uploadMaxHumanFilesize']))) : '' ?>">
|
||||
<input type="hidden" id="max_upload" name="MAX_FILE_SIZE" value="<?php isset($_['uploadMaxFilesize']) ? p($_['uploadMaxFilesize']) : '' ?>">
|
||||
<input type="hidden" id="upload_limit" value="<?php isset($_['uploadLimit']) ? p($_['uploadLimit']) : '' ?>">
|
||||
<input type="hidden" id="free_space" value="<?php isset($_['freeSpace']) ? p($_['freeSpace']) : '' ?>">
|
||||
<?php if(isset($_['dirToken'])):?>
|
||||
<input type="hidden" id="publicUploadRequestToken" name="requesttoken" value="<?php p($_['requesttoken']) ?>" />
|
||||
<input type="hidden" id="dirToken" name="dirToken" value="<?php p($_['dirToken']) ?>" />
|
||||
<?php endif;?>
|
||||
<input type="hidden" class="max_human_file_size"
|
||||
value="(max <?php isset($_['uploadMaxHumanFilesize']) ? p($_['uploadMaxHumanFilesize']) : ''; ?>)">
|
||||
<input type="file" id="file_upload_start" name='files[]'
|
||||
data-url="<?php print_unescaped(OCP\Util::linkTo('files', 'ajax/upload.php')); ?>" />
|
||||
<a href="#" class="svg icon-upload"></a>
|
||||
</div>
|
||||
<div id="uploadprogresswrapper">
|
||||
<div id="uploadprogressbar"></div>
|
||||
<input type="button" class="stop" style="display:none"
|
||||
value="<?php p($l->t('Cancel upload'));?>"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="file_action_panel"></div>
|
||||
<div class="notCreatable notPublic hidden">
|
||||
<?php p($l->t('You don’t have permission to upload or create files here'))?>
|
||||
</div>
|
||||
<input type="hidden" name="permissions" value="" id="permissions">
|
||||
</div>
|
||||
|
||||
<div id="emptycontent" class="hidden"><?php p($l->t('Nothing in here. Upload something!'))?></div>
|
||||
|
||||
<table id="filestable" data-allow-public-upload="<?php p($_['publicUploadEnabled'])?>" data-preview-x="36" data-preview-y="36">
|
||||
<thead>
|
||||
<tr>
|
||||
<th id='headerName' class="hidden column-name">
|
||||
<div id="headerName-container">
|
||||
<input type="checkbox" id="select_all_files" class="select-all"/>
|
||||
<label for="select_all_files"></label>
|
||||
<a class="name sort columntitle" data-sort="name"><span><?php p($l->t( 'Name' )); ?></span><span class="sort-indicator"></span></a>
|
||||
<span id="selectedActionsList" class="selectedActions">
|
||||
<?php if($_['allowZipDownload']) : ?>
|
||||
<a href="" class="download">
|
||||
<img class="svg" alt="Download"
|
||||
src="<?php print_unescaped(OCP\image_path("core", "actions/download.svg")); ?>" />
|
||||
<?php p($l->t('Download'))?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
</div>
|
||||
</th>
|
||||
<th id="headerSize" class="hidden column-size">
|
||||
<a class="size sort columntitle" data-sort="size"><span><?php p($l->t('Size')); ?></span><span class="sort-indicator"></span></a>
|
||||
</th>
|
||||
<th id="headerDate" class="hidden column-mtime">
|
||||
<a id="modified" class="columntitle" data-sort="mtime"><span><?php p($l->t( 'Modified' )); ?></span><span class="sort-indicator"></span></a>
|
||||
<span class="selectedActions"><a href="" class="delete-selected">
|
||||
<?php p($l->t('Delete'))?>
|
||||
<img class="svg" alt="<?php p($l->t('Delete'))?>"
|
||||
src="<?php print_unescaped(OCP\image_path("core", "actions/delete.svg")); ?>" />
|
||||
</a></span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="fileList">
|
||||
</tbody>
|
||||
<tfoot>
|
||||
</tfoot>
|
||||
</table>
|
||||
<input type="hidden" name="allowZipDownload" id="allowZipDownload" value="<?php p($_['allowZipDownload']); ?>" />
|
||||
<input type="hidden" name="dir" id="dir" value="" />
|
||||
<div id="editor"></div><!-- FIXME Do not use this div in your app! It is deprecated and will be removed in the future! -->
|
||||
<div id="uploadsize-message" title="<?php p($l->t('Upload too large'))?>">
|
||||
<p>
|
||||
<?php p($l->t('The files you are trying to upload exceed the maximum size for file uploads on this server.'));?>
|
||||
</p>
|
||||
</div>
|
||||
<div id="scanning-message">
|
||||
<h3>
|
||||
<?php p($l->t('Files are being scanned, please wait.'));?> <span id='scan-count'></span>
|
||||
</h3>
|
||||
<p>
|
||||
<?php p($l->t('Current scanning'));?> <span id='scan-current'></span>
|
||||
</p>
|
||||
</div>
|
220
apps/files/tests/js/appSpec.js
Normal file
220
apps/files/tests/js/appSpec.js
Normal file
|
@ -0,0 +1,220 @@
|
|||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Vincent Petry
|
||||
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
describe('OCA.Files.App tests', function() {
|
||||
var App = OCA.Files.App;
|
||||
var pushStateStub;
|
||||
var parseUrlQueryStub;
|
||||
|
||||
beforeEach(function() {
|
||||
$('#testArea').append(
|
||||
'<div id="content" class="app-files">' +
|
||||
'<div id="app-navigation">' +
|
||||
'<ul><li data-id="files"><a>Files</a></li>' +
|
||||
'<li data-id="other"><a>Other</a></li>' +
|
||||
'</div>' +
|
||||
'<div id="app-content">' +
|
||||
'<div id="app-content-files" class="hidden">' +
|
||||
'</div>' +
|
||||
'<div id="app-content-other" class="hidden">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
pushStateStub = sinon.stub(OC.Util.History, 'pushState');
|
||||
parseUrlQueryStub = sinon.stub(OC.Util.History, 'parseUrlQuery');
|
||||
parseUrlQueryStub.returns({});
|
||||
|
||||
App.initialize();
|
||||
});
|
||||
afterEach(function() {
|
||||
App.navigation = null;
|
||||
App.fileList = null;
|
||||
App.files = null;
|
||||
App.fileActions.clear();
|
||||
App.fileActions = null;
|
||||
|
||||
pushStateStub.restore();
|
||||
parseUrlQueryStub.restore();
|
||||
});
|
||||
|
||||
describe('initialization', function() {
|
||||
it('initializes the default file list with the default file actions', function() {
|
||||
expect(App.fileList).toBeDefined();
|
||||
expect(App.fileList.fileActions.actions.all).toBeDefined();
|
||||
expect(App.fileList.$el.is('#app-content-files')).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('URL handling', function() {
|
||||
it('pushes the state to the URL when current app changed directory', function() {
|
||||
$('#app-content-files').trigger(new $.Event('changeDirectory', {dir: 'subdir'}));
|
||||
expect(pushStateStub.calledOnce).toEqual(true);
|
||||
expect(pushStateStub.getCall(0).args[0].dir).toEqual('subdir');
|
||||
expect(pushStateStub.getCall(0).args[0].view).not.toBeDefined();
|
||||
|
||||
$('li[data-id=other]>a').click();
|
||||
pushStateStub.reset();
|
||||
|
||||
$('#app-content-other').trigger(new $.Event('changeDirectory', {dir: 'subdir'}));
|
||||
expect(pushStateStub.calledOnce).toEqual(true);
|
||||
expect(pushStateStub.getCall(0).args[0].dir).toEqual('subdir');
|
||||
expect(pushStateStub.getCall(0).args[0].view).toEqual('other');
|
||||
});
|
||||
describe('onpopstate', function() {
|
||||
it('sends "urlChanged" event to current app', function() {
|
||||
var handler = sinon.stub();
|
||||
$('#app-content-files').on('urlChanged', handler);
|
||||
App._onPopState({view: 'files', dir: '/somedir'});
|
||||
expect(handler.calledOnce).toEqual(true);
|
||||
expect(handler.getCall(0).args[0].view).toEqual('files');
|
||||
expect(handler.getCall(0).args[0].dir).toEqual('/somedir');
|
||||
});
|
||||
it('sends "show" event to current app and sets navigation', function() {
|
||||
var showHandlerFiles = sinon.stub();
|
||||
var showHandlerOther = sinon.stub();
|
||||
var hideHandlerFiles = sinon.stub();
|
||||
var hideHandlerOther = sinon.stub();
|
||||
$('#app-content-files').on('show', showHandlerFiles);
|
||||
$('#app-content-files').on('hide', hideHandlerFiles);
|
||||
$('#app-content-other').on('show', showHandlerOther);
|
||||
$('#app-content-other').on('hide', hideHandlerOther);
|
||||
App._onPopState({view: 'other', dir: '/somedir'});
|
||||
expect(showHandlerFiles.notCalled).toEqual(true);
|
||||
expect(hideHandlerFiles.calledOnce).toEqual(true);
|
||||
expect(showHandlerOther.calledOnce).toEqual(true);
|
||||
expect(hideHandlerOther.notCalled).toEqual(true);
|
||||
|
||||
showHandlerFiles.reset();
|
||||
showHandlerOther.reset();
|
||||
hideHandlerFiles.reset();
|
||||
hideHandlerOther.reset();
|
||||
|
||||
App._onPopState({view: 'files', dir: '/somedir'});
|
||||
expect(showHandlerFiles.calledOnce).toEqual(true);
|
||||
expect(hideHandlerFiles.notCalled).toEqual(true);
|
||||
expect(showHandlerOther.notCalled).toEqual(true);
|
||||
expect(hideHandlerOther.calledOnce).toEqual(true);
|
||||
|
||||
expect(App.navigation.getActiveItem()).toEqual('files');
|
||||
expect($('#app-content-files').hasClass('hidden')).toEqual(false);
|
||||
expect($('#app-content-other').hasClass('hidden')).toEqual(true);
|
||||
});
|
||||
it('does not send "show" or "hide" event to current app when already visible', function() {
|
||||
var showHandler = sinon.stub();
|
||||
var hideHandler = sinon.stub();
|
||||
$('#app-content-files').on('show', showHandler);
|
||||
$('#app-content-files').on('hide', hideHandler);
|
||||
App._onPopState({view: 'files', dir: '/somedir'});
|
||||
expect(showHandler.notCalled).toEqual(true);
|
||||
expect(hideHandler.notCalled).toEqual(true);
|
||||
});
|
||||
it('state defaults to files app with root dir', function() {
|
||||
var handler = sinon.stub();
|
||||
parseUrlQueryStub.returns({});
|
||||
$('#app-content-files').on('urlChanged', handler);
|
||||
App._onPopState();
|
||||
expect(handler.calledOnce).toEqual(true);
|
||||
expect(handler.getCall(0).args[0].view).toEqual('files');
|
||||
expect(handler.getCall(0).args[0].dir).toEqual('/');
|
||||
});
|
||||
it('activates files app if invalid view is passed', function() {
|
||||
App._onPopState({view: 'invalid', dir: '/somedir'});
|
||||
|
||||
expect(App.navigation.getActiveItem()).toEqual('files');
|
||||
expect($('#app-content-files').hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
});
|
||||
describe('navigation', function() {
|
||||
it('switches the navigation item and panel visibility when onpopstate', function() {
|
||||
App._onPopState({view: 'other', dir: '/somedir'});
|
||||
expect(App.navigation.getActiveItem()).toEqual('other');
|
||||
expect($('#app-content-files').hasClass('hidden')).toEqual(true);
|
||||
expect($('#app-content-other').hasClass('hidden')).toEqual(false);
|
||||
expect($('li[data-id=files]').hasClass('selected')).toEqual(false);
|
||||
expect($('li[data-id=other]').hasClass('selected')).toEqual(true);
|
||||
|
||||
App._onPopState({view: 'files', dir: '/somedir'});
|
||||
|
||||
expect(App.navigation.getActiveItem()).toEqual('files');
|
||||
expect($('#app-content-files').hasClass('hidden')).toEqual(false);
|
||||
expect($('#app-content-other').hasClass('hidden')).toEqual(true);
|
||||
expect($('li[data-id=files]').hasClass('selected')).toEqual(true);
|
||||
expect($('li[data-id=other]').hasClass('selected')).toEqual(false);
|
||||
});
|
||||
it('clicking on navigation switches the panel visibility', function() {
|
||||
$('li[data-id=other]>a').click();
|
||||
expect(App.navigation.getActiveItem()).toEqual('other');
|
||||
expect($('#app-content-files').hasClass('hidden')).toEqual(true);
|
||||
expect($('#app-content-other').hasClass('hidden')).toEqual(false);
|
||||
expect($('li[data-id=files]').hasClass('selected')).toEqual(false);
|
||||
expect($('li[data-id=other]').hasClass('selected')).toEqual(true);
|
||||
|
||||
$('li[data-id=files]>a').click();
|
||||
expect(App.navigation.getActiveItem()).toEqual('files');
|
||||
expect($('#app-content-files').hasClass('hidden')).toEqual(false);
|
||||
expect($('#app-content-other').hasClass('hidden')).toEqual(true);
|
||||
expect($('li[data-id=files]').hasClass('selected')).toEqual(true);
|
||||
expect($('li[data-id=other]').hasClass('selected')).toEqual(false);
|
||||
});
|
||||
it('clicking on navigation sends "show" and "urlChanged" event', function() {
|
||||
var handler = sinon.stub();
|
||||
var showHandler = sinon.stub();
|
||||
$('#app-content-other').on('urlChanged', handler);
|
||||
$('#app-content-other').on('show', showHandler);
|
||||
$('li[data-id=other]>a').click();
|
||||
expect(handler.calledOnce).toEqual(true);
|
||||
expect(handler.getCall(0).args[0].view).toEqual('other');
|
||||
expect(handler.getCall(0).args[0].dir).toEqual('/');
|
||||
expect(showHandler.calledOnce).toEqual(true);
|
||||
});
|
||||
it('clicking on activate navigation only sends "urlChanged" event', function() {
|
||||
var handler = sinon.stub();
|
||||
var showHandler = sinon.stub();
|
||||
$('#app-content-files').on('urlChanged', handler);
|
||||
$('#app-content-files').on('show', showHandler);
|
||||
$('li[data-id=files]>a').click();
|
||||
expect(handler.calledOnce).toEqual(true);
|
||||
expect(handler.getCall(0).args[0].view).toEqual('files');
|
||||
expect(handler.getCall(0).args[0].dir).toEqual('/');
|
||||
expect(showHandler.notCalled).toEqual(true);
|
||||
});
|
||||
});
|
||||
describe('viewer mode', function() {
|
||||
it('toggles the sidebar when viewer mode is enabled', function() {
|
||||
$('#app-content-files').trigger(
|
||||
new $.Event('changeViewerMode', {viewerModeEnabled: true}
|
||||
));
|
||||
expect($('#app-navigation').hasClass('hidden')).toEqual(true);
|
||||
expect($('.app-files').hasClass('viewer-mode no-sidebar')).toEqual(true);
|
||||
|
||||
$('#app-content-files').trigger(
|
||||
new $.Event('changeViewerMode', {viewerModeEnabled: false}
|
||||
));
|
||||
|
||||
expect($('#app-navigation').hasClass('hidden')).toEqual(false);
|
||||
expect($('.app-files').hasClass('viewer-mode no-sidebar')).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -20,7 +20,9 @@
|
|||
*/
|
||||
|
||||
/* global BreadCrumb */
|
||||
describe('BreadCrumb tests', function() {
|
||||
describe('OCA.Files.BreadCrumb tests', function() {
|
||||
var BreadCrumb = OCA.Files.BreadCrumb;
|
||||
|
||||
describe('Rendering', function() {
|
||||
var bc;
|
||||
beforeEach(function() {
|
||||
|
|
|
@ -19,35 +19,45 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/* global OC, FileActions, FileList */
|
||||
describe('FileActions tests', function() {
|
||||
var $filesTable;
|
||||
describe('OCA.Files.FileActions tests', function() {
|
||||
var $filesTable, fileList;
|
||||
var FileActions = OCA.Files.FileActions;
|
||||
|
||||
beforeEach(function() {
|
||||
// init horrible parameters
|
||||
var $body = $('body');
|
||||
var $body = $('#testArea');
|
||||
$body.append('<input type="hidden" id="dir" value="/subdir"></input>');
|
||||
$body.append('<input type="hidden" id="permissions" value="31"></input>');
|
||||
// dummy files table
|
||||
$filesTable = $body.append('<table id="filestable"></table>');
|
||||
FileList.files = [];
|
||||
fileList = new OCA.Files.FileList($('#testArea'));
|
||||
FileActions.registerDefaultActions(fileList);
|
||||
});
|
||||
afterEach(function() {
|
||||
FileActions.clear();
|
||||
fileList = undefined;
|
||||
$('#dir, #permissions, #filestable').remove();
|
||||
});
|
||||
it('calling clear() clears file actions', function() {
|
||||
FileActions.clear();
|
||||
expect(FileActions.actions).toEqual({});
|
||||
expect(FileActions.defaults).toEqual({});
|
||||
expect(FileActions.icons).toEqual({});
|
||||
expect(FileActions.currentFile).toBe(null);
|
||||
});
|
||||
it('calling display() sets file actions', function() {
|
||||
var fileData = {
|
||||
id: 18,
|
||||
type: 'file',
|
||||
name: 'testName.txt',
|
||||
mimetype: 'plain/text',
|
||||
mimetype: 'text/plain',
|
||||
size: '1234',
|
||||
etag: 'a01234c',
|
||||
mtime: '123456'
|
||||
};
|
||||
|
||||
// note: FileActions.display() is called implicitly
|
||||
var $tr = FileList.add(fileData);
|
||||
var $tr = fileList.add(fileData);
|
||||
|
||||
// actions defined after call
|
||||
expect($tr.find('.action.action-download').length).toEqual(1);
|
||||
|
@ -61,12 +71,12 @@ describe('FileActions tests', function() {
|
|||
id: 18,
|
||||
type: 'file',
|
||||
name: 'testName.txt',
|
||||
mimetype: 'plain/text',
|
||||
mimetype: 'text/plain',
|
||||
size: '1234',
|
||||
etag: 'a01234c',
|
||||
mtime: '123456'
|
||||
};
|
||||
var $tr = FileList.add(fileData);
|
||||
var $tr = fileList.add(fileData);
|
||||
|
||||
FileActions.display($tr.find('td.filename'), true);
|
||||
FileActions.display($tr.find('td.filename'), true);
|
||||
|
@ -82,12 +92,12 @@ describe('FileActions tests', function() {
|
|||
id: 18,
|
||||
type: 'file',
|
||||
name: 'testName.txt',
|
||||
mimetype: 'plain/text',
|
||||
mimetype: 'text/plain',
|
||||
size: '1234',
|
||||
etag: 'a01234c',
|
||||
mtime: '123456'
|
||||
};
|
||||
var $tr = FileList.add(fileData);
|
||||
var $tr = fileList.add(fileData);
|
||||
FileActions.display($tr.find('td.filename'), true);
|
||||
|
||||
$tr.find('.action-download').click();
|
||||
|
@ -97,17 +107,17 @@ describe('FileActions tests', function() {
|
|||
redirectStub.restore();
|
||||
});
|
||||
it('deletes file when clicking delete', function() {
|
||||
var deleteStub = sinon.stub(FileList, 'do_delete');
|
||||
var deleteStub = sinon.stub(fileList, 'do_delete');
|
||||
var fileData = {
|
||||
id: 18,
|
||||
type: 'file',
|
||||
name: 'testName.txt',
|
||||
mimetype: 'plain/text',
|
||||
mimetype: 'text/plain',
|
||||
size: '1234',
|
||||
etag: 'a01234c',
|
||||
mtime: '123456'
|
||||
};
|
||||
var $tr = FileList.add(fileData);
|
||||
var $tr = fileList.add(fileData);
|
||||
FileActions.display($tr.find('td.filename'), true);
|
||||
|
||||
$tr.find('.action.delete').click();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -19,8 +19,9 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/* global OC, Files */
|
||||
describe('Files tests', function() {
|
||||
describe('OCA.Files.Files tests', function() {
|
||||
var Files = OCA.Files.Files;
|
||||
|
||||
describe('File name validation', function() {
|
||||
it('Validates correct file names', function() {
|
||||
var fileNames = [
|
||||
|
@ -83,18 +84,6 @@ describe('Files tests', function() {
|
|||
});
|
||||
});
|
||||
describe('getDownloadUrl', function() {
|
||||
var curDirStub;
|
||||
beforeEach(function() {
|
||||
curDirStub = sinon.stub(FileList, 'getCurrentDirectory');
|
||||
});
|
||||
afterEach(function() {
|
||||
curDirStub.restore();
|
||||
});
|
||||
it('returns the ajax download URL when only filename specified', function() {
|
||||
curDirStub.returns('/subdir');
|
||||
var url = Files.getDownloadUrl('test file.txt');
|
||||
expect(url).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=test%20file.txt');
|
||||
});
|
||||
it('returns the ajax download URL when filename and dir specified', function() {
|
||||
var url = Files.getDownloadUrl('test file.txt', '/subdir');
|
||||
expect(url).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=test%20file.txt');
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
*/
|
||||
|
||||
/* global FileSummary */
|
||||
describe('FileSummary tests', function() {
|
||||
describe('OCA.Files.FileSummary tests', function() {
|
||||
var FileSummary = OCA.Files.FileSummary;
|
||||
var $container;
|
||||
|
||||
beforeEach(function() {
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
#controls {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#preview {
|
||||
background: #fff;
|
||||
text-align: center;
|
||||
|
|
|
@ -8,95 +8,164 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/* global OC, FileActions, FileList, Files */
|
||||
/* global FileActions, Files */
|
||||
/* global dragOptions, folderDropOptions */
|
||||
OCA.Sharing = {};
|
||||
if (!OCA.Files) {
|
||||
OCA.Files = {};
|
||||
}
|
||||
OCA.Sharing.PublicApp = {
|
||||
_initialized: false,
|
||||
|
||||
$(document).ready(function() {
|
||||
initialize: function($el) {
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
this._initialized = true;
|
||||
// file list mode ?
|
||||
if ($el.find('#filestable')) {
|
||||
this.fileList = new OCA.Files.FileList(
|
||||
$el,
|
||||
{
|
||||
scrollContainer: $(window),
|
||||
dragOptions: dragOptions,
|
||||
folderDropOptions: folderDropOptions
|
||||
}
|
||||
);
|
||||
this.files = OCA.Files.Files;
|
||||
this.files.initialize();
|
||||
}
|
||||
|
||||
var mimetype = $('#mimetype').val();
|
||||
var mimetype = $('#mimetype').val();
|
||||
|
||||
if (typeof FileActions !== 'undefined') {
|
||||
// Show file preview if previewer is available, images are already handled by the template
|
||||
if (mimetype.substr(0, mimetype.indexOf('/')) !== 'image' && $('.publicpreview').length === 0) {
|
||||
// Trigger default action if not download TODO
|
||||
var action = FileActions.getDefault(mimetype, 'file', OC.PERMISSION_READ);
|
||||
if (typeof action !== 'undefined') {
|
||||
action($('#filename').val());
|
||||
if (typeof FileActions !== 'undefined') {
|
||||
// Show file preview if previewer is available, images are already handled by the template
|
||||
if (mimetype.substr(0, mimetype.indexOf('/')) !== 'image' && $('.publicpreview').length === 0) {
|
||||
// Trigger default action if not download TODO
|
||||
var action = FileActions.getDefault(mimetype, 'file', OC.PERMISSION_READ);
|
||||
if (typeof action !== 'undefined') {
|
||||
action($('#filename').val());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dynamically load image previews
|
||||
if (mimetype.substr(0, mimetype.indexOf('/')) === 'image' ) {
|
||||
// dynamically load image previews
|
||||
if (mimetype.substr(0, mimetype.indexOf('/')) === 'image' ) {
|
||||
|
||||
var params = {
|
||||
x: $(document).width() * window.devicePixelRatio,
|
||||
a: 'true',
|
||||
file: encodeURIComponent($('#dir').val() + $('#filename').val()),
|
||||
t: $('#sharingToken').val()
|
||||
};
|
||||
|
||||
var img = $('<img class="publicpreview">');
|
||||
img.attr('src', OC.filePath('files_sharing', 'ajax', 'publicpreview.php') + '?' + OC.buildQueryString(params));
|
||||
img.appendTo('#imgframe');
|
||||
}
|
||||
|
||||
// override since the format is different
|
||||
if (typeof Files !== 'undefined') {
|
||||
Files.getDownloadUrl = function(filename, dir) {
|
||||
if ($.isArray(filename)) {
|
||||
filename = JSON.stringify(filename);
|
||||
}
|
||||
var path = dir || FileList.getCurrentDirectory();
|
||||
var params = {
|
||||
service: 'files',
|
||||
t: $('#sharingToken').val(),
|
||||
path: path,
|
||||
files: filename,
|
||||
download: null
|
||||
x: $(document).width() * window.devicePixelRatio,
|
||||
a: 'true',
|
||||
file: encodeURIComponent($('#dir').val() + $('#filename').val()),
|
||||
t: $('#sharingToken').val()
|
||||
};
|
||||
return OC.filePath('', '', 'public.php') + '?' + OC.buildQueryString(params);
|
||||
};
|
||||
|
||||
Files.getAjaxUrl = function(action, params) {
|
||||
params = params || {};
|
||||
params.t = $('#sharingToken').val();
|
||||
return OC.filePath('files_sharing', 'ajax', action + '.php') + '?' + OC.buildQueryString(params);
|
||||
};
|
||||
var img = $('<img class="publicpreview">');
|
||||
img.attr('src', OC.filePath('files_sharing', 'ajax', 'publicpreview.php') + '?' + OC.buildQueryString(params));
|
||||
img.appendTo('#imgframe');
|
||||
}
|
||||
|
||||
FileList.linkTo = function(dir) {
|
||||
var params = {
|
||||
service: 'files',
|
||||
t: $('#sharingToken').val(),
|
||||
dir: dir
|
||||
if (this.fileList) {
|
||||
// TODO: move this to a separate PublicFileList class that extends OCA.Files.FileList (+ unit tests)
|
||||
this.fileList.getDownloadUrl = function(filename, dir) {
|
||||
if ($.isArray(filename)) {
|
||||
filename = JSON.stringify(filename);
|
||||
}
|
||||
var path = dir || FileList.getCurrentDirectory();
|
||||
var params = {
|
||||
service: 'files',
|
||||
t: $('#sharingToken').val(),
|
||||
path: path,
|
||||
files: filename,
|
||||
download: null
|
||||
};
|
||||
return OC.filePath('', '', 'public.php') + '?' + OC.buildQueryString(params);
|
||||
};
|
||||
return OC.filePath('', '', 'public.php') + '?' + OC.buildQueryString(params);
|
||||
};
|
||||
|
||||
Files.generatePreviewUrl = function(urlSpec) {
|
||||
urlSpec.t = $('#dirToken').val();
|
||||
return OC.generateUrl('/apps/files_sharing/ajax/publicpreview.php?') + $.param(urlSpec);
|
||||
};
|
||||
|
||||
var file_upload_start = $('#file_upload_start');
|
||||
file_upload_start.on('fileuploadadd', function(e, data) {
|
||||
var fileDirectory = '';
|
||||
if(typeof data.files[0].relativePath !== 'undefined') {
|
||||
fileDirectory = data.files[0].relativePath;
|
||||
}
|
||||
|
||||
// Add custom data to the upload handler
|
||||
data.formData = {
|
||||
requesttoken: $('#publicUploadRequestToken').val(),
|
||||
dirToken: $('#dirToken').val(),
|
||||
subdir: $('input#dir').val(),
|
||||
file_directory: fileDirectory
|
||||
this.fileList.getAjaxUrl = function(action, params) {
|
||||
params = params || {};
|
||||
params.t = $('#sharingToken').val();
|
||||
return OC.filePath('files_sharing', 'ajax', action + '.php') + '?' + OC.buildQueryString(params);
|
||||
};
|
||||
|
||||
this.fileList.linkTo = function(dir) {
|
||||
var params = {
|
||||
service: 'files',
|
||||
t: $('#sharingToken').val(),
|
||||
dir: dir
|
||||
};
|
||||
return OC.filePath('', '', 'public.php') + '?' + OC.buildQueryString(params);
|
||||
};
|
||||
|
||||
this.fileList.generatePreviewUrl = function(urlSpec) {
|
||||
urlSpec.t = $('#dirToken').val();
|
||||
return OC.generateUrl('/apps/files_sharing/ajax/publicpreview.php?') + $.param(urlSpec);
|
||||
};
|
||||
|
||||
var file_upload_start = $('#file_upload_start');
|
||||
file_upload_start.on('fileuploadadd', function(e, data) {
|
||||
var fileDirectory = '';
|
||||
if(typeof data.files[0].relativePath !== 'undefined') {
|
||||
fileDirectory = data.files[0].relativePath;
|
||||
}
|
||||
|
||||
// Add custom data to the upload handler
|
||||
data.formData = {
|
||||
requesttoken: $('#publicUploadRequestToken').val(),
|
||||
dirToken: $('#dirToken').val(),
|
||||
subdir: $('input#dir').val(),
|
||||
file_directory: fileDirectory
|
||||
};
|
||||
});
|
||||
|
||||
this.fileActions = _.extend({}, OCA.Files.FileActions);
|
||||
this.fileActions.registerDefaultActions(this.fileList);
|
||||
delete this.fileActions.actions.all.Share;
|
||||
this.fileList.setFileActions(this.fileActions);
|
||||
|
||||
this.fileList.changeDirectory($('#dir').val() || '/', false, true);
|
||||
|
||||
// URL history handling
|
||||
this.fileList.$el.on('changeDirectory', _.bind(this._onDirectoryChanged, this));
|
||||
OC.Util.History.addOnPopStateHandler(_.bind(this._onUrlChanged, this));
|
||||
}
|
||||
|
||||
$(document).on('click', '#directLink', function() {
|
||||
$(this).focus();
|
||||
$(this).select();
|
||||
});
|
||||
|
||||
// legacy
|
||||
window.FileList = this.fileList;
|
||||
},
|
||||
|
||||
_onDirectoryChanged: function(e) {
|
||||
OC.Util.History.pushState({
|
||||
service: 'files',
|
||||
t: $('#sharingToken').val(),
|
||||
// arghhhh, why is this not called "dir" !?
|
||||
path: e.dir
|
||||
});
|
||||
},
|
||||
|
||||
_onUrlChanged: function(params) {
|
||||
this.fileList.changeDirectory(params.path || params.dir, false, true);
|
||||
}
|
||||
};
|
||||
|
||||
$(document).on('click', '#directLink', function() {
|
||||
$(this).focus();
|
||||
$(this).select();
|
||||
});
|
||||
$(document).ready(function() {
|
||||
var App = OCA.Sharing.PublicApp;
|
||||
App.initialize($('#preview'));
|
||||
|
||||
// HACK: for oc-dialogs previews that depends on Files:
|
||||
Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) {
|
||||
return App.fileList.lazyLoadPreview({
|
||||
path: path,
|
||||
mime: mime,
|
||||
callback: ready,
|
||||
width: width,
|
||||
height: height,
|
||||
etag: etag
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -8,15 +8,15 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/* global OC, t, FileList, FileActions */
|
||||
/* global FileList, FileActions */
|
||||
$(document).ready(function() {
|
||||
|
||||
var disableSharing = $('#disableSharing').data('status'),
|
||||
sharesLoaded = false;
|
||||
var sharesLoaded = false;
|
||||
|
||||
if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined' && !disableSharing) {
|
||||
var oldCreateRow = FileList._createRow;
|
||||
FileList._createRow = function(fileData) {
|
||||
if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined') {
|
||||
// TODO: make a separate class for this or a hook or jQuery event ?
|
||||
var oldCreateRow = OCA.Files.FileList.prototype._createRow;
|
||||
OCA.Files.FileList.prototype._createRow = function(fileData) {
|
||||
var tr = oldCreateRow.apply(this, arguments);
|
||||
if (fileData.shareOwner) {
|
||||
tr.attr('data-share-owner', fileData.shareOwner);
|
||||
|
@ -25,14 +25,16 @@ $(document).ready(function() {
|
|||
};
|
||||
|
||||
$('#fileList').on('fileActionsReady',function(){
|
||||
|
||||
var allShared = $('#fileList').find('[data-share-owner] [data-Action="Share"]');
|
||||
var $fileList = $(this);
|
||||
var allShared = $fileList.find('[data-share-owner] [data-Action="Share"]');
|
||||
allShared.addClass('permanent');
|
||||
allShared.find('span').text(function(){
|
||||
var $owner = $(this).closest('tr').attr('data-share-owner');
|
||||
return ' ' + t('files_sharing', 'Shared by {owner}', {owner: $owner});
|
||||
});
|
||||
|
||||
// FIXME: these calls are also working on hard-coded
|
||||
// list selectors...
|
||||
if (!sharesLoaded){
|
||||
OC.Share.loadIcons('file');
|
||||
// assume that we got all shares, so switching directories
|
||||
|
|
|
@ -149,11 +149,11 @@ if (isset($path)) {
|
|||
|
||||
$freeSpace=OCP\Util::freeSpace($path);
|
||||
$uploadLimit=OCP\Util::uploadLimit();
|
||||
$folder = new OCP\Template('files', 'index', '');
|
||||
$folder = new OCP\Template('files', 'list', '');
|
||||
$folder->assign('dir', $getPath);
|
||||
$folder->assign('dirToken', $linkItem['token']);
|
||||
$folder->assign('permissions', OCP\PERMISSION_READ);
|
||||
$folder->assign('isPublic',true);
|
||||
$folder->assign('isPublic', true);
|
||||
$folder->assign('publicUploadEnabled', 'no');
|
||||
$folder->assign('files', $files);
|
||||
$folder->assign('uploadMaxFilesize', $maxUploadFilesize);
|
||||
|
@ -162,7 +162,6 @@ if (isset($path)) {
|
|||
$folder->assign('uploadLimit', $uploadLimit); // PHP upload limit
|
||||
$folder->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true)));
|
||||
$folder->assign('usedSpacePercent', 0);
|
||||
$folder->assign('disableSharing', true);
|
||||
$folder->assign('trash', false);
|
||||
$tmpl->assign('folder', $folder->fetchPage());
|
||||
$allowZip = OCP\Config::getSystemValue('allowZipDownload', true);
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
<?php
|
||||
|
||||
//OC::$CLASSPATH['OCA\Files_Trashbin\Hooks'] = 'files_trashbin/lib/hooks.php';
|
||||
//OC::$CLASSPATH['OCA\Files_Trashbin\Trashbin'] = 'files_trashbin/lib/trash.php';
|
||||
$l = OC_L10N::get('files_trashbin');
|
||||
|
||||
// register hooks
|
||||
\OCA\Files_Trashbin\Trashbin::registerHooks();
|
||||
|
||||
\OCA\Files\App::getNavigationManager()->add(
|
||||
array(
|
||||
"id" => 'trashbin',
|
||||
"appname" => 'files_trashbin',
|
||||
"script" => 'list.php',
|
||||
"order" => 1,
|
||||
"name" => $l->t('Deleted files')
|
||||
)
|
||||
);
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
#fileList tr[data-type="file"] td a.name,
|
||||
#fileList tr[data-type="file"] td a.name span {
|
||||
/*
|
||||
* Copyright (c) 2014
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
#app-content-trashbin tbody tr[data-type="file"] td a.name,
|
||||
#app-content-trashbin tbody tr[data-type="file"] td a.name span {
|
||||
cursor: default;
|
||||
}
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
|
||||
// Check if we are a user
|
||||
OCP\User::checkLoggedIn();
|
||||
|
||||
OCP\App::setActiveNavigationEntry('files_index');
|
||||
|
||||
OCP\Util::addScript('files_trashbin', 'disableDefaultActions');
|
||||
OCP\Util::addScript('files', 'fileactions');
|
||||
$tmpl = new OCP\Template('files_trashbin', 'index', 'user');
|
||||
|
||||
OCP\Util::addStyle('files', 'files');
|
||||
OCP\Util::addStyle('files_trashbin', 'trash');
|
||||
OCP\Util::addScript('files', 'filesummary');
|
||||
OCP\Util::addScript('files', 'breadcrumb');
|
||||
OCP\Util::addScript('files', 'filelist');
|
||||
// filelist overrides
|
||||
OCP\Util::addScript('files_trashbin', 'filelist');
|
||||
OCP\Util::addscript('files', 'files');
|
||||
OCP\Util::addScript('files_trashbin', 'trash');
|
||||
|
||||
$dir = isset($_GET['dir']) ? stripslashes($_GET['dir']) : '';
|
||||
|
||||
$isIE8 = false;
|
||||
preg_match('/MSIE (.*?);/', $_SERVER['HTTP_USER_AGENT'], $matches);
|
||||
if (count($matches) > 0 && $matches[1] <= 8){
|
||||
$isIE8 = true;
|
||||
}
|
||||
|
||||
// if IE8 and "?dir=path" was specified, reformat the URL to use a hash like "#?dir=path"
|
||||
if ($isIE8 && isset($_GET['dir'])){
|
||||
if ($dir === ''){
|
||||
$dir = '/';
|
||||
}
|
||||
header('Location: ' . OCP\Util::linkTo('files_trashbin', 'index.php') . '#?dir=' . \OCP\Util::encodePath($dir));
|
||||
exit();
|
||||
}
|
||||
|
||||
$tmpl->assign('dir', $dir);
|
||||
$tmpl->assign('disableSharing', true);
|
||||
|
||||
$tmpl->printPage();
|
82
apps/files_trashbin/js/app.js
Normal file
82
apps/files_trashbin/js/app.js
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) 2014
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
|
||||
OCA.Trashbin = {};
|
||||
OCA.Trashbin.App = {
|
||||
_initialized: false,
|
||||
|
||||
initialize: function($el) {
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
this._initialized = true;
|
||||
this.fileList = new OCA.Trashbin.FileList(
|
||||
$('#app-content-trashbin'), {
|
||||
scrollContainer: $('#app-content')
|
||||
}
|
||||
);
|
||||
this.registerFileActions(this.fileList);
|
||||
},
|
||||
|
||||
registerFileActions: function(fileList) {
|
||||
var self = this;
|
||||
var fileActions = _.extend({}, OCA.Files.FileActions);
|
||||
fileActions.clear();
|
||||
fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename) {
|
||||
var dir = fileList.getCurrentDirectory();
|
||||
if (dir !== '/') {
|
||||
dir = dir + '/';
|
||||
}
|
||||
fileList.changeDirectory(dir + filename);
|
||||
});
|
||||
|
||||
fileActions.setDefault('dir', 'Open');
|
||||
|
||||
fileActions.register('all', 'Restore', OC.PERMISSION_READ, OC.imagePath('core', 'actions/history'), function(filename) {
|
||||
var tr = fileList.findFileEl(filename);
|
||||
var deleteAction = tr.children("td.date").children(".action.delete");
|
||||
deleteAction.removeClass('delete-icon').addClass('progress-icon');
|
||||
fileList.disableActions();
|
||||
$.post(OC.filePath('files_trashbin', 'ajax', 'undelete.php'), {
|
||||
files: JSON.stringify([filename]),
|
||||
dir: fileList.getCurrentDirectory()
|
||||
},
|
||||
_.bind(fileList._removeCallback, fileList)
|
||||
);
|
||||
}, t('files_trashbin', 'Restore'));
|
||||
|
||||
fileActions.register('all', 'Delete', OC.PERMISSION_READ, function() {
|
||||
return OC.imagePath('core', 'actions/delete');
|
||||
}, function(filename) {
|
||||
$('.tipsy').remove();
|
||||
var tr = fileList.findFileEl(filename);
|
||||
var deleteAction = tr.children("td.date").children(".action.delete");
|
||||
deleteAction.removeClass('delete-icon').addClass('progress-icon');
|
||||
fileList.disableActions();
|
||||
$.post(OC.filePath('files_trashbin', 'ajax', 'delete.php'), {
|
||||
files: JSON.stringify([filename]),
|
||||
dir: fileList.getCurrentDirectory()
|
||||
},
|
||||
_.bind(fileList._removeCallback, fileList)
|
||||
);
|
||||
});
|
||||
fileList.setFileActions(fileActions);
|
||||
}
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#app-content-trashbin').one('show', function() {
|
||||
var App = OCA.Trashbin.App;
|
||||
App.initialize($('#app-content-trashbin'));
|
||||
// force breadcrumb init
|
||||
// App.fileList.changeDirectory(App.fileList.getCurrentDirectory(), false, true);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
/* disable download and sharing actions */
|
||||
var disableDownloadActions = true;
|
||||
var trashBinApp = true;
|
|
@ -1,8 +1,14 @@
|
|||
/* global OC, t, FileList */
|
||||
/*
|
||||
* Copyright (c) 2014
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
(function() {
|
||||
FileList.appName = t('files_trashbin', 'Deleted files');
|
||||
|
||||
FileList._deletedRegExp = new RegExp(/^(.+)\.d[0-9]+$/);
|
||||
var DELETED_REGEXP = new RegExp(/^(.+)\.d[0-9]+$/);
|
||||
|
||||
/**
|
||||
* Convert a file name in the format filename.d12345 to the real file name.
|
||||
|
@ -11,195 +17,248 @@
|
|||
* @param name file name
|
||||
* @return converted file name
|
||||
*/
|
||||
FileList.getDeletedFileName = function(name) {
|
||||
function getDeletedFileName(name) {
|
||||
name = OC.basename(name);
|
||||
var match = FileList._deletedRegExp.exec(name);
|
||||
var match = DELETED_REGEXP.exec(name);
|
||||
if (match && match.length > 1) {
|
||||
name = match[1];
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
var oldSetCurrentDir = FileList._setCurrentDir;
|
||||
FileList._setCurrentDir = function(targetDir) {
|
||||
oldSetCurrentDir.apply(this, arguments);
|
||||
|
||||
var baseDir = OC.basename(targetDir);
|
||||
if (baseDir !== '') {
|
||||
FileList.setPageTitle(FileList.getDeletedFileName(baseDir));
|
||||
}
|
||||
};
|
||||
|
||||
var oldCreateRow = FileList._createRow;
|
||||
FileList._createRow = function() {
|
||||
// FIXME: MEGAHACK until we find a better solution
|
||||
var tr = oldCreateRow.apply(this, arguments);
|
||||
tr.find('td.filesize').remove();
|
||||
return tr;
|
||||
};
|
||||
|
||||
FileList._onClickBreadCrumb = function(e) {
|
||||
var $el = $(e.target).closest('.crumb'),
|
||||
index = $el.index(),
|
||||
$targetDir = $el.data('dir');
|
||||
// first one is home, let the link makes it default action
|
||||
if (index !== 0) {
|
||||
e.preventDefault();
|
||||
FileList.changeDirectory($targetDir);
|
||||
}
|
||||
};
|
||||
|
||||
var oldRenderRow = FileList._renderRow;
|
||||
FileList._renderRow = function(fileData, options) {
|
||||
options = options || {};
|
||||
var dir = FileList.getCurrentDirectory();
|
||||
var dirListing = dir !== '' && dir !== '/';
|
||||
// show deleted time as mtime
|
||||
if (fileData.mtime) {
|
||||
fileData.mtime = parseInt(fileData.mtime, 10);
|
||||
}
|
||||
if (!dirListing) {
|
||||
fileData.displayName = fileData.name;
|
||||
fileData.name = fileData.name + '.d' + Math.floor(fileData.mtime / 1000);
|
||||
}
|
||||
return oldRenderRow.call(this, fileData, options);
|
||||
};
|
||||
|
||||
FileList.linkTo = function(dir){
|
||||
return OC.linkTo('files_trashbin', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/');
|
||||
};
|
||||
|
||||
FileList.updateEmptyContent = function(){
|
||||
var $fileList = $('#fileList');
|
||||
var exists = $fileList.find('tr:first').exists();
|
||||
$('#emptycontent').toggleClass('hidden', exists);
|
||||
$('#filestable th').toggleClass('hidden', !exists);
|
||||
};
|
||||
|
||||
var oldInit = FileList.initialize;
|
||||
FileList.initialize = function() {
|
||||
var result = oldInit.apply(this, arguments);
|
||||
$('.undelete').click('click', FileList._onClickRestoreSelected);
|
||||
this.setSort('mtime', 'desc');
|
||||
return result;
|
||||
};
|
||||
|
||||
FileList._removeCallback = function(result) {
|
||||
if (result.status !== 'success') {
|
||||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error'));
|
||||
}
|
||||
|
||||
var files = result.data.success;
|
||||
var $el;
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
$el = FileList.remove(OC.basename(files[i].filename), {updateSummary: false});
|
||||
FileList.fileSummary.remove({type: $el.attr('data-type'), size: $el.attr('data-size')});
|
||||
}
|
||||
FileList.fileSummary.update();
|
||||
FileList.updateEmptyContent();
|
||||
enableActions();
|
||||
}
|
||||
|
||||
FileList._onClickRestoreSelected = function(event) {
|
||||
event.preventDefault();
|
||||
var allFiles = $('#select_all').is(':checked');
|
||||
var files = [];
|
||||
var params = {};
|
||||
disableActions();
|
||||
if (allFiles) {
|
||||
FileList.showMask();
|
||||
params = {
|
||||
allfiles: true,
|
||||
dir: FileList.getCurrentDirectory()
|
||||
};
|
||||
}
|
||||
else {
|
||||
files = _.pluck(FileList.getSelectedFiles(), 'name');
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete");
|
||||
deleteAction.removeClass('delete-icon').addClass('progress-icon');
|
||||
}
|
||||
params = {
|
||||
files: JSON.stringify(files),
|
||||
dir: FileList.getCurrentDirectory()
|
||||
};
|
||||
}
|
||||
|
||||
$.post(OC.filePath('files_trashbin', 'ajax', 'undelete.php'),
|
||||
params,
|
||||
function(result) {
|
||||
if (allFiles) {
|
||||
if (result.status !== 'success') {
|
||||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error'));
|
||||
}
|
||||
FileList.hideMask();
|
||||
// simply remove all files
|
||||
FileList.setFiles([]);
|
||||
enableActions();
|
||||
}
|
||||
else {
|
||||
FileList._removeCallback(result);
|
||||
}
|
||||
}
|
||||
);
|
||||
var FileList = function($el) {
|
||||
this.initialize($el);
|
||||
};
|
||||
FileList.prototype = _.extend({}, OCA.Files.FileList.prototype, {
|
||||
id: 'trashbin',
|
||||
appName: t('files_trashbin', 'Deleted files'),
|
||||
|
||||
FileList._onClickDeleteSelected = function(event) {
|
||||
event.preventDefault();
|
||||
var allFiles = $('#select_all').is(':checked');
|
||||
var files = [];
|
||||
var params = {};
|
||||
if (allFiles) {
|
||||
params = {
|
||||
allfiles: true,
|
||||
dir: FileList.getCurrentDirectory()
|
||||
};
|
||||
}
|
||||
else {
|
||||
files = _.pluck(FileList.getSelectedFiles(), 'name');
|
||||
params = {
|
||||
files: JSON.stringify(files),
|
||||
dir: FileList.getCurrentDirectory()
|
||||
};
|
||||
}
|
||||
initialize: function() {
|
||||
var result = OCA.Files.FileList.prototype.initialize.apply(this, arguments);
|
||||
this.$el.find('.undelete').click('click', _.bind(this._onClickRestoreSelected, this));
|
||||
|
||||
disableActions();
|
||||
if (allFiles) {
|
||||
FileList.showMask();
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete");
|
||||
deleteAction.removeClass('delete-icon').addClass('progress-icon');
|
||||
this.setSort('mtime', 'desc');
|
||||
/**
|
||||
* Override crumb making to add "Deleted Files" entry
|
||||
* and convert files with ".d" extensions to a more
|
||||
* user friendly name.
|
||||
*/
|
||||
this.breadcrumb._makeCrumbs = function() {
|
||||
var parts = OCA.Files.BreadCrumb.prototype._makeCrumbs.apply(this, arguments);
|
||||
for (var i = 1; i < parts.length; i++) {
|
||||
parts[i].name = getDeletedFileName(parts[i].name);
|
||||
}
|
||||
return parts;
|
||||
};
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Override to only return read permissions
|
||||
*/
|
||||
getDirectoryPermissions: function() {
|
||||
return OC.PERMISSION_READ | OC.PERMISSION_DELETE;
|
||||
},
|
||||
|
||||
_setCurrentDir: function(targetDir) {
|
||||
OCA.Files.FileList.prototype._setCurrentDir.apply(this, arguments);
|
||||
|
||||
var baseDir = OC.basename(targetDir);
|
||||
if (baseDir !== '') {
|
||||
this.setPageTitle(getDeletedFileName(baseDir));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
$.post(OC.filePath('files_trashbin', 'ajax', 'delete.php'),
|
||||
_createRow: function() {
|
||||
// FIXME: MEGAHACK until we find a better solution
|
||||
var tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments);
|
||||
tr.find('td.filesize').remove();
|
||||
return tr;
|
||||
},
|
||||
|
||||
_renderRow: function(fileData, options) {
|
||||
options = options || {};
|
||||
var dir = this.getCurrentDirectory();
|
||||
var dirListing = dir !== '' && dir !== '/';
|
||||
// show deleted time as mtime
|
||||
if (fileData.mtime) {
|
||||
fileData.mtime = parseInt(fileData.mtime, 10);
|
||||
}
|
||||
if (!dirListing) {
|
||||
fileData.displayName = fileData.name;
|
||||
fileData.name = fileData.name + '.d' + Math.floor(fileData.mtime / 1000);
|
||||
}
|
||||
return OCA.Files.FileList.prototype._renderRow.call(this, fileData, options);
|
||||
},
|
||||
|
||||
getAjaxUrl: function(action, params) {
|
||||
var q = '';
|
||||
if (params) {
|
||||
q = '?' + OC.buildQueryString(params);
|
||||
}
|
||||
return OC.filePath('files_trashbin', 'ajax', action + '.php') + q;
|
||||
},
|
||||
|
||||
setupUploadEvents: function() {
|
||||
// override and do nothing
|
||||
},
|
||||
|
||||
linkTo: function(dir){
|
||||
return OC.linkTo('files', 'index.php')+"?view=trashbin&dir="+ encodeURIComponent(dir).replace(/%2F/g, '/');
|
||||
},
|
||||
|
||||
updateEmptyContent: function(){
|
||||
var exists = this.$fileList.find('tr:first').exists();
|
||||
this.$el.find('#emptycontent').toggleClass('hidden', exists);
|
||||
this.$el.find('#filestable th').toggleClass('hidden', !exists);
|
||||
},
|
||||
|
||||
_removeCallback: function(result) {
|
||||
if (result.status !== 'success') {
|
||||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error'));
|
||||
}
|
||||
|
||||
var files = result.data.success;
|
||||
var $el;
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
$el = this.remove(OC.basename(files[i].filename), {updateSummary: false});
|
||||
this.fileSummary.remove({type: $el.attr('data-type'), size: $el.attr('data-size')});
|
||||
}
|
||||
this.fileSummary.update();
|
||||
this.updateEmptyContent();
|
||||
this.enableActions();
|
||||
},
|
||||
|
||||
_onClickRestoreSelected: function(event) {
|
||||
event.preventDefault();
|
||||
var self = this;
|
||||
var allFiles = this.$el.find('.select-all').is(':checked');
|
||||
var files = [];
|
||||
var params = {};
|
||||
this.disableActions();
|
||||
if (allFiles) {
|
||||
this.showMask();
|
||||
params = {
|
||||
allfiles: true,
|
||||
dir: this.getCurrentDirectory()
|
||||
};
|
||||
}
|
||||
else {
|
||||
files = _.pluck(this.getSelectedFiles(), 'name');
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var deleteAction = this.findFileEl(files[i]).children("td.date").children(".action.delete");
|
||||
deleteAction.removeClass('delete-icon').addClass('progress-icon');
|
||||
}
|
||||
params = {
|
||||
files: JSON.stringify(files),
|
||||
dir: this.getCurrentDirectory()
|
||||
};
|
||||
}
|
||||
|
||||
$.post(OC.filePath('files_trashbin', 'ajax', 'undelete.php'),
|
||||
params,
|
||||
function(result) {
|
||||
if (allFiles) {
|
||||
if (result.status !== 'success') {
|
||||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error'));
|
||||
}
|
||||
FileList.hideMask();
|
||||
self.hideMask();
|
||||
// simply remove all files
|
||||
FileList.setFiles([]);
|
||||
enableActions();
|
||||
self.setFiles([]);
|
||||
self.enableActions();
|
||||
}
|
||||
else {
|
||||
FileList._removeCallback(result);
|
||||
self._removeCallback(result);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
);
|
||||
},
|
||||
|
||||
var oldClickFile = FileList._onClickFile;
|
||||
FileList._onClickFile = function(event) {
|
||||
var mime = $(this).parent().parent().data('mime');
|
||||
if (mime !== 'httpd/unix-directory') {
|
||||
_onClickDeleteSelected: function(event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
return oldClickFile.apply(this, arguments);
|
||||
};
|
||||
var self = this;
|
||||
var allFiles = this.$el.find('.select-all').is(':checked');
|
||||
var files = [];
|
||||
var params = {};
|
||||
if (allFiles) {
|
||||
params = {
|
||||
allfiles: true,
|
||||
dir: this.getCurrentDirectory()
|
||||
};
|
||||
}
|
||||
else {
|
||||
files = _.pluck(this.getSelectedFiles(), 'name');
|
||||
params = {
|
||||
files: JSON.stringify(files),
|
||||
dir: this.getCurrentDirectory()
|
||||
};
|
||||
}
|
||||
|
||||
this.disableActions();
|
||||
if (allFiles) {
|
||||
this.showMask();
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var deleteAction = this.findFileEl(files[i]).children("td.date").children(".action.delete");
|
||||
deleteAction.removeClass('delete-icon').addClass('progress-icon');
|
||||
}
|
||||
}
|
||||
|
||||
$.post(OC.filePath('files_trashbin', 'ajax', 'delete.php'),
|
||||
params,
|
||||
function(result) {
|
||||
if (allFiles) {
|
||||
if (result.status !== 'success') {
|
||||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error'));
|
||||
}
|
||||
self.hideMask();
|
||||
// simply remove all files
|
||||
self.setFiles([]);
|
||||
self.enableActions();
|
||||
}
|
||||
else {
|
||||
self._removeCallback(result);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
_onClickFile: function(event) {
|
||||
var mime = $(this).parent().parent().data('mime');
|
||||
if (mime !== 'httpd/unix-directory') {
|
||||
event.preventDefault();
|
||||
}
|
||||
return OCA.Files.FileList.prototype._onClickFile.apply(this, arguments);
|
||||
},
|
||||
|
||||
generatePreviewUrl: function(urlSpec) {
|
||||
return OC.generateUrl('/apps/files_trashbin/ajax/preview.php?') + $.param(urlSpec);
|
||||
},
|
||||
|
||||
getDownloadUrl: function() {
|
||||
// no downloads
|
||||
return '#';
|
||||
},
|
||||
|
||||
enableActions: function() {
|
||||
this.$el.find('.action').css('display', 'inline');
|
||||
this.$el.find(':input:checkbox').css('display', 'inline');
|
||||
},
|
||||
|
||||
disableActions: function() {
|
||||
this.$el.find('.action').css('display', 'none');
|
||||
this.$el.find(':input:checkbox').css('display', 'none');
|
||||
},
|
||||
|
||||
updateStorageStatistics: function() {
|
||||
// no op because the trashbin doesn't have
|
||||
// storage info like free space / used space
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
OCA.Trashbin.FileList = FileList;
|
||||
})();
|
||||
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2014
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
|
||||
/* global OC, t, BreadCrumb, FileActions, FileList, Files */
|
||||
$(document).ready(function() {
|
||||
var deletedRegExp = new RegExp(/^(.+)\.d[0-9]+$/);
|
||||
|
||||
/**
|
||||
* Convert a file name in the format filename.d12345 to the real file name.
|
||||
* This will use basename.
|
||||
* The name will not be changed if it has no ".d12345" suffix.
|
||||
* @param name file name
|
||||
* @return converted file name
|
||||
*/
|
||||
function getDeletedFileName(name) {
|
||||
name = OC.basename(name);
|
||||
var match = deletedRegExp.exec(name);
|
||||
if (match && match.length > 1) {
|
||||
name = match[1];
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
Files.updateStorageStatistics = function() {
|
||||
// no op because the trashbin doesn't have
|
||||
// storage info like free space / used space
|
||||
};
|
||||
|
||||
if (typeof FileActions !== 'undefined') {
|
||||
FileActions.register('all', 'Restore', OC.PERMISSION_READ, OC.imagePath('core', 'actions/history'), function(filename) {
|
||||
var tr = FileList.findFileEl(filename);
|
||||
var deleteAction = tr.children("td.date").children(".action.delete");
|
||||
deleteAction.removeClass('delete-icon').addClass('progress-icon');
|
||||
disableActions();
|
||||
$.post(OC.filePath('files_trashbin', 'ajax', 'undelete.php'), {
|
||||
files: JSON.stringify([filename]),
|
||||
dir: FileList.getCurrentDirectory()
|
||||
},
|
||||
FileList._removeCallback
|
||||
);
|
||||
}, t('files_trashbin', 'Restore'));
|
||||
};
|
||||
|
||||
FileActions.register('all', 'Delete', OC.PERMISSION_READ, function() {
|
||||
return OC.imagePath('core', 'actions/delete');
|
||||
}, function(filename) {
|
||||
$('.tipsy').remove();
|
||||
var tr = FileList.findFileEl(filename);
|
||||
var deleteAction = tr.children("td.date").children(".action.delete");
|
||||
deleteAction.removeClass('delete-icon').addClass('progress-icon');
|
||||
disableActions();
|
||||
$.post(OC.filePath('files_trashbin', 'ajax', 'delete.php'), {
|
||||
files: JSON.stringify([filename]),
|
||||
dir: FileList.getCurrentDirectory()
|
||||
},
|
||||
FileList._removeCallback
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Override crumb URL maker (hacky!)
|
||||
*/
|
||||
FileList.breadcrumb.getCrumbUrl = function(part, index) {
|
||||
if (index === 0) {
|
||||
return OC.linkTo('files', 'index.php');
|
||||
}
|
||||
return OC.linkTo('files_trashbin', 'index.php')+"?dir=" + encodeURIComponent(part.dir);
|
||||
};
|
||||
|
||||
Files.generatePreviewUrl = function(urlSpec) {
|
||||
return OC.generateUrl('/apps/files_trashbin/ajax/preview.php?') + $.param(urlSpec);
|
||||
};
|
||||
|
||||
Files.getDownloadUrl = function(action, params) {
|
||||
// no downloads
|
||||
return '#';
|
||||
};
|
||||
|
||||
Files.getAjaxUrl = function(action, params) {
|
||||
var q = '';
|
||||
if (params) {
|
||||
q = '?' + OC.buildQueryString(params);
|
||||
}
|
||||
return OC.filePath('files_trashbin', 'ajax', action + '.php') + q;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Override crumb making to add "Deleted Files" entry
|
||||
* and convert files with ".d" extensions to a more
|
||||
* user friendly name.
|
||||
*/
|
||||
var oldMakeCrumbs = BreadCrumb.prototype._makeCrumbs;
|
||||
BreadCrumb.prototype._makeCrumbs = function() {
|
||||
var parts = oldMakeCrumbs.apply(this, arguments);
|
||||
// duplicate first part
|
||||
parts.unshift(parts[0]);
|
||||
parts[1] = {
|
||||
dir: '/',
|
||||
name: t('files_trashbin', 'Deleted Files')
|
||||
};
|
||||
for (var i = 2; i < parts.length; i++) {
|
||||
parts[i].name = getDeletedFileName(parts[i].name);
|
||||
}
|
||||
return parts;
|
||||
};
|
||||
|
||||
FileActions.actions.dir = {
|
||||
// only keep 'Open' action for navigation
|
||||
'Open': FileActions.actions.dir.Open
|
||||
};
|
||||
});
|
||||
|
||||
function enableActions() {
|
||||
$(".action").css("display", "inline");
|
||||
$(":input:checkbox").css("display", "inline");
|
||||
}
|
||||
|
||||
function disableActions() {
|
||||
$(".action").css("display", "none");
|
||||
$(":input:checkbox").css("display", "none");
|
||||
}
|
||||
|
11
apps/files_trashbin/list.php
Normal file
11
apps/files_trashbin/list.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
// Check if we are a user
|
||||
OCP\User::checkLoggedIn();
|
||||
|
||||
|
||||
$tmpl = new OCP\Template('files_trashbin', 'index', '');
|
||||
OCP\Util::addStyle('files_trashbin', 'trash');
|
||||
OCP\Util::addScript('files_trashbin', 'app');
|
||||
OCP\Util::addScript('files_trashbin', 'filelist');
|
||||
$tmpl->printPage();
|
|
@ -6,17 +6,15 @@
|
|||
|
||||
<div id="emptycontent" class="hidden"><?php p($l->t('Nothing in here. Your trash bin is empty!'))?></div>
|
||||
|
||||
<input type="hidden" id="permissions" value="0">
|
||||
<input type="hidden" id="disableSharing" data-status="<?php p($_['disableSharing']); ?>">
|
||||
<input type="hidden" name="dir" value="<?php p($_['dir']) ?>" id="dir">
|
||||
<input type="hidden" name="dir" value="" id="dir">
|
||||
|
||||
<table id="filestable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th id='headerName' class="hidden column-name">
|
||||
<div id="headerName-container">
|
||||
<input type="checkbox" id="select_all" />
|
||||
<label for="select_all"></label>
|
||||
<input type="checkbox" id="select_all_trash" class="select-all"/>
|
||||
<label for="select_all_trash"></label>
|
||||
<a class="name sort columntitle" data-sort="name"><span><?php p($l->t( 'Name' )); ?></span><span class="sort-indicator"></span></a>
|
||||
<span id="selectedActionsList" class='selectedActions'>
|
||||
<a href="" class="undelete">
|
||||
|
|
69
apps/files_trashbin/tests/js/appSpec.js
Normal file
69
apps/files_trashbin/tests/js/appSpec.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Vincent Petry
|
||||
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
describe('OCA.Trashbin.App tests', function() {
|
||||
var App = OCA.Trashbin.App;
|
||||
|
||||
beforeEach(function() {
|
||||
$('#testArea').append(
|
||||
'<div id="app-navigation">' +
|
||||
'<ul><li data-id="files"><a>Files</a></li>' +
|
||||
'<li data-id="trashbin"><a>Trashbin</a></li>' +
|
||||
'</div>' +
|
||||
'<div id="app-content">' +
|
||||
'<div id="app-content-files" class="hidden">' +
|
||||
'</div>' +
|
||||
'<div id="app-content-trashbin" class="hidden">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
);
|
||||
App.initialize($('#app-content-trashbin'));
|
||||
});
|
||||
afterEach(function() {
|
||||
App._initialized = false;
|
||||
App.fileList = null;
|
||||
});
|
||||
|
||||
describe('initialization', function() {
|
||||
it('creates a custom filelist instance', function() {
|
||||
App.initialize();
|
||||
expect(App.fileList).toBeDefined();
|
||||
expect(App.fileList.$el.is('#app-content-trashbin')).toEqual(true);
|
||||
});
|
||||
|
||||
it('registers custom file actions', function() {
|
||||
var fileActions;
|
||||
App.initialize();
|
||||
|
||||
fileActions = App.fileList.fileActions;
|
||||
|
||||
expect(fileActions.actions.all).toBeDefined();
|
||||
expect(fileActions.actions.all.Restore).toBeDefined();
|
||||
expect(fileActions.actions.all.Delete).toBeDefined();
|
||||
|
||||
expect(fileActions.actions.all.Rename).not.toBeDefined();
|
||||
expect(fileActions.actions.all.Download).not.toBeDefined();
|
||||
|
||||
expect(fileActions.defaults.dir).toEqual('Open');
|
||||
});
|
||||
});
|
||||
});
|
309
apps/files_trashbin/tests/js/filelistSpec.js
Normal file
309
apps/files_trashbin/tests/js/filelistSpec.js
Normal file
|
@ -0,0 +1,309 @@
|
|||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Vincent Petry
|
||||
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
describe('OCA.Trashbin.FileList tests', function() {
|
||||
var testFiles, alertStub, notificationStub, fileList;
|
||||
var FileActions = OCA.Files.FileActions;
|
||||
|
||||
beforeEach(function() {
|
||||
alertStub = sinon.stub(OC.dialogs, 'alert');
|
||||
notificationStub = sinon.stub(OC.Notification, 'show');
|
||||
|
||||
// init parameters and test table elements
|
||||
$('#testArea').append(
|
||||
'<div id="app-content-trashbin">' +
|
||||
// init horrible parameters
|
||||
'<input type="hidden" id="dir" value="/"></input>' +
|
||||
// set this but it shouldn't be used (could be the one from the
|
||||
// files app)
|
||||
'<input type="hidden" id="permissions" value="31"></input>' +
|
||||
// dummy controls
|
||||
'<div id="controls">' +
|
||||
' <div class="actions creatable"></div>' +
|
||||
' <div class="notCreatable"></div>' +
|
||||
'</div>' +
|
||||
// dummy table
|
||||
// TODO: at some point this will be rendered by the fileList class itself!
|
||||
'<table id="filestable">' +
|
||||
'<thead><tr><th id="headerName" class="hidden">' +
|
||||
'<input type="checkbox" id="select_all_trash" class="select-all">' +
|
||||
'<span class="name">Name</span>' +
|
||||
'<span class="selectedActions hidden">' +
|
||||
'<a href class="undelete">Restore</a>' +
|
||||
'<a href class="delete-selected">Delete</a></span>' +
|
||||
'</th></tr></thead>' +
|
||||
'<tbody id="fileList"></tbody>' +
|
||||
'<tfoot></tfoot>' +
|
||||
'</table>' +
|
||||
'<div id="emptycontent">Empty content message</div>' +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
testFiles = [{
|
||||
id: 1,
|
||||
type: 'file',
|
||||
name: 'One.txt',
|
||||
mtime: 11111000,
|
||||
mimetype: 'text/plain',
|
||||
etag: 'abc'
|
||||
}, {
|
||||
id: 2,
|
||||
type: 'file',
|
||||
name: 'Two.jpg',
|
||||
mtime: 22222000,
|
||||
mimetype: 'image/jpeg',
|
||||
etag: 'def',
|
||||
}, {
|
||||
id: 3,
|
||||
type: 'file',
|
||||
name: 'Three.pdf',
|
||||
mtime: 33333000,
|
||||
mimetype: 'application/pdf',
|
||||
etag: '123',
|
||||
}, {
|
||||
id: 4,
|
||||
type: 'dir',
|
||||
mtime: 99999000,
|
||||
name: 'somedir',
|
||||
mimetype: 'httpd/unix-directory',
|
||||
etag: '456'
|
||||
}];
|
||||
|
||||
fileList = new OCA.Trashbin.FileList($('#app-content-trashbin'));
|
||||
OCA.Trashbin.App.registerFileActions(fileList);
|
||||
});
|
||||
afterEach(function() {
|
||||
testFiles = undefined;
|
||||
fileList = undefined;
|
||||
|
||||
FileActions.clear();
|
||||
$('#dir').remove();
|
||||
notificationStub.restore();
|
||||
alertStub.restore();
|
||||
});
|
||||
describe('Initialization', function() {
|
||||
it('Sorts by mtime by default', function() {
|
||||
expect(fileList._sort).toEqual('mtime');
|
||||
expect(fileList._sortDirection).toEqual('desc');
|
||||
});
|
||||
it('Always returns read and delete permission', function() {
|
||||
expect(fileList.getDirectoryPermissions()).toEqual(OC.PERMISSION_READ | OC.PERMISSION_DELETE);
|
||||
});
|
||||
});
|
||||
describe('Breadcrumbs', function() {
|
||||
beforeEach(function() {
|
||||
var data = {
|
||||
status: 'success',
|
||||
data: {
|
||||
files: testFiles,
|
||||
permissions: 1
|
||||
}
|
||||
};
|
||||
fakeServer.respondWith(/\/index\.php\/apps\/files_trashbin\/ajax\/list.php\?dir=%2Fsubdir/, [
|
||||
200, {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
JSON.stringify(data)
|
||||
]);
|
||||
});
|
||||
it('links the breadcrumb to the trashbin view', function() {
|
||||
fileList.changeDirectory('/subdir', false, true);
|
||||
fakeServer.respond();
|
||||
var $crumbs = fileList.$el.find('#controls .crumb');
|
||||
expect($crumbs.length).toEqual(2);
|
||||
expect($crumbs.eq(0).find('a').text()).toEqual('');
|
||||
expect($crumbs.eq(0).find('a').attr('href'))
|
||||
.toEqual(OC.webroot + '/index.php/apps/files?view=trashbin&dir=/');
|
||||
expect($crumbs.eq(1).find('a').text()).toEqual('subdir');
|
||||
expect($crumbs.eq(1).find('a').attr('href'))
|
||||
.toEqual(OC.webroot + '/index.php/apps/files?view=trashbin&dir=/subdir');
|
||||
});
|
||||
});
|
||||
describe('Rendering rows', function() {
|
||||
it('renders rows with the correct data when in root', function() {
|
||||
// dir listing is false when in root
|
||||
$('#dir').val('/');
|
||||
fileList.setFiles(testFiles);
|
||||
var $rows = fileList.$el.find('tbody tr');
|
||||
var $tr = $rows.eq(0);
|
||||
expect($rows.length).toEqual(4);
|
||||
expect($tr.attr('data-id')).toEqual('1');
|
||||
expect($tr.attr('data-type')).toEqual('file');
|
||||
expect($tr.attr('data-file')).toEqual('One.txt.d11111');
|
||||
expect($tr.attr('data-size')).not.toBeDefined();
|
||||
expect($tr.attr('data-etag')).toEqual('abc');
|
||||
expect($tr.attr('data-permissions')).toEqual('9'); // read and delete
|
||||
expect($tr.attr('data-mime')).toEqual('text/plain');
|
||||
expect($tr.attr('data-mtime')).toEqual('11111000');
|
||||
expect($tr.find('a.name').attr('href')).toEqual('#');
|
||||
|
||||
expect($tr.find('.nametext').text().trim()).toEqual('One.txt');
|
||||
|
||||
expect(fileList.findFileEl('One.txt.d11111')[0]).toEqual($tr[0]);
|
||||
});
|
||||
it('renders rows with the correct data when in subdirectory', function() {
|
||||
// dir listing is true when in a subdir
|
||||
$('#dir').val('/subdir');
|
||||
|
||||
fileList.setFiles(testFiles);
|
||||
var $rows = fileList.$el.find('tbody tr');
|
||||
var $tr = $rows.eq(0);
|
||||
expect($rows.length).toEqual(4);
|
||||
expect($tr.attr('data-id')).toEqual('1');
|
||||
expect($tr.attr('data-type')).toEqual('file');
|
||||
expect($tr.attr('data-file')).toEqual('One.txt');
|
||||
expect($tr.attr('data-size')).not.toBeDefined();
|
||||
expect($tr.attr('data-etag')).toEqual('abc');
|
||||
expect($tr.attr('data-permissions')).toEqual('9'); // read and delete
|
||||
expect($tr.attr('data-mime')).toEqual('text/plain');
|
||||
expect($tr.attr('data-mtime')).toEqual('11111000');
|
||||
expect($tr.find('a.name').attr('href')).toEqual('#');
|
||||
|
||||
expect($tr.find('.nametext').text().trim()).toEqual('One.txt');
|
||||
|
||||
expect(fileList.findFileEl('One.txt')[0]).toEqual($tr[0]);
|
||||
});
|
||||
it('does not render a size column', function() {
|
||||
expect(fileList.$el.find('tbody tr .filesize').length).toEqual(0);
|
||||
});
|
||||
});
|
||||
describe('File actions', function() {
|
||||
describe('Deleting single files', function() {
|
||||
// TODO: checks ajax call
|
||||
// TODO: checks spinner
|
||||
// TODO: remove item after delete
|
||||
// TODO: bring back item if delete failed
|
||||
});
|
||||
describe('Restoring single files', function() {
|
||||
// TODO: checks ajax call
|
||||
// TODO: checks spinner
|
||||
// TODO: remove item after restore
|
||||
// TODO: bring back item if restore failed
|
||||
});
|
||||
});
|
||||
describe('file previews', function() {
|
||||
// TODO: check that preview URL is going through files_trashbin
|
||||
});
|
||||
describe('loading file list', function() {
|
||||
// TODO: check that ajax URL is going through files_trashbin
|
||||
});
|
||||
describe('breadcrumbs', function() {
|
||||
// TODO: test label + URL
|
||||
});
|
||||
describe('Global Actions', function() {
|
||||
beforeEach(function() {
|
||||
fileList.setFiles(testFiles);
|
||||
fileList.findFileEl('One.txt.d11111').find('input:checkbox').click();
|
||||
fileList.findFileEl('Three.pdf.d33333').find('input:checkbox').click();
|
||||
fileList.findFileEl('somedir.d99999').find('input:checkbox').click();
|
||||
});
|
||||
describe('Delete', function() {
|
||||
it('Deletes selected files when "Delete" clicked', function() {
|
||||
var request;
|
||||
$('.selectedActions .delete-selected').click();
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
request = fakeServer.requests[0];
|
||||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/delete.php');
|
||||
expect(OC.parseQueryString(request.requestBody))
|
||||
.toEqual({'dir': '/', files: '["One.txt.d11111","Three.pdf.d33333","somedir.d99999"]'});
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({
|
||||
status: 'success',
|
||||
data: {
|
||||
success: [
|
||||
{filename: 'One.txt.d11111'},
|
||||
{filename: 'Three.pdf.d33333'},
|
||||
{filename: 'somedir.d99999'}
|
||||
]
|
||||
}
|
||||
})
|
||||
);
|
||||
expect(fileList.findFileEl('One.txt.d11111').length).toEqual(0);
|
||||
expect(fileList.findFileEl('Three.pdf.d33333').length).toEqual(0);
|
||||
expect(fileList.findFileEl('somedir.d99999').length).toEqual(0);
|
||||
expect(fileList.findFileEl('Two.jpg.d22222').length).toEqual(1);
|
||||
});
|
||||
it('Deletes all files when all selected when "Delete" clicked', function() {
|
||||
var request;
|
||||
$('.select-all').click();
|
||||
$('.selectedActions .delete-selected').click();
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
request = fakeServer.requests[0];
|
||||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/delete.php');
|
||||
expect(OC.parseQueryString(request.requestBody))
|
||||
.toEqual({'dir': '/', allfiles: 'true'});
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({status: 'success'})
|
||||
);
|
||||
expect(fileList.isEmpty).toEqual(true);
|
||||
});
|
||||
});
|
||||
describe('Restore', function() {
|
||||
it('Restores selected files when "Restore" clicked', function() {
|
||||
var request;
|
||||
$('.selectedActions .undelete').click();
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
request = fakeServer.requests[0];
|
||||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/undelete.php');
|
||||
expect(OC.parseQueryString(request.requestBody))
|
||||
.toEqual({'dir': '/', files: '["One.txt.d11111","Three.pdf.d33333","somedir.d99999"]'});
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({
|
||||
status: 'success',
|
||||
data: {
|
||||
success: [
|
||||
{filename: 'One.txt.d11111'},
|
||||
{filename: 'Three.pdf.d33333'},
|
||||
{filename: 'somedir.d99999'}
|
||||
]
|
||||
}
|
||||
})
|
||||
);
|
||||
expect(fileList.findFileEl('One.txt.d11111').length).toEqual(0);
|
||||
expect(fileList.findFileEl('Three.pdf.d33333').length).toEqual(0);
|
||||
expect(fileList.findFileEl('somedir.d99999').length).toEqual(0);
|
||||
expect(fileList.findFileEl('Two.jpg.d22222').length).toEqual(1);
|
||||
});
|
||||
it('Restores all files when all selected when "Restore" clicked', function() {
|
||||
var request;
|
||||
$('.select-all').click();
|
||||
$('.selectedActions .undelete').click();
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
request = fakeServer.requests[0];
|
||||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/undelete.php');
|
||||
expect(OC.parseQueryString(request.requestBody))
|
||||
.toEqual({'dir': '/', allfiles: 'true'});
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({status: 'success'})
|
||||
);
|
||||
expect(fileList.isEmpty).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -178,6 +178,9 @@
|
|||
bottom: 0;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
#app-settings.opened #app-settings-content {
|
||||
display: block;
|
||||
}
|
||||
#app-settings-header {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
|
113
core/js/js.js
113
core/js/js.js
|
@ -1318,6 +1318,114 @@ OC.Util = {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility class for the history API,
|
||||
* includes fallback to using the URL hash when
|
||||
* the browser doesn't support the history API.
|
||||
*/
|
||||
OC.Util.History = {
|
||||
_handlers: [],
|
||||
|
||||
/**
|
||||
* Push the current URL parameters to the history stack
|
||||
* and change the visible URL.
|
||||
* Note: this includes a workaround for IE8/IE9 that uses
|
||||
* the hash part instead of the search part.
|
||||
*
|
||||
* @param params to append to the URL, can be either a string
|
||||
* or a map
|
||||
*/
|
||||
pushState: function(params) {
|
||||
var strParams;
|
||||
if (typeof(params) === 'string') {
|
||||
strParams = params;
|
||||
}
|
||||
else {
|
||||
strParams = OC.buildQueryString(params);
|
||||
}
|
||||
if (window.history.pushState) {
|
||||
var url = location.pathname + '?' + strParams;
|
||||
window.history.pushState(params, '', url);
|
||||
}
|
||||
// use URL hash for IE8
|
||||
else {
|
||||
window.location.hash = '?' + strParams;
|
||||
// inhibit next onhashchange that just added itself
|
||||
// to the event queue
|
||||
this._cancelPop = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a popstate handler
|
||||
*
|
||||
* @param handler function
|
||||
*/
|
||||
addOnPopStateHandler: function(handler) {
|
||||
this._handlers.push(handler);
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a query string from the hash part of the URL.
|
||||
* (workaround for IE8 / IE9)
|
||||
*/
|
||||
_parseHashQuery: function() {
|
||||
var hash = window.location.hash,
|
||||
pos = hash.indexOf('?');
|
||||
if (pos >= 0) {
|
||||
return hash.substr(pos + 1);
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
_decodeQuery: function(query) {
|
||||
return query.replace(/\+/g, ' ');
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse the query/search part of the URL.
|
||||
* Also try and parse it from the URL hash (for IE8)
|
||||
*
|
||||
* @return map of parameters
|
||||
*/
|
||||
parseUrlQuery: function() {
|
||||
var query = this._parseHashQuery(),
|
||||
params;
|
||||
// try and parse from URL hash first
|
||||
if (query) {
|
||||
params = OC.parseQueryString(this._decodeQuery(query));
|
||||
}
|
||||
// else read from query attributes
|
||||
if (!params) {
|
||||
params = OC.parseQueryString(this._decodeQuery(location.search));
|
||||
}
|
||||
return params || {};
|
||||
},
|
||||
|
||||
_onPopState: function(e) {
|
||||
if (this._cancelPop) {
|
||||
this._cancelPop = false;
|
||||
return;
|
||||
}
|
||||
var params;
|
||||
if (!this._handlers.length) {
|
||||
return;
|
||||
}
|
||||
params = (e && e.state) || this.parseUrlQuery() || {};
|
||||
for (var i = 0; i < this._handlers.length; i++) {
|
||||
this._handlers[i](params);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// fallback to hashchange when no history support
|
||||
if (window.history.pushState) {
|
||||
window.onpopstate = _.bind(OC.Util.History._onPopState, OC.Util.History);
|
||||
}
|
||||
else {
|
||||
$(window).on('hashchange', _.bind(OC.Util.History._onPopState, OC.Util.History));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a variable by name
|
||||
* @param {string} name
|
||||
|
@ -1367,6 +1475,11 @@ OC.set=function(name, value) {
|
|||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* Namespace for apps
|
||||
*/
|
||||
window.OCA = {};
|
||||
|
||||
/**
|
||||
* select a range in an input field
|
||||
* @link http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area
|
||||
|
|
|
@ -123,7 +123,7 @@
|
|||
</div></nav>
|
||||
|
||||
<div id="content-wrapper">
|
||||
<div id="content">
|
||||
<div id="content" class="app-<?php p($_['appid']) ?>">
|
||||
<?php print_unescaped($_['content']); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -30,6 +30,7 @@ class OC_Template extends \OC\Template\Base {
|
|||
private $renderas; // Create a full page?
|
||||
private $path; // The path to the template
|
||||
private $headers=array(); //custom headers
|
||||
protected $app; // app id
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
|
@ -62,6 +63,7 @@ class OC_Template extends \OC\Template\Base {
|
|||
// Set the private data
|
||||
$this->renderas = $renderas;
|
||||
$this->path = $path;
|
||||
$this->app = $app;
|
||||
|
||||
parent::__construct($template, $requesttoken, $l10n, $themeDefaults);
|
||||
}
|
||||
|
@ -172,7 +174,7 @@ class OC_Template extends \OC\Template\Base {
|
|||
$data = parent::fetchPage();
|
||||
|
||||
if( $this->renderas ) {
|
||||
$page = new OC_TemplateLayout($this->renderas);
|
||||
$page = new OC_TemplateLayout($this->renderas, $this->app);
|
||||
|
||||
// Add custom headers
|
||||
$page->assign('headers', $this->headers, false);
|
||||
|
|
|
@ -15,8 +15,9 @@ class OC_TemplateLayout extends OC_Template {
|
|||
|
||||
/**
|
||||
* @param string $renderas
|
||||
* @param string $appid application id
|
||||
*/
|
||||
public function __construct( $renderas ) {
|
||||
public function __construct( $renderas, $appid = '' ) {
|
||||
// Decide which page we show
|
||||
|
||||
if( $renderas == 'user' ) {
|
||||
|
@ -43,6 +44,7 @@ class OC_TemplateLayout extends OC_Template {
|
|||
|
||||
// Add navigation entry
|
||||
$this->assign( 'application', '', false );
|
||||
$this->assign( 'appid', $appid );
|
||||
$navigation = OC_App::getNavigation();
|
||||
$this->assign( 'navigation', $navigation);
|
||||
$this->assign( 'settingsnavigation', OC_App::getSettingsNavigation());
|
||||
|
|
|
@ -133,12 +133,6 @@ if($_['passwordChangeSupported']) {
|
|||
<?php endif; ?>
|
||||
</form>
|
||||
|
||||
<div class="section">
|
||||
<h2><?php p($l->t('WebDAV'));?></h2>
|
||||
<code><?php print_unescaped(OC_Helper::linkToRemote('webdav')); ?></code><br />
|
||||
<em><?php print_unescaped($l->t('Use this address to <a href="%s" target="_blank">access your Files via WebDAV</a>', array(link_to_docs('user-webdav'))));?></em>
|
||||
</div>
|
||||
|
||||
<?php foreach($_['forms'] as $form) {
|
||||
print_unescaped($form);
|
||||
};?>
|
||||
|
|
|
@ -43,7 +43,7 @@ module.exports = function(config) {
|
|||
return apps;
|
||||
*/
|
||||
// other apps tests don't run yet... needs further research / clean up
|
||||
return ['files'];
|
||||
return ['files', 'files_trashbin'];
|
||||
}
|
||||
|
||||
// respect NOCOVERAGE env variable
|
||||
|
|
Loading…
Reference in a new issue