Added favorites feature to the files app
This commit is contained in:
parent
c6be491a89
commit
a5bb66f4a7
|
@ -11,6 +11,8 @@ namespace OCA\Files\Appinfo;
|
||||||
use OC\AppFramework\Utility\SimpleContainer;
|
use OC\AppFramework\Utility\SimpleContainer;
|
||||||
use OCA\Files\Controller\ApiController;
|
use OCA\Files\Controller\ApiController;
|
||||||
use OCP\AppFramework\App;
|
use OCP\AppFramework\App;
|
||||||
|
use \OCA\Files\Service\TagService;
|
||||||
|
use \OCP\IContainer;
|
||||||
|
|
||||||
class Application extends App {
|
class Application extends App {
|
||||||
public function __construct(array $urlParams=array()) {
|
public function __construct(array $urlParams=array()) {
|
||||||
|
@ -21,10 +23,44 @@ class Application extends App {
|
||||||
/**
|
/**
|
||||||
* Controllers
|
* Controllers
|
||||||
*/
|
*/
|
||||||
$container->registerService('APIController', function (SimpleContainer $c) {
|
$container->registerService('APIController', function (IContainer $c) {
|
||||||
return new ApiController(
|
return new ApiController(
|
||||||
$c->query('AppName'),
|
$c->query('AppName'),
|
||||||
$c->query('Request')
|
$c->query('Request'),
|
||||||
|
$c->query('TagService')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Core
|
||||||
|
*/
|
||||||
|
$container->registerService('L10N', function(IContainer $c) {
|
||||||
|
return $c->query('ServerContainer')->getL10N($c->query('AppName'));
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Services
|
||||||
|
*/
|
||||||
|
$container->registerService('Tagger', function(IContainer $c) {
|
||||||
|
return $c->query('ServerContainer')->getTagManager()->load('files');
|
||||||
|
});
|
||||||
|
$container->registerService('TagService', function(IContainer $c) {
|
||||||
|
$homeFolder = $c->query('ServerContainer')->getUserFolder();
|
||||||
|
return new TagService(
|
||||||
|
$c->query('ServerContainer')->getUserSession(),
|
||||||
|
$c->query('Tagger'),
|
||||||
|
$homeFolder
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controllers
|
||||||
|
*/
|
||||||
|
$container->registerService('APIController', function (IContainer $c) {
|
||||||
|
return new ApiController(
|
||||||
|
$c->query('AppName'),
|
||||||
|
$c->query('Request'),
|
||||||
|
$c->query('TagService')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,31 @@
|
||||||
namespace OCA\Files\Appinfo;
|
namespace OCA\Files\Appinfo;
|
||||||
|
|
||||||
$application = new Application();
|
$application = new Application();
|
||||||
$application->registerRoutes($this, array('routes' => array(
|
$application->registerRoutes(
|
||||||
array('name' => 'API#getThumbnail', 'url' => '/api/v1/thumbnail/{x}/{y}/{file}', 'verb' => 'GET', 'requirements' => array('file' => '.+')),
|
$this,
|
||||||
)));
|
array(
|
||||||
|
'routes' => array(
|
||||||
|
array(
|
||||||
|
'name' => 'API#getThumbnail',
|
||||||
|
'url' => '/api/v1/thumbnail/{x}/{y}/{file}',
|
||||||
|
'verb' => 'GET',
|
||||||
|
'requirements' => array('file' => '.+')
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'API#updateFileTags',
|
||||||
|
'url' => '/api/v1/files/{path}',
|
||||||
|
'verb' => 'POST',
|
||||||
|
'requirements' => array('path' => '.+'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'API#getFilesByTag',
|
||||||
|
'url' => '/api/v1/tags/{tagName}/files',
|
||||||
|
'verb' => 'GET',
|
||||||
|
'requirements' => array('tagName' => '.+'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
/** @var $this \OC\Route\Router */
|
/** @var $this \OC\Route\Router */
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,16 @@ use OCP\AppFramework\Http;
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use OCP\AppFramework\Http\JSONResponse;
|
use OCP\AppFramework\Http\JSONResponse;
|
||||||
|
use OCP\AppFramework\Http\DataResponse;
|
||||||
use OCP\AppFramework\Http\DownloadResponse;
|
use OCP\AppFramework\Http\DownloadResponse;
|
||||||
use OC\Preview;
|
use OC\Preview;
|
||||||
|
use OCA\Files\Service\TagService;
|
||||||
|
|
||||||
class ApiController extends Controller {
|
class ApiController extends Controller {
|
||||||
|
|
||||||
public function __construct($appName, IRequest $request){
|
public function __construct($appName, IRequest $request, TagService $tagService){
|
||||||
parent::__construct($appName, $request);
|
parent::__construct($appName, $request);
|
||||||
|
$this->tagService = $tagService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,4 +52,50 @@ class ApiController extends Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the info of the specified file path
|
||||||
|
* The passed tags are absolute, which means they will
|
||||||
|
* replace the actual tag selection.
|
||||||
|
*
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @CORS
|
||||||
|
*
|
||||||
|
* @param string $path path
|
||||||
|
* @param array $tags array of tags
|
||||||
|
*/
|
||||||
|
public function updateFileTags($path, $tags = null) {
|
||||||
|
$result = array();
|
||||||
|
// if tags specified or empty array, update tags
|
||||||
|
if (!is_null($tags)) {
|
||||||
|
try {
|
||||||
|
$this->tagService->updateFileTags($path, $tags);
|
||||||
|
} catch (\OCP\Files\NotFoundException $e) {
|
||||||
|
return new DataResponse($e->getMessage(), Http::STATUS_NOT_FOUND);
|
||||||
|
}
|
||||||
|
$result['tags'] = $tags;
|
||||||
|
}
|
||||||
|
return new DataResponse($result, Http::STATUS_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all files tagged with the given tag.
|
||||||
|
*
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @CORS
|
||||||
|
*
|
||||||
|
* @param array $tagName tag name to filter by
|
||||||
|
*/
|
||||||
|
public function getFilesByTag($tagName) {
|
||||||
|
$files = array();
|
||||||
|
$fileInfos = $this->tagService->getFilesByTag($tagName);
|
||||||
|
foreach ($fileInfos as &$fileInfo) {
|
||||||
|
$file = \OCA\Files\Helper::formatFileInfo($fileInfo);
|
||||||
|
$parts = explode('/', dirname($fileInfo->getPath()), 4);
|
||||||
|
$file['path'] = '/' . $parts[3];
|
||||||
|
$file['tags'] = array($tagName);
|
||||||
|
$files[] = $file;
|
||||||
|
}
|
||||||
|
return new DataResponse(array('files' => $files), Http::STATUS_OK);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -286,6 +286,9 @@ table td.filename .nametext {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
#fileList.has-favorites td.filename a.name {
|
||||||
|
left: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
table td.filename .nametext .innernametext {
|
table td.filename .nametext .innernametext {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@ -417,6 +420,18 @@ table td.filename .uploadtext {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#fileList tr td.filename .favorite {
|
||||||
|
display: inline-block;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
#fileList tr td.filename .action-favorite {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
width: 30px;
|
||||||
|
line-height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
#uploadsize-message,#delete-confirm { display:none; }
|
#uploadsize-message,#delete-confirm { display:none; }
|
||||||
|
|
||||||
/* File actions */
|
/* File actions */
|
||||||
|
@ -442,7 +457,7 @@ table td.filename .uploadtext {
|
||||||
padding: 17px 14px;
|
padding: 17px 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#fileList .action.action-share-notification span, #fileList a {
|
#fileList .action.action-share-notification span, #fileList a.name {
|
||||||
cursor: default !important;
|
cursor: default !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
use OCA\Files\Appinfo\Application;
|
||||||
|
|
||||||
// Check if we are a user
|
// Check if we are a user
|
||||||
OCP\User::checkLoggedIn();
|
OCP\User::checkLoggedIn();
|
||||||
|
@ -38,8 +39,14 @@ OCP\Util::addscript('files', 'filesummary');
|
||||||
OCP\Util::addscript('files', 'breadcrumb');
|
OCP\Util::addscript('files', 'breadcrumb');
|
||||||
OCP\Util::addscript('files', 'filelist');
|
OCP\Util::addscript('files', 'filelist');
|
||||||
|
|
||||||
|
\OCP\Util::addScript('files', 'favoritesfilelist');
|
||||||
|
\OCP\Util::addScript('files', 'tagsplugin');
|
||||||
|
\OCP\Util::addScript('files', 'favoritesplugin');
|
||||||
|
|
||||||
OCP\App::setActiveNavigationEntry('files_index');
|
OCP\App::setActiveNavigationEntry('files_index');
|
||||||
|
|
||||||
|
$l = \OC::$server->getL10N('files');
|
||||||
|
|
||||||
$isIE8 = false;
|
$isIE8 = false;
|
||||||
preg_match('/MSIE (.*?);/', $_SERVER['HTTP_USER_AGENT'], $matches);
|
preg_match('/MSIE (.*?);/', $_SERVER['HTTP_USER_AGENT'], $matches);
|
||||||
if (count($matches) > 0 && $matches[1] <= 9) {
|
if (count($matches) > 0 && $matches[1] <= 9) {
|
||||||
|
@ -79,6 +86,16 @@ function sortNavigationItems($item1, $item2) {
|
||||||
return $item1['order'] - $item2['order'];
|
return $item1['order'] - $item2['order'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
\OCA\Files\App::getNavigationManager()->add(
|
||||||
|
array(
|
||||||
|
"id" => 'favorites',
|
||||||
|
"appname" => 'files',
|
||||||
|
"script" => 'simplelist.php',
|
||||||
|
"order" => 50,
|
||||||
|
"name" => $l->t('Favorites')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
$navItems = \OCA\Files\App::getNavigationManager()->getAll();
|
$navItems = \OCA\Files\App::getNavigationManager()->getAll();
|
||||||
usort($navItems, 'sortNavigationItems');
|
usort($navItems, 'sortNavigationItems');
|
||||||
$nav->assign('navigationItems', $navItems);
|
$nav->assign('navigationItems', $navItems);
|
||||||
|
|
|
@ -80,6 +80,8 @@
|
||||||
// refer to the one of the "files" view
|
// refer to the one of the "files" view
|
||||||
window.FileList = this.fileList;
|
window.FileList = this.fileList;
|
||||||
|
|
||||||
|
OC.Plugins.attach('OCA.Files.App', this);
|
||||||
|
|
||||||
this._setupEvents();
|
this._setupEvents();
|
||||||
// trigger URL change event handlers
|
// trigger URL change event handlers
|
||||||
this._onPopState(urlParams);
|
this._onPopState(urlParams);
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// HACK: this piece needs to be loaded AFTER the files app (for unit tests)
|
||||||
|
$(document).ready(function() {
|
||||||
|
(function(OCA) {
|
||||||
|
/**
|
||||||
|
* @class OCA.Files.FavoritesFileList
|
||||||
|
* @augments OCA.Files.FavoritesFileList
|
||||||
|
*
|
||||||
|
* @classdesc Favorites file list.
|
||||||
|
* Displays the list of files marked as favorites
|
||||||
|
*
|
||||||
|
* @param $el container element with existing markup for the #controls
|
||||||
|
* and a table
|
||||||
|
* @param [options] map of options, see other parameters
|
||||||
|
*/
|
||||||
|
var FavoritesFileList = function($el, options) {
|
||||||
|
this.initialize($el, options);
|
||||||
|
};
|
||||||
|
FavoritesFileList.prototype = _.extend({}, OCA.Files.FileList.prototype,
|
||||||
|
/** @lends OCA.Files.FavoritesFileList.prototype */ {
|
||||||
|
id: 'favorites',
|
||||||
|
appName: 'Favorites',
|
||||||
|
|
||||||
|
_clientSideSort: true,
|
||||||
|
_allowSelection: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
initialize: function($el, options) {
|
||||||
|
OCA.Files.FileList.prototype.initialize.apply(this, arguments);
|
||||||
|
if (this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OC.Plugins.attach('OCA.Files.FavoritesFileList', this);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateEmptyContent: function() {
|
||||||
|
var dir = this.getCurrentDirectory();
|
||||||
|
if (dir === '/') {
|
||||||
|
// root has special permissions
|
||||||
|
this.$el.find('#emptycontent').toggleClass('hidden', !this.isEmpty);
|
||||||
|
this.$el.find('#filestable thead th').toggleClass('hidden', this.isEmpty);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
OCA.Files.FileList.prototype.updateEmptyContent.apply(this, arguments);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getDirectoryPermissions: function() {
|
||||||
|
return OC.PERMISSION_READ | OC.PERMISSION_DELETE;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateStorageStatistics: function() {
|
||||||
|
// no op because it doesn't have
|
||||||
|
// storage info like free space / used space
|
||||||
|
},
|
||||||
|
|
||||||
|
reload: function() {
|
||||||
|
var tagName = OC.TAG_FAVORITE;
|
||||||
|
this.showMask();
|
||||||
|
if (this._reloadCall) {
|
||||||
|
this._reloadCall.abort();
|
||||||
|
}
|
||||||
|
this._reloadCall = $.ajax({
|
||||||
|
url: OC.generateUrl('/apps/files/api/v1/tags/{tagName}/files', {tagName: tagName}),
|
||||||
|
type: 'GET',
|
||||||
|
dataType: 'json'
|
||||||
|
});
|
||||||
|
var callBack = this.reloadCallback.bind(this);
|
||||||
|
return this._reloadCall.then(callBack, callBack);
|
||||||
|
},
|
||||||
|
|
||||||
|
reloadCallback: function(result) {
|
||||||
|
delete this._reloadCall;
|
||||||
|
this.hideMask();
|
||||||
|
|
||||||
|
if (result.files) {
|
||||||
|
this.setFiles(result.files.sort(this._sortComparator));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO: error handling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
OCA.Files.FavoritesFileList = FavoritesFileList;
|
||||||
|
})(OCA);
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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(OCA) {
|
||||||
|
/**
|
||||||
|
* @namespace OCA.Files.FavoritesPlugin
|
||||||
|
*
|
||||||
|
* Registers the favorites file list from the files app sidebar.
|
||||||
|
*/
|
||||||
|
OCA.Files.FavoritesPlugin = {
|
||||||
|
name: 'Favorites',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type OCA.Files.FavoritesFileList
|
||||||
|
*/
|
||||||
|
favoritesFileList: null,
|
||||||
|
|
||||||
|
attach: function() {
|
||||||
|
var self = this;
|
||||||
|
$('#app-content-favorites').on('show.plugin-favorites', function(e) {
|
||||||
|
self.showFileList($(e.target));
|
||||||
|
});
|
||||||
|
$('#app-content-favorites').on('hide.plugin-favorites', function() {
|
||||||
|
self.hideFileList();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
detach: function() {
|
||||||
|
if (this.favoritesFileList) {
|
||||||
|
this.favoritesFileList.destroy();
|
||||||
|
OCA.Files.fileActions.off('setDefault.plugin-favorites', this._onActionsUpdated);
|
||||||
|
OCA.Files.fileActions.off('registerAction.plugin-favorites', this._onActionsUpdated);
|
||||||
|
$('#app-content-favorites').off('.plugin-favorites');
|
||||||
|
this.favoritesFileList = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
showFileList: function($el) {
|
||||||
|
if (!this.favoritesFileList) {
|
||||||
|
this.favoritesFileList = this._createFavoritesFileList($el);
|
||||||
|
}
|
||||||
|
return this.favoritesFileList;
|
||||||
|
},
|
||||||
|
|
||||||
|
hideFileList: function() {
|
||||||
|
if (this.favoritesFileList) {
|
||||||
|
this.favoritesFileList.$fileList.empty();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the favorites file list.
|
||||||
|
*
|
||||||
|
* @param $el container for the file list
|
||||||
|
* @return {OCA.Files.FavoritesFileList} file list
|
||||||
|
*/
|
||||||
|
_createFavoritesFileList: function($el) {
|
||||||
|
var fileActions = this._createFileActions();
|
||||||
|
// register favorite list for sidebar section
|
||||||
|
return new OCA.Files.FavoritesFileList(
|
||||||
|
$el, {
|
||||||
|
fileActions: fileActions,
|
||||||
|
scrollContainer: $('#app-content')
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
_createFileActions: function() {
|
||||||
|
// inherit file actions from the files app
|
||||||
|
var fileActions = new OCA.Files.FileActions();
|
||||||
|
// note: not merging the legacy actions because legacy apps are not
|
||||||
|
// compatible with the sharing overview and need to be adapted first
|
||||||
|
fileActions.registerDefaultActions();
|
||||||
|
fileActions.merge(OCA.Files.fileActions);
|
||||||
|
|
||||||
|
if (!this._globalActionsInitialized) {
|
||||||
|
// in case actions are registered later
|
||||||
|
this._onActionsUpdated = _.bind(this._onActionsUpdated, this);
|
||||||
|
OCA.Files.fileActions.on('setDefault.plugin-favorites', this._onActionsUpdated);
|
||||||
|
OCA.Files.fileActions.on('registerAction.plugin-favorites', this._onActionsUpdated);
|
||||||
|
this._globalActionsInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// when the user clicks on a folder, redirect to the corresponding
|
||||||
|
// folder in the files app instead of opening it directly
|
||||||
|
fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) {
|
||||||
|
OCA.Files.App.setActiveView('files', {silent: true});
|
||||||
|
OCA.Files.App.fileList.changeDirectory(context.$file.attr('data-path') + '/' + filename, true, true);
|
||||||
|
});
|
||||||
|
fileActions.setDefault('dir', 'Open');
|
||||||
|
return fileActions;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onActionsUpdated: function(ev) {
|
||||||
|
if (ev.action) {
|
||||||
|
this.favoritesFileList.fileActions.registerAction(ev.action);
|
||||||
|
} else if (ev.defaultAction) {
|
||||||
|
this.favoritesFileList.fileActions.setDefault(
|
||||||
|
ev.defaultAction.mime,
|
||||||
|
ev.defaultAction.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
})(OCA);
|
||||||
|
|
||||||
|
OC.Plugins.register('OCA.Files.App', OCA.Files.FavoritesPlugin);
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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(OCA) {
|
||||||
|
|
||||||
|
OCA.Files = OCA.Files || {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace OCA.Files.TagsPlugin
|
||||||
|
*
|
||||||
|
* Extends the file actions and file list to include a favorite action icon
|
||||||
|
* and addition "data-tags" and "data-favorite" attributes.
|
||||||
|
*/
|
||||||
|
OCA.Files.TagsPlugin = {
|
||||||
|
name: 'Tags',
|
||||||
|
|
||||||
|
allowedLists: [
|
||||||
|
'files',
|
||||||
|
'favorites'
|
||||||
|
],
|
||||||
|
|
||||||
|
_extendFileActions: function(fileActions) {
|
||||||
|
var self = this;
|
||||||
|
// register "star" action
|
||||||
|
fileActions.registerAction({
|
||||||
|
name: 'favorite',
|
||||||
|
displayName: 'Favorite',
|
||||||
|
mime: 'all',
|
||||||
|
permissions: OC.PERMISSION_READ,
|
||||||
|
render: function(actionSpec, isDefault, context) {
|
||||||
|
// TODO: use proper icon
|
||||||
|
var $file = context.$file;
|
||||||
|
var isFavorite = $file.data('favorite') === true;
|
||||||
|
var starState = isFavorite ? '★' : '☆';
|
||||||
|
var $icon = $(
|
||||||
|
'<a href="#" class="action action-favorite ' + (isFavorite ? 'permanent' : '') + '">' +
|
||||||
|
starState + '</a>'
|
||||||
|
);
|
||||||
|
$file.find('td:first>.favorite').prepend($icon);
|
||||||
|
return $icon;
|
||||||
|
},
|
||||||
|
actionHandler: function(fileName, context) {
|
||||||
|
var $actionEl = context.$file.find('.action-favorite');
|
||||||
|
var $file = context.$file;
|
||||||
|
var dir = context.dir || context.fileList.getCurrentDirectory();
|
||||||
|
var tags = $file.attr('data-tags');
|
||||||
|
if (_.isUndefined(tags)) {
|
||||||
|
tags = '';
|
||||||
|
}
|
||||||
|
tags = tags.split('|');
|
||||||
|
tags = _.without(tags, '');
|
||||||
|
var isFavorite = tags.indexOf(OC.TAG_FAVORITE) >= 0;
|
||||||
|
if (isFavorite) {
|
||||||
|
// remove tag from list
|
||||||
|
tags = _.without(tags, OC.TAG_FAVORITE);
|
||||||
|
} else {
|
||||||
|
tags.push(OC.TAG_FAVORITE);
|
||||||
|
}
|
||||||
|
if ($actionEl.hasClass('icon-loading')) {
|
||||||
|
// do nothing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$actionEl.addClass('icon-loading permanent');
|
||||||
|
self.applyFileTags(
|
||||||
|
dir + '/' + fileName,
|
||||||
|
tags
|
||||||
|
).then(function() {
|
||||||
|
// TODO: read from result
|
||||||
|
$actionEl.removeClass('icon-loading');
|
||||||
|
$actionEl.html(isFavorite ? '☆' : '★');
|
||||||
|
$actionEl.toggleClass('permanent', !isFavorite);
|
||||||
|
$file.attr('data-tags', tags.join('|'));
|
||||||
|
$file.attr('data-favorite', !isFavorite);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_extendFileList: function(fileList) {
|
||||||
|
// extend row prototype
|
||||||
|
fileList.$fileList.addClass('has-favorites');
|
||||||
|
var oldCreateRow = fileList._createRow;
|
||||||
|
fileList._createRow = function(fileData) {
|
||||||
|
var $tr = oldCreateRow.apply(this, arguments);
|
||||||
|
if (fileData.tags) {
|
||||||
|
$tr.attr('data-tags', fileData.tags.join('|'));
|
||||||
|
if (fileData.tags.indexOf(OC.TAG_FAVORITE) >= 0) {
|
||||||
|
$tr.attr('data-favorite', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$tr.find('td:first').prepend('<div class="favorite"></div>');
|
||||||
|
return $tr;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
attach: function(fileList) {
|
||||||
|
if (this.allowedLists.indexOf(fileList.id) < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._extendFileActions(fileList.fileActions);
|
||||||
|
this._extendFileList(fileList);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the given files' tags with the specified ones.
|
||||||
|
*
|
||||||
|
* @param {String} fileName path to the file or folder to tag
|
||||||
|
* @param {Array.<String>} tagNames array of tag names
|
||||||
|
*/
|
||||||
|
applyFileTags: function(fileName, tagNames) {
|
||||||
|
var encodedPath = OC.encodePath(fileName);
|
||||||
|
while (encodedPath[0] === '/') {
|
||||||
|
encodedPath = encodedPath.substr(1);
|
||||||
|
}
|
||||||
|
return $.ajax({
|
||||||
|
url: OC.generateUrl('/apps/files/api/v1/files/') + encodedPath,
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify({
|
||||||
|
tags: tagNames || []
|
||||||
|
}),
|
||||||
|
dataType: 'json',
|
||||||
|
type: 'POST'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})(OCA);
|
||||||
|
|
||||||
|
OC.Plugins.register('OCA.Files.FileList', OCA.Files.TagsPlugin);
|
||||||
|
|
|
@ -122,6 +122,9 @@ class Helper
|
||||||
$entry['size'] = $i['size'];
|
$entry['size'] = $i['size'];
|
||||||
$entry['type'] = $i['type'];
|
$entry['type'] = $i['type'];
|
||||||
$entry['etag'] = $i['etag'];
|
$entry['etag'] = $i['etag'];
|
||||||
|
if (isset($i['tags'])) {
|
||||||
|
$entry['tags'] = $i['tags'];
|
||||||
|
}
|
||||||
if (isset($i['displayname_owner'])) {
|
if (isset($i['displayname_owner'])) {
|
||||||
$entry['shareOwner'] = $i['displayname_owner'];
|
$entry['shareOwner'] = $i['displayname_owner'];
|
||||||
}
|
}
|
||||||
|
@ -171,10 +174,32 @@ class Helper
|
||||||
*/
|
*/
|
||||||
public static function getFiles($dir, $sortAttribute = 'name', $sortDescending = false) {
|
public static function getFiles($dir, $sortAttribute = 'name', $sortDescending = false) {
|
||||||
$content = \OC\Files\Filesystem::getDirectoryContent($dir);
|
$content = \OC\Files\Filesystem::getDirectoryContent($dir);
|
||||||
|
$content = self::populateTags($content);
|
||||||
|
|
||||||
return self::sortFiles($content, $sortAttribute, $sortDescending);
|
return self::sortFiles($content, $sortAttribute, $sortDescending);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate the result set with file tags
|
||||||
|
*
|
||||||
|
* @param array file list
|
||||||
|
* @return file list populated with tags
|
||||||
|
*/
|
||||||
|
public static function populateTags($fileList) {
|
||||||
|
$filesById = array();
|
||||||
|
foreach ($fileList as $fileData) {
|
||||||
|
$filesById[$fileData['fileid']] = $fileData;
|
||||||
|
}
|
||||||
|
$tagger = \OC::$server->getTagManager()->load('files');
|
||||||
|
$tags = $tagger->getTagsForObjects(array_keys($filesById));
|
||||||
|
if ($tags) {
|
||||||
|
foreach ($tags as $fileId => $fileTags) {
|
||||||
|
$filesById[$fileId]['tags'] = $fileTags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $fileList;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort the given file info array
|
* Sort the given file info array
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OCA\Files\Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service class to manage tags on files.
|
||||||
|
*/
|
||||||
|
class TagService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \OCP\IUserSession
|
||||||
|
*/
|
||||||
|
private $userSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \OCP\ITags
|
||||||
|
*/
|
||||||
|
private $tagger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \OCP\Files\Folder
|
||||||
|
*/
|
||||||
|
private $homeFolder;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
\OCP\IUserSession $userSession,
|
||||||
|
\OCP\ITags $tagger,
|
||||||
|
\OCP\Files\Folder $homeFolder
|
||||||
|
) {
|
||||||
|
$this->userSession = $userSession;
|
||||||
|
$this->tagger = $tagger;
|
||||||
|
$this->homeFolder = $homeFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the tags of the specified file path.
|
||||||
|
* The passed tags are absolute, which means they will
|
||||||
|
* replace the actual tag selection.
|
||||||
|
*
|
||||||
|
* @param string $path path
|
||||||
|
* @param array $tags array of tags
|
||||||
|
* @return array list of tags
|
||||||
|
* @throws \OCP\NotFoundException if the file does not exist
|
||||||
|
*/
|
||||||
|
public function updateFileTags($path, $tags) {
|
||||||
|
$fileId = $this->homeFolder->get($path)->getId();
|
||||||
|
|
||||||
|
$currentTags = $this->tagger->getTagsForObjects(array($fileId));
|
||||||
|
|
||||||
|
if (!empty($currentTags)) {
|
||||||
|
$currentTags = current($currentTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newTags = array_diff($tags, $currentTags);
|
||||||
|
foreach ($newTags as $tag) {
|
||||||
|
$this->tagger->tagAs($fileId, $tag);
|
||||||
|
}
|
||||||
|
$deletedTags = array_diff($currentTags, $tags);
|
||||||
|
foreach ($deletedTags as $tag) {
|
||||||
|
$this->tagger->unTag($fileId, $tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: re-read from tagger to make sure the
|
||||||
|
// list is up to date, in case of concurrent changes ?
|
||||||
|
return $tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the tags of the specified file path.
|
||||||
|
* The passed tags are absolute, which means they will
|
||||||
|
* replace the actual tag selection.
|
||||||
|
*
|
||||||
|
* @param array $tagName tag name to filter by
|
||||||
|
* @return FileInfo[] list of matching files
|
||||||
|
* @throws \Exception if the tag does not exist
|
||||||
|
*/
|
||||||
|
public function getFilesByTag($tagName) {
|
||||||
|
$nodes = $this->homeFolder->searchByTag(
|
||||||
|
$tagName, $this->userSession->getUser()->getUId()
|
||||||
|
);
|
||||||
|
foreach ($nodes as &$node) {
|
||||||
|
$node = $node->getFileInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $nodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ownCloud - Simple 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();
|
||||||
|
|
||||||
|
// renders the controls and table headers template
|
||||||
|
$tmpl = new OCP\Template('files', 'simplelist', '');
|
||||||
|
$tmpl->printPage();
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
<div id="controls">
|
||||||
|
<div id="file_action_panel"></div>
|
||||||
|
</div>
|
||||||
|
<div id='notification'></div>
|
||||||
|
|
||||||
|
<div id="emptycontent" class="hidden"></div>
|
||||||
|
|
||||||
|
<input type="hidden" name="dir" value="" id="dir">
|
||||||
|
|
||||||
|
<table id="filestable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th id='headerName' class="hidden column-name">
|
||||||
|
<div id="headerName-container">
|
||||||
|
<a class="name sort columntitle" data-sort="name"><span><?php p($l->t( 'Name' )); ?></span><span class="sort-indicator"></span></a>
|
||||||
|
</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>
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
describe('OCA.Files.FavoritesFileList tests', function() {
|
||||||
|
var fileList;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
// init parameters and test table elements
|
||||||
|
$('#testArea').append(
|
||||||
|
'<div id="app-content-container">' +
|
||||||
|
// init horrible parameters
|
||||||
|
'<input type="hidden" id="dir" value="/"></input>' +
|
||||||
|
'<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 column-name">' +
|
||||||
|
'<a class="name columntitle" data-sort="name"><span>Name</span><span class="sort-indicator"></span></a>' +
|
||||||
|
'</th>' +
|
||||||
|
'<th class="hidden column-mtime">' +
|
||||||
|
'<a class="columntitle" data-sort="mtime"><span class="sort-indicator"></span></a>' +
|
||||||
|
'</th>' +
|
||||||
|
'</tr></thead>' +
|
||||||
|
'<tbody id="fileList"></tbody>' +
|
||||||
|
'<tfoot></tfoot>' +
|
||||||
|
'</table>' +
|
||||||
|
'<div id="emptycontent">Empty content message</div>' +
|
||||||
|
'</div>'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
afterEach(function() {
|
||||||
|
fileList.destroy();
|
||||||
|
fileList = undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('loading file list', function() {
|
||||||
|
var response;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
fileList = new OCA.Files.FavoritesFileList(
|
||||||
|
$('#app-content-container')
|
||||||
|
);
|
||||||
|
OCA.Files.FavoritesPlugin.attach(fileList);
|
||||||
|
|
||||||
|
fileList.reload();
|
||||||
|
|
||||||
|
/* jshint camelcase: false */
|
||||||
|
response = {
|
||||||
|
files: [{
|
||||||
|
id: 7,
|
||||||
|
name: 'test.txt',
|
||||||
|
path: '/somedir',
|
||||||
|
size: 123,
|
||||||
|
mtime: 11111000,
|
||||||
|
tags: [OC.TAG_FAVORITE],
|
||||||
|
permissions: OC.PERMISSION_ALL,
|
||||||
|
mimetype: 'text/plain'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
it('render files', function() {
|
||||||
|
var request;
|
||||||
|
|
||||||
|
expect(fakeServer.requests.length).toEqual(1);
|
||||||
|
request = fakeServer.requests[0];
|
||||||
|
expect(request.url).toEqual(
|
||||||
|
OC.generateUrl('apps/files/api/v1/tags/{tagName}/files', {tagName: OC.TAG_FAVORITE})
|
||||||
|
);
|
||||||
|
|
||||||
|
fakeServer.requests[0].respond(
|
||||||
|
200,
|
||||||
|
{ 'Content-Type': 'application/json' },
|
||||||
|
JSON.stringify(response)
|
||||||
|
);
|
||||||
|
|
||||||
|
var $rows = fileList.$el.find('tbody tr');
|
||||||
|
var $tr = $rows.eq(0);
|
||||||
|
expect($rows.length).toEqual(1);
|
||||||
|
expect($tr.attr('data-id')).toEqual('7');
|
||||||
|
expect($tr.attr('data-type')).toEqual('file');
|
||||||
|
expect($tr.attr('data-file')).toEqual('test.txt');
|
||||||
|
expect($tr.attr('data-path')).toEqual('/somedir');
|
||||||
|
expect($tr.attr('data-size')).toEqual('123');
|
||||||
|
expect(parseInt($tr.attr('data-permissions'), 10))
|
||||||
|
.toEqual(OC.PERMISSION_ALL);
|
||||||
|
expect($tr.attr('data-mime')).toEqual('text/plain');
|
||||||
|
expect($tr.attr('data-mtime')).toEqual('11111000');
|
||||||
|
expect($tr.find('a.name').attr('href')).toEqual(
|
||||||
|
OC.webroot +
|
||||||
|
'/index.php/apps/files/ajax/download.php' +
|
||||||
|
'?dir=%2Fsomedir&files=test.txt'
|
||||||
|
);
|
||||||
|
expect($tr.find('.nametext').text().trim()).toEqual('test.txt');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
describe('OCA.Files.FavoritesPlugin tests', function() {
|
||||||
|
var Plugin = OCA.Files.FavoritesPlugin;
|
||||||
|
var fileList;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
$('#testArea').append(
|
||||||
|
'<div id="app-navigation">' +
|
||||||
|
'<ul><li data-id="files"><a>Files</a></li>' +
|
||||||
|
'<li data-id="sharingin"><a></a></li>' +
|
||||||
|
'<li data-id="sharingout"><a></a></li>' +
|
||||||
|
'</ul></div>' +
|
||||||
|
'<div id="app-content">' +
|
||||||
|
'<div id="app-content-files" class="hidden">' +
|
||||||
|
'</div>' +
|
||||||
|
'<div id="app-content-favorites" class="hidden">' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>'
|
||||||
|
);
|
||||||
|
OC.Plugins.attach('OCA.Files.App', Plugin);
|
||||||
|
fileList = Plugin.showFileList($('#app-content-favorites'));
|
||||||
|
});
|
||||||
|
afterEach(function() {
|
||||||
|
OC.Plugins.detach('OCA.Files.App', Plugin);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('initialization', function() {
|
||||||
|
it('inits favorites list on show', function() {
|
||||||
|
expect(fileList).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('file actions', function() {
|
||||||
|
var oldLegacyFileActions;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
oldLegacyFileActions = window.FileActions;
|
||||||
|
window.FileActions = new OCA.Files.FileActions();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
window.FileActions = oldLegacyFileActions;
|
||||||
|
});
|
||||||
|
it('provides default file actions', function() {
|
||||||
|
var fileActions = fileList.fileActions;
|
||||||
|
|
||||||
|
expect(fileActions.actions.all).toBeDefined();
|
||||||
|
expect(fileActions.actions.all.Delete).toBeDefined();
|
||||||
|
expect(fileActions.actions.all.Rename).toBeDefined();
|
||||||
|
expect(fileActions.actions.all.Download).toBeDefined();
|
||||||
|
|
||||||
|
expect(fileActions.defaults.dir).toEqual('Open');
|
||||||
|
});
|
||||||
|
it('provides custom file actions', function() {
|
||||||
|
var actionStub = sinon.stub();
|
||||||
|
// regular file action
|
||||||
|
OCA.Files.fileActions.register(
|
||||||
|
'all',
|
||||||
|
'RegularTest',
|
||||||
|
OC.PERMISSION_READ,
|
||||||
|
OC.imagePath('core', 'actions/shared'),
|
||||||
|
actionStub
|
||||||
|
);
|
||||||
|
|
||||||
|
Plugin.favoritesFileList = null;
|
||||||
|
fileList = Plugin.showFileList($('#app-content-favorites'));
|
||||||
|
|
||||||
|
expect(fileList.fileActions.actions.all.RegularTest).toBeDefined();
|
||||||
|
});
|
||||||
|
it('does not provide legacy file actions', function() {
|
||||||
|
var actionStub = sinon.stub();
|
||||||
|
// legacy file action
|
||||||
|
window.FileActions.register(
|
||||||
|
'all',
|
||||||
|
'LegacyTest',
|
||||||
|
OC.PERMISSION_READ,
|
||||||
|
OC.imagePath('core', 'actions/shared'),
|
||||||
|
actionStub
|
||||||
|
);
|
||||||
|
|
||||||
|
Plugin.favoritesFileList = null;
|
||||||
|
fileList = Plugin.showFileList($('#app-content-favorites'));
|
||||||
|
|
||||||
|
expect(fileList.fileActions.actions.all.LegacyTest).not.toBeDefined();
|
||||||
|
});
|
||||||
|
it('redirects to files app when opening a directory', function() {
|
||||||
|
var oldList = OCA.Files.App.fileList;
|
||||||
|
// dummy new list to make sure it exists
|
||||||
|
OCA.Files.App.fileList = new OCA.Files.FileList($('<table><thead></thead><tbody></tbody></table>'));
|
||||||
|
|
||||||
|
var setActiveViewStub = sinon.stub(OCA.Files.App, 'setActiveView');
|
||||||
|
// create dummy table so we can click the dom
|
||||||
|
var $table = '<table><thead></thead><tbody id="fileList"></tbody></table>';
|
||||||
|
$('#app-content-favorites').append($table);
|
||||||
|
|
||||||
|
Plugin.favoritesFileList = null;
|
||||||
|
fileList = Plugin.showFileList($('#app-content-favorites'));
|
||||||
|
|
||||||
|
fileList.setFiles([{
|
||||||
|
name: 'testdir',
|
||||||
|
type: 'dir',
|
||||||
|
path: '/somewhere/inside/subdir',
|
||||||
|
counterParts: ['user2'],
|
||||||
|
shareOwner: 'user2'
|
||||||
|
}]);
|
||||||
|
|
||||||
|
fileList.findFileEl('testdir').find('td a.name').click();
|
||||||
|
|
||||||
|
expect(OCA.Files.App.fileList.getCurrentDirectory()).toEqual('/somewhere/inside/subdir/testdir');
|
||||||
|
|
||||||
|
expect(setActiveViewStub.calledOnce).toEqual(true);
|
||||||
|
expect(setActiveViewStub.calledWith('files')).toEqual(true);
|
||||||
|
|
||||||
|
setActiveViewStub.restore();
|
||||||
|
|
||||||
|
// restore old list
|
||||||
|
OCA.Files.App.fileList = oldList;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
describe('OCA.Files.TagsPlugin tests', function() {
|
||||||
|
var fileList;
|
||||||
|
var testFiles;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
var $content = $('<div id="content"></div>');
|
||||||
|
$('#testArea').append($content);
|
||||||
|
// dummy file list
|
||||||
|
var $div = $(
|
||||||
|
'<div>' +
|
||||||
|
'<table id="filestable">' +
|
||||||
|
'<thead></thead>' +
|
||||||
|
'<tbody id="fileList"></tbody>' +
|
||||||
|
'</table>' +
|
||||||
|
'</div>');
|
||||||
|
$('#content').append($div);
|
||||||
|
|
||||||
|
fileList = new OCA.Files.FileList($div);
|
||||||
|
OCA.Files.TagsPlugin.attach(fileList);
|
||||||
|
|
||||||
|
testFiles = [{
|
||||||
|
id: 1,
|
||||||
|
type: 'file',
|
||||||
|
name: 'One.txt',
|
||||||
|
path: '/subdir',
|
||||||
|
mimetype: 'text/plain',
|
||||||
|
size: 12,
|
||||||
|
permissions: OC.PERMISSION_ALL,
|
||||||
|
etag: 'abc',
|
||||||
|
shareOwner: 'User One',
|
||||||
|
isShareMountPoint: false,
|
||||||
|
tags: ['tag1', 'tag2']
|
||||||
|
}];
|
||||||
|
});
|
||||||
|
afterEach(function() {
|
||||||
|
fileList.destroy();
|
||||||
|
fileList = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Favorites icon', function() {
|
||||||
|
it('renders favorite icon and extra data', function() {
|
||||||
|
var $action, $tr;
|
||||||
|
fileList.setFiles(testFiles);
|
||||||
|
$tr = fileList.$el.find('tbody tr:first');
|
||||||
|
$action = $tr.find('.action-favorite');
|
||||||
|
expect($action.length).toEqual(1);
|
||||||
|
expect($action.hasClass('permanent')).toEqual(false);
|
||||||
|
|
||||||
|
expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2']);
|
||||||
|
expect($tr.attr('data-favorite')).not.toBeDefined();
|
||||||
|
});
|
||||||
|
it('renders permanent favorite icon and extra data', function() {
|
||||||
|
var $action, $tr;
|
||||||
|
testFiles[0].tags.push(OC.TAG_FAVORITE);
|
||||||
|
fileList.setFiles(testFiles);
|
||||||
|
$tr = fileList.$el.find('tbody tr:first');
|
||||||
|
$action = $tr.find('.action-favorite');
|
||||||
|
expect($action.length).toEqual(1);
|
||||||
|
expect($action.hasClass('permanent')).toEqual(true);
|
||||||
|
|
||||||
|
expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', OC.TAG_FAVORITE]);
|
||||||
|
expect($tr.attr('data-favorite')).toEqual('true');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('Applying tags', function() {
|
||||||
|
it('sends request to server and updates icon', function() {
|
||||||
|
// TODO
|
||||||
|
fileList.setFiles(testFiles);
|
||||||
|
});
|
||||||
|
it('sends all tags to server when applyFileTags() is called ', function() {
|
||||||
|
// TODO
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,121 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
namespace OCA\Files;
|
||||||
|
|
||||||
|
use \OCA\Files\Service\TagService;
|
||||||
|
|
||||||
|
class TagServiceTest extends \Test\TestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \OCP\Files\Folder
|
||||||
|
*/
|
||||||
|
private $root;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \OCA\Files\Service\TagService
|
||||||
|
*/
|
||||||
|
private $tagService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \OCP\ITags
|
||||||
|
*/
|
||||||
|
private $tagger;
|
||||||
|
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
$this->user = $this->getUniqueId('user');
|
||||||
|
\OC_User::createUser($this->user, 'test');
|
||||||
|
\OC_User::setUserId($this->user);
|
||||||
|
\OC_Util::setupFS($this->user);
|
||||||
|
/**
|
||||||
|
* @var \OCP\IUser
|
||||||
|
*/
|
||||||
|
$user = new \OC\User\User($this->user, null);
|
||||||
|
/**
|
||||||
|
* @var \OCP\IUserSession
|
||||||
|
*/
|
||||||
|
$userSession = $this->getMock('\OCP\IUserSession');
|
||||||
|
$userSession->expects($this->any())
|
||||||
|
->method('getUser')
|
||||||
|
->withAnyParameters()
|
||||||
|
->will($this->returnValue($user));
|
||||||
|
|
||||||
|
$this->root = \OC::$server->getUserFolder();
|
||||||
|
|
||||||
|
$this->tagger = \OC::$server->getTagManager()->load('files');
|
||||||
|
$this->tagService = new TagService(
|
||||||
|
$userSession,
|
||||||
|
$this->tagger,
|
||||||
|
$this->root
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown() {
|
||||||
|
\OC_User::setUserId('');
|
||||||
|
\OC_User::deleteUser($this->user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpdateFileTags() {
|
||||||
|
$tag1 = 'tag1';
|
||||||
|
$tag2 = 'tag2';
|
||||||
|
|
||||||
|
$subdir = $this->root->newFolder('subdir');
|
||||||
|
$testFile = $subdir->newFile('test.txt');
|
||||||
|
$testFile->putContent('test contents');
|
||||||
|
|
||||||
|
$fileId = $testFile->getId();
|
||||||
|
|
||||||
|
// set tags
|
||||||
|
$this->tagService->updateFileTags('subdir/test.txt', array($tag1, $tag2));
|
||||||
|
|
||||||
|
$this->assertEquals(array($fileId), $this->tagger->getIdsForTag($tag1));
|
||||||
|
$this->assertEquals(array($fileId), $this->tagger->getIdsForTag($tag2));
|
||||||
|
|
||||||
|
// remove tag
|
||||||
|
$result = $this->tagService->updateFileTags('subdir/test.txt', array($tag2));
|
||||||
|
$this->assertEquals(array(), $this->tagger->getIdsForTag($tag1));
|
||||||
|
$this->assertEquals(array($fileId), $this->tagger->getIdsForTag($tag2));
|
||||||
|
|
||||||
|
// clear tags
|
||||||
|
$result = $this->tagService->updateFileTags('subdir/test.txt', array());
|
||||||
|
$this->assertEquals(array(), $this->tagger->getIdsForTag($tag1));
|
||||||
|
$this->assertEquals(array(), $this->tagger->getIdsForTag($tag2));
|
||||||
|
|
||||||
|
// non-existing file
|
||||||
|
$caught = false;
|
||||||
|
try {
|
||||||
|
$this->tagService->updateFileTags('subdir/unexist.txt', array($tag1));
|
||||||
|
} catch (\OCP\Files\NotFoundException $e) {
|
||||||
|
$caught = true;
|
||||||
|
}
|
||||||
|
$this->assertTrue($caught);
|
||||||
|
|
||||||
|
$subdir->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ var OC={
|
||||||
PERMISSION_DELETE:8,
|
PERMISSION_DELETE:8,
|
||||||
PERMISSION_SHARE:16,
|
PERMISSION_SHARE:16,
|
||||||
PERMISSION_ALL:31,
|
PERMISSION_ALL:31,
|
||||||
|
TAG_FAVORITE: '_$!<Favorite>!$_',
|
||||||
/* jshint camelcase: false */
|
/* jshint camelcase: false */
|
||||||
webroot:oc_webroot,
|
webroot:oc_webroot,
|
||||||
appswebroots:(typeof oc_appswebroots !== 'undefined') ? oc_appswebroots:false,
|
appswebroots:(typeof oc_appswebroots !== 'undefined') ? oc_appswebroots:false,
|
||||||
|
@ -211,6 +212,24 @@ var OC={
|
||||||
return OC.filePath(app,'img',file);
|
return OC.filePath(app,'img',file);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URI-Encodes a file path but keep the path slashes.
|
||||||
|
*
|
||||||
|
* @param path path
|
||||||
|
* @return encoded path
|
||||||
|
*/
|
||||||
|
encodePath: function(path) {
|
||||||
|
if (!path) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
var parts = path.split('/');
|
||||||
|
var result = [];
|
||||||
|
for (var i = 0; i < parts.length; i++) {
|
||||||
|
result.push(encodeURIComponent(parts[i]));
|
||||||
|
}
|
||||||
|
return result.join('/');
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a script for the server and load it. If the script is already loaded,
|
* Load a script for the server and load it. If the script is already loaded,
|
||||||
* the event handler will be called directly
|
* the event handler will be called directly
|
||||||
|
|
Loading…
Reference in New Issue