Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add file action to explicitly lock a file #37460

Merged
merged 1 commit into from
Jun 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions apps/dav/lib/Connector/Sabre/PublicDavLocksPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
namespace OCA\DAV\Connector\Sabre;

use Sabre\DAV\Locks\Backend\BackendInterface;
use Sabre\DAV\Locks\LockInfo;
use Sabre\DAV\PropFind;
use Sabre\DAV\INode;
use Sabre\DAV\Exception\MethodNotAllowed;
Expand Down Expand Up @@ -85,4 +86,18 @@ public function httpUnlock(RequestInterface $request, ResponseInterface $respons
throw new MethodNotAllowed('Locking not allowed from public endpoint');
}
}

/**
* Generates the response for successful LOCK requests.
* @todo this method can be removed once upstream released a new version with this fix https://github.com/sabre-io/dav/pull/1273
*
* @return string
*/
protected function generateLockResponse(LockInfo $lockInfo) {
$contextUri = $this->server->getBaseUri();

return $this->server->xml->write('{DAV:}prop', [
'{DAV:}lockdiscovery' => new LockDiscovery([$lockInfo]),
], $contextUri);
}
}
39 changes: 36 additions & 3 deletions apps/files/js/filelockplugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,9 @@
}).map(function(xmlvalue) {
return parseLockNode(xmlvalue);
}).value();

}
return data;
});


},

/**
Expand Down Expand Up @@ -154,6 +151,42 @@
}
});

if (oc_appconfig.files.enable_lock_file_action) {
fileList.fileActions.registerAction({
name: 'lock',
mime: 'all',
displayName: t('files', 'Lock file'),
permissions: OC.PERMISSION_UPDATE,
type: OCA.Files.FileActions.TYPE_DROPDOWN,
iconClass: 'icon-lock-open',
actionHandler: function (filename, context) {
const file = context.fileInfoModel.getFullPath();
context.fileInfoModel._filesClient.lock(file).then(function (result, response) {
const xml = response.xhr.responseXML;
const activelock = xml.getElementsByTagNameNS('DAV:', 'activelock');
const lock = parseLockNode(activelock[0]);
context.fileInfoModel.set('activeLocks', [lock]);
}, function (error) {
console.log(error)
OC.Notification.show(t('files', 'Failed to lock.'));
});
}
});

fileList.fileActions.addAdvancedFilter(function (actions, context) {
var $file = context.$file;
if (context.fileInfoModel && context.fileInfoModel.attributes.mimetype === 'httpd/unix-directory') {
delete (actions.lock);
return actions;
}
var isLocked = $file.data('activelocks');
if (isLocked && isLocked.length > 0) {
delete (actions.lock);
}
return actions;
});
}

},

renderLink: function () {
Expand Down
18 changes: 15 additions & 3 deletions apps/files/js/locktabview.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
var self = this;
var $target = $(event.target).closest('.lock-entry');
var lockIndex = parseInt($target.attr('data-index'), 10);

var currentLock = this.model.get('activeLocks')[lockIndex];

// FIXME: move to FileInfoModel
Expand All @@ -75,7 +74,6 @@
// implicit clone of array else backbone doesn't fire change event
var locks = _.without(self.model.get('activeLocks') || [], currentLock);
self.model.set('activeLocks', locks);
self.render();
}
else if (result.status === 403) {
OC.Notification.show(t('files', 'Could not unlock, please contact the lock owner {owner}', {owner: currentLock.owner}));
Expand Down Expand Up @@ -121,7 +119,21 @@
canDisplay: function(fileInfo) {
// don't display if no lock is set
return fileInfo && fileInfo.get('activeLocks') && fileInfo.get('activeLocks').length > 0;
}
},

setFileInfo: function(fileInfo) {
if (this.model !== fileInfo) {
this.model = fileInfo;
this.render();
if (fileInfo) {
const self = this;
this.model.on('change', function(data) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have concerns about this...
What if the original this.model had more events? Shouldn't we configure the same on the new model? This could also apply if the original "change" event is modified or simply does another thing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While testing I never received an event from the old model, which seems reasonable since it is no longer being used.

Nevertheless what will happen in the worstcase: the tab will render again ... 🤷

self.render();
});
}
}
},

});

OCA.Files.LockTabView = LockTabView;
Expand Down
12 changes: 8 additions & 4 deletions apps/files/lib/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,17 @@ public static function getNavigationManager() {
}

public static function extendJsConfig($array) {
$maxChunkSize = (int)(\OC::$server->getConfig()->getAppValue('files', 'max_chunk_size', (10 * 1024 * 1024)));
$uploadStallTimeout = (int)(\OC::$server->getConfig()->getAppValue('files', 'upload_stall_timeout', 60)); // in seconds
$uploadStallRetries = (int)(\OC::$server->getConfig()->getAppValue('files', 'upload_stall_retries', 100));
$config = \OC::$server->getConfig();
$maxChunkSize = (int)($config->getAppValue('files', 'max_chunk_size', (10 * 1024 * 1024)));
$uploadStallTimeout = (int)($config->getAppValue('files', 'upload_stall_timeout', 60)); // in seconds
$uploadStallRetries = (int)($config->getAppValue('files', 'upload_stall_retries', 100));
$enableLockFileAction = (boolean)($config->getAppValue('files', 'enable_lock_file_action', false));

$array['array']['oc_appconfig']['files'] = [
'max_chunk_size' => $maxChunkSize,
'upload_stall_timeout' => $uploadStallTimeout,
'upload_stall_retries' => $uploadStallRetries
'upload_stall_retries' => $uploadStallRetries,
'enable_lock_file_action' => $enableLockFileAction
];
}
}
9 changes: 6 additions & 3 deletions apps/files/tests/js/filelockpluginSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ describe('OCA.Files.LockPlugin tests', function() {
var currentUserStub;

beforeEach(function() {
oc_appconfig = oc_appconfig || {};
oc_appconfig.files = oc_appconfig.files || {};
oc_appconfig.files.enable_lock_file_action = true;
var $content = $('<div id="content"></div>');
$('#testArea').append($content);
// dummy file list
Expand Down Expand Up @@ -134,8 +137,8 @@ describe('OCA.Files.LockPlugin tests', function() {
requestDeferred = new $.Deferred();
requestStub = sinon.stub(dav.Client.prototype, 'propFind').returns(requestDeferred.promise());
});
afterEach(function() {
requestStub.restore();
afterEach(function() {
requestStub.restore();
});

function makeLockXml(owner) {
Expand Down Expand Up @@ -186,7 +189,7 @@ describe('OCA.Files.LockPlugin tests', function() {

return xml;
}

it('parses lock information from response XML to JSON', function(done) {
var xml = dav.Client.prototype.parseMultiStatus(makeLockXml('lock owner'));
var promise = fileList.reload();
Expand Down
3 changes: 3 additions & 0 deletions changelog/unreleased/37460
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Change: Add file action to lock a file

https://github.com/owncloud/core/pull/37460
37 changes: 37 additions & 0 deletions core/js/files/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,43 @@
return promise;
},

lock: function(path, options) {
if (!path) {
throw 'Missing argument "path"';
}
var self = this;
var deferred = $.Deferred();
var promise = deferred.promise();

options = _.extend({
'pathIsUrl' : false
}, options);

const lockBody = "<?xml version='1.0' encoding='UTF-8'?>\n" +
"<d:lockinfo xmlns:d='DAV:'>\n" +
" <d:lockscope>\n" +
" <d:exclusive/>\n" +
" </d:lockscope>\n" +
"</d:lockinfo>\n";

this._client.request(
'LOCK',
options.pathIsUrl ? path : this._buildUrl(path),
{},
lockBody
).then(
function(result) {
if (self._isSuccessStatus(result.status)) {
deferred.resolve(result.status, result);
} else {
result = _.extend(result, self._getSabreException(result));
deferred.reject(result.status, result);
}
}
);
return promise;
},

/**
* Creates a directory
*
Expand Down