Compare commits
146 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 304cf28f4a | |||
| 6073704ace | |||
| bc1c247a13 | |||
| 3a5ba033d5 | |||
| 6fd0734944 | |||
| ee05b1b6d6 | |||
| af7616743e | |||
| 0cd61b01fd | |||
| 1c370506e4 | |||
| b61bb83680 | |||
| 01816a2ba2 | |||
| 3f3bcbeb47 | |||
| b71cf0b903 | |||
| 07257762e2 | |||
| 5a177b42df | |||
| 24be4e5add | |||
| efe8384f5c | |||
| 044db63190 | |||
| 5fc440b98a | |||
| 99b26d6916 | |||
| ee1e7d9726 | |||
| daf14861e4 | |||
| cbcdcda436 | |||
| 4e9e85816d | |||
| 997c307251 | |||
| e627c7942e | |||
| 54ad29c3ed | |||
| 7573ba9a4d | |||
| 0dc31ee2fe | |||
| 24eb04f724 | |||
| e1b59ad7f0 | |||
| 73750f0c1e | |||
| ad95e308ba | |||
| 937a1b2b93 | |||
| 019e72040a | |||
| 6d0e167c4d | |||
| 2a0460d885 | |||
| 7d2bb6c028 | |||
| dbec295647 | |||
| a2be470dce | |||
| 0aca631216 | |||
| d935bdfd6a | |||
| 25ef9909f0 | |||
| e1642400f5 | |||
| 2b4ac38a08 | |||
| eb12cac973 | |||
| 86b822dd52 | |||
| 79932c6d98 | |||
| 6209b40647 | |||
| 8f04555e22 | |||
| d3d18b1abc | |||
| 4035bcf26a | |||
| 74e54da304 | |||
| 70be8a309f | |||
| 74a33b0e56 | |||
| ea6917d18a | |||
| a399905aff | |||
| aba6b938f9 | |||
| 84e69f4931 | |||
| ab87fc7d3a | |||
| 234d9d8a58 | |||
| c2f5fd44c5 | |||
| 06036df9f7 | |||
| 462fc21b06 | |||
| 7a2ef950b3 | |||
| a4d9d9f845 | |||
| 7164178cd3 | |||
| 0f3adb856f | |||
| a2b5a2f109 | |||
| f3d58eebfc | |||
| 0ea4274c8d | |||
| b0cecfb92a | |||
| cddf111dbb | |||
| 1cf1119b0b | |||
| 394751a985 | |||
| 26d98e9772 | |||
| 7ddbaf649f | |||
| 9c5dcb4cbf | |||
| dbf5c266c7 | |||
| 7010f42986 | |||
| 530f313dff | |||
| f4e68ca36a | |||
| 4408507fc7 | |||
| 68a92f8186 | |||
| 541474d5ec | |||
| cf84a1128d | |||
| 490101a3a1 | |||
| 7cbc348780 | |||
| 331750a3b7 | |||
| ca811d3079 | |||
| 37efd1d001 | |||
| 4dd5efd464 | |||
| 9ca706b046 | |||
| 970a9c2382 | |||
| be9df999fc | |||
| 96feb6f05b | |||
| be9e72084d | |||
| 38c7902795 | |||
| 36066e1382 | |||
| e3a35506c4 | |||
| 99b78345a0 | |||
| 206eb29331 | |||
| 8ee2009de3 | |||
| ab0fc36a5f | |||
| 0cdece9bd4 | |||
| f8870a07b4 | |||
| 01bff3bc24 | |||
| 6bd1b03288 | |||
| 90d436e1e0 | |||
| bd835f9ecc | |||
| 0d868a9843 | |||
| ab84f785d3 | |||
| 063c2db084 | |||
| 68f0d107f5 | |||
| 1d04317fbc | |||
| dc3aee5acc | |||
| 2729c0417d | |||
| c4c8296495 | |||
| 0eb374fe12 | |||
| 8165c08cc5 | |||
| 614a22cf32 | |||
| 6f20f60f5c | |||
| 8ac5398f4e | |||
| ba46cfed3a | |||
| c8da73667b | |||
| a50643ebc9 | |||
| 718edf7828 | |||
| 71144522c9 | |||
| 9f052e44c8 | |||
| cc03a1df0b | |||
| a470b4917c | |||
| 4a0ca37cb3 | |||
| a4cbdfdc1f | |||
| b6f5e13b32 | |||
| da3cfb8710 | |||
| b0abb583c9 | |||
| f2ed3e726a | |||
| 2ff3e87f74 | |||
| f4ffa94e7f | |||
| a1ce7ac872 | |||
| f7b62abcce | |||
| baac1ad489 | |||
| 7b00b943ee | |||
| 02493563a2 | |||
| ee80cea2bd | |||
| 06bfe1139b |
@@ -1,4 +1,4 @@
|
||||
# Version: 8.2.0
|
||||
# Version: 8.2.1
|
||||
<IfModule mod_headers.c>
|
||||
<IfModule mod_fcgid.c>
|
||||
<IfModule mod_setenvif.c>
|
||||
|
||||
+1
-1
Submodule 3rdparty updated: 1914e923a4...25b0acc9e8
@@ -14,18 +14,19 @@
|
||||
<name>Default encryption module</name>
|
||||
<license>AGPL</license>
|
||||
<author>Bjoern Schiessle, Clark Tomlinson</author>
|
||||
<requiremin>8</requiremin>
|
||||
<shipped>true</shipped>
|
||||
<documentation>
|
||||
<user>user-encryption</user>
|
||||
<admin>admin-encryption</admin>
|
||||
</documentation>
|
||||
<rememberlogin>false</rememberlogin>
|
||||
<version>1.1.0</version>
|
||||
<types>
|
||||
<filesystem/>
|
||||
</types>
|
||||
<dependencies>
|
||||
<lib>openssl</lib>
|
||||
<owncloud min-version="8.2" />
|
||||
</dependencies>
|
||||
|
||||
</info>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
1.0.0
|
||||
@@ -53,7 +53,7 @@ class Crypt {
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
* @var IUser
|
||||
* @var string
|
||||
*/
|
||||
private $user;
|
||||
/**
|
||||
@@ -73,7 +73,7 @@ class Crypt {
|
||||
*/
|
||||
public function __construct(ILogger $logger, IUserSession $userSession, IConfig $config) {
|
||||
$this->logger = $logger;
|
||||
$this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser() : false;
|
||||
$this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : '"no user given"';
|
||||
$this->config = $config;
|
||||
$this->supportedKeyFormats = ['hash', 'password'];
|
||||
}
|
||||
@@ -89,7 +89,7 @@ class Crypt {
|
||||
$res = $this->getOpenSSLPKey();
|
||||
|
||||
if (!$res) {
|
||||
$log->error("Encryption Library couldn't generate users key-pair for {$this->user->getUID()}",
|
||||
$log->error("Encryption Library couldn't generate users key-pair for {$this->user}",
|
||||
['app' => 'encryption']);
|
||||
|
||||
if (openssl_error_string()) {
|
||||
@@ -108,7 +108,7 @@ class Crypt {
|
||||
'privateKey' => $privateKey
|
||||
];
|
||||
}
|
||||
$log->error('Encryption library couldn\'t export users private key, please check your servers OpenSSL configuration.' . $this->user->getUID(),
|
||||
$log->error('Encryption library couldn\'t export users private key, please check your servers OpenSSL configuration.' . $this->user,
|
||||
['app' => 'encryption']);
|
||||
if (openssl_error_string()) {
|
||||
$log->error('Encryption Library:' . openssl_error_string(),
|
||||
|
||||
@@ -62,6 +62,8 @@ class MigrationTest extends \Test\TestCase {
|
||||
}
|
||||
|
||||
protected function createDummyShareKeys($uid) {
|
||||
$this->loginAsUser($uid);
|
||||
|
||||
$this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/folder3/file3');
|
||||
$this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/file2');
|
||||
$this->view->mkdir($uid . '/files_encryption/keys/folder1/file.1');
|
||||
@@ -87,6 +89,8 @@ class MigrationTest extends \Test\TestCase {
|
||||
}
|
||||
|
||||
protected function createDummyUserKeys($uid) {
|
||||
$this->loginAsUser($uid);
|
||||
|
||||
$this->view->mkdir($uid . '/files_encryption/');
|
||||
$this->view->mkdir('/files_encryption/public_keys');
|
||||
$this->view->file_put_contents($uid . '/files_encryption/' . $uid . '.privateKey', 'privateKey');
|
||||
@@ -94,6 +98,8 @@ class MigrationTest extends \Test\TestCase {
|
||||
}
|
||||
|
||||
protected function createDummyFileKeys($uid) {
|
||||
$this->loginAsUser($uid);
|
||||
|
||||
$this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/folder3/file3');
|
||||
$this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/file2');
|
||||
$this->view->mkdir($uid . '/files_encryption/keys/folder1/file.1');
|
||||
@@ -105,6 +111,8 @@ class MigrationTest extends \Test\TestCase {
|
||||
}
|
||||
|
||||
protected function createDummyFiles($uid) {
|
||||
$this->loginAsUser($uid);
|
||||
|
||||
$this->view->mkdir($uid . '/files/folder1/folder2/folder3/file3');
|
||||
$this->view->mkdir($uid . '/files/folder1/folder2/file2');
|
||||
$this->view->mkdir($uid . '/files/folder1/file.1');
|
||||
@@ -116,6 +124,8 @@ class MigrationTest extends \Test\TestCase {
|
||||
}
|
||||
|
||||
protected function createDummyFilesInTrash($uid) {
|
||||
$this->loginAsUser($uid);
|
||||
|
||||
$this->view->mkdir($uid . '/files_trashbin/keys/file1.d5457864');
|
||||
$this->view->mkdir($uid . '/files_trashbin/keys/folder1.d7437648723/file2');
|
||||
$this->view->file_put_contents($uid . '/files_trashbin/keys/file1.d5457864/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data');
|
||||
@@ -165,6 +175,7 @@ class MigrationTest extends \Test\TestCase {
|
||||
|
||||
$this->createDummySystemWideKeys();
|
||||
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject|\OCA\Encryption\Migration $m */
|
||||
$m = $this->getMockBuilder('OCA\Encryption\Migration')
|
||||
->setConstructorArgs(
|
||||
[
|
||||
@@ -176,27 +187,38 @@ class MigrationTest extends \Test\TestCase {
|
||||
)->setMethods(['getSystemMountPoints'])->getMock();
|
||||
|
||||
$m->expects($this->any())->method('getSystemMountPoints')
|
||||
->willReturn([['mountpoint' => 'folder1'], ['mountpoint' => 'folder2']]);
|
||||
->will($this->returnValue([['mountpoint' => 'folder1'], ['mountpoint' => 'folder2']]));
|
||||
|
||||
$m->reorganizeFolderStructure();
|
||||
// even if it runs twice folder should always move only once
|
||||
$m->reorganizeFolderStructure();
|
||||
|
||||
$this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER1);
|
||||
|
||||
$this->assertTrue(
|
||||
$this->view->file_exists(
|
||||
self::TEST_ENCRYPTION_MIGRATION_USER1 . '/files_encryption/' .
|
||||
$this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.publicKey')
|
||||
);
|
||||
|
||||
$this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER2);
|
||||
|
||||
$this->assertTrue(
|
||||
$this->view->file_exists(
|
||||
self::TEST_ENCRYPTION_MIGRATION_USER2 . '/files_encryption/' .
|
||||
$this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.publicKey')
|
||||
);
|
||||
|
||||
$this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER3);
|
||||
|
||||
$this->assertTrue(
|
||||
$this->view->file_exists(
|
||||
self::TEST_ENCRYPTION_MIGRATION_USER3 . '/files_encryption/' .
|
||||
$this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.publicKey')
|
||||
);
|
||||
|
||||
$this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER1);
|
||||
|
||||
$this->assertTrue(
|
||||
$this->view->file_exists(
|
||||
'/files_encryption/' . $this->moduleId . '/systemwide_1.publicKey')
|
||||
@@ -217,6 +239,8 @@ class MigrationTest extends \Test\TestCase {
|
||||
}
|
||||
|
||||
protected function verifyFilesInTrash($uid) {
|
||||
$this->loginAsUser($uid);
|
||||
|
||||
// share keys
|
||||
$this->assertTrue(
|
||||
$this->view->file_exists($uid . '/files_encryption/keys/files_trashbin/file1.d5457864/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey')
|
||||
@@ -244,6 +268,7 @@ class MigrationTest extends \Test\TestCase {
|
||||
protected function verifyNewKeyPath($uid) {
|
||||
// private key
|
||||
if ($uid !== '') {
|
||||
$this->loginAsUser($uid);
|
||||
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/' . $this->moduleId . '/'. $uid . '.privateKey'));
|
||||
}
|
||||
// file keys
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<description>File Management</description>
|
||||
<licence>AGPL</licence>
|
||||
<author>Robin Appelman, Vincent Petry</author>
|
||||
<requiremin>4.93</requiremin>
|
||||
<shipped>true</shipped>
|
||||
<standalone/>
|
||||
<default_enable/>
|
||||
<version>1.2.0</version>
|
||||
<types>
|
||||
<filesystem/>
|
||||
</types>
|
||||
@@ -16,6 +16,9 @@
|
||||
<files>appinfo/remote.php</files>
|
||||
<webdav>appinfo/remote.php</webdav>
|
||||
</remote>
|
||||
<dependencies>
|
||||
<owncloud min-version="8.2" />
|
||||
</dependencies>
|
||||
<documentation>
|
||||
<user>user-files</user>
|
||||
</documentation>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
1.1.11
|
||||
@@ -85,19 +85,6 @@
|
||||
background-image: url('../img/delete.svg');
|
||||
}
|
||||
|
||||
/* move Deleted Files to bottom of sidebar */
|
||||
.nav-trashbin {
|
||||
position: fixed !important;
|
||||
bottom: 44px;
|
||||
width: inherit !important;
|
||||
background-color: #fff;
|
||||
border-right: 1px solid #eee;
|
||||
}
|
||||
/* double padding to account for Deleted files entry, issue with Firefox */
|
||||
.app-files #app-navigation > ul li:nth-last-child(2) {
|
||||
margin-bottom: 44px;
|
||||
}
|
||||
|
||||
#app-navigation .nav-files a.nav-icon-files {
|
||||
width: auto;
|
||||
}
|
||||
@@ -745,6 +732,10 @@ table.dragshadow td.size {
|
||||
margin: -10px;
|
||||
}
|
||||
|
||||
html.ie8 #controls .button.new {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.newFileMenu {
|
||||
width: 140px;
|
||||
margin-left: -56px;
|
||||
@@ -796,3 +787,4 @@ table.dragshadow td.size {
|
||||
color: #000;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
* - TODO music upload button
|
||||
*/
|
||||
|
||||
/* global jQuery, oc_requesttoken, humanFileSize */
|
||||
/* global jQuery, oc_requesttoken, humanFileSize, FileList */
|
||||
|
||||
/**
|
||||
* Function that will allow us to know if Ajax uploads are supported
|
||||
@@ -47,6 +47,26 @@ function supportAjaxUploadWithProgress() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add form data into the given form data
|
||||
*
|
||||
* @param {Array|Object} formData form data which can either be an array or an object
|
||||
* @param {Object} newData key-values to add to the form data
|
||||
*
|
||||
* @return updated form data
|
||||
*/
|
||||
function addFormData(formData, newData) {
|
||||
// in IE8, formData is an array instead of object
|
||||
if (_.isArray(formData)) {
|
||||
_.each(newData, function(value, key) {
|
||||
formData.push({name: key, value: value});
|
||||
});
|
||||
} else {
|
||||
formData = _.extend(formData, newData);
|
||||
}
|
||||
return formData;
|
||||
}
|
||||
|
||||
/**
|
||||
* keeps track of uploads in progress and implements callbacks for the conflicts dialog
|
||||
* @namespace
|
||||
@@ -75,6 +95,9 @@ OC.Upload = {
|
||||
this._uploads.push(jqXHR);
|
||||
}
|
||||
},
|
||||
showUploadCancelMessage: _.debounce(function() {
|
||||
OC.Notification.showTemporary(t('files', 'Upload cancelled.'), {timeout: 10});
|
||||
}, 500),
|
||||
/**
|
||||
* Checks the currently known uploads.
|
||||
* returns true if any hxr has the state 'pending'
|
||||
@@ -140,9 +163,9 @@ OC.Upload = {
|
||||
data.data.append('resolution', 'replace');
|
||||
} else {
|
||||
if (!data.formData) {
|
||||
data.formData = [];
|
||||
data.formData = {};
|
||||
}
|
||||
data.formData.push({name:'resolution', value:'replace'}); //hack for ie8
|
||||
addFormData(data.formData, {resolution: 'replace'});
|
||||
}
|
||||
data.submit();
|
||||
},
|
||||
@@ -156,9 +179,9 @@ OC.Upload = {
|
||||
data.data.append('resolution', 'autorename');
|
||||
} else {
|
||||
if (!data.formData) {
|
||||
data.formData = [];
|
||||
data.formData = {};
|
||||
}
|
||||
data.formData.push({name:'resolution', value:'autorename'}); //hack for ie8
|
||||
addFormData(data.formData, {resolution: 'autorename'});
|
||||
}
|
||||
data.submit();
|
||||
},
|
||||
@@ -182,7 +205,7 @@ OC.Upload = {
|
||||
* @param {function} callbacks.onCancel
|
||||
*/
|
||||
checkExistingFiles: function (selection, callbacks) {
|
||||
var fileList = OCA.Files.App.fileList;
|
||||
var fileList = FileList;
|
||||
var conflicts = [];
|
||||
// only keep non-conflicting uploads
|
||||
selection.uploads = _.filter(selection.uploads, function(upload) {
|
||||
@@ -399,26 +422,28 @@ OC.Upload = {
|
||||
submit: function(e, data) {
|
||||
OC.Upload.rememberUpload(data);
|
||||
if (!data.formData) {
|
||||
data.formData = [];
|
||||
data.formData = {};
|
||||
}
|
||||
|
||||
var fileDirectory = '';
|
||||
if(typeof data.files[0].relativePath !== 'undefined') {
|
||||
fileDirectory = data.files[0].relativePath;
|
||||
}
|
||||
// FIXME: prevent re-adding the same
|
||||
data.formData.push({name: 'requesttoken', value: oc_requesttoken});
|
||||
data.formData.push({name: 'dir', value: data.targetDir || FileList.getCurrentDirectory()});
|
||||
data.formData.push({name: 'file_directory', value: fileDirectory});
|
||||
|
||||
addFormData(data.formData, {
|
||||
requesttoken: oc_requesttoken,
|
||||
dir: data.targetDir || FileList.getCurrentDirectory(),
|
||||
file_directory: fileDirectory
|
||||
});
|
||||
},
|
||||
fail: function(e, data) {
|
||||
OC.Upload.log('fail', e, data);
|
||||
if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) {
|
||||
if (data.textStatus === 'abort') {
|
||||
OC.Notification.show(t('files', 'Upload cancelled.'));
|
||||
OC.Upload.showUploadCancelMessage();
|
||||
} else {
|
||||
// HTTP connection problem
|
||||
OC.Notification.show(data.errorThrown);
|
||||
OC.Notification.showTemporary(data.errorThrown, {timeout: 10});
|
||||
if (data.result) {
|
||||
var result = JSON.parse(data.result);
|
||||
if (result && result[0] && result[0].data && result[0].data.code === 'targetnotfound') {
|
||||
@@ -427,10 +452,6 @@ OC.Upload = {
|
||||
}
|
||||
}
|
||||
}
|
||||
//hide notification after 10 sec
|
||||
setTimeout(function() {
|
||||
OC.Notification.hide();
|
||||
}, 10000);
|
||||
}
|
||||
OC.Upload.deleteUpload(data);
|
||||
},
|
||||
|
||||
+41
-28
@@ -386,12 +386,15 @@
|
||||
* Update the details view to display the given file
|
||||
*
|
||||
* @param {string} fileName file name from the current list
|
||||
* @param {boolean} [show=true] whether to open the sidebar if it was closed
|
||||
*/
|
||||
_updateDetailsView: function(fileName) {
|
||||
_updateDetailsView: function(fileName, show) {
|
||||
if (!this._detailsView) {
|
||||
return;
|
||||
}
|
||||
|
||||
// show defaults to true
|
||||
show = _.isUndefined(show) || !!show;
|
||||
var oldFileInfo = this._detailsView.getFileInfo();
|
||||
if (oldFileInfo) {
|
||||
// TODO: use more efficient way, maybe track the highlight
|
||||
@@ -409,7 +412,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._detailsView.$el.hasClass('disappear')) {
|
||||
if (show && this._detailsView.$el.hasClass('disappear')) {
|
||||
OC.Apps.showAppSidebar(this._detailsView.$el);
|
||||
}
|
||||
|
||||
@@ -760,7 +763,7 @@
|
||||
*/
|
||||
elementToFile: function($el){
|
||||
$el = $($el);
|
||||
return {
|
||||
var data = {
|
||||
id: parseInt($el.attr('data-id'), 10),
|
||||
name: $el.attr('data-file'),
|
||||
mimetype: $el.attr('data-mime'),
|
||||
@@ -770,6 +773,15 @@
|
||||
etag: $el.attr('data-etag'),
|
||||
permissions: parseInt($el.attr('data-permissions'), 10)
|
||||
};
|
||||
var icon = $el.attr('data-icon');
|
||||
if (icon) {
|
||||
data.icon = icon;
|
||||
}
|
||||
var mountType = $el.attr('data-mounttype');
|
||||
if (mountType) {
|
||||
data.mountType = mountType;
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -892,11 +904,12 @@
|
||||
mtime = parseInt(fileData.mtime, 10),
|
||||
mime = fileData.mimetype,
|
||||
path = fileData.path,
|
||||
dataIcon = null,
|
||||
linkUrl;
|
||||
options = options || {};
|
||||
|
||||
if (isNaN(mtime)) {
|
||||
mtime = new Date().getTime()
|
||||
mtime = new Date().getTime();
|
||||
}
|
||||
|
||||
if (type === 'dir') {
|
||||
@@ -904,6 +917,7 @@
|
||||
|
||||
if (fileData.mountType && fileData.mountType.indexOf('external') === 0) {
|
||||
icon = OC.MimeType.getIconUrl('dir-external');
|
||||
dataIcon = icon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -919,6 +933,11 @@
|
||||
"data-permissions": fileData.permissions || this.getDirectoryPermissions()
|
||||
});
|
||||
|
||||
if (dataIcon) {
|
||||
// icon override
|
||||
tr.attr('data-icon', dataIcon);
|
||||
}
|
||||
|
||||
if (fileData.mountType) {
|
||||
tr.attr('data-mounttype', fileData.mountType);
|
||||
}
|
||||
@@ -1170,7 +1189,7 @@
|
||||
// display actions
|
||||
this.fileActions.display(filenameTd, !options.silent, this);
|
||||
|
||||
if (fileData.isPreviewAvailable) {
|
||||
if (fileData.isPreviewAvailable && mime !== 'httpd/unix-directory') {
|
||||
var iconDiv = filenameTd.find('.thumbnail');
|
||||
// lazy load / newly inserted td ?
|
||||
// the typeof check ensures that the default value of animate is true
|
||||
@@ -1350,7 +1369,7 @@
|
||||
) {
|
||||
OC.redirect(OC.generateUrl('apps/files'));
|
||||
}
|
||||
OC.Notification.show(result.data.message);
|
||||
OC.Notification.showTemporary(result.data.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1358,7 +1377,7 @@
|
||||
if (result.status === 403) {
|
||||
// Go home
|
||||
this.changeDirectory('/');
|
||||
OC.Notification.show(t('files', 'This operation is forbidden'));
|
||||
OC.Notification.showTemporary(t('files', 'This operation is forbidden'));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1366,7 +1385,7 @@
|
||||
if (result.status === 500) {
|
||||
// Go home
|
||||
this.changeDirectory('/');
|
||||
OC.Notification.show(t('files', 'This directory is unavailable, please check the logs or contact the administrator'));
|
||||
OC.Notification.showTemporary(t('files', 'This directory is unavailable, please check the logs or contact the administrator'));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1640,15 +1659,11 @@
|
||||
} else {
|
||||
OC.Notification.hide();
|
||||
if (result.status === 'error' && result.data.message) {
|
||||
OC.Notification.show(result.data.message);
|
||||
OC.Notification.showTemporary(result.data.message);
|
||||
}
|
||||
else {
|
||||
OC.Notification.show(t('files', 'Error moving file.'));
|
||||
OC.Notification.showTemporary(t('files', 'Error moving file.'));
|
||||
}
|
||||
// hide notification after 10 sec
|
||||
setTimeout(function() {
|
||||
OC.Notification.hide();
|
||||
}, 10000);
|
||||
}
|
||||
} else {
|
||||
OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error'));
|
||||
@@ -1771,7 +1786,7 @@
|
||||
tr.remove();
|
||||
tr = self.add(fileInfo, {updateSummary: false, silent: true});
|
||||
self.$fileList.trigger($.Event('fileActionsReady', {fileList: self, $files: $(tr)}));
|
||||
self._updateDetailsView(fileInfo.name);
|
||||
self._updateDetailsView(fileInfo.name, false);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -2011,17 +2026,15 @@
|
||||
self.fileSummary.update();
|
||||
self.updateSelectionSummary();
|
||||
self.updateStorageStatistics();
|
||||
// in case there was a "storage full" permanent notification
|
||||
OC.Notification.hide();
|
||||
} else {
|
||||
if (result.status === 'error' && result.data.message) {
|
||||
OC.Notification.show(result.data.message);
|
||||
OC.Notification.showTemporary(result.data.message);
|
||||
}
|
||||
else {
|
||||
OC.Notification.show(t('files', 'Error deleting file.'));
|
||||
OC.Notification.showTemporary(t('files', 'Error deleting file.'));
|
||||
}
|
||||
// hide notification after 10 sec
|
||||
setTimeout(function() {
|
||||
OC.Notification.hide();
|
||||
}, 10000);
|
||||
if (params.allfiles) {
|
||||
// reload the page as we don't know what files were deleted
|
||||
// and which ones remain
|
||||
@@ -2262,11 +2275,7 @@
|
||||
*/
|
||||
_showPermissionDeniedNotification: function() {
|
||||
var message = t('core', 'You don’t have permission to upload or create files here');
|
||||
OC.Notification.show(message);
|
||||
//hide notification after 10 sec
|
||||
setTimeout(function() {
|
||||
OC.Notification.hide();
|
||||
}, 5000);
|
||||
OC.Notification.showTemporary(message);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -2620,14 +2629,18 @@
|
||||
* Register a tab view to be added to all views
|
||||
*/
|
||||
registerTabView: function(tabView) {
|
||||
this._detailsView.addTabView(tabView);
|
||||
if (this._detailsView) {
|
||||
this._detailsView.addTabView(tabView);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Register a detail view to be added to all views
|
||||
*/
|
||||
registerDetailView: function(detailView) {
|
||||
this._detailsView.addDetailView(detailView);
|
||||
if (this._detailsView) {
|
||||
this._detailsView.addDetailView(detailView);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
'<div class="fileName"><h3 title="{{name}}" class="ellipsis">{{name}}</h3></div>' +
|
||||
' <div class="file-details ellipsis">' +
|
||||
' <a href="#" ' +
|
||||
' alt="{{starAltText}}"' +
|
||||
' class="action action-favorite favorite">' +
|
||||
' <img class="svg" src="{{starIcon}}" />' +
|
||||
' <img class="svg" alt="{{starAltText}}" src="{{starIcon}}" />' +
|
||||
' </a>' +
|
||||
' {{#if hasSize}}<span class="size" title="{{altSize}}">{{size}}</span>, {{/if}}<span class="date" title="{{altDate}}">{{date}}</span>' +
|
||||
' </div>' +
|
||||
@@ -66,10 +65,10 @@
|
||||
this._fileList = options.fileList;
|
||||
this._fileActions = options.fileActions;
|
||||
if (!this._fileList) {
|
||||
throw 'Missing requird parameter "fileList"';
|
||||
throw 'Missing required parameter "fileList"';
|
||||
}
|
||||
if (!this._fileActions) {
|
||||
throw 'Missing requird parameter "fileActions"';
|
||||
throw 'Missing required parameter "fileActions"';
|
||||
}
|
||||
},
|
||||
|
||||
@@ -129,8 +128,8 @@
|
||||
$iconDiv.addClass('icon-loading icon-32');
|
||||
this.loadPreview(this.model.getFullPath(), this.model.get('mimetype'), this.model.get('etag'), $iconDiv, $container, this.model.isImage());
|
||||
} else {
|
||||
// TODO: special icons / shared / external
|
||||
$iconDiv.css('background-image', 'url("' + OC.MimeType.getIconUrl('dir') + '")');
|
||||
var iconUrl = this.model.get('icon') || OC.MimeType.getIconUrl('dir');
|
||||
$iconDiv.css('background-image', 'url("' + iconUrl + '")');
|
||||
OC.Util.scaleFixForIE8($iconDiv);
|
||||
}
|
||||
this.$el.find('[title]').tooltip({placement: 'bottom'});
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
namespace OCA\Files\Tests\Command;
|
||||
|
||||
use OCA\Files\Command\DeleteOrphanedFiles;
|
||||
use OCP\Files\StorageNotAvailableException;
|
||||
|
||||
class DeleteOrphanedFilesTest extends \Test\TestCase {
|
||||
|
||||
@@ -110,7 +111,11 @@ class DeleteOrphanedFilesTest extends \Test\TestCase {
|
||||
|
||||
$this->assertCount(0, $this->getFile($fileInfo->getId()), 'Asserts that file gets cleaned up');
|
||||
|
||||
$view->unlink('files/test');
|
||||
// since we deleted the storage it might throw a (valid) StorageNotAvailableException
|
||||
try {
|
||||
$view->unlink('files/test');
|
||||
} catch (StorageNotAvailableException $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,4 +117,100 @@ describe('OC.Upload tests', function() {
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('Upload conflicts', function() {
|
||||
var oldFileList;
|
||||
var conflictDialogStub;
|
||||
var callbacks;
|
||||
|
||||
beforeEach(function() {
|
||||
oldFileList = FileList;
|
||||
$('#testArea').append(
|
||||
'<div id="tableContainer">' +
|
||||
'<table id="filestable">' +
|
||||
'<thead><tr>' +
|
||||
'<th id="headerName" class="hidden column-name">' +
|
||||
'<input type="checkbox" id="select_all_files" class="select-all">' +
|
||||
'<a class="name columntitle" data-sort="name"><span>Name</span><span class="sort-indicator"></span></a>' +
|
||||
'<span id="selectedActionsList" class="selectedActions hidden">' +
|
||||
'<a href class="download"><img src="actions/download.svg">Download</a>' +
|
||||
'<a href class="delete-selected">Delete</a></span>' +
|
||||
'</th>' +
|
||||
'<th class="hidden column-size"><a class="columntitle" data-sort="size"><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>'
|
||||
);
|
||||
FileList = new OCA.Files.FileList($('#tableContainer'));
|
||||
|
||||
FileList.add({name: 'conflict.txt', mimetype: 'text/plain'});
|
||||
FileList.add({name: 'conflict2.txt', mimetype: 'text/plain'});
|
||||
|
||||
conflictDialogStub = sinon.stub(OC.dialogs, 'fileexists');
|
||||
callbacks = {
|
||||
onNoConflicts: sinon.stub()
|
||||
};
|
||||
});
|
||||
afterEach(function() {
|
||||
conflictDialogStub.restore();
|
||||
|
||||
FileList.destroy();
|
||||
FileList = oldFileList;
|
||||
});
|
||||
it('does not show conflict dialog when no client side conflict', function() {
|
||||
var selection = {
|
||||
// yes, the format of uploads is weird...
|
||||
uploads: [
|
||||
{files: [{name: 'noconflict.txt'}]},
|
||||
{files: [{name: 'noconflict2.txt'}]}
|
||||
]
|
||||
};
|
||||
|
||||
OC.Upload.checkExistingFiles(selection, callbacks);
|
||||
|
||||
expect(conflictDialogStub.notCalled).toEqual(true);
|
||||
expect(callbacks.onNoConflicts.calledOnce).toEqual(true);
|
||||
expect(callbacks.onNoConflicts.calledWith(selection)).toEqual(true);
|
||||
});
|
||||
it('shows conflict dialog when no client side conflict', function() {
|
||||
var selection = {
|
||||
// yes, the format of uploads is weird...
|
||||
uploads: [
|
||||
{files: [{name: 'conflict.txt'}]},
|
||||
{files: [{name: 'conflict2.txt'}]},
|
||||
{files: [{name: 'noconflict.txt'}]}
|
||||
]
|
||||
};
|
||||
|
||||
var deferred = $.Deferred();
|
||||
conflictDialogStub.returns(deferred.promise());
|
||||
deferred.resolve();
|
||||
|
||||
OC.Upload.checkExistingFiles(selection, callbacks);
|
||||
|
||||
expect(conflictDialogStub.callCount).toEqual(3);
|
||||
expect(conflictDialogStub.getCall(1).args[0])
|
||||
.toEqual({files: [ { name: 'conflict.txt' } ]});
|
||||
expect(conflictDialogStub.getCall(1).args[1])
|
||||
.toEqual({ name: 'conflict.txt', mimetype: 'text/plain', directory: '/' });
|
||||
expect(conflictDialogStub.getCall(1).args[2]).toEqual({ name: 'conflict.txt' });
|
||||
|
||||
// yes, the dialog must be called several times...
|
||||
expect(conflictDialogStub.getCall(2).args[0]).toEqual({
|
||||
files: [ { name: 'conflict2.txt' } ]
|
||||
});
|
||||
expect(conflictDialogStub.getCall(2).args[1])
|
||||
.toEqual({ name: 'conflict2.txt', mimetype: 'text/plain', directory: '/' });
|
||||
expect(conflictDialogStub.getCall(2).args[2]).toEqual({ name: 'conflict2.txt' });
|
||||
|
||||
expect(callbacks.onNoConflicts.calledOnce).toEqual(true);
|
||||
expect(callbacks.onNoConflicts.calledWith({
|
||||
uploads: [
|
||||
{files: [{name: 'noconflict.txt'}]}
|
||||
]
|
||||
})).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1166,7 +1166,7 @@ describe('OCA.Files.FileList tests', function() {
|
||||
it('renders provided icon for file when provided', function() {
|
||||
var fileData = {
|
||||
type: 'file',
|
||||
name: 'test dir',
|
||||
name: 'test file',
|
||||
icon: OC.webroot + '/core/img/filetypes/application-pdf.svg',
|
||||
mimetype: 'application/pdf'
|
||||
};
|
||||
@@ -1178,7 +1178,7 @@ describe('OCA.Files.FileList tests', function() {
|
||||
it('renders preview when no icon was provided and preview is available', function() {
|
||||
var fileData = {
|
||||
type: 'file',
|
||||
name: 'test dir',
|
||||
name: 'test file',
|
||||
isPreviewAvailable: true
|
||||
};
|
||||
var $tr = fileList.add(fileData);
|
||||
@@ -1192,7 +1192,7 @@ describe('OCA.Files.FileList tests', function() {
|
||||
it('renders default file type icon when no icon was provided and no preview is available', function() {
|
||||
var fileData = {
|
||||
type: 'file',
|
||||
name: 'test dir',
|
||||
name: 'test file',
|
||||
isPreviewAvailable: false
|
||||
};
|
||||
var $tr = fileList.add(fileData);
|
||||
@@ -1200,6 +1200,47 @@ describe('OCA.Files.FileList tests', function() {
|
||||
expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
|
||||
expect(previewLoadStub.notCalled).toEqual(true);
|
||||
});
|
||||
it('does not render preview for directories', function() {
|
||||
var fileData = {
|
||||
type: 'dir',
|
||||
mimetype: 'httpd/unix-directory',
|
||||
name: 'test dir',
|
||||
isPreviewAvailable: true
|
||||
};
|
||||
var $tr = fileList.add(fileData);
|
||||
var $td = $tr.find('td.filename');
|
||||
expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/core/img/filetypes/folder.svg');
|
||||
expect(previewLoadStub.notCalled).toEqual(true);
|
||||
});
|
||||
it('render external storage icon for external storage root', function() {
|
||||
var fileData = {
|
||||
type: 'dir',
|
||||
mimetype: 'httpd/unix-directory',
|
||||
name: 'test dir',
|
||||
isPreviewAvailable: true,
|
||||
mountType: 'external-root'
|
||||
};
|
||||
var $tr = fileList.add(fileData);
|
||||
var $td = $tr.find('td.filename');
|
||||
expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/core/img/filetypes/folder-external.svg');
|
||||
expect(previewLoadStub.notCalled).toEqual(true);
|
||||
});
|
||||
it('render external storage icon for external storage subdir', function() {
|
||||
var fileData = {
|
||||
type: 'dir',
|
||||
mimetype: 'httpd/unix-directory',
|
||||
name: 'test dir',
|
||||
isPreviewAvailable: true,
|
||||
mountType: 'external'
|
||||
};
|
||||
var $tr = fileList.add(fileData);
|
||||
var $td = $tr.find('td.filename');
|
||||
expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/core/img/filetypes/folder-external.svg');
|
||||
expect(previewLoadStub.notCalled).toEqual(true);
|
||||
// default icon override
|
||||
expect($tr.attr('data-icon')).toEqual(OC.webroot + '/core/img/filetypes/folder-external.svg');
|
||||
});
|
||||
|
||||
});
|
||||
describe('viewer mode', function() {
|
||||
it('enabling viewer mode hides files table and action buttons', function() {
|
||||
@@ -1879,15 +1920,54 @@ describe('OCA.Files.FileList tests', function() {
|
||||
$tr2.find('td.filename .name').trigger(e);
|
||||
expect(fileList.getSelectedFiles().length).toEqual(0);
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
describe('Details sidebar', function() {
|
||||
beforeEach(function() {
|
||||
fileList.setFiles(testFiles);
|
||||
fileList.showDetailsView('Two.jpg');
|
||||
});
|
||||
describe('registering', function() {
|
||||
var addTabStub;
|
||||
var addDetailStub;
|
||||
|
||||
beforeEach(function() {
|
||||
addTabStub = sinon.stub(OCA.Files.DetailsView.prototype, 'addTabView');
|
||||
addDetailStub = sinon.stub(OCA.Files.DetailsView.prototype, 'addDetailView');
|
||||
});
|
||||
afterEach(function() {
|
||||
addTabStub.restore();
|
||||
addDetailStub.restore();
|
||||
});
|
||||
it('forward the registered views to the underlying DetailsView', function() {
|
||||
fileList.destroy();
|
||||
fileList = new OCA.Files.FileList($('#app-content-files'), {
|
||||
detailsViewEnabled: true
|
||||
});
|
||||
fileList.registerTabView(new OCA.Files.DetailTabView());
|
||||
fileList.registerDetailView(new OCA.Files.DetailFileInfoView());
|
||||
|
||||
expect(addTabStub.calledOnce).toEqual(true);
|
||||
// twice because the filelist already registers one by default
|
||||
expect(addDetailStub.calledTwice).toEqual(true);
|
||||
});
|
||||
it('does not error when registering panels when not details view configured', function() {
|
||||
fileList.destroy();
|
||||
fileList = new OCA.Files.FileList($('#app-content-files'), {
|
||||
detailsViewEnabled: false
|
||||
});
|
||||
fileList.registerTabView(new OCA.Files.DetailTabView());
|
||||
fileList.registerDetailView(new OCA.Files.DetailFileInfoView());
|
||||
|
||||
expect(addTabStub.notCalled).toEqual(true);
|
||||
expect(addDetailStub.notCalled).toEqual(true);
|
||||
});
|
||||
});
|
||||
it('triggers file action when clicking on row if no details view configured', function() {
|
||||
fileList._detailsView = null;
|
||||
fileList.destroy();
|
||||
fileList = new OCA.Files.FileList($('#app-content-files'), {
|
||||
detailsViewEnabled: false
|
||||
});
|
||||
var updateDetailsViewStub = sinon.stub(fileList, '_updateDetailsView');
|
||||
var actionStub = sinon.stub();
|
||||
fileList.setFiles(testFiles);
|
||||
|
||||
@@ -112,6 +112,20 @@ describe('OCA.Files.MainFileInfoDetailView tests', function() {
|
||||
|
||||
lazyLoadPreviewStub.restore();
|
||||
});
|
||||
it('uses icon from model if present in model', function() {
|
||||
var lazyLoadPreviewStub = sinon.stub(fileList, 'lazyLoadPreview');
|
||||
testFileInfo.set('mimetype', 'httpd/unix-directory');
|
||||
testFileInfo.set('icon', OC.MimeType.getIconUrl('dir-external'));
|
||||
view.setFileInfo(testFileInfo);
|
||||
|
||||
expect(lazyLoadPreviewStub.notCalled).toEqual(true);
|
||||
|
||||
expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(false);
|
||||
expect(view.$el.find('.thumbnail').css('background-image'))
|
||||
.toContain('filetypes/folder-external.svg');
|
||||
|
||||
lazyLoadPreviewStub.restore();
|
||||
});
|
||||
it('displays thumbnail', function() {
|
||||
var lazyLoadPreviewStub = sinon.stub(fileList, 'lazyLoadPreview');
|
||||
|
||||
|
||||
@@ -14,12 +14,13 @@
|
||||
<admin>admin-external-storage</admin>
|
||||
</documentation>
|
||||
<rememberlogin>false</rememberlogin>
|
||||
<version>0.3.0</version>
|
||||
<types>
|
||||
<filesystem/>
|
||||
</types>
|
||||
<ocsid>166048</ocsid>
|
||||
|
||||
<dependencies>
|
||||
<owncloud min-version="8" />
|
||||
<owncloud min-version="8.2" />
|
||||
</dependencies>
|
||||
</info>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
0.2.3
|
||||
@@ -288,6 +288,7 @@ class Dropbox extends \OC\Files\Storage\Common {
|
||||
try {
|
||||
$this->dropbox->putFile(self::$tempFiles[$tmpFile], $handle);
|
||||
unlink($tmpFile);
|
||||
$this->deleteMetaData(self::$tempFiles[$tmpFile]);
|
||||
} catch (\Exception $exception) {
|
||||
\OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<?php $checkboxId = uniqid("checkbox_"); ?>
|
||||
<input type="checkbox"
|
||||
id="<?php p($checkboxId); ?>"
|
||||
<?php if (!empty($classes)): ?> class="<?php p(implode(' ', $classes)); ?>"<?php endif; ?>
|
||||
<?php if (!empty($classes)): ?> class="checkbox <?php p(implode(' ', $classes)); ?>"<?php endif; ?>
|
||||
data-parameter="<?php p($parameter->getName()); ?>"
|
||||
<?php if ($value === true): ?> checked="checked"<?php endif; ?>
|
||||
/>
|
||||
@@ -191,7 +191,7 @@
|
||||
|
||||
<?php if ($_['isAdminPage']): ?>
|
||||
<br />
|
||||
<input type="checkbox" name="allowUserMounting" id="allowUserMounting"
|
||||
<input type="checkbox" name="allowUserMounting" id="allowUserMounting" class="checkbox"
|
||||
value="1" <?php if ($_['allowUserMounting'] == 'yes') print_unescaped(' checked="checked"'); ?> />
|
||||
<label for="allowUserMounting"><?php p($l->t('Enable User External Storage')); ?></label> <span id="userMountingMsg" class="msg"></span>
|
||||
|
||||
@@ -201,7 +201,7 @@
|
||||
<?php if ($deprecateTo = $backend->getDeprecateTo()): ?>
|
||||
<input type="hidden" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" data-deprecate-to="<?php p($deprecateTo->getIdentifier()); ?>" />
|
||||
<?php else: ?>
|
||||
<input type="checkbox" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" <?php if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) print_unescaped(' checked="checked"'); ?> />
|
||||
<input type="checkbox" id="allowUserMountingBackends<?php p($i); ?>" class="checkbox" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" <?php if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) print_unescaped(' checked="checked"'); ?> />
|
||||
<label for="allowUserMountingBackends<?php p($i); ?>"><?php p($backend->getText()); ?></label> <br />
|
||||
<?php endif; ?>
|
||||
<?php $i++; ?>
|
||||
|
||||
@@ -53,6 +53,7 @@ class Server2Server {
|
||||
return new \OC_OCS_Result(null, 400, 'The mountpoint name contains invalid characters.');
|
||||
}
|
||||
|
||||
// FIXME this should be a method in the user management instead
|
||||
\OCP\Util::writeLog('files_sharing', 'shareWith before, ' . $shareWith, \OCP\Util::DEBUG);
|
||||
\OCP\Util::emitHook(
|
||||
'\OCA\Files_Sharing\API\Server2Server',
|
||||
|
||||
@@ -9,12 +9,15 @@ Turning the feature off removes shared files and folders on the server for all s
|
||||
</description>
|
||||
<licence>AGPL</licence>
|
||||
<author>Michael Gapczynski, Bjoern Schiessle</author>
|
||||
<requiremin>4.93</requiremin>
|
||||
<shipped>true</shipped>
|
||||
<default_enable/>
|
||||
<version>0.7.0</version>
|
||||
<types>
|
||||
<filesystem/>
|
||||
</types>
|
||||
<dependencies>
|
||||
<owncloud min-version="8.2" />
|
||||
</dependencies>
|
||||
<public>
|
||||
<files>public.php</files>
|
||||
<webdav>publicwebdav.php</webdav>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
0.6.3
|
||||
@@ -18,7 +18,3 @@ input[type='submit'] {
|
||||
fieldset > p {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#password-icon {
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
.shareTabView .shareWithLoading {
|
||||
padding-left: 10px;
|
||||
position: relative;
|
||||
right: 30px;
|
||||
top: 2px;
|
||||
}
|
||||
@@ -73,9 +72,18 @@
|
||||
}
|
||||
|
||||
.shareTabView .icon-loading-small {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
z-index: 1;
|
||||
background-color: white;
|
||||
padding: 2px;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.shareTabView .shareWithList .icon-loading-small,
|
||||
.shareTabView .linkShareView .icon-loading-small {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.shareTabView .linkPass .icon-loading-small {
|
||||
margin-top: 9px;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ OCA.Sharing.PublicApp = {
|
||||
$el,
|
||||
{
|
||||
id: 'files.public',
|
||||
scrollContainer: $(window),
|
||||
scrollContainer: $('#content-wrapper'),
|
||||
dragOptions: dragOptions,
|
||||
folderDropOptions: folderDropOptions,
|
||||
fileActions: fileActions,
|
||||
|
||||
@@ -130,6 +130,13 @@
|
||||
// remove icon, if applicable
|
||||
OC.Share.markFileAsShared($tr, false, false);
|
||||
}
|
||||
var newIcon = $tr.attr('data-icon');
|
||||
// in case markFileAsShared decided to change the icon,
|
||||
// we need to modify the model
|
||||
// (FIXME: yes, this is hacky)
|
||||
if (fileInfoModel.get('icon') !== newIcon) {
|
||||
fileInfoModel.set('icon', newIcon);
|
||||
}
|
||||
});
|
||||
fileList.registerTabView(shareTab);
|
||||
},
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
namespace OC\Files\Cache;
|
||||
|
||||
use OC\User\NoUserException;
|
||||
use OCP\Share_Backend_Collection;
|
||||
|
||||
/**
|
||||
@@ -64,7 +65,12 @@ class Shared_Cache extends Cache {
|
||||
}
|
||||
$source = \OC_Share_Backend_File::getSource($target, $this->storage->getShare());
|
||||
if (isset($source['path']) && isset($source['fileOwner'])) {
|
||||
\OC\Files\Filesystem::initMountPoints($source['fileOwner']);
|
||||
try {
|
||||
\OC\Files\Filesystem::initMountPoints($source['fileOwner']);
|
||||
} catch(NoUserException $e) {
|
||||
\OC::$server->getLogger()->logException($e, ['app' => 'files_sharing']);
|
||||
return false;
|
||||
}
|
||||
$mounts = \OC\Files\Filesystem::getMountByNumericId($source['storage']);
|
||||
if (is_array($mounts) and !empty($mounts)) {
|
||||
$fullPath = $mounts[0]->getMountPoint() . $source['path'];
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
placeholder="<?php p($l->t('Password')); ?>" value=""
|
||||
autocomplete="off" autocapitalize="off" autocorrect="off"
|
||||
autofocus />
|
||||
<img class="svg" id="password-icon" src="<?php print_unescaped(image_path('', 'actions/password.svg')); ?>" alt=""/>
|
||||
<input type="submit" value=""
|
||||
class="svg icon-confirm input-button-inline" />
|
||||
</p>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
href="<?php p(link_to_docs('admin-sharing-federated')); ?>"></a>
|
||||
|
||||
<p>
|
||||
<input type="checkbox" name="outgoing_server2server_share_enabled" id="outgoingServer2serverShareEnabled"
|
||||
<input type="checkbox" name="outgoing_server2server_share_enabled" id="outgoingServer2serverShareEnabled" class="checkbox"
|
||||
value="1" <?php if ($_['outgoingServer2serverShareEnabled']) print_unescaped('checked="checked"'); ?> />
|
||||
<label for="outgoingServer2serverShareEnabled">
|
||||
<?php p($l->t('Allow users on this server to send shares to other servers'));?>
|
||||
@@ -17,7 +17,7 @@
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="checkbox" name="incoming_server2server_share_enabled" id="incomingServer2serverShareEnabled"
|
||||
<input type="checkbox" name="incoming_server2server_share_enabled" id="incomingServer2serverShareEnabled" class="checkbox"
|
||||
value="1" <?php if ($_['incomingServer2serverShareEnabled']) print_unescaped('checked="checked"'); ?> />
|
||||
<label for="incomingServer2serverShareEnabled">
|
||||
<?php p($l->t('Allow users on this server to receive shares from other servers'));?>
|
||||
|
||||
@@ -47,8 +47,10 @@ class Test_Files_Sharing_Storage extends OCA\Files_sharing\Tests\TestCase {
|
||||
}
|
||||
|
||||
protected function tearDown() {
|
||||
$this->view->unlink($this->folder);
|
||||
$this->view->unlink($this->filename);
|
||||
if ($this->view) {
|
||||
$this->view->unlink($this->folder);
|
||||
$this->view->unlink($this->filename);
|
||||
}
|
||||
|
||||
\OC\Files\Filesystem::getLoader()->removeStorageWrapper('oc_trashbin');
|
||||
|
||||
@@ -85,8 +87,9 @@ class Test_Files_Sharing_Storage extends OCA\Files_sharing\Tests\TestCase {
|
||||
$this->assertFalse($user2View->is_dir($this->folder));
|
||||
|
||||
// delete the local folder
|
||||
$fullPath = \OC_Config::getValue('datadirectory') . '/' . self::TEST_FILES_SHARING_API_USER2 . '/files/localfolder';
|
||||
rmdir($fullPath);
|
||||
/** @var \OC\Files\Storage\Storage $storage */
|
||||
list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath('/' . self::TEST_FILES_SHARING_API_USER2 . '/files/localfolder');
|
||||
$storage->rmdir($internalPath);
|
||||
|
||||
//enforce reload of the mount points
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
@@ -108,9 +108,8 @@ class Test_Files_Sharing_Watcher extends OCA\Files_sharing\Tests\TestCase {
|
||||
$this->sharedCache->put('', array('mtime' => 10, 'storage_mtime' => 10, 'size' => '-1', 'mimetype' => 'httpd/unix-directory'));
|
||||
|
||||
// run the propagation code
|
||||
$result = $this->sharedStorage->getWatcher()->checkUpdate('');
|
||||
|
||||
$this->assertTrue($result);
|
||||
$this->sharedStorage->getWatcher()->checkUpdate('');
|
||||
$this->sharedStorage->getCache()->correctFolderSize('');
|
||||
|
||||
// the owner's parent dirs must have increase size
|
||||
$newSizes = self::getOwnerDirSizes('files/container/shareddir');
|
||||
@@ -139,9 +138,8 @@ class Test_Files_Sharing_Watcher extends OCA\Files_sharing\Tests\TestCase {
|
||||
$this->sharedCache->put('subdir', array('mtime' => 10, 'storage_mtime' => 10, 'size' => $dataLen, 'mimetype' => 'text/plain'));
|
||||
|
||||
// run the propagation code
|
||||
$result = $this->sharedStorage->getWatcher()->checkUpdate('subdir');
|
||||
|
||||
$this->assertTrue($result);
|
||||
$this->sharedStorage->getWatcher()->checkUpdate('subdir');
|
||||
$this->sharedStorage->getCache()->correctFolderSize('subdir');
|
||||
|
||||
// the owner's parent dirs must have increase size
|
||||
$newSizes = self::getOwnerDirSizes('files/container/shareddir/subdir');
|
||||
|
||||
@@ -10,11 +10,14 @@ To prevent a user from running out of disk space, the ownCloud Deleted files app
|
||||
<licence>AGPL</licence>
|
||||
<author>Bjoern Schiessle</author>
|
||||
<shipped>true</shipped>
|
||||
<requiremin>4.9</requiremin>
|
||||
<default_enable/>
|
||||
<version>0.7.0</version>
|
||||
<types>
|
||||
<filesystem/>
|
||||
</types>
|
||||
<dependencies>
|
||||
<owncloud min-version="8.2" />
|
||||
</dependencies>
|
||||
<documentation>
|
||||
<user>user-trashbin</user>
|
||||
</documentation>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
0.6.3
|
||||
@@ -18,3 +18,21 @@
|
||||
#app-content-trashbin #filestable .summary .filesize {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#app-navigation > ul {
|
||||
padding-bottom: 44px;
|
||||
}
|
||||
|
||||
/* move Deleted Files to bottom of sidebar */
|
||||
.nav-trashbin {
|
||||
position: fixed !important;
|
||||
bottom: 44px;
|
||||
width: inherit !important;
|
||||
background-color: #fff;
|
||||
border-right: 1px solid #eee;
|
||||
}
|
||||
/* double padding to account for Deleted files entry, issue with Firefox */
|
||||
.app-files #app-navigation > ul li:nth-last-child(2) {
|
||||
margin-bottom: 44px;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ OCA.Trashbin.App = {
|
||||
this.fileList = new OCA.Trashbin.FileList(
|
||||
$('#app-content-trashbin'), {
|
||||
scrollContainer: $('#app-content'),
|
||||
fileActions: this._createFileActions()
|
||||
fileActions: this._createFileActions(),
|
||||
detailsViewEnabled: false
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
@@ -581,8 +581,9 @@ class Trashbin {
|
||||
if ($quota === null || $quota === 'none') {
|
||||
$quota = \OC\Files\Filesystem::free_space('/');
|
||||
$softQuota = false;
|
||||
if ($quota === \OCP\Files\FileInfo::SPACE_UNKNOWN) {
|
||||
$quota = 0;
|
||||
// inf or unknown free space
|
||||
if ($quota < 0) {
|
||||
$quota = PHP_INT_MAX;
|
||||
}
|
||||
} else {
|
||||
$quota = \OCP\Util::computerFileSize($quota);
|
||||
|
||||
@@ -236,6 +236,8 @@ class Test_Trashbin extends \Test\TestCase {
|
||||
// user2-1.txt should have been expired
|
||||
$this->verifyArray($filesInTrashUser2AfterDelete, array('user2-2.txt', 'user1-4.txt'));
|
||||
|
||||
self::loginHelper(self::TEST_TRASHBIN_USER1);
|
||||
|
||||
// user1-1.txt and user1-3.txt should have been expired
|
||||
$filesInTrashUser1AfterDelete = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1);
|
||||
|
||||
@@ -600,22 +602,24 @@ class Test_Trashbin extends \Test\TestCase {
|
||||
|
||||
// delete source folder
|
||||
list($storage, $internalPath) = $this->rootView->resolvePath('/' . self::TEST_TRASHBIN_USER1 . '/files/folder');
|
||||
$folderAbsPath = $storage->getSourcePath($internalPath);
|
||||
// make folder read-only
|
||||
chmod($folderAbsPath, 0555);
|
||||
if ($storage instanceof \OC\Files\Storage\Local) {
|
||||
$folderAbsPath = $storage->getSourcePath($internalPath);
|
||||
// make folder read-only
|
||||
chmod($folderAbsPath, 0555);
|
||||
|
||||
$this->assertTrue(
|
||||
OCA\Files_Trashbin\Trashbin::restore(
|
||||
'file1.txt.d' . $trashedFile->getMtime(),
|
||||
$trashedFile->getName(),
|
||||
$trashedFile->getMtime()
|
||||
)
|
||||
);
|
||||
$this->assertTrue(
|
||||
OCA\Files_Trashbin\Trashbin::restore(
|
||||
'file1.txt.d' . $trashedFile->getMtime(),
|
||||
$trashedFile->getName(),
|
||||
$trashedFile->getMtime()
|
||||
)
|
||||
);
|
||||
|
||||
$file = $userFolder->get('file1.txt');
|
||||
$this->assertEquals('foo', $file->getContent());
|
||||
$file = $userFolder->get('file1.txt');
|
||||
$this->assertEquals('foo', $file->getContent());
|
||||
|
||||
chmod($folderAbsPath, 0755);
|
||||
chmod($folderAbsPath, 0755);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,16 +4,19 @@
|
||||
<name>Versions</name>
|
||||
<licence>AGPL</licence>
|
||||
<author>Frank Karlitschek, Bjoern Schiessle</author>
|
||||
<requiremin>4.93</requiremin>
|
||||
<shipped>true</shipped>
|
||||
<description>
|
||||
This application enables ownCloud to automatically maintain older versions of files that are changed. When enabled, a hidden versions folder is provisioned in every user’s directory and is used to store old file versions. A user can revert to an older version through the web interface at any time, with the replaced file becoming a version. ownCloud then automatically manages the versions folder to ensure the user doesn’t run out of Quota because of versions.
|
||||
In addition to the expiry of versions, ownCloud’s versions app makes certain never to use more than 50% of the user’s currently available free space. If stored versions exceed this limit, ownCloud will delete the oldest versions first until it meets this limit. More information is available in the Versions documentation.
|
||||
|
||||
</description>
|
||||
<version>1.1.0</version>
|
||||
<types>
|
||||
<filesystem/>
|
||||
</types>
|
||||
<dependencies>
|
||||
<owncloud min-version="8.2" />
|
||||
</dependencies>
|
||||
<documentation>
|
||||
<user>user-versions</user>
|
||||
</documentation>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
1.0.6
|
||||
@@ -347,7 +347,20 @@ class Storage {
|
||||
$view->lockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
|
||||
$view->lockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
|
||||
|
||||
$result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
|
||||
// TODO add a proper way of overwriting a file while maintaining file ids
|
||||
if ($storage1->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage') || $storage2->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage')) {
|
||||
$source = $storage1->fopen($internalPath1, 'r');
|
||||
$target = $storage2->fopen($internalPath2, 'w');
|
||||
list(, $result) = \OC_Helper::streamCopy($source, $target);
|
||||
fclose($source);
|
||||
fclose($target);
|
||||
|
||||
if ($result !== false) {
|
||||
$storage1->unlink($internalPath1);
|
||||
}
|
||||
} else {
|
||||
$result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
|
||||
}
|
||||
|
||||
$view->unlockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
|
||||
$view->unlockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
|
||||
@@ -663,17 +676,21 @@ class Storage {
|
||||
|
||||
// calculate available space for version history
|
||||
// subtract size of files and current versions size from quota
|
||||
if ($softQuota) {
|
||||
$files_view = new \OC\Files\View('/'.$uid.'/files');
|
||||
$rootInfo = $files_view->getFileInfo('/', false);
|
||||
$free = $quota-$rootInfo['size']; // remaining free space for user
|
||||
if ( $free > 0 ) {
|
||||
$availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - ($versionsSize + $offset); // how much space can be used for versions
|
||||
if ($quota >= 0) {
|
||||
if ($softQuota) {
|
||||
$files_view = new \OC\Files\View('/' . $uid . '/files');
|
||||
$rootInfo = $files_view->getFileInfo('/', false);
|
||||
$free = $quota - $rootInfo['size']; // remaining free space for user
|
||||
if ($free > 0) {
|
||||
$availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - ($versionsSize + $offset); // how much space can be used for versions
|
||||
} else {
|
||||
$availableSpace = $free - $versionsSize - $offset;
|
||||
}
|
||||
} else {
|
||||
$availableSpace = $free - $versionsSize - $offset;
|
||||
$availableSpace = $quota - $offset;
|
||||
}
|
||||
} else {
|
||||
$availableSpace = $quota - $offset;
|
||||
$availableSpace = PHP_INT_MAX;
|
||||
}
|
||||
|
||||
$allVersions = Storage::getVersions($uid, $filename);
|
||||
|
||||
@@ -301,11 +301,10 @@ class Test_Files_Versioning extends \Test\TestCase {
|
||||
// execute rename hook of versions app
|
||||
\OC\Files\Filesystem::rename('/folder1/test.txt', '/folder1/folder2/test.txt');
|
||||
|
||||
|
||||
self::loginHelper(self::TEST_VERSIONS_USER2);
|
||||
|
||||
$this->runCommands();
|
||||
|
||||
self::loginHelper(self::TEST_VERSIONS_USER);
|
||||
|
||||
$this->assertFalse($this->rootView->file_exists($v1));
|
||||
$this->assertFalse($this->rootView->file_exists($v2));
|
||||
|
||||
|
||||
@@ -13,14 +13,17 @@
|
||||
</description>
|
||||
<licence>AGPL</licence>
|
||||
<author>Tom Needham</author>
|
||||
<requiremin>8</requiremin>
|
||||
<shipped>true</shipped>
|
||||
<default_enable/>
|
||||
<documentation>
|
||||
<admin>admin-provisioning-api</admin>
|
||||
</documentation>
|
||||
<version>0.3.0</version>
|
||||
<types>
|
||||
<!-- this is used to disable the feature of enabling an app for specific groups only because this would break this app -->
|
||||
<filesystem/>
|
||||
</types>
|
||||
<dependencies>
|
||||
<owncloud min-version="8.2" />
|
||||
</dependencies>
|
||||
</info>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
0.2
|
||||
@@ -9,8 +9,8 @@ A user logs into ownCloud with their LDAP or AD credentials, and is granted acce
|
||||
</description>
|
||||
<licence>AGPL</licence>
|
||||
<author>Dominik Schmidt and Arthur Schiwon</author>
|
||||
<requiremin>4.93</requiremin>
|
||||
<shipped>true</shipped>
|
||||
<version>0.7.0</version>
|
||||
<types>
|
||||
<authentication/>
|
||||
</types>
|
||||
@@ -19,5 +19,6 @@ A user logs into ownCloud with their LDAP or AD credentials, and is granted acce
|
||||
</documentation>
|
||||
<dependencies>
|
||||
<lib>ldap</lib>
|
||||
<owncloud min-version="8.2" />
|
||||
</dependencies>
|
||||
</info>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
0.6.3
|
||||
@@ -33,7 +33,6 @@ namespace OCA\user_ldap;
|
||||
|
||||
use OCA\user_ldap\lib\Access;
|
||||
use OCA\user_ldap\lib\BackendUtility;
|
||||
use OCA\user_ldap\lib\user\User;
|
||||
|
||||
class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
|
||||
protected $enabled = false;
|
||||
@@ -198,11 +197,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
|
||||
return array();
|
||||
}
|
||||
$seen[$DN] = 1;
|
||||
$user = $this->access->userManager->get($DN);
|
||||
if(!$user instanceof User) {
|
||||
return array();
|
||||
}
|
||||
$groups = $user->getMemberOfGroups();
|
||||
$groups = $this->access->readAttribute($DN, 'memberOf');
|
||||
if (!is_array($groups)) {
|
||||
return array();
|
||||
}
|
||||
@@ -214,7 +209,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
|
||||
$subGroups = $this->_getGroupDNsFromMemberOf($group, $seen);
|
||||
$allGroups = array_merge($allGroups, $subGroups);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $allGroups;
|
||||
}
|
||||
|
||||
|
||||
@@ -352,6 +352,7 @@ OCA = OCA || {};
|
||||
$('.ldap_submit').button();
|
||||
$('.ldap_action_test_connection').button();
|
||||
$('#ldapSettings').tabs({ beforeActivate: this.onTabChange });
|
||||
$('#ldapSettings :input').tooltip({placement: "right", container: "body", trigger: "hover"});
|
||||
|
||||
this.initControls();
|
||||
this.disableTabs();
|
||||
|
||||
@@ -671,6 +671,20 @@ class Access extends LDAPUtility implements user\IUserTools {
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* counts the number of users according to a provided loginName and
|
||||
* utilizing the login filter.
|
||||
*
|
||||
* @param string $loginName
|
||||
* @return array
|
||||
*/
|
||||
public function countUsersByLoginName($loginName) {
|
||||
$loginName = $this->escapeFilterPart($loginName);
|
||||
$filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
|
||||
$users = $this->countUsers($filter);
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filter
|
||||
* @param string|string[] $attr
|
||||
|
||||
@@ -150,8 +150,9 @@ class Configuration {
|
||||
$setMethod = 'setRawValue';
|
||||
break;
|
||||
case 'homeFolderNamingRule':
|
||||
if(!empty(trim($val)) && strpos($val, 'attr:') === false) {
|
||||
$val = 'attr:'.trim($val);
|
||||
$trimmedVal = trim($val);
|
||||
if(!empty($trimmedVal) && strpos($val, 'attr:') === false) {
|
||||
$val = 'attr:'.$trimmedVal;
|
||||
}
|
||||
break;
|
||||
case 'ldapBase':
|
||||
|
||||
@@ -631,12 +631,12 @@ class Wizard extends LDAPUtility {
|
||||
throw new \Exception('missing placeholder');
|
||||
}
|
||||
|
||||
$users = $this->access->fetchUsersByLoginName($loginName);
|
||||
$users = $this->access->countUsersByLoginName($loginName);
|
||||
if($this->ldap->errno($cr) !== 0) {
|
||||
throw new \Exception($this->ldap->error($cr));
|
||||
}
|
||||
$filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter);
|
||||
$this->result->addChange('ldap_test_loginname', count($users));
|
||||
$this->result->addChange('ldap_test_loginname', $users);
|
||||
$this->result->addChange('ldap_test_effective_filter', $filter);
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
@@ -42,14 +42,9 @@ class Test_Group_Ldap extends \Test\TestCase {
|
||||
$connector = $this->getMock('\OCA\user_ldap\lib\Connection',
|
||||
$conMethods,
|
||||
array($lw, null, null));
|
||||
$um = new \OCA\user_ldap\lib\user\Manager(
|
||||
$this->getMock('\OCP\IConfig'),
|
||||
$this->getMock('\OCA\user_ldap\lib\FilesystemHelper'),
|
||||
$this->getMock('\OCA\user_ldap\lib\LogWrapper'),
|
||||
$this->getMock('\OCP\IAvatarManager'),
|
||||
$this->getMock('\OCP\Image'),
|
||||
$this->getMock('\OCP\IDBConnection')
|
||||
);
|
||||
$um = $this->getMockBuilder('\OCA\user_ldap\lib\user\Manager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$access = $this->getMock('\OCA\user_ldap\lib\Access',
|
||||
$accMethods,
|
||||
array($connector, $lw, $um));
|
||||
|
||||
@@ -29,5 +29,8 @@ namespace OCA\user_ldap\tests\integration;
|
||||
* scope of these tests, we replace it with a mock.
|
||||
*/
|
||||
class FakeManager extends \OCA\user_ldap\lib\user\Manager {
|
||||
public function __construct() {}
|
||||
public function __construct() {
|
||||
$this->ocConfig = \OC::$server->getConfig();
|
||||
$this->image = new \OCP\Image();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Arthur Schiwon <blizzz@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2015, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\user_ldap\tests\integration\lib;
|
||||
|
||||
use OCA\user_ldap\lib\user\Manager as LDAPUserManager;
|
||||
use OCA\user_ldap\tests\integration\AbstractIntegrationTest;
|
||||
use OCA\User_LDAP\Mapping\UserMapping;
|
||||
use OCA\user_ldap\USER_LDAP;
|
||||
|
||||
require_once __DIR__ . '/../../../../../lib/base.php';
|
||||
|
||||
class IntegrationTestUserHome extends AbstractIntegrationTest {
|
||||
|
||||
/**
|
||||
* prepares the LDAP environment and sets up a test configuration for
|
||||
* the LDAP backend.
|
||||
*/
|
||||
public function init() {
|
||||
require(__DIR__ . '/../setup-scripts/createExplicitUsers.php');
|
||||
parent::init();
|
||||
}
|
||||
|
||||
/**
|
||||
* tests countUsersByLoginName where it is expected that the login name does
|
||||
* not match any LDAP user
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function case1() {
|
||||
$result = $this->access->countUsersByLoginName('nothere');
|
||||
return $result === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tests countUsersByLoginName where it is expected that the login name does
|
||||
* match one LDAP user
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function case2() {
|
||||
$result = $this->access->countUsersByLoginName('alice');
|
||||
return $result === 1;
|
||||
}
|
||||
}
|
||||
|
||||
require_once(__DIR__ . '/../setup-scripts/config.php');
|
||||
$test = new IntegrationTestUserHome($host, $port, $adn, $apwd, $bdn);
|
||||
$test->init();
|
||||
$test->run();
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Arthur Schiwon <blizzz@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2015, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\user_ldap\tests\integration\lib;
|
||||
|
||||
use OCA\user_ldap\lib\user\Manager as LDAPUserManager;
|
||||
use OCA\user_ldap\tests\integration\AbstractIntegrationTest;
|
||||
use OCA\User_LDAP\Mapping\UserMapping;
|
||||
use OCA\user_ldap\USER_LDAP;
|
||||
|
||||
require_once __DIR__ . '/../../../../../lib/base.php';
|
||||
|
||||
class IntegrationTestUserHome extends AbstractIntegrationTest {
|
||||
/** @var UserMapping */
|
||||
protected $mapping;
|
||||
|
||||
/** @var USER_LDAP */
|
||||
protected $backend;
|
||||
|
||||
/**
|
||||
* prepares the LDAP environment and sets up a test configuration for
|
||||
* the LDAP backend.
|
||||
*/
|
||||
public function init() {
|
||||
require(__DIR__ . '/../setup-scripts/createExplicitUsers.php');
|
||||
parent::init();
|
||||
|
||||
$this->mapping = new UserMapping(\OC::$server->getDatabaseConnection());
|
||||
$this->mapping->clear();
|
||||
$this->access->setUserMapper($this->mapping);
|
||||
$this->backend = new \OCA\user_ldap\USER_LDAP($this->access, \OC::$server->getConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* tests fetchUserByLoginName where it is expected that the login name does
|
||||
* not match any LDAP user
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function case1() {
|
||||
$result = $this->access->fetchUsersByLoginName('nothere');
|
||||
return $result === [];
|
||||
}
|
||||
|
||||
/**
|
||||
* tests fetchUserByLoginName where it is expected that the login name does
|
||||
* match one LDAP user
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function case2() {
|
||||
$result = $this->access->fetchUsersByLoginName('alice');
|
||||
return count($result) === 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
require_once(__DIR__ . '/../setup-scripts/config.php');
|
||||
$test = new IntegrationTestUserHome($host, $port, $adn, $apwd, $bdn);
|
||||
$test->init();
|
||||
$test->run();
|
||||
@@ -270,6 +270,7 @@ function execute_tests {
|
||||
fi
|
||||
|
||||
if [ "$PRIMARY_STORAGE_CONFIG" == "swift" ] ; then
|
||||
cd ..
|
||||
echo "Kill the swift docker"
|
||||
tests/objectstore/stop-swift-ceph.sh
|
||||
fi
|
||||
|
||||
@@ -513,6 +513,14 @@ $CONFIG = array(
|
||||
*/
|
||||
'loglevel' => 2,
|
||||
|
||||
/**
|
||||
* If you maintain different instances and aggregate the logs, you may want
|
||||
* to distinguish between them. ``syslog_tag`` can be set per instance
|
||||
* with a unique id. Only available if ``log_type`` is set to ``syslog``.
|
||||
* The default value is ``ownCloud``.
|
||||
*/
|
||||
'syslog_tag' => 'ownCloud',
|
||||
|
||||
/**
|
||||
* Log condition for log level increase based on conditions. Once one of these
|
||||
* conditions is met, the required log level is set to debug. This allows to
|
||||
@@ -1144,15 +1152,6 @@ $CONFIG = array(
|
||||
*/
|
||||
'debug' => false,
|
||||
|
||||
/**
|
||||
* Skips the migration test during upgrades
|
||||
*
|
||||
* If this is set to true the migration test are deactivated during upgrade.
|
||||
* This is only recommended in installations where upgrade tests are run in
|
||||
* advance with the same data on a test system.
|
||||
*/
|
||||
'update.skip-migration-test' => false,
|
||||
|
||||
/**
|
||||
* This entry is just here to show a warning in case somebody copied the sample
|
||||
* configuration. DO NOT ADD THIS SWITCH TO YOUR CONFIGURATION!
|
||||
|
||||
+23
-3
@@ -48,9 +48,28 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
|
||||
$shareType = (int)$_POST['shareType'];
|
||||
$shareWith = $_POST['shareWith'];
|
||||
$itemSourceName = isset($_POST['itemSourceName']) ? (string)$_POST['itemSourceName'] : null;
|
||||
if ($shareType === OCP\Share::SHARE_TYPE_LINK && $shareWith == '') {
|
||||
$shareWith = null;
|
||||
|
||||
/*
|
||||
* Nasty nasty fix for https://github.com/owncloud/core/issues/19950
|
||||
*/
|
||||
$passwordChanged = null;
|
||||
if (is_array($shareWith)) {
|
||||
$passwordChanged = ($shareWith['passwordChanged'] === 'true');
|
||||
if ($shareType === OCP\Share::SHARE_TYPE_LINK && $shareWith['password'] === '') {
|
||||
$shareWith = null;
|
||||
} else {
|
||||
$shareWith = $shareWith['password'];
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* We need this branch since the calendar and contacts also use this
|
||||
* endpoint
|
||||
*/
|
||||
if ($shareType === OCP\Share::SHARE_TYPE_LINK && $shareWith === '') {
|
||||
$shareWith = null;
|
||||
}
|
||||
}
|
||||
|
||||
$itemSourceName=(isset($_POST['itemSourceName'])) ? (string)$_POST['itemSourceName']:'';
|
||||
|
||||
$token = OCP\Share::shareItem(
|
||||
@@ -60,7 +79,8 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
|
||||
$shareWith,
|
||||
$_POST['permissions'],
|
||||
$itemSourceName,
|
||||
(!empty($_POST['expirationDate']) ? new \DateTime((string)$_POST['expirationDate']) : null)
|
||||
(!empty($_POST['expirationDate']) ? new \DateTime((string)$_POST['expirationDate']) : null),
|
||||
$passwordChanged
|
||||
);
|
||||
|
||||
if (is_string($token)) {
|
||||
|
||||
+13
-10
@@ -41,21 +41,12 @@ if (OC::checkUpgrade(false)) {
|
||||
// avoid side effects
|
||||
\OC_User::setIncognitoMode(true);
|
||||
|
||||
|
||||
|
||||
$logger = \OC::$server->getLogger();
|
||||
$config = \OC::$server->getConfig();
|
||||
$updater = new \OC\Updater(
|
||||
\OC::$server->getHTTPHelper(),
|
||||
$config,
|
||||
\OC::$server->getConfig(),
|
||||
$logger
|
||||
);
|
||||
|
||||
if ($config->getSystemValue('update.skip-migration-test', false)) {
|
||||
$eventSource->send('success', (string)$l->t('Migration tests are skipped - "update.skip-migration-test" is activated in config.php'));
|
||||
$updater->setSimulateStepEnabled(false);
|
||||
}
|
||||
|
||||
$incompatibleApps = [];
|
||||
$disabledThirdPartyApps = [];
|
||||
|
||||
@@ -68,12 +59,24 @@ if (OC::checkUpgrade(false)) {
|
||||
$updater->listen('\OC\Updater', 'maintenanceActive', function () use ($eventSource, $l) {
|
||||
$eventSource->send('success', (string)$l->t('Maintenance mode is kept active'));
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'dbUpgradeBefore', function () use($eventSource, $l) {
|
||||
$eventSource->send('success', (string)$l->t('Updating database schema'));
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'dbUpgrade', function () use ($eventSource, $l) {
|
||||
$eventSource->send('success', (string)$l->t('Updated database'));
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($eventSource, $l) {
|
||||
$eventSource->send('success', (string)$l->t('Checking whether the database schema can be updated (this can take a long time depending on the database size)'));
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'dbSimulateUpgrade', function () use ($eventSource, $l) {
|
||||
$eventSource->send('success', (string)$l->t('Checked database schema update'));
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($eventSource, $l) {
|
||||
$eventSource->send('success', (string)$l->t('Checking updates of apps'));
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($eventSource, $l) {
|
||||
$eventSource->send('success', (string)$l->t('Checking whether the database schema for %s can be updated (this can take a long time depending on the database size)', [$app]));
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'appUpgradeCheck', function () use ($eventSource, $l) {
|
||||
$eventSource->send('success', (string)$l->t('Checked database schema update for apps'));
|
||||
});
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace OC\Core\Command\Maintenance;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class Repair extends Command {
|
||||
@@ -49,10 +50,24 @@ class Repair extends Command {
|
||||
protected function configure() {
|
||||
$this
|
||||
->setName('maintenance:repair')
|
||||
->setDescription('repair this installation');
|
||||
->setDescription('repair this installation')
|
||||
->addOption(
|
||||
'include-expensive',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'Use this option when you want to include resource and load expensive tasks'
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$includeExpensive = $input->getOption('include-expensive');
|
||||
if ($includeExpensive) {
|
||||
foreach ($this->repair->getExpensiveRepairSteps() as $step) {
|
||||
$this->repair->addStep($step);
|
||||
}
|
||||
}
|
||||
|
||||
$maintenanceMode = $this->config->getSystemValue('maintenance', false);
|
||||
$this->config->setSystemValue('maintenance', true);
|
||||
|
||||
|
||||
@@ -96,12 +96,6 @@ class Upgrade extends Command {
|
||||
$updateStepEnabled = true;
|
||||
$skip3rdPartyAppsDisable = false;
|
||||
|
||||
if ($this->config->getSystemValue('update.skip-migration-test', false)) {
|
||||
$output->writeln(
|
||||
'<info>"skip-migration-test" is activated via config.php</info>'
|
||||
);
|
||||
$simulateStepEnabled = false;
|
||||
}
|
||||
if ($input->getOption('skip-migration-test')) {
|
||||
$simulateStepEnabled = false;
|
||||
}
|
||||
@@ -155,9 +149,15 @@ class Upgrade extends Command {
|
||||
}
|
||||
$output->writeln($message);
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'dbUpgradeBefore', function () use($output) {
|
||||
$output->writeln('<info>Updating database schema</info>');
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'dbUpgrade', function () use($output) {
|
||||
$output->writeln('<info>Updated database</info>');
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($output) {
|
||||
$output->writeln('<info>Checking whether the database schema can be updated (this can take a long time depending on the database size)</info>');
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'dbSimulateUpgrade', function () use($output) {
|
||||
$output->writeln('<info>Checked database schema update</info>');
|
||||
});
|
||||
@@ -176,6 +176,12 @@ class Upgrade extends Command {
|
||||
$updater->listen('\OC\Updater', 'repairError', function ($app) use($output) {
|
||||
$output->writeln('<error>Repair error: ' . $app . '</error>');
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($output) {
|
||||
$output->writeln('<info>Checking updates of apps</info>');
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($output) {
|
||||
$output->writeln("<info>Checking whether the database schema for <$app> can be updated (this can take a long time depending on the database size)</info>");
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'appUpgradeCheck', function () use ($output) {
|
||||
$output->writeln('<info>Checked database schema update for apps</info>');
|
||||
});
|
||||
|
||||
+7
-2
@@ -35,7 +35,6 @@
|
||||
height: 100%;
|
||||
width: inherit;
|
||||
overflow: auto;
|
||||
padding-bottom: 44px;
|
||||
-moz-box-sizing: border-box; box-sizing: border-box;
|
||||
}
|
||||
#app-navigation li {
|
||||
@@ -308,8 +307,13 @@
|
||||
-o-filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75));
|
||||
filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75));
|
||||
}
|
||||
.ie8 .bubble {
|
||||
.ie .bubble,
|
||||
.ie #app-navigation .app-navigation-entry-menu,
|
||||
.ie .bubble:after,
|
||||
.ie #app-navigation .app-navigation-entry-menu:after {
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
.ie8 .bubble {
|
||||
margin-top: 18px;
|
||||
}
|
||||
.ie8 .bubble:after {
|
||||
@@ -691,6 +695,7 @@ em {
|
||||
.popovermenu .menuitem .no-icon {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,12 @@ select {
|
||||
background-image: url('../img/actions/settings.png');
|
||||
}
|
||||
|
||||
.lte9 input[type="submit"], .lte9 input[type="button"],
|
||||
.lte9 button, .lte9 .button,
|
||||
.lte9 #quota, .lte9 select, .lte9 .pager li a {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
/* IE8 needs PNG image for header logo */
|
||||
.ie8 #header .logo {
|
||||
background-image: url(../img/logo-icon-175px.png);
|
||||
|
||||
@@ -585,30 +585,6 @@ input[type="submit"].enabled {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
/* Icons for username and password fields to better recognize them */
|
||||
#adminlogin,
|
||||
#adminpass,
|
||||
input[name='adminpass-clone'],
|
||||
#user,
|
||||
#password,
|
||||
input[name='password-clone'] {
|
||||
width: 223px !important;
|
||||
padding-left: 36px !important;
|
||||
}
|
||||
#adminlogin+label+img,
|
||||
#adminpass-icon,
|
||||
#user+label+img,
|
||||
#password-icon {
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 22px;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)";
|
||||
opacity: .3;
|
||||
}
|
||||
#adminpass-icon, #password-icon {
|
||||
top: 17px;
|
||||
}
|
||||
|
||||
/* General new input field look */
|
||||
#body-login input[type="text"],
|
||||
#body-login input[type="password"],
|
||||
@@ -1204,4 +1180,3 @@ fieldset.warning legend + p, fieldset.update legend + p {
|
||||
opacity: 0;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
|
||||
}
|
||||
|
||||
|
||||
+11
-1
@@ -1632,6 +1632,15 @@ OC.Util = {
|
||||
return $el;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether this is IE
|
||||
*
|
||||
* @return {bool} true if this is IE, false otherwise
|
||||
*/
|
||||
isIE: function() {
|
||||
return $('html').hasClass('ie');
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether this is IE8
|
||||
*
|
||||
@@ -1989,7 +1998,8 @@ jQuery.fn.tipsy = function(argument) {
|
||||
placement: 'bottom',
|
||||
delay: { 'show': 0, 'hide': 0},
|
||||
trigger: 'hover',
|
||||
html: false
|
||||
html: false,
|
||||
container: 'body'
|
||||
};
|
||||
if(argument.gravity) {
|
||||
switch(argument.gravity) {
|
||||
|
||||
@@ -46,6 +46,8 @@ OC.MimeType = {
|
||||
return 'folder';
|
||||
} else if (mimeType === 'dir-shared' && $.inArray('folder-shared', files) !== -1) {
|
||||
return 'folder-shared';
|
||||
} else if (mimeType === 'dir-public' && $.inArray('folder-public', files) !== -1) {
|
||||
return 'folder-public';
|
||||
} else if (mimeType === 'dir-external' && $.inArray('folder-external', files) !== -1) {
|
||||
return 'folder-external';
|
||||
} else if ($.inArray(icon, files) !== -1) {
|
||||
|
||||
+14
-3
@@ -253,14 +253,25 @@ OC.Share = _.extend(OC.Share || {}, {
|
||||
// update folder icon
|
||||
if (type === 'dir' && (hasShares || hasLink || owner)) {
|
||||
if (hasLink) {
|
||||
shareFolderIcon = OC.imagePath('core', 'filetypes/folder-public');
|
||||
shareFolderIcon = OC.MimeType.getIconUrl('dir-public');
|
||||
}
|
||||
else {
|
||||
shareFolderIcon = OC.imagePath('core', 'filetypes/folder-shared');
|
||||
shareFolderIcon = OC.MimeType.getIconUrl('dir-shared');
|
||||
}
|
||||
$tr.find('.filename .thumbnail').css('background-image', 'url(' + shareFolderIcon + ')');
|
||||
$tr.attr('data-icon', shareFolderIcon);
|
||||
} else if (type === 'dir') {
|
||||
shareFolderIcon = OC.imagePath('core', 'filetypes/folder');
|
||||
var mountType = $tr.attr('data-mounttype');
|
||||
// FIXME: duplicate of FileList._createRow logic for external folder,
|
||||
// need to refactor the icon logic into a single code path eventually
|
||||
if (mountType && mountType.indexOf('external') === 0) {
|
||||
shareFolderIcon = OC.MimeType.getIconUrl('dir-external');
|
||||
$tr.attr('data-icon', shareFolderIcon);
|
||||
} else {
|
||||
shareFolderIcon = OC.MimeType.getIconUrl('dir');
|
||||
// back to default
|
||||
$tr.removeAttr('data-icon');
|
||||
}
|
||||
$tr.find('.filename .thumbnail').css('background-image', 'url(' + shareFolderIcon + ')');
|
||||
}
|
||||
// update share action text / icon
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/* global moment */
|
||||
|
||||
(function() {
|
||||
if (!OC.Share) {
|
||||
OC.Share = {};
|
||||
@@ -19,9 +21,9 @@
|
||||
// in the LinkShareView to ease reusing it in future. Then,
|
||||
// modifications (getting rid of IDs) are still necessary.
|
||||
'{{#if isLinkShare}}' +
|
||||
'<input type="checkbox" name="expirationCheckbox" class="expirationCheckbox checkbox" id="expirationCheckbox" value="1" ' +
|
||||
'<input type="checkbox" name="expirationCheckbox" class="expirationCheckbox checkbox" id="expirationCheckbox-{{cid}}" value="1" ' +
|
||||
'{{#if isExpirationSet}}checked="checked"{{/if}} {{#if disableCheckbox}}disabled="disabled"{{/if}} />' +
|
||||
'<label for="expirationCheckbox">{{setExpirationLabel}}</label>' +
|
||||
'<label for="expirationCheckbox-{{cid}}">{{setExpirationLabel}}</label>' +
|
||||
'<div class="expirationDateContainer {{#unless isExpirationSet}}hidden{{/unless}}">' +
|
||||
' <label for="expirationDate" class="hidden-visually" value="{{expirationDate}}">{{expirationLabel}}</label>' +
|
||||
' <input id="expirationDate" class="datepicker" type="text" placeholder="{{expirationDatePlaceholder}}" value="{{expirationValue}}" />' +
|
||||
@@ -134,11 +136,11 @@
|
||||
|
||||
var expiration;
|
||||
if (isExpirationSet) {
|
||||
expiration = moment(this.model.get('linkShare').expiration, 'YYYY-MM-DD').format('DD-MM-YYYY')
|
||||
expiration = moment(this.model.get('linkShare').expiration, 'YYYY-MM-DD').format('DD-MM-YYYY');
|
||||
}
|
||||
|
||||
var expirationTemplate = this.template();
|
||||
this.$el.html(expirationTemplate({
|
||||
this.$el.html(this.template({
|
||||
cid: this.cid,
|
||||
setExpirationLabel: t('core', 'Set expiration date'),
|
||||
expirationLabel: t('core', 'Expiration'),
|
||||
expirationDatePlaceholder: t('core', 'Expiration date'),
|
||||
@@ -186,11 +188,11 @@
|
||||
* @returns {Function} from Handlebars
|
||||
* @private
|
||||
*/
|
||||
template: function () {
|
||||
template: function (data) {
|
||||
if (!this._template) {
|
||||
this._template = Handlebars.compile(TEMPLATE);
|
||||
}
|
||||
return this._template;
|
||||
return this._template(data);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -13,36 +13,42 @@
|
||||
OC.Share = {};
|
||||
}
|
||||
|
||||
var PASSWORD_PLACEHOLDER = '**********';
|
||||
var PASSWORD_PLACEHOLDER_MESSAGE = t('core', 'Choose a password for the public link');
|
||||
|
||||
var TEMPLATE =
|
||||
'{{#if shareAllowed}}' +
|
||||
'<span class="icon-loading-small hidden"></span>' +
|
||||
'<input type="checkbox" name="linkCheckbox" id="linkCheckbox" class="checkbox" value="1" {{#if isLinkShare}}checked="checked"{{/if}} /><label for="linkCheckbox">{{linkShareLabel}}</label>' +
|
||||
'<input type="checkbox" name="linkCheckbox" id="linkCheckbox-{{cid}}" class="checkbox linkCheckbox" value="1" {{#if isLinkShare}}checked="checked"{{/if}} />' +
|
||||
'<label for="linkCheckbox-{{cid}}">{{linkShareLabel}}</label>' +
|
||||
'<br />' +
|
||||
'<label for="linkText" class="hidden-visually">{{urlLabel}}</label>' +
|
||||
'<input id="linkText" {{#unless isLinkShare}}class="hidden"{{/unless}} type="text" readonly="readonly" value="{{shareLinkURL}}" />' +
|
||||
'<label for="linkText-{{cid}}" class="hidden-visually">{{urlLabel}}</label>' +
|
||||
'<input id="linkText-{{cid}}" class="linkText {{#unless isLinkShare}}hidden{{/unless}}" type="text" readonly="readonly" value="{{shareLinkURL}}" />' +
|
||||
' {{#if showPasswordCheckBox}}' +
|
||||
'<input type="checkbox" name="showPassword" id="showPassword" class="checkbox" {{#if isPasswordSet}}checked="checked"{{/if}} value="1" /><label for="showPassword">{{enablePasswordLabel}}</label>' +
|
||||
'<input type="checkbox" name="showPassword" id="showPassword-{{cid}}" class="checkbox showPasswordCheckbox" {{#if isPasswordSet}}checked="checked"{{/if}} value="1" />' +
|
||||
'<label for="showPassword-{{cid}}">{{enablePasswordLabel}}</label>' +
|
||||
' {{/if}}' +
|
||||
'<div id="linkPass" {{#unless isPasswordSet}}class="hidden"{{/unless}}>' +
|
||||
' <label for="linkPassText" class="hidden-visually">{{passwordLabel}}</label>' +
|
||||
' <input id="linkPassText" type="password" placeholder="{{passwordPlaceholder}}" />' +
|
||||
'<div id="linkPass" class="linkPass {{#unless isPasswordSet}}hidden{{/unless}}">' +
|
||||
' <label for="linkPassText-{{cid}}" class="hidden-visually">{{passwordLabel}}</label>' +
|
||||
' <input id="linkPassText-{{cid}}" class="linkPassText" type="password" placeholder="{{passwordPlaceholder}}" />' +
|
||||
' <span class="icon-loading-small hidden"></span>' +
|
||||
'</div>' +
|
||||
' {{#if publicUpload}}' +
|
||||
'<div id="allowPublicUploadWrapper">' +
|
||||
' <span class="icon-loading-small hidden"></span>' +
|
||||
' <input type="checkbox" value="1" name="allowPublicUpload" id="sharingDialogAllowPublicUpload" class="checkbox" {{{publicUploadChecked}}} />' +
|
||||
'<label for="sharingDialogAllowPublicUpload">{{publicUploadLabel}}</label>' +
|
||||
' <input type="checkbox" value="1" name="allowPublicUpload" id="sharingDialogAllowPublicUpload-{{cid}}" class="checkbox publicUploadCheckbox" {{{publicUploadChecked}}} />' +
|
||||
'<label for="sharingDialogAllowPublicUpload-{{cid}}">{{publicUploadLabel}}</label>' +
|
||||
'</div>' +
|
||||
' {{/if}}' +
|
||||
' {{#if mailPublicNotificationEnabled}}' +
|
||||
'<form id="emailPrivateLink" class="emailPrivateLinkForm">' +
|
||||
' <input id="email" value="" placeholder="{{mailPrivatePlaceholder}}" type="text" />' +
|
||||
' <input id="emailButton" type="submit" value="{{mailButtonText}}" />' +
|
||||
' <input id="email" class="emailField" value="" placeholder="{{mailPrivatePlaceholder}}" type="text" />' +
|
||||
' <input id="emailButton" class="emailButton" type="submit" value="{{mailButtonText}}" />' +
|
||||
'</form>' +
|
||||
' {{/if}}' +
|
||||
'{{else}}' +
|
||||
'<input id="shareWith" type="text" placeholder="{{noSharingPlaceholder}}" disabled="disabled"/>' +
|
||||
// FIXME: this doesn't belong in this view
|
||||
'<input id="shareWith-{{cid}}" class="shareWithField" type="text" placeholder="{{noSharingPlaceholder}}" disabled="disabled"/>' +
|
||||
'{{/if}}'
|
||||
;
|
||||
|
||||
@@ -70,7 +76,13 @@
|
||||
showLink: true,
|
||||
|
||||
events: {
|
||||
'submit .emailPrivateLinkForm': '_onEmailPrivateLink'
|
||||
'submit .emailPrivateLinkForm': '_onEmailPrivateLink',
|
||||
'focusout input.linkPassText': 'onPasswordEntered',
|
||||
'keyup input.linkPassText': 'onPasswordKeyUp',
|
||||
'click .linkCheckbox': 'onLinkCheckBoxChange',
|
||||
'click .linkText': 'onLinkTextClick',
|
||||
'change .publicUploadCheckbox': 'onAllowPublicUploadChange',
|
||||
'click .showPasswordCheckbox': 'onShowPasswordClick'
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
@@ -98,12 +110,20 @@
|
||||
throw 'missing OC.Share.ShareConfigModel';
|
||||
}
|
||||
|
||||
_.bindAll(this, 'onLinkCheckBoxChange', 'onPasswordEntered',
|
||||
'onShowPasswordClick', 'onAllowPublicUploadChange');
|
||||
_.bindAll(
|
||||
this,
|
||||
'_onEmailPrivateLink',
|
||||
'onLinkCheckBoxChange',
|
||||
'onPasswordEntered',
|
||||
'onPasswordKeyUp',
|
||||
'onLinkTextClick',
|
||||
'onShowPasswordClick',
|
||||
'onAllowPublicUploadChange'
|
||||
);
|
||||
},
|
||||
|
||||
onLinkCheckBoxChange: function() {
|
||||
var $checkBox = this.$el.find('#linkCheckbox');
|
||||
var $checkBox = this.$el.find('.linkCheckbox');
|
||||
var $loading = $checkBox.siblings('.icon-loading-small');
|
||||
if(!$loading.hasClass('hidden')) {
|
||||
return false;
|
||||
@@ -115,49 +135,53 @@
|
||||
// this will create it
|
||||
this.model.saveLinkShare();
|
||||
} else {
|
||||
this.$el.find('#linkPass').slideToggle(OC.menuSpeed);
|
||||
// TODO drop with IE8 drop
|
||||
if($('html').hasClass('ie8')) {
|
||||
this.$el.find('#linkPassText').attr('placeholder', null);
|
||||
this.$el.find('#linkPassText').val('');
|
||||
}
|
||||
this.$el.find('#linkPassText').focus();
|
||||
this.$el.find('.linkPass').slideToggle(OC.menuSpeed);
|
||||
this.$el.find('.linkPassText').focus();
|
||||
}
|
||||
} else {
|
||||
$loading.removeClass('hidden');
|
||||
if (this.model.get('linkShare').isLinkShare) {
|
||||
this.model.removeLinkShare();
|
||||
} else {
|
||||
this.$el.find('#linkPass').slideToggle(OC.menuSpeed);
|
||||
this.$el.find('.linkPass').slideToggle(OC.menuSpeed);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onLinkTextClick: function() {
|
||||
this.focus();
|
||||
this.select();
|
||||
var $el = this.$el.find('.linkText');
|
||||
$el.focus();
|
||||
$el.select();
|
||||
},
|
||||
|
||||
onShowPasswordClick: function() {
|
||||
this.$el.find('#linkPass').slideToggle(OC.menuSpeed);
|
||||
if(!this.$el.find('#showPassword').is(':checked')) {
|
||||
this.$el.find('.linkPass').slideToggle(OC.menuSpeed);
|
||||
if(!this.$el.find('.showPasswordCheckbox').is(':checked')) {
|
||||
this.model.setPassword('');
|
||||
this.model.saveLinkShare();
|
||||
} else {
|
||||
this.$el.find('#linkPassText').focus();
|
||||
this.$el.find('.linkPassText').focus();
|
||||
}
|
||||
},
|
||||
|
||||
onPasswordKeyUp: function(event) {
|
||||
if(event.keyCode == 13) {
|
||||
this.onPasswordEntered();
|
||||
}
|
||||
},
|
||||
|
||||
onPasswordEntered: function() {
|
||||
var self = this;
|
||||
var $loading = this.$el.find('#linkPass .icon-loading-small');
|
||||
var $loading = this.$el.find('.linkPass .icon-loading-small');
|
||||
if (!$loading.hasClass('hidden')) {
|
||||
// still in process
|
||||
return;
|
||||
}
|
||||
var $input = this.$el.find('#linkPassText');
|
||||
var $input = this.$el.find('.linkPassText');
|
||||
$input.removeClass('error');
|
||||
var password = $input.val();
|
||||
if(password === '') {
|
||||
// in IE9 the password might be the placeholder due to bugs in the placeholders polyfill
|
||||
if(password === '' || password === PASSWORD_PLACEHOLDER || password === PASSWORD_PLACEHOLDER_MESSAGE) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -178,29 +202,35 @@
|
||||
},
|
||||
|
||||
onAllowPublicUploadChange: function() {
|
||||
this.$el.find('#sharingDialogAllowPublicUpload')
|
||||
.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock');
|
||||
this.model.setPublicUpload(this.$el.find('#sharingDialogAllowPublicUpload').is(':checked'));
|
||||
var $checkbox = this.$('.publicUploadCheckbox');
|
||||
$checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock');
|
||||
this.model.setPublicUpload($checkbox.is(':checked'));
|
||||
this.model.saveLinkShare();
|
||||
},
|
||||
|
||||
_onEmailPrivateLink: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var $emailField = this.$el.find('#email');
|
||||
var $emailButton = this.$el.find('#emailButton');
|
||||
var email = this.$el.find('#email').val();
|
||||
var $emailField = this.$el.find('.emailField');
|
||||
var $emailButton = this.$el.find('.emailButton');
|
||||
var email = $emailField.val();
|
||||
if (email !== '') {
|
||||
$emailField.prop('disabled', true);
|
||||
$emailButton.prop('disabled', true);
|
||||
$emailField.val(t('core', 'Sending ...'));
|
||||
this.model.sendEmailPrivateLink(email).then(function() {
|
||||
this.model.sendEmailPrivateLink(email).done(function() {
|
||||
$emailField.css('font-weight', 'bold').val(t('core','Email sent'));
|
||||
setTimeout(function() {
|
||||
$emailField.css('font-weight', 'normal').val('');
|
||||
$emailField.val('');
|
||||
$emailField.css('font-weight', 'normal');
|
||||
$emailField.prop('disabled', false);
|
||||
$emailButton.prop('disabled', false);
|
||||
}, 2000);
|
||||
}).fail(function() {
|
||||
$emailField.val(email);
|
||||
$emailField.css('font-weight', 'normal');
|
||||
$emailField.prop('disabled', false);
|
||||
$emailButton.prop('disabled', false);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
@@ -237,6 +267,7 @@
|
||||
|| !this.model.get('linkShare').password);
|
||||
|
||||
this.$el.html(linkShareTemplate({
|
||||
cid: this.cid,
|
||||
shareAllowed: true,
|
||||
isLinkShare: isLinkShare,
|
||||
shareLinkURL: this.model.get('linkShare').link,
|
||||
@@ -244,7 +275,7 @@
|
||||
urlLabel: t('core', 'Link'),
|
||||
enablePasswordLabel: t('core', 'Password protect'),
|
||||
passwordLabel: t('core', 'Password'),
|
||||
passwordPlaceholder: isPasswordSet ? '**********' : t('core', 'Choose a password for the public link'),
|
||||
passwordPlaceholder: isPasswordSet ? PASSWORD_PLACEHOLDER : PASSWORD_PLACEHOLDER_MESSAGE,
|
||||
isPasswordSet: isPasswordSet,
|
||||
showPasswordCheckBox: showPasswordCheckBox,
|
||||
publicUpload: publicUpload && isLinkShare,
|
||||
@@ -255,20 +286,7 @@
|
||||
mailButtonText: t('core', 'Send')
|
||||
}));
|
||||
|
||||
// TODO: move this to delegate events instead
|
||||
this.$el.find('#linkCheckbox').click(this.onLinkCheckBoxChange);
|
||||
this.$el.find('#sharingDialogAllowPublicUpload').change(this.onAllowPublicUploadChange);
|
||||
this.$el.find('#linkText').click(this.onLinkTextClick);
|
||||
this.$el.find('#showPassword').click(this.onShowPasswordClick);
|
||||
this.$el.find('#linkPassText').focusout(this.onPasswordEntered);
|
||||
var view = this;
|
||||
this.$el.find('#linkPassText').keyup(function(event) {
|
||||
if(event.keyCode == 13) {
|
||||
view.onPasswordEntered();
|
||||
}
|
||||
});
|
||||
|
||||
var $emailField = this.$el.find('#email');
|
||||
var $emailField = this.$el.find('.emailField');
|
||||
if (isLinkShare && $emailField.length !== 0) {
|
||||
$emailField.autocomplete({
|
||||
minLength: 1,
|
||||
@@ -295,6 +313,12 @@
|
||||
};
|
||||
}
|
||||
|
||||
// TODO drop with IE8 drop
|
||||
if($('html').hasClass('ie8')) {
|
||||
this.$el.find('#linkPassText').removeAttr('placeholder');
|
||||
this.$el.find('#linkPassText').val('');
|
||||
}
|
||||
|
||||
this.delegateEvents();
|
||||
|
||||
return this;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
}
|
||||
|
||||
var TEMPLATE =
|
||||
'<ul id="shareWithList">' +
|
||||
'<ul id="shareWithList" class="shareWithList">' +
|
||||
'{{#each sharees}}' +
|
||||
' {{#if isCollection}}' +
|
||||
' <li data-collection="{{collectionID}}">{{text}}</li>' +
|
||||
@@ -27,31 +27,31 @@
|
||||
' {{/if}}' +
|
||||
' <span class="username">{{shareWithDisplayName}}</span>' +
|
||||
' {{#if mailPublicNotificationEnabled}} {{#unless isRemoteShare}}' +
|
||||
' <input id="mail-{{shareWith}}" type="checkbox" name="mailNotification" class="mailNotification checkbox" {{#if wasMailSent}}checked="checked"{{/if}} />' +
|
||||
' <label for="mail-{{shareWith}}">{{notifyByMailLabel}}</label>' +
|
||||
' <input id="mail-{{cid}}-{{shareWith}}" type="checkbox" name="mailNotification" class="mailNotification checkbox" {{#if wasMailSent}}checked="checked"{{/if}} />' +
|
||||
' <label for="mail-{{cid}}-{{shareWith}}">{{notifyByMailLabel}}</label>' +
|
||||
' {{/unless}} {{/if}}' +
|
||||
' {{#if isResharingAllowed}} {{#if sharePermissionPossible}} {{#unless isRemoteShare}}' +
|
||||
' <input id="canShare-{{shareWith}}" type="checkbox" name="share" class="permissions checkbox" {{#if hasSharePermission}}checked="checked"{{/if}} data-permissions="{{sharePermission}}" />' +
|
||||
' <label for="canShare-{{shareWith}}">{{canShareLabel}}</label>' +
|
||||
' <input id="canShare-{{cid}}-{{shareWith}}" type="checkbox" name="share" class="permissions checkbox" {{#if hasSharePermission}}checked="checked"{{/if}} data-permissions="{{sharePermission}}" />' +
|
||||
' <label for="canShare-{{cid}}-{{shareWith}}">{{canShareLabel}}</label>' +
|
||||
' {{/unless}} {{/if}} {{/if}}' +
|
||||
' {{#if editPermissionPossible}}' +
|
||||
' <input id="canEdit-{{shareWith}}" type="checkbox" name="edit" class="permissions checkbox" {{#if hasEditPermission}}checked="checked"{{/if}} />' +
|
||||
' <label for="canEdit-{{shareWith}}">{{canEditLabel}}</label>' +
|
||||
' <input id="canEdit-{{cid}}-{{shareWith}}" type="checkbox" name="edit" class="permissions checkbox" {{#if hasEditPermission}}checked="checked"{{/if}} />' +
|
||||
' <label for="canEdit-{{cid}}-{{shareWith}}">{{canEditLabel}}</label>' +
|
||||
' {{/if}}' +
|
||||
' {{#unless isRemoteShare}}' +
|
||||
' <a href="#" class="showCruds"><img class="svg" alt="{{crudsLabel}}" src="{{triangleSImage}}"/></a>' +
|
||||
' <div class="cruds hidden">' +
|
||||
' {{#if createPermissionPossible}}' +
|
||||
' <input id="canCreate-{{shareWith}}" type="checkbox" name="create" class="permissions checkbox" {{#if hasCreatePermission}}checked="checked"{{/if}} data-permissions="{{createPermission}}"/>' +
|
||||
' <label for="canCreate-{{shareWith}}">{{createPermissionLabel}}</label>' +
|
||||
' <input id="canCreate-{{cid}}-{{shareWith}}" type="checkbox" name="create" class="permissions checkbox" {{#if hasCreatePermission}}checked="checked"{{/if}} data-permissions="{{createPermission}}"/>' +
|
||||
' <label for="canCreate-{{cid}}-{{shareWith}}">{{createPermissionLabel}}</label>' +
|
||||
' {{/if}}' +
|
||||
' {{#if updatePermissionPossible}}' +
|
||||
' <input id="canUpdate-{{shareWith}}" type="checkbox" name="update" class="permissions checkbox" {{#if hasUpdatePermission}}checked="checked"{{/if}} data-permissions="{{updatePermission}}"/>' +
|
||||
' <label for="canUpdate-{{shareWith}}">{{updatePermissionLabel}}</label>' +
|
||||
' <input id="canUpdate-{{cid}}-{{shareWith}}" type="checkbox" name="update" class="permissions checkbox" {{#if hasUpdatePermission}}checked="checked"{{/if}} data-permissions="{{updatePermission}}"/>' +
|
||||
' <label for="canUpdate-{{cid}}-{{shareWith}}">{{updatePermissionLabel}}</label>' +
|
||||
' {{/if}}' +
|
||||
' {{#if deletePermissionPossible}} {{#unless isRemoteShare}}' +
|
||||
' <input id="canDelete-{{shareWith}}" type="checkbox" name="delete" class="permissions checkbox" {{#if hasDeletePermission}}checked="checked"{{/if}} data-permissions="{{deletePermission}}"/>' +
|
||||
' <label for="canDelete-{{shareWith}}">{{deletePermissionLabel}}</label>' +
|
||||
' <input id="canDelete-{{cid}}-{{shareWith}}" type="checkbox" name="delete" class="permissions checkbox" {{#if hasDeletePermission}}checked="checked"{{/if}} data-permissions="{{deletePermission}}"/>' +
|
||||
' <label for="canDelete-{{cid}}-{{shareWith}}">{{deletePermissionLabel}}</label>' +
|
||||
' {{/unless}} {{/if}}' +
|
||||
' </div>' +
|
||||
' {{/unless}}' +
|
||||
@@ -146,6 +146,7 @@
|
||||
}
|
||||
|
||||
return _.extend(hasPermissionOverride, {
|
||||
cid: this.cid,
|
||||
hasSharePermission: this.model.hasSharePermission(shareIndex),
|
||||
hasEditPermission: this.model.hasEditPermission(shareIndex),
|
||||
hasCreatePermission: this.model.hasCreatePermission(shareIndex),
|
||||
@@ -209,8 +210,8 @@
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var shareeListTemplate = this.template();
|
||||
this.$el.html(shareeListTemplate({
|
||||
this.$el.html(this.template({
|
||||
cid: this.cid,
|
||||
sharees: this.getShareeList()
|
||||
}));
|
||||
|
||||
@@ -235,11 +236,11 @@
|
||||
* @returns {Function} from Handlebars
|
||||
* @private
|
||||
*/
|
||||
template: function () {
|
||||
template: function (data) {
|
||||
if (!this._template) {
|
||||
this._template = Handlebars.compile(TEMPLATE);
|
||||
}
|
||||
return this._template;
|
||||
return this._template(data);
|
||||
},
|
||||
|
||||
onUnshare: function(event) {
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
var TEMPLATE_BASE =
|
||||
'<div class="resharerInfoView subView"></div>' +
|
||||
'{{#if isSharingAllowed}}' +
|
||||
'<label for="shareWith" class="hidden-visually">{{shareLabel}}</label>' +
|
||||
'<label for="shareWith-{{cid}}" class="hidden-visually">{{shareLabel}}</label>' +
|
||||
'<div class="oneline">' +
|
||||
' <input id="shareWith" type="text" placeholder="{{sharePlaceholder}}" />' +
|
||||
' <input id="shareWith-{{cid}}" class="shareWithField" type="text" placeholder="{{sharePlaceholder}}" />' +
|
||||
' <span class="shareWithLoading icon-loading-small hidden"></span>'+
|
||||
'{{{remoteShareInfo}}}' +
|
||||
'</div>' +
|
||||
@@ -127,7 +127,7 @@
|
||||
$loading.addClass('hidden');
|
||||
$loading.removeClass('inlineblock');
|
||||
if (result.status == 'success' && result.data.length > 0) {
|
||||
$("#shareWith").autocomplete("option", "autoFocus", true);
|
||||
$('.shareWithField').autocomplete("option", "autoFocus", true);
|
||||
response(result.data);
|
||||
} else {
|
||||
response();
|
||||
@@ -184,7 +184,7 @@
|
||||
this._loadingOnce = true;
|
||||
// the first time, focus on the share field after the spinner disappeared
|
||||
_.defer(function() {
|
||||
self.$('#shareWith').focus();
|
||||
self.$('.shareWithField').focus();
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -193,13 +193,14 @@
|
||||
var baseTemplate = this._getTemplate('base', TEMPLATE_BASE);
|
||||
|
||||
this.$el.html(baseTemplate({
|
||||
cid: this.cid,
|
||||
shareLabel: t('core', 'Share'),
|
||||
sharePlaceholder: this._renderSharePlaceholderPart(),
|
||||
remoteShareInfo: this._renderRemoteShareInfoPart(),
|
||||
isSharingAllowed: this.model.sharePermissionPossible()
|
||||
}));
|
||||
|
||||
var $shareField = this.$el.find('#shareWith');
|
||||
var $shareField = this.$el.find('.shareWithField');
|
||||
if ($shareField.length) {
|
||||
$shareField.autocomplete({
|
||||
minLength: 2,
|
||||
|
||||
@@ -116,7 +116,8 @@
|
||||
|
||||
// TODO: use backbone's default value mechanism once this is a separate model
|
||||
var requiredAttributes = [
|
||||
{ name: 'password', defaultValue: '' },
|
||||
{ name: 'password', defaultValue: '' },
|
||||
{ name: 'passwordChanged', defaultValue: false },
|
||||
{ name: 'permissions', defaultValue: OC.PERMISSION_READ },
|
||||
{ name: 'expiration', defaultValue: this.configModel.getDefaultExpirationDateString() }
|
||||
];
|
||||
@@ -136,11 +137,16 @@
|
||||
}
|
||||
});
|
||||
|
||||
var password = {
|
||||
password: attributes.password,
|
||||
passwordChanged: attributes.passwordChanged
|
||||
};
|
||||
|
||||
OC.Share.share(
|
||||
itemType,
|
||||
itemSource,
|
||||
OC.Share.SHARE_TYPE_LINK,
|
||||
attributes.password,
|
||||
password,
|
||||
attributes.permissions,
|
||||
this.fileInfoModel.get('name'),
|
||||
attributes.expiration,
|
||||
@@ -208,6 +214,7 @@
|
||||
*/
|
||||
setPassword: function(password) {
|
||||
this.get('linkShare').password = password;
|
||||
this.get('linkShare').passwordChanged = true;
|
||||
},
|
||||
|
||||
addShare: function(attributes, options) {
|
||||
@@ -522,11 +529,12 @@
|
||||
* @param {string} recipientEmail recipient email address
|
||||
*/
|
||||
sendEmailPrivateLink: function(recipientEmail) {
|
||||
var deferred = $.Deferred();
|
||||
var itemType = this.get('itemType');
|
||||
var itemSource = this.get('itemSource');
|
||||
var linkShare = this.get('linkShare');
|
||||
|
||||
return $.post(
|
||||
$.post(
|
||||
OC.generateUrl('core/ajax/share.php'), {
|
||||
action: 'email',
|
||||
toaddress: recipientEmail,
|
||||
@@ -540,8 +548,13 @@
|
||||
if (!result || result.status !== 'success') {
|
||||
// FIXME: a model should not show dialogs
|
||||
OC.dialogs.alert(result.data.message, t('core', 'Error while sending notification'));
|
||||
deferred.reject();
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise();
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -142,6 +142,13 @@ describe('OC.Share tests', function() {
|
||||
|
||||
checkIcon('filetypes/folder-public');
|
||||
});
|
||||
it('shows external storage icon if external mount point', function() {
|
||||
$file.attr('data-type', 'dir');
|
||||
$file.attr('data-mountType', 'external');
|
||||
OC.Share.markFileAsShared($file, false, false);
|
||||
|
||||
checkIcon('filetypes/folder-external');
|
||||
});
|
||||
});
|
||||
|
||||
describe('displaying the recipoients', function() {
|
||||
|
||||
@@ -80,6 +80,9 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
model: shareModel
|
||||
});
|
||||
|
||||
// required for proper event propagation when simulating clicks in some cases (jquery bugs)
|
||||
$('#testArea').append(dialog.$el);
|
||||
|
||||
// triggers rendering
|
||||
shareModel.set({
|
||||
shares: [],
|
||||
@@ -111,6 +114,7 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
/* jshint camelcase:false */
|
||||
oc_appconfig.core = oldAppConfig;
|
||||
|
||||
dialog.remove();
|
||||
fetchStub.restore();
|
||||
|
||||
autocompleteStub.restore();
|
||||
@@ -127,7 +131,7 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
dialog.render();
|
||||
|
||||
// Toggle linkshare
|
||||
dialog.$el.find('[name=linkCheckbox]').click();
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
@@ -136,13 +140,14 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
|
||||
// Enable password, enter password and focusout
|
||||
dialog.$el.find('[name=showPassword]').click();
|
||||
dialog.$el.find('#linkPassText').focus();
|
||||
dialog.$el.find('#linkPassText').val('foo');
|
||||
dialog.$el.find('#linkPassText').focusout();
|
||||
dialog.$el.find('.linkPassText').focus();
|
||||
dialog.$el.find('.linkPassText').val('foo');
|
||||
dialog.$el.find('.linkPassText').focusout();
|
||||
|
||||
expect(fakeServer.requests[1].method).toEqual('POST');
|
||||
var body = OC.parseQueryString(fakeServer.requests[1].requestBody);
|
||||
expect(body.shareWith).toEqual('foo');
|
||||
expect(body['shareWith[password]']).toEqual('foo');
|
||||
expect(body['shareWith[passwordChanged]']).toEqual('true');
|
||||
|
||||
fetchStub.reset();
|
||||
|
||||
@@ -157,8 +162,8 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
// fetching the model will rerender the view
|
||||
dialog.render();
|
||||
|
||||
expect(dialog.$el.find('#linkPassText').val()).toEqual('');
|
||||
expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('**********');
|
||||
expect(dialog.$el.find('.linkPassText').val()).toEqual('');
|
||||
expect(dialog.$el.find('.linkPassText').attr('placeholder')).toEqual('**********');
|
||||
});
|
||||
it('update password on enter', function() {
|
||||
$('#allowShareWithLink').val('yes');
|
||||
@@ -166,7 +171,7 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
dialog.render();
|
||||
|
||||
// Toggle linkshare
|
||||
dialog.$el.find('[name=linkCheckbox]').click();
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
@@ -175,13 +180,14 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
|
||||
// Enable password and enter password
|
||||
dialog.$el.find('[name=showPassword]').click();
|
||||
dialog.$el.find('#linkPassText').focus();
|
||||
dialog.$el.find('#linkPassText').val('foo');
|
||||
dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
|
||||
dialog.$el.find('.linkPassText').focus();
|
||||
dialog.$el.find('.linkPassText').val('foo');
|
||||
dialog.$el.find('.linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
|
||||
|
||||
expect(fakeServer.requests[1].method).toEqual('POST');
|
||||
var body = OC.parseQueryString(fakeServer.requests[1].requestBody);
|
||||
expect(body.shareWith).toEqual('foo');
|
||||
expect(body['shareWith[password]']).toEqual('foo');
|
||||
expect(body['shareWith[passwordChanged]']).toEqual('true');
|
||||
|
||||
fetchStub.reset();
|
||||
|
||||
@@ -196,22 +202,22 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
// fetching the model will rerender the view
|
||||
dialog.render();
|
||||
|
||||
expect(dialog.$el.find('#linkPassText').val()).toEqual('');
|
||||
expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('**********');
|
||||
expect(dialog.$el.find('.linkPassText').val()).toEqual('');
|
||||
expect(dialog.$el.find('.linkPassText').attr('placeholder')).toEqual('**********');
|
||||
});
|
||||
it('shows share with link checkbox when allowed', function() {
|
||||
$('#allowShareWithLink').val('yes');
|
||||
|
||||
dialog.render();
|
||||
|
||||
expect(dialog.$el.find('#linkCheckbox').length).toEqual(1);
|
||||
expect(dialog.$el.find('.linkCheckbox').length).toEqual(1);
|
||||
});
|
||||
it('does not show share with link checkbox when not allowed', function() {
|
||||
$('#allowShareWithLink').val('no');
|
||||
|
||||
dialog.render();
|
||||
|
||||
expect(dialog.$el.find('#linkCheckbox').length).toEqual(0);
|
||||
expect(dialog.$el.find('.linkCheckbox').length).toEqual(0);
|
||||
});
|
||||
it('shows populated link share when a link share exists', function() {
|
||||
// this is how the OC.Share class does it...
|
||||
@@ -228,8 +234,31 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
|
||||
dialog.render();
|
||||
|
||||
expect(dialog.$el.find('#linkCheckbox').prop('checked')).toEqual(true);
|
||||
expect(dialog.$el.find('#linkText').val()).toEqual(link);
|
||||
expect(dialog.$el.find('.linkCheckbox').prop('checked')).toEqual(true);
|
||||
expect(dialog.$el.find('.linkText').val()).toEqual(link);
|
||||
});
|
||||
it('autofocus link text when clicked', function() {
|
||||
$('#allowShareWithLink').val('yes');
|
||||
|
||||
dialog.render();
|
||||
|
||||
// Toggle linkshare
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({data: {token: 'xyz'}, status: 'success'})
|
||||
);
|
||||
|
||||
var focusStub = sinon.stub($.fn, 'focus');
|
||||
var selectStub = sinon.stub($.fn, 'select');
|
||||
dialog.$el.find('.linkText').click();
|
||||
|
||||
expect(focusStub.calledOnce).toEqual(true);
|
||||
expect(selectStub.calledOnce).toEqual(true);
|
||||
|
||||
focusStub.restore();
|
||||
selectStub.restore();
|
||||
});
|
||||
describe('password', function() {
|
||||
var slideToggleStub;
|
||||
@@ -250,7 +279,7 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
configModel.set('enforcePasswordForPublicLink', true);
|
||||
dialog.render();
|
||||
|
||||
dialog.$el.find('[name=linkCheckbox]').click();
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
|
||||
// The password linkPass field is shown (slideToggle is called).
|
||||
// No request is made yet
|
||||
@@ -259,7 +288,7 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
expect(fakeServer.requests.length).toEqual(0);
|
||||
|
||||
// Now untoggle share by link
|
||||
dialog.$el.find('[name=linkCheckbox]').click();
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
dialog.render();
|
||||
|
||||
// Password field disappears and no ajax requests have been made
|
||||
@@ -302,31 +331,31 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
dialog.render();
|
||||
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
|
||||
expect(dialog.$el.find('#expirationDate').val()).toEqual('');
|
||||
expect(dialog.$el.find('.datepicker').val()).toEqual('');
|
||||
});
|
||||
it('does not check expiration date checkbox for new share', function() {
|
||||
dialog.render();
|
||||
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
|
||||
expect(dialog.$el.find('#expirationDate').val()).toEqual('');
|
||||
expect(dialog.$el.find('.datepicker').val()).toEqual('');
|
||||
});
|
||||
it('checks expiration date checkbox and populates field when expiration date was set', function() {
|
||||
shareModel.get('linkShare').expiration = '2014-02-01 00:00:00';
|
||||
dialog.render();
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
|
||||
expect(dialog.$el.find('#expirationDate').val()).toEqual('01-02-2014');
|
||||
expect(dialog.$el.find('.datepicker').val()).toEqual('01-02-2014');
|
||||
});
|
||||
it('sets default date when default date setting is enabled', function() {
|
||||
configModel.set('isDefaultExpireDateEnabled', true);
|
||||
dialog.render();
|
||||
dialog.$el.find('[name=linkCheckbox]').click();
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
// here fetch would be called and the server returns the expiration date
|
||||
shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
|
||||
dialog.render();
|
||||
|
||||
// enabled by default
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
|
||||
expect(dialog.$el.find('#expirationDate').val()).toEqual('27-01-2014');
|
||||
expect(dialog.$el.find('.datepicker').val()).toEqual('27-01-2014');
|
||||
|
||||
// disabling is allowed
|
||||
dialog.$el.find('[name=expirationCheckbox]').click();
|
||||
@@ -338,13 +367,13 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
isDefaultExpireDateEnforced: true
|
||||
});
|
||||
dialog.render();
|
||||
dialog.$el.find('[name=linkCheckbox]').click();
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
// here fetch would be called and the server returns the expiration date
|
||||
shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
|
||||
dialog.render();
|
||||
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
|
||||
expect(dialog.$el.find('#expirationDate').val()).toEqual('27-01-2014');
|
||||
expect(dialog.$el.find('.datepicker').val()).toEqual('27-01-2014');
|
||||
|
||||
// disabling is not allowed
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('disabled')).toEqual(true);
|
||||
@@ -358,14 +387,14 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
isDefaultExpireDateEnforced: true
|
||||
});
|
||||
dialog.render();
|
||||
dialog.$el.find('[name=linkCheckbox]').click();
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
// here fetch would be called and the server returns the expiration date
|
||||
shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
|
||||
dialog.render();
|
||||
|
||||
//Enter password
|
||||
dialog.$el.find('#linkPassText').val('foo');
|
||||
dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
|
||||
dialog.$el.find('.linkPassText').val('foo');
|
||||
dialog.$el.find('.linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
@@ -373,26 +402,16 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
);
|
||||
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
|
||||
expect(dialog.$el.find('#expirationDate').val()).toEqual('27-01-2014');
|
||||
expect(dialog.$el.find('.datepicker').val()).toEqual('27-01-2014');
|
||||
|
||||
// disabling is not allowed
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('disabled')).toEqual(true);
|
||||
dialog.$el.find('[name=expirationCheckbox]').click();
|
||||
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
|
||||
});
|
||||
it('displayes email form when sending emails is enabled', function() {
|
||||
$('input[name=mailPublicNotificationEnabled]').val('yes');
|
||||
dialog.render();
|
||||
expect(dialog.$('#emailPrivateLink').length).toEqual(1);
|
||||
});
|
||||
it('not renders email form when sending emails is disabled', function() {
|
||||
$('input[name=mailPublicNotificationEnabled]').val('no');
|
||||
dialog.render();
|
||||
expect(dialog.$('#emailPrivateLink').length).toEqual(0);
|
||||
});
|
||||
it('sets picker minDate to today and no maxDate by default', function() {
|
||||
dialog.render();
|
||||
dialog.$el.find('[name=linkCheckbox]').click();
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
dialog.$el.find('[name=expirationCheckbox]').click();
|
||||
expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
|
||||
expect($.datepicker._defaults.maxDate).toEqual(null);
|
||||
@@ -403,7 +422,7 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
isDefaultExpireDateEnforced: true
|
||||
});
|
||||
dialog.render();
|
||||
dialog.$el.find('[name=linkCheckbox]').click();
|
||||
dialog.$el.find('.linkCheckbox').click();
|
||||
expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
|
||||
expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0));
|
||||
});
|
||||
@@ -422,6 +441,76 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0));
|
||||
});
|
||||
});
|
||||
describe('send link by email', function() {
|
||||
var sendEmailPrivateLinkStub;
|
||||
var clock;
|
||||
|
||||
beforeEach(function() {
|
||||
configModel.set({
|
||||
isMailPublicNotificationEnabled: true
|
||||
});
|
||||
|
||||
shareModel.set('linkShare', {
|
||||
isLinkShare: true,
|
||||
token: 'tehtoken',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
expiration: null
|
||||
});
|
||||
|
||||
sendEmailPrivateLinkStub = sinon.stub(dialog.model, "sendEmailPrivateLink");
|
||||
clock = sinon.useFakeTimers();
|
||||
});
|
||||
afterEach(function() {
|
||||
sendEmailPrivateLinkStub.restore();
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('displayes form when sending emails is enabled', function() {
|
||||
$('input[name=mailPublicNotificationEnabled]').val('yes');
|
||||
dialog.render();
|
||||
expect(dialog.$('#emailPrivateLink').length).toEqual(1);
|
||||
});
|
||||
it('form not rendered when sending emails is disabled', function() {
|
||||
$('input[name=mailPublicNotificationEnabled]').val('no');
|
||||
dialog.render();
|
||||
expect(dialog.$('#emailPrivateLink').length).toEqual(0);
|
||||
});
|
||||
it('input cleared on success', function() {
|
||||
var defer = $.Deferred();
|
||||
sendEmailPrivateLinkStub.returns(defer.promise());
|
||||
|
||||
$('input[name=mailPublicNotificationEnabled]').val('yes');
|
||||
dialog.render();
|
||||
|
||||
dialog.$el.find('#emailPrivateLink #email').val('a@b.c');
|
||||
dialog.$el.find('#emailPrivateLink').trigger('submit');
|
||||
|
||||
expect(sendEmailPrivateLinkStub.callCount).toEqual(1);
|
||||
expect(dialog.$el.find('#emailPrivateLink #email').val()).toEqual('Sending ...');
|
||||
|
||||
defer.resolve();
|
||||
expect(dialog.$el.find('#emailPrivateLink #email').val()).toEqual('Email sent');
|
||||
|
||||
clock.tick(2000);
|
||||
expect(dialog.$el.find('#emailPrivateLink #email').val()).toEqual('');
|
||||
});
|
||||
it('input not cleared on failure', function() {
|
||||
var defer = $.Deferred();
|
||||
sendEmailPrivateLinkStub.returns(defer.promise());
|
||||
|
||||
$('input[name=mailPublicNotificationEnabled]').val('yes');
|
||||
dialog.render();
|
||||
|
||||
dialog.$el.find('#emailPrivateLink #email').val('a@b.c');
|
||||
dialog.$el.find('#emailPrivateLink').trigger('submit');
|
||||
|
||||
expect(sendEmailPrivateLinkStub.callCount).toEqual(1);
|
||||
expect(dialog.$el.find('#emailPrivateLink #email').val()).toEqual('Sending ...');
|
||||
|
||||
defer.reject();
|
||||
expect(dialog.$el.find('#emailPrivateLink #email').val()).toEqual('a@b.c');
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('check for avatar', function() {
|
||||
beforeEach(function() {
|
||||
@@ -472,7 +561,7 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
it('test correct function calls', function() {
|
||||
expect(avatarStub.calledTwice).toEqual(true);
|
||||
expect(placeholderStub.calledTwice).toEqual(true);
|
||||
expect(dialog.$('#shareWithList').children().length).toEqual(3);
|
||||
expect(dialog.$('.shareWithList').children().length).toEqual(3);
|
||||
expect(dialog.$('.avatar').length).toEqual(4);
|
||||
});
|
||||
|
||||
@@ -586,7 +675,7 @@ describe('OC.Share.ShareDialogView', function() {
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
dialog.render();
|
||||
expect(dialog.$el.find('#shareWith').prop('disabled')).toEqual(true);
|
||||
expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(true);
|
||||
});
|
||||
it('shows reshare owner', function() {
|
||||
shareModel.set({
|
||||
|
||||
@@ -378,5 +378,105 @@ describe('OC.Share.ShareItemModel', function() {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('sendEmailPrivateLink', function() {
|
||||
it('succeeds', function() {
|
||||
loadItemStub.yields({
|
||||
shares: [{
|
||||
displayname_owner: 'root',
|
||||
expiration: null,
|
||||
file_source: 123,
|
||||
file_target: '/folder',
|
||||
id: 20,
|
||||
item_source: '123',
|
||||
item_type: 'folder',
|
||||
mail_send: '0',
|
||||
parent: null,
|
||||
path: '/folder',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
share_type: OC.Share.SHARE_TYPE_LINK,
|
||||
share_with: null,
|
||||
stime: 1403884258,
|
||||
storage: 1,
|
||||
token: 'tehtoken',
|
||||
uid_owner: 'root'
|
||||
}]
|
||||
});
|
||||
model.fetch();
|
||||
|
||||
var res = model.sendEmailPrivateLink('foo@bar.com');
|
||||
|
||||
expect(res.state()).toEqual('pending');
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
expect(fakeServer.requests[0].url).toEqual(OC.generateUrl('core/ajax/share.php'));
|
||||
expect(OC.parseQueryString(fakeServer.requests[0].requestBody)).toEqual(
|
||||
{
|
||||
action: 'email',
|
||||
toaddress: 'foo@bar.com',
|
||||
link: model.get('linkShare').link,
|
||||
itemType: 'file',
|
||||
itemSource: '123',
|
||||
file: 'shared_file_name.txt',
|
||||
expiration: ''
|
||||
}
|
||||
)
|
||||
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({status: 'success'})
|
||||
);
|
||||
expect(res.state()).toEqual('resolved');
|
||||
});
|
||||
|
||||
it('fails', function() {
|
||||
loadItemStub.yields({
|
||||
shares: [{
|
||||
displayname_owner: 'root',
|
||||
expiration: null,
|
||||
file_source: 123,
|
||||
file_target: '/folder',
|
||||
id: 20,
|
||||
item_source: '123',
|
||||
item_type: 'folder',
|
||||
mail_send: '0',
|
||||
parent: null,
|
||||
path: '/folder',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
share_type: OC.Share.SHARE_TYPE_LINK,
|
||||
share_with: null,
|
||||
stime: 1403884258,
|
||||
storage: 1,
|
||||
token: 'tehtoken',
|
||||
uid_owner: 'root'
|
||||
}]
|
||||
});
|
||||
model.fetch();
|
||||
|
||||
var res = model.sendEmailPrivateLink('foo@bar.com');
|
||||
|
||||
expect(res.state()).toEqual('pending');
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
expect(fakeServer.requests[0].url).toEqual(OC.generateUrl('core/ajax/share.php'));
|
||||
expect(OC.parseQueryString(fakeServer.requests[0].requestBody)).toEqual(
|
||||
{
|
||||
action: 'email',
|
||||
toaddress: 'foo@bar.com',
|
||||
link: model.get('linkShare').link,
|
||||
itemType: 'file',
|
||||
itemSource: '123',
|
||||
file: 'shared_file_name.txt',
|
||||
expiration: ''
|
||||
}
|
||||
)
|
||||
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({data: {message: 'fail'}, status: 'error'})
|
||||
);
|
||||
expect(res.state()).toEqual('rejected');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,3 @@
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#password-icon {
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ script('core', 'lostpassword');
|
||||
<p>
|
||||
<label for="password" class="infield"><?php p($l->t('New password')); ?></label>
|
||||
<input type="password" name="password" id="password" value="" placeholder="<?php p($l->t('New Password')); ?>" required />
|
||||
<img class="svg" id="password-icon" src="<?php print_unescaped(image_path('', 'actions/password.svg')); ?>" alt=""/>
|
||||
</p>
|
||||
<input type="submit" id="submit" value="<?php p($l->t('Reset password')); ?>" />
|
||||
<p class="text-center">
|
||||
|
||||
@@ -43,7 +43,6 @@ script('core', [
|
||||
value="<?php p($_['adminlogin']); ?>"
|
||||
autocomplete="off" autocapitalize="off" autocorrect="off" autofocus required>
|
||||
<label for="adminlogin" class="infield"><?php p($l->t( 'Username' )); ?></label>
|
||||
<img class="svg" src="<?php p(image_path('', 'actions/user.svg')); ?>" alt="">
|
||||
</p>
|
||||
<p class="groupbottom">
|
||||
<input type="password" name="adminpass" data-typetoggle="#show" id="adminpass"
|
||||
@@ -51,7 +50,6 @@ script('core', [
|
||||
value="<?php p($_['adminpass']); ?>"
|
||||
autocomplete="off" autocapitalize="off" autocorrect="off" required>
|
||||
<label for="adminpass" class="infield"><?php p($l->t( 'Password' )); ?></label>
|
||||
<img class="svg" id="adminpass-icon" src="<?php print_unescaped(image_path('', 'actions/password.svg')); ?>" alt="">
|
||||
<input type="checkbox" id="show" name="show">
|
||||
<label for="show" class="svg"></label>
|
||||
<div class="strengthify-wrapper"></div>
|
||||
|
||||
@@ -45,7 +45,6 @@ script('core', [
|
||||
<?php p($_['user_autofocus'] ? 'autofocus' : ''); ?>
|
||||
autocomplete="on" autocapitalize="off" autocorrect="off" required>
|
||||
<label for="user" class="infield"><?php p($l->t('Username')); ?></label>
|
||||
<img class="svg" src="<?php print_unescaped(image_path('', 'actions/user.svg')); ?>" alt=""/>
|
||||
</p>
|
||||
|
||||
<p class="groupbottom">
|
||||
@@ -54,7 +53,6 @@ script('core', [
|
||||
<?php p($_['user_autofocus'] ? '' : 'autofocus'); ?>
|
||||
autocomplete="on" autocapitalize="off" autocorrect="off" required>
|
||||
<label for="password" class="infield"><?php p($l->t('Password')); ?></label>
|
||||
<img class="svg" id="password-icon" src="<?php print_unescaped(image_path('', 'actions/password.svg')); ?>" alt=""/>
|
||||
<input type="submit" id="submit" class="login primary icon-confirm svg" title="<?php p($l->t('Log in')); ?>" value="" disabled="disabled"/>
|
||||
</p>
|
||||
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@
|
||||
dataType: 'script',
|
||||
url: options.zxcvbn
|
||||
}).done(function() {
|
||||
me.bind('keyup input', function() {
|
||||
me.bind('keyup input change', function() {
|
||||
var password = $(this).val(),
|
||||
// hide strengthigy if no input is provided
|
||||
opacity = (password === '') ? 0 : 1,
|
||||
|
||||
+21
-1
@@ -133,7 +133,18 @@ class OC {
|
||||
OC_Config::$object = new \OC\Config(self::$configDir);
|
||||
|
||||
OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen(OC::$SERVERROOT)));
|
||||
$scriptName = $_SERVER['SCRIPT_NAME'];
|
||||
/**
|
||||
* FIXME: The following lines are required because we can't yet instantiiate
|
||||
* \OC::$server->getRequest() since \OC::$server does not yet exist.
|
||||
*/
|
||||
$params = [
|
||||
'server' => [
|
||||
'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'],
|
||||
'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'],
|
||||
],
|
||||
];
|
||||
$fakeRequest = new \OC\AppFramework\Http\Request($params, null, new \OC\AllConfig(new \OC\SystemConfig()));
|
||||
$scriptName = $fakeRequest->getScriptName();
|
||||
if (substr($scriptName, -1) == '/') {
|
||||
$scriptName .= 'index.php';
|
||||
//make sure suburi follows the same rules as scriptName
|
||||
@@ -145,6 +156,7 @@ class OC {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (OC::$CLI) {
|
||||
OC::$WEBROOT = OC_Config::getValue('overwritewebroot', '');
|
||||
} else {
|
||||
@@ -608,6 +620,14 @@ class OC {
|
||||
OC_User::useBackend(new OC_User_Database());
|
||||
OC_Group::useBackend(new OC_Group_Database());
|
||||
|
||||
// Subscribe to the hook
|
||||
\OCP\Util::connectHook(
|
||||
'\OCA\Files_Sharing\API\Server2Server',
|
||||
'preLoginNameUsedAsUserName',
|
||||
'\OC_User_Database',
|
||||
'preLoginNameUsedAsUserName'
|
||||
);
|
||||
|
||||
//setup extra user backends
|
||||
if (!self::checkUpgrade(false)) {
|
||||
OC_User::setupBackends();
|
||||
|
||||
@@ -34,6 +34,8 @@ class DeprecationCheck extends AbstractCheck implements ICheck {
|
||||
*/
|
||||
protected function getLocalClasses() {
|
||||
return [
|
||||
'OC_JSON' => '8.2.0',
|
||||
|
||||
'OCP\Config' => '8.0.0',
|
||||
'OCP\Contacts' => '8.1.0',
|
||||
'OCP\DB' => '8.1.0',
|
||||
@@ -49,6 +51,15 @@ class DeprecationCheck extends AbstractCheck implements ICheck {
|
||||
*/
|
||||
protected function getLocalConstants() {
|
||||
return [
|
||||
'OC_API::GUEST_AUTH' => '8.2.0',
|
||||
'OC_API::USER_AUTH' => '8.2.0',
|
||||
'OC_API::SUBADMIN_AUTH' => '8.2.0',
|
||||
'OC_API::ADMIN_AUTH' => '8.2.0',
|
||||
'OC_API::RESPOND_UNAUTHORISED' => '8.2.0',
|
||||
'OC_API::RESPOND_SERVER_ERROR' => '8.2.0',
|
||||
'OC_API::RESPOND_NOT_FOUND' => '8.2.0',
|
||||
'OC_API::RESPOND_UNKNOWN_ERROR' => '8.2.0',
|
||||
|
||||
'OCP::PERMISSION_CREATE' => '8.0.0',
|
||||
'OCP::PERMISSION_READ' => '8.0.0',
|
||||
'OCP::PERMISSION_UPDATE' => '8.0.0',
|
||||
@@ -80,8 +91,13 @@ class DeprecationCheck extends AbstractCheck implements ICheck {
|
||||
*/
|
||||
protected function getLocalMethods() {
|
||||
return [
|
||||
'OC_L10N::get' => '8.2.0',
|
||||
|
||||
'OCP\Activity\IManager::publishActivity' => '8.2.0',
|
||||
|
||||
'OCP\App::register' => '8.1.0',
|
||||
'OCP\App::addNavigationEntry' => '8.1.0',
|
||||
'OCP\App::getActiveNavigationEntry' => '8.2.0',
|
||||
'OCP\App::setActiveNavigationEntry' => '8.1.0',
|
||||
|
||||
'OCP\AppFramework\Controller::params' => '7.0.0',
|
||||
@@ -115,8 +131,12 @@ class DeprecationCheck extends AbstractCheck implements ICheck {
|
||||
'OCP\IAppConfig::setValue' => '8.0.0',
|
||||
'OCP\IAppConfig::deleteApp' => '8.0.0',
|
||||
|
||||
'OCP\IDBConnection::createQueryBuilder' => '8.2.0',
|
||||
'OCP\IDBConnection::getExpressionBuilder' => '8.2.0',
|
||||
|
||||
'OCP\ISearch::search' => '8.0.0',
|
||||
|
||||
'OCP\IServerContainer::getCache' => '8.2.0',
|
||||
'OCP\IServerContainer::getDb' => '8.1.0',
|
||||
'OCP\IServerContainer::getHTTPHelper' => '8.1.0',
|
||||
|
||||
@@ -128,20 +148,21 @@ class DeprecationCheck extends AbstractCheck implements ICheck {
|
||||
'OCP\User::logout' => '8.1.0',
|
||||
'OCP\User::checkPassword' => '8.1.0',
|
||||
|
||||
'OCP\Util::sendMail' => '8.1.0',
|
||||
'OCP\Util::formatDate' => '8.0.0',
|
||||
'OCP\Util::encryptedFiles' => '8.1.0',
|
||||
'OCP\Util::linkToRoute' => '8.1.0',
|
||||
'OCP\Util::linkTo' => '8.1.0',
|
||||
'OCP\Util::formatDate' => '8.0.0',
|
||||
'OCP\Util::generateRandomBytes' => '8.1.0',
|
||||
'OCP\Util::getServerHost' => '8.1.0',
|
||||
'OCP\Util::getServerProtocol' => '8.1.0',
|
||||
'OCP\Util::getRequestUri' => '8.1.0',
|
||||
'OCP\Util::getScriptName' => '8.1.0',
|
||||
'OCP\Util::imagePath' => '8.1.0',
|
||||
'OCP\Util::isValidFileName' => '8.1.0',
|
||||
'OCP\Util::generateRandomBytes' => '8.1.0',
|
||||
'OCP\Util::linkToRoute' => '8.1.0',
|
||||
'OCP\Util::linkTo' => '8.1.0',
|
||||
'OCP\Util::logException' => '8.2.0',
|
||||
'OCP\Util::mb_str_replace' => '8.2.0',
|
||||
'OCP\Util::mb_substr_replace' => '8.2.0',
|
||||
'OCP\Util::sendMail' => '8.1.0',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,9 +167,9 @@ class NodeVisitor extends NodeVisitorAbstract {
|
||||
* $c = "OC_API";
|
||||
* $n = $i::ADMIN_AUTH;
|
||||
*/
|
||||
} else {
|
||||
$this->checkBlackListConstant($node->class->toString(), $node->name, $node);
|
||||
}
|
||||
|
||||
$this->checkBlackListConstant($node->class->toString(), $node->name, $node);
|
||||
}
|
||||
}
|
||||
if ($node instanceof Node\Expr\New_) {
|
||||
|
||||
@@ -88,20 +88,17 @@ class Request implements \ArrayAccess, \Countable, IRequest {
|
||||
* - string 'method' the request method (GET, POST etc)
|
||||
* - string|false 'requesttoken' the requesttoken or false when not available
|
||||
* @param ISecureRandom $secureRandom
|
||||
* @param ICrypto $crypto
|
||||
* @param IConfig $config
|
||||
* @param string $stream
|
||||
* @see http://www.php.net/manual/en/reserved.variables.php
|
||||
*/
|
||||
public function __construct(array $vars=array(),
|
||||
ISecureRandom $secureRandom = null,
|
||||
ICrypto $crypto,
|
||||
IConfig $config,
|
||||
$stream='php://input') {
|
||||
$this->inputStream = $stream;
|
||||
$this->items['params'] = array();
|
||||
$this->secureRandom = $secureRandom;
|
||||
$this->crypto = $crypto;
|
||||
$this->config = $config;
|
||||
|
||||
if(!array_key_exists('method', $vars)) {
|
||||
@@ -439,22 +436,18 @@ class Request implements \ArrayAccess, \Countable, IRequest {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decrypt token to prevent BREACH like attacks
|
||||
// Deobfuscate token to prevent BREACH like attacks
|
||||
$token = explode(':', $token);
|
||||
if (count($token) !== 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$encryptedToken = $token[0];
|
||||
$obfuscatedToken = $token[0];
|
||||
$secret = $token[1];
|
||||
try {
|
||||
$decryptedToken = $this->crypto->decrypt($encryptedToken, $secret);
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
$deobfuscatedToken = base64_decode($obfuscatedToken) ^ $secret;
|
||||
|
||||
// Check if the token is valid
|
||||
if(\OCP\Security\StringUtils::equals($decryptedToken, $this->items['requesttoken'])) {
|
||||
if(\OCP\Security\StringUtils::equals($deobfuscatedToken, $this->items['requesttoken'])) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
||||
@@ -99,6 +99,11 @@ class Auth extends AbstractBasic {
|
||||
if($user && $this->isDavAuthenticated($user)) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
if($user && is_null(\OC::$server->getSession()->get(self::DAV_AUTHENTICATED))) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -114,6 +119,7 @@ class Auth extends AbstractBasic {
|
||||
* @param string $realm
|
||||
* @return bool
|
||||
* @throws ServiceUnavailable
|
||||
* @throws NotAuthenticated
|
||||
*/
|
||||
public function authenticate(\Sabre\DAV\Server $server, $realm) {
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
|
||||
const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL';
|
||||
const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size';
|
||||
const GETETAG_PROPERTYNAME = '{DAV:}getetag';
|
||||
const GETLASTMODIFIED_PROPERTYNAME = '{DAV:}getlastmodified';
|
||||
const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified';
|
||||
|
||||
/**
|
||||
* Reference to main server object
|
||||
@@ -101,7 +101,7 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
|
||||
$server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME;
|
||||
|
||||
// normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH
|
||||
$allowedProperties = ['{DAV:}getetag', '{DAV:}getlastmodified'];
|
||||
$allowedProperties = ['{DAV:}getetag'];
|
||||
$server->protectedProperties = array_diff($server->protectedProperties, $allowedProperties);
|
||||
|
||||
$this->server = $server;
|
||||
@@ -132,6 +132,10 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
|
||||
if ($sourceDir !== $destinationDir) {
|
||||
$sourceFileInfo = $this->fileView->getFileInfo($source);
|
||||
|
||||
if ($sourceFileInfo === false) {
|
||||
throw new \Sabre\DAV\Exception\NotFound($source . ' does not exist');
|
||||
}
|
||||
|
||||
if (!$sourceFileInfo->isDeletable()) {
|
||||
throw new \Sabre\DAV\Exception\Forbidden($source . " cannot be deleted");
|
||||
}
|
||||
@@ -208,7 +212,7 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
|
||||
* @return void
|
||||
*/
|
||||
public function handleUpdateProperties($path, PropPatch $propPatch) {
|
||||
$propPatch->handle(self::GETLASTMODIFIED_PROPERTYNAME, function($time) use ($path) {
|
||||
$propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function($time) use ($path) {
|
||||
if (empty($time)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ class OC_Files {
|
||||
|
||||
if (!is_array($files)) {
|
||||
$filename = $dir . '/' . $files;
|
||||
if (!$view->is_dir($files)) {
|
||||
if (!$view->is_dir($filename)) {
|
||||
self::getSingleFile($view, $dir, $files, $onlyHeader);
|
||||
return;
|
||||
}
|
||||
|
||||
Vendored
+9
-7
@@ -171,13 +171,15 @@ class Updater {
|
||||
|
||||
if ($sourceStorage && $targetStorage) {
|
||||
$targetCache = $targetStorage->getCache($sourceInternalPath);
|
||||
if ($targetCache->inCache($targetInternalPath)) {
|
||||
$targetCache->remove($targetInternalPath);
|
||||
}
|
||||
if ($sourceStorage === $targetStorage) {
|
||||
$targetCache->move($sourceInternalPath, $targetInternalPath);
|
||||
} else {
|
||||
$targetCache->moveFromCache($sourceStorage->getCache(), $sourceInternalPath, $targetInternalPath);
|
||||
if ($sourceStorage->getCache($sourceInternalPath)->inCache($sourceInternalPath)) {
|
||||
if ($targetCache->inCache($targetInternalPath)) {
|
||||
$targetCache->remove($targetInternalPath);
|
||||
}
|
||||
if ($sourceStorage === $targetStorage) {
|
||||
$targetCache->move($sourceInternalPath, $targetInternalPath);
|
||||
} else {
|
||||
$targetCache->moveFromCache($sourceStorage->getCache(), $sourceInternalPath, $targetInternalPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (pathinfo($sourceInternalPath, PATHINFO_EXTENSION) !== pathinfo($targetInternalPath, PATHINFO_EXTENSION)) {
|
||||
|
||||
@@ -337,7 +337,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
|
||||
'size' => 0,
|
||||
'mtime' => $mtime,
|
||||
'storage_mtime' => $mtime,
|
||||
'permissions' => \OCP\Constants::PERMISSION_ALL,
|
||||
'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
|
||||
);
|
||||
$fileId = $this->getCache()->put($path, $stat);
|
||||
try {
|
||||
@@ -362,7 +362,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
|
||||
if (empty($stat)) {
|
||||
// create new file
|
||||
$stat = array(
|
||||
'permissions' => \OCP\Constants::PERMISSION_ALL,
|
||||
'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
|
||||
);
|
||||
}
|
||||
// update stat with new data
|
||||
|
||||
@@ -1941,7 +1941,7 @@ class View {
|
||||
$pathSegments = explode('/', $path);
|
||||
if (isset($pathSegments[2])) {
|
||||
// E.g.: /username/files/path-to-file
|
||||
return $pathSegments[2] === 'files';
|
||||
return ($pathSegments[2] === 'files') && (count($pathSegments) > 3);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -33,6 +33,21 @@ abstract class AbstractLockingProvider implements ILockingProvider {
|
||||
'exclusive' => []
|
||||
];
|
||||
|
||||
/**
|
||||
* Check if we've locally acquired a lock
|
||||
*
|
||||
* @param string $path
|
||||
* @param int $type
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasAcquiredLock($path, $type) {
|
||||
if ($type === self::LOCK_SHARED) {
|
||||
return isset($this->acquiredLocks['shared'][$path]) && $this->acquiredLocks['shared'][$path] > 0;
|
||||
} else {
|
||||
return isset($this->acquiredLocks['exclusive'][$path]) && $this->acquiredLocks['exclusive'][$path] === true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a locally acquired lock
|
||||
*
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace OC\Lock;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\ILogger;
|
||||
use OCP\Lock\ILockingProvider;
|
||||
use OCP\Lock\LockedException;
|
||||
|
||||
/**
|
||||
@@ -46,8 +47,48 @@ class DBLockingProvider extends AbstractLockingProvider {
|
||||
*/
|
||||
private $timeFactory;
|
||||
|
||||
private $sharedLocks = [];
|
||||
|
||||
const TTL = 3600; // how long until we clear stray locks in seconds
|
||||
|
||||
/**
|
||||
* Check if we have an open shared lock for a path
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
protected function isLocallyLocked($path) {
|
||||
return isset($this->sharedLocks[$path]) && $this->sharedLocks[$path];
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a locally acquired lock
|
||||
*
|
||||
* @param string $path
|
||||
* @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
|
||||
*/
|
||||
protected function markAcquire($path, $type) {
|
||||
parent::markAcquire($path, $type);
|
||||
if ($type === self::LOCK_SHARED) {
|
||||
$this->sharedLocks[$path] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the type of an existing tracked lock
|
||||
*
|
||||
* @param string $path
|
||||
* @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE
|
||||
*/
|
||||
protected function markChange($path, $targetType) {
|
||||
parent::markChange($path, $targetType);
|
||||
if ($targetType === self::LOCK_SHARED) {
|
||||
$this->sharedLocks[$path] = true;
|
||||
} else if ($targetType === self::LOCK_EXCLUSIVE) {
|
||||
$this->sharedLocks[$path] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \OCP\IDBConnection $connection
|
||||
* @param \OCP\ILogger $logger
|
||||
@@ -85,11 +126,19 @@ class DBLockingProvider extends AbstractLockingProvider {
|
||||
* @return bool
|
||||
*/
|
||||
public function isLocked($path, $type) {
|
||||
if ($this->hasAcquiredLock($path, $type)) {
|
||||
return true;
|
||||
}
|
||||
$query = $this->connection->prepare('SELECT `lock` from `*PREFIX*file_locks` WHERE `key` = ?');
|
||||
$query->execute([$path]);
|
||||
$lockValue = (int)$query->fetchColumn();
|
||||
if ($type === self::LOCK_SHARED) {
|
||||
return $lockValue > 0;
|
||||
if ($this->isLocallyLocked($path)) {
|
||||
// if we have a shared lock we kept open locally but it's released we always have at least 1 shared lock in the db
|
||||
return $lockValue > 1;
|
||||
} else {
|
||||
return $lockValue > 0;
|
||||
}
|
||||
} else if ($type === self::LOCK_EXCLUSIVE) {
|
||||
return $lockValue === -1;
|
||||
} else {
|
||||
@@ -105,19 +154,27 @@ class DBLockingProvider extends AbstractLockingProvider {
|
||||
public function acquireLock($path, $type) {
|
||||
$expire = $this->getExpireTime();
|
||||
if ($type === self::LOCK_SHARED) {
|
||||
$result = $this->initLockField($path,1);
|
||||
if ($result <= 0) {
|
||||
$result = $this->connection->executeUpdate (
|
||||
'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` + 1, `ttl` = ? WHERE `key` = ? AND `lock` >= 0',
|
||||
[$expire, $path]
|
||||
);
|
||||
if (!$this->isLocallyLocked($path)) {
|
||||
$result = $this->initLockField($path, 1);
|
||||
if ($result <= 0) {
|
||||
$result = $this->connection->executeUpdate(
|
||||
'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` + 1, `ttl` = ? WHERE `key` = ? AND `lock` >= 0',
|
||||
[$expire, $path]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$result = 1;
|
||||
}
|
||||
} else {
|
||||
$result = $this->initLockField($path,-1);
|
||||
$existing = 0;
|
||||
if ($this->hasAcquiredLock($path, ILockingProvider::LOCK_SHARED) === false && $this->isLocallyLocked($path)) {
|
||||
$existing = 1;
|
||||
}
|
||||
$result = $this->initLockField($path, -1);
|
||||
if ($result <= 0) {
|
||||
$result = $this->connection->executeUpdate(
|
||||
'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = 0',
|
||||
[$expire, $path]
|
||||
'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = ?',
|
||||
[$expire, $path, $existing]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -132,19 +189,15 @@ class DBLockingProvider extends AbstractLockingProvider {
|
||||
* @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
|
||||
*/
|
||||
public function releaseLock($path, $type) {
|
||||
if ($type === self::LOCK_SHARED) {
|
||||
$this->connection->executeUpdate(
|
||||
'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` - 1 WHERE `key` = ? AND `lock` > 0',
|
||||
[$path]
|
||||
);
|
||||
} else {
|
||||
$this->markRelease($path, $type);
|
||||
|
||||
// we keep shared locks till the end of the request so we can re-use them
|
||||
if ($type === self::LOCK_EXCLUSIVE) {
|
||||
$this->connection->executeUpdate(
|
||||
'UPDATE `*PREFIX*file_locks` SET `lock` = 0 WHERE `key` = ? AND `lock` = -1',
|
||||
[$path]
|
||||
);
|
||||
}
|
||||
|
||||
$this->markRelease($path, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,6 +215,10 @@ class DBLockingProvider extends AbstractLockingProvider {
|
||||
[$expire, $path]
|
||||
);
|
||||
} else {
|
||||
// since we only keep one shared lock in the db we need to check if we have more then one shared lock locally manually
|
||||
if (isset($this->acquiredLocks['shared'][$path]) && $this->acquiredLocks['shared'][$path] > 1) {
|
||||
throw new LockedException($path);
|
||||
}
|
||||
$result = $this->connection->executeUpdate(
|
||||
'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = 1',
|
||||
[$expire, $path]
|
||||
@@ -184,10 +241,27 @@ class DBLockingProvider extends AbstractLockingProvider {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* release all lock acquired by this instance which were marked using the mark* methods
|
||||
*/
|
||||
public function releaseAll() {
|
||||
parent::releaseAll();
|
||||
|
||||
// since we keep shared locks we need to manually clean those
|
||||
foreach ($this->sharedLocks as $path => $lock) {
|
||||
if ($lock) {
|
||||
$this->connection->executeUpdate(
|
||||
'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` - 1 WHERE `key` = ? AND `lock` > 0',
|
||||
[$path]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
try {
|
||||
$this->cleanEmptyLocks();
|
||||
} catch (\PDOException $e) {
|
||||
} catch (\Exception $e) {
|
||||
// If the table is missing, the clean up was successful
|
||||
if ($this->connection->tableExists('file_locks')) {
|
||||
throw $e;
|
||||
|
||||
@@ -34,7 +34,7 @@ class OC_Log_Syslog {
|
||||
* Init class data
|
||||
*/
|
||||
public static function init() {
|
||||
openlog('ownCloud', LOG_PID | LOG_CONS, LOG_USER);
|
||||
openlog(OC_Config::getValue("syslog_tag", "ownCloud"), LOG_PID | LOG_CONS, LOG_USER);
|
||||
// Close at shutdown
|
||||
register_shutdown_function('closelog');
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ use OC\Repair\AssetCache;
|
||||
use OC\Repair\CleanTags;
|
||||
use OC\Repair\Collation;
|
||||
use OC\Repair\DropOldJobs;
|
||||
use OC\Repair\OldGroupMembershipShares;
|
||||
use OC\Repair\RemoveGetETagEntries;
|
||||
use OC\Repair\SqliteAutoincrement;
|
||||
use OC\Repair\DropOldTables;
|
||||
@@ -118,6 +119,18 @@ class Repair extends BasicEmitter {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns expensive repair steps to be run on the
|
||||
* command line with a special option.
|
||||
*
|
||||
* @return array of RepairStep instances
|
||||
*/
|
||||
public static function getExpensiveRepairSteps() {
|
||||
return [
|
||||
new OldGroupMembershipShares(\OC::$server->getDatabaseConnection(), \OC::$server->getGroupManager()),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repair steps to be run before an
|
||||
* upgrade.
|
||||
|
||||
@@ -438,7 +438,6 @@ class Server extends SimpleContainer implements IServerContainer {
|
||||
'requesttoken' => $requestToken,
|
||||
],
|
||||
$this->getSecureRandom(),
|
||||
$this->getCrypto(),
|
||||
$this->getConfig(),
|
||||
$stream
|
||||
);
|
||||
@@ -512,7 +511,6 @@ class Server extends SimpleContainer implements IServerContainer {
|
||||
: null,
|
||||
],
|
||||
new SecureRandom(),
|
||||
$c->getCrypto(),
|
||||
$c->getConfig()
|
||||
);
|
||||
|
||||
|
||||
+32
-15
@@ -597,11 +597,12 @@ class Share extends Constants {
|
||||
* @param int $permissions CRUDS
|
||||
* @param string $itemSourceName
|
||||
* @param \DateTime $expirationDate
|
||||
* @param bool $passwordChanged
|
||||
* @return boolean|string Returns true on success or false on failure, Returns token on success for links
|
||||
* @throws \OC\HintException when the share type is remote and the shareWith is invalid
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null) {
|
||||
public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null, $passwordChanged = null) {
|
||||
|
||||
$backend = self::getBackend($itemType);
|
||||
$l = \OC::$server->getL10N('lib');
|
||||
@@ -619,10 +620,13 @@ class Share extends Constants {
|
||||
if (is_null($itemSourceName)) {
|
||||
$itemSourceName = $itemSource;
|
||||
}
|
||||
$itemName = $itemSourceName;
|
||||
|
||||
// check if file can be shared
|
||||
if ($itemType === 'file' or $itemType === 'folder') {
|
||||
$path = \OC\Files\Filesystem::getPath($itemSource);
|
||||
$itemName = $path;
|
||||
|
||||
// verify that the file exists before we try to share it
|
||||
if (!$path) {
|
||||
$message = 'Sharing %s failed, because the file does not exist';
|
||||
@@ -633,8 +637,8 @@ class Share extends Constants {
|
||||
// verify that the user has share permission
|
||||
if (!\OC\Files\Filesystem::isSharable($path)) {
|
||||
$message = 'You are not allowed to share %s';
|
||||
$message_t = $l->t('You are not allowed to share %s', array($itemSourceName));
|
||||
\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
|
||||
$message_t = $l->t('You are not allowed to share %s', [$path]);
|
||||
\OCP\Util::writeLog('OCP\Share', sprintf($message, $path), \OCP\Util::DEBUG);
|
||||
throw new \Exception($message_t);
|
||||
}
|
||||
}
|
||||
@@ -677,9 +681,9 @@ class Share extends Constants {
|
||||
// Verify share type and sharing conditions are met
|
||||
if ($shareType === self::SHARE_TYPE_USER) {
|
||||
if ($shareWith == $uidOwner) {
|
||||
$message = 'Sharing %s failed, because the user %s is the item owner';
|
||||
$message_t = $l->t('Sharing %s failed, because the user %s is the item owner', array($itemSourceName, $shareWith));
|
||||
\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
|
||||
$message = 'Sharing %s failed, because you can not share with yourself';
|
||||
$message_t = $l->t('Sharing %s failed, because you can not share with yourself', [$itemName]);
|
||||
\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
|
||||
throw new \Exception($message_t);
|
||||
}
|
||||
if (!\OC_User::userExists($shareWith)) {
|
||||
@@ -772,14 +776,25 @@ class Share extends Constants {
|
||||
$updateExistingShare = true;
|
||||
}
|
||||
|
||||
// Generate hash of password - same method as user passwords
|
||||
if (is_string($shareWith) && $shareWith !== '') {
|
||||
self::verifyPassword($shareWith);
|
||||
$shareWith = \OC::$server->getHasher()->hash($shareWith);
|
||||
if ($passwordChanged === null) {
|
||||
// Generate hash of password - same method as user passwords
|
||||
if (is_string($shareWith) && $shareWith !== '') {
|
||||
self::verifyPassword($shareWith);
|
||||
$shareWith = \OC::$server->getHasher()->hash($shareWith);
|
||||
} else {
|
||||
// reuse the already set password, but only if we change permissions
|
||||
// otherwise the user disabled the password protection
|
||||
if ($checkExists && (int)$permissions !== (int)$oldPermissions) {
|
||||
$shareWith = $checkExists['share_with'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// reuse the already set password, but only if we change permissions
|
||||
// otherwise the user disabled the password protection
|
||||
if ($checkExists && (int)$permissions !== (int)$oldPermissions) {
|
||||
if ($passwordChanged === true) {
|
||||
if (is_string($shareWith) && $shareWith !== '') {
|
||||
self::verifyPassword($shareWith);
|
||||
$shareWith = \OC::$server->getHasher()->hash($shareWith);
|
||||
}
|
||||
} else if ($updateExistingShare) {
|
||||
$shareWith = $checkExists['share_with'];
|
||||
}
|
||||
}
|
||||
@@ -2208,7 +2223,7 @@ class Share extends Constants {
|
||||
// Check if attempting to share back to owner
|
||||
if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) {
|
||||
$message = 'Sharing %s failed, because the user %s is the original sharer';
|
||||
$message_t = $l->t('Sharing %s failed, because the user %s is the original sharer', array($itemSourceName, $shareWith));
|
||||
$message_t = $l->t('Sharing failed, because the user %s is the original sharer', [$shareWith]);
|
||||
|
||||
\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
|
||||
throw new \Exception($message_t);
|
||||
@@ -2632,7 +2647,9 @@ class Share extends Constants {
|
||||
*/
|
||||
private static function isFileReachable($path, $ownerStorageId) {
|
||||
// if outside the home storage, file is always considered reachable
|
||||
if (!(substr($ownerStorageId, 0, 6) === 'home::')) {
|
||||
if (!(substr($ownerStorageId, 0, 6) === 'home::' ||
|
||||
substr($ownerStorageId, 0, 13) === 'object::user:'
|
||||
)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -337,6 +337,8 @@ class Updater extends BasicEmitter {
|
||||
}
|
||||
|
||||
protected function checkCoreUpgrade() {
|
||||
$this->emit('\OC\Updater', 'dbSimulateUpgradeBefore');
|
||||
|
||||
// simulate core DB upgrade
|
||||
\OC_DB::simulateUpdateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml');
|
||||
|
||||
@@ -344,6 +346,8 @@ class Updater extends BasicEmitter {
|
||||
}
|
||||
|
||||
protected function doCoreUpgrade() {
|
||||
$this->emit('\OC\Updater', 'dbUpgradeBefore');
|
||||
|
||||
// do the real upgrade
|
||||
\OC_DB::updateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml');
|
||||
|
||||
@@ -355,6 +359,7 @@ class Updater extends BasicEmitter {
|
||||
*/
|
||||
protected function checkAppUpgrade($version) {
|
||||
$apps = \OC_App::getEnabledApps();
|
||||
$this->emit('\OC\Updater', 'appUpgradeCheckBefore');
|
||||
|
||||
foreach ($apps as $appId) {
|
||||
$info = \OC_App::getAppInfo($appId);
|
||||
@@ -372,6 +377,7 @@ class Updater extends BasicEmitter {
|
||||
$this->includePreUpdate($appId);
|
||||
}
|
||||
if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/database.xml')) {
|
||||
$this->emit('\OC\Updater', 'appSimulateUpdate', array($appId));
|
||||
\OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,6 +294,20 @@ class OC_User_Database extends OC_User_Backend implements \OCP\IUserBackend {
|
||||
return $result->fetchOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the username for the given login name in the correct casing
|
||||
*
|
||||
* @param string $loginName
|
||||
* @return string|false
|
||||
*/
|
||||
public function loginName2UserName($loginName) {
|
||||
if ($this->userExists($loginName)) {
|
||||
return $this->cache[$loginName]['uid'];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backend name to be shown in user management
|
||||
* @return string the name of the backend to be shown
|
||||
@@ -302,4 +316,22 @@ class OC_User_Database extends OC_User_Backend implements \OCP\IUserBackend {
|
||||
return 'Database';
|
||||
}
|
||||
|
||||
public static function preLoginNameUsedAsUserName($param) {
|
||||
if(!isset($param['uid'])) {
|
||||
throw new \Exception('key uid is expected to be set in $param');
|
||||
}
|
||||
|
||||
$backends = \OC::$server->getUserManager()->getBackends();
|
||||
foreach ($backends as $backend) {
|
||||
if ($backend instanceof \OC_User_Database) {
|
||||
/** @var \OC_User_Database $backend */
|
||||
$uid = $backend->loginName2UserName($param['uid']);
|
||||
if ($uid !== false) {
|
||||
$param['uid'] = $uid;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+12
-10
@@ -1093,7 +1093,7 @@ class OC_Util {
|
||||
return $id;
|
||||
}
|
||||
|
||||
protected static $encryptedToken;
|
||||
protected static $obfuscatedToken;
|
||||
/**
|
||||
* Register an get/post call. Important to prevent CSRF attacks.
|
||||
*
|
||||
@@ -1107,24 +1107,27 @@ class OC_Util {
|
||||
*/
|
||||
public static function callRegister() {
|
||||
// Use existing token if function has already been called
|
||||
if(isset(self::$encryptedToken)) {
|
||||
return self::$encryptedToken;
|
||||
if(isset(self::$obfuscatedToken)) {
|
||||
return self::$obfuscatedToken;
|
||||
}
|
||||
|
||||
$tokenLength = 30;
|
||||
|
||||
// Check if a token exists
|
||||
if (!\OC::$server->getSession()->exists('requesttoken')) {
|
||||
// No valid token found, generate a new one.
|
||||
$requestToken = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(30);
|
||||
$requestToken = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate($tokenLength);
|
||||
\OC::$server->getSession()->set('requesttoken', $requestToken);
|
||||
} else {
|
||||
// Valid token already exists, send it
|
||||
$requestToken = \OC::$server->getSession()->get('requesttoken');
|
||||
}
|
||||
|
||||
// Encrypt the token to mitigate breach-like attacks
|
||||
$sharedSecret = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(10);
|
||||
self::$encryptedToken = \OC::$server->getCrypto()->encrypt($requestToken, $sharedSecret) . ':' . $sharedSecret;
|
||||
return self::$encryptedToken;
|
||||
// XOR the token to mitigate breach-like attacks
|
||||
$sharedSecret = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate($tokenLength);
|
||||
self::$obfuscatedToken = base64_encode($requestToken ^ $sharedSecret) .':'.$sharedSecret;
|
||||
|
||||
return self::$obfuscatedToken;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1300,12 +1303,11 @@ class OC_Util {
|
||||
/**
|
||||
* Get URL content
|
||||
* @param string $url Url to get content
|
||||
* @deprecated Use \OC::$server->getHTTPHelper()->getUrlContent($url);
|
||||
* @throws Exception If the URL does not start with http:// or https://
|
||||
* @return string of the response or false on error
|
||||
* This function get the content of a page via curl, if curl is enabled.
|
||||
* If not, file_get_contents is used.
|
||||
* @deprecated Use \OCP\Http\Client\IClientService
|
||||
* @deprecated Use \OC::$server->getHTTPClientService()->newClient()->get($url);
|
||||
*/
|
||||
public static function getUrlContent($url) {
|
||||
try {
|
||||
|
||||
@@ -255,13 +255,14 @@ class Share extends \OC\Share\Constants {
|
||||
* @param int $permissions CRUDS
|
||||
* @param string $itemSourceName
|
||||
* @param \DateTime $expirationDate
|
||||
* @param bool $passwordChanged
|
||||
* @return bool|string Returns true on success or false on failure, Returns token on success for links
|
||||
* @throws \OC\HintException when the share type is remote and the shareWith is invalid
|
||||
* @throws \Exception
|
||||
* @since 5.0.0 - parameter $itemSourceName was added in 6.0.0, parameter $expirationDate was added in 7.0.0
|
||||
* @since 5.0.0 - parameter $itemSourceName was added in 6.0.0, parameter $expirationDate was added in 7.0.0, paramter $passwordChanged added in 9.0.0
|
||||
*/
|
||||
public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null) {
|
||||
return \OC\Share\Share::shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName, $expirationDate);
|
||||
public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null, $passwordChanged = null) {
|
||||
return \OC\Share\Share::shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName, $expirationDate, $passwordChanged);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user