diff options
Diffstat (limited to 'apps')
232 files changed, 4637 insertions, 351 deletions
diff --git a/apps/comments/css/comments.css b/apps/comments/css/comments.css index b86ed38efe7..57a731184a0 100644 --- a/apps/comments/css/comments.css +++ b/apps/comments/css/comments.css @@ -14,7 +14,7 @@ #commentsTabView .newCommentForm .message { width: 90%; - resize: none; + resize: vertical; } #commentsTabView .newCommentForm .submitLoading { @@ -31,6 +31,49 @@ line-height: 28px; } +#commentsTabView .comment { + position: relative; + z-index: 1; +} + +#commentsTabView .comment.collapsed .message { + white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */ + white-space: -webkit-pre-wrap; /*Chrome & Safari */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* css-3 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ + word-break: break-all; + white-space: normal; +} + +#commentsTabView .comment.collapsed .message { + max-height: 70px; + overflow: hidden; +} + +#commentsTabView .comment .message-overlay { + display: none; +} + +#commentsTabView .comment.collapsed .message-overlay { + display: block; + position: absolute; + z-index: 2; + height: 50px; + pointer-events: none; + left: 0; + right: 0; + bottom: 0; + background: -moz-linear-gradient(rgba(255,255,255,0), rgba(255,255,255,1)); + background: -webkit-linear-gradient(rgba(255,255,255,0), rgba(255,255,255,1)); + background: -o-linear-gradient(rgba(255,255,255,0), rgba(255,255,255,1)); + background: -ms-linear-gradient(rgba(255,255,255,0), rgba(255,255,255,1)); + background: linear-gradient(rgba(255,255,255,0), rgba(255,255,255,1)); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#00FFFFFF', endColorstr='#FFFFFFFF'); + background-repeat: no-repeat; +} + #commentsTabView .authorRow>div { display: inline-block; vertical-align: middle; @@ -77,6 +120,14 @@ visibility: hidden; } +#commentsTabView .message.error { + color: #e9322d; + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} + .app-files .action-comment>img { margin-right: 5px; } diff --git a/apps/comments/js/commentstabview.js b/apps/comments/js/commentstabview.js index 8faf98b35ab..1326d544b1a 100644 --- a/apps/comments/js/commentstabview.js +++ b/apps/comments/js/commentstabview.js @@ -41,7 +41,7 @@ '</div>'; var COMMENT_TEMPLATE = - '<li class="comment{{#if isUnread}} unread{{/if}}" data-id="{{id}}">' + + '<li class="comment{{#if isUnread}} unread{{/if}}{{#if isLong}} collapsed{{/if}}" data-id="{{id}}">' + ' <div class="authorRow">' + ' {{#if avatarEnabled}}' + ' <div class="avatar" {{#if actorId}}data-username="{{actorId}}"{{/if}}> </div>' + @@ -53,6 +53,9 @@ ' <div class="date has-tooltip" title="{{altDate}}">{{date}}</div>' + ' </div>' + ' <div class="message">{{{formattedMessage}}}</div>' + + '{{#if isLong}}' + + ' <div class="message-overlay"></div>' + + '{{/if}}' + '</li>'; /** @@ -68,9 +71,12 @@ 'click .showMore': '_onClickShowMore', 'click .action.edit': '_onClickEditComment', 'click .action.delete': '_onClickDeleteComment', - 'click .cancel': '_onClickCloseComment' + 'click .cancel': '_onClickCloseComment', + 'click .comment': '_onClickComment' }, + _commentMaxLength: 1000, + initialize: function() { OCA.Files.DetailTabView.prototype.initialize.apply(this, arguments); this.collection = new OCA.Comments.CommentCollection(); @@ -80,7 +86,10 @@ this._avatarsEnabled = !!OC.config.enable_avatars; + this._commentMaxThreshold = this._commentMaxLength * 0.9; + // TODO: error handling + _.bindAll(this, '_onTypeComment'); }, template: function(params) { @@ -119,7 +128,8 @@ params = _.extend({ avatarEnabled: this._avatarsEnabled, editTooltip: t('comments', 'Edit comment'), - isUserAuthor: OC.getCurrentUser().uid === params.actorId + isUserAuthor: OC.getCurrentUser().uid === params.actorId, + isLong: this._isLong(params.message) }, params); if (params.actorType === 'deleted_users') { @@ -162,6 +172,7 @@ this.$el.find('.avatar').avatar(OC.getCurrentUser().uid, 28); } this.delegateEvents(); + this.$el.find('textarea').on('keyup input change', this._onTypeComment); }, _formatItem: function(commentModel) { @@ -258,10 +269,11 @@ submitText: t('comments', 'Save') }, commentToEdit.attributes))); - $comment.addClass('hidden'); + $comment.addClass('hidden').removeClass('collapsed'); // spawn form $comment.after($formRow); $formRow.data('commentEl', $comment); + $formRow.find('textarea').on('keyup input change', this._onTypeComment); // copy avatar element from original to avoid flickering $formRow.find('.avatar').replaceWith($comment.find('.avatar').clone()); @@ -270,6 +282,35 @@ return false; }, + _onTypeComment: function(ev) { + var $field = $(ev.target); + var len = $field.val().length; + var $submitButton = $field.data('submitButtonEl'); + if (!$submitButton) { + $submitButton = $field.closest('form').find('.submit'); + $field.data('submitButtonEl', $submitButton); + } + $field.tooltip('hide'); + if (len > this._commentMaxThreshold) { + $field.attr('data-original-title', t('comments', 'Allowed characters {count} of {max}', {count: len, max: this._commentMaxLength})); + $field.tooltip({trigger: 'manual'}); + $field.tooltip('show'); + $field.addClass('error'); + } + + var limitExceeded = (len > this._commentMaxLength); + $field.toggleClass('error', limitExceeded); + $submitButton.prop('disabled', limitExceeded); + }, + + _onClickComment: function(ev) { + var $row = $(ev.target); + if (!$row.is('.comment')) { + $row = $row.closest('.comment'); + } + $row.removeClass('collapsed'); + }, + _onClickCloseComment: function(ev) { ev.preventDefault(); var $row = $(ev.target).closest('.comment'); @@ -318,7 +359,7 @@ var message = $textArea.val().trim(); e.preventDefault(); - if (!message.length) { + if (!message.length || message.length > this._commentMaxLength) { return; } @@ -378,6 +419,14 @@ } return false; + }, + + /** + * Returns whether the given message is long and needs + * collapsing + */ + _isLong: function(message) { + return message.length > 250 || (message.match(/\n/g) || []).length > 1; } }); diff --git a/apps/comments/tests/js/commentstabviewSpec.js b/apps/comments/tests/js/commentstabviewSpec.js index 9e986899f7d..70930da7520 100644 --- a/apps/comments/tests/js/commentstabviewSpec.js +++ b/apps/comments/tests/js/commentstabviewSpec.js @@ -25,6 +25,20 @@ describe('OCA.Comments.CommentsTabView tests', function() { var testComments; var clock; + /** + * Creates a dummy message with the given length + * + * @param {int} len length + * @return {string} message + */ + function createMessageWithLength(len) { + var bigMessage = ''; + for (var i = 0; i < len; i++) { + bigMessage += 'a'; + } + return bigMessage; + } + beforeEach(function() { clock = sinon.useFakeTimers(Date.UTC(2016, 1, 3, 10, 5, 9)); fetchStub = sinon.stub(OCA.Comments.CommentCollection.prototype, 'fetchNext'); @@ -201,7 +215,55 @@ describe('OCA.Comments.CommentsTabView tests', function() { expect(createStub.notCalled).toEqual(true); }); + it('does not create a comment if the field length is too large', function() { + var bigMessage = ''; + for (var i = 0; i < view._commentMaxLength * 2; i++) { + bigMessage += 'a'; + } + view.$el.find('.message').val(bigMessage); + view.$el.find('form').submit(); + expect(createStub.notCalled).toEqual(true); + }); + describe('limit indicator', function() { + var tooltipStub; + var $message; + var $submitButton; + + beforeEach(function() { + tooltipStub = sinon.stub($.fn, 'tooltip'); + $message = view.$el.find('.message'); + $submitButton = view.$el.find('.submit'); + }); + afterEach(function() { + tooltipStub.restore(); + }); + + it('does not displays tooltip when limit is far away', function() { + $message.val(createMessageWithLength(3)); + $message.trigger('change'); + + expect(tooltipStub.calledWith('show')).toEqual(false); + expect($submitButton.prop('disabled')).toEqual(false); + expect($message.hasClass('error')).toEqual(false); + }); + it('displays tooltip when limit is almost reached', function() { + $message.val(createMessageWithLength(view._commentMaxLength - 2)); + $message.trigger('change'); + + expect(tooltipStub.calledWith('show')).toEqual(true); + expect($submitButton.prop('disabled')).toEqual(false); + expect($message.hasClass('error')).toEqual(false); + }); + it('displays tooltip and disabled button when limit is exceeded', function() { + $message.val(createMessageWithLength(view._commentMaxLength + 2)); + $message.trigger('change'); + + expect(tooltipStub.calledWith('show')).toEqual(true); + expect($submitButton.prop('disabled')).toEqual(true); + expect($message.hasClass('error')).toEqual(true); + }); + }); }); describe('editing comments', function() { var saveStub; @@ -336,6 +398,22 @@ describe('OCA.Comments.CommentsTabView tests', function() { destroyStub.restore(); }); + it('does not submit comment if the field is empty', function() { + var $comment = view.$el.find('.comment[data-id=1]'); + $comment.find('.action.edit').click(); + $comment.find('.message').val(' '); + $comment.find('form').submit(); + + expect(saveStub.notCalled).toEqual(true); + }); + it('does not submit comment if the field length is too large', function() { + var $comment = view.$el.find('.comment[data-id=1]'); + $comment.find('.action.edit').click(); + $comment.find('.message').val(createMessageWithLength(view._commentMaxLength * 2)); + $comment.find('form').submit(); + + expect(saveStub.notCalled).toEqual(true); + }); }); describe('read marker', function() { var updateMarkerStub; diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml index f035d19d862..4f3a93dbf8b 100644 --- a/apps/dav/appinfo/info.xml +++ b/apps/dav/appinfo/info.xml @@ -14,6 +14,12 @@ <files>appinfo/v1/webdav.php</files> <webdav>appinfo/v1/webdav.php</webdav> <dav>appinfo/v2/remote.php</dav> + <!-- carddav endpoints as used before ownCloud 9.0 --> + <contacts>appinfo/v1/carddav.php</contacts> + <carddav>appinfo/v1/carddav.php</carddav> + <!-- caldav endpoints as used before ownCloud 9.0 --> + <calendar>appinfo/v1/caldav.php</calendar> + <caldav>appinfo/v1/caldav.php</caldav> </remote> <public> <webdav>appinfo/v1/publicwebdav.php</webdav> diff --git a/apps/dav/appinfo/v1/caldav.php b/apps/dav/appinfo/v1/caldav.php new file mode 100644 index 00000000000..f860ced3877 --- /dev/null +++ b/apps/dav/appinfo/v1/caldav.php @@ -0,0 +1,69 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2016, 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/> + * + */ + +// Backends +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\Connector\Sabre\Auth; +use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin; +use OCA\DAV\Connector\Sabre\MaintenancePlugin; +use OCA\DAV\Connector\Sabre\Principal; +use Sabre\CalDAV\CalendarRoot; + +$authBackend = new Auth( + \OC::$server->getSession(), + \OC::$server->getUserSession(), + 'principals/' +); +$principalBackend = new Principal( + \OC::$server->getUserManager(), + \OC::$server->getGroupManager(), + 'principals/' +); +$db = \OC::$server->getDatabaseConnection(); +$calDavBackend = new CalDavBackend($db, $principalBackend); + +// Root nodes +$principalCollection = new \Sabre\CalDAV\Principal\Collection($principalBackend); +$principalCollection->disableListing = true; // Disable listing + +$addressBookRoot = new CalendarRoot($principalBackend, $calDavBackend); +$addressBookRoot->disableListing = true; // Disable listing + +$nodes = array( + $principalCollection, + $addressBookRoot, +); + +// Fire up server +$server = new \Sabre\DAV\Server($nodes); +$server->httpRequest->setUrl(\OC::$server->getRequest()->getRequestUri()); +$server->setBaseUri($baseuri); + +// Add plugins +$server->addPlugin(new MaintenancePlugin()); +$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, 'ownCloud')); +$server->addPlugin(new \Sabre\CalDAV\Plugin()); +$server->addPlugin(new \Sabre\DAVACL\Plugin()); +$server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin()); +$server->addPlugin(new ExceptionLoggerPlugin('caldav', \OC::$server->getLogger())); + +// And off we go! +$server->exec(); diff --git a/apps/dav/appinfo/v1/carddav.php b/apps/dav/appinfo/v1/carddav.php new file mode 100644 index 00000000000..e0c79c75b72 --- /dev/null +++ b/apps/dav/appinfo/v1/carddav.php @@ -0,0 +1,70 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2016, 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/> + * + */ + +// Backends +use OCA\DAV\CardDAV\AddressBookRoot; +use OCA\DAV\CardDAV\CardDavBackend; +use OCA\DAV\Connector\Sabre\AppEnabledPlugin; +use OCA\DAV\Connector\Sabre\Auth; +use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin; +use OCA\DAV\Connector\Sabre\MaintenancePlugin; +use OCA\DAV\Connector\Sabre\Principal; +use Sabre\CardDAV\Plugin; + +$authBackend = new Auth( + \OC::$server->getSession(), + \OC::$server->getUserSession(), + 'principals/' +); +$principalBackend = new Principal( + \OC::$server->getUserManager(), + \OC::$server->getGroupManager(), + 'principals/' +); +$db = \OC::$server->getDatabaseConnection(); +$cardDavBackend = new CardDavBackend($db, $principalBackend); + +// Root nodes +$principalCollection = new \Sabre\CalDAV\Principal\Collection($principalBackend); +$principalCollection->disableListing = true; // Disable listing + +$addressBookRoot = new AddressBookRoot($principalBackend, $cardDavBackend); +$addressBookRoot->disableListing = true; // Disable listing + +$nodes = array( + $principalCollection, + $addressBookRoot, +); + +// Fire up server +$server = new \Sabre\DAV\Server($nodes); +$server->httpRequest->setUrl(\OC::$server->getRequest()->getRequestUri()); +$server->setBaseUri($baseuri); +// Add plugins +$server->addPlugin(new MaintenancePlugin()); +$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, 'ownCloud')); +$server->addPlugin(new Plugin()); +$server->addPlugin(new \Sabre\DAVACL\Plugin()); +$server->addPlugin(new \Sabre\CardDAV\VCFExportPlugin()); +$server->addPlugin(new ExceptionLoggerPlugin('carddav', \OC::$server->getLogger())); + +// And off we go! +$server->exec(); diff --git a/apps/dav/appinfo/v1/webdav.php b/apps/dav/appinfo/v1/webdav.php index d75c3526bdd..3d3e51e84bc 100644 --- a/apps/dav/appinfo/v1/webdav.php +++ b/apps/dav/appinfo/v1/webdav.php @@ -40,7 +40,8 @@ $serverFactory = new \OCA\DAV\Connector\Sabre\ServerFactory( // Backends $authBackend = new \OCA\DAV\Connector\Sabre\Auth( \OC::$server->getSession(), - \OC::$server->getUserSession() + \OC::$server->getUserSession(), + 'principals/' ); $requestUri = \OC::$server->getRequest()->getRequestUri(); diff --git a/apps/dav/lib/caldav/caldavbackend.php b/apps/dav/lib/caldav/caldavbackend.php index 3aa493e5087..52b4812b05b 100644 --- a/apps/dav/lib/caldav/caldavbackend.php +++ b/apps/dav/lib/caldav/caldavbackend.php @@ -106,7 +106,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription public function __construct(\OCP\IDBConnection $db, Principal $principalBackend) { $this->db = $db; $this->principalBackend = $principalBackend; - $this->sharingBackend = new Backend($this->db, 'calendar'); + $this->sharingBackend = new Backend($this->db, $principalBackend, 'calendar'); } /** @@ -172,7 +172,9 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $calendar[$xmlName] = $row[$dbName]; } - $calendars[$calendar['id']] = $calendar; + if (!isset($calendars[$calendar['id']])) { + $calendars[$calendar['id']] = $calendar; + } } $stmt->closeCursor(); @@ -221,7 +223,9 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $calendar[$xmlName] = $row[$dbName]; } - $calendars[$calendar['id']] = $calendar; + if (!isset($calendars[$calendar['id']])) { + $calendars[$calendar['id']] = $calendar; + } } $result->closeCursor(); diff --git a/apps/dav/lib/carddav/addressbookroot.php b/apps/dav/lib/carddav/addressbookroot.php index 2680135dec2..99c36c2e767 100644 --- a/apps/dav/lib/carddav/addressbookroot.php +++ b/apps/dav/lib/carddav/addressbookroot.php @@ -40,6 +40,9 @@ class AddressBookRoot extends \Sabre\CardDAV\AddressBookRoot { function getName() { + if ($this->principalPrefix === 'principals') { + return parent::getName(); + } // Grabbing all the components of the principal path. $parts = explode('/', $this->principalPrefix); diff --git a/apps/dav/lib/carddav/carddavbackend.php b/apps/dav/lib/carddav/carddavbackend.php index 9ca166c22a2..c4f29b39d0d 100644 --- a/apps/dav/lib/carddav/carddavbackend.php +++ b/apps/dav/lib/carddav/carddavbackend.php @@ -72,7 +72,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { public function __construct(IDBConnection $db, Principal $principalBackend) { $this->db = $db; $this->principalBackend = $principalBackend; - $this->sharingBackend = new Backend($this->db, 'addressbook'); + $this->sharingBackend = new Backend($this->db, $principalBackend, 'addressbook'); } /** @@ -93,11 +93,11 @@ class CardDavBackend implements BackendInterface, SyncSupport { * @return array */ function getAddressBooksForUser($principalUri) { + $principalUri = $this->convertPrincipal($principalUri, true); $query = $this->db->getQueryBuilder(); $query->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken']) ->from('addressbooks') - ->where($query->expr()->eq('principaluri', $query->createParameter('principaluri'))) - ->setParameter('principaluri', $principalUri); + ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri))); $addressBooks = []; @@ -106,7 +106,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { $addressBooks[$row['id']] = [ 'id' => $row['id'], 'uri' => $row['uri'], - 'principaluri' => $row['principaluri'], + 'principaluri' => $this->convertPrincipal($row['principaluri'], false), '{DAV:}displayname' => $row['displayname'], '{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], '{http://calendarserver.org/ns/}getctag' => $row['synctoken'], @@ -133,17 +133,19 @@ class CardDavBackend implements BackendInterface, SyncSupport { list(, $name) = URLUtil::splitPath($row['principaluri']); $uri = $row['uri'] . '_shared_by_' . $name; $displayName = $row['displayname'] . "($name)"; - $addressBooks[$row['id']] = [ - 'id' => $row['id'], - 'uri' => $uri, - 'principaluri' => $principalUri, - '{DAV:}displayname' => $displayName, - '{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], - '{http://calendarserver.org/ns/}getctag' => $row['synctoken'], - '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', - '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $row['principaluri'], - '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => $row['access'] === self::ACCESS_READ, - ]; + if (!isset($addressBooks[$row['id']])) { + $addressBooks[$row['id']] = [ + 'id' => $row['id'], + 'uri' => $uri, + 'principaluri' => $principalUri, + '{DAV:}displayname' => $displayName, + '{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], + '{http://calendarserver.org/ns/}getctag' => $row['synctoken'], + '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', + '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $row['principaluri'], + '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => $row['access'] === self::ACCESS_READ, + ]; + } } $result->closeCursor(); @@ -919,4 +921,15 @@ class CardDavBackend implements BackendInterface, SyncSupport { public function applyShareAcl($addressBookId, $acl) { return $this->sharingBackend->applyShareAcl($addressBookId, $acl); } + + private function convertPrincipal($principalUri, $toV2) { + if ($this->principalBackend->getPrincipalPrefix() === 'principals') { + list(, $name) = URLUtil::splitPath($principalUri); + if ($toV2 === true) { + return "principals/users/$name"; + } + return "principals/$name"; + } + return $principalUri; + } } diff --git a/apps/dav/lib/comments/commentsplugin.php b/apps/dav/lib/comments/commentsplugin.php index 282c14df1e8..56d94cc33e9 100644 --- a/apps/dav/lib/comments/commentsplugin.php +++ b/apps/dav/lib/comments/commentsplugin.php @@ -116,6 +116,11 @@ class CommentsPlugin extends ServerPlugin { $data, $request->getHeader('Content-Type') ); + + // update read marker for the current user/poster to avoid + // having their own comments marked as unread + $node->setReadMarker(null); + $url = $request->getUrl() . '/' . urlencode($comment->getId()); $response->setHeader('Content-Location', $url); diff --git a/apps/dav/lib/connector/sabre/auth.php b/apps/dav/lib/connector/sabre/auth.php index cc679e44dbe..a046e078482 100644 --- a/apps/dav/lib/connector/sabre/auth.php +++ b/apps/dav/lib/connector/sabre/auth.php @@ -49,12 +49,14 @@ class Auth extends AbstractBasic { /** * @param ISession $session * @param IUserSession $userSession + * @param string $principalPrefix */ public function __construct(ISession $session, - IUserSession $userSession) { + IUserSession $userSession, + $principalPrefix = 'principals/users/') { $this->session = $session; $this->userSession = $userSession; - $this->principalPrefix = 'principals/users/'; + $this->principalPrefix = $principalPrefix; } /** diff --git a/apps/dav/lib/connector/sabre/filesreportplugin.php b/apps/dav/lib/connector/sabre/filesreportplugin.php new file mode 100644 index 00000000000..5bdd7a71ddc --- /dev/null +++ b/apps/dav/lib/connector/sabre/filesreportplugin.php @@ -0,0 +1,321 @@ +<?php +/** + * @author Vincent Petry <pvince81@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\DAV\Connector\Sabre; + +use OC\Files\View; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\Exception\PreconditionFailed; +use Sabre\DAV\Exception\ReportNotSupported; +use Sabre\DAV\Exception\BadRequest; +use Sabre\DAV\ServerPlugin; +use Sabre\DAV\Tree; +use Sabre\DAV\Xml\Element\Response; +use Sabre\DAV\Xml\Response\MultiStatus; +use Sabre\DAV\PropFind; +use OCP\SystemTag\ISystemTagObjectMapper; +use OCP\IUserSession; +use OCP\Files\Folder; +use OCP\IGroupManager; +use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\TagNotFoundException; + +class FilesReportPlugin extends ServerPlugin { + + // namespace + const NS_OWNCLOUD = 'http://owncloud.org/ns'; + const REPORT_NAME = '{http://owncloud.org/ns}filter-files'; + const SYSTEMTAG_PROPERTYNAME = '{http://owncloud.org/ns}systemtag'; + + /** + * Reference to main server object + * + * @var \Sabre\DAV\Server + */ + private $server; + + /** + * @var Tree + */ + private $tree; + + /** + * @var View + */ + private $fileView; + + /** + * @var ISystemTagManager + */ + private $tagManager; + + /** + * @var ISystemTagObjectMapper + */ + private $tagMapper; + + /** + * @var IUserSession + */ + private $userSession; + + /** + * @var IGroupManager + */ + private $groupManager; + + /** + * @var Folder + */ + private $userFolder; + + /** + * @param Tree $tree + * @param View $view + */ + public function __construct(Tree $tree, + View $view, + ISystemTagManager $tagManager, + ISystemTagObjectMapper $tagMapper, + IUserSession $userSession, + IGroupManager $groupManager, + Folder $userFolder + ) { + $this->tree = $tree; + $this->fileView = $view; + $this->tagManager = $tagManager; + $this->tagMapper = $tagMapper; + $this->userSession = $userSession; + $this->groupManager = $groupManager; + $this->userFolder = $userFolder; + } + + /** + * This initializes the plugin. + * + * This function is called by \Sabre\DAV\Server, after + * addPlugin is called. + * + * This method should set up the required event subscriptions. + * + * @param \Sabre\DAV\Server $server + * @return void + */ + public function initialize(\Sabre\DAV\Server $server) { + + $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc'; + + $this->server = $server; + $this->server->on('report', array($this, 'onReport')); + } + + /** + * Returns a list of reports this plugin supports. + * + * This will be used in the {DAV:}supported-report-set property. + * + * @param string $uri + * @return array + */ + public function getSupportedReportSet($uri) { + return [self::REPORT_NAME]; + } + + /** + * REPORT operations to look for files + * + * @param string $reportName + * @param [] $report + * @param string $uri + * @return bool + * @throws NotFound + * @throws ReportNotSupported + */ + public function onReport($reportName, $report, $uri) { + $reportTargetNode = $this->server->tree->getNodeForPath($uri); + if (!$reportTargetNode instanceof Directory || $reportName !== self::REPORT_NAME) { + throw new ReportNotSupported(); + } + + $ns = '{' . $this::NS_OWNCLOUD . '}'; + $requestedProps = []; + $filterRules = []; + + // parse report properties and gather filter info + foreach ($report as $reportProps) { + $name = $reportProps['name']; + if ($name === $ns . 'filter-rules') { + $filterRules = $reportProps['value']; + } else if ($name === '{DAV:}prop') { + // propfind properties + foreach ($reportProps['value'] as $propVal) { + $requestedProps[] = $propVal['name']; + } + } + } + + if (empty($filterRules)) { + // an empty filter would return all existing files which would be slow + throw new BadRequest('Missing filter-rule block in request'); + } + + // gather all file ids matching filter + try { + $resultFileIds = $this->processFilterRules($filterRules); + } catch (TagNotFoundException $e) { + throw new PreconditionFailed('Cannot filter by non-existing tag', 0, $e); + } + + // find sabre nodes by file id, restricted to the root node path + $results = $this->findNodesByFileIds($reportTargetNode, $resultFileIds); + + $responses = $this->prepareResponses($requestedProps, $results); + + $xml = $this->server->xml->write( + '{DAV:}multistatus', + new MultiStatus($responses) + ); + + $this->server->httpResponse->setStatus(207); + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $this->server->httpResponse->setBody($xml); + + return false; + } + + /** + * Find file ids matching the given filter rules + * + * @param array $filterRules + * @return array array of unique file id results + * + * @throws TagNotFoundException whenever a tag was not found + */ + public function processFilterRules($filterRules) { + $ns = '{' . $this::NS_OWNCLOUD . '}'; + $resultFileIds = []; + $systemTagIds = []; + foreach ($filterRules as $filterRule) { + if ($filterRule['name'] === $ns . 'systemtag') { + $systemTagIds[] = $filterRule['value']; + } + } + + // check user permissions, if applicable + if (!$this->isAdmin()) { + // check visibility/permission + $tags = $this->tagManager->getTagsByIds($systemTagIds); + $unknownTagIds = []; + foreach ($tags as $tag) { + if (!$tag->isUserVisible()) { + $unknownTagIds[] = $tag->getId(); + } + } + + if (!empty($unknownTagIds)) { + throw new TagNotFoundException('Tag with ids ' . implode(', ', $unknownTagIds) . ' not found'); + } + } + + // fetch all file ids and intersect them + foreach ($systemTagIds as $systemTagId) { + $fileIds = $this->tagMapper->getObjectIdsForTags($systemTagId, 'files'); + + if (empty($resultFileIds)) { + $resultFileIds = $fileIds; + } else { + $resultFileIds = array_intersect($resultFileIds, $fileIds); + } + } + return $resultFileIds; + } + + /** + * Prepare propfind response for the given nodes + * + * @param string[] $requestedProps requested properties + * @param Node[] nodes nodes for which to fetch and prepare responses + * @return Response[] + */ + public function prepareResponses($requestedProps, $nodes) { + $responses = []; + foreach ($nodes as $node) { + $propFind = new PropFind($node->getPath(), $requestedProps); + + $this->server->getPropertiesByNode($propFind, $node); + // copied from Sabre Server's getPropertiesForPath + $result = $propFind->getResultForMultiStatus(); + $result['href'] = $propFind->getPath(); + + $resourceType = $this->server->getResourceTypeForNode($node); + if (in_array('{DAV:}collection', $resourceType) || in_array('{DAV:}principal', $resourceType)) { + $result['href'] .= '/'; + } + + $responses[] = new Response( + rtrim($this->server->getBaseUri(), '/') . $node->getPath(), + $result, + 200 + ); + } + return $responses; + } + + /** + * Find Sabre nodes by file ids + * + * @param Node $rootNode root node for search + * @param array $fileIds file ids + * @return Node[] array of Sabre nodes + */ + public function findNodesByFileIds($rootNode, $fileIds) { + $folder = $this->userFolder; + if (trim($rootNode->getPath(), '/') !== '') { + $folder = $folder->get($rootNode->getPath()); + } + + $results = []; + foreach ($fileIds as $fileId) { + $entry = $folder->getById($fileId); + if ($entry) { + $entry = current($entry); + if ($entry instanceof \OCP\Files\File) { + $results[] = new File($this->fileView, $entry); + } else if ($entry instanceof \OCP\Files\Folder) { + $results[] = new Directory($this->fileView, $entry); + } + } + } + + return $results; + } + + /** + * Returns whether the currently logged in user is an administrator + */ + private function isAdmin() { + $user = $this->userSession->getUser(); + if ($user !== null) { + return $this->groupManager->isAdmin($user->getUID()); + } + return false; + } +} diff --git a/apps/dav/lib/connector/sabre/principal.php b/apps/dav/lib/connector/sabre/principal.php index 5f02d1271df..4f26390e3cc 100644 --- a/apps/dav/lib/connector/sabre/principal.php +++ b/apps/dav/lib/connector/sabre/principal.php @@ -46,12 +46,20 @@ class Principal implements BackendInterface { /** @var IGroupManager */ private $groupManager; + /** @var string */ + private $principalPrefix; + /** * @param IUserManager $userManager + * @param IGroupManager $groupManager + * @param string $principalPrefix */ - public function __construct(IUserManager $userManager, IGroupManager $groupManager) { + public function __construct(IUserManager $userManager, + IGroupManager $groupManager, + $principalPrefix = 'principals/users/') { $this->userManager = $userManager; $this->groupManager = $groupManager; + $this->principalPrefix = trim($principalPrefix, '/'); } /** @@ -70,7 +78,7 @@ class Principal implements BackendInterface { public function getPrincipalsByPrefix($prefixPath) { $principals = []; - if ($prefixPath === 'principals/users') { + if ($prefixPath === $this->principalPrefix) { foreach($this->userManager->search('') as $user) { $principals[] = $this->userToPrincipal($user); } @@ -88,20 +96,15 @@ class Principal implements BackendInterface { * @return array */ public function getPrincipalByPath($path) { - $elements = explode('/', $path); - if ($elements[0] !== 'principals') { - return null; - } - if ($elements[1] !== 'users') { - return null; - } - $name = $elements[2]; - $user = $this->userManager->get($name); + list($prefix, $name) = URLUtil::splitPath($path); - if (!is_null($user)) { - return $this->userToPrincipal($user); - } + if ($prefix === $this->principalPrefix) { + $user = $this->userManager->get($name); + if (!is_null($user)) { + return $this->userToPrincipal($user); + } + } return null; } @@ -132,7 +135,7 @@ class Principal implements BackendInterface { public function getGroupMembership($principal) { list($prefix, $name) = URLUtil::splitPath($principal); - if ($prefix === 'principals/users') { + if ($prefix === $this->principalPrefix) { $user = $this->userManager->get($name); if (!$user) { throw new Exception('Principal not found'); @@ -141,11 +144,9 @@ class Principal implements BackendInterface { $groups = $this->groupManager->getUserGroups($user); $groups = array_map(function($group) { /** @var IGroup $group */ - return 'principals/groups/' . $group->getGID(); + return $this->principalPrefix . '/' . $group->getGID(); }, $groups); - $groups[]= 'principals/users/'.$name.'/calendar-proxy-read'; - $groups[]= 'principals/users/'.$name.'/calendar-proxy-write'; return $groups; } return []; @@ -200,7 +201,7 @@ class Principal implements BackendInterface { $userId = $user->getUID(); $displayName = $user->getDisplayName(); $principal = [ - 'uri' => "principals/users/$userId", + 'uri' => $this->principalPrefix . '/' . $userId, '{DAV:}displayname' => is_null($displayName) ? $userId : $displayName, ]; @@ -212,4 +213,8 @@ class Principal implements BackendInterface { return $principal; } + public function getPrincipalPrefix() { + return $this->principalPrefix; + } + } diff --git a/apps/dav/lib/connector/sabre/serverfactory.php b/apps/dav/lib/connector/sabre/serverfactory.php index fa4fda46870..9a828787a0d 100644 --- a/apps/dav/lib/connector/sabre/serverfactory.php +++ b/apps/dav/lib/connector/sabre/serverfactory.php @@ -115,7 +115,7 @@ class ServerFactory { // wait with registering these until auth is handled and the filesystem is setup $server->on('beforeMethod', function () use ($server, $objectTree, $viewCallBack) { // ensure the skeleton is copied - \OC::$server->getUserFolder(); + $userFolder = \OC::$server->getUserFolder(); /** @var \OC\Files\View $view */ $view = $viewCallBack(); @@ -135,6 +135,15 @@ class ServerFactory { if($this->userSession->isLoggedIn()) { $server->addPlugin(new \OCA\DAV\Connector\Sabre\TagsPlugin($objectTree, $this->tagManager)); $server->addPlugin(new \OCA\DAV\Connector\Sabre\CommentPropertiesPlugin(\OC::$server->getCommentsManager(), $this->userSession)); + $server->addPlugin(new \OCA\DAV\Connector\Sabre\FilesReportPlugin( + $objectTree, + $view, + \OC::$server->getSystemTagManager(), + \OC::$server->getSystemTagObjectMapper(), + $this->userSession, + \OC::$server->getGroupManager(), + $userFolder + )); // custom properties plugin must be the last one $server->addPlugin( new \Sabre\DAV\PropertyStorage\Plugin( diff --git a/apps/dav/lib/dav/sharing/backend.php b/apps/dav/lib/dav/sharing/backend.php index 0b28891fbc4..7556706b6d0 100644 --- a/apps/dav/lib/dav/sharing/backend.php +++ b/apps/dav/lib/dav/sharing/backend.php @@ -24,27 +24,30 @@ namespace OCA\DAV\DAV\Sharing; +use OCA\DAV\Connector\Sabre\Principal; use OCP\IDBConnection; class Backend { /** @var IDBConnection */ private $db; + /** @var Principal */ + private $principalBackend; + /** @var string */ + private $resourceType; const ACCESS_OWNER = 1; const ACCESS_READ_WRITE = 2; const ACCESS_READ = 3; - /** @var string */ - private $resourceType; - /** - * CardDavBackend constructor. - * * @param IDBConnection $db + * @param Principal $principalBackend + * @param string $resourceType */ - public function __construct(IDBConnection $db, $resourceType) { + public function __construct(IDBConnection $db, Principal $principalBackend, $resourceType) { $this->db = $db; + $this->principalBackend = $principalBackend; $this->resourceType = $resourceType; } @@ -141,6 +144,7 @@ class Backend { * * readOnly - boolean * * summary - Optional, a description for the share * + * @param int $resourceId * @return array */ public function getShares($resourceId) { @@ -153,9 +157,10 @@ class Backend { $shares = []; while($row = $result->fetch()) { + $p = $this->principalBackend->getPrincipalByPath($row['principaluri']); $shares[]= [ 'href' => "principal:${row['principaluri']}", -// 'commonName' => isset($p['{DAV:}displayname']) ? $p['{DAV:}displayname'] : '', + 'commonName' => isset($p['{DAV:}displayname']) ? $p['{DAV:}displayname'] : '', 'status' => 1, 'readOnly' => ($row['access'] == self::ACCESS_READ), '{'.\OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD.'}principal' => $row['principaluri'] diff --git a/apps/dav/tests/unit/comments/commentsplugin.php b/apps/dav/tests/unit/comments/commentsplugin.php index bd0b56fc650..9822137bbea 100644 --- a/apps/dav/tests/unit/comments/commentsplugin.php +++ b/apps/dav/tests/unit/comments/commentsplugin.php @@ -92,6 +92,10 @@ class CommentsPlugin extends \Test\TestCase { ->method('getId') ->will($this->returnValue('42')); + $node->expects($this->once()) + ->method('setReadMarker') + ->with(null); + $this->commentsManager->expects($this->once()) ->method('create') ->with('users', 'alice', 'files', '42') diff --git a/apps/dav/tests/unit/connector/sabre/filesreportplugin.php b/apps/dav/tests/unit/connector/sabre/filesreportplugin.php new file mode 100644 index 00000000000..853e52f5039 --- /dev/null +++ b/apps/dav/tests/unit/connector/sabre/filesreportplugin.php @@ -0,0 +1,519 @@ +<?php +/** + * @author Vincent Petry <pvince81@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\DAV\Tests\Unit\Sabre\Connector; + +use OCA\DAV\Connector\Sabre\FilesReportPlugin as FilesReportPluginImplementation; +use Sabre\DAV\Exception\NotFound; +use OCP\SystemTag\ISystemTagObjectMapper; +use OC\Files\View; +use OCP\Files\Folder; +use OCP\IGroupManager; +use OCP\SystemTag\ISystemTagManager; + +class FilesReportPlugin extends \Test\TestCase { + /** @var \Sabre\DAV\Server */ + private $server; + + /** @var \Sabre\DAV\Tree */ + private $tree; + + /** @var ISystemTagObjectMapper */ + private $tagMapper; + + /** @var ISystemTagManager */ + private $tagManager; + + /** @var \OCP\IUserSession */ + private $userSession; + + /** @var FilesReportPluginImplementation */ + private $plugin; + + /** @var View **/ + private $view; + + /** @var IGroupManager **/ + private $groupManager; + + /** @var Folder **/ + private $userFolder; + + public function setUp() { + parent::setUp(); + $this->tree = $this->getMockBuilder('\Sabre\DAV\Tree') + ->disableOriginalConstructor() + ->getMock(); + + $this->view = $this->getMockBuilder('\OC\Files\View') + ->disableOriginalConstructor() + ->getMock(); + + $this->server = $this->getMockBuilder('\Sabre\DAV\Server') + ->setConstructorArgs([$this->tree]) + ->setMethods(['getRequestUri']) + ->getMock(); + + $this->groupManager = $this->getMockBuilder('\OCP\IGroupManager') + ->disableOriginalConstructor() + ->getMock(); + + $this->userFolder = $this->getMockBuilder('\OCP\Files\Folder') + ->disableOriginalConstructor() + ->getMock(); + + $this->tagManager = $this->getMock('\OCP\SystemTag\ISystemTagManager'); + $this->tagMapper = $this->getMock('\OCP\SystemTag\ISystemTagObjectMapper'); + $this->userSession = $this->getMock('\OCP\IUserSession'); + + $user = $this->getMock('\OCP\IUser'); + $user->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('testuser')); + $this->userSession->expects($this->any()) + ->method('getUser') + ->will($this->returnValue($user)); + + $this->plugin = new FilesReportPluginImplementation( + $this->tree, + $this->view, + $this->tagManager, + $this->tagMapper, + $this->userSession, + $this->groupManager, + $this->userFolder + ); + } + + /** + * @expectedException \Sabre\DAV\Exception\ReportNotSupported + */ + public function testOnReportInvalidNode() { + $path = 'totally/unrelated/13'; + + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->with('/' . $path) + ->will($this->returnValue($this->getMock('\Sabre\DAV\INode'))); + + $this->server->expects($this->any()) + ->method('getRequestUri') + ->will($this->returnValue($path)); + $this->plugin->initialize($this->server); + + $this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, [], '/' . $path); + } + + /** + * @expectedException \Sabre\DAV\Exception\ReportNotSupported + */ + public function testOnReportInvalidReportName() { + $path = 'test'; + + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->with('/' . $path) + ->will($this->returnValue($this->getMock('\Sabre\DAV\INode'))); + + $this->server->expects($this->any()) + ->method('getRequestUri') + ->will($this->returnValue($path)); + $this->plugin->initialize($this->server); + + $this->plugin->onReport('{whoever}whatever', [], '/' . $path); + } + + public function testOnReport() { + $path = 'test'; + + $parameters = [ + [ + 'name' => '{DAV:}prop', + 'value' => [ + ['name' => '{DAV:}getcontentlength', 'value' => ''], + ['name' => '{http://owncloud.org/ns}size', 'value' => ''], + ], + ], + [ + 'name' => '{http://owncloud.org/ns}filter-rules', + 'value' => [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], + ], + ], + ]; + + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(true)); + + $this->tagMapper->expects($this->at(0)) + ->method('getObjectIdsForTags') + ->with('123', 'files') + ->will($this->returnValue(['111', '222'])); + $this->tagMapper->expects($this->at(1)) + ->method('getObjectIdsForTags') + ->with('456', 'files') + ->will($this->returnValue(['111', '222', '333'])); + + $reportTargetNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory') + ->disableOriginalConstructor() + ->getMock(); + + $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') + ->disableOriginalConstructor() + ->getMock(); + + $response->expects($this->once()) + ->method('setHeader') + ->with('Content-Type', 'application/xml; charset=utf-8'); + + $response->expects($this->once()) + ->method('setStatus') + ->with(207); + + $response->expects($this->once()) + ->method('setBody'); + + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->with('/' . $path) + ->will($this->returnValue($reportTargetNode)); + + $filesNode1 = $this->getMockBuilder('\OCP\Files\Folder') + ->disableOriginalConstructor() + ->getMock(); + $filesNode2 = $this->getMockBuilder('\OCP\Files\File') + ->disableOriginalConstructor() + ->getMock(); + + $this->userFolder->expects($this->at(0)) + ->method('getById') + ->with('111') + ->will($this->returnValue([$filesNode1])); + $this->userFolder->expects($this->at(1)) + ->method('getById') + ->with('222') + ->will($this->returnValue([$filesNode2])); + + $this->server->expects($this->any()) + ->method('getRequestUri') + ->will($this->returnValue($path)); + $this->server->httpResponse = $response; + $this->plugin->initialize($this->server); + + $this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, $parameters, '/' . $path); + } + + public function testFindNodesByFileIdsRoot() { + $filesNode1 = $this->getMockBuilder('\OCP\Files\Folder') + ->disableOriginalConstructor() + ->getMock(); + $filesNode1->expects($this->once()) + ->method('getName') + ->will($this->returnValue('first node')); + + $filesNode2 = $this->getMockBuilder('\OCP\Files\File') + ->disableOriginalConstructor() + ->getMock(); + $filesNode2->expects($this->once()) + ->method('getName') + ->will($this->returnValue('second node')); + + $reportTargetNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory') + ->disableOriginalConstructor() + ->getMock(); + $reportTargetNode->expects($this->any()) + ->method('getPath') + ->will($this->returnValue('/')); + + $this->userFolder->expects($this->at(0)) + ->method('getById') + ->with('111') + ->will($this->returnValue([$filesNode1])); + $this->userFolder->expects($this->at(1)) + ->method('getById') + ->with('222') + ->will($this->returnValue([$filesNode2])); + + $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']); + + $this->assertCount(2, $result); + $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\Directory', $result[0]); + $this->assertEquals('first node', $result[0]->getName()); + $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\File', $result[1]); + $this->assertEquals('second node', $result[1]->getName()); + } + + public function testFindNodesByFileIdsSubDir() { + $filesNode1 = $this->getMockBuilder('\OCP\Files\Folder') + ->disableOriginalConstructor() + ->getMock(); + $filesNode1->expects($this->once()) + ->method('getName') + ->will($this->returnValue('first node')); + + $filesNode2 = $this->getMockBuilder('\OCP\Files\File') + ->disableOriginalConstructor() + ->getMock(); + $filesNode2->expects($this->once()) + ->method('getName') + ->will($this->returnValue('second node')); + + $reportTargetNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory') + ->disableOriginalConstructor() + ->getMock(); + $reportTargetNode->expects($this->any()) + ->method('getPath') + ->will($this->returnValue('/sub1/sub2')); + + + $subNode = $this->getMockBuilder('\OCP\Files\Folder') + ->disableOriginalConstructor() + ->getMock(); + + $this->userFolder->expects($this->at(0)) + ->method('get') + ->with('/sub1/sub2') + ->will($this->returnValue($subNode)); + + $subNode->expects($this->at(0)) + ->method('getById') + ->with('111') + ->will($this->returnValue([$filesNode1])); + $subNode->expects($this->at(1)) + ->method('getById') + ->with('222') + ->will($this->returnValue([$filesNode2])); + + $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']); + + $this->assertCount(2, $result); + $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\Directory', $result[0]); + $this->assertEquals('first node', $result[0]->getName()); + $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\File', $result[1]); + $this->assertEquals('second node', $result[1]->getName()); + } + + public function testPrepareResponses() { + $requestedProps = ['{DAV:}getcontentlength', '{http://owncloud.org/ns}fileid', '{DAV:}resourcetype']; + + $node1 = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory') + ->disableOriginalConstructor() + ->getMock(); + $node2 = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File') + ->disableOriginalConstructor() + ->getMock(); + + $node1->expects($this->once()) + ->method('getInternalFileId') + ->will($this->returnValue('111')); + $node2->expects($this->once()) + ->method('getInternalFileId') + ->will($this->returnValue('222')); + $node2->expects($this->once()) + ->method('getSize') + ->will($this->returnValue(1024)); + + $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\FilesPlugin($this->tree, $this->view)); + $this->plugin->initialize($this->server); + $responses = $this->plugin->prepareResponses($requestedProps, [$node1, $node2]); + + $this->assertCount(2, $responses); + + $this->assertEquals(200, $responses[0]->getHttpStatus()); + $this->assertEquals(200, $responses[1]->getHttpStatus()); + + $props1 = $responses[0]->getResponseProperties(); + $this->assertEquals('111', $props1[200]['{http://owncloud.org/ns}fileid']); + $this->assertNull($props1[404]['{DAV:}getcontentlength']); + $this->assertInstanceOf('\Sabre\DAV\Xml\Property\ResourceType', $props1[200]['{DAV:}resourcetype']); + $resourceType1 = $props1[200]['{DAV:}resourcetype']->getValue(); + $this->assertEquals('{DAV:}collection', $resourceType1[0]); + + $props2 = $responses[1]->getResponseProperties(); + $this->assertEquals('1024', $props2[200]['{DAV:}getcontentlength']); + $this->assertEquals('222', $props2[200]['{http://owncloud.org/ns}fileid']); + $this->assertInstanceOf('\Sabre\DAV\Xml\Property\ResourceType', $props2[200]['{DAV:}resourcetype']); + $this->assertCount(0, $props2[200]['{DAV:}resourcetype']->getValue()); + } + + public function testProcessFilterRulesSingle() { + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(true)); + + $this->tagMapper->expects($this->once()) + ->method('getObjectIdsForTags') + ->with('123') + ->will($this->returnValue(['111', '222'])); + + $rules = [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ]; + + $this->assertEquals(['111', '222'], $this->plugin->processFilterRules($rules)); + } + + public function testProcessFilterRulesAndCondition() { + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(true)); + + $this->tagMapper->expects($this->at(0)) + ->method('getObjectIdsForTags') + ->with('123') + ->will($this->returnValue(['111', '222'])); + $this->tagMapper->expects($this->at(1)) + ->method('getObjectIdsForTags') + ->with('456') + ->will($this->returnValue(['222', '333'])); + + $rules = [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], + ]; + + $this->assertEquals(['222'], array_values($this->plugin->processFilterRules($rules))); + } + + public function testProcessFilterRulesInvisibleTagAsAdmin() { + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(true)); + + $tag1 = $this->getMock('\OCP\SystemTag\ISystemTag'); + $tag1->expects($this->any()) + ->method('getId') + ->will($this->returnValue('123')); + $tag1->expects($this->any()) + ->method('isUserVisible') + ->will($this->returnValue(true)); + + $tag2 = $this->getMock('\OCP\SystemTag\ISystemTag'); + $tag2->expects($this->any()) + ->method('getId') + ->will($this->returnValue('123')); + $tag2->expects($this->any()) + ->method('isUserVisible') + ->will($this->returnValue(false)); + + // no need to fetch tags to check permissions + $this->tagManager->expects($this->never()) + ->method('getTagsByIds'); + + $this->tagMapper->expects($this->at(0)) + ->method('getObjectIdsForTags') + ->with('123') + ->will($this->returnValue(['111', '222'])); + $this->tagMapper->expects($this->at(1)) + ->method('getObjectIdsForTags') + ->with('456') + ->will($this->returnValue(['222', '333'])); + + $rules = [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], + ]; + + $this->assertEquals(['222'], array_values($this->plugin->processFilterRules($rules))); + } + + /** + * @expectedException \OCP\SystemTag\TagNotFoundException + */ + public function testProcessFilterRulesInvisibleTagAsUser() { + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(false)); + + $tag1 = $this->getMock('\OCP\SystemTag\ISystemTag'); + $tag1->expects($this->any()) + ->method('getId') + ->will($this->returnValue('123')); + $tag1->expects($this->any()) + ->method('isUserVisible') + ->will($this->returnValue(true)); + + $tag2 = $this->getMock('\OCP\SystemTag\ISystemTag'); + $tag2->expects($this->any()) + ->method('getId') + ->will($this->returnValue('123')); + $tag2->expects($this->any()) + ->method('isUserVisible') + ->will($this->returnValue(false)); // invisible + + $this->tagManager->expects($this->once()) + ->method('getTagsByIds') + ->with(['123', '456']) + ->will($this->returnValue([$tag1, $tag2])); + + $rules = [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], + ]; + + $this->plugin->processFilterRules($rules); + } + + public function testProcessFilterRulesVisibleTagAsUser() { + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(false)); + + $tag1 = $this->getMock('\OCP\SystemTag\ISystemTag'); + $tag1->expects($this->any()) + ->method('getId') + ->will($this->returnValue('123')); + $tag1->expects($this->any()) + ->method('isUserVisible') + ->will($this->returnValue(true)); + + $tag2 = $this->getMock('\OCP\SystemTag\ISystemTag'); + $tag2->expects($this->any()) + ->method('getId') + ->will($this->returnValue('123')); + $tag2->expects($this->any()) + ->method('isUserVisible') + ->will($this->returnValue(true)); + + $this->tagManager->expects($this->once()) + ->method('getTagsByIds') + ->with(['123', '456']) + ->will($this->returnValue([$tag1, $tag2])); + + $this->tagMapper->expects($this->at(0)) + ->method('getObjectIdsForTags') + ->with('123') + ->will($this->returnValue(['111', '222'])); + $this->tagMapper->expects($this->at(1)) + ->method('getObjectIdsForTags') + ->with('456') + ->will($this->returnValue(['222', '333'])); + + $rules = [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], + ]; + + $this->assertEquals(['222'], array_values($this->plugin->processFilterRules($rules))); + } +} diff --git a/apps/dav/tests/unit/connector/sabre/principal.php b/apps/dav/tests/unit/connector/sabre/principal.php index d6bc7cd405f..07bfd5d263b 100644 --- a/apps/dav/tests/unit/connector/sabre/principal.php +++ b/apps/dav/tests/unit/connector/sabre/principal.php @@ -211,10 +211,7 @@ class Principal extends TestCase { ->method('getUserGroups') ->willReturn([]); - $expectedResponse = [ - 'principals/users/foo/calendar-proxy-read', - 'principals/users/foo/calendar-proxy-write' - ]; + $expectedResponse = []; $response = $this->connector->getGroupMembership('principals/users/foo'); $this->assertSame($expectedResponse, $response); } diff --git a/apps/federatedfilesharing/appinfo/app.php b/apps/federatedfilesharing/appinfo/app.php new file mode 100644 index 00000000000..804ab69759c --- /dev/null +++ b/apps/federatedfilesharing/appinfo/app.php @@ -0,0 +1,27 @@ +<?php +/** + * @author Björn Schießle <schiessle@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\FederatedFileSharing\AppInfo; + +use OCP\AppFramework\App; + +new App('federatedfilesharing'); + diff --git a/apps/federatedfilesharing/appinfo/info.xml b/apps/federatedfilesharing/appinfo/info.xml new file mode 100644 index 00000000000..d88ea2640e1 --- /dev/null +++ b/apps/federatedfilesharing/appinfo/info.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<info> + <id>federatedfilesharing</id> + <name>Federated File Sharing</name> + <description>Provide federated file sharing across ownCloud servers</description> + <licence>AGPL</licence> + <author>Bjoern Schiessle, Roeland Jago Douma</author> + <version>0.1.0</version> + <namespace>FederatedFileSharing</namespace> + <category>other</category> + <dependencies> + <owncloud min-version="9.0" max-version="9.0" /> + </dependencies> +</info> diff --git a/apps/federatedfilesharing/lib/addresshandler.php b/apps/federatedfilesharing/lib/addresshandler.php new file mode 100644 index 00000000000..92768f11b95 --- /dev/null +++ b/apps/federatedfilesharing/lib/addresshandler.php @@ -0,0 +1,184 @@ +<?php +/** + * @author Björn Schießle <schiessle@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\FederatedFileSharing; +use OC\HintException; +use OCP\IL10N; +use OCP\IURLGenerator; + +/** + * Class AddressHandler - parse, modify and construct federated sharing addresses + * + * @package OCA\FederatedFileSharing + */ +class AddressHandler { + + /** @var IL10N */ + private $l; + + /** @var IURLGenerator */ + private $urlGenerator; + + /** + * AddressHandler constructor. + * + * @param IURLGenerator $urlGenerator + * @param IL10N $il10n + */ + public function __construct( + IURLGenerator $urlGenerator, + IL10N $il10n + ) { + $this->l = $il10n; + $this->urlGenerator = $urlGenerator; + } + + /** + * split user and remote from federated cloud id + * + * @param string $address federated share address + * @return array [user, remoteURL] + * @throws HintException + */ + public function splitUserRemote($address) { + if (strpos($address, '@') === false) { + $hint = $this->l->t('Invalid Federated Cloud ID'); + throw new HintException('Invalid Federated Cloud ID', $hint); + } + + // Find the first character that is not allowed in user names + $id = str_replace('\\', '/', $address); + $posSlash = strpos($id, '/'); + $posColon = strpos($id, ':'); + + if ($posSlash === false && $posColon === false) { + $invalidPos = strlen($id); + } else if ($posSlash === false) { + $invalidPos = $posColon; + } else if ($posColon === false) { + $invalidPos = $posSlash; + } else { + $invalidPos = min($posSlash, $posColon); + } + + // Find the last @ before $invalidPos + $pos = $lastAtPos = 0; + while ($lastAtPos !== false && $lastAtPos <= $invalidPos) { + $pos = $lastAtPos; + $lastAtPos = strpos($id, '@', $pos + 1); + } + + if ($pos !== false) { + $user = substr($id, 0, $pos); + $remote = substr($id, $pos + 1); + $remote = $this->fixRemoteURL($remote); + if (!empty($user) && !empty($remote)) { + return array($user, $remote); + } + } + + $hint = $this->l->t('Invalid Federated Cloud ID'); + throw new HintException('Invalid Federated Cloud ID', $hint); + } + + /** + * generate remote URL part of federated ID + * + * @return string url of the current server + */ + public function generateRemoteURL() { + $url = $this->urlGenerator->getAbsoluteURL('/'); + return $url; + } + + /** + * check if two federated cloud IDs refer to the same user + * + * @param string $user1 + * @param string $server1 + * @param string $user2 + * @param string $server2 + * @return bool true if both users and servers are the same + */ + public function compareAddresses($user1, $server1, $user2, $server2) { + $normalizedServer1 = strtolower($this->removeProtocolFromUrl($server1)); + $normalizedServer2 = strtolower($this->removeProtocolFromUrl($server2)); + + if (rtrim($normalizedServer1, '/') === rtrim($normalizedServer2, '/')) { + // FIXME this should be a method in the user management instead + \OCP\Util::emitHook( + '\OCA\Files_Sharing\API\Server2Server', + 'preLoginNameUsedAsUserName', + array('uid' => &$user1) + ); + \OCP\Util::emitHook( + '\OCA\Files_Sharing\API\Server2Server', + 'preLoginNameUsedAsUserName', + array('uid' => &$user2) + ); + + if ($user1 === $user2) { + return true; + } + } + + return false; + } + + /** + * remove protocol from URL + * + * @param string $url + * @return string + */ + public function removeProtocolFromUrl($url) { + if (strpos($url, 'https://') === 0) { + return substr($url, strlen('https://')); + } else if (strpos($url, 'http://') === 0) { + return substr($url, strlen('http://')); + } + + return $url; + } + + /** + * Strips away a potential file names and trailing slashes: + * - http://localhost + * - http://localhost/ + * - http://localhost/index.php + * - http://localhost/index.php/s/{shareToken} + * + * all return: http://localhost + * + * @param string $remote + * @return string + */ + protected function fixRemoteURL($remote) { + $remote = str_replace('\\', '/', $remote); + if ($fileNamePosition = strpos($remote, '/index.php')) { + $remote = substr($remote, 0, $fileNamePosition); + } + $remote = rtrim($remote, '/'); + + return $remote; + } + +} diff --git a/apps/federatedfilesharing/lib/federatedshareprovider.php b/apps/federatedfilesharing/lib/federatedshareprovider.php new file mode 100644 index 00000000000..05a9432a32f --- /dev/null +++ b/apps/federatedfilesharing/lib/federatedshareprovider.php @@ -0,0 +1,556 @@ +<?php +/** + * @author Björn Schießle <schiessle@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\FederatedFileSharing; + +use OC\Share20\Share; +use OCP\Files\IRootFolder; +use OCP\IL10N; +use OCP\ILogger; +use OCP\Share\IShare; +use OCP\Share\IShareProvider; +use OC\Share20\Exception\InvalidShare; +use OCP\Share\Exceptions\ShareNotFound; +use OCP\Files\NotFoundException; +use OCP\IDBConnection; +use OCP\Files\Node; + +/** + * Class FederatedShareProvider + * + * @package OCA\FederatedFileSharing + */ +class FederatedShareProvider implements IShareProvider { + + const SHARE_TYPE_REMOTE = 6; + + /** @var IDBConnection */ + private $dbConnection; + + /** @var AddressHandler */ + private $addressHandler; + + /** @var Notifications */ + private $notifications; + + /** @var TokenHandler */ + private $tokenHandler; + + /** @var IL10N */ + private $l; + + /** @var ILogger */ + private $logger; + + /** @var IRootFolder */ + private $rootFolder; + + /** + * DefaultShareProvider constructor. + * + * @param IDBConnection $connection + * @param AddressHandler $addressHandler + * @param Notifications $notifications + * @param TokenHandler $tokenHandler + * @param IL10N $l10n + * @param ILogger $logger + * @param IRootFolder $rootFolder + */ + public function __construct( + IDBConnection $connection, + AddressHandler $addressHandler, + Notifications $notifications, + TokenHandler $tokenHandler, + IL10N $l10n, + ILogger $logger, + IRootFolder $rootFolder + ) { + $this->dbConnection = $connection; + $this->addressHandler = $addressHandler; + $this->notifications = $notifications; + $this->tokenHandler = $tokenHandler; + $this->l = $l10n; + $this->logger = $logger; + $this->rootFolder = $rootFolder; + } + + /** + * Return the identifier of this provider. + * + * @return string Containing only [a-zA-Z0-9] + */ + public function identifier() { + return 'ocFederatedSharing'; + } + + /** + * Share a path + * + * @param IShare $share + * @return IShare The share object + * @throws ShareNotFound + * @throws \Exception + */ + public function create(IShare $share) { + + $shareWith = $share->getSharedWith(); + $itemSource = $share->getNodeId(); + $itemType = $share->getNodeType(); + $uidOwner = $share->getShareOwner(); + $permissions = $share->getPermissions(); + $sharedBy = $share->getSharedBy(); + + /* + * Check if file is not already shared with the remote user + */ + $alreadyShared = $this->getSharedWith($shareWith, self::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0); + if (!empty($alreadyShared)) { + $message = 'Sharing %s failed, because this item is already shared with %s'; + $message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith)); + $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); + throw new \Exception($message_t); + } + + + // don't allow federated shares if source and target server are the same + list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith); + $currentServer = $this->addressHandler->generateRemoteURL(); + $currentUser = $sharedBy; + if ($this->addressHandler->compareAddresses($user, $remote, $currentUser, $currentServer)) { + $message = 'Not allowed to create a federated share with the same user.'; + $message_t = $this->l->t('Not allowed to create a federated share with the same user'); + $this->logger->debug($message, ['app' => 'Federated File Sharing']); + throw new \Exception($message_t); + } + + $token = $this->tokenHandler->generateToken(); + + $shareWith = $user . '@' . $remote; + + $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token); + + $send = $this->notifications->sendRemoteShare( + $token, + $shareWith, + $share->getNode()->getName(), + $shareId, + $share->getSharedBy() + ); + + $data = $this->getRawShare($shareId); + $share = $this->createShare($data); + + if ($send === false) { + $this->delete($share); + $message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.', + [$share->getNode()->getName(), $shareWith]); + throw new \Exception($message_t); + } + + return $share; + } + + /** + * add share to the database and return the ID + * + * @param int $itemSource + * @param string $itemType + * @param string $shareWith + * @param string $sharedBy + * @param string $uidOwner + * @param int $permissions + * @param string $token + * @return int + */ + private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->insert('share') + ->setValue('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)) + ->setValue('item_type', $qb->createNamedParameter($itemType)) + ->setValue('item_source', $qb->createNamedParameter($itemSource)) + ->setValue('file_source', $qb->createNamedParameter($itemSource)) + ->setValue('share_with', $qb->createNamedParameter($shareWith)) + ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) + ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) + ->setValue('permissions', $qb->createNamedParameter($permissions)) + ->setValue('token', $qb->createNamedParameter($token)) + ->setValue('stime', $qb->createNamedParameter(time())); + + $qb->execute(); + $id = $qb->getLastInsertId(); + + return (int)$id; + } + + /** + * Update a share + * + * @param IShare $share + * @return IShare The share object + */ + public function update(IShare $share) { + /* + * We allow updating the permissions of federated shares + */ + $qb = $this->dbConnection->getQueryBuilder(); + $qb->update('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->execute(); + + return $share; + } + + /** + * @inheritdoc + */ + public function move(IShare $share, $recipient) { + /* + * This function does nothing yet as it is just for outgoing + * federated shares. + */ + return $share; + } + + /** + * Get all children of this share + * + * @param IShare $parent + * @return IShare[] + */ + public function getChildren(IShare $parent) { + $children = []; + + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) + ->orderBy('id'); + + $cursor = $qb->execute(); + while($data = $cursor->fetch()) { + $children[] = $this->createShare($data); + } + $cursor->closeCursor(); + + return $children; + } + + /** + * Delete a share + * + * @param IShare $share + */ + public function delete(IShare $share) { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->delete('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))); + $qb->execute(); + + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith()); + $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken()); + } + + /** + * @inheritdoc + */ + public function deleteFromSelf(IShare $share, $recipient) { + // nothing to do here. Technically deleteFromSelf in the context of federated + // shares is a umount of a external storage. This is handled here + // apps/files_sharing/lib/external/manager.php + // TODO move this code over to this app + return; + } + + /** + * @inheritdoc + */ + public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('*') + ->from('share'); + + $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); + + /** + * Reshares for this user are shares where they are the owner. + */ + if ($reshares === false) { + //Special case for old shares created via the web UI + $or1 = $qb->expr()->andX( + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), + $qb->expr()->isNull('uid_initiator') + ); + + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), + $or1 + ) + ); + } else { + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) + ) + ); + } + + if ($node !== null) { + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); + } + + if ($limit !== -1) { + $qb->setMaxResults($limit); + } + + $qb->setFirstResult($offset); + $qb->orderBy('id'); + + $cursor = $qb->execute(); + $shares = []; + while($data = $cursor->fetch()) { + $shares[] = $this->createShare($data); + } + $cursor->closeCursor(); + + return $shares; + } + + /** + * @inheritdoc + */ + public function getShareById($id, $recipientId = null) { + $qb = $this->dbConnection->getQueryBuilder(); + + $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); + + $cursor = $qb->execute(); + $data = $cursor->fetch(); + $cursor->closeCursor(); + + if ($data === false) { + throw new ShareNotFound(); + } + + try { + $share = $this->createShare($data); + } catch (InvalidShare $e) { + throw new ShareNotFound(); + } + + return $share; + } + + /** + * Get shares for a given path + * + * @param \OCP\Files\Node $path + * @return IShare[] + */ + public function getSharesByPath(Node $path) { + $qb = $this->dbConnection->getQueryBuilder(); + + $cursor = $qb->select('*') + ->from('share') + ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) + ->execute(); + + $shares = []; + while($data = $cursor->fetch()) { + $shares[] = $this->createShare($data); + } + $cursor->closeCursor(); + + return $shares; + } + + /** + * @inheritdoc + */ + public function getSharedWith($userId, $shareType, $node, $limit, $offset) { + /** @var IShare[] $shares */ + $shares = []; + + //Get shares directly with this user + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('*') + ->from('share'); + + // Order by id + $qb->orderBy('id'); + + // Set limit and offset + if ($limit !== -1) { + $qb->setMaxResults($limit); + } + $qb->setFirstResult($offset); + + $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); + $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); + + // Filter by node if provided + if ($node !== null) { + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); + } + + $cursor = $qb->execute(); + + while($data = $cursor->fetch()) { + $shares[] = $this->createShare($data); + } + $cursor->closeCursor(); + + + return $shares; + } + + /** + * Get a share by token + * + * @param string $token + * @return IShare + * @throws ShareNotFound + */ + public function getShareByToken($token) { + $qb = $this->dbConnection->getQueryBuilder(); + + $cursor = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) + ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) + ->execute(); + + $data = $cursor->fetch(); + + if ($data === false) { + throw new ShareNotFound(); + } + + try { + $share = $this->createShare($data); + } catch (InvalidShare $e) { + throw new ShareNotFound(); + } + + return $share; + } + + /** + * get database row of a give share + * + * @param $id + * @return array + * @throws ShareNotFound + */ + private function getRawShare($id) { + + // Now fetch the inserted share and create a complete share object + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); + + $cursor = $qb->execute(); + $data = $cursor->fetch(); + $cursor->closeCursor(); + + if ($data === false) { + throw new ShareNotFound; + } + + return $data; + } + + /** + * Create a share object from an database row + * + * @param array $data + * @return IShare + * @throws InvalidShare + * @throws ShareNotFound + */ + private function createShare($data) { + + $share = new Share($this->rootFolder); + $share->setId((int)$data['id']) + ->setShareType((int)$data['share_type']) + ->setPermissions((int)$data['permissions']) + ->setTarget($data['file_target']) + ->setMailSend((bool)$data['mail_send']) + ->setToken($data['token']); + + $shareTime = new \DateTime(); + $shareTime->setTimestamp((int)$data['stime']); + $share->setShareTime($shareTime); + $share->setSharedWith($data['share_with']); + + if ($data['uid_initiator'] !== null) { + $share->setShareOwner($data['uid_owner']); + $share->setSharedBy($data['uid_initiator']); + } else { + //OLD SHARE + $share->setSharedBy($data['uid_owner']); + $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); + + $owner = $path->getOwner(); + $share->setShareOwner($owner->getUID()); + } + + $share->setNodeId((int)$data['file_source']); + $share->setNodeType($data['item_type']); + + $share->setProviderId($this->identifier()); + + return $share; + } + + /** + * Get the node with file $id for $user + * + * @param string $userId + * @param int $id + * @return \OCP\Files\File|\OCP\Files\Folder + * @throws InvalidShare + */ + private function getNode($userId, $id) { + try { + $userFolder = $this->rootFolder->getUserFolder($userId); + } catch (NotFoundException $e) { + throw new InvalidShare(); + } + + $nodes = $userFolder->getById($id); + + if (empty($nodes)) { + throw new InvalidShare(); + } + + return $nodes[0]; + } + +} diff --git a/apps/federatedfilesharing/lib/notifications.php b/apps/federatedfilesharing/lib/notifications.php new file mode 100644 index 00000000000..d778ac87828 --- /dev/null +++ b/apps/federatedfilesharing/lib/notifications.php @@ -0,0 +1,144 @@ +<?php +/** + * @author Björn Schießle <schiessle@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\FederatedFileSharing; + + +use OCP\Http\Client\IClientService; + +class Notifications { + + const BASE_PATH_TO_SHARE_API = '/ocs/v1.php/cloud/shares'; + const RESPONSE_FORMAT = 'json'; // default response format for ocs calls + + /** @var AddressHandler */ + private $addressHandler; + + /** @var IClientService */ + private $httpClientService; + + /** + * Notifications constructor. + * + * @param AddressHandler $addressHandler + * @param IClientService $httpClientService + */ + public function __construct( + AddressHandler $addressHandler, + IClientService $httpClientService + ) { + $this->addressHandler = $addressHandler; + $this->httpClientService = $httpClientService; + } + + /** + * send server-to-server share to remote server + * + * @param string $token + * @param string $shareWith + * @param string $name + * @param int $remote_id + * @param string $owner + * @return bool + */ + public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner) { + + list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith); + + if ($user && $remote) { + $url = $remote . self::BASE_PATH_TO_SHARE_API . '?format=' . self::RESPONSE_FORMAT; + $local = $this->addressHandler->generateRemoteURL(); + + $fields = array( + 'shareWith' => $user, + 'token' => $token, + 'name' => $name, + 'remoteId' => $remote_id, + 'owner' => $owner, + 'remote' => $local, + ); + + $url = $this->addressHandler->removeProtocolFromUrl($url); + $result = $this->tryHttpPost($url, $fields); + $status = json_decode($result['result'], true); + + if ($result['success'] && $status['ocs']['meta']['statuscode'] === 100) { + \OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $remote]); + return true; + } + + } + + return false; + } + + /** + * send server-to-server unshare to remote server + * + * @param string $remote url + * @param int $id share id + * @param string $token + * @return bool + */ + public function sendRemoteUnShare($remote, $id, $token) { + $url = rtrim($remote, '/') . self::BASE_PATH_TO_SHARE_API . '/' . $id . '/unshare?format=' . self::RESPONSE_FORMAT; + $fields = array('token' => $token, 'format' => 'json'); + $url = $this->addressHandler->removeProtocolFromUrl($url); + $result = $this->tryHttpPost($url, $fields); + $status = json_decode($result['result'], true); + + return ($result['success'] && $status['ocs']['meta']['statuscode'] === 100); + } + + /** + * try http post first with https and then with http as a fallback + * + * @param string $url + * @param array $fields post parameters + * @return array + */ + private function tryHttpPost($url, array $fields) { + $client = $this->httpClientService->newClient(); + $protocol = 'https://'; + $result = [ + 'success' => false, + 'result' => '', + ]; + $try = 0; + while ($result['success'] === false && $try < 2) { + try { + $response = $client->post($protocol . $url, [ + 'body' => $fields + ]); + $result['result'] = $response->getBody(); + $result['success'] = true; + break; + } catch (\Exception $e) { + $try++; + $protocol = 'http://'; + } + } + + return $result; + } + +} diff --git a/apps/federatedfilesharing/lib/tokenhandler.php b/apps/federatedfilesharing/lib/tokenhandler.php new file mode 100644 index 00000000000..ec5f73127d6 --- /dev/null +++ b/apps/federatedfilesharing/lib/tokenhandler.php @@ -0,0 +1,61 @@ +<?php +/** + * @author Björn Schießle <schiessle@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\FederatedFileSharing; + + +use OCP\Security\ISecureRandom; + +/** + * Class TokenHandler + * + * @package OCA\FederatedFileSharing + */ +class TokenHandler { + + const TOKEN_LENGTH = 15; + + /** @var ISecureRandom */ + private $secureRandom; + + /** + * TokenHandler constructor. + * + * @param ISecureRandom $secureRandom + */ + public function __construct(ISecureRandom $secureRandom) { + $this->secureRandom = $secureRandom; + } + + /** + * generate to token used to authenticate federated shares + * + * @return string + */ + public function generateToken() { + $token = $this->secureRandom->generate( + self::TOKEN_LENGTH, + ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS); + return $token; + } + +} diff --git a/apps/federatedfilesharing/tests/addresshandlertest.php b/apps/federatedfilesharing/tests/addresshandlertest.php new file mode 100644 index 00000000000..b1c23dc75bf --- /dev/null +++ b/apps/federatedfilesharing/tests/addresshandlertest.php @@ -0,0 +1,198 @@ +<?php +/** + * @author Björn Schießle <schiessle@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\FederatedFileSharing\Tests; + + +use OCA\FederatedFileSharing\AddressHandler; +use OCP\IL10N; +use OCP\IURLGenerator; +use Test\TestCase; + +class AddressHandlerTest extends TestCase { + + /** @var AddressHandler */ + private $addressHandler; + + /** @var IURLGenerator | \PHPUnit_Framework_MockObject_MockObject */ + private $urlGenerator; + + /** @var IL10N | \PHPUnit_Framework_MockObject_MockObject */ + private $il10n; + + public function setUp() { + parent::setUp(); + + $this->urlGenerator = $this->getMock('OCP\IURLGenerator'); + $this->il10n = $this->getMock('OCP\IL10N'); + + $this->addressHandler = new AddressHandler($this->urlGenerator, $this->il10n); + } + + public function dataTestSplitUserRemote() { + $userPrefix = ['user@name', 'username']; + $protocols = ['', 'http://', 'https://']; + $remotes = [ + 'localhost', + 'local.host', + 'dev.local.host', + 'dev.local.host/path', + 'dev.local.host/at@inpath', + '127.0.0.1', + '::1', + '::192.0.2.128', + '::192.0.2.128/at@inpath', + ]; + + $testCases = []; + foreach ($userPrefix as $user) { + foreach ($remotes as $remote) { + foreach ($protocols as $protocol) { + $baseUrl = $user . '@' . $protocol . $remote; + + $testCases[] = [$baseUrl, $user, $protocol . $remote]; + $testCases[] = [$baseUrl . '/', $user, $protocol . $remote]; + $testCases[] = [$baseUrl . '/index.php', $user, $protocol . $remote]; + $testCases[] = [$baseUrl . '/index.php/s/token', $user, $protocol . $remote]; + } + } + } + return $testCases; + } + + /** + * @dataProvider dataTestSplitUserRemote + * + * @param string $remote + * @param string $expectedUser + * @param string $expectedUrl + */ + public function testSplitUserRemote($remote, $expectedUser, $expectedUrl) { + list($remoteUser, $remoteUrl) = $this->addressHandler->splitUserRemote($remote); + $this->assertSame($expectedUser, $remoteUser); + $this->assertSame($expectedUrl, $remoteUrl); + } + + public function dataTestSplitUserRemoteError() { + return array( + // Invalid path + array('user@'), + + // Invalid user + array('@server'), + array('us/er@server'), + array('us:er@server'), + + // Invalid splitting + array('user'), + array(''), + array('us/erserver'), + array('us:erserver'), + ); + } + + /** + * @dataProvider dataTestSplitUserRemoteError + * + * @param string $id + * @expectedException \OC\HintException + */ + public function testSplitUserRemoteError($id) { + $this->addressHandler->splitUserRemote($id); + } + + /** + * @dataProvider dataTestCompareAddresses + * + * @param string $user1 + * @param string $server1 + * @param string $user2 + * @param string $server2 + * @param bool $expected + */ + public function testCompareAddresses($user1, $server1, $user2, $server2, $expected) { + $this->assertSame($expected, + $this->addressHandler->compareAddresses($user1, $server1, $user2, $server2) + ); + } + + public function dataTestCompareAddresses() { + return [ + ['user1', 'http://server1', 'user1', 'http://server1', true], + ['user1', 'https://server1', 'user1', 'http://server1', true], + ['user1', 'http://serVer1', 'user1', 'http://server1', true], + ['user1', 'http://server1/', 'user1', 'http://server1', true], + ['user1', 'server1', 'user1', 'http://server1', true], + ['user1', 'http://server1', 'user1', 'http://server2', false], + ['user1', 'https://server1', 'user1', 'http://server2', false], + ['user1', 'http://serVer1', 'user1', 'http://serer2', false], + ['user1', 'http://server1/', 'user1', 'http://server2', false], + ['user1', 'server1', 'user1', 'http://server2', false], + ['user1', 'http://server1', 'user2', 'http://server1', false], + ['user1', 'https://server1', 'user2', 'http://server1', false], + ['user1', 'http://serVer1', 'user2', 'http://server1', false], + ['user1', 'http://server1/', 'user2', 'http://server1', false], + ['user1', 'server1', 'user2', 'http://server1', false], + ]; + } + + /** + * @dataProvider dataTestRemoveProtocolFromUrl + * + * @param string $url + * @param string $expectedResult + */ + public function testRemoveProtocolFromUrl($url, $expectedResult) { + $result = $this->addressHandler->removeProtocolFromUrl($url); + $this->assertSame($expectedResult, $result); + } + + public function dataTestRemoveProtocolFromUrl() { + return [ + ['http://owncloud.org', 'owncloud.org'], + ['https://owncloud.org', 'owncloud.org'], + ['owncloud.org', 'owncloud.org'], + ]; + } + + /** + * @dataProvider dataTestFixRemoteUrl + * + * @param string $url + * @param string $expected + */ + public function testFixRemoteUrl($url, $expected) { + $this->assertSame($expected, + $this->invokePrivate($this->addressHandler, 'fixRemoteURL', [$url]) + ); + } + + public function dataTestFixRemoteUrl() { + return [ + ['http://localhost', 'http://localhost'], + ['http://localhost/', 'http://localhost'], + ['http://localhost/index.php', 'http://localhost'], + ['http://localhost/index.php/s/AShareToken', 'http://localhost'], + ]; + } + +} diff --git a/apps/federatedfilesharing/tests/federatedshareprovidertest.php b/apps/federatedfilesharing/tests/federatedshareprovidertest.php new file mode 100644 index 00000000000..a7ff02e7697 --- /dev/null +++ b/apps/federatedfilesharing/tests/federatedshareprovidertest.php @@ -0,0 +1,447 @@ +<?php + +namespace OCA\FederatedFileSharing\Tests; + + +use OCA\FederatedFileSharing\AddressHandler; +use OCA\FederatedFileSharing\FederatedShareProvider; +use OCA\FederatedFileSharing\Notifications; +use OCA\FederatedFileSharing\TokenHandler; +use OCP\Files\IRootFolder; +use OCP\IDBConnection; +use OCP\IL10N; +use OCP\ILogger; +use OCP\Share\IManager; +use Test\TestCase; + +/** + * Class FederatedShareProviderTest + * + * @package OCA\FederatedFileSharing\Tests + * @group DB + */ +class FederatedShareProviderTest extends TestCase { + + /** @var IDBConnection */ + protected $connection; + /** @var AddressHandler | \PHPUnit_Framework_MockObject_MockObject */ + protected $addressHandler; + /** @var Notifications | \PHPUnit_Framework_MockObject_MockObject */ + protected $notifications; + /** @var TokenHandler */ + protected $tokenHandler; + /** @var IL10N */ + protected $l; + /** @var ILogger */ + protected $logger; + /** @var IRootFolder | \PHPUnit_Framework_MockObject_MockObject */ + protected $rootFolder; + + /** @var IManager */ + protected $shareManager; + /** @var FederatedShareProvider */ + protected $provider; + + + public function setUp() { + parent::setUp(); + + $this->connection = \OC::$server->getDatabaseConnection(); + $this->notifications = $this->getMockBuilder('OCA\FederatedFileSharing\Notifications') + ->disableOriginalConstructor() + ->getMock(); + $this->tokenHandler = $this->getMockBuilder('OCA\FederatedFileSharing\TokenHandler') + ->disableOriginalConstructor() + ->getMock(); + $this->l = $this->getMock('OCP\IL10N'); + $this->l->method('t') + ->will($this->returnCallback(function($text, $parameters = []) { + return vsprintf($text, $parameters); + })); + $this->logger = $this->getMock('OCP\ILogger'); + $this->rootFolder = $this->getMock('OCP\Files\IRootFolder'); + $this->addressHandler = new AddressHandler(\OC::$server->getURLGenerator(), $this->l); + + $this->provider = new FederatedShareProvider( + $this->connection, + $this->addressHandler, + $this->notifications, + $this->tokenHandler, + $this->l, + $this->logger, + $this->rootFolder + ); + + $this->shareManager = \OC::$server->getShareManager(); + } + + public function tearDown() { + $this->connection->getQueryBuilder()->delete('share')->execute(); + + return parent::tearDown(); + } + + public function testCreate() { + $share = $this->shareManager->newShare(); + + $node = $this->getMock('\OCP\Files\File'); + $node->method('getId')->willReturn(42); + $node->method('getName')->willReturn('myFile'); + + $share->setSharedWith('user@server.com') + ->setSharedBy('sharedBy') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node); + + $this->tokenHandler->method('generateToken')->willReturn('token'); + + $this->notifications->expects($this->once()) + ->method('sendRemoteShare') + ->with( + $this->equalTo('token'), + $this->equalTo('user@server.com'), + $this->equalTo('myFile'), + $this->anything(), + 'sharedBy' + )->willReturn(true); + + $this->rootFolder->expects($this->never())->method($this->anything()); + + $share = $this->provider->create($share); + + $qb = $this->connection->getQueryBuilder(); + $stmt = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->execute(); + + $data = $stmt->fetch(); + $stmt->closeCursor(); + + $expected = [ + 'share_type' => \OCP\Share::SHARE_TYPE_REMOTE, + 'share_with' => 'user@server.com', + 'uid_owner' => 'shareOwner', + 'uid_initiator' => 'sharedBy', + 'item_type' => 'file', + 'item_source' => 42, + 'file_source' => 42, + 'permissions' => 19, + 'accepted' => 0, + 'token' => 'token', + ]; + $this->assertArraySubset($expected, $data); + + $this->assertEquals($data['id'], $share->getId()); + $this->assertEquals(\OCP\Share::SHARE_TYPE_REMOTE, $share->getShareType()); + $this->assertEquals('user@server.com', $share->getSharedWith()); + $this->assertEquals('sharedBy', $share->getSharedBy()); + $this->assertEquals('shareOwner', $share->getShareOwner()); + $this->assertEquals('file', $share->getNodeType()); + $this->assertEquals(42, $share->getNodeId()); + $this->assertEquals(19, $share->getPermissions()); + $this->assertEquals('token', $share->getToken()); + } + + public function testCreateCouldNotFindServer() { + $share = $this->shareManager->newShare(); + + $node = $this->getMock('\OCP\Files\File'); + $node->method('getId')->willReturn(42); + $node->method('getName')->willReturn('myFile'); + + $share->setSharedWith('user@server.com') + ->setSharedBy('sharedBy') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node); + + $this->tokenHandler->method('generateToken')->willReturn('token'); + + $this->notifications->expects($this->once()) + ->method('sendRemoteShare') + ->with( + $this->equalTo('token'), + $this->equalTo('user@server.com'), + $this->equalTo('myFile'), + $this->anything(), + 'sharedBy' + )->willReturn(false); + + $this->rootFolder->expects($this->once()) + ->method('getUserFolder') + ->with('shareOwner') + ->will($this->returnSelf()); + $this->rootFolder->method('getById') + ->with('42') + ->willReturn([$node]); + + try { + $share = $this->provider->create($share); + $this->fail(); + } catch (\Exception $e) { + $this->assertEquals('Sharing myFile failed, could not find user@server.com, maybe the server is currently unreachable.', $e->getMessage()); + } + + $qb = $this->connection->getQueryBuilder(); + $stmt = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->execute(); + + $data = $stmt->fetch(); + $stmt->closeCursor(); + + $this->assertFalse($data); + } + + public function testCreateShareWithSelf() { + $share = $this->shareManager->newShare(); + + $node = $this->getMock('\OCP\Files\File'); + $node->method('getId')->willReturn(42); + $node->method('getName')->willReturn('myFile'); + + $shareWith = 'sharedBy@' . $this->addressHandler->generateRemoteURL(); + + $share->setSharedWith($shareWith) + ->setSharedBy('sharedBy') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node); + + $this->rootFolder->expects($this->never())->method($this->anything()); + + try { + $share = $this->provider->create($share); + $this->fail(); + } catch (\Exception $e) { + $this->assertEquals('Not allowed to create a federated share with the same user', $e->getMessage()); + } + + $qb = $this->connection->getQueryBuilder(); + $stmt = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->execute(); + + $data = $stmt->fetch(); + $stmt->closeCursor(); + + $this->assertFalse($data); + } + + public function testCreateAlreadyShared() { + $share = $this->shareManager->newShare(); + + $node = $this->getMock('\OCP\Files\File'); + $node->method('getId')->willReturn(42); + $node->method('getName')->willReturn('myFile'); + + $share->setSharedWith('user@server.com') + ->setSharedBy('sharedBy') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node); + + $this->tokenHandler->method('generateToken')->willReturn('token'); + + $this->notifications->expects($this->once()) + ->method('sendRemoteShare') + ->with( + $this->equalTo('token'), + $this->equalTo('user@server.com'), + $this->equalTo('myFile'), + $this->anything(), + 'sharedBy' + )->willReturn(true); + + $this->rootFolder->expects($this->never())->method($this->anything()); + + $this->provider->create($share); + + try { + $this->provider->create($share); + } catch (\Exception $e) { + $this->assertEquals('Sharing myFile failed, because this item is already shared with user@server.com', $e->getMessage()); + } + } + + public function testUpdate() { + $share = $this->shareManager->newShare(); + + $node = $this->getMock('\OCP\Files\File'); + $node->method('getId')->willReturn(42); + $node->method('getName')->willReturn('myFile'); + + $share->setSharedWith('user@server.com') + ->setSharedBy('sharedBy') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node); + + $this->tokenHandler->method('generateToken')->willReturn('token'); + + $this->notifications->expects($this->once()) + ->method('sendRemoteShare') + ->with( + $this->equalTo('token'), + $this->equalTo('user@server.com'), + $this->equalTo('myFile'), + $this->anything(), + 'sharedBy' + )->willReturn(true); + + $this->rootFolder->expects($this->never())->method($this->anything()); + + $share = $this->provider->create($share); + + $share->setPermissions(1); + $this->provider->update($share); + + $share = $this->provider->getShareById($share->getId()); + + $this->assertEquals(1, $share->getPermissions()); + } + + public function testGetSharedBy() { + $node = $this->getMock('\OCP\Files\File'); + $node->method('getId')->willReturn(42); + $node->method('getName')->willReturn('myFile'); + + $this->tokenHandler->method('generateToken')->willReturn('token'); + $this->notifications + ->method('sendRemoteShare') + ->willReturn(true); + + $this->rootFolder->expects($this->never())->method($this->anything()); + + $share = $this->shareManager->newShare(); + $share->setSharedWith('user@server.com') + ->setSharedBy('sharedBy') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node); + $this->provider->create($share); + + $share2 = $this->shareManager->newShare(); + $share2->setSharedWith('user2@server.com') + ->setSharedBy('sharedBy2') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node); + $this->provider->create($share2); + + $shares = $this->provider->getSharesBy('sharedBy', \OCP\Share::SHARE_TYPE_REMOTE, null, false, -1, 0); + + $this->assertCount(1, $shares); + $this->assertEquals('user@server.com', $shares[0]->getSharedWith()); + $this->assertEquals('sharedBy', $shares[0]->getSharedBy()); + } + + public function testGetSharedByWithNode() { + $node = $this->getMock('\OCP\Files\File'); + $node->method('getId')->willReturn(42); + $node->method('getName')->willReturn('myFile'); + + $this->tokenHandler->method('generateToken')->willReturn('token'); + $this->notifications + ->method('sendRemoteShare') + ->willReturn(true); + + $this->rootFolder->expects($this->never())->method($this->anything()); + + $share = $this->shareManager->newShare(); + $share->setSharedWith('user@server.com') + ->setSharedBy('sharedBy') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node); + $this->provider->create($share); + + $node2 = $this->getMock('\OCP\Files\File'); + $node2->method('getId')->willReturn(43); + $node2->method('getName')->willReturn('myOtherFile'); + + $share2 = $this->shareManager->newShare(); + $share2->setSharedWith('user@server.com') + ->setSharedBy('sharedBy') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node2); + $this->provider->create($share2); + + $shares = $this->provider->getSharesBy('sharedBy', \OCP\Share::SHARE_TYPE_REMOTE, $node2, false, -1, 0); + + $this->assertCount(1, $shares); + $this->assertEquals(43, $shares[0]->getNodeId()); + } + + public function testGetSharedByWithReshares() { + $node = $this->getMock('\OCP\Files\File'); + $node->method('getId')->willReturn(42); + $node->method('getName')->willReturn('myFile'); + + $this->tokenHandler->method('generateToken')->willReturn('token'); + $this->notifications + ->method('sendRemoteShare') + ->willReturn(true); + + $this->rootFolder->expects($this->never())->method($this->anything()); + + $share = $this->shareManager->newShare(); + $share->setSharedWith('user@server.com') + ->setSharedBy('shareOwner') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node); + $this->provider->create($share); + + $share2 = $this->shareManager->newShare(); + $share2->setSharedWith('user2@server.com') + ->setSharedBy('sharedBy') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node); + $this->provider->create($share2); + + $shares = $this->provider->getSharesBy('shareOwner', \OCP\Share::SHARE_TYPE_REMOTE, null, true, -1, 0); + + $this->assertCount(2, $shares); + } + + public function testGetSharedByWithLimit() { + $node = $this->getMock('\OCP\Files\File'); + $node->method('getId')->willReturn(42); + $node->method('getName')->willReturn('myFile'); + + $this->tokenHandler->method('generateToken')->willReturn('token'); + $this->notifications + ->method('sendRemoteShare') + ->willReturn(true); + + $this->rootFolder->expects($this->never())->method($this->anything()); + + $share = $this->shareManager->newShare(); + $share->setSharedWith('user@server.com') + ->setSharedBy('sharedBy') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node); + $this->provider->create($share); + + $share2 = $this->shareManager->newShare(); + $share2->setSharedWith('user2@server.com') + ->setSharedBy('sharedBy') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node); + $this->provider->create($share2); + + $shares = $this->provider->getSharesBy('shareOwner', \OCP\Share::SHARE_TYPE_REMOTE, null, true, 1, 1); + + $this->assertCount(1, $shares); + $this->assertEquals('user2@server.com', $shares[0]->getSharedWith()); + } +} diff --git a/apps/federatedfilesharing/tests/tokenhandlertest.php b/apps/federatedfilesharing/tests/tokenhandlertest.php new file mode 100644 index 00000000000..4ff428d4a46 --- /dev/null +++ b/apps/federatedfilesharing/tests/tokenhandlertest.php @@ -0,0 +1,62 @@ +<?php +/** + * @author Björn Schießle <schiessle@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\FederatedFileSharing\Tests; + + +use OCA\FederatedFileSharing\TokenHandler; +use OCP\Security\ISecureRandom; +use Test\TestCase; + +class TokenHandlerTest extends TestCase { + + /** @var TokenHandler */ + private $tokenHandler; + + /** @var ISecureRandom | \PHPUnit_Framework_MockObject_MockObject */ + private $secureRandom; + + /** @var int */ + private $expectedTokenLength = 15; + + public function setUp() { + parent::setUp(); + + $this->secureRandom = $this->getMock('OCP\Security\ISecureRandom'); + + $this->tokenHandler = new TokenHandler($this->secureRandom); + } + + public function testGenerateToken() { + + $this->secureRandom->expects($this->once())->method('generate') + ->with( + $this->expectedTokenLength, + ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS + ) + ->willReturn(true); + + $this->assertTrue($this->tokenHandler->generateToken()); + + } + +} diff --git a/apps/files/appinfo/info.xml b/apps/files/appinfo/info.xml index 136baa2ed07..b31232b799a 100644 --- a/apps/files/appinfo/info.xml +++ b/apps/files/appinfo/info.xml @@ -6,7 +6,7 @@ <licence>AGPL</licence> <author>Robin Appelman, Vincent Petry</author> <default_enable/> - <version>1.4.2</version> + <version>1.4.3</version> <types> <filesystem/> </types> diff --git a/apps/files/appinfo/install.php b/apps/files/appinfo/install.php index b9a893d1ee8..ae08e21a22e 100644 --- a/apps/files/appinfo/install.php +++ b/apps/files/appinfo/install.php @@ -21,4 +21,4 @@ // Cron job for scanning user storages \OC::$server->getJobList()->add('OCA\Files\BackgroundJob\ScanFiles'); -\OC::$server->getJobList()->add('OCA\Files\BackgroundJob\DeleteOrphanedTagsJob'); +\OC::$server->getJobList()->add('OCA\Files\BackgroundJob\DeleteOrphanedItems'); diff --git a/apps/files/appinfo/update.php b/apps/files/appinfo/update.php index 003f6916ac5..cb682cbc426 100644 --- a/apps/files/appinfo/update.php +++ b/apps/files/appinfo/update.php @@ -99,4 +99,4 @@ if ($installedVersion === '1.1.9' && ( // Add cron job for scanning user storages \OC::$server->getJobList()->add('OCA\Files\BackgroundJob\ScanFiles'); -\OC::$server->getJobList()->add('OCA\Files\BackgroundJob\DeleteOrphanedTagsJob'); +\OC::$server->getJobList()->add('OCA\Files\BackgroundJob\DeleteOrphanedItems'); diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 4929039f837..389b7fae806 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -530,8 +530,8 @@ html.ie8 #fileList tr.selected td.filename>.selectCheckBox { } a.action > img { - max-height: 16px; - max-width: 16px; + height: 16px; + width: 16px; vertical-align: text-bottom; } diff --git a/apps/files/l10n/de_DE.js b/apps/files/l10n/de_DE.js index ce8a9132a0c..e0a3744a378 100644 --- a/apps/files/l10n/de_DE.js +++ b/apps/files/l10n/de_DE.js @@ -46,6 +46,7 @@ OC.L10N.register( "Could not move \"{file}\"" : "Die Datei konnte nicht verschoben werden \"{file}\"", "{newName} already exists" : "{newName} existiert bereits", "Could not rename \"{fileName}\", it does not exist any more" : "Die Datei konnte nicht umbennant werden \"{fileName}\", da die Datei nicht mehr existiert", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Der Name „{targetName}“ wird bereits im Ordner „{dir}“ benutzt. Bitte wählen Sie einen anderen Namen.", "Could not rename \"{fileName}\"" : "Die Datei konnte nicht umbenannt werden \"{fileName}\"", "Could not create file \"{file}\"" : "Die Datei konnte nicht erstellt werden \"{file}\"", "Could not create file \"{file}\" because it already exists" : "Die Datei konnte nicht erstellt werden \"{file}\", da diese bereits existiert", @@ -100,6 +101,7 @@ OC.L10N.register( "Maximum upload size" : "Maximale Upload-Größe", "max. possible: " : "maximal möglich:", "Save" : "Speichern", + "With PHP-FPM it might take 5 minutes for changes to be applied." : "Bei PHP-FPM kann es 5 Minuten dauern, bis Änderungen angewendet sind.", "Missing permissions to edit from here." : "Fehlende Berechtigungen um von hier aus zu bearbeiten.", "Settings" : "Einstellungen", "WebDAV" : "WebDAV", diff --git a/apps/files/l10n/de_DE.json b/apps/files/l10n/de_DE.json index f6a3abc9891..cdac7613678 100644 --- a/apps/files/l10n/de_DE.json +++ b/apps/files/l10n/de_DE.json @@ -44,6 +44,7 @@ "Could not move \"{file}\"" : "Die Datei konnte nicht verschoben werden \"{file}\"", "{newName} already exists" : "{newName} existiert bereits", "Could not rename \"{fileName}\", it does not exist any more" : "Die Datei konnte nicht umbennant werden \"{fileName}\", da die Datei nicht mehr existiert", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Der Name „{targetName}“ wird bereits im Ordner „{dir}“ benutzt. Bitte wählen Sie einen anderen Namen.", "Could not rename \"{fileName}\"" : "Die Datei konnte nicht umbenannt werden \"{fileName}\"", "Could not create file \"{file}\"" : "Die Datei konnte nicht erstellt werden \"{file}\"", "Could not create file \"{file}\" because it already exists" : "Die Datei konnte nicht erstellt werden \"{file}\", da diese bereits existiert", @@ -98,6 +99,7 @@ "Maximum upload size" : "Maximale Upload-Größe", "max. possible: " : "maximal möglich:", "Save" : "Speichern", + "With PHP-FPM it might take 5 minutes for changes to be applied." : "Bei PHP-FPM kann es 5 Minuten dauern, bis Änderungen angewendet sind.", "Missing permissions to edit from here." : "Fehlende Berechtigungen um von hier aus zu bearbeiten.", "Settings" : "Einstellungen", "WebDAV" : "WebDAV", diff --git a/apps/files/lib/backgroundjob/deleteorphanedtagsjob.php b/apps/files/lib/backgroundjob/deleteorphaneditems.php index 33f455b5b40..cefa1d655de 100644 --- a/apps/files/lib/backgroundjob/deleteorphanedtagsjob.php +++ b/apps/files/lib/backgroundjob/deleteorphaneditems.php @@ -22,11 +22,12 @@ namespace OCA\Files\BackgroundJob; use OC\BackgroundJob\TimedJob; +use OCP\DB\QueryBuilder\IQueryBuilder; /** * Delete all share entries that have no matching entries in the file cache table. */ -class DeleteOrphanedTagsJob extends TimedJob { +class DeleteOrphanedItems extends TimedJob { /** @var \OCP\IDBConnection */ protected $connection; @@ -58,6 +59,8 @@ class DeleteOrphanedTagsJob extends TimedJob { public function run($argument) { $this->cleanSystemTags(); $this->cleanUserTags(); + $this->cleanComments(); + $this->cleanCommentMarkers(); } /** @@ -65,19 +68,29 @@ class DeleteOrphanedTagsJob extends TimedJob { * * @return int Number of deleted entries */ - protected function cleanSystemTags() { + protected function cleanUp($table, $idCol, $typeCol) { $subQuery = $this->connection->getQueryBuilder(); $subQuery->select($subQuery->expr()->literal('1')) ->from('filecache', 'f') - ->where($subQuery->expr()->eq('objectid', 'f.fileid')); + ->where($subQuery->expr()->eq($idCol, 'f.fileid')); $query = $this->connection->getQueryBuilder(); - $deletedEntries = $query->delete('systemtag_object_mapping') - ->where($query->expr()->eq('objecttype', $query->expr()->literal('files'))) + $deletedEntries = $query->delete($table) + ->where($query->expr()->eq($typeCol, $query->expr()->literal('files'))) ->andWhere($query->expr()->isNull($query->createFunction('(' . $subQuery->getSql() . ')'))) ->execute(); - $this->logger->debug("$deletedEntries orphaned system tag relations deleted", ['app' => 'DeleteOrphanedTagsJob']); + return $deletedEntries; + } + + /** + * Deleting orphaned system tag mappings + * + * @return int Number of deleted entries + */ + protected function cleanSystemTags() { + $deletedEntries = $this->cleanUp('systemtag_object_mapping', 'objectid', 'objecttype'); + $this->logger->debug("$deletedEntries orphaned system tag relations deleted", ['app' => 'DeleteOrphanedItems']); return $deletedEntries; } @@ -87,18 +100,32 @@ class DeleteOrphanedTagsJob extends TimedJob { * @return int Number of deleted entries */ protected function cleanUserTags() { - $subQuery = $this->connection->getQueryBuilder(); - $subQuery->select($subQuery->expr()->literal('1')) - ->from('filecache', 'f') - ->where($subQuery->expr()->eq('objid', 'f.fileid')); + $deletedEntries = $this->cleanUp('vcategory_to_object', 'objid', 'type'); + $this->logger->debug("$deletedEntries orphaned user tag relations deleted", ['app' => 'DeleteOrphanedItems']); + return $deletedEntries; + } - $query = $this->connection->getQueryBuilder(); - $deletedEntries = $query->delete('vcategory_to_object') - ->where($query->expr()->eq('type', $query->expr()->literal('files'))) - ->andWhere($query->expr()->isNull($query->createFunction('(' . $subQuery->getSql() . ')'))) - ->execute(); + /** + * Deleting orphaned comments + * + * @return int Number of deleted entries + */ + protected function cleanComments() { + $qb = $this->connection->getQueryBuilder(); + $deletedEntries = $this->cleanUp('comments', $qb->expr()->castColumn('object_id', IQueryBuilder::PARAM_INT), 'object_type'); + $this->logger->debug("$deletedEntries orphaned comments deleted", ['app' => 'DeleteOrphanedItems']); + return $deletedEntries; + } - $this->logger->debug("$deletedEntries orphaned user tag relations deleted", ['app' => 'DeleteOrphanedTagsJob']); + /** + * Deleting orphaned comment read markers + * + * @return int Number of deleted entries + */ + protected function cleanCommentMarkers() { + $qb = $this->connection->getQueryBuilder(); + $deletedEntries = $this->cleanUp('comments_read_markers', $qb->expr()->castColumn('object_id', IQueryBuilder::PARAM_INT), 'object_type'); + $this->logger->debug("$deletedEntries orphaned comment read marks deleted", ['app' => 'DeleteOrphanedItems']); return $deletedEntries; } diff --git a/apps/files/tests/backgroundjob/DeleteOrphanedTagsJobTest.php b/apps/files/tests/backgroundjob/DeleteOrphanedItemsJobTest.php index d2e9d77cb20..8d8e5d3be52 100644 --- a/apps/files/tests/backgroundjob/DeleteOrphanedTagsJobTest.php +++ b/apps/files/tests/backgroundjob/DeleteOrphanedItemsJobTest.php @@ -21,17 +21,17 @@ namespace OCA\Files\Tests\BackgroundJob; -use OCA\Files\BackgroundJob\DeleteOrphanedTagsJob; +use OCA\Files\BackgroundJob\DeleteOrphanedItems; use OCP\DB\QueryBuilder\IQueryBuilder; /** - * Class DeleteOrphanedTagsJobTest + * Class DeleteOrphanedItemsJobTest * * @group DB * * @package Test\BackgroundJob */ -class DeleteOrphanedTagsJobTest extends \Test\TestCase { +class DeleteOrphanedItemsJobTest extends \Test\TestCase { /** @var \OCP\IDBConnection */ protected $connection; @@ -93,7 +93,7 @@ class DeleteOrphanedTagsJobTest extends \Test\TestCase { $mapping = $this->getMappings('systemtag_object_mapping'); $this->assertCount(2, $mapping); - $job = new DeleteOrphanedTagsJob(); + $job = new DeleteOrphanedItems(); $this->invokePrivate($job, 'cleanSystemTags'); $mapping = $this->getMappings('systemtag_object_mapping'); @@ -142,7 +142,7 @@ class DeleteOrphanedTagsJobTest extends \Test\TestCase { $mapping = $this->getMappings('vcategory_to_object'); $this->assertCount(2, $mapping); - $job = new DeleteOrphanedTagsJob(); + $job = new DeleteOrphanedItems(); $this->invokePrivate($job, 'cleanUserTags'); $mapping = $this->getMappings('vcategory_to_object'); @@ -155,4 +155,104 @@ class DeleteOrphanedTagsJobTest extends \Test\TestCase { $this->cleanMapping('vcategory_to_object'); } + /** + * Test clearing orphaned system tag mappings + */ + public function testClearComments() { + $this->cleanMapping('comments'); + + $query = $this->connection->getQueryBuilder(); + $query->insert('filecache') + ->values([ + 'storage' => $query->createNamedParameter(1337, IQueryBuilder::PARAM_INT), + 'path' => $query->createNamedParameter('apps/files/tests/deleteorphanedtagsjobtest.php'), + 'path_hash' => $query->createNamedParameter(md5('apps/files/tests/deleteorphanedtagsjobtest.php')), + ])->execute(); + $fileId = $query->getLastInsertId(); + + // Existing file + $query = $this->connection->getQueryBuilder(); + $query->insert('comments') + ->values([ + 'object_id' => $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT), + 'object_type' => $query->createNamedParameter('files'), + 'actor_id' => $query->createNamedParameter('Alice', IQueryBuilder::PARAM_INT), + 'actor_type' => $query->createNamedParameter('users'), + ])->execute(); + + // Non-existing file + $query = $this->connection->getQueryBuilder(); + $query->insert('comments') + ->values([ + 'object_id' => $query->createNamedParameter($fileId + 1, IQueryBuilder::PARAM_INT), + 'object_type' => $query->createNamedParameter('files'), + 'actor_id' => $query->createNamedParameter('Alice', IQueryBuilder::PARAM_INT), + 'actor_type' => $query->createNamedParameter('users'), + ])->execute(); + + $mapping = $this->getMappings('comments'); + $this->assertCount(2, $mapping); + + $job = new DeleteOrphanedItems(); + $this->invokePrivate($job, 'cleanComments'); + + $mapping = $this->getMappings('comments'); + $this->assertCount(1, $mapping); + + $query = $this->connection->getQueryBuilder(); + $query->delete('filecache') + ->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))) + ->execute(); + $this->cleanMapping('comments'); + } + + /** + * Test clearing orphaned system tag mappings + */ + public function testClearCommentReadMarks() { + $this->cleanMapping('comments_read_markers'); + + $query = $this->connection->getQueryBuilder(); + $query->insert('filecache') + ->values([ + 'storage' => $query->createNamedParameter(1337, IQueryBuilder::PARAM_INT), + 'path' => $query->createNamedParameter('apps/files/tests/deleteorphanedtagsjobtest.php'), + 'path_hash' => $query->createNamedParameter(md5('apps/files/tests/deleteorphanedtagsjobtest.php')), + ])->execute(); + $fileId = $query->getLastInsertId(); + + // Existing file + $query = $this->connection->getQueryBuilder(); + $query->insert('comments_read_markers') + ->values([ + 'object_id' => $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT), + 'object_type' => $query->createNamedParameter('files'), + 'user_id' => $query->createNamedParameter('Alice', IQueryBuilder::PARAM_INT), + ])->execute(); + + // Non-existing file + $query = $this->connection->getQueryBuilder(); + $query->insert('comments_read_markers') + ->values([ + 'object_id' => $query->createNamedParameter($fileId + 1, IQueryBuilder::PARAM_INT), + 'object_type' => $query->createNamedParameter('files'), + 'user_id' => $query->createNamedParameter('Alice', IQueryBuilder::PARAM_INT), + ])->execute(); + + $mapping = $this->getMappings('comments_read_markers'); + $this->assertCount(2, $mapping); + + $job = new DeleteOrphanedItems(); + $this->invokePrivate($job, 'cleanCommentMarkers'); + + $mapping = $this->getMappings('comments_read_markers'); + $this->assertCount(1, $mapping); + + $query = $this->connection->getQueryBuilder(); + $query->delete('filecache') + ->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))) + ->execute(); + $this->cleanMapping('comments_read_markers'); + } + } diff --git a/apps/files_external/appinfo/register_command.php b/apps/files_external/appinfo/register_command.php index 929becce77a..927ce9869f9 100644 --- a/apps/files_external/appinfo/register_command.php +++ b/apps/files_external/appinfo/register_command.php @@ -27,6 +27,9 @@ use OCA\Files_External\Command\Applicable; use OCA\Files_External\Command\Import; use OCA\Files_External\Command\Export; use OCA\Files_External\Command\Delete; +use OCA\Files_External\Command\Create; +use OCA\Files_External\Command\Backends; +use OCA\Files_External\Command\Verify; $userManager = OC::$server->getUserManager(); $userSession = OC::$server->getUserSession(); @@ -47,3 +50,6 @@ $application->add(new Applicable($globalStorageService, $userManager, $groupMana $application->add(new Import($globalStorageService, $userStorageService, $userSession, $userManager, $importLegacyStorageService, $backendService)); $application->add(new Export($globalStorageService, $userStorageService, $userSession, $userManager)); $application->add(new Delete($globalStorageService, $userStorageService, $userSession, $userManager)); +$application->add(new Create($globalStorageService, $userStorageService, $userManager, $userSession, $backendService)); +$application->add(new Backends($backendService)); +$application->add(new Verify($globalStorageService)); diff --git a/apps/files_external/command/backends.php b/apps/files_external/command/backends.php new file mode 100644 index 00000000000..fd11c36ec40 --- /dev/null +++ b/apps/files_external/command/backends.php @@ -0,0 +1,112 @@ +<?php +/** + * @author Robin Appelman <icewind@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\Files_External\Command; + +use OC\Core\Command\Base; +use OCA\Files_External\Lib\Auth\AuthMechanism; +use OCA\Files_External\Lib\Backend\Backend; +use OCA\Files_External\Lib\DefinitionParameter; +use OCA\Files_External\Service\BackendService; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableHelper; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\Input; +use Symfony\Component\Console\Output\OutputInterface; + +class Backends extends Base { + /** @var BackendService */ + private $backendService; + + function __construct(BackendService $backendService + ) { + parent::__construct(); + + $this->backendService = $backendService; + } + + protected function configure() { + $this + ->setName('files_external:backends') + ->setDescription('Show available authentication and storage backends') + ->addArgument( + 'type', + InputArgument::OPTIONAL, + 'only show backends of a certain type. Possible values are "authentication" or "storage"' + )->addArgument( + 'backend', + InputArgument::OPTIONAL, + 'only show information of a specific backend' + ); + parent::configure(); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $authBackends = $this->backendService->getAuthMechanisms(); + $storageBackends = $this->backendService->getBackends(); + + $data = [ + 'authentication' => array_map([$this, 'serializeAuthBackend'], $authBackends), + 'storage' => array_map([$this, 'serializeAuthBackend'], $storageBackends) + ]; + + $type = $input->getArgument('type'); + $backend = $input->getArgument('backend'); + if ($type) { + if (!isset($data[$type])) { + $output->writeln('<error>Invalid type "' . $type . '". Possible values are "authentication" or "storage"</error>'); + return 1; + } + $data = $data[$type]; + + if ($backend) { + if (!isset($data[$backend])) { + $output->writeln('<error>Unknown backend "' . $backend . '" of type "' . $type . '"</error>'); + return 1; + } + $data = $data[$backend]; + } + } + + $this->writeArrayInOutputFormat($input, $output, $data); + } + + private function serializeAuthBackend(\JsonSerializable $backend) { + $data = $backend->jsonSerialize(); + $result = [ + 'name' => $data['name'], + 'identifier' => $data['identifier'], + 'configuration' => array_map(function (DefinitionParameter $parameter) { + return $parameter->getTypeName(); + }, $data['configuration']) + ]; + if ($backend instanceof Backend) { + $result['storage_class'] = $backend->getStorageClass(); + $authBackends = $this->backendService->getAuthMechanismsByScheme(array_keys($backend->getAuthSchemes())); + $result['supported_authentication_backends'] = array_keys($authBackends); + } + return $result; + } +} diff --git a/apps/files_external/command/create.php b/apps/files_external/command/create.php new file mode 100644 index 00000000000..4b0ef99ff3a --- /dev/null +++ b/apps/files_external/command/create.php @@ -0,0 +1,226 @@ +<?php +/** + * @author Robin Appelman <icewind@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\Files_External\Command; + +use OC\Core\Command\Base; +use OC\Files\Filesystem; +use OC\User\NoUserException; +use OCA\Files_External\Lib\Auth\AuthMechanism; +use OCA\Files_External\Lib\Backend\Backend; +use OCA\Files_External\Lib\DefinitionParameter; +use OCA\Files_external\Lib\StorageConfig; +use OCA\Files_External\Service\BackendService; +use OCA\Files_external\Service\GlobalStoragesService; +use OCA\Files_external\Service\ImportLegacyStoragesService; +use OCA\Files_external\Service\UserStoragesService; +use OCP\IUserManager; +use OCP\IUserSession; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableHelper; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\Input; +use Symfony\Component\Console\Output\OutputInterface; + +class Create extends Base { + /** + * @var GlobalStoragesService + */ + private $globalService; + + /** + * @var UserStoragesService + */ + private $userService; + + /** + * @var IUserManager + */ + private $userManager; + + /** @var BackendService */ + private $backendService; + + /** @var IUserSession */ + private $userSession; + + function __construct(GlobalStoragesService $globalService, + UserStoragesService $userService, + IUserManager $userManager, + IUserSession $userSession, + BackendService $backendService + ) { + parent::__construct(); + $this->globalService = $globalService; + $this->userService = $userService; + $this->userManager = $userManager; + $this->userSession = $userSession; + $this->backendService = $backendService; + } + + protected function configure() { + $this + ->setName('files_external:create') + ->setDescription('Create a new mount configuration') + ->addOption( + 'user', + null, + InputOption::VALUE_OPTIONAL, + 'user to add the mount configuration for, if not set the mount will be added as system mount' + ) + ->addArgument( + 'mount_point', + InputArgument::REQUIRED, + 'mount point for the new mount' + ) + ->addArgument( + 'storage_backend', + InputArgument::REQUIRED, + 'storage backend identifier for the new mount, see `occ files_external:backends` for possible values' + ) + ->addArgument( + 'authentication_backend', + InputArgument::REQUIRED, + 'authentication backend identifier for the new mount, see `occ files_external:backends` for possible values' + ) + ->addOption( + 'config', + 'c', + InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'Mount configuration option in key=value format' + ) + ->addOption( + 'dry', + null, + InputOption::VALUE_NONE, + 'Don\'t save the created mount, only list the new mount' + ); + parent::configure(); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $user = $input->getOption('user'); + $mountPoint = $input->getArgument('mount_point'); + $storageIdentifier = $input->getArgument('storage_backend'); + $authIdentifier = $input->getArgument('authentication_backend'); + $configInput = $input->getOption('config'); + + $storageBackend = $this->backendService->getBackend($storageIdentifier); + $authBackend = $this->backendService->getAuthMechanism($authIdentifier); + + if (!Filesystem::isValidPath($mountPoint)) { + $output->writeln('<error>Invalid mountpoint "' . $mountPoint . '"</error>'); + return 1; + } + if (is_null($storageBackend)) { + $output->writeln('<error>Storage backend with identifier "' . $storageIdentifier . '" not found (see `occ files_external:backends` for possible values)</error>'); + return 404; + } + if (is_null($authBackend)) { + $output->writeln('<error>Authentication backend with identifier "' . $authIdentifier . '" not found (see `occ files_external:backends` for possible values)</error>'); + return 404; + } + $supportedSchemes = array_keys($storageBackend->getAuthSchemes()); + if (!in_array($authBackend->getScheme(), $supportedSchemes)) { + $output->writeln('<error>Authentication backend "' . $authIdentifier . '" not valid for storage backend "' . $storageIdentifier . '" (see `occ files_external:backends storage ' . $storageIdentifier . '` for possible values)</error>'); + return 1; + } + + $config = []; + foreach ($configInput as $configOption) { + if (!strpos($configOption, '=')) { + $output->writeln('<error>Invalid mount configuration option "' . $configOption . '"</error>'); + return 1; + } + list($key, $value) = explode('=', $configOption, 2); + if (!$this->validateParam($key, $value, $storageBackend, $authBackend)) { + $output->writeln('<error>Unknown configuration for backends "' . $key . '"</error>'); + return 1; + } + $config[$key] = $value; + } + + $mount = new StorageConfig(); + $mount->setMountPoint($mountPoint); + $mount->setBackend($storageBackend); + $mount->setAuthMechanism($authBackend); + $mount->setBackendOptions($config); + + if ($user) { + if (!$this->userManager->userExists($user)) { + $output->writeln('<error>User "' . $user . '" not found</error>'); + return 1; + } + $mount->setApplicableUsers([$user]); + } + + if ($input->getOption('dry')) { + $this->showMount($user, $mount, $input, $output); + } else { + $this->getStorageService($user)->addStorage($mount); + if ($input->getOption('output') === self::OUTPUT_FORMAT_PLAIN) { + $output->writeln('<info>Storage created with id ' . $mount->getId() . '</info>'); + } else { + $output->writeln($mount->getId()); + } + } + return 0; + } + + private function validateParam($key, &$value, Backend $storageBackend, AuthMechanism $authBackend) { + $params = array_merge($storageBackend->getParameters(), $authBackend->getParameters()); + foreach ($params as $param) { + /** @var DefinitionParameter $param */ + if ($param->getName() === $key) { + if ($param->getType() === DefinitionParameter::VALUE_BOOLEAN) { + $value = ($value === 'true'); + } + return true; + } + } + return false; + } + + private function showMount($user, StorageConfig $mount, InputInterface $input, OutputInterface $output) { + $listCommand = new ListCommand($this->globalService, $this->userService, $this->userSession, $this->userManager); + $listInput = new ArrayInput([], $listCommand->getDefinition()); + $listInput->setOption('output', $input->getOption('output')); + $listInput->setOption('show-password', true); + $listCommand->listMounts($user, [$mount], $listInput, $output); + } + + protected function getStorageService($userId) { + if (!empty($userId)) { + $user = $this->userManager->get($userId); + if (is_null($user)) { + throw new NoUserException("user $userId not found"); + } + $this->userSession->setUser($user); + return $this->userService; + } else { + return $this->globalService; + } + } +} diff --git a/apps/files_external/command/verify.php b/apps/files_external/command/verify.php new file mode 100644 index 00000000000..f985cb401af --- /dev/null +++ b/apps/files_external/command/verify.php @@ -0,0 +1,145 @@ +<?php +/** + * @author Robin Appelman <icewind@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\Files_External\Command; + +use OC\Core\Command\Base; +use OCA\Files_External\Lib\Auth\AuthMechanism; +use OCA\Files_External\Lib\Backend\Backend; +use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException; +use OCA\Files_external\Lib\StorageConfig; +use OCA\Files_external\NotFoundException; +use OCA\Files_external\Service\GlobalStoragesService; +use OCP\Files\StorageNotAvailableException; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class Verify extends Base { + /** + * @var GlobalStoragesService + */ + protected $globalService; + + function __construct(GlobalStoragesService $globalService) { + parent::__construct(); + $this->globalService = $globalService; + } + + protected function configure() { + $this + ->setName('files_external:verify') + ->setDescription('Verify mount configuration') + ->addArgument( + 'mount_id', + InputArgument::REQUIRED, + 'The id of the mount to check' + )->addOption( + 'config', + 'c', + InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'Additional config option to set before checking in key=value pairs, required for certain auth backends such as login credentails' + ); + parent::configure(); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $mountId = $input->getArgument('mount_id'); + $configInput = $input->getOption('config'); + + try { + $mount = $this->globalService->getStorage($mountId); + } catch (NotFoundException $e) { + $output->writeln('<error>Mount with id "' . $mountId . ' not found, check "occ files_external:list" to get available mounts"</error>'); + return 404; + } + + $this->updateStorageStatus($mount, $configInput, $output); + + $this->writeArrayInOutputFormat($input, $output, [ + 'status' => StorageNotAvailableException::getStateCodeName($mount->getStatus()), + 'code' => $mount->getStatus(), + 'message' => $mount->getStatusMessage() + ]); + } + + private function manipulateStorageConfig(StorageConfig $storage) { + /** @var AuthMechanism */ + $authMechanism = $storage->getAuthMechanism(); + $authMechanism->manipulateStorageConfig($storage); + /** @var Backend */ + $backend = $storage->getBackend(); + $backend->manipulateStorageConfig($storage); + } + + private function updateStorageStatus(StorageConfig &$storage, $configInput, OutputInterface $output) { + try { + try { + $this->manipulateStorageConfig($storage); + } catch (InsufficientDataForMeaningfulAnswerException $e) { + if (count($configInput) === 0) { // extra config options might solve the error + throw $e; + } + } + + foreach ($configInput as $configOption) { + if (!strpos($configOption, '=')) { + $output->writeln('<error>Invalid mount configuration option "' . $configOption . '"</error>'); + return; + } + list($key, $value) = explode('=', $configOption, 2); + $storage->setBackendOption($key, $value); + } + + /** @var Backend */ + $backend = $storage->getBackend(); + // update status (can be time-consuming) + $storage->setStatus( + \OC_Mount_Config::getBackendStatus( + $backend->getStorageClass(), + $storage->getBackendOptions(), + false + ) + ); + } catch (InsufficientDataForMeaningfulAnswerException $e) { + $status = $e->getCode() ? $e->getCode() : StorageNotAvailableException::STATUS_INDETERMINATE; + $storage->setStatus( + $status, + $e->getMessage() + ); + } catch (StorageNotAvailableException $e) { + $storage->setStatus( + $e->getCode(), + $e->getMessage() + ); + } catch (\Exception $e) { + // FIXME: convert storage exceptions to StorageNotAvailableException + $storage->setStatus( + StorageNotAvailableException::STATUS_ERROR, + get_class($e) . ': ' . $e->getMessage() + ); + } + } +} diff --git a/apps/files_external/l10n/af_ZA.js b/apps/files_external/l10n/af_ZA.js index d28e14d67ce..25117ae0ea7 100644 --- a/apps/files_external/l10n/af_ZA.js +++ b/apps/files_external/l10n/af_ZA.js @@ -2,6 +2,7 @@ OC.L10N.register( "files_external", { "Personal" : "Persoonlik", + "Saving..." : "Stoor...", "Username" : "Gebruikersnaam", "Password" : "Wagwoord", "Share" : "Deel" diff --git a/apps/files_external/l10n/af_ZA.json b/apps/files_external/l10n/af_ZA.json index 53622f796ff..f55ea5fd515 100644 --- a/apps/files_external/l10n/af_ZA.json +++ b/apps/files_external/l10n/af_ZA.json @@ -1,5 +1,6 @@ { "translations": { "Personal" : "Persoonlik", + "Saving..." : "Stoor...", "Username" : "Gebruikersnaam", "Password" : "Wagwoord", "Share" : "Deel" diff --git a/apps/files_external/l10n/ar.js b/apps/files_external/l10n/ar.js index 3e7438796f4..5317a463fd5 100644 --- a/apps/files_external/l10n/ar.js +++ b/apps/files_external/l10n/ar.js @@ -5,11 +5,13 @@ OC.L10N.register( "System" : "النظام", "Never" : "أبدا", "Saved" : "حفظ", + "Saving..." : "جاري الحفظ...", + "Save" : "حفظ", + "Username" : "إسم المستخدم", + "Password" : "كلمة السر", "None" : "لا شيء", "App key" : "مفتاح التطبيق", "App secret" : "التطبيق السري", - "Username" : "إسم المستخدم", - "Password" : "كلمة السر", "Bucket" : "الحزمة", "Hostname" : "إسم الإستضافة", "Port" : "المنفذ", diff --git a/apps/files_external/l10n/ar.json b/apps/files_external/l10n/ar.json index 29423a07906..35573c98be5 100644 --- a/apps/files_external/l10n/ar.json +++ b/apps/files_external/l10n/ar.json @@ -3,11 +3,13 @@ "System" : "النظام", "Never" : "أبدا", "Saved" : "حفظ", + "Saving..." : "جاري الحفظ...", + "Save" : "حفظ", + "Username" : "إسم المستخدم", + "Password" : "كلمة السر", "None" : "لا شيء", "App key" : "مفتاح التطبيق", "App secret" : "التطبيق السري", - "Username" : "إسم المستخدم", - "Password" : "كلمة السر", "Bucket" : "الحزمة", "Hostname" : "إسم الإستضافة", "Port" : "المنفذ", diff --git a/apps/files_external/l10n/ast.js b/apps/files_external/l10n/ast.js index f67309631e8..e39088432a3 100644 --- a/apps/files_external/l10n/ast.js +++ b/apps/files_external/l10n/ast.js @@ -11,13 +11,15 @@ OC.L10N.register( "All users. Type to select user or group." : "Tolos usuarios. Escribe pa seleccionar usuariu o grupu.", "(group)" : "(grupu)", "Saved" : "Guardáu", + "Saving..." : "Guardando...", + "Save" : "Guardar", + "Username" : "Nome d'usuariu", + "Password" : "Contraseña", "None" : "Dengún", "App key" : "App principal", "App secret" : "App secreta", "Client ID" : "ID de veceru", "Client secret" : "Veceru secretu", - "Username" : "Nome d'usuariu", - "Password" : "Contraseña", "API key" : "clave API", "Public key" : "Clave pública", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/ast.json b/apps/files_external/l10n/ast.json index b4bc4355bc4..66b7beff665 100644 --- a/apps/files_external/l10n/ast.json +++ b/apps/files_external/l10n/ast.json @@ -9,13 +9,15 @@ "All users. Type to select user or group." : "Tolos usuarios. Escribe pa seleccionar usuariu o grupu.", "(group)" : "(grupu)", "Saved" : "Guardáu", + "Saving..." : "Guardando...", + "Save" : "Guardar", + "Username" : "Nome d'usuariu", + "Password" : "Contraseña", "None" : "Dengún", "App key" : "App principal", "App secret" : "App secreta", "Client ID" : "ID de veceru", "Client secret" : "Veceru secretu", - "Username" : "Nome d'usuariu", - "Password" : "Contraseña", "API key" : "clave API", "Public key" : "Clave pública", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/az.js b/apps/files_external/l10n/az.js index 42c9a496185..409d5e52425 100644 --- a/apps/files_external/l10n/az.js +++ b/apps/files_external/l10n/az.js @@ -16,13 +16,15 @@ OC.L10N.register( "All users. Type to select user or group." : "Sistem istifadəçiləri. Daxil edin ki, istifadəçi və ya qrupu seçəsiniz.", "(group)" : "(qrup)", "Saved" : "Saxlanıldı", + "Saving..." : "Saxlama...", + "Save" : "Saxla", + "Username" : "İstifadəçi adı", + "Password" : "Şifrə", "None" : "Heç bir", "App key" : "Proqram açarı", "App secret" : "Proqram sirri", "Client ID" : "Müştəri İD-s", "Client secret" : "Müxtəri sirri", - "Username" : "İstifadəçi adı", - "Password" : "Şifrə", "Public key" : "İctimai açar", "Amazon S3" : "Amazon S3", "Bucket" : "Vedrə", diff --git a/apps/files_external/l10n/az.json b/apps/files_external/l10n/az.json index 4e01cdf954c..cb8e6bdbfd8 100644 --- a/apps/files_external/l10n/az.json +++ b/apps/files_external/l10n/az.json @@ -14,13 +14,15 @@ "All users. Type to select user or group." : "Sistem istifadəçiləri. Daxil edin ki, istifadəçi və ya qrupu seçəsiniz.", "(group)" : "(qrup)", "Saved" : "Saxlanıldı", + "Saving..." : "Saxlama...", + "Save" : "Saxla", + "Username" : "İstifadəçi adı", + "Password" : "Şifrə", "None" : "Heç bir", "App key" : "Proqram açarı", "App secret" : "Proqram sirri", "Client ID" : "Müştəri İD-s", "Client secret" : "Müxtəri sirri", - "Username" : "İstifadəçi adı", - "Password" : "Şifrə", "Public key" : "İctimai açar", "Amazon S3" : "Amazon S3", "Bucket" : "Vedrə", diff --git a/apps/files_external/l10n/bg_BG.js b/apps/files_external/l10n/bg_BG.js index cc52682f956..c3c608449f4 100644 --- a/apps/files_external/l10n/bg_BG.js +++ b/apps/files_external/l10n/bg_BG.js @@ -15,13 +15,15 @@ OC.L10N.register( "All users. Type to select user or group." : "Всички потребители. Пиши, за да избереш потребител или група.", "(group)" : "(група)", "Saved" : "Запазено", + "Saving..." : "Запазване...", + "Save" : "Запазване", + "Username" : "Потребителско Име", + "Password" : "Парола", "None" : "Няма", "App key" : "App key", "App secret" : "App secret", "Client ID" : "Client ID", "Client secret" : "Client secret", - "Username" : "Потребителско Име", - "Password" : "Парола", "API key" : "API ключ", "Public key" : "Публичен ключ", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/bg_BG.json b/apps/files_external/l10n/bg_BG.json index df3fe1c20e8..d2495ea1e90 100644 --- a/apps/files_external/l10n/bg_BG.json +++ b/apps/files_external/l10n/bg_BG.json @@ -13,13 +13,15 @@ "All users. Type to select user or group." : "Всички потребители. Пиши, за да избереш потребител или група.", "(group)" : "(група)", "Saved" : "Запазено", + "Saving..." : "Запазване...", + "Save" : "Запазване", + "Username" : "Потребителско Име", + "Password" : "Парола", "None" : "Няма", "App key" : "App key", "App secret" : "App secret", "Client ID" : "Client ID", "Client secret" : "Client secret", - "Username" : "Потребителско Име", - "Password" : "Парола", "API key" : "API ключ", "Public key" : "Публичен ключ", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/bn_BD.js b/apps/files_external/l10n/bn_BD.js index 32d40f3fb8a..6236acedb4d 100644 --- a/apps/files_external/l10n/bn_BD.js +++ b/apps/files_external/l10n/bn_BD.js @@ -8,12 +8,14 @@ OC.L10N.register( "Access granted" : "অধিগমনের অনুমতি প্রদান করা হলো", "(group)" : "(গোষ্ঠি)", "Saved" : "সংরক্ষণ করা হলো", + "Saving..." : "সংরক্ষণ করা হচ্ছে..", + "Save" : "সংরক্ষণ", + "Username" : "ব্যবহারকারী", + "Password" : "কূটশব্দ", "None" : "কোনটিই নয়", "App key" : "অ্যাপ কি", "App secret" : "অ্যাপ সিক্রেট", "Client ID" : "ক্লায়েন্ট পরিচিতি", - "Username" : "ব্যবহারকারী", - "Password" : "কূটশব্দ", "Amazon S3" : "আমাজন S3", "Bucket" : "বালতি", "Hostname" : "হোস্টনেম", diff --git a/apps/files_external/l10n/bn_BD.json b/apps/files_external/l10n/bn_BD.json index 3a2d8d4f2af..0ffdadc6c00 100644 --- a/apps/files_external/l10n/bn_BD.json +++ b/apps/files_external/l10n/bn_BD.json @@ -6,12 +6,14 @@ "Access granted" : "অধিগমনের অনুমতি প্রদান করা হলো", "(group)" : "(গোষ্ঠি)", "Saved" : "সংরক্ষণ করা হলো", + "Saving..." : "সংরক্ষণ করা হচ্ছে..", + "Save" : "সংরক্ষণ", + "Username" : "ব্যবহারকারী", + "Password" : "কূটশব্দ", "None" : "কোনটিই নয়", "App key" : "অ্যাপ কি", "App secret" : "অ্যাপ সিক্রেট", "Client ID" : "ক্লায়েন্ট পরিচিতি", - "Username" : "ব্যবহারকারী", - "Password" : "কূটশব্দ", "Amazon S3" : "আমাজন S3", "Bucket" : "বালতি", "Hostname" : "হোস্টনেম", diff --git a/apps/files_external/l10n/bn_IN.js b/apps/files_external/l10n/bn_IN.js index 31f903204aa..68b491e3ff5 100644 --- a/apps/files_external/l10n/bn_IN.js +++ b/apps/files_external/l10n/bn_IN.js @@ -2,6 +2,8 @@ OC.L10N.register( "files_external", { "Saved" : "সংরক্ষিত", + "Saving..." : "সংরক্ষণ করা হচ্ছে ...", + "Save" : "সেভ", "Username" : "ইউজারনেম", "URL" : "URL", "Host" : "হোস্ট", diff --git a/apps/files_external/l10n/bn_IN.json b/apps/files_external/l10n/bn_IN.json index be89b6f43ea..c34e1711140 100644 --- a/apps/files_external/l10n/bn_IN.json +++ b/apps/files_external/l10n/bn_IN.json @@ -1,5 +1,7 @@ { "translations": { "Saved" : "সংরক্ষিত", + "Saving..." : "সংরক্ষণ করা হচ্ছে ...", + "Save" : "সেভ", "Username" : "ইউজারনেম", "URL" : "URL", "Host" : "হোস্ট", diff --git a/apps/files_external/l10n/bs.js b/apps/files_external/l10n/bs.js index 2cd3cf47509..26ad4915e96 100644 --- a/apps/files_external/l10n/bs.js +++ b/apps/files_external/l10n/bs.js @@ -3,9 +3,11 @@ OC.L10N.register( { "Personal" : "Osobno", "Saved" : "Spremljeno", - "None" : "Ništa", + "Saving..." : "Spremanje...", + "Save" : "Spremi", "Username" : "Korisničko ime", "Password" : "Lozinka", + "None" : "Ništa", "Port" : "Priključak", "WebDAV" : "WebDAV", "Local" : "Lokalno", diff --git a/apps/files_external/l10n/bs.json b/apps/files_external/l10n/bs.json index e2d76555f08..94edcb1ea59 100644 --- a/apps/files_external/l10n/bs.json +++ b/apps/files_external/l10n/bs.json @@ -1,9 +1,11 @@ { "translations": { "Personal" : "Osobno", "Saved" : "Spremljeno", - "None" : "Ništa", + "Saving..." : "Spremanje...", + "Save" : "Spremi", "Username" : "Korisničko ime", "Password" : "Lozinka", + "None" : "Ništa", "Port" : "Priključak", "WebDAV" : "WebDAV", "Local" : "Lokalno", diff --git a/apps/files_external/l10n/ca.js b/apps/files_external/l10n/ca.js index ff11ac0228b..72f8bb62c9b 100644 --- a/apps/files_external/l10n/ca.js +++ b/apps/files_external/l10n/ca.js @@ -21,13 +21,15 @@ OC.L10N.register( "All users. Type to select user or group." : "Tots els usuaris. Escriu per seleccionar un usuari o grup.", "(group)" : "(grup)", "Saved" : "Desat", + "Saving..." : "Desant...", + "Save" : "Desa", + "Username" : "Nom d'usuari", + "Password" : "Contrasenya", "None" : "Cap", "App key" : "Clau de l'aplicació", "App secret" : "Secret de l'aplicació", "Client ID" : "Client ID", "Client secret" : "Secret del client", - "Username" : "Nom d'usuari", - "Password" : "Contrasenya", "API key" : "codi API", "Public key" : "Clau pública", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/ca.json b/apps/files_external/l10n/ca.json index a64721a4bf5..647d2ea27b8 100644 --- a/apps/files_external/l10n/ca.json +++ b/apps/files_external/l10n/ca.json @@ -19,13 +19,15 @@ "All users. Type to select user or group." : "Tots els usuaris. Escriu per seleccionar un usuari o grup.", "(group)" : "(grup)", "Saved" : "Desat", + "Saving..." : "Desant...", + "Save" : "Desa", + "Username" : "Nom d'usuari", + "Password" : "Contrasenya", "None" : "Cap", "App key" : "Clau de l'aplicació", "App secret" : "Secret de l'aplicació", "Client ID" : "Client ID", "Client secret" : "Secret del client", - "Username" : "Nom d'usuari", - "Password" : "Contrasenya", "API key" : "codi API", "Public key" : "Clau pública", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/cs_CZ.js b/apps/files_external/l10n/cs_CZ.js index 2021edbbe09..73c1e1df66f 100644 --- a/apps/files_external/l10n/cs_CZ.js +++ b/apps/files_external/l10n/cs_CZ.js @@ -37,6 +37,8 @@ OC.L10N.register( "(group)" : "(skupina)", "Admin defined" : "Nastaveno administrátorem", "Saved" : "Uloženo", + "Saving..." : "Ukládám...", + "Save" : "Uložit", "Empty response from the server" : "Prázdná odpověď serveru", "Couldn't access. Please logout and login to activate this mount point" : "Nelze připojit. Pro aktivaci tohoto přípojného bodu se prosím odhlašte a znovu přihlašte", "Couldn't get the information from the ownCloud server: {code} {type}" : "Nelze obdržet informaci z ownCloud serveru: {code} {type}", @@ -45,6 +47,12 @@ OC.L10N.register( "External mount error" : "Chyba vzdáleného úložiště", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Nelze obdržet seznam síťových úložišť systému Windows: prázdná odpověď serveru", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Některá z nastavených vzdálených úložišť nejsou připojena. Pro více informací prosím klikněte na červenou šipku(y)", + "Please enter the credentials for the {mount} mount" : "Zadejte prosím přihlašovací údaje k přípojnému bodu {mount}", + "Username" : "Uživatelské jméno", + "Password" : "Heslo", + "Credentials saved" : "Přihlašovací údaje uloženy", + "Credentials saving failed" : "Uložení přihlašovacích údajů selhalo", + "Credentials required" : "Vyžadovány přihlašovací údaje", "Access key" : "Přístupový klíč", "Secret key" : "Tajný klíč", "Builtin" : "Zabudované", @@ -56,12 +64,11 @@ OC.L10N.register( "Client ID" : "Klientské ID", "Client secret" : "Klientské tajemství", "OpenStack" : "OpenStack", - "Username" : "Uživatelské jméno", - "Password" : "Heslo", "Tenant name" : "Jméno vlastníka", "Identity endpoint URL" : "Identifikační koncový bod URL", "Rackspace" : "Rackspace", "API key" : "Klíč API", + "Global Credentails" : "Globální přihlašovací údaje", "Log-in credentials, save in database" : "Přihlašovací údaje, ukládat do databáze", "Username and password" : "Uživatelské jméno a heslo", "Log-in credentials, save in session" : "Přihlašovací údaje, ukládat v sezení", @@ -108,6 +115,7 @@ OC.L10N.register( "Storage type" : "Typ úložiště", "Scope" : "Rozsah", "External Storage" : "Externí úložiště", + "Global Credentials" : "Globální přihlašovací údaje", "Folder name" : "Název složky", "Authentication" : "Ověření", "Configuration" : "Nastavení", diff --git a/apps/files_external/l10n/cs_CZ.json b/apps/files_external/l10n/cs_CZ.json index fbec266dc9b..1dc14a07af4 100644 --- a/apps/files_external/l10n/cs_CZ.json +++ b/apps/files_external/l10n/cs_CZ.json @@ -35,6 +35,8 @@ "(group)" : "(skupina)", "Admin defined" : "Nastaveno administrátorem", "Saved" : "Uloženo", + "Saving..." : "Ukládám...", + "Save" : "Uložit", "Empty response from the server" : "Prázdná odpověď serveru", "Couldn't access. Please logout and login to activate this mount point" : "Nelze připojit. Pro aktivaci tohoto přípojného bodu se prosím odhlašte a znovu přihlašte", "Couldn't get the information from the ownCloud server: {code} {type}" : "Nelze obdržet informaci z ownCloud serveru: {code} {type}", @@ -43,6 +45,12 @@ "External mount error" : "Chyba vzdáleného úložiště", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Nelze obdržet seznam síťových úložišť systému Windows: prázdná odpověď serveru", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Některá z nastavených vzdálených úložišť nejsou připojena. Pro více informací prosím klikněte na červenou šipku(y)", + "Please enter the credentials for the {mount} mount" : "Zadejte prosím přihlašovací údaje k přípojnému bodu {mount}", + "Username" : "Uživatelské jméno", + "Password" : "Heslo", + "Credentials saved" : "Přihlašovací údaje uloženy", + "Credentials saving failed" : "Uložení přihlašovacích údajů selhalo", + "Credentials required" : "Vyžadovány přihlašovací údaje", "Access key" : "Přístupový klíč", "Secret key" : "Tajný klíč", "Builtin" : "Zabudované", @@ -54,12 +62,11 @@ "Client ID" : "Klientské ID", "Client secret" : "Klientské tajemství", "OpenStack" : "OpenStack", - "Username" : "Uživatelské jméno", - "Password" : "Heslo", "Tenant name" : "Jméno vlastníka", "Identity endpoint URL" : "Identifikační koncový bod URL", "Rackspace" : "Rackspace", "API key" : "Klíč API", + "Global Credentails" : "Globální přihlašovací údaje", "Log-in credentials, save in database" : "Přihlašovací údaje, ukládat do databáze", "Username and password" : "Uživatelské jméno a heslo", "Log-in credentials, save in session" : "Přihlašovací údaje, ukládat v sezení", @@ -106,6 +113,7 @@ "Storage type" : "Typ úložiště", "Scope" : "Rozsah", "External Storage" : "Externí úložiště", + "Global Credentials" : "Globální přihlašovací údaje", "Folder name" : "Název složky", "Authentication" : "Ověření", "Configuration" : "Nastavení", diff --git a/apps/files_external/l10n/cy_GB.js b/apps/files_external/l10n/cy_GB.js index 4364445be0c..50aaaad0ec9 100644 --- a/apps/files_external/l10n/cy_GB.js +++ b/apps/files_external/l10n/cy_GB.js @@ -2,9 +2,11 @@ OC.L10N.register( "files_external", { "Personal" : "Personol", - "None" : "Dim", + "Saving..." : "Yn cadw...", + "Save" : "Cadw", "Username" : "Enw defnyddiwr", "Password" : "Cyfrinair", + "None" : "Dim", "URL" : "URL", "Location" : "Lleoliad", "ownCloud" : "ownCloud", diff --git a/apps/files_external/l10n/cy_GB.json b/apps/files_external/l10n/cy_GB.json index 4916cb270ab..258d72b5b6f 100644 --- a/apps/files_external/l10n/cy_GB.json +++ b/apps/files_external/l10n/cy_GB.json @@ -1,8 +1,10 @@ { "translations": { "Personal" : "Personol", - "None" : "Dim", + "Saving..." : "Yn cadw...", + "Save" : "Cadw", "Username" : "Enw defnyddiwr", "Password" : "Cyfrinair", + "None" : "Dim", "URL" : "URL", "Location" : "Lleoliad", "ownCloud" : "ownCloud", diff --git a/apps/files_external/l10n/da.js b/apps/files_external/l10n/da.js index 70f43819496..d3c6c895d96 100644 --- a/apps/files_external/l10n/da.js +++ b/apps/files_external/l10n/da.js @@ -32,6 +32,10 @@ OC.L10N.register( "All users. Type to select user or group." : "Alle brugere. Indtast for at vælge bruger eller gruppe.", "(group)" : "(gruppe)", "Saved" : "Gemt", + "Saving..." : "Gemmer...", + "Save" : "Gem", + "Username" : "Brugernavn", + "Password" : "Kodeord", "Access key" : "Adgangsnøgle", "Secret key" : "Hemmelig nøgle", "Builtin" : "Indbygget", @@ -43,8 +47,6 @@ OC.L10N.register( "Client ID" : "Klient-ID", "Client secret" : "Klient hemmelighed", "OpenStack" : "OpenStack", - "Username" : "Brugernavn", - "Password" : "Kodeord", "Tenant name" : "Lejernavn", "Identity endpoint URL" : "Identificer afslutnings URL", "Rackspace" : "Hyldeplads", diff --git a/apps/files_external/l10n/da.json b/apps/files_external/l10n/da.json index aaeaaa58ae0..513533dd49e 100644 --- a/apps/files_external/l10n/da.json +++ b/apps/files_external/l10n/da.json @@ -30,6 +30,10 @@ "All users. Type to select user or group." : "Alle brugere. Indtast for at vælge bruger eller gruppe.", "(group)" : "(gruppe)", "Saved" : "Gemt", + "Saving..." : "Gemmer...", + "Save" : "Gem", + "Username" : "Brugernavn", + "Password" : "Kodeord", "Access key" : "Adgangsnøgle", "Secret key" : "Hemmelig nøgle", "Builtin" : "Indbygget", @@ -41,8 +45,6 @@ "Client ID" : "Klient-ID", "Client secret" : "Klient hemmelighed", "OpenStack" : "OpenStack", - "Username" : "Brugernavn", - "Password" : "Kodeord", "Tenant name" : "Lejernavn", "Identity endpoint URL" : "Identificer afslutnings URL", "Rackspace" : "Hyldeplads", diff --git a/apps/files_external/l10n/de.js b/apps/files_external/l10n/de.js index 41b86dbeb48..709450948ee 100644 --- a/apps/files_external/l10n/de.js +++ b/apps/files_external/l10n/de.js @@ -28,7 +28,11 @@ OC.L10N.register( "(group)" : "(group)", "Admin defined" : "Administrator festlegen", "Saved" : "Gespeichert", + "Saving..." : "Speichern…", + "Save" : "Speichern", "There was an error with message: " : "Es ist ein Fehler mit folgender Meldung aufgetreten:", + "Username" : "Benutzername", + "Password" : "Passwort", "Access key" : "Zugangsschlüssel", "Secret key" : "Geheimer Schlüssel", "None" : "Keine", @@ -39,8 +43,6 @@ OC.L10N.register( "Client ID" : "Client-ID", "Client secret" : "Geheime Zeichenkette des Client", "OpenStack" : "OpenStack", - "Username" : "Benutzername", - "Password" : "Passwort", "Tenant name" : "Name des Mieters", "Rackspace" : "Rackspace", "API key" : "API-Schlüssel", diff --git a/apps/files_external/l10n/de.json b/apps/files_external/l10n/de.json index 2b50a822507..9c645c31298 100644 --- a/apps/files_external/l10n/de.json +++ b/apps/files_external/l10n/de.json @@ -26,7 +26,11 @@ "(group)" : "(group)", "Admin defined" : "Administrator festlegen", "Saved" : "Gespeichert", + "Saving..." : "Speichern…", + "Save" : "Speichern", "There was an error with message: " : "Es ist ein Fehler mit folgender Meldung aufgetreten:", + "Username" : "Benutzername", + "Password" : "Passwort", "Access key" : "Zugangsschlüssel", "Secret key" : "Geheimer Schlüssel", "None" : "Keine", @@ -37,8 +41,6 @@ "Client ID" : "Client-ID", "Client secret" : "Geheime Zeichenkette des Client", "OpenStack" : "OpenStack", - "Username" : "Benutzername", - "Password" : "Passwort", "Tenant name" : "Name des Mieters", "Rackspace" : "Rackspace", "API key" : "API-Schlüssel", diff --git a/apps/files_external/l10n/de_AT.js b/apps/files_external/l10n/de_AT.js index 6c71c45f217..962fbf116f0 100644 --- a/apps/files_external/l10n/de_AT.js +++ b/apps/files_external/l10n/de_AT.js @@ -2,6 +2,7 @@ OC.L10N.register( "files_external", { "Personal" : "Persönlich", + "Save" : "Speichern", "Username" : "Benutzername", "Password" : "Passwort", "Port" : "Port", diff --git a/apps/files_external/l10n/de_AT.json b/apps/files_external/l10n/de_AT.json index 0d956dc81c6..48154b4f189 100644 --- a/apps/files_external/l10n/de_AT.json +++ b/apps/files_external/l10n/de_AT.json @@ -1,5 +1,6 @@ { "translations": { "Personal" : "Persönlich", + "Save" : "Speichern", "Username" : "Benutzername", "Password" : "Passwort", "Port" : "Port", diff --git a/apps/files_external/l10n/de_DE.js b/apps/files_external/l10n/de_DE.js index ef860918474..514a9dcd8ab 100644 --- a/apps/files_external/l10n/de_DE.js +++ b/apps/files_external/l10n/de_DE.js @@ -23,13 +23,15 @@ OC.L10N.register( "All users. Type to select user or group." : "Alle Benutzer. Benutzer oder Gruppe zur Auswahl eingeben.", "(group)" : "(group)", "Saved" : "Gespeichert", + "Saving..." : "Speichervorgang…", + "Save" : "Speichern", + "Username" : "Benutzername", + "Password" : "Passwort", "None" : "Keine", "App key" : "App-Schlüssel", "App secret" : "Geheime Zeichenkette der App", "Client ID" : "Client-ID", "Client secret" : "Geheime Zeichenkette des Client", - "Username" : "Benutzername", - "Password" : "Passwort", "API key" : "API-Schlüssel", "Username and password" : "Benutzername und Passwort", "Public key" : "Öffentlicher Schlüssel", diff --git a/apps/files_external/l10n/de_DE.json b/apps/files_external/l10n/de_DE.json index 75a84334764..2c8af227c3b 100644 --- a/apps/files_external/l10n/de_DE.json +++ b/apps/files_external/l10n/de_DE.json @@ -21,13 +21,15 @@ "All users. Type to select user or group." : "Alle Benutzer. Benutzer oder Gruppe zur Auswahl eingeben.", "(group)" : "(group)", "Saved" : "Gespeichert", + "Saving..." : "Speichervorgang…", + "Save" : "Speichern", + "Username" : "Benutzername", + "Password" : "Passwort", "None" : "Keine", "App key" : "App-Schlüssel", "App secret" : "Geheime Zeichenkette der App", "Client ID" : "Client-ID", "Client secret" : "Geheime Zeichenkette des Client", - "Username" : "Benutzername", - "Password" : "Passwort", "API key" : "API-Schlüssel", "Username and password" : "Benutzername und Passwort", "Public key" : "Öffentlicher Schlüssel", diff --git a/apps/files_external/l10n/el.js b/apps/files_external/l10n/el.js index c04d3d015d3..c807978e7a2 100644 --- a/apps/files_external/l10n/el.js +++ b/apps/files_external/l10n/el.js @@ -34,6 +34,10 @@ OC.L10N.register( "All users. Type to select user or group." : "Όλοι οι χρήστες. Πληκτρολογήστε για να επιλέξετε χρήστη ή ομάδα.", "(group)" : "(ομάδα)", "Saved" : "Αποθηκεύτηκαν", + "Saving..." : "Γίνεται αποθήκευση...", + "Save" : "Αποθήκευση", + "Username" : "Όνομα χρήστη", + "Password" : "Κωδικός πρόσβασης", "Access key" : "Κλειδί πρόσβασης", "Secret key" : "Μυστικό κλειδί", "Builtin" : "Builtin", @@ -45,8 +49,6 @@ OC.L10N.register( "Client ID" : "ID πελάτη", "Client secret" : "Μυστικό πελάτη", "OpenStack" : "OpenStack", - "Username" : "Όνομα χρήστη", - "Password" : "Κωδικός πρόσβασης", "Tenant name" : "Όνομα \"ένοικου\"", "Identity endpoint URL" : "URL τελικού σημείου ταυτοποίησης", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/el.json b/apps/files_external/l10n/el.json index 98a6d18bfc3..44c41265093 100644 --- a/apps/files_external/l10n/el.json +++ b/apps/files_external/l10n/el.json @@ -32,6 +32,10 @@ "All users. Type to select user or group." : "Όλοι οι χρήστες. Πληκτρολογήστε για να επιλέξετε χρήστη ή ομάδα.", "(group)" : "(ομάδα)", "Saved" : "Αποθηκεύτηκαν", + "Saving..." : "Γίνεται αποθήκευση...", + "Save" : "Αποθήκευση", + "Username" : "Όνομα χρήστη", + "Password" : "Κωδικός πρόσβασης", "Access key" : "Κλειδί πρόσβασης", "Secret key" : "Μυστικό κλειδί", "Builtin" : "Builtin", @@ -43,8 +47,6 @@ "Client ID" : "ID πελάτη", "Client secret" : "Μυστικό πελάτη", "OpenStack" : "OpenStack", - "Username" : "Όνομα χρήστη", - "Password" : "Κωδικός πρόσβασης", "Tenant name" : "Όνομα \"ένοικου\"", "Identity endpoint URL" : "URL τελικού σημείου ταυτοποίησης", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/en_GB.js b/apps/files_external/l10n/en_GB.js index adba5abf88c..6390b2ffe7e 100644 --- a/apps/files_external/l10n/en_GB.js +++ b/apps/files_external/l10n/en_GB.js @@ -21,13 +21,15 @@ OC.L10N.register( "All users. Type to select user or group." : "All users. Type to select user or group.", "(group)" : "(group)", "Saved" : "Saved", + "Saving..." : "Saving...", + "Save" : "Save", + "Username" : "Username", + "Password" : "Password", "None" : "None", "App key" : "App key", "App secret" : "App secret", "Client ID" : "Client ID", "Client secret" : "Client secret", - "Username" : "Username", - "Password" : "Password", "API key" : "API key", "Public key" : "Public key", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/en_GB.json b/apps/files_external/l10n/en_GB.json index 04d43cf4854..ab3cd25c571 100644 --- a/apps/files_external/l10n/en_GB.json +++ b/apps/files_external/l10n/en_GB.json @@ -19,13 +19,15 @@ "All users. Type to select user or group." : "All users. Type to select user or group.", "(group)" : "(group)", "Saved" : "Saved", + "Saving..." : "Saving...", + "Save" : "Save", + "Username" : "Username", + "Password" : "Password", "None" : "None", "App key" : "App key", "App secret" : "App secret", "Client ID" : "Client ID", "Client secret" : "Client secret", - "Username" : "Username", - "Password" : "Password", "API key" : "API key", "Public key" : "Public key", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/eo.js b/apps/files_external/l10n/eo.js index ddab2360a6d..556287de7b5 100644 --- a/apps/files_external/l10n/eo.js +++ b/apps/files_external/l10n/eo.js @@ -6,13 +6,15 @@ OC.L10N.register( "Grant access" : "Doni alirpermeson", "Access granted" : "Alirpermeso donita", "Saved" : "Konservita", + "Saving..." : "Konservante...", + "Save" : "Konservi", + "Username" : "Uzantonomo", + "Password" : "Pasvorto", "None" : "Nenio", "App key" : "Aplikaĵoklavo", "App secret" : "Aplikaĵosekreto", "Client ID" : "Klientidentigilo", "Client secret" : "Klientosekreto", - "Username" : "Uzantonomo", - "Password" : "Pasvorto", "API key" : "API-klavo", "Public key" : "Publika ŝlosilo", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/eo.json b/apps/files_external/l10n/eo.json index ec91f40abac..aa944f959f7 100644 --- a/apps/files_external/l10n/eo.json +++ b/apps/files_external/l10n/eo.json @@ -4,13 +4,15 @@ "Grant access" : "Doni alirpermeson", "Access granted" : "Alirpermeso donita", "Saved" : "Konservita", + "Saving..." : "Konservante...", + "Save" : "Konservi", + "Username" : "Uzantonomo", + "Password" : "Pasvorto", "None" : "Nenio", "App key" : "Aplikaĵoklavo", "App secret" : "Aplikaĵosekreto", "Client ID" : "Klientidentigilo", "Client secret" : "Klientosekreto", - "Username" : "Uzantonomo", - "Password" : "Pasvorto", "API key" : "API-klavo", "Public key" : "Publika ŝlosilo", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/es.js b/apps/files_external/l10n/es.js index 8a49dd4ec05..42695f85a4b 100644 --- a/apps/files_external/l10n/es.js +++ b/apps/files_external/l10n/es.js @@ -35,6 +35,8 @@ OC.L10N.register( "(group)" : "(grupo)", "Admin defined" : "Admin definido", "Saved" : "Guardado", + "Saving..." : "Guardando...", + "Save" : "Guardar", "Empty response from the server" : "Respuesta vacía desde el servidor", "Couldn't access. Please logout and login to activate this mount point" : "No se puede acceder. Por favor cierra sesión e iníciala de nuevo para activar este punto de montaje", "Couldn't get the information from the ownCloud server: {code} {type}" : "No se puede obtener información acerca del servidor de OwnCloud: {code} {type}", @@ -43,6 +45,8 @@ OC.L10N.register( "External mount error" : "Error de montaje externo", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "No se puede obtener la lista de unidades de red y sus puntos de montaje de Windows: respuesta vacía desde el servidor", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Algunos de los puntos de montaje externos configurados no están conectados. Por favor, haga clic en la fila roja (s) para obtener más información", + "Username" : "Nombre de usuario", + "Password" : "Contraseña", "Access key" : "Clave de acceso", "Secret key" : "Clave secreta", "Builtin" : "Incorporado", @@ -54,8 +58,6 @@ OC.L10N.register( "Client ID" : "ID de Cliente", "Client secret" : "Cliente secreto", "OpenStack" : "OpenStack", - "Username" : "Nombre de usuario", - "Password" : "Contraseña", "Tenant name" : "Nombre del inquilino", "Identity endpoint URL" : "Identidad de punto final URL", "Rackspace" : "Espacio de Rack", diff --git a/apps/files_external/l10n/es.json b/apps/files_external/l10n/es.json index c3b81338776..24110d3f4a1 100644 --- a/apps/files_external/l10n/es.json +++ b/apps/files_external/l10n/es.json @@ -33,6 +33,8 @@ "(group)" : "(grupo)", "Admin defined" : "Admin definido", "Saved" : "Guardado", + "Saving..." : "Guardando...", + "Save" : "Guardar", "Empty response from the server" : "Respuesta vacía desde el servidor", "Couldn't access. Please logout and login to activate this mount point" : "No se puede acceder. Por favor cierra sesión e iníciala de nuevo para activar este punto de montaje", "Couldn't get the information from the ownCloud server: {code} {type}" : "No se puede obtener información acerca del servidor de OwnCloud: {code} {type}", @@ -41,6 +43,8 @@ "External mount error" : "Error de montaje externo", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "No se puede obtener la lista de unidades de red y sus puntos de montaje de Windows: respuesta vacía desde el servidor", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Algunos de los puntos de montaje externos configurados no están conectados. Por favor, haga clic en la fila roja (s) para obtener más información", + "Username" : "Nombre de usuario", + "Password" : "Contraseña", "Access key" : "Clave de acceso", "Secret key" : "Clave secreta", "Builtin" : "Incorporado", @@ -52,8 +56,6 @@ "Client ID" : "ID de Cliente", "Client secret" : "Cliente secreto", "OpenStack" : "OpenStack", - "Username" : "Nombre de usuario", - "Password" : "Contraseña", "Tenant name" : "Nombre del inquilino", "Identity endpoint URL" : "Identidad de punto final URL", "Rackspace" : "Espacio de Rack", diff --git a/apps/files_external/l10n/es_AR.js b/apps/files_external/l10n/es_AR.js index 7fb87f1a1d3..6204947c3d3 100644 --- a/apps/files_external/l10n/es_AR.js +++ b/apps/files_external/l10n/es_AR.js @@ -6,9 +6,11 @@ OC.L10N.register( "Grant access" : "Permitir acceso", "Access granted" : "Acceso permitido", "Saved" : "Guardado", - "None" : "Ninguno", + "Saving..." : "Guardando...", + "Save" : "Guardar", "Username" : "Nombre de usuario", "Password" : "Contraseña", + "None" : "Ninguno", "API key" : "clave API", "Port" : "Puerto", "Region" : "Provincia", diff --git a/apps/files_external/l10n/es_AR.json b/apps/files_external/l10n/es_AR.json index 9fb735f7a3a..e651ce0ec46 100644 --- a/apps/files_external/l10n/es_AR.json +++ b/apps/files_external/l10n/es_AR.json @@ -4,9 +4,11 @@ "Grant access" : "Permitir acceso", "Access granted" : "Acceso permitido", "Saved" : "Guardado", - "None" : "Ninguno", + "Saving..." : "Guardando...", + "Save" : "Guardar", "Username" : "Nombre de usuario", "Password" : "Contraseña", + "None" : "Ninguno", "API key" : "clave API", "Port" : "Puerto", "Region" : "Provincia", diff --git a/apps/files_external/l10n/es_MX.js b/apps/files_external/l10n/es_MX.js index c805ce16662..ca094579742 100644 --- a/apps/files_external/l10n/es_MX.js +++ b/apps/files_external/l10n/es_MX.js @@ -6,9 +6,11 @@ OC.L10N.register( "Grant access" : "Conceder acceso", "Access granted" : "Acceso concedido", "Saved" : "Guardado", - "None" : "Ninguno", + "Saving..." : "Guardando...", + "Save" : "Guardar", "Username" : "Nombre de usuario", "Password" : "Contraseña", + "None" : "Ninguno", "API key" : "clave API", "Public key" : "Llave pública", "Port" : "Puerto", diff --git a/apps/files_external/l10n/es_MX.json b/apps/files_external/l10n/es_MX.json index 1df9bf70436..e120c71f62f 100644 --- a/apps/files_external/l10n/es_MX.json +++ b/apps/files_external/l10n/es_MX.json @@ -4,9 +4,11 @@ "Grant access" : "Conceder acceso", "Access granted" : "Acceso concedido", "Saved" : "Guardado", - "None" : "Ninguno", + "Saving..." : "Guardando...", + "Save" : "Guardar", "Username" : "Nombre de usuario", "Password" : "Contraseña", + "None" : "Ninguno", "API key" : "clave API", "Public key" : "Llave pública", "Port" : "Puerto", diff --git a/apps/files_external/l10n/et_EE.js b/apps/files_external/l10n/et_EE.js index 989ff604952..35c77c8ca07 100644 --- a/apps/files_external/l10n/et_EE.js +++ b/apps/files_external/l10n/et_EE.js @@ -26,9 +26,13 @@ OC.L10N.register( "(group)" : "(grupp)", "Admin defined" : "Admini poolt määratud", "Saved" : "Salvestatud", + "Saving..." : "Salvestamine...", + "Save" : "Salvesta", "Couldn't get the list of external mount points: {type}" : "Välise ühenduspunkti hankimine ebaõnnestus: {type}", "There was an error with message: " : "Sõnumiga tekkis tõrge:", "External mount error" : "Välise seostamise tõrge", + "Username" : "Kasutajanimi", + "Password" : "Parool", "Access key" : "Ligipääsuvõti", "Secret key" : "Salavõti", "Builtin" : "Sisseehitatud", @@ -40,8 +44,6 @@ OC.L10N.register( "Client ID" : "Kliendi ID", "Client secret" : "Kliendi salasõna", "OpenStack" : "OpenStack", - "Username" : "Kasutajanimi", - "Password" : "Parool", "API key" : "API võti", "RSA public key" : "RSA avalik võti", "Public key" : "Avalik võti", diff --git a/apps/files_external/l10n/et_EE.json b/apps/files_external/l10n/et_EE.json index ad8e87e55f9..5577f556844 100644 --- a/apps/files_external/l10n/et_EE.json +++ b/apps/files_external/l10n/et_EE.json @@ -24,9 +24,13 @@ "(group)" : "(grupp)", "Admin defined" : "Admini poolt määratud", "Saved" : "Salvestatud", + "Saving..." : "Salvestamine...", + "Save" : "Salvesta", "Couldn't get the list of external mount points: {type}" : "Välise ühenduspunkti hankimine ebaõnnestus: {type}", "There was an error with message: " : "Sõnumiga tekkis tõrge:", "External mount error" : "Välise seostamise tõrge", + "Username" : "Kasutajanimi", + "Password" : "Parool", "Access key" : "Ligipääsuvõti", "Secret key" : "Salavõti", "Builtin" : "Sisseehitatud", @@ -38,8 +42,6 @@ "Client ID" : "Kliendi ID", "Client secret" : "Kliendi salasõna", "OpenStack" : "OpenStack", - "Username" : "Kasutajanimi", - "Password" : "Parool", "API key" : "API võti", "RSA public key" : "RSA avalik võti", "Public key" : "Avalik võti", diff --git a/apps/files_external/l10n/eu.js b/apps/files_external/l10n/eu.js index 58742552e76..b80e05f2c40 100644 --- a/apps/files_external/l10n/eu.js +++ b/apps/files_external/l10n/eu.js @@ -11,13 +11,15 @@ OC.L10N.register( "All users. Type to select user or group." : "Erabiltzaile guztiak. Idatzi erabiltzaile edo taldea hautatzeko.", "(group)" : "(taldea)", "Saved" : "Gordeta", + "Saving..." : "Gordetzen...", + "Save" : "Gorde", + "Username" : "Erabiltzaile izena", + "Password" : "Pasahitza", "None" : "Ezer", "App key" : "Aplikazio gakoa", "App secret" : "App sekretua", "Client ID" : "Bezero ID", "Client secret" : "Bezeroaren Sekretua", - "Username" : "Erabiltzaile izena", - "Password" : "Pasahitza", "API key" : "APIaren gakoa", "Public key" : "Gako publikoa", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/eu.json b/apps/files_external/l10n/eu.json index f039441b464..d6dac29bba7 100644 --- a/apps/files_external/l10n/eu.json +++ b/apps/files_external/l10n/eu.json @@ -9,13 +9,15 @@ "All users. Type to select user or group." : "Erabiltzaile guztiak. Idatzi erabiltzaile edo taldea hautatzeko.", "(group)" : "(taldea)", "Saved" : "Gordeta", + "Saving..." : "Gordetzen...", + "Save" : "Gorde", + "Username" : "Erabiltzaile izena", + "Password" : "Pasahitza", "None" : "Ezer", "App key" : "Aplikazio gakoa", "App secret" : "App sekretua", "Client ID" : "Bezero ID", "Client secret" : "Bezeroaren Sekretua", - "Username" : "Erabiltzaile izena", - "Password" : "Pasahitza", "API key" : "APIaren gakoa", "Public key" : "Gako publikoa", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/fa.js b/apps/files_external/l10n/fa.js index 0c1076fc994..f9e67ddb0af 100644 --- a/apps/files_external/l10n/fa.js +++ b/apps/files_external/l10n/fa.js @@ -18,6 +18,10 @@ OC.L10N.register( "Never" : "هرگز", "(group)" : "(گروه)", "Saved" : "ذخیره شد", + "Saving..." : "در حال ذخیره سازی...", + "Save" : "ذخیره", + "Username" : "نام کاربری", + "Password" : "گذرواژه", "Access key" : "کلید دسترسی", "Secret key" : "کلید مخفی", "None" : "هیچکدام", @@ -25,8 +29,6 @@ OC.L10N.register( "App secret" : "کد برنامه", "OAuth2" : "OAuth2", "OpenStack" : "OpenStack", - "Username" : "نام کاربری", - "Password" : "گذرواژه", "API key" : "کلید API ", "Username and password" : "نام کاربری و رمز عبور", "Public key" : "کلید عمومی", diff --git a/apps/files_external/l10n/fa.json b/apps/files_external/l10n/fa.json index 056bad259a6..e5292902c47 100644 --- a/apps/files_external/l10n/fa.json +++ b/apps/files_external/l10n/fa.json @@ -16,6 +16,10 @@ "Never" : "هرگز", "(group)" : "(گروه)", "Saved" : "ذخیره شد", + "Saving..." : "در حال ذخیره سازی...", + "Save" : "ذخیره", + "Username" : "نام کاربری", + "Password" : "گذرواژه", "Access key" : "کلید دسترسی", "Secret key" : "کلید مخفی", "None" : "هیچکدام", @@ -23,8 +27,6 @@ "App secret" : "کد برنامه", "OAuth2" : "OAuth2", "OpenStack" : "OpenStack", - "Username" : "نام کاربری", - "Password" : "گذرواژه", "API key" : "کلید API ", "Username and password" : "نام کاربری و رمز عبور", "Public key" : "کلید عمومی", diff --git a/apps/files_external/l10n/fi_FI.js b/apps/files_external/l10n/fi_FI.js index ec969586d6d..ec2c7d7a52c 100644 --- a/apps/files_external/l10n/fi_FI.js +++ b/apps/files_external/l10n/fi_FI.js @@ -17,6 +17,7 @@ OC.L10N.register( "Error generating key pair" : "Virhe luotaessa avainparia", "Enable encryption" : "Käytä salausta", "Enable previews" : "Käytä esikatseluja", + "Enable sharing" : "Käytä jakamista", "Check for changes" : "Tarkista muutokset", "Never" : "Ei koskaan", "Once every direct access" : "Kerran aina suoran käytön yhteydessä", @@ -24,6 +25,8 @@ OC.L10N.register( "(group)" : "(ryhmä)", "Admin defined" : "Ylläpitäjän määrittämä", "Saved" : "Tallennettu", + "Saving..." : "Tallennetaan...", + "Save" : "Tallenna", "Empty response from the server" : "Tyhjä vastaus palvelimelta", "Couldn't access. Please logout and login to activate this mount point" : "Käyttö epäonnistui. Kirjaudu ulos ja takaisin sisään aktivoidaksesi tämän liitospisteen", "Couldn't get the information from the ownCloud server: {code} {type}" : "Tietojen saaminen ownCloud-palvelimelta epäonnistui: {code} {type}", @@ -32,6 +35,12 @@ OC.L10N.register( "External mount error" : "Ulkoinen liitosvirhe", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Windows-verkkoasemien liitospisteiden listauksen noutaminen epäonnistui: tyhjä vastaus palvelimelta", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Jotkin määritetyt erilliset liitospisteet eivät ole yhdistettynä. Napsauta punaisia rivejä saadaksesi lisätietoja", + "Please enter the credentials for the {mount} mount" : "Anna kirjautumistiedot liitokselle {mount}", + "Username" : "Käyttäjätunnus", + "Password" : "Salasana", + "Credentials saved" : "Kirjautumistiedot tallennettu", + "Credentials saving failed" : "Kirjautumistietojen tallentaminen epäonnistui", + "Credentials required" : "Kirjautumistiedot vaaditaan", "Builtin" : "Sisäänrakennettu", "None" : "Ei mitään", "OAuth1" : "OAuth1", @@ -41,8 +50,6 @@ OC.L10N.register( "Client ID" : "Asiakkaan tunniste", "Client secret" : "Asiakassalaisuus", "OpenStack" : "OpenStack", - "Username" : "Käyttäjätunnus", - "Password" : "Salasana", "Rackspace" : "Rackspace", "API key" : "API-avain", "Log-in credentials, save in database" : "Kirjautumistiedot, tallenna tietokantaan", @@ -85,6 +92,7 @@ OC.L10N.register( "Name" : "Nimi", "Storage type" : "Tallennustilan tyyppi", "External Storage" : "Erillinen tallennusväline", + "Global Credentials" : "Globaalit kirjautumistiedot", "Folder name" : "Kansion nimi", "Authentication" : "Tunnistautuminen", "Configuration" : "Asetukset", diff --git a/apps/files_external/l10n/fi_FI.json b/apps/files_external/l10n/fi_FI.json index 8c39acff034..e74582f1962 100644 --- a/apps/files_external/l10n/fi_FI.json +++ b/apps/files_external/l10n/fi_FI.json @@ -15,6 +15,7 @@ "Error generating key pair" : "Virhe luotaessa avainparia", "Enable encryption" : "Käytä salausta", "Enable previews" : "Käytä esikatseluja", + "Enable sharing" : "Käytä jakamista", "Check for changes" : "Tarkista muutokset", "Never" : "Ei koskaan", "Once every direct access" : "Kerran aina suoran käytön yhteydessä", @@ -22,6 +23,8 @@ "(group)" : "(ryhmä)", "Admin defined" : "Ylläpitäjän määrittämä", "Saved" : "Tallennettu", + "Saving..." : "Tallennetaan...", + "Save" : "Tallenna", "Empty response from the server" : "Tyhjä vastaus palvelimelta", "Couldn't access. Please logout and login to activate this mount point" : "Käyttö epäonnistui. Kirjaudu ulos ja takaisin sisään aktivoidaksesi tämän liitospisteen", "Couldn't get the information from the ownCloud server: {code} {type}" : "Tietojen saaminen ownCloud-palvelimelta epäonnistui: {code} {type}", @@ -30,6 +33,12 @@ "External mount error" : "Ulkoinen liitosvirhe", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Windows-verkkoasemien liitospisteiden listauksen noutaminen epäonnistui: tyhjä vastaus palvelimelta", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Jotkin määritetyt erilliset liitospisteet eivät ole yhdistettynä. Napsauta punaisia rivejä saadaksesi lisätietoja", + "Please enter the credentials for the {mount} mount" : "Anna kirjautumistiedot liitokselle {mount}", + "Username" : "Käyttäjätunnus", + "Password" : "Salasana", + "Credentials saved" : "Kirjautumistiedot tallennettu", + "Credentials saving failed" : "Kirjautumistietojen tallentaminen epäonnistui", + "Credentials required" : "Kirjautumistiedot vaaditaan", "Builtin" : "Sisäänrakennettu", "None" : "Ei mitään", "OAuth1" : "OAuth1", @@ -39,8 +48,6 @@ "Client ID" : "Asiakkaan tunniste", "Client secret" : "Asiakassalaisuus", "OpenStack" : "OpenStack", - "Username" : "Käyttäjätunnus", - "Password" : "Salasana", "Rackspace" : "Rackspace", "API key" : "API-avain", "Log-in credentials, save in database" : "Kirjautumistiedot, tallenna tietokantaan", @@ -83,6 +90,7 @@ "Name" : "Nimi", "Storage type" : "Tallennustilan tyyppi", "External Storage" : "Erillinen tallennusväline", + "Global Credentials" : "Globaalit kirjautumistiedot", "Folder name" : "Kansion nimi", "Authentication" : "Tunnistautuminen", "Configuration" : "Asetukset", diff --git a/apps/files_external/l10n/fr.js b/apps/files_external/l10n/fr.js index cc1c1bc693c..5328746e5be 100644 --- a/apps/files_external/l10n/fr.js +++ b/apps/files_external/l10n/fr.js @@ -29,6 +29,7 @@ OC.L10N.register( "Error generating key pair" : "Erreur lors de la génération des clés", "Enable encryption" : "Activer le chiffrement", "Enable previews" : "Activer les prévisualisations", + "Enable sharing" : "Activer le Partage", "Check for changes" : "Rechercher les modifications", "Never" : "Jamais", "Once every direct access" : "Une fois à chaque accès direct", @@ -36,14 +37,23 @@ OC.L10N.register( "(group)" : "(groupe)", "Admin defined" : "Défini par l'administrateur", "Saved" : "Sauvegardé", + "Saving..." : "Enregistrement...", + "Save" : "Enregistrer", "Empty response from the server" : "Réponse vide du serveur", "Couldn't access. Please logout and login to activate this mount point" : "Impossible d'accéder. Veuillez vous déconnecter et vous reconnecter pour activer ce point de montage.", "Couldn't get the information from the ownCloud server: {code} {type}" : "Impossible d'obtenir l'information depuis le serveur ownCloud : {code} {type}", "Couldn't get the list of external mount points: {type}" : "Impossible de récupérer la liste des points de montage externes : {type}", "There was an error with message: " : "Il y a eu une erreur avec le message :", "External mount error" : "Erreur de point de montage externe", + "external-storage" : "Stockage externe", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Impossible d'obtenir la liste des points de montage des disques réseaux Windows : Réponse vide du serveur", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Certains points de montage externes configurés ne sont pas connectés. Veuillez cliquer sur la(les) ligne(s) rouge(s) pour plus d'informations", + "Please enter the credentials for the {mount} mount" : "Veuillez entrer les identifiants pour le montage {mount}", + "Username" : "Nom d'utilisateur", + "Password" : "Mot de passe", + "Credentials saved" : "Identifiants sauvegardés", + "Credentials saving failed" : "La sauvegarde des Identifiants a échoué", + "Credentials required" : "Des informations d'identification sont requises", "Access key" : "Clé d'accès", "Secret key" : "Clé secrète", "Builtin" : "Intégré", @@ -55,13 +65,15 @@ OC.L10N.register( "Client ID" : "ID Client", "Client secret" : "Secret client", "OpenStack" : "OpenStack", - "Username" : "Nom d'utilisateur", - "Password" : "Mot de passe", "Tenant name" : "Tenant name", "Identity endpoint URL" : "Identity endpoint URL", "Rackspace" : "Rackspace", "API key" : "Clé API", + "Global Credentails" : "Identifiants globaux", + "Log-in credentials, save in database" : "Identifiants de connexion, sauvegardés dans la base de données", "Username and password" : "Nom d'utilisateur et mot de passe", + "Log-in credentials, save in session" : "Identifiants de connexion, sauvegardés pour la session", + "User entered, store in database" : "Identifiant utilisateur entré, enregistré dans la base de données", "RSA public key" : "Clé publique RSA", "Public key" : "Clef publique", "Amazon S3" : "Amazon S3", @@ -104,6 +116,7 @@ OC.L10N.register( "Storage type" : "Type de stockage", "Scope" : "Portée", "External Storage" : "Stockage externe", + "Global Credentials" : "Identifiants Globaux", "Folder name" : "Nom du dossier", "Authentication" : "Authentification", "Configuration" : "Configuration", diff --git a/apps/files_external/l10n/fr.json b/apps/files_external/l10n/fr.json index 47a70fcd12a..bfbfe0c85ca 100644 --- a/apps/files_external/l10n/fr.json +++ b/apps/files_external/l10n/fr.json @@ -27,6 +27,7 @@ "Error generating key pair" : "Erreur lors de la génération des clés", "Enable encryption" : "Activer le chiffrement", "Enable previews" : "Activer les prévisualisations", + "Enable sharing" : "Activer le Partage", "Check for changes" : "Rechercher les modifications", "Never" : "Jamais", "Once every direct access" : "Une fois à chaque accès direct", @@ -34,14 +35,23 @@ "(group)" : "(groupe)", "Admin defined" : "Défini par l'administrateur", "Saved" : "Sauvegardé", + "Saving..." : "Enregistrement...", + "Save" : "Enregistrer", "Empty response from the server" : "Réponse vide du serveur", "Couldn't access. Please logout and login to activate this mount point" : "Impossible d'accéder. Veuillez vous déconnecter et vous reconnecter pour activer ce point de montage.", "Couldn't get the information from the ownCloud server: {code} {type}" : "Impossible d'obtenir l'information depuis le serveur ownCloud : {code} {type}", "Couldn't get the list of external mount points: {type}" : "Impossible de récupérer la liste des points de montage externes : {type}", "There was an error with message: " : "Il y a eu une erreur avec le message :", "External mount error" : "Erreur de point de montage externe", + "external-storage" : "Stockage externe", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Impossible d'obtenir la liste des points de montage des disques réseaux Windows : Réponse vide du serveur", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Certains points de montage externes configurés ne sont pas connectés. Veuillez cliquer sur la(les) ligne(s) rouge(s) pour plus d'informations", + "Please enter the credentials for the {mount} mount" : "Veuillez entrer les identifiants pour le montage {mount}", + "Username" : "Nom d'utilisateur", + "Password" : "Mot de passe", + "Credentials saved" : "Identifiants sauvegardés", + "Credentials saving failed" : "La sauvegarde des Identifiants a échoué", + "Credentials required" : "Des informations d'identification sont requises", "Access key" : "Clé d'accès", "Secret key" : "Clé secrète", "Builtin" : "Intégré", @@ -53,13 +63,15 @@ "Client ID" : "ID Client", "Client secret" : "Secret client", "OpenStack" : "OpenStack", - "Username" : "Nom d'utilisateur", - "Password" : "Mot de passe", "Tenant name" : "Tenant name", "Identity endpoint URL" : "Identity endpoint URL", "Rackspace" : "Rackspace", "API key" : "Clé API", + "Global Credentails" : "Identifiants globaux", + "Log-in credentials, save in database" : "Identifiants de connexion, sauvegardés dans la base de données", "Username and password" : "Nom d'utilisateur et mot de passe", + "Log-in credentials, save in session" : "Identifiants de connexion, sauvegardés pour la session", + "User entered, store in database" : "Identifiant utilisateur entré, enregistré dans la base de données", "RSA public key" : "Clé publique RSA", "Public key" : "Clef publique", "Amazon S3" : "Amazon S3", @@ -102,6 +114,7 @@ "Storage type" : "Type de stockage", "Scope" : "Portée", "External Storage" : "Stockage externe", + "Global Credentials" : "Identifiants Globaux", "Folder name" : "Nom du dossier", "Authentication" : "Authentification", "Configuration" : "Configuration", diff --git a/apps/files_external/l10n/gl.js b/apps/files_external/l10n/gl.js index 26e54ee7453..21298c9f542 100644 --- a/apps/files_external/l10n/gl.js +++ b/apps/files_external/l10n/gl.js @@ -21,13 +21,15 @@ OC.L10N.register( "All users. Type to select user or group." : "Todos os usuarios. Escriba para seleccionar usuario ou grupo.", "(group)" : "(grupo)", "Saved" : "Gardado", + "Saving..." : "Gardando...", + "Save" : "Gardar", + "Username" : "Nome de usuario", + "Password" : "Contrasinal", "None" : "Ningún", "App key" : "Clave da API", "App secret" : "Secreto da aplicación", "Client ID" : "ID do cliente", "Client secret" : "Secreto do cliente", - "Username" : "Nome de usuario", - "Password" : "Contrasinal", "API key" : "Chave da API", "Public key" : "Chave pública", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/gl.json b/apps/files_external/l10n/gl.json index e9be28bfa18..da3ad490acd 100644 --- a/apps/files_external/l10n/gl.json +++ b/apps/files_external/l10n/gl.json @@ -19,13 +19,15 @@ "All users. Type to select user or group." : "Todos os usuarios. Escriba para seleccionar usuario ou grupo.", "(group)" : "(grupo)", "Saved" : "Gardado", + "Saving..." : "Gardando...", + "Save" : "Gardar", + "Username" : "Nome de usuario", + "Password" : "Contrasinal", "None" : "Ningún", "App key" : "Clave da API", "App secret" : "Secreto da aplicación", "Client ID" : "ID do cliente", "Client secret" : "Secreto do cliente", - "Username" : "Nome de usuario", - "Password" : "Contrasinal", "API key" : "Chave da API", "Public key" : "Chave pública", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/he.js b/apps/files_external/l10n/he.js index 20b61065241..e748676589a 100644 --- a/apps/files_external/l10n/he.js +++ b/apps/files_external/l10n/he.js @@ -37,6 +37,8 @@ OC.L10N.register( "(group)" : "(קבוצה)", "Admin defined" : "הוגדר מנהל", "Saved" : "נשמר", + "Saving..." : "שמירה…", + "Save" : "שמירה", "Empty response from the server" : "תגובה ריקה מהשרת", "Couldn't access. Please logout and login to activate this mount point" : "לא ניתן להכנס. יש להתנתק ולהתחבר כדי להפעיל את נקודת העיגון הזו", "Couldn't get the information from the ownCloud server: {code} {type}" : "לא ניתן היה לקבל את המידע משרת ה- ownCloud: {code} {type}", @@ -45,6 +47,8 @@ OC.L10N.register( "External mount error" : "שגיאת עגינה חיצונית", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "לא ניתן היה לקבל את רשימת נקודות העיגון של כונן הרשת של Window: תגובה ריקה מהשרת", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "חלק מנקודות העיגון החיצוניות שהוגדרו אינן מחוברות. יש ללחוץ על השורה/ות האדומה/ות למידע נוסף", + "Username" : "שם משתמש", + "Password" : "סיסמא", "Access key" : "מפתח כניסה", "Secret key" : "מפתח סודי", "Builtin" : "מובנה", @@ -56,12 +60,11 @@ OC.L10N.register( "Client ID" : "זיהוי לקוח", "Client secret" : "סוד לקוח", "OpenStack" : "OpenStack", - "Username" : "שם משתמש", - "Password" : "סיסמא", "Tenant name" : "שם דייר", "Identity endpoint URL" : "זהות נתיב נקודת קצה", "Rackspace" : "חץ אחורה", "API key" : "מפתח API", + "Global Credentails" : "אישורי אימות גלובלים", "Log-in credentials, save in database" : "אישורי התחברות, נשמרים במסד הנתונים", "Username and password" : "שם משתמש וסיסמא", "Log-in credentials, save in session" : "אישורי התחברות, נשמרים במידע שיחה - סשן", @@ -108,6 +111,7 @@ OC.L10N.register( "Storage type" : "סוג אחסון", "Scope" : "היקף", "External Storage" : "אחסון חיצוני", + "Global Credentials" : "אישורי אימות גלובלים", "Folder name" : "שם התיקייה", "Authentication" : "אימות", "Configuration" : "הגדרות", diff --git a/apps/files_external/l10n/he.json b/apps/files_external/l10n/he.json index 5f4d4c46ac5..539534b3121 100644 --- a/apps/files_external/l10n/he.json +++ b/apps/files_external/l10n/he.json @@ -35,6 +35,8 @@ "(group)" : "(קבוצה)", "Admin defined" : "הוגדר מנהל", "Saved" : "נשמר", + "Saving..." : "שמירה…", + "Save" : "שמירה", "Empty response from the server" : "תגובה ריקה מהשרת", "Couldn't access. Please logout and login to activate this mount point" : "לא ניתן להכנס. יש להתנתק ולהתחבר כדי להפעיל את נקודת העיגון הזו", "Couldn't get the information from the ownCloud server: {code} {type}" : "לא ניתן היה לקבל את המידע משרת ה- ownCloud: {code} {type}", @@ -43,6 +45,8 @@ "External mount error" : "שגיאת עגינה חיצונית", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "לא ניתן היה לקבל את רשימת נקודות העיגון של כונן הרשת של Window: תגובה ריקה מהשרת", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "חלק מנקודות העיגון החיצוניות שהוגדרו אינן מחוברות. יש ללחוץ על השורה/ות האדומה/ות למידע נוסף", + "Username" : "שם משתמש", + "Password" : "סיסמא", "Access key" : "מפתח כניסה", "Secret key" : "מפתח סודי", "Builtin" : "מובנה", @@ -54,12 +58,11 @@ "Client ID" : "זיהוי לקוח", "Client secret" : "סוד לקוח", "OpenStack" : "OpenStack", - "Username" : "שם משתמש", - "Password" : "סיסמא", "Tenant name" : "שם דייר", "Identity endpoint URL" : "זהות נתיב נקודת קצה", "Rackspace" : "חץ אחורה", "API key" : "מפתח API", + "Global Credentails" : "אישורי אימות גלובלים", "Log-in credentials, save in database" : "אישורי התחברות, נשמרים במסד הנתונים", "Username and password" : "שם משתמש וסיסמא", "Log-in credentials, save in session" : "אישורי התחברות, נשמרים במידע שיחה - סשן", @@ -106,6 +109,7 @@ "Storage type" : "סוג אחסון", "Scope" : "היקף", "External Storage" : "אחסון חיצוני", + "Global Credentials" : "אישורי אימות גלובלים", "Folder name" : "שם התיקייה", "Authentication" : "אימות", "Configuration" : "הגדרות", diff --git a/apps/files_external/l10n/hi.js b/apps/files_external/l10n/hi.js index 3d3b750ebd8..43b29acaddc 100644 --- a/apps/files_external/l10n/hi.js +++ b/apps/files_external/l10n/hi.js @@ -2,6 +2,8 @@ OC.L10N.register( "files_external", { "Personal" : "यक्तिगत", + "Saving..." : "सहेज रहे हैं...", + "Save" : "सहेजें", "Username" : "प्रयोक्ता का नाम", "Password" : "पासवर्ड", "Share" : "साझा करें" diff --git a/apps/files_external/l10n/hi.json b/apps/files_external/l10n/hi.json index 6e1117f610e..c3b1ad8623b 100644 --- a/apps/files_external/l10n/hi.json +++ b/apps/files_external/l10n/hi.json @@ -1,5 +1,7 @@ { "translations": { "Personal" : "यक्तिगत", + "Saving..." : "सहेज रहे हैं...", + "Save" : "सहेजें", "Username" : "प्रयोक्ता का नाम", "Password" : "पासवर्ड", "Share" : "साझा करें" diff --git a/apps/files_external/l10n/hr.js b/apps/files_external/l10n/hr.js index 8c632eba518..53a9c598c6d 100644 --- a/apps/files_external/l10n/hr.js +++ b/apps/files_external/l10n/hr.js @@ -11,13 +11,15 @@ OC.L10N.register( "All users. Type to select user or group." : "Svi korisnici. Započnite unos za izbor korisnika ili grupe.", "(group)" : "(grupa)", "Saved" : "Spremljeno", + "Saving..." : "Spremanje...", + "Save" : "Spremi", + "Username" : "Korisničko ime", + "Password" : "Lozinka", "None" : "Ništa", "App key" : "Ključ za aplikacije", "App secret" : "Tajna aplikacije", "Client ID" : "ID klijenta", "Client secret" : "Klijentski tajni ključ", - "Username" : "Korisničko ime", - "Password" : "Lozinka", "Amazon S3" : "Amazon S3", "Bucket" : "Kantica", "Hostname" : "Naziv poslužitelja", diff --git a/apps/files_external/l10n/hr.json b/apps/files_external/l10n/hr.json index 610a42d46e2..cbb11276c24 100644 --- a/apps/files_external/l10n/hr.json +++ b/apps/files_external/l10n/hr.json @@ -9,13 +9,15 @@ "All users. Type to select user or group." : "Svi korisnici. Započnite unos za izbor korisnika ili grupe.", "(group)" : "(grupa)", "Saved" : "Spremljeno", + "Saving..." : "Spremanje...", + "Save" : "Spremi", + "Username" : "Korisničko ime", + "Password" : "Lozinka", "None" : "Ništa", "App key" : "Ključ za aplikacije", "App secret" : "Tajna aplikacije", "Client ID" : "ID klijenta", "Client secret" : "Klijentski tajni ključ", - "Username" : "Korisničko ime", - "Password" : "Lozinka", "Amazon S3" : "Amazon S3", "Bucket" : "Kantica", "Hostname" : "Naziv poslužitelja", diff --git a/apps/files_external/l10n/hu_HU.js b/apps/files_external/l10n/hu_HU.js index 4eb93dab872..4e8ceef7a4c 100644 --- a/apps/files_external/l10n/hu_HU.js +++ b/apps/files_external/l10n/hu_HU.js @@ -18,19 +18,21 @@ OC.L10N.register( "(group)" : "(csoport)", "Admin defined" : "Adminisztrátor definiálva", "Saved" : "Elmentve", + "Saving..." : "Mentés...", + "Save" : "Mentés", "Empty response from the server" : "Üres válasz a szervertől", "Couldn't access. Please logout and login to activate this mount point" : "Nem férhető hozzá. Kérjük próbálj meg ki- és bejelentkezni a csatolási pont aktiválásához.", "Couldn't get the information from the ownCloud server: {code} {type}" : "Nem sikerült lekérdezni az információkat az ownCloud szerverről: {code} {type}", "There was an error with message: " : "Hiba történt ezzel az üzenettel:", "External mount error" : "Külső csatolási hiba", + "Username" : "Felhasználónév", + "Password" : "Jelszó", "Access key" : "Hozzáférési kulcs", "Secret key" : "Titkos kulcs", "None" : "Egyik sem", "App key" : "App kulcs", "App secret" : "App titkos kulcs", "Client secret" : "Kliens titkos", - "Username" : "Felhasználónév", - "Password" : "Jelszó", "API key" : "API kulcs", "Username and password" : "Felhasználónév és jelszó", "RSA public key" : "RSA publikus kulcs", diff --git a/apps/files_external/l10n/hu_HU.json b/apps/files_external/l10n/hu_HU.json index bc50176e5a3..6966c5720ab 100644 --- a/apps/files_external/l10n/hu_HU.json +++ b/apps/files_external/l10n/hu_HU.json @@ -16,19 +16,21 @@ "(group)" : "(csoport)", "Admin defined" : "Adminisztrátor definiálva", "Saved" : "Elmentve", + "Saving..." : "Mentés...", + "Save" : "Mentés", "Empty response from the server" : "Üres válasz a szervertől", "Couldn't access. Please logout and login to activate this mount point" : "Nem férhető hozzá. Kérjük próbálj meg ki- és bejelentkezni a csatolási pont aktiválásához.", "Couldn't get the information from the ownCloud server: {code} {type}" : "Nem sikerült lekérdezni az információkat az ownCloud szerverről: {code} {type}", "There was an error with message: " : "Hiba történt ezzel az üzenettel:", "External mount error" : "Külső csatolási hiba", + "Username" : "Felhasználónév", + "Password" : "Jelszó", "Access key" : "Hozzáférési kulcs", "Secret key" : "Titkos kulcs", "None" : "Egyik sem", "App key" : "App kulcs", "App secret" : "App titkos kulcs", "Client secret" : "Kliens titkos", - "Username" : "Felhasználónév", - "Password" : "Jelszó", "API key" : "API kulcs", "Username and password" : "Felhasználónév és jelszó", "RSA public key" : "RSA publikus kulcs", diff --git a/apps/files_external/l10n/hy.js b/apps/files_external/l10n/hy.js index 1092d48d575..ad4caac8057 100644 --- a/apps/files_external/l10n/hy.js +++ b/apps/files_external/l10n/hy.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Personal" : "Անձնական", "Never" : "Երբեք", + "Save" : "Պահպանել", "Username" : "Օգտանուն", "Password" : "Գաղտնաբառ", "URL" : "URL", diff --git a/apps/files_external/l10n/hy.json b/apps/files_external/l10n/hy.json index 1fecd4f3da1..87c69207ad3 100644 --- a/apps/files_external/l10n/hy.json +++ b/apps/files_external/l10n/hy.json @@ -1,6 +1,7 @@ { "translations": { "Personal" : "Անձնական", "Never" : "Երբեք", + "Save" : "Պահպանել", "Username" : "Օգտանուն", "Password" : "Գաղտնաբառ", "URL" : "URL", diff --git a/apps/files_external/l10n/ia.js b/apps/files_external/l10n/ia.js index d13ee4ac10f..9e0c7cb322b 100644 --- a/apps/files_external/l10n/ia.js +++ b/apps/files_external/l10n/ia.js @@ -3,6 +3,8 @@ OC.L10N.register( { "Personal" : "Personal", "Saved" : "Salveguardate", + "Saving..." : "Salveguardante...", + "Save" : "Salveguardar", "Username" : "Nomine de usator", "Password" : "Contrasigno", "Region" : "Region", diff --git a/apps/files_external/l10n/ia.json b/apps/files_external/l10n/ia.json index 43562ce9e5b..a626ccda34f 100644 --- a/apps/files_external/l10n/ia.json +++ b/apps/files_external/l10n/ia.json @@ -1,6 +1,8 @@ { "translations": { "Personal" : "Personal", "Saved" : "Salveguardate", + "Saving..." : "Salveguardante...", + "Save" : "Salveguardar", "Username" : "Nomine de usator", "Password" : "Contrasigno", "Region" : "Region", diff --git a/apps/files_external/l10n/id.js b/apps/files_external/l10n/id.js index 15f33571ee6..5610025a9f6 100644 --- a/apps/files_external/l10n/id.js +++ b/apps/files_external/l10n/id.js @@ -32,6 +32,10 @@ OC.L10N.register( "All users. Type to select user or group." : "Semua pengguna. Ketik untuk memilih pengguna atau grup.", "(group)" : "(grup)", "Saved" : "Disimpan", + "Saving..." : "Menyimpan...", + "Save" : "Simpan", + "Username" : "Nama Pengguna", + "Password" : "Sandi", "Access key" : "Kunci akses", "Secret key" : "Kunci rahasia", "Builtin" : "Internal", @@ -43,8 +47,6 @@ OC.L10N.register( "Client ID" : "ID Klien", "Client secret" : "Rahasia klien", "OpenStack" : "OpenStack", - "Username" : "Nama Pengguna", - "Password" : "Sandi", "Tenant name" : "Nama tenant", "Identity endpoint URL" : "Identitas URL akhir", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/id.json b/apps/files_external/l10n/id.json index 00f78599006..5722287c42c 100644 --- a/apps/files_external/l10n/id.json +++ b/apps/files_external/l10n/id.json @@ -30,6 +30,10 @@ "All users. Type to select user or group." : "Semua pengguna. Ketik untuk memilih pengguna atau grup.", "(group)" : "(grup)", "Saved" : "Disimpan", + "Saving..." : "Menyimpan...", + "Save" : "Simpan", + "Username" : "Nama Pengguna", + "Password" : "Sandi", "Access key" : "Kunci akses", "Secret key" : "Kunci rahasia", "Builtin" : "Internal", @@ -41,8 +45,6 @@ "Client ID" : "ID Klien", "Client secret" : "Rahasia klien", "OpenStack" : "OpenStack", - "Username" : "Nama Pengguna", - "Password" : "Sandi", "Tenant name" : "Nama tenant", "Identity endpoint URL" : "Identitas URL akhir", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/is.js b/apps/files_external/l10n/is.js index a1edd11fc1c..0f60a1e3af3 100644 --- a/apps/files_external/l10n/is.js +++ b/apps/files_external/l10n/is.js @@ -5,9 +5,11 @@ OC.L10N.register( "Grant access" : "Veita aðgengi", "Access granted" : "Aðgengi veitt", "Saved" : "Vistað", - "None" : "Ekkert", + "Saving..." : "Er að vista ...", + "Save" : "Vista", "Username" : "Notendanafn", "Password" : "Lykilorð", + "None" : "Ekkert", "WebDAV" : "WebDAV", "URL" : "URL", "Host" : "Netþjónn", diff --git a/apps/files_external/l10n/is.json b/apps/files_external/l10n/is.json index 6d52ed3a896..0a700436b7f 100644 --- a/apps/files_external/l10n/is.json +++ b/apps/files_external/l10n/is.json @@ -3,9 +3,11 @@ "Grant access" : "Veita aðgengi", "Access granted" : "Aðgengi veitt", "Saved" : "Vistað", - "None" : "Ekkert", + "Saving..." : "Er að vista ...", + "Save" : "Vista", "Username" : "Notendanafn", "Password" : "Lykilorð", + "None" : "Ekkert", "WebDAV" : "WebDAV", "URL" : "URL", "Host" : "Netþjónn", diff --git a/apps/files_external/l10n/it.js b/apps/files_external/l10n/it.js index b6fad31111d..5b07891a8f4 100644 --- a/apps/files_external/l10n/it.js +++ b/apps/files_external/l10n/it.js @@ -37,14 +37,23 @@ OC.L10N.register( "(group)" : "(gruppo)", "Admin defined" : "Definito dall'amministratore", "Saved" : "Salvato", + "Saving..." : "Salvataggio in corso...", + "Save" : "Salva", "Empty response from the server" : "Risposta vuota dal server", "Couldn't access. Please logout and login to activate this mount point" : "Impossibile accedere. Chiudi la sessione e accedi nuovamente per attivare questo punto di mount", "Couldn't get the information from the ownCloud server: {code} {type}" : "Impossibile ottenere le informazioni dal server ownCloud: {code} {type}", "Couldn't get the list of external mount points: {type}" : "Impossibile ottenere l'elenco dei punti di mount esterni: {type}", "There was an error with message: " : "Si è verificato un errore con il messaggio:", "External mount error" : "Errore di mount esterno", + "external-storage" : "archiviazione-esterna", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Impossibile ottenere l'elenco dei punti di mount delle unità di rete Windows: risposta vuota dal server", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alcuni dei punti di mount esterni configurati non sono connessi. Fai clic sulle righe rosse per ulteriori informazioni", + "Please enter the credentials for the {mount} mount" : "Digita le credenziali per montare {mount}", + "Username" : "Nome utente", + "Password" : "Password", + "Credentials saved" : "Credenziali salvate", + "Credentials saving failed" : "Salvataggio delle credenziali non riuscito", + "Credentials required" : "Credenziali richieste", "Access key" : "Chiave di accesso", "Secret key" : "Chiave segreta", "Builtin" : "Integrata", @@ -56,12 +65,11 @@ OC.L10N.register( "Client ID" : "ID client", "Client secret" : "Segreto del client", "OpenStack" : "OpenStack", - "Username" : "Nome utente", - "Password" : "Password", "Tenant name" : "Nome tenant", "Identity endpoint URL" : "URL endpoint delle identità", "Rackspace" : "Rackspace", "API key" : "Chiave API", + "Global Credentails" : "Credenziali globali", "Log-in credentials, save in database" : "Credenziali di accesso, salva nel database", "Username and password" : "Nome utente e password", "Log-in credentials, save in session" : "Credenziali di accesso, salva nella sessione", @@ -108,6 +116,7 @@ OC.L10N.register( "Storage type" : "Tipo di archiviazione", "Scope" : "Ambito", "External Storage" : "Archiviazione esterna", + "Global Credentials" : "Credenziali globali", "Folder name" : "Nome della cartella", "Authentication" : "Autenticazione", "Configuration" : "Configurazione", diff --git a/apps/files_external/l10n/it.json b/apps/files_external/l10n/it.json index e253c22b6b1..8257027e0f3 100644 --- a/apps/files_external/l10n/it.json +++ b/apps/files_external/l10n/it.json @@ -35,14 +35,23 @@ "(group)" : "(gruppo)", "Admin defined" : "Definito dall'amministratore", "Saved" : "Salvato", + "Saving..." : "Salvataggio in corso...", + "Save" : "Salva", "Empty response from the server" : "Risposta vuota dal server", "Couldn't access. Please logout and login to activate this mount point" : "Impossibile accedere. Chiudi la sessione e accedi nuovamente per attivare questo punto di mount", "Couldn't get the information from the ownCloud server: {code} {type}" : "Impossibile ottenere le informazioni dal server ownCloud: {code} {type}", "Couldn't get the list of external mount points: {type}" : "Impossibile ottenere l'elenco dei punti di mount esterni: {type}", "There was an error with message: " : "Si è verificato un errore con il messaggio:", "External mount error" : "Errore di mount esterno", + "external-storage" : "archiviazione-esterna", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Impossibile ottenere l'elenco dei punti di mount delle unità di rete Windows: risposta vuota dal server", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alcuni dei punti di mount esterni configurati non sono connessi. Fai clic sulle righe rosse per ulteriori informazioni", + "Please enter the credentials for the {mount} mount" : "Digita le credenziali per montare {mount}", + "Username" : "Nome utente", + "Password" : "Password", + "Credentials saved" : "Credenziali salvate", + "Credentials saving failed" : "Salvataggio delle credenziali non riuscito", + "Credentials required" : "Credenziali richieste", "Access key" : "Chiave di accesso", "Secret key" : "Chiave segreta", "Builtin" : "Integrata", @@ -54,12 +63,11 @@ "Client ID" : "ID client", "Client secret" : "Segreto del client", "OpenStack" : "OpenStack", - "Username" : "Nome utente", - "Password" : "Password", "Tenant name" : "Nome tenant", "Identity endpoint URL" : "URL endpoint delle identità", "Rackspace" : "Rackspace", "API key" : "Chiave API", + "Global Credentails" : "Credenziali globali", "Log-in credentials, save in database" : "Credenziali di accesso, salva nel database", "Username and password" : "Nome utente e password", "Log-in credentials, save in session" : "Credenziali di accesso, salva nella sessione", @@ -106,6 +114,7 @@ "Storage type" : "Tipo di archiviazione", "Scope" : "Ambito", "External Storage" : "Archiviazione esterna", + "Global Credentials" : "Credenziali globali", "Folder name" : "Nome della cartella", "Authentication" : "Autenticazione", "Configuration" : "Configurazione", diff --git a/apps/files_external/l10n/ja.js b/apps/files_external/l10n/ja.js index f14c07808ca..6e49805c03e 100644 --- a/apps/files_external/l10n/ja.js +++ b/apps/files_external/l10n/ja.js @@ -29,6 +29,7 @@ OC.L10N.register( "Error generating key pair" : "キーペアの生成エラー", "Enable encryption" : "暗号化を有効に", "Enable previews" : "プレビューを有効に", + "Enable sharing" : "共有の有効化", "Check for changes" : "変更点を確認", "Never" : "更新無", "Once every direct access" : "直指定時のみ", @@ -36,14 +37,23 @@ OC.L10N.register( "(group)" : "(グループ)", "Admin defined" : "管理者設定済", "Saved" : "保存されました", + "Saving..." : "保存中...", + "Save" : "保存", "Empty response from the server" : "サーバーから空の応答がありました", "Couldn't access. Please logout and login to activate this mount point" : "アクセス出来ませんでした。このマウントポイントを有効にするには一度ログアウトしてからログインしてください。", "Couldn't get the information from the ownCloud server: {code} {type}" : "ownCloud サーバーから情報を取得出来ませんでした。: {code} {type}", "Couldn't get the list of external mount points: {type}" : "外部マウントポイントのリストを取得出来ませんでした。: {type}", "There was an error with message: " : "メッセージ付きのエラーが発生しました:", "External mount error" : "外部マウントエラー", + "external-storage" : "外部ストレージ", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Windows ネットワークドライブのマウントポイントリストを取得出来ませんでした:サーバーから空の応答がありました", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "いくつかの設定済み外部マウントポイントに接続できませんでした。詳細情報は赤い行をクリックしてください", + "Please enter the credentials for the {mount} mount" : " {mount} のマウントのために必要な資格情報を入力してください", + "Username" : "ユーザー名", + "Password" : "パスワード", + "Credentials saved" : "資格情報を保存しました", + "Credentials saving failed" : "資格情報の保存に失敗しました", + "Credentials required" : "資格情報が必要です", "Access key" : "アクセスキー", "Secret key" : "シークレットキー", "Builtin" : "ビルトイン", @@ -55,8 +65,6 @@ OC.L10N.register( "Client ID" : "クライアントID", "Client secret" : "クライアント秘密キー", "OpenStack" : "OpenStack", - "Username" : "ユーザー名", - "Password" : "パスワード", "Tenant name" : "テナント名", "Identity endpoint URL" : "認証エンドポイントURL", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/ja.json b/apps/files_external/l10n/ja.json index df6f7f44618..82e2970fd86 100644 --- a/apps/files_external/l10n/ja.json +++ b/apps/files_external/l10n/ja.json @@ -27,6 +27,7 @@ "Error generating key pair" : "キーペアの生成エラー", "Enable encryption" : "暗号化を有効に", "Enable previews" : "プレビューを有効に", + "Enable sharing" : "共有の有効化", "Check for changes" : "変更点を確認", "Never" : "更新無", "Once every direct access" : "直指定時のみ", @@ -34,14 +35,23 @@ "(group)" : "(グループ)", "Admin defined" : "管理者設定済", "Saved" : "保存されました", + "Saving..." : "保存中...", + "Save" : "保存", "Empty response from the server" : "サーバーから空の応答がありました", "Couldn't access. Please logout and login to activate this mount point" : "アクセス出来ませんでした。このマウントポイントを有効にするには一度ログアウトしてからログインしてください。", "Couldn't get the information from the ownCloud server: {code} {type}" : "ownCloud サーバーから情報を取得出来ませんでした。: {code} {type}", "Couldn't get the list of external mount points: {type}" : "外部マウントポイントのリストを取得出来ませんでした。: {type}", "There was an error with message: " : "メッセージ付きのエラーが発生しました:", "External mount error" : "外部マウントエラー", + "external-storage" : "外部ストレージ", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Windows ネットワークドライブのマウントポイントリストを取得出来ませんでした:サーバーから空の応答がありました", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "いくつかの設定済み外部マウントポイントに接続できませんでした。詳細情報は赤い行をクリックしてください", + "Please enter the credentials for the {mount} mount" : " {mount} のマウントのために必要な資格情報を入力してください", + "Username" : "ユーザー名", + "Password" : "パスワード", + "Credentials saved" : "資格情報を保存しました", + "Credentials saving failed" : "資格情報の保存に失敗しました", + "Credentials required" : "資格情報が必要です", "Access key" : "アクセスキー", "Secret key" : "シークレットキー", "Builtin" : "ビルトイン", @@ -53,8 +63,6 @@ "Client ID" : "クライアントID", "Client secret" : "クライアント秘密キー", "OpenStack" : "OpenStack", - "Username" : "ユーザー名", - "Password" : "パスワード", "Tenant name" : "テナント名", "Identity endpoint URL" : "認証エンドポイントURL", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/ka_GE.js b/apps/files_external/l10n/ka_GE.js index e82c778862e..f6f98b1b5dd 100644 --- a/apps/files_external/l10n/ka_GE.js +++ b/apps/files_external/l10n/ka_GE.js @@ -5,9 +5,11 @@ OC.L10N.register( "Personal" : "პირადი", "Grant access" : "დაშვების მინიჭება", "Access granted" : "დაშვება მინიჭებულია", - "None" : "არა", + "Saving..." : "შენახვა...", + "Save" : "შენახვა", "Username" : "მომხმარებლის სახელი", "Password" : "პაროლი", + "None" : "არა", "API key" : "API გასაღები", "Port" : "პორტი", "Region" : "რეგიონი", diff --git a/apps/files_external/l10n/ka_GE.json b/apps/files_external/l10n/ka_GE.json index a706d42225b..3ffba305a95 100644 --- a/apps/files_external/l10n/ka_GE.json +++ b/apps/files_external/l10n/ka_GE.json @@ -3,9 +3,11 @@ "Personal" : "პირადი", "Grant access" : "დაშვების მინიჭება", "Access granted" : "დაშვება მინიჭებულია", - "None" : "არა", + "Saving..." : "შენახვა...", + "Save" : "შენახვა", "Username" : "მომხმარებლის სახელი", "Password" : "პაროლი", + "None" : "არა", "API key" : "API გასაღები", "Port" : "პორტი", "Region" : "რეგიონი", diff --git a/apps/files_external/l10n/km.js b/apps/files_external/l10n/km.js index e5aad9697ae..2c7b69e872b 100644 --- a/apps/files_external/l10n/km.js +++ b/apps/files_external/l10n/km.js @@ -6,9 +6,11 @@ OC.L10N.register( "Grant access" : "ទទួលសិទ្ធិចូល", "Access granted" : "បានទទួលសិទ្ធិចូល", "Saved" : "បានរក្សាទុក", - "None" : "គ្មាន", + "Saving..." : "កំពុងរក្សាទុក", + "Save" : "រក្សាទុក", "Username" : "ឈ្មោះអ្នកប្រើ", "Password" : "ពាក្យសម្ងាត់", + "None" : "គ្មាន", "Port" : "ច្រក", "WebDAV" : "WebDAV", "URL" : "URL", diff --git a/apps/files_external/l10n/km.json b/apps/files_external/l10n/km.json index 71213394d53..518d4ff260a 100644 --- a/apps/files_external/l10n/km.json +++ b/apps/files_external/l10n/km.json @@ -4,9 +4,11 @@ "Grant access" : "ទទួលសិទ្ធិចូល", "Access granted" : "បានទទួលសិទ្ធិចូល", "Saved" : "បានរក្សាទុក", - "None" : "គ្មាន", + "Saving..." : "កំពុងរក្សាទុក", + "Save" : "រក្សាទុក", "Username" : "ឈ្មោះអ្នកប្រើ", "Password" : "ពាក្យសម្ងាត់", + "None" : "គ្មាន", "Port" : "ច្រក", "WebDAV" : "WebDAV", "URL" : "URL", diff --git a/apps/files_external/l10n/kn.js b/apps/files_external/l10n/kn.js index 9d01033e356..b92c9382cd4 100644 --- a/apps/files_external/l10n/kn.js +++ b/apps/files_external/l10n/kn.js @@ -3,9 +3,11 @@ OC.L10N.register( { "Personal" : "ವೈಯಕ್ತಿಕ", "Saved" : "ಉಳಿಸಿದ", - "None" : "ಯಾವುದೂ ಇಲ್ಲ", + "Saving..." : "ಉಳಿಸಲಾಗುತ್ತಿದೆ ...", + "Save" : "ಉಳಿಸಿ", "Username" : "ಬಳಕೆಯ ಹೆಸರು", "Password" : "ಗುಪ್ತ ಪದ", + "None" : "ಯಾವುದೂ ಇಲ್ಲ", "Port" : "ರೇವು", "WebDAV" : "WebDAV", "URL" : "ಜಾಲದ ಕೊಂಡಿ", diff --git a/apps/files_external/l10n/kn.json b/apps/files_external/l10n/kn.json index 0380f431d1e..9841df0ae93 100644 --- a/apps/files_external/l10n/kn.json +++ b/apps/files_external/l10n/kn.json @@ -1,9 +1,11 @@ { "translations": { "Personal" : "ವೈಯಕ್ತಿಕ", "Saved" : "ಉಳಿಸಿದ", - "None" : "ಯಾವುದೂ ಇಲ್ಲ", + "Saving..." : "ಉಳಿಸಲಾಗುತ್ತಿದೆ ...", + "Save" : "ಉಳಿಸಿ", "Username" : "ಬಳಕೆಯ ಹೆಸರು", "Password" : "ಗುಪ್ತ ಪದ", + "None" : "ಯಾವುದೂ ಇಲ್ಲ", "Port" : "ರೇವು", "WebDAV" : "WebDAV", "URL" : "ಜಾಲದ ಕೊಂಡಿ", diff --git a/apps/files_external/l10n/ko.js b/apps/files_external/l10n/ko.js index df30d86ca13..4ce4907cbd0 100644 --- a/apps/files_external/l10n/ko.js +++ b/apps/files_external/l10n/ko.js @@ -33,6 +33,10 @@ OC.L10N.register( "All users. Type to select user or group." : "모든 사용자입니다. 사용자나 그룹을 선택하려면 입력하십시오", "(group)" : "(그룹)", "Saved" : "저장됨", + "Saving..." : "저장 중...", + "Save" : "저장", + "Username" : "사용자 이름", + "Password" : "암호", "Access key" : "접근 키", "Secret key" : "비밀 키", "Builtin" : "내장", @@ -44,8 +48,6 @@ OC.L10N.register( "Client ID" : "클라이언트 ID", "Client secret" : "클라이언트 비밀 값", "OpenStack" : "OpenStack", - "Username" : "사용자 이름", - "Password" : "암호", "Tenant name" : "테넌트 이름", "Identity endpoint URL" : "아이덴티티 끝점(Endpoint) URL", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/ko.json b/apps/files_external/l10n/ko.json index a5f4b4946ca..a55e4f6da6d 100644 --- a/apps/files_external/l10n/ko.json +++ b/apps/files_external/l10n/ko.json @@ -31,6 +31,10 @@ "All users. Type to select user or group." : "모든 사용자입니다. 사용자나 그룹을 선택하려면 입력하십시오", "(group)" : "(그룹)", "Saved" : "저장됨", + "Saving..." : "저장 중...", + "Save" : "저장", + "Username" : "사용자 이름", + "Password" : "암호", "Access key" : "접근 키", "Secret key" : "비밀 키", "Builtin" : "내장", @@ -42,8 +46,6 @@ "Client ID" : "클라이언트 ID", "Client secret" : "클라이언트 비밀 값", "OpenStack" : "OpenStack", - "Username" : "사용자 이름", - "Password" : "암호", "Tenant name" : "테넌트 이름", "Identity endpoint URL" : "아이덴티티 끝점(Endpoint) URL", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/ku_IQ.js b/apps/files_external/l10n/ku_IQ.js index 72c1e8313be..49211f62ef6 100644 --- a/apps/files_external/l10n/ku_IQ.js +++ b/apps/files_external/l10n/ku_IQ.js @@ -1,9 +1,11 @@ OC.L10N.register( "files_external", { - "None" : "هیچ", + "Saving..." : "پاشکهوتدهکات...", + "Save" : "پاشکهوتکردن", "Username" : "ناوی بهکارهێنهر", "Password" : "وشەی تێپەربو", + "None" : "هیچ", "URL" : "ناونیشانی بهستهر", "Location" : "شوێن", "Share" : "هاوبەشی کردن", diff --git a/apps/files_external/l10n/ku_IQ.json b/apps/files_external/l10n/ku_IQ.json index 9ef5a71e818..cb73bd6b8b6 100644 --- a/apps/files_external/l10n/ku_IQ.json +++ b/apps/files_external/l10n/ku_IQ.json @@ -1,7 +1,9 @@ { "translations": { - "None" : "هیچ", + "Saving..." : "پاشکهوتدهکات...", + "Save" : "پاشکهوتکردن", "Username" : "ناوی بهکارهێنهر", "Password" : "وشەی تێپەربو", + "None" : "هیچ", "URL" : "ناونیشانی بهستهر", "Location" : "شوێن", "Share" : "هاوبەشی کردن", diff --git a/apps/files_external/l10n/lb.js b/apps/files_external/l10n/lb.js index 7e03d3b1b99..67558b46275 100644 --- a/apps/files_external/l10n/lb.js +++ b/apps/files_external/l10n/lb.js @@ -3,6 +3,8 @@ OC.L10N.register( { "Personal" : "Perséinlech", "Saved" : "Gespäichert", + "Saving..." : "Speicheren...", + "Save" : "Späicheren", "Username" : "Benotzernumm", "Password" : "Passwuert", "Port" : "Port", diff --git a/apps/files_external/l10n/lb.json b/apps/files_external/l10n/lb.json index 5c6797533b5..8d570532dd1 100644 --- a/apps/files_external/l10n/lb.json +++ b/apps/files_external/l10n/lb.json @@ -1,6 +1,8 @@ { "translations": { "Personal" : "Perséinlech", "Saved" : "Gespäichert", + "Saving..." : "Speicheren...", + "Save" : "Späicheren", "Username" : "Benotzernumm", "Password" : "Passwuert", "Port" : "Port", diff --git a/apps/files_external/l10n/lt_LT.js b/apps/files_external/l10n/lt_LT.js index 3a871070f45..c08e6d60781 100644 --- a/apps/files_external/l10n/lt_LT.js +++ b/apps/files_external/l10n/lt_LT.js @@ -9,9 +9,11 @@ OC.L10N.register( "Grant access" : "Suteikti priėjimą", "Access granted" : "Priėjimas suteiktas", "Saved" : "Išsaugoti", - "None" : "Nieko", + "Saving..." : "Saugoma...", + "Save" : "Išsaugoti", "Username" : "Prisijungimo vardas", "Password" : "Slaptažodis", + "None" : "Nieko", "API key" : "API raktas", "Port" : "Prievadas", "Region" : "Regionas", diff --git a/apps/files_external/l10n/lt_LT.json b/apps/files_external/l10n/lt_LT.json index 854f753acaf..4fcc86df00b 100644 --- a/apps/files_external/l10n/lt_LT.json +++ b/apps/files_external/l10n/lt_LT.json @@ -7,9 +7,11 @@ "Grant access" : "Suteikti priėjimą", "Access granted" : "Priėjimas suteiktas", "Saved" : "Išsaugoti", - "None" : "Nieko", + "Saving..." : "Saugoma...", + "Save" : "Išsaugoti", "Username" : "Prisijungimo vardas", "Password" : "Slaptažodis", + "None" : "Nieko", "API key" : "API raktas", "Port" : "Prievadas", "Region" : "Regionas", diff --git a/apps/files_external/l10n/lv.js b/apps/files_external/l10n/lv.js index d6733a1d9c0..c3ae3cb2fce 100644 --- a/apps/files_external/l10n/lv.js +++ b/apps/files_external/l10n/lv.js @@ -7,9 +7,11 @@ OC.L10N.register( "Access granted" : "Piešķirta pieeja", "Enable encryption" : "Ieslēgt šifrēšanu", "Saved" : "Saglabāts", - "None" : "Nav", + "Saving..." : "Saglabā...", + "Save" : "Saglabāt", "Username" : "Lietotājvārds", "Password" : "Parole", + "None" : "Nav", "Port" : "Ports", "WebDAV" : "WebDAV", "URL" : "URL", diff --git a/apps/files_external/l10n/lv.json b/apps/files_external/l10n/lv.json index 57fe7cbc048..61dfeffa0e1 100644 --- a/apps/files_external/l10n/lv.json +++ b/apps/files_external/l10n/lv.json @@ -5,9 +5,11 @@ "Access granted" : "Piešķirta pieeja", "Enable encryption" : "Ieslēgt šifrēšanu", "Saved" : "Saglabāts", - "None" : "Nav", + "Saving..." : "Saglabā...", + "Save" : "Saglabāt", "Username" : "Lietotājvārds", "Password" : "Parole", + "None" : "Nav", "Port" : "Ports", "WebDAV" : "WebDAV", "URL" : "URL", diff --git a/apps/files_external/l10n/mk.js b/apps/files_external/l10n/mk.js index e75f1afa620..a2826d5caea 100644 --- a/apps/files_external/l10n/mk.js +++ b/apps/files_external/l10n/mk.js @@ -5,9 +5,11 @@ OC.L10N.register( "Grant access" : "Дозволи пристап", "Access granted" : "Пристапот е дозволен", "Saved" : "Снимено", - "None" : "Ништо", + "Saving..." : "Снимам...", + "Save" : "Сними", "Username" : "Корисничко име", "Password" : "Лозинка", + "None" : "Ништо", "API key" : "API key", "Port" : "Порта", "Region" : "Регион", diff --git a/apps/files_external/l10n/mk.json b/apps/files_external/l10n/mk.json index ae095b46310..e41cb7d4ae0 100644 --- a/apps/files_external/l10n/mk.json +++ b/apps/files_external/l10n/mk.json @@ -3,9 +3,11 @@ "Grant access" : "Дозволи пристап", "Access granted" : "Пристапот е дозволен", "Saved" : "Снимено", - "None" : "Ништо", + "Saving..." : "Снимам...", + "Save" : "Сними", "Username" : "Корисничко име", "Password" : "Лозинка", + "None" : "Ништо", "API key" : "API key", "Port" : "Порта", "Region" : "Регион", diff --git a/apps/files_external/l10n/mn.js b/apps/files_external/l10n/mn.js index a83f8310862..c451e4d7588 100644 --- a/apps/files_external/l10n/mn.js +++ b/apps/files_external/l10n/mn.js @@ -1,6 +1,7 @@ OC.L10N.register( "files_external", { + "Save" : "Хадгалах", "Username" : "Хэрэглэгчийн нэр", "Password" : "Нууц үг", "Share" : "Түгээх" diff --git a/apps/files_external/l10n/mn.json b/apps/files_external/l10n/mn.json index e28fa6e52ad..2296b74bf50 100644 --- a/apps/files_external/l10n/mn.json +++ b/apps/files_external/l10n/mn.json @@ -1,4 +1,5 @@ { "translations": { + "Save" : "Хадгалах", "Username" : "Хэрэглэгчийн нэр", "Password" : "Нууц үг", "Share" : "Түгээх" diff --git a/apps/files_external/l10n/ms_MY.js b/apps/files_external/l10n/ms_MY.js index 1b28ef4226a..0736bcbd436 100644 --- a/apps/files_external/l10n/ms_MY.js +++ b/apps/files_external/l10n/ms_MY.js @@ -2,6 +2,8 @@ OC.L10N.register( "files_external", { "Personal" : "Peribadi", + "Saving..." : "Sedang menyimpan...", + "Save" : "Simpan", "Username" : "Nama pengguna", "Password" : "Kata laluan", "Region" : "Wilayah", diff --git a/apps/files_external/l10n/ms_MY.json b/apps/files_external/l10n/ms_MY.json index 79293106272..d84f5859451 100644 --- a/apps/files_external/l10n/ms_MY.json +++ b/apps/files_external/l10n/ms_MY.json @@ -1,5 +1,7 @@ { "translations": { "Personal" : "Peribadi", + "Saving..." : "Sedang menyimpan...", + "Save" : "Simpan", "Username" : "Nama pengguna", "Password" : "Kata laluan", "Region" : "Wilayah", diff --git a/apps/files_external/l10n/nb_NO.js b/apps/files_external/l10n/nb_NO.js index fc791fe60ab..dcb9da4ac1f 100644 --- a/apps/files_external/l10n/nb_NO.js +++ b/apps/files_external/l10n/nb_NO.js @@ -35,6 +35,8 @@ OC.L10N.register( "(group)" : "(gruppe)", "Admin defined" : "Admin-definert", "Saved" : "Lagret", + "Saving..." : "Lagrer...", + "Save" : "Lagre", "Empty response from the server" : "Tomt svar fra serveren", "Couldn't access. Please logout and login to activate this mount point" : "Fikk ikke tilgang. Vennligst logg ut og inn igjen for å aktivere dette oppkoblingspunktet.", "Couldn't get the information from the ownCloud server: {code} {type}" : "Klarte ikke å hente informasjon fra ownCloud-serveren: {code} {type}", @@ -43,6 +45,8 @@ OC.L10N.register( "External mount error" : "Ekstern oppkoblingsfeil", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Klarte ikke å hente listen over oppkoblingspunkter for Windows nettverk-disker: tomt svar fra serveren", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Noen av de konfigurerte eksterne oppkoblingspunktene er ikke tilkoblet. Klikk på de røde raden(e) for mer informasjon.", + "Username" : "Brukernavn", + "Password" : "Passord", "Access key" : "Tilgangsnøkkel", "Secret key" : "Hemmelig nøkkel", "Builtin" : "Innebygget", @@ -54,8 +58,6 @@ OC.L10N.register( "Client ID" : "Client ID", "Client secret" : "Client secret", "OpenStack" : "OpenStack", - "Username" : "Brukernavn", - "Password" : "Passord", "Tenant name" : "Prosjektnavn", "Identity endpoint URL" : "URL for identitets-endepunkt", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/nb_NO.json b/apps/files_external/l10n/nb_NO.json index 0e956952948..c7f56d21534 100644 --- a/apps/files_external/l10n/nb_NO.json +++ b/apps/files_external/l10n/nb_NO.json @@ -33,6 +33,8 @@ "(group)" : "(gruppe)", "Admin defined" : "Admin-definert", "Saved" : "Lagret", + "Saving..." : "Lagrer...", + "Save" : "Lagre", "Empty response from the server" : "Tomt svar fra serveren", "Couldn't access. Please logout and login to activate this mount point" : "Fikk ikke tilgang. Vennligst logg ut og inn igjen for å aktivere dette oppkoblingspunktet.", "Couldn't get the information from the ownCloud server: {code} {type}" : "Klarte ikke å hente informasjon fra ownCloud-serveren: {code} {type}", @@ -41,6 +43,8 @@ "External mount error" : "Ekstern oppkoblingsfeil", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Klarte ikke å hente listen over oppkoblingspunkter for Windows nettverk-disker: tomt svar fra serveren", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Noen av de konfigurerte eksterne oppkoblingspunktene er ikke tilkoblet. Klikk på de røde raden(e) for mer informasjon.", + "Username" : "Brukernavn", + "Password" : "Passord", "Access key" : "Tilgangsnøkkel", "Secret key" : "Hemmelig nøkkel", "Builtin" : "Innebygget", @@ -52,8 +56,6 @@ "Client ID" : "Client ID", "Client secret" : "Client secret", "OpenStack" : "OpenStack", - "Username" : "Brukernavn", - "Password" : "Passord", "Tenant name" : "Prosjektnavn", "Identity endpoint URL" : "URL for identitets-endepunkt", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/nds.js b/apps/files_external/l10n/nds.js index b4649fe681a..f86bc638795 100644 --- a/apps/files_external/l10n/nds.js +++ b/apps/files_external/l10n/nds.js @@ -32,6 +32,9 @@ OC.L10N.register( "All users. Type to select user or group." : "Alle Benutzer. Tippe, um eine Benutzergruppe auszuwählen.", "(group)" : "(Gruppe)", "Saved" : "Gespeichert", + "Saving..." : "Speichern...", + "Username" : "Benutzername", + "Password" : "Passwort", "Access key" : "Schlüssel für Zugriff", "Secret key" : "Geheimer Schlüssel", "Builtin" : "Eingebaut", @@ -43,8 +46,6 @@ OC.L10N.register( "Client ID" : "Client ID", "Client secret" : "Client Geheimnis", "OpenStack" : "OpenStack", - "Username" : "Benutzername", - "Password" : "Passwort", "API key" : "API Schlüssel", "Username and password" : "Benutzername und Passwort", "RSA public key" : "Öffentlicher RSA Schlüssel", diff --git a/apps/files_external/l10n/nds.json b/apps/files_external/l10n/nds.json index 75e36016384..c22b072cdf5 100644 --- a/apps/files_external/l10n/nds.json +++ b/apps/files_external/l10n/nds.json @@ -30,6 +30,9 @@ "All users. Type to select user or group." : "Alle Benutzer. Tippe, um eine Benutzergruppe auszuwählen.", "(group)" : "(Gruppe)", "Saved" : "Gespeichert", + "Saving..." : "Speichern...", + "Username" : "Benutzername", + "Password" : "Passwort", "Access key" : "Schlüssel für Zugriff", "Secret key" : "Geheimer Schlüssel", "Builtin" : "Eingebaut", @@ -41,8 +44,6 @@ "Client ID" : "Client ID", "Client secret" : "Client Geheimnis", "OpenStack" : "OpenStack", - "Username" : "Benutzername", - "Password" : "Passwort", "API key" : "API Schlüssel", "Username and password" : "Benutzername und Passwort", "RSA public key" : "Öffentlicher RSA Schlüssel", diff --git a/apps/files_external/l10n/nl.js b/apps/files_external/l10n/nl.js index a3f3ca9a566..243dde9a6c3 100644 --- a/apps/files_external/l10n/nl.js +++ b/apps/files_external/l10n/nl.js @@ -29,6 +29,7 @@ OC.L10N.register( "Error generating key pair" : "Fout bij genereren sleutelpaar", "Enable encryption" : "Versleuteling inschakelen", "Enable previews" : "Activeren voorbeelden", + "Enable sharing" : "Activeren delen", "Check for changes" : "Controleren op wijzigingen", "Never" : "Nooit", "Once every direct access" : "Een keer bij elke directe toegang", @@ -36,14 +37,23 @@ OC.L10N.register( "(group)" : "(groep)", "Admin defined" : "Beheerder gedefinieerd", "Saved" : "Bewaard", + "Saving..." : "Opslaan", + "Save" : "Bewaren", "Empty response from the server" : "Lege reactie van de server", "Couldn't access. Please logout and login to activate this mount point" : "Geen toegang. Log uit en opnieuw in om dit koppelpunt te activeren", "Couldn't get the information from the ownCloud server: {code} {type}" : "Kon geen informatie van de ownCloud server krijgen: {code} {type}", "Couldn't get the list of external mount points: {type}" : "Kon geen overzicht met externe koppelpunten krijgen: {type}", "There was an error with message: " : "Er was een fout met de volgende melding:", "External mount error" : "Extern koppelpunt fout", + "external-storage" : "externe opslag", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Kon geen overzicht met Windows netwerk koppelpunten krijgen: lege reactie van de server", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Sommige van de geconfigureerde koppelpunten zijn niet verbonden. Klok op de rode rij(en) voor meer informatie", + "Please enter the credentials for the {mount} mount" : "Geef de inloggegeven sop voor de {mount} mount", + "Username" : "Gebruikersnaam", + "Password" : "Wachtwoord", + "Credentials saved" : "Inloggegevens opgeslagen", + "Credentials saving failed" : "Opslaan inloggegevens mislukt", + "Credentials required" : "Inloggegevens vereist", "Access key" : "Access Key", "Secret key" : "Geheime sleutel", "Builtin" : "Ingebouwd", @@ -55,13 +65,15 @@ OC.L10N.register( "Client ID" : "Client ID", "Client secret" : "Client secret", "OpenStack" : "OpenStack", - "Username" : "Gebruikersnaam", - "Password" : "Wachtwoord", "Tenant name" : "Naam tenant", "Identity endpoint URL" : "Identiteiten endpoint URL", "Rackspace" : "Rackspace", "API key" : "API sleutel", + "Global Credentails" : "Globale inloggegevens", + "Log-in credentials, save in database" : "Inloggegevens, opslaan in database", "Username and password" : "Gebruikersnaam en wachtwoord", + "Log-in credentials, save in session" : "Inloggegevens, opslaan in sessie", + "User entered, store in database" : "Gebruiker opgegeven, opslaan in database", "RSA public key" : "RSA publieke sleutel", "Public key" : "Publieke sleutel", "Amazon S3" : "Amazon S3", @@ -104,6 +116,7 @@ OC.L10N.register( "Storage type" : "Opslagtype", "Scope" : "Scope", "External Storage" : "Externe opslag", + "Global Credentials" : "Globale inloggegevens", "Folder name" : "Mapnaam", "Authentication" : "Authenticatie", "Configuration" : "Configuratie", diff --git a/apps/files_external/l10n/nl.json b/apps/files_external/l10n/nl.json index 8654cdc662d..bcc9149945b 100644 --- a/apps/files_external/l10n/nl.json +++ b/apps/files_external/l10n/nl.json @@ -27,6 +27,7 @@ "Error generating key pair" : "Fout bij genereren sleutelpaar", "Enable encryption" : "Versleuteling inschakelen", "Enable previews" : "Activeren voorbeelden", + "Enable sharing" : "Activeren delen", "Check for changes" : "Controleren op wijzigingen", "Never" : "Nooit", "Once every direct access" : "Een keer bij elke directe toegang", @@ -34,14 +35,23 @@ "(group)" : "(groep)", "Admin defined" : "Beheerder gedefinieerd", "Saved" : "Bewaard", + "Saving..." : "Opslaan", + "Save" : "Bewaren", "Empty response from the server" : "Lege reactie van de server", "Couldn't access. Please logout and login to activate this mount point" : "Geen toegang. Log uit en opnieuw in om dit koppelpunt te activeren", "Couldn't get the information from the ownCloud server: {code} {type}" : "Kon geen informatie van de ownCloud server krijgen: {code} {type}", "Couldn't get the list of external mount points: {type}" : "Kon geen overzicht met externe koppelpunten krijgen: {type}", "There was an error with message: " : "Er was een fout met de volgende melding:", "External mount error" : "Extern koppelpunt fout", + "external-storage" : "externe opslag", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Kon geen overzicht met Windows netwerk koppelpunten krijgen: lege reactie van de server", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Sommige van de geconfigureerde koppelpunten zijn niet verbonden. Klok op de rode rij(en) voor meer informatie", + "Please enter the credentials for the {mount} mount" : "Geef de inloggegeven sop voor de {mount} mount", + "Username" : "Gebruikersnaam", + "Password" : "Wachtwoord", + "Credentials saved" : "Inloggegevens opgeslagen", + "Credentials saving failed" : "Opslaan inloggegevens mislukt", + "Credentials required" : "Inloggegevens vereist", "Access key" : "Access Key", "Secret key" : "Geheime sleutel", "Builtin" : "Ingebouwd", @@ -53,13 +63,15 @@ "Client ID" : "Client ID", "Client secret" : "Client secret", "OpenStack" : "OpenStack", - "Username" : "Gebruikersnaam", - "Password" : "Wachtwoord", "Tenant name" : "Naam tenant", "Identity endpoint URL" : "Identiteiten endpoint URL", "Rackspace" : "Rackspace", "API key" : "API sleutel", + "Global Credentails" : "Globale inloggegevens", + "Log-in credentials, save in database" : "Inloggegevens, opslaan in database", "Username and password" : "Gebruikersnaam en wachtwoord", + "Log-in credentials, save in session" : "Inloggegevens, opslaan in sessie", + "User entered, store in database" : "Gebruiker opgegeven, opslaan in database", "RSA public key" : "RSA publieke sleutel", "Public key" : "Publieke sleutel", "Amazon S3" : "Amazon S3", @@ -102,6 +114,7 @@ "Storage type" : "Opslagtype", "Scope" : "Scope", "External Storage" : "Externe opslag", + "Global Credentials" : "Globale inloggegevens", "Folder name" : "Mapnaam", "Authentication" : "Authenticatie", "Configuration" : "Configuratie", diff --git a/apps/files_external/l10n/nn_NO.js b/apps/files_external/l10n/nn_NO.js index 325a8a57528..7d07e956688 100644 --- a/apps/files_external/l10n/nn_NO.js +++ b/apps/files_external/l10n/nn_NO.js @@ -2,6 +2,8 @@ OC.L10N.register( "files_external", { "Personal" : "Personleg", + "Saving..." : "Lagrar …", + "Save" : "Lagra", "Username" : "Brukarnamn", "Password" : "Passord", "Region" : "Region/fylke", diff --git a/apps/files_external/l10n/nn_NO.json b/apps/files_external/l10n/nn_NO.json index 26e6c02d806..46c27c8197f 100644 --- a/apps/files_external/l10n/nn_NO.json +++ b/apps/files_external/l10n/nn_NO.json @@ -1,5 +1,7 @@ { "translations": { "Personal" : "Personleg", + "Saving..." : "Lagrar …", + "Save" : "Lagra", "Username" : "Brukarnamn", "Password" : "Passord", "Region" : "Region/fylke", diff --git a/apps/files_external/l10n/oc.js b/apps/files_external/l10n/oc.js index 716326f1cae..a11b664fe5e 100644 --- a/apps/files_external/l10n/oc.js +++ b/apps/files_external/l10n/oc.js @@ -33,6 +33,10 @@ OC.L10N.register( "All users. Type to select user or group." : "Totes los utilizaires. Clicatz aicí per restrénher.", "(group)" : "(grop)", "Saved" : "Enregistrat", + "Saving..." : "Enregistrament...", + "Save" : "Enregistrar", + "Username" : "Nom d'utilizaire", + "Password" : "Senhal", "Access key" : "Clau d'accès", "Secret key" : "Clau secreta", "Builtin" : "Integrat", @@ -44,8 +48,6 @@ OC.L10N.register( "Client ID" : "ID Client", "Client secret" : "Secret client", "OpenStack" : "OpenStack", - "Username" : "Nom d'utilizaire", - "Password" : "Senhal", "Tenant name" : "Tenant name", "Identity endpoint URL" : "Identity endpoint URL", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/oc.json b/apps/files_external/l10n/oc.json index bc882de1456..09c99f60adb 100644 --- a/apps/files_external/l10n/oc.json +++ b/apps/files_external/l10n/oc.json @@ -31,6 +31,10 @@ "All users. Type to select user or group." : "Totes los utilizaires. Clicatz aicí per restrénher.", "(group)" : "(grop)", "Saved" : "Enregistrat", + "Saving..." : "Enregistrament...", + "Save" : "Enregistrar", + "Username" : "Nom d'utilizaire", + "Password" : "Senhal", "Access key" : "Clau d'accès", "Secret key" : "Clau secreta", "Builtin" : "Integrat", @@ -42,8 +46,6 @@ "Client ID" : "ID Client", "Client secret" : "Secret client", "OpenStack" : "OpenStack", - "Username" : "Nom d'utilizaire", - "Password" : "Senhal", "Tenant name" : "Tenant name", "Identity endpoint URL" : "Identity endpoint URL", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/pa.js b/apps/files_external/l10n/pa.js index ff376198ae4..5a2de008d1e 100644 --- a/apps/files_external/l10n/pa.js +++ b/apps/files_external/l10n/pa.js @@ -1,6 +1,7 @@ OC.L10N.register( "files_external", { + "Saving..." : "...ਸੰਭਾਲਿਆ ਜਾ ਰਿਹਾ ਹੈ", "Username" : "ਯੂਜ਼ਰ-ਨਾਂ", "Password" : "ਪਾਸਵਰ", "ownCloud" : "ਓਵਨਕਲਾਉਡ", diff --git a/apps/files_external/l10n/pa.json b/apps/files_external/l10n/pa.json index a2ba80af130..4814737ab5a 100644 --- a/apps/files_external/l10n/pa.json +++ b/apps/files_external/l10n/pa.json @@ -1,4 +1,5 @@ { "translations": { + "Saving..." : "...ਸੰਭਾਲਿਆ ਜਾ ਰਿਹਾ ਹੈ", "Username" : "ਯੂਜ਼ਰ-ਨਾਂ", "Password" : "ਪਾਸਵਰ", "ownCloud" : "ਓਵਨਕਲਾਉਡ", diff --git a/apps/files_external/l10n/pl.js b/apps/files_external/l10n/pl.js index 523a913d1f2..bf1bf642be2 100644 --- a/apps/files_external/l10n/pl.js +++ b/apps/files_external/l10n/pl.js @@ -21,13 +21,15 @@ OC.L10N.register( "All users. Type to select user or group." : "Wszyscy użytkownicy. Zacznij pisać, aby wybrać użytkownika lub grupę.", "(group)" : "(grupa)", "Saved" : "Zapisano", + "Saving..." : "Zapisywanie...", + "Save" : "Zapisz", + "Username" : "Nazwa użytkownika", + "Password" : "Hasło", "None" : "Nic", "App key" : "Klucz aplikacji", "App secret" : "Hasło aplikacji", "Client ID" : "ID klienta", "Client secret" : "Hasło klienta", - "Username" : "Nazwa użytkownika", - "Password" : "Hasło", "API key" : "Klucz API", "Public key" : "Klucz publiczny", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/pl.json b/apps/files_external/l10n/pl.json index 25d379cc730..2d77bdca9ee 100644 --- a/apps/files_external/l10n/pl.json +++ b/apps/files_external/l10n/pl.json @@ -19,13 +19,15 @@ "All users. Type to select user or group." : "Wszyscy użytkownicy. Zacznij pisać, aby wybrać użytkownika lub grupę.", "(group)" : "(grupa)", "Saved" : "Zapisano", + "Saving..." : "Zapisywanie...", + "Save" : "Zapisz", + "Username" : "Nazwa użytkownika", + "Password" : "Hasło", "None" : "Nic", "App key" : "Klucz aplikacji", "App secret" : "Hasło aplikacji", "Client ID" : "ID klienta", "Client secret" : "Hasło klienta", - "Username" : "Nazwa użytkownika", - "Password" : "Hasło", "API key" : "Klucz API", "Public key" : "Klucz publiczny", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/pt_BR.js b/apps/files_external/l10n/pt_BR.js index b98c7efe0a4..8706a554d1e 100644 --- a/apps/files_external/l10n/pt_BR.js +++ b/apps/files_external/l10n/pt_BR.js @@ -37,6 +37,8 @@ OC.L10N.register( "(group)" : "(grupo)", "Admin defined" : "Definido pelo administrador", "Saved" : "Salvo", + "Saving..." : "Salvando...", + "Save" : "Salvar", "Empty response from the server" : "Resposta vazia a partir do servidor", "Couldn't access. Please logout and login to activate this mount point" : "Não foi possível acessar. Por favor, desconectar e conectar novamente para ativar este ponto de montagem", "Couldn't get the information from the ownCloud server: {code} {type}" : "Não foi possível obter as informações do servidor ownCloud: {code} {type}", @@ -45,6 +47,8 @@ OC.L10N.register( "External mount error" : "Erro de montagem externa", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Não foi possível obter a lista unidades de pontos de montagem da rede do Windows: resposta vazia a partir do servidor", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Por favor clique na linha vermelha(s) para mais informações", + "Username" : "Nome de Usuário", + "Password" : "Senha", "Access key" : "Chave da acesso", "Secret key" : "Chave secreta", "Builtin" : "Construídas em", @@ -56,12 +60,11 @@ OC.L10N.register( "Client ID" : "ID do Cliente", "Client secret" : "Segredo do cliente", "OpenStack" : "OpenStack", - "Username" : "Nome de Usuário", - "Password" : "Senha", "Tenant name" : "Nome do inquilino", "Identity endpoint URL" : "Identidade pontofinal URL", "Rackspace" : "Espaço em rack", "API key" : "Chave API", + "Global Credentails" : "Credenciais Globais", "Log-in credentials, save in database" : "Credenciais de login, salvos no banco de dados", "Username and password" : "Nome de Usuário e senha", "Log-in credentials, save in session" : "Credenciais de login, guardados em sessão", @@ -108,6 +111,7 @@ OC.L10N.register( "Storage type" : "Tipo de armazenamento", "Scope" : "Escopo", "External Storage" : "Armazenamento Externo", + "Global Credentials" : "Credenciais Globais", "Folder name" : "Nome da pasta", "Authentication" : "Autenticação", "Configuration" : "Configuração", diff --git a/apps/files_external/l10n/pt_BR.json b/apps/files_external/l10n/pt_BR.json index 3e351013fd1..9cdbf5d7a11 100644 --- a/apps/files_external/l10n/pt_BR.json +++ b/apps/files_external/l10n/pt_BR.json @@ -35,6 +35,8 @@ "(group)" : "(grupo)", "Admin defined" : "Definido pelo administrador", "Saved" : "Salvo", + "Saving..." : "Salvando...", + "Save" : "Salvar", "Empty response from the server" : "Resposta vazia a partir do servidor", "Couldn't access. Please logout and login to activate this mount point" : "Não foi possível acessar. Por favor, desconectar e conectar novamente para ativar este ponto de montagem", "Couldn't get the information from the ownCloud server: {code} {type}" : "Não foi possível obter as informações do servidor ownCloud: {code} {type}", @@ -43,6 +45,8 @@ "External mount error" : "Erro de montagem externa", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Não foi possível obter a lista unidades de pontos de montagem da rede do Windows: resposta vazia a partir do servidor", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Por favor clique na linha vermelha(s) para mais informações", + "Username" : "Nome de Usuário", + "Password" : "Senha", "Access key" : "Chave da acesso", "Secret key" : "Chave secreta", "Builtin" : "Construídas em", @@ -54,12 +58,11 @@ "Client ID" : "ID do Cliente", "Client secret" : "Segredo do cliente", "OpenStack" : "OpenStack", - "Username" : "Nome de Usuário", - "Password" : "Senha", "Tenant name" : "Nome do inquilino", "Identity endpoint URL" : "Identidade pontofinal URL", "Rackspace" : "Espaço em rack", "API key" : "Chave API", + "Global Credentails" : "Credenciais Globais", "Log-in credentials, save in database" : "Credenciais de login, salvos no banco de dados", "Username and password" : "Nome de Usuário e senha", "Log-in credentials, save in session" : "Credenciais de login, guardados em sessão", @@ -106,6 +109,7 @@ "Storage type" : "Tipo de armazenamento", "Scope" : "Escopo", "External Storage" : "Armazenamento Externo", + "Global Credentials" : "Credenciais Globais", "Folder name" : "Nome da pasta", "Authentication" : "Autenticação", "Configuration" : "Configuração", diff --git a/apps/files_external/l10n/pt_PT.js b/apps/files_external/l10n/pt_PT.js index ef75f504bcf..5a549eb04c1 100644 --- a/apps/files_external/l10n/pt_PT.js +++ b/apps/files_external/l10n/pt_PT.js @@ -37,14 +37,23 @@ OC.L10N.register( "(group)" : "(grupo)", "Admin defined" : "Administrador definido", "Saved" : "Guardado", + "Saving..." : "A guardar...", + "Save" : "Guardar", "Empty response from the server" : "Resposta vazia a partir do servidor", "Couldn't access. Please logout and login to activate this mount point" : "Não foi possível aceder. Por favor, faça logout e login para ativar este ponto de montagem", "Couldn't get the information from the ownCloud server: {code} {type}" : "Não foi possível recolher a informação do servidor ownCloud: {code} {type}", "Couldn't get the list of external mount points: {type}" : "Não foi possível conseguir a lista de pontos de montagem externos: {type}", "There was an error with message: " : "Houve um erro com a mensagem:", "External mount error" : "Erro de montagem externa", + "external-storage" : "Armazenamento Externo", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Não foi possível conseguir a lista de pontos de montagem Windows na rede: resposta vazia do servidor", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Por favor, clique na fila vermelha para mais informação", + "Please enter the credentials for the {mount} mount" : "Por favor, introduza as credenciais para {mount}", + "Username" : "Nome de utilizador", + "Password" : "Palavra-passe", + "Credentials saved" : "Credenciais guardadas", + "Credentials saving failed" : "Falha ao salvar credenciais", + "Credentials required" : "Credenciais necessárias", "Access key" : "Código de acesso", "Secret key" : "Código secreto", "Builtin" : "Integrado", @@ -56,12 +65,11 @@ OC.L10N.register( "Client ID" : "Id. do Cliente", "Client secret" : "Segredo do cliente\\\\", "OpenStack" : "OpenStack", - "Username" : "Nome de utilizador", - "Password" : "Palavra-passe", "Tenant name" : "Nome do locatário", "Identity endpoint URL" : "Identidade URL endpoint", "Rackspace" : "Rackspace", "API key" : "Chave API", + "Global Credentails" : "Credenciais Gerais", "Log-in credentials, save in database" : "Credenciais de login, guardar na base de dados", "Username and password" : "Nome de utilizador e palavra-passe", "Log-in credentials, save in session" : "Credenciais de login, guardar na sessão", @@ -108,6 +116,7 @@ OC.L10N.register( "Storage type" : "Tipo de Armazenamento", "Scope" : "Âmbito", "External Storage" : "Armazenamento Externo", + "Global Credentials" : "Credenciais Gerais", "Folder name" : "Nome da pasta", "Authentication" : "Autenticação", "Configuration" : "Configuração", diff --git a/apps/files_external/l10n/pt_PT.json b/apps/files_external/l10n/pt_PT.json index 686802379ee..98eac2442e5 100644 --- a/apps/files_external/l10n/pt_PT.json +++ b/apps/files_external/l10n/pt_PT.json @@ -35,14 +35,23 @@ "(group)" : "(grupo)", "Admin defined" : "Administrador definido", "Saved" : "Guardado", + "Saving..." : "A guardar...", + "Save" : "Guardar", "Empty response from the server" : "Resposta vazia a partir do servidor", "Couldn't access. Please logout and login to activate this mount point" : "Não foi possível aceder. Por favor, faça logout e login para ativar este ponto de montagem", "Couldn't get the information from the ownCloud server: {code} {type}" : "Não foi possível recolher a informação do servidor ownCloud: {code} {type}", "Couldn't get the list of external mount points: {type}" : "Não foi possível conseguir a lista de pontos de montagem externos: {type}", "There was an error with message: " : "Houve um erro com a mensagem:", "External mount error" : "Erro de montagem externa", + "external-storage" : "Armazenamento Externo", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Não foi possível conseguir a lista de pontos de montagem Windows na rede: resposta vazia do servidor", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Por favor, clique na fila vermelha para mais informação", + "Please enter the credentials for the {mount} mount" : "Por favor, introduza as credenciais para {mount}", + "Username" : "Nome de utilizador", + "Password" : "Palavra-passe", + "Credentials saved" : "Credenciais guardadas", + "Credentials saving failed" : "Falha ao salvar credenciais", + "Credentials required" : "Credenciais necessárias", "Access key" : "Código de acesso", "Secret key" : "Código secreto", "Builtin" : "Integrado", @@ -54,12 +63,11 @@ "Client ID" : "Id. do Cliente", "Client secret" : "Segredo do cliente\\\\", "OpenStack" : "OpenStack", - "Username" : "Nome de utilizador", - "Password" : "Palavra-passe", "Tenant name" : "Nome do locatário", "Identity endpoint URL" : "Identidade URL endpoint", "Rackspace" : "Rackspace", "API key" : "Chave API", + "Global Credentails" : "Credenciais Gerais", "Log-in credentials, save in database" : "Credenciais de login, guardar na base de dados", "Username and password" : "Nome de utilizador e palavra-passe", "Log-in credentials, save in session" : "Credenciais de login, guardar na sessão", @@ -106,6 +114,7 @@ "Storage type" : "Tipo de Armazenamento", "Scope" : "Âmbito", "External Storage" : "Armazenamento Externo", + "Global Credentials" : "Credenciais Gerais", "Folder name" : "Nome da pasta", "Authentication" : "Autenticação", "Configuration" : "Configuração", diff --git a/apps/files_external/l10n/ro.js b/apps/files_external/l10n/ro.js index cc3a065a21d..636ba958cb5 100644 --- a/apps/files_external/l10n/ro.js +++ b/apps/files_external/l10n/ro.js @@ -6,9 +6,11 @@ OC.L10N.register( "Grant access" : "Permite accesul", "Access granted" : "Acces permis", "Saved" : "Salvat", - "None" : "Niciuna", + "Saving..." : "Se salvează...", + "Save" : "Salvează", "Username" : "Nume utilizator", "Password" : "Parolă", + "None" : "Niciuna", "API key" : "Cheie API", "Public key" : "Cheie publică", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/ro.json b/apps/files_external/l10n/ro.json index 2f1f8e32883..7cff0ef5521 100644 --- a/apps/files_external/l10n/ro.json +++ b/apps/files_external/l10n/ro.json @@ -4,9 +4,11 @@ "Grant access" : "Permite accesul", "Access granted" : "Acces permis", "Saved" : "Salvat", - "None" : "Niciuna", + "Saving..." : "Se salvează...", + "Save" : "Salvează", "Username" : "Nume utilizator", "Password" : "Parolă", + "None" : "Niciuna", "API key" : "Cheie API", "Public key" : "Cheie publică", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/ru.js b/apps/files_external/l10n/ru.js index 989f2e82a41..e0f55115d97 100644 --- a/apps/files_external/l10n/ru.js +++ b/apps/files_external/l10n/ru.js @@ -37,6 +37,8 @@ OC.L10N.register( "(group)" : "(группа)", "Admin defined" : "Админ определен", "Saved" : "Сохранено", + "Saving..." : "Сохранение...", + "Save" : "Сохранить", "Empty response from the server" : "Пустой ответ от сервера", "Couldn't access. Please logout and login to activate this mount point" : "Не удалось получить доступ. Пожалуйста, выйти и войдите чтобы активировать эту точку монтирования", "Couldn't get the information from the ownCloud server: {code} {type}" : "Не удалось получить информацию от сервера OwnCloud: {code} {type}", @@ -45,6 +47,8 @@ OC.L10N.register( "External mount error" : "Ошибка внешнего монтажа", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Не удалось получить список окон сетевого диска точки монтирования: пустой ответ от сервера", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Некоторые из настроенных внешних точек монтирования не подключены. Для получения дополнительной информации, пожалуйста нажмите на красную строку (ы)", + "Username" : "Имя пользователя", + "Password" : "Пароль", "Access key" : "Ключ доступа", "Secret key" : "Секретный ключ", "Builtin" : "Встроенный", @@ -56,8 +60,6 @@ OC.L10N.register( "Client ID" : "Идентификатор клиента", "Client secret" : "Клиентский ключ ", "OpenStack" : "OpenStack", - "Username" : "Имя пользователя", - "Password" : "Пароль", "Tenant name" : "Имя арендатора", "Identity endpoint URL" : "Удостоверение конечной точки URL", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/ru.json b/apps/files_external/l10n/ru.json index 53595aee4a5..eda8da90aca 100644 --- a/apps/files_external/l10n/ru.json +++ b/apps/files_external/l10n/ru.json @@ -35,6 +35,8 @@ "(group)" : "(группа)", "Admin defined" : "Админ определен", "Saved" : "Сохранено", + "Saving..." : "Сохранение...", + "Save" : "Сохранить", "Empty response from the server" : "Пустой ответ от сервера", "Couldn't access. Please logout and login to activate this mount point" : "Не удалось получить доступ. Пожалуйста, выйти и войдите чтобы активировать эту точку монтирования", "Couldn't get the information from the ownCloud server: {code} {type}" : "Не удалось получить информацию от сервера OwnCloud: {code} {type}", @@ -43,6 +45,8 @@ "External mount error" : "Ошибка внешнего монтажа", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Не удалось получить список окон сетевого диска точки монтирования: пустой ответ от сервера", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Некоторые из настроенных внешних точек монтирования не подключены. Для получения дополнительной информации, пожалуйста нажмите на красную строку (ы)", + "Username" : "Имя пользователя", + "Password" : "Пароль", "Access key" : "Ключ доступа", "Secret key" : "Секретный ключ", "Builtin" : "Встроенный", @@ -54,8 +58,6 @@ "Client ID" : "Идентификатор клиента", "Client secret" : "Клиентский ключ ", "OpenStack" : "OpenStack", - "Username" : "Имя пользователя", - "Password" : "Пароль", "Tenant name" : "Имя арендатора", "Identity endpoint URL" : "Удостоверение конечной точки URL", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/si_LK.js b/apps/files_external/l10n/si_LK.js index 82b68af82eb..0cfb795d78c 100644 --- a/apps/files_external/l10n/si_LK.js +++ b/apps/files_external/l10n/si_LK.js @@ -4,9 +4,11 @@ OC.L10N.register( "Personal" : "පෞද්ගලික", "Grant access" : "පිවිසුම ලබාදෙන්න", "Access granted" : "පිවිසීමට හැක", - "None" : "කිසිවක් නැත", + "Saving..." : "සුරැකෙමින් පවතී...", + "Save" : "සුරකින්න", "Username" : "පරිශීලක නම", "Password" : "මුර පදය", + "None" : "කිසිවක් නැත", "Port" : "තොට", "Region" : "කළාපය", "URL" : "URL", diff --git a/apps/files_external/l10n/si_LK.json b/apps/files_external/l10n/si_LK.json index 2150ac0f9b2..d93df685e77 100644 --- a/apps/files_external/l10n/si_LK.json +++ b/apps/files_external/l10n/si_LK.json @@ -2,9 +2,11 @@ "Personal" : "පෞද්ගලික", "Grant access" : "පිවිසුම ලබාදෙන්න", "Access granted" : "පිවිසීමට හැක", - "None" : "කිසිවක් නැත", + "Saving..." : "සුරැකෙමින් පවතී...", + "Save" : "සුරකින්න", "Username" : "පරිශීලක නම", "Password" : "මුර පදය", + "None" : "කිසිවක් නැත", "Port" : "තොට", "Region" : "කළාපය", "URL" : "URL", diff --git a/apps/files_external/l10n/sk_SK.js b/apps/files_external/l10n/sk_SK.js index 72fb57a1e0b..af390cf579f 100644 --- a/apps/files_external/l10n/sk_SK.js +++ b/apps/files_external/l10n/sk_SK.js @@ -32,6 +32,10 @@ OC.L10N.register( "All users. Type to select user or group." : "Všetci používatelia. Začnite písať pre výber používateľa alebo skupinu.", "(group)" : "(skupina)", "Saved" : "Uložené", + "Saving..." : "Ukladám...", + "Save" : "Uložiť", + "Username" : "Používateľské meno", + "Password" : "Heslo", "Access key" : "Prístupový kľúč", "Secret key" : "Tajný kľúč", "Builtin" : "Vstavaný", @@ -43,8 +47,6 @@ OC.L10N.register( "Client ID" : "Client ID", "Client secret" : "Heslo klienta", "OpenStack" : "OpenStack", - "Username" : "Používateľské meno", - "Password" : "Heslo", "Tenant name" : "Meno nájomcu", "Identity endpoint URL" : "Endpoint URL identita", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/sk_SK.json b/apps/files_external/l10n/sk_SK.json index 267eb5b48b9..ff677233a9f 100644 --- a/apps/files_external/l10n/sk_SK.json +++ b/apps/files_external/l10n/sk_SK.json @@ -30,6 +30,10 @@ "All users. Type to select user or group." : "Všetci používatelia. Začnite písať pre výber používateľa alebo skupinu.", "(group)" : "(skupina)", "Saved" : "Uložené", + "Saving..." : "Ukladám...", + "Save" : "Uložiť", + "Username" : "Používateľské meno", + "Password" : "Heslo", "Access key" : "Prístupový kľúč", "Secret key" : "Tajný kľúč", "Builtin" : "Vstavaný", @@ -41,8 +45,6 @@ "Client ID" : "Client ID", "Client secret" : "Heslo klienta", "OpenStack" : "OpenStack", - "Username" : "Používateľské meno", - "Password" : "Heslo", "Tenant name" : "Meno nájomcu", "Identity endpoint URL" : "Endpoint URL identita", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/sl.js b/apps/files_external/l10n/sl.js index 62b35ffab5e..807c6c82693 100644 --- a/apps/files_external/l10n/sl.js +++ b/apps/files_external/l10n/sl.js @@ -25,7 +25,11 @@ OC.L10N.register( "All users. Type to select user or group." : "Vsi uporabniki. Skupino ali uporabnika je mogoče tudi izbrati.", "(group)" : "(skupina)", "Saved" : "Shranjeno", + "Saving..." : "Poteka shranjevanje ...", + "Save" : "Shrani", "External mount error" : "Notranja napaka priklopa", + "Username" : "Uporabniško ime", + "Password" : "Geslo", "Access key" : "Ključ za dostop", "Secret key" : "Skriti ključ", "Builtin" : "Vgrajeno", @@ -37,8 +41,6 @@ OC.L10N.register( "Client ID" : "ID odjemalca", "Client secret" : "Skrivni ključ odjemalca", "OpenStack" : "OpenStack", - "Username" : "Uporabniško ime", - "Password" : "Geslo", "Tenant name" : "Ime uporabnika", "API key" : "Ključ API", "Username and password" : "Uporabniško ime in geslo", diff --git a/apps/files_external/l10n/sl.json b/apps/files_external/l10n/sl.json index 3a806fbd25b..42739bc315c 100644 --- a/apps/files_external/l10n/sl.json +++ b/apps/files_external/l10n/sl.json @@ -23,7 +23,11 @@ "All users. Type to select user or group." : "Vsi uporabniki. Skupino ali uporabnika je mogoče tudi izbrati.", "(group)" : "(skupina)", "Saved" : "Shranjeno", + "Saving..." : "Poteka shranjevanje ...", + "Save" : "Shrani", "External mount error" : "Notranja napaka priklopa", + "Username" : "Uporabniško ime", + "Password" : "Geslo", "Access key" : "Ključ za dostop", "Secret key" : "Skriti ključ", "Builtin" : "Vgrajeno", @@ -35,8 +39,6 @@ "Client ID" : "ID odjemalca", "Client secret" : "Skrivni ključ odjemalca", "OpenStack" : "OpenStack", - "Username" : "Uporabniško ime", - "Password" : "Geslo", "Tenant name" : "Ime uporabnika", "API key" : "Ključ API", "Username and password" : "Uporabniško ime in geslo", diff --git a/apps/files_external/l10n/sq.js b/apps/files_external/l10n/sq.js index 1005519b4da..bab6a24df7f 100644 --- a/apps/files_external/l10n/sq.js +++ b/apps/files_external/l10n/sq.js @@ -37,6 +37,8 @@ OC.L10N.register( "(group)" : "(grup)", "Admin defined" : "Përcaktuar nga përgjegjësi", "Saved" : "U ruajt", + "Saving..." : "Po ruhet …", + "Save" : "Ruaje", "Empty response from the server" : "Përgjigje e zbrazët prej shërbyesit", "Couldn't access. Please logout and login to activate this mount point" : "S’fut dot. Ju lutemi, dilni dhe hyni që të aktivizohet kjo pikë montimi", "Couldn't get the information from the ownCloud server: {code} {type}" : "S’u morën dot të dhëna nga shërbyesi ownCloud: {code} {type}", @@ -45,6 +47,12 @@ OC.L10N.register( "External mount error" : "Gabim i jashtëm montimi", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "S’u mor dot lista e pikave të montimit Windows network drive: përgjigje e zbrazët nga shërbyesi", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Disa nga pikat e jashtme të formësuara të montimit s’janë të lidhura. Ju lutemi, klikoni në shigjetën(at) e kuqe për më tepër të dhëna", + "Please enter the credentials for the {mount} mount" : "Ju lutemi, jepni kredencialet për pikën e montimit {mount}", + "Username" : "Emër përdoruesi", + "Password" : "Fjalëkalim", + "Credentials saved" : "Kredencialet u ruajtën", + "Credentials saving failed" : "Ruajtja e kredencialeve dështoi", + "Credentials required" : "Lypsen kredenciale", "Access key" : "Kyç hyrjesh", "Secret key" : "Kyç i fshehtë", "Builtin" : "I brendshëm", @@ -56,11 +64,10 @@ OC.L10N.register( "Client ID" : "ID klienti", "Client secret" : "E fshehtë klienti", "OpenStack" : "OpenStack", - "Username" : "Emër përdoruesi", - "Password" : "Fjalëkalim", "Tenant name" : "Emër qiraxhiu", "Rackspace" : "Rackspace", "API key" : "Kyç API", + "Global Credentails" : "Kredenciale Globale", "Log-in credentials, save in database" : "Kredenciale hyrjesh, ruaje në bazën e të dhënave", "Username and password" : "Emër përdoruesi dhe fjalëkalim", "Log-in credentials, save in session" : "Kredenciale hyrjesh, ruaje në sesion", @@ -107,6 +114,7 @@ OC.L10N.register( "Storage type" : "Lloj depozite", "Scope" : "Shtrirje", "External Storage" : "Depozitë e Jashtme", + "Global Credentials" : "Kredenciale Globale", "Folder name" : "Emër dosjeje", "Authentication" : "Mirëfilltësim", "Configuration" : "Formësim", diff --git a/apps/files_external/l10n/sq.json b/apps/files_external/l10n/sq.json index 5cadfb3acd0..7c132ddd855 100644 --- a/apps/files_external/l10n/sq.json +++ b/apps/files_external/l10n/sq.json @@ -35,6 +35,8 @@ "(group)" : "(grup)", "Admin defined" : "Përcaktuar nga përgjegjësi", "Saved" : "U ruajt", + "Saving..." : "Po ruhet …", + "Save" : "Ruaje", "Empty response from the server" : "Përgjigje e zbrazët prej shërbyesit", "Couldn't access. Please logout and login to activate this mount point" : "S’fut dot. Ju lutemi, dilni dhe hyni që të aktivizohet kjo pikë montimi", "Couldn't get the information from the ownCloud server: {code} {type}" : "S’u morën dot të dhëna nga shërbyesi ownCloud: {code} {type}", @@ -43,6 +45,12 @@ "External mount error" : "Gabim i jashtëm montimi", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "S’u mor dot lista e pikave të montimit Windows network drive: përgjigje e zbrazët nga shërbyesi", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Disa nga pikat e jashtme të formësuara të montimit s’janë të lidhura. Ju lutemi, klikoni në shigjetën(at) e kuqe për më tepër të dhëna", + "Please enter the credentials for the {mount} mount" : "Ju lutemi, jepni kredencialet për pikën e montimit {mount}", + "Username" : "Emër përdoruesi", + "Password" : "Fjalëkalim", + "Credentials saved" : "Kredencialet u ruajtën", + "Credentials saving failed" : "Ruajtja e kredencialeve dështoi", + "Credentials required" : "Lypsen kredenciale", "Access key" : "Kyç hyrjesh", "Secret key" : "Kyç i fshehtë", "Builtin" : "I brendshëm", @@ -54,11 +62,10 @@ "Client ID" : "ID klienti", "Client secret" : "E fshehtë klienti", "OpenStack" : "OpenStack", - "Username" : "Emër përdoruesi", - "Password" : "Fjalëkalim", "Tenant name" : "Emër qiraxhiu", "Rackspace" : "Rackspace", "API key" : "Kyç API", + "Global Credentails" : "Kredenciale Globale", "Log-in credentials, save in database" : "Kredenciale hyrjesh, ruaje në bazën e të dhënave", "Username and password" : "Emër përdoruesi dhe fjalëkalim", "Log-in credentials, save in session" : "Kredenciale hyrjesh, ruaje në sesion", @@ -105,6 +112,7 @@ "Storage type" : "Lloj depozite", "Scope" : "Shtrirje", "External Storage" : "Depozitë e Jashtme", + "Global Credentials" : "Kredenciale Globale", "Folder name" : "Emër dosjeje", "Authentication" : "Mirëfilltësim", "Configuration" : "Formësim", diff --git a/apps/files_external/l10n/sr.js b/apps/files_external/l10n/sr.js index a90d36b586f..0600f747509 100644 --- a/apps/files_external/l10n/sr.js +++ b/apps/files_external/l10n/sr.js @@ -21,13 +21,15 @@ OC.L10N.register( "All users. Type to select user or group." : "Сви корисници. Куцајте за избор корисника или групе.", "(group)" : "(група)", "Saved" : "Сачувано", + "Saving..." : "Уписујем...", + "Save" : "Сачувај", + "Username" : "Корисничко име", + "Password" : "Лозинка", "None" : "Ништа", "App key" : "Кључ апликације", "App secret" : "Тајна апликације", "Client ID" : "ИД клијента", "Client secret" : "Тајна клијента", - "Username" : "Корисничко име", - "Password" : "Лозинка", "API key" : "API кључ", "Public key" : "Јавни кључ", "Amazon S3" : "Амазон С3", diff --git a/apps/files_external/l10n/sr.json b/apps/files_external/l10n/sr.json index 6c938e8a14c..aa8471447df 100644 --- a/apps/files_external/l10n/sr.json +++ b/apps/files_external/l10n/sr.json @@ -19,13 +19,15 @@ "All users. Type to select user or group." : "Сви корисници. Куцајте за избор корисника или групе.", "(group)" : "(група)", "Saved" : "Сачувано", + "Saving..." : "Уписујем...", + "Save" : "Сачувај", + "Username" : "Корисничко име", + "Password" : "Лозинка", "None" : "Ништа", "App key" : "Кључ апликације", "App secret" : "Тајна апликације", "Client ID" : "ИД клијента", "Client secret" : "Тајна клијента", - "Username" : "Корисничко име", - "Password" : "Лозинка", "API key" : "API кључ", "Public key" : "Јавни кључ", "Amazon S3" : "Амазон С3", diff --git a/apps/files_external/l10n/sr@latin.js b/apps/files_external/l10n/sr@latin.js index 0420189571c..3f73fd420ed 100644 --- a/apps/files_external/l10n/sr@latin.js +++ b/apps/files_external/l10n/sr@latin.js @@ -11,12 +11,14 @@ OC.L10N.register( "All users. Type to select user or group." : "Svi korisnici. Kucajte da biste izabrali korisnika ili grupu.", "(group)" : "(grupa)", "Saved" : "Sačuvano", + "Saving..." : "Upisujem...", + "Save" : "Sačuvaj", + "Username" : "Korisničko ime", + "Password" : "Lozinka", "App key" : "Ključ Aplikacije", "App secret" : "Tajna lozinka Aplikacije", "Client ID" : "Identifikator klijenta", "Client secret" : "Tajna lozinka klijenta", - "Username" : "Korisničko ime", - "Password" : "Lozinka", "Amazon S3" : "Amazon S3", "Bucket" : "Korpa", "Hostname" : "Ime računara", diff --git a/apps/files_external/l10n/sr@latin.json b/apps/files_external/l10n/sr@latin.json index 3320ab2c863..92df9c7146b 100644 --- a/apps/files_external/l10n/sr@latin.json +++ b/apps/files_external/l10n/sr@latin.json @@ -9,12 +9,14 @@ "All users. Type to select user or group." : "Svi korisnici. Kucajte da biste izabrali korisnika ili grupu.", "(group)" : "(grupa)", "Saved" : "Sačuvano", + "Saving..." : "Upisujem...", + "Save" : "Sačuvaj", + "Username" : "Korisničko ime", + "Password" : "Lozinka", "App key" : "Ključ Aplikacije", "App secret" : "Tajna lozinka Aplikacije", "Client ID" : "Identifikator klijenta", "Client secret" : "Tajna lozinka klijenta", - "Username" : "Korisničko ime", - "Password" : "Lozinka", "Amazon S3" : "Amazon S3", "Bucket" : "Korpa", "Hostname" : "Ime računara", diff --git a/apps/files_external/l10n/sv.js b/apps/files_external/l10n/sv.js index f20b96caed8..76da77967db 100644 --- a/apps/files_external/l10n/sv.js +++ b/apps/files_external/l10n/sv.js @@ -11,13 +11,15 @@ OC.L10N.register( "All users. Type to select user or group." : "Alla användare. Skriv för att välja användare eller grupp.", "(group)" : "(grupp)", "Saved" : "Sparad", + "Saving..." : "Sparar...", + "Save" : "Spara", + "Username" : "Användarnamn", + "Password" : "Lösenord", "None" : "Ingen", "App key" : "App-nyckel", "App secret" : "App-hemlighet", "Client ID" : "Klient ID", "Client secret" : "klient secret", - "Username" : "Användarnamn", - "Password" : "Lösenord", "API key" : "API-nyckel", "Public key" : "Publik nyckel", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/sv.json b/apps/files_external/l10n/sv.json index cd3dc13296d..6f5d4dfe3aa 100644 --- a/apps/files_external/l10n/sv.json +++ b/apps/files_external/l10n/sv.json @@ -9,13 +9,15 @@ "All users. Type to select user or group." : "Alla användare. Skriv för att välja användare eller grupp.", "(group)" : "(grupp)", "Saved" : "Sparad", + "Saving..." : "Sparar...", + "Save" : "Spara", + "Username" : "Användarnamn", + "Password" : "Lösenord", "None" : "Ingen", "App key" : "App-nyckel", "App secret" : "App-hemlighet", "Client ID" : "Klient ID", "Client secret" : "klient secret", - "Username" : "Användarnamn", - "Password" : "Lösenord", "API key" : "API-nyckel", "Public key" : "Publik nyckel", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/ta_LK.js b/apps/files_external/l10n/ta_LK.js index 8ab0f6d513c..87f2dabd7ce 100644 --- a/apps/files_external/l10n/ta_LK.js +++ b/apps/files_external/l10n/ta_LK.js @@ -4,9 +4,11 @@ OC.L10N.register( "Personal" : "தனிப்பட்ட", "Grant access" : "அனுமதியை வழங்கல்", "Access granted" : "அனுமதி வழங்கப்பட்டது", - "None" : "ஒன்றுமில்லை", + "Saving..." : "சேமிக்கப்படுகிறது...", + "Save" : "சேமிக்க ", "Username" : "பயனாளர் பெயர்", "Password" : "கடவுச்சொல்", + "None" : "ஒன்றுமில்லை", "Port" : "துறை ", "Region" : "பிரதேசம்", "URL" : "URL", diff --git a/apps/files_external/l10n/ta_LK.json b/apps/files_external/l10n/ta_LK.json index dd1c1003728..00959a601b1 100644 --- a/apps/files_external/l10n/ta_LK.json +++ b/apps/files_external/l10n/ta_LK.json @@ -2,9 +2,11 @@ "Personal" : "தனிப்பட்ட", "Grant access" : "அனுமதியை வழங்கல்", "Access granted" : "அனுமதி வழங்கப்பட்டது", - "None" : "ஒன்றுமில்லை", + "Saving..." : "சேமிக்கப்படுகிறது...", + "Save" : "சேமிக்க ", "Username" : "பயனாளர் பெயர்", "Password" : "கடவுச்சொல்", + "None" : "ஒன்றுமில்லை", "Port" : "துறை ", "Region" : "பிரதேசம்", "URL" : "URL", diff --git a/apps/files_external/l10n/te.js b/apps/files_external/l10n/te.js index 155676d9a8f..4c05e915da4 100644 --- a/apps/files_external/l10n/te.js +++ b/apps/files_external/l10n/te.js @@ -2,6 +2,7 @@ OC.L10N.register( "files_external", { "Personal" : "వ్యక్తిగతం", + "Save" : "భద్రపరచు", "Username" : "వాడుకరి పేరు", "Password" : "సంకేతపదం", "Name" : "పేరు", diff --git a/apps/files_external/l10n/te.json b/apps/files_external/l10n/te.json index 5a55f60376a..493efe033f7 100644 --- a/apps/files_external/l10n/te.json +++ b/apps/files_external/l10n/te.json @@ -1,5 +1,6 @@ { "translations": { "Personal" : "వ్యక్తిగతం", + "Save" : "భద్రపరచు", "Username" : "వాడుకరి పేరు", "Password" : "సంకేతపదం", "Name" : "పేరు", diff --git a/apps/files_external/l10n/th_TH.js b/apps/files_external/l10n/th_TH.js index 19ac36fa429..e8f3aa3aaec 100644 --- a/apps/files_external/l10n/th_TH.js +++ b/apps/files_external/l10n/th_TH.js @@ -37,6 +37,8 @@ OC.L10N.register( "(group)" : "(กลุ่ม)", "Admin defined" : "ถูกกำหนดโดยผู้ดูแลระบบ", "Saved" : "บันทึกแล้ว", + "Saving..." : "กำลังบันทึกข้อมูล...", + "Save" : "บันทึก", "Empty response from the server" : "ไม่มีการตอบสนองจากเซิร์ฟเวอร์", "Couldn't access. Please logout and login to activate this mount point" : "ไม่สามารถเข้าถึง กรุณออกจากระบบและาเข้าสู่ระบบใหม่เพื่อเปิดใช้งานจุดเชื่อมต่อนี้", "Couldn't get the information from the ownCloud server: {code} {type}" : "ไม่สามารถรับข้อมูลจากเซิร์ฟเวอร์ ownCloud: {code} {type}", @@ -45,6 +47,8 @@ OC.L10N.register( "External mount error" : "การติดจากตั้งภายนอกเกิดข้อผิดพลาด", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "ไม่สามารถรับรายชื่อไดรฟ์เครือข่ายของวินโดว์ส จุดที่ติดตั้ง: ไม่มีการตอบสนองจากเซิร์ฟเวอร์", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "การกำหนดค่าบางส่วนของจุดเชื่อมต่อภายนอกไม่ถูกเชื่อมต่อ กรุณาคลิกที่ตรงสีแดงสำหรับข้อมูลเพิ่มเติม", + "Username" : "ชื่อผู้ใช้งาน", + "Password" : "รหัสผ่าน", "Access key" : "คีย์การเข้าถึง", "Secret key" : "คีย์ลับ", "Builtin" : "ในตัว", @@ -56,8 +60,6 @@ OC.L10N.register( "Client ID" : "Client ID", "Client secret" : "Client secret", "OpenStack" : "OpenStack", - "Username" : "ชื่อผู้ใช้งาน", - "Password" : "รหัสผ่าน", "Tenant name" : "ชื่อผู้เช่า", "Identity endpoint URL" : "ตัวตนของ URL ปลายทาง", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/th_TH.json b/apps/files_external/l10n/th_TH.json index f3ef84f8a0f..5cdfd95411f 100644 --- a/apps/files_external/l10n/th_TH.json +++ b/apps/files_external/l10n/th_TH.json @@ -35,6 +35,8 @@ "(group)" : "(กลุ่ม)", "Admin defined" : "ถูกกำหนดโดยผู้ดูแลระบบ", "Saved" : "บันทึกแล้ว", + "Saving..." : "กำลังบันทึกข้อมูล...", + "Save" : "บันทึก", "Empty response from the server" : "ไม่มีการตอบสนองจากเซิร์ฟเวอร์", "Couldn't access. Please logout and login to activate this mount point" : "ไม่สามารถเข้าถึง กรุณออกจากระบบและาเข้าสู่ระบบใหม่เพื่อเปิดใช้งานจุดเชื่อมต่อนี้", "Couldn't get the information from the ownCloud server: {code} {type}" : "ไม่สามารถรับข้อมูลจากเซิร์ฟเวอร์ ownCloud: {code} {type}", @@ -43,6 +45,8 @@ "External mount error" : "การติดจากตั้งภายนอกเกิดข้อผิดพลาด", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "ไม่สามารถรับรายชื่อไดรฟ์เครือข่ายของวินโดว์ส จุดที่ติดตั้ง: ไม่มีการตอบสนองจากเซิร์ฟเวอร์", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "การกำหนดค่าบางส่วนของจุดเชื่อมต่อภายนอกไม่ถูกเชื่อมต่อ กรุณาคลิกที่ตรงสีแดงสำหรับข้อมูลเพิ่มเติม", + "Username" : "ชื่อผู้ใช้งาน", + "Password" : "รหัสผ่าน", "Access key" : "คีย์การเข้าถึง", "Secret key" : "คีย์ลับ", "Builtin" : "ในตัว", @@ -54,8 +58,6 @@ "Client ID" : "Client ID", "Client secret" : "Client secret", "OpenStack" : "OpenStack", - "Username" : "ชื่อผู้ใช้งาน", - "Password" : "รหัสผ่าน", "Tenant name" : "ชื่อผู้เช่า", "Identity endpoint URL" : "ตัวตนของ URL ปลายทาง", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/tr.js b/apps/files_external/l10n/tr.js index cb506b24c1f..0473544da04 100644 --- a/apps/files_external/l10n/tr.js +++ b/apps/files_external/l10n/tr.js @@ -33,6 +33,10 @@ OC.L10N.register( "All users. Type to select user or group." : "Tüm kullanıcılar. Kullanıcı veya grup seçmek için yazın.", "(group)" : "(grup)", "Saved" : "Kaydedildi", + "Saving..." : "Kaydediliyor...", + "Save" : "Kaydet", + "Username" : "Kullanıcı Adı", + "Password" : "Parola", "Access key" : "Erişim anahtarı", "Secret key" : "Gizli anahtar", "Builtin" : "Yerleşik", @@ -44,8 +48,6 @@ OC.L10N.register( "Client ID" : "İstemci kimliği", "Client secret" : "İstemci parolası", "OpenStack" : "OpenStack", - "Username" : "Kullanıcı Adı", - "Password" : "Parola", "Tenant name" : "Kiracı adı", "Identity endpoint URL" : "Kimlik uç nokta URL'si", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/tr.json b/apps/files_external/l10n/tr.json index 1eeaafa433c..384bf0afe2f 100644 --- a/apps/files_external/l10n/tr.json +++ b/apps/files_external/l10n/tr.json @@ -31,6 +31,10 @@ "All users. Type to select user or group." : "Tüm kullanıcılar. Kullanıcı veya grup seçmek için yazın.", "(group)" : "(grup)", "Saved" : "Kaydedildi", + "Saving..." : "Kaydediliyor...", + "Save" : "Kaydet", + "Username" : "Kullanıcı Adı", + "Password" : "Parola", "Access key" : "Erişim anahtarı", "Secret key" : "Gizli anahtar", "Builtin" : "Yerleşik", @@ -42,8 +46,6 @@ "Client ID" : "İstemci kimliği", "Client secret" : "İstemci parolası", "OpenStack" : "OpenStack", - "Username" : "Kullanıcı Adı", - "Password" : "Parola", "Tenant name" : "Kiracı adı", "Identity endpoint URL" : "Kimlik uç nokta URL'si", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/ug.js b/apps/files_external/l10n/ug.js index 37adb851973..a10cc344c4a 100644 --- a/apps/files_external/l10n/ug.js +++ b/apps/files_external/l10n/ug.js @@ -3,9 +3,11 @@ OC.L10N.register( { "External storage" : "سىرتقى ساقلىغۇچ", "Personal" : "شەخسىي", - "None" : "يوق", + "Saving..." : "ساقلاۋاتىدۇ…", + "Save" : "ساقلا", "Username" : "ئىشلەتكۈچى ئاتى", "Password" : "ئىم", + "None" : "يوق", "Port" : "ئېغىز", "WebDAV" : "WebDAV", "URL" : "URL", diff --git a/apps/files_external/l10n/ug.json b/apps/files_external/l10n/ug.json index f7a072bfe38..db22e09ec11 100644 --- a/apps/files_external/l10n/ug.json +++ b/apps/files_external/l10n/ug.json @@ -1,9 +1,11 @@ { "translations": { "External storage" : "سىرتقى ساقلىغۇچ", "Personal" : "شەخسىي", - "None" : "يوق", + "Saving..." : "ساقلاۋاتىدۇ…", + "Save" : "ساقلا", "Username" : "ئىشلەتكۈچى ئاتى", "Password" : "ئىم", + "None" : "يوق", "Port" : "ئېغىز", "WebDAV" : "WebDAV", "URL" : "URL", diff --git a/apps/files_external/l10n/uk.js b/apps/files_external/l10n/uk.js index 702b3e328ce..0333998c9df 100644 --- a/apps/files_external/l10n/uk.js +++ b/apps/files_external/l10n/uk.js @@ -17,13 +17,15 @@ OC.L10N.register( "All users. Type to select user or group." : "Всі користувачі. Введіть ім'я користувача або групи.", "(group)" : "(група)", "Saved" : "Збережено", + "Saving..." : "Збереження...", + "Save" : "Зберегти", + "Username" : "Ім'я користувача", + "Password" : "Пароль", "None" : "Жоден", "App key" : "Ключ додатку", "App secret" : "Секретний ключ додатку", "Client ID" : "Ідентифікатор клієнта", "Client secret" : "Ключ клієнта", - "Username" : "Ім'я користувача", - "Password" : "Пароль", "API key" : "ключ API", "Public key" : "Відкритий ключ", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/uk.json b/apps/files_external/l10n/uk.json index cddda62118f..f45274195ae 100644 --- a/apps/files_external/l10n/uk.json +++ b/apps/files_external/l10n/uk.json @@ -15,13 +15,15 @@ "All users. Type to select user or group." : "Всі користувачі. Введіть ім'я користувача або групи.", "(group)" : "(група)", "Saved" : "Збережено", + "Saving..." : "Збереження...", + "Save" : "Зберегти", + "Username" : "Ім'я користувача", + "Password" : "Пароль", "None" : "Жоден", "App key" : "Ключ додатку", "App secret" : "Секретний ключ додатку", "Client ID" : "Ідентифікатор клієнта", "Client secret" : "Ключ клієнта", - "Username" : "Ім'я користувача", - "Password" : "Пароль", "API key" : "ключ API", "Public key" : "Відкритий ключ", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/ur_PK.js b/apps/files_external/l10n/ur_PK.js index 55c8ee1f1bc..0a41084c07b 100644 --- a/apps/files_external/l10n/ur_PK.js +++ b/apps/files_external/l10n/ur_PK.js @@ -2,6 +2,8 @@ OC.L10N.register( "files_external", { "Personal" : "شخصی", + "Saving..." : "محفوظ ھو رہا ہے ...", + "Save" : "حفظ", "Username" : "یوزر نیم", "Password" : "پاسورڈ", "URL" : "یو ار ایل", diff --git a/apps/files_external/l10n/ur_PK.json b/apps/files_external/l10n/ur_PK.json index e8b9c79e1f1..175d2e32414 100644 --- a/apps/files_external/l10n/ur_PK.json +++ b/apps/files_external/l10n/ur_PK.json @@ -1,5 +1,7 @@ { "translations": { "Personal" : "شخصی", + "Saving..." : "محفوظ ھو رہا ہے ...", + "Save" : "حفظ", "Username" : "یوزر نیم", "Password" : "پاسورڈ", "URL" : "یو ار ایل", diff --git a/apps/files_external/l10n/vi.js b/apps/files_external/l10n/vi.js index 65b3d492429..642299b8941 100644 --- a/apps/files_external/l10n/vi.js +++ b/apps/files_external/l10n/vi.js @@ -6,9 +6,11 @@ OC.L10N.register( "Grant access" : "Cấp quyền truy cập", "Access granted" : "Đã cấp quyền truy cập", "Saved" : "Đã lưu", - "None" : "Không gì cả", + "Saving..." : "Đang lưu...", + "Save" : "Lưu", "Username" : "Tên đăng nhập", "Password" : "Mật khẩu", + "None" : "Không gì cả", "Port" : "Cổng", "Region" : "Vùng/miền", "WebDAV" : "WebDAV", diff --git a/apps/files_external/l10n/vi.json b/apps/files_external/l10n/vi.json index 031dddee8e3..95cf65233d7 100644 --- a/apps/files_external/l10n/vi.json +++ b/apps/files_external/l10n/vi.json @@ -4,9 +4,11 @@ "Grant access" : "Cấp quyền truy cập", "Access granted" : "Đã cấp quyền truy cập", "Saved" : "Đã lưu", - "None" : "Không gì cả", + "Saving..." : "Đang lưu...", + "Save" : "Lưu", "Username" : "Tên đăng nhập", "Password" : "Mật khẩu", + "None" : "Không gì cả", "Port" : "Cổng", "Region" : "Vùng/miền", "WebDAV" : "WebDAV", diff --git a/apps/files_external/l10n/zh_CN.js b/apps/files_external/l10n/zh_CN.js index ca35a97bb53..989c31e915a 100644 --- a/apps/files_external/l10n/zh_CN.js +++ b/apps/files_external/l10n/zh_CN.js @@ -10,9 +10,11 @@ OC.L10N.register( "Access granted" : "权限已授予。", "Enable encryption" : "启用加密", "Saved" : "已保存", - "None" : "无", + "Saving..." : "保存中...", + "Save" : "保存", "Username" : "用户名", "Password" : "密码", + "None" : "无", "API key" : "API密匙", "Amazon S3" : "Amazon S3", "Port" : "端口", diff --git a/apps/files_external/l10n/zh_CN.json b/apps/files_external/l10n/zh_CN.json index 53c1df78899..dfc01a86c7b 100644 --- a/apps/files_external/l10n/zh_CN.json +++ b/apps/files_external/l10n/zh_CN.json @@ -8,9 +8,11 @@ "Access granted" : "权限已授予。", "Enable encryption" : "启用加密", "Saved" : "已保存", - "None" : "无", + "Saving..." : "保存中...", + "Save" : "保存", "Username" : "用户名", "Password" : "密码", + "None" : "无", "API key" : "API密匙", "Amazon S3" : "Amazon S3", "Port" : "端口", diff --git a/apps/files_external/l10n/zh_HK.js b/apps/files_external/l10n/zh_HK.js index 7a38dd7ffed..8a9eafeb906 100644 --- a/apps/files_external/l10n/zh_HK.js +++ b/apps/files_external/l10n/zh_HK.js @@ -3,9 +3,11 @@ OC.L10N.register( { "Personal" : "個人", "Saved" : "已儲存", - "None" : "空", + "Saving..." : "儲存中...", + "Save" : "儲存", "Username" : "用戶名稱", "Password" : "密碼", + "None" : "空", "Port" : "連接埠", "WebDAV" : "WebDAV", "URL" : "網址", diff --git a/apps/files_external/l10n/zh_HK.json b/apps/files_external/l10n/zh_HK.json index befeede4bbe..d6dbd19c486 100644 --- a/apps/files_external/l10n/zh_HK.json +++ b/apps/files_external/l10n/zh_HK.json @@ -1,9 +1,11 @@ { "translations": { "Personal" : "個人", "Saved" : "已儲存", - "None" : "空", + "Saving..." : "儲存中...", + "Save" : "儲存", "Username" : "用戶名稱", "Password" : "密碼", + "None" : "空", "Port" : "連接埠", "WebDAV" : "WebDAV", "URL" : "網址", diff --git a/apps/files_external/l10n/zh_TW.js b/apps/files_external/l10n/zh_TW.js index ff0857709e8..1d12fc25384 100644 --- a/apps/files_external/l10n/zh_TW.js +++ b/apps/files_external/l10n/zh_TW.js @@ -33,10 +33,14 @@ OC.L10N.register( "All users. Type to select user or group." : "所有人都可以使用,或者選擇特定使用者、群組", "(group)" : "(群組)", "Saved" : "已儲存", + "Saving..." : "儲存中...", + "Save" : "儲存", "Couldn't access. Please logout and login to activate this mount point" : "無法存取。請重新登出再登入啟動此掛載點。", "Couldn't get the information from the ownCloud server: {code} {type}" : "無法從ownCloud伺服器得到資訊: {code} {type}", "Couldn't get the list of external mount points: {type}" : "無法得到外部掛載點的列表: {type}", "External mount error" : "外部掛載錯誤", + "Username" : "使用者名稱", + "Password" : "密碼", "Access key" : "存取金鑰", "Secret key" : "私密金鑰", "None" : "無", @@ -45,8 +49,6 @@ OC.L10N.register( "Client ID" : "客戶端ID", "Client secret" : "客戶端密碼", "OpenStack" : "OpenStack", - "Username" : "使用者名稱", - "Password" : "密碼", "Tenant name" : "租戶/專案名稱", "Identity endpoint URL" : "身份識別終端點 URL", "Rackspace" : "Rackspace", diff --git a/apps/files_external/l10n/zh_TW.json b/apps/files_external/l10n/zh_TW.json index 4fd9424bc6f..4363e8ea347 100644 --- a/apps/files_external/l10n/zh_TW.json +++ b/apps/files_external/l10n/zh_TW.json @@ -31,10 +31,14 @@ "All users. Type to select user or group." : "所有人都可以使用,或者選擇特定使用者、群組", "(group)" : "(群組)", "Saved" : "已儲存", + "Saving..." : "儲存中...", + "Save" : "儲存", "Couldn't access. Please logout and login to activate this mount point" : "無法存取。請重新登出再登入啟動此掛載點。", "Couldn't get the information from the ownCloud server: {code} {type}" : "無法從ownCloud伺服器得到資訊: {code} {type}", "Couldn't get the list of external mount points: {type}" : "無法得到外部掛載點的列表: {type}", "External mount error" : "外部掛載錯誤", + "Username" : "使用者名稱", + "Password" : "密碼", "Access key" : "存取金鑰", "Secret key" : "私密金鑰", "None" : "無", @@ -43,8 +47,6 @@ "Client ID" : "客戶端ID", "Client secret" : "客戶端密碼", "OpenStack" : "OpenStack", - "Username" : "使用者名稱", - "Password" : "密碼", "Tenant name" : "租戶/專案名稱", "Identity endpoint URL" : "身份識別終端點 URL", "Rackspace" : "Rackspace", diff --git a/apps/files_external/lib/definitionparameter.php b/apps/files_external/lib/definitionparameter.php index 27c6af0fcda..4d4bb13bebd 100644 --- a/apps/files_external/lib/definitionparameter.php +++ b/apps/files_external/lib/definitionparameter.php @@ -93,6 +93,22 @@ class DefinitionParameter implements \JsonSerializable { } /** + * @return string + */ + public function getTypeName() { + switch ($this->type) { + case self::VALUE_BOOLEAN: + return 'boolean'; + case self::VALUE_TEXT: + return 'text'; + case self::VALUE_PASSWORD: + return 'password'; + default: + return 'unknown'; + } + } + + /** * @return int */ public function getFlags() { diff --git a/apps/files_sharing/api/share20ocs.php b/apps/files_sharing/api/share20ocs.php index d7c5d004d9c..67a94aaf8aa 100644 --- a/apps/files_sharing/api/share20ocs.php +++ b/apps/files_sharing/api/share20ocs.php @@ -26,6 +26,7 @@ use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUser; use OCP\Files\IRootFolder; +use OCP\Share; use OCP\Share\IManager; use OCP\Share\Exceptions\ShareNotFound; @@ -164,13 +165,20 @@ class Share20OCS { } if ($share === null) { - //For now federated shares are handled by the old endpoint. - return \OCA\Files_Sharing\API\Local::getShare(['id' => $id]); + if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { + return new \OC_OCS_Result(null, 404, 'wrong share ID, share doesn\'t exist.'); + } + + try { + $share = $this->shareManager->getShareById('ocFederatedSharing:' . $id); + } catch (ShareNotFound $e) { + return new \OC_OCS_Result(null, 404, 'wrong share ID, share doesn\'t exist.'); + } } if ($this->canAccessShare($share)) { $share = $this->formatShare($share); - return new \OC_OCS_Result($share); + return new \OC_OCS_Result([$share]); } else { return new \OC_OCS_Result(null, 404, 'wrong share ID, share doesn\'t exist.'); } @@ -195,7 +203,15 @@ class Share20OCS { // Could not find the share as internal share... maybe it is a federated share if ($share === null) { - return \OCA\Files_Sharing\API\Local::deleteShare(['id' => $id]); + if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { + return new \OC_OCS_Result(null, 404, 'wrong share ID, share doesn\'t exist.'); + } + + try { + $share = $this->shareManager->getShareById('ocFederatedSharing:' . $id); + } catch (ShareNotFound $e) { + return new \OC_OCS_Result(null, 404, 'wrong share ID, share doesn\'t exist.'); + } } if (!$this->canAccessShare($share)) { @@ -313,8 +329,12 @@ class Share20OCS { } } else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) { - //fixme Remote shares are handled by old code path for now - return \OCA\Files_Sharing\API\Local::createShare([]); + if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { + return new \OC_OCS_Result(null, 403, 'Sharing '.$path.' failed, because the backend does not allow shares from type '.$shareType); + } + + $share->setSharedWith($shareWith); + $share->setPermissions($permissions); } else { return new \OC_OCS_Result(null, 400, "unknown share type"); } @@ -368,11 +388,12 @@ class Share20OCS { /** @var \OCP\Share\IShare[] $shares */ $shares = []; foreach ($nodes as $node) { - $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $node, false, -1, 0)); + $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $node, false, -1, 0)); $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $node, false, -1, 0)); - $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_LINK, $node, false, -1, 0)); - //TODO: Add federated shares - + $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_LINK, $node, false, -1, 0)); + if ($this->shareManager->outgoingServer2ServerSharesAllowed()) { + $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_REMOTE, $node, false, -1, 0)); + } } $formatted = []; @@ -427,10 +448,14 @@ class Share20OCS { $userShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $path, $reshares, -1, 0); $groupShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0); $linkShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0); - //TODO: Add federated shares - $shares = array_merge($userShares, $groupShares, $linkShares); + if ($this->shareManager->outgoingServer2ServerSharesAllowed()) { + $federatedShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0); + $shares = array_merge($shares, $federatedShares); + } + + $formatted = []; foreach ($shares as $share) { $formatted[] = $this->formatShare($share); @@ -456,7 +481,15 @@ class Share20OCS { // Could not find the share as internal share... maybe it is a federated share if ($share === null) { - return \OCA\Files_Sharing\API\Local::updateShare(['id' => $id]); + if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { + return new \OC_OCS_Result(null, 404, 'wrong share ID, share doesn\'t exist.'); + } + + try { + $share = $this->shareManager->getShareById('ocFederatedSharing:' . $id); + } catch (ShareNotFound $e) { + return new \OC_OCS_Result(null, 404, 'wrong share ID, share doesn\'t exist.'); + } } if (!$this->canAccessShare($share)) { diff --git a/apps/files_sharing/appinfo/info.xml b/apps/files_sharing/appinfo/info.xml index 17826be47b4..29ae15e4722 100644 --- a/apps/files_sharing/appinfo/info.xml +++ b/apps/files_sharing/appinfo/info.xml @@ -10,7 +10,7 @@ Turning the feature off removes shared files and folders on the server for all s <licence>AGPL</licence> <author>Michael Gapczynski, Bjoern Schiessle</author> <default_enable/> - <version>0.9.0</version> + <version>0.9.1</version> <types> <filesystem/> </types> diff --git a/apps/files_sharing/appinfo/update.php b/apps/files_sharing/appinfo/update.php index d754a95705c..ced227a107b 100644 --- a/apps/files_sharing/appinfo/update.php +++ b/apps/files_sharing/appinfo/update.php @@ -25,7 +25,7 @@ use OCA\Files_Sharing\Migration; $installedVersion = \OC::$server->getConfig()->getAppValue('files_sharing', 'installed_version'); // Migration OC8.2 -> OC9 -if (version_compare($installedVersion, '0.9.0', '<')) { +if (version_compare($installedVersion, '0.9.1', '<')) { $m = new Migration(\OC::$server->getDatabaseConnection()); $m->removeReShares(); $m->updateInitiatorInfo(); diff --git a/apps/files_sharing/lib/migration.php b/apps/files_sharing/lib/migration.php index 90e0dead480..e7346385510 100644 --- a/apps/files_sharing/lib/migration.php +++ b/apps/files_sharing/lib/migration.php @@ -142,7 +142,8 @@ class Migration { [ \OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, - \OCP\Share::SHARE_TYPE_LINK + \OCP\Share::SHARE_TYPE_LINK, + \OCP\Share::SHARE_TYPE_REMOTE, ], Connection::PARAM_INT_ARRAY ) @@ -185,7 +186,8 @@ class Migration { [ \OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, - \OCP\Share::SHARE_TYPE_LINK + \OCP\Share::SHARE_TYPE_LINK, + \OCP\Share::SHARE_TYPE_REMOTE, ], Connection::PARAM_INT_ARRAY ) diff --git a/apps/files_sharing/tests/api/share20ocstest.php b/apps/files_sharing/tests/api/share20ocstest.php index a93cd5f58ce..a1094ce4b22 100644 --- a/apps/files_sharing/tests/api/share20ocstest.php +++ b/apps/files_sharing/tests/api/share20ocstest.php @@ -97,10 +97,17 @@ class Share20OCSTest extends \Test\TestCase { public function testDeleteShareShareNotFound() { $this->shareManager - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('getShareById') - ->with('ocinternal:42') - ->will($this->throwException(new \OCP\Share\Exceptions\ShareNotFound())); + ->will($this->returnCallback(function($id) { + if ($id === 'ocinternal:42' || $id === 'ocFederatedSharing:42') { + throw new \OCP\Share\Exceptions\ShareNotFound(); + } else { + throw new \Exception(); + } + })); + + $this->shareManager->method('outgoingServer2ServerSharesAllowed')->willReturn(true); $expected = new \OC_OCS_Result(null, 404, 'wrong share ID, share doesn\'t exist.'); $this->assertEquals($expected, $this->ocs->deleteShare(42)); @@ -387,7 +394,7 @@ class Share20OCSTest extends \Test\TestCase { ['group', $group], ])); - $expected = new \OC_OCS_Result($result); + $expected = new \OC_OCS_Result([$result]); $this->assertEquals($expected->getData(), $ocs->getShare($share->getId())->getData()); } diff --git a/apps/files_sharing/tests/migrationtest.php b/apps/files_sharing/tests/migrationtest.php index e1c047e0342..8a40b76a642 100644 --- a/apps/files_sharing/tests/migrationtest.php +++ b/apps/files_sharing/tests/migrationtest.php @@ -209,6 +209,23 @@ class MigrationTest extends TestCase { $this->assertSame(1, $query->execute() ); + $parent = $query->getLastInsertId(); + // third re-share, should be attached to the first user share after migration + $query->setParameter('share_type', \OCP\Share::SHARE_TYPE_REMOTE) + ->setParameter('share_with', 'user@server.com') + ->setParameter('uid_owner', 'user3') + ->setParameter('uid_initiator', '') + ->setParameter('parent', $parent) + ->setParameter('item_type', 'file') + ->setParameter('item_source', '2') + ->setParameter('item_target', '/2') + ->setParameter('file_source', 2) + ->setParameter('file_target', '/foobar') + ->setParameter('permissions', 31) + ->setParameter('stime', time()); + $this->assertSame(1, + $query->execute() + ); } public function testRemoveReShares() { @@ -221,7 +238,7 @@ class MigrationTest extends TestCase { $query = $this->connection->getQueryBuilder(); $query->select('*')->from($this->table)->orderBy('id'); $result = $query->execute()->fetchAll(); - $this->assertSame(8, count($result)); + $this->assertSame(9, count($result)); // shares which shouldn't be modified for ($i = 0; $i < 4; $i++) { @@ -238,7 +255,7 @@ class MigrationTest extends TestCase { $this->assertEmpty($result[5]['uid_initiator']); $this->assertNull($result[5]['parent']); // flatted re-shares - for($i = 6; $i < 8; $i++) { + for($i = 6; $i < 9; $i++) { $this->assertSame('owner2', $result[$i]['uid_owner']); $user = 'user' . ($i - 5); $this->assertSame($user, $result[$i]['uid_initiator']); diff --git a/apps/files_versions/lib/storage.php b/apps/files_versions/lib/storage.php index 35b3110928b..88a4126dabd 100644 --- a/apps/files_versions/lib/storage.php +++ b/apps/files_versions/lib/storage.php @@ -52,6 +52,10 @@ class Storage { const DEFAULTMAXSIZE=50; // unit: percentage; 50% of available disk space/quota const VERSIONS_ROOT = 'files_versions/'; + const DELETE_TRIGGER_MASTER_REMOVED = 0; + const DELETE_TRIGGER_RETENTION_CONSTRAINT = 1; + const DELETE_TRIGGER_QUOTA_EXCEEDED = 2; + // files for which we can remove the versions after the delete operation was successful private static $deletedFiles = array(); @@ -210,9 +214,9 @@ class Storage { $versions = self::getVersions($uid, $filename); if (!empty($versions)) { foreach ($versions as $v) { - \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $path . $v['version'])); + \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $path . $v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED)); self::deleteVersion($view, $filename . '.v' . $v['version']); - \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $path . $v['version'])); + \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $path . $v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED)); } } } @@ -309,6 +313,7 @@ class Storage { Storage::scheduleExpire($uid, $file); \OC_Hook::emit('\OCP\Versions', 'rollback', array( 'path' => $filename, + 'revision' => $revision, )); return true; } else if ($versionCreated) { @@ -444,9 +449,9 @@ class Storage { $view = new \OC\Files\View('/' . $uid . '/files_versions'); if (!empty($toDelete)) { foreach ($toDelete as $version) { - \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $version['path'].'.v'.$version['version'])); + \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT)); self::deleteVersion($view, $version['path'] . '.v' . $version['version']); - \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $version['path'].'.v'.$version['version'])); + \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT)); } } } @@ -705,9 +710,9 @@ class Storage { } foreach($toDelete as $key => $path) { - \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $path)); + \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED)); self::deleteVersion($versionsFileview, $path); - \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $path)); + \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED)); unset($allVersions[$key]); // update array with the versions we keep \OCP\Util::writeLog('files_versions', "Expire: " . $path, \OCP\Util::DEBUG); } @@ -722,9 +727,9 @@ class Storage { reset($allVersions); while ($availableSpace < 0 && $i < $numOfVersions) { $version = current($allVersions); - \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $version['path'].'.v'.$version['version'])); + \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED)); self::deleteVersion($versionsFileview, $version['path'] . '.v' . $version['version']); - \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $version['path'].'.v'.$version['version'])); + \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED)); \OCP\Util::writeLog('files_versions', 'running out of space! Delete oldest version: ' . $version['path'].'.v'.$version['version'] , \OCP\Util::DEBUG); $versionsSize -= $version['size']; $availableSpace += $version['size']; diff --git a/apps/files_versions/tests/versions.php b/apps/files_versions/tests/versions.php index b85877cebd3..d7d9c7a4fb4 100644 --- a/apps/files_versions/tests/versions.php +++ b/apps/files_versions/tests/versions.php @@ -652,7 +652,9 @@ class Test_Files_Versioning extends \Test\TestCase { 'path' => '/sub/test.txt', ); - $this->assertEquals($expectedParams, $params); + $this->assertEquals($expectedParams['path'], $params['path']); + $this->assertTrue(array_key_exists('revision', $params)); + $this->assertTrue($params['revision'] > 0); $this->assertEquals('version2', $this->rootView->file_get_contents($filePath)); $info2 = $this->rootView->getFileInfo($filePath); diff --git a/apps/user_ldap/js/wizard/wizardTabAdvanced.js b/apps/user_ldap/js/wizard/wizardTabAdvanced.js index 9e898ba2fc8..d1e5002d40a 100644 --- a/apps/user_ldap/js/wizard/wizardTabAdvanced.js +++ b/apps/user_ldap/js/wizard/wizardTabAdvanced.js @@ -55,6 +55,10 @@ OCA = OCA || {}; $element: $('#ldap_display_name'), setMethod: 'setUserDisplayName' }, + ldap_user_display_name_2: { + $element: $('#ldap_user_display_name_2'), + setMethod: 'setUserDisplayName2' + }, ldap_base_users: { $element: $('#ldap_base_users'), setMethod: 'setBaseDNUsers' @@ -195,6 +199,15 @@ OCA = OCA || {}; }, /** + * sets the additional user display name attribute + * + * @param {string} attribute + */ + setUserDisplayName2: function(attribute) { + this.setElementValue(this.managedItems.ldap_user_display_name_2.$element, attribute); + }, + + /** * sets the Base DN for users * * @param {string} base diff --git a/apps/user_ldap/l10n/cs_CZ.js b/apps/user_ldap/l10n/cs_CZ.js index e78755719b7..687f1848d2c 100644 --- a/apps/user_ldap/l10n/cs_CZ.js +++ b/apps/user_ldap/l10n/cs_CZ.js @@ -122,6 +122,7 @@ OC.L10N.register( "Directory Settings" : "Nastavení adresáře", "User Display Name Field" : "Pole zobrazovaného jména uživatele", "The LDAP attribute to use to generate the user's display name." : "LDAP atribut použitý k vytvoření zobrazovaného jména uživatele.", + "2nd User Display Name Field" : "Druhé pole zobrazovaného jména uživatele", "Base User Tree" : "Základní uživatelský strom", "One User Base DN per line" : "Jedna uživatelská základní DN na řádku", "User Search Attributes" : "Atributy vyhledávání uživatelů", diff --git a/apps/user_ldap/l10n/cs_CZ.json b/apps/user_ldap/l10n/cs_CZ.json index 37e56cda1a7..6ec1f1abfed 100644 --- a/apps/user_ldap/l10n/cs_CZ.json +++ b/apps/user_ldap/l10n/cs_CZ.json @@ -120,6 +120,7 @@ "Directory Settings" : "Nastavení adresáře", "User Display Name Field" : "Pole zobrazovaného jména uživatele", "The LDAP attribute to use to generate the user's display name." : "LDAP atribut použitý k vytvoření zobrazovaného jména uživatele.", + "2nd User Display Name Field" : "Druhé pole zobrazovaného jména uživatele", "Base User Tree" : "Základní uživatelský strom", "One User Base DN per line" : "Jedna uživatelská základní DN na řádku", "User Search Attributes" : "Atributy vyhledávání uživatelů", diff --git a/apps/user_ldap/l10n/fr.js b/apps/user_ldap/l10n/fr.js index a013589cff2..e7136bb70a5 100644 --- a/apps/user_ldap/l10n/fr.js +++ b/apps/user_ldap/l10n/fr.js @@ -132,6 +132,8 @@ OC.L10N.register( "One Group Base DN per line" : "Un DN de base groupe par ligne", "Group Search Attributes" : "Attributs de recherche des groupes", "Group-Member association" : "Association groupe-membre", + "Dynamic Group Member URL" : "URL de Membre de Groupe Dynamique", + "The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)" : "L'attribut LDAP mis sur les objets de groupe contient une URL de recherche LDAP qui détermine quels objets appartiennent à ce groupe. (Un attribut vide désactive la fonctionnalité de groupe dynamique)", "Nested Groups" : "Groupes imbriqués", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "Si activé, les groupes contenant d'autres groupes sont pris en charge (fonctionne uniquement si l'attribut membre du groupe contient des DNs).", "Paging chunksize" : "Paging chunksize", diff --git a/apps/user_ldap/l10n/fr.json b/apps/user_ldap/l10n/fr.json index 0b6c076f0d3..50bac76a781 100644 --- a/apps/user_ldap/l10n/fr.json +++ b/apps/user_ldap/l10n/fr.json @@ -130,6 +130,8 @@ "One Group Base DN per line" : "Un DN de base groupe par ligne", "Group Search Attributes" : "Attributs de recherche des groupes", "Group-Member association" : "Association groupe-membre", + "Dynamic Group Member URL" : "URL de Membre de Groupe Dynamique", + "The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)" : "L'attribut LDAP mis sur les objets de groupe contient une URL de recherche LDAP qui détermine quels objets appartiennent à ce groupe. (Un attribut vide désactive la fonctionnalité de groupe dynamique)", "Nested Groups" : "Groupes imbriqués", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "Si activé, les groupes contenant d'autres groupes sont pris en charge (fonctionne uniquement si l'attribut membre du groupe contient des DNs).", "Paging chunksize" : "Paging chunksize", diff --git a/apps/user_ldap/l10n/he.js b/apps/user_ldap/l10n/he.js index 56be7447e72..0e4b78f0373 100644 --- a/apps/user_ldap/l10n/he.js +++ b/apps/user_ldap/l10n/he.js @@ -113,7 +113,16 @@ OC.L10N.register( "Turn off SSL certificate validation." : "כיבוי אימות אישורי אבטחה SSL.", "in seconds. A change empties the cache." : "בשניות. שינוי מרוקן את המטמון.", "Directory Settings" : "הגדרות תיקייה", + "User Display Name Field" : "שדה שם תצוגה למשתמש", "Base User Tree" : "עץ משתמש בסיסי", - "in bytes" : "בבתים" + "Base Group Tree" : "עץ קבוצה בסיסי", + "Group Search Attributes" : "מאפייני חיפוש קבוצה", + "Special Attributes" : "מאפיינים מיוחדים", + "Quota Field" : "שדה מכסה", + "Quota Default" : "ברירת מחדל מכסה", + "in bytes" : "בבתים", + "Email Field" : "שדה דואר אלקטרוני", + "Internal Username" : "שם משתמש פנימי", + "Internal Username Attribute:" : "מאפיין שם משתמש פנימי:" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/user_ldap/l10n/he.json b/apps/user_ldap/l10n/he.json index ca2ba2f91a6..21b0cbb70a4 100644 --- a/apps/user_ldap/l10n/he.json +++ b/apps/user_ldap/l10n/he.json @@ -111,7 +111,16 @@ "Turn off SSL certificate validation." : "כיבוי אימות אישורי אבטחה SSL.", "in seconds. A change empties the cache." : "בשניות. שינוי מרוקן את המטמון.", "Directory Settings" : "הגדרות תיקייה", + "User Display Name Field" : "שדה שם תצוגה למשתמש", "Base User Tree" : "עץ משתמש בסיסי", - "in bytes" : "בבתים" + "Base Group Tree" : "עץ קבוצה בסיסי", + "Group Search Attributes" : "מאפייני חיפוש קבוצה", + "Special Attributes" : "מאפיינים מיוחדים", + "Quota Field" : "שדה מכסה", + "Quota Default" : "ברירת מחדל מכסה", + "in bytes" : "בבתים", + "Email Field" : "שדה דואר אלקטרוני", + "Internal Username" : "שם משתמש פנימי", + "Internal Username Attribute:" : "מאפיין שם משתמש פנימי:" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/user_ldap/l10n/it.js b/apps/user_ldap/l10n/it.js index 8bb42e1a72a..7bc6de0d622 100644 --- a/apps/user_ldap/l10n/it.js +++ b/apps/user_ldap/l10n/it.js @@ -122,6 +122,8 @@ OC.L10N.register( "Directory Settings" : "Impostazioni delle cartelle", "User Display Name Field" : "Campo per la visualizzazione del nome utente", "The LDAP attribute to use to generate the user's display name." : "L'attributo LDAP da usare per generare il nome visualizzato dell'utente.", + "2nd User Display Name Field" : "Campo per il secondo nome visualizzato dell'utente", + "Optional. An LDAP attribute to be added to the display name in brackets. Results in e.g. »John Doe (john.doe@example.org)«." : "Facoltativo. Un attributo LDAP da aggiungere al nome visualizzato tra parentesi. Ad es. »Mario Rossi (mario.rossi@esempio.org)«.", "Base User Tree" : "Struttura base dell'utente", "One User Base DN per line" : "Un DN base utente per riga", "User Search Attributes" : "Attributi di ricerca utente", @@ -132,6 +134,8 @@ OC.L10N.register( "One Group Base DN per line" : "Un DN base gruppo per riga", "Group Search Attributes" : "Attributi di ricerca gruppo", "Group-Member association" : "Associazione gruppo-utente ", + "Dynamic Group Member URL" : "URL membro di gruppo dinamico", + "The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)" : "L'attributo LDAP che sugli oggetti di gruppo contiene un URL di ricerca LDAP che determina quali oggetti appartengono al gruppo. (Un valore vuoto disabilità la funzionalità di appartenenza ai gruppi dinamica)", "Nested Groups" : "Gruppi nidificati", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "Quando è attivato, i gruppi che contengono altri gruppi sono supportati. (Funziona solo se l'attributo del gruppo membro contiene DN.)", "Paging chunksize" : "Dimensione del blocco di paginazione", diff --git a/apps/user_ldap/l10n/it.json b/apps/user_ldap/l10n/it.json index 73234261016..412508e64a7 100644 --- a/apps/user_ldap/l10n/it.json +++ b/apps/user_ldap/l10n/it.json @@ -120,6 +120,8 @@ "Directory Settings" : "Impostazioni delle cartelle", "User Display Name Field" : "Campo per la visualizzazione del nome utente", "The LDAP attribute to use to generate the user's display name." : "L'attributo LDAP da usare per generare il nome visualizzato dell'utente.", + "2nd User Display Name Field" : "Campo per il secondo nome visualizzato dell'utente", + "Optional. An LDAP attribute to be added to the display name in brackets. Results in e.g. »John Doe (john.doe@example.org)«." : "Facoltativo. Un attributo LDAP da aggiungere al nome visualizzato tra parentesi. Ad es. »Mario Rossi (mario.rossi@esempio.org)«.", "Base User Tree" : "Struttura base dell'utente", "One User Base DN per line" : "Un DN base utente per riga", "User Search Attributes" : "Attributi di ricerca utente", @@ -130,6 +132,8 @@ "One Group Base DN per line" : "Un DN base gruppo per riga", "Group Search Attributes" : "Attributi di ricerca gruppo", "Group-Member association" : "Associazione gruppo-utente ", + "Dynamic Group Member URL" : "URL membro di gruppo dinamico", + "The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)" : "L'attributo LDAP che sugli oggetti di gruppo contiene un URL di ricerca LDAP che determina quali oggetti appartengono al gruppo. (Un valore vuoto disabilità la funzionalità di appartenenza ai gruppi dinamica)", "Nested Groups" : "Gruppi nidificati", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "Quando è attivato, i gruppi che contengono altri gruppi sono supportati. (Funziona solo se l'attributo del gruppo membro contiene DN.)", "Paging chunksize" : "Dimensione del blocco di paginazione", diff --git a/apps/user_ldap/l10n/ja.js b/apps/user_ldap/l10n/ja.js index e138c46e08e..bafddc9b25d 100644 --- a/apps/user_ldap/l10n/ja.js +++ b/apps/user_ldap/l10n/ja.js @@ -122,6 +122,7 @@ OC.L10N.register( "Directory Settings" : "ディレクトリ設定", "User Display Name Field" : "ユーザー表示名のフィールド", "The LDAP attribute to use to generate the user's display name." : "ユーザーの表示名の生成に利用するLDAP属性", + "2nd User Display Name Field" : "第2ユーザー表示名のフィールド", "Base User Tree" : "ベースユーザーツリー", "One User Base DN per line" : "1行に1つのユーザーベースDN", "User Search Attributes" : "ユーザー検索属性", @@ -132,6 +133,7 @@ OC.L10N.register( "One Group Base DN per line" : "1行に1つのグループベースDN", "Group Search Attributes" : "グループ検索属性", "Group-Member association" : "グループとメンバーの関連付け", + "Dynamic Group Member URL" : "ダイナミックグループメンバーURL", "Nested Groups" : "ネストされたグループ", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "オンにすると、グループを含むグループが有効になります。(グループメンバーの属性にDNが含まれる場合のみ利用できます。)", "Paging chunksize" : "ページ分割サイズ", diff --git a/apps/user_ldap/l10n/ja.json b/apps/user_ldap/l10n/ja.json index ef69c48adcc..f03609104e9 100644 --- a/apps/user_ldap/l10n/ja.json +++ b/apps/user_ldap/l10n/ja.json @@ -120,6 +120,7 @@ "Directory Settings" : "ディレクトリ設定", "User Display Name Field" : "ユーザー表示名のフィールド", "The LDAP attribute to use to generate the user's display name." : "ユーザーの表示名の生成に利用するLDAP属性", + "2nd User Display Name Field" : "第2ユーザー表示名のフィールド", "Base User Tree" : "ベースユーザーツリー", "One User Base DN per line" : "1行に1つのユーザーベースDN", "User Search Attributes" : "ユーザー検索属性", @@ -130,6 +131,7 @@ "One Group Base DN per line" : "1行に1つのグループベースDN", "Group Search Attributes" : "グループ検索属性", "Group-Member association" : "グループとメンバーの関連付け", + "Dynamic Group Member URL" : "ダイナミックグループメンバーURL", "Nested Groups" : "ネストされたグループ", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "オンにすると、グループを含むグループが有効になります。(グループメンバーの属性にDNが含まれる場合のみ利用できます。)", "Paging chunksize" : "ページ分割サイズ", diff --git a/apps/user_ldap/l10n/nl.js b/apps/user_ldap/l10n/nl.js index 27f6dd9ffc7..418baacea33 100644 --- a/apps/user_ldap/l10n/nl.js +++ b/apps/user_ldap/l10n/nl.js @@ -122,6 +122,7 @@ OC.L10N.register( "Directory Settings" : "Mapinstellingen", "User Display Name Field" : "Veld gebruikers weergavenaam", "The LDAP attribute to use to generate the user's display name." : "Het te gebruiken LDAP attribuut voor het genereren van de weergavenaam voor de gebruiker.", + "2nd User Display Name Field" : "2e gebruiker weergavenaam veld", "Base User Tree" : "Basis Gebruikers Structuur", "One User Base DN per line" : "Een User Base DN per regel", "User Search Attributes" : "Attributen voor gebruikerszoekopdrachten", @@ -132,6 +133,7 @@ OC.L10N.register( "One Group Base DN per line" : "Een Group Base DN per regel", "Group Search Attributes" : "Attributen voor groepszoekopdrachten", "Group-Member association" : "Groepslid associatie", + "Dynamic Group Member URL" : "Dynamisch Groepslid URL", "Nested Groups" : "Geneste groepen", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "Wanneer ingeschakeld worden groepen binnen groepen ondersteund. (Werkt alleen als het groepslid attribuut DNs bevat)", "Paging chunksize" : "Paging chunkgrootte", diff --git a/apps/user_ldap/l10n/nl.json b/apps/user_ldap/l10n/nl.json index c98d7aa64dc..3dd4745dd8b 100644 --- a/apps/user_ldap/l10n/nl.json +++ b/apps/user_ldap/l10n/nl.json @@ -120,6 +120,7 @@ "Directory Settings" : "Mapinstellingen", "User Display Name Field" : "Veld gebruikers weergavenaam", "The LDAP attribute to use to generate the user's display name." : "Het te gebruiken LDAP attribuut voor het genereren van de weergavenaam voor de gebruiker.", + "2nd User Display Name Field" : "2e gebruiker weergavenaam veld", "Base User Tree" : "Basis Gebruikers Structuur", "One User Base DN per line" : "Een User Base DN per regel", "User Search Attributes" : "Attributen voor gebruikerszoekopdrachten", @@ -130,6 +131,7 @@ "One Group Base DN per line" : "Een Group Base DN per regel", "Group Search Attributes" : "Attributen voor groepszoekopdrachten", "Group-Member association" : "Groepslid associatie", + "Dynamic Group Member URL" : "Dynamisch Groepslid URL", "Nested Groups" : "Geneste groepen", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "Wanneer ingeschakeld worden groepen binnen groepen ondersteund. (Werkt alleen als het groepslid attribuut DNs bevat)", "Paging chunksize" : "Paging chunkgrootte", diff --git a/apps/user_ldap/l10n/pt_BR.js b/apps/user_ldap/l10n/pt_BR.js index e027c930ceb..8e52b17dce6 100644 --- a/apps/user_ldap/l10n/pt_BR.js +++ b/apps/user_ldap/l10n/pt_BR.js @@ -132,6 +132,8 @@ OC.L10N.register( "One Group Base DN per line" : "Um grupo-base DN por linha", "Group Search Attributes" : "Atributos de Busca de Grupo", "Group-Member association" : "Associação Grupo-Membro", + "Dynamic Group Member URL" : "Membro do Grupo Dinâmico URL", + "The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)" : "O atributo LDAP que em objetos do grupo contém uma pesquisa URL LDAP que determina quais objetos pertencem ao grupo. (Um cenário vazio desativa a funcionalidade de membros de grupo dinâmico.)", "Nested Groups" : "Grupos Aninhados", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "Quando habilitado, os grupos que contêm os grupos são suportados. (Só funciona se o atributo de membro de grupo contém DNs.)", "Paging chunksize" : "Bloco de paginação", diff --git a/apps/user_ldap/l10n/pt_BR.json b/apps/user_ldap/l10n/pt_BR.json index 8251fa082a6..4ca70230a19 100644 --- a/apps/user_ldap/l10n/pt_BR.json +++ b/apps/user_ldap/l10n/pt_BR.json @@ -130,6 +130,8 @@ "One Group Base DN per line" : "Um grupo-base DN por linha", "Group Search Attributes" : "Atributos de Busca de Grupo", "Group-Member association" : "Associação Grupo-Membro", + "Dynamic Group Member URL" : "Membro do Grupo Dinâmico URL", + "The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)" : "O atributo LDAP que em objetos do grupo contém uma pesquisa URL LDAP que determina quais objetos pertencem ao grupo. (Um cenário vazio desativa a funcionalidade de membros de grupo dinâmico.)", "Nested Groups" : "Grupos Aninhados", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "Quando habilitado, os grupos que contêm os grupos são suportados. (Só funciona se o atributo de membro de grupo contém DNs.)", "Paging chunksize" : "Bloco de paginação", diff --git a/apps/user_ldap/l10n/pt_PT.js b/apps/user_ldap/l10n/pt_PT.js index d6bd1d9d515..788c092eb32 100644 --- a/apps/user_ldap/l10n/pt_PT.js +++ b/apps/user_ldap/l10n/pt_PT.js @@ -122,6 +122,8 @@ OC.L10N.register( "Directory Settings" : "Definições de directorias", "User Display Name Field" : "Mostrador do nome de utilizador.", "The LDAP attribute to use to generate the user's display name." : "Atributo LDAP para gerar o nome de utilizador do ownCloud.", + "2nd User Display Name Field" : "2.º Mostrador do Nome de Utilizador.", + "Optional. An LDAP attribute to be added to the display name in brackets. Results in e.g. »John Doe (john.doe@example.org)«." : "Opcional. Atributo LDAP a ser adicionado ao nome de utilizador entre parênteses. Resultados em e.g. »John Doe (john.doe@example.org)«.", "Base User Tree" : "Base da árvore de utilizadores.", "One User Base DN per line" : "Uma base de utilizador DN por linha", "User Search Attributes" : "Utilizar atributos de pesquisa", @@ -132,6 +134,8 @@ OC.L10N.register( "One Group Base DN per line" : "Uma base de grupo DN por linha", "Group Search Attributes" : "Atributos de pesquisa de grupo", "Group-Member association" : "Associar utilizador ao grupo.", + "Dynamic Group Member URL" : "URL Dinâmica de Membro do Grupo", + "The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)" : "O atributo LDAP que em objetos de grupo contém um URL de pesquisa LDAP que determina que objetos pertencem ao grupo. (Uma definição vazia desativa a funcionalidade de membros de grupo dinâmico.)", "Nested Groups" : "Grupos agrupados", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "Quando habilitado os grupos, os grupos são suportados. (Só funciona se o atributo de membro de grupo contém DNs.)", "Paging chunksize" : "Bloco de paginação", diff --git a/apps/user_ldap/l10n/pt_PT.json b/apps/user_ldap/l10n/pt_PT.json index 8073db3efa6..f75b1d1e5ea 100644 --- a/apps/user_ldap/l10n/pt_PT.json +++ b/apps/user_ldap/l10n/pt_PT.json @@ -120,6 +120,8 @@ "Directory Settings" : "Definições de directorias", "User Display Name Field" : "Mostrador do nome de utilizador.", "The LDAP attribute to use to generate the user's display name." : "Atributo LDAP para gerar o nome de utilizador do ownCloud.", + "2nd User Display Name Field" : "2.º Mostrador do Nome de Utilizador.", + "Optional. An LDAP attribute to be added to the display name in brackets. Results in e.g. »John Doe (john.doe@example.org)«." : "Opcional. Atributo LDAP a ser adicionado ao nome de utilizador entre parênteses. Resultados em e.g. »John Doe (john.doe@example.org)«.", "Base User Tree" : "Base da árvore de utilizadores.", "One User Base DN per line" : "Uma base de utilizador DN por linha", "User Search Attributes" : "Utilizar atributos de pesquisa", @@ -130,6 +132,8 @@ "One Group Base DN per line" : "Uma base de grupo DN por linha", "Group Search Attributes" : "Atributos de pesquisa de grupo", "Group-Member association" : "Associar utilizador ao grupo.", + "Dynamic Group Member URL" : "URL Dinâmica de Membro do Grupo", + "The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)" : "O atributo LDAP que em objetos de grupo contém um URL de pesquisa LDAP que determina que objetos pertencem ao grupo. (Uma definição vazia desativa a funcionalidade de membros de grupo dinâmico.)", "Nested Groups" : "Grupos agrupados", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "Quando habilitado os grupos, os grupos são suportados. (Só funciona se o atributo de membro de grupo contém DNs.)", "Paging chunksize" : "Bloco de paginação", diff --git a/apps/user_ldap/l10n/sq.js b/apps/user_ldap/l10n/sq.js index aaebf07862d..81da1fbf76b 100644 --- a/apps/user_ldap/l10n/sq.js +++ b/apps/user_ldap/l10n/sq.js @@ -122,6 +122,8 @@ OC.L10N.register( "Directory Settings" : "Rregullime Drejtorie", "User Display Name Field" : "Fushë Emri Përdoruesi Në Ekran", "The LDAP attribute to use to generate the user's display name." : "Atribut LDAP që përdoret për të prodhuar emër ekrani për përdoruesin.", + "2nd User Display Name Field" : "Fushë e 2-të Emri Përdoruesi Në Ekran", + "Optional. An LDAP attribute to be added to the display name in brackets. Results in e.g. »John Doe (john.doe@example.org)«." : "Opsionale. Një atribut LDAP për t’u shtuar te emri në ekran, në kllapa. Përfundimi do t’i ngjante »John Doe (john.doe@example.org)«.", "Base User Tree" : "Strukturë Bazë Përdoruesi", "One User Base DN per line" : "Një DN Bazë Përdoruesi për rresht", "User Search Attributes" : "Atribute Kërkimesh Nga Përdoruesi", @@ -132,8 +134,12 @@ OC.L10N.register( "One Group Base DN per line" : "Një DN Bazë Grupi për rresht", "Group Search Attributes" : "Atribute Kërkimi Grupi", "Group-Member association" : "Përshoqërim Grup-Përdorues", + "Dynamic Group Member URL" : "URL Anëtari Grupi Dinamik", + "The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)" : "Atributi LDAP që në objekte grupi përmban një URL kërkimi LDAP që përcakton se cilat objekte i përkasin grupit. (Nëse rregullimi lihet i zbrazët, funksioni i anëtarësisë në grup dinamik.)", "Nested Groups" : "Grupe Brenda Njëri-Tjetrit", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "Kur aktivizohet, grupet që përmbajnë grupe mbulohen. (Funksionon vetëm nëse atributi për anëtar grupi përmban DN-ra.)", + "Paging chunksize" : "Madhësi copash faqosjeje", + "Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" : "Madhësi copash të përdorura për kërkime LDAP të sistemuara në faqe, kërkime që japin përfundime të papërpunuara, të tilla si numër përdoruesish ose grupesh. (Caktimi si 0 i çaktivizon kërkimet e faqosura LDAP për këto raste.)", "Special Attributes" : "Atribute Speciale", "Quota Field" : "Fushë Kuotash", "Quota Default" : "Parazgjedhje Kuotash", diff --git a/apps/user_ldap/l10n/sq.json b/apps/user_ldap/l10n/sq.json index 335a42630b5..f4f0224dd0a 100644 --- a/apps/user_ldap/l10n/sq.json +++ b/apps/user_ldap/l10n/sq.json @@ -120,6 +120,8 @@ "Directory Settings" : "Rregullime Drejtorie", "User Display Name Field" : "Fushë Emri Përdoruesi Në Ekran", "The LDAP attribute to use to generate the user's display name." : "Atribut LDAP që përdoret për të prodhuar emër ekrani për përdoruesin.", + "2nd User Display Name Field" : "Fushë e 2-të Emri Përdoruesi Në Ekran", + "Optional. An LDAP attribute to be added to the display name in brackets. Results in e.g. »John Doe (john.doe@example.org)«." : "Opsionale. Një atribut LDAP për t’u shtuar te emri në ekran, në kllapa. Përfundimi do t’i ngjante »John Doe (john.doe@example.org)«.", "Base User Tree" : "Strukturë Bazë Përdoruesi", "One User Base DN per line" : "Një DN Bazë Përdoruesi për rresht", "User Search Attributes" : "Atribute Kërkimesh Nga Përdoruesi", @@ -130,8 +132,12 @@ "One Group Base DN per line" : "Një DN Bazë Grupi për rresht", "Group Search Attributes" : "Atribute Kërkimi Grupi", "Group-Member association" : "Përshoqërim Grup-Përdorues", + "Dynamic Group Member URL" : "URL Anëtari Grupi Dinamik", + "The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)" : "Atributi LDAP që në objekte grupi përmban një URL kërkimi LDAP që përcakton se cilat objekte i përkasin grupit. (Nëse rregullimi lihet i zbrazët, funksioni i anëtarësisë në grup dinamik.)", "Nested Groups" : "Grupe Brenda Njëri-Tjetrit", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "Kur aktivizohet, grupet që përmbajnë grupe mbulohen. (Funksionon vetëm nëse atributi për anëtar grupi përmban DN-ra.)", + "Paging chunksize" : "Madhësi copash faqosjeje", + "Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" : "Madhësi copash të përdorura për kërkime LDAP të sistemuara në faqe, kërkime që japin përfundime të papërpunuara, të tilla si numër përdoruesish ose grupesh. (Caktimi si 0 i çaktivizon kërkimet e faqosura LDAP për këto raste.)", "Special Attributes" : "Atribute Speciale", "Quota Field" : "Fushë Kuotash", "Quota Default" : "Parazgjedhje Kuotash", diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 82cd342ae2f..16b942084c4 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -521,6 +521,7 @@ class Access extends LDAPUtility implements user\IUserTools { private function ldap2ownCloudNames($ldapObjects, $isUsers) { if($isUsers) { $nameAttribute = $this->connection->ldapUserDisplayName; + $sndAttribute = $this->connection->ldapUserDisplayName2; } else { $nameAttribute = $this->connection->ldapGroupDisplayName; } @@ -542,13 +543,14 @@ class Access extends LDAPUtility implements user\IUserTools { if($isUsers) { //cache the user names so it does not need to be retrieved //again later (e.g. sharing dialogue). - $this->cacheUserExists($ocName); - if(!is_null($nameByLDAP)) { - $this->cacheUserDisplayName($ocName, $nameByLDAP); + if(is_null($nameByLDAP)) { + continue; } + $sndName = isset($ldapObject[$sndAttribute]) + ? $ldapObject[$sndAttribute] : ''; + $this->cacheUserDisplayName($ocName, $nameByLDAP, $sndName); } } - continue; } return $ownCloudNames; } @@ -575,8 +577,11 @@ class Access extends LDAPUtility implements user\IUserTools { * caches the user display name * @param string $ocName the internal ownCloud username * @param string $displayName the display name + * @param string $displayName2 the second display name */ - public function cacheUserDisplayName($ocName, $displayName) { + public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') { + $user = $this->userManager->get($ocName); + $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2); $cacheKeyTrunk = 'getDisplayName'; $this->connection->writeToCache($cacheKeyTrunk.$ocName, $displayName); } @@ -1300,7 +1305,7 @@ class Access extends LDAPUtility implements user\IUserTools { return false; } $result=$testConnection->bind(); - $this->connection->bind(); + $this->ldap->unbind($this->connection->getConnectionResource()); return $result; } diff --git a/apps/user_ldap/lib/configuration.php b/apps/user_ldap/lib/configuration.php index 11090f1301b..f829160b62a 100644 --- a/apps/user_ldap/lib/configuration.php +++ b/apps/user_ldap/lib/configuration.php @@ -48,6 +48,7 @@ class Configuration { 'turnOffCertCheck' => null, 'ldapIgnoreNamingRules' => null, 'ldapUserDisplayName' => null, + 'ldapUserDisplayName2' => null, 'ldapUserFilterObjectclass' => null, 'ldapUserFilterGroups' => null, 'ldapUserFilter' => null, @@ -208,6 +209,7 @@ class Configuration { case 'ldapAgentPassword': $readMethod = 'getPwd'; break; + case 'ldapUserDisplayName2': case 'ldapGroupDisplayName': $readMethod = 'getLcValue'; break; @@ -418,6 +420,7 @@ class Configuration { 'ldap_groupfilter_objectclass' => '', 'ldap_groupfilter_groups' => '', 'ldap_display_name' => 'displayName', + 'ldap_user_display_name_2' => '', 'ldap_group_display_name' => 'cn', 'ldap_tls' => 0, 'ldap_quota_def' => '', @@ -475,6 +478,7 @@ class Configuration { 'ldap_groupfilter_objectclass' => 'ldapGroupFilterObjectclass', 'ldap_groupfilter_groups' => 'ldapGroupFilterGroups', 'ldap_display_name' => 'ldapUserDisplayName', + 'ldap_user_display_name_2' => 'ldapUserDisplayName2', 'ldap_group_display_name' => 'ldapGroupDisplayName', 'ldap_tls' => 'ldapTLS', 'ldap_quota_def' => 'ldapQuotaDefault', diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php index addd7d0b51d..b1ed23f95fe 100644 --- a/apps/user_ldap/lib/connection.php +++ b/apps/user_ldap/lib/connection.php @@ -37,6 +37,7 @@ use OC\ServerNotAvailableException; * * @property string ldapUserFilter * @property string ldapUserDisplayName + * @property string ldapUserDisplayName2 * @property boolean hasPagedResultSupport * @property string[] ldapBaseUsers * @property int|string ldapPagingSize holds an integer diff --git a/apps/user_ldap/lib/user/manager.php b/apps/user_ldap/lib/user/manager.php index cfa333b06d4..d76142c79ac 100644 --- a/apps/user_ldap/lib/user/manager.php +++ b/apps/user_ldap/lib/user/manager.php @@ -147,6 +147,7 @@ class Manager { $this->access->getConnection()->ldapQuotaAttribute, $this->access->getConnection()->ldapEmailAttribute, $this->access->getConnection()->ldapUserDisplayName, + $this->access->getConnection()->ldapUserDisplayName2, ); foreach($possible as $attr) { if(!is_null($attr)) { diff --git a/apps/user_ldap/lib/user/user.php b/apps/user_ldap/lib/user/user.php index 13f88aa6507..3bc790a6c10 100644 --- a/apps/user_ldap/lib/user/user.php +++ b/apps/user_ldap/lib/user/user.php @@ -168,13 +168,22 @@ class User { unset($attr); //displayName + $displayName = $displayName2 = ''; $attr = strtolower($this->connection->ldapUserDisplayName); if(isset($ldapEntry[$attr])) { $displayName = $ldapEntry[$attr][0]; - if(!empty($displayName)) { - $this->storeDisplayName($displayName); - $this->access->cacheUserDisplayName($this->getUsername(), $displayName); - } + } + $attr = strtolower($this->connection->ldapUserDisplayName2); + if(isset($ldapEntry[$attr])) { + $displayName2 = $ldapEntry[$attr][0]; + } + if(!empty($displayName)) { + $this->composeAndStoreDisplayName($displayName); + $this->access->cacheUserDisplayName( + $this->getUsername(), + $displayName, + $displayName2 + ); } unset($attr); @@ -350,6 +359,7 @@ class User { /** * Stores a key-value pair in relation to this user + * * @param string $key * @param string $value */ @@ -358,11 +368,19 @@ class User { } /** - * Stores the display name in the databae + * Composes the display name and stores it in the database. The final + * display name is returned. + * * @param string $displayName + * @param string $displayName2 + * @returns string the effective display name */ - public function storeDisplayName($displayName) { + public function composeAndStoreDisplayName($displayName, $displayName2 = '') { + if(!empty($displayName2)) { + $displayName .= ' (' . $displayName2 . ')'; + } $this->store('displayName', $displayName); + return $displayName; } /** diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index 1c0c9a8acb7..23e6d5591a9 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -84,6 +84,7 @@ style('user_ldap', 'settings'); <h3><?php p($l->t('Directory Settings'));?></h3> <div> <p><label for="ldap_display_name"><?php p($l->t('User Display Name Field'));?></label><input type="text" id="ldap_display_name" name="ldap_display_name" data-default="<?php p($_['ldap_display_name_default']); ?>" title="<?php p($l->t('The LDAP attribute to use to generate the user\'s display name.'));?>" /></p> + <p><label for="ldap_user_display_name_2"><?php p($l->t('2nd User Display Name Field'));?></label><input type="text" id="ldap_user_display_name_2" name="ldap_user_display_name_2" data-default="<?php p($_['ldap_user_display_name_2_default']); ?>" title="<?php p($l->t('Optional. An LDAP attribute to be added to the display name in brackets. Results in e.g. »John Doe (john.doe@example.org)«.'));?>" /></p> <p><label for="ldap_base_users"><?php p($l->t('Base User Tree'));?></label><textarea id="ldap_base_users" name="ldap_base_users" placeholder="<?php p($l->t('One User Base DN per line'));?>" data-default="<?php p($_['ldap_base_users_default']); ?>" title="<?php p($l->t('Base User Tree'));?>"></textarea></p> <p><label for="ldap_attributes_for_user_search"><?php p($l->t('User Search Attributes'));?></label><textarea id="ldap_attributes_for_user_search" name="ldap_attributes_for_user_search" placeholder="<?php p($l->t('Optional; one attribute per line'));?>" data-default="<?php p($_['ldap_attributes_for_user_search_default']); ?>" title="<?php p($l->t('User Search Attributes'));?>"></textarea></p> <p><label for="ldap_group_display_name"><?php p($l->t('Group Display Name Field'));?></label><input type="text" id="ldap_group_display_name" name="ldap_group_display_name" data-default="<?php p($_['ldap_group_display_name_default']); ?>" title="<?php p($l->t('The LDAP attribute to use to generate the groups\'s display name.'));?>" /></p> diff --git a/apps/user_ldap/tests/integration/lib/user/IntegrationTestUserDisplayName.php b/apps/user_ldap/tests/integration/lib/user/IntegrationTestUserDisplayName.php new file mode 100644 index 00000000000..fcf85371d3a --- /dev/null +++ b/apps/user_ldap/tests/integration/lib/user/IntegrationTestUserDisplayName.php @@ -0,0 +1,103 @@ +<?php +/** + * @author Arthur Schiwon <blizzz@owncloud.com> + * @author Morris Jobke <hey@morrisjobke.de> + * + * @copyright Copyright (c) 2016, 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/> + * + */ +use OCA\user_ldap\lib\user\User; +use OCA\User_LDAP\Mapping\UserMapping; +use OCA\user_ldap\tests\integration\AbstractIntegrationTest; + +require_once __DIR__ . '/../../../../../../lib/base.php'; + +class IntegrationTestUserDisplayName extends AbstractIntegrationTest { + /** @var UserMapping */ + protected $mapping; + + /** + * 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); + $userBackend = new OCA\user_ldap\USER_LDAP($this->access, \OC::$server->getConfig()); + \OC_User::useBackend($userBackend); + } + + /** + * adds a map entry for the user, so we know the username + * + * @param $dn + * @param $username + */ + private function prepareUser($dn, $username) { + // assigns our self-picked oc username to the dn + $this->mapping->map($dn, $username, 'fakeUUID-' . $username); + } + + /** + * tests whether a display name consisting of two parts is created correctly + * + * @return bool + */ + protected function case1() { + $username = 'alice1337'; + $dn = 'uid=alice,ou=Users,' . $this->base; + $this->prepareUser($dn, $username); + $displayName = \OC::$server->getUserManager()->get($username)->getDisplayName(); + + return strpos($displayName, '(Alice@example.com)') !== false; + } + + /** + * tests whether a display name consisting of one part is created correctly + * + * @return bool + */ + protected function case2() { + $this->connection->setConfiguration([ + 'ldapUserDisplayName2' => '', + ]); + $username = 'boris23421'; + $dn = 'uid=boris,ou=Users,' . $this->base; + $this->prepareUser($dn, $username); + $displayName = \OC::$server->getUserManager()->get($username)->getDisplayName(); + + return strpos($displayName, '(Boris@example.com)') === false; + } + + /** + * sets up the LDAP configuration to be used for the test + */ + protected function initConnection() { + parent::initConnection(); + $this->connection->setConfiguration([ + 'ldapUserDisplayName' => 'displayName', + 'ldapUserDisplayName2' => 'mail', + ]); + } +} + +require_once(__DIR__ . '/../../setup-scripts/config.php'); +$test = new IntegrationTestUserDisplayName($host, $port, $adn, $apwd, $bdn); +$test->init(); +$test->run(); diff --git a/apps/user_ldap/tests/integration/setup-scripts/createExplicitUsers.php b/apps/user_ldap/tests/integration/setup-scripts/createExplicitUsers.php index e580dece40c..fb5609865c4 100644 --- a/apps/user_ldap/tests/integration/setup-scripts/createExplicitUsers.php +++ b/apps/user_ldap/tests/integration/setup-scripts/createExplicitUsers.php @@ -63,6 +63,7 @@ foreach ($users as $uid) { $entry['sn'] = $sn; $entry['userPassword'] = $uid; $entry['displayName'] = $sn . ', ' . $fn; + $entry['mail'] = $fn . '@example.com'; $ok = ldap_add($cr, $newDN, $entry); if ($ok) { diff --git a/apps/user_ldap/tests/user/user.php b/apps/user_ldap/tests/user/user.php index 6fa7f3b6b27..046edf58968 100644 --- a/apps/user_ldap/tests/user/user.php +++ b/apps/user_ldap/tests/user/user.php @@ -749,7 +749,7 @@ class Test_User_User extends \Test\TestCase { 'markRefreshTime', 'updateQuota', 'updateEmail', - 'storeDisplayName', + 'composeAndStoreDisplayName', 'storeLDAPUserName', 'getHomePath', 'updateAvatar' @@ -894,4 +894,29 @@ class Test_User_User extends \Test\TestCase { $user->getHomePath(); } + + public function displayNameProvider() { + return [ + ['Roland Deschain', '', 'Roland Deschain'], + ['Roland Deschain', null, 'Roland Deschain'], + ['Roland Deschain', 'gunslinger@darktower.com', 'Roland Deschain (gunslinger@darktower.com)'], + ]; + } + + /** + * @dataProvider displayNameProvider + */ + public function testComposeAndStoreDisplayName($part1, $part2, $expected) { + list($access, $config, $filesys, $image, $log, $avaMgr, , $userMgr) = + $this->getTestInstances(); + + $config->expects($this->once()) + ->method('setUserValue'); + + $user = new User( + 'user', 'cn=user', $access, $config, $filesys, $image, $log, $avaMgr, $userMgr); + + $displayName = $user->composeAndStoreDisplayName($part1, $part2); + $this->assertSame($expected, $displayName); + } } diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php index 65a4bbdda77..865b7e61892 100644 --- a/apps/user_ldap/user_ldap.php +++ b/apps/user_ldap/user_ldap.php @@ -176,6 +176,11 @@ class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn $this->access->connection->ldapUserDisplayName . '=*', $this->access->getFilterPartForUserSearch($search) )); + $attrs = array($this->access->connection->ldapUserDisplayName, 'dn'); + $additionalAttribute = $this->access->connection->ldapUserDisplayName2; + if(!empty($additionalAttribute)) { + $attrs[] = $additionalAttribute; + } \OCP\Util::writeLog('user_ldap', 'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter, @@ -350,13 +355,30 @@ class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn return $displayName; } + //Check whether the display name is configured to have a 2nd feature + $additionalAttribute = $this->access->connection->ldapUserDisplayName2; + $displayName2 = ''; + if(!empty($additionalAttribute)) { + $displayName2 = $this->access->readAttribute( + $this->access->username2dn($uid), + $additionalAttribute); + } + $displayName = $this->access->readAttribute( $this->access->username2dn($uid), $this->access->connection->ldapUserDisplayName); if($displayName && (count($displayName) > 0)) { - $this->access->connection->writeToCache($cacheKey, $displayName[0]); - return $displayName[0]; + $displayName = $displayName[0]; + + if(is_array($displayName2) && (count($displayName2) > 0)) { + $displayName2 = $displayName2[0]; + } + + $user = $this->access->userManager->get($uid); + $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2); + $this->access->connection->writeToCache($cacheKey, $displayName); + return $displayName; } return null; |