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 OCA\Files\Controller\ApiController;
|
||||
use OCP\AppFramework\App;
|
||||
use \OCA\Files\Service\TagService;
|
||||
use \OCP\IContainer;
|
||||
|
||||
class Application extends App {
|
||||
public function __construct(array $urlParams=array()) {
|
||||
|
@ -21,10 +23,44 @@ class Application extends App {
|
|||
/**
|
||||
* Controllers
|
||||
*/
|
||||
$container->registerService('APIController', function (SimpleContainer $c) {
|
||||
$container->registerService('APIController', function (IContainer $c) {
|
||||
return new ApiController(
|
||||
$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;
|
||||
|
||||
$application = new Application();
|
||||
$application->registerRoutes($this, array('routes' => array(
|
||||
array('name' => 'API#getThumbnail', 'url' => '/api/v1/thumbnail/{x}/{y}/{file}', 'verb' => 'GET', 'requirements' => array('file' => '.+')),
|
||||
)));
|
||||
|
||||
$application->registerRoutes(
|
||||
$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 */
|
||||
|
||||
|
|
|
@ -12,13 +12,16 @@ use OCP\AppFramework\Http;
|
|||
use OCP\AppFramework\Controller;
|
||||
use OCP\IRequest;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\Http\DownloadResponse;
|
||||
use OC\Preview;
|
||||
use OCA\Files\Service\TagService;
|
||||
|
||||
class ApiController extends Controller {
|
||||
|
||||
public function __construct($appName, IRequest $request){
|
||||
public function __construct($appName, IRequest $request, TagService $tagService){
|
||||
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;
|
||||
height: 100%;
|
||||
}
|
||||
#fileList.has-favorites td.filename a.name {
|
||||
left: 50px;
|
||||
}
|
||||
|
||||
table td.filename .nametext .innernametext {
|
||||
text-overflow: ellipsis;
|
||||
|
@ -417,6 +420,18 @@ table td.filename .uploadtext {
|
|||
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; }
|
||||
|
||||
/* File actions */
|
||||
|
@ -442,7 +457,7 @@ table td.filename .uploadtext {
|
|||
padding: 17px 14px;
|
||||
}
|
||||
|
||||
#fileList .action.action-share-notification span, #fileList a {
|
||||
#fileList .action.action-share-notification span, #fileList a.name {
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
use OCA\Files\Appinfo\Application;
|
||||
|
||||
// Check if we are a user
|
||||
OCP\User::checkLoggedIn();
|
||||
|
@ -38,8 +39,14 @@ OCP\Util::addscript('files', 'filesummary');
|
|||
OCP\Util::addscript('files', 'breadcrumb');
|
||||
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');
|
||||
|
||||
$l = \OC::$server->getL10N('files');
|
||||
|
||||
$isIE8 = false;
|
||||
preg_match('/MSIE (.*?);/', $_SERVER['HTTP_USER_AGENT'], $matches);
|
||||
if (count($matches) > 0 && $matches[1] <= 9) {
|
||||
|
@ -79,6 +86,16 @@ function sortNavigationItems($item1, $item2) {
|
|||
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();
|
||||
usort($navItems, 'sortNavigationItems');
|
||||
$nav->assign('navigationItems', $navItems);
|
||||
|
|
|
@ -80,6 +80,8 @@
|
|||
// refer to the one of the "files" view
|
||||
window.FileList = this.fileList;
|
||||
|
||||
OC.Plugins.attach('OCA.Files.App', this);
|
||||
|
||||
this._setupEvents();
|
||||
// trigger URL change event handlers
|
||||
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['type'] = $i['type'];
|
||||
$entry['etag'] = $i['etag'];
|
||||
if (isset($i['tags'])) {
|
||||
$entry['tags'] = $i['tags'];
|
||||
}
|
||||
if (isset($i['displayname_owner'])) {
|
||||
$entry['shareOwner'] = $i['displayname_owner'];
|
||||
}
|
||||
|
@ -171,10 +174,32 @@ class Helper
|
|||
*/
|
||||
public static function getFiles($dir, $sortAttribute = 'name', $sortDescending = false) {
|
||||
$content = \OC\Files\Filesystem::getDirectoryContent($dir);
|
||||
$content = self::populateTags($content);
|
||||
|
||||
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
|
||||
*
|
||||
|
|
|
@ -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_SHARE:16,
|
||||
PERMISSION_ALL:31,
|
||||
TAG_FAVORITE: '_$!<Favorite>!$_',
|
||||
/* jshint camelcase: false */
|
||||
webroot:oc_webroot,
|
||||
appswebroots:(typeof oc_appswebroots !== 'undefined') ? oc_appswebroots:false,
|
||||
|
@ -211,6 +212,24 @@ var OC={
|
|||
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,
|
||||
* the event handler will be called directly
|
||||
|
|
Loading…
Reference in New Issue