aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files/ajax/list.php69
-rw-r--r--apps/files/js/fileactions.js5
-rw-r--r--apps/files/js/filelist.js57
-rw-r--r--apps/files/tests/js/filelistSpec.js22
-rw-r--r--apps/files_sharing/css/public.css14
-rw-r--r--apps/files_sharing/js/external.js2
-rw-r--r--apps/files_sharing/js/public.js1
-rw-r--r--apps/files_sharing/js/share.js90
-rw-r--r--apps/files_sharing/lib/external/manager.php2
-rw-r--r--apps/files_sharing/lib/external/scanner.php18
-rw-r--r--apps/files_sharing/lib/external/storage.php85
-rw-r--r--apps/files_sharing/lib/updater.php23
-rw-r--r--apps/files_sharing/templates/public.php2
-rw-r--r--apps/files_sharing/tests/externalstorage.php1
-rw-r--r--apps/files_sharing/tests/js/shareSpec.js8
-rw-r--r--apps/files_sharing/tests/updater.php36
-rw-r--r--core/css/mobile.css14
-rw-r--r--core/js/share.js11
-rw-r--r--lib/private/appframework/http/dispatcher.php2
-rw-r--r--lib/private/connector/sabre/objecttree.php10
-rw-r--r--lib/private/files/mount/manager.php4
-rw-r--r--lib/private/files/storage/dav.php44
-rw-r--r--lib/public/files/storageinvalidexception.php22
-rw-r--r--lib/public/files/storagenotavailableexception.php22
-rw-r--r--public.php2
25 files changed, 423 insertions, 143 deletions
diff --git a/apps/files/ajax/list.php b/apps/files/ajax/list.php
index bae3628402f..b4641343ed4 100644
--- a/apps/files/ajax/list.php
+++ b/apps/files/ajax/list.php
@@ -2,29 +2,54 @@
OCP\JSON::checkLoggedIn();
\OC::$session->close();
+$l = OC_L10N::get('files');
// Load the files
-$dir = isset( $_GET['dir'] ) ? $_GET['dir'] : '';
+$dir = isset($_GET['dir']) ? $_GET['dir'] : '';
$dir = \OC\Files\Filesystem::normalizePath($dir);
-$dirInfo = \OC\Files\Filesystem::getFileInfo($dir);
-if (!$dirInfo || !$dirInfo->getType() === 'dir') {
- header("HTTP/1.0 404 Not Found");
- exit();
-}
-
-$data = array();
-$baseUrl = OCP\Util::linkTo('files', 'index.php') . '?dir=';
-
-$permissions = $dirInfo->getPermissions();
-
-$sortAttribute = isset( $_GET['sort'] ) ? $_GET['sort'] : 'name';
-$sortDirection = isset( $_GET['sortdirection'] ) ? ($_GET['sortdirection'] === 'desc') : false;
-// make filelist
-$files = \OCA\Files\Helper::getFiles($dir, $sortAttribute, $sortDirection);
-
-$data['directory'] = $dir;
-$data['files'] = \OCA\Files\Helper::formatFileInfos($files);
-$data['permissions'] = $permissions;
-
-OCP\JSON::success(array('data' => $data));
+try {
+ $dirInfo = \OC\Files\Filesystem::getFileInfo($dir);
+ if (!$dirInfo || !$dirInfo->getType() === 'dir') {
+ header("HTTP/1.0 404 Not Found");
+ exit();
+ }
+
+ $data = array();
+ $baseUrl = OCP\Util::linkTo('files', 'index.php') . '?dir=';
+
+ $permissions = $dirInfo->getPermissions();
+
+ $sortAttribute = isset($_GET['sort']) ? $_GET['sort'] : 'name';
+ $sortDirection = isset($_GET['sortdirection']) ? ($_GET['sortdirection'] === 'desc') : false;
+
+ // make filelist
+
+ $files = \OCA\Files\Helper::getFiles($dir, $sortAttribute, $sortDirection);
+ $data['directory'] = $dir;
+ $data['files'] = \OCA\Files\Helper::formatFileInfos($files);
+ $data['permissions'] = $permissions;
+
+ OCP\JSON::success(array('data' => $data));
+} catch (\OCP\Files\StorageNotAvailableException $e) {
+ OCP\JSON::error(array(
+ 'data' => array(
+ 'exception' => '\OCP\Files\StorageNotAvailableException',
+ 'message' => $l->t('Storage not available')
+ )
+ ));
+} catch (\OCP\Files\StorageInvalidException $e) {
+ OCP\JSON::error(array(
+ 'data' => array(
+ 'exception' => '\OCP\Files\StorageInvalidException',
+ 'message' => $l->t('Storage invalid')
+ )
+ ));
+} catch (\Exception $e) {
+ OCP\JSON::error(array(
+ 'data' => array(
+ 'exception' => '\Exception',
+ 'message' => $l->t('Unknown error')
+ )
+ ));
+}
diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js
index fc7c9ccacef..e06d2912274 100644
--- a/apps/files/js/fileactions.js
+++ b/apps/files/js/fileactions.js
@@ -181,11 +181,12 @@
return;
}
this.currentFile = parent;
+ var $tr = parent.closest('tr');
var self = this;
var actions = this.getActions(this.getCurrentMimeType(), this.getCurrentType(), this.getCurrentPermissions());
var file = this.getCurrentFile();
var nameLinks;
- if (parent.closest('tr').data('renaming')) {
+ if ($tr.data('renaming')) {
return;
}
@@ -278,7 +279,7 @@
}
if (triggerEvent){
- fileList.$fileList.trigger(jQuery.Event("fileActionsReady", {fileList: fileList}));
+ fileList.$fileList.trigger(jQuery.Event("fileActionsReady", {fileList: fileList, $files: $tr}));
}
},
getCurrentFile: function () {
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 0477a657035..94f161943a1 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -473,6 +473,7 @@
/**
* Appends the next page of files into the table
* @param animate true to animate the new elements
+ * @return array of DOM elements of the newly added files
*/
_nextPage: function(animate) {
var index = this.$fileList.children().length,
@@ -483,7 +484,7 @@
isAllSelected = this.isAllSelected();
if (index >= this.files.length) {
- return;
+ return false;
}
while (count > 0 && index < this.files.length) {
@@ -496,12 +497,17 @@
}
if (animate) {
tr.addClass('appear transparent');
- newTrs.push(tr);
}
+ newTrs.push(tr);
index++;
count--;
}
+ // trigger event for newly added rows
+ if (newTrs.length > 0) {
+ this.$fileList.trigger($.Event('fileActionsReady', {fileList: this, $files: newTrs}));
+ }
+
if (animate) {
// defer, for animation
window.setTimeout(function() {
@@ -510,6 +516,7 @@
}
}, 0);
}
+ return newTrs;
},
/**
@@ -518,9 +525,16 @@
*/
_onFileActionsUpdated: function() {
var self = this;
- this.$fileList.find('tr td.filename').each(function() {
- self.fileActions.display($(this), true, self);
+ var $files = this.$fileList.find('tr');
+ if (!$files.length) {
+ return;
+ }
+
+ $files.each(function() {
+ self.fileActions.display($(this).find('td.filename'), false, self);
});
+ this.$fileList.trigger($.Event('fileActionsReady', {fileList: this, $files: $files}));
+
},
/**
@@ -532,7 +546,6 @@
// detach to make adding multiple rows faster
this.files = filesArray;
- this.$fileList.detach();
this.$fileList.empty();
// clear "Select all" checkbox
@@ -541,10 +554,7 @@
this.isEmpty = this.files.length === 0;
this._nextPage();
- this.$el.find('thead').after(this.$fileList);
-
this.updateEmptyContent();
- this.$fileList.trigger($.Event('fileActionsReady', {fileList: this}));
this.fileSummary.calculate(filesArray);
@@ -836,13 +846,18 @@
* @param {boolean} force set to true to force changing directory
*/
changeDirectory: function(targetDir, changeUrl, force) {
+ var self = this;
var currentDir = this.getCurrentDirectory();
targetDir = targetDir || '/';
if (!force && currentDir === targetDir) {
return;
}
this._setCurrentDir(targetDir, changeUrl);
- this.reload();
+ this.reload().then(function(success){
+ if (!success) {
+ self.changeDirectory(currentDir, true);
+ }
+ });
},
linkTo: function(dir) {
return OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/');
@@ -902,7 +917,6 @@
* @brief Reloads the file list using ajax call
*/
reload: function() {
- var self = this;
this._selectedFiles = {};
this._selectionSummary.clear();
this.$el.find('.select-all').prop('checked', false);
@@ -916,14 +930,10 @@
dir : this.getCurrentDirectory(),
sort: this._sort,
sortdirection: this._sortDirection
- },
- error: function(result) {
- self.reloadCallback(result);
- },
- success: function(result) {
- self.reloadCallback(result);
}
});
+ var callBack = this.reloadCallback.bind(this);
+ return this._reloadCall.then(callBack, callBack);
},
reloadCallback: function(result) {
delete this._reloadCall;
@@ -931,17 +941,17 @@
if (!result || result.status === 'error') {
OC.Notification.show(result.data.message);
- return;
+ return false;
}
if (result.status === 404) {
// go back home
this.changeDirectory('/');
- return;
+ return false;
}
// aborted ?
if (result.status === 0){
- return;
+ return true;
}
// TODO: should rather return upload file size through
@@ -953,6 +963,7 @@
}
this.setFiles(result.data.files);
+ return true
},
updateStorageStatistics: function(force) {
@@ -1282,16 +1293,16 @@
// reinsert row
self.files.splice(tr.index(), 1);
tr.remove();
- self.add(fileInfo, {updateSummary: false, silent: true});
- self.$fileList.trigger($.Event('fileActionsReady', {fileList: self}));
+ tr = self.add(fileInfo, {updateSummary: false, silent: true});
+ self.$fileList.trigger($.Event('fileActionsReady', {fileList: self, $files: $(tr)}));
}
});
} else {
// add back the old file info when cancelled
self.files.splice(tr.index(), 1);
tr.remove();
- self.add(oldFileInfo, {updateSummary: false, silent: true});
- self.$fileList.trigger($.Event('fileActionsReady', {fileList: self}));
+ tr = self.add(oldFileInfo, {updateSummary: false, silent: true});
+ self.$fileList.trigger($.Event('fileActionsReady', {fileList: self, $files: $(tr)}));
}
} catch (error) {
input.attr('title', error);
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
index a699177767a..713cd5468d8 100644
--- a/apps/files/tests/js/filelistSpec.js
+++ b/apps/files/tests/js/filelistSpec.js
@@ -797,13 +797,24 @@ describe('OCA.Files.FileList tests', function() {
fileList.$fileList.on('fileActionsReady', handler);
fileList.setFiles(testFiles);
expect(handler.calledOnce).toEqual(true);
+ expect(handler.getCall(0).args[0].$files.length).toEqual(testFiles.length);
});
it('triggers "fileActionsReady" event after single add', function() {
var handler = sinon.stub();
+ var $tr;
fileList.setFiles(testFiles);
fileList.$fileList.on('fileActionsReady', handler);
- fileList.add({name: 'test.txt'});
+ $tr = fileList.add({name: 'test.txt'});
+ expect(handler.calledOnce).toEqual(true);
+ expect(handler.getCall(0).args[0].$files.is($tr)).toEqual(true);
+ });
+ it('triggers "fileActionsReady" event after next page load with the newly appended files', function() {
+ var handler = sinon.stub();
+ fileList.setFiles(generateFiles(0, 64));
+ fileList.$fileList.on('fileActionsReady', handler);
+ fileList._nextPage();
expect(handler.calledOnce).toEqual(true);
+ expect(handler.getCall(0).args[0].$files.length).toEqual(fileList.pageSize);
});
it('does not trigger "fileActionsReady" event after single add with silent argument', function() {
var handler = sinon.stub();
@@ -1630,6 +1641,7 @@ describe('OCA.Files.FileList tests', function() {
});
it('redisplays actions when new actions have been registered', function() {
var actionStub = sinon.stub();
+ var readyHandler = sinon.stub();
var clock = sinon.useFakeTimers();
var debounceStub = sinon.stub(_, 'debounce', function(callback) {
return function() {
@@ -1637,11 +1649,15 @@ describe('OCA.Files.FileList tests', function() {
_.defer(callback);
};
});
+
// need to reinit the list to make the debounce call
fileList.destroy();
fileList = new OCA.Files.FileList($('#app-content-files'));
fileList.setFiles(testFiles);
+
+ fileList.$fileList.on('fileActionsReady', readyHandler);
+
fileList.fileActions.register(
'text/plain',
'Test',
@@ -1654,9 +1670,13 @@ describe('OCA.Files.FileList tests', function() {
);
var $tr = fileList.findFileEl('One.txt');
expect($tr.find('.action-test').length).toEqual(0);
+ expect(readyHandler.notCalled).toEqual(true);
+
// update is delayed
clock.tick(100);
expect($tr.find('.action-test').length).toEqual(1);
+ expect(readyHandler.calledOnce).toEqual(true);
+
clock.restore();
debounceStub.restore();
});
diff --git a/apps/files_sharing/css/public.css b/apps/files_sharing/css/public.css
index 31c3bca8748..ddd9d10d664 100644
--- a/apps/files_sharing/css/public.css
+++ b/apps/files_sharing/css/public.css
@@ -88,20 +88,6 @@ thead {
max-width: 90%;
}
-.header-right {
- transition: opacity 500ms ease 0s;
- -moz-transition: opacity 500ms ease 0s;
- -ms-transition: opacity 500ms ease 0s;
- -o-transition: opacity 500ms ease 0s;
- -webkit-transition: opacity 500ms ease 0s;
-}
-
-.header-right:hover, .header-right.active {
- opacity: 1;
- -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
- filter: alpha(opacity=100);
-}
-
/* within #save */
#remote_address {
margin: 0;
diff --git a/apps/files_sharing/js/external.js b/apps/files_sharing/js/external.js
index 1eb447da896..969a2b184d4 100644
--- a/apps/files_sharing/js/external.js
+++ b/apps/files_sharing/js/external.js
@@ -58,7 +58,7 @@ $(document).ready(function () {
if (params.remote && params.token && params.owner && params.name) {
// clear hash, it is unlikely that it contain any extra parameters
location.hash = '';
- params.passwordProtected = parseInt(params.passwordProtected, 10) === 1;
+ params.passwordProtected = parseInt(params.protected, 10) === 1;
OCA.Sharing.showAddExternalDialog(
params.remote,
params.token,
diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js
index f911f3689eb..a5027da0cfb 100644
--- a/apps/files_sharing/js/public.js
+++ b/apps/files_sharing/js/public.js
@@ -165,7 +165,6 @@ OCA.Sharing.PublicApp = {
$('#save > button').click(function () {
$(this).hide();
- $('.header-right').addClass('active');
$('.save-form').css('display', 'inline');
$('#remote_address').focus();
});
diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js
index 2efed5310bc..e46be4ada46 100644
--- a/apps/files_sharing/js/share.js
+++ b/apps/files_sharing/js/share.js
@@ -36,54 +36,34 @@
}
return tr;
};
-
- var oldRenderRow = OCA.Files.FileList.prototype._renderRow;
- OCA.Files.FileList.prototype._renderRow = function(fileData) {
- var $tr = oldRenderRow.apply(this, arguments);
- // if the statuses are loaded already, use them for the icon
- // (needed when scrolling to the next page)
- var shareStatus = OC.Share.statuses[fileData.id];
- if (fileData.shareOwner || fileData.recipientsDisplayName || shareStatus) {
- var permissions = $tr.data('permissions');
- var hasLink = !!(shareStatus && shareStatus.link);
- if (permissions & OC.PERMISSION_SHARE) {
- OC.Share.markFileAsShared($tr, true, hasLink);
- } else {
- // if no share action exists because the admin disabled sharing for this user
- // we create a share notification action to inform the user about files
- // shared with him otherwise we just update the existing share action.
- // TODO: make this work like/with OC.Share.markFileAsShared()
- var shareNotification = '<a class="action action-share-notification permanent"' +
- ' data-action="Share-Notification" href="#" original-title="">' +
- ' <img class="svg" src="' + OC.imagePath('core', 'actions/share') + '"></img>';
- $tr.find('.fileactions').append(function() {
- var shareBy = t('files_sharing', 'Shared by {owner}', {owner: escapeHTML(fileData.shareOwner)});
- var $result = $(shareNotification + '<span> ' + shareBy + '</span></span>');
- $result.on('click', function() {
- return false;
- });
- return $result;
- });
- }
- }
- return $tr;
- };
}
// use delegate to catch the case with multiple file lists
- $('#content').delegate('#fileList', 'fileActionsReady',function(ev){
+ $('#content').delegate('#fileList', 'fileActionsReady', function(ev){
var fileList = ev.fileList;
+ var $files = ev.$files;
+
+ function updateIcons($files) {
+ if (!$files) {
+ // if none specified, update all
+ $files = fileList.$fileList.find('tr');
+ }
+ _.each($files, function(file) {
+ OCA.Sharing.Util.updateFileActionIcon($(file));
+ });
+ }
+
if (!OCA.Sharing.sharesLoaded){
- OC.Share.loadIcons('file', fileList);
+ OC.Share.loadIcons('file', fileList, function() {
+ // since we don't know which files are affected, just refresh them all
+ updateIcons();
+ });
// assume that we got all shares, so switching directories
// will not invalidate that list
OCA.Sharing.sharesLoaded = true;
}
else{
- // this will update the icons for all the currently visible elements
- // additionally added elements when scrolling down will be
- // updated in the _renderRow override
- OC.Share.updateIcons('file', fileList);
+ updateIcons($files);
}
});
@@ -141,6 +121,40 @@
},
/**
+ * Update the file action share icon for the given file
+ *
+ * @param $tr file element of the file to update
+ */
+ updateFileActionIcon: function($tr) {
+ // if the statuses are loaded already, use them for the icon
+ // (needed when scrolling to the next page)
+ var shareStatus = OC.Share.statuses[$tr.data('id')];
+ if (shareStatus || $tr.attr('data-share-recipients') || $tr.attr('data-share-owner')) {
+ var permissions = $tr.data('permissions');
+ var hasLink = !!(shareStatus && shareStatus.link);
+ OC.Share.markFileAsShared($tr, true, hasLink);
+ if ((permissions & OC.PERMISSION_SHARE) === 0) {
+ // if no share action exists because the admin disabled sharing for this user
+ // we create a share notification action to inform the user about files
+ // shared with him otherwise we just update the existing share action.
+ // TODO: make this work like/with OC.Share.markFileAsShared()
+ $tr.find('.fileactions .action-share-notification').remove();
+ var shareNotification = '<a class="action action-share-notification permanent"' +
+ ' data-action="Share-Notification" href="#" original-title="">' +
+ ' <img class="svg" src="' + OC.imagePath('core', 'actions/share') + '"></img>';
+ $tr.find('.fileactions').append(function() {
+ var shareBy = t('files_sharing', 'Shared by {owner}', {owner: escapeHTML($tr.attr('data-share-owner'))});
+ var $result = $(shareNotification + '<span> ' + shareBy + '</span></span>');
+ $result.on('click', function() {
+ return false;
+ });
+ return $result;
+ });
+ }
+ }
+ },
+
+ /**
* Formats a recipients array to be displayed.
* The first four recipients will be shown and the
* other ones will be shown as "+x" where "x" is the number of
diff --git a/apps/files_sharing/lib/external/manager.php b/apps/files_sharing/lib/external/manager.php
index af317cc2027..dda283f4952 100644
--- a/apps/files_sharing/lib/external/manager.php
+++ b/apps/files_sharing/lib/external/manager.php
@@ -113,7 +113,9 @@ class Manager {
* @return Mount
*/
protected function mountShare($data) {
+ $data['manager'] = $this;
$mountPoint = '/' . $this->userSession->getUser()->getUID() . '/files' . $data['mountpoint'];
+ $data['mountpoint'] = $mountPoint;
$mount = new Mount(self::STORAGE, $mountPoint, $data, $this, $this->storageLoader);
$this->mountManager->addMount($mount);
return $mount;
diff --git a/apps/files_sharing/lib/external/scanner.php b/apps/files_sharing/lib/external/scanner.php
index 8921dd1a4c0..4dc5d4be9d8 100644
--- a/apps/files_sharing/lib/external/scanner.php
+++ b/apps/files_sharing/lib/external/scanner.php
@@ -19,23 +19,7 @@ class Scanner extends \OC\Files\Cache\Scanner {
}
public function scanAll() {
- $remote = $this->storage->getRemote();
- $token = $this->storage->getToken();
- $password = $this->storage->getPassword();
- $url = $remote . '/index.php/apps/files_sharing/shareinfo?t=' . $token;
-
- $ch = curl_init();
-
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_POST, 1);
- curl_setopt($ch, CURLOPT_POSTFIELDS,
- http_build_query(array('password' => $password)));
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
-
- $result = curl_exec($ch);
- curl_close($ch);
-
- $data = json_decode($result, true);
+ $data = $this->storage->getShareInfo();
if ($data['status'] === 'success') {
$this->addResult($data['data'], '');
} else {
diff --git a/apps/files_sharing/lib/external/storage.php b/apps/files_sharing/lib/external/storage.php
index 454196f15ae..3a0de51192e 100644
--- a/apps/files_sharing/lib/external/storage.php
+++ b/apps/files_sharing/lib/external/storage.php
@@ -10,7 +10,11 @@ namespace OCA\Files_Sharing\External;
use OC\Files\Filesystem;
use OC\Files\Storage\DAV;
+use OC\ForbiddenException;
use OCA\Files_Sharing\ISharedStorage;
+use OCP\Files\NotFoundException;
+use OCP\Files\StorageInvalidException;
+use OCP\Files\StorageNotAvailableException;
class Storage extends DAV implements ISharedStorage {
/**
@@ -35,7 +39,13 @@ class Storage extends DAV implements ISharedStorage {
private $updateChecked = false;
+ /**
+ * @var \OCA\Files_Sharing\External\Manager
+ */
+ private $manager;
+
public function __construct($options) {
+ $this->manager = $options['manager'];
$this->remote = $options['remote'];
$this->remoteUser = $options['owner'];
list($protocol, $remote) = explode('://', $this->remote);
@@ -108,6 +118,8 @@ class Storage extends DAV implements ISharedStorage {
*
* @param string $path
* @param int $time
+ * @throws \OCP\Files\StorageNotAvailableException
+ * @throws \OCP\Files\StorageInvalidException
* @return bool
*/
public function hasUpdated($path, $time) {
@@ -117,6 +129,77 @@ class Storage extends DAV implements ISharedStorage {
return false;
}
$this->updateChecked = true;
- return parent::hasUpdated('', $time);
+ try {
+ return parent::hasUpdated('', $time);
+ } catch (StorageNotAvailableException $e) {
+ // see if we can find out why the share is unavailable\
+ try {
+ $this->getShareInfo();
+ } catch (NotFoundException $shareException) {
+ // a 404 can either mean that the share no longer exists or there is no ownCloud on the remote
+ if ($this->testRemote()) {
+ // valid ownCloud instance means that the public share no longer exists
+ // since this is permanent (re-sharing the file will create a new token)
+ // we remove the invalid storage
+ $this->manager->removeShare($this->mountPoint);
+ $this->manager->getMountManager()->removeMount($this->mountPoint);
+ throw new StorageInvalidException();
+ } else {
+ // ownCloud instance is gone, likely to be a temporary server configuration error
+ throw $e;
+ }
+ } catch(\Exception $shareException) {
+ // todo, maybe handle 403 better and ask the user for a new password
+ throw $e;
+ }
+ throw $e;
+ }
+ }
+
+ /**
+ * check if the configured remote is a valid ownCloud instance
+ *
+ * @return bool
+ */
+ protected function testRemote() {
+ try {
+ $result = file_get_contents($this->remote . '/status.php');
+ $data = json_decode($result);
+ return is_object($data) and !empty($data->version);
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ public function getShareInfo() {
+ $remote = $this->getRemote();
+ $token = $this->getToken();
+ $password = $this->getPassword();
+ $url = $remote . '/index.php/apps/files_sharing/shareinfo?t=' . $token;
+
+ $ch = curl_init();
+
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_POST, 1);
+ curl_setopt($ch, CURLOPT_POSTFIELDS,
+ http_build_query(array('password' => $password)));
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+
+ $result = curl_exec($ch);
+
+ $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ curl_close($ch);
+
+ switch ($status) {
+ case 401:
+ case 403:
+ throw new ForbiddenException();
+ case 404:
+ throw new NotFoundException();
+ case 500:
+ throw new \Exception();
+ }
+
+ return json_decode($result, true);
}
}
diff --git a/apps/files_sharing/lib/updater.php b/apps/files_sharing/lib/updater.php
index e114c3ba0ac..aac4ed196de 100644
--- a/apps/files_sharing/lib/updater.php
+++ b/apps/files_sharing/lib/updater.php
@@ -109,6 +109,7 @@ class Shared_Updater {
static public function renameHook($params) {
self::correctFolders($params['newpath']);
self::correctFolders(pathinfo($params['oldpath'], PATHINFO_DIRNAME));
+ self::renameChildren($params['oldpath'], $params['newpath']);
}
/**
@@ -209,4 +210,26 @@ class Shared_Updater {
$findAndRemoveShares->execute(array());
}
+ /**
+ * rename mount point from the children if the parent was renamed
+ *
+ * @param string $oldPath old path relative to data/user/files
+ * @param string $newPath new path relative to data/user/files
+ */
+ static private function renameChildren($oldPath, $newPath) {
+
+ $absNewPath = \OC\Files\Filesystem::normalizePath('/' . \OCP\User::getUser() . '/files/' . $newPath);
+ $absOldPath = \OC\Files\Filesystem::normalizePath('/' . \OCP\User::getUser() . '/files/' . $oldPath);
+
+ $mountManager = \OC\Files\Filesystem::getMountManager();
+ $mountedShares = $mountManager->findIn('/' . \OCP\User::getUser() . '/files/' . $oldPath);
+ foreach ($mountedShares as $mount) {
+ if ($mount->getStorage()->instanceOfStorage('OCA\Files_Sharing\ISharedStorage')) {
+ $mountPoint = $mount->getMountPoint();
+ $target = str_replace($absOldPath, $absNewPath, $mountPoint);
+ $mount->moveMount($target);
+ }
+ }
+ }
+
}
diff --git a/apps/files_sharing/templates/public.php b/apps/files_sharing/templates/public.php
index c053aaabece..65e620a3425 100644
--- a/apps/files_sharing/templates/public.php
+++ b/apps/files_sharing/templates/public.php
@@ -17,7 +17,7 @@
<div class="header-right">
<span id="details">
<span id="save" data-protected="<?php p($_['protected'])?>" data-owner="<?php p($_['displayName'])?>" data-name="<?php p($_['filename'])?>">
- <button><?php p($l->t('Save to ownCloud')) ?></button>
+ <button><?php p($l->t('Add to your ownCloud')) ?></button>
<form class="save-form hidden" action="#">
<input type="text" id="remote_address" placeholder="<?php p($l->t('example.com/owncloud')) ?>"/>
<input type="submit" value="<?php p($l->t('Save')) ?>"/>
diff --git a/apps/files_sharing/tests/externalstorage.php b/apps/files_sharing/tests/externalstorage.php
index 60c5787f295..1258148af53 100644
--- a/apps/files_sharing/tests/externalstorage.php
+++ b/apps/files_sharing/tests/externalstorage.php
@@ -72,6 +72,7 @@ class Test_Files_Sharing_External_Storage extends \PHPUnit_Framework_TestCase {
'mountpoint' => 'remoteshare',
'token' => 'abcdef',
'password' => '',
+ 'manager' => null
)
);
$this->assertEquals($baseUri, $storage->getBaseUri());
diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js
index 3d4fc754821..600859b4e7d 100644
--- a/apps/files_sharing/tests/js/shareSpec.js
+++ b/apps/files_sharing/tests/js/shareSpec.js
@@ -80,6 +80,14 @@ describe('OCA.Sharing.Util tests', function() {
// TODO: test data-permissions, data-share-owner, etc
});
describe('Share action icon', function() {
+ beforeEach(function() {
+ OC.Share.statuses = {1: {link: false, path: '/subdir'}};
+ OCA.Sharing.sharesLoaded = true;
+ });
+ afterEach(function() {
+ OC.Share.statuses = {};
+ OCA.Sharing.sharesLoaded = false;
+ });
it('do not shows share text when not shared', function() {
var $action, $tr;
OC.Share.statuses = {};
diff --git a/apps/files_sharing/tests/updater.php b/apps/files_sharing/tests/updater.php
index cdb44068254..5ec53488702 100644
--- a/apps/files_sharing/tests/updater.php
+++ b/apps/files_sharing/tests/updater.php
@@ -217,4 +217,40 @@ class Test_Files_Sharing_Updater extends Test_Files_Sharing_Base {
}
+ /**
+ * if a folder gets renamed all children mount points should be renamed too
+ */
+ function testRename() {
+
+ $fileinfo = \OC\Files\Filesystem::getFileInfo($this->folder);
+ $result = \OCP\Share::shareItem('folder', $fileinfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2, 31);
+ $this->assertTrue($result);
+
+ $this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
+
+ // make sure that the shared folder exists
+ $this->assertTrue(\OC\Files\Filesystem::file_exists($this->folder));
+
+ \OC\Files\Filesystem::mkdir('oldTarget');
+ \OC\Files\Filesystem::mkdir('oldTarget/subfolder');
+ \OC\Files\Filesystem::mkdir('newTarget');
+
+ \OC\Files\Filesystem::rename($this->folder, 'oldTarget/subfolder/' . $this->folder);
+
+ // re-login to make sure that the new mount points are initialized
+ $this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
+
+ \OC\Files\Filesystem::rename('/oldTarget', '/newTarget/oldTarget');
+
+ // re-login to make sure that the new mount points are initialized
+ $this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
+
+ $this->assertTrue(\OC\Files\Filesystem::file_exists('/newTarget/oldTarget/subfolder/' . $this->folder));
+
+ // cleanup
+ $this->loginHelper(self::TEST_FILES_SHARING_API_USER1);
+ $result = \OCP\Share::unshare('folder', $fileinfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
+ $this->assertTrue($result);
+ }
+
}
diff --git a/core/css/mobile.css b/core/css/mobile.css
index 2003e0a7f45..bdca29d888a 100644
--- a/core/css/mobile.css
+++ b/core/css/mobile.css
@@ -82,6 +82,11 @@
background-color: #fff;
overflow-x: hidden !important;
}
+/* allow horizontal scrollbar in settings
+ otherwise user management is not usable on mobile */
+#body-settings #app-content {
+ overflow-x: auto !important;
+}
#app-navigation-toggle {
position: fixed;
@@ -129,6 +134,15 @@ table.multiselect thead {
}
+/* prevent overflow in user management controls bar */
+#usersearchform {
+ display: none;
+}
+#body-settings #controls {
+ min-width: 768px !important;
+}
+
+
/* fix controls bar jumping when navigation is slid out */
.snapjs-left #app-navigation-toggle,
.snapjs-left #controls {
diff --git a/core/js/share.js b/core/js/share.js
index 5763664c5de..62271a43ceb 100644
--- a/core/js/share.js
+++ b/core/js/share.js
@@ -29,10 +29,13 @@ OC.Share={
* files "Share" icon to "Shared" according to their share status and
* share type.
*
+ * If a callback is specified, the update step is skipped.
+ *
* @param itemType item type
* @param fileList file list instance, defaults to OCA.Files.App.fileList
+ * @param callback function to call after the shares were loaded
*/
- loadIcons:function(itemType, fileList) {
+ loadIcons:function(itemType, fileList, callback) {
// Load all share icons
$.get(
OC.filePath('core', 'ajax', 'share.php'),
@@ -45,7 +48,11 @@ OC.Share={
$.each(result.data, function(item, data) {
OC.Share.statuses[item] = data;
});
- OC.Share.updateIcons(itemType, fileList);
+ if (_.isFunction(callback)) {
+ callback(OC.Share.statuses);
+ } else {
+ OC.Share.updateIcons(itemType, fileList);
+ }
}
}
);
diff --git a/lib/private/appframework/http/dispatcher.php b/lib/private/appframework/http/dispatcher.php
index fa8d3c47a8b..7f2717951a5 100644
--- a/lib/private/appframework/http/dispatcher.php
+++ b/lib/private/appframework/http/dispatcher.php
@@ -145,7 +145,7 @@ class Dispatcher {
) {
$value = false;
- } elseif(in_array($type, $types)) {
+ } elseif($value !== null && in_array($type, $types)) {
settype($value, $type);
}
diff --git a/lib/private/connector/sabre/objecttree.php b/lib/private/connector/sabre/objecttree.php
index 54596db3c47..d7a96cfc88e 100644
--- a/lib/private/connector/sabre/objecttree.php
+++ b/lib/private/connector/sabre/objecttree.php
@@ -11,6 +11,8 @@ namespace OC\Connector\Sabre;
use OC\Files\FileInfo;
use OC\Files\Filesystem;
use OC\Files\Mount\MoveableMount;
+use OCP\Files\StorageInvalidException;
+use OCP\Files\StorageNotAvailableException;
class ObjectTree extends \Sabre\DAV\ObjectTree {
@@ -83,7 +85,13 @@ class ObjectTree extends \Sabre\DAV\ObjectTree {
}
} else {
// read from cache
- $info = $this->fileView->getFileInfo($path);
+ try {
+ $info = $this->fileView->getFileInfo($path);
+ } catch (StorageNotAvailableException $e) {
+ throw new \Sabre\DAV\Exception\ServiceUnavailable('Storage not available');
+ } catch (StorageInvalidException $e){
+ throw new \Sabre\DAV\Exception\NotFound('Storage ' . $path . ' is invalid');
+ }
}
if (!$info) {
diff --git a/lib/private/files/mount/manager.php b/lib/private/files/mount/manager.php
index 45a9f339fba..e5180cfe173 100644
--- a/lib/private/files/mount/manager.php
+++ b/lib/private/files/mount/manager.php
@@ -27,6 +27,10 @@ class Manager {
* @param string $mountPoint
*/
public function removeMount($mountPoint) {
+ $mountPoint = Filesystem::normalizePath($mountPoint);
+ if (strlen($mountPoint) > 1) {
+ $mountPoint .= '/';
+ }
unset($this->mounts[$mountPoint]);
}
diff --git a/lib/private/files/storage/dav.php b/lib/private/files/storage/dav.php
index 8b97f750204..726688fe444 100644
--- a/lib/private/files/storage/dav.php
+++ b/lib/private/files/storage/dav.php
@@ -8,6 +8,9 @@
namespace OC\Files\Storage;
+use OCP\Files\StorageNotAvailableException;
+use Sabre\DAV\Exception;
+
class DAV extends \OC\Files\Storage\Common {
protected $password;
protected $user;
@@ -463,29 +466,36 @@ class DAV extends \OC\Files\Storage\Common {
*
* @param string $path
* @param int $time
+ * @throws \OCP\Files\StorageNotAvailableException
* @return bool
*/
public function hasUpdated($path, $time) {
$this->init();
- $response = $this->client->propfind($this->encodePath($path), array(
- '{DAV:}getlastmodified',
- '{DAV:}getetag',
- '{http://owncloud.org/ns}permissions'
- ));
- if (isset($response['{DAV:}getetag'])) {
- $cachedData = $this->getCache()->get($path);
- $etag = trim($response['{DAV:}getetag'], '"');
- if ($cachedData['etag'] !== $etag) {
- return true;
- } else if (isset($response['{http://owncloud.org/ns}permissions'])) {
- $permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
- return $permissions !== $cachedData['permissions'];
+ try {
+ $response = $this->client->propfind($this->encodePath($path), array(
+ '{DAV:}getlastmodified',
+ '{DAV:}getetag',
+ '{http://owncloud.org/ns}permissions'
+ ));
+ if (isset($response['{DAV:}getetag'])) {
+ $cachedData = $this->getCache()->get($path);
+ $etag = trim($response['{DAV:}getetag'], '"');
+ if ($cachedData['etag'] !== $etag) {
+ return true;
+ } else if (isset($response['{http://owncloud.org/ns}permissions'])) {
+ $permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
+ return $permissions !== $cachedData['permissions'];
+ } else {
+ return false;
+ }
} else {
- return false;
+ $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
+ return $remoteMtime > $time;
}
- } else {
- $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
- return $remoteMtime > $time;
+ } catch (Exception\NotFound $e) {
+ return false;
+ } catch (Exception $e) {
+ throw new StorageNotAvailableException();
}
}
}
diff --git a/lib/public/files/storageinvalidexception.php b/lib/public/files/storageinvalidexception.php
new file mode 100644
index 00000000000..7419ccc1d11
--- /dev/null
+++ b/lib/public/files/storageinvalidexception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+/**
+ * Public interface of ownCloud for apps to use.
+ * Files/AlreadyExistsException class
+ */
+
+// use OCP namespace for all classes that are considered public.
+// This means that they should be used by apps instead of the internal ownCloud classes
+namespace OCP\Files;
+
+/**
+ * Storage has invalid configuration
+ */
+class StorageInvalidException extends \Exception {
+}
diff --git a/lib/public/files/storagenotavailableexception.php b/lib/public/files/storagenotavailableexception.php
new file mode 100644
index 00000000000..b526cb4ea0f
--- /dev/null
+++ b/lib/public/files/storagenotavailableexception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+/**
+ * Public interface of ownCloud for apps to use.
+ * Files/AlreadyExistsException class
+ */
+
+// use OCP namespace for all classes that are considered public.
+// This means that they should be used by apps instead of the internal ownCloud classes
+namespace OCP\Files;
+
+/**
+ * Storage is temporarily not available
+ */
+class StorageNotAvailableException extends \Exception {
+}
diff --git a/public.php b/public.php
index 1f858fd073d..2ac082dba57 100644
--- a/public.php
+++ b/public.php
@@ -17,7 +17,7 @@ try {
if (!$pathInfo && !isset($_GET['service'])) {
header('HTTP/1.0 404 Not Found');
exit;
- } elseif ($_GET['service']) {
+ } elseif (isset($_GET['service'])) {
$service = $_GET['service'];
} else {
$pathInfo = trim($pathInfo, '/');