diff options
418 files changed, 9867 insertions, 3002 deletions
diff --git a/3rdparty b/3rdparty -Subproject c45d817921543d2f0562ac4f3be61404b1d4a35 +Subproject b94f7d38f6e13825fd34c7113827d3c369a689a diff --git a/apps/encryption/js/encryption.js b/apps/encryption/js/encryption.js index a6c1bea89b2..361347b44b7 100644 --- a/apps/encryption/js/encryption.js +++ b/apps/encryption/js/encryption.js @@ -5,15 +5,11 @@ * See the COPYING-README file. */ -if (!OC.Encryption) { - OC.Encryption = {}; -} - /** * @namespace * @memberOf OC */ -OC.Encryption = { +OC.Encryption = _.extend(OC.Encryption || {}, { displayEncryptionWarning: function () { if (!OC.currentUser || !OC.Notification.isHidden()) { return; @@ -28,7 +24,7 @@ OC.Encryption = { } ); } -}; +}); $(document).ready(function() { // wait for other apps/extensions to register their event handlers and file actions // in the "ready" clause diff --git a/apps/encryption/js/settings-personal.js b/apps/encryption/js/settings-personal.js index fa94bea6bc5..75ebab5059c 100644 --- a/apps/encryption/js/settings-personal.js +++ b/apps/encryption/js/settings-personal.js @@ -4,11 +4,7 @@ * See the COPYING-README file. */ -if (!OC.Encryption) { - OC.Encryption = {}; -} - -OC.Encryption = { +OC.Encryption = _.extend(OC.Encryption || {}, { updatePrivateKeyPassword: function () { var oldPrivateKeyPassword = $('input:password[id="oldPrivateKeyPassword"]').val(); var newPrivateKeyPassword = $('input:password[id="newPrivateKeyPassword"]').val(); @@ -26,7 +22,7 @@ OC.Encryption = { OC.msg.finishedError('#ocDefaultEncryptionModule .msg', JSON.parse(jqXHR.responseText).message); }); } -}; +}); $(document).ready(function () { diff --git a/apps/encryption/l10n/th_TH.js b/apps/encryption/l10n/th_TH.js index 0d4b3eaebbb..d6a2d00d123 100644 --- a/apps/encryption/l10n/th_TH.js +++ b/apps/encryption/l10n/th_TH.js @@ -40,7 +40,7 @@ OC.L10N.register( "ownCloud basic encryption module" : "ownCloud โมดูลการเข้ารหัสขั้นพื้นฐาน", "Your private key password no longer matches your log-in password." : "รหัสการเข้ารหัสผ่านส่วนตัวของคุณไม่ตรงกับรหัสผ่านในการเข้าสู่ระบบของคุณ", "Set your old private key password to your current log-in password:" : "ตั้งรหัสการเข้ารหัสผ่านส่วนตัวเก่าของคุณเพื่อเข้าสู่ระบบในปัจจุบันของคุณ:", - " If you don't remember your old password you can ask your administrator to recover your files." : "ถ้าคุณลืมรหัสผ่านเก่าของคุณคุณสามารถขอให้ผู้ดูแลระบบกู้คืนไฟล์ของคุณ", + " If you don't remember your old password you can ask your administrator to recover your files." : "ถ้าคุณลืมรหัสผ่านเก่าของคุณ คุณสามารถขอให้ผู้ดูแลระบบกู้คืนไฟล์ของคุณ", "Old log-in password" : "เข้าสู่ระบบด้วยรหัสผ่านเก่า", "Current log-in password" : "เข้าสู่ระบบด้วยรหัสผ่านปัจจุบัน", "Update Private Key Password" : "อัพเดทรหัสการเข้ารหัสผ่านส่วนตัว", diff --git a/apps/encryption/l10n/th_TH.json b/apps/encryption/l10n/th_TH.json index 3546042dee9..5c904340983 100644 --- a/apps/encryption/l10n/th_TH.json +++ b/apps/encryption/l10n/th_TH.json @@ -38,7 +38,7 @@ "ownCloud basic encryption module" : "ownCloud โมดูลการเข้ารหัสขั้นพื้นฐาน", "Your private key password no longer matches your log-in password." : "รหัสการเข้ารหัสผ่านส่วนตัวของคุณไม่ตรงกับรหัสผ่านในการเข้าสู่ระบบของคุณ", "Set your old private key password to your current log-in password:" : "ตั้งรหัสการเข้ารหัสผ่านส่วนตัวเก่าของคุณเพื่อเข้าสู่ระบบในปัจจุบันของคุณ:", - " If you don't remember your old password you can ask your administrator to recover your files." : "ถ้าคุณลืมรหัสผ่านเก่าของคุณคุณสามารถขอให้ผู้ดูแลระบบกู้คืนไฟล์ของคุณ", + " If you don't remember your old password you can ask your administrator to recover your files." : "ถ้าคุณลืมรหัสผ่านเก่าของคุณ คุณสามารถขอให้ผู้ดูแลระบบกู้คืนไฟล์ของคุณ", "Old log-in password" : "เข้าสู่ระบบด้วยรหัสผ่านเก่า", "Current log-in password" : "เข้าสู่ระบบด้วยรหัสผ่านปัจจุบัน", "Update Private Key Password" : "อัพเดทรหัสการเข้ารหัสผ่านส่วนตัว", diff --git a/apps/encryption/lib/migration.php b/apps/encryption/lib/migration.php index 789f5f77757..5396a7db627 100644 --- a/apps/encryption/lib/migration.php +++ b/apps/encryption/lib/migration.php @@ -37,9 +37,10 @@ class Migration { private $connection; /** @var IConfig */ private $config; - /** @var ILogger */ private $logger; + /** @var string*/ + protected $installedVersion; /** * @param IConfig $config @@ -54,6 +55,7 @@ class Migration { $this->moduleId = \OCA\Encryption\Crypto\Encryption::ID; $this->config = $config; $this->logger = $logger; + $this->installedVersion = $this->config->getAppValue('files_encryption', 'installed_version', '-1'); } public function finalCleanUp() { @@ -66,12 +68,16 @@ class Migration { * update file cache, copy unencrypted_size to the 'size' column */ private function updateFileCache() { - $query = $this->connection->getQueryBuilder(); - $query->update('*PREFIX*filecache') - ->set('size', 'unencrypted_size') - ->where($query->expr()->eq('encrypted', $query->createParameter('encrypted'))) - ->setParameter('encrypted', 1); - $query->execute(); + // make sure that we don't update the file cache multiple times + // only update during the first run + if ($this->installedVersion !== '-1') { + $query = $this->connection->getQueryBuilder(); + $query->update('filecache') + ->set('size', 'unencrypted_size') + ->where($query->expr()->eq('encrypted', $query->createParameter('encrypted'))) + ->setParameter('encrypted', 1); + $query->execute(); + } } /** @@ -144,6 +150,12 @@ class Migration { */ public function updateDB() { + // make sure that we don't update the file cache multiple times + // only update during the first run + if ($this->installedVersion === '-1') { + return; + } + // delete left-over from old encryption which is no longer needed $this->config->deleteAppValue('files_encryption', 'ocsid'); $this->config->deleteAppValue('files_encryption', 'types'); @@ -151,7 +163,7 @@ class Migration { $oldAppValues = $this->connection->getQueryBuilder(); $oldAppValues->select('*') - ->from('*PREFIX*appconfig') + ->from('appconfig') ->where($oldAppValues->expr()->eq('appid', $oldAppValues->createParameter('appid'))) ->setParameter('appid', 'files_encryption'); $appSettings = $oldAppValues->execute(); @@ -166,7 +178,7 @@ class Migration { $oldPreferences = $this->connection->getQueryBuilder(); $oldPreferences->select('*') - ->from('*PREFIX*preferences') + ->from('preferences') ->where($oldPreferences->expr()->eq('appid', $oldPreferences->createParameter('appid'))) ->setParameter('appid', 'files_encryption'); $preferenceSettings = $oldPreferences->execute(); diff --git a/apps/encryption/tests/lib/MigrationTest.php b/apps/encryption/tests/lib/MigrationTest.php index a05418c5f26..bb1f0a310a2 100644 --- a/apps/encryption/tests/lib/MigrationTest.php +++ b/apps/encryption/tests/lib/MigrationTest.php @@ -291,12 +291,12 @@ class MigrationTest extends \Test\TestCase { /** @var \OCP\IDBConnection $connection */ $connection = \OC::$server->getDatabaseConnection(); $query = $connection->getQueryBuilder(); - $query->delete('*PREFIX*appconfig') + $query->delete('appconfig') ->where($query->expr()->eq('appid', $query->createParameter('appid'))) ->setParameter('appid', 'encryption'); $query->execute(); $query = $connection->getQueryBuilder(); - $query->delete('*PREFIX*preferences') + $query->delete('preferences') ->where($query->expr()->eq('appid', $query->createParameter('appid'))) ->setParameter('appid', 'encryption'); $query->execute(); @@ -306,12 +306,13 @@ class MigrationTest extends \Test\TestCase { $this->prepareDB(); $m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger); + $this->invokePrivate($m, 'installedVersion', ['0.7']); $m->updateDB(); - $this->verifyDB('*PREFIX*appconfig', 'files_encryption', 0); - $this->verifyDB('*PREFIX*preferences', 'files_encryption', 0); - $this->verifyDB('*PREFIX*appconfig', 'encryption', 3); - $this->verifyDB('*PREFIX*preferences', 'encryption', 1); + $this->verifyDB('appconfig', 'files_encryption', 0); + $this->verifyDB('preferences', 'files_encryption', 0); + $this->verifyDB('appconfig', 'encryption', 3); + $this->verifyDB('preferences', 'encryption', 1); } @@ -325,19 +326,20 @@ class MigrationTest extends \Test\TestCase { $config->setUserValue(self::TEST_ENCRYPTION_MIGRATION_USER1, 'encryption', 'recoverKeyEnabled', '9'); $m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger); + $this->invokePrivate($m, 'installedVersion', ['0.7']); $m->updateDB(); - $this->verifyDB('*PREFIX*appconfig', 'files_encryption', 0); - $this->verifyDB('*PREFIX*preferences', 'files_encryption', 0); - $this->verifyDB('*PREFIX*appconfig', 'encryption', 3); - $this->verifyDB('*PREFIX*preferences', 'encryption', 1); + $this->verifyDB('appconfig', 'files_encryption', 0); + $this->verifyDB('preferences', 'files_encryption', 0); + $this->verifyDB('appconfig', 'encryption', 3); + $this->verifyDB('preferences', 'encryption', 1); // check if the existing values where overwritten correctly /** @var \OC\DB\Connection $connection */ $connection = \OC::$server->getDatabaseConnection(); $query = $connection->getQueryBuilder(); $query->select('configvalue') - ->from('*PREFIX*appconfig') + ->from('appconfig') ->where($query->expr()->andX( $query->expr()->eq('appid', $query->createParameter('appid')), $query->expr()->eq('configkey', $query->createParameter('configkey')) @@ -351,7 +353,7 @@ class MigrationTest extends \Test\TestCase { $query = $connection->getQueryBuilder(); $query->select('configvalue') - ->from('*PREFIX*preferences') + ->from('preferences') ->where($query->expr()->andX( $query->expr()->eq('appid', $query->createParameter('appid')), $query->expr()->eq('configkey', $query->createParameter('configkey')), @@ -388,6 +390,7 @@ class MigrationTest extends \Test\TestCase { public function testUpdateFileCache() { $this->prepareFileCache(); $m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger); + $this->invokePrivate($m, 'installedVersion', ['0.7']); self::invokePrivate($m, 'updateFileCache'); // check results @@ -396,7 +399,7 @@ class MigrationTest extends \Test\TestCase { $connection = \OC::$server->getDatabaseConnection(); $query = $connection->getQueryBuilder(); $query->select('*') - ->from('*PREFIX*filecache'); + ->from('filecache'); $result = $query->execute(); $entries = $result->fetchAll(); foreach($entries as $entry) { @@ -414,15 +417,15 @@ class MigrationTest extends \Test\TestCase { /** @var \OCP\IDBConnection $connection */ $connection = \OC::$server->getDatabaseConnection(); $query = $connection->getQueryBuilder(); - $query->delete('*PREFIX*filecache'); + $query->delete('filecache'); $query->execute(); $query = $connection->getQueryBuilder(); $result = $query->select('fileid') - ->from('*PREFIX*filecache') + ->from('filecache') ->setMaxResults(1)->execute()->fetchAll(); $this->assertEmpty($result); $query = $connection->getQueryBuilder(); - $query->insert('*PREFIX*filecache') + $query->insert('filecache') ->values( array( 'storage' => $query->createParameter('storage'), @@ -444,7 +447,7 @@ class MigrationTest extends \Test\TestCase { } $query = $connection->getQueryBuilder(); $result = $query->select('fileid') - ->from('*PREFIX*filecache') + ->from('filecache') ->execute()->fetchAll(); $this->assertSame(19, count($result)); } diff --git a/apps/files/ajax/download.php b/apps/files/ajax/download.php index e67635ab853..26bab8837b4 100644 --- a/apps/files/ajax/download.php +++ b/apps/files/ajax/download.php @@ -39,4 +39,15 @@ if (!is_array($files_list)) { $files_list = array($files); } +/** + * this sets a cookie to be able to recognize the start of the download + * the content must not be longer than 32 characters and must only contain + * alphanumeric characters + */ +if(isset($_GET['downloadStartSecret']) + && !isset($_GET['downloadStartSecret'][32]) + && preg_match('!^[a-zA-Z0-9]+$!', $_GET['downloadStartSecret']) === 1) { + setcookie('ocDownloadStarted', $_GET['downloadStartSecret'], time() + 20, '/'); +} + OC_Files::get($dir, $files_list, $_SERVER['REQUEST_METHOD'] == 'HEAD'); diff --git a/apps/files/appinfo/application.php b/apps/files/appinfo/application.php index c8aaf375d96..6ba77e09556 100644 --- a/apps/files/appinfo/application.php +++ b/apps/files/appinfo/application.php @@ -1,6 +1,5 @@ <?php /** - * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Tobias Kaminsky <tobias@kaminsky.me> * @author Vincent Petry <pvince81@owncloud.com> @@ -21,8 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ - -namespace OCA\Files\Appinfo; +namespace OCA\Files\AppInfo; use OCA\Files\Controller\ApiController; use OCP\AppFramework\App; @@ -68,5 +66,10 @@ class Application extends App { $homeFolder ); }); + + /* + * Register capabilities + */ + $container->registerCapability('OCA\Files\Capabilities'); } } diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php index fff3332ef49..36479ae13d0 100644 --- a/apps/files/appinfo/remote.php +++ b/apps/files/appinfo/remote.php @@ -33,51 +33,23 @@ set_time_limit(0); // Turn off output buffering to prevent memory problems \OC_Util::obEnd(); +$serverFactory = new \OC\Connector\Sabre\ServerFactory( + \OC::$server->getConfig(), + \OC::$server->getLogger(), + \OC::$server->getDatabaseConnection(), + \OC::$server->getUserSession(), + \OC::$server->getMountManager(), + \OC::$server->getTagManager() +); + // Backends $authBackend = new \OC\Connector\Sabre\Auth(); +$requestUri = \OC::$server->getRequest()->getRequestUri(); -// Fire up server -$objectTree = new \OC\Connector\Sabre\ObjectTree(); -$server = new \OC\Connector\Sabre\Server($objectTree); -// Set URL explicitly due to reverse-proxy situations -$server->httpRequest->setUrl(\OC::$server->getRequest()->getRequestUri()); -$server->setBaseUri($baseuri); - -// Load plugins -$defaults = new OC_Defaults(); -$server->addPlugin(new \OC\Connector\Sabre\BlockLegacyClientPlugin(\OC::$server->getConfig())); -$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $defaults->getName())); -// FIXME: The following line is a workaround for legacy components relying on being able to send a GET to / -$server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin()); -$server->addPlugin(new \OC\Connector\Sabre\FilesPlugin($objectTree)); -$server->addPlugin(new \OC\Connector\Sabre\MaintenancePlugin(\OC::$server->getConfig())); -$server->addPlugin(new \OC\Connector\Sabre\ExceptionLoggerPlugin('webdav', \OC::$server->getLogger())); - -// wait with registering these until auth is handled and the filesystem is setup -$server->on('beforeMethod', function () use ($server, $objectTree) { - $view = \OC\Files\Filesystem::getView(); - $rootInfo = $view->getFileInfo(''); - - // Create ownCloud Dir - $mountManager = \OC\Files\Filesystem::getMountManager(); - $rootDir = new \OC\Connector\Sabre\Directory($view, $rootInfo); - $objectTree->init($rootDir, $view, $mountManager); - - $server->addPlugin(new \OC\Connector\Sabre\TagsPlugin($objectTree, \OC::$server->getTagManager())); - $server->addPlugin(new \OC\Connector\Sabre\QuotaPlugin($view)); - - // custom properties plugin must be the last one - $server->addPlugin( - new \Sabre\DAV\PropertyStorage\Plugin( - new \OC\Connector\Sabre\CustomPropertiesBackend( - $objectTree, - \OC::$server->getDatabaseConnection(), - \OC::$server->getUserSession()->getUser() - ) - ) - ); - $server->addPlugin(new \OC\Connector\Sabre\CopyEtagHeaderPlugin()); -}, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request +$server = $serverFactory->createServer($baseuri, $requestUri, $authBackend, function() { + // use the view for the logged in user + return \OC\Files\Filesystem::getView(); +}); // And off we go! $server->exec(); diff --git a/apps/files/appinfo/routes.php b/apps/files/appinfo/routes.php index 5aa52f17a29..d1b8954d5ce 100644 --- a/apps/files/appinfo/routes.php +++ b/apps/files/appinfo/routes.php @@ -1,9 +1,9 @@ <?php /** * @author Bart Visscher <bartv@thisnet.nl> - * @author Joas Schilling <nickvergessen@owncloud.com> * @author Lukas Reschke <lukas@owncloud.com> * @author Morris Jobke <hey@morrisjobke.de> + * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Tobias Kaminsky <tobias@kaminsky.me> * @author Tom Needham <tom@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> @@ -24,8 +24,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ - -namespace OCA\Files\Appinfo; +namespace OCA\Files\AppInfo; $application = new Application(); $application->registerRoutes( @@ -82,6 +81,4 @@ $this->create('files_ajax_upload', 'ajax/upload.php') $this->create('download', 'download{file}') ->requirements(array('file' => '.*')) ->actionInclude('files/download.php'); - -// Register with the capabilities API -\OCP\API::register('get', '/cloud/capabilities', array('OCA\Files\Capabilities', 'getCapabilities'), 'files', \OCP\API::USER_AUTH); + diff --git a/apps/files/css/detailsView.css b/apps/files/css/detailsView.css new file mode 100644 index 00000000000..76629cb790f --- /dev/null +++ b/apps/files/css/detailsView.css @@ -0,0 +1,55 @@ +#app-sidebar .detailFileInfoContainer { + min-height: 50px; + padding: 15px; +} + +#app-sidebar .detailFileInfoContainer > div { + clear: both; +} + +#app-sidebar .mainFileInfoView { + margin-right: 20px; /* accomodate for close icon */ +} + +#app-sidebar .thumbnail { + width: 50px; + height: 50px; + float: left; + margin-right: 10px; + background-size: 50px; +} + +#app-sidebar .ellipsis { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +#app-sidebar .fileName { + font-size: 16px; + padding-top: 3px; +} + +#app-sidebar .file-details { + margin-top: 3px; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + opacity: .5; +} +#app-sidebar .action-favorite { + vertical-align: text-bottom; + padding: 10px; + margin: -10px; +} + +#app-sidebar .detailList { + float: left; +} + +#app-sidebar .close { + position: absolute; + top: 0; + right: 0; + padding: 15px; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + opacity: .5; +} diff --git a/apps/files/css/files.css b/apps/files/css/files.css index e4bf791761d..26ba86b28c8 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -103,6 +103,10 @@ min-height: 100%; } +.app-files #app-content { + overflow-x: hidden; +} + /* icons for sidebar */ .nav-icon-files { background-image: url('../img/folder.svg'); @@ -143,6 +147,7 @@ #filestable tbody tr:active { background-color: rgb(240,240,240); } +#filestable tbody tr.highlighted, #filestable tbody tr.selected { background-color: rgb(230,230,230); } @@ -244,8 +249,8 @@ table th.column-last, table td.column-last { box-sizing: border-box; position: relative; /* this can not be just width, both need to be set … table styling */ - min-width: 176px; - max-width: 176px; + min-width: 130px; + max-width: 130px; } /* Multiselect bar */ @@ -321,14 +326,7 @@ table td.filename .nametext, .uploadtext, .modified, .column-last>span:first-chi position: relative; overflow: hidden; text-overflow: ellipsis; - width: 90%; -} -/* ellipsize long modified dates to make room for showing delete button */ -#fileList tr:hover .modified, -#fileList tr:focus .modified, -#fileList tr:hover .column-last>span:first-child, -#fileList tr:focus .column-last>span:first-child { - width: 75%; + width: 110px; } /* TODO fix usability bug (accidental file/folder selection) */ @@ -367,45 +365,27 @@ table td.filename .nametext .innernametext { @media only screen and (min-width: 1366px) { table td.filename .nametext .innernametext { - max-width: 760px; - } - - table tr:hover td.filename .nametext .innernametext, - table tr:focus td.filename .nametext .innernametext { - max-width: 480px; + max-width: 660px; } } - @media only screen and (min-width: 1200px) and (max-width: 1366px) { table td.filename .nametext .innernametext { - max-width: 600px; - } - - table tr:hover td.filename .nametext .innernametext, - table tr:focus td.filename .nametext .innernametext { - max-width: 320px; + max-width: 500px; } } - -@media only screen and (min-width: 1000px) and (max-width: 1200px) { +@media only screen and (min-width: 1100px) and (max-width: 1200px) { table td.filename .nametext .innernametext { max-width: 400px; } - - table tr:hover td.filename .nametext .innernametext, - table tr:focus td.filename .nametext .innernametext { - max-width: 120px; +} +@media only screen and (min-width: 1000px) and (max-width: 1100px) { + table td.filename .nametext .innernametext { + max-width: 310px; } } - @media only screen and (min-width: 768px) and (max-width: 1000px) { table td.filename .nametext .innernametext { - max-width: 320px; - } - - table tr:hover td.filename .nametext .innernametext, - table tr:focus td.filename .nametext .innernametext { - max-width: 40px; + max-width: 240px; } } @@ -512,6 +492,23 @@ table td.filename .uploadtext { font-size: 11px; } +.busy .fileactions, .busy .action { + visibility: hidden; +} + +/* fix position of bubble pointer for Files app */ +.bubble, +#app-navigation .app-navigation-entry-menu { + border-top-right-radius: 3px; +} +.bubble:after, +#app-navigation .app-navigation-entry-menu:after { + right: 6px; +} +.bubble:before, +#app-navigation .app-navigation-entry-menu:before { + right: 6px; +} /* force show the loading icon, not only on hover */ #fileList .icon-loading-small { @@ -522,21 +519,15 @@ table td.filename .uploadtext { } #fileList img.move2trash { display:inline; margin:-8px 0; padding:16px 8px 16px 8px !important; float:right; } -#fileList a.action.delete { - position: absolute; - right: 15px; - padding: 17px 14px; -} #fileList .action.action-share-notification span, #fileList a.name { cursor: default !important; } -a.action>img { - max-height:16px; - max-width:16px; - vertical-align:text-bottom; - margin-bottom: -1px; +a.action > img { + max-height: 16px; + max-width: 16px; + vertical-align: text-bottom; } /* Actions for selected files */ @@ -573,24 +564,29 @@ a.action>img { display:none; } -#fileList a.action[data-action="Rename"] { - padding: 16px 14px 17px !important; -} - .ie8 #fileList a.action img, #fileList tr:hover a.action, #fileList a.action.permanent, #fileList tr:focus a.action, #fileList a.action.permanent, #fileList tr:hover a.action.no-permission:hover, -#fileList tr:focus a.action.no-permission:focus -/*#fileList .name:focus .action*/ { +#fileList tr:focus a.action.no-permission:focus, +/*#fileList .name:focus .action,*/ +/* also enforce the low opacity for disabled links that are hovered/focused */ +.ie8 #fileList a.action.disabled:hover img, +#fileList tr:hover a.action.disabled:hover, +#fileList tr:focus a.action.disabled:focus, +#fileList .name:focus a.action.disabled:focus, +#fileList a.action.disabled img { -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter: alpha(opacity=50); opacity: .5; display:inline; } .ie8 #fileList a.action:hover img, +#fileList tr a.action.disabled.action-download, +#fileList tr:hover a.action.disabled.action-download:hover, +#fileList tr:focus a.action.disabled.action-download:focus, #fileList tr:hover a.action:hover, #fileList tr:focus a.action:focus, #fileList .name:focus a.action:focus { @@ -599,6 +595,18 @@ a.action>img { opacity: 1; display:inline; } +#fileList tr a.action.disabled { + background: none; +} + +#selectedActionsList a.download.disabled, +#fileList tr a.action.action-download.disabled { + color: #000000; +} + +#fileList tr:hover a.action.disabled:hover * { + cursor: default; +} .summary { -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; @@ -667,3 +675,44 @@ table.dragshadow td.size { .mask.transparent{ opacity: 0; } + +.fileActionsMenu { + padding: 4px 12px; +} +.fileActionsMenu li { + padding: 5px 0; +} +#fileList .fileActionsMenu a.action img { + padding: initial; +} +#fileList .fileActionsMenu a.action { + padding: 10px; + margin: -10px; +} + +.fileActionsMenu.hidden { + display: none; +} + +#fileList .fileActionsMenu .action { + display: block; + line-height: 30px; + padding-left: 5px; + color: #000; + padding: 0; +} + +.fileActionsMenu .action img, +.fileActionsMenu .action .no-icon { + display: inline-block; + width: 16px; + margin-right: 5px; +} + +.fileActionsMenu .action { + opacity: 0.5; +} + +.fileActionsMenu li:hover .action { + opacity: 1; +} diff --git a/apps/files/css/mobile.css b/apps/files/css/mobile.css index 4881f7c70e4..dd8244a2913 100644 --- a/apps/files/css/mobile.css +++ b/apps/files/css/mobile.css @@ -5,11 +5,6 @@ min-width: initial !important; } -/* do not show Deleted Files on mobile, not optimized yet and button too long */ -#controls #trash { - display: none; -} - /* hide size and date columns */ table th#headerSize, table td.filesize, @@ -38,7 +33,8 @@ table td.filename .nametext { } /* always show actions on mobile, not only on hover */ -#fileList a.action { +#fileList a.action, +#fileList a.action.action-menu.permanent { -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)" !important; filter: alpha(opacity=20) !important; opacity: .2 !important; @@ -50,17 +46,19 @@ table td.filename .nametext { filter: alpha(opacity=70) !important; opacity: .7 !important; } -/* do not show Rename or Versions on mobile */ -#fileList .action.action-rename, -#fileList .action.action-versions { - display: none !important; +#fileList a.action.action-menu img { + padding-left: 2px; +} + +#fileList .fileActionsMenu { + margin-right: 5px; } /* some padding for better clickability */ #fileList a.action img { padding: 0 6px 0 12px; } -/* hide text of the actions on mobile */ -#fileList a.action span { +/* hide text of the share action on mobile */ +#fileList a.action-share span { display: none; } diff --git a/apps/files/index.php b/apps/files/index.php index 4f103f975cb..a41ec059b55 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -50,6 +50,12 @@ OCP\Util::addscript('files', 'search'); \OCP\Util::addScript('files', 'tagsplugin'); \OCP\Util::addScript('files', 'favoritesplugin'); +\OCP\Util::addScript('files', 'detailfileinfoview'); +\OCP\Util::addScript('files', 'detailtabview'); +\OCP\Util::addScript('files', 'mainfileinfodetailview'); +\OCP\Util::addScript('files', 'detailsview'); +\OCP\Util::addStyle('files', 'detailsView'); + \OC_Util::addVendorScript('core', 'handlebars/handlebars'); OCP\App::setActiveNavigationEntry('files_index'); @@ -132,6 +138,7 @@ foreach ($navItems as $item) { } OCP\Util::addscript('files', 'fileactions'); +OCP\Util::addscript('files', 'fileactionsmenu'); OCP\Util::addscript('files', 'files'); OCP\Util::addscript('files', 'navigation'); OCP\Util::addscript('files', 'keyboardshortcuts'); diff --git a/apps/files/js/detailfileinfoview.js b/apps/files/js/detailfileinfoview.js new file mode 100644 index 00000000000..9a88b5e2d8a --- /dev/null +++ b/apps/files/js/detailfileinfoview.js @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + /** + * @class OCA.Files.DetailFileInfoView + * @classdesc + * + * Displays a block of details about the file info. + * + */ + var DetailFileInfoView = function() { + this.initialize(); + }; + /** + * @memberof OCA.Files + */ + DetailFileInfoView.prototype = { + /** + * jQuery element + */ + $el: null, + + _template: null, + + /** + * Currently displayed file info + * + * @type OCA.Files.FileInfo + */ + _fileInfo: null, + + /** + * Initialize the details view + */ + initialize: function() { + this.$el = $('<div class="detailFileInfoView"></div>'); + }, + + /** + * returns the jQuery object for HTML output + * + * @returns {jQuery} + */ + get$: function() { + return this.$el; + }, + + /** + * Destroy / uninitialize this instance. + */ + destroy: function() { + if (this.$el) { + this.$el.remove(); + } + }, + + /** + * Renders this details view + * + * @abstract + */ + render: function() { + // to be implemented in subclass + }, + + /** + * Sets the file info to be displayed in the view + * + * @param {OCA.Files.FileInfo} fileInfo file info to set + */ + setFileInfo: function(fileInfo) { + this._fileInfo = fileInfo; + this.render(); + }, + + /** + * Returns the file info. + * + * @return {OCA.Files.FileInfo} file info + */ + getFileInfo: function() { + return this._fileInfo; + } + }; + + OCA.Files.DetailFileInfoView = DetailFileInfoView; +})(); + diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js new file mode 100644 index 00000000000..7b7bd013f9e --- /dev/null +++ b/apps/files/js/detailsview.js @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2015 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + + var TEMPLATE = + '<div>' + + ' <div class="detailFileInfoContainer">' + + ' </div>' + + ' <div>' + + ' <ul class="tabHeaders">' + + ' </ul>' + + ' <div class="tabsContainer">' + + ' </div>' + + ' </div>' + + ' <a class="close icon-close" href="#" alt="{{closeLabel}}"></a>' + + '</div>'; + + var TEMPLATE_TAB_HEADER = + '<li class="tabHeader {{#if selected}}selected{{/if}}" data-tabid="{{tabId}}" data-tabindex="{{tabIndex}}"><a href="#">{{label}}</a></li>'; + + /** + * @class OCA.Files.DetailsView + * @classdesc + * + * The details view show details about a selected file. + * + */ + var DetailsView = function() { + this.initialize(); + }; + + /** + * @memberof OCA.Files + */ + DetailsView.prototype = { + + /** + * jQuery element + */ + $el: null, + + _template: null, + _templateTabHeader: null, + + /** + * Currently displayed file info + * + * @type OCA.Files.FileInfo + */ + _fileInfo: null, + + /** + * List of detail tab views + * + * @type Array<OCA.Files.DetailTabView> + */ + _tabViews: [], + + /** + * List of detail file info views + * + * @type Array<OCA.Files.DetailFileInfoView> + */ + _detailFileInfoViews: [], + + /** + * Id of the currently selected tab + * + * @type string + */ + _currentTabId: null, + + /** + * Initialize the details view + */ + initialize: function() { + this.$el = $('<div id="app-sidebar"></div>'); + this.fileInfo = null; + this._tabViews = []; + this._detailFileInfoViews = []; + + this.$el.on('click', 'a.close', function(event) { + OC.Apps.hideAppSidebar(); + event.preventDefault(); + }); + + this.$el.on('click', '.tabHeaders .tabHeader', _.bind(this._onClickTab, this)); + + // uncomment to add some dummy tabs for testing + //this._addTestTabs(); + }, + + /** + * Destroy / uninitialize this instance. + */ + destroy: function() { + if (this.$el) { + this.$el.remove(); + } + }, + + _onClickTab: function(e) { + var $target = $(e.target); + if (!$target.hasClass('tabHeader')) { + $target = $target.closest('.tabHeader'); + } + var tabIndex = $target.attr('data-tabindex'); + var targetTab; + if (_.isUndefined(tabIndex)) { + return; + } + + this.$el.find('.tabsContainer .tab').addClass('hidden'); + targetTab = this._tabViews[tabIndex]; + targetTab.$el.removeClass('hidden'); + + this.$el.find('.tabHeaders li').removeClass('selected'); + $target.addClass('selected'); + + e.preventDefault(); + }, + + _addTestTabs: function() { + for (var j = 0; j < 2; j++) { + var testView = new OCA.Files.DetailTabView('testtab' + j); + testView.index = j; + testView.getLabel = function() { return 'Test tab ' + this.index; }; + testView.render = function() { + this.$el.empty(); + for (var i = 0; i < 100; i++) { + this.$el.append('<div>Test tab ' + this.index + ' row ' + i + '</div>'); + } + }; + this._tabViews.push(testView); + } + }, + + /** + * Renders this details view + */ + render: function() { + var self = this; + this.$el.empty(); + + if (!this._template) { + this._template = Handlebars.compile(TEMPLATE); + } + + if (!this._templateTabHeader) { + this._templateTabHeader = Handlebars.compile(TEMPLATE_TAB_HEADER); + } + + var $el = $(this._template({ + closeLabel: t('files', 'Close') + })); + var $tabsContainer = $el.find('.tabsContainer'); + var $tabHeadsContainer = $el.find('.tabHeaders'); + var $detailsContainer = $el.find('.detailFileInfoContainer'); + + // render details + _.each(this._detailFileInfoViews, function(detailView) { + $detailsContainer.append(detailView.get$()); + }); + + if (this._tabViews.length > 0) { + if (!this._currentTab) { + this._currentTab = this._tabViews[0].getId(); + } + + // render tabs + _.each(this._tabViews, function(tabView, i) { + // hidden by default + var $el = tabView.get$(); + var isCurrent = (tabView.getId() === self._currentTab); + if (!isCurrent) { + $el.addClass('hidden'); + } + $tabsContainer.append($el); + + $tabHeadsContainer.append(self._templateTabHeader({ + tabId: tabView.getId(), + tabIndex: i, + label: tabView.getLabel(), + selected: isCurrent + })); + }); + } + + // TODO: select current tab + + this.$el.append($el); + }, + + /** + * Sets the file info to be displayed in the view + * + * @param {OCA.Files.FileInfo} fileInfo file info to set + */ + setFileInfo: function(fileInfo) { + this._fileInfo = fileInfo; + + this.render(); + + // notify all panels + _.each(this._tabViews, function(tabView) { + tabView.setFileInfo(fileInfo); + }); + _.each(this._detailFileInfoViews, function(detailView) { + detailView.setFileInfo(fileInfo); + }); + }, + + /** + * Returns the file info. + * + * @return {OCA.Files.FileInfo} file info + */ + getFileInfo: function() { + return this._fileInfo; + }, + + /** + * Adds a tab in the tab view + * + * @param {OCA.Files.DetailTabView} tab view + */ + addTabView: function(tabView) { + this._tabViews.push(tabView); + }, + + /** + * Adds a detail view for file info. + * + * @param {OCA.Files.DetailFileInfoView} detail view + */ + addDetailView: function(detailView) { + this._detailFileInfoViews.push(detailView); + } + }; + + OCA.Files.DetailsView = DetailsView; +})(); + diff --git a/apps/files/js/detailtabview.js b/apps/files/js/detailtabview.js new file mode 100644 index 00000000000..b9b1dda2ccc --- /dev/null +++ b/apps/files/js/detailtabview.js @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2015 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + + /** + * @class OCA.Files.DetailTabView + * @classdesc + * + * Base class for tab views to display file information. + * + */ + var DetailTabView = function(id) { + this.initialize(id); + }; + + /** + * @memberof OCA.Files + */ + DetailTabView.prototype = { + /** + * jQuery element + */ + $el: null, + + /** + * Tab id + */ + _id: null, + + /** + * Tab label + */ + _label: null, + + _template: null, + + /** + * Currently displayed file info + * + * @type OCA.Files.FileInfo + */ + _fileInfo: null, + + /** + * Initialize the details view + * + * @param {string} id tab id + */ + initialize: function(id) { + if (!id) { + throw 'Argument "id" is required'; + } + this._id = id; + this.$el = $('<div class="tab"></div>'); + this.$el.attr('data-tabid', id); + }, + + /** + * Destroy / uninitialize this instance. + */ + destroy: function() { + if (this.$el) { + this.$el.remove(); + } + }, + + /** + * Returns the tab element id + * + * @return {string} tab id + */ + getId: function() { + return this._id; + }, + + /** + * Returns the tab label + * + * @return {String} label + */ + getLabel: function() { + return 'Tab ' + this._id; + }, + + /** + * returns the jQuery object for HTML output + * + * @returns {jQuery} + */ + get$: function() { + return this.$el; + }, + + /** + * Renders this details view + * + * @abstract + */ + render: function() { + // to be implemented in subclass + // FIXME: code is only for testing + this.$el.empty(); + this.$el.append('<div>Hello ' + this._id + '</div>'); + }, + + /** + * Sets the file info to be displayed in the view + * + * @param {OCA.Files.FileInfo} fileInfo file info to set + */ + setFileInfo: function(fileInfo) { + this._fileInfo = fileInfo; + this.render(); + }, + + /** + * Returns the file info. + * + * @return {OCA.Files.FileInfo} file info + */ + getFileInfo: function() { + return this._fileInfo; + } + }; + + OCA.Files.DetailTabView = DetailTabView; +})(); + diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 1956fda0077..43f74c5816d 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -10,6 +10,12 @@ (function() { + var TEMPLATE_FILE_ACTION_TRIGGER = + '<a class="action action-{{nameLowerCase}}" href="#" data-action="{{name}}">' + + '{{#if icon}}<img class="svg" alt="{{altText}}" src="{{icon}}" />{{/if}}' + + '{{#if displayName}}<span> {{displayName}}</span>{{/if}}' + + '</a>'; + /** * Construct a new FileActions instance * @constructs FileActions @@ -18,6 +24,8 @@ var FileActions = function() { this.initialize(); }; + FileActions.TYPE_DROPDOWN = 0; + FileActions.TYPE_INLINE = 1; FileActions.prototype = { /** @lends FileActions.prototype */ actions: {}, @@ -38,6 +46,8 @@ */ _updateListeners: {}, + _fileActionTriggerTemplate: null, + /** * @private */ @@ -46,6 +56,8 @@ // abusing jquery for events until we get a real event lib this.$el = $('<div class="dummy-fileactions hidden"></div>'); $('body').append(this.$el); + + this._showMenuClosure = _.bind(this._showMenu, this); }, /** @@ -111,6 +123,7 @@ displayName: displayName || name }); }, + /** * Register action * @@ -125,15 +138,14 @@ displayName: action.displayName, mime: mime, icon: action.icon, - permissions: action.permissions + permissions: action.permissions, + type: action.type || FileActions.TYPE_DROPDOWN }; if (_.isUndefined(action.displayName)) { actionSpec.displayName = t('files', name); } if (_.isFunction(action.render)) { actionSpec.render = action.render; - } else { - actionSpec.render = _.bind(this._defaultRenderAction, this); } if (!this.actions[mime]) { this.actions[mime] = {}; @@ -162,6 +174,16 @@ this.defaults[mime] = name; this._notifyUpdateListeners('setDefault', {defaultAction: {mime: mime, name: name}}); }, + + /** + * Returns a map of file actions handlers matching the given conditions + * + * @param {string} mime mime type + * @param {string} type "dir" or "file" + * @param {int} permissions permissions + * + * @return {Object.<string,OCA.Files.FileActions~actionHandler>} map of action name to action spec + */ get: function (mime, type, permissions) { var actions = this.getActions(mime, type, permissions); var filteredActions = {}; @@ -170,6 +192,16 @@ }); return filteredActions; }, + + /** + * Returns an array of file actions matching the given conditions + * + * @param {string} mime mime type + * @param {string} type "dir" or "file" + * @param {int} permissions permissions + * + * @return {Array.<OCA.Files.FileAction>} array of action specs + */ getActions: function (mime, type, permissions) { var actions = {}; if (this.actions.all) { @@ -197,7 +229,37 @@ }); return filteredActions; }, + + /** + * Returns the default file action handler for the given conditions + * + * @param {string} mime mime type + * @param {string} type "dir" or "file" + * @param {int} permissions permissions + * + * @return {OCA.Files.FileActions~actionHandler} action handler + * + * @deprecated use getDefaultFileAction instead + */ getDefault: function (mime, type, permissions) { + var defaultActionSpec = this.getDefaultFileAction(mime, type, permissions); + if (defaultActionSpec) { + return defaultActionSpec.action; + } + return undefined; + }, + + /** + * Returns the default file action handler for the given conditions + * + * @param {string} mime mime type + * @param {string} type "dir" or "file" + * @param {int} permissions permissions + * + * @return {OCA.Files.FileActions~actionHandler} action handler + * @since 8.2 + */ + getDefaultFileAction: function(mime, type, permissions) { var mimePart; if (mime) { mimePart = mime.substr(0, mime.indexOf('/')); @@ -212,9 +274,10 @@ } else { name = this.defaults.all; } - var actions = this.get(mime, type, permissions); + var actions = this.getActions(mime, type, permissions); return actions[name]; }, + /** * Default function to render actions * @@ -224,87 +287,82 @@ * @param {OCA.Files.FileActionContext} context action context */ _defaultRenderAction: function(actionSpec, isDefault, context) { - var name = actionSpec.name; - if (name === 'Download' || !isDefault) { - var $actionLink = this._makeActionLink(actionSpec, context); + if (!isDefault) { + var params = { + name: actionSpec.name, + nameLowerCase: actionSpec.name.toLowerCase(), + displayName: actionSpec.displayName, + icon: actionSpec.icon, + altText: actionSpec.altText, + }; + if (_.isFunction(actionSpec.icon)) { + params.icon = actionSpec.icon(context.$file.attr('data-file')); + } + + var $actionLink = this._makeActionLink(params, context); context.$file.find('a.name>span.fileactions').append($actionLink); + $actionLink.addClass('permanent'); return $actionLink; } }, + /** * Renders the action link element * - * @param {OCA.Files.FileAction} actionSpec action object - * @param {OCA.Files.FileActionContext} context action context + * @param {Object} params action params */ - _makeActionLink: function(actionSpec, context) { - var img = actionSpec.icon; - if (img && img.call) { - img = img(context.$file.attr('data-file')); - } - var html = '<a href="#">'; - if (img) { - html += '<img class="svg" alt="" src="' + img + '" />'; + _makeActionLink: function(params) { + if (!this._fileActionTriggerTemplate) { + this._fileActionTriggerTemplate = Handlebars.compile(TEMPLATE_FILE_ACTION_TRIGGER); } - if (actionSpec.displayName) { - html += '<span> ' + actionSpec.displayName + '</span>'; - } - html += '</a>'; - return $(html); + return $(this._fileActionTriggerTemplate(params)); }, + /** - * Custom renderer for the "Rename" action. - * Displays the rename action as an icon behind the file name. + * Displays the file actions dropdown menu * - * @param {OCA.Files.FileAction} actionSpec file action to render - * @param {boolean} isDefault true if the action is a default action, - * false otherwise - * @param {OCAFiles.FileActionContext} context rendering context + * @param {string} fileName file name + * @param {OCA.Files.FileActionContext} context rendering context */ - _renderRenameAction: function(actionSpec, isDefault, context) { - var $actionEl = this._makeActionLink(actionSpec, context); - var $container = context.$file.find('a.name span.nametext'); - $actionEl.find('img').attr('alt', t('files', 'Rename')); - $container.find('.action-rename').remove(); - $container.append($actionEl); - return $actionEl; + _showMenu: function(fileName, context) { + var menu; + var $trigger = context.$file.closest('tr').find('.fileactions .action-menu'); + $trigger.addClass('open'); + + menu = new OCA.Files.FileActionsMenu(); + menu.$el.on('afterHide', function() { + context.$file.removeClass('mouseOver'); + $trigger.removeClass('open'); + menu.remove(); + }); + + context.$file.addClass('mouseOver'); + context.$file.find('td.filename').append(menu.$el); + menu.show(context); }, + /** - * Custom renderer for the "Delete" action. - * Displays the "Delete" action as a trash icon at the end of - * the table row. - * - * @param {OCA.Files.FileAction} actionSpec file action to render - * @param {boolean} isDefault true if the action is a default action, - * false otherwise - * @param {OCAFiles.FileActionContext} context rendering context + * Renders the menu trigger on the given file list row + * + * @param {Object} $tr file list row element + * @param {OCA.Files.FileActionContext} context rendering context */ - _renderDeleteAction: function(actionSpec, isDefault, context) { - var mountType = context.$file.attr('data-mounttype'); - var deleteTitle = t('files', 'Delete'); - if (mountType === 'external-root') { - deleteTitle = t('files', 'Disconnect storage'); - } else if (mountType === 'shared-root') { - deleteTitle = t('files', 'Unshare'); - } - var cssClasses = 'action delete icon-delete'; - if((context.$file.data('permissions') & OC.PERMISSION_DELETE) === 0) { - // add css class no-permission to delete icon - cssClasses += ' no-permission'; - deleteTitle = t('files', 'No permission to delete'); - } - var $actionLink = $('<a href="#" original-title="' + - escapeHTML(deleteTitle) + - '" class="' +cssClasses + '">' + - '<span class="hidden-visually">' + escapeHTML(deleteTitle) + '</span>' + - '</a>' - ); - var $container = context.$file.find('td:last'); - $container.find('.delete').remove(); - $container.append($actionLink); - return $actionLink; + _renderMenuTrigger: function($tr, context) { + // remove previous + $tr.find('.action-menu').remove(); + + var $el = this._renderInlineAction({ + name: 'menu', + displayName: '', + icon: OC.imagePath('core', 'actions/more'), + altText: t('files', 'Actions'), + action: this._showMenuClosure + }, false, context); + + $el.addClass('permanent'); }, + /** * Renders the action element by calling actionSpec.render() and * registers the click event to process the action. @@ -312,25 +370,32 @@ * @param {OCA.Files.FileAction} actionSpec file action to render * @param {boolean} isDefault true if the action is a default action, * false otherwise - * @param {OCAFiles.FileActionContext} context rendering context + * @param {OCA.Files.FileActionContext} context rendering context */ - _renderAction: function(actionSpec, isDefault, context) { - var $actionEl = actionSpec.render(actionSpec, isDefault, context); + _renderInlineAction: function(actionSpec, isDefault, context) { + var renderFunc = actionSpec.render || _.bind(this._defaultRenderAction, this); + var $actionEl = renderFunc(actionSpec, isDefault, context); if (!$actionEl || !$actionEl.length) { return; } - $actionEl.addClass('action action-' + actionSpec.name.toLowerCase()); - $actionEl.attr('data-action', actionSpec.name); $actionEl.on( 'click', { a: null }, function(event) { + event.stopPropagation(); + event.preventDefault(); + + if ($actionEl.hasClass('open')) { + return; + } + var $file = $(event.target).closest('tr'); + if ($file.hasClass('busy')) { + return; + } var currentFile = $file.find('td.filename'); var fileName = $file.attr('data-file'); - event.stopPropagation(); - event.preventDefault(); context.fileActions.currentFile = currentFile; // also set on global object for legacy apps @@ -346,6 +411,7 @@ ); return $actionEl; }, + /** * Display file actions for the given element * @param parent "td" element of the file for which to display actions @@ -376,36 +442,29 @@ nameLinks = parent.children('a.name'); nameLinks.find('.fileactions, .nametext .action').remove(); nameLinks.append('<span class="fileactions" />'); - var defaultAction = this.getDefault( + var defaultAction = this.getDefaultFileAction( this.getCurrentMimeType(), this.getCurrentType(), this.getCurrentPermissions() ); + var context = { + $file: $tr, + fileActions: this, + fileList: fileList + }; + $.each(actions, function (name, actionSpec) { - if (name !== 'Share') { - self._renderAction( + if (actionSpec.type === FileActions.TYPE_INLINE) { + self._renderInlineAction( actionSpec, - actionSpec.action === defaultAction, { - $file: $tr, - fileActions: this, - fileList : fileList - } + defaultAction && actionSpec.name === defaultAction.name, + context ); } }); - // added here to make sure it's always the last action - var shareActionSpec = actions.Share; - if (shareActionSpec){ - this._renderAction( - shareActionSpec, - shareActionSpec.action === defaultAction, { - $file: $tr, - fileActions: this, - fileList: fileList - } - ); - } + + this._renderMenuTrigger($tr, context); if (triggerEvent){ fileList.$fileList.trigger(jQuery.Event("fileActionsReady", {fileList: fileList, $files: $tr})); @@ -429,35 +488,42 @@ */ registerDefaultActions: function() { this.registerAction({ - name: 'Delete', - displayName: '', + name: 'Download', + displayName: t('files', 'Download'), mime: 'all', - // permission is READ because we show a hint instead if there is no permission permissions: OC.PERMISSION_READ, - icon: function() { - return OC.imagePath('core', 'actions/delete'); + icon: function () { + return OC.imagePath('core', 'actions/download'); }, - render: _.bind(this._renderDeleteAction, this), - actionHandler: function(fileName, context) { - // if there is no permission to delete do nothing - if((context.$file.data('permissions') & OC.PERMISSION_DELETE) === 0) { + actionHandler: function (filename, context) { + var dir = context.dir || context.fileList.getCurrentDirectory(); + var url = context.fileList.getDownloadUrl(filename, dir); + + var downloadFileaction = $(context.$file).find('.fileactions .action-download'); + + // don't allow a second click on the download action + if(downloadFileaction.hasClass('disabled')) { return; } - context.fileList.do_delete(fileName, context.dir); - $('.tipsy').remove(); + + if (url) { + var disableLoadingState = function() { + context.fileList.showFileBusyState(filename, false); + }; + + context.fileList.showFileBusyState(downloadFileaction, true); + OCA.Files.Files.handleDownload(url, disableLoadingState); + } } }); - // t('files', 'Rename') this.registerAction({ name: 'Rename', - displayName: '', mime: 'all', permissions: OC.PERMISSION_UPDATE, icon: function() { return OC.imagePath('core', 'actions/rename'); }, - render: _.bind(this._renderRenameAction, this), actionHandler: function (filename, context) { context.fileList.rename(filename); } @@ -471,23 +537,51 @@ context.fileList.changeDirectory(dir + filename); }); - this.setDefault('dir', 'Open'); - - this.register('all', 'Download', OC.PERMISSION_READ, function () { - return OC.imagePath('core', 'actions/download'); - }, function (filename, context) { - var dir = context.dir || context.fileList.getCurrentDirectory(); - var url = context.fileList.getDownloadUrl(filename, dir); - if (url) { - OC.redirect(url); + this.registerAction({ + name: 'Delete', + mime: 'all', + // permission is READ because we show a hint instead if there is no permission + permissions: OC.PERMISSION_READ, + icon: function() { + return OC.imagePath('core', 'actions/delete'); + }, + actionHandler: function(fileName, context) { + // if there is no permission to delete do nothing + if((context.$file.data('permissions') & OC.PERMISSION_DELETE) === 0) { + return; + } + context.fileList.do_delete(fileName, context.dir); + $('.tipsy').remove(); } - }, t('files', 'Download')); + }); + + this.setDefault('dir', 'Open'); } }; OCA.Files.FileActions = FileActions; /** + * Replaces the download icon with a loading spinner and vice versa + * - also adds the class disabled to the passed in element + * + * @param downloadButtonElement download fileaction + * @param {boolean} showIt whether to show the spinner(true) or to hide it(false) + */ + OCA.Files.FileActions.updateFileActionSpinner = function(downloadButtonElement, showIt) { + var icon = downloadButtonElement.find('img'), + sourceImage = icon.attr('src'); + + if(showIt) { + downloadButtonElement.addClass('disabled'); + icon.attr('src', sourceImage.replace('actions/download.svg', 'loading-small.gif')); + } else { + downloadButtonElement.removeClass('disabled'); + icon.attr('src', sourceImage.replace('loading-small.gif', 'actions/download.svg')); + } + }; + + /** * File action attributes. * * @todo make this a real class in the future diff --git a/apps/files/js/fileactionsmenu.js b/apps/files/js/fileactionsmenu.js new file mode 100644 index 00000000000..623ebde5442 --- /dev/null +++ b/apps/files/js/fileactionsmenu.js @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2014 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + + var TEMPLATE_MENU = + '<ul>' + + '{{#each items}}' + + '<li>' + + '<a href="#" class="action action-{{nameLowerCase}} permanent" data-action="{{name}}">{{#if icon}}<img src="{{icon}}"/>{{else}}<span class="no-icon"></span>{{/if}}<span>{{displayName}}</span></a>' + + '</li>' + + '{{/each}}' + + '</ul>'; + + /** + * Construct a new FileActionsMenu instance + * @constructs FileActionsMenu + * @memberof OCA.Files + */ + var FileActionsMenu = OC.Backbone.View.extend({ + tagName: 'div', + className: 'fileActionsMenu bubble hidden open menu', + + /** + * Current context + * + * @type OCA.Files.FileActionContext + */ + _context: null, + + events: { + 'click a.action': '_onClickAction' + }, + + template: function(data) { + if (!OCA.Files.FileActionsMenu._TEMPLATE) { + OCA.Files.FileActionsMenu._TEMPLATE = Handlebars.compile(TEMPLATE_MENU); + } + return OCA.Files.FileActionsMenu._TEMPLATE(data); + }, + + /** + * Event handler whenever an action has been clicked within the menu + * + * @param {Object} event event object + */ + _onClickAction: function(event) { + var $target = $(event.target); + if (!$target.is('a')) { + $target = $target.closest('a'); + } + var fileActions = this._context.fileActions; + var actionName = $target.attr('data-action'); + var actions = fileActions.getActions( + fileActions.getCurrentMimeType(), + fileActions.getCurrentType(), + fileActions.getCurrentPermissions() + ); + var actionSpec = actions[actionName]; + var fileName = this._context.$file.attr('data-file'); + + event.stopPropagation(); + event.preventDefault(); + + OC.hideMenus(); + + actionSpec.action( + fileName, + this._context + ); + }, + + /** + * Renders the menu with the currently set items + */ + render: function() { + var fileActions = this._context.fileActions; + var actions = fileActions.getActions( + fileActions.getCurrentMimeType(), + fileActions.getCurrentType(), + fileActions.getCurrentPermissions() + ); + + var defaultAction = fileActions.getDefaultFileAction( + fileActions.getCurrentMimeType(), + fileActions.getCurrentType(), + fileActions.getCurrentPermissions() + ); + + var items = _.filter(actions, function(actionSpec) { + return ( + actionSpec.type === OCA.Files.FileActions.TYPE_DROPDOWN && + (!defaultAction || actionSpec.name !== defaultAction.name) + ); + }); + items = _.map(items, function(item) { + item.nameLowerCase = item.name.toLowerCase(); + return item; + }); + + this.$el.html(this.template({ + items: items + })); + }, + + /** + * Displays the menu under the given element + * + * @param {OCA.Files.FileActionContext} context context + * @param {Object} $trigger trigger element + */ + show: function(context) { + this._context = context; + + this.render(); + this.$el.removeClass('hidden'); + + OC.showMenu(null, this.$el); + } + }); + + OCA.Files.FileActionsMenu = FileActionsMenu; + +})(); + diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 8236ef3b4ac..e297edcf11b 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -23,6 +23,7 @@ * @param [options.scrollContainer] scrollable container, defaults to $(window) * @param [options.dragOptions] drag options, disabled by default * @param [options.folderDropOptions] folder drop options, disabled by default + * @param [options.detailsViewEnabled=true] whether to enable details view */ var FileList = function($el, options) { this.initialize($el, options); @@ -65,6 +66,11 @@ fileSummary: null, /** + * @type OCA.Files.DetailsView + */ + _detailsView: null, + + /** * Whether the file list was initialized already. * @type boolean */ @@ -205,6 +211,13 @@ } this.breadcrumb = new OCA.Files.BreadCrumb(breadcrumbOptions); + if (_.isUndefined(options.detailsViewEnabled) || options.detailsViewEnabled) { + this._detailsView = new OCA.Files.DetailsView(); + this._detailsView.addDetailView(new OCA.Files.MainFileInfoDetailView()); + this._detailsView.$el.insertBefore(this.$el); + this._detailsView.$el.addClass('disappear'); + } + this.$el.find('#controls').prepend(this.breadcrumb.$el); this.$el.find('thead th .columntitle').click(_.bind(this._onClickHeader, this)); @@ -216,6 +229,13 @@ this.updateSearch(); + this.$el.on('click', function(event) { + var $target = $(event.target); + // click outside file row ? + if (!$target.closest('tbody').length && !$target.closest('#app-sidebar').length) { + self._updateDetailsView(null); + } + }); this.$fileList.on('click','td.filename>a.name', _.bind(this._onClickFile, this)); this.$fileList.on('change', 'td.filename>.selectCheckBox', _.bind(this._onClickFileCheckbox, this)); this.$el.on('urlChanged', _.bind(this._onUrlChanged, this)); @@ -263,6 +283,37 @@ }, /** + * Update the details view to display the given file + * + * @param {OCA.Files.FileInfo} fileInfo file info to display + */ + _updateDetailsView: function(fileInfo) { + if (!this._detailsView) { + return; + } + + var self = this; + var oldFileInfo = this._detailsView.getFileInfo(); + if (oldFileInfo) { + // TODO: use more efficient way, maybe track the highlight + this.$fileList.children().filterAttr('data-id', '' + oldFileInfo.id).removeClass('highlighted'); + } + + if (!fileInfo) { + OC.Apps.hideAppSidebar(); + this._detailsView.setFileInfo(null); + return; + } + + this.$fileList.children().filterAttr('data-id', '' + fileInfo.id).addClass('highlighted'); + this._detailsView.setFileInfo(_.extend({ + path: this.getCurrentDirectory() + }, fileInfo)); + this._detailsView.$el.scrollTop(0); + _.defer(OC.Apps.showAppSidebar); + }, + + /** * Event handler for when the window size changed */ _onResize: function() { @@ -315,6 +366,12 @@ delete this._selectedFiles[$tr.data('id')]; this._selectionSummary.remove(data); } + if (this._selectionSummary.getTotal() === 1) { + this._updateDetailsView(_.values(this._selectedFiles)[0]); + } else { + // show nothing when multiple files are selected + this._updateDetailsView(null); + } this.$el.find('.select-all').prop('checked', this._selectionSummary.getTotal() === this.files.length); }, @@ -350,27 +407,34 @@ this._selectFileEl($tr, !$checkbox.prop('checked')); this.updateSelectionSummary(); } else { - var filename = $tr.attr('data-file'); - var renaming = $tr.data('renaming'); - if (!renaming) { - this.fileActions.currentFile = $tr.find('td'); - var mime = this.fileActions.getCurrentMimeType(); - var type = this.fileActions.getCurrentType(); - var permissions = this.fileActions.getCurrentPermissions(); - var action = this.fileActions.getDefault(mime,type, permissions); - if (action) { - event.preventDefault(); - // also set on global object for legacy apps - window.FileActions.currentFile = this.fileActions.currentFile; - action(filename, { - $file: $tr, - fileList: this, - fileActions: this.fileActions, - dir: $tr.attr('data-path') || this.getCurrentDirectory() - }); + // clicked directly on the name + if (!this._detailsView || $(event.target).is('.nametext') || $(event.target).closest('.nametext').length) { + var filename = $tr.attr('data-file'); + var renaming = $tr.data('renaming'); + if (!renaming) { + this.fileActions.currentFile = $tr.find('td'); + var mime = this.fileActions.getCurrentMimeType(); + var type = this.fileActions.getCurrentType(); + var permissions = this.fileActions.getCurrentPermissions(); + var action = this.fileActions.getDefault(mime,type, permissions); + if (action) { + event.preventDefault(); + // also set on global object for legacy apps + window.FileActions.currentFile = this.fileActions.currentFile; + action(filename, { + $file: $tr, + fileList: this, + fileActions: this.fileActions, + dir: $tr.attr('data-path') || this.getCurrentDirectory() + }); + } + // deselect row + $(event.target).closest('a').blur(); } - // deselect row - $(event.target).closest('a').blur(); + } else { + var fileInfo = this.files[$tr.index()]; + this._updateDetailsView(fileInfo); + event.preventDefault(); } } }, @@ -417,7 +481,21 @@ else { files = _.pluck(this.getSelectedFiles(), 'name'); } - OC.redirect(this.getDownloadUrl(files, dir)); + + var downloadFileaction = $('#selectedActionsList').find('.download'); + + // don't allow a second click on the download action + if(downloadFileaction.hasClass('disabled')) { + event.preventDefault(); + return; + } + + var disableLoadingState = function(){ + OCA.Files.FileActions.updateFileActionSpinner(downloadFileaction, false); + }; + + OCA.Files.FileActions.updateFileActionSpinner(downloadFileaction, true); + OCA.Files.Files.handleDownload(this.getDownloadUrl(files, dir), disableLoadingState); return false; }, @@ -811,7 +889,7 @@ var formatted; var text; if (mtime > 0) { - formatted = formatDate(mtime); + formatted = OC.Util.formatDate(mtime); text = OC.Util.relativeModifiedDate(mtime); } else { formatted = t('files', 'Unable to determine date'); @@ -1225,6 +1303,12 @@ ready(iconURL); // set mimeicon URL urlSpec.file = OCA.Files.Files.fixPath(path); + if (options.x) { + urlSpec.x = options.x; + } + if (options.y) { + urlSpec.y = options.y; + } if (etag){ // use etag as cache buster @@ -1360,9 +1444,7 @@ } _.each(fileNames, function(fileName) { var $tr = self.findFileEl(fileName); - var $thumbEl = $tr.find('.thumbnail'); - var oldBackgroundImage = $thumbEl.css('background-image'); - $thumbEl.css('background-image', 'url('+ OC.imagePath('core', 'loading.gif') + ')'); + self.showFileBusyState($tr, true); // TODO: improve performance by sending all file names in a single call $.post( OC.filePath('files', 'ajax', 'move.php'), @@ -1404,7 +1486,7 @@ } else { OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error')); } - $thumbEl.css('background-image', oldBackgroundImage); + self.showFileBusyState($tr, false); } ); }); @@ -1465,14 +1547,13 @@ try { var newName = input.val(); - var $thumbEl = tr.find('.thumbnail'); input.tipsy('hide'); form.remove(); if (newName !== oldname) { checkInput(); // mark as loading (temp element) - $thumbEl.css('background-image', 'url('+ OC.imagePath('core', 'loading.gif') + ')'); + self.showFileBusyState(tr, true); tr.attr('data-file', newName); var basename = newName; if (newName.indexOf('.') > 0 && tr.data('type') !== 'dir') { @@ -1480,7 +1561,6 @@ } td.find('a.name span.nametext').text(basename); td.children('a.name').show(); - tr.find('.fileactions, .action').addClass('hidden'); $.ajax({ url: OC.filePath('files','ajax','rename.php'), @@ -1507,6 +1587,7 @@ tr.remove(); tr = self.add(fileInfo, {updateSummary: false, silent: true}); self.$fileList.trigger($.Event('fileActionsReady', {fileList: self, $files: $(tr)})); + self._updateDetailsView(fileInfo); } }); } else { @@ -1551,6 +1632,44 @@ inList:function(file) { return this.findFileEl(file).length; }, + + /** + * Shows busy state on a given file row or multiple + * + * @param {string|Array.<string>} files file name or array of file names + * @param {bool} [busy=true] busy state, true for busy, false to remove busy state + * + * @since 8.2 + */ + showFileBusyState: function(files, state) { + var self = this; + if (!_.isArray(files)) { + files = [files]; + } + + if (_.isUndefined(state)) { + state = true; + } + + _.each(files, function($tr) { + // jquery element already ? + if (!$tr.is) { + $tr = self.findFileEl($tr); + } + + var $thumbEl = $tr.find('.thumbnail'); + $tr.toggleClass('busy', state); + + if (state) { + $thumbEl.attr('data-oldimage', $thumbEl.css('background-image')); + $thumbEl.css('background-image', 'url('+ OC.imagePath('core', 'loading.gif') + ')'); + } else { + $thumbEl.css('background-image', $thumbEl.attr('data-oldimage')); + $thumbEl.removeAttr('data-oldimage'); + } + }); + }, + /** * Delete the given files from the given dir * @param files file names list (without path) @@ -1564,9 +1683,8 @@ files=[files]; } if (files) { + this.showFileBusyState(files, true); for (var i=0; i<files.length; i++) { - var deleteAction = this.findFileEl(files[i]).children("td.date").children(".action.delete"); - deleteAction.removeClass('icon-delete').addClass('icon-loading-small'); } } // Finish any existing actions @@ -1584,7 +1702,7 @@ // no files passed, delete all in current dir params.allfiles = true; // show spinner for all files - this.$fileList.find('tr>td.date .action.delete').removeClass('icon-delete').addClass('icon-loading-small'); + this.$fileList.find('tr').addClass('busy'); } $.post(OC.filePath('files', 'ajax', 'delete.php'), @@ -1627,8 +1745,7 @@ } else { $.each(files,function(index,file) { - var deleteAction = self.findFileEl(file).find('.action.delete'); - deleteAction.removeClass('icon-loading-small').addClass('icon-delete'); + self.$fileList.find('tr').removeClass('busy'); }); } } @@ -1663,6 +1780,7 @@ } this.$table.addClass('hidden'); + this.$el.find('#emptycontent').addClass('hidden'); $mask = $('<div class="mask transparent"></div>'); @@ -2162,6 +2280,20 @@ } }); + }, + + /** + * Register a tab view to be added to all views + */ + registerTabView: function(tabView) { + this._detailsView.addTabView(tabView); + }, + + /** + * Register a detail view to be added to all views + */ + registerDetailView: function(detailView) { + this._detailsView.addDetailView(detailView); } }; diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 034045ee40b..19cc3b26e44 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -271,8 +271,33 @@ FileList.scrollTo(getURLParameter('scrollto')); } */ + }, + + /** + * Handles the download and calls the callback function once the download has started + * - browser sends download request and adds parameter with a token + * - server notices this token and adds a set cookie to the download response + * - browser now adds this cookie for the domain + * - JS periodically checks for this cookie and then knows when the download has started to call the callback + * + * @param {string} url download URL + * @param {function} callback function to call once the download has started + */ + handleDownload: function(url, callback) { + var randomToken = Math.random().toString(36).substring(2), + checkForDownloadCookie = function() { + if (!OC.Util.isCookieSetToValue('ocDownloadStarted', randomToken)){ + return false; + } else { + callback(); + return true; + } + }; + + OC.redirect(url + '&downloadStartSecret=' + randomToken); + OC.Util.waitFor(checkForDownloadCookie, 500); } - } + }; Files._updateStorageStatisticsDebounced = _.debounce(Files._updateStorageStatistics, 250); OCA.Files.Files = Files; diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js new file mode 100644 index 00000000000..a00d907d0d6 --- /dev/null +++ b/apps/files/js/mainfileinfodetailview.js @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2015 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + var TEMPLATE = + '<div class="thumbnail"></div><div title="{{name}}" class="fileName ellipsis">{{name}}</div>' + + '<div class="file-details ellipsis">' + + ' <a href="#" ' + + ' alt="{{starAltText}}"' + + ' class="action action-favorite favorite">' + + ' <img class="svg" src="{{starIcon}}" />' + + ' </a>' + + ' <span class="size" title="{{altSize}}">{{size}}</span>, <span class="date" title="{{altDate}}">{{date}}</span>' + + '</div>'; + + /** + * @class OCA.Files.MainFileInfoDetailView + * @classdesc + * + * Displays main details about a file + * + */ + var MainFileInfoDetailView = function() { + this.initialize(); + }; + /** + * @memberof OCA.Files + */ + MainFileInfoDetailView.prototype = _.extend({}, OCA.Files.DetailFileInfoView.prototype, + /** @lends OCA.Files.MainFileInfoDetailView.prototype */ { + _template: null, + + /** + * Initialize the details view + */ + initialize: function() { + this.$el = $('<div class="mainFileInfoView"></div>'); + }, + + /** + * Renders this details view + */ + render: function() { + this.$el.empty(); + + if (!this._template) { + this._template = Handlebars.compile(TEMPLATE); + } + + if (this._fileInfo) { + var isFavorite = (this._fileInfo.tags || []).indexOf(OC.TAG_FAVORITE) >= 0; + this.$el.append(this._template({ + nameLabel: t('files', 'Name'), + name: this._fileInfo.name, + pathLabel: t('files', 'Path'), + path: this._fileInfo.path, + sizeLabel: t('files', 'Size'), + size: OC.Util.humanFileSize(this._fileInfo.size, true), + altSize: n('files', '%n byte', '%n bytes', this._fileInfo.size), + dateLabel: t('files', 'Modified'), + altDate: OC.Util.formatDate(this._fileInfo.mtime), + date: OC.Util.relativeModifiedDate(this._fileInfo.mtime), + starAltText: isFavorite ? t('files', 'Favorited') : t('files', 'Favorite'), + starIcon: OC.imagePath('core', isFavorite ? 'actions/starred' : 'actions/star') + })); + + // TODO: we really need OC.Previews + var $iconDiv = this.$el.find('.thumbnail'); + if (this._fileInfo.mimetype !== 'httpd/unix-directory') { + // TODO: inject utility class? + FileList.lazyLoadPreview({ + path: this._fileInfo.path + '/' + this._fileInfo.name, + mime: this._fileInfo.mimetype, + etag: this._fileInfo.etag, + x: 50, + y: 50, + callback: function(previewUrl) { + $iconDiv.css('background-image', 'url("' + previewUrl + '")'); + } + }); + } else { + // TODO: special icons / shared / external + $iconDiv.css('background-image', 'url("' + OC.MimeType.getIconUrl('dir') + '")'); + } + this.$el.find('[title]').tooltip({placement: 'bottom'}); + } + } + }); + + OCA.Files.MainFileInfoDetailView = MainFileInfoDetailView; +})(); diff --git a/apps/files/js/tagsplugin.js b/apps/files/js/tagsplugin.js index 293e25176f3..ec69ce4b965 100644 --- a/apps/files/js/tagsplugin.js +++ b/apps/files/js/tagsplugin.js @@ -81,6 +81,7 @@ displayName: 'Favorite', mime: 'all', permissions: OC.PERMISSION_READ, + type: OCA.Files.FileActions.TYPE_INLINE, render: function(actionSpec, isDefault, context) { var $file = context.$file; var isFavorite = $file.data('favorite') === true; diff --git a/apps/files/l10n/ro.js b/apps/files/l10n/ro.js index 16699e084f6..541ff6e444a 100644 --- a/apps/files/l10n/ro.js +++ b/apps/files/l10n/ro.js @@ -30,7 +30,7 @@ OC.L10N.register( "Favorites" : "Favorite", "Home" : "Acasă", "Unable to upload {filename} as it is a directory or has 0 bytes" : "Nu se poate încărca {filename} deoarece este un director sau are mărimea de 0 octeți", - "Total file size {size1} exceeds upload limit {size2}" : "Mărimea fișierului este {size1} ce depășește limita de incarcare de {size2}", + "Total file size {size1} exceeds upload limit {size2}" : "Mărimea fișierului este {size1} ce depășește limita de încărcare de {size2}", "Not enough free space, you are uploading {size1} but only {size2} is left" : "Spațiu liber insuficient, încărcați {size1} însă doar {size2} disponibil rămas", "Upload cancelled." : "Încărcare anulată.", "Could not get result from server." : "Nu se poate obține rezultatul de la server.", @@ -55,7 +55,7 @@ OC.L10N.register( "Modified" : "Modificat", "_%n folder_::_%n folders_" : ["%n director","%n directoare","%n directoare"], "_%n file_::_%n files_" : ["%n fișier","%n fișiere","%n fișiere"], - "You don’t have permission to upload or create files here" : "Nu aveti permisiunea de a incarca sau crea fisiere aici", + "You don’t have permission to upload or create files here" : "Nu aveți permisiunea de a încărca sau crea fișiere aici", "_Uploading %n file_::_Uploading %n files_" : ["Se încarcă %n fișier.","Se încarcă %n fișiere.","Se încarcă %n fișiere."], "\"{name}\" is an invalid file name." : "\"{name}\" este un nume de fișier nevalid.", "File name cannot be empty." : "Numele fișierului nu poate rămâne gol.", diff --git a/apps/files/l10n/ro.json b/apps/files/l10n/ro.json index d09af6ba759..6c4536d5357 100644 --- a/apps/files/l10n/ro.json +++ b/apps/files/l10n/ro.json @@ -28,7 +28,7 @@ "Favorites" : "Favorite", "Home" : "Acasă", "Unable to upload {filename} as it is a directory or has 0 bytes" : "Nu se poate încărca {filename} deoarece este un director sau are mărimea de 0 octeți", - "Total file size {size1} exceeds upload limit {size2}" : "Mărimea fișierului este {size1} ce depășește limita de incarcare de {size2}", + "Total file size {size1} exceeds upload limit {size2}" : "Mărimea fișierului este {size1} ce depășește limita de încărcare de {size2}", "Not enough free space, you are uploading {size1} but only {size2} is left" : "Spațiu liber insuficient, încărcați {size1} însă doar {size2} disponibil rămas", "Upload cancelled." : "Încărcare anulată.", "Could not get result from server." : "Nu se poate obține rezultatul de la server.", @@ -53,7 +53,7 @@ "Modified" : "Modificat", "_%n folder_::_%n folders_" : ["%n director","%n directoare","%n directoare"], "_%n file_::_%n files_" : ["%n fișier","%n fișiere","%n fișiere"], - "You don’t have permission to upload or create files here" : "Nu aveti permisiunea de a incarca sau crea fisiere aici", + "You don’t have permission to upload or create files here" : "Nu aveți permisiunea de a încărca sau crea fișiere aici", "_Uploading %n file_::_Uploading %n files_" : ["Se încarcă %n fișier.","Se încarcă %n fișiere.","Se încarcă %n fișiere."], "\"{name}\" is an invalid file name." : "\"{name}\" este un nume de fișier nevalid.", "File name cannot be empty." : "Numele fișierului nu poate rămâne gol.", diff --git a/apps/files/lib/activity.php b/apps/files/lib/activity.php index fff49ea4ea5..bf80d0cfd7c 100644 --- a/apps/files/lib/activity.php +++ b/apps/files/lib/activity.php @@ -30,6 +30,7 @@ use OCP\IL10N; use OCP\IURLGenerator; class Activity implements IExtension { + const APP_FILES = 'files'; const FILTER_FILES = 'files'; const FILTER_FAVORITES = 'files_favorites'; @@ -78,7 +79,7 @@ class Activity implements IExtension { * @return IL10N */ protected function getL10N($languageCode = null) { - return $this->languageFactory->get('files', $languageCode); + return $this->languageFactory->get(self::APP_FILES, $languageCode); } /** @@ -86,14 +87,21 @@ class Activity implements IExtension { * If no additional types are to be added false is to be returned * * @param string $languageCode - * @return array|false + * @return array|false Array "stringID of the type" => "translated string description for the setting" + * or Array "stringID of the type" => [ + * 'desc' => "translated string description for the setting" + * 'methods' => [self::METHOD_*], + * ] */ public function getNotificationTypes($languageCode) { $l = $this->getL10N($languageCode); return [ self::TYPE_SHARE_CREATED => (string) $l->t('A new file or folder has been <strong>created</strong>'), self::TYPE_SHARE_CHANGED => (string) $l->t('A file or folder has been <strong>changed</strong>'), - self::TYPE_FAVORITES => (string) $l->t('Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>'), + self::TYPE_FAVORITES => [ + 'desc' => (string) $l->t('Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>'), + 'methods' => [self::METHOD_STREAM], + ], self::TYPE_SHARE_DELETED => (string) $l->t('A file or folder has been <strong>deleted</strong>'), self::TYPE_SHARE_RESTORED => (string) $l->t('A file or folder has been <strong>restored</strong>'), ]; @@ -107,7 +115,7 @@ class Activity implements IExtension { * @return array|false */ public function getDefaultTypes($method) { - if ($method === 'stream') { + if ($method === self::METHOD_STREAM) { $settings = array(); $settings[] = self::TYPE_SHARE_CREATED; $settings[] = self::TYPE_SHARE_CHANGED; @@ -132,29 +140,30 @@ class Activity implements IExtension { * @return string|false */ public function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode) { - if ($app !== 'files') { + if ($app !== self::APP_FILES) { return false; } + $l = $this->getL10N($languageCode); switch ($text) { case 'created_self': - return (string) $this->l->t('You created %1$s', $params); + return (string) $l->t('You created %1$s', $params); case 'created_by': - return (string) $this->l->t('%2$s created %1$s', $params); + return (string) $l->t('%2$s created %1$s', $params); case 'created_public': - return (string) $this->l->t('%1$s was created in a public folder', $params); + return (string) $l->t('%1$s was created in a public folder', $params); case 'changed_self': - return (string) $this->l->t('You changed %1$s', $params); + return (string) $l->t('You changed %1$s', $params); case 'changed_by': - return (string) $this->l->t('%2$s changed %1$s', $params); + return (string) $l->t('%2$s changed %1$s', $params); case 'deleted_self': - return (string) $this->l->t('You deleted %1$s', $params); + return (string) $l->t('You deleted %1$s', $params); case 'deleted_by': - return (string) $this->l->t('%2$s deleted %1$s', $params); + return (string) $l->t('%2$s deleted %1$s', $params); case 'restored_self': - return (string) $this->l->t('You restored %1$s', $params); + return (string) $l->t('You restored %1$s', $params); case 'restored_by': - return (string) $this->l->t('%2$s restored %1$s', $params); + return (string) $l->t('%2$s restored %1$s', $params); default: return false; @@ -173,7 +182,7 @@ class Activity implements IExtension { * @return array|false */ function getSpecialParameterList($app, $text) { - if ($app === 'files') { + if ($app === self::APP_FILES) { switch ($text) { case 'created_self': case 'created_by': @@ -223,7 +232,7 @@ class Activity implements IExtension { * @return integer|false */ public function getGroupParameter($activity) { - if ($activity['app'] === 'files') { + if ($activity['app'] === self::APP_FILES) { switch ($activity['subject']) { case 'created_self': case 'created_by': @@ -309,7 +318,7 @@ class Activity implements IExtension { $user = $this->activityManager->getCurrentUserId(); // Display actions from all files if ($filter === self::FILTER_FILES) { - return ['`app` = ?', ['files']]; + return ['`app` = ?', [self::APP_FILES]]; } if (!$user) { @@ -323,7 +332,7 @@ class Activity implements IExtension { $favorites = $this->helper->getFavoriteFilePaths($user); } catch (\RuntimeException $e) { // Too many favorites, can not put them into one query anymore... - return ['`app` = ?', ['files']]; + return ['`app` = ?', [self::APP_FILES]]; } /* @@ -331,7 +340,7 @@ class Activity implements IExtension { * or `file` is a favorite or in a favorite folder */ $parameters = $fileQueryList = []; - $parameters[] = 'files'; + $parameters[] = self::APP_FILES; $fileQueryList[] = '(`type` <> ? AND `type` <> ?)'; $parameters[] = self::TYPE_SHARE_CREATED; @@ -346,7 +355,7 @@ class Activity implements IExtension { $parameters[] = $favorite . '/%'; } - $parameters[] = 'files'; + $parameters[] = self::APP_FILES; return [ ' CASE WHEN `app` = ? THEN (' . implode(' OR ', $fileQueryList) . ') ELSE `app` <> ? END ', @@ -363,6 +372,6 @@ class Activity implements IExtension { * @return bool */ protected function userSettingFavoritesOnly($user) { - return (bool) $this->config->getUserValue($user, 'activity', 'notify_stream_' . self::TYPE_FAVORITES, false); + return (bool) $this->config->getUserValue($user, 'activity', 'notify_' . self::METHOD_STREAM . '_' . self::TYPE_FAVORITES, false); } } diff --git a/apps/files/lib/capabilities.php b/apps/files/lib/capabilities.php index 05d12864dca..2e19283e4d6 100644 --- a/apps/files/lib/capabilities.php +++ b/apps/files/lib/capabilities.php @@ -1,7 +1,7 @@ <?php /** * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Morris Jobke <hey@morrisjobke.de> + * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Tom Needham <tom@owncloud.com> * * @copyright Copyright (c) 2015, ownCloud, Inc. @@ -20,19 +20,28 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ - -namespace OCA\Files; -class Capabilities { - - public static function getCapabilities() { - return new \OC_OCS_Result(array( - 'capabilities' => array( - 'files' => array( - 'bigfilechunking' => true, - ), - ), - )); +namespace OCA\Files; + +use OCP\Capabilities\ICapability; + +/** + * Class Capabilities + * + * @package OCA\Files + */ +class Capabilities implements ICapability { + + /** + * Return this classes capabilities + * + * @return array + */ + public function getCapabilities() { + return [ + 'files' => [ + 'bigfilechunking' => true, + ], + ]; } - } diff --git a/apps/files/tests/activitytest.php b/apps/files/tests/activitytest.php index 4ab8ad11eae..cdb1d21bcd8 100644 --- a/apps/files/tests/activitytest.php +++ b/apps/files/tests/activitytest.php @@ -42,6 +42,9 @@ class ActivityTest extends TestCase { /** @var \PHPUnit_Framework_MockObject_MockObject */ protected $activityHelper; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $l10nFactory; + /** @var \OCA\Files\Activity */ protected $activityExtension; @@ -67,8 +70,28 @@ class ActivityTest extends TestCase { $this->config ); + $this->l10nFactory = $this->getMockBuilder('OC\L10N\Factory') + ->disableOriginalConstructor() + ->getMock(); + $deL10n = $this->getMockBuilder('OC_L10N') + ->disableOriginalConstructor() + ->getMock(); + $deL10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($argument) { + return 'translate(' . $argument . ')'; + }); + + $this->l10nFactory->expects($this->any()) + ->method('get') + ->willReturnMap([ + ['files', null, new \OC_L10N('files', 'en')], + ['files', 'en', new \OC_L10N('files', 'en')], + ['files', 'de', $deL10n], + ]); + $this->activityExtension = $activityExtension = new Activity( - new \OC\L10N\Factory(), + $this->l10nFactory, $this->getMockBuilder('OCP\IURLGenerator')->disableOriginalConstructor()->getMock(), $this->activityManager, $this->activityHelper, @@ -111,6 +134,26 @@ class ActivityTest extends TestCase { $this->activityExtension->translate('files_sharing', '', [], false, false, 'en'), 'Asserting that no translations are set for files_sharing' ); + + // Test english + $this->assertNotFalse( + $this->activityExtension->translate('files', 'deleted_self', ['file'], false, false, 'en'), + 'Asserting that translations are set for files.deleted_self' + ); + $this->assertStringStartsWith( + 'You deleted ', + $this->activityExtension->translate('files', 'deleted_self', ['file'], false, false, 'en') + ); + + // Test translation + $this->assertNotFalse( + $this->activityExtension->translate('files', 'deleted_self', ['file'], false, false, 'de'), + 'Asserting that translations are set for files.deleted_self' + ); + $this->assertStringStartsWith( + 'translate(You deleted ', + $this->activityExtension->translate('files', 'deleted_self', ['file'], false, false, 'de') + ); } public function testGetSpecialParameterList() { diff --git a/apps/files/tests/js/detailsviewSpec.js b/apps/files/tests/js/detailsviewSpec.js new file mode 100644 index 00000000000..db1e24fd68e --- /dev/null +++ b/apps/files/tests/js/detailsviewSpec.js @@ -0,0 +1,105 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2015 Vincent Petry <pvince81@owncloud.com> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +describe('OCA.Files.DetailsView tests', function() { + var detailsView; + + beforeEach(function() { + detailsView = new OCA.Files.DetailsView(); + }); + afterEach(function() { + detailsView.destroy(); + detailsView = undefined; + }); + it('renders itself empty when nothing registered', function() { + detailsView.render(); + expect(detailsView.$el.find('.detailFileInfoContainer').length).toEqual(1); + expect(detailsView.$el.find('.tabsContainer').length).toEqual(1); + }); + describe('file info detail view', function() { + it('renders registered view', function() { + var testView = new OCA.Files.DetailFileInfoView(); + var testView2 = new OCA.Files.DetailFileInfoView(); + detailsView.addDetailView(testView); + detailsView.addDetailView(testView2); + detailsView.render(); + + expect(detailsView.$el.find('.detailFileInfoContainer .detailFileInfoView').length).toEqual(2); + }); + it('updates registered tabs when fileinfo is updated', function() { + var viewRenderStub = sinon.stub(OCA.Files.DetailFileInfoView.prototype, 'render'); + var testView = new OCA.Files.DetailFileInfoView(); + var testView2 = new OCA.Files.DetailFileInfoView(); + detailsView.addDetailView(testView); + detailsView.addDetailView(testView2); + detailsView.render(); + + var fileInfo = {id: 5, name: 'test.txt'}; + viewRenderStub.reset(); + detailsView.setFileInfo(fileInfo); + + expect(testView.getFileInfo()).toEqual(fileInfo); + expect(testView2.getFileInfo()).toEqual(fileInfo); + + expect(viewRenderStub.callCount).toEqual(2); + viewRenderStub.restore(); + }); + }); + describe('tabs', function() { + var testView, testView2; + + beforeEach(function() { + testView = new OCA.Files.DetailTabView('test1'); + testView2 = new OCA.Files.DetailTabView('test2'); + detailsView.addTabView(testView); + detailsView.addTabView(testView2); + detailsView.render(); + }); + it('renders registered tabs', function() { + expect(detailsView.$el.find('.tab').length).toEqual(2); + }); + it('updates registered tabs when fileinfo is updated', function() { + var tabRenderStub = sinon.stub(OCA.Files.DetailTabView.prototype, 'render'); + var fileInfo = {id: 5, name: 'test.txt'}; + tabRenderStub.reset(); + detailsView.setFileInfo(fileInfo); + + expect(testView.getFileInfo()).toEqual(fileInfo); + expect(testView2.getFileInfo()).toEqual(fileInfo); + + expect(tabRenderStub.callCount).toEqual(2); + tabRenderStub.restore(); + }); + it('selects the first tab by default', function() { + expect(detailsView.$el.find('.tabHeader').eq(0).hasClass('selected')).toEqual(true); + expect(detailsView.$el.find('.tabHeader').eq(1).hasClass('selected')).toEqual(false); + expect(detailsView.$el.find('.tab').eq(0).hasClass('hidden')).toEqual(false); + expect(detailsView.$el.find('.tab').eq(1).hasClass('hidden')).toEqual(true); + }); + it('switches the current tab when clicking on tab header', function() { + detailsView.$el.find('.tabHeader').eq(1).click(); + expect(detailsView.$el.find('.tabHeader').eq(0).hasClass('selected')).toEqual(false); + expect(detailsView.$el.find('.tabHeader').eq(1).hasClass('selected')).toEqual(true); + expect(detailsView.$el.find('.tab').eq(0).hasClass('hidden')).toEqual(true); + expect(detailsView.$el.find('.tab').eq(1).hasClass('hidden')).toEqual(false); + }); + }); +}); diff --git a/apps/files/tests/js/favoritespluginspec.js b/apps/files/tests/js/favoritespluginspec.js index 90b40ede74b..1b144c28707 100644 --- a/apps/files/tests/js/favoritespluginspec.js +++ b/apps/files/tests/js/favoritespluginspec.js @@ -113,7 +113,7 @@ describe('OCA.Files.FavoritesPlugin tests', function() { shareOwner: 'user2' }]); - fileList.findFileEl('testdir').find('td a.name').click(); + fileList.findFileEl('testdir').find('td .nametext').click(); expect(OCA.Files.App.fileList.getCurrentDirectory()).toEqual('/somewhere/inside/subdir/testdir'); diff --git a/apps/files/tests/js/fileactionsSpec.js b/apps/files/tests/js/fileactionsSpec.js index 53fa8707674..236cff6cafd 100644 --- a/apps/files/tests/js/fileactionsSpec.js +++ b/apps/files/tests/js/fileactionsSpec.js @@ -20,8 +20,7 @@ */ describe('OCA.Files.FileActions tests', function() { - var $filesTable, fileList; - var FileActions; + var fileList, fileActions; beforeEach(function() { // init horrible parameters @@ -29,211 +28,191 @@ describe('OCA.Files.FileActions tests', function() { $body.append('<input type="hidden" id="dir" value="/subdir"></input>'); $body.append('<input type="hidden" id="permissions" value="31"></input>'); // dummy files table - $filesTable = $body.append('<table id="filestable"></table>'); - fileList = new OCA.Files.FileList($('#testArea')); - FileActions = new OCA.Files.FileActions(); - FileActions.registerDefaultActions(); + fileActions = new OCA.Files.FileActions(); + fileActions.registerAction({ + name: 'Testdropdown', + displayName: 'Testdropdowndisplay', + mime: 'all', + permissions: OC.PERMISSION_READ, + icon: function () { + return OC.imagePath('core', 'actions/download'); + } + }); + + fileActions.registerAction({ + name: 'Testinline', + displayName: 'Testinlinedisplay', + type: OCA.Files.FileActions.TYPE_INLINE, + mime: 'all', + permissions: OC.PERMISSION_READ + }); + + fileActions.registerAction({ + name: 'Testdefault', + displayName: 'Testdefaultdisplay', + mime: 'all', + permissions: OC.PERMISSION_READ + }); + fileActions.setDefault('all', 'Testdefault'); + fileList = new OCA.Files.FileList($body, { + fileActions: fileActions + }); }); afterEach(function() { - FileActions = null; + fileActions = null; fileList.destroy(); fileList = undefined; $('#dir, #permissions, #filestable').remove(); }); it('calling clear() clears file actions', function() { - FileActions.clear(); - expect(FileActions.actions).toEqual({}); - expect(FileActions.defaults).toEqual({}); - expect(FileActions.icons).toEqual({}); - expect(FileActions.currentFile).toBe(null); + fileActions.clear(); + expect(fileActions.actions).toEqual({}); + expect(fileActions.defaults).toEqual({}); + expect(fileActions.icons).toEqual({}); + expect(fileActions.currentFile).toBe(null); }); - it('calling display() sets file actions', function() { - var fileData = { - id: 18, - type: 'file', - name: 'testName.txt', - mimetype: 'text/plain', - size: '1234', - etag: 'a01234c', - mtime: '123456' - }; - - // note: FileActions.display() is called implicitly - var $tr = fileList.add(fileData); - - // actions defined after call - expect($tr.find('.action.action-download').length).toEqual(1); - expect($tr.find('.action.action-download').attr('data-action')).toEqual('Download'); - expect($tr.find('.nametext .action.action-rename').length).toEqual(1); - expect($tr.find('.nametext .action.action-rename').attr('data-action')).toEqual('Rename'); - expect($tr.find('.action.delete').length).toEqual(1); - }); - it('calling display() twice correctly replaces file actions', function() { - var fileData = { - id: 18, - type: 'file', - name: 'testName.txt', - mimetype: 'text/plain', - size: '1234', - etag: 'a01234c', - mtime: '123456' - }; - var $tr = fileList.add(fileData); - - FileActions.display($tr.find('td.filename'), true, fileList); - FileActions.display($tr.find('td.filename'), true, fileList); - - // actions defined after cal - expect($tr.find('.action.action-download').length).toEqual(1); - expect($tr.find('.nametext .action.action-rename').length).toEqual(1); - expect($tr.find('.action.delete').length).toEqual(1); - }); - it('redirects to download URL when clicking download', function() { - var redirectStub = sinon.stub(OC, 'redirect'); - var fileData = { - id: 18, - type: 'file', - name: 'testName.txt', - mimetype: 'text/plain', - size: '1234', - etag: 'a01234c', - mtime: '123456' - }; - var $tr = fileList.add(fileData); - FileActions.display($tr.find('td.filename'), true, fileList); - - $tr.find('.action-download').click(); - - expect(redirectStub.calledOnce).toEqual(true); - expect(redirectStub.getCall(0).args[0]).toEqual( - OC.webroot + - '/index.php/apps/files/ajax/download.php' + - '?dir=%2Fsubdir&files=testName.txt'); - redirectStub.restore(); - }); - it('takes the file\'s path into account when clicking download', function() { - var redirectStub = sinon.stub(OC, 'redirect'); - var fileData = { - id: 18, - type: 'file', - name: 'testName.txt', - path: '/anotherpath/there', - mimetype: 'text/plain', - size: '1234', - etag: 'a01234c', - mtime: '123456' - }; - var $tr = fileList.add(fileData); - FileActions.display($tr.find('td.filename'), true, fileList); - - $tr.find('.action-download').click(); - - expect(redirectStub.calledOnce).toEqual(true); - expect(redirectStub.getCall(0).args[0]).toEqual( - OC.webroot + '/index.php/apps/files/ajax/download.php' + - '?dir=%2Fanotherpath%2Fthere&files=testName.txt' - ); - redirectStub.restore(); - }); - it('deletes file when clicking delete', function() { - var deleteStub = sinon.stub(fileList, 'do_delete'); - var fileData = { - id: 18, - type: 'file', - name: 'testName.txt', - path: '/somepath/dir', - mimetype: 'text/plain', - size: '1234', - etag: 'a01234c', - mtime: '123456' - }; - var $tr = fileList.add(fileData); - FileActions.display($tr.find('td.filename'), true, fileList); - - $tr.find('.action.delete').click(); - - expect(deleteStub.calledOnce).toEqual(true); - expect(deleteStub.getCall(0).args[0]).toEqual('testName.txt'); - expect(deleteStub.getCall(0).args[1]).toEqual('/somepath/dir'); - deleteStub.restore(); - }); - it('shows delete hint when no permission to delete', function() { - var deleteStub = sinon.stub(fileList, 'do_delete'); - var fileData = { - id: 18, - type: 'file', - name: 'testName.txt', - path: '/somepath/dir', - mimetype: 'text/plain', - size: '1234', - etag: 'a01234c', - mtime: '123456', - permissions: OC.PERMISSION_READ - }; - var $tr = fileList.add(fileData); - FileActions.display($tr.find('td.filename'), true, fileList); + describe('displaying actions', function() { + var $tr; - var $action = $tr.find('.action.delete'); + beforeEach(function() { + var fileData = { + id: 18, + type: 'file', + name: 'testName.txt', + mimetype: 'text/plain', + size: '1234', + etag: 'a01234c', + mtime: '123456', + permissions: OC.PERMISSION_READ | OC.PERMISSION_UPDATE + }; - expect($action.hasClass('no-permission')).toEqual(true); - deleteStub.restore(); - }); - it('shows delete hint not when permission to delete', function() { - var deleteStub = sinon.stub(fileList, 'do_delete'); - var fileData = { - id: 18, - type: 'file', - name: 'testName.txt', - path: '/somepath/dir', - mimetype: 'text/plain', - size: '1234', - etag: 'a01234c', - mtime: '123456', - permissions: OC.PERMISSION_DELETE - }; - var $tr = fileList.add(fileData); - FileActions.display($tr.find('td.filename'), true, fileList); - - var $action = $tr.find('.action.delete'); - - expect($action.hasClass('no-permission')).toEqual(false); - deleteStub.restore(); + // note: FileActions.display() is called implicitly + $tr = fileList.add(fileData); + }); + it('renders inline file actions', function() { + // actions defined after call + expect($tr.find('.action.action-testinline').length).toEqual(1); + expect($tr.find('.action.action-testinline').attr('data-action')).toEqual('Testinline'); + }); + it('does not render dropdown actions', function() { + expect($tr.find('.action.action-testdropdown').length).toEqual(0); + }); + it('does not render default action', function() { + expect($tr.find('.action.action-testdefault').length).toEqual(0); + }); + it('replaces file actions when displayed twice', function() { + fileActions.display($tr.find('td.filename'), true, fileList); + fileActions.display($tr.find('td.filename'), true, fileList); + + expect($tr.find('.action.action-testinline').length).toEqual(1); + }); + it('renders actions menu trigger', function() { + expect($tr.find('.action.action-menu').length).toEqual(1); + expect($tr.find('.action.action-menu').attr('data-action')).toEqual('menu'); + }); + it('only renders actions relevant to the mime type', function() { + fileActions.registerAction({ + name: 'Match', + displayName: 'MatchDisplay', + type: OCA.Files.FileActions.TYPE_INLINE, + mime: 'text/plain', + permissions: OC.PERMISSION_READ + }); + fileActions.registerAction({ + name: 'Nomatch', + displayName: 'NoMatchDisplay', + type: OCA.Files.FileActions.TYPE_INLINE, + mime: 'application/octet-stream', + permissions: OC.PERMISSION_READ + }); + + fileActions.display($tr.find('td.filename'), true, fileList); + expect($tr.find('.action.action-match').length).toEqual(1); + expect($tr.find('.action.action-nomatch').length).toEqual(0); + }); + it('only renders actions relevant to the permissions', function() { + fileActions.registerAction({ + name: 'Match', + displayName: 'MatchDisplay', + type: OCA.Files.FileActions.TYPE_INLINE, + mime: 'text/plain', + permissions: OC.PERMISSION_UPDATE + }); + fileActions.registerAction({ + name: 'Nomatch', + displayName: 'NoMatchDisplay', + type: OCA.Files.FileActions.TYPE_INLINE, + mime: 'text/plain', + permissions: OC.PERMISSION_DELETE + }); + + fileActions.display($tr.find('td.filename'), true, fileList); + expect($tr.find('.action.action-match').length).toEqual(1); + expect($tr.find('.action.action-nomatch').length).toEqual(0); + }); }); - it('passes context to action handler', function() { - var actionStub = sinon.stub(); - var fileData = { - id: 18, - type: 'file', - name: 'testName.txt', - mimetype: 'text/plain', - size: '1234', - etag: 'a01234c', - mtime: '123456' - }; - var $tr = fileList.add(fileData); - FileActions.register( - 'all', - 'Test', - OC.PERMISSION_READ, - OC.imagePath('core', 'actions/test'), - actionStub - ); - FileActions.display($tr.find('td.filename'), true, fileList); - $tr.find('.action-test').click(); - expect(actionStub.calledOnce).toEqual(true); - expect(actionStub.getCall(0).args[0]).toEqual('testName.txt'); - var context = actionStub.getCall(0).args[1]; - expect(context.$file.is($tr)).toEqual(true); - expect(context.fileList).toBeDefined(); - expect(context.fileActions).toBeDefined(); - expect(context.dir).toEqual('/subdir'); - - // when data-path is defined - actionStub.reset(); - $tr.attr('data-path', '/somepath'); - $tr.find('.action-test').click(); - context = actionStub.getCall(0).args[1]; - expect(context.dir).toEqual('/somepath'); + describe('action handler', function() { + var actionStub, $tr; + + beforeEach(function() { + var fileData = { + id: 18, + type: 'file', + name: 'testName.txt', + mimetype: 'text/plain', + size: '1234', + etag: 'a01234c', + mtime: '123456' + }; + actionStub = sinon.stub(); + fileActions.registerAction({ + name: 'Test', + type: OCA.Files.FileActions.TYPE_INLINE, + mime: 'all', + icon: OC.imagePath('core', 'actions/test'), + permissions: OC.PERMISSION_READ, + actionHandler: actionStub + }); + $tr = fileList.add(fileData); + }); + it('passes context to action handler', function() { + $tr.find('.action-test').click(); + expect(actionStub.calledOnce).toEqual(true); + expect(actionStub.getCall(0).args[0]).toEqual('testName.txt'); + var context = actionStub.getCall(0).args[1]; + expect(context.$file.is($tr)).toEqual(true); + expect(context.fileList).toBeDefined(); + expect(context.fileActions).toBeDefined(); + expect(context.dir).toEqual('/subdir'); + + // when data-path is defined + actionStub.reset(); + $tr.attr('data-path', '/somepath'); + $tr.find('.action-test').click(); + context = actionStub.getCall(0).args[1]; + expect(context.dir).toEqual('/somepath'); + }); + describe('actions menu', function() { + it('shows actions menu inside row when clicking the menu trigger', function() { + expect($tr.find('td.filename .fileActionsMenu').length).toEqual(0); + $tr.find('.action-menu').click(); + expect($tr.find('td.filename .fileActionsMenu').length).toEqual(1); + }); + it('shows highlight on current row', function() { + $tr.find('.action-menu').click(); + expect($tr.hasClass('mouseOver')).toEqual(true); + }); + it('cleans up after hiding', function() { + var clock = sinon.useFakeTimers(); + $tr.find('.action-menu').click(); + expect($tr.find('.fileActionsMenu').length).toEqual(1); + OC.hideMenus(); + // sliding animation + clock.tick(500); + expect($tr.hasClass('mouseOver')).toEqual(false); + expect($tr.find('.fileActionsMenu').length).toEqual(0); + }); + }); }); describe('custom rendering', function() { var $tr; @@ -251,10 +230,11 @@ describe('OCA.Files.FileActions tests', function() { }); it('regular function', function() { var actionStub = sinon.stub(); - FileActions.registerAction({ + fileActions.registerAction({ name: 'Test', displayName: '', mime: 'all', + type: OCA.Files.FileActions.TYPE_INLINE, permissions: OC.PERMISSION_READ, render: function(actionSpec, isDefault, context) { expect(actionSpec.name).toEqual('Test'); @@ -266,13 +246,13 @@ describe('OCA.Files.FileActions tests', function() { expect(context.fileList).toEqual(fileList); expect(context.$file[0]).toEqual($tr[0]); - var $customEl = $('<a href="#"><span>blabli</span><span>blabla</span></a>'); + var $customEl = $('<a class="action action-test" href="#"><span>blabli</span><span>blabla</span></a>'); $tr.find('td:first').append($customEl); return $customEl; }, actionHandler: actionStub }); - FileActions.display($tr.find('td.filename'), true, fileList); + fileActions.display($tr.find('td.filename'), true, fileList); var $actionEl = $tr.find('td:first .action-test'); expect($actionEl.length).toEqual(1); @@ -306,20 +286,22 @@ describe('OCA.Files.FileActions tests', function() { var actions2 = new OCA.Files.FileActions(); var actionStub1 = sinon.stub(); var actionStub2 = sinon.stub(); - actions1.register( - 'all', - 'Test', - OC.PERMISSION_READ, - OC.imagePath('core', 'actions/test'), - actionStub1 - ); - actions2.register( - 'all', - 'Test2', - OC.PERMISSION_READ, - OC.imagePath('core', 'actions/test'), - actionStub2 - ); + actions1.registerAction({ + name: 'Test', + type: OCA.Files.FileActions.TYPE_INLINE, + mime: 'all', + permissions: OC.PERMISSION_READ, + icon: OC.imagePath('core', 'actions/test'), + actionHandler: actionStub1 + }); + actions2.registerAction({ + name: 'Test2', + type: OCA.Files.FileActions.TYPE_INLINE, + mime: 'all', + permissions: OC.PERMISSION_READ, + icon: OC.imagePath('core', 'actions/test'), + actionHandler: actionStub2 + }); actions2.merge(actions1); actions2.display($tr.find('td.filename'), true, fileList); @@ -342,20 +324,22 @@ describe('OCA.Files.FileActions tests', function() { var actions2 = new OCA.Files.FileActions(); var actionStub1 = sinon.stub(); var actionStub2 = sinon.stub(); - actions1.register( - 'all', - 'Test', - OC.PERMISSION_READ, - OC.imagePath('core', 'actions/test'), - actionStub1 - ); - actions2.register( - 'all', - 'Test', // override - OC.PERMISSION_READ, - OC.imagePath('core', 'actions/test'), - actionStub2 - ); + actions1.registerAction({ + name: 'Test', + type: OCA.Files.FileActions.TYPE_INLINE, + mime: 'all', + permissions: OC.PERMISSION_READ, + icon: OC.imagePath('core', 'actions/test'), + actionHandler: actionStub1 + }); + actions2.registerAction({ + name: 'Test', // override + mime: 'all', + type: OCA.Files.FileActions.TYPE_INLINE, + permissions: OC.PERMISSION_READ, + icon: OC.imagePath('core', 'actions/test'), + actionHandler: actionStub2 + }); actions1.merge(actions2); actions1.display($tr.find('td.filename'), true, fileList); @@ -371,24 +355,26 @@ describe('OCA.Files.FileActions tests', function() { var actions2 = new OCA.Files.FileActions(); var actionStub1 = sinon.stub(); var actionStub2 = sinon.stub(); - actions1.register( - 'all', - 'Test', - OC.PERMISSION_READ, - OC.imagePath('core', 'actions/test'), - actionStub1 - ); + actions1.registerAction({ + mime: 'all', + name: 'Test', + type: OCA.Files.FileActions.TYPE_INLINE, + permissions: OC.PERMISSION_READ, + icon: OC.imagePath('core', 'actions/test'), + actionHandler: actionStub1 + }); actions1.merge(actions2); // late override - actions1.register( - 'all', - 'Test', // override - OC.PERMISSION_READ, - OC.imagePath('core', 'actions/test'), - actionStub2 - ); + actions1.registerAction({ + mime: 'all', + name: 'Test', // override + type: OCA.Files.FileActions.TYPE_INLINE, + permissions: OC.PERMISSION_READ, + icon: OC.imagePath('core', 'actions/test'), + actionHandler: actionStub2 + }); actions1.display($tr.find('td.filename'), true, fileList); @@ -403,25 +389,27 @@ describe('OCA.Files.FileActions tests', function() { var actions2 = new OCA.Files.FileActions(); var actionStub1 = sinon.stub(); var actionStub2 = sinon.stub(); - actions1.register( - 'all', - 'Test', - OC.PERMISSION_READ, - OC.imagePath('core', 'actions/test'), - actionStub1 - ); + actions1.registerAction({ + mime: 'all', + name: 'Test', + type: OCA.Files.FileActions.TYPE_INLINE, + permissions: OC.PERMISSION_READ, + icon: OC.imagePath('core', 'actions/test'), + actionHandler: actionStub1 + }); // copy the Test action to actions2 actions2.merge(actions1); // late override - actions2.register( - 'all', - 'Test', // override - OC.PERMISSION_READ, - OC.imagePath('core', 'actions/test'), - actionStub2 - ); + actions2.registerAction({ + mime: 'all', + name: 'Test', // override + type: OCA.Files.FileActions.TYPE_INLINE, + permissions: OC.PERMISSION_READ, + icon: OC.imagePath('core', 'actions/test'), + actionHandler: actionStub2 + }); // check if original actions still call the correct handler actions1.display($tr.find('td.filename'), true, fileList); @@ -444,42 +432,45 @@ describe('OCA.Files.FileActions tests', function() { it('notifies update event handlers once after multiple changes', function() { var actionStub = sinon.stub(); var handler = sinon.stub(); - FileActions.on('registerAction', handler); - FileActions.register( - 'all', - 'Test', - OC.PERMISSION_READ, - OC.imagePath('core', 'actions/test'), - actionStub - ); - FileActions.register( - 'all', - 'Test2', - OC.PERMISSION_READ, - OC.imagePath('core', 'actions/test'), - actionStub - ); + fileActions.on('registerAction', handler); + fileActions.registerAction({ + mime: 'all', + name: 'Test', + type: OCA.Files.FileActions.TYPE_INLINE, + permissions: OC.PERMISSION_READ, + icon: OC.imagePath('core', 'actions/test'), + actionHandler: actionStub + }); + fileActions.registerAction({ + mime: 'all', + name: 'Test2', + permissions: OC.PERMISSION_READ, + icon: OC.imagePath('core', 'actions/test'), + actionHandler: actionStub + }); expect(handler.calledTwice).toEqual(true); }); it('does not notifies update event handlers after unregistering', function() { var actionStub = sinon.stub(); var handler = sinon.stub(); - FileActions.on('registerAction', handler); - FileActions.off('registerAction', handler); - FileActions.register( - 'all', - 'Test', - OC.PERMISSION_READ, - OC.imagePath('core', 'actions/test'), - actionStub - ); - FileActions.register( - 'all', - 'Test2', - OC.PERMISSION_READ, - OC.imagePath('core', 'actions/test'), - actionStub - ); + fileActions.on('registerAction', handler); + fileActions.off('registerAction', handler); + fileActions.registerAction({ + mime: 'all', + name: 'Test', + type: OCA.Files.FileActions.TYPE_INLINE, + permissions: OC.PERMISSION_READ, + icon: OC.imagePath('core', 'actions/test'), + actionHandler: actionStub + }); + fileActions.registerAction({ + mime: 'all', + name: 'Test2', + type: OCA.Files.FileActions.TYPE_INLINE, + permissions: OC.PERMISSION_READ, + icon: OC.imagePath('core', 'actions/test'), + actionHandler: actionStub + }); expect(handler.notCalled).toEqual(true); }); }); diff --git a/apps/files/tests/js/fileactionsmenuSpec.js b/apps/files/tests/js/fileactionsmenuSpec.js new file mode 100644 index 00000000000..0cfd12a2d04 --- /dev/null +++ b/apps/files/tests/js/fileactionsmenuSpec.js @@ -0,0 +1,273 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2015 Vincent Petry <pvince81@owncloud.com> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +describe('OCA.Files.FileActionsMenu tests', function() { + var fileList, fileActions, menu, actionStub, $tr; + + beforeEach(function() { + // init horrible parameters + var $body = $('#testArea'); + $body.append('<input type="hidden" id="dir" value="/subdir"></input>'); + $body.append('<input type="hidden" id="permissions" value="31"></input>'); + // dummy files table + actionStub = sinon.stub(); + fileActions = new OCA.Files.FileActions(); + fileList = new OCA.Files.FileList($body, { + fileActions: fileActions + }); + + fileActions.registerAction({ + name: 'Testdropdown', + displayName: 'Testdropdowndisplay', + mime: 'all', + permissions: OC.PERMISSION_READ, + icon: function () { + return OC.imagePath('core', 'actions/download'); + }, + actionHandler: actionStub + }); + + fileActions.registerAction({ + name: 'Testdropdownnoicon', + displayName: 'Testdropdowndisplaynoicon', + mime: 'all', + permissions: OC.PERMISSION_READ, + actionHandler: actionStub + }); + + fileActions.registerAction({ + name: 'Testinline', + displayName: 'Testinlinedisplay', + type: OCA.Files.FileActions.TYPE_INLINE, + mime: 'all', + permissions: OC.PERMISSION_READ + }); + + fileActions.registerAction({ + name: 'Testdefault', + displayName: 'Testdefaultdisplay', + mime: 'all', + permissions: OC.PERMISSION_READ + }); + fileActions.setDefault('all', 'Testdefault'); + + var fileData = { + id: 18, + type: 'file', + name: 'testName.txt', + mimetype: 'text/plain', + size: '1234', + etag: 'a01234c', + mtime: '123456' + }; + $tr = fileList.add(fileData); + + var menuContext = { + $file: $tr, + fileList: fileList, + fileActions: fileActions, + dir: fileList.getCurrentDirectory() + }; + menu = new OCA.Files.FileActionsMenu(); + menu.show(menuContext); + }); + afterEach(function() { + fileActions = null; + fileList.destroy(); + fileList = undefined; + menu.remove(); + $('#dir, #permissions, #filestable').remove(); + }); + + describe('rendering', function() { + it('renders dropdown actions in menu', function() { + var $action = menu.$el.find('a[data-action=Testdropdown]'); + expect($action.length).toEqual(1); + expect($action.find('img').attr('src')) + .toEqual(OC.imagePath('core', 'actions/download')); + expect($action.find('.no-icon').length).toEqual(0); + + $action = menu.$el.find('a[data-action=Testdropdownnoicon]'); + expect($action.length).toEqual(1); + expect($action.find('img').length).toEqual(0); + expect($action.find('.no-icon').length).toEqual(1); + }); + it('does not render default actions', function() { + expect(menu.$el.find('a[data-action=Testdefault]').length).toEqual(0); + }); + it('does not render inline actions', function() { + expect(menu.$el.find('a[data-action=Testinline]').length).toEqual(0); + }); + it('only renders actions relevant to the mime type', function() { + fileActions.registerAction({ + name: 'Match', + displayName: 'MatchDisplay', + mime: 'text/plain', + permissions: OC.PERMISSION_READ + }); + fileActions.registerAction({ + name: 'Nomatch', + displayName: 'NoMatchDisplay', + mime: 'application/octet-stream', + permissions: OC.PERMISSION_READ + }); + + menu.render(); + expect(menu.$el.find('a[data-action=Match]').length).toEqual(1); + expect(menu.$el.find('a[data-action=NoMatch]').length).toEqual(0); + }); + it('only renders actions relevant to the permissions', function() { + fileActions.registerAction({ + name: 'Match', + displayName: 'MatchDisplay', + mime: 'text/plain', + permissions: OC.PERMISSION_UPDATE + }); + fileActions.registerAction({ + name: 'Nomatch', + displayName: 'NoMatchDisplay', + mime: 'text/plain', + permissions: OC.PERMISSION_DELETE + }); + + menu.render(); + expect(menu.$el.find('a[data-action=Match]').length).toEqual(1); + expect(menu.$el.find('a[data-action=NoMatch]').length).toEqual(0); + }); + }); + + describe('action handler', function() { + it('calls action handler when clicking menu item', function() { + var $action = menu.$el.find('a[data-action=Testdropdown]'); + $action.click(); + + expect(actionStub.calledOnce).toEqual(true); + expect(actionStub.getCall(0).args[0]).toEqual('testName.txt'); + expect(actionStub.getCall(0).args[1].$file[0]).toEqual($tr[0]); + expect(actionStub.getCall(0).args[1].fileList).toEqual(fileList); + expect(actionStub.getCall(0).args[1].fileActions).toEqual(fileActions); + expect(actionStub.getCall(0).args[1].dir).toEqual('/subdir'); + }); + }); + describe('default actions from registerDefaultActions', function() { + beforeEach(function() { + fileActions.clear(); + fileActions.registerDefaultActions(); + }); + it('redirects to download URL when clicking download', function() { + var redirectStub = sinon.stub(OC, 'redirect'); + var fileData = { + id: 18, + type: 'file', + name: 'testName.txt', + mimetype: 'text/plain', + size: '1234', + etag: 'a01234c', + mtime: '123456' + }; + var $tr = fileList.add(fileData); + fileActions.display($tr.find('td.filename'), true, fileList); + + var menuContext = { + $file: $tr, + fileList: fileList, + fileActions: fileActions, + dir: fileList.getCurrentDirectory() + }; + menu = new OCA.Files.FileActionsMenu(); + menu.show(menuContext); + + menu.$el.find('.action-download').click(); + + expect(redirectStub.calledOnce).toEqual(true); + expect(redirectStub.getCall(0).args[0]).toContain( + OC.webroot + + '/index.php/apps/files/ajax/download.php' + + '?dir=%2Fsubdir&files=testName.txt'); + redirectStub.restore(); + }); + it('takes the file\'s path into account when clicking download', function() { + var redirectStub = sinon.stub(OC, 'redirect'); + var fileData = { + id: 18, + type: 'file', + name: 'testName.txt', + path: '/anotherpath/there', + mimetype: 'text/plain', + size: '1234', + etag: 'a01234c', + mtime: '123456' + }; + var $tr = fileList.add(fileData); + fileActions.display($tr.find('td.filename'), true, fileList); + + var menuContext = { + $file: $tr, + fileList: fileList, + fileActions: fileActions, + dir: '/anotherpath/there' + }; + menu = new OCA.Files.FileActionsMenu(); + menu.show(menuContext); + + menu.$el.find('.action-download').click(); + + expect(redirectStub.calledOnce).toEqual(true); + expect(redirectStub.getCall(0).args[0]).toContain( + OC.webroot + '/index.php/apps/files/ajax/download.php' + + '?dir=%2Fanotherpath%2Fthere&files=testName.txt' + ); + redirectStub.restore(); + }); + it('deletes file when clicking delete', function() { + var deleteStub = sinon.stub(fileList, 'do_delete'); + var fileData = { + id: 18, + type: 'file', + name: 'testName.txt', + path: '/somepath/dir', + mimetype: 'text/plain', + size: '1234', + etag: 'a01234c', + mtime: '123456' + }; + var $tr = fileList.add(fileData); + fileActions.display($tr.find('td.filename'), true, fileList); + + var menuContext = { + $file: $tr, + fileList: fileList, + fileActions: fileActions, + dir: '/somepath/dir' + }; + menu = new OCA.Files.FileActionsMenu(); + menu.show(menuContext); + + menu.$el.find('.action-delete').click(); + + expect(deleteStub.calledOnce).toEqual(true); + expect(deleteStub.getCall(0).args[0]).toEqual('testName.txt'); + expect(deleteStub.getCall(0).args[1]).toEqual('/somepath/dir'); + deleteStub.restore(); + }); + }); +}); + diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index 316df0281e9..57e16626403 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -77,8 +77,8 @@ describe('OCA.Files.FileList tests', function() { '<th id="headerName" class="hidden column-name">' + '<input type="checkbox" id="select_all_files" class="select-all">' + '<a class="name columntitle" data-sort="name"><span>Name</span><span class="sort-indicator"></span></a>' + - '<span class="selectedActions hidden">' + - '<a href class="download">Download</a>' + + '<span id="selectedActionsList" class="selectedActions hidden">' + + '<a href class="download"><img src="actions/download.svg">Download</a>' + '<a href class="delete-selected">Delete</a></span>' + '</th>' + '<th class="hidden column-size"><a class="columntitle" data-sort="size"><span class="sort-indicator"></span></a></th>' + @@ -456,19 +456,19 @@ describe('OCA.Files.FileList tests', function() { expect(notificationStub.notCalled).toEqual(true); }); - it('shows spinner on files to be deleted', function() { + it('shows busy state on files to be deleted', function() { fileList.setFiles(testFiles); doDelete(); - expect(fileList.findFileEl('One.txt').find('.icon-loading-small:not(.icon-delete)').length).toEqual(1); - expect(fileList.findFileEl('Three.pdf').find('.icon-delete:not(.icon-loading-small)').length).toEqual(1); + expect(fileList.findFileEl('One.txt').hasClass('busy')).toEqual(true); + expect(fileList.findFileEl('Three.pdf').hasClass('busy')).toEqual(false); }); - it('shows spinner on all files when deleting all', function() { + it('shows busy state on all files when deleting all', function() { fileList.setFiles(testFiles); fileList.do_delete(); - expect(fileList.$fileList.find('tr .icon-loading-small:not(.icon-delete)').length).toEqual(4); + expect(fileList.$fileList.find('tr.busy').length).toEqual(4); }); it('updates summary when deleting last file', function() { var $summary; @@ -625,7 +625,7 @@ describe('OCA.Files.FileList tests', function() { doCancelRename(); expect($summary.find('.info').text()).toEqual('1 folder and 3 files'); }); - it('Hides actions while rename in progress', function() { + it('Shows busy state while rename in progress', function() { var $tr; doRename(); @@ -634,8 +634,7 @@ describe('OCA.Files.FileList tests', function() { expect($tr.length).toEqual(1); expect(fileList.findFileEl('One.txt').length).toEqual(0); // file actions are hidden - expect($tr.find('.action').hasClass('hidden')).toEqual(true); - expect($tr.find('.fileactions').hasClass('hidden')).toEqual(true); + expect($tr.hasClass('busy')).toEqual(true); // input and form are gone expect(fileList.$fileList.find('input.filename').length).toEqual(0); @@ -1775,7 +1774,7 @@ describe('OCA.Files.FileList tests', function() { var redirectStub = sinon.stub(OC, 'redirect'); $('.selectedActions .download').click(); expect(redirectStub.calledOnce).toEqual(true); - expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22One.txt%22%2C%22Three.pdf%22%2C%22somedir%22%5D'); + expect(redirectStub.getCall(0).args[0]).toContain(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22One.txt%22%2C%22Three.pdf%22%2C%22somedir%22%5D'); redirectStub.restore(); }); it('Downloads root folder when all selected in root folder', function() { @@ -1784,7 +1783,7 @@ describe('OCA.Files.FileList tests', function() { var redirectStub = sinon.stub(OC, 'redirect'); $('.selectedActions .download').click(); expect(redirectStub.calledOnce).toEqual(true); - expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files='); + expect(redirectStub.getCall(0).args[0]).toContain(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files='); redirectStub.restore(); }); it('Downloads parent folder when all selected in subfolder', function() { @@ -1792,7 +1791,7 @@ describe('OCA.Files.FileList tests', function() { var redirectStub = sinon.stub(OC, 'redirect'); $('.selectedActions .download').click(); expect(redirectStub.calledOnce).toEqual(true); - expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=subdir'); + expect(redirectStub.getCall(0).args[0]).toContain(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=subdir'); redirectStub.restore(); }); }); @@ -1870,8 +1869,13 @@ describe('OCA.Files.FileList tests', function() { }); }) }); - describe('File actions', function() { - it('Clicking on a file name will trigger default action', function() { + describe('Details sidebar', function() { + beforeEach(function() { + fileList.setFiles(testFiles); + }); + it('Clicking on a file row will trigger file action if no details view configured', function() { + fileList._detailsView = null; + var updateDetailsViewStub = sinon.stub(fileList, '_updateDetailsView'); var actionStub = sinon.stub(); fileList.setFiles(testFiles); fileList.fileActions.register( @@ -1888,6 +1892,46 @@ describe('OCA.Files.FileList tests', function() { var $tr = fileList.findFileEl('One.txt'); $tr.find('td.filename>a.name').click(); expect(actionStub.calledOnce).toEqual(true); + expect(updateDetailsViewStub.notCalled).toEqual(true); + updateDetailsViewStub.restore(); + }); + it('Clicking on a file row will trigger details sidebar', function() { + fileList.fileActions.setDefault('text/plain', 'Test'); + var $tr = fileList.findFileEl('One.txt'); + $tr.find('td.filename>a.name').click(); + expect($tr.hasClass('highlighted')).toEqual(true); + + expect(fileList._detailsView.getFileInfo().id).toEqual(1); + }); + it('Clicking outside to deselect a file row will trigger details sidebar', function() { + var $tr = fileList.findFileEl('One.txt'); + $tr.find('td.filename>a.name').click(); + + fileList.$el.find('tfoot').click(); + + expect($tr.hasClass('highlighted')).toEqual(false); + expect(fileList._detailsView.getFileInfo()).toEqual(null); + }); + }); + describe('File actions', function() { + it('Clicking on a file name will trigger default action', function() { + var actionStub = sinon.stub(); + fileList.setFiles(testFiles); + fileList.fileActions.registerAction({ + mime: 'text/plain', + name: 'Test', + type: OCA.Files.FileActions.TYPE_INLINE, + permissions: OC.PERMISSION_ALL, + icon: function() { + // Specify icon for hitory button + return OC.imagePath('core','actions/history'); + }, + actionHandler: actionStub + }); + fileList.fileActions.setDefault('text/plain', 'Test'); + var $tr = fileList.findFileEl('One.txt'); + $tr.find('td.filename .nametext').click(); + expect(actionStub.calledOnce).toEqual(true); expect(actionStub.getCall(0).args[0]).toEqual('One.txt'); var context = actionStub.getCall(0).args[1]; expect(context.$file.is($tr)).toEqual(true); @@ -1914,16 +1958,17 @@ describe('OCA.Files.FileList tests', function() { fileList.$fileList.on('fileActionsReady', readyHandler); - fileList.fileActions.register( - 'text/plain', - 'Test', - OC.PERMISSION_ALL, - function() { + fileList.fileActions.registerAction({ + mime: 'text/plain', + name: 'Test', + type: OCA.Files.FileActions.TYPE_INLINE, + permissions: OC.PERMISSION_ALL, + icon: function() { // Specify icon for hitory button return OC.imagePath('core','actions/history'); }, - actionStub - ); + actionHandler: actionStub + }); var $tr = fileList.findFileEl('One.txt'); expect($tr.find('.action-test').length).toEqual(0); expect(readyHandler.notCalled).toEqual(true); @@ -2212,6 +2257,8 @@ describe('OCA.Files.FileList tests', function() { }); }); describe('Handeling errors', function () { + var redirectStub; + beforeEach(function () { redirectStub = sinon.stub(OC, 'redirect'); @@ -2237,4 +2284,36 @@ describe('OCA.Files.FileList tests', function() { expect(redirectStub.calledWith(OC.generateUrl('apps/files'))).toEqual(true); }); }); + describe('showFileBusyState', function() { + var $tr; + + beforeEach(function() { + fileList.setFiles(testFiles); + $tr = fileList.findFileEl('Two.jpg'); + }); + it('shows spinner on busy rows', function() { + fileList.showFileBusyState('Two.jpg', true); + expect($tr.hasClass('busy')).toEqual(true); + expect(OC.TestUtil.getImageUrl($tr.find('.thumbnail'))) + .toEqual(OC.imagePath('core', 'loading.gif')); + + fileList.showFileBusyState('Two.jpg', false); + expect($tr.hasClass('busy')).toEqual(false); + expect(OC.TestUtil.getImageUrl($tr.find('.thumbnail'))) + .toEqual(OC.imagePath('core', 'filetypes/image.svg')); + }); + it('accepts multiple input formats', function() { + _.each([ + 'Two.jpg', + ['Two.jpg'], + $tr, + [$tr] + ], function(testCase) { + fileList.showFileBusyState(testCase, true); + expect($tr.hasClass('busy')).toEqual(true); + fileList.showFileBusyState(testCase, false); + expect($tr.hasClass('busy')).toEqual(false); + }); + }); + }); }); diff --git a/apps/files/tests/js/mainfileinfodetailviewSpec.js b/apps/files/tests/js/mainfileinfodetailviewSpec.js new file mode 100644 index 00000000000..10ad38097c6 --- /dev/null +++ b/apps/files/tests/js/mainfileinfodetailviewSpec.js @@ -0,0 +1,104 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2015 Vincent Petry <pvince81@owncloud.com> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +describe('OCA.Files.MainFileInfoDetailView tests', function() { + var view, tooltipStub, previewStub, fncLazyLoadPreview, fileListMock; + + beforeEach(function() { + tooltipStub = sinon.stub($.fn, 'tooltip'); + fileListMock = sinon.mock(OCA.Files.FileList.prototype); + view = new OCA.Files.MainFileInfoDetailView(); + }); + afterEach(function() { + view.destroy(); + view = undefined; + tooltipStub.restore(); + fileListMock.restore(); + + }); + describe('rendering', function() { + var testFileInfo; + beforeEach(function() { + view = new OCA.Files.MainFileInfoDetailView(); + testFileInfo = { + id: 5, + name: 'One.txt', + path: '/subdir', + size: 123456789, + mtime: Date.UTC(2015, 6, 17, 1, 2, 0, 0) + }; + }); + it('displays basic info', function() { + var clock = sinon.useFakeTimers(Date.UTC(2015, 6, 17, 1, 2, 0, 3)); + var dateExpected = OC.Util.formatDate(Date(Date.UTC(2015, 6, 17, 1, 2, 0, 0))); + view.setFileInfo(testFileInfo); + expect(view.$el.find('.fileName').text()).toEqual('One.txt'); + expect(view.$el.find('.fileName').attr('title')).toEqual('One.txt'); + expect(view.$el.find('.size').text()).toEqual('117.7 MB'); + expect(view.$el.find('.size').attr('title')).toEqual('123456789 bytes'); + expect(view.$el.find('.date').text()).toEqual('a few seconds ago'); + expect(view.$el.find('.date').attr('title')).toEqual(dateExpected); + clock.restore(); + }); + it('displays favorite icon', function() { + view.setFileInfo(_.extend(testFileInfo, { + tags: [OC.TAG_FAVORITE] + })); + expect(view.$el.find('.favorite img').attr('src')) + .toEqual(OC.imagePath('core', 'actions/starred')); + + view.setFileInfo(_.extend(testFileInfo, { + tags: [] + })); + expect(view.$el.find('.favorite img').attr('src')) + .toEqual(OC.imagePath('core', 'actions/star')); + }); + it('displays mime icon', function() { + // File + view.setFileInfo(_.extend(testFileInfo, { + mimetype: 'text/calendar' + })); + + expect(view.$el.find('.thumbnail').css('background-image')) + .toContain('filetypes/text-calendar.svg'); + + // Folder + view.setFileInfo(_.extend(testFileInfo, { + mimetype: 'httpd/unix-directory' + })); + + expect(view.$el.find('.thumbnail').css('background-image')) + .toContain('filetypes/folder.svg'); + }); + it('displays thumbnail', function() { + view.setFileInfo(_.extend(testFileInfo, { + mimetype: 'text/plain' + })); + + var expectation = fileListMock.expects('lazyLoadPreview'); + expectation.once(); + + view.setFileInfo(testFileInfo); + + fileListMock.verify(); + }); + }); +}); diff --git a/apps/files_external/appinfo/application.php b/apps/files_external/appinfo/application.php index 62d4d142ba6..d77a302466c 100644 --- a/apps/files_external/appinfo/application.php +++ b/apps/files_external/appinfo/application.php @@ -21,7 +21,7 @@ * */ -namespace OCA\Files_External\Appinfo; +namespace OCA\Files_External\AppInfo; use \OCA\Files_External\Controller\AjaxController; use \OCP\AppFramework\App; diff --git a/apps/files_external/appinfo/routes.php b/apps/files_external/appinfo/routes.php index 97eb1353b1e..bc4b0e98c91 100644 --- a/apps/files_external/appinfo/routes.php +++ b/apps/files_external/appinfo/routes.php @@ -23,7 +23,7 @@ * */ -namespace OCA\Files_External\Appinfo; +namespace OCA\Files_External\AppInfo; /** * @var $this \OC\Route\Router diff --git a/apps/files_external/controller/ajaxcontroller.php b/apps/files_external/controller/ajaxcontroller.php index 6225cd0b619..cb2de432286 100644 --- a/apps/files_external/controller/ajaxcontroller.php +++ b/apps/files_external/controller/ajaxcontroller.php @@ -25,6 +25,7 @@ namespace OCA\Files_External\Controller; use OCP\AppFramework\Controller; use OCP\IRequest; use OCP\AppFramework\Http\JSONResponse; +use phpseclib\Crypt\RSA; class AjaxController extends Controller { public function __construct($appName, IRequest $request) { @@ -32,8 +33,8 @@ class AjaxController extends Controller { } private function generateSshKeys() { - $rsa = new \Crypt_RSA(); - $rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_OPENSSH); + $rsa = new RSA(); + $rsa->setPublicKeyFormat(RSA::PUBLIC_FORMAT_OPENSSH); $rsa->setPassword(\OC::$server->getConfig()->getSystemValue('secret', '')); $key = $rsa->createKey(); diff --git a/apps/files_external/lib/amazons3.php b/apps/files_external/lib/amazons3.php index 02a02710a14..b956a607eba 100644 --- a/apps/files_external/lib/amazons3.php +++ b/apps/files_external/lib/amazons3.php @@ -40,6 +40,7 @@ require 'aws-autoloader.php'; use Aws\S3\S3Client; use Aws\S3\Exception\S3Exception; +use Icewind\Streams\IteratorDirectory; class AmazonS3 extends \OC\Files\Storage\Common { @@ -284,9 +285,7 @@ class AmazonS3 extends \OC\Files\Storage\Common { $files[] = $file; } - \OC\Files\Stream\Dir::register('amazons3' . $path, $files); - - return opendir('fakedir://amazons3' . $path); + return IteratorDirectory::wrap($files); } catch (S3Exception $e) { \OCP\Util::logException('files_external', $e); return false; diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 5dc6d06ae06..8fcf39cc767 100644 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -31,6 +31,8 @@ * */ +use phpseclib\Crypt\AES; + /** * Class to configure mount.json globally and for users */ @@ -494,8 +496,16 @@ class OC_Mount_Config { if (class_exists($class)) { try { $storage = new $class($options); - if ($storage->test($isPersonal)) { - return self::STATUS_SUCCESS; + + try { + $result = $storage->test($isPersonal); + $storage->setAvailability($result); + if ($result) { + return self::STATUS_SUCCESS; + } + } catch (\Exception $e) { + $storage->setAvailability(false); + throw $e; } } catch (Exception $exception) { \OCP\Util::logException('files_external', $exception); @@ -895,10 +905,7 @@ class OC_Mount_Config { * Returns the encryption cipher */ private static function getCipher() { - if (!class_exists('Crypt_AES', false)) { - include('Crypt/AES.php'); - } - $cipher = new Crypt_AES(CRYPT_AES_MODE_CBC); + $cipher = new AES(AES::MODE_CBC); $cipher->setKey(\OC::$server->getConfig()->getSystemValue('passwordsalt', null)); return $cipher; } diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php index 78219f8f06e..2d1aea1afc8 100644 --- a/apps/files_external/lib/dropbox.php +++ b/apps/files_external/lib/dropbox.php @@ -29,6 +29,8 @@ namespace OC\Files\Storage; +use Icewind\Streams\IteratorDirectory; + require_once __DIR__ . '/../3rdparty/Dropbox/autoload.php'; class Dropbox extends \OC\Files\Storage\Common { @@ -156,8 +158,7 @@ class Dropbox extends \OC\Files\Storage\Common { foreach ($contents as $file) { $files[] = basename($file['path']); } - \OC\Files\Stream\Dir::register('dropbox'.$path, $files); - return opendir('fakedir://dropbox'.$path); + return IteratorDirectory::wrap($files); } return false; } diff --git a/apps/files_external/lib/google.php b/apps/files_external/lib/google.php index 8199d97eacb..2ca550dfe7c 100644 --- a/apps/files_external/lib/google.php +++ b/apps/files_external/lib/google.php @@ -32,6 +32,8 @@ namespace OC\Files\Storage; +use Icewind\Streams\IteratorDirectory; + set_include_path(get_include_path().PATH_SEPARATOR. \OC_App::getAppPath('files_external').'/3rdparty/google-api-php-client/src'); require_once 'Google/Client.php'; @@ -291,8 +293,7 @@ class Google extends \OC\Files\Storage\Common { } $pageToken = $children->getNextPageToken(); } - \OC\Files\Stream\Dir::register('google'.$path, $files); - return opendir('fakedir://google'.$path); + return IteratorDirectory::wrap($files); } else { return false; } diff --git a/apps/files_external/lib/sftp.php b/apps/files_external/lib/sftp.php index cbe090311a9..7f921b5342f 100644 --- a/apps/files_external/lib/sftp.php +++ b/apps/files_external/lib/sftp.php @@ -29,9 +29,12 @@ * */ namespace OC\Files\Storage; +use Icewind\Streams\IteratorDirectory; + +use phpseclib\Net\SFTP\Stream; /** -* Uses phpseclib's Net_SFTP class and the Net_SFTP_Stream stream wrapper to +* Uses phpseclib's Net\SFTP class and the Net\SFTP\Stream stream wrapper to * provide access to SFTP servers. */ class SFTP extends \OC\Files\Storage\Common { @@ -42,7 +45,7 @@ class SFTP extends \OC\Files\Storage\Common { private $port = 22; /** - * @var \Net_SFTP + * @var SFTP */ protected $client; @@ -51,10 +54,10 @@ class SFTP extends \OC\Files\Storage\Common { */ public function __construct($params) { // Register sftp:// - \Net_SFTP_Stream::register(); + Stream::register(); $this->host = $params['host']; - + //deals with sftp://server example $proto = strpos($this->host, '://'); if ($proto != false) { @@ -87,7 +90,7 @@ class SFTP extends \OC\Files\Storage\Common { /** * Returns the connection. * - * @return \Net_SFTP connected client instance + * @return \phpseclib\Net\SFTP connected client instance * @throws \Exception when the connection failed */ public function getConnection() { @@ -96,7 +99,7 @@ class SFTP extends \OC\Files\Storage\Common { } $hostKeys = $this->readHostKeys(); - $this->client = new \Net_SFTP($this->host, $this->port); + $this->client = new \phpseclib\Net\SFTP($this->host, $this->port); // The SSH Host Key MUST be verified before login(). $currentHostKey = $this->client->getServerPublicHostKey(); @@ -278,8 +281,7 @@ class SFTP extends \OC\Files\Storage\Common { $dirStream[] = $file; } } - \OC\Files\Stream\Dir::register($id, $dirStream); - return opendir('fakedir://' . $id); + return IteratorDirectory::wrap($dirStream); } catch(\Exception $e) { return false; } diff --git a/apps/files_external/lib/sftp_key.php b/apps/files_external/lib/sftp_key.php index 1bcea6bc96d..a193b323678 100644 --- a/apps/files_external/lib/sftp_key.php +++ b/apps/files_external/lib/sftp_key.php @@ -22,14 +22,15 @@ */ namespace OC\Files\Storage; -/** -* Uses phpseclib's Net_SFTP class and the Net_SFTP_Stream stream wrapper to -* provide access to SFTP servers. -*/ +use phpseclib\Crypt\RSA; + class SFTP_Key extends \OC\Files\Storage\SFTP { private $publicKey; private $privateKey; + /** + * {@inheritdoc} + */ public function __construct($params) { parent::__construct($params); $this->publicKey = $params['public_key']; @@ -39,7 +40,7 @@ class SFTP_Key extends \OC\Files\Storage\SFTP { /** * Returns the connection. * - * @return \Net_SFTP connected client instance + * @return \phpseclib\Net\SFTP connected client instance * @throws \Exception when the connection failed */ public function getConnection() { @@ -48,7 +49,7 @@ class SFTP_Key extends \OC\Files\Storage\SFTP { } $hostKeys = $this->readHostKeys(); - $this->client = new \Net_SFTP($this->getHost()); + $this->client = new \phpseclib\Net\SFTP($this->getHost()); // The SSH Host Key MUST be verified before login(). $currentHostKey = $this->client->getServerPublicHostKey(); @@ -74,10 +75,10 @@ class SFTP_Key extends \OC\Files\Storage\SFTP { /** * Returns the private key to be used for authentication to the remote server. * - * @return \Crypt_RSA instance or null in case of a failure to load the key. + * @return RSA instance or null in case of a failure to load the key. */ private function getPrivateKey() { - $key = new \Crypt_RSA(); + $key = new RSA(); $key->setPassword(\OC::$server->getConfig()->getSystemValue('secret', '')); if (!$key->loadKey($this->privateKey)) { // Should this exception rather than return null? diff --git a/apps/files_external/lib/streamwrapper.php b/apps/files_external/lib/streamwrapper.php index f2438a5487b..387667a81a9 100644 --- a/apps/files_external/lib/streamwrapper.php +++ b/apps/files_external/lib/streamwrapper.php @@ -40,8 +40,11 @@ abstract class StreamWrapper extends Common { } public function rmdir($path) { - if ($this->file_exists($path) && $this->isDeletable($path)) { + if ($this->is_dir($path) && $this->isDeletable($path)) { $dh = $this->opendir($path); + if (!is_resource($dh)) { + return false; + } while (($file = readdir($dh)) !== false) { if ($this->is_dir($path . '/' . $file)) { $this->rmdir($path . '/' . $file); diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php index 50f0d40805a..d8107e58fed 100644 --- a/apps/files_external/lib/swift.php +++ b/apps/files_external/lib/swift.php @@ -32,6 +32,7 @@ namespace OC\Files\Storage; use Guzzle\Http\Exception\ClientErrorResponseException; +use Icewind\Streams\IteratorDirectory; use OpenCloud; use OpenCloud\Common\Exceptions; use OpenCloud\OpenStack; @@ -222,8 +223,7 @@ class Swift extends \OC\Files\Storage\Common { } } - \OC\Files\Stream\Dir::register('swift' . $path, $files); - return opendir('fakedir://swift' . $path); + return IteratorDirectory::wrap($files); } catch (\Exception $e) { \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); return false; diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index f72f5024622..9000fafd8dd 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -56,6 +56,7 @@ $application->setupPropagation(); \OCP\Util::addScript('files_sharing', 'share'); \OCP\Util::addScript('files_sharing', 'external'); +\OCP\Util::addStyle('files_sharing', 'sharetabview'); // FIXME: registering a job here will cause additional useless SQL queries // when the route is not cron.php, needs a better way diff --git a/apps/files_sharing/appinfo/application.php b/apps/files_sharing/appinfo/application.php index b9c2844d78c..2fe9019d54e 100644 --- a/apps/files_sharing/appinfo/application.php +++ b/apps/files_sharing/appinfo/application.php @@ -2,6 +2,7 @@ /** * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <icewind@owncloud.com> + * @author Roeland Jago Douma <roeland@famdouma.nl> * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 @@ -20,7 +21,7 @@ * */ -namespace OCA\Files_Sharing\Appinfo; +namespace OCA\Files_Sharing\AppInfo; use OCA\Files_Sharing\Helper; use OCA\Files_Sharing\MountProvider; @@ -31,6 +32,7 @@ use OCA\Files_Sharing\Controllers\ExternalSharesController; use OCA\Files_Sharing\Controllers\ShareController; use OCA\Files_Sharing\Middleware\SharingCheckMiddleware; use \OCP\IContainer; +use OCA\Files_Sharing\Capabilities; class Application extends App { public function __construct(array $urlParams = array()) { @@ -122,6 +124,11 @@ class Application extends App { $server->getConfig() ); }); + + /* + * Register capabilities + */ + $container->registerCapability('OCA\Files_Sharing\Capabilities'); } public function registerMountProviders() { diff --git a/apps/files_sharing/appinfo/routes.php b/apps/files_sharing/appinfo/routes.php index 21d21a83441..1e99267a43a 100644 --- a/apps/files_sharing/appinfo/routes.php +++ b/apps/files_sharing/appinfo/routes.php @@ -97,8 +97,3 @@ API::register('delete', array('\OCA\Files_Sharing\API\Remote', 'declineShare'), 'files_sharing'); -// Register with the capabilities API -API::register('get', - '/cloud/capabilities', - array('OCA\Files_Sharing\Capabilities', 'getCapabilities'), - 'files_sharing', API::USER_AUTH); diff --git a/apps/files_sharing/css/sharetabview.css b/apps/files_sharing/css/sharetabview.css new file mode 100644 index 00000000000..42c9bee7173 --- /dev/null +++ b/apps/files_sharing/css/sharetabview.css @@ -0,0 +1,3 @@ +.app-files .shareTabView { + min-height: 100px; +} diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index 5923e426f05..1993efe7d73 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -57,7 +57,8 @@ OCA.Sharing.PublicApp = { scrollContainer: $(window), dragOptions: dragOptions, folderDropOptions: folderDropOptions, - fileActions: fileActions + fileActions: fileActions, + detailsViewEnabled: false } ); this.files = OCA.Files.Files; diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index e7823454c53..04700b84011 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -89,57 +89,63 @@ } }); - fileActions.register( - 'all', - 'Share', - OC.PERMISSION_SHARE, - OC.imagePath('core', 'actions/share'), - function(filename, context) { - - var $tr = context.$file; - var itemType = 'file'; - if ($tr.data('type') === 'dir') { - itemType = 'folder'; - } - var possiblePermissions = $tr.data('share-permissions'); - if (_.isUndefined(possiblePermissions)) { - possiblePermissions = $tr.data('permissions'); - } + fileActions.registerAction({ + name: 'Share', + displayName: '', + mime: 'all', + permissions: OC.PERMISSION_SHARE, + icon: OC.imagePath('core', 'actions/share'), + type: OCA.Files.FileActions.TYPE_INLINE, + actionHandler: function(filename, context) { + var $tr = context.$file; + var itemType = 'file'; + if ($tr.data('type') === 'dir') { + itemType = 'folder'; + } + var possiblePermissions = $tr.data('share-permissions'); + if (_.isUndefined(possiblePermissions)) { + possiblePermissions = $tr.data('permissions'); + } - var appendTo = $tr.find('td.filename'); - // Check if drop down is already visible for a different file - if (OC.Share.droppedDown) { - if ($tr.attr('data-id') !== $('#dropdown').attr('data-item-source')) { - OC.Share.hideDropDown(function () { - $tr.addClass('mouseOver'); - OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename); - }); + var appendTo = $tr.find('td.filename'); + // Check if drop down is already visible for a different file + if (OC.Share.droppedDown) { + if ($tr.attr('data-id') !== $('#dropdown').attr('data-item-source')) { + OC.Share.hideDropDown(function () { + $tr.addClass('mouseOver'); + OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename); + }); + } else { + OC.Share.hideDropDown(); + } } else { - OC.Share.hideDropDown(); + $tr.addClass('mouseOver'); + OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename); } - } else { - $tr.addClass('mouseOver'); - OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename); + $('#dropdown').on('sharesChanged', function(ev) { + // files app current cannot show recipients on load, so we don't update the + // icon when changed for consistency + if (context.fileList.$el.closest('#app-content-files').length) { + return; + } + var recipients = _.pluck(ev.shares[OC.Share.SHARE_TYPE_USER], 'share_with_displayname'); + var groupRecipients = _.pluck(ev.shares[OC.Share.SHARE_TYPE_GROUP], 'share_with_displayname'); + recipients = recipients.concat(groupRecipients); + // note: we only update the data attribute because updateIcon() + // is called automatically after this event + if (recipients.length) { + $tr.attr('data-share-recipients', OCA.Sharing.Util.formatRecipients(recipients)); + } + else { + $tr.removeAttr('data-share-recipients'); + } + }); } - $('#dropdown').on('sharesChanged', function(ev) { - // files app current cannot show recipients on load, so we don't update the - // icon when changed for consistency - if (context.fileList.$el.closest('#app-content-files').length) { - return; - } - var recipients = _.pluck(ev.shares[OC.Share.SHARE_TYPE_USER], 'share_with_displayname'); - var groupRecipients = _.pluck(ev.shares[OC.Share.SHARE_TYPE_GROUP], 'share_with_displayname'); - recipients = recipients.concat(groupRecipients); - // note: we only update the data attribute because updateIcon() - // is called automatically after this event - if (recipients.length) { - $tr.attr('data-share-recipients', OCA.Sharing.Util.formatRecipients(recipients)); - } - else { - $tr.removeAttr('data-share-recipients'); - } - }); - }, t('files_sharing', 'Share')); + }); + + OC.addScript('files_sharing', 'sharetabview').done(function() { + fileList.registerTabView(new OCA.Sharing.ShareTabView('shareTabView')); + }); }, /** diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js new file mode 100644 index 00000000000..e02de923751 --- /dev/null +++ b/apps/files_sharing/js/sharetabview.js @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + var TEMPLATE = + '<div>Owner: {{owner}}'; + + /** + * @class OCA.Sharing.ShareTabView + * @classdesc + * + * Displays sharing information + * + */ + var ShareTabView = function(id) { + this.initialize(id); + }; + /** + * @memberof OCA.Sharing + */ + ShareTabView.prototype = _.extend({}, OCA.Files.DetailTabView.prototype, + /** @lends OCA.Sharing.ShareTabView.prototype */ { + _template: null, + + /** + * Initialize the details view + */ + initialize: function() { + OCA.Files.DetailTabView.prototype.initialize.apply(this, arguments); + this.$el.addClass('shareTabView'); + }, + + getLabel: function() { + return t('files_sharing', 'Sharing'); + }, + + /** + * Renders this details view + */ + render: function() { + this.$el.empty(); + + if (!this._template) { + this._template = Handlebars.compile(TEMPLATE); + } + + if (this._fileInfo) { + this.$el.append(this._template({ + owner: this._fileInfo.shareOwner || OC.currentUser + })); + + } else { + // TODO: render placeholder text? + } + } + }); + + OCA.Sharing.ShareTabView = ShareTabView; +})(); + diff --git a/apps/files_sharing/l10n/gl.js b/apps/files_sharing/l10n/gl.js index d04db75d0b2..40694fc0d30 100644 --- a/apps/files_sharing/l10n/gl.js +++ b/apps/files_sharing/l10n/gl.js @@ -41,8 +41,8 @@ OC.L10N.register( "%2$s shared %1$s with you" : "%2$s compartiu %1$s con vostede", "You shared %1$s via link" : "Vostede compartiu %1$s mediante ligazón", "Shares" : "Comparticións", - "Share with me through my #ownCloud Federated Cloud ID, see %s" : "Compartido comigo a través do meu ID da nube federada do #ownCloud , vexa %s", - "Share with me through my #ownCloud Federated Cloud ID" : "Compartido comigo a través do meu ID da nube federada do #ownCloud", + "Share with me through my #ownCloud Federated Cloud ID, see %s" : "Comparte comigo a través do meu ID da nube federada do #ownCloud , vexa %s", + "Share with me through my #ownCloud Federated Cloud ID" : "Comparte comigo a través do meu ID da nube federada do #ownCloud", "This share is password-protected" : "Esta compartición está protexida con contrasinal", "The password is wrong. Try again." : "O contrasinal é incorrecto. Ténteo de novo.", "Password" : "Contrasinal", @@ -67,7 +67,7 @@ OC.L10N.register( "Your Federated Cloud ID:" : "ID da súa nube federada:", "Share it:" : "Compártao:", "Add it to your website:" : "Engádao o seu sitio web:", - "Share with me via ownCloud" : "Compartido comigo a través do ownCloud", + "Share with me via ownCloud" : "Comparte comigo a través do ownCloud", "HTML Code:" : "Código HTML:" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_sharing/l10n/gl.json b/apps/files_sharing/l10n/gl.json index 45f6a369f93..97eb5d14bf4 100644 --- a/apps/files_sharing/l10n/gl.json +++ b/apps/files_sharing/l10n/gl.json @@ -39,8 +39,8 @@ "%2$s shared %1$s with you" : "%2$s compartiu %1$s con vostede", "You shared %1$s via link" : "Vostede compartiu %1$s mediante ligazón", "Shares" : "Comparticións", - "Share with me through my #ownCloud Federated Cloud ID, see %s" : "Compartido comigo a través do meu ID da nube federada do #ownCloud , vexa %s", - "Share with me through my #ownCloud Federated Cloud ID" : "Compartido comigo a través do meu ID da nube federada do #ownCloud", + "Share with me through my #ownCloud Federated Cloud ID, see %s" : "Comparte comigo a través do meu ID da nube federada do #ownCloud , vexa %s", + "Share with me through my #ownCloud Federated Cloud ID" : "Comparte comigo a través do meu ID da nube federada do #ownCloud", "This share is password-protected" : "Esta compartición está protexida con contrasinal", "The password is wrong. Try again." : "O contrasinal é incorrecto. Ténteo de novo.", "Password" : "Contrasinal", @@ -65,7 +65,7 @@ "Your Federated Cloud ID:" : "ID da súa nube federada:", "Share it:" : "Compártao:", "Add it to your website:" : "Engádao o seu sitio web:", - "Share with me via ownCloud" : "Compartido comigo a través do ownCloud", + "Share with me via ownCloud" : "Comparte comigo a través do ownCloud", "HTML Code:" : "Código HTML:" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_sharing/lib/activity.php b/apps/files_sharing/lib/activity.php index e531674ddc2..204c0a037b9 100644 --- a/apps/files_sharing/lib/activity.php +++ b/apps/files_sharing/lib/activity.php @@ -106,7 +106,7 @@ class Activity implements IExtension { self::TYPE_REMOTE_SHARE, ]; - if ($method === 'stream') { + if ($method === self::METHOD_STREAM) { $defaultTypes[] = self::TYPE_PUBLIC_LINKS; } diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index bb62e8078ad..c25dc92409f 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -60,7 +60,7 @@ class Shared_Cache extends Cache { if ($target === false || $target === $this->storage->getMountPoint()) { $target = ''; } - $source = \OC_Share_Backend_File::getSource($target, $this->storage->getMountPoint(), $this->storage->getItemType()); + $source = \OC_Share_Backend_File::getSource($target, $this->storage->getShare()); if (isset($source['path']) && isset($source['fileOwner'])) { \OC\Files\Filesystem::initMountPoints($source['fileOwner']); $mounts = \OC\Files\Filesystem::getMountByNumericId($source['storage']); @@ -242,7 +242,7 @@ class Shared_Cache extends Cache { */ protected function getMoveInfo($path) { $cache = $this->getSourceCache($path); - $file = \OC_Share_Backend_File::getSource($path, $this->storage->getMountPoint(), $this->storage->getItemType()); + $file = \OC_Share_Backend_File::getSource($path, $this->storage->getShare()); return [$cache->getNumericStorageId(), $file['path']]; } diff --git a/apps/files_sharing/lib/capabilities.php b/apps/files_sharing/lib/capabilities.php index ac6454c3433..ef69a40078b 100644 --- a/apps/files_sharing/lib/capabilities.php +++ b/apps/files_sharing/lib/capabilities.php @@ -20,6 +20,7 @@ */ namespace OCA\Files_Sharing; +use OCP\Capabilities\ICapability; use \OCP\IConfig; /** @@ -27,32 +28,21 @@ use \OCP\IConfig; * * @package OCA\Files_Sharing */ -class Capabilities { +class Capabilities implements ICapability { /** @var IConfig */ private $config; - /** - * @param IConfig $config - */ public function __construct(IConfig $config) { $this->config = $config; } /** - * @return \OC_OCS_Result - */ - public static function getCapabilities() { - $config = \OC::$server->getConfig(); - $cap = new Capabilities($config); - return $cap->getCaps(); - } - - - /** - * @return \OC_OCS_Result + * Return this classes capabilities + * + * @return array */ - public function getCaps() { + public function getCapabilities() { $res = []; $public = []; @@ -76,12 +66,8 @@ class Capabilities { $res['resharing'] = $this->config->getAppValue('core', 'shareapi_allow_resharing', 'yes') === 'yes'; - - return new \OC_OCS_Result([ - 'capabilities' => [ - 'files_sharing' => $res - ], - ]); + return [ + 'files_sharing' => $res, + ]; } - } diff --git a/apps/files_sharing/lib/propagation/recipientpropagator.php b/apps/files_sharing/lib/propagation/recipientpropagator.php index 97ea452aa6c..11764106861 100644 --- a/apps/files_sharing/lib/propagation/recipientpropagator.php +++ b/apps/files_sharing/lib/propagation/recipientpropagator.php @@ -133,8 +133,8 @@ class RecipientPropagator { $this->markDirty($share, microtime(true)); // propagate up the share tree - $user = $share['uid_owner']; - if($user !== $this->userId) { + if ($share['share_with'] === $this->userId) { + $user = $share['uid_owner']; $view = new View('/' . $user . '/files'); $path = $view->getPath($share['file_source']); $watcher = new ChangeWatcher($view, $this->manager->getSharePropagator($user)); diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index 9c09e05408b..7bbc2083702 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -206,27 +206,15 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { /** * @param string $target - * @param string $mountPoint - * @param string $itemType + * @param array $share * @return array|false source item */ - public static function getSource($target, $mountPoint, $itemType) { - if ($itemType === 'folder') { - $source = \OCP\Share::getItemSharedWith('folder', $mountPoint, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); - if ($source && $target !== '') { - // note: in case of ext storage mount points the path might be empty - // which would cause a leading slash to appear - $source['path'] = ltrim($source['path'] . '/' . $target, '/'); - } - } else { - $source = \OCP\Share::getItemSharedWith('file', $mountPoint, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); - } - if ($source) { - return self::resolveReshares($source); + public static function getSource($target, $share) { + if ($share['item_type'] === 'folder' && $target !== '') { + // note: in case of ext storage mount points the path might be empty + // which would cause a leading slash to appear + $share['path'] = ltrim($share['path'] . '/' . $target, '/'); } - - \OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::DEBUG); - return false; + return self::resolveReshares($share); } - } diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index ff01489d77b..66803db1425 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -83,14 +83,14 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { if (!isset($this->files[$target])) { // Check for partial files if (pathinfo($target, PATHINFO_EXTENSION) === 'part') { - $source = \OC_Share_Backend_File::getSource(substr($target, 0, -5), $this->getMountPoint(), $this->getItemType()); + $source = \OC_Share_Backend_File::getSource(substr($target, 0, -5), $this->getShare()); if ($source) { $source['path'] .= '.part'; // All partial files have delete permission $source['permissions'] |= \OCP\Constants::PERMISSION_DELETE; } } else { - $source = \OC_Share_Backend_File::getSource($target, $this->getMountPoint(), $this->getItemType()); + $source = \OC_Share_Backend_File::getSource($target, $this->getShare()); } $this->files[$target] = $source; } diff --git a/apps/files_sharing/publicwebdav.php b/apps/files_sharing/publicwebdav.php index 5bde908109d..eec158dd4b6 100644 --- a/apps/files_sharing/publicwebdav.php +++ b/apps/files_sharing/publicwebdav.php @@ -33,24 +33,18 @@ OC_Util::obEnd(); // Backends $authBackend = new OCA\Files_Sharing\Connector\PublicAuth(\OC::$server->getConfig()); -// Fire up server -$objectTree = new \OC\Connector\Sabre\ObjectTree(); -$server = new \OC\Connector\Sabre\Server($objectTree); -// Set URL explicitly due to reverse-proxy situations -$server->httpRequest->setUrl(\OC::$server->getRequest()->getRequestUri()); -$server->setBaseUri($baseuri); +$serverFactory = new \OC\Connector\Sabre\ServerFactory( + \OC::$server->getConfig(), + \OC::$server->getLogger(), + \OC::$server->getDatabaseConnection(), + \OC::$server->getUserSession(), + \OC::$server->getMountManager(), + \OC::$server->getTagManager() +); -// Load plugins -$defaults = new OC_Defaults(); -$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $defaults->getName())); -// FIXME: The following line is a workaround for legacy components relying on being able to send a GET to / -$server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin()); -$server->addPlugin(new \OC\Connector\Sabre\FilesPlugin($objectTree, true)); -$server->addPlugin(new \OC\Connector\Sabre\MaintenancePlugin(\OC::$server->getConfig())); -$server->addPlugin(new \OC\Connector\Sabre\ExceptionLoggerPlugin('webdav', \OC::$server->getLogger())); +$requestUri = \OC::$server->getRequest()->getRequestUri(); -// wait with registering these until auth is handled and the filesystem is setup -$server->on('beforeMethod', function () use ($server, $objectTree, $authBackend) { +$server = $serverFactory->createServer($baseuri, $requestUri, $authBackend, function () use ($authBackend) { if (OCA\Files_Sharing\Helper::isOutgoingServer2serverShareEnabled() === false) { // this is what is thrown when trying to access a non-existing share throw new \Sabre\DAV\Exception\NotAuthenticated(); @@ -72,20 +66,8 @@ $server->on('beforeMethod', function () use ($server, $objectTree, $authBackend) $ownerView = \OC\Files\Filesystem::getView(); $path = $ownerView->getPath($fileId); - $view = new \OC\Files\View($ownerView->getAbsolutePath($path)); - $rootInfo = $view->getFileInfo(''); - - // Create ownCloud Dir - if ($rootInfo->getType() === 'dir') { - $root = new \OC\Connector\Sabre\Directory($view, $rootInfo); - } else { - $root = new \OC\Connector\Sabre\File($view, $rootInfo); - } - $mountManager = \OC\Files\Filesystem::getMountManager(); - $objectTree->init($root, $view, $mountManager); - - $server->addPlugin(new \OC\Connector\Sabre\QuotaPlugin($view)); -}, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request + return new \OC\Files\View($ownerView->getAbsolutePath($path)); +}); // And off we go! $server->exec(); diff --git a/apps/files_sharing/templates/public.php b/apps/files_sharing/templates/public.php index ffe0472b2b1..2962f62520d 100644 --- a/apps/files_sharing/templates/public.php +++ b/apps/files_sharing/templates/public.php @@ -7,6 +7,7 @@ OCP\Util::addStyle('files_sharing', 'public'); OCP\Util::addStyle('files_sharing', 'mobile'); OCP\Util::addScript('files_sharing', 'public'); OCP\Util::addScript('files', 'fileactions'); +OCP\Util::addScript('files', 'fileactionsmenu'); OCP\Util::addScript('files', 'jquery.iframe-transport'); OCP\Util::addScript('files', 'jquery.fileupload'); diff --git a/apps/files_sharing/tests/capabilities.php b/apps/files_sharing/tests/capabilities.php index a7c487bf589..b0f6390b013 100644 --- a/apps/files_sharing/tests/capabilities.php +++ b/apps/files_sharing/tests/capabilities.php @@ -36,9 +36,8 @@ class FilesSharingCapabilitiesTest extends \Test\TestCase { * @return string[] */ private function getFilesSharingPart(array $data) { - $this->assertArrayHasKey('capabilities', $data); - $this->assertArrayHasKey('files_sharing', $data['capabilities']); - return $data['capabilities']['files_sharing']; + $this->assertArrayHasKey('files_sharing', $data); + return $data['files_sharing']; } /** @@ -53,7 +52,7 @@ class FilesSharingCapabilitiesTest extends \Test\TestCase { $stub = $this->getMockBuilder('\OCP\IConfig')->disableOriginalConstructor()->getMock(); $stub->method('getAppValue')->will($this->returnValueMap($map)); $cap = new Capabilities($stub); - $result = $this->getFilesSharingPart($cap->getCaps()->getData()); + $result = $this->getFilesSharingPart($cap->getCapabilities()); return $result; } diff --git a/apps/files_sharing/tests/etagpropagation.php b/apps/files_sharing/tests/etagpropagation.php index d978daf200c..8da4e6f29bd 100644 --- a/apps/files_sharing/tests/etagpropagation.php +++ b/apps/files_sharing/tests/etagpropagation.php @@ -266,15 +266,15 @@ class EtagPropagation extends TestCase { \OCP\Share::unshare( 'folder', $folderId, - \OCP\Share::SHARE_TYPE_USER, + \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2 ) ); $this->assertEtagsForFoldersChanged([ // direct recipient affected - self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER2, // reshare recipient affected - self::TEST_FILES_SHARING_API_USER4, + self::TEST_FILES_SHARING_API_USER4, ]); $this->assertAllUnchaged(); @@ -287,9 +287,9 @@ class EtagPropagation extends TestCase { ); $this->assertEtagsForFoldersChanged([ // direct recipient affected - self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER2, // reshare recipient affected - self::TEST_FILES_SHARING_API_USER4, + self::TEST_FILES_SHARING_API_USER4, ]); $this->assertAllUnchaged(); @@ -398,4 +398,13 @@ class EtagPropagation extends TestCase { $this->assertAllUnchaged(); } + + public function testRecipientUploadInDirectReshare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + Filesystem::file_put_contents('/directReshare/test.txt', 'sad'); + $this->assertEtagsNotChanged([self::TEST_FILES_SHARING_API_USER3]); + $this->assertEtagsChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchaged(); + } } diff --git a/apps/files_sharing/tests/js/appSpec.js b/apps/files_sharing/tests/js/appSpec.js index 49bca568001..133bd44f750 100644 --- a/apps/files_sharing/tests/js/appSpec.js +++ b/apps/files_sharing/tests/js/appSpec.js @@ -132,7 +132,7 @@ describe('OCA.Sharing.App tests', function() { shareOwner: 'user2' }]); - fileListIn.findFileEl('testdir').find('td a.name').click(); + fileListIn.findFileEl('testdir').find('td .nametext').click(); expect(OCA.Files.App.fileList.getCurrentDirectory()).toEqual('/somewhere/inside/subdir/testdir'); diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js index aa409285ca4..581e15caf93 100644 --- a/apps/files_sharing/tests/js/shareSpec.js +++ b/apps/files_sharing/tests/js/shareSpec.js @@ -97,7 +97,7 @@ describe('OCA.Sharing.Util tests', function() { }]); $tr = fileList.$el.find('tbody tr:first'); $action = $tr.find('.action-share'); - expect($action.hasClass('permanent')).toEqual(false); + expect($action.hasClass('permanent')).toEqual(true); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder.svg'); expect($action.find('img').length).toEqual(1); @@ -257,7 +257,7 @@ describe('OCA.Sharing.Util tests', function() { $action = fileList.$el.find('tbody tr:first .action-share'); $tr = fileList.$el.find('tr:first'); - expect($action.hasClass('permanent')).toEqual(false); + expect($action.hasClass('permanent')).toEqual(true); $tr.find('.action-share').click(); @@ -344,7 +344,7 @@ describe('OCA.Sharing.Util tests', function() { expect($tr.attr('data-share-recipients')).not.toBeDefined(); OC.Share.updateIcon('file', 1); - expect($action.hasClass('permanent')).toEqual(false); + expect($action.hasClass('permanent')).toEqual(true); }); it('keep share text after updating reshare', function() { var $action, $tr; diff --git a/apps/files_sharing/tests/sharedstorage.php b/apps/files_sharing/tests/sharedstorage.php index 7c28d0431e1..de510cf1eec 100644 --- a/apps/files_sharing/tests/sharedstorage.php +++ b/apps/files_sharing/tests/sharedstorage.php @@ -441,4 +441,43 @@ class Test_Files_Sharing_Storage extends OCA\Files_sharing\Tests\TestCase { self::loginHelper(self::TEST_FILES_SHARING_API_USER1); $this->view->unlink($this->folder); } + + public function testNameConflict() { + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + $view1 = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files'); + $view1->mkdir('foo'); + $folderInfo1 = $view1->getFileInfo('foo'); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER3); + $view3 = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files'); + $view3->mkdir('foo'); + $folderInfo2 = $view3->getFileInfo('foo'); + + // share a folder with the same name from two different users to the same user + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + + \OCP\Share::shareItem('folder', $folderInfo1['fileid'], \OCP\Share::SHARE_TYPE_GROUP, + self::TEST_FILES_SHARING_API_GROUP1, 31); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER2); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER3); + + \OCP\Share::shareItem('folder', $folderInfo2['fileid'], \OCP\Share::SHARE_TYPE_GROUP, + self::TEST_FILES_SHARING_API_GROUP1, 31); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER2); + $view2 = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); + + $this->assertTrue($view2->file_exists('/foo')); + $this->assertTrue($view2->file_exists('/foo (2)')); + + $mount = $view2->getMount('/foo'); + $this->assertInstanceOf('\OCA\Files_Sharing\SharedMount', $mount); + /** @var \OC\Files\Storage\Shared $storage */ + $storage = $mount->getStorage(); + + $source = $storage->getFile(''); + $this->assertEquals(self::TEST_FILES_SHARING_API_USER1, $source['uid_owner']); + } } diff --git a/apps/files_trashbin/appinfo/application.php b/apps/files_trashbin/appinfo/application.php new file mode 100644 index 00000000000..8d76d40f639 --- /dev/null +++ b/apps/files_trashbin/appinfo/application.php @@ -0,0 +1,37 @@ +<?php +/** + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @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_Trashbin\AppInfo; + +use OCP\AppFramework\App; + +class Application extends App { + public function __construct(array $urlParams = array()) { + parent::__construct('files_trashbin', $urlParams); + + $container = $this->getContainer(); + + /* + * Register capabilities + */ + $container->registerCapability('OCA\Files_Trashbin\Capabilities'); + } +} diff --git a/apps/files_trashbin/appinfo/routes.php b/apps/files_trashbin/appinfo/routes.php index 99a03d6b969..cf3d7b77ec2 100644 --- a/apps/files_trashbin/appinfo/routes.php +++ b/apps/files_trashbin/appinfo/routes.php @@ -1,9 +1,8 @@ <?php /** * @author Georg Ehrke <georg@owncloud.com> - * @author Joas Schilling <nickvergessen@owncloud.com> * @author Lukas Reschke <lukas@owncloud.com> - * @author Morris Jobke <hey@morrisjobke.de> + * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @copyright Copyright (c) 2015, ownCloud, Inc. @@ -22,6 +21,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + +namespace OCA\Files_Trashbin\AppInfo; + +$application = new Application(); + $this->create('core_ajax_trashbin_preview', 'ajax/preview.php') ->actionInclude('files_trashbin/ajax/preview.php'); $this->create('files_trashbin_ajax_delete', 'ajax/delete.php') @@ -33,6 +37,3 @@ $this->create('files_trashbin_ajax_list', 'ajax/list.php') $this->create('files_trashbin_ajax_undelete', 'ajax/undelete.php') ->actionInclude('files_trashbin/ajax/undelete.php'); - -// Register with the capabilities API -\OCP\API::register('get', '/cloud/capabilities', array('OCA\Files_Trashbin\Capabilities', 'getCapabilities'), 'files_trashbin', \OCP\API::USER_AUTH); diff --git a/apps/files_trashbin/command/cleanup.php b/apps/files_trashbin/command/cleanup.php index 0cc94912339..60717abac18 100644 --- a/apps/files_trashbin/command/cleanup.php +++ b/apps/files_trashbin/command/cleanup.php @@ -108,7 +108,7 @@ class CleanUp extends Command { if ($this->rootFolder->nodeExists('/' . $uid . '/files_trashbin')) { $this->rootFolder->get('/' . $uid . '/files_trashbin')->delete(); $query = $this->dbConnection->getQueryBuilder(); - $query->delete('*PREFIX*files_trash') + $query->delete('files_trash') ->where($query->expr()->eq('user', $query->createParameter('uid'))) ->setParameter('uid', $uid); $query->execute(); diff --git a/apps/files_trashbin/js/app.js b/apps/files_trashbin/js/app.js index 315349d293c..473cce88a71 100644 --- a/apps/files_trashbin/js/app.js +++ b/apps/files_trashbin/js/app.js @@ -59,7 +59,6 @@ OCA.Trashbin.App = { fileActions.registerAction({ name: 'Delete', - displayName: '', mime: 'all', permissions: OC.PERMISSION_READ, icon: function() { diff --git a/apps/files_trashbin/lib/capabilities.php b/apps/files_trashbin/lib/capabilities.php index 89b268489b5..c991cc8be65 100644 --- a/apps/files_trashbin/lib/capabilities.php +++ b/apps/files_trashbin/lib/capabilities.php @@ -2,6 +2,7 @@ /** * @author Lukas Reschke <lukas@owncloud.com> * @author Morris Jobke <hey@morrisjobke.de> + * @author Roeland Jago Douma <roeland@famdouma.nl> * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 @@ -22,25 +23,26 @@ namespace OCA\Files_Trashbin; +use OCP\Capabilities\ICapability; /** * Class Capabilities * * @package OCA\Files_Trashbin */ -class Capabilities { +class Capabilities implements ICapability { /** - * @return \OC_OCS_Result + * Return this classes capabilities + * + * @return array */ - public static function getCapabilities() { - return new \OC_OCS_Result(array( - 'capabilities' => array( - 'files' => array( - 'undelete' => true, - ), - ), - )); + public function getCapabilities() { + return [ + 'files' => [ + 'undelete' => true + ] + ]; } } diff --git a/apps/files_trashbin/lib/helper.php b/apps/files_trashbin/lib/helper.php index 42412d5d4c9..f51185712a9 100644 --- a/apps/files_trashbin/lib/helper.php +++ b/apps/files_trashbin/lib/helper.php @@ -83,7 +83,7 @@ class Helper $i = array( 'name' => $id, 'mtime' => $timestamp, - 'mimetype' => \OC_Helper::getFileNameMimeType($id), + 'mimetype' => $view->is_dir($dir . '/' . $entryName) ? 'httpd/unix-directory' : \OC_Helper::getFileNameMimeType($id), 'type' => $view->is_dir($dir . '/' . $entryName) ? 'dir' : 'file', 'directory' => ($dir === '/') ? '' : $dir, ); diff --git a/apps/files_trashbin/tests/command/cleanuptest.php b/apps/files_trashbin/tests/command/cleanuptest.php index a7400e901fa..d4cccee448e 100644 --- a/apps/files_trashbin/tests/command/cleanuptest.php +++ b/apps/files_trashbin/tests/command/cleanuptest.php @@ -43,7 +43,7 @@ class CleanUpTest extends TestCase { protected $dbConnection; /** @var string */ - protected $trashTable = '*PREFIX*files_trash'; + protected $trashTable = 'files_trash'; /** @var string */ protected $user0 = 'user0'; diff --git a/apps/files_versions/appinfo/application.php b/apps/files_versions/appinfo/application.php new file mode 100644 index 00000000000..bab36b48510 --- /dev/null +++ b/apps/files_versions/appinfo/application.php @@ -0,0 +1,37 @@ +<?php +/** + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @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_Versions\AppInfo; + +use OCP\AppFramework\App; + +class Application extends App { + public function __construct(array $urlParams = array()) { + parent::__construct('files_versions', $urlParams); + + $container = $this->getContainer(); + + /* + * Register capabilities + */ + $container->registerCapability('OCA\Files_Versions\Capabilities'); + } +} diff --git a/apps/files_versions/appinfo/routes.php b/apps/files_versions/appinfo/routes.php index 5dbed1f93eb..9bab86d9224 100644 --- a/apps/files_versions/appinfo/routes.php +++ b/apps/files_versions/appinfo/routes.php @@ -1,10 +1,10 @@ <?php /** * @author Björn Schießle <schiessle@owncloud.com> - * @author Joas Schilling <nickvergessen@owncloud.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@owncloud.com> * @author Morris Jobke <hey@morrisjobke.de> + * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Tom Needham <tom@owncloud.com> * @@ -25,6 +25,10 @@ * */ +namespace OCA\Files_Versions\AppInfo; + +$application = new Application(); + /** @var $this \OCP\Route\IRouter */ $this->create('core_ajax_versions_preview', '/preview')->action( function() { @@ -38,5 +42,3 @@ $this->create('files_versions_ajax_getVersions', 'ajax/getVersions.php') $this->create('files_versions_ajax_rollbackVersion', 'ajax/rollbackVersion.php') ->actionInclude('files_versions/ajax/rollbackVersion.php'); -// Register with the capabilities API -\OCP\API::register('get', '/cloud/capabilities', array('OCA\Files_Versions\Capabilities', 'getCapabilities'), 'files_versions', \OCP\API::USER_AUTH); diff --git a/apps/files_versions/lib/capabilities.php b/apps/files_versions/lib/capabilities.php index aea31b25240..11b98038f46 100644 --- a/apps/files_versions/lib/capabilities.php +++ b/apps/files_versions/lib/capabilities.php @@ -2,6 +2,7 @@ /** * @author Christopher Schäpers <kondou@ts.unde.re> * @author Morris Jobke <hey@morrisjobke.de> + * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Tom Needham <tom@owncloud.com> * * @copyright Copyright (c) 2015, ownCloud, Inc. @@ -23,16 +24,20 @@ namespace OCA\Files_Versions; -class Capabilities { +use OCP\Capabilities\ICapability; + +class Capabilities implements ICapability { - public static function getCapabilities() { - return new \OC_OCS_Result(array( - 'capabilities' => array( - 'files' => array( - 'versioning' => true, - ), - ), - )); + /** + * Return this classes capabilities + * + * @return array + */ + public function getCapabilities() { + return [ + 'files' => [ + 'versioning' => true + ] + ]; } - } diff --git a/apps/provisioning_api/appinfo/routes.php b/apps/provisioning_api/appinfo/routes.php index 323c8d609c7..2ee3a185dae 100644 --- a/apps/provisioning_api/appinfo/routes.php +++ b/apps/provisioning_api/appinfo/routes.php @@ -21,30 +21,45 @@ * */ -// Users +namespace OCA\Provisioning_API\AppInfo; + use OCP\API; -API::register('get', '/cloud/users', array('OCA\Provisioning_API\Users', 'getUsers'), 'provisioning_api', API::ADMIN_AUTH); -API::register('post', '/cloud/users', array('OCA\Provisioning_API\Users', 'addUser'), 'provisioning_api', API::ADMIN_AUTH); -API::register('get', '/cloud/users/{userid}', array('OCA\Provisioning_API\Users', 'getUser'), 'provisioning_api', API::USER_AUTH); -API::register('put', '/cloud/users/{userid}', array('OCA\Provisioning_API\Users', 'editUser'), 'provisioning_api', API::USER_AUTH); -API::register('delete', '/cloud/users/{userid}', array('OCA\Provisioning_API\Users', 'deleteUser'), 'provisioning_api', API::SUBADMIN_AUTH); -API::register('get', '/cloud/users/{userid}/groups', array('OCA\Provisioning_API\Users', 'getUsersGroups'), 'provisioning_api', API::USER_AUTH); -API::register('post', '/cloud/users/{userid}/groups', array('OCA\Provisioning_API\Users', 'addToGroup'), 'provisioning_api', API::SUBADMIN_AUTH); -API::register('delete', '/cloud/users/{userid}/groups', array('OCA\Provisioning_API\Users', 'removeFromGroup'), 'provisioning_api', API::SUBADMIN_AUTH); -API::register('post', '/cloud/users/{userid}/subadmins', array('OCA\Provisioning_API\Users', 'addSubAdmin'), 'provisioning_api', API::ADMIN_AUTH); -API::register('delete', '/cloud/users/{userid}/subadmins', array('OCA\Provisioning_API\Users', 'removeSubAdmin'), 'provisioning_api', API::ADMIN_AUTH); -API::register('get', '/cloud/users/{userid}/subadmins', array('OCA\Provisioning_API\Users', 'getUserSubAdminGroups'), 'provisioning_api', API::ADMIN_AUTH); +// Users +$users = new \OCA\Provisioning_API\Users( + \OC::$server->getUserManager(), + \OC::$server->getConfig(), + \OC::$server->getGroupManager(), + \OC::$server->getUserSession() +); +API::register('get', '/cloud/users', [$users, 'getUsers'], 'provisioning_api', API::ADMIN_AUTH); +API::register('post', '/cloud/users', [$users, 'addUser'], 'provisioning_api', API::ADMIN_AUTH); +API::register('get', '/cloud/users/{userid}', [$users, 'getUser'], 'provisioning_api', API::USER_AUTH); +API::register('put', '/cloud/users/{userid}', [$users, 'editUser'], 'provisioning_api', API::USER_AUTH); +API::register('delete', '/cloud/users/{userid}', [$users, 'deleteUser'], 'provisioning_api', API::SUBADMIN_AUTH); +API::register('get', '/cloud/users/{userid}/groups', [$users, 'getUsersGroups'], 'provisioning_api', API::USER_AUTH); +API::register('post', '/cloud/users/{userid}/groups', [$users, 'addToGroup'], 'provisioning_api', API::SUBADMIN_AUTH); +API::register('delete', '/cloud/users/{userid}/groups', [$users, 'removeFromGroup'], 'provisioning_api', API::SUBADMIN_AUTH); +API::register('post', '/cloud/users/{userid}/subadmins', [$users, 'addSubAdmin'], 'provisioning_api', API::ADMIN_AUTH); +API::register('delete', '/cloud/users/{userid}/subadmins', [$users, 'removeSubAdmin'], 'provisioning_api', API::ADMIN_AUTH); +API::register('get', '/cloud/users/{userid}/subadmins', [$users, 'getUserSubAdminGroups'], 'provisioning_api', API::ADMIN_AUTH); // Groups -API::register('get', '/cloud/groups', array('OCA\Provisioning_API\Groups', 'getGroups'), 'provisioning_api', API::SUBADMIN_AUTH); -API::register('post', '/cloud/groups', array('OCA\Provisioning_API\Groups', 'addGroup'), 'provisioning_api', API::SUBADMIN_AUTH); -API::register('get', '/cloud/groups/{groupid}', array('OCA\Provisioning_API\Groups', 'getGroup'), 'provisioning_api', API::SUBADMIN_AUTH); -API::register('delete', '/cloud/groups/{groupid}', array('OCA\Provisioning_API\Groups', 'deleteGroup'), 'provisioning_api', API::ADMIN_AUTH); -API::register('get', '/cloud/groups/{groupid}/subadmins', array('OCA\Provisioning_API\Groups', 'getSubAdminsOfGroup'), 'provisioning_api', API::ADMIN_AUTH); +$groups = new \OCA\Provisioning_API\Groups( + \OC::$server->getGroupManager(), + \OC::$server->getUserSession() +); +API::register('get', '/cloud/groups', [$groups, 'getGroups'], 'provisioning_api', API::SUBADMIN_AUTH); +API::register('post', '/cloud/groups', [$groups, 'addGroup'], 'provisioning_api', API::SUBADMIN_AUTH); +API::register('get', '/cloud/groups/{groupid}', [$groups, 'getGroup'], 'provisioning_api', API::SUBADMIN_AUTH); +API::register('delete', '/cloud/groups/{groupid}', [$groups, 'deleteGroup'], 'provisioning_api', API::ADMIN_AUTH); +API::register('get', '/cloud/groups/{groupid}/subadmins', [$groups, 'getSubAdminsOfGroup'], 'provisioning_api', API::ADMIN_AUTH); // Apps -API::register('get', '/cloud/apps', array('OCA\Provisioning_API\Apps', 'getApps'), 'provisioning_api', API::ADMIN_AUTH); -API::register('get', '/cloud/apps/{appid}', array('OCA\Provisioning_API\Apps', 'getAppInfo'), 'provisioning_api', API::ADMIN_AUTH); -API::register('post', '/cloud/apps/{appid}', array('OCA\Provisioning_API\Apps', 'enable'), 'provisioning_api', API::ADMIN_AUTH); -API::register('delete', '/cloud/apps/{appid}', array('OCA\Provisioning_API\Apps', 'disable'), 'provisioning_api', API::ADMIN_AUTH); +$apps = new \OCA\Provisioning_API\Apps( + \OC::$server->getAppManager() +); +API::register('get', '/cloud/apps', [$apps, 'getApps'], 'provisioning_api', API::ADMIN_AUTH); +API::register('get', '/cloud/apps/{appid}', [$apps, 'getAppInfo'], 'provisioning_api', API::ADMIN_AUTH); +API::register('post', '/cloud/apps/{appid}', [$apps, 'enable'], 'provisioning_api', API::ADMIN_AUTH); +API::register('delete', '/cloud/apps/{appid}', [$apps, 'disable'], 'provisioning_api', API::ADMIN_AUTH); diff --git a/apps/provisioning_api/lib/apps.php b/apps/provisioning_api/lib/apps.php index 22713865c1e..168f6f3cad8 100644 --- a/apps/provisioning_api/lib/apps.php +++ b/apps/provisioning_api/lib/apps.php @@ -28,7 +28,14 @@ use \OC_App; class Apps { - public static function getApps($parameters){ + /** @var \OCP\App\IAppManager */ + private $appManager; + + public function __construct(\OCP\App\IAppManager $appManager) { + $this->appManager = $appManager; + } + + public function getApps($parameters){ $apps = OC_App::listAllApps(); $list = array(); foreach($apps as $app) { @@ -55,9 +62,9 @@ class Apps { } } - public static function getAppInfo($parameters){ + public function getAppInfo($parameters){ $app = $parameters['appid']; - $info = OC_App::getAppInfo($app); + $info = \OCP\App::getAppInfo($app); if(!is_null($info)) { return new OC_OCS_Result(OC_App::getAppInfo($app)); } else { @@ -65,15 +72,15 @@ class Apps { } } - public static function enable($parameters){ + public function enable($parameters){ $app = $parameters['appid']; - OC_App::enable($app); + $this->appManager->enableApp($app); return new OC_OCS_Result(null, 100); } - public static function disable($parameters){ + public function disable($parameters){ $app = $parameters['appid']; - OC_App::disable($app); + $this->appManager->disableApp($app); return new OC_OCS_Result(null, 100); } diff --git a/apps/provisioning_api/lib/groups.php b/apps/provisioning_api/lib/groups.php index 81a5a6e5c30..91d0a1c6342 100644 --- a/apps/provisioning_api/lib/groups.php +++ b/apps/provisioning_api/lib/groups.php @@ -24,33 +24,65 @@ namespace OCA\Provisioning_API; use \OC_OCS_Result; -use \OC_Group; use \OC_SubAdmin; class Groups{ + /** @var \OCP\IGroupManager */ + private $groupManager; + + /** @var \OCP\IUserSession */ + private $userSession; + + /** + * @param \OCP\IGroupManager $groupManager + * @param \OCP\IUserSession $userSession + */ + public function __construct(\OCP\IGroupManager $groupManager, + \OCP\IUserSession $userSession) { + $this->groupManager = $groupManager; + $this->userSession = $userSession; + } + /** * returns a list of groups */ - public static function getGroups($parameters){ + public function getGroups($parameters){ $search = !empty($_GET['search']) ? $_GET['search'] : ''; $limit = !empty($_GET['limit']) ? $_GET['limit'] : null; $offset = !empty($_GET['offset']) ? $_GET['offset'] : null; - return new OC_OCS_Result(array('groups' => OC_Group::getGroups($search, $limit, $offset))); + + $groups = $this->groupManager->search($search, $limit, $offset); + $groups = array_map(function($group) { + return $group->getGID(); + }, $groups); + + return new OC_OCS_Result(['groups' => $groups]); } /** * returns an array of users in the group specified */ - public static function getGroup($parameters){ + public function getGroup($parameters) { + // Check if user is logged in + $user = $this->userSession->getUser(); + if ($user === null) { + return new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED); + } + // Check the group exists - if(!OC_Group::groupExists($parameters['groupid'])){ + if(!$this->groupManager->groupExists($parameters['groupid'])){ return new OC_OCS_Result(null, \OCP\API::RESPOND_NOT_FOUND, 'The requested group could not be found'); } // Check subadmin has access to this group - if(\OC_User::isAdminUser(\OC_User::getUser()) - || in_array($parameters['groupid'], \OC_SubAdmin::getSubAdminsGroups(\OC_User::getUser()))){ - return new OC_OCS_Result(array('users' => OC_Group::usersInGroup($parameters['groupid']))); + if($this->groupManager->isAdmin($user->getUID()) + || in_array($parameters['groupid'], \OC_SubAdmin::getSubAdminsGroups($user->getUID()))){ + $users = $this->groupManager->get($parameters['groupid'])->getUsers(); + $users = array_map(function($user) { + return $user->getUID(); + }, $users); + $users = array_values($users); + return new OC_OCS_Result(['users' => $users]); } else { return new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED, 'User does not have access to specified group'); } @@ -59,7 +91,7 @@ class Groups{ /** * creates a new group */ - public static function addGroup($parameters){ + public function addGroup($parameters){ // Validate name $groupid = isset($_POST['groupid']) ? $_POST['groupid'] : ''; if( preg_match( '/[^a-zA-Z0-9 _\.@\-]/', $groupid ) || empty($groupid)){ @@ -67,21 +99,18 @@ class Groups{ return new OC_OCS_Result(null, 101, 'Invalid group name'); } // Check if it exists - if(OC_Group::groupExists($groupid)){ + if($this->groupManager->groupExists($groupid)){ return new OC_OCS_Result(null, 102); } - if(OC_Group::createGroup($groupid)){ - return new OC_OCS_Result(null, 100); - } else { - return new OC_OCS_Result(null, 103); - } + $this->groupManager->createGroup($groupid); + return new OC_OCS_Result(null, 100); } - public static function deleteGroup($parameters){ + public function deleteGroup($parameters){ // Check it exists - if(!OC_Group::groupExists($parameters['groupid'])){ + if(!$this->groupManager->groupExists($parameters['groupid'])){ return new OC_OCS_Result(null, 101); - } else if($parameters['groupid'] == 'admin' || !OC_Group::deleteGroup($parameters['groupid'])){ + } else if($parameters['groupid'] === 'admin' || !$this->groupManager->get($parameters['groupid'])->delete()){ // Cannot delete admin group return new OC_OCS_Result(null, 102); } else { @@ -89,10 +118,10 @@ class Groups{ } } - public static function getSubAdminsOfGroup($parameters) { + public function getSubAdminsOfGroup($parameters) { $group = $parameters['groupid']; // Check group exists - if(!OC_Group::groupExists($group)) { + if(!$this->groupManager->groupExists($group)) { return new OC_OCS_Result(null, 101, 'Group does not exist'); } // Go diff --git a/apps/provisioning_api/lib/users.php b/apps/provisioning_api/lib/users.php index fada85b293d..f5b201a55ea 100644 --- a/apps/provisioning_api/lib/users.php +++ b/apps/provisioning_api/lib/users.php @@ -27,32 +27,64 @@ namespace OCA\Provisioning_API; use \OC_OCS_Result; use \OC_SubAdmin; -use \OC_User; -use \OC_Group; use \OC_Helper; use OCP\Files\NotFoundException; class Users { + /** @var \OCP\IUserManager */ + private $userManager; + + /** @var \OCP\IConfig */ + private $config; + + /** @var \OCP\IGroupManager */ + private $groupManager; + + /** @var \OCP\IUserSession */ + private $userSession; + + /** + * @param \OCP\IUserManager $userManager + * @param \OCP\IConfig $config + * @param \OCP\IGroupManager $groupManager + * @param \OCP\IUserSession $user + */ + public function __construct(\OCP\IUserManager $userManager, + \OCP\IConfig $config, + \OCP\IGroupManager $groupManager, + \OCP\IUserSession $userSession) { + $this->userManager = $userManager; + $this->config = $config; + $this->groupManager = $groupManager; + $this->userSession = $userSession; + } + /** * returns a list of users */ - public static function getUsers(){ + public function getUsers(){ $search = !empty($_GET['search']) ? $_GET['search'] : ''; $limit = !empty($_GET['limit']) ? $_GET['limit'] : null; $offset = !empty($_GET['offset']) ? $_GET['offset'] : null; - return new OC_OCS_Result(array('users' => OC_User::getUsers($search, $limit, $offset))); + + $users = $this->userManager->search($search, $limit, $offset); + $users = array_keys($users); + + return new OC_OCS_Result([ + 'users' => $users + ]); } - public static function addUser(){ + public function addUser(){ $userId = isset($_POST['userid']) ? $_POST['userid'] : null; $password = isset($_POST['password']) ? $_POST['password'] : null; - if(OC_User::userExists($userId)) { + if($this->userManager->userExists($userId)) { \OCP\Util::writeLog('ocs_api', 'Failed addUser attempt: User already exists.', \OCP\Util::ERROR); return new OC_OCS_Result(null, 102, 'User already exists'); } else { try { - OC_User::createUser($userId, $password); + $this->userManager->createUser($userId, $password); \OCP\Util::writeLog('ocs_api', 'Successful addUser call with userid: '.$_POST['userid'], \OCP\Util::INFO); return new OC_OCS_Result(null, 100); } catch (\Exception $e) { @@ -65,25 +97,32 @@ class Users { /** * gets user info */ - public static function getUser($parameters){ + public function getUser($parameters){ $userId = $parameters['userid']; + + // Check if user is logged in + $user = $this->userSession->getUser(); + if ($user === null) { + return new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED); + } + // Admin? Or SubAdmin? - if(OC_User::isAdminUser(OC_User::getUser()) || OC_SubAdmin::isUserAccessible(OC_User::getUser(), $userId)) { + if($this->groupManager->isAdmin($user->getUID()) || OC_SubAdmin::isUserAccessible($user->getUID(), $userId)) { // Check they exist - if(!OC_User::userExists($userId)) { + if(!$this->userManager->userExists($userId)) { return new OC_OCS_Result(null, \OCP\API::RESPOND_NOT_FOUND, 'The requested user could not be found'); } // Show all - $return = array( + $return = [ 'email', 'enabled', - ); - if(OC_User::getUser() != $userId) { + ]; + if($user->getUID() !== $userId) { $return[] = 'quota'; } } else { // Check they are looking up themselves - if(OC_User::getUser() != $userId) { + if($user->getUID() !== $userId) { return new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED); } // Return some additional information compared to the core route @@ -93,14 +132,12 @@ class Users { ); } - $config = \OC::$server->getConfig(); - // Find the data $data = []; $data = self::fillStorageInfo($userId, $data); - $data['enabled'] = $config->getUserValue($userId, 'core', 'enabled', 'true'); - $data['email'] = $config->getUserValue($userId, 'settings', 'email'); - $data['displayname'] = OC_User::getDisplayName($parameters['userid']); + $data['enabled'] = $this->config->getUserValue($userId, 'core', 'enabled', 'true'); + $data['email'] = $this->config->getUserValue($userId, 'settings', 'email'); + $data['displayname'] = $this->userManager->get($parameters['userid'])->getDisplayName(); // Return the appropriate data $responseData = array(); @@ -114,21 +151,28 @@ class Users { /** * edit users */ - public static function editUser($parameters){ + public function editUser($parameters){ $userId = $parameters['userid']; - if($userId === OC_User::getUser()) { + + // Check if user is logged in + $user = $this->userSession->getUser(); + if ($user === null) { + return new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED); + } + + if($userId === $user->getUID()) { // Editing self (display, email) $permittedFields[] = 'display'; $permittedFields[] = 'email'; $permittedFields[] = 'password'; // If admin they can edit their own quota - if(OC_User::isAdminUser(OC_User::getUser())) { + if($this->groupManager->isAdmin($user->getUID())) { $permittedFields[] = 'quota'; } } else { // Check if admin / subadmin - if(OC_SubAdmin::isUserAccessible(OC_User::getUser(), $userId) - || OC_User::isAdminUser(OC_User::getUser())) { + if(OC_SubAdmin::isUserAccessible($user->getUID(), $userId) + || $this->groupManager->isAdmin($user->getUID())) { // They have permissions over the user $permittedFields[] = 'display'; $permittedFields[] = 'quota'; @@ -146,7 +190,7 @@ class Users { // Process the edit switch($parameters['_put']['key']){ case 'display': - OC_User::setDisplayName($userId, $parameters['_put']['value']); + $this->userManager->get($userId)->setDisplayName($parameters['_put']['value']); break; case 'quota': $quota = $parameters['_put']['value']; @@ -154,27 +198,27 @@ class Users { if (is_numeric($quota)) { $quota = floatval($quota); } else { - $quota = OC_Helper::computerFileSize($quota); + $quota = \OCP\Util::computerFileSize($quota); } if ($quota === false) { return new OC_OCS_Result(null, 103, "Invalid quota value {$parameters['_put']['value']}"); } - if($quota == 0) { + if($quota === 0) { $quota = 'default'; - }else if($quota == -1){ + }else if($quota === -1){ $quota = 'none'; } else { - $quota = OC_Helper::humanFileSize($quota); + $quota = \OCP\Util::humanFileSize($quota); } } - \OC::$server->getConfig()->setUserValue($userId, 'files', 'quota', $quota); + $this->config->setUserValue($userId, 'files', 'quota', $quota); break; case 'password': - OC_User::setPassword($userId, $parameters['_put']['value']); + $this->userManager->get($userId)->setPassword($parameters['_put']['value']); break; case 'email': if(filter_var($parameters['_put']['value'], FILTER_VALIDATE_EMAIL)) { - \OC::$server->getConfig()->setUserValue($userId, 'settings', 'email', $parameters['_put']['value']); + $this->config->setUserValue($userId, 'settings', 'email', $parameters['_put']['value']); } else { return new OC_OCS_Result(null, 102); } @@ -186,32 +230,53 @@ class Users { return new OC_OCS_Result(null, 100); } - public static function deleteUser($parameters){ - if(!OC_User::userExists($parameters['userid']) - || $parameters['userid'] === OC_User::getUser()) { + public function deleteUser($parameters){ + // Check if user is logged in + $user = $this->userSession->getUser(); + if ($user === null) { + return new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED); + } + + if(!$this->userManager->userExists($parameters['userid']) + || $parameters['userid'] === $user->getUID()) { return new OC_OCS_Result(null, 101); } // If not permitted - if(!OC_User::isAdminUser(OC_User::getUser()) && !OC_SubAdmin::isUserAccessible(OC_User::getUser(), $parameters['userid'])) { + if(!$this->groupManager->isAdmin($user->getUID()) && !OC_SubAdmin::isUserAccessible($user->getUID(), $parameters['userid'])) { return new OC_OCS_Result(null, 997); } // Go ahead with the delete - if(OC_User::deleteUser($parameters['userid'])) { + if($this->userManager->get($parameters['userid'])->delete()) { return new OC_OCS_Result(null, 100); } else { return new OC_OCS_Result(null, 101); } } - public static function getUsersGroups($parameters){ - if($parameters['userid'] === OC_User::getUser() || OC_User::isAdminUser(OC_User::getUser())) { + public function getUsersGroups($parameters) { + // Check if user is logged in + $user = $this->userSession->getUser(); + if ($user === null) { + return new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED); + } + + if($parameters['userid'] === $user->getUID() || $this->groupManager->isAdmin($user->getUID())) { // Self lookup or admin lookup - return new OC_OCS_Result(array('groups' => OC_Group::getUserGroups($parameters['userid']))); + return new OC_OCS_Result([ + 'groups' => $this->groupManager->getUserGroupIds( + $this->userManager->get($parameters['userid']) + ) + ]); } else { // Looking up someone else - if(OC_SubAdmin::isUserAccessible(OC_User::getUser(), $parameters['userid'])) { + if(OC_SubAdmin::isUserAccessible($user->getUID(), $parameters['userid'])) { // Return the group that the method caller is subadmin of for the user in question - $groups = array_intersect(OC_SubAdmin::getSubAdminsGroups(OC_User::getUser()), OC_Group::getUserGroups($parameters['userid'])); + $groups = array_intersect( + OC_SubAdmin::getSubAdminsGroups($user->getUID()), + $this->groupManager->getUserGroupIds( + $this->userManager->get($parameters['userid']) + ) + ); return new OC_OCS_Result(array('groups' => $groups)); } else { // Not permitted @@ -221,78 +286,96 @@ class Users { } - public static function addToGroup($parameters){ + public function addToGroup($parameters){ + // Check if user is logged in + $user = $this->userSession->getUser(); + if ($user === null) { + return new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED); + } + $group = !empty($_POST['groupid']) ? $_POST['groupid'] : null; if(is_null($group)){ return new OC_OCS_Result(null, 101); } // Check they're an admin - if(!OC_Group::inGroup(OC_User::getUser(), 'admin')){ + if(!$this->groupManager->isInGroup($user->getUID(), 'admin')){ // This user doesn't have rights to add a user to this group return new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED); } // Check if the group exists - if(!OC_Group::groupExists($group)){ + if(!$this->groupManager->groupExists($group)){ return new OC_OCS_Result(null, 102); } // Check if the user exists - if(!OC_User::userExists($parameters['userid'])){ + if(!$this->userManager->userExists($parameters['userid'])){ return new OC_OCS_Result(null, 103); } // Add user to group - return OC_Group::addToGroup($parameters['userid'], $group) ? new OC_OCS_Result(null, 100) : new OC_OCS_Result(null, 105); + $this->groupManager->get($group)->addUser( + $this->userManager->get($parameters['userid']) + ); + return new OC_OCS_Result(null, 100); } - public static function removeFromGroup($parameters){ + public function removeFromGroup($parameters) { + // Check if user is logged in + $user = $this->userSession->getUser(); + if ($user === null) { + return new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED); + } + $group = !empty($parameters['_delete']['groupid']) ? $parameters['_delete']['groupid'] : null; if(is_null($group)){ return new OC_OCS_Result(null, 101); } // If they're not an admin, check they are a subadmin of the group in question - if(!OC_Group::inGroup(OC_User::getUser(), 'admin') && !OC_SubAdmin::isSubAdminofGroup(OC_User::getUser(), $group)){ + if(!$this->groupManager->isInGroup($user->getUID(), 'admin') && !OC_SubAdmin::isSubAdminofGroup($user->getUID(), $group)){ return new OC_OCS_Result(null, 104); } // Check they aren't removing themselves from 'admin' or their 'subadmin; group - if($parameters['userid'] === OC_User::getUser()){ - if(OC_Group::inGroup(OC_User::getUser(), 'admin')){ + if($parameters['userid'] === $user->getUID()){ + if($this->groupManager->isInGroup($user->getUID(), 'admin')){ if($group === 'admin'){ return new OC_OCS_Result(null, 105, 'Cannot remove yourself from the admin group'); } } else { // Not an admin, check they are not removing themself from their subadmin group - if(in_array($group, OC_SubAdmin::getSubAdminsGroups(OC_User::getUser()))){ + if(in_array($group, OC_SubAdmin::getSubAdminsGroups($user->getUID()))){ return new OC_OCS_Result(null, 105, 'Cannot remove yourself from this group as you are a SubAdmin'); } } } // Check if the group exists - if(!OC_Group::groupExists($group)){ + if(!$this->groupManager->groupExists($group)){ return new OC_OCS_Result(null, 102); } // Check if the user exists - if(!OC_User::userExists($parameters['userid'])){ + if(!$this->userManager->userExists($parameters['userid'])){ return new OC_OCS_Result(null, 103); } // Remove user from group - return OC_Group::removeFromGroup($parameters['userid'], $group) ? new OC_OCS_Result(null, 100) : new OC_OCS_Result(null, 105); + $this->groupManager->get($group)->removeUser( + $this->userManager->get($parameters['userid']) + ); + return new OC_OCS_Result(null, 100); } /** * Creates a subadmin */ - public static function addSubAdmin($parameters) { + public function addSubAdmin($parameters) { $group = $_POST['groupid']; $user = $parameters['userid']; // Check if the user exists - if(!OC_User::userExists($user)) { + if(!$this->userManager->userExists($user)) { return new OC_OCS_Result(null, 101, 'User does not exist'); } // Check if group exists - if(!OC_Group::groupExists($group)) { + if(!$this->groupManager->groupExists($group)) { return new OC_OCS_Result(null, 102, 'Group:'.$group.' does not exist'); } // Check if trying to make subadmin of admin group - if(strtolower($group) == 'admin') { + if(strtolower($group) === 'admin') { return new OC_OCS_Result(null, 103, 'Cannot create subadmins for admin group'); } // We cannot be subadmin twice @@ -311,11 +394,11 @@ class Users { /** * Removes a subadmin from a group */ - public static function removeSubAdmin($parameters) { + public function removeSubAdmin($parameters) { $group = $parameters['_delete']['groupid']; $user = $parameters['userid']; // Check if the user exists - if(!OC_User::userExists($user)) { + if(!$this->userManager->userExists($user)) { return new OC_OCS_Result(null, 101, 'User does not exist'); } // Check if they are a subadmin of this said group @@ -333,10 +416,10 @@ class Users { /** * @Get the groups a user is a subadmin of */ - public static function getUserSubAdminGroups($parameters) { + public function getUserSubAdminGroups($parameters) { $user = $parameters['userid']; // Check if the user exists - if(!OC_User::userExists($user)) { + if(!$this->userManager->userExists($user)) { return new OC_OCS_Result(null, 101, 'User does not exist'); } // Get the subadmin groups diff --git a/apps/provisioning_api/tests/appstest.php b/apps/provisioning_api/tests/appstest.php index c4298f017fc..f2a3977eac4 100644 --- a/apps/provisioning_api/tests/appstest.php +++ b/apps/provisioning_api/tests/appstest.php @@ -25,8 +25,17 @@ namespace OCA\Provisioning_API\Tests; class AppsTest extends TestCase { + + public function setup() { + parent::setup(); + $this->appManager = \OC::$server->getAppManager(); + $this->groupManager = \OC::$server->getGroupManager(); + $this->userSession = \OC::$server->getUserSession(); + $this->api = new \OCA\Provisioning_API\Apps($this->appManager); + } + public function testGetAppInfo() { - $result = \OCA\provisioning_API\Apps::getAppInfo(array('appid' => 'provisioning_api')); + $result = $this->api->getAppInfo(['appid' => 'provisioning_api']); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); @@ -34,7 +43,7 @@ class AppsTest extends TestCase { public function testGetAppInfoOnBadAppID() { - $result = \OCA\provisioning_API\Apps::getAppInfo(array('appid' => 'not_provisioning_api')); + $result = $this->api->getAppInfo(['appid' => 'not_provisioning_api']); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); $this->assertEquals(\OCP\API::RESPOND_NOT_FOUND, $result->getStatusCode()); @@ -44,10 +53,10 @@ class AppsTest extends TestCase { public function testGetApps() { $user = $this->generateUsers(); - \OC_Group::addToGroup($user, 'admin'); - self::loginAsUser($user); + $this->groupManager->get('admin')->addUser($user); + $this->userSession->setUser($user); - $result = \OCA\provisioning_API\Apps::getApps(array()); + $result = $this->api->getApps([]); $this->assertTrue($result->succeeded()); $data = $result->getData(); @@ -58,7 +67,7 @@ class AppsTest extends TestCase { public function testGetAppsEnabled() { $_GET['filter'] = 'enabled'; - $result = \OCA\provisioning_API\Apps::getApps(array('filter' => 'enabled')); + $result = $this->api->getApps(['filter' => 'enabled']); $this->assertTrue($result->succeeded()); $data = $result->getData(); $this->assertEquals(count(\OC_App::getEnabledApps()), count($data['apps'])); @@ -68,7 +77,7 @@ class AppsTest extends TestCase { public function testGetAppsDisabled() { $_GET['filter'] = 'disabled'; - $result = \OCA\provisioning_API\Apps::getApps(array('filter' => 'disabled')); + $result = $this->api->getApps(['filter' => 'disabled']); $this->assertTrue($result->succeeded()); $data = $result->getData(); $apps = \OC_App::listAllApps(); @@ -78,6 +87,12 @@ class AppsTest extends TestCase { } $disabled = array_diff($list, \OC_App::getEnabledApps()); $this->assertEquals(count($disabled), count($data['apps'])); + } + public function testGetAppsInvalidFilter() { + $_GET['filter'] = 'foo'; + $result = $this->api->getApps([]); + $this->assertFalse($result->succeeded()); + $this->assertEquals(101, $result->getStatusCode()); } } diff --git a/apps/provisioning_api/tests/groupstest.php b/apps/provisioning_api/tests/groupstest.php index b8b02790698..73044e33120 100644 --- a/apps/provisioning_api/tests/groupstest.php +++ b/apps/provisioning_api/tests/groupstest.php @@ -24,18 +24,79 @@ namespace OCA\Provisioning_API\Tests; +use OCP\IUserManager; +use OCP\IGroupManager; +use OCP\IUserSession; + class GroupsTest extends TestCase { + + /** @var IUserManager */ + protected $userManager; + + /** @var IGroupManager */ + protected $groupManager; + + /** @var IUserSession */ + protected $userSession; + + protected function setup() { + parent::setup(); + + $this->userManager = \OC::$server->getUserManager(); + $this->groupManager = \OC::$server->getGroupManager(); + $this->userSession = \OC::$server->getUserSession(); + $this->api = new \OCA\Provisioning_API\Groups( + $this->groupManager, + $this->userSession + ); + } + + public function testGetGroups() { + $groups = []; + $id = $this->getUniqueID(); + + for ($i=0; $i < 10; $i++) { + $groups[] = $this->groupManager->createGroup($id . '_' . $i); + } + + $_GET = []; + $result = $this->api->getGroups([]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertTrue($result->succeeded()); + $this->assertCount(count($this->groupManager->search('')), $result->getData()['groups']); + $this->assertContains('admin', $result->getData()['groups']); + foreach ($groups as $group) { + $this->assertContains($group->getGID(), $result->getData()['groups']); + } + + $_GET = [ + 'search' => $id, + 'limit' => 5, + 'offset' => 2 + ]; + $result = $this->api->getGroups([]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertTrue($result->succeeded()); + $this->assertCount(5, $result->getData()['groups']); + foreach (array_splice($groups, 2, 5) as $group) { + $this->assertContains($group->getGID(), $result->getData()['groups']); + } + + foreach ($groups as $group) { + $group->delete(); + } + } + public function testGetGroupAsUser() { $users = $this->generateUsers(2); - self::loginAsUser($users[0]); + $this->userSession->setUser($users[0]); - $group = $this->getUniqueID(); - \OC_Group::createGroup($group); - \OC_Group::addToGroup($users[1], $group); + $group = $this->groupManager->createGroup($this->getUniqueID()); + $group->addUser($users[1]); - $result = \OCA\provisioning_api\Groups::getGroup(array( - 'groupid' => $group, + $result = $this->api->getGroup(array( + 'groupid' => $group->getGID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); @@ -47,18 +108,17 @@ class GroupsTest extends TestCase { public function testGetGroupAsSubadmin() { $users = $this->generateUsers(2); - self::loginAsUser($users[0]); + $this->userSession->setUser($users[0]); - $group = $this->getUniqueID(); - \OC_Group::createGroup($group); - \OC_Group::addToGroup($users[0], $group); - \OC_Group::addToGroup($users[1], $group); + $group = $this->groupManager->createGroup($this->getUniqueID()); + $group->addUser($users[0]); + $group->addUser($users[1]); - \OC_SubAdmin::createSubAdmin($users[0], $group); + \OC_SubAdmin::createSubAdmin($users[0]->getUID(), $group->getGID()); - $result = \OCA\provisioning_api\Groups::getGroup(array( - 'groupid' => $group, - )); + $result = $this->api->getGroup([ + 'groupid' => $group->getGID(), + ]); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); @@ -67,6 +127,10 @@ class GroupsTest extends TestCase { $resultData = $result->getData(); $resultData = $resultData['users']; + $users = array_map(function($user) { + return $user->getUID(); + }, $users); + sort($users); sort($resultData); $this->assertEquals($users, $resultData); @@ -76,20 +140,18 @@ class GroupsTest extends TestCase { public function testGetGroupAsIrrelevantSubadmin() { $users = $this->generateUsers(2); - self::loginAsUser($users[0]); + $this->userSession->setUser($users[0]); - $group = $this->getUniqueID(); - \OC_Group::createGroup($group); - $group2 = $this->getUniqueID(); - \OC_Group::createGroup($group2); - \OC_Group::addToGroup($users[1], $group); - \OC_Group::addToGroup($users[0], $group2); + $group1 = $this->groupManager->createGroup($this->getUniqueID()); + $group2 = $this->groupManager->createGroup($this->getUniqueID()); + $group1->addUser($users[1]); + $group2->addUser($users[0]); - \OC_SubAdmin::createSubAdmin($users[0], $group2); + \OC_SubAdmin::createSubAdmin($users[0]->getUID(), $group2->getGID()); - $result = \OCA\provisioning_api\Groups::getGroup(array( - 'groupid' => $group, - )); + $result = $this->api->getGroup([ + 'groupid' => $group1->getGID(), + ]); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); @@ -100,49 +162,129 @@ class GroupsTest extends TestCase { public function testGetGroupAsAdmin() { $users = $this->generateUsers(2); - self::loginAsUser($users[0]); + $this->userSession->setUser($users[0]); - $group = $this->getUniqueID(); - \OC_Group::createGroup($group); + $group = $this->groupManager->createGroup($this->getUniqueID()); - \OC_Group::addToGroup($users[1], $group); - \OC_Group::addToGroup($users[0], 'admin'); + $group->addUser($users[1]); + $this->groupManager->get('admin')->addUser($users[0]); - $result = \OCA\provisioning_api\Groups::getGroup(array( - 'groupid' => $group, - )); + $result = $this->api->getGroup([ + 'groupid' => $group->getGID(), + ]); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); - $this->assertEquals(array('users' => array($users[1])), $result->getData()); + $this->assertEquals(['users' => [$users[1]->getUID()]], $result->getData()); + + } + + public function testGetGroupNonExisting() { + $result = $this->api->getGroup([ + 'groupid' => $this->getUniqueId() + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(\OCP\API::RESPOND_NOT_FOUND, $result->getStatusCode()); + $this->assertEquals('The requested group could not be found', $result->getMeta()['message']); } public function testGetSubAdminsOfGroup() { $user1 = $this->generateUsers(); $user2 = $this->generateUsers(); - self::loginAsUser($user1); - \OC_Group::addToGroup($user1, 'admin'); - $group1 = $this->getUniqueID(); - \OC_Group::createGroup($group1); - \OC_SubAdmin::createSubAdmin($user2, $group1); - $result = \OCA\provisioning_api\Groups::getSubAdminsOfGroup(array( - 'groupid' => $group1, - )); + $this->userSession->setUser($user1); + $this->groupManager->get('admin')->addUser($user1); + $group1 = $this->groupManager->createGroup($this->getUniqueID()); + \OC_SubAdmin::createSubAdmin($user2->getUID(), $group1->getGID()); + $result = $this->api->getSubAdminsOfGroup([ + 'groupid' => $group1->getGID(), + ]); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); $data = $result->getData(); - $this->assertEquals($user2, reset($data)); - \OC_Group::deleteGroup($group1); + $this->assertEquals($user2->getUID(), reset($data)); + $group1->delete(); $user1 = $this->generateUsers(); - self::loginAsUser($user1); - \OC_Group::addToGroup($user1, 'admin'); - $result = \OCA\provisioning_api\Groups::getSubAdminsOfGroup(array( + $this->userSession->setUser($user1); + $this->groupManager->get('admin')->addUser($user1); + $result = $this->api->getSubAdminsOfGroup([ 'groupid' => $this->getUniqueID(), - )); + ]); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); $this->assertEquals(101, $result->getStatusCode()); } + + public function testAddGroupEmptyGroup() { + $_POST = []; + $result = $this->api->addGroup([]); + + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(101, $result->getStatusCode()); + $this->assertEquals('Invalid group name', $result->getMeta()['message']); + } + + public function testAddGroupExistingGroup() { + $group = $this->groupManager->createGroup($this->getUniqueID()); + + $_POST = [ + 'groupid' => $group->getGID() + ]; + $result = $this->api->addGroup([]); + + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(102, $result->getStatusCode()); + + $group->delete(); + } + + public function testAddGroup() { + $group = $this->getUniqueId(); + + $_POST = [ + 'groupid' => $group + ]; + + $result = $this->api->addGroup([]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertTrue($result->succeeded()); + $this->assertTrue($this->groupManager->groupExists($group)); + + $this->groupManager->get($group)->delete(); + } + + public function testDeleteGroupNonExisting() { + $group = $this->getUniqueId(); + + $result = $this->api->deleteGroup([ + 'groupid' => $group + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(101, $result->getStatusCode()); + } + + public function testDeleteAdminGroup() { + $result = $this->api->deleteGroup([ + 'groupid' => 'admin' + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(102, $result->getStatusCode()); + } + + public function testDeleteGroup() { + $group = $this->groupManager->createGroup($this->getUniqueId()); + + $result = $this->api->deleteGroup([ + 'groupid' => $group->getGID() + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertTrue($result->succeeded()); + $this->assertFalse($this->groupManager->groupExists($group->getGID())); + } } diff --git a/apps/provisioning_api/tests/testcase.php b/apps/provisioning_api/tests/testcase.php index 3d0468daa12..ee7eb2a5a9a 100644 --- a/apps/provisioning_api/tests/testcase.php +++ b/apps/provisioning_api/tests/testcase.php @@ -22,12 +22,24 @@ namespace OCA\Provisioning_API\Tests; +use OCP\IUserManager; +use OCP\IGroupManager; + abstract class TestCase extends \Test\TestCase { protected $users = array(); + /** @var IUserManager */ + protected $userManager; + + /** @var IGroupManager */ + protected $groupManager; + protected function setUp() { parent::setUp(); - \OC_Group::createGroup('admin'); + + $this->userManager = \OC::$server->getUserManager(); + $this->groupManager = \OC::$server->getGroupManager(); + $this->groupManager->createGroup('admin'); } /** @@ -38,8 +50,7 @@ abstract class TestCase extends \Test\TestCase { protected function generateUsers($num = 1) { $users = array(); for ($i = 0; $i < $num; $i++) { - $user = $this->getUniqueID(); - \OC_User::createUser($user, 'password'); + $user = $this->userManager->createUser($this->getUniqueID(), 'password'); $this->users[] = $user; $users[] = $user; } @@ -48,11 +59,10 @@ abstract class TestCase extends \Test\TestCase { protected function tearDown() { foreach($this->users as $user) { - \OC_User::deleteUser($user); + $user->delete(); } - \OC_Group::deleteGroup('admin'); - + $this->groupManager->get('admin')->delete(); parent::tearDown(); } } diff --git a/apps/provisioning_api/tests/userstest.php b/apps/provisioning_api/tests/userstest.php index f2862565039..350586f8335 100644 --- a/apps/provisioning_api/tests/userstest.php +++ b/apps/provisioning_api/tests/userstest.php @@ -26,34 +26,67 @@ namespace OCA\Provisioning_API\Tests; +use OCP\IUserManager; +use OCP\IConfig; +use OCP\IGroupManager; +use OCP\IUserSession; + class UsersTest extends TestCase { + + /** @var IUserManager */ + protected $userManager; + + /** @var IConfig */ + protected $config; + + /** @var IGroupManager */ + protected $groupManager; + + /** @var IUserSession */ + protected $userSession; + protected function resetParams() { $_GET = null; $_POST = null; } + protected function setup() { + parent::setup(); + + $this->userManager = \OC::$server->getUserManager(); + $this->config = \OC::$server->getConfig(); + $this->groupManager = \OC::$server->getGroupManager(); + $this->userSession = \OC::$server->getUserSession(); + $this->api = new \OCA\Provisioning_Api\Users( + $this->userManager, + $this->config, + $this->groupManager, + $this->userSession + ); + } + // Test getting the list of users public function testGetUsers() { - $result = \OCA\provisioning_API\Users::getUsers(array()); + $result = $this->api->getUsers(); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); $count = $result->getData(); $count = count($count['users']); - $this->assertEquals(count(\OC_User::getUsers()), $count); + $this->assertEquals(count($this->userManager->search('', null, null)), $count); $user = $this->generateUsers(); - $_GET['search'] = $user; - $result = \OCA\provisioning_API\Users::getUsers(array()); + $_GET['search'] = $user->getUID(); + $result = $this->api->getUsers(); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); $data = $result->getData(); - $this->assertEquals($user, reset($data['users'])); + $this->assertEquals($user->getUID(), reset($data['users'])); // Add several users $this->generateUsers(10); $this->resetParams(); $_GET['limit'] = 2; - $result = \OCA\provisioning_API\Users::getUsers(array()); + $result = $this->api->getUsers(); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); $count = $result->getData(); @@ -63,45 +96,93 @@ class UsersTest extends TestCase { $this->resetParams(); $_GET['limit'] = 1; $_GET['offset'] = 1; - $result = \OCA\provisioning_API\Users::getUsers(array()); + $result = $this->api->getUsers(array()); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); $data = $result->getData(); - $this->assertEquals(\OC_User::getUsers('', 1, 1), $data['users']); + $this->assertEquals(array_keys($this->userManager->search('', 1, 1)), $data['users']); } public function testAddUser() { $this->resetParams(); $_POST['userid'] = $this->getUniqueID(); $_POST['password'] = 'password'; - $result = \OCA\provisioning_API\Users::addUser(array()); + $result = $this->api->addUser(); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); - $this->assertTrue(\OC_User::userExists($_POST['userid'])); - $this->assertEquals($_POST['userid'], \OC_User::checkPassword($_POST['userid'], $_POST['password'])); - $this->users[] = $_POST['userid']; + $this->assertTrue($this->userManager->userExists($_POST['userid'])); + $this->assertEquals($_POST['userid'], $this->userManager->checkPassword($_POST['userid'], $_POST['password'])->getUID()); + $this->users[] = $this->userManager->get($_POST['userid']); + } + + public function testAddUserTwice() { + $this->resetParams(); + $_POST['userid'] = $this->getUniqueID(); + $_POST['password'] = 'password'; + $this->api->addUser(); + $result = $this->api->addUser(); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(102, $result->getStatusCode()); + $this->assertEquals('User already exists', $result->getMeta()['message']); + } + + public function testAddUserFails() { + $uid = $this->getUniqueID(); + + $userManager = $this->getMockBuilder('\OCP\IUserManager') + ->disableOriginalConstructor() + ->getMock(); + + $userManager->expects($this->once()) + ->method('userExists') + ->with($uid) + ->willReturn(false); + $userManager->expects($this->once()) + ->method('createUser') + ->with($uid, 'password') + ->will($this->throwException(new \Exception)); + + $api = new \OCA\Provisioning_Api\Users( + $userManager, + $this->config, + $this->groupManager, + $this->userSession + ); + + $this->resetParams(); + $_POST['userid'] = $uid; + $_POST['password'] = 'password'; + $result = $api->addUser(); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(101, $result->getStatusCode()); + $this->assertEquals('Bad request', $result->getMeta()['message']); } public function testGetUserOnSelf() { $user = $this->generateUsers(); - self::loginAsUser($user); - $params['userid'] = $user; - $result = \OCA\provisioning_API\Users::getUser($params); + $user->setDisplayName('foobar'); + $this->userSession->setUser($user); + $params['userid'] = $user->getUID(); + $result = $this->api->getUser($params); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); $data = $result->getData(); + + $this->assertEquals('foobar', $data['displayname']); } public function testGetUserOnNonExistingUser() { $user = $this->generateUsers(); - \OC_Group::addToGroup($user, 'admin'); - self::loginAsUser($user); + $this->groupManager->get('admin')->addUser($user); + $this->userSession->setUser($user); $params = array(); $params['userid'] = $this->getUniqueID(); - while(\OC_User::userExists($params['userid'])) { + while($this->userManager->userExists($params['userid'])) { $params['userid'] = $this->getUniqueID(); } - $result = \OCA\provisioning_API\Users::getUser($params); + $result = $this->api->getUser($params); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); $this->assertEquals(\OCP\API::RESPOND_NOT_FOUND, $result->getStatusCode()); @@ -111,33 +192,32 @@ class UsersTest extends TestCase { public function testGetUserOnOtherUser() { $users = $this->generateUsers(2); $params['userid'] = $users[0]; - self::loginAsUser($users[1]); - $result = \OCA\provisioning_API\Users::getUser($params); + $this->userSession->setUser($users[1]); + $result = $this->api->getUser($params); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); // Now as as admin $users = $this->generateUsers(2); - $params['userid'] = $users[0]; + $params['userid'] = $users[0]->getUID(); // login to generate home - self::loginAsUser($users[0]); - \OC_Group::addToGroup($users[1], 'admin'); - self::loginAsUser($users[1]); - $result = \OCA\provisioning_API\Users::getUser($params); + $this->userSession->setUser($users[0]); + $this->groupManager->get('admin')->addUser($users[1]); + $this->userSession->setUser($users[1]); + $result = $this->api->getUser($params); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); $data = $result->getData(); - $this->assertEquals(\OC::$server->getConfig()->getUserValue($users[0], 'core', 'enabled', 'true'), $data['enabled']); + $this->assertEquals(\OC::$server->getConfig()->getUserValue($users[0]->getUID(), 'core', 'enabled', 'true'), $data['enabled']); } public function testEditOwnDisplayName() { - // Test editing own name $user = $this->generateUsers(); - self::loginAsUser($user); - $result = \OCA\provisioning_API\Users::editUser( + $this->userSession->setUser($user); + $result = $this->api->editUser( array( - 'userid' => $user, + 'userid' => $user->getUID(), '_put' => array( 'key' => 'display', 'value' => 'newname', @@ -146,41 +226,39 @@ class UsersTest extends TestCase { ); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); - $this->assertEquals('newname', \OC_User::getDisplayName($user)); + $this->assertEquals('newname', $user->getDisplayName()); } public function testAdminEditDisplayNameOfUser() { - // Test admin editing users name $user = $this->generateUsers(); - \OC_Group::addToGroup($user, 'admin'); - self::loginAsUser($user); + $this->groupManager->get('admin')->addUser($user); + $this->userSession->setUser($user); $user2 = $this->generateUsers(); - $result = \OCA\provisioning_API\Users::editUser( - array( - 'userid' => $user2, - '_put' => array( + $result = $this->api->editUser( + [ + 'userid' => $user2->getUID(), + '_put' => [ 'key' => 'display', 'value' => 'newname', - ), - ) + ], + ] ); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); - $this->assertEquals('newname', \OC_User::getDisplayName($user2)); + $this->assertEquals('newname', $user2->getDisplayName()); } public function testUserEditOtherUserDisplayName() { - // Test editing other users name $user = $this->generateUsers(); - self::loginAsUser($user); + $this->userSession->setUser($user); $user2 = $this->generateUsers(); - $result = \OCA\provisioning_API\Users::editUser( + $result = $this->api->editUser( array( - 'userid' => $user2, + 'userid' => $user2->getUID(), '_put' => array( 'key' => 'display', 'value' => 'newname', @@ -199,11 +277,33 @@ class UsersTest extends TestCase { */ public function testEditOwnQuota($expected, $quota) { $user = $this->generateUsers(); - \OC_Group::addToGroup($user, 'admin'); - self::loginAsUser($user); - $result = \OCA\provisioning_API\Users::editUser( + $this->userSession->setUser($user); + $result = $this->api->editUser( + [ + 'userid' => $user->getUID(), + '_put' => [ + 'key' => 'quota', + 'value' => $quota, + ], + ] + ); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(997, $result->getStatusCode()); + } + + /** + * @dataProvider providesQuotas + * @param $expected + * @param $quota + */ + public function testEditOwnQuotaAsAdmin($expected, $quota) { + $user = $this->generateUsers(); + $this->groupManager->get('admin')->addUser($user); + $this->userSession->setUser($user); + $result = $this->api->editUser( [ - 'userid' => $user, + 'userid' => $user->getUID(), '_put' => [ 'key' => 'quota', 'value' => $quota, @@ -221,16 +321,18 @@ class UsersTest extends TestCase { [true, 'none'], [true, 'default'], [false, 'qwertzu'], + [true, 0], + [true, -1] ]; } public function testAdminEditOwnQuota() { $user = $this->generateUsers(); - \OC_Group::addToGroup($user, 'admin'); - self::loginAsUser($user); - $result = \OCA\provisioning_API\Users::editUser( + $this->groupManager->get('admin')->addUser($user); + $this->userSession->setUser($user); + $result = $this->api->editUser( array( - 'userid' => $user, + 'userid' => $user->getUID(), '_put' => array( 'key' => 'quota', 'value' => '20G', @@ -243,12 +345,12 @@ class UsersTest extends TestCase { public function testAdminEditOtherUserQuota() { $user = $this->generateUsers(); - \OC_Group::addToGroup($user, 'admin'); - self::loginAsUser($user); + $this->groupManager->get('admin')->addUser($user); + $this->userSession->setUser($user); $user2 = $this->generateUsers(); - $result = \OCA\provisioning_API\Users::editUser( + $result = $this->api->editUser( array( - 'userid' => $user2, + 'userid' => $user2->getUID(), '_put' => array( 'key' => 'quota', 'value' => '20G', @@ -261,11 +363,11 @@ class UsersTest extends TestCase { public function testUserEditOtherUserQuota() { $user = $this->generateUsers(); - self::loginAsUser($user); + $this->userSession->setUser($user); $user2 = $this->generateUsers(); - $result = \OCA\provisioning_API\Users::editUser( + $result = $this->api->editUser( array( - 'userid' => $user2, + 'userid' => $user2->getUID(), '_put' => array( 'key' => 'quota', 'value' => '20G', @@ -279,10 +381,10 @@ class UsersTest extends TestCase { public function testUserEditOwnEmail() { $user = $this->generateUsers(); $email = 'test@example.com'; - self::loginAsUser($user); - $result = \OCA\provisioning_API\Users::editUser( + $this->userSession->setUser($user); + $result = $this->api->editUser( array( - 'userid' => $user, + 'userid' => $user->getUID(), '_put' => array( 'key' => 'email', 'value' => $email, @@ -291,16 +393,32 @@ class UsersTest extends TestCase { ); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); - $this->assertEquals($email, \OC::$server->getConfig()->getUserValue($user, 'settings', 'email', null)); + $this->assertEquals($email, \OC::$server->getConfig()->getUserValue($user->getUID(), 'settings', 'email', null)); + } + + public function testUserEditOwnEmailInvalid() { + $user = $this->generateUsers(); + $email = 'test@example'; + $this->userSession->setUser($user); + $result = $this->api->editUser([ + 'userid' => $user->getUID(), + '_put' => [ + 'key' => 'email', + 'value' => $email, + ], + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(102, $result->getStatusCode()); } public function testUserEditOtherUserEmailAsUser() { $users = $this->generateUsers(2); $email = 'test@example.com'; - self::loginAsUser($users[0]); - $result = \OCA\provisioning_API\Users::editUser( + $this->userSession->setUser($users[0]); + $result = $this->api->editUser( array( - 'userid' => $users[1], + 'userid' => $users[1]->getUID(), '_put' => array( 'key' => 'email', 'value' => $email, @@ -314,11 +432,11 @@ class UsersTest extends TestCase { public function testUserEditOtherUserEmailAsAdmin() { $users = $this->generateUsers(2); $email = 'test@example.com'; - self::loginAsUser($users[0]); - \OC_Group::addToGroup($users[0], 'admin'); - $result = \OCA\provisioning_API\Users::editUser( + $this->userSession->setUser($users[0]); + $this->groupManager->get('admin')->addUser($users[0]); + $result = $this->api->editUser( array( - 'userid' => $users[1], + 'userid' => $users[1]->getUID(), '_put' => array( 'key' => 'email', 'value' => $email, @@ -327,14 +445,60 @@ class UsersTest extends TestCase { ); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); - $this->assertEquals($email, \OC::$server->getConfig()->getUserValue($users[1], 'settings', 'email', null)); + $this->assertEquals($email, \OC::$server->getConfig()->getUserValue($users[1]->getUID(), 'settings', 'email', null)); + } + + public function testUserEditOwnPassword() { + $user = $this->generateUsers(); + $password = 'foo'; + $this->userSession->setUser($user); + $result = $this->api->editUser([ + 'userid' => $user->getUID(), + '_put' => [ + 'key' => 'password', + 'value' => $password, + ], + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertTrue($result->succeeded()); + } + + public function testUserEditOtherUserPasswordAsUser() { + $users = $this->generateUsers(2); + $password = 'foo'; + $this->userSession->setUser($users[0]); + $result = $this->api->editUser([ + 'userid' => $users[1]->getUID(), + '_put' => [ + 'key' => 'password', + 'value' => $password, + ], + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + } + + public function testUserEditOtherUserPasswordAsAdmin() { + $users = $this->generateUsers(2); + $password = 'foo'; + $this->userSession->setUser($users[0]); + $this->groupManager->get('admin')->addUser($users[0]); + $result = $this->api->editUser([ + 'userid' => $users[1]->getUID(), + '_put' => [ + 'key' => 'password', + 'value' => $password, + ], + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertTrue($result->succeeded()); } public function testDeleteSelf() { $user = $this->generateUsers(); - self::loginAsUser($user); - $result = \OCA\provisioning_API\Users::deleteUser(array( - 'userid' => $user, + $this->userSession->setUser($user); + $result = $this->api->deleteUser(array( + 'userid' => $user->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); @@ -342,10 +506,10 @@ class UsersTest extends TestCase { public function testDeleteOtherAsUser() { $user = $this->generateUsers(); - self::loginAsUser($user); + $this->userSession->setUser($user); $user2 = $this->generateUsers(); - $result = \OCA\provisioning_API\Users::deleteUser(array( - 'userid' => $user2, + $result = $this->api->deleteUser(array( + 'userid' => $user2->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); @@ -353,48 +517,45 @@ class UsersTest extends TestCase { public function testDeleteOtherAsSubAdmin() { $user = $this->generateUsers(); - self::loginAsUser($user); + $this->userSession->setUser($user); $user2 = $this->generateUsers(); - $group = $this->getUniqueID(); - \OC_Group::createGroup($group); - \OC_Group::addToGroup($user, $group); - \OC_Group::addToGroup($user2, $group); - \OC_SubAdmin::createSubAdmin($user, $group); - $result = \OCA\provisioning_API\Users::deleteUser(array( - 'userid' => $user2, + $group = $this->groupManager->createGroup($this->getUniqueID()); + $group->addUser($user); + $group->addUser($user2); + \OC_SubAdmin::createSubAdmin($user->getUID(), $group->getGID()); + $result = $this->api->deleteUser(array( + 'userid' => $user2->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); - \OC_Group::deleteGroup($group); + $group->delete(); } public function testDeleteOtherAsIrelevantSubAdmin() { $user = $this->generateUsers(); - self::loginAsUser($user); + $this->userSession->setUser($user); $user2 = $this->generateUsers(); - $group = $this->getUniqueID(); - $group2 = $this->getUniqueID(); - \OC_Group::createGroup($group); - \OC_Group::createGroup($group2); - \OC_Group::addToGroup($user, $group); - \OC_Group::addToGroup($user2, $group2); - \OC_SubAdmin::createSubAdmin($user, $group); - $result = \OCA\provisioning_API\Users::deleteUser(array( - 'userid' => $user2, - )); + $group = $this->groupManager->createGroup($this->getUniqueID()); + $group2 = $this->groupManager->createGroup($this->getUniqueID()); + $group->addUser($user); + $group2->addUser($user2); + \OC_SubAdmin::createSubAdmin($user->getUID(), $group->getGID()); + $result = $this->api->deleteUser(array( + 'userid' => $user2->getUID(), + )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); - \OC_Group::deleteGroup($group); - \OC_Group::deleteGroup($group2); + $group->delete(); + $group2->delete(); } public function testDeleteOtherAsAdmin() { $user = $this->generateUsers(); - \OC_Group::addToGroup($user, 'admin'); - self::loginAsUser($user); + $this->groupManager->get('admin')->addUser($user); + $this->userSession->setUser($user); $user2 = $this->generateUsers(); - $result = \OCA\provisioning_API\Users::deleteUser(array( - 'userid' => $user2, + $result = $this->api->deleteUser(array( + 'userid' => $user2->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); @@ -402,288 +563,485 @@ class UsersTest extends TestCase { public function testDeleteSelfAsAdmin() { $user = $this->generateUsers(); - \OC_Group::addToGroup($user, 'admin'); - self::loginAsUser($user); - $result = \OCA\provisioning_API\Users::deleteUser(array( - 'userid' => $user, + $this->groupManager->get('admin')->addUser($user); + $this->userSession->setUser($user); + $result = $this->api->deleteUser(array( + 'userid' => $user->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); } + public function testDeleteFails() { + $user = $this->getMockBuilder('\OCP\IUser') + ->disableOriginalConstructor() + ->getMock(); + $user->expects($this->once()) + ->method('delete') + ->willReturn(false); + + $user2 = $this->getMockBuilder('\OCP\IUser') + ->disableOriginalConstructor() + ->getMock(); + $user2->expects($this->any()) + ->method('getUID') + ->willReturn('user2'); + + $userManager = $this->getMockBuilder('\OCP\IUserManager') + ->disableOriginalConstructor() + ->getMock(); + $userManager->expects($this->once()) + ->method('userExists') + ->with('user') + ->willReturn(true); + $userManager->expects($this->once()) + ->method('get') + ->with('user') + ->willReturn($user); + + $userSession = $this->getMockBuilder('\OCP\IUserSession') + ->disableOriginalConstructor() + ->getMock(); + $userSession->expects($this->once()) + ->method('getUser') + ->willReturn($user2); + + $groupManager = $this->getMockBuilder('\OCP\IGroupManager') + ->disableOriginalConstructor() + ->getMock(); + $groupManager->expects($this->once()) + ->method('isAdmin') + ->with('user2') + ->willReturn(true); + + $api = new \OCA\Provisioning_Api\Users( + $userManager, + $this->config, + $groupManager, + $userSession + ); + + $result = $api->deleteUser([ + 'userid' => 'user', + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(101, $result->getStatusCode()); + } + public function testGetUsersGroupsOnSelf() { $user = $this->generateUsers(); - self::loginAsUser($user); + $this->userSession->setUser($user); $group = $this->getUniqueID(); - \OC_Group::createGroup($group); - \OC_Group::addToGroup($user, $group); - $result = \OCA\provisioning_API\Users::getUsersGroups(array( - 'userid' => $user, + $group = $this->groupManager->createGroup($group); + $group->addUser($user); + $result = $this->api->getUsersGroups(array( + 'userid' => $user->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); $data = $result->getData(); - $this->assertEquals($group, reset($data['groups'])); + $this->assertEquals($group->getGID(), reset($data['groups'])); $this->assertEquals(1, count($data['groups'])); - \OC_Group::deleteGroup($group); + $group->delete(); } public function testGetUsersGroupOnOther() { $user1 = $this->generateUsers(); $user2 = $this->generateUsers(); - self::loginAsUser($user1); + $this->userSession->setUser($user1); $group = $this->getUniqueID(); - \OC_Group::createGroup($group); - \OC_Group::addToGroup($user2, $group); - $result = \OCA\provisioning_API\Users::getUsersGroups(array( - 'userid' => $user2, + $group = $this->groupManager->createGroup($group); + $group->addUser($user2); + $result = $this->api->getUsersGroups(array( + 'userid' => $user2->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); - \OC_Group::deleteGroup($group); + $group->delete(); } public function testGetUsersGroupOnOtherAsAdmin() { $user1 = $this->generateUsers(); - \OC_Group::addToGroup($user1, 'admin'); + $this->groupManager->get('admin')->addUser($user1); $user2 = $this->generateUsers(); - self::loginAsUser($user1); + $this->userSession->setUser($user1); $group = $this->getUniqueID(); - \OC_Group::createGroup($group); - \OC_Group::addToGroup($user2, $group); - $result = \OCA\provisioning_API\Users::getUsersGroups(array( - 'userid' => $user2, + $group = $this->groupManager->createGroup($group); + $group->addUser($user2); + $result = $this->api->getUsersGroups(array( + 'userid' => $user2->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); $data = $result->getData(); - $this->assertEquals($group, reset($data['groups'])); + $this->assertEquals($group->getGID(), reset($data['groups'])); $this->assertEquals(1, count($data['groups'])); - \OC_Group::deleteGroup($group); + $group->delete(); } public function testGetUsersGroupsOnOtherAsSubAdmin() { $user1 = $this->generateUsers(); $user2 = $this->generateUsers(); - self::loginAsUser($user1); + $this->userSession->setUser($user1); $group1 = $this->getUniqueID(); $group2 = $this->getUniqueID(); - \OC_Group::createGroup($group1); - \OC_Group::createGroup($group2); - \OC_Group::addToGroup($user2, $group1); - \OC_Group::addToGroup($user2, $group2); - \OC_Group::addToGroup($user1, $group1); - \OC_SubAdmin::createSubAdmin($user1, $group1); - $result = \OCA\provisioning_API\Users::getUsersGroups(array( - 'userid' => $user2, + $group1 = $this->groupManager->createGroup($group1); + $group2 = $this->groupManager->createGroup($group2); + $group1->addUser($user2); + $group2->addUser($user2); + $group1->addUser($user1); + \OC_SubAdmin::createSubAdmin($user1->getUID(), $group1->getGID()); + $result = $this->api->getUsersGroups(array( + 'userid' => $user2->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); $data = $result->getData(); - $this->assertEquals($group1, reset($data['groups'])); + $this->assertEquals($group1->getGID(), reset($data['groups'])); $this->assertEquals(1, count($data['groups'])); - \OC_Group::deleteGroup($group1); - \OC_Group::deleteGroup($group2); + $group1->delete(); + $group2->delete(); } public function testGetUsersGroupsOnOtherAsIrelevantSubAdmin() { $user1 = $this->generateUsers(); $user2 = $this->generateUsers(); - self::loginAsUser($user1); + $this->userSession->setUser($user1); $group1 = $this->getUniqueID(); $group2 = $this->getUniqueID(); - \OC_Group::createGroup($group1); - \OC_Group::createGroup($group2); - \OC_Group::addToGroup($user2, $group2); - \OC_Group::addToGroup($user1, $group1); - \OC_SubAdmin::createSubAdmin($user1, $group1); - $result = \OCA\provisioning_API\Users::getUsersGroups(array( - 'userid' => $user2, + $group1 = $this->groupManager->createGroup($group1); + $group2 = $this->groupManager->createGroup($group2); + $group2->addUser($user2); + $group1->addUser($user1); + \OC_SubAdmin::createSubAdmin($user1->getUID(), $group1->getGID()); + $result = $this->api->getUsersGroups(array( + 'userid' => $user2->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); - \OC_Group::deleteGroup($group1); - \OC_Group::deleteGroup($group2); + $group1->delete(); + $group2->delete(); } public function testAddToGroup() { $user = $this->generateUsers(); $group = $this->getUniqueID(); - \OC_Group::createGroup($group); - self::loginAsUser($user); - $_POST['groupid'] = $group; - $result = \OCA\provisioning_API\Users::addToGroup(array( - 'userid' => $user, + $group = $this->groupManager->createGroup($group); + $this->userSession->setUser($user); + $_POST['groupid'] = $group->getGID(); + $result = $this->api->addToGroup(array( + 'userid' => $user->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); - $this->assertFalse(\OC_Group::inGroup($user, $group)); - \OC_Group::deleteGroup($group); + $this->assertFalse($group->inGroup($user)); + $group->delete(); } public function testAddToGroupAsAdmin() { $user = $this->generateUsers(); - \OC_Group::addToGroup($user, 'admin'); + $this->groupManager->get('admin')->addUser($user); $group = $this->getUniqueID(); - \OC_Group::createGroup($group); + $group = $this->groupManager->createGroup($group); $user2 = $this->generateUsers(); - self::loginAsUser($user); - $_POST['groupid'] = $group; - $result = \OCA\provisioning_API\Users::addToGroup(array( - 'userid' => $user2, + $this->userSession->setUser($user); + $_POST['groupid'] = $group->getGID(); + $result = $this->api->addToGroup(array( + 'userid' => $user2->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); - $this->assertTrue(\OC_Group::inGroup($user2, $group)); - \OC_Group::deleteGroup($group); + $this->assertTrue($group->inGroup($user2)); + $group->delete(); } public function testAddToGroupAsSubAdmin() { $user1 = $this->generateUsers(); $user2 = $this->generateUsers(); - self::loginAsUser($user1); + $this->userSession->setUser($user1); $group1 = $this->getUniqueID(); - \OC_Group::createGroup($group1); - \OC_SubAdmin::createSubAdmin($user1, $group1); - $_POST['groupid'] = $group1; - $result = \OCA\provisioning_API\Users::addToGroup(array( - 'userid' => $user2, + $group1 = $this->groupManager->createGroup($group1); + \OC_SubAdmin::createSubAdmin($user1->getUID(), $group1->getGID()); + $_POST['groupid'] = $group1->getGID(); + $result = $this->api->addToGroup(array( + 'userid' => $user2->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); - $this->assertFalse(\OC_Group::inGroup($user2, $group1)); - \OC_Group::deleteGroup($group1); + $this->assertFalse($group1->inGroup($user2)); + $group1->delete(); } public function testAddToGroupAsIrelevantSubAdmin() { $user1 = $this->generateUsers(); $user2 = $this->generateUsers(); - self::loginAsUser($user1); + $this->userSession->setUser($user1); $group1 = $this->getUniqueID(); $group2 = $this->getUniqueID(); - \OC_Group::createGroup($group1); - \OC_Group::createGroup($group2); - \OC_SubAdmin::createSubAdmin($user1, $group1); - $_POST['groupid'] = $group2; - $result = \OCA\provisioning_API\Users::addToGroup(array( - 'userid' => $user2, + $group1 = $this->groupManager->createGroup($group1); + $group2 = $this->groupManager->createGroup($group2); + \OC_SubAdmin::createSubAdmin($user1->getUID(), $group1->getGID()); + $_POST['groupid'] = $group2->getGID(); + $result = $this->api->addToGroup(array( + 'userid' => $user2->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); - $this->assertFalse(\OC_Group::inGroup($user2, $group2)); - \OC_Group::deleteGroup($group1); - \OC_Group::deleteGroup($group2); + $this->assertFalse($group2->inGroup($user2)); + $group1->delete(); + $group2->delete(); + } + + public function testAddToGroupNoGroupId() { + $_POST['groupid'] = ''; + $result = $this->api->addToGroup([ + 'userid' => $this->getUniqueID(), + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(101, $result->getStatusCode()); + } + + public function testAddToNonExistingGroup() { + $user = $this->generateUsers(); + $this->groupManager->get('admin')->addUser($user); + $this->userSession->setUser($user); + + $group = $this->groupManager->createGroup($this->getUniqueID()); + $_POST['groupid'] = $group->getGID(); + $result = $this->api->addToGroup([ + 'userid' => $this->getUniqueID(), + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(103, $result->getStatusCode()); + } + + public function testAddNonExistingUserToGroup() { + $user = $this->generateUsers(); + $this->groupManager->get('admin')->addUser($user); + $this->userSession->setUser($user); + + $_POST['groupid'] = $this->getUniqueID(); + $result = $this->api->addToGroup([ + 'userid' => $this->getUniqueID(), + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(102, $result->getStatusCode()); } // test delete /cloud/users/{userid}/groups public function testRemoveFromGroupAsSelf() { $user1 = $this->generateUsers(); - self::loginAsUser($user1); + $this->userSession->setUser($user1); $group1 = $this->getUniqueID(); - \OC_Group::createGroup($group1); - \OC_Group::addToGroup($user1, $group1); - $result = \OCA\provisioning_api\Users::removeFromGroup(array( - 'userid' => $user1, + $group1 = $this->groupManager->createGroup($group1); + $group1->addUser($user1); + $result = $this->api->removeFromGroup(array( + 'userid' => $user1->getUID(), '_delete' => array( - 'groupid' => $group1, + 'groupid' => $group1->getGID(), ), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); - $this->assertTrue(\OC_Group::inGroup($user1, $group1)); - \OC_Group::deleteGroup($group1); + $this->assertTrue($group1->inGroup($user1)); + $group1->delete(); } public function testRemoveFromGroupAsAdmin() { $user1 = $this->generateUsers(); $user2 = $this->generateUsers(); - self::loginAsUser($user1); + $this->userSession->setUser($user1); $group1 = $this->getUniqueID(); - \OC_Group::createGroup($group1); - \OC_Group::addToGroup($user2, $group1); - \OC_Group::addToGroup($user1, 'admin'); - $result = \OCA\provisioning_api\Users::removeFromGroup(array( - 'userid' => $user2, + $group1 = $this->groupManager->createGroup($group1); + $group1->addUser($user2); + $this->groupManager->get('admin')->addUser($user1); + $result = $this->api->removeFromGroup(array( + 'userid' => $user2->getUID(), '_delete' => array( - 'groupid' => $group1, + 'groupid' => $group1->getGID(), ), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); - $this->assertFalse(\OC_Group::inGroup($user2, $group1)); - \OC_Group::deleteGroup($group1); + $this->assertFalse($group1->inGroup($user2)); + $group1->delete(); + } + + public function testRemoveSelfFromGroupAsAdmin() { + $user1 = $this->generateUsers(); + $this->userSession->setUser($user1); + $group1 = $this->groupManager->createGroup($this->getUniqueID()); + $group1->addUser($user1); + $this->groupManager->get('admin')->addUser($user1); + $result = $this->api->removeFromGroup([ + 'userid' => $user1->getUID(), + '_delete' => [ + 'groupid' => $group1->getGID(), + ], + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertTrue($result->succeeded()); + $this->assertFalse($group1->inGroup($user1)); + $group1->delete(); } public function testRemoveFromGroupAsSubAdmin() { $user1 = $this->generateUsers(); - self::loginAsUser($user1); + $this->userSession->setUser($user1); $user2 = $this->generateUsers(); $group1 = $this->getUniqueID(); - \OC_Group::createGroup($group1); - \OC_Group::addToGroup($user1, $group1); - \OC_Group::addToGroup($user2, $group1); - \OC_SubAdmin::createSubAdmin($user1, $group1); - $result = \OCA\provisioning_api\Users::removeFromGroup(array( - 'userid' => $user2, + $group1 = $this->groupManager->createGroup($group1); + $group1->addUser($user1); + $group1->addUser($user2); + \OC_SubAdmin::createSubAdmin($user1->getUID(), $group1->getGID()); + $result = $this->api->removeFromGroup(array( + 'userid' => $user2->getUID(), '_delete' => array( - 'groupid' => $group1, + 'groupid' => $group1->getGID(), ), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); - $this->assertFalse(\OC_Group::inGroup($user2, $group1)); - \OC_Group::deleteGroup($group1); + $this->assertFalse($group1->inGroup($user2)); + $group1->delete(); } public function testRemoveFromGroupAsIrelevantSubAdmin() { $user1 = $this->generateUsers(); - self::loginAsUser($user1); + $this->userSession->setUser($user1); $user2 = $this->generateUsers(); $group1 = $this->getUniqueID(); $group2 = $this->getUniqueID(); - \OC_Group::createGroup($group1); - \OC_Group::createGroup($group2); - \OC_Group::addToGroup($user1, $group1); - \OC_Group::addToGroup($user2, $group2); - \OC_SubAdmin::createSubAdmin($user1, $group1); - $result = \OCA\provisioning_api\Users::removeFromGroup(array( - 'userid' => $user2, + $group1 = $this->groupManager->createGroup($group1); + $group2 = $this->groupManager->createGroup($group2); + $group1->addUser($user1); + $group2->addUser($user2); + \OC_SubAdmin::createSubAdmin($user1->getUID(), $group1->getGID()); + $result = $this->api->removeFromGroup(array( + 'userid' => $user2->getUID(), '_delete' => array( - 'groupid' => $group2, + 'groupid' => $group2->getGID(), ), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); - $this->assertTrue(\OC_Group::inGroup($user2, $group2)); - \OC_Group::deleteGroup($group1); - \OC_Group::deleteGroup($group2); + $this->assertTrue($group2->inGroup($user2)); + $group1->delete(); + $group2->delete(); + } + + public function testRemoveFromGroupNoGroupId() { + $result = $this->api->removeFromGroup([ + '_delete' => [ + 'groupid' => '' + ], + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(101, $result->getStatusCode()); } + public function testRemoveSelfFromAdminAsAdmin() { + $user = $this->generateUsers(); + $this->userSession->setUser($user); + $this->groupManager->get('admin')->addUser($user); + + $result = $this->api->removeFromGroup([ + 'userid' => $user->getUID(), + '_delete' => [ + 'groupid' => 'admin' + ], + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(105, $result->getStatusCode()); + $this->assertEquals('Cannot remove yourself from the admin group', $result->getMeta()['message']); + } + + public function testRemoveSelfFromSubAdminGroupAsSubAdmin() { + $user = $this->generateUsers(); + $this->userSession->setUser($user); + $group = $this->groupManager->createGroup($this->getUniqueID()); + \OC_SubAdmin::createSubAdmin($user->getUID(), $group->getGID()); + + $result = $this->api->removeFromGroup([ + 'userid' => $user->getUID(), + '_delete' => [ + 'groupid' => $group->getGID() + ], + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(105, $result->getStatusCode()); + $this->assertEquals('Cannot remove yourself from this group as you are a SubAdmin', $result->getMeta()['message']); + $group->delete(); + } + + public function testRemoveFromNonExistingGroup() { + $user1 = $this->generateUsers(); + $this->userSession->setUser($user1); + $this->groupManager->get('admin')->addUser($user1); + + $user2 = $this->generateUsers(); + $result = $this->api->removeFromGroup([ + 'userid' => $user2->getUID(), + '_delete' => [ + 'groupid' => $this->getUniqueID() + ], + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(102, $result->getStatusCode()); + } + + public function testRemoveFromNonGroupNonExistingUser() { + $user = $this->generateUsers(); + $this->userSession->setUser($user); + $this->groupManager->get('admin')->addUser($user); + + $group = $this->groupManager->createGroup($this->getUniqueID()); + + $result = $this->api->removeFromGroup([ + 'userid' => $this->getUniqueID(), + '_delete' => [ + 'groupid' => $group->getGID() + ], + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(103, $result->getStatusCode()); + } + + public function testCreateSubAdmin() { $user1 = $this->generateUsers(); $user2 = $this->generateUsers(); - self::loginAsUser($user1); - \OC_Group::addToGroup($user1, 'admin'); + $this->userSession->setUser($user1); + $this->groupManager->get('admin')->addUser($user1); $group1 = $this->getUniqueID(); - \OC_Group::createGroup($group1); - $_POST['groupid'] = $group1; - $result = \OCA\provisioning_api\Users::addSubAdmin(array( - 'userid' => $user2, + $group1 = $this->groupManager->createGroup($group1); + $_POST['groupid'] = $group1->getGID(); + $result = $this->api->addSubAdmin(array( + 'userid' => $user2->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); - $this->assertTrue(\OC_SubAdmin::isSubAdminofGroup($user2, $group1)); - \OC_Group::deleteGroup($group1); + $this->assertTrue(\OC_SubAdmin::isSubAdminofGroup($user2->getUID(), $group1->getGID())); + $group1->delete(); $this->resetParams(); $user1 = $this->generateUsers(); $user2 = $this->generateUsers(); - self::loginAsUser($user1); - \OC_Group::addToGroup($user1, 'admin'); + $this->userSession->setUser($user1); + $this->groupManager->get('admin')->addUser($user1); $_POST['groupid'] = 'admin'; - $result = \OCA\provisioning_api\Users::addSubAdmin(array( - 'userid' => $user2, + $result = $this->api->addSubAdmin(array( + 'userid' => $user2->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertEquals(103, $result->getStatusCode()); @@ -692,46 +1050,58 @@ class UsersTest extends TestCase { $this->resetParams(); $user1 = $this->generateUsers(); - self::loginAsUser($user1); - \OC_Group::addToGroup($user1, 'admin'); + $this->userSession->setUser($user1); + $this->groupManager->get('admin')->addUser($user1); $group1 = $this->getUniqueID(); - \OC_Group::createGroup($group1); - $_POST['groupid'] = $group1; - $result = \OCA\provisioning_api\Users::addSubAdmin(array( + $group1 = $this->groupManager->createGroup($group1); + $_POST['groupid'] = $group1->getGID(); + $result = $this->api->addSubAdmin(array( 'userid' => $this->getUniqueID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); $this->assertEquals(101, $result->getStatusCode()); - \OC_Group::deleteGroup($group1); + $group1->delete(); + + $user1 = $this->generateUsers(); + $this->userSession->setUser($user1); + $group = $this->getUniqueID(); + $_POST['groupid'] = $group; + $result = $this->api->addSubAdmin([ + 'userid' => $user1->getUID() + ]); + $this->assertInstanceOf('OC_OCS_Result', $result); + $this->assertFalse($result->succeeded()); + $this->assertEquals(102, $result->getStatusCode()); + $this->assertEquals('Group:'.$group.' does not exist', $result->getMeta()['message']); } public function testRemoveSubAdmin() { $user1 = $this->generateUsers(); $user2 = $this->generateUsers(); - self::loginAsUser($user1); - \OC_Group::addToGroup($user1, 'admin'); + $this->userSession->setUser($user1); + $this->groupManager->get('admin')->addUser($user1); $group1 = $this->getUniqueID(); - \OC_Group::createGroup($group1); - \OC_SubAdmin::createSubAdmin($user2, $group1); - $result = \OCA\provisioning_api\Users::removeSubAdmin(array( - 'userid' => $user2, + $group1 = $this->groupManager->createGroup($group1); + \OC_SubAdmin::createSubAdmin($user2->getUID(), $group1->getGID()); + $result = $this->api->removeSubAdmin(array( + 'userid' => $user2->getUID(), '_delete' => array( - 'groupid' => $group1, + 'groupid' => $group1->getGID(), ), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); - $this->assertTrue(!\OC_SubAdmin::isSubAdminofGroup($user2, $group1)); - \OC_Group::deleteGroup($group1); + $this->assertTrue(!\OC_SubAdmin::isSubAdminofGroup($user2->getUID(), $group1->getGID())); + $group1->delete(); $user1 = $this->generateUsers(); - self::loginAsUser($user1); - \OC_Group::addToGroup($user1, 'admin'); - $result = \OCA\provisioning_api\Users::removeSubAdmin(array( + $this->userSession->setUser($user1); + $this->groupManager->get('admin')->addUser($user1); + $result = $this->api->removeSubAdmin(array( 'userid' => $this->getUniqueID(), '_delete' => array( - 'groupid' => $group1, + 'groupid' => $group1->getGID(), ), )); $this->assertInstanceOf('OC_OCS_Result', $result); @@ -742,45 +1112,44 @@ class UsersTest extends TestCase { $user1 = $this->generateUsers(); $user2 = $this->generateUsers(); - self::loginAsUser($user1); - \OC_Group::addToGroup($user1, 'admin'); + $this->userSession->setUser($user1); + $this->groupManager->get('admin')->addUser($user1); $group1 = $this->getUniqueID(); - \OC_Group::createGroup($group1); - $_POST['groupid'] = $group1; - $result = \OCA\provisioning_api\Users::removeSubAdmin(array( - 'userid' => $user2, + $group1 = $this->groupManager->createGroup($group1); + $_POST['groupid'] = $group1->getGID(); + $result = $this->api->removeSubAdmin(array( + 'userid' => $user2->getUID(), '_delete' => array( - 'groupid' => $group1, + 'groupid' => $group1->getGID(), ), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertFalse($result->succeeded()); $this->assertEquals(102, $result->getStatusCode()); - \OC_Group::deleteGroup($group1); + $group1->delete(); } public function testGetSubAdminGroups() { $user1 = $this->generateUsers(); $user2 = $this->generateUsers(); - self::loginAsUser($user1); - \OC_Group::addToGroup($user1, 'admin'); + $this->userSession->setUser($user1); + $this->groupManager->get('admin')->addUser($user1); $group1 = $this->getUniqueID(); - \OC_Group::createGroup($group1); - \OC_SubAdmin::createSubAdmin($user2, $group1); - $result = \OCA\provisioning_api\Users::getUserSubAdminGroups(array( - 'userid' => $user2, + $group1 = $this->groupManager->createGroup($group1); + \OC_SubAdmin::createSubAdmin($user2->getUID(), $group1->getGID()); + $result = $this->api->getUserSubAdminGroups(array( + 'userid' => $user2->getUID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); $data = $result->getData(); - $this->assertEquals($group1, reset($data)); - \OC_Group::deleteGroup($group1); + $this->assertEquals($group1->getGID(), reset($data)); + $group1->delete(); $user1 = $this->generateUsers(); - self::loginAsUser($user1); - \OC_Group::addToGroup($user1, 'admin'); - $group1 = $this->getUniqueID(); - $result = \OCA\provisioning_api\Users::getUserSubAdminGroups(array( + $this->userSession->setUser($user1); + $this->groupManager->get('admin')->addUser($user1); + $result = $this->api->getUserSubAdminGroups(array( 'userid' => $this->getUniqueID(), )); $this->assertInstanceOf('OC_OCS_Result', $result); @@ -791,25 +1160,25 @@ class UsersTest extends TestCase { public function testSubAdminOfGroupAlreadySubAdmin() { $user1 = $this->generateUsers(); $user2 = $this->generateUsers(); - self::loginAsUser($user1); - \OC_Group::addToGroup($user1, 'admin'); - $group1 = $this->getUniqueID(); - \OC_Group::createGroup($group1); + $this->userSession->setUser($user1); + $this->groupManager->get('admin')->addUser($user1); + $group1 = $this->groupManager->createGroup($this->getUniqueID()); //Make user2 subadmin of group1 - $_POST['groupid'] = $group1; - $result = \OCA\provisioning_api\Users::addSubAdmin([ - 'userid' => $user2, + $_POST['groupid'] = $group1->getGID(); + $result = $this->api->addSubAdmin([ + 'userid' => $user2->getUID(), ]); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); //Make user2 subadmin of group1 again - $_POST['groupid'] = $group1; - $result = \OCA\provisioning_api\Users::addSubAdmin([ - 'userid' => $user2, + $_POST['groupid'] = $group1->getGID(); + $result = $this->api->addSubAdmin([ + 'userid' => $user2->getUID(), ]); $this->assertInstanceOf('OC_OCS_Result', $result); $this->assertTrue($result->succeeded()); + $group1->delete(); } } diff --git a/apps/user_ldap/appinfo/update.php b/apps/user_ldap/appinfo/update.php new file mode 100644 index 00000000000..b904bce072e --- /dev/null +++ b/apps/user_ldap/appinfo/update.php @@ -0,0 +1,26 @@ +<?php +/** + * @author Morris Jobke <hey@morrisjobke.de> + * + * @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/> + * + */ + +$installedVersion = \OC::$server->getConfig()->getAppValue('user_ldap', 'installed_version'); + +if (version_compare($installedVersion, '0.6.1', '<')) { + \OC::$server->getConfig()->setAppValue('user_ldap', 'enforce_home_folder_naming_rule', false); +} diff --git a/apps/user_ldap/tests/integration/lib/integrationtestuserhome.php b/apps/user_ldap/tests/integration/lib/integrationtestuserhome.php new file mode 100644 index 00000000000..f34fca81c2d --- /dev/null +++ b/apps/user_ldap/tests/integration/lib/integrationtestuserhome.php @@ -0,0 +1,159 @@ +<?php +/** + * Created by PhpStorm. + * User: blizzz + * Date: 06.08.15 + * Time: 08:19 + */ + +namespace OCA\user_ldap\tests\integration\lib; + +use OCA\user_ldap\lib\user\Manager as LDAPUserManager; +use OCA\user_ldap\tests\integration\AbstractIntegrationTest; +use OCA\User_LDAP\Mapping\UserMapping; +use OCA\user_ldap\USER_LDAP; + +require_once __DIR__ . '/../../../../../lib/base.php'; + +class IntegrationTestUserHome extends AbstractIntegrationTest { + /** @var UserMapping */ + protected $mapping; + + /** @var USER_LDAP */ + protected $backend; + + /** + * prepares the LDAP environment and sets up a test configuration for + * the LDAP backend. + */ + public function init() { + require(__DIR__ . '/../setup-scripts/createExplicitUsers.php'); + parent::init(); + + $this->mapping = new UserMapping(\OC::$server->getDatabaseConnection()); + $this->mapping->clear(); + $this->access->setUserMapper($this->mapping); + $this->backend = new \OCA\user_ldap\USER_LDAP($this->access, \OC::$server->getConfig()); + } + + /** + * sets up the LDAP configuration to be used for the test + */ + protected function initConnection() { + parent::initConnection(); + $this->connection->setConfiguration([ + 'homeFolderNamingRule' => 'homeDirectory', + ]); + } + + /** + * initializes an LDAP user manager instance + * @return LDAPUserManager + */ + protected function initUserManager() { + $this->userManager = new LDAPUserManager( + \OC::$server->getConfig(), + new \OCA\user_ldap\lib\FilesystemHelper(), + new \OCA\user_ldap\lib\LogWrapper(), + \OC::$server->getAvatarManager(), + new \OCP\Image(), + \OC::$server->getDatabaseConnection() + ); + } + + /** + * homeDirectory on LDAP is empty. Return values of getHome should be + * identical to user name, following ownCloud default. + * + * @return bool + */ + protected function case1() { + \OC::$server->getConfig()->setAppValue('user_ldap', 'enforce_home_folder_naming_rule', false); + $userManager = \oc::$server->getUserManager(); + $userManager->clearBackends(); + $userManager->registerBackend($this->backend); + $users = $userManager->search('', 5, 0); + + foreach($users as $user) { + $home = $user->getHome(); + $uid = $user->getUID(); + $posFound = strpos($home, '/' . $uid); + $posExpected = strlen($home) - (strlen($uid) + 1); + if($posFound === false || $posFound !== $posExpected) { + print('"' . $user->getUID() . '" was not found in "' . $home . '" or does not end with it.' . PHP_EOL); + return false; + } + } + + return true; + } + + /** + * homeDirectory on LDAP is empty. Having the attributes set is enforced. + * + * @return bool + */ + protected function case2() { + \OC::$server->getConfig()->setAppValue('user_ldap', 'enforce_home_folder_naming_rule', true); + $userManager = \oc::$server->getUserManager(); + // clearing backends is critical, otherwise the userManager will have + // the user objects cached and the value from case1 returned + $userManager->clearBackends(); + $userManager->registerBackend($this->backend); + $users = $userManager->search('', 5, 0); + + try { + foreach ($users as $user) { + $user->getHome(); + print('User home was retrieved without throwing an Exception!' . PHP_EOL); + return false; + } + } catch (\Exception $e) { + if(strpos($e->getMessage(), 'Home dir attribute') === 0) { + return true; + } + } + + return false; + } + + /** + * homeDirectory on LDAP is set to "attr:" which is effectively empty. + * Return values of getHome should be ownCloud default. + * + * @return bool + */ + protected function case3() { + \OC::$server->getConfig()->setAppValue('user_ldap', 'enforce_home_folder_naming_rule', true); + $this->connection->setConfiguration([ + 'homeFolderNamingRule' => 'attr:', + ]); + $userManager = \oc::$server->getUserManager(); + $userManager->clearBackends(); + $userManager->registerBackend($this->backend); + $users = $userManager->search('', 5, 0); + + try { + foreach ($users as $user) { + $home = $user->getHome(); + $uid = $user->getUID(); + $posFound = strpos($home, '/' . $uid); + $posExpected = strlen($home) - (strlen($uid) + 1); + if ($posFound === false || $posFound !== $posExpected) { + print('"' . $user->getUID() . '" was not found in "' . $home . '" or does not end with it.' . PHP_EOL); + return false; + } + } + } catch (\Exception $e) { + print("Unexpected Exception: " . $e->getMessage() . PHP_EOL); + return false; + } + + return true; + } +} + +require_once(__DIR__ . '/../setup-scripts/config.php'); +$test = new IntegrationTestUserHome($host, $port, $adn, $apwd, $bdn); +$test->init(); +$test->run(); diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php index caff30a0e60..a2f4b4ee9e5 100644 --- a/apps/user_ldap/user_ldap.php +++ b/apps/user_ldap/user_ldap.php @@ -266,7 +266,8 @@ class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn if($this->access->connection->isCached($cacheKey)) { return $this->access->connection->getFromCache($cacheKey); } - if(strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0) { + if(strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0 && + $this->access->connection->homeFolderNamingRule !== 'attr:') { $attr = substr($this->access->connection->homeFolderNamingRule, strlen('attr:')); $homedir = $this->access->readAttribute( $this->access->username2dn($uid), $attr); @@ -293,6 +294,10 @@ class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn //TODO: if home directory changes, the old one needs to be removed. return $homedir; } + if($this->ocConfig->getAppValue('user_ldap', 'enforce_home_folder_naming_rule', true)) { + // a naming rule attribute is defined, but it doesn't exist for that LDAP user + throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: ' . $uid); + } } //false will apply default behaviour as defined and done by OC_User diff --git a/bower.json b/bower.json index 112a9a907cd..1fe270d9f3c 100644 --- a/bower.json +++ b/bower.json @@ -26,6 +26,7 @@ "snapjs": "~2.0.0-rc1", "strengthify": "0.4.1", "underscore": "~1.8.0", - "bootstrap": "~3.3.5" + "bootstrap": "~3.3.5", + "backbone": "~1.2.1" } } diff --git a/config/config.sample.php b/config/config.sample.php index 3b5632087f6..5c362e94250 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -1017,7 +1017,13 @@ $CONFIG = array( /** * Headers that should be trusted as client IP address in combination with - * `trusted_proxies` + * `trusted_proxies`. If the HTTP header looks like 'X-Forwarded-For', then use + * 'HTTP_X_FORWARDED_FOR' here. + * + * If set incorrectly, a client can spoof their IP address as visible to + * ownCloud, bypassing access controls and making logs useless! + * + * Defaults to 'HTTP_X_FORWARED_FOR' if unset */ 'forwarded_for_headers' => array('HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR'), diff --git a/core/avatar/avatarcontroller.php b/core/avatar/avatarcontroller.php index 95baf23f4fa..2c4be827738 100644 --- a/core/avatar/avatarcontroller.php +++ b/core/avatar/avatarcontroller.php @@ -134,6 +134,10 @@ class AvatarController extends Controller { if (isset($path)) { $path = stripslashes($path); $view = new \OC\Files\View('/'.$userId.'/files'); + if ($view->filesize($path) > 20*1024*1024) { + return new DataResponse(['data' => ['message' => $this->l->t('File is too big')]], + Http::STATUS_BAD_REQUEST); + } $fileName = $view->getLocalFile($path); } elseif (!is_null($files)) { if ( @@ -141,6 +145,10 @@ class AvatarController extends Controller { is_uploaded_file($files['tmp_name'][0]) && !\OC\Files\Filesystem::isFileBlacklisted($files['tmp_name'][0]) ) { + if ($files['size'][0] > 20*1024*1024) { + return new DataResponse(['data' => ['message' => $this->l->t('File is too big')]], + Http::STATUS_BAD_REQUEST); + } $this->cache->set('avatar_upload', file_get_contents($files['tmp_name'][0]), 7200); $view = new \OC\Files\View('/'.$userId.'/cache'); $fileName = $view->getLocalFile('avatar_upload'); diff --git a/core/command/maintenance/install.php b/core/command/maintenance/install.php index 2fea5add438..7f5d9cae647 100644 --- a/core/command/maintenance/install.php +++ b/core/command/maintenance/install.php @@ -61,7 +61,10 @@ class Install extends Command { protected function execute(InputInterface $input, OutputInterface $output) { // validate the environment - $setupHelper = new Setup($this->config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'), new \OC_Defaults()); + $server = \OC::$server; + $setupHelper = new Setup($this->config, $server->getIniWrapper(), + $server->getL10N('lib'), new \OC_Defaults(), $server->getLogger(), + $server->getSecureRandom()); $sysInfo = $setupHelper->getSystemInfo(true); $errors = $sysInfo['errors']; if (count($errors) > 0) { diff --git a/core/css/apps.css b/core/css/apps.css index 57133729f15..300b186bba2 100644 --- a/core/css/apps.css +++ b/core/css/apps.css @@ -292,13 +292,13 @@ list-style-type: none; } +.bubble, #app-navigation .app-navigation-entry-menu { - display: none; position: absolute; background-color: #eee; color: #333; border-radius: 3px; - border-top-right-radius: 0px; + border-top-right-radius: 0; z-index: 110; margin: -5px 14px 5px 10px; right: 0; @@ -310,11 +310,17 @@ filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75)); } +#app-navigation .app-navigation-entry-menu { + display: none; +} + #app-navigation .app-navigation-entry-menu.open { display: block; } /* miraculous border arrow stuff */ +.bubble:after, +.bubble:before, #app-navigation .app-navigation-entry-menu:after, #app-navigation .app-navigation-entry-menu:before { bottom: 100%; @@ -327,12 +333,15 @@ pointer-events: none; } +.bubble:after, #app-navigation .app-navigation-entry-menu:after { border-color: rgba(238, 238, 238, 0); border-bottom-color: #eee; border-width: 10px; margin-left: -10px; } + +.bubble:before, #app-navigation .app-navigation-entry-menu:before { border-color: rgba(187, 187, 187, 0); border-bottom-color: #bbb; @@ -417,7 +426,39 @@ min-height: 100%; } +/* APP-SIDEBAR ----------------------------------------------------------------*/ +/* + Sidebar: a sidebar to be used within #app-content + have it as first element within app-content in order to shrink other + sibling containers properly. Compare Files app for example. +*/ +#app-sidebar { + position: fixed; + top: 45px; + right: 0; + left: auto; + bottom: 0; + width: 27%; + display: block; + background: #eee; + -webkit-transition: margin-right 300ms; + -moz-transition: margin-right 300ms; + -o-transition: margin-right 300ms; + transition: margin-right 300ms; + overflow-x: hidden; + overflow-y: auto; + visibility: visible; + z-index: 500; +} + +#app-content.with-app-sidebar { + margin-right: 27%; +} + +#app-sidebar.disappear { + visibility: hidden; +} /* APP-SETTINGS ---------------------------------------------------------------*/ @@ -556,3 +597,50 @@ em { padding:16px; } +/* generic tab styles */ +.tabHeaders { + margin: 15px; + background-color: #1D2D44; +} + +.tabHeaders .tabHeader { + float: left; + border: 1px solid #ddd; + padding: 5px; + cursor: pointer; + background-color: #f8f8f8; + font-weight: bold; +} +.tabHeaders .tabHeader, .tabHeaders .tabHeader a { + color: #888; +} + +.tabHeaders .tabHeader:first-child { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} + +.tabHeaders .tabHeader:last-child { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +.tabHeaders .tabHeader.selected, +.tabHeaders .tabHeader:hover { + background-color: #e8e8e8; +} + +.tabHeaders .tabHeader.selected, +.tabHeaders .tabHeader.selected a, +.tabHeaders .tabHeader:hover, +.tabHeaders .tabHeader:hover a { + color: #000; +} + +.tabsContainer { + clear: left; +} + +.tabsContainer .tab { + padding: 15px; +} diff --git a/core/css/mobile.css b/core/css/mobile.css index 80217d7069c..2256d821d73 100644 --- a/core/css/mobile.css +++ b/core/css/mobile.css @@ -103,6 +103,10 @@ z-index: 1000; } +#app-sidebar{ + width: 100%; +} + /* allow horizontal scrollbar in settings otherwise user management is not usable on mobile */ #body-settings #app-content { diff --git a/core/js/apps.js b/core/js/apps.js index 71170bbc23a..d0d351f5147 100644 --- a/core/js/apps.js +++ b/core/js/apps.js @@ -21,6 +21,26 @@ }; /** + * Shows the #app-sidebar and add .with-app-sidebar to subsequent siblings + */ + exports.Apps.showAppSidebar = function() { + var $appSidebar = $('#app-sidebar'); + $appSidebar.removeClass('disappear') + $('#app-content').addClass('with-app-sidebar'); + + }; + + /** + * Shows the #app-sidebar and removes .with-app-sidebar from subsequent + * siblings + */ + exports.Apps.hideAppSidebar = function() { + var $appSidebar = $('#app-sidebar'); + $appSidebar.addClass('disappear'); + $('#app-content').removeClass('with-app-sidebar'); + }; + + /** * Provides a way to slide down a target area through a button and slide it * up if the user clicks somewhere else. Used for the news app settings and * add new field. diff --git a/core/js/core.json b/core/js/core.json index 0f052b798a9..a67491c4a35 100644 --- a/core/js/core.json +++ b/core/js/core.json @@ -7,7 +7,8 @@ "moment/min/moment-with-locales.js", "handlebars/handlebars.js", "blueimp-md5/js/md5.js", - "bootstrap/js/tooltip.js" + "bootstrap/js/tooltip.js", + "backbone/backbone.js" ], "libraries": [ "jquery-showpassword.js", @@ -19,7 +20,9 @@ "jquery.ocdialog.js", "oc-dialogs.js", "js.js", + "oc-backbone.js", "l10n.js", + "apps.js", "share.js", "octemplate.js", "eventsource.js", diff --git a/core/js/js.js b/core/js/js.js index 8380d56e31e..89bb9a71430 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -571,21 +571,20 @@ var OC={ * @todo Write documentation */ registerMenu: function($toggle, $menuEl) { + var self = this; $menuEl.addClass('menu'); $toggle.on('click.menu', function(event) { // prevent the link event (append anchor to URL) event.preventDefault(); if ($menuEl.is(OC._currentMenu)) { - $menuEl.slideUp(OC.menuSpeed); - OC._currentMenu = null; - OC._currentMenuToggle = null; + self.hideMenus(); return; } // another menu was open? else if (OC._currentMenu) { // close it - OC._currentMenu.hide(); + self.hideMenus(); } $menuEl.slideToggle(OC.menuSpeed); OC._currentMenu = $menuEl; @@ -599,15 +598,56 @@ var OC={ unregisterMenu: function($toggle, $menuEl) { // close menu if opened if ($menuEl.is(OC._currentMenu)) { - $menuEl.slideUp(OC.menuSpeed); - OC._currentMenu = null; - OC._currentMenuToggle = null; + this.hideMenus(); } $toggle.off('click.menu').removeClass('menutoggle'); $menuEl.removeClass('menu'); }, /** + * Hides any open menus + * + * @param {Function} complete callback when the hiding animation is done + */ + hideMenus: function(complete) { + if (OC._currentMenu) { + var lastMenu = OC._currentMenu; + OC._currentMenu.trigger(new $.Event('beforeHide')); + OC._currentMenu.slideUp(OC.menuSpeed, function() { + lastMenu.trigger(new $.Event('afterHide')); + if (complete) { + complete.apply(this, arguments); + } + }); + } + OC._currentMenu = null; + OC._currentMenuToggle = null; + }, + + /** + * Shows a given element as menu + * + * @param {Object} [$toggle=null] menu toggle + * @param {Object} $menuEl menu element + * @param {Function} complete callback when the showing animation is done + */ + showMenu: function($toggle, $menuEl, complete) { + if ($menuEl.is(OC._currentMenu)) { + return; + } + this.hideMenus(); + OC._currentMenu = $menuEl; + OC._currentMenuToggle = $toggle; + $menuEl.trigger(new $.Event('beforeShow')); + $menuEl.show(); + $menuEl.trigger(new $.Event('afterShow')); + // no animation + if (_.isFunction()) { + complete(); + } + }, + + /** * Wrapper for matchMedia * * This is makes it possible for unit tests to @@ -1256,11 +1296,8 @@ function initCore() { // don't close when clicking on the menu directly or a menu toggle return false; } - if (OC._currentMenu) { - OC._currentMenu.slideUp(OC.menuSpeed); - } - OC._currentMenu = null; - OC._currentMenuToggle = null; + + OC.hideMenus(); }); @@ -1366,13 +1403,13 @@ function initCore() { // if there is a scrollbar … if($('#app-content').get(0).scrollHeight > $('#app-content').height()) { if($(window).width() > 768) { - controlsWidth = $('#content').width() - $('#app-navigation').width() - getScrollBarWidth(); + controlsWidth = $('#content').width() - $('#app-navigation').width() - $('#app-sidebar').width() - getScrollBarWidth(); } else { controlsWidth = $('#content').width() - getScrollBarWidth(); } } else { // if there is none if($(window).width() > 768) { - controlsWidth = $('#content').width() - $('#app-navigation').width(); + controlsWidth = $('#content').width() - $('#app-navigation').width() - $('#app-sidebar').width(); } else { controlsWidth = $('#content').width(); } @@ -1609,8 +1646,38 @@ OC.Util = { } } return aa.length - bb.length; + }, + /** + * Calls the callback in a given interval until it returns true + * @param {function} callback + * @param {integer} interval in milliseconds + */ + waitFor: function(callback, interval) { + var internalCallback = function() { + if(callback() !== true) { + setTimeout(internalCallback, interval); + } + }; + + internalCallback(); + }, + /** + * Checks if a cookie with the given name is present and is set to the provided value. + * @param {string} name name of the cookie + * @param {string} value value of the cookie + * @return {boolean} true if the cookie with the given name has the given value + */ + isCookieSetToValue: function(name, value) { + var cookies = document.cookie.split(';'); + for (var i=0; i < cookies.length; i++) { + var cookie = cookies[i].split('='); + if (cookie[0].trim() === name && cookie[1].trim() === value) { + return true; + } + } + return false; } -} +}; /** * Utility class for the history API, diff --git a/core/js/oc-backbone.js b/core/js/oc-backbone.js new file mode 100644 index 00000000000..75a40979340 --- /dev/null +++ b/core/js/oc-backbone.js @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2015 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ +if(!_.isUndefined(Backbone)) { + OC.Backbone = Backbone.noConflict(); +} diff --git a/core/js/setupchecks.js b/core/js/setupchecks.js index 35f24b188fa..fd192e6563b 100644 --- a/core/js/setupchecks.js +++ b/core/js/setupchecks.js @@ -72,6 +72,16 @@ if(data.isUsedTlsLibOutdated) { messages.push(data.isUsedTlsLibOutdated); } + if(data.phpSupported && data.phpSupported.eol) { + messages.push( + t('core', 'Your PHP version ({version}) is no longer <a href="{phpLink}">supported by PHP</a>. We encourage you to upgrade your PHP version to take advantage of performance and security updates provided by PHP.', {version: data.phpSupported.version, phpLink: 'https://secure.php.net/supported-versions.php'}) + ); + } + if(!data.forwardedForHeadersWorking) { + messages.push( + t('core', 'The reverse proxy headers configuration is incorrect, or you are accessing ownCloud from a trusted proxy. If you are not accessing ownCloud from a trusted proxy, this is a security issue and can allow an attacker to spoof their IP address as visible to ownCloud. Further information can be found in our <a href="{docLink}">documentation</a>.', {docLink: data.reverseProxyDocs}) + ); + } } else { messages.push(t('core', 'Error occurred while checking server setup')); } diff --git a/core/js/share.js b/core/js/share.js index 99fd08c6411..57dd0dd6553 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -266,7 +266,6 @@ OC.Share={ if (hasShares || owner) { recipients = $tr.attr('data-share-recipients'); - action.addClass('permanent'); message = t('core', 'Shared'); // even if reshared, only show "Shared by" if (owner) { @@ -281,8 +280,7 @@ OC.Share={ } } else { - action.removeClass('permanent'); - action.html(' <span>'+ escapeHTML(t('core', 'Share'))+'</span>').prepend(img); + action.html('<span></span>').prepend(img); } if (hasLink) { image = OC.imagePath('core', 'actions/public'); diff --git a/core/js/tests/specHelper.js b/core/js/tests/specHelper.js index 29293e89bcb..dbe005ba2e9 100644 --- a/core/js/tests/specHelper.js +++ b/core/js/tests/specHelper.js @@ -121,6 +121,8 @@ window.isPhantom = /phantom/i.test(navigator.userAgent); OC.TestUtil = TestUtil; } + moment.locale('en'); + // reset plugins OC.Plugins._plugins = []; diff --git a/core/js/tests/specs/setupchecksSpec.js b/core/js/tests/specs/setupchecksSpec.js index ec8a732b4a1..d0efcf4b284 100644 --- a/core/js/tests/specs/setupchecksSpec.js +++ b/core/js/tests/specs/setupchecksSpec.js @@ -66,7 +66,12 @@ describe('OC.SetupChecks tests', function() { { 'Content-Type': 'application/json' }, - JSON.stringify({isUrandomAvailable: true, serverHasInternetConnection: false, memcacheDocs: 'https://doc.owncloud.org/server/go.php?to=admin-performance'}) + JSON.stringify({ + isUrandomAvailable: true, + serverHasInternetConnection: false, + memcacheDocs: 'https://doc.owncloud.org/server/go.php?to=admin-performance', + forwardedForHeadersWorking: true + }) ); async.done(function( data, s, x ){ @@ -83,7 +88,13 @@ describe('OC.SetupChecks tests', function() { { 'Content-Type': 'application/json' }, - JSON.stringify({isUrandomAvailable: true, serverHasInternetConnection: false, dataDirectoryProtected: false, memcacheDocs: 'https://doc.owncloud.org/server/go.php?to=admin-performance'}) + JSON.stringify({ + isUrandomAvailable: true, + serverHasInternetConnection: false, + dataDirectoryProtected: false, + memcacheDocs: 'https://doc.owncloud.org/server/go.php?to=admin-performance', + forwardedForHeadersWorking: true + }) ); async.done(function( data, s, x ){ @@ -100,7 +111,13 @@ describe('OC.SetupChecks tests', function() { { 'Content-Type': 'application/json', }, - JSON.stringify({isUrandomAvailable: true, serverHasInternetConnection: false, dataDirectoryProtected: false, isMemcacheConfigured: true}) + JSON.stringify({ + isUrandomAvailable: true, + serverHasInternetConnection: false, + dataDirectoryProtected: false, + isMemcacheConfigured: true, + forwardedForHeadersWorking: true + }) ); async.done(function( data, s, x ){ @@ -117,7 +134,14 @@ describe('OC.SetupChecks tests', function() { { 'Content-Type': 'application/json', }, - JSON.stringify({isUrandomAvailable: false, securityDocs: 'https://docs.owncloud.org/myDocs.html', serverHasInternetConnection: true, dataDirectoryProtected: true, isMemcacheConfigured: true}) + JSON.stringify({ + isUrandomAvailable: false, + securityDocs: 'https://docs.owncloud.org/myDocs.html', + serverHasInternetConnection: true, + dataDirectoryProtected: true, + isMemcacheConfigured: true, + forwardedForHeadersWorking: true + }) ); async.done(function( data, s, x ){ @@ -126,6 +150,30 @@ describe('OC.SetupChecks tests', function() { }); }); + it('should return an error if the forwarded for headers are not working', function(done) { + var async = OC.SetupChecks.checkSetup(); + + suite.server.requests[0].respond( + 200, + { + 'Content-Type': 'application/json', + }, + JSON.stringify({ + isUrandomAvailable: true, + serverHasInternetConnection: true, + dataDirectoryProtected: true, + isMemcacheConfigured: true, + forwardedForHeadersWorking: false, + reverseProxyDocs: 'https://docs.owncloud.org/foo/bar.html' + }) + ); + + async.done(function( data, s, x ){ + expect(data).toEqual(['The reverse proxy headers configuration is incorrect, or you are accessing ownCloud from a trusted proxy. If you are not accessing ownCloud from a trusted proxy, this is a security issue and can allow an attacker to spoof their IP address as visible to ownCloud. Further information can be found in our <a href="https://docs.owncloud.org/foo/bar.html">documentation</a>.']); + done(); + }); + }); + it('should return an error if the response has no statuscode 200', function(done) { var async = OC.SetupChecks.checkSetup(); @@ -142,6 +190,31 @@ describe('OC.SetupChecks tests', function() { done(); }); }); + + it('should return an error if the php version is no longer supported', function(done) { + var async = OC.SetupChecks.checkSetup(); + + suite.server.requests[0].respond( + 200, + { + 'Content-Type': 'application/json', + }, + JSON.stringify({ + isUrandomAvailable: true, + securityDocs: 'https://docs.owncloud.org/myDocs.html', + serverHasInternetConnection: true, + dataDirectoryProtected: true, + isMemcacheConfigured: true, + forwardedForHeadersWorking: true, + phpSupported: {eol: true, version: '5.4.0'} + }) + ); + + async.done(function( data, s, x ){ + expect(data).toEqual(['Your PHP version (5.4.0) is no longer <a href="https://secure.php.net/supported-versions.php">supported by PHP</a>. We encourage you to upgrade your PHP version to take advantage of performance and security updates provided by PHP.']); + done(); + }); + }); }); describe('checkGeneric', function() { diff --git a/core/l10n/cs_CZ.js b/core/l10n/cs_CZ.js index df8c8036a13..3e24ec26330 100644 --- a/core/l10n/cs_CZ.js +++ b/core/l10n/cs_CZ.js @@ -13,6 +13,7 @@ OC.L10N.register( "Repair error: " : "Chyba opravy:", "Following incompatible apps have been disabled: %s" : "Následující nekompatibilní aplikace byly zakázány: %s", "Following apps have been disabled: %s" : "Následující aplikace byly vypnuty: %s", + "File is too big" : "Soubor je příliš velký", "Invalid file provided" : "Zadán neplatný soubor", "No image or file provided" : "Soubor nebo obrázek nebyl zadán", "Unknown filetype" : "Neznámý typ souboru", diff --git a/core/l10n/cs_CZ.json b/core/l10n/cs_CZ.json index c4d112e44b6..4069b611b5c 100644 --- a/core/l10n/cs_CZ.json +++ b/core/l10n/cs_CZ.json @@ -11,6 +11,7 @@ "Repair error: " : "Chyba opravy:", "Following incompatible apps have been disabled: %s" : "Následující nekompatibilní aplikace byly zakázány: %s", "Following apps have been disabled: %s" : "Následující aplikace byly vypnuty: %s", + "File is too big" : "Soubor je příliš velký", "Invalid file provided" : "Zadán neplatný soubor", "No image or file provided" : "Soubor nebo obrázek nebyl zadán", "Unknown filetype" : "Neznámý typ souboru", diff --git a/core/l10n/da.js b/core/l10n/da.js index c6f27c7124a..8c244641c34 100644 --- a/core/l10n/da.js +++ b/core/l10n/da.js @@ -13,6 +13,7 @@ OC.L10N.register( "Repair error: " : "Reparationsfejl:", "Following incompatible apps have been disabled: %s" : "Følgende inkompatible apps er blevet deaktiveret: %s", "Following apps have been disabled: %s" : "Følgende apps er blevet deaktiveret: %s", + "File is too big" : "Filen er for stor", "Invalid file provided" : "Der er angivet en ugyldig fil", "No image or file provided" : "Ingen fil eller billede givet", "Unknown filetype" : "Ukendt filtype", diff --git a/core/l10n/da.json b/core/l10n/da.json index 9688cb28ed7..0b79a2731b4 100644 --- a/core/l10n/da.json +++ b/core/l10n/da.json @@ -11,6 +11,7 @@ "Repair error: " : "Reparationsfejl:", "Following incompatible apps have been disabled: %s" : "Følgende inkompatible apps er blevet deaktiveret: %s", "Following apps have been disabled: %s" : "Følgende apps er blevet deaktiveret: %s", + "File is too big" : "Filen er for stor", "Invalid file provided" : "Der er angivet en ugyldig fil", "No image or file provided" : "Ingen fil eller billede givet", "Unknown filetype" : "Ukendt filtype", diff --git a/core/l10n/es.js b/core/l10n/es.js index 6b206f8bb96..982e5383f07 100644 --- a/core/l10n/es.js +++ b/core/l10n/es.js @@ -13,6 +13,7 @@ OC.L10N.register( "Repair error: " : "Error que reparar:", "Following incompatible apps have been disabled: %s" : "Las siguientes apps incompatibles se han deshabilitado: %s", "Following apps have been disabled: %s" : "Siguiendo aplicaciones ha sido deshabilitado: %s", + "File is too big" : "El archivo es demasiado grande", "Invalid file provided" : "Archivo inválido", "No image or file provided" : "No se especificó ningún archivo o imagen", "Unknown filetype" : "Tipo de archivo desconocido", diff --git a/core/l10n/es.json b/core/l10n/es.json index ce593dac652..bf73857e4e9 100644 --- a/core/l10n/es.json +++ b/core/l10n/es.json @@ -11,6 +11,7 @@ "Repair error: " : "Error que reparar:", "Following incompatible apps have been disabled: %s" : "Las siguientes apps incompatibles se han deshabilitado: %s", "Following apps have been disabled: %s" : "Siguiendo aplicaciones ha sido deshabilitado: %s", + "File is too big" : "El archivo es demasiado grande", "Invalid file provided" : "Archivo inválido", "No image or file provided" : "No se especificó ningún archivo o imagen", "Unknown filetype" : "Tipo de archivo desconocido", diff --git a/core/l10n/fi_FI.js b/core/l10n/fi_FI.js index 52d8364c747..fbb7098a9c3 100644 --- a/core/l10n/fi_FI.js +++ b/core/l10n/fi_FI.js @@ -13,6 +13,7 @@ OC.L10N.register( "Repair error: " : "Korjausvirhe:", "Following incompatible apps have been disabled: %s" : "Seuraavat yhteensopimattomat sovellukset on poistettu käytöstä: %s", "Following apps have been disabled: %s" : "Seuraavat sovellukset on poistettu käytöstä: %s", + "File is too big" : "Tiedosto on liian suuri", "Invalid file provided" : "Määritetty virheellinen tiedosto", "No image or file provided" : "Kuvaa tai tiedostoa ei määritelty", "Unknown filetype" : "Tuntematon tiedostotyyppi", diff --git a/core/l10n/fi_FI.json b/core/l10n/fi_FI.json index 1738aeed31f..4dc72a9bef1 100644 --- a/core/l10n/fi_FI.json +++ b/core/l10n/fi_FI.json @@ -11,6 +11,7 @@ "Repair error: " : "Korjausvirhe:", "Following incompatible apps have been disabled: %s" : "Seuraavat yhteensopimattomat sovellukset on poistettu käytöstä: %s", "Following apps have been disabled: %s" : "Seuraavat sovellukset on poistettu käytöstä: %s", + "File is too big" : "Tiedosto on liian suuri", "Invalid file provided" : "Määritetty virheellinen tiedosto", "No image or file provided" : "Kuvaa tai tiedostoa ei määritelty", "Unknown filetype" : "Tuntematon tiedostotyyppi", diff --git a/core/l10n/gl.js b/core/l10n/gl.js index 0d37512f4f5..5bf08ff8613 100644 --- a/core/l10n/gl.js +++ b/core/l10n/gl.js @@ -13,6 +13,7 @@ OC.L10N.register( "Repair error: " : "Arranxar o erro:", "Following incompatible apps have been disabled: %s" : "As seguintes aplicacións incompatíbeis foron desactivadas: %s", "Following apps have been disabled: %s" : "As seguintes aplicacións foron desactivadas: %s", + "File is too big" : "O ficheiro é grande de máis", "Invalid file provided" : "O ficheiro fornecido non é válido", "No image or file provided" : "Non forneceu ningunha imaxe ou ficheiro", "Unknown filetype" : "Tipo de ficheiro descoñecido", diff --git a/core/l10n/gl.json b/core/l10n/gl.json index b32d636ecd2..fc303761900 100644 --- a/core/l10n/gl.json +++ b/core/l10n/gl.json @@ -11,6 +11,7 @@ "Repair error: " : "Arranxar o erro:", "Following incompatible apps have been disabled: %s" : "As seguintes aplicacións incompatíbeis foron desactivadas: %s", "Following apps have been disabled: %s" : "As seguintes aplicacións foron desactivadas: %s", + "File is too big" : "O ficheiro é grande de máis", "Invalid file provided" : "O ficheiro fornecido non é válido", "No image or file provided" : "Non forneceu ningunha imaxe ou ficheiro", "Unknown filetype" : "Tipo de ficheiro descoñecido", diff --git a/core/l10n/it.js b/core/l10n/it.js index 0651fca572a..67438dc4c98 100644 --- a/core/l10n/it.js +++ b/core/l10n/it.js @@ -13,6 +13,7 @@ OC.L10N.register( "Repair error: " : "Errore di riparazione:", "Following incompatible apps have been disabled: %s" : "Le seguenti applicazioni incompatibili sono state disabilitate: %s", "Following apps have been disabled: %s" : "Le seguenti applicazioni sono state disabilitate: %s", + "File is too big" : "Il file è troppo grande", "Invalid file provided" : "File non valido fornito", "No image or file provided" : "Non è stata fornita alcun immagine o file", "Unknown filetype" : "Tipo di file sconosciuto", diff --git a/core/l10n/it.json b/core/l10n/it.json index e89883fbedf..05f260041d8 100644 --- a/core/l10n/it.json +++ b/core/l10n/it.json @@ -11,6 +11,7 @@ "Repair error: " : "Errore di riparazione:", "Following incompatible apps have been disabled: %s" : "Le seguenti applicazioni incompatibili sono state disabilitate: %s", "Following apps have been disabled: %s" : "Le seguenti applicazioni sono state disabilitate: %s", + "File is too big" : "Il file è troppo grande", "Invalid file provided" : "File non valido fornito", "No image or file provided" : "Non è stata fornita alcun immagine o file", "Unknown filetype" : "Tipo di file sconosciuto", diff --git a/core/l10n/pt_BR.js b/core/l10n/pt_BR.js index 08cda274ab7..3d4843bcb81 100644 --- a/core/l10n/pt_BR.js +++ b/core/l10n/pt_BR.js @@ -13,6 +13,7 @@ OC.L10N.register( "Repair error: " : "Reparação de erro:", "Following incompatible apps have been disabled: %s" : "Seguir aplicativos incompatíveis foi desativado: %s", "Following apps have been disabled: %s" : "Os seguintes aplicativos foram desabilitados: %s", + "File is too big" : "O arquivo é muito grande", "Invalid file provided" : "Arquivo fornecido inválido", "No image or file provided" : "Nenhuma imagem ou arquivo fornecido", "Unknown filetype" : "Tipo de arquivo desconhecido", diff --git a/core/l10n/pt_BR.json b/core/l10n/pt_BR.json index ddce0d164c1..d4e2fe1fd3f 100644 --- a/core/l10n/pt_BR.json +++ b/core/l10n/pt_BR.json @@ -11,6 +11,7 @@ "Repair error: " : "Reparação de erro:", "Following incompatible apps have been disabled: %s" : "Seguir aplicativos incompatíveis foi desativado: %s", "Following apps have been disabled: %s" : "Os seguintes aplicativos foram desabilitados: %s", + "File is too big" : "O arquivo é muito grande", "Invalid file provided" : "Arquivo fornecido inválido", "No image or file provided" : "Nenhuma imagem ou arquivo fornecido", "Unknown filetype" : "Tipo de arquivo desconhecido", diff --git a/core/l10n/th_TH.js b/core/l10n/th_TH.js index d40f2890fbc..9e56c2e84d1 100644 --- a/core/l10n/th_TH.js +++ b/core/l10n/th_TH.js @@ -13,6 +13,7 @@ OC.L10N.register( "Repair error: " : "เกิดข้อผิดพลาดในการซ่อมแซม:", "Following incompatible apps have been disabled: %s" : "แอพพลิเคชันต่อไปนี้เข้ากันไม่ได้มันจะถูกปิดการใช้งาน: %s", "Following apps have been disabled: %s" : "แอพฯดังต่อไปนี้ถูกปิดการใช้งาน: %s", + "File is too big" : "ไฟล์มีขนาดใหญ่เกินไป", "Invalid file provided" : "ระบุไฟล์ไม่ถูกต้อง", "No image or file provided" : "ไม่มีรูปภาพหรือไฟล์ที่ระบุ", "Unknown filetype" : "ไม่รู้จักชนิดของไฟล์", @@ -217,7 +218,7 @@ OC.L10N.register( "An internal error occured." : "เกิดข้อผิดพลาดภายใน", "Please try again or contact your administrator." : "โปรดลองอีกครั้งหรือติดต่อผู้ดูแลระบบ", "Forgot your password? Reset it!" : "ลืมรหัสผ่าน?", - "remember" : "จำรหัสผ่าน", + "remember" : "จดจำฉัน", "Log in" : "เข้าสู่ระบบ", "Alternative Logins" : "ทางเลือกการเข้าสู่ระบบ", "Hey there,<br><br>just letting you know that %s shared <strong>%s</strong> with you.<br><a href=\"%s\">View it!</a><br><br>" : "นี่คุณ,<br><br>อยากให้คุณทราบว่า %s ได้แชร์ <strong>%s</strong> กับคุณ <br><a href=\"%s\">คลิกดูที่นี่</a><br><br>", diff --git a/core/l10n/th_TH.json b/core/l10n/th_TH.json index f5a93c5a335..8dc55d8f0e7 100644 --- a/core/l10n/th_TH.json +++ b/core/l10n/th_TH.json @@ -11,6 +11,7 @@ "Repair error: " : "เกิดข้อผิดพลาดในการซ่อมแซม:", "Following incompatible apps have been disabled: %s" : "แอพพลิเคชันต่อไปนี้เข้ากันไม่ได้มันจะถูกปิดการใช้งาน: %s", "Following apps have been disabled: %s" : "แอพฯดังต่อไปนี้ถูกปิดการใช้งาน: %s", + "File is too big" : "ไฟล์มีขนาดใหญ่เกินไป", "Invalid file provided" : "ระบุไฟล์ไม่ถูกต้อง", "No image or file provided" : "ไม่มีรูปภาพหรือไฟล์ที่ระบุ", "Unknown filetype" : "ไม่รู้จักชนิดของไฟล์", @@ -215,7 +216,7 @@ "An internal error occured." : "เกิดข้อผิดพลาดภายใน", "Please try again or contact your administrator." : "โปรดลองอีกครั้งหรือติดต่อผู้ดูแลระบบ", "Forgot your password? Reset it!" : "ลืมรหัสผ่าน?", - "remember" : "จำรหัสผ่าน", + "remember" : "จดจำฉัน", "Log in" : "เข้าสู่ระบบ", "Alternative Logins" : "ทางเลือกการเข้าสู่ระบบ", "Hey there,<br><br>just letting you know that %s shared <strong>%s</strong> with you.<br><a href=\"%s\">View it!</a><br><br>" : "นี่คุณ,<br><br>อยากให้คุณทราบว่า %s ได้แชร์ <strong>%s</strong> กับคุณ <br><a href=\"%s\">คลิกดูที่นี่</a><br><br>", diff --git a/core/vendor/.gitignore b/core/vendor/.gitignore index 81dac0364d0..bcbb59b6f24 100644 --- a/core/vendor/.gitignore +++ b/core/vendor/.gitignore @@ -119,3 +119,6 @@ bootstrap/** !bootstrap/js bootstrap/js/* !bootstrap/js/tooltip.js + +# backbone +backbone/backbone-min* diff --git a/core/vendor/backbone/.bower.json b/core/vendor/backbone/.bower.json new file mode 100644 index 00000000000..578c8ffb669 --- /dev/null +++ b/core/vendor/backbone/.bower.json @@ -0,0 +1,33 @@ +{ + "name": "backbone", + "version": "1.2.1", + "main": "backbone.js", + "dependencies": { + "underscore": ">=1.7.0" + }, + "ignore": [ + "docs", + "examples", + "test", + "*.yml", + "*.html", + "*.ico", + "*.md", + "CNAME", + ".*", + "karma.*", + "component.json", + "package.json" + ], + "homepage": "https://github.com/jashkenas/backbone", + "_release": "1.2.1", + "_resolution": { + "type": "version", + "tag": "1.2.1", + "commit": "938a8ff934fd4de4f0009f68d43f500f5920b490" + }, + "_source": "git://github.com/jashkenas/backbone.git", + "_target": "~1.2.1", + "_originalSource": "backbone", + "_direct": true +}
\ No newline at end of file diff --git a/core/vendor/backbone/LICENSE b/core/vendor/backbone/LICENSE new file mode 100644 index 00000000000..184d1b99645 --- /dev/null +++ b/core/vendor/backbone/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2010-2015 Jeremy Ashkenas, DocumentCloud + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/vendor/backbone/backbone.js b/core/vendor/backbone/backbone.js new file mode 100644 index 00000000000..58800425c70 --- /dev/null +++ b/core/vendor/backbone/backbone.js @@ -0,0 +1,1873 @@ +// Backbone.js 1.2.1 + +// (c) 2010-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Backbone may be freely distributed under the MIT license. +// For all details and documentation: +// http://backbonejs.org + +(function(factory) { + + // Establish the root object, `window` (`self`) in the browser, or `global` on the server. + // We use `self` instead of `window` for `WebWorker` support. + var root = (typeof self == 'object' && self.self == self && self) || + (typeof global == 'object' && global.global == global && global); + + // Set up Backbone appropriately for the environment. Start with AMD. + if (typeof define === 'function' && define.amd) { + define(['underscore', 'jquery', 'exports'], function(_, $, exports) { + // Export global even in AMD case in case this script is loaded with + // others that may still expect a global Backbone. + root.Backbone = factory(root, exports, _, $); + }); + + // Next for Node.js or CommonJS. jQuery may not be needed as a module. + } else if (typeof exports !== 'undefined') { + var _ = require('underscore'), $; + try { $ = require('jquery'); } catch(e) {} + factory(root, exports, _, $); + + // Finally, as a browser global. + } else { + root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); + } + +}(function(root, Backbone, _, $) { + + // Initial Setup + // ------------- + + // Save the previous value of the `Backbone` variable, so that it can be + // restored later on, if `noConflict` is used. + var previousBackbone = root.Backbone; + + // Create a local reference to a common array method we'll want to use later. + var slice = [].slice; + + // Current version of the library. Keep in sync with `package.json`. + Backbone.VERSION = '1.2.1'; + + // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns + // the `$` variable. + Backbone.$ = $; + + // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable + // to its previous owner. Returns a reference to this Backbone object. + Backbone.noConflict = function() { + root.Backbone = previousBackbone; + return this; + }; + + // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option + // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and + // set a `X-Http-Method-Override` header. + Backbone.emulateHTTP = false; + + // Turn on `emulateJSON` to support legacy servers that can't deal with direct + // `application/json` requests ... this will encode the body as + // `application/x-www-form-urlencoded` instead and will send the model in a + // form param named `model`. + Backbone.emulateJSON = false; + + // Proxy Underscore methods to a Backbone class' prototype using a + // particular attribute as the data argument + var addMethod = function(length, method, attribute) { + switch (length) { + case 1: return function() { + return _[method](this[attribute]); + }; + case 2: return function(value) { + return _[method](this[attribute], value); + }; + case 3: return function(iteratee, context) { + return _[method](this[attribute], iteratee, context); + }; + case 4: return function(iteratee, defaultVal, context) { + return _[method](this[attribute], iteratee, defaultVal, context); + }; + default: return function() { + var args = slice.call(arguments); + args.unshift(this[attribute]); + return _[method].apply(_, args); + }; + } + }; + var addUnderscoreMethods = function(Class, methods, attribute) { + _.each(methods, function(length, method) { + if (_[method]) Class.prototype[method] = addMethod(length, method, attribute); + }); + }; + + // Backbone.Events + // --------------- + + // A module that can be mixed in to *any object* in order to provide it with + // custom events. You may bind with `on` or remove with `off` callback + // functions to an event; `trigger`-ing an event fires all callbacks in + // succession. + // + // var object = {}; + // _.extend(object, Backbone.Events); + // object.on('expand', function(){ alert('expanded'); }); + // object.trigger('expand'); + // + var Events = Backbone.Events = {}; + + // Regular expression used to split event strings. + var eventSplitter = /\s+/; + + // Iterates over the standard `event, callback` (as well as the fancy multiple + // space-separated events `"change blur", callback` and jQuery-style event + // maps `{event: callback}`), reducing them by manipulating `memo`. + // Passes a normalized single event name and callback, as well as any + // optional `opts`. + var eventsApi = function(iteratee, memo, name, callback, opts) { + var i = 0, names; + if (name && typeof name === 'object') { + // Handle event maps. + if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback; + for (names = _.keys(name); i < names.length ; i++) { + memo = iteratee(memo, names[i], name[names[i]], opts); + } + } else if (name && eventSplitter.test(name)) { + // Handle space separated event names. + for (names = name.split(eventSplitter); i < names.length; i++) { + memo = iteratee(memo, names[i], callback, opts); + } + } else { + memo = iteratee(memo, name, callback, opts); + } + return memo; + }; + + // Bind an event to a `callback` function. Passing `"all"` will bind + // the callback to all events fired. + Events.on = function(name, callback, context) { + return internalOn(this, name, callback, context); + }; + + // An internal use `on` function, used to guard the `listening` argument from + // the public API. + var internalOn = function(obj, name, callback, context, listening) { + obj._events = eventsApi(onApi, obj._events || {}, name, callback, { + context: context, + ctx: obj, + listening: listening + }); + + if (listening) { + var listeners = obj._listeners || (obj._listeners = {}); + listeners[listening.id] = listening; + } + + return obj; + }; + + // Inversion-of-control versions of `on`. Tell *this* object to listen to + // an event in another object... keeping track of what it's listening to. + Events.listenTo = function(obj, name, callback) { + if (!obj) return this; + var id = obj._listenId || (obj._listenId = _.uniqueId('l')); + var listeningTo = this._listeningTo || (this._listeningTo = {}); + var listening = listeningTo[id]; + + // This object is not listening to any other events on `obj` yet. + // Setup the necessary references to track the listening callbacks. + if (!listening) { + var thisId = this._listenId || (this._listenId = _.uniqueId('l')); + listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0}; + } + + // Bind callbacks on obj, and keep track of them on listening. + internalOn(obj, name, callback, this, listening); + return this; + }; + + // The reducing API that adds a callback to the `events` object. + var onApi = function(events, name, callback, options) { + if (callback) { + var handlers = events[name] || (events[name] = []); + var context = options.context, ctx = options.ctx, listening = options.listening; + if (listening) listening.count++; + + handlers.push({ callback: callback, context: context, ctx: context || ctx, listening: listening }); + } + return events; + }; + + // Remove one or many callbacks. If `context` is null, removes all + // callbacks with that function. If `callback` is null, removes all + // callbacks for the event. If `name` is null, removes all bound + // callbacks for all events. + Events.off = function(name, callback, context) { + if (!this._events) return this; + this._events = eventsApi(offApi, this._events, name, callback, { + context: context, + listeners: this._listeners + }); + return this; + }; + + // Tell this object to stop listening to either specific events ... or + // to every object it's currently listening to. + Events.stopListening = function(obj, name, callback) { + var listeningTo = this._listeningTo; + if (!listeningTo) return this; + + var ids = obj ? [obj._listenId] : _.keys(listeningTo); + + for (var i = 0; i < ids.length; i++) { + var listening = listeningTo[ids[i]]; + + // If listening doesn't exist, this object is not currently + // listening to obj. Break out early. + if (!listening) break; + + listening.obj.off(name, callback, this); + } + if (_.isEmpty(listeningTo)) this._listeningTo = void 0; + + return this; + }; + + // The reducing API that removes a callback from the `events` object. + var offApi = function(events, name, callback, options) { + // No events to consider. + if (!events) return; + + var i = 0, listening; + var context = options.context, listeners = options.listeners; + + // Delete all events listeners and "drop" events. + if (!name && !callback && !context) { + var ids = _.keys(listeners); + for (; i < ids.length; i++) { + listening = listeners[ids[i]]; + delete listeners[listening.id]; + delete listening.listeningTo[listening.objId]; + } + return; + } + + var names = name ? [name] : _.keys(events); + for (; i < names.length; i++) { + name = names[i]; + var handlers = events[name]; + + // Bail out if there are no events stored. + if (!handlers) break; + + // Replace events if there are any remaining. Otherwise, clean up. + var remaining = []; + for (var j = 0; j < handlers.length; j++) { + var handler = handlers[j]; + if ( + callback && callback !== handler.callback && + callback !== handler.callback._callback || + context && context !== handler.context + ) { + remaining.push(handler); + } else { + listening = handler.listening; + if (listening && --listening.count === 0) { + delete listeners[listening.id]; + delete listening.listeningTo[listening.objId]; + } + } + } + + // Update tail event if the list has any events. Otherwise, clean up. + if (remaining.length) { + events[name] = remaining; + } else { + delete events[name]; + } + } + if (_.size(events)) return events; + }; + + // Bind an event to only be triggered a single time. After the first time + // the callback is invoked, it will be removed. When multiple events are + // passed in using the space-separated syntax, the event will fire once for every + // event you passed in, not once for a combination of all events + Events.once = function(name, callback, context) { + // Map the event into a `{event: once}` object. + var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this)); + return this.on(events, void 0, context); + }; + + // Inversion-of-control versions of `once`. + Events.listenToOnce = function(obj, name, callback) { + // Map the event into a `{event: once}` object. + var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj)); + return this.listenTo(obj, events); + }; + + // Reduces the event callbacks into a map of `{event: onceWrapper}`. + // `offer` unbinds the `onceWrapper` after it has been called. + var onceMap = function(map, name, callback, offer) { + if (callback) { + var once = map[name] = _.once(function() { + offer(name, once); + callback.apply(this, arguments); + }); + once._callback = callback; + } + return map; + }; + + // Trigger one or many events, firing all bound callbacks. Callbacks are + // passed the same arguments as `trigger` is, apart from the event name + // (unless you're listening on `"all"`, which will cause your callback to + // receive the true name of the event as the first argument). + Events.trigger = function(name) { + if (!this._events) return this; + + var length = Math.max(0, arguments.length - 1); + var args = Array(length); + for (var i = 0; i < length; i++) args[i] = arguments[i + 1]; + + eventsApi(triggerApi, this._events, name, void 0, args); + return this; + }; + + // Handles triggering the appropriate event callbacks. + var triggerApi = function(objEvents, name, cb, args) { + if (objEvents) { + var events = objEvents[name]; + var allEvents = objEvents.all; + if (events && allEvents) allEvents = allEvents.slice(); + if (events) triggerEvents(events, args); + if (allEvents) triggerEvents(allEvents, [name].concat(args)); + } + return objEvents; + }; + + // A difficult-to-believe, but optimized internal dispatch function for + // triggering events. Tries to keep the usual cases speedy (most internal + // Backbone events have 3 arguments). + var triggerEvents = function(events, args) { + var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; + switch (args.length) { + case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; + case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; + case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; + case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; + default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; + } + }; + + // Aliases for backwards compatibility. + Events.bind = Events.on; + Events.unbind = Events.off; + + // Allow the `Backbone` object to serve as a global event bus, for folks who + // want global "pubsub" in a convenient place. + _.extend(Backbone, Events); + + // Backbone.Model + // -------------- + + // Backbone **Models** are the basic data object in the framework -- + // frequently representing a row in a table in a database on your server. + // A discrete chunk of data and a bunch of useful, related methods for + // performing computations and transformations on that data. + + // Create a new model with the specified attributes. A client id (`cid`) + // is automatically generated and assigned for you. + var Model = Backbone.Model = function(attributes, options) { + var attrs = attributes || {}; + options || (options = {}); + this.cid = _.uniqueId(this.cidPrefix); + this.attributes = {}; + if (options.collection) this.collection = options.collection; + if (options.parse) attrs = this.parse(attrs, options) || {}; + attrs = _.defaults({}, attrs, _.result(this, 'defaults')); + this.set(attrs, options); + this.changed = {}; + this.initialize.apply(this, arguments); + }; + + // Attach all inheritable methods to the Model prototype. + _.extend(Model.prototype, Events, { + + // A hash of attributes whose current and previous value differ. + changed: null, + + // The value returned during the last failed validation. + validationError: null, + + // The default name for the JSON `id` attribute is `"id"`. MongoDB and + // CouchDB users may want to set this to `"_id"`. + idAttribute: 'id', + + // The prefix is used to create the client id which is used to identify models locally. + // You may want to override this if you're experiencing name clashes with model ids. + cidPrefix: 'c', + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // Return a copy of the model's `attributes` object. + toJSON: function(options) { + return _.clone(this.attributes); + }, + + // Proxy `Backbone.sync` by default -- but override this if you need + // custom syncing semantics for *this* particular model. + sync: function() { + return Backbone.sync.apply(this, arguments); + }, + + // Get the value of an attribute. + get: function(attr) { + return this.attributes[attr]; + }, + + // Get the HTML-escaped value of an attribute. + escape: function(attr) { + return _.escape(this.get(attr)); + }, + + // Returns `true` if the attribute contains a value that is not null + // or undefined. + has: function(attr) { + return this.get(attr) != null; + }, + + // Special-cased proxy to underscore's `_.matches` method. + matches: function(attrs) { + return !!_.iteratee(attrs, this)(this.attributes); + }, + + // Set a hash of model attributes on the object, firing `"change"`. This is + // the core primitive operation of a model, updating the data and notifying + // anyone who needs to know about the change in state. The heart of the beast. + set: function(key, val, options) { + if (key == null) return this; + + // Handle both `"key", value` and `{key: value}` -style arguments. + var attrs; + if (typeof key === 'object') { + attrs = key; + options = val; + } else { + (attrs = {})[key] = val; + } + + options || (options = {}); + + // Run validation. + if (!this._validate(attrs, options)) return false; + + // Extract attributes and options. + var unset = options.unset; + var silent = options.silent; + var changes = []; + var changing = this._changing; + this._changing = true; + + if (!changing) { + this._previousAttributes = _.clone(this.attributes); + this.changed = {}; + } + + var current = this.attributes; + var changed = this.changed; + var prev = this._previousAttributes; + + // Check for changes of `id`. + if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; + + // For each `set` attribute, update or delete the current value. + for (var attr in attrs) { + val = attrs[attr]; + if (!_.isEqual(current[attr], val)) changes.push(attr); + if (!_.isEqual(prev[attr], val)) { + changed[attr] = val; + } else { + delete changed[attr]; + } + unset ? delete current[attr] : current[attr] = val; + } + + // Trigger all relevant attribute changes. + if (!silent) { + if (changes.length) this._pending = options; + for (var i = 0; i < changes.length; i++) { + this.trigger('change:' + changes[i], this, current[changes[i]], options); + } + } + + // You might be wondering why there's a `while` loop here. Changes can + // be recursively nested within `"change"` events. + if (changing) return this; + if (!silent) { + while (this._pending) { + options = this._pending; + this._pending = false; + this.trigger('change', this, options); + } + } + this._pending = false; + this._changing = false; + return this; + }, + + // Remove an attribute from the model, firing `"change"`. `unset` is a noop + // if the attribute doesn't exist. + unset: function(attr, options) { + return this.set(attr, void 0, _.extend({}, options, {unset: true})); + }, + + // Clear all attributes on the model, firing `"change"`. + clear: function(options) { + var attrs = {}; + for (var key in this.attributes) attrs[key] = void 0; + return this.set(attrs, _.extend({}, options, {unset: true})); + }, + + // Determine if the model has changed since the last `"change"` event. + // If you specify an attribute name, determine if that attribute has changed. + hasChanged: function(attr) { + if (attr == null) return !_.isEmpty(this.changed); + return _.has(this.changed, attr); + }, + + // Return an object containing all the attributes that have changed, or + // false if there are no changed attributes. Useful for determining what + // parts of a view need to be updated and/or what attributes need to be + // persisted to the server. Unset attributes will be set to undefined. + // You can also pass an attributes object to diff against the model, + // determining if there *would be* a change. + changedAttributes: function(diff) { + if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; + var old = this._changing ? this._previousAttributes : this.attributes; + var changed = {}; + for (var attr in diff) { + var val = diff[attr]; + if (_.isEqual(old[attr], val)) continue; + changed[attr] = val; + } + return _.size(changed) ? changed : false; + }, + + // Get the previous value of an attribute, recorded at the time the last + // `"change"` event was fired. + previous: function(attr) { + if (attr == null || !this._previousAttributes) return null; + return this._previousAttributes[attr]; + }, + + // Get all of the attributes of the model at the time of the previous + // `"change"` event. + previousAttributes: function() { + return _.clone(this._previousAttributes); + }, + + // Fetch the model from the server, merging the response with the model's + // local attributes. Any changed attributes will trigger a "change" event. + fetch: function(options) { + options = _.extend({parse: true}, options); + var model = this; + var success = options.success; + options.success = function(resp) { + var serverAttrs = options.parse ? model.parse(resp, options) : resp; + if (!model.set(serverAttrs, options)) return false; + if (success) success.call(options.context, model, resp, options); + model.trigger('sync', model, resp, options); + }; + wrapError(this, options); + return this.sync('read', this, options); + }, + + // Set a hash of model attributes, and sync the model to the server. + // If the server returns an attributes hash that differs, the model's + // state will be `set` again. + save: function(key, val, options) { + // Handle both `"key", value` and `{key: value}` -style arguments. + var attrs; + if (key == null || typeof key === 'object') { + attrs = key; + options = val; + } else { + (attrs = {})[key] = val; + } + + options = _.extend({validate: true, parse: true}, options); + var wait = options.wait; + + // If we're not waiting and attributes exist, save acts as + // `set(attr).save(null, opts)` with validation. Otherwise, check if + // the model will be valid when the attributes, if any, are set. + if (attrs && !wait) { + if (!this.set(attrs, options)) return false; + } else { + if (!this._validate(attrs, options)) return false; + } + + // After a successful server-side save, the client is (optionally) + // updated with the server-side state. + var model = this; + var success = options.success; + var attributes = this.attributes; + options.success = function(resp) { + // Ensure attributes are restored during synchronous saves. + model.attributes = attributes; + var serverAttrs = options.parse ? model.parse(resp, options) : resp; + if (wait) serverAttrs = _.extend({}, attrs, serverAttrs); + if (serverAttrs && !model.set(serverAttrs, options)) return false; + if (success) success.call(options.context, model, resp, options); + model.trigger('sync', model, resp, options); + }; + wrapError(this, options); + + // Set temporary attributes if `{wait: true}` to properly find new ids. + if (attrs && wait) this.attributes = _.extend({}, attributes, attrs); + + var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); + if (method === 'patch' && !options.attrs) options.attrs = attrs; + var xhr = this.sync(method, this, options); + + // Restore attributes. + this.attributes = attributes; + + return xhr; + }, + + // Destroy this model on the server if it was already persisted. + // Optimistically removes the model from its collection, if it has one. + // If `wait: true` is passed, waits for the server to respond before removal. + destroy: function(options) { + options = options ? _.clone(options) : {}; + var model = this; + var success = options.success; + var wait = options.wait; + + var destroy = function() { + model.stopListening(); + model.trigger('destroy', model, model.collection, options); + }; + + options.success = function(resp) { + if (wait) destroy(); + if (success) success.call(options.context, model, resp, options); + if (!model.isNew()) model.trigger('sync', model, resp, options); + }; + + var xhr = false; + if (this.isNew()) { + _.defer(options.success); + } else { + wrapError(this, options); + xhr = this.sync('delete', this, options); + } + if (!wait) destroy(); + return xhr; + }, + + // Default URL for the model's representation on the server -- if you're + // using Backbone's restful methods, override this to change the endpoint + // that will be called. + url: function() { + var base = + _.result(this, 'urlRoot') || + _.result(this.collection, 'url') || + urlError(); + if (this.isNew()) return base; + var id = this.get(this.idAttribute); + return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id); + }, + + // **parse** converts a response into the hash of attributes to be `set` on + // the model. The default implementation is just to pass the response along. + parse: function(resp, options) { + return resp; + }, + + // Create a new model with identical attributes to this one. + clone: function() { + return new this.constructor(this.attributes); + }, + + // A model is new if it has never been saved to the server, and lacks an id. + isNew: function() { + return !this.has(this.idAttribute); + }, + + // Check if the model is currently in a valid state. + isValid: function(options) { + return this._validate({}, _.defaults({validate: true}, options)); + }, + + // Run validation against the next complete set of model attributes, + // returning `true` if all is well. Otherwise, fire an `"invalid"` event. + _validate: function(attrs, options) { + if (!options.validate || !this.validate) return true; + attrs = _.extend({}, this.attributes, attrs); + var error = this.validationError = this.validate(attrs, options) || null; + if (!error) return true; + this.trigger('invalid', this, error, _.extend(options, {validationError: error})); + return false; + } + + }); + + // Underscore methods that we want to implement on the Model. + var modelMethods = { keys: 1, values: 1, pairs: 1, invert: 1, pick: 0, + omit: 0, chain: 1, isEmpty: 1 }; + + // Mix in each Underscore method as a proxy to `Model#attributes`. + addUnderscoreMethods(Model, modelMethods, 'attributes'); + + // Backbone.Collection + // ------------------- + + // If models tend to represent a single row of data, a Backbone Collection is + // more analogous to a table full of data ... or a small slice or page of that + // table, or a collection of rows that belong together for a particular reason + // -- all of the messages in this particular folder, all of the documents + // belonging to this particular author, and so on. Collections maintain + // indexes of their models, both in order, and for lookup by `id`. + + // Create a new **Collection**, perhaps to contain a specific type of `model`. + // If a `comparator` is specified, the Collection will maintain + // its models in sort order, as they're added and removed. + var Collection = Backbone.Collection = function(models, options) { + options || (options = {}); + if (options.model) this.model = options.model; + if (options.comparator !== void 0) this.comparator = options.comparator; + this._reset(); + this.initialize.apply(this, arguments); + if (models) this.reset(models, _.extend({silent: true}, options)); + }; + + // Default options for `Collection#set`. + var setOptions = {add: true, remove: true, merge: true}; + var addOptions = {add: true, remove: false}; + + // Define the Collection's inheritable methods. + _.extend(Collection.prototype, Events, { + + // The default model for a collection is just a **Backbone.Model**. + // This should be overridden in most cases. + model: Model, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // The JSON representation of a Collection is an array of the + // models' attributes. + toJSON: function(options) { + return this.map(function(model) { return model.toJSON(options); }); + }, + + // Proxy `Backbone.sync` by default. + sync: function() { + return Backbone.sync.apply(this, arguments); + }, + + // Add a model, or list of models to the set. + add: function(models, options) { + return this.set(models, _.extend({merge: false}, options, addOptions)); + }, + + // Remove a model, or a list of models from the set. + remove: function(models, options) { + options = _.extend({}, options); + var singular = !_.isArray(models); + models = singular ? [models] : _.clone(models); + var removed = this._removeModels(models, options); + if (!options.silent && removed) this.trigger('update', this, options); + return singular ? removed[0] : removed; + }, + + // Update a collection by `set`-ing a new list of models, adding new ones, + // removing models that are no longer present, and merging models that + // already exist in the collection, as necessary. Similar to **Model#set**, + // the core operation for updating the data contained by the collection. + set: function(models, options) { + options = _.defaults({}, options, setOptions); + if (options.parse && !this._isModel(models)) models = this.parse(models, options); + var singular = !_.isArray(models); + models = singular ? (models ? [models] : []) : models.slice(); + var id, model, attrs, existing, sort; + var at = options.at; + if (at != null) at = +at; + if (at < 0) at += this.length + 1; + var sortable = this.comparator && (at == null) && options.sort !== false; + var sortAttr = _.isString(this.comparator) ? this.comparator : null; + var toAdd = [], toRemove = [], modelMap = {}; + var add = options.add, merge = options.merge, remove = options.remove; + var order = !sortable && add && remove ? [] : false; + var orderChanged = false; + + // Turn bare objects into model references, and prevent invalid models + // from being added. + for (var i = 0; i < models.length; i++) { + attrs = models[i]; + + // If a duplicate is found, prevent it from being added and + // optionally merge it into the existing model. + if (existing = this.get(attrs)) { + if (remove) modelMap[existing.cid] = true; + if (merge && attrs !== existing) { + attrs = this._isModel(attrs) ? attrs.attributes : attrs; + if (options.parse) attrs = existing.parse(attrs, options); + existing.set(attrs, options); + if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true; + } + models[i] = existing; + + // If this is a new, valid model, push it to the `toAdd` list. + } else if (add) { + model = models[i] = this._prepareModel(attrs, options); + if (!model) continue; + toAdd.push(model); + this._addReference(model, options); + } + + // Do not add multiple models with the same `id`. + model = existing || model; + if (!model) continue; + id = this.modelId(model.attributes); + if (order && (model.isNew() || !modelMap[id])) { + order.push(model); + + // Check to see if this is actually a new model at this index. + orderChanged = orderChanged || !this.models[i] || model.cid !== this.models[i].cid; + } + + modelMap[id] = true; + } + + // Remove nonexistent models if appropriate. + if (remove) { + for (var i = 0; i < this.length; i++) { + if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model); + } + if (toRemove.length) this._removeModels(toRemove, options); + } + + // See if sorting is needed, update `length` and splice in new models. + if (toAdd.length || orderChanged) { + if (sortable) sort = true; + this.length += toAdd.length; + if (at != null) { + for (var i = 0; i < toAdd.length; i++) { + this.models.splice(at + i, 0, toAdd[i]); + } + } else { + if (order) this.models.length = 0; + var orderedModels = order || toAdd; + for (var i = 0; i < orderedModels.length; i++) { + this.models.push(orderedModels[i]); + } + } + } + + // Silently sort the collection if appropriate. + if (sort) this.sort({silent: true}); + + // Unless silenced, it's time to fire all appropriate add/sort events. + if (!options.silent) { + var addOpts = at != null ? _.clone(options) : options; + for (var i = 0; i < toAdd.length; i++) { + if (at != null) addOpts.index = at + i; + (model = toAdd[i]).trigger('add', model, this, addOpts); + } + if (sort || orderChanged) this.trigger('sort', this, options); + if (toAdd.length || toRemove.length) this.trigger('update', this, options); + } + + // Return the added (or merged) model (or models). + return singular ? models[0] : models; + }, + + // When you have more items than you want to add or remove individually, + // you can reset the entire set with a new list of models, without firing + // any granular `add` or `remove` events. Fires `reset` when finished. + // Useful for bulk operations and optimizations. + reset: function(models, options) { + options = options ? _.clone(options) : {}; + for (var i = 0; i < this.models.length; i++) { + this._removeReference(this.models[i], options); + } + options.previousModels = this.models; + this._reset(); + models = this.add(models, _.extend({silent: true}, options)); + if (!options.silent) this.trigger('reset', this, options); + return models; + }, + + // Add a model to the end of the collection. + push: function(model, options) { + return this.add(model, _.extend({at: this.length}, options)); + }, + + // Remove a model from the end of the collection. + pop: function(options) { + var model = this.at(this.length - 1); + return this.remove(model, options); + }, + + // Add a model to the beginning of the collection. + unshift: function(model, options) { + return this.add(model, _.extend({at: 0}, options)); + }, + + // Remove a model from the beginning of the collection. + shift: function(options) { + var model = this.at(0); + return this.remove(model, options); + }, + + // Slice out a sub-array of models from the collection. + slice: function() { + return slice.apply(this.models, arguments); + }, + + // Get a model from the set by id. + get: function(obj) { + if (obj == null) return void 0; + var id = this.modelId(this._isModel(obj) ? obj.attributes : obj); + return this._byId[obj] || this._byId[id] || this._byId[obj.cid]; + }, + + // Get the model at the given index. + at: function(index) { + if (index < 0) index += this.length; + return this.models[index]; + }, + + // Return models with matching attributes. Useful for simple cases of + // `filter`. + where: function(attrs, first) { + var matches = _.matches(attrs); + return this[first ? 'find' : 'filter'](function(model) { + return matches(model.attributes); + }); + }, + + // Return the first model with matching attributes. Useful for simple cases + // of `find`. + findWhere: function(attrs) { + return this.where(attrs, true); + }, + + // Force the collection to re-sort itself. You don't need to call this under + // normal circumstances, as the set will maintain sort order as each item + // is added. + sort: function(options) { + if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); + options || (options = {}); + + // Run sort based on type of `comparator`. + if (_.isString(this.comparator) || this.comparator.length === 1) { + this.models = this.sortBy(this.comparator, this); + } else { + this.models.sort(_.bind(this.comparator, this)); + } + + if (!options.silent) this.trigger('sort', this, options); + return this; + }, + + // Pluck an attribute from each model in the collection. + pluck: function(attr) { + return _.invoke(this.models, 'get', attr); + }, + + // Fetch the default set of models for this collection, resetting the + // collection when they arrive. If `reset: true` is passed, the response + // data will be passed through the `reset` method instead of `set`. + fetch: function(options) { + options = _.extend({parse: true}, options); + var success = options.success; + var collection = this; + options.success = function(resp) { + var method = options.reset ? 'reset' : 'set'; + collection[method](resp, options); + if (success) success.call(options.context, collection, resp, options); + collection.trigger('sync', collection, resp, options); + }; + wrapError(this, options); + return this.sync('read', this, options); + }, + + // Create a new instance of a model in this collection. Add the model to the + // collection immediately, unless `wait: true` is passed, in which case we + // wait for the server to agree. + create: function(model, options) { + options = options ? _.clone(options) : {}; + var wait = options.wait; + model = this._prepareModel(model, options); + if (!model) return false; + if (!wait) this.add(model, options); + var collection = this; + var success = options.success; + options.success = function(model, resp, callbackOpts) { + if (wait) collection.add(model, callbackOpts); + if (success) success.call(callbackOpts.context, model, resp, callbackOpts); + }; + model.save(null, options); + return model; + }, + + // **parse** converts a response into a list of models to be added to the + // collection. The default implementation is just to pass it through. + parse: function(resp, options) { + return resp; + }, + + // Create a new collection with an identical list of models as this one. + clone: function() { + return new this.constructor(this.models, { + model: this.model, + comparator: this.comparator + }); + }, + + // Define how to uniquely identify models in the collection. + modelId: function (attrs) { + return attrs[this.model.prototype.idAttribute || 'id']; + }, + + // Private method to reset all internal state. Called when the collection + // is first initialized or reset. + _reset: function() { + this.length = 0; + this.models = []; + this._byId = {}; + }, + + // Prepare a hash of attributes (or other model) to be added to this + // collection. + _prepareModel: function(attrs, options) { + if (this._isModel(attrs)) { + if (!attrs.collection) attrs.collection = this; + return attrs; + } + options = options ? _.clone(options) : {}; + options.collection = this; + var model = new this.model(attrs, options); + if (!model.validationError) return model; + this.trigger('invalid', this, model.validationError, options); + return false; + }, + + // Internal method called by both remove and set. + // Returns removed models, or false if nothing is removed. + _removeModels: function(models, options) { + var removed = []; + for (var i = 0; i < models.length; i++) { + var model = this.get(models[i]); + if (!model) continue; + + var index = this.indexOf(model); + this.models.splice(index, 1); + this.length--; + + if (!options.silent) { + options.index = index; + model.trigger('remove', model, this, options); + } + + removed.push(model); + this._removeReference(model, options); + } + return removed.length ? removed : false; + }, + + // Method for checking whether an object should be considered a model for + // the purposes of adding to the collection. + _isModel: function (model) { + return model instanceof Model; + }, + + // Internal method to create a model's ties to a collection. + _addReference: function(model, options) { + this._byId[model.cid] = model; + var id = this.modelId(model.attributes); + if (id != null) this._byId[id] = model; + model.on('all', this._onModelEvent, this); + }, + + // Internal method to sever a model's ties to a collection. + _removeReference: function(model, options) { + delete this._byId[model.cid]; + var id = this.modelId(model.attributes); + if (id != null) delete this._byId[id]; + if (this === model.collection) delete model.collection; + model.off('all', this._onModelEvent, this); + }, + + // Internal method called every time a model in the set fires an event. + // Sets need to update their indexes when models change ids. All other + // events simply proxy through. "add" and "remove" events that originate + // in other collections are ignored. + _onModelEvent: function(event, model, collection, options) { + if ((event === 'add' || event === 'remove') && collection !== this) return; + if (event === 'destroy') this.remove(model, options); + if (event === 'change') { + var prevId = this.modelId(model.previousAttributes()); + var id = this.modelId(model.attributes); + if (prevId !== id) { + if (prevId != null) delete this._byId[prevId]; + if (id != null) this._byId[id] = model; + } + } + this.trigger.apply(this, arguments); + } + + }); + + // Underscore methods that we want to implement on the Collection. + // 90% of the core usefulness of Backbone Collections is actually implemented + // right here: + var collectionMethods = { forEach: 3, each: 3, map: 3, collect: 3, reduce: 4, + foldl: 4, inject: 4, reduceRight: 4, foldr: 4, find: 3, detect: 3, filter: 3, + select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 2, + contains: 2, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3, + head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3, + without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3, + isEmpty: 1, chain: 1, sample: 3, partition: 3 }; + + // Mix in each Underscore method as a proxy to `Collection#models`. + addUnderscoreMethods(Collection, collectionMethods, 'models'); + + // Underscore methods that take a property name as an argument. + var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy']; + + // Use attributes instead of properties. + _.each(attributeMethods, function(method) { + if (!_[method]) return; + Collection.prototype[method] = function(value, context) { + var iterator = _.isFunction(value) ? value : function(model) { + return model.get(value); + }; + return _[method](this.models, iterator, context); + }; + }); + + // Backbone.View + // ------------- + + // Backbone Views are almost more convention than they are actual code. A View + // is simply a JavaScript object that represents a logical chunk of UI in the + // DOM. This might be a single item, an entire list, a sidebar or panel, or + // even the surrounding frame which wraps your whole app. Defining a chunk of + // UI as a **View** allows you to define your DOM events declaratively, without + // having to worry about render order ... and makes it easy for the view to + // react to specific changes in the state of your models. + + // Creating a Backbone.View creates its initial element outside of the DOM, + // if an existing element is not provided... + var View = Backbone.View = function(options) { + this.cid = _.uniqueId('view'); + _.extend(this, _.pick(options, viewOptions)); + this._ensureElement(); + this.initialize.apply(this, arguments); + }; + + // Cached regex to split keys for `delegate`. + var delegateEventSplitter = /^(\S+)\s*(.*)$/; + + // List of view options to be merged as properties. + var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; + + // Set up all inheritable **Backbone.View** properties and methods. + _.extend(View.prototype, Events, { + + // The default `tagName` of a View's element is `"div"`. + tagName: 'div', + + // jQuery delegate for element lookup, scoped to DOM elements within the + // current view. This should be preferred to global lookups where possible. + $: function(selector) { + return this.$el.find(selector); + }, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // **render** is the core function that your view should override, in order + // to populate its element (`this.el`), with the appropriate HTML. The + // convention is for **render** to always return `this`. + render: function() { + return this; + }, + + // Remove this view by taking the element out of the DOM, and removing any + // applicable Backbone.Events listeners. + remove: function() { + this._removeElement(); + this.stopListening(); + return this; + }, + + // Remove this view's element from the document and all event listeners + // attached to it. Exposed for subclasses using an alternative DOM + // manipulation API. + _removeElement: function() { + this.$el.remove(); + }, + + // Change the view's element (`this.el` property) and re-delegate the + // view's events on the new element. + setElement: function(element) { + this.undelegateEvents(); + this._setElement(element); + this.delegateEvents(); + return this; + }, + + // Creates the `this.el` and `this.$el` references for this view using the + // given `el`. `el` can be a CSS selector or an HTML string, a jQuery + // context or an element. Subclasses can override this to utilize an + // alternative DOM manipulation API and are only required to set the + // `this.el` property. + _setElement: function(el) { + this.$el = el instanceof Backbone.$ ? el : Backbone.$(el); + this.el = this.$el[0]; + }, + + // Set callbacks, where `this.events` is a hash of + // + // *{"event selector": "callback"}* + // + // { + // 'mousedown .title': 'edit', + // 'click .button': 'save', + // 'click .open': function(e) { ... } + // } + // + // pairs. Callbacks will be bound to the view, with `this` set properly. + // Uses event delegation for efficiency. + // Omitting the selector binds the event to `this.el`. + delegateEvents: function(events) { + events || (events = _.result(this, 'events')); + if (!events) return this; + this.undelegateEvents(); + for (var key in events) { + var method = events[key]; + if (!_.isFunction(method)) method = this[method]; + if (!method) continue; + var match = key.match(delegateEventSplitter); + this.delegate(match[1], match[2], _.bind(method, this)); + } + return this; + }, + + // Add a single event listener to the view's element (or a child element + // using `selector`). This only works for delegate-able events: not `focus`, + // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer. + delegate: function(eventName, selector, listener) { + this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener); + return this; + }, + + // Clears all callbacks previously bound to the view by `delegateEvents`. + // You usually don't need to use this, but may wish to if you have multiple + // Backbone views attached to the same DOM element. + undelegateEvents: function() { + if (this.$el) this.$el.off('.delegateEvents' + this.cid); + return this; + }, + + // A finer-grained `undelegateEvents` for removing a single delegated event. + // `selector` and `listener` are both optional. + undelegate: function(eventName, selector, listener) { + this.$el.off(eventName + '.delegateEvents' + this.cid, selector, listener); + return this; + }, + + // Produces a DOM element to be assigned to your view. Exposed for + // subclasses using an alternative DOM manipulation API. + _createElement: function(tagName) { + return document.createElement(tagName); + }, + + // Ensure that the View has a DOM element to render into. + // If `this.el` is a string, pass it through `$()`, take the first + // matching element, and re-assign it to `el`. Otherwise, create + // an element from the `id`, `className` and `tagName` properties. + _ensureElement: function() { + if (!this.el) { + var attrs = _.extend({}, _.result(this, 'attributes')); + if (this.id) attrs.id = _.result(this, 'id'); + if (this.className) attrs['class'] = _.result(this, 'className'); + this.setElement(this._createElement(_.result(this, 'tagName'))); + this._setAttributes(attrs); + } else { + this.setElement(_.result(this, 'el')); + } + }, + + // Set attributes from a hash on this view's element. Exposed for + // subclasses using an alternative DOM manipulation API. + _setAttributes: function(attributes) { + this.$el.attr(attributes); + } + + }); + + // Backbone.sync + // ------------- + + // Override this function to change the manner in which Backbone persists + // models to the server. You will be passed the type of request, and the + // model in question. By default, makes a RESTful Ajax request + // to the model's `url()`. Some possible customizations could be: + // + // * Use `setTimeout` to batch rapid-fire updates into a single request. + // * Send up the models as XML instead of JSON. + // * Persist models via WebSockets instead of Ajax. + // + // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests + // as `POST`, with a `_method` parameter containing the true HTTP method, + // as well as all requests with the body as `application/x-www-form-urlencoded` + // instead of `application/json` with the model in a param named `model`. + // Useful when interfacing with server-side languages like **PHP** that make + // it difficult to read the body of `PUT` requests. + Backbone.sync = function(method, model, options) { + var type = methodMap[method]; + + // Default options, unless specified. + _.defaults(options || (options = {}), { + emulateHTTP: Backbone.emulateHTTP, + emulateJSON: Backbone.emulateJSON + }); + + // Default JSON-request options. + var params = {type: type, dataType: 'json'}; + + // Ensure that we have a URL. + if (!options.url) { + params.url = _.result(model, 'url') || urlError(); + } + + // Ensure that we have the appropriate request data. + if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { + params.contentType = 'application/json'; + params.data = JSON.stringify(options.attrs || model.toJSON(options)); + } + + // For older servers, emulate JSON by encoding the request into an HTML-form. + if (options.emulateJSON) { + params.contentType = 'application/x-www-form-urlencoded'; + params.data = params.data ? {model: params.data} : {}; + } + + // For older servers, emulate HTTP by mimicking the HTTP method with `_method` + // And an `X-HTTP-Method-Override` header. + if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { + params.type = 'POST'; + if (options.emulateJSON) params.data._method = type; + var beforeSend = options.beforeSend; + options.beforeSend = function(xhr) { + xhr.setRequestHeader('X-HTTP-Method-Override', type); + if (beforeSend) return beforeSend.apply(this, arguments); + }; + } + + // Don't process data on a non-GET request. + if (params.type !== 'GET' && !options.emulateJSON) { + params.processData = false; + } + + // Pass along `textStatus` and `errorThrown` from jQuery. + var error = options.error; + options.error = function(xhr, textStatus, errorThrown) { + options.textStatus = textStatus; + options.errorThrown = errorThrown; + if (error) error.call(options.context, xhr, textStatus, errorThrown); + }; + + // Make the request, allowing the user to override any Ajax options. + var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); + model.trigger('request', model, xhr, options); + return xhr; + }; + + // Map from CRUD to HTTP for our default `Backbone.sync` implementation. + var methodMap = { + 'create': 'POST', + 'update': 'PUT', + 'patch': 'PATCH', + 'delete': 'DELETE', + 'read': 'GET' + }; + + // Set the default implementation of `Backbone.ajax` to proxy through to `$`. + // Override this if you'd like to use a different library. + Backbone.ajax = function() { + return Backbone.$.ajax.apply(Backbone.$, arguments); + }; + + // Backbone.Router + // --------------- + + // Routers map faux-URLs to actions, and fire events when routes are + // matched. Creating a new one sets its `routes` hash, if not set statically. + var Router = Backbone.Router = function(options) { + options || (options = {}); + if (options.routes) this.routes = options.routes; + this._bindRoutes(); + this.initialize.apply(this, arguments); + }; + + // Cached regular expressions for matching named param parts and splatted + // parts of route strings. + var optionalParam = /\((.*?)\)/g; + var namedParam = /(\(\?)?:\w+/g; + var splatParam = /\*\w+/g; + var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; + + // Set up all inheritable **Backbone.Router** properties and methods. + _.extend(Router.prototype, Events, { + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // Manually bind a single named route to a callback. For example: + // + // this.route('search/:query/p:num', 'search', function(query, num) { + // ... + // }); + // + route: function(route, name, callback) { + if (!_.isRegExp(route)) route = this._routeToRegExp(route); + if (_.isFunction(name)) { + callback = name; + name = ''; + } + if (!callback) callback = this[name]; + var router = this; + Backbone.history.route(route, function(fragment) { + var args = router._extractParameters(route, fragment); + if (router.execute(callback, args, name) !== false) { + router.trigger.apply(router, ['route:' + name].concat(args)); + router.trigger('route', name, args); + Backbone.history.trigger('route', router, name, args); + } + }); + return this; + }, + + // Execute a route handler with the provided parameters. This is an + // excellent place to do pre-route setup or post-route cleanup. + execute: function(callback, args, name) { + if (callback) callback.apply(this, args); + }, + + // Simple proxy to `Backbone.history` to save a fragment into the history. + navigate: function(fragment, options) { + Backbone.history.navigate(fragment, options); + return this; + }, + + // Bind all defined routes to `Backbone.history`. We have to reverse the + // order of the routes here to support behavior where the most general + // routes can be defined at the bottom of the route map. + _bindRoutes: function() { + if (!this.routes) return; + this.routes = _.result(this, 'routes'); + var route, routes = _.keys(this.routes); + while ((route = routes.pop()) != null) { + this.route(route, this.routes[route]); + } + }, + + // Convert a route string into a regular expression, suitable for matching + // against the current location hash. + _routeToRegExp: function(route) { + route = route.replace(escapeRegExp, '\\$&') + .replace(optionalParam, '(?:$1)?') + .replace(namedParam, function(match, optional) { + return optional ? match : '([^/?]+)'; + }) + .replace(splatParam, '([^?]*?)'); + return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'); + }, + + // Given a route, and a URL fragment that it matches, return the array of + // extracted decoded parameters. Empty or unmatched parameters will be + // treated as `null` to normalize cross-browser behavior. + _extractParameters: function(route, fragment) { + var params = route.exec(fragment).slice(1); + return _.map(params, function(param, i) { + // Don't decode the search params. + if (i === params.length - 1) return param || null; + return param ? decodeURIComponent(param) : null; + }); + } + + }); + + // Backbone.History + // ---------------- + + // Handles cross-browser history management, based on either + // [pushState](http://diveintohtml5.info/history.html) and real URLs, or + // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) + // and URL fragments. If the browser supports neither (old IE, natch), + // falls back to polling. + var History = Backbone.History = function() { + this.handlers = []; + _.bindAll(this, 'checkUrl'); + + // Ensure that `History` can be used outside of the browser. + if (typeof window !== 'undefined') { + this.location = window.location; + this.history = window.history; + } + }; + + // Cached regex for stripping a leading hash/slash and trailing space. + var routeStripper = /^[#\/]|\s+$/g; + + // Cached regex for stripping leading and trailing slashes. + var rootStripper = /^\/+|\/+$/g; + + // Cached regex for stripping urls of hash. + var pathStripper = /#.*$/; + + // Has the history handling already been started? + History.started = false; + + // Set up all inheritable **Backbone.History** properties and methods. + _.extend(History.prototype, Events, { + + // The default interval to poll for hash changes, if necessary, is + // twenty times a second. + interval: 50, + + // Are we at the app root? + atRoot: function() { + var path = this.location.pathname.replace(/[^\/]$/, '$&/'); + return path === this.root && !this.getSearch(); + }, + + // Does the pathname match the root? + matchRoot: function() { + var path = this.decodeFragment(this.location.pathname); + var root = path.slice(0, this.root.length - 1) + '/'; + return root === this.root; + }, + + // Unicode characters in `location.pathname` are percent encoded so they're + // decoded for comparison. `%25` should not be decoded since it may be part + // of an encoded parameter. + decodeFragment: function(fragment) { + return decodeURI(fragment.replace(/%25/g, '%2525')); + }, + + // In IE6, the hash fragment and search params are incorrect if the + // fragment contains `?`. + getSearch: function() { + var match = this.location.href.replace(/#.*/, '').match(/\?.+/); + return match ? match[0] : ''; + }, + + // Gets the true hash value. Cannot use location.hash directly due to bug + // in Firefox where location.hash will always be decoded. + getHash: function(window) { + var match = (window || this).location.href.match(/#(.*)$/); + return match ? match[1] : ''; + }, + + // Get the pathname and search params, without the root. + getPath: function() { + var path = this.decodeFragment( + this.location.pathname + this.getSearch() + ).slice(this.root.length - 1); + return path.charAt(0) === '/' ? path.slice(1) : path; + }, + + // Get the cross-browser normalized URL fragment from the path or hash. + getFragment: function(fragment) { + if (fragment == null) { + if (this._usePushState || !this._wantsHashChange) { + fragment = this.getPath(); + } else { + fragment = this.getHash(); + } + } + return fragment.replace(routeStripper, ''); + }, + + // Start the hash change handling, returning `true` if the current URL matches + // an existing route, and `false` otherwise. + start: function(options) { + if (History.started) throw new Error('Backbone.history has already been started'); + History.started = true; + + // Figure out the initial configuration. Do we need an iframe? + // Is pushState desired ... is it available? + this.options = _.extend({root: '/'}, this.options, options); + this.root = this.options.root; + this._wantsHashChange = this.options.hashChange !== false; + this._hasHashChange = 'onhashchange' in window; + this._useHashChange = this._wantsHashChange && this._hasHashChange; + this._wantsPushState = !!this.options.pushState; + this._hasPushState = !!(this.history && this.history.pushState); + this._usePushState = this._wantsPushState && this._hasPushState; + this.fragment = this.getFragment(); + + // Normalize root to always include a leading and trailing slash. + this.root = ('/' + this.root + '/').replace(rootStripper, '/'); + + // Transition from hashChange to pushState or vice versa if both are + // requested. + if (this._wantsHashChange && this._wantsPushState) { + + // If we've started off with a route from a `pushState`-enabled + // browser, but we're currently in a browser that doesn't support it... + if (!this._hasPushState && !this.atRoot()) { + var root = this.root.slice(0, -1) || '/'; + this.location.replace(root + '#' + this.getPath()); + // Return immediately as browser will do redirect to new url + return true; + + // Or if we've started out with a hash-based route, but we're currently + // in a browser where it could be `pushState`-based instead... + } else if (this._hasPushState && this.atRoot()) { + this.navigate(this.getHash(), {replace: true}); + } + + } + + // Proxy an iframe to handle location events if the browser doesn't + // support the `hashchange` event, HTML5 history, or the user wants + // `hashChange` but not `pushState`. + if (!this._hasHashChange && this._wantsHashChange && !this._usePushState) { + this.iframe = document.createElement('iframe'); + this.iframe.src = 'javascript:0'; + this.iframe.style.display = 'none'; + this.iframe.tabIndex = -1; + var body = document.body; + // Using `appendChild` will throw on IE < 9 if the document is not ready. + var iWindow = body.insertBefore(this.iframe, body.firstChild).contentWindow; + iWindow.document.open(); + iWindow.document.close(); + iWindow.location.hash = '#' + this.fragment; + } + + // Add a cross-platform `addEventListener` shim for older browsers. + var addEventListener = window.addEventListener || function (eventName, listener) { + return attachEvent('on' + eventName, listener); + }; + + // Depending on whether we're using pushState or hashes, and whether + // 'onhashchange' is supported, determine how we check the URL state. + if (this._usePushState) { + addEventListener('popstate', this.checkUrl, false); + } else if (this._useHashChange && !this.iframe) { + addEventListener('hashchange', this.checkUrl, false); + } else if (this._wantsHashChange) { + this._checkUrlInterval = setInterval(this.checkUrl, this.interval); + } + + if (!this.options.silent) return this.loadUrl(); + }, + + // Disable Backbone.history, perhaps temporarily. Not useful in a real app, + // but possibly useful for unit testing Routers. + stop: function() { + // Add a cross-platform `removeEventListener` shim for older browsers. + var removeEventListener = window.removeEventListener || function (eventName, listener) { + return detachEvent('on' + eventName, listener); + }; + + // Remove window listeners. + if (this._usePushState) { + removeEventListener('popstate', this.checkUrl, false); + } else if (this._useHashChange && !this.iframe) { + removeEventListener('hashchange', this.checkUrl, false); + } + + // Clean up the iframe if necessary. + if (this.iframe) { + document.body.removeChild(this.iframe); + this.iframe = null; + } + + // Some environments will throw when clearing an undefined interval. + if (this._checkUrlInterval) clearInterval(this._checkUrlInterval); + History.started = false; + }, + + // Add a route to be tested when the fragment changes. Routes added later + // may override previous routes. + route: function(route, callback) { + this.handlers.unshift({route: route, callback: callback}); + }, + + // Checks the current URL to see if it has changed, and if it has, + // calls `loadUrl`, normalizing across the hidden iframe. + checkUrl: function(e) { + var current = this.getFragment(); + + // If the user pressed the back button, the iframe's hash will have + // changed and we should use that for comparison. + if (current === this.fragment && this.iframe) { + current = this.getHash(this.iframe.contentWindow); + } + + if (current === this.fragment) return false; + if (this.iframe) this.navigate(current); + this.loadUrl(); + }, + + // Attempt to load the current URL fragment. If a route succeeds with a + // match, returns `true`. If no defined routes matches the fragment, + // returns `false`. + loadUrl: function(fragment) { + // If the root doesn't match, no routes can match either. + if (!this.matchRoot()) return false; + fragment = this.fragment = this.getFragment(fragment); + return _.any(this.handlers, function(handler) { + if (handler.route.test(fragment)) { + handler.callback(fragment); + return true; + } + }); + }, + + // Save a fragment into the hash history, or replace the URL state if the + // 'replace' option is passed. You are responsible for properly URL-encoding + // the fragment in advance. + // + // The options object can contain `trigger: true` if you wish to have the + // route callback be fired (not usually desirable), or `replace: true`, if + // you wish to modify the current URL without adding an entry to the history. + navigate: function(fragment, options) { + if (!History.started) return false; + if (!options || options === true) options = {trigger: !!options}; + + // Normalize the fragment. + fragment = this.getFragment(fragment || ''); + + // Don't include a trailing slash on the root. + var root = this.root; + if (fragment === '' || fragment.charAt(0) === '?') { + root = root.slice(0, -1) || '/'; + } + var url = root + fragment; + + // Strip the hash and decode for matching. + fragment = this.decodeFragment(fragment.replace(pathStripper, '')); + + if (this.fragment === fragment) return; + this.fragment = fragment; + + // If pushState is available, we use it to set the fragment as a real URL. + if (this._usePushState) { + this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url); + + // If hash changes haven't been explicitly disabled, update the hash + // fragment to store history. + } else if (this._wantsHashChange) { + this._updateHash(this.location, fragment, options.replace); + if (this.iframe && (fragment !== this.getHash(this.iframe.contentWindow))) { + var iWindow = this.iframe.contentWindow; + + // Opening and closing the iframe tricks IE7 and earlier to push a + // history entry on hash-tag change. When replace is true, we don't + // want this. + if (!options.replace) { + iWindow.document.open(); + iWindow.document.close(); + } + + this._updateHash(iWindow.location, fragment, options.replace); + } + + // If you've told us that you explicitly don't want fallback hashchange- + // based history, then `navigate` becomes a page refresh. + } else { + return this.location.assign(url); + } + if (options.trigger) return this.loadUrl(fragment); + }, + + // Update the hash location, either replacing the current entry, or adding + // a new one to the browser history. + _updateHash: function(location, fragment, replace) { + if (replace) { + var href = location.href.replace(/(javascript:|#).*$/, ''); + location.replace(href + '#' + fragment); + } else { + // Some browsers require that `hash` contains a leading #. + location.hash = '#' + fragment; + } + } + + }); + + // Create the default Backbone.history. + Backbone.history = new History; + + // Helpers + // ------- + + // Helper function to correctly set up the prototype chain for subclasses. + // Similar to `goog.inherits`, but uses a hash of prototype properties and + // class properties to be extended. + var extend = function(protoProps, staticProps) { + var parent = this; + var child; + + // The constructor function for the new subclass is either defined by you + // (the "constructor" property in your `extend` definition), or defaulted + // by us to simply call the parent constructor. + if (protoProps && _.has(protoProps, 'constructor')) { + child = protoProps.constructor; + } else { + child = function(){ return parent.apply(this, arguments); }; + } + + // Add static properties to the constructor function, if supplied. + _.extend(child, parent, staticProps); + + // Set the prototype chain to inherit from `parent`, without calling + // `parent` constructor function. + var Surrogate = function(){ this.constructor = child; }; + Surrogate.prototype = parent.prototype; + child.prototype = new Surrogate; + + // Add prototype properties (instance properties) to the subclass, + // if supplied. + if (protoProps) _.extend(child.prototype, protoProps); + + // Set a convenience property in case the parent's prototype is needed + // later. + child.__super__ = parent.prototype; + + return child; + }; + + // Set up inheritance for the model, collection, router, view and history. + Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend; + + // Throw an error when a URL is needed, and none is supplied. + var urlError = function() { + throw new Error('A "url" property or function must be specified'); + }; + + // Wrap an optional error callback with a fallback error event. + var wrapError = function(model, options) { + var error = options.error; + options.error = function(resp) { + if (error) error.call(options.context, model, resp, options); + model.trigger('error', model, resp, options); + }; + }; + + return Backbone; + +})); diff --git a/db_structure.xml b/db_structure.xml index 6d1cf6973c5..870c0ab018d 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -102,6 +102,18 @@ <length>4</length> </field> + <field> + <name>available</name> + <type>boolean</type> + <default>true</default> + <notnull>true</notnull> + </field> + + <field> + <name>last_checked</name> + <type>integer</type> + </field> + <index> <name>storages_id_index</name> <unique>true</unique> diff --git a/lib/base.php b/lib/base.php index fde67839560..c0f3e50142e 100644 --- a/lib/base.php +++ b/lib/base.php @@ -407,6 +407,8 @@ class OC { OC_Util::addScript('mimetype'); OC_Util::addScript('mimetypelist'); OC_Util::addVendorScript('snapjs/dist/latest/snap'); + OC_Util::addVendorScript('core', 'backbone/backbone'); + OC_Util::addScript('oc-backbone'); // avatars if (\OC::$server->getSystemConfig()->getValue('enable_avatars', true) === true) { @@ -835,7 +837,9 @@ class OC { // Check if ownCloud is installed or in maintenance (update) mode if (!$systemConfig->getValue('installed', false)) { \OC::$server->getSession()->clear(); - $setupHelper = new OC\Setup(\OC::$server->getConfig(), \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'), new \OC_Defaults()); + $setupHelper = new OC\Setup(\OC::$server->getConfig(), \OC::$server->getIniWrapper(), + \OC::$server->getL10N('lib'), new \OC_Defaults(), \OC::$server->getLogger(), + \OC::$server->getSecureRandom()); $controller = new OC\Core\Setup\Controller($setupHelper); $controller->run($_POST); exit(); diff --git a/lib/l10n/ar.js b/lib/l10n/ar.js index 7b47710e6ea..9ec243672e1 100644 --- a/lib/l10n/ar.js +++ b/lib/l10n/ar.js @@ -26,14 +26,11 @@ OC.L10N.register( "%s enter the database username." : "%s ادخل اسم المستخدم الخاص بقاعدة البيانات.", "%s enter the database name." : "%s ادخل اسم فاعدة البيانات", "%s you may not use dots in the database name" : "%s لا يسمح لك باستخدام نقطه (.) في اسم قاعدة البيانات", - "MySQL/MariaDB username and/or password not valid" : "اسم مستخدم أو كلمة مرور MySQL/MariaDB غير صحيحين", - "You need to enter either an existing account or the administrator." : "انت بحاجة لكتابة اسم مستخدم موجود أو حساب المدير.", - "DB Error: \"%s\"" : "خطأ في قواعد البيانات : \"%s\"", - "Offending command was: \"%s\"" : "الأمر المخالف كان : \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "مستخدم MySQL/MariaDB '%s'@'localhost' موجود مسبقا", - "Drop this user from MySQL/MariaDB." : "حذف هذا المستخدم من MySQL/MariaDB", "Oracle connection could not be established" : "لم تنجح محاولة اتصال Oracle", "Oracle username and/or password not valid" : "اسم المستخدم و/أو كلمة المرور لنظام Oracle غير صحيح", + "DB Error: \"%s\"" : "خطأ في قواعد البيانات : \"%s\"", + "Offending command was: \"%s\"" : "الأمر المخالف كان : \"%s\"", + "You need to enter either an existing account or the administrator." : "انت بحاجة لكتابة اسم مستخدم موجود أو حساب المدير.", "Offending command was: \"%s\", name: %s, password: %s" : "الأمر المخالف كان : \"%s\", اسم المستخدم : %s, كلمة المرور: %s", "PostgreSQL username and/or password not valid" : "اسم المستخدم / أو كلمة المرور الخاصة بـPostgreSQL غير صحيحة", "Set an admin username." : "اعداد اسم مستخدم للمدير", diff --git a/lib/l10n/ar.json b/lib/l10n/ar.json index 5dbb256d8de..06d47dc255a 100644 --- a/lib/l10n/ar.json +++ b/lib/l10n/ar.json @@ -24,14 +24,11 @@ "%s enter the database username." : "%s ادخل اسم المستخدم الخاص بقاعدة البيانات.", "%s enter the database name." : "%s ادخل اسم فاعدة البيانات", "%s you may not use dots in the database name" : "%s لا يسمح لك باستخدام نقطه (.) في اسم قاعدة البيانات", - "MySQL/MariaDB username and/or password not valid" : "اسم مستخدم أو كلمة مرور MySQL/MariaDB غير صحيحين", - "You need to enter either an existing account or the administrator." : "انت بحاجة لكتابة اسم مستخدم موجود أو حساب المدير.", - "DB Error: \"%s\"" : "خطأ في قواعد البيانات : \"%s\"", - "Offending command was: \"%s\"" : "الأمر المخالف كان : \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "مستخدم MySQL/MariaDB '%s'@'localhost' موجود مسبقا", - "Drop this user from MySQL/MariaDB." : "حذف هذا المستخدم من MySQL/MariaDB", "Oracle connection could not be established" : "لم تنجح محاولة اتصال Oracle", "Oracle username and/or password not valid" : "اسم المستخدم و/أو كلمة المرور لنظام Oracle غير صحيح", + "DB Error: \"%s\"" : "خطأ في قواعد البيانات : \"%s\"", + "Offending command was: \"%s\"" : "الأمر المخالف كان : \"%s\"", + "You need to enter either an existing account or the administrator." : "انت بحاجة لكتابة اسم مستخدم موجود أو حساب المدير.", "Offending command was: \"%s\", name: %s, password: %s" : "الأمر المخالف كان : \"%s\", اسم المستخدم : %s, كلمة المرور: %s", "PostgreSQL username and/or password not valid" : "اسم المستخدم / أو كلمة المرور الخاصة بـPostgreSQL غير صحيحة", "Set an admin username." : "اعداد اسم مستخدم للمدير", diff --git a/lib/l10n/ast.js b/lib/l10n/ast.js index 84f56543087..3fb120b255b 100644 --- a/lib/l10n/ast.js +++ b/lib/l10n/ast.js @@ -45,16 +45,11 @@ OC.L10N.register( "%s enter the database username." : "%s introducir l'usuariu de la base de datos.", "%s enter the database name." : "%s introducir nome de la base de datos.", "%s you may not use dots in the database name" : "%s nun pues usar puntos nel nome de la base de datos", - "MySQL/MariaDB username and/or password not valid" : "Nome d'usuariu o contraseña MySQL/MariaDB non válidos", - "You need to enter either an existing account or the administrator." : "Tienes d'inxertar una cuenta esistente o la del alministrador.", - "DB Error: \"%s\"" : "Fallu BD: \"%s\"", - "Offending command was: \"%s\"" : "Comandu infractor: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "usuariu MySQL/MariaDB '%s'@'localhost' yá esiste.", - "Drop this user from MySQL/MariaDB" : "Desaniciar esti usuariu de MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Yá esiste l'usuariu de MySQL/MariaDB '%s'@'%%'", - "Drop this user from MySQL/MariaDB." : "Desaniciar esti usuariu de MySQL/MariaDB", "Oracle connection could not be established" : "Nun pudo afitase la conexón d'Oracle", "Oracle username and/or password not valid" : "Nome d'usuariu o contraseña d'Oracle non válidos", + "DB Error: \"%s\"" : "Fallu BD: \"%s\"", + "Offending command was: \"%s\"" : "Comandu infractor: \"%s\"", + "You need to enter either an existing account or the administrator." : "Tienes d'inxertar una cuenta esistente o la del alministrador.", "Offending command was: \"%s\", name: %s, password: %s" : "El comandu infractor foi: \"%s\", nome: %s, contraseña: %s", "PostgreSQL username and/or password not valid" : "Nome d'usuariu o contraseña PostgreSQL non válidos", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nun ta sofitáu y %s nun furrulará afayadizamente nesta plataforma. ¡Úsalu baxo'l to riesgu!", diff --git a/lib/l10n/ast.json b/lib/l10n/ast.json index 2cfbfa3c40c..f51d08ad731 100644 --- a/lib/l10n/ast.json +++ b/lib/l10n/ast.json @@ -43,16 +43,11 @@ "%s enter the database username." : "%s introducir l'usuariu de la base de datos.", "%s enter the database name." : "%s introducir nome de la base de datos.", "%s you may not use dots in the database name" : "%s nun pues usar puntos nel nome de la base de datos", - "MySQL/MariaDB username and/or password not valid" : "Nome d'usuariu o contraseña MySQL/MariaDB non válidos", - "You need to enter either an existing account or the administrator." : "Tienes d'inxertar una cuenta esistente o la del alministrador.", - "DB Error: \"%s\"" : "Fallu BD: \"%s\"", - "Offending command was: \"%s\"" : "Comandu infractor: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "usuariu MySQL/MariaDB '%s'@'localhost' yá esiste.", - "Drop this user from MySQL/MariaDB" : "Desaniciar esti usuariu de MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Yá esiste l'usuariu de MySQL/MariaDB '%s'@'%%'", - "Drop this user from MySQL/MariaDB." : "Desaniciar esti usuariu de MySQL/MariaDB", "Oracle connection could not be established" : "Nun pudo afitase la conexón d'Oracle", "Oracle username and/or password not valid" : "Nome d'usuariu o contraseña d'Oracle non válidos", + "DB Error: \"%s\"" : "Fallu BD: \"%s\"", + "Offending command was: \"%s\"" : "Comandu infractor: \"%s\"", + "You need to enter either an existing account or the administrator." : "Tienes d'inxertar una cuenta esistente o la del alministrador.", "Offending command was: \"%s\", name: %s, password: %s" : "El comandu infractor foi: \"%s\", nome: %s, contraseña: %s", "PostgreSQL username and/or password not valid" : "Nome d'usuariu o contraseña PostgreSQL non válidos", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nun ta sofitáu y %s nun furrulará afayadizamente nesta plataforma. ¡Úsalu baxo'l to riesgu!", diff --git a/lib/l10n/az.js b/lib/l10n/az.js index 36c1c342595..de944f63c0e 100644 --- a/lib/l10n/az.js +++ b/lib/l10n/az.js @@ -24,12 +24,9 @@ OC.L10N.register( "Unknown user" : "Istifadəçi tanınmır ", "%s enter the database username." : "Verilənlər bazası istifadəçi adını %s daxil et.", "%s enter the database name." : "Verilənlər bazası adını %s daxil et.", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB istifadəçi adı və/ya şifrəsi düzgün deyil :", - "DB Error: \"%s\"" : "DB səhvi: \"%s\"", - "Drop this user from MySQL/MariaDB" : "Bu istifadəçini MySQL/MariaDB-dən sil", - "Drop this user from MySQL/MariaDB." : "Bu istifadəçini MySQL/MariaDB-dən sil.", "Oracle connection could not be established" : "Oracle qoşulması alınmır", "Oracle username and/or password not valid" : "Oracle istifadəçi adı və/ya şifrəsi düzgün deyil", + "DB Error: \"%s\"" : "DB səhvi: \"%s\"", "Set an admin username." : "İnzibatçı istifadəçi adını təyin et.", "Set an admin password." : "İnzibatçı şifrəsini təyin et.", "%s shared »%s« with you" : "%s yayımlandı »%s« sizinlə", diff --git a/lib/l10n/az.json b/lib/l10n/az.json index ec86f64ae80..1c1fb247747 100644 --- a/lib/l10n/az.json +++ b/lib/l10n/az.json @@ -22,12 +22,9 @@ "Unknown user" : "Istifadəçi tanınmır ", "%s enter the database username." : "Verilənlər bazası istifadəçi adını %s daxil et.", "%s enter the database name." : "Verilənlər bazası adını %s daxil et.", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB istifadəçi adı və/ya şifrəsi düzgün deyil :", - "DB Error: \"%s\"" : "DB səhvi: \"%s\"", - "Drop this user from MySQL/MariaDB" : "Bu istifadəçini MySQL/MariaDB-dən sil", - "Drop this user from MySQL/MariaDB." : "Bu istifadəçini MySQL/MariaDB-dən sil.", "Oracle connection could not be established" : "Oracle qoşulması alınmır", "Oracle username and/or password not valid" : "Oracle istifadəçi adı və/ya şifrəsi düzgün deyil", + "DB Error: \"%s\"" : "DB səhvi: \"%s\"", "Set an admin username." : "İnzibatçı istifadəçi adını təyin et.", "Set an admin password." : "İnzibatçı şifrəsini təyin et.", "%s shared »%s« with you" : "%s yayımlandı »%s« sizinlə", diff --git a/lib/l10n/bg_BG.js b/lib/l10n/bg_BG.js index 62ae863e33a..a02d35061ef 100644 --- a/lib/l10n/bg_BG.js +++ b/lib/l10n/bg_BG.js @@ -52,16 +52,11 @@ OC.L10N.register( "%s enter the database username." : "%s въведи потребителско име за базата данни.", "%s enter the database name." : "%s въведи име на базата данни.", "%s you may not use dots in the database name" : "%s, не може да ползваш точки в името на базата данни.", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB потребителското име и/или паролата са невалидни.", - "You need to enter either an existing account or the administrator." : "Необходимо е да въведеш съществуващ профил или като администратор.", - "DB Error: \"%s\"" : "Грешка в базата данни: \"%s\".", - "Offending command was: \"%s\"" : "Проблемната команда беше: \"%s\".", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB потребител '%s'@'localhost' вече съществува.", - "Drop this user from MySQL/MariaDB" : "Премахни този потребител от MySQL/MariaDB.", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB потребител '%s'@'%%' вече съществува.", - "Drop this user from MySQL/MariaDB." : "Премахни този потребител от MySQL/MariaDB.", "Oracle connection could not be established" : "Oracle връзка не можа да се осъществи.", "Oracle username and/or password not valid" : "Невалидно Oracle потребителско име и/или парола.", + "DB Error: \"%s\"" : "Грешка в базата данни: \"%s\".", + "Offending command was: \"%s\"" : "Проблемната команда беше: \"%s\".", + "You need to enter either an existing account or the administrator." : "Необходимо е да въведеш съществуващ профил или като администратор.", "Offending command was: \"%s\", name: %s, password: %s" : "Проблемната команда беше: \"%s\", име: %s, парола: %s.", "PostgreSQL username and/or password not valid" : "Невалидно PostgreSQL потребителско име и/или парола.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X не се подържа и %s няма да работи правилно на тази платформа. Използвайте го на свой собствен риск!", diff --git a/lib/l10n/bg_BG.json b/lib/l10n/bg_BG.json index a68df436b81..3b9b899e53f 100644 --- a/lib/l10n/bg_BG.json +++ b/lib/l10n/bg_BG.json @@ -50,16 +50,11 @@ "%s enter the database username." : "%s въведи потребителско име за базата данни.", "%s enter the database name." : "%s въведи име на базата данни.", "%s you may not use dots in the database name" : "%s, не може да ползваш точки в името на базата данни.", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB потребителското име и/или паролата са невалидни.", - "You need to enter either an existing account or the administrator." : "Необходимо е да въведеш съществуващ профил или като администратор.", - "DB Error: \"%s\"" : "Грешка в базата данни: \"%s\".", - "Offending command was: \"%s\"" : "Проблемната команда беше: \"%s\".", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB потребител '%s'@'localhost' вече съществува.", - "Drop this user from MySQL/MariaDB" : "Премахни този потребител от MySQL/MariaDB.", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB потребител '%s'@'%%' вече съществува.", - "Drop this user from MySQL/MariaDB." : "Премахни този потребител от MySQL/MariaDB.", "Oracle connection could not be established" : "Oracle връзка не можа да се осъществи.", "Oracle username and/or password not valid" : "Невалидно Oracle потребителско име и/или парола.", + "DB Error: \"%s\"" : "Грешка в базата данни: \"%s\".", + "Offending command was: \"%s\"" : "Проблемната команда беше: \"%s\".", + "You need to enter either an existing account or the administrator." : "Необходимо е да въведеш съществуващ профил или като администратор.", "Offending command was: \"%s\", name: %s, password: %s" : "Проблемната команда беше: \"%s\", име: %s, парола: %s.", "PostgreSQL username and/or password not valid" : "Невалидно PostgreSQL потребителско име и/или парола.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X не се подържа и %s няма да работи правилно на тази платформа. Използвайте го на свой собствен риск!", diff --git a/lib/l10n/ca.js b/lib/l10n/ca.js index cd61c04b0a1..1a2b29e088b 100644 --- a/lib/l10n/ca.js +++ b/lib/l10n/ca.js @@ -44,16 +44,11 @@ OC.L10N.register( "%s enter the database username." : "%s escriviu el nom d'usuari de la base de dades.", "%s enter the database name." : "%s escriviu el nom de la base de dades.", "%s you may not use dots in the database name" : "%s no podeu usar punts en el nom de la base de dades", - "MySQL/MariaDB username and/or password not valid" : "El nom d'usuari i/o la contrasenya de MySQL/MariaDB no són vàlids", - "You need to enter either an existing account or the administrator." : "Heu d'escriure un compte existent o el d'administrador.", - "DB Error: \"%s\"" : "Error DB: \"%s\"", - "Offending command was: \"%s\"" : "L'ordre en conflicte és: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "L'usuari MySQL/MariaDB '%s'@'localhost' ja existeix.", - "Drop this user from MySQL/MariaDB" : "Esborreu aquest usuari de MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "L'usuari MySQL/MariaDB '%s'@'%%' ja existeix", - "Drop this user from MySQL/MariaDB." : "Esborreu aquest usuari de MySQL/MariaDB.", "Oracle connection could not be established" : "No s'ha pogut establir la connexió Oracle", "Oracle username and/or password not valid" : "Nom d'usuari i/o contrasenya Oracle no vàlids", + "DB Error: \"%s\"" : "Error DB: \"%s\"", + "Offending command was: \"%s\"" : "L'ordre en conflicte és: \"%s\"", + "You need to enter either an existing account or the administrator." : "Heu d'escriure un compte existent o el d'administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "L'ordre en conflicte és: \"%s\", nom: %s, contrasenya: %s", "PostgreSQL username and/or password not valid" : "Nom d'usuari i/o contrasenya PostgreSQL no vàlids", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X no té suport i %s no funcionarà correctament en aquesta plataforma. Useu-ho al vostre risc!", diff --git a/lib/l10n/ca.json b/lib/l10n/ca.json index 8341138d23f..2fac46f86a0 100644 --- a/lib/l10n/ca.json +++ b/lib/l10n/ca.json @@ -42,16 +42,11 @@ "%s enter the database username." : "%s escriviu el nom d'usuari de la base de dades.", "%s enter the database name." : "%s escriviu el nom de la base de dades.", "%s you may not use dots in the database name" : "%s no podeu usar punts en el nom de la base de dades", - "MySQL/MariaDB username and/or password not valid" : "El nom d'usuari i/o la contrasenya de MySQL/MariaDB no són vàlids", - "You need to enter either an existing account or the administrator." : "Heu d'escriure un compte existent o el d'administrador.", - "DB Error: \"%s\"" : "Error DB: \"%s\"", - "Offending command was: \"%s\"" : "L'ordre en conflicte és: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "L'usuari MySQL/MariaDB '%s'@'localhost' ja existeix.", - "Drop this user from MySQL/MariaDB" : "Esborreu aquest usuari de MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "L'usuari MySQL/MariaDB '%s'@'%%' ja existeix", - "Drop this user from MySQL/MariaDB." : "Esborreu aquest usuari de MySQL/MariaDB.", "Oracle connection could not be established" : "No s'ha pogut establir la connexió Oracle", "Oracle username and/or password not valid" : "Nom d'usuari i/o contrasenya Oracle no vàlids", + "DB Error: \"%s\"" : "Error DB: \"%s\"", + "Offending command was: \"%s\"" : "L'ordre en conflicte és: \"%s\"", + "You need to enter either an existing account or the administrator." : "Heu d'escriure un compte existent o el d'administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "L'ordre en conflicte és: \"%s\", nom: %s, contrasenya: %s", "PostgreSQL username and/or password not valid" : "Nom d'usuari i/o contrasenya PostgreSQL no vàlids", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X no té suport i %s no funcionarà correctament en aquesta plataforma. Useu-ho al vostre risc!", diff --git a/lib/l10n/cs_CZ.js b/lib/l10n/cs_CZ.js index 8c5f68e5e7e..8885ecd6e10 100644 --- a/lib/l10n/cs_CZ.js +++ b/lib/l10n/cs_CZ.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "Zadejte uživatelské jméno %s databáze.", "%s enter the database name." : "Zadejte název databáze pro %s databáze.", "%s you may not use dots in the database name" : "V názvu databáze %s nesmíte používat tečky.", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB uživatelské jméno a/nebo heslo je neplatné", - "You need to enter either an existing account or the administrator." : "Musíte zadat existující účet či správce.", - "DB Error: \"%s\"" : "Chyba databáze: \"%s\"", - "Offending command was: \"%s\"" : "Příslušný příkaz byl: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB uživatel '%s'@'localhost' již existuje.", - "Drop this user from MySQL/MariaDB" : "Smazat tohoto uživatele z MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB uživatel '%s'@'%%' již existuje", - "Drop this user from MySQL/MariaDB." : "Smazat tohoto uživatele z MySQL/MariaDB.", "Oracle connection could not be established" : "Spojení s Oracle nemohlo být navázáno", "Oracle username and/or password not valid" : "Uživatelské jméno či heslo Oracle není platné", + "DB Error: \"%s\"" : "Chyba databáze: \"%s\"", + "Offending command was: \"%s\"" : "Příslušný příkaz byl: \"%s\"", + "You need to enter either an existing account or the administrator." : "Musíte zadat existující účet či správce.", "Offending command was: \"%s\", name: %s, password: %s" : "Příslušný příkaz byl: \"%s\", jméno: %s, heslo: %s", "PostgreSQL username and/or password not valid" : "Uživatelské jméno či heslo PostgreSQL není platné", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X není podporován a %s nebude na této platformě správně fungovat. Používejte pouze na vlastní nebezpečí!", diff --git a/lib/l10n/cs_CZ.json b/lib/l10n/cs_CZ.json index 6a4e3556402..c5f50a73848 100644 --- a/lib/l10n/cs_CZ.json +++ b/lib/l10n/cs_CZ.json @@ -64,16 +64,11 @@ "%s enter the database username." : "Zadejte uživatelské jméno %s databáze.", "%s enter the database name." : "Zadejte název databáze pro %s databáze.", "%s you may not use dots in the database name" : "V názvu databáze %s nesmíte používat tečky.", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB uživatelské jméno a/nebo heslo je neplatné", - "You need to enter either an existing account or the administrator." : "Musíte zadat existující účet či správce.", - "DB Error: \"%s\"" : "Chyba databáze: \"%s\"", - "Offending command was: \"%s\"" : "Příslušný příkaz byl: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB uživatel '%s'@'localhost' již existuje.", - "Drop this user from MySQL/MariaDB" : "Smazat tohoto uživatele z MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB uživatel '%s'@'%%' již existuje", - "Drop this user from MySQL/MariaDB." : "Smazat tohoto uživatele z MySQL/MariaDB.", "Oracle connection could not be established" : "Spojení s Oracle nemohlo být navázáno", "Oracle username and/or password not valid" : "Uživatelské jméno či heslo Oracle není platné", + "DB Error: \"%s\"" : "Chyba databáze: \"%s\"", + "Offending command was: \"%s\"" : "Příslušný příkaz byl: \"%s\"", + "You need to enter either an existing account or the administrator." : "Musíte zadat existující účet či správce.", "Offending command was: \"%s\", name: %s, password: %s" : "Příslušný příkaz byl: \"%s\", jméno: %s, heslo: %s", "PostgreSQL username and/or password not valid" : "Uživatelské jméno či heslo PostgreSQL není platné", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X není podporován a %s nebude na této platformě správně fungovat. Používejte pouze na vlastní nebezpečí!", diff --git a/lib/l10n/cy_GB.js b/lib/l10n/cy_GB.js index 316c4560a9e..851e37b43ae 100644 --- a/lib/l10n/cy_GB.js +++ b/lib/l10n/cy_GB.js @@ -17,10 +17,10 @@ OC.L10N.register( "%s enter the database username." : "%s rhowch enw defnyddiwr y gronfa ddata.", "%s enter the database name." : "%s rhowch enw'r gronfa ddata.", "%s you may not use dots in the database name" : "%s does dim hawl defnyddio dot yn enw'r gronfa ddata", - "You need to enter either an existing account or the administrator." : "Rhaid i chi naill ai gyflwyno cyfrif presennol neu'r gweinyddwr.", + "Oracle username and/or password not valid" : "Enw a/neu gyfrinair Oracle annilys", "DB Error: \"%s\"" : "Gwall DB: \"%s\"", "Offending command was: \"%s\"" : "Y gorchymyn wnaeth beri tramgwydd oedd: \"%s\"", - "Oracle username and/or password not valid" : "Enw a/neu gyfrinair Oracle annilys", + "You need to enter either an existing account or the administrator." : "Rhaid i chi naill ai gyflwyno cyfrif presennol neu'r gweinyddwr.", "Offending command was: \"%s\", name: %s, password: %s" : "Y gorchymyn wnaeth beri tramgwydd oedd: \"%s\", enw: %s, cyfrinair: %s", "PostgreSQL username and/or password not valid" : "Enw a/neu gyfrinair PostgreSQL annilys", "Set an admin username." : "Creu enw defnyddiwr i'r gweinyddwr.", diff --git a/lib/l10n/cy_GB.json b/lib/l10n/cy_GB.json index 495a348e385..2088b3c2f9d 100644 --- a/lib/l10n/cy_GB.json +++ b/lib/l10n/cy_GB.json @@ -15,10 +15,10 @@ "%s enter the database username." : "%s rhowch enw defnyddiwr y gronfa ddata.", "%s enter the database name." : "%s rhowch enw'r gronfa ddata.", "%s you may not use dots in the database name" : "%s does dim hawl defnyddio dot yn enw'r gronfa ddata", - "You need to enter either an existing account or the administrator." : "Rhaid i chi naill ai gyflwyno cyfrif presennol neu'r gweinyddwr.", + "Oracle username and/or password not valid" : "Enw a/neu gyfrinair Oracle annilys", "DB Error: \"%s\"" : "Gwall DB: \"%s\"", "Offending command was: \"%s\"" : "Y gorchymyn wnaeth beri tramgwydd oedd: \"%s\"", - "Oracle username and/or password not valid" : "Enw a/neu gyfrinair Oracle annilys", + "You need to enter either an existing account or the administrator." : "Rhaid i chi naill ai gyflwyno cyfrif presennol neu'r gweinyddwr.", "Offending command was: \"%s\", name: %s, password: %s" : "Y gorchymyn wnaeth beri tramgwydd oedd: \"%s\", enw: %s, cyfrinair: %s", "PostgreSQL username and/or password not valid" : "Enw a/neu gyfrinair PostgreSQL annilys", "Set an admin username." : "Creu enw defnyddiwr i'r gweinyddwr.", diff --git a/lib/l10n/da.js b/lib/l10n/da.js index d7580be42e3..877f97ad8b1 100644 --- a/lib/l10n/da.js +++ b/lib/l10n/da.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s indtast database brugernavnet.", "%s enter the database name." : "%s indtast database navnet.", "%s you may not use dots in the database name" : "%s du må ikke bruge punktummer i databasenavnet.", - "MySQL/MariaDB username and/or password not valid" : "Ugyldigt MySQL/MariaDB brugernavn og/eller kodeord ", - "You need to enter either an existing account or the administrator." : "Du bliver nødt til at indtaste en eksisterende bruger eller en administrator.", - "DB Error: \"%s\"" : "Databasefejl: \"%s\"", - "Offending command was: \"%s\"" : "Fejlende kommando var: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB bruger '%s'@'localhost' eksistere allerede.", - "Drop this user from MySQL/MariaDB" : "Slet denne bruger fra MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB bruger '%s'@'%%' eksistere allerede", - "Drop this user from MySQL/MariaDB." : "Drop denne bruger fra MySQL/MariaDB.", "Oracle connection could not be established" : "Oracle forbindelsen kunne ikke etableres", "Oracle username and/or password not valid" : "Oracle brugernavn og/eller kodeord er ikke gyldigt.", + "DB Error: \"%s\"" : "Databasefejl: \"%s\"", + "Offending command was: \"%s\"" : "Fejlende kommando var: \"%s\"", + "You need to enter either an existing account or the administrator." : "Du bliver nødt til at indtaste en eksisterende bruger eller en administrator.", "Offending command was: \"%s\", name: %s, password: %s" : "Fejlende kommando var: \"%s\", navn: %s, password: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL brugernavn og/eller kodeord er ikke gyldigt.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X understøttes ikke og %s vil ikke virke optimalt på denne platform. Anvend på eget ansvar!", diff --git a/lib/l10n/da.json b/lib/l10n/da.json index 59e5b0b0498..bf5429d002e 100644 --- a/lib/l10n/da.json +++ b/lib/l10n/da.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s indtast database brugernavnet.", "%s enter the database name." : "%s indtast database navnet.", "%s you may not use dots in the database name" : "%s du må ikke bruge punktummer i databasenavnet.", - "MySQL/MariaDB username and/or password not valid" : "Ugyldigt MySQL/MariaDB brugernavn og/eller kodeord ", - "You need to enter either an existing account or the administrator." : "Du bliver nødt til at indtaste en eksisterende bruger eller en administrator.", - "DB Error: \"%s\"" : "Databasefejl: \"%s\"", - "Offending command was: \"%s\"" : "Fejlende kommando var: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB bruger '%s'@'localhost' eksistere allerede.", - "Drop this user from MySQL/MariaDB" : "Slet denne bruger fra MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB bruger '%s'@'%%' eksistere allerede", - "Drop this user from MySQL/MariaDB." : "Drop denne bruger fra MySQL/MariaDB.", "Oracle connection could not be established" : "Oracle forbindelsen kunne ikke etableres", "Oracle username and/or password not valid" : "Oracle brugernavn og/eller kodeord er ikke gyldigt.", + "DB Error: \"%s\"" : "Databasefejl: \"%s\"", + "Offending command was: \"%s\"" : "Fejlende kommando var: \"%s\"", + "You need to enter either an existing account or the administrator." : "Du bliver nødt til at indtaste en eksisterende bruger eller en administrator.", "Offending command was: \"%s\", name: %s, password: %s" : "Fejlende kommando var: \"%s\", navn: %s, password: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL brugernavn og/eller kodeord er ikke gyldigt.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X understøttes ikke og %s vil ikke virke optimalt på denne platform. Anvend på eget ansvar!", diff --git a/lib/l10n/de.js b/lib/l10n/de.js index a99213f8ac3..f4341e53b4d 100644 --- a/lib/l10n/de.js +++ b/lib/l10n/de.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s gib den Datenbank-Benutzernamen an.", "%s enter the database name." : "%s gib den Datenbanknamen an.", "%s you may not use dots in the database name" : "%s Der Datenbankname darf keine Punkte enthalten", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB-Benutzername und/oder -Passwort sind nicht gültig", - "You need to enter either an existing account or the administrator." : "Du musst entweder ein existierendes Benutzerkonto oder das Administratorenkonto angeben.", - "DB Error: \"%s\"" : "DB-Fehler: „%s“", - "Offending command was: \"%s\"" : "Fehlerhafter Befehl war: „%s“", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB Benutzer '%s'@'localhost' existiert bereits.", - "Drop this user from MySQL/MariaDB" : "Diesen Benutzer aus MySQL/MariaDB löschen", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB-Benutzer '%s'@'%%' existiert bereits", - "Drop this user from MySQL/MariaDB." : "Diesen Benutzer aus MySQL/MariaDB löschen.", "Oracle connection could not be established" : "Es konnte keine Verbindung zur Oracle-Datenbank hergestellt werden", "Oracle username and/or password not valid" : "Oracle-Benutzername und/oder -Passwort ungültig", + "DB Error: \"%s\"" : "DB-Fehler: „%s“", + "Offending command was: \"%s\"" : "Fehlerhafter Befehl war: „%s“", + "You need to enter either an existing account or the administrator." : "Du musst entweder ein existierendes Benutzerkonto oder das Administratorenkonto angeben.", "Offending command was: \"%s\", name: %s, password: %s" : "Fehlerhafter Befehl war: „%s“, Name: %s, Passwort: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL-Benutzername und/oder -Passwort ungültig", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X wird nicht unterstützt und %s wird auf dieser Plattform nicht richtig funktionieren. Die Benutzung erfolgt auf eigene Gefahr!", diff --git a/lib/l10n/de.json b/lib/l10n/de.json index 8cf5a7ebbd0..18dbd51317e 100644 --- a/lib/l10n/de.json +++ b/lib/l10n/de.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s gib den Datenbank-Benutzernamen an.", "%s enter the database name." : "%s gib den Datenbanknamen an.", "%s you may not use dots in the database name" : "%s Der Datenbankname darf keine Punkte enthalten", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB-Benutzername und/oder -Passwort sind nicht gültig", - "You need to enter either an existing account or the administrator." : "Du musst entweder ein existierendes Benutzerkonto oder das Administratorenkonto angeben.", - "DB Error: \"%s\"" : "DB-Fehler: „%s“", - "Offending command was: \"%s\"" : "Fehlerhafter Befehl war: „%s“", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB Benutzer '%s'@'localhost' existiert bereits.", - "Drop this user from MySQL/MariaDB" : "Diesen Benutzer aus MySQL/MariaDB löschen", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB-Benutzer '%s'@'%%' existiert bereits", - "Drop this user from MySQL/MariaDB." : "Diesen Benutzer aus MySQL/MariaDB löschen.", "Oracle connection could not be established" : "Es konnte keine Verbindung zur Oracle-Datenbank hergestellt werden", "Oracle username and/or password not valid" : "Oracle-Benutzername und/oder -Passwort ungültig", + "DB Error: \"%s\"" : "DB-Fehler: „%s“", + "Offending command was: \"%s\"" : "Fehlerhafter Befehl war: „%s“", + "You need to enter either an existing account or the administrator." : "Du musst entweder ein existierendes Benutzerkonto oder das Administratorenkonto angeben.", "Offending command was: \"%s\", name: %s, password: %s" : "Fehlerhafter Befehl war: „%s“, Name: %s, Passwort: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL-Benutzername und/oder -Passwort ungültig", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X wird nicht unterstützt und %s wird auf dieser Plattform nicht richtig funktionieren. Die Benutzung erfolgt auf eigene Gefahr!", diff --git a/lib/l10n/de_DE.js b/lib/l10n/de_DE.js index af8eb24550e..9eda034348f 100644 --- a/lib/l10n/de_DE.js +++ b/lib/l10n/de_DE.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s geben Sie den Datenbank-Benutzernamen an.", "%s enter the database name." : "%s geben Sie den Datenbank-Namen an.", "%s you may not use dots in the database name" : "%s Der Datenbank-Name darf keine Punkte enthalten", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB-Benutzername und/oder -Passwort sind nicht gültig", - "You need to enter either an existing account or the administrator." : "Sie müssen entweder ein existierendes Benutzerkonto oder das Administratoren-Konto angeben.", - "DB Error: \"%s\"" : "DB-Fehler: „%s“", - "Offending command was: \"%s\"" : "Fehlerhafter Befehl war: „%s“", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB Benutzer '%s'@'localhost' existiert bereits.", - "Drop this user from MySQL/MariaDB" : "Löschen Sie diesen Benutzer von MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB-Benutzer '%s'@'%%' existiert bereits", - "Drop this user from MySQL/MariaDB." : "Löschen Sie diesen Benutzer von MySQL/MariaDB.", "Oracle connection could not be established" : "Die Oracle-Verbindung konnte nicht aufgebaut werden.", "Oracle username and/or password not valid" : "Oracle-Benutzername und/oder -Passwort ungültig", + "DB Error: \"%s\"" : "DB-Fehler: „%s“", + "Offending command was: \"%s\"" : "Fehlerhafter Befehl war: „%s“", + "You need to enter either an existing account or the administrator." : "Sie müssen entweder ein existierendes Benutzerkonto oder das Administratoren-Konto angeben.", "Offending command was: \"%s\", name: %s, password: %s" : "Fehlerhafter Befehl war: „%s“, Name: %s, Passwort: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL-Benutzername und/oder -Passwort ungültig", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X wird nicht unterstützt und %s wird auf dieser Plattform nicht richtig funktionieren. Die Benutzung erfolgt auf eigene Gefahr!", diff --git a/lib/l10n/de_DE.json b/lib/l10n/de_DE.json index 2d4191141db..9a9b4341a56 100644 --- a/lib/l10n/de_DE.json +++ b/lib/l10n/de_DE.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s geben Sie den Datenbank-Benutzernamen an.", "%s enter the database name." : "%s geben Sie den Datenbank-Namen an.", "%s you may not use dots in the database name" : "%s Der Datenbank-Name darf keine Punkte enthalten", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB-Benutzername und/oder -Passwort sind nicht gültig", - "You need to enter either an existing account or the administrator." : "Sie müssen entweder ein existierendes Benutzerkonto oder das Administratoren-Konto angeben.", - "DB Error: \"%s\"" : "DB-Fehler: „%s“", - "Offending command was: \"%s\"" : "Fehlerhafter Befehl war: „%s“", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB Benutzer '%s'@'localhost' existiert bereits.", - "Drop this user from MySQL/MariaDB" : "Löschen Sie diesen Benutzer von MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB-Benutzer '%s'@'%%' existiert bereits", - "Drop this user from MySQL/MariaDB." : "Löschen Sie diesen Benutzer von MySQL/MariaDB.", "Oracle connection could not be established" : "Die Oracle-Verbindung konnte nicht aufgebaut werden.", "Oracle username and/or password not valid" : "Oracle-Benutzername und/oder -Passwort ungültig", + "DB Error: \"%s\"" : "DB-Fehler: „%s“", + "Offending command was: \"%s\"" : "Fehlerhafter Befehl war: „%s“", + "You need to enter either an existing account or the administrator." : "Sie müssen entweder ein existierendes Benutzerkonto oder das Administratoren-Konto angeben.", "Offending command was: \"%s\", name: %s, password: %s" : "Fehlerhafter Befehl war: „%s“, Name: %s, Passwort: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL-Benutzername und/oder -Passwort ungültig", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X wird nicht unterstützt und %s wird auf dieser Plattform nicht richtig funktionieren. Die Benutzung erfolgt auf eigene Gefahr!", diff --git a/lib/l10n/el.js b/lib/l10n/el.js index 213869e7758..2e47db30473 100644 --- a/lib/l10n/el.js +++ b/lib/l10n/el.js @@ -63,16 +63,11 @@ OC.L10N.register( "%s enter the database username." : "%s εισάγετε το όνομα χρήστη της βάσης δεδομένων.", "%s enter the database name." : "%s εισάγετε το όνομα της βάσης δεδομένων.", "%s you may not use dots in the database name" : "%s μάλλον δεν χρησιμοποιείτε τελείες στο όνομα της βάσης δεδομένων", - "MySQL/MariaDB username and/or password not valid" : "Μη έγκυρο όνομα χρήστη ή/και συνθηματικό της MySQL/MariaDB", - "You need to enter either an existing account or the administrator." : "Χρειάζεται να εισάγετε είτε έναν υπάρχον λογαριασμό ή του διαχειριστή.", - "DB Error: \"%s\"" : "Σφάλμα Βάσης Δεδομένων: \"%s\"", - "Offending command was: \"%s\"" : "Η εντολη παραβατικοτητας ηταν: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Υπάρχει ήδη ο χρήστης '%s'@'localhost' της MySQL/MariaDB", - "Drop this user from MySQL/MariaDB" : "Κατάργηση του χρήστη από MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Υπάρχει ήδη ο χρήστης '%s'@'%%' της MySQL/MariaDB", - "Drop this user from MySQL/MariaDB." : "Κατάργηση του χρήστη από MySQL/MariaDB.", "Oracle connection could not be established" : "Αδυναμία σύνδεσης Oracle", "Oracle username and/or password not valid" : "Μη έγκυρος χρήστης και/ή συνθηματικό της Oracle", + "DB Error: \"%s\"" : "Σφάλμα Βάσης Δεδομένων: \"%s\"", + "Offending command was: \"%s\"" : "Η εντολη παραβατικοτητας ηταν: \"%s\"", + "You need to enter either an existing account or the administrator." : "Χρειάζεται να εισάγετε είτε έναν υπάρχον λογαριασμό ή του διαχειριστή.", "Offending command was: \"%s\", name: %s, password: %s" : "Η εντολη παραβατικοτητας ηταν: \"%s\", ονομα: %s, κωδικος: %s", "PostgreSQL username and/or password not valid" : "Μη έγκυρος χρήστης και/ή συνθηματικό της PostgreSQL", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Το Mac OS X δεν υποστηρίζεται και το %s δεν θα λειτουργήσει σωστά σε αυτή την πλατφόρμα. Χρησιμοποιείτε με δική σας ευθύνη!", diff --git a/lib/l10n/el.json b/lib/l10n/el.json index 4b2527a396c..7b0b9b149ad 100644 --- a/lib/l10n/el.json +++ b/lib/l10n/el.json @@ -61,16 +61,11 @@ "%s enter the database username." : "%s εισάγετε το όνομα χρήστη της βάσης δεδομένων.", "%s enter the database name." : "%s εισάγετε το όνομα της βάσης δεδομένων.", "%s you may not use dots in the database name" : "%s μάλλον δεν χρησιμοποιείτε τελείες στο όνομα της βάσης δεδομένων", - "MySQL/MariaDB username and/or password not valid" : "Μη έγκυρο όνομα χρήστη ή/και συνθηματικό της MySQL/MariaDB", - "You need to enter either an existing account or the administrator." : "Χρειάζεται να εισάγετε είτε έναν υπάρχον λογαριασμό ή του διαχειριστή.", - "DB Error: \"%s\"" : "Σφάλμα Βάσης Δεδομένων: \"%s\"", - "Offending command was: \"%s\"" : "Η εντολη παραβατικοτητας ηταν: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Υπάρχει ήδη ο χρήστης '%s'@'localhost' της MySQL/MariaDB", - "Drop this user from MySQL/MariaDB" : "Κατάργηση του χρήστη από MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Υπάρχει ήδη ο χρήστης '%s'@'%%' της MySQL/MariaDB", - "Drop this user from MySQL/MariaDB." : "Κατάργηση του χρήστη από MySQL/MariaDB.", "Oracle connection could not be established" : "Αδυναμία σύνδεσης Oracle", "Oracle username and/or password not valid" : "Μη έγκυρος χρήστης και/ή συνθηματικό της Oracle", + "DB Error: \"%s\"" : "Σφάλμα Βάσης Δεδομένων: \"%s\"", + "Offending command was: \"%s\"" : "Η εντολη παραβατικοτητας ηταν: \"%s\"", + "You need to enter either an existing account or the administrator." : "Χρειάζεται να εισάγετε είτε έναν υπάρχον λογαριασμό ή του διαχειριστή.", "Offending command was: \"%s\", name: %s, password: %s" : "Η εντολη παραβατικοτητας ηταν: \"%s\", ονομα: %s, κωδικος: %s", "PostgreSQL username and/or password not valid" : "Μη έγκυρος χρήστης και/ή συνθηματικό της PostgreSQL", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Το Mac OS X δεν υποστηρίζεται και το %s δεν θα λειτουργήσει σωστά σε αυτή την πλατφόρμα. Χρησιμοποιείτε με δική σας ευθύνη!", diff --git a/lib/l10n/en_GB.js b/lib/l10n/en_GB.js index e388b0d1172..0e252700f5a 100644 --- a/lib/l10n/en_GB.js +++ b/lib/l10n/en_GB.js @@ -63,16 +63,11 @@ OC.L10N.register( "%s enter the database username." : "%s enter the database username.", "%s enter the database name." : "%s enter the database name.", "%s you may not use dots in the database name" : "%s you may not use dots in the database name", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB username and/or password not valid", - "You need to enter either an existing account or the administrator." : "You need to enter either an existing account or the administrator.", - "DB Error: \"%s\"" : "DB Error: \"%s\"", - "Offending command was: \"%s\"" : "Offending command was: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB user '%s'@'localhost' exists already.", - "Drop this user from MySQL/MariaDB" : "Drop this user from MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB user '%s'@'%%' already exists", - "Drop this user from MySQL/MariaDB." : "Drop this user from MySQL/MariaDB.", "Oracle connection could not be established" : "Oracle connection could not be established", "Oracle username and/or password not valid" : "Oracle username and/or password not valid", + "DB Error: \"%s\"" : "DB Error: \"%s\"", + "Offending command was: \"%s\"" : "Offending command was: \"%s\"", + "You need to enter either an existing account or the administrator." : "You need to enter either an existing account or the administrator.", "Offending command was: \"%s\", name: %s, password: %s" : "Offending command was: \"%s\", name: %s, password: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL username and/or password not valid", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! ", diff --git a/lib/l10n/en_GB.json b/lib/l10n/en_GB.json index b15f5c22147..9c57df59762 100644 --- a/lib/l10n/en_GB.json +++ b/lib/l10n/en_GB.json @@ -61,16 +61,11 @@ "%s enter the database username." : "%s enter the database username.", "%s enter the database name." : "%s enter the database name.", "%s you may not use dots in the database name" : "%s you may not use dots in the database name", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB username and/or password not valid", - "You need to enter either an existing account or the administrator." : "You need to enter either an existing account or the administrator.", - "DB Error: \"%s\"" : "DB Error: \"%s\"", - "Offending command was: \"%s\"" : "Offending command was: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB user '%s'@'localhost' exists already.", - "Drop this user from MySQL/MariaDB" : "Drop this user from MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB user '%s'@'%%' already exists", - "Drop this user from MySQL/MariaDB." : "Drop this user from MySQL/MariaDB.", "Oracle connection could not be established" : "Oracle connection could not be established", "Oracle username and/or password not valid" : "Oracle username and/or password not valid", + "DB Error: \"%s\"" : "DB Error: \"%s\"", + "Offending command was: \"%s\"" : "Offending command was: \"%s\"", + "You need to enter either an existing account or the administrator." : "You need to enter either an existing account or the administrator.", "Offending command was: \"%s\", name: %s, password: %s" : "Offending command was: \"%s\", name: %s, password: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL username and/or password not valid", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! ", diff --git a/lib/l10n/eo.js b/lib/l10n/eo.js index 429936513b6..df94b174599 100644 --- a/lib/l10n/eo.js +++ b/lib/l10n/eo.js @@ -37,12 +37,9 @@ OC.L10N.register( "%s enter the database username." : "%s enigu la uzantonomon de la datumbazo.", "%s enter the database name." : "%s enigu la nomon de la datumbazo.", "%s you may not use dots in the database name" : "%s vi ne povas uzi punktojn en la nomo de la datumbazo", - "MySQL/MariaDB username and/or password not valid" : "La MySQL/MariaDB-uzantonomo kajaŭ pasvorto ne validas.", - "DB Error: \"%s\"" : "Datumbaza eraro: “%s”", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "La MySQL/MariaDB-uzanto '%s'@'localhost' jam ekzistas.", - "MySQL/MariaDB user '%s'@'%%' already exists" : "La MySQL/MariaDB-uzanto '%s'@'%%' jam ekzistas", "Oracle connection could not be established" : "Konekto al Oracle ne povas stariĝi", "Oracle username and/or password not valid" : "La uzantonomo de Oracle aŭ la pasvorto ne validas", + "DB Error: \"%s\"" : "Datumbaza eraro: “%s”", "PostgreSQL username and/or password not valid" : "La uzantonomo de PostgreSQL aŭ la pasvorto ne validas", "Set an admin username." : "Starigi administran uzantonomon.", "Set an admin password." : "Starigi administran pasvorton.", diff --git a/lib/l10n/eo.json b/lib/l10n/eo.json index ede9a189486..8140c421d57 100644 --- a/lib/l10n/eo.json +++ b/lib/l10n/eo.json @@ -35,12 +35,9 @@ "%s enter the database username." : "%s enigu la uzantonomon de la datumbazo.", "%s enter the database name." : "%s enigu la nomon de la datumbazo.", "%s you may not use dots in the database name" : "%s vi ne povas uzi punktojn en la nomo de la datumbazo", - "MySQL/MariaDB username and/or password not valid" : "La MySQL/MariaDB-uzantonomo kajaŭ pasvorto ne validas.", - "DB Error: \"%s\"" : "Datumbaza eraro: “%s”", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "La MySQL/MariaDB-uzanto '%s'@'localhost' jam ekzistas.", - "MySQL/MariaDB user '%s'@'%%' already exists" : "La MySQL/MariaDB-uzanto '%s'@'%%' jam ekzistas", "Oracle connection could not be established" : "Konekto al Oracle ne povas stariĝi", "Oracle username and/or password not valid" : "La uzantonomo de Oracle aŭ la pasvorto ne validas", + "DB Error: \"%s\"" : "Datumbaza eraro: “%s”", "PostgreSQL username and/or password not valid" : "La uzantonomo de PostgreSQL aŭ la pasvorto ne validas", "Set an admin username." : "Starigi administran uzantonomon.", "Set an admin password." : "Starigi administran pasvorton.", diff --git a/lib/l10n/es.js b/lib/l10n/es.js index 620a8e9eff5..d071dc624e0 100644 --- a/lib/l10n/es.js +++ b/lib/l10n/es.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s ingresar el usuario de la base de datos.", "%s enter the database name." : "%s ingresar el nombre de la base de datos", "%s you may not use dots in the database name" : "%s puede utilizar puntos en el nombre de la base de datos", - "MySQL/MariaDB username and/or password not valid" : "Nombre de usuario y/o contraseña de MySQL/MariaDB inválidos", - "You need to enter either an existing account or the administrator." : "Tiene que ingresar una cuenta existente o la del administrador.", - "DB Error: \"%s\"" : "Error BD: \"%s\"", - "Offending command was: \"%s\"" : "Comando infractor: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "El usuario de MySQL/MariaDB '%s'@'localhost' ya existe.", - "Drop this user from MySQL/MariaDB" : "Eliminar este usuario de MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "El usuario de MySQL/MariaDB '%s'@'%%' ya existe", - "Drop this user from MySQL/MariaDB." : "Eliminar este usuario de MySQL/MariaDB.", "Oracle connection could not be established" : "No se pudo establecer la conexión a Oracle", "Oracle username and/or password not valid" : "Usuario y/o contraseña de Oracle no válidos", + "DB Error: \"%s\"" : "Error BD: \"%s\"", + "Offending command was: \"%s\"" : "Comando infractor: \"%s\"", + "You need to enter either an existing account or the administrator." : "Tiene que ingresar una cuenta existente o la del administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "Comando infractor: \"%s\", nombre: %s, contraseña: %s", "PostgreSQL username and/or password not valid" : "Usuario y/o contraseña de PostgreSQL no válidos", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X no está soportado y %s no funcionará bien en esta plataforma. ¡Úsela bajo su propio riesgo! ", diff --git a/lib/l10n/es.json b/lib/l10n/es.json index 226b89768e7..28655f2c92d 100644 --- a/lib/l10n/es.json +++ b/lib/l10n/es.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s ingresar el usuario de la base de datos.", "%s enter the database name." : "%s ingresar el nombre de la base de datos", "%s you may not use dots in the database name" : "%s puede utilizar puntos en el nombre de la base de datos", - "MySQL/MariaDB username and/or password not valid" : "Nombre de usuario y/o contraseña de MySQL/MariaDB inválidos", - "You need to enter either an existing account or the administrator." : "Tiene que ingresar una cuenta existente o la del administrador.", - "DB Error: \"%s\"" : "Error BD: \"%s\"", - "Offending command was: \"%s\"" : "Comando infractor: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "El usuario de MySQL/MariaDB '%s'@'localhost' ya existe.", - "Drop this user from MySQL/MariaDB" : "Eliminar este usuario de MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "El usuario de MySQL/MariaDB '%s'@'%%' ya existe", - "Drop this user from MySQL/MariaDB." : "Eliminar este usuario de MySQL/MariaDB.", "Oracle connection could not be established" : "No se pudo establecer la conexión a Oracle", "Oracle username and/or password not valid" : "Usuario y/o contraseña de Oracle no válidos", + "DB Error: \"%s\"" : "Error BD: \"%s\"", + "Offending command was: \"%s\"" : "Comando infractor: \"%s\"", + "You need to enter either an existing account or the administrator." : "Tiene que ingresar una cuenta existente o la del administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "Comando infractor: \"%s\", nombre: %s, contraseña: %s", "PostgreSQL username and/or password not valid" : "Usuario y/o contraseña de PostgreSQL no válidos", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X no está soportado y %s no funcionará bien en esta plataforma. ¡Úsela bajo su propio riesgo! ", diff --git a/lib/l10n/es_AR.js b/lib/l10n/es_AR.js index ea372e9fdd6..572933980cb 100644 --- a/lib/l10n/es_AR.js +++ b/lib/l10n/es_AR.js @@ -35,11 +35,11 @@ OC.L10N.register( "%s enter the database username." : "%s Entrá el usuario de la base de datos", "%s enter the database name." : "%s Entrá el nombre de la base de datos.", "%s you may not use dots in the database name" : "%s no podés usar puntos en el nombre de la base de datos", - "You need to enter either an existing account or the administrator." : "Tenés que ingresar una cuenta existente o el administrador.", - "DB Error: \"%s\"" : "Error DB: \"%s\"", - "Offending command was: \"%s\"" : "El comando no comprendido es: \"%s\"", "Oracle connection could not be established" : "No fue posible establecer la conexión a Oracle", "Oracle username and/or password not valid" : "El nombre de usuario y/o contraseña no son válidos", + "DB Error: \"%s\"" : "Error DB: \"%s\"", + "Offending command was: \"%s\"" : "El comando no comprendido es: \"%s\"", + "You need to enter either an existing account or the administrator." : "Tenés que ingresar una cuenta existente o el administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "El comando no comprendido es: \"%s\", nombre: \"%s\", contraseña: \"%s\"", "PostgreSQL username and/or password not valid" : "Nombre de usuario o contraseña PostgradeSQL inválido.", "Set an admin username." : "Configurar un nombre de administrador.", diff --git a/lib/l10n/es_AR.json b/lib/l10n/es_AR.json index bb0ee769129..9a459d7117d 100644 --- a/lib/l10n/es_AR.json +++ b/lib/l10n/es_AR.json @@ -33,11 +33,11 @@ "%s enter the database username." : "%s Entrá el usuario de la base de datos", "%s enter the database name." : "%s Entrá el nombre de la base de datos.", "%s you may not use dots in the database name" : "%s no podés usar puntos en el nombre de la base de datos", - "You need to enter either an existing account or the administrator." : "Tenés que ingresar una cuenta existente o el administrador.", - "DB Error: \"%s\"" : "Error DB: \"%s\"", - "Offending command was: \"%s\"" : "El comando no comprendido es: \"%s\"", "Oracle connection could not be established" : "No fue posible establecer la conexión a Oracle", "Oracle username and/or password not valid" : "El nombre de usuario y/o contraseña no son válidos", + "DB Error: \"%s\"" : "Error DB: \"%s\"", + "Offending command was: \"%s\"" : "El comando no comprendido es: \"%s\"", + "You need to enter either an existing account or the administrator." : "Tenés que ingresar una cuenta existente o el administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "El comando no comprendido es: \"%s\", nombre: \"%s\", contraseña: \"%s\"", "PostgreSQL username and/or password not valid" : "Nombre de usuario o contraseña PostgradeSQL inválido.", "Set an admin username." : "Configurar un nombre de administrador.", diff --git a/lib/l10n/es_MX.js b/lib/l10n/es_MX.js index bfc9fb50b94..253b5fce3ab 100644 --- a/lib/l10n/es_MX.js +++ b/lib/l10n/es_MX.js @@ -41,11 +41,11 @@ OC.L10N.register( "%s enter the database username." : "%s ingresar el usuario de la base de datos.", "%s enter the database name." : "%s ingresar el nombre de la base de datos", "%s you may not use dots in the database name" : "%s puede utilizar puntos en el nombre de la base de datos", - "You need to enter either an existing account or the administrator." : "Tiene que ingresar una cuenta existente o la del administrador.", - "DB Error: \"%s\"" : "Error BD: \"%s\"", - "Offending command was: \"%s\"" : "Comando infractor: \"%s\"", "Oracle connection could not be established" : "No se pudo establecer la conexión a Oracle", "Oracle username and/or password not valid" : "Usuario y/o contraseña de Oracle no válidos", + "DB Error: \"%s\"" : "Error BD: \"%s\"", + "Offending command was: \"%s\"" : "Comando infractor: \"%s\"", + "You need to enter either an existing account or the administrator." : "Tiene que ingresar una cuenta existente o la del administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "Comando infractor: \"%s\", nombre: %s, contraseña: %s", "PostgreSQL username and/or password not valid" : "Usuario y/o contraseña de PostgreSQL no válidos", "Set an admin username." : "Configurar un nombre de usuario del administrador", diff --git a/lib/l10n/es_MX.json b/lib/l10n/es_MX.json index ac64ac35141..0bd876d2e70 100644 --- a/lib/l10n/es_MX.json +++ b/lib/l10n/es_MX.json @@ -39,11 +39,11 @@ "%s enter the database username." : "%s ingresar el usuario de la base de datos.", "%s enter the database name." : "%s ingresar el nombre de la base de datos", "%s you may not use dots in the database name" : "%s puede utilizar puntos en el nombre de la base de datos", - "You need to enter either an existing account or the administrator." : "Tiene que ingresar una cuenta existente o la del administrador.", - "DB Error: \"%s\"" : "Error BD: \"%s\"", - "Offending command was: \"%s\"" : "Comando infractor: \"%s\"", "Oracle connection could not be established" : "No se pudo establecer la conexión a Oracle", "Oracle username and/or password not valid" : "Usuario y/o contraseña de Oracle no válidos", + "DB Error: \"%s\"" : "Error BD: \"%s\"", + "Offending command was: \"%s\"" : "Comando infractor: \"%s\"", + "You need to enter either an existing account or the administrator." : "Tiene que ingresar una cuenta existente o la del administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "Comando infractor: \"%s\", nombre: %s, contraseña: %s", "PostgreSQL username and/or password not valid" : "Usuario y/o contraseña de PostgreSQL no válidos", "Set an admin username." : "Configurar un nombre de usuario del administrador", diff --git a/lib/l10n/et_EE.js b/lib/l10n/et_EE.js index 7f224210f81..9ab86b3a415 100644 --- a/lib/l10n/et_EE.js +++ b/lib/l10n/et_EE.js @@ -8,6 +8,12 @@ OC.L10N.register( "Sample configuration detected" : "Tuvastati näidisseaded", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Tuvastati, et kopeeriti näidisseaded. See võib lõhkuda sinu saidi ja see pole toetatud. Palun loe enne faili config.php muutmist dokumentatsiooni", "PHP %s or higher is required." : "PHP %s või uuem on nõutav.", + "PHP with a version lower than %s is required." : "Nõutud on PHP madalama versiooniga kui %s.", + "Following databases are supported: %s" : "Toetatud on järgnevad andmebaasid: %s", + "The command line tool %s could not be found" : "Käsurea töövahendit %s ei leitud", + "The library %s is not available." : "Teek %s pole saadaval.", + "Following platforms are supported: %s" : "Toetatud on järgnevad platformid: %s", + "ownCloud %s or higher is required." : "ownCloud %s või uuem on nõutav.", "Help" : "Abiinfo", "Personal" : "Isiklik", "Users" : "Kasutajad", @@ -48,16 +54,11 @@ OC.L10N.register( "%s enter the database username." : "%s sisesta andmebaasi kasutajatunnus.", "%s enter the database name." : "%s sisesta andmebaasi nimi.", "%s you may not use dots in the database name" : "%s punktide kasutamine andmebaasi nimes pole lubatud", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB kasutajatunnus ja/või parool pole õiged", - "You need to enter either an existing account or the administrator." : "Sisesta kas juba olemasolev konto või administrator.", - "DB Error: \"%s\"" : "Andmebaasi viga: \"%s\"", - "Offending command was: \"%s\"" : "Tõrkuv käsk oli: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB kasutaja '%s'@'localhost' on juba olemas.", - "Drop this user from MySQL/MariaDB" : "Kustuta see MySQL/MariaDB kasutaja", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB user '%s'@'%%' on juba olemas", - "Drop this user from MySQL/MariaDB." : "Kustuta see MySQL/MariaDB kasutaja.", "Oracle connection could not be established" : "Ei suuda luua ühendust Oracle baasiga", "Oracle username and/or password not valid" : "Oracle kasutajatunnus ja/või parool pole õiged", + "DB Error: \"%s\"" : "Andmebaasi viga: \"%s\"", + "Offending command was: \"%s\"" : "Tõrkuv käsk oli: \"%s\"", + "You need to enter either an existing account or the administrator." : "Sisesta kas juba olemasolev konto või administrator.", "Offending command was: \"%s\", name: %s, password: %s" : "Tõrkuv käsk oli: \"%s\", nimi: %s, parool: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL kasutajatunnus ja/või parool pole õiged", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X ei ole toetatud ja %s ei pruugi korralikult toimida sellel platvormil. Kasuta seda omal vastutusel!", @@ -66,6 +67,7 @@ OC.L10N.register( "Set an admin password." : "Määra admini parool.", "Can't create or write into the data directory %s" : "Ei suuda luua või kirjutada andmete kataloogi %s", "%s shared »%s« with you" : "%s jagas sinuga »%s«", + "%s via %s" : "%s läbi %s", "Sharing %s failed, because the file does not exist" : "%s jagamine ebaõnnestus, kuna faili pole olemas", "You are not allowed to share %s" : "Sul pole lubatud %s jagada", "Sharing %s failed, because the user %s is the item owner" : "%s jagamine ebaõnnestus, kuna kuna kasutaja %s on üksuse omanik", @@ -81,6 +83,7 @@ OC.L10N.register( "Setting permissions for %s failed, because the item was not found" : "Lubade seadistus %s jaoks ebaõnnestus, kuna üksust ei leitud", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Aegumise kuupäeva ei saa määrata. Jagamised ei saa aeguda hiljem kui %s peale jagamist.", "Cannot set expiration date. Expiration date is in the past" : "Aegumiskuupäeva ei saa määrata. Aegumise kuupäev on minevikus", + "Cannot clear expiration date. Shares are required to have an expiration date." : "Aegumise kuupäeva ei saa tühjendada. Jagamistel peab olema aegumise kuupäev.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Jagamise tagarakend %s peab kasutusele võtma OCP\\Share_Backend liidese", "Sharing backend %s not found" : "Jagamise tagarakendit %s ei leitud", "Sharing backend for %s not found" : "Jagamise tagarakendit %s jaoks ei leitud", @@ -95,6 +98,7 @@ OC.L10N.register( "A valid password must be provided" : "Sisesta nõuetele vastav parool", "The username is already being used" : "Kasutajanimi on juba kasutuses", "No database drivers (sqlite, mysql, or postgresql) installed." : "Ühtegi andmebaasi (sqlite, mysql või postgresql) draiverit pole paigaldatud.", + "Microsoft Windows Platform is not supported" : "Microsoft Windows platform pole toetatud", "Cannot write into \"config\" directory" : "Ei saa kirjutada \"config\" kataloogi", "Cannot write into \"apps\" directory" : "Ei saa kirjutada \"apps\" kataloogi!", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Tavaliselt saab selle lahendada %s andes veebiserverile rakendite kataloogile kirjutusõigused %s või keelates seadetes rakendikogu.", diff --git a/lib/l10n/et_EE.json b/lib/l10n/et_EE.json index a4692185d70..acbb36fe431 100644 --- a/lib/l10n/et_EE.json +++ b/lib/l10n/et_EE.json @@ -6,6 +6,12 @@ "Sample configuration detected" : "Tuvastati näidisseaded", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Tuvastati, et kopeeriti näidisseaded. See võib lõhkuda sinu saidi ja see pole toetatud. Palun loe enne faili config.php muutmist dokumentatsiooni", "PHP %s or higher is required." : "PHP %s või uuem on nõutav.", + "PHP with a version lower than %s is required." : "Nõutud on PHP madalama versiooniga kui %s.", + "Following databases are supported: %s" : "Toetatud on järgnevad andmebaasid: %s", + "The command line tool %s could not be found" : "Käsurea töövahendit %s ei leitud", + "The library %s is not available." : "Teek %s pole saadaval.", + "Following platforms are supported: %s" : "Toetatud on järgnevad platformid: %s", + "ownCloud %s or higher is required." : "ownCloud %s või uuem on nõutav.", "Help" : "Abiinfo", "Personal" : "Isiklik", "Users" : "Kasutajad", @@ -46,16 +52,11 @@ "%s enter the database username." : "%s sisesta andmebaasi kasutajatunnus.", "%s enter the database name." : "%s sisesta andmebaasi nimi.", "%s you may not use dots in the database name" : "%s punktide kasutamine andmebaasi nimes pole lubatud", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB kasutajatunnus ja/või parool pole õiged", - "You need to enter either an existing account or the administrator." : "Sisesta kas juba olemasolev konto või administrator.", - "DB Error: \"%s\"" : "Andmebaasi viga: \"%s\"", - "Offending command was: \"%s\"" : "Tõrkuv käsk oli: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB kasutaja '%s'@'localhost' on juba olemas.", - "Drop this user from MySQL/MariaDB" : "Kustuta see MySQL/MariaDB kasutaja", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB user '%s'@'%%' on juba olemas", - "Drop this user from MySQL/MariaDB." : "Kustuta see MySQL/MariaDB kasutaja.", "Oracle connection could not be established" : "Ei suuda luua ühendust Oracle baasiga", "Oracle username and/or password not valid" : "Oracle kasutajatunnus ja/või parool pole õiged", + "DB Error: \"%s\"" : "Andmebaasi viga: \"%s\"", + "Offending command was: \"%s\"" : "Tõrkuv käsk oli: \"%s\"", + "You need to enter either an existing account or the administrator." : "Sisesta kas juba olemasolev konto või administrator.", "Offending command was: \"%s\", name: %s, password: %s" : "Tõrkuv käsk oli: \"%s\", nimi: %s, parool: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL kasutajatunnus ja/või parool pole õiged", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X ei ole toetatud ja %s ei pruugi korralikult toimida sellel platvormil. Kasuta seda omal vastutusel!", @@ -64,6 +65,7 @@ "Set an admin password." : "Määra admini parool.", "Can't create or write into the data directory %s" : "Ei suuda luua või kirjutada andmete kataloogi %s", "%s shared »%s« with you" : "%s jagas sinuga »%s«", + "%s via %s" : "%s läbi %s", "Sharing %s failed, because the file does not exist" : "%s jagamine ebaõnnestus, kuna faili pole olemas", "You are not allowed to share %s" : "Sul pole lubatud %s jagada", "Sharing %s failed, because the user %s is the item owner" : "%s jagamine ebaõnnestus, kuna kuna kasutaja %s on üksuse omanik", @@ -79,6 +81,7 @@ "Setting permissions for %s failed, because the item was not found" : "Lubade seadistus %s jaoks ebaõnnestus, kuna üksust ei leitud", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Aegumise kuupäeva ei saa määrata. Jagamised ei saa aeguda hiljem kui %s peale jagamist.", "Cannot set expiration date. Expiration date is in the past" : "Aegumiskuupäeva ei saa määrata. Aegumise kuupäev on minevikus", + "Cannot clear expiration date. Shares are required to have an expiration date." : "Aegumise kuupäeva ei saa tühjendada. Jagamistel peab olema aegumise kuupäev.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Jagamise tagarakend %s peab kasutusele võtma OCP\\Share_Backend liidese", "Sharing backend %s not found" : "Jagamise tagarakendit %s ei leitud", "Sharing backend for %s not found" : "Jagamise tagarakendit %s jaoks ei leitud", @@ -93,6 +96,7 @@ "A valid password must be provided" : "Sisesta nõuetele vastav parool", "The username is already being used" : "Kasutajanimi on juba kasutuses", "No database drivers (sqlite, mysql, or postgresql) installed." : "Ühtegi andmebaasi (sqlite, mysql või postgresql) draiverit pole paigaldatud.", + "Microsoft Windows Platform is not supported" : "Microsoft Windows platform pole toetatud", "Cannot write into \"config\" directory" : "Ei saa kirjutada \"config\" kataloogi", "Cannot write into \"apps\" directory" : "Ei saa kirjutada \"apps\" kataloogi!", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Tavaliselt saab selle lahendada %s andes veebiserverile rakendite kataloogile kirjutusõigused %s või keelates seadetes rakendikogu.", diff --git a/lib/l10n/eu.js b/lib/l10n/eu.js index 4a0ffb3b893..6bf5d8f0315 100644 --- a/lib/l10n/eu.js +++ b/lib/l10n/eu.js @@ -54,16 +54,11 @@ OC.L10N.register( "%s enter the database username." : "%s sartu datu basearen erabiltzaile izena.", "%s enter the database name." : "%s sartu datu basearen izena.", "%s you may not use dots in the database name" : "%s ezin duzu punturik erabili datu basearen izenean.", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB erabiltzaile edota pasahitza ez dira egokiak", - "You need to enter either an existing account or the administrator." : "Existitzen den kontu bat edo administradorearena jarri behar duzu.", - "DB Error: \"%s\"" : "DB errorea: \"%s\"", - "Offending command was: \"%s\"" : "Errorea komando honek sortu du: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB '%s'@'localhost' erabiltzailea dagoeneko existitzen da.", - "Drop this user from MySQL/MariaDB" : "Ezabatu erabiltzaile hau MySQL/MariaDBtik", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB '%s'@'%%' erabiltzailea dagoeneko existitzen da", - "Drop this user from MySQL/MariaDB." : "Ezabatu erabiltzaile hau MySQL/MariaDBtik.", "Oracle connection could not be established" : "Ezin da Oracle konexioa sortu", "Oracle username and/or password not valid" : "Oracle erabiltzaile edota pasahitza ez dira egokiak.", + "DB Error: \"%s\"" : "DB errorea: \"%s\"", + "Offending command was: \"%s\"" : "Errorea komando honek sortu du: \"%s\"", + "You need to enter either an existing account or the administrator." : "Existitzen den kontu bat edo administradorearena jarri behar duzu.", "Offending command was: \"%s\", name: %s, password: %s" : "Errorea komando honek sortu du: \"%s\", izena: %s, pasahitza: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL erabiltzaile edota pasahitza ez dira egokiak.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X-ek ez du sostengurik eta %s gaizki ibili daiteke plataforma honetan. Erabiltzekotan, zure ardurapean.", diff --git a/lib/l10n/eu.json b/lib/l10n/eu.json index 631bc569732..2f9c4033615 100644 --- a/lib/l10n/eu.json +++ b/lib/l10n/eu.json @@ -52,16 +52,11 @@ "%s enter the database username." : "%s sartu datu basearen erabiltzaile izena.", "%s enter the database name." : "%s sartu datu basearen izena.", "%s you may not use dots in the database name" : "%s ezin duzu punturik erabili datu basearen izenean.", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB erabiltzaile edota pasahitza ez dira egokiak", - "You need to enter either an existing account or the administrator." : "Existitzen den kontu bat edo administradorearena jarri behar duzu.", - "DB Error: \"%s\"" : "DB errorea: \"%s\"", - "Offending command was: \"%s\"" : "Errorea komando honek sortu du: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB '%s'@'localhost' erabiltzailea dagoeneko existitzen da.", - "Drop this user from MySQL/MariaDB" : "Ezabatu erabiltzaile hau MySQL/MariaDBtik", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB '%s'@'%%' erabiltzailea dagoeneko existitzen da", - "Drop this user from MySQL/MariaDB." : "Ezabatu erabiltzaile hau MySQL/MariaDBtik.", "Oracle connection could not be established" : "Ezin da Oracle konexioa sortu", "Oracle username and/or password not valid" : "Oracle erabiltzaile edota pasahitza ez dira egokiak.", + "DB Error: \"%s\"" : "DB errorea: \"%s\"", + "Offending command was: \"%s\"" : "Errorea komando honek sortu du: \"%s\"", + "You need to enter either an existing account or the administrator." : "Existitzen den kontu bat edo administradorearena jarri behar duzu.", "Offending command was: \"%s\", name: %s, password: %s" : "Errorea komando honek sortu du: \"%s\", izena: %s, pasahitza: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL erabiltzaile edota pasahitza ez dira egokiak.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X-ek ez du sostengurik eta %s gaizki ibili daiteke plataforma honetan. Erabiltzekotan, zure ardurapean.", diff --git a/lib/l10n/fa.js b/lib/l10n/fa.js index 10c5c9b78cc..8e3a0c6071f 100644 --- a/lib/l10n/fa.js +++ b/lib/l10n/fa.js @@ -22,11 +22,11 @@ OC.L10N.register( "%s enter the database username." : "%s نام کاربری پایگاه داده را وارد نمایید.", "%s enter the database name." : "%s نام پایگاه داده را وارد نمایید.", "%s you may not use dots in the database name" : "%s شما نباید از نقطه در نام پایگاه داده استفاده نمایید.", - "You need to enter either an existing account or the administrator." : "شما نیاز به وارد کردن یک حساب کاربری موجود یا حساب مدیریتی دارید.", - "DB Error: \"%s\"" : "خطای پایگاه داده: \"%s\"", - "Offending command was: \"%s\"" : "دستور متخلف عبارت است از: \"%s\"", "Oracle connection could not be established" : "ارتباط اراکل نمیتواند برقرار باشد.", "Oracle username and/or password not valid" : "نام کاربری و / یا رمزعبور اراکل معتبر نیست.", + "DB Error: \"%s\"" : "خطای پایگاه داده: \"%s\"", + "Offending command was: \"%s\"" : "دستور متخلف عبارت است از: \"%s\"", + "You need to enter either an existing account or the administrator." : "شما نیاز به وارد کردن یک حساب کاربری موجود یا حساب مدیریتی دارید.", "Offending command was: \"%s\", name: %s, password: %s" : "دستور متخلف عبارت است از: \"%s\"، نام: \"%s\"، رمزعبور:\"%s\"", "PostgreSQL username and/or password not valid" : "PostgreSQL نام کاربری و / یا رمزعبور معتبر نیست.", "Set an admin username." : "یک نام کاربری برای مدیر تنظیم نمایید.", diff --git a/lib/l10n/fa.json b/lib/l10n/fa.json index ee6b7669341..f434f6517af 100644 --- a/lib/l10n/fa.json +++ b/lib/l10n/fa.json @@ -20,11 +20,11 @@ "%s enter the database username." : "%s نام کاربری پایگاه داده را وارد نمایید.", "%s enter the database name." : "%s نام پایگاه داده را وارد نمایید.", "%s you may not use dots in the database name" : "%s شما نباید از نقطه در نام پایگاه داده استفاده نمایید.", - "You need to enter either an existing account or the administrator." : "شما نیاز به وارد کردن یک حساب کاربری موجود یا حساب مدیریتی دارید.", - "DB Error: \"%s\"" : "خطای پایگاه داده: \"%s\"", - "Offending command was: \"%s\"" : "دستور متخلف عبارت است از: \"%s\"", "Oracle connection could not be established" : "ارتباط اراکل نمیتواند برقرار باشد.", "Oracle username and/or password not valid" : "نام کاربری و / یا رمزعبور اراکل معتبر نیست.", + "DB Error: \"%s\"" : "خطای پایگاه داده: \"%s\"", + "Offending command was: \"%s\"" : "دستور متخلف عبارت است از: \"%s\"", + "You need to enter either an existing account or the administrator." : "شما نیاز به وارد کردن یک حساب کاربری موجود یا حساب مدیریتی دارید.", "Offending command was: \"%s\", name: %s, password: %s" : "دستور متخلف عبارت است از: \"%s\"، نام: \"%s\"، رمزعبور:\"%s\"", "PostgreSQL username and/or password not valid" : "PostgreSQL نام کاربری و / یا رمزعبور معتبر نیست.", "Set an admin username." : "یک نام کاربری برای مدیر تنظیم نمایید.", diff --git a/lib/l10n/fi_FI.js b/lib/l10n/fi_FI.js index 39083305ae9..3a06e5e4f6e 100644 --- a/lib/l10n/fi_FI.js +++ b/lib/l10n/fi_FI.js @@ -63,15 +63,10 @@ OC.L10N.register( "%s enter the database username." : "%s anna tietokannan käyttäjätunnus.", "%s enter the database name." : "%s anna tietokannan nimi.", "%s you may not use dots in the database name" : "%s et voi käyttää pisteitä tietokannan nimessä", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB-käyttäjätunnus ja/tai salasana on virheellinen", - "DB Error: \"%s\"" : "Tietokantavirhe: \"%s\"", - "Offending command was: \"%s\"" : "Loukkaava komento oli: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB-käyttäjä '%s'@'localhost' on jo olemassa.", - "Drop this user from MySQL/MariaDB" : "Pudota tämä käyttäjä MySQL/MariaDB:stä", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB-käyttäjä '%s'@'%%' on jo olemassa", - "Drop this user from MySQL/MariaDB." : "Pudota tämä käyttäjä MySQL/MariaDB:stä.", "Oracle connection could not be established" : "Oracle-yhteyttä ei voitu muodostaa", "Oracle username and/or password not valid" : "Oraclen käyttäjätunnus ja/tai salasana on väärin", + "DB Error: \"%s\"" : "Tietokantavirhe: \"%s\"", + "Offending command was: \"%s\"" : "Loukkaava komento oli: \"%s\"", "Offending command was: \"%s\", name: %s, password: %s" : "Loukkaava komento oli: \"%s\", nimi: %s, salasana: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL:n käyttäjätunnus ja/tai salasana on väärin", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X ei ole tuettu, joten %s ei toimi kunnolla tällä alustalla. Käytä omalla vastuulla!", diff --git a/lib/l10n/fi_FI.json b/lib/l10n/fi_FI.json index a9bb243d1ba..27fe7745881 100644 --- a/lib/l10n/fi_FI.json +++ b/lib/l10n/fi_FI.json @@ -61,15 +61,10 @@ "%s enter the database username." : "%s anna tietokannan käyttäjätunnus.", "%s enter the database name." : "%s anna tietokannan nimi.", "%s you may not use dots in the database name" : "%s et voi käyttää pisteitä tietokannan nimessä", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB-käyttäjätunnus ja/tai salasana on virheellinen", - "DB Error: \"%s\"" : "Tietokantavirhe: \"%s\"", - "Offending command was: \"%s\"" : "Loukkaava komento oli: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB-käyttäjä '%s'@'localhost' on jo olemassa.", - "Drop this user from MySQL/MariaDB" : "Pudota tämä käyttäjä MySQL/MariaDB:stä", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB-käyttäjä '%s'@'%%' on jo olemassa", - "Drop this user from MySQL/MariaDB." : "Pudota tämä käyttäjä MySQL/MariaDB:stä.", "Oracle connection could not be established" : "Oracle-yhteyttä ei voitu muodostaa", "Oracle username and/or password not valid" : "Oraclen käyttäjätunnus ja/tai salasana on väärin", + "DB Error: \"%s\"" : "Tietokantavirhe: \"%s\"", + "Offending command was: \"%s\"" : "Loukkaava komento oli: \"%s\"", "Offending command was: \"%s\", name: %s, password: %s" : "Loukkaava komento oli: \"%s\", nimi: %s, salasana: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL:n käyttäjätunnus ja/tai salasana on väärin", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X ei ole tuettu, joten %s ei toimi kunnolla tällä alustalla. Käytä omalla vastuulla!", diff --git a/lib/l10n/fr.js b/lib/l10n/fr.js index 6c5eeaf6201..8e7de3462a3 100644 --- a/lib/l10n/fr.js +++ b/lib/l10n/fr.js @@ -65,16 +65,11 @@ OC.L10N.register( "%s enter the database username." : "%s entrez le nom d'utilisateur de la base de données.", "%s enter the database name." : "%s entrez le nom de la base de données.", "%s you may not use dots in the database name" : "%s vous ne pouvez pas utiliser de points dans le nom de la base de données", - "MySQL/MariaDB username and/or password not valid" : "Nom d'utilisateur et/ou mot de passe MySQL/MariaDB non valide", - "You need to enter either an existing account or the administrator." : "Vous devez spécifier le nom d'un compte existant, ou celui de l'administrateur.", - "DB Error: \"%s\"" : "Erreur de la base de données : \"%s\"", - "Offending command was: \"%s\"" : "La requête en cause est : \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "L'utilisateur MySQL/MariaDB '%s'@'localhost' existe déjà.", - "Drop this user from MySQL/MariaDB" : "Supprimez cet utilisateur de la base MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "L'utilisateur MySQL/MariaDB '%s'@'%%' existe déjà", - "Drop this user from MySQL/MariaDB." : "Supprimez cet utilisateur de la base MySQL/MariaDB.", "Oracle connection could not be established" : "La connexion Oracle ne peut être établie", "Oracle username and/or password not valid" : "Nom d'utilisateur et/ou mot de passe de la base Oracle non valide(s)", + "DB Error: \"%s\"" : "Erreur de la base de données : \"%s\"", + "Offending command was: \"%s\"" : "La requête en cause est : \"%s\"", + "You need to enter either an existing account or the administrator." : "Vous devez spécifier le nom d'un compte existant, ou celui de l'administrateur.", "Offending command was: \"%s\", name: %s, password: %s" : "La requête en cause est : \"%s\", nom : %s, mot de passe : %s", "PostgreSQL username and/or password not valid" : "Nom d'utilisateur et/ou mot de passe de la base PostgreSQL non valide(s)", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X n'est pas pris en charge et %s ne fonctionnera pas correctement sur cette plate-forme. Son utilisation est à vos risques et périls !", diff --git a/lib/l10n/fr.json b/lib/l10n/fr.json index 83abadc866f..25bcf5b17ee 100644 --- a/lib/l10n/fr.json +++ b/lib/l10n/fr.json @@ -63,16 +63,11 @@ "%s enter the database username." : "%s entrez le nom d'utilisateur de la base de données.", "%s enter the database name." : "%s entrez le nom de la base de données.", "%s you may not use dots in the database name" : "%s vous ne pouvez pas utiliser de points dans le nom de la base de données", - "MySQL/MariaDB username and/or password not valid" : "Nom d'utilisateur et/ou mot de passe MySQL/MariaDB non valide", - "You need to enter either an existing account or the administrator." : "Vous devez spécifier le nom d'un compte existant, ou celui de l'administrateur.", - "DB Error: \"%s\"" : "Erreur de la base de données : \"%s\"", - "Offending command was: \"%s\"" : "La requête en cause est : \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "L'utilisateur MySQL/MariaDB '%s'@'localhost' existe déjà.", - "Drop this user from MySQL/MariaDB" : "Supprimez cet utilisateur de la base MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "L'utilisateur MySQL/MariaDB '%s'@'%%' existe déjà", - "Drop this user from MySQL/MariaDB." : "Supprimez cet utilisateur de la base MySQL/MariaDB.", "Oracle connection could not be established" : "La connexion Oracle ne peut être établie", "Oracle username and/or password not valid" : "Nom d'utilisateur et/ou mot de passe de la base Oracle non valide(s)", + "DB Error: \"%s\"" : "Erreur de la base de données : \"%s\"", + "Offending command was: \"%s\"" : "La requête en cause est : \"%s\"", + "You need to enter either an existing account or the administrator." : "Vous devez spécifier le nom d'un compte existant, ou celui de l'administrateur.", "Offending command was: \"%s\", name: %s, password: %s" : "La requête en cause est : \"%s\", nom : %s, mot de passe : %s", "PostgreSQL username and/or password not valid" : "Nom d'utilisateur et/ou mot de passe de la base PostgreSQL non valide(s)", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X n'est pas pris en charge et %s ne fonctionnera pas correctement sur cette plate-forme. Son utilisation est à vos risques et périls !", diff --git a/lib/l10n/gl.js b/lib/l10n/gl.js index db549cbb36a..bc2afd3f6e5 100644 --- a/lib/l10n/gl.js +++ b/lib/l10n/gl.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s introduza o nome de usuario da base de datos", "%s enter the database name." : "%s introduza o nome da base de datos", "%s you may not use dots in the database name" : "%s non se poden empregar puntos na base de datos", - "MySQL/MariaDB username and/or password not valid" : "O nome e/ou o contrasinal do usuario de MySQL/MariaDB non é correcto", - "You need to enter either an existing account or the administrator." : "Deberá introducir unha conta existente ou o administrador.", - "DB Error: \"%s\"" : "Produciuse un erro na base de datos: «%s»", - "Offending command was: \"%s\"" : "A orde infractora foi: «%s»", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Xa existe o usuario «%s»@«localhost» no MySQL/MariaDB.", - "Drop this user from MySQL/MariaDB" : "Eliminar este usuario do MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Xa existe o usuario «%s»@«%%» no MySQL/MariaDB", - "Drop this user from MySQL/MariaDB." : "Eliminar este usuario do MySQL/MariaDB.", "Oracle connection could not be established" : "Non foi posíbel estabelecer a conexión con Oracle", "Oracle username and/or password not valid" : "O nome de usuario e/ou contrasinal de Oracle é incorrecto", + "DB Error: \"%s\"" : "Produciuse un erro na base de datos: «%s»", + "Offending command was: \"%s\"" : "A orde infractora foi: «%s»", + "You need to enter either an existing account or the administrator." : "Deberá introducir unha conta existente ou o administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "A orde infractora foi: «%s», nome: %s, contrasinal: %s", "PostgreSQL username and/or password not valid" : "Nome de usuario e/ou contrasinal de PostgreSQL incorrecto", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X non é compatíbel e %s non funcionará correctamente nesta plataforma. Utilíceo baixo a súa responsabilidade!", diff --git a/lib/l10n/gl.json b/lib/l10n/gl.json index 9c0260ca2ac..9607f9fad18 100644 --- a/lib/l10n/gl.json +++ b/lib/l10n/gl.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s introduza o nome de usuario da base de datos", "%s enter the database name." : "%s introduza o nome da base de datos", "%s you may not use dots in the database name" : "%s non se poden empregar puntos na base de datos", - "MySQL/MariaDB username and/or password not valid" : "O nome e/ou o contrasinal do usuario de MySQL/MariaDB non é correcto", - "You need to enter either an existing account or the administrator." : "Deberá introducir unha conta existente ou o administrador.", - "DB Error: \"%s\"" : "Produciuse un erro na base de datos: «%s»", - "Offending command was: \"%s\"" : "A orde infractora foi: «%s»", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Xa existe o usuario «%s»@«localhost» no MySQL/MariaDB.", - "Drop this user from MySQL/MariaDB" : "Eliminar este usuario do MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Xa existe o usuario «%s»@«%%» no MySQL/MariaDB", - "Drop this user from MySQL/MariaDB." : "Eliminar este usuario do MySQL/MariaDB.", "Oracle connection could not be established" : "Non foi posíbel estabelecer a conexión con Oracle", "Oracle username and/or password not valid" : "O nome de usuario e/ou contrasinal de Oracle é incorrecto", + "DB Error: \"%s\"" : "Produciuse un erro na base de datos: «%s»", + "Offending command was: \"%s\"" : "A orde infractora foi: «%s»", + "You need to enter either an existing account or the administrator." : "Deberá introducir unha conta existente ou o administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "A orde infractora foi: «%s», nome: %s, contrasinal: %s", "PostgreSQL username and/or password not valid" : "Nome de usuario e/ou contrasinal de PostgreSQL incorrecto", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X non é compatíbel e %s non funcionará correctamente nesta plataforma. Utilíceo baixo a súa responsabilidade!", diff --git a/lib/l10n/hr.js b/lib/l10n/hr.js index eabef9e3a52..16ac6ff2821 100644 --- a/lib/l10n/hr.js +++ b/lib/l10n/hr.js @@ -49,16 +49,11 @@ OC.L10N.register( "%s enter the database username." : "%s unesite naziva korisnika baze podataka.", "%s enter the database name." : "%s unesite naziv baze podataka", "%s you may not use dots in the database name" : "%s ne smijete koristiti točke u nazivu baze podataka", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB korisničko ime i/ili lozinka neispravni", - "You need to enter either an existing account or the administrator." : "Trebate unijeti neki postojeći račun ili administratora.", - "DB Error: \"%s\"" : "DB pogreška: \"%s\"", - "Offending command was: \"%s\"" : "Neispravna naredba je bila: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB korisnik '%s'@'localhost' već postoji.", - "Drop this user from MySQL/MariaDB" : "Ispustite ovog korisnika iz MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB korisnik '%s'@'%%' već postoji", - "Drop this user from MySQL/MariaDB." : "Ispustite ovog korisnika iz MySQL/MariaDB.", "Oracle connection could not be established" : "Vezu Oracle nije moguće uspostaviti", "Oracle username and/or password not valid" : "Korisničko ime i/ili lozinka Oracle neispravni", + "DB Error: \"%s\"" : "DB pogreška: \"%s\"", + "Offending command was: \"%s\"" : "Neispravna naredba je bila: \"%s\"", + "You need to enter either an existing account or the administrator." : "Trebate unijeti neki postojeći račun ili administratora.", "Offending command was: \"%s\", name: %s, password: %s" : "Neispravna naredba je bila: \"%s\", ime: %s, lozinka: %s", "PostgreSQL username and/or password not valid" : "Korisničko ime i/ili lozinka PostgreSQL neispravni", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nije podržan i %s na ovoj platformi neće raditi kako treba.", diff --git a/lib/l10n/hr.json b/lib/l10n/hr.json index cacf151453d..291e59261ba 100644 --- a/lib/l10n/hr.json +++ b/lib/l10n/hr.json @@ -47,16 +47,11 @@ "%s enter the database username." : "%s unesite naziva korisnika baze podataka.", "%s enter the database name." : "%s unesite naziv baze podataka", "%s you may not use dots in the database name" : "%s ne smijete koristiti točke u nazivu baze podataka", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB korisničko ime i/ili lozinka neispravni", - "You need to enter either an existing account or the administrator." : "Trebate unijeti neki postojeći račun ili administratora.", - "DB Error: \"%s\"" : "DB pogreška: \"%s\"", - "Offending command was: \"%s\"" : "Neispravna naredba je bila: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB korisnik '%s'@'localhost' već postoji.", - "Drop this user from MySQL/MariaDB" : "Ispustite ovog korisnika iz MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB korisnik '%s'@'%%' već postoji", - "Drop this user from MySQL/MariaDB." : "Ispustite ovog korisnika iz MySQL/MariaDB.", "Oracle connection could not be established" : "Vezu Oracle nije moguće uspostaviti", "Oracle username and/or password not valid" : "Korisničko ime i/ili lozinka Oracle neispravni", + "DB Error: \"%s\"" : "DB pogreška: \"%s\"", + "Offending command was: \"%s\"" : "Neispravna naredba je bila: \"%s\"", + "You need to enter either an existing account or the administrator." : "Trebate unijeti neki postojeći račun ili administratora.", "Offending command was: \"%s\", name: %s, password: %s" : "Neispravna naredba je bila: \"%s\", ime: %s, lozinka: %s", "PostgreSQL username and/or password not valid" : "Korisničko ime i/ili lozinka PostgreSQL neispravni", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nije podržan i %s na ovoj platformi neće raditi kako treba.", diff --git a/lib/l10n/hu_HU.js b/lib/l10n/hu_HU.js index c90d9737cae..6c0d797441e 100644 --- a/lib/l10n/hu_HU.js +++ b/lib/l10n/hu_HU.js @@ -43,16 +43,11 @@ OC.L10N.register( "%s enter the database username." : "%s adja meg az adatbázist elérő felhasználó login nevét.", "%s enter the database name." : "%s adja meg az adatbázis nevét.", "%s you may not use dots in the database name" : "%s az adatbázis neve nem tartalmazhat pontot", - "MySQL/MariaDB username and/or password not valid" : "A MySQL/MariaDB felhasználónév és/vagy jelszó nem megfelelő", - "You need to enter either an existing account or the administrator." : "Vagy egy létező felhasználó vagy az adminisztrátor bejelentkezési nevét kell megadnia", - "DB Error: \"%s\"" : "Adatbázis hiba: \"%s\"", - "Offending command was: \"%s\"" : "A hibát ez a parancs okozta: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "A MySQL/MariaDB felhasználó '%s'@'localhost' már létezik.", - "Drop this user from MySQL/MariaDB" : "Töröljük ez a felhasználót a MySQL/MariaDB-rendszerből", - "MySQL/MariaDB user '%s'@'%%' already exists" : "A MySQL/MariaDB felhasználó '%s'@'%%' már létezik.", - "Drop this user from MySQL/MariaDB." : "Töröljük ez a felhasználót a MySQL/MariaDB-rendszerből.", "Oracle connection could not be established" : "Az Oracle kapcsolat nem hozható létre", "Oracle username and/or password not valid" : "Az Oracle felhasználói név és/vagy jelszó érvénytelen", + "DB Error: \"%s\"" : "Adatbázis hiba: \"%s\"", + "Offending command was: \"%s\"" : "A hibát ez a parancs okozta: \"%s\"", + "You need to enter either an existing account or the administrator." : "Vagy egy létező felhasználó vagy az adminisztrátor bejelentkezési nevét kell megadnia", "Offending command was: \"%s\", name: %s, password: %s" : "A hibát okozó parancs ez volt: \"%s\", login név: %s, jelszó: %s", "PostgreSQL username and/or password not valid" : "A PostgreSQL felhasználói név és/vagy jelszó érvénytelen", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "A Mac OS X nem támogatott és %s nem lesz teljesen működőképes. Csak saját felelősségre használja!", diff --git a/lib/l10n/hu_HU.json b/lib/l10n/hu_HU.json index 773a06297b3..1c717e6a4cf 100644 --- a/lib/l10n/hu_HU.json +++ b/lib/l10n/hu_HU.json @@ -41,16 +41,11 @@ "%s enter the database username." : "%s adja meg az adatbázist elérő felhasználó login nevét.", "%s enter the database name." : "%s adja meg az adatbázis nevét.", "%s you may not use dots in the database name" : "%s az adatbázis neve nem tartalmazhat pontot", - "MySQL/MariaDB username and/or password not valid" : "A MySQL/MariaDB felhasználónév és/vagy jelszó nem megfelelő", - "You need to enter either an existing account or the administrator." : "Vagy egy létező felhasználó vagy az adminisztrátor bejelentkezési nevét kell megadnia", - "DB Error: \"%s\"" : "Adatbázis hiba: \"%s\"", - "Offending command was: \"%s\"" : "A hibát ez a parancs okozta: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "A MySQL/MariaDB felhasználó '%s'@'localhost' már létezik.", - "Drop this user from MySQL/MariaDB" : "Töröljük ez a felhasználót a MySQL/MariaDB-rendszerből", - "MySQL/MariaDB user '%s'@'%%' already exists" : "A MySQL/MariaDB felhasználó '%s'@'%%' már létezik.", - "Drop this user from MySQL/MariaDB." : "Töröljük ez a felhasználót a MySQL/MariaDB-rendszerből.", "Oracle connection could not be established" : "Az Oracle kapcsolat nem hozható létre", "Oracle username and/or password not valid" : "Az Oracle felhasználói név és/vagy jelszó érvénytelen", + "DB Error: \"%s\"" : "Adatbázis hiba: \"%s\"", + "Offending command was: \"%s\"" : "A hibát ez a parancs okozta: \"%s\"", + "You need to enter either an existing account or the administrator." : "Vagy egy létező felhasználó vagy az adminisztrátor bejelentkezési nevét kell megadnia", "Offending command was: \"%s\", name: %s, password: %s" : "A hibát okozó parancs ez volt: \"%s\", login név: %s, jelszó: %s", "PostgreSQL username and/or password not valid" : "A PostgreSQL felhasználói név és/vagy jelszó érvénytelen", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "A Mac OS X nem támogatott és %s nem lesz teljesen működőképes. Csak saját felelősségre használja!", diff --git a/lib/l10n/id.js b/lib/l10n/id.js index 3643a6a6ef7..432ea5c0192 100644 --- a/lib/l10n/id.js +++ b/lib/l10n/id.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s masukkan nama pengguna basis data.", "%s enter the database name." : "%s masukkan nama basis data.", "%s you may not use dots in the database name" : "%s anda tidak boleh menggunakan karakter titik pada nama basis data", - "MySQL/MariaDB username and/or password not valid" : "Nama pengguna dan/atau sandi MySQL/MariaDB tidak sah", - "You need to enter either an existing account or the administrator." : "Anda harus memasukkan akun yang sudah ada atau administrator.", - "DB Error: \"%s\"" : "Kesalahan Basis Data: \"%s\"", - "Offending command was: \"%s\"" : "Perintah yang bermasalah: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "'%s'@'localhost' pengguna MySQL/MariaDB sudah ada.", - "Drop this user from MySQL/MariaDB" : "Drop pengguna ini dari MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "'%s'@'%%' pengguna MySQL/MariaDB sudah ada.", - "Drop this user from MySQL/MariaDB." : "Drop pengguna ini dari MySQL/MariaDB.", "Oracle connection could not be established" : "Koneksi Oracle tidak dapat dibuat", "Oracle username and/or password not valid" : "Nama pengguna dan/atau sandi Oracle tidak sah", + "DB Error: \"%s\"" : "Kesalahan Basis Data: \"%s\"", + "Offending command was: \"%s\"" : "Perintah yang bermasalah: \"%s\"", + "You need to enter either an existing account or the administrator." : "Anda harus memasukkan akun yang sudah ada atau administrator.", "Offending command was: \"%s\", name: %s, password: %s" : "Perintah yang bermasalah: \"%s\", nama pengguna: %s, sandi: %s", "PostgreSQL username and/or password not valid" : "Nama pengguna dan/atau sandi PostgreSQL tidak valid", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X tidak didukung dan %s tidak akan bekerja dengan baik pada platform ini. Gunakan dengan resiko Anda sendiri!", diff --git a/lib/l10n/id.json b/lib/l10n/id.json index 18135d0e470..6aeffa363da 100644 --- a/lib/l10n/id.json +++ b/lib/l10n/id.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s masukkan nama pengguna basis data.", "%s enter the database name." : "%s masukkan nama basis data.", "%s you may not use dots in the database name" : "%s anda tidak boleh menggunakan karakter titik pada nama basis data", - "MySQL/MariaDB username and/or password not valid" : "Nama pengguna dan/atau sandi MySQL/MariaDB tidak sah", - "You need to enter either an existing account or the administrator." : "Anda harus memasukkan akun yang sudah ada atau administrator.", - "DB Error: \"%s\"" : "Kesalahan Basis Data: \"%s\"", - "Offending command was: \"%s\"" : "Perintah yang bermasalah: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "'%s'@'localhost' pengguna MySQL/MariaDB sudah ada.", - "Drop this user from MySQL/MariaDB" : "Drop pengguna ini dari MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "'%s'@'%%' pengguna MySQL/MariaDB sudah ada.", - "Drop this user from MySQL/MariaDB." : "Drop pengguna ini dari MySQL/MariaDB.", "Oracle connection could not be established" : "Koneksi Oracle tidak dapat dibuat", "Oracle username and/or password not valid" : "Nama pengguna dan/atau sandi Oracle tidak sah", + "DB Error: \"%s\"" : "Kesalahan Basis Data: \"%s\"", + "Offending command was: \"%s\"" : "Perintah yang bermasalah: \"%s\"", + "You need to enter either an existing account or the administrator." : "Anda harus memasukkan akun yang sudah ada atau administrator.", "Offending command was: \"%s\", name: %s, password: %s" : "Perintah yang bermasalah: \"%s\", nama pengguna: %s, sandi: %s", "PostgreSQL username and/or password not valid" : "Nama pengguna dan/atau sandi PostgreSQL tidak valid", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X tidak didukung dan %s tidak akan bekerja dengan baik pada platform ini. Gunakan dengan resiko Anda sendiri!", diff --git a/lib/l10n/it.js b/lib/l10n/it.js index 7b86d4d30b0..df79ffed2ed 100644 --- a/lib/l10n/it.js +++ b/lib/l10n/it.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s digita il nome utente del database.", "%s enter the database name." : "%s digita il nome del database.", "%s you may not use dots in the database name" : "%s non dovresti utilizzare punti nel nome del database", - "MySQL/MariaDB username and/or password not valid" : "Nome utente e/o password di MySQL/MariaDB non validi", - "You need to enter either an existing account or the administrator." : "È necessario inserire un account esistente o l'amministratore.", - "DB Error: \"%s\"" : "Errore DB: \"%s\"", - "Offending command was: \"%s\"" : "Il comando non consentito era: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "L'utente MySQL/MariaDB '%s'@'localhost' esiste già.", - "Drop this user from MySQL/MariaDB" : "Elimina questo utente da MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "L'utente MySQL/MariaDB '%s'@'%%' esiste già", - "Drop this user from MySQL/MariaDB." : "Elimina questo utente da MySQL/MariaDB.", "Oracle connection could not be established" : "La connessione a Oracle non può essere stabilita", "Oracle username and/or password not valid" : "Nome utente e/o password di Oracle non validi", + "DB Error: \"%s\"" : "Errore DB: \"%s\"", + "Offending command was: \"%s\"" : "Il comando non consentito era: \"%s\"", + "You need to enter either an existing account or the administrator." : "È necessario inserire un account esistente o l'amministratore.", "Offending command was: \"%s\", name: %s, password: %s" : "Il comando non consentito era: \"%s\", nome: %s, password: %s", "PostgreSQL username and/or password not valid" : "Nome utente e/o password di PostgreSQL non validi", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X non è supportato e %s non funzionerà correttamente su questa piattaforma. Usalo a tuo rischio!", diff --git a/lib/l10n/it.json b/lib/l10n/it.json index 47d3dd8fbca..8f5681d5964 100644 --- a/lib/l10n/it.json +++ b/lib/l10n/it.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s digita il nome utente del database.", "%s enter the database name." : "%s digita il nome del database.", "%s you may not use dots in the database name" : "%s non dovresti utilizzare punti nel nome del database", - "MySQL/MariaDB username and/or password not valid" : "Nome utente e/o password di MySQL/MariaDB non validi", - "You need to enter either an existing account or the administrator." : "È necessario inserire un account esistente o l'amministratore.", - "DB Error: \"%s\"" : "Errore DB: \"%s\"", - "Offending command was: \"%s\"" : "Il comando non consentito era: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "L'utente MySQL/MariaDB '%s'@'localhost' esiste già.", - "Drop this user from MySQL/MariaDB" : "Elimina questo utente da MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "L'utente MySQL/MariaDB '%s'@'%%' esiste già", - "Drop this user from MySQL/MariaDB." : "Elimina questo utente da MySQL/MariaDB.", "Oracle connection could not be established" : "La connessione a Oracle non può essere stabilita", "Oracle username and/or password not valid" : "Nome utente e/o password di Oracle non validi", + "DB Error: \"%s\"" : "Errore DB: \"%s\"", + "Offending command was: \"%s\"" : "Il comando non consentito era: \"%s\"", + "You need to enter either an existing account or the administrator." : "È necessario inserire un account esistente o l'amministratore.", "Offending command was: \"%s\", name: %s, password: %s" : "Il comando non consentito era: \"%s\", nome: %s, password: %s", "PostgreSQL username and/or password not valid" : "Nome utente e/o password di PostgreSQL non validi", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X non è supportato e %s non funzionerà correttamente su questa piattaforma. Usalo a tuo rischio!", diff --git a/lib/l10n/ja.js b/lib/l10n/ja.js index 0e74416c8e7..20ad385491c 100644 --- a/lib/l10n/ja.js +++ b/lib/l10n/ja.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s のデータベースのユーザー名を入力してください。", "%s enter the database name." : "%s のデータベース名を入力してください。", "%s you may not use dots in the database name" : "%s ではデータベース名にドットを利用できないかもしれません。", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB のユーザー名及び/またはパスワードが無効", - "You need to enter either an existing account or the administrator." : "既存のアカウントもしくは管理者のどちらかを入力する必要があります。", - "DB Error: \"%s\"" : "DBエラー: \"%s\"", - "Offending command was: \"%s\"" : "違反コマンド: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB のユーザー '%s'@'localhost' はすでに存在します。", - "Drop this user from MySQL/MariaDB" : "MySQL/MariaDB からこのユーザーを削除", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB のユーザー '%s'@'%%' はすでに存在します", - "Drop this user from MySQL/MariaDB." : "MySQL/MariaDB からこのユーザーを削除。", "Oracle connection could not be established" : "Oracleへの接続が確立できませんでした。", "Oracle username and/or password not valid" : "Oracleのユーザー名もしくはパスワードは有効ではありません", + "DB Error: \"%s\"" : "DBエラー: \"%s\"", + "Offending command was: \"%s\"" : "違反コマンド: \"%s\"", + "You need to enter either an existing account or the administrator." : "既存のアカウントもしくは管理者のどちらかを入力する必要があります。", "Offending command was: \"%s\", name: %s, password: %s" : "違反コマンド: \"%s\"、名前: %s、パスワード: %s", "PostgreSQL username and/or password not valid" : "PostgreSQLのユーザー名もしくはパスワードは有効ではありません", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X では、サポートされていません。このOSでは、%sは正常に動作しないかもしれません。ご自身の責任においてご利用ください。", diff --git a/lib/l10n/ja.json b/lib/l10n/ja.json index 977342549dd..0bb7269a52e 100644 --- a/lib/l10n/ja.json +++ b/lib/l10n/ja.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s のデータベースのユーザー名を入力してください。", "%s enter the database name." : "%s のデータベース名を入力してください。", "%s you may not use dots in the database name" : "%s ではデータベース名にドットを利用できないかもしれません。", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB のユーザー名及び/またはパスワードが無効", - "You need to enter either an existing account or the administrator." : "既存のアカウントもしくは管理者のどちらかを入力する必要があります。", - "DB Error: \"%s\"" : "DBエラー: \"%s\"", - "Offending command was: \"%s\"" : "違反コマンド: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB のユーザー '%s'@'localhost' はすでに存在します。", - "Drop this user from MySQL/MariaDB" : "MySQL/MariaDB からこのユーザーを削除", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB のユーザー '%s'@'%%' はすでに存在します", - "Drop this user from MySQL/MariaDB." : "MySQL/MariaDB からこのユーザーを削除。", "Oracle connection could not be established" : "Oracleへの接続が確立できませんでした。", "Oracle username and/or password not valid" : "Oracleのユーザー名もしくはパスワードは有効ではありません", + "DB Error: \"%s\"" : "DBエラー: \"%s\"", + "Offending command was: \"%s\"" : "違反コマンド: \"%s\"", + "You need to enter either an existing account or the administrator." : "既存のアカウントもしくは管理者のどちらかを入力する必要があります。", "Offending command was: \"%s\", name: %s, password: %s" : "違反コマンド: \"%s\"、名前: %s、パスワード: %s", "PostgreSQL username and/or password not valid" : "PostgreSQLのユーザー名もしくはパスワードは有効ではありません", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X では、サポートされていません。このOSでは、%sは正常に動作しないかもしれません。ご自身の責任においてご利用ください。", diff --git a/lib/l10n/ka_GE.js b/lib/l10n/ka_GE.js index 7a97fd05c75..1259cf83dbb 100644 --- a/lib/l10n/ka_GE.js +++ b/lib/l10n/ka_GE.js @@ -17,10 +17,10 @@ OC.L10N.register( "%s enter the database username." : "%s შეიყვანეთ ბაზის იუზერნეიმი.", "%s enter the database name." : "%s შეიყვანეთ ბაზის სახელი.", "%s you may not use dots in the database name" : "%s არ მიუთითოთ წერტილი ბაზის სახელში", - "You need to enter either an existing account or the administrator." : "თქვენ უნდა შეიყვანოთ არსებული მომხმარებელის სახელი ან ადმინისტრატორი.", + "Oracle username and/or password not valid" : "Oracle იუზერნეიმი და/ან პაროლი არ არის სწორი", "DB Error: \"%s\"" : "DB შეცდომა: \"%s\"", "Offending command was: \"%s\"" : "Offending ბრძანება იყო: \"%s\"", - "Oracle username and/or password not valid" : "Oracle იუზერნეიმი და/ან პაროლი არ არის სწორი", + "You need to enter either an existing account or the administrator." : "თქვენ უნდა შეიყვანოთ არსებული მომხმარებელის სახელი ან ადმინისტრატორი.", "Offending command was: \"%s\", name: %s, password: %s" : "Offending ბრძანება იყო: \"%s\", სახელი: %s, პაროლი: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL იუზერნეიმი და/ან პაროლი არ არის სწორი", "Set an admin username." : "დააყენეთ ადმინისტრატორის სახელი.", diff --git a/lib/l10n/ka_GE.json b/lib/l10n/ka_GE.json index b19e37d7864..d32642f460b 100644 --- a/lib/l10n/ka_GE.json +++ b/lib/l10n/ka_GE.json @@ -15,10 +15,10 @@ "%s enter the database username." : "%s შეიყვანეთ ბაზის იუზერნეიმი.", "%s enter the database name." : "%s შეიყვანეთ ბაზის სახელი.", "%s you may not use dots in the database name" : "%s არ მიუთითოთ წერტილი ბაზის სახელში", - "You need to enter either an existing account or the administrator." : "თქვენ უნდა შეიყვანოთ არსებული მომხმარებელის სახელი ან ადმინისტრატორი.", + "Oracle username and/or password not valid" : "Oracle იუზერნეიმი და/ან პაროლი არ არის სწორი", "DB Error: \"%s\"" : "DB შეცდომა: \"%s\"", "Offending command was: \"%s\"" : "Offending ბრძანება იყო: \"%s\"", - "Oracle username and/or password not valid" : "Oracle იუზერნეიმი და/ან პაროლი არ არის სწორი", + "You need to enter either an existing account or the administrator." : "თქვენ უნდა შეიყვანოთ არსებული მომხმარებელის სახელი ან ადმინისტრატორი.", "Offending command was: \"%s\", name: %s, password: %s" : "Offending ბრძანება იყო: \"%s\", სახელი: %s, პაროლი: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL იუზერნეიმი და/ან პაროლი არ არის სწორი", "Set an admin username." : "დააყენეთ ადმინისტრატორის სახელი.", diff --git a/lib/l10n/km.js b/lib/l10n/km.js index e37b1b80571..ad68233cf62 100644 --- a/lib/l10n/km.js +++ b/lib/l10n/km.js @@ -25,8 +25,8 @@ OC.L10N.register( "%s enter the database username." : "%s វាយបញ្ចូលឈ្មោះអ្នកប្រើមូលដ្ឋានទិន្នន័យ។", "%s enter the database name." : "%s វាយបញ្ចូលឈ្មោះមូលដ្ឋានទិន្នន័យ។", "%s you may not use dots in the database name" : "%s អ្នកអាចមិនប្រើសញ្ញាចុចនៅក្នុងឈ្មោះមូលដ្ឋានទិន្នន័យ", - "DB Error: \"%s\"" : "កំហុស DB៖ \"%s\"", "Oracle connection could not be established" : "មិនអាចបង្កើតការតភ្ជាប់ Oracle", + "DB Error: \"%s\"" : "កំហុស DB៖ \"%s\"", "PostgreSQL username and/or password not valid" : "ឈ្មោះអ្នកប្រើ និង/ឬ ពាក្យសម្ងាត់ PostgreSQL គឺមិនត្រូវទេ", "Set an admin username." : "កំណត់ឈ្មោះអ្នកគ្រប់គ្រង។", "Set an admin password." : "កំណត់ពាក្យសម្ងាត់អ្នកគ្រប់គ្រង។", diff --git a/lib/l10n/km.json b/lib/l10n/km.json index 2f9399ff3bf..9133d012c1e 100644 --- a/lib/l10n/km.json +++ b/lib/l10n/km.json @@ -23,8 +23,8 @@ "%s enter the database username." : "%s វាយបញ្ចូលឈ្មោះអ្នកប្រើមូលដ្ឋានទិន្នន័យ។", "%s enter the database name." : "%s វាយបញ្ចូលឈ្មោះមូលដ្ឋានទិន្នន័យ។", "%s you may not use dots in the database name" : "%s អ្នកអាចមិនប្រើសញ្ញាចុចនៅក្នុងឈ្មោះមូលដ្ឋានទិន្នន័យ", - "DB Error: \"%s\"" : "កំហុស DB៖ \"%s\"", "Oracle connection could not be established" : "មិនអាចបង្កើតការតភ្ជាប់ Oracle", + "DB Error: \"%s\"" : "កំហុស DB៖ \"%s\"", "PostgreSQL username and/or password not valid" : "ឈ្មោះអ្នកប្រើ និង/ឬ ពាក្យសម្ងាត់ PostgreSQL គឺមិនត្រូវទេ", "Set an admin username." : "កំណត់ឈ្មោះអ្នកគ្រប់គ្រង។", "Set an admin password." : "កំណត់ពាក្យសម្ងាត់អ្នកគ្រប់គ្រង។", diff --git a/lib/l10n/ko.js b/lib/l10n/ko.js index 2ebb014894e..a2ec230d59f 100644 --- a/lib/l10n/ko.js +++ b/lib/l10n/ko.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s 데이터베이스 사용자 이름을 입력해 주십시오.", "%s enter the database name." : "%s 데이터베이스 이름을 입력하십시오.", "%s you may not use dots in the database name" : "%s 데이터베이스 이름에는 마침표를 사용할 수 없습니다", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB 사용자 이름이나 암호가 올바르지 않습니다", - "You need to enter either an existing account or the administrator." : "기존 계정이나 administrator(관리자)를 입력해야 합니다.", - "DB Error: \"%s\"" : "DB 오류: \"%s\"", - "Offending command was: \"%s\"" : "잘못된 명령: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB '%s'@'localhost' 사용자가 이미 존재합니다", - "Drop this user from MySQL/MariaDB" : "MySQL/MariaDB에서 이 사용자 삭제하기", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB '%s'@'%%' 사용자가 이미 존재합니다", - "Drop this user from MySQL/MariaDB." : "MySQL/MariaDB에서 이 사용자를 삭제합니다.", "Oracle connection could not be established" : "Oracle 연결을 수립할 수 없습니다.", "Oracle username and/or password not valid" : "Oracle 사용자 이름이나 암호가 잘못되었습니다.", + "DB Error: \"%s\"" : "DB 오류: \"%s\"", + "Offending command was: \"%s\"" : "잘못된 명령: \"%s\"", + "You need to enter either an existing account or the administrator." : "기존 계정이나 administrator(관리자)를 입력해야 합니다.", "Offending command was: \"%s\", name: %s, password: %s" : "잘못된 명령: \"%s\", 이름: %s, 암호: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL 사용자 이름 또는 암호가 잘못되었습니다", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X은 지원하지 않으며 %s이(가) 이 플랫폼에서 올바르게 작동하지 않을 수도 있습니다. 본인 책임으로 사용하십시오! ", diff --git a/lib/l10n/ko.json b/lib/l10n/ko.json index c0d2b7df303..fa52b9a8f05 100644 --- a/lib/l10n/ko.json +++ b/lib/l10n/ko.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s 데이터베이스 사용자 이름을 입력해 주십시오.", "%s enter the database name." : "%s 데이터베이스 이름을 입력하십시오.", "%s you may not use dots in the database name" : "%s 데이터베이스 이름에는 마침표를 사용할 수 없습니다", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB 사용자 이름이나 암호가 올바르지 않습니다", - "You need to enter either an existing account or the administrator." : "기존 계정이나 administrator(관리자)를 입력해야 합니다.", - "DB Error: \"%s\"" : "DB 오류: \"%s\"", - "Offending command was: \"%s\"" : "잘못된 명령: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB '%s'@'localhost' 사용자가 이미 존재합니다", - "Drop this user from MySQL/MariaDB" : "MySQL/MariaDB에서 이 사용자 삭제하기", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB '%s'@'%%' 사용자가 이미 존재합니다", - "Drop this user from MySQL/MariaDB." : "MySQL/MariaDB에서 이 사용자를 삭제합니다.", "Oracle connection could not be established" : "Oracle 연결을 수립할 수 없습니다.", "Oracle username and/or password not valid" : "Oracle 사용자 이름이나 암호가 잘못되었습니다.", + "DB Error: \"%s\"" : "DB 오류: \"%s\"", + "Offending command was: \"%s\"" : "잘못된 명령: \"%s\"", + "You need to enter either an existing account or the administrator." : "기존 계정이나 administrator(관리자)를 입력해야 합니다.", "Offending command was: \"%s\", name: %s, password: %s" : "잘못된 명령: \"%s\", 이름: %s, 암호: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL 사용자 이름 또는 암호가 잘못되었습니다", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X은 지원하지 않으며 %s이(가) 이 플랫폼에서 올바르게 작동하지 않을 수도 있습니다. 본인 책임으로 사용하십시오! ", diff --git a/lib/l10n/lt_LT.js b/lib/l10n/lt_LT.js index 88e095c82b0..e37141e9bef 100644 --- a/lib/l10n/lt_LT.js +++ b/lib/l10n/lt_LT.js @@ -35,11 +35,11 @@ OC.L10N.register( "%s enter the database username." : "%s įrašykite duombazės naudotojo vardą.", "%s enter the database name." : "%s įrašykite duombazės pavadinimą.", "%s you may not use dots in the database name" : "%s negalite naudoti taškų duombazės pavadinime", - "You need to enter either an existing account or the administrator." : "Turite prisijungti su egzistuojančia paskyra arba su administratoriumi.", - "DB Error: \"%s\"" : "DB klaida: \"%s\"", - "Offending command was: \"%s\"" : "Vykdyta komanda buvo: \"%s\"", "Oracle connection could not be established" : "Nepavyko sukurti Oracle ryšio", "Oracle username and/or password not valid" : "Neteisingas Oracle naudotojo vardas ir/arba slaptažodis", + "DB Error: \"%s\"" : "DB klaida: \"%s\"", + "Offending command was: \"%s\"" : "Vykdyta komanda buvo: \"%s\"", + "You need to enter either an existing account or the administrator." : "Turite prisijungti su egzistuojančia paskyra arba su administratoriumi.", "Offending command was: \"%s\", name: %s, password: %s" : "Vykdyta komanda buvo: \"%s\", name: %s, password: %s", "PostgreSQL username and/or password not valid" : "Neteisingas PostgreSQL naudotojo vardas ir/arba slaptažodis", "Set an admin username." : "Nustatyti administratoriaus naudotojo vardą.", diff --git a/lib/l10n/lt_LT.json b/lib/l10n/lt_LT.json index 4cbb88d433c..c823af258cd 100644 --- a/lib/l10n/lt_LT.json +++ b/lib/l10n/lt_LT.json @@ -33,11 +33,11 @@ "%s enter the database username." : "%s įrašykite duombazės naudotojo vardą.", "%s enter the database name." : "%s įrašykite duombazės pavadinimą.", "%s you may not use dots in the database name" : "%s negalite naudoti taškų duombazės pavadinime", - "You need to enter either an existing account or the administrator." : "Turite prisijungti su egzistuojančia paskyra arba su administratoriumi.", - "DB Error: \"%s\"" : "DB klaida: \"%s\"", - "Offending command was: \"%s\"" : "Vykdyta komanda buvo: \"%s\"", "Oracle connection could not be established" : "Nepavyko sukurti Oracle ryšio", "Oracle username and/or password not valid" : "Neteisingas Oracle naudotojo vardas ir/arba slaptažodis", + "DB Error: \"%s\"" : "DB klaida: \"%s\"", + "Offending command was: \"%s\"" : "Vykdyta komanda buvo: \"%s\"", + "You need to enter either an existing account or the administrator." : "Turite prisijungti su egzistuojančia paskyra arba su administratoriumi.", "Offending command was: \"%s\", name: %s, password: %s" : "Vykdyta komanda buvo: \"%s\", name: %s, password: %s", "PostgreSQL username and/or password not valid" : "Neteisingas PostgreSQL naudotojo vardas ir/arba slaptažodis", "Set an admin username." : "Nustatyti administratoriaus naudotojo vardą.", diff --git a/lib/l10n/lv.js b/lib/l10n/lv.js index bc5024042b4..5505d0cd7cc 100644 --- a/lib/l10n/lv.js +++ b/lib/l10n/lv.js @@ -20,11 +20,11 @@ OC.L10N.register( "%s enter the database username." : "%s ievadiet datubāzes lietotājvārdu.", "%s enter the database name." : "%s ievadiet datubāzes nosaukumu.", "%s you may not use dots in the database name" : "%s datubāžu nosaukumos nedrīkst izmantot punktus", - "You need to enter either an existing account or the administrator." : "Jums jāievada vai nu esošs vai administratora konts.", - "DB Error: \"%s\"" : "DB kļūda — “%s”", - "Offending command was: \"%s\"" : "Vainīgā komanda bija “%s”", "Oracle connection could not be established" : "Nevar izveidot savienojumu ar Oracle", "Oracle username and/or password not valid" : "Nav derīga Oracle parole un/vai lietotājvārds", + "DB Error: \"%s\"" : "DB kļūda — “%s”", + "Offending command was: \"%s\"" : "Vainīgā komanda bija “%s”", + "You need to enter either an existing account or the administrator." : "Jums jāievada vai nu esošs vai administratora konts.", "Offending command was: \"%s\", name: %s, password: %s" : "Vainīgā komanda bija \"%s\", vārds: %s, parole: %s", "PostgreSQL username and/or password not valid" : "Nav derīga PostgreSQL parole un/vai lietotājvārds", "Set an admin username." : "Iestatiet administratora lietotājvārdu.", diff --git a/lib/l10n/lv.json b/lib/l10n/lv.json index 2caeb13b8f1..13b38a73a2c 100644 --- a/lib/l10n/lv.json +++ b/lib/l10n/lv.json @@ -18,11 +18,11 @@ "%s enter the database username." : "%s ievadiet datubāzes lietotājvārdu.", "%s enter the database name." : "%s ievadiet datubāzes nosaukumu.", "%s you may not use dots in the database name" : "%s datubāžu nosaukumos nedrīkst izmantot punktus", - "You need to enter either an existing account or the administrator." : "Jums jāievada vai nu esošs vai administratora konts.", - "DB Error: \"%s\"" : "DB kļūda — “%s”", - "Offending command was: \"%s\"" : "Vainīgā komanda bija “%s”", "Oracle connection could not be established" : "Nevar izveidot savienojumu ar Oracle", "Oracle username and/or password not valid" : "Nav derīga Oracle parole un/vai lietotājvārds", + "DB Error: \"%s\"" : "DB kļūda — “%s”", + "Offending command was: \"%s\"" : "Vainīgā komanda bija “%s”", + "You need to enter either an existing account or the administrator." : "Jums jāievada vai nu esošs vai administratora konts.", "Offending command was: \"%s\", name: %s, password: %s" : "Vainīgā komanda bija \"%s\", vārds: %s, parole: %s", "PostgreSQL username and/or password not valid" : "Nav derīga PostgreSQL parole un/vai lietotājvārds", "Set an admin username." : "Iestatiet administratora lietotājvārdu.", diff --git a/lib/l10n/mk.js b/lib/l10n/mk.js index 82b2d09f392..197a25c2172 100644 --- a/lib/l10n/mk.js +++ b/lib/l10n/mk.js @@ -21,9 +21,9 @@ OC.L10N.register( "%s enter the database username." : "%s внеси го корисничкото име за базата.", "%s enter the database name." : "%s внеси го името на базата.", "%s you may not use dots in the database name" : "%s не можеш да користиш точки во името на базата", + "Oracle username and/or password not valid" : "Oracle корисничкото име и/или лозинката не се валидни", "DB Error: \"%s\"" : "DB грешка: \"%s\"", "Offending command was: \"%s\"" : "Навредувшката команда беше: \"%s\"", - "Oracle username and/or password not valid" : "Oracle корисничкото име и/или лозинката не се валидни", "PostgreSQL username and/or password not valid" : "PostgreSQL корисничкото име и/или лозинка не се валидни", "Set an admin username." : "Постави администраторско корисничко име", "Set an admin password." : "Постави администраторска лозинка.", diff --git a/lib/l10n/mk.json b/lib/l10n/mk.json index f48e88a2bcb..2b396044a21 100644 --- a/lib/l10n/mk.json +++ b/lib/l10n/mk.json @@ -19,9 +19,9 @@ "%s enter the database username." : "%s внеси го корисничкото име за базата.", "%s enter the database name." : "%s внеси го името на базата.", "%s you may not use dots in the database name" : "%s не можеш да користиш точки во името на базата", + "Oracle username and/or password not valid" : "Oracle корисничкото име и/или лозинката не се валидни", "DB Error: \"%s\"" : "DB грешка: \"%s\"", "Offending command was: \"%s\"" : "Навредувшката команда беше: \"%s\"", - "Oracle username and/or password not valid" : "Oracle корисничкото име и/или лозинката не се валидни", "PostgreSQL username and/or password not valid" : "PostgreSQL корисничкото име и/или лозинка не се валидни", "Set an admin username." : "Постави администраторско корисничко име", "Set an admin password." : "Постави администраторска лозинка.", diff --git a/lib/l10n/nb_NO.js b/lib/l10n/nb_NO.js index ba4e26b22b7..d09fa5ca6e8 100644 --- a/lib/l10n/nb_NO.js +++ b/lib/l10n/nb_NO.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s legg inn brukernavn for databasen.", "%s enter the database name." : "%s legg inn navnet på databasen.", "%s you may not use dots in the database name" : "%s du kan ikke bruke punktum i databasenavnet", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB-brukernavn og/eller -passord ikke gyldig", - "You need to enter either an existing account or the administrator." : "Du må legge inn enten en eksisterende konto eller administratoren.", - "DB Error: \"%s\"" : "Databasefeil: \"%s\"", - "Offending command was: \"%s\"" : "Kommandoen som feilet: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB-bruker '%s'@'localhost' finnes allerede.", - "Drop this user from MySQL/MariaDB" : "Fjern denne brukeren fra MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB-bruker '%s'@'%%' finnes allerede", - "Drop this user from MySQL/MariaDB." : "Fjern denne brukeren fra MySQL/MariaDB.", "Oracle connection could not be established" : "Klarte ikke å etablere forbindelse til Oracle", "Oracle username and/or password not valid" : "Oracle-brukernavn og/eller passord er ikke gyldig", + "DB Error: \"%s\"" : "Databasefeil: \"%s\"", + "Offending command was: \"%s\"" : "Kommandoen som feilet: \"%s\"", + "You need to enter either an existing account or the administrator." : "Du må legge inn enten en eksisterende konto eller administratoren.", "Offending command was: \"%s\", name: %s, password: %s" : "Kommando som feilet: \"%s\", navn: %s, passord: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL-brukernavn og/eller passord er ikke gyldig", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X støttes ikke og %s vil ikke fungere korrekt på denne plattformen. Bruk på egen risiko!", diff --git a/lib/l10n/nb_NO.json b/lib/l10n/nb_NO.json index a11f9dc87e5..665ae866969 100644 --- a/lib/l10n/nb_NO.json +++ b/lib/l10n/nb_NO.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s legg inn brukernavn for databasen.", "%s enter the database name." : "%s legg inn navnet på databasen.", "%s you may not use dots in the database name" : "%s du kan ikke bruke punktum i databasenavnet", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB-brukernavn og/eller -passord ikke gyldig", - "You need to enter either an existing account or the administrator." : "Du må legge inn enten en eksisterende konto eller administratoren.", - "DB Error: \"%s\"" : "Databasefeil: \"%s\"", - "Offending command was: \"%s\"" : "Kommandoen som feilet: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB-bruker '%s'@'localhost' finnes allerede.", - "Drop this user from MySQL/MariaDB" : "Fjern denne brukeren fra MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB-bruker '%s'@'%%' finnes allerede", - "Drop this user from MySQL/MariaDB." : "Fjern denne brukeren fra MySQL/MariaDB.", "Oracle connection could not be established" : "Klarte ikke å etablere forbindelse til Oracle", "Oracle username and/or password not valid" : "Oracle-brukernavn og/eller passord er ikke gyldig", + "DB Error: \"%s\"" : "Databasefeil: \"%s\"", + "Offending command was: \"%s\"" : "Kommandoen som feilet: \"%s\"", + "You need to enter either an existing account or the administrator." : "Du må legge inn enten en eksisterende konto eller administratoren.", "Offending command was: \"%s\", name: %s, password: %s" : "Kommando som feilet: \"%s\", navn: %s, passord: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL-brukernavn og/eller passord er ikke gyldig", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X støttes ikke og %s vil ikke fungere korrekt på denne plattformen. Bruk på egen risiko!", diff --git a/lib/l10n/nl.js b/lib/l10n/nl.js index 6c5aa7174d1..c7a68c9000f 100644 --- a/lib/l10n/nl.js +++ b/lib/l10n/nl.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s opgeven database gebruikersnaam.", "%s enter the database name." : "%s opgeven databasenaam.", "%s you may not use dots in the database name" : "%s er mogen geen puntjes in de databasenaam voorkomen", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB gebruikersnaam en/of wachtwoord ongeldig", - "You need to enter either an existing account or the administrator." : "Geef of een bestaand account op of het beheerdersaccount.", - "DB Error: \"%s\"" : "DB Fout: \"%s\"", - "Offending command was: \"%s\"" : "Onjuiste commande was: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB gebruiker '%s'@'localhost' bestaat al.", - "Drop this user from MySQL/MariaDB" : "Verwijder deze gebruiker uit MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB gebruiker '%s'@'%%' bestaat al", - "Drop this user from MySQL/MariaDB." : "Verwijder deze gebruiker uit MySQL/MariaDB.", "Oracle connection could not be established" : "Er kon geen verbinding met Oracle worden bereikt", "Oracle username and/or password not valid" : "Oracle gebruikersnaam en/of wachtwoord ongeldig", + "DB Error: \"%s\"" : "DB Fout: \"%s\"", + "Offending command was: \"%s\"" : "Onjuiste commande was: \"%s\"", + "You need to enter either an existing account or the administrator." : "Geef of een bestaand account op of het beheerdersaccount.", "Offending command was: \"%s\", name: %s, password: %s" : "Onjuiste commando was: \"%s\", naam: %s, wachtwoord: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL gebruikersnaam en/of wachtwoord ongeldig", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OSX wordt niet ondersteund en %s zal niet goed werken op dit platform. Gebruik het op uw eigen risico!", diff --git a/lib/l10n/nl.json b/lib/l10n/nl.json index d903594307f..560e20fbd4f 100644 --- a/lib/l10n/nl.json +++ b/lib/l10n/nl.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s opgeven database gebruikersnaam.", "%s enter the database name." : "%s opgeven databasenaam.", "%s you may not use dots in the database name" : "%s er mogen geen puntjes in de databasenaam voorkomen", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB gebruikersnaam en/of wachtwoord ongeldig", - "You need to enter either an existing account or the administrator." : "Geef of een bestaand account op of het beheerdersaccount.", - "DB Error: \"%s\"" : "DB Fout: \"%s\"", - "Offending command was: \"%s\"" : "Onjuiste commande was: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB gebruiker '%s'@'localhost' bestaat al.", - "Drop this user from MySQL/MariaDB" : "Verwijder deze gebruiker uit MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB gebruiker '%s'@'%%' bestaat al", - "Drop this user from MySQL/MariaDB." : "Verwijder deze gebruiker uit MySQL/MariaDB.", "Oracle connection could not be established" : "Er kon geen verbinding met Oracle worden bereikt", "Oracle username and/or password not valid" : "Oracle gebruikersnaam en/of wachtwoord ongeldig", + "DB Error: \"%s\"" : "DB Fout: \"%s\"", + "Offending command was: \"%s\"" : "Onjuiste commande was: \"%s\"", + "You need to enter either an existing account or the administrator." : "Geef of een bestaand account op of het beheerdersaccount.", "Offending command was: \"%s\", name: %s, password: %s" : "Onjuiste commando was: \"%s\", naam: %s, wachtwoord: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL gebruikersnaam en/of wachtwoord ongeldig", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OSX wordt niet ondersteund en %s zal niet goed werken op dit platform. Gebruik het op uw eigen risico!", diff --git a/lib/l10n/pl.js b/lib/l10n/pl.js index cf53425b5b2..611a0b4c6f1 100644 --- a/lib/l10n/pl.js +++ b/lib/l10n/pl.js @@ -54,16 +54,11 @@ OC.L10N.register( "%s enter the database username." : "%s wpisz nazwę użytkownika do bazy", "%s enter the database name." : "%s wpisz nazwę bazy.", "%s you may not use dots in the database name" : "%s nie można używać kropki w nazwie bazy danych", - "MySQL/MariaDB username and/or password not valid" : "Użytkownik i/lub hasło do MySQL/MariaDB są niepoprawne", - "You need to enter either an existing account or the administrator." : "Należy wprowadzić istniejące konto użytkownika lub administratora.", - "DB Error: \"%s\"" : "Błąd DB: \"%s\"", - "Offending command was: \"%s\"" : "Niepoprawna komenda: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Użytkownik '%s'@'localhost' MySQL/MariaDB już istnieje.", - "Drop this user from MySQL/MariaDB" : "Usuń tego użytkownika z MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Użytkownik '%s'@'%%' MySQL/MariaDB już istnieje.", - "Drop this user from MySQL/MariaDB." : "Usuń tego użytkownika z MySQL/MariaDB", "Oracle connection could not be established" : "Nie można ustanowić połączenia z bazą Oracle", "Oracle username and/or password not valid" : "Oracle: Nazwa użytkownika i/lub hasło jest niepoprawne", + "DB Error: \"%s\"" : "Błąd DB: \"%s\"", + "Offending command was: \"%s\"" : "Niepoprawna komenda: \"%s\"", + "You need to enter either an existing account or the administrator." : "Należy wprowadzić istniejące konto użytkownika lub administratora.", "Offending command was: \"%s\", name: %s, password: %s" : "Niepoprawne polecania: \"%s\", nazwa: %s, hasło: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL: Nazwa użytkownika i/lub hasło jest niepoprawne", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nie jest wspierany i %s nie będzie działać poprawnie na tej platformie. Używasz na własne ryzyko!", diff --git a/lib/l10n/pl.json b/lib/l10n/pl.json index 92bb3273814..2e96f414a66 100644 --- a/lib/l10n/pl.json +++ b/lib/l10n/pl.json @@ -52,16 +52,11 @@ "%s enter the database username." : "%s wpisz nazwę użytkownika do bazy", "%s enter the database name." : "%s wpisz nazwę bazy.", "%s you may not use dots in the database name" : "%s nie można używać kropki w nazwie bazy danych", - "MySQL/MariaDB username and/or password not valid" : "Użytkownik i/lub hasło do MySQL/MariaDB są niepoprawne", - "You need to enter either an existing account or the administrator." : "Należy wprowadzić istniejące konto użytkownika lub administratora.", - "DB Error: \"%s\"" : "Błąd DB: \"%s\"", - "Offending command was: \"%s\"" : "Niepoprawna komenda: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Użytkownik '%s'@'localhost' MySQL/MariaDB już istnieje.", - "Drop this user from MySQL/MariaDB" : "Usuń tego użytkownika z MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Użytkownik '%s'@'%%' MySQL/MariaDB już istnieje.", - "Drop this user from MySQL/MariaDB." : "Usuń tego użytkownika z MySQL/MariaDB", "Oracle connection could not be established" : "Nie można ustanowić połączenia z bazą Oracle", "Oracle username and/or password not valid" : "Oracle: Nazwa użytkownika i/lub hasło jest niepoprawne", + "DB Error: \"%s\"" : "Błąd DB: \"%s\"", + "Offending command was: \"%s\"" : "Niepoprawna komenda: \"%s\"", + "You need to enter either an existing account or the administrator." : "Należy wprowadzić istniejące konto użytkownika lub administratora.", "Offending command was: \"%s\", name: %s, password: %s" : "Niepoprawne polecania: \"%s\", nazwa: %s, hasło: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL: Nazwa użytkownika i/lub hasło jest niepoprawne", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nie jest wspierany i %s nie będzie działać poprawnie na tej platformie. Używasz na własne ryzyko!", diff --git a/lib/l10n/pt_BR.js b/lib/l10n/pt_BR.js index dc7da9a13f9..d348ac705f3 100644 --- a/lib/l10n/pt_BR.js +++ b/lib/l10n/pt_BR.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s insira o nome de usuário do banco de dados.", "%s enter the database name." : "%s insira o nome do banco de dados.", "%s you may not use dots in the database name" : "%s você não pode usar pontos no nome do banco de dados", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB nome de usuário e/ou senha não é válida", - "You need to enter either an existing account or the administrator." : "Você precisa inserir uma conta existente ou a do administrador.", - "DB Error: \"%s\"" : "Erro no BD: \"%s\"", - "Offending command was: \"%s\"" : "Comando ofensivo era: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB usuário '%s'@'localhost' já existe.", - "Drop this user from MySQL/MariaDB" : "Eliminar esse usuário de MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB usuário '%s'@'%%' já existe", - "Drop this user from MySQL/MariaDB." : "Eliminar esse usuário de MySQL/MariaDB", "Oracle connection could not be established" : "Conexão Oracle não pode ser estabelecida", "Oracle username and/or password not valid" : "Nome de usuário e/ou senha Oracle inválido(s)", + "DB Error: \"%s\"" : "Erro no BD: \"%s\"", + "Offending command was: \"%s\"" : "Comando ofensivo era: \"%s\"", + "You need to enter either an existing account or the administrator." : "Você precisa inserir uma conta existente ou a do administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "Comando ofensivo era: \"%s\", nome: %s, senha: %s", "PostgreSQL username and/or password not valid" : "Nome de usuário e/ou senha PostgreSQL inválido(s)", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X não é suportado e %s não funcionará corretamente nesta plataforma. Use-o por sua conta e risco!", diff --git a/lib/l10n/pt_BR.json b/lib/l10n/pt_BR.json index 4688e4412c0..63192f8a5bd 100644 --- a/lib/l10n/pt_BR.json +++ b/lib/l10n/pt_BR.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s insira o nome de usuário do banco de dados.", "%s enter the database name." : "%s insira o nome do banco de dados.", "%s you may not use dots in the database name" : "%s você não pode usar pontos no nome do banco de dados", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB nome de usuário e/ou senha não é válida", - "You need to enter either an existing account or the administrator." : "Você precisa inserir uma conta existente ou a do administrador.", - "DB Error: \"%s\"" : "Erro no BD: \"%s\"", - "Offending command was: \"%s\"" : "Comando ofensivo era: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB usuário '%s'@'localhost' já existe.", - "Drop this user from MySQL/MariaDB" : "Eliminar esse usuário de MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB usuário '%s'@'%%' já existe", - "Drop this user from MySQL/MariaDB." : "Eliminar esse usuário de MySQL/MariaDB", "Oracle connection could not be established" : "Conexão Oracle não pode ser estabelecida", "Oracle username and/or password not valid" : "Nome de usuário e/ou senha Oracle inválido(s)", + "DB Error: \"%s\"" : "Erro no BD: \"%s\"", + "Offending command was: \"%s\"" : "Comando ofensivo era: \"%s\"", + "You need to enter either an existing account or the administrator." : "Você precisa inserir uma conta existente ou a do administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "Comando ofensivo era: \"%s\", nome: %s, senha: %s", "PostgreSQL username and/or password not valid" : "Nome de usuário e/ou senha PostgreSQL inválido(s)", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X não é suportado e %s não funcionará corretamente nesta plataforma. Use-o por sua conta e risco!", diff --git a/lib/l10n/pt_PT.js b/lib/l10n/pt_PT.js index 982223bf863..75149346f0b 100644 --- a/lib/l10n/pt_PT.js +++ b/lib/l10n/pt_PT.js @@ -59,16 +59,11 @@ OC.L10N.register( "%s enter the database username." : "%s introduza o nome de utilizador da base de dados", "%s enter the database name." : "%s introduza o nome da base de dados", "%s you may not use dots in the database name" : "%s não é permitido utilizar pontos (.) no nome da base de dados", - "MySQL/MariaDB username and/or password not valid" : "Nome de utilizador/password do MySQL/Maria DB inválida", - "You need to enter either an existing account or the administrator." : "Precisa de introduzir uma conta existente ou de administrador", - "DB Error: \"%s\"" : "Erro na BD: \"%s\"", - "Offending command was: \"%s\"" : "O comando gerador de erro foi: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "O utilizador '%s'@'localhost' do MySQL/MariaDB já existe.", - "Drop this user from MySQL/MariaDB" : "Eliminar este utilizador do MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "O utilizador '%s'@'%%' do MySQL/MariaDB já existe", - "Drop this user from MySQL/MariaDB." : "Eliminar este utilizador do MySQL/MariaDB", "Oracle connection could not be established" : "Não foi possível estabelecer a ligação Oracle", "Oracle username and/or password not valid" : "Nome de utilizador/password do Oracle inválida", + "DB Error: \"%s\"" : "Erro na BD: \"%s\"", + "Offending command was: \"%s\"" : "O comando gerador de erro foi: \"%s\"", + "You need to enter either an existing account or the administrator." : "Precisa de introduzir uma conta existente ou de administrador", "Offending command was: \"%s\", name: %s, password: %s" : "O comando gerador de erro foi: \"%s\", nome: %s, password: %s", "PostgreSQL username and/or password not valid" : "Nome de utilizador/password do PostgreSQL inválido", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Esta plataforma não suporta o sistema operativo Mac OS X e o %s poderá não funcionar correctamente. Utilize por sua conta e risco.", diff --git a/lib/l10n/pt_PT.json b/lib/l10n/pt_PT.json index a8722023222..21a3b857230 100644 --- a/lib/l10n/pt_PT.json +++ b/lib/l10n/pt_PT.json @@ -57,16 +57,11 @@ "%s enter the database username." : "%s introduza o nome de utilizador da base de dados", "%s enter the database name." : "%s introduza o nome da base de dados", "%s you may not use dots in the database name" : "%s não é permitido utilizar pontos (.) no nome da base de dados", - "MySQL/MariaDB username and/or password not valid" : "Nome de utilizador/password do MySQL/Maria DB inválida", - "You need to enter either an existing account or the administrator." : "Precisa de introduzir uma conta existente ou de administrador", - "DB Error: \"%s\"" : "Erro na BD: \"%s\"", - "Offending command was: \"%s\"" : "O comando gerador de erro foi: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "O utilizador '%s'@'localhost' do MySQL/MariaDB já existe.", - "Drop this user from MySQL/MariaDB" : "Eliminar este utilizador do MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "O utilizador '%s'@'%%' do MySQL/MariaDB já existe", - "Drop this user from MySQL/MariaDB." : "Eliminar este utilizador do MySQL/MariaDB", "Oracle connection could not be established" : "Não foi possível estabelecer a ligação Oracle", "Oracle username and/or password not valid" : "Nome de utilizador/password do Oracle inválida", + "DB Error: \"%s\"" : "Erro na BD: \"%s\"", + "Offending command was: \"%s\"" : "O comando gerador de erro foi: \"%s\"", + "You need to enter either an existing account or the administrator." : "Precisa de introduzir uma conta existente ou de administrador", "Offending command was: \"%s\", name: %s, password: %s" : "O comando gerador de erro foi: \"%s\", nome: %s, password: %s", "PostgreSQL username and/or password not valid" : "Nome de utilizador/password do PostgreSQL inválido", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Esta plataforma não suporta o sistema operativo Mac OS X e o %s poderá não funcionar correctamente. Utilize por sua conta e risco.", diff --git a/lib/l10n/ro.js b/lib/l10n/ro.js index 0668d655779..ba7d11cdfb1 100644 --- a/lib/l10n/ro.js +++ b/lib/l10n/ro.js @@ -32,13 +32,8 @@ OC.L10N.register( "Token expired. Please reload page." : "Token expirat. Te rugăm să reîncarci pagina.", "Unknown user" : "Utilizator necunoscut", "%s enter the database name." : "%s introduceți numele bazei de date", - "MySQL/MariaDB username and/or password not valid" : "Nume utilizator și/sau parolă MySQL/MariaDB greșită", - "DB Error: \"%s\"" : "Eroare Bază de Date: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Utilizatorul MySQL/MariaDB '%s'@'localhost' deja există.", - "Drop this user from MySQL/MariaDB" : "Șterge acest utilizator din MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Utilizatorul MySQL/MariaDB '%s'@'%%' deja există.", - "Drop this user from MySQL/MariaDB." : "Șterge acest utilizator din MySQL/MariaDB.", "Oracle connection could not be established" : "Conexiunea Oracle nu a putut fi stabilită", + "DB Error: \"%s\"" : "Eroare Bază de Date: \"%s\"", "PostgreSQL username and/or password not valid" : "Nume utilizator și/sau parolă PostgreSQL greșită", "Set an admin username." : "Setează un nume de administrator.", "Set an admin password." : "Setează o parolă de administrator.", diff --git a/lib/l10n/ro.json b/lib/l10n/ro.json index 3f276814926..8e85455c50c 100644 --- a/lib/l10n/ro.json +++ b/lib/l10n/ro.json @@ -30,13 +30,8 @@ "Token expired. Please reload page." : "Token expirat. Te rugăm să reîncarci pagina.", "Unknown user" : "Utilizator necunoscut", "%s enter the database name." : "%s introduceți numele bazei de date", - "MySQL/MariaDB username and/or password not valid" : "Nume utilizator și/sau parolă MySQL/MariaDB greșită", - "DB Error: \"%s\"" : "Eroare Bază de Date: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Utilizatorul MySQL/MariaDB '%s'@'localhost' deja există.", - "Drop this user from MySQL/MariaDB" : "Șterge acest utilizator din MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Utilizatorul MySQL/MariaDB '%s'@'%%' deja există.", - "Drop this user from MySQL/MariaDB." : "Șterge acest utilizator din MySQL/MariaDB.", "Oracle connection could not be established" : "Conexiunea Oracle nu a putut fi stabilită", + "DB Error: \"%s\"" : "Eroare Bază de Date: \"%s\"", "PostgreSQL username and/or password not valid" : "Nume utilizator și/sau parolă PostgreSQL greșită", "Set an admin username." : "Setează un nume de administrator.", "Set an admin password." : "Setează o parolă de administrator.", diff --git a/lib/l10n/ru.js b/lib/l10n/ru.js index 4d58e4aed2c..16331c5cd76 100644 --- a/lib/l10n/ru.js +++ b/lib/l10n/ru.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s введите имя пользователя базы данных.", "%s enter the database name." : "%s введите имя базы данных.", "%s you may not use dots in the database name" : "%s Вы не можете использовать точки в имени базы данных", - "MySQL/MariaDB username and/or password not valid" : "Неверное имя пользователя и/или пароль MySQL/MariaDB", - "You need to enter either an existing account or the administrator." : "Вы должны войти или в существующий аккаунт или под администратором.", - "DB Error: \"%s\"" : "Ошибка БД: \"%s\"", - "Offending command was: \"%s\"" : "Вызываемая команда была: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Пользователь MySQL/MariaDB '%s'@'localhost' уже существует.", - "Drop this user from MySQL/MariaDB" : "Удалить данного участника из MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Пользователь MySQL/MariaDB '%s'@'%%' уже существует.", - "Drop this user from MySQL/MariaDB." : "Удалить данного участника из MySQL/MariaDB.", "Oracle connection could not be established" : "Соединение с Oracle не может быть установлено", "Oracle username and/or password not valid" : "Неверное имя пользователя и/или пароль Oracle", + "DB Error: \"%s\"" : "Ошибка БД: \"%s\"", + "Offending command was: \"%s\"" : "Вызываемая команда была: \"%s\"", + "You need to enter either an existing account or the administrator." : "Вы должны войти или в существующий аккаунт или под администратором.", "Offending command was: \"%s\", name: %s, password: %s" : "Вызываемая команда была: \"%s\", имя: %s, пароль: %s", "PostgreSQL username and/or password not valid" : "Неверное имя пользователя и/или пароль PostgreSQL", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X не поддерживается и %s может работать некорректно на данной платформе. Используйте на свой страх и риск!", diff --git a/lib/l10n/ru.json b/lib/l10n/ru.json index 914cf2ac1da..c295748adab 100644 --- a/lib/l10n/ru.json +++ b/lib/l10n/ru.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s введите имя пользователя базы данных.", "%s enter the database name." : "%s введите имя базы данных.", "%s you may not use dots in the database name" : "%s Вы не можете использовать точки в имени базы данных", - "MySQL/MariaDB username and/or password not valid" : "Неверное имя пользователя и/или пароль MySQL/MariaDB", - "You need to enter either an existing account or the administrator." : "Вы должны войти или в существующий аккаунт или под администратором.", - "DB Error: \"%s\"" : "Ошибка БД: \"%s\"", - "Offending command was: \"%s\"" : "Вызываемая команда была: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Пользователь MySQL/MariaDB '%s'@'localhost' уже существует.", - "Drop this user from MySQL/MariaDB" : "Удалить данного участника из MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Пользователь MySQL/MariaDB '%s'@'%%' уже существует.", - "Drop this user from MySQL/MariaDB." : "Удалить данного участника из MySQL/MariaDB.", "Oracle connection could not be established" : "Соединение с Oracle не может быть установлено", "Oracle username and/or password not valid" : "Неверное имя пользователя и/или пароль Oracle", + "DB Error: \"%s\"" : "Ошибка БД: \"%s\"", + "Offending command was: \"%s\"" : "Вызываемая команда была: \"%s\"", + "You need to enter either an existing account or the administrator." : "Вы должны войти или в существующий аккаунт или под администратором.", "Offending command was: \"%s\", name: %s, password: %s" : "Вызываемая команда была: \"%s\", имя: %s, пароль: %s", "PostgreSQL username and/or password not valid" : "Неверное имя пользователя и/или пароль PostgreSQL", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X не поддерживается и %s может работать некорректно на данной платформе. Используйте на свой страх и риск!", diff --git a/lib/l10n/sk_SK.js b/lib/l10n/sk_SK.js index 93db1d7aafd..ca0beaf334a 100644 --- a/lib/l10n/sk_SK.js +++ b/lib/l10n/sk_SK.js @@ -58,16 +58,11 @@ OC.L10N.register( "%s enter the database username." : "Zadajte používateľské meno %s databázy.", "%s enter the database name." : "Zadajte názov databázy pre %s databázy.", "%s you may not use dots in the database name" : "V názve databázy %s nemôžete používať bodky", - "MySQL/MariaDB username and/or password not valid" : "Používateľské meno a/alebo heslo pre MySQL/MariaDB databázu je neplatné", - "You need to enter either an existing account or the administrator." : "Musíte zadať jestvujúci účet alebo administrátora.", - "DB Error: \"%s\"" : "Chyba DB: \"%s\"", - "Offending command was: \"%s\"" : "Podozrivý príkaz bol: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Používateľ '%s'@'localhost' už v MySQL/MariaDB existuje.", - "Drop this user from MySQL/MariaDB" : "Zahodiť používateľa z MySQL/MariaDB.", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Používateľ '%s'@'%%' už v MySQL/MariaDB existuje", - "Drop this user from MySQL/MariaDB." : "Zahodiť používateľa z MySQL/MariaDB.", "Oracle connection could not be established" : "Nie je možné pripojiť sa k Oracle", "Oracle username and/or password not valid" : "Používateľské meno a/alebo heslo pre Oracle databázu je neplatné", + "DB Error: \"%s\"" : "Chyba DB: \"%s\"", + "Offending command was: \"%s\"" : "Podozrivý príkaz bol: \"%s\"", + "You need to enter either an existing account or the administrator." : "Musíte zadať jestvujúci účet alebo administrátora.", "Offending command was: \"%s\", name: %s, password: %s" : "Podozrivý príkaz bol: \"%s\", meno: %s, heslo: %s", "PostgreSQL username and/or password not valid" : "Používateľské meno a/alebo heslo pre PostgreSQL databázu je neplatné", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nie je podporovaný a %s nebude správne fungovať na tejto platforme. Použite ho na vlastné riziko!", diff --git a/lib/l10n/sk_SK.json b/lib/l10n/sk_SK.json index 45e1bd738ce..65334f58988 100644 --- a/lib/l10n/sk_SK.json +++ b/lib/l10n/sk_SK.json @@ -56,16 +56,11 @@ "%s enter the database username." : "Zadajte používateľské meno %s databázy.", "%s enter the database name." : "Zadajte názov databázy pre %s databázy.", "%s you may not use dots in the database name" : "V názve databázy %s nemôžete používať bodky", - "MySQL/MariaDB username and/or password not valid" : "Používateľské meno a/alebo heslo pre MySQL/MariaDB databázu je neplatné", - "You need to enter either an existing account or the administrator." : "Musíte zadať jestvujúci účet alebo administrátora.", - "DB Error: \"%s\"" : "Chyba DB: \"%s\"", - "Offending command was: \"%s\"" : "Podozrivý príkaz bol: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Používateľ '%s'@'localhost' už v MySQL/MariaDB existuje.", - "Drop this user from MySQL/MariaDB" : "Zahodiť používateľa z MySQL/MariaDB.", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Používateľ '%s'@'%%' už v MySQL/MariaDB existuje", - "Drop this user from MySQL/MariaDB." : "Zahodiť používateľa z MySQL/MariaDB.", "Oracle connection could not be established" : "Nie je možné pripojiť sa k Oracle", "Oracle username and/or password not valid" : "Používateľské meno a/alebo heslo pre Oracle databázu je neplatné", + "DB Error: \"%s\"" : "Chyba DB: \"%s\"", + "Offending command was: \"%s\"" : "Podozrivý príkaz bol: \"%s\"", + "You need to enter either an existing account or the administrator." : "Musíte zadať jestvujúci účet alebo administrátora.", "Offending command was: \"%s\", name: %s, password: %s" : "Podozrivý príkaz bol: \"%s\", meno: %s, heslo: %s", "PostgreSQL username and/or password not valid" : "Používateľské meno a/alebo heslo pre PostgreSQL databázu je neplatné", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nie je podporovaný a %s nebude správne fungovať na tejto platforme. Použite ho na vlastné riziko!", diff --git a/lib/l10n/sl.js b/lib/l10n/sl.js index 64e16a2a6ba..48df48d71c6 100644 --- a/lib/l10n/sl.js +++ b/lib/l10n/sl.js @@ -60,16 +60,11 @@ OC.L10N.register( "%s enter the database username." : "%s - vnos uporabniškega imena podatkovne zbirke.", "%s enter the database name." : "%s - vnos imena podatkovne zbirke.", "%s you may not use dots in the database name" : "%s - v imenu podatkovne zbirke ni dovoljeno uporabljati pik.", - "MySQL/MariaDB username and/or password not valid" : "Uporabniško ime ali geslo za MySQL/MariaDB ni veljavno", - "You need to enter either an existing account or the administrator." : "Prijaviti se je treba v obstoječi ali pa skrbniški račun.", - "DB Error: \"%s\"" : "Napaka podatkovne zbirke: \"%s\"", - "Offending command was: \"%s\"" : "Napačni ukaz je: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Uporabnik podatkovne zbirke MySQL/MariaDB '%s'@'localhost' že obstaja.", - "Drop this user from MySQL/MariaDB" : "Odstrani uporabnika iz podatkovne zbirke MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Uporabnik podatkovne zbirke MySQL/MariaDB '%s'@'%%' že obstaja.", - "Drop this user from MySQL/MariaDB." : "Odstrani uporabnika iz podatkovne zbirke MySQL/MariaDB.", "Oracle connection could not be established" : "Povezave s sistemom Oracle ni mogoče vzpostaviti.", "Oracle username and/or password not valid" : "Uporabniško ime ali geslo Oracle ni veljavno", + "DB Error: \"%s\"" : "Napaka podatkovne zbirke: \"%s\"", + "Offending command was: \"%s\"" : "Napačni ukaz je: \"%s\"", + "You need to enter either an existing account or the administrator." : "Prijaviti se je treba v obstoječi ali pa skrbniški račun.", "Offending command was: \"%s\", name: %s, password: %s" : "Napačni ukaz je: \"%s\", ime: %s, geslo: %s", "PostgreSQL username and/or password not valid" : "Uporabniško ime ali geslo PostgreSQL ni veljavno", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Sistem Mac OS X ni podprt, zato %s v tem okolju ne bo deloval zanesljivo. Program uporabljate na lastno odgovornost! ", diff --git a/lib/l10n/sl.json b/lib/l10n/sl.json index a7d519f495f..8507e1194d9 100644 --- a/lib/l10n/sl.json +++ b/lib/l10n/sl.json @@ -58,16 +58,11 @@ "%s enter the database username." : "%s - vnos uporabniškega imena podatkovne zbirke.", "%s enter the database name." : "%s - vnos imena podatkovne zbirke.", "%s you may not use dots in the database name" : "%s - v imenu podatkovne zbirke ni dovoljeno uporabljati pik.", - "MySQL/MariaDB username and/or password not valid" : "Uporabniško ime ali geslo za MySQL/MariaDB ni veljavno", - "You need to enter either an existing account or the administrator." : "Prijaviti se je treba v obstoječi ali pa skrbniški račun.", - "DB Error: \"%s\"" : "Napaka podatkovne zbirke: \"%s\"", - "Offending command was: \"%s\"" : "Napačni ukaz je: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Uporabnik podatkovne zbirke MySQL/MariaDB '%s'@'localhost' že obstaja.", - "Drop this user from MySQL/MariaDB" : "Odstrani uporabnika iz podatkovne zbirke MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Uporabnik podatkovne zbirke MySQL/MariaDB '%s'@'%%' že obstaja.", - "Drop this user from MySQL/MariaDB." : "Odstrani uporabnika iz podatkovne zbirke MySQL/MariaDB.", "Oracle connection could not be established" : "Povezave s sistemom Oracle ni mogoče vzpostaviti.", "Oracle username and/or password not valid" : "Uporabniško ime ali geslo Oracle ni veljavno", + "DB Error: \"%s\"" : "Napaka podatkovne zbirke: \"%s\"", + "Offending command was: \"%s\"" : "Napačni ukaz je: \"%s\"", + "You need to enter either an existing account or the administrator." : "Prijaviti se je treba v obstoječi ali pa skrbniški račun.", "Offending command was: \"%s\", name: %s, password: %s" : "Napačni ukaz je: \"%s\", ime: %s, geslo: %s", "PostgreSQL username and/or password not valid" : "Uporabniško ime ali geslo PostgreSQL ni veljavno", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Sistem Mac OS X ni podprt, zato %s v tem okolju ne bo deloval zanesljivo. Program uporabljate na lastno odgovornost! ", diff --git a/lib/l10n/sq.js b/lib/l10n/sq.js index 422814a14c1..fbf1b58beee 100644 --- a/lib/l10n/sq.js +++ b/lib/l10n/sq.js @@ -20,10 +20,10 @@ OC.L10N.register( "%s enter the database username." : "% shkruani përdoruesin e database-it.", "%s enter the database name." : "%s shkruani emrin e database-it.", "%s you may not use dots in the database name" : "%s nuk mund të përdorni pikat tek emri i database-it", - "You need to enter either an existing account or the administrator." : "Duhet të përdorni një llogari ekzistuese ose llogarinë e administratorit.", + "Oracle username and/or password not valid" : "Përdoruesi dhe/apo kodi i Oracle-it i pavlefshëm", "DB Error: \"%s\"" : "Veprim i gabuar i DB-it: \"%s\"", "Offending command was: \"%s\"" : "Komanda e gabuar ishte: \"%s\"", - "Oracle username and/or password not valid" : "Përdoruesi dhe/apo kodi i Oracle-it i pavlefshëm", + "You need to enter either an existing account or the administrator." : "Duhet të përdorni një llogari ekzistuese ose llogarinë e administratorit.", "Offending command was: \"%s\", name: %s, password: %s" : "Komanda e gabuar ishte: \"%s\", përdoruesi: %s, kodi: %s", "PostgreSQL username and/or password not valid" : "Përdoruesi dhe/apo kodi i PostgreSQL i pavlefshëm", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nuk është i mbështetur dhe %s nuk do të funksionojë si duhet në këtë platformë. Përdoreni nën përgjegjësinë tuaj!", diff --git a/lib/l10n/sq.json b/lib/l10n/sq.json index ad3f12eb1a0..b0a23493d40 100644 --- a/lib/l10n/sq.json +++ b/lib/l10n/sq.json @@ -18,10 +18,10 @@ "%s enter the database username." : "% shkruani përdoruesin e database-it.", "%s enter the database name." : "%s shkruani emrin e database-it.", "%s you may not use dots in the database name" : "%s nuk mund të përdorni pikat tek emri i database-it", - "You need to enter either an existing account or the administrator." : "Duhet të përdorni një llogari ekzistuese ose llogarinë e administratorit.", + "Oracle username and/or password not valid" : "Përdoruesi dhe/apo kodi i Oracle-it i pavlefshëm", "DB Error: \"%s\"" : "Veprim i gabuar i DB-it: \"%s\"", "Offending command was: \"%s\"" : "Komanda e gabuar ishte: \"%s\"", - "Oracle username and/or password not valid" : "Përdoruesi dhe/apo kodi i Oracle-it i pavlefshëm", + "You need to enter either an existing account or the administrator." : "Duhet të përdorni një llogari ekzistuese ose llogarinë e administratorit.", "Offending command was: \"%s\", name: %s, password: %s" : "Komanda e gabuar ishte: \"%s\", përdoruesi: %s, kodi: %s", "PostgreSQL username and/or password not valid" : "Përdoruesi dhe/apo kodi i PostgreSQL i pavlefshëm", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nuk është i mbështetur dhe %s nuk do të funksionojë si duhet në këtë platformë. Përdoreni nën përgjegjësinë tuaj!", diff --git a/lib/l10n/sr.js b/lib/l10n/sr.js index 15dd58c232a..286856622c7 100644 --- a/lib/l10n/sr.js +++ b/lib/l10n/sr.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s унеси корисничко име базе података.", "%s enter the database name." : "%s унеси име базе података.", "%s you may not use dots in the database name" : "%s не можете користити тачке у имену базе података", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB корисничко име и/или лозинка нису исправни", - "You need to enter either an existing account or the administrator." : "Потребно је да унесете или постојећи налог или администраторски.", - "DB Error: \"%s\"" : "Грешка базе података: \"%s\"", - "Offending command was: \"%s\"" : "Неисправна команда је: „%s“", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB корисник '%s'@'localhost' већ постоји.", - "Drop this user from MySQL/MariaDB" : "Обришите овог корисника из MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB корисник '%s'@'%%' већ постоји", - "Drop this user from MySQL/MariaDB." : "Обришите овог корисника из MySQL/MariaDB.", "Oracle connection could not be established" : "Веза са базом података Oracle не може бити успостављена", "Oracle username and/or password not valid" : "Oracle корисничко име и/или лозинка нису исправни", + "DB Error: \"%s\"" : "Грешка базе података: \"%s\"", + "Offending command was: \"%s\"" : "Неисправна команда је: „%s“", + "You need to enter either an existing account or the administrator." : "Потребно је да унесете или постојећи налог или администраторски.", "Offending command was: \"%s\", name: %s, password: %s" : "Неисправна команда је: „%s“, назив: %s, лозинка: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL корисничко име и/или лозинка нису исправни", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Мек ОС Икс није подржан и %s неће радити исправно на овој платформи. Користите га на сопствени ризик!", diff --git a/lib/l10n/sr.json b/lib/l10n/sr.json index 9170dcc796b..2e76f5e9000 100644 --- a/lib/l10n/sr.json +++ b/lib/l10n/sr.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s унеси корисничко име базе података.", "%s enter the database name." : "%s унеси име базе података.", "%s you may not use dots in the database name" : "%s не можете користити тачке у имену базе података", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB корисничко име и/или лозинка нису исправни", - "You need to enter either an existing account or the administrator." : "Потребно је да унесете или постојећи налог или администраторски.", - "DB Error: \"%s\"" : "Грешка базе података: \"%s\"", - "Offending command was: \"%s\"" : "Неисправна команда је: „%s“", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB корисник '%s'@'localhost' већ постоји.", - "Drop this user from MySQL/MariaDB" : "Обришите овог корисника из MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB корисник '%s'@'%%' већ постоји", - "Drop this user from MySQL/MariaDB." : "Обришите овог корисника из MySQL/MariaDB.", "Oracle connection could not be established" : "Веза са базом података Oracle не може бити успостављена", "Oracle username and/or password not valid" : "Oracle корисничко име и/или лозинка нису исправни", + "DB Error: \"%s\"" : "Грешка базе података: \"%s\"", + "Offending command was: \"%s\"" : "Неисправна команда је: „%s“", + "You need to enter either an existing account or the administrator." : "Потребно је да унесете или постојећи налог или администраторски.", "Offending command was: \"%s\", name: %s, password: %s" : "Неисправна команда је: „%s“, назив: %s, лозинка: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL корисничко име и/или лозинка нису исправни", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Мек ОС Икс није подржан и %s неће радити исправно на овој платформи. Користите га на сопствени ризик!", diff --git a/lib/l10n/sv.js b/lib/l10n/sv.js index 3a6389af3f5..c3b2d0655df 100644 --- a/lib/l10n/sv.js +++ b/lib/l10n/sv.js @@ -54,16 +54,11 @@ OC.L10N.register( "%s enter the database username." : "%s ange databasanvändare.", "%s enter the database name." : "%s ange databasnamn", "%s you may not use dots in the database name" : "%s du får inte använda punkter i databasnamnet", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB användarnamn och/eller lösenord är felaktigt", - "You need to enter either an existing account or the administrator." : "Du måste antingen ange ett befintligt konto eller administratör.", - "DB Error: \"%s\"" : "DB fel: \"%s\"", - "Offending command was: \"%s\"" : "Det felaktiga kommandot var: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB användare '%s'@'localhost' existerar redan.", - "Drop this user from MySQL/MariaDB" : "Radera denna användare från MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB användare '%s'@'%%' existerar redan", - "Drop this user from MySQL/MariaDB." : "Radera denna användare från MySQL/MariaDB.", "Oracle connection could not be established" : "Oracle-anslutning kunde inte etableras", "Oracle username and/or password not valid" : "Oracle-användarnamnet och/eller lösenordet är felaktigt", + "DB Error: \"%s\"" : "DB fel: \"%s\"", + "Offending command was: \"%s\"" : "Det felaktiga kommandot var: \"%s\"", + "You need to enter either an existing account or the administrator." : "Du måste antingen ange ett befintligt konto eller administratör.", "Offending command was: \"%s\", name: %s, password: %s" : "Det felande kommandot var: \"%s\", name: %s, password: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL-användarnamnet och/eller lösenordet är felaktigt", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X stöds inte och %s kommer inte att fungera korrekt på denna plattform. Använd på egen risk!", diff --git a/lib/l10n/sv.json b/lib/l10n/sv.json index 74539f5a932..5b53e335a79 100644 --- a/lib/l10n/sv.json +++ b/lib/l10n/sv.json @@ -52,16 +52,11 @@ "%s enter the database username." : "%s ange databasanvändare.", "%s enter the database name." : "%s ange databasnamn", "%s you may not use dots in the database name" : "%s du får inte använda punkter i databasnamnet", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB användarnamn och/eller lösenord är felaktigt", - "You need to enter either an existing account or the administrator." : "Du måste antingen ange ett befintligt konto eller administratör.", - "DB Error: \"%s\"" : "DB fel: \"%s\"", - "Offending command was: \"%s\"" : "Det felaktiga kommandot var: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB användare '%s'@'localhost' existerar redan.", - "Drop this user from MySQL/MariaDB" : "Radera denna användare från MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB användare '%s'@'%%' existerar redan", - "Drop this user from MySQL/MariaDB." : "Radera denna användare från MySQL/MariaDB.", "Oracle connection could not be established" : "Oracle-anslutning kunde inte etableras", "Oracle username and/or password not valid" : "Oracle-användarnamnet och/eller lösenordet är felaktigt", + "DB Error: \"%s\"" : "DB fel: \"%s\"", + "Offending command was: \"%s\"" : "Det felaktiga kommandot var: \"%s\"", + "You need to enter either an existing account or the administrator." : "Du måste antingen ange ett befintligt konto eller administratör.", "Offending command was: \"%s\", name: %s, password: %s" : "Det felande kommandot var: \"%s\", name: %s, password: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL-användarnamnet och/eller lösenordet är felaktigt", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X stöds inte och %s kommer inte att fungera korrekt på denna plattform. Använd på egen risk!", diff --git a/lib/l10n/th_TH.js b/lib/l10n/th_TH.js index f6e9a13b09c..c2d327e22d5 100644 --- a/lib/l10n/th_TH.js +++ b/lib/l10n/th_TH.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s ใส่ชื่อผู้ใช้ฐานข้อมูล", "%s enter the database name." : "%s ใส่ชื่อฐานข้อมูล", "%s you may not use dots in the database name" : "%s บางที่คุณไม่ควรใช้จุดในชื่อฐานข้อมูล", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB ชื่อผู้ใช้ และ/หรือ รหัสผ่านไม่ถูกต้อง", - "You need to enter either an existing account or the administrator." : "คุณจำเป็นต้องใส่ทั้งบัญชีที่มีอยู่หรือบัญชีผู้ดูแล", - "DB Error: \"%s\"" : "ข้อผิดพลาดในฐานข้อมูล: \"%s\"", - "Offending command was: \"%s\"" : "คำสั่งที่ทำผิดคือ: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB มีผู้ใช้งาน '%s'@'localhost' อยู่แล้ว", - "Drop this user from MySQL/MariaDB" : "ลดจำนวนผู้ใช้ลงจาก MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB มีผู้ใช้งาน '%s'@'%%' อยู่แล้ว", - "Drop this user from MySQL/MariaDB." : "ลดจำนวนผู้ใช้ลงจาก MySQL/MariaDB", "Oracle connection could not be established" : "ไม่สามารถสร้างการเชื่อมต่อกับ Oracle ", "Oracle username and/or password not valid" : "Oracle ชื่อผู้ใช้ และ/หรือ รหัสผ่านไม่ถูกต้อง", + "DB Error: \"%s\"" : "ข้อผิดพลาดในฐานข้อมูล: \"%s\"", + "Offending command was: \"%s\"" : "คำสั่งที่ทำผิดคือ: \"%s\"", + "You need to enter either an existing account or the administrator." : "คุณจำเป็นต้องใส่ทั้งบัญชีที่มีอยู่หรือบัญชีผู้ดูแล", "Offending command was: \"%s\", name: %s, password: %s" : "คำสั่งที่กระทำผิดคือ: \"%s\", ชื่อผู้ใช้: %s, รหัสผ่าน: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL ชื่อผู้ใช้ และ/หรือ รหัสผ่านไม่ถูกต้อง", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "ระบบปฏิบัติการ Mac OS X ไม่ได้รับการสนับสนุนและ %s จะไม่ทำงานบนแพลตฟอร์มนี้ ใช้มันบนความเสี่ยงของคุณเอง!", diff --git a/lib/l10n/th_TH.json b/lib/l10n/th_TH.json index 66328f44471..d6ffcb5f73d 100644 --- a/lib/l10n/th_TH.json +++ b/lib/l10n/th_TH.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s ใส่ชื่อผู้ใช้ฐานข้อมูล", "%s enter the database name." : "%s ใส่ชื่อฐานข้อมูล", "%s you may not use dots in the database name" : "%s บางที่คุณไม่ควรใช้จุดในชื่อฐานข้อมูล", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB ชื่อผู้ใช้ และ/หรือ รหัสผ่านไม่ถูกต้อง", - "You need to enter either an existing account or the administrator." : "คุณจำเป็นต้องใส่ทั้งบัญชีที่มีอยู่หรือบัญชีผู้ดูแล", - "DB Error: \"%s\"" : "ข้อผิดพลาดในฐานข้อมูล: \"%s\"", - "Offending command was: \"%s\"" : "คำสั่งที่ทำผิดคือ: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB มีผู้ใช้งาน '%s'@'localhost' อยู่แล้ว", - "Drop this user from MySQL/MariaDB" : "ลดจำนวนผู้ใช้ลงจาก MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB มีผู้ใช้งาน '%s'@'%%' อยู่แล้ว", - "Drop this user from MySQL/MariaDB." : "ลดจำนวนผู้ใช้ลงจาก MySQL/MariaDB", "Oracle connection could not be established" : "ไม่สามารถสร้างการเชื่อมต่อกับ Oracle ", "Oracle username and/or password not valid" : "Oracle ชื่อผู้ใช้ และ/หรือ รหัสผ่านไม่ถูกต้อง", + "DB Error: \"%s\"" : "ข้อผิดพลาดในฐานข้อมูล: \"%s\"", + "Offending command was: \"%s\"" : "คำสั่งที่ทำผิดคือ: \"%s\"", + "You need to enter either an existing account or the administrator." : "คุณจำเป็นต้องใส่ทั้งบัญชีที่มีอยู่หรือบัญชีผู้ดูแล", "Offending command was: \"%s\", name: %s, password: %s" : "คำสั่งที่กระทำผิดคือ: \"%s\", ชื่อผู้ใช้: %s, รหัสผ่าน: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL ชื่อผู้ใช้ และ/หรือ รหัสผ่านไม่ถูกต้อง", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "ระบบปฏิบัติการ Mac OS X ไม่ได้รับการสนับสนุนและ %s จะไม่ทำงานบนแพลตฟอร์มนี้ ใช้มันบนความเสี่ยงของคุณเอง!", diff --git a/lib/l10n/tr.js b/lib/l10n/tr.js index c23981fb818..d26505b4a1e 100644 --- a/lib/l10n/tr.js +++ b/lib/l10n/tr.js @@ -66,16 +66,11 @@ OC.L10N.register( "%s enter the database username." : "%s veritabanı kullanıcı adını girin.", "%s enter the database name." : "%s veritabanı adını girin.", "%s you may not use dots in the database name" : "%s veritabanı adında nokta kullanamayabilirsiniz", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB kullanıcı adı ve/veya parolası geçersiz", - "You need to enter either an existing account or the administrator." : "Mevcut bit hesap ya da yönetici hesabını girmelisiniz.", - "DB Error: \"%s\"" : "VT Hatası: \"%s\"", - "Offending command was: \"%s\"" : "Saldırgan komut: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB kullanıcı '%s'@'localhost' zaten mevcut.", - "Drop this user from MySQL/MariaDB" : "Bu kullanıcıyı MySQL/MariaDB'dan at (drop)", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB kullanıcısı '%s'@'%%' zaten mevcut", - "Drop this user from MySQL/MariaDB." : "Bu kullanıcıyı MySQL/MariaDB'dan at (drop).", "Oracle connection could not be established" : "Oracle bağlantısı kurulamadı", "Oracle username and/or password not valid" : "Oracle kullanıcı adı ve/veya parolası geçerli değil", + "DB Error: \"%s\"" : "VT Hatası: \"%s\"", + "Offending command was: \"%s\"" : "Saldırgan komut: \"%s\"", + "You need to enter either an existing account or the administrator." : "Mevcut bit hesap ya da yönetici hesabını girmelisiniz.", "Offending command was: \"%s\", name: %s, password: %s" : "Hatalı komut: \"%s\", ad: %s, parola: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL kullanıcı adı ve/veya parolası geçerli değil", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X desteklenmiyor ve %s bu platformda düzgün çalışmayacak. Kendi riskinizle kullanın!", diff --git a/lib/l10n/tr.json b/lib/l10n/tr.json index b5dc4e85b96..4170115682d 100644 --- a/lib/l10n/tr.json +++ b/lib/l10n/tr.json @@ -64,16 +64,11 @@ "%s enter the database username." : "%s veritabanı kullanıcı adını girin.", "%s enter the database name." : "%s veritabanı adını girin.", "%s you may not use dots in the database name" : "%s veritabanı adında nokta kullanamayabilirsiniz", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB kullanıcı adı ve/veya parolası geçersiz", - "You need to enter either an existing account or the administrator." : "Mevcut bit hesap ya da yönetici hesabını girmelisiniz.", - "DB Error: \"%s\"" : "VT Hatası: \"%s\"", - "Offending command was: \"%s\"" : "Saldırgan komut: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB kullanıcı '%s'@'localhost' zaten mevcut.", - "Drop this user from MySQL/MariaDB" : "Bu kullanıcıyı MySQL/MariaDB'dan at (drop)", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB kullanıcısı '%s'@'%%' zaten mevcut", - "Drop this user from MySQL/MariaDB." : "Bu kullanıcıyı MySQL/MariaDB'dan at (drop).", "Oracle connection could not be established" : "Oracle bağlantısı kurulamadı", "Oracle username and/or password not valid" : "Oracle kullanıcı adı ve/veya parolası geçerli değil", + "DB Error: \"%s\"" : "VT Hatası: \"%s\"", + "Offending command was: \"%s\"" : "Saldırgan komut: \"%s\"", + "You need to enter either an existing account or the administrator." : "Mevcut bit hesap ya da yönetici hesabını girmelisiniz.", "Offending command was: \"%s\", name: %s, password: %s" : "Hatalı komut: \"%s\", ad: %s, parola: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL kullanıcı adı ve/veya parolası geçerli değil", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X desteklenmiyor ve %s bu platformda düzgün çalışmayacak. Kendi riskinizle kullanın!", diff --git a/lib/l10n/uk.js b/lib/l10n/uk.js index c01a045095f..7c90934c71f 100644 --- a/lib/l10n/uk.js +++ b/lib/l10n/uk.js @@ -60,16 +60,11 @@ OC.L10N.register( "%s enter the database username." : "%s введіть ім'я користувача бази даних.", "%s enter the database name." : "%s введіть назву бази даних.", "%s you may not use dots in the database name" : "%s не можна використовувати крапки в назві бази даних", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB ім'я користувача та/або пароль не дійсні", - "You need to enter either an existing account or the administrator." : "Вам потрібно ввести або існуючий обліковий запис або administrator.", - "DB Error: \"%s\"" : "Помилка БД: \"%s\"", - "Offending command was: \"%s\"" : "Команда, що викликала проблему: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Користувач MySQL/MariaDB '%s'@'localhost' вже існує.", - "Drop this user from MySQL/MariaDB" : "Видалити цього користувача з MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Користувач MySQL/MariaDB '%s'@'%%' вже існує", - "Drop this user from MySQL/MariaDB." : "Видалити цього користувача з MySQL/MariaDB.", "Oracle connection could not be established" : "Не можемо з'єднатися з Oracle ", "Oracle username and/or password not valid" : "Oracle ім'я користувача та/або пароль не дійсні", + "DB Error: \"%s\"" : "Помилка БД: \"%s\"", + "Offending command was: \"%s\"" : "Команда, що викликала проблему: \"%s\"", + "You need to enter either an existing account or the administrator." : "Вам потрібно ввести або існуючий обліковий запис або administrator.", "Offending command was: \"%s\", name: %s, password: %s" : "Команда, що викликала проблему: \"%s\", ім'я: %s, пароль: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL ім'я користувача та/або пароль не дійсні", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X не підтримується і %s не буде коректно працювати на цій платформі. Випробовуєте на свій риск!", diff --git a/lib/l10n/uk.json b/lib/l10n/uk.json index d4accc5f93e..f0ca3a30641 100644 --- a/lib/l10n/uk.json +++ b/lib/l10n/uk.json @@ -58,16 +58,11 @@ "%s enter the database username." : "%s введіть ім'я користувача бази даних.", "%s enter the database name." : "%s введіть назву бази даних.", "%s you may not use dots in the database name" : "%s не можна використовувати крапки в назві бази даних", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB ім'я користувача та/або пароль не дійсні", - "You need to enter either an existing account or the administrator." : "Вам потрібно ввести або існуючий обліковий запис або administrator.", - "DB Error: \"%s\"" : "Помилка БД: \"%s\"", - "Offending command was: \"%s\"" : "Команда, що викликала проблему: \"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "Користувач MySQL/MariaDB '%s'@'localhost' вже існує.", - "Drop this user from MySQL/MariaDB" : "Видалити цього користувача з MySQL/MariaDB", - "MySQL/MariaDB user '%s'@'%%' already exists" : "Користувач MySQL/MariaDB '%s'@'%%' вже існує", - "Drop this user from MySQL/MariaDB." : "Видалити цього користувача з MySQL/MariaDB.", "Oracle connection could not be established" : "Не можемо з'єднатися з Oracle ", "Oracle username and/or password not valid" : "Oracle ім'я користувача та/або пароль не дійсні", + "DB Error: \"%s\"" : "Помилка БД: \"%s\"", + "Offending command was: \"%s\"" : "Команда, що викликала проблему: \"%s\"", + "You need to enter either an existing account or the administrator." : "Вам потрібно ввести або існуючий обліковий запис або administrator.", "Offending command was: \"%s\", name: %s, password: %s" : "Команда, що викликала проблему: \"%s\", ім'я: %s, пароль: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL ім'я користувача та/або пароль не дійсні", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X не підтримується і %s не буде коректно працювати на цій платформі. Випробовуєте на свій риск!", diff --git a/lib/l10n/zh_CN.js b/lib/l10n/zh_CN.js index 305c4f097b6..9ec40a93aab 100644 --- a/lib/l10n/zh_CN.js +++ b/lib/l10n/zh_CN.js @@ -39,16 +39,11 @@ OC.L10N.register( "%s enter the database username." : "%s 输入数据库用户名。", "%s enter the database name." : "%s 输入数据库名称。", "%s you may not use dots in the database name" : "%s 您不能在数据库名称中使用英文句号。", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB 数据库用户名和/或密码无效", - "You need to enter either an existing account or the administrator." : "你需要输入一个数据库中已有的账户或管理员账户。", - "DB Error: \"%s\"" : "数据库错误:\"%s\"", - "Offending command was: \"%s\"" : "冲突命令为:\"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB 用户 '%s'@'localhost' 已存在。", - "Drop this user from MySQL/MariaDB" : "建议从 MySQL/MariaDB 数据库中删除此用户", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB 用户 '%s'@'%%' 已存在", - "Drop this user from MySQL/MariaDB." : "建议从 MySQL/MariaDB 数据库中删除此用户。", "Oracle connection could not be established" : "不能建立甲骨文连接", "Oracle username and/or password not valid" : "Oracle 数据库用户名和/或密码无效", + "DB Error: \"%s\"" : "数据库错误:\"%s\"", + "Offending command was: \"%s\"" : "冲突命令为:\"%s\"", + "You need to enter either an existing account or the administrator." : "你需要输入一个数据库中已有的账户或管理员账户。", "Offending command was: \"%s\", name: %s, password: %s" : "冲突命令为:\"%s\",名称:%s,密码:%s", "PostgreSQL username and/or password not valid" : "PostgreSQL 数据库用户名和/或密码无效", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X 不被支持并且 %s 在这个平台上无法正常工作。请自行承担风险!", diff --git a/lib/l10n/zh_CN.json b/lib/l10n/zh_CN.json index ff07f7840b3..1d9fbf2ae3c 100644 --- a/lib/l10n/zh_CN.json +++ b/lib/l10n/zh_CN.json @@ -37,16 +37,11 @@ "%s enter the database username." : "%s 输入数据库用户名。", "%s enter the database name." : "%s 输入数据库名称。", "%s you may not use dots in the database name" : "%s 您不能在数据库名称中使用英文句号。", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB 数据库用户名和/或密码无效", - "You need to enter either an existing account or the administrator." : "你需要输入一个数据库中已有的账户或管理员账户。", - "DB Error: \"%s\"" : "数据库错误:\"%s\"", - "Offending command was: \"%s\"" : "冲突命令为:\"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB 用户 '%s'@'localhost' 已存在。", - "Drop this user from MySQL/MariaDB" : "建议从 MySQL/MariaDB 数据库中删除此用户", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB 用户 '%s'@'%%' 已存在", - "Drop this user from MySQL/MariaDB." : "建议从 MySQL/MariaDB 数据库中删除此用户。", "Oracle connection could not be established" : "不能建立甲骨文连接", "Oracle username and/or password not valid" : "Oracle 数据库用户名和/或密码无效", + "DB Error: \"%s\"" : "数据库错误:\"%s\"", + "Offending command was: \"%s\"" : "冲突命令为:\"%s\"", + "You need to enter either an existing account or the administrator." : "你需要输入一个数据库中已有的账户或管理员账户。", "Offending command was: \"%s\", name: %s, password: %s" : "冲突命令为:\"%s\",名称:%s,密码:%s", "PostgreSQL username and/or password not valid" : "PostgreSQL 数据库用户名和/或密码无效", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X 不被支持并且 %s 在这个平台上无法正常工作。请自行承担风险!", diff --git a/lib/l10n/zh_TW.js b/lib/l10n/zh_TW.js index acd5bde29f0..d9a7dd965d6 100644 --- a/lib/l10n/zh_TW.js +++ b/lib/l10n/zh_TW.js @@ -44,16 +44,11 @@ OC.L10N.register( "%s enter the database username." : "%s 輸入資料庫使用者名稱。", "%s enter the database name." : "%s 輸入資料庫名稱。", "%s you may not use dots in the database name" : "%s 資料庫名稱不能包含小數點", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB 使用者或密碼不正確", - "You need to enter either an existing account or the administrator." : "您必須輸入一個現有的帳號或管理員帳號。", - "DB Error: \"%s\"" : "資料庫錯誤:\"%s\"", - "Offending command was: \"%s\"" : "有問題的指令是:\"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB 使用者 '%s'@'localhost' 已經存在", - "Drop this user from MySQL/MariaDB" : "自 MySQL/MariaDB 刪除這個使用者", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB 使用者 '%s'@'%%' 已經存在", - "Drop this user from MySQL/MariaDB." : "自 MySQL/MariaDB 刪除這個使用者", "Oracle connection could not be established" : "無法建立 Oracle 資料庫連線", "Oracle username and/or password not valid" : "Oracle 用戶名和/或密碼無效", + "DB Error: \"%s\"" : "資料庫錯誤:\"%s\"", + "Offending command was: \"%s\"" : "有問題的指令是:\"%s\"", + "You need to enter either an existing account or the administrator." : "您必須輸入一個現有的帳號或管理員帳號。", "Offending command was: \"%s\", name: %s, password: %s" : "有問題的指令是:\"%s\" ,使用者:\"%s\",密碼:\"%s\"", "PostgreSQL username and/or password not valid" : "PostgreSQL 用戶名和/或密碼無效", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "不支援 Mac OS X 而且 %s 在這個平台上面無法正常運作,請自行衡量風險!", diff --git a/lib/l10n/zh_TW.json b/lib/l10n/zh_TW.json index 6c7eb7b3f55..3544bde0b1e 100644 --- a/lib/l10n/zh_TW.json +++ b/lib/l10n/zh_TW.json @@ -42,16 +42,11 @@ "%s enter the database username." : "%s 輸入資料庫使用者名稱。", "%s enter the database name." : "%s 輸入資料庫名稱。", "%s you may not use dots in the database name" : "%s 資料庫名稱不能包含小數點", - "MySQL/MariaDB username and/or password not valid" : "MySQL/MariaDB 使用者或密碼不正確", - "You need to enter either an existing account or the administrator." : "您必須輸入一個現有的帳號或管理員帳號。", - "DB Error: \"%s\"" : "資料庫錯誤:\"%s\"", - "Offending command was: \"%s\"" : "有問題的指令是:\"%s\"", - "MySQL/MariaDB user '%s'@'localhost' exists already." : "MySQL/MariaDB 使用者 '%s'@'localhost' 已經存在", - "Drop this user from MySQL/MariaDB" : "自 MySQL/MariaDB 刪除這個使用者", - "MySQL/MariaDB user '%s'@'%%' already exists" : "MySQL/MariaDB 使用者 '%s'@'%%' 已經存在", - "Drop this user from MySQL/MariaDB." : "自 MySQL/MariaDB 刪除這個使用者", "Oracle connection could not be established" : "無法建立 Oracle 資料庫連線", "Oracle username and/or password not valid" : "Oracle 用戶名和/或密碼無效", + "DB Error: \"%s\"" : "資料庫錯誤:\"%s\"", + "Offending command was: \"%s\"" : "有問題的指令是:\"%s\"", + "You need to enter either an existing account or the administrator." : "您必須輸入一個現有的帳號或管理員帳號。", "Offending command was: \"%s\", name: %s, password: %s" : "有問題的指令是:\"%s\" ,使用者:\"%s\",密碼:\"%s\"", "PostgreSQL username and/or password not valid" : "PostgreSQL 用戶名和/或密碼無效", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "不支援 Mac OS X 而且 %s 在這個平台上面無法正常運作,請自行衡量風險!", diff --git a/lib/private/activitymanager.php b/lib/private/activitymanager.php index 7b1d5d29f2e..938335a87e1 100644 --- a/lib/private/activitymanager.php +++ b/lib/private/activitymanager.php @@ -56,14 +56,16 @@ class ActivityManager implements IManager { $this->config = $config; } - /** - * @var \Closure[] - */ + /** @var \Closure[] */ + private $consumersClosures = array(); + + /** @var IConsumer[] */ private $consumers = array(); - /** - * @var \Closure[] - */ + /** @var \Closure[] */ + private $extensionsClosures = array(); + + /** @var IExtension[] */ private $extensions = array(); /** @var array list of filters "name" => "is valid" */ @@ -80,6 +82,48 @@ class ActivityManager implements IManager { protected $specialParameters = array(); /** + * @return \OCP\Activity\IConsumer[] + */ + protected function getConsumers() { + if (!empty($this->consumers)) { + return $this->consumers; + } + + $this->consumers = []; + foreach($this->consumersClosures as $consumer) { + $c = $consumer(); + if ($c instanceof IConsumer) { + $this->consumers[] = $c; + } else { + throw new \InvalidArgumentException('The given consumer does not implement the \OCP\Activity\IConsumer interface'); + } + } + + return $this->consumers; + } + + /** + * @return \OCP\Activity\IExtension[] + */ + protected function getExtensions() { + if (!empty($this->extensions)) { + return $this->extensions; + } + + $this->extensions = []; + foreach($this->extensionsClosures as $extension) { + $e = $extension(); + if ($e instanceof IExtension) { + $this->extensions[] = $e; + } else { + throw new \InvalidArgumentException('The given extension does not implement the \OCP\Activity\IExtension interface'); + } + } + + return $this->extensions; + } + + /** * @param $app * @param $subject * @param $subjectParams @@ -93,10 +137,8 @@ class ActivityManager implements IManager { * @return mixed */ function publishActivity($app, $subject, $subjectParams, $message, $messageParams, $file, $link, $affectedUser, $type, $priority) { - foreach($this->consumers as $consumer) { - $c = $consumer(); - if ($c instanceof IConsumer) { - try { + foreach($this->getConsumers() as $c) { + try { $c->receive( $app, $subject, @@ -108,11 +150,9 @@ class ActivityManager implements IManager { $affectedUser, $type, $priority); - } catch (\Exception $ex) { - // TODO: log the exception - } + } catch (\Exception $ex) { + // TODO: log the exception } - } } @@ -125,7 +165,8 @@ class ActivityManager implements IManager { * @param \Closure $callable */ function registerConsumer(\Closure $callable) { - array_push($this->consumers, $callable); + array_push($this->consumersClosures, $callable); + $this->consumers = []; } /** @@ -138,7 +179,8 @@ class ActivityManager implements IManager { * @return void */ function registerExtension(\Closure $callable) { - array_push($this->extensions, $callable); + array_push($this->extensionsClosures, $callable); + $this->extensions = []; } /** @@ -149,13 +191,10 @@ class ActivityManager implements IManager { */ function getNotificationTypes($languageCode) { $notificationTypes = array(); - foreach($this->extensions as $extension) { - $c = $extension(); - if ($c instanceof IExtension) { - $result = $c->getNotificationTypes($languageCode); - if (is_array($result)) { - $notificationTypes = array_merge($notificationTypes, $result); - } + foreach ($this->getExtensions() as $c) { + $result = $c->getNotificationTypes($languageCode); + if (is_array($result)) { + $notificationTypes = array_merge($notificationTypes, $result); } } @@ -168,13 +207,10 @@ class ActivityManager implements IManager { */ function getDefaultTypes($method) { $defaultTypes = array(); - foreach($this->extensions as $extension) { - $c = $extension(); - if ($c instanceof IExtension) { - $types = $c->getDefaultTypes($method); - if (is_array($types)) { - $defaultTypes = array_merge($types, $defaultTypes); - } + foreach ($this->getExtensions() as $c) { + $types = $c->getDefaultTypes($method); + if (is_array($types)) { + $defaultTypes = array_merge($types, $defaultTypes); } } return $defaultTypes; @@ -189,14 +225,11 @@ class ActivityManager implements IManager { return $this->typeIcons[$type]; } - foreach($this->extensions as $extension) { - $c = $extension(); - if ($c instanceof IExtension) { - $icon = $c->getTypeIcon($type); - if (is_string($icon)) { - $this->typeIcons[$type] = $icon; - return $icon; - } + foreach ($this->getExtensions() as $c) { + $icon = $c->getTypeIcon($type); + if (is_string($icon)) { + $this->typeIcons[$type] = $icon; + return $icon; } } @@ -214,13 +247,10 @@ class ActivityManager implements IManager { * @return string|false */ function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode) { - foreach($this->extensions as $extension) { - $c = $extension(); - if ($c instanceof IExtension) { - $translation = $c->translate($app, $text, $params, $stripPath, $highlightParams, $languageCode); - if (is_string($translation)) { - return $translation; - } + foreach ($this->getExtensions() as $c) { + $translation = $c->translate($app, $text, $params, $stripPath, $highlightParams, $languageCode); + if (is_string($translation)) { + return $translation; } } @@ -241,14 +271,11 @@ class ActivityManager implements IManager { $this->specialParameters[$app] = array(); } - foreach($this->extensions as $extension) { - $c = $extension(); - if ($c instanceof IExtension) { - $specialParameter = $c->getSpecialParameterList($app, $text); - if (is_array($specialParameter)) { - $this->specialParameters[$app][$text] = $specialParameter; - return $specialParameter; - } + foreach ($this->getExtensions() as $c) { + $specialParameter = $c->getSpecialParameterList($app, $text); + if (is_array($specialParameter)) { + $this->specialParameters[$app][$text] = $specialParameter; + return $specialParameter; } } @@ -261,13 +288,10 @@ class ActivityManager implements IManager { * @return integer|false */ function getGroupParameter($activity) { - foreach($this->extensions as $extension) { - $c = $extension(); - if ($c instanceof IExtension) { - $parameter = $c->getGroupParameter($activity); - if ($parameter !== false) { - return $parameter; - } + foreach ($this->getExtensions() as $c) { + $parameter = $c->getGroupParameter($activity); + if ($parameter !== false) { + return $parameter; } } @@ -282,14 +306,11 @@ class ActivityManager implements IManager { 'apps' => array(), 'top' => array(), ); - foreach($this->extensions as $extension) { - $c = $extension(); - if ($c instanceof IExtension) { - $additionalEntries = $c->getNavigation(); - if (is_array($additionalEntries)) { - $entries['apps'] = array_merge($entries['apps'], $additionalEntries['apps']); - $entries['top'] = array_merge($entries['top'], $additionalEntries['top']); - } + foreach ($this->getExtensions() as $c) { + $additionalEntries = $c->getNavigation(); + if (is_array($additionalEntries)) { + $entries['apps'] = array_merge($entries['apps'], $additionalEntries['apps']); + $entries['top'] = array_merge($entries['top'], $additionalEntries['top']); } } @@ -305,13 +326,10 @@ class ActivityManager implements IManager { return $this->validFilters[$filterValue]; } - foreach($this->extensions as $extension) { - $c = $extension(); - if ($c instanceof IExtension) { - if ($c->isFilterValid($filterValue) === true) { - $this->validFilters[$filterValue] = true; - return true; - } + foreach ($this->getExtensions() as $c) { + if ($c->isFilterValid($filterValue) === true) { + $this->validFilters[$filterValue] = true; + return true; } } @@ -329,13 +347,10 @@ class ActivityManager implements IManager { return $types; } - foreach($this->extensions as $extension) { - $c = $extension(); - if ($c instanceof IExtension) { - $result = $c->filterNotificationTypes($types, $filter); - if (is_array($result)) { - $types = $result; - } + foreach ($this->getExtensions() as $c) { + $result = $c->filterNotificationTypes($types, $filter); + if (is_array($result)) { + $types = $result; } } return $types; @@ -353,16 +368,13 @@ class ActivityManager implements IManager { $conditions = array(); $parameters = array(); - foreach($this->extensions as $extension) { - $c = $extension(); - if ($c instanceof IExtension) { - $result = $c->getQueryForFilter($filter); - if (is_array($result)) { - list($condition, $parameter) = $result; - if ($condition && is_array($parameter)) { - $conditions[] = $condition; - $parameters = array_merge($parameters, $parameter); - } + foreach ($this->getExtensions() as $c) { + $result = $c->getQueryForFilter($filter); + if (is_array($result)) { + list($condition, $parameter) = $result; + if ($condition && is_array($parameter)) { + $conditions[] = $condition; + $parameters = array_merge($parameters, $parameter); } } } diff --git a/lib/private/api.php b/lib/private/api.php index 8e483b7efe9..fb2110471b2 100644 --- a/lib/private/api.php +++ b/lib/private/api.php @@ -1,4 +1,7 @@ <?php +use OCP\API; +use OCP\AppFramework\Http; + /** * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> @@ -82,7 +85,7 @@ class OC_API { * @param array $requirements */ public static function register($method, $url, $action, $app, - $authLevel = \OCP\API::USER_AUTH, + $authLevel = API::USER_AUTH, $defaults = array(), $requirements = array()) { $name = strtolower($method).$url; @@ -123,7 +126,7 @@ class OC_API { if(!self::isAuthorised($action)) { $responses[] = array( 'app' => $action['app'], - 'response' => new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED, 'Unauthorised'), + 'response' => new OC_OCS_Result(null, API::RESPOND_UNAUTHORISED, 'Unauthorised'), 'shipped' => OC_App::isShipped($action['app']), ); continue; @@ -131,7 +134,7 @@ class OC_API { if(!is_callable($action['action'])) { $responses[] = array( 'app' => $action['app'], - 'response' => new OC_OCS_Result(null, \OCP\API::RESPOND_NOT_FOUND, 'Api method not found'), + 'response' => new OC_OCS_Result(null, API::RESPOND_NOT_FOUND, 'Api method not found'), 'shipped' => OC_App::isShipped($action['app']), ); continue; @@ -252,15 +255,15 @@ class OC_API { private static function isAuthorised($action) { $level = $action['authlevel']; switch($level) { - case \OCP\API::GUEST_AUTH: + case API::GUEST_AUTH: // Anyone can access return true; break; - case \OCP\API::USER_AUTH: + case API::USER_AUTH: // User required return self::loginUser(); break; - case \OCP\API::SUBADMIN_AUTH: + case API::SUBADMIN_AUTH: // Check for subadmin $user = self::loginUser(); if(!$user) { @@ -275,7 +278,7 @@ class OC_API { } } break; - case \OCP\API::ADMIN_AUTH: + case API::ADMIN_AUTH: // Check for admin $user = self::loginUser(); if(!$user) { @@ -342,28 +345,25 @@ class OC_API { */ public static function respond($result, $format='xml') { // Send 401 headers if unauthorised - if($result->getStatusCode() === \OCP\API::RESPOND_UNAUTHORISED) { + if($result->getStatusCode() === API::RESPOND_UNAUTHORISED) { header('WWW-Authenticate: Basic realm="Authorisation Required"'); header('HTTP/1.0 401 Unauthorized'); } - $response = array( - 'ocs' => array( - 'meta' => $result->getMeta(), - 'data' => $result->getData(), - ), - ); - if ($format == 'json') { - OC_JSON::encodedPrint($response); - } else if ($format == 'xml') { - header('Content-type: text/xml; charset=UTF-8'); - $writer = new XMLWriter(); - $writer->openMemory(); - $writer->setIndent( true ); - $writer->startDocument(); - self::toXML($response, $writer); - $writer->endDocument(); - echo $writer->outputMemory(true); + + foreach($result->getHeaders() as $name => $value) { + header($name . ': ' . $value); } + + if (self::isV2()) { + $statusCode = self::mapStatusCodes($result->getStatusCode()); + if (!is_null($statusCode)) { + OC_Response::setStatus($statusCode); + } + } + + self::setContentType($format); + $body = self::renderResult($result, $format); + echo $body; } /** @@ -400,8 +400,8 @@ class OC_API { /** * Based on the requested format the response content type is set */ - public static function setContentType() { - $format = self::requestedFormat(); + public static function setContentType($format = null) { + $format = is_null($format) ? self::requestedFormat() : $format; if ($format === 'xml') { header('Content-type: text/xml; charset=UTF-8'); return; @@ -415,5 +415,64 @@ class OC_API { header('Content-Type: application/octet-stream; charset=utf-8'); } + /** + * @return boolean + */ + private static function isV2() { + $request = \OC::$server->getRequest(); + $script = $request->getScriptName(); + + return $script === '/ocs/v2.php'; + } + + /** + * @param integer $sc + * @return int + */ + public static function mapStatusCodes($sc) { + switch ($sc) { + case API::RESPOND_NOT_FOUND: + return Http::STATUS_NOT_FOUND; + case API::RESPOND_SERVER_ERROR: + return Http::STATUS_INTERNAL_SERVER_ERROR; + case API::RESPOND_UNKNOWN_ERROR: + return Http::STATUS_INTERNAL_SERVER_ERROR; + case API::RESPOND_UNAUTHORISED: + // already handled for v1 + return null; + case 100: + return Http::STATUS_OK; + } + // any 2xx, 4xx and 5xx will be used as is + if ($sc >= 200 && $sc < 600) { + return $sc; + } + + return Http::STATUS_BAD_REQUEST; + } + /** + * @param OC_OCS_Result $result + * @param string $format + * @return string + */ + public static function renderResult($result, $format) { + $response = array( + 'ocs' => array( + 'meta' => $result->getMeta(), + 'data' => $result->getData(), + ), + ); + if ($format == 'json') { + return OC_JSON::encode($response); + } + + $writer = new XMLWriter(); + $writer->openMemory(); + $writer->setIndent(true); + $writer->startDocument(); + self::toXML($response, $writer); + $writer->endDocument(); + return $writer->outputMemory(true); + } } diff --git a/lib/private/appframework/dependencyinjection/dicontainer.php b/lib/private/appframework/dependencyinjection/dicontainer.php index c7ce6545972..544da74a010 100644 --- a/lib/private/appframework/dependencyinjection/dicontainer.php +++ b/lib/private/appframework/dependencyinjection/dicontainer.php @@ -57,6 +57,7 @@ class DIContainer extends SimpleContainer implements IAppContainer { * @param string $appName the name of the app */ public function __construct($appName, $urlParams = array()){ + parent::__construct(); $this['AppName'] = $appName; $this['urlParams'] = $urlParams; @@ -96,6 +97,10 @@ class DIContainer extends SimpleContainer implements IAppContainer { return $this->getServer()->getMemCacheFactory(); }); + $this->registerService('OC\\CapabilitiesManager', function($c) { + return $this->getServer()->getCapabilitiesManager(); + }); + $this->registerService('OCP\\IConfig', function($c) { return $this->getServer()->getConfig(); }); @@ -212,6 +217,10 @@ class DIContainer extends SimpleContainer implements IAppContainer { return $this->getServer()->getUserSession(); }); + $this->registerService('OCP\\ISession', function($c) { + return $this->getServer()->getSession(); + }); + $this->registerService('ServerContainer', function ($c) { return $this->getServer(); }); @@ -386,5 +395,15 @@ class DIContainer extends SimpleContainer implements IAppContainer { \OCP\Util::writeLog($this->getAppName(), $message, $level); } + /** + * Register a capability + * + * @param string $serviceName e.g. 'OCA\Files\Capabilities' + */ + public function registerCapability($serviceName) { + $this->query('OC\CapabilitiesManager')->registerCapability(function() use ($serviceName) { + return $this->query($serviceName); + }); + } } diff --git a/lib/private/appframework/http/request.php b/lib/private/appframework/http/request.php index baf2f0c4745..aaad286e843 100644 --- a/lib/private/appframework/http/request.php +++ b/lib/private/appframework/http/request.php @@ -416,12 +416,10 @@ class Request implements \ArrayAccess, \Countable, IRequest { } // Check if the token is valid - if($token !== $this->items['requesttoken']) { - // Not valid - return false; - } else { - // Valid token + if(\OCP\Security\StringUtils::equals($token, $this->items['requesttoken'])) { return true; + } else { + return false; } } @@ -454,7 +452,10 @@ class Request implements \ArrayAccess, \Countable, IRequest { $trustedProxies = $this->config->getSystemValue('trusted_proxies', []); if(is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) { - $forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', []); + $forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [ + 'HTTP_X_FORWARDED_FOR' + // only have one default, so we cannot ship an insecure product out of the box + ]); foreach($forwardedForHeaders as $header) { if(isset($this->server[$header])) { diff --git a/lib/private/appframework/utility/simplecontainer.php b/lib/private/appframework/utility/simplecontainer.php index 9458d59014a..83a08acde26 100644 --- a/lib/private/appframework/utility/simplecontainer.php +++ b/lib/private/appframework/utility/simplecontainer.php @@ -150,7 +150,7 @@ class SimpleContainer extends Container implements IContainer { public function registerAlias($alias, $target) { $this->registerService($alias, function (IContainer $container) use ($target) { return $container->query($target); - }); + }, false); } /* diff --git a/lib/private/capabilitiesmanager.php b/lib/private/capabilitiesmanager.php new file mode 100644 index 00000000000..74154f2c631 --- /dev/null +++ b/lib/private/capabilitiesmanager.php @@ -0,0 +1,64 @@ +<?php +/** + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @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 OC; + + +use OCP\Capabilities\ICapability; + +class CapabilitiesManager { + + /** + * @var \Closure[] + */ + private $capabilities = array(); + + /** + * Get an array of al the capabilities that are registered at this manager + * + * @throws \InvalidArgumentException + * @return array + */ + public function getCapabilities() { + $capabilities = []; + foreach($this->capabilities as $capability) { + $c = $capability(); + if ($c instanceof ICapability) { + $capabilities = array_replace_recursive($capabilities, $c->getCapabilities()); + } else { + throw new \InvalidArgumentException('The given Capability (' . get_class($c) . ') does not implement the ICapability interface'); + } + } + + return $capabilities; + } + + /** + * In order to improve lazy loading a closure can be registered which will be called in case + * capabilities are actually requested + * + * $callable has to return an instance of OCP\Capabilities\ICapability + * + * @param \Closure $callable + */ + public function registerCapability(\Closure $callable) { + array_push($this->capabilities, $callable); + } +} diff --git a/lib/private/connector/sabre/exceptionloggerplugin.php b/lib/private/connector/sabre/exceptionloggerplugin.php index 0b89ae4aef6..53a1f738ea6 100644 --- a/lib/private/connector/sabre/exceptionloggerplugin.php +++ b/lib/private/connector/sabre/exceptionloggerplugin.php @@ -28,7 +28,7 @@ use Sabre\DAV\Exception; use Sabre\HTTP\Response; class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin { - private $nonFatalExceptions = array( + protected $nonFatalExceptions = array( 'Sabre\DAV\Exception\NotAuthenticated' => true, // the sync client uses this to find out whether files exist, // so it is not always an error, log it as debug @@ -95,6 +95,7 @@ class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin { $exception = [ 'Message' => $message, + 'Exception' => $exceptionClass, 'Code' => $ex->getCode(), 'Trace' => $ex->getTraceAsString(), 'File' => $ex->getFile(), diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php index 18bd3b8d91d..b7d0c547f24 100644 --- a/lib/private/connector/sabre/file.php +++ b/lib/private/connector/sabre/file.php @@ -208,10 +208,9 @@ class File extends Node implements IFile { } // since we skipped the view we need to scan and emit the hooks ourselves - $partStorage->getScanner()->scanFile($internalPath); + $this->fileView->getUpdater()->update($this->path); if ($view) { - $this->fileView->getUpdater()->propagate($hookPath); if (!$exists) { \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array( \OC\Files\Filesystem::signal_param_path => $hookPath @@ -333,7 +332,7 @@ class File extends Node implements IFile { $info = \OC_FileChunking::decodeName($name); if (empty($info)) { - throw new NotImplemented(); + throw new NotImplemented('Invalid chunk name'); } $chunk_handler = new \OC_FileChunking($info); $bytesWritten = $chunk_handler->store($info['index'], $data); diff --git a/lib/private/connector/sabre/objecttree.php b/lib/private/connector/sabre/objecttree.php index 1e9b9ba59e2..18d3c1dcf23 100644 --- a/lib/private/connector/sabre/objecttree.php +++ b/lib/private/connector/sabre/objecttree.php @@ -41,7 +41,7 @@ class ObjectTree extends \Sabre\DAV\Tree { protected $fileView; /** - * @var \OC\Files\Mount\Manager + * @var \OCP\Files\Mount\IMountManager */ protected $mountManager; @@ -54,9 +54,9 @@ class ObjectTree extends \Sabre\DAV\Tree { /** * @param \Sabre\DAV\INode $rootNode * @param \OC\Files\View $view - * @param \OC\Files\Mount\Manager $mountManager + * @param \OCP\Files\Mount\IMountManager $mountManager */ - public function init(\Sabre\DAV\INode $rootNode, \OC\Files\View $view, \OC\Files\Mount\Manager $mountManager) { + public function init(\Sabre\DAV\INode $rootNode, \OC\Files\View $view, \OCP\Files\Mount\IMountManager $mountManager) { $this->rootNode = $rootNode; $this->fileView = $view; $this->mountManager = $mountManager; diff --git a/lib/private/connector/sabre/serverfactory.php b/lib/private/connector/sabre/serverfactory.php new file mode 100644 index 00000000000..525ff0104cd --- /dev/null +++ b/lib/private/connector/sabre/serverfactory.php @@ -0,0 +1,107 @@ +<?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 OC\Connector\Sabre; + +use OCP\Files\Mount\IMountManager; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\ILogger; +use OCP\ITagManager; +use OCP\IUserSession; +use Sabre\DAV\Auth\Backend\BackendInterface; + +class ServerFactory { + public function __construct( + IConfig $config, + ILogger $logger, + IDBConnection $databaseConnection, + IUserSession $userSession, + IMountManager $mountManager, + ITagManager $tagManager + ) { + $this->config = $config; + $this->logger = $logger; + $this->databaseConnection = $databaseConnection; + $this->userSession = $userSession; + $this->mountManager = $mountManager; + $this->tagManager = $tagManager; + } + + /** + * @param string $baseUri + * @param string $requestUri + * @param BackendInterface $authBackend + * @param callable $viewCallBack callback that should return the view for the dav endpoint + * @return Server + */ + public function createServer($baseUri, $requestUri, BackendInterface $authBackend, callable $viewCallBack) { + // Fire up server + $objectTree = new \OC\Connector\Sabre\ObjectTree(); + $server = new \OC\Connector\Sabre\Server($objectTree); + // Set URL explicitly due to reverse-proxy situations + $server->httpRequest->setUrl($requestUri); + $server->setBaseUri($baseUri); + + // Load plugins + $defaults = new \OC_Defaults(); + $server->addPlugin(new \OC\Connector\Sabre\BlockLegacyClientPlugin($this->config)); + $server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $defaults->getName())); + // FIXME: The following line is a workaround for legacy components relying on being able to send a GET to / + $server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin()); + $server->addPlugin(new \OC\Connector\Sabre\FilesPlugin($objectTree)); + $server->addPlugin(new \OC\Connector\Sabre\MaintenancePlugin($this->config)); + $server->addPlugin(new \OC\Connector\Sabre\ExceptionLoggerPlugin('webdav', $this->logger)); + + // wait with registering these until auth is handled and the filesystem is setup + $server->on('beforeMethod', function () use ($server, $objectTree, $viewCallBack) { + /** @var \OC\Files\View $view */ + $view = $viewCallBack(); + $rootInfo = $view->getFileInfo(''); + + // Create ownCloud Dir + if ($rootInfo->getType() === 'dir') { + $root = new \OC\Connector\Sabre\Directory($view, $rootInfo); + } else { + $root = new \OC\Connector\Sabre\File($view, $rootInfo); + } + $objectTree->init($root, $view, $this->mountManager); + + $server->addPlugin(new \OC\Connector\Sabre\QuotaPlugin($view)); + + if($this->userSession->isLoggedIn()) { + $server->addPlugin(new \OC\Connector\Sabre\TagsPlugin($objectTree, $this->tagManager)); + // custom properties plugin must be the last one + $server->addPlugin( + new \Sabre\DAV\PropertyStorage\Plugin( + new \OC\Connector\Sabre\CustomPropertiesBackend( + $objectTree, + $this->databaseConnection, + $this->userSession->getUser() + ) + ) + ); + } + $server->addPlugin(new \OC\Connector\Sabre\CopyEtagHeaderPlugin()); + }, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request + return $server; + } +} diff --git a/lib/private/db/querybuilder/querybuilder.php b/lib/private/db/querybuilder/querybuilder.php index 1a1408876f1..1d97faf77cc 100644 --- a/lib/private/db/querybuilder/querybuilder.php +++ b/lib/private/db/querybuilder/querybuilder.php @@ -37,6 +37,9 @@ class QueryBuilder implements IQueryBuilder { /** @var QuoteHelper */ private $helper; + /** @var bool */ + private $automaticTablePrefix = true; + /** * Initializes a new QueryBuilder. * @@ -49,6 +52,17 @@ class QueryBuilder implements IQueryBuilder { } /** + * Enable/disable automatic prefixing of table names with the oc_ prefix + * + * @param bool $enabled If set to true table names will be prefixed with the + * owncloud database prefix automatically. + * @since 8.2.0 + */ + public function automaticTablePrefix($enabled) { + $this->automaticTablePrefix = (bool) $enabled; + } + + /** * Gets an ExpressionBuilder used for object-oriented construction of query expressions. * This producer method is intended for convenient inline usage. Example: * @@ -329,7 +343,7 @@ class QueryBuilder implements IQueryBuilder { */ public function delete($delete = null, $alias = null) { $this->queryBuilder->delete( - $this->helper->quoteColumnName($delete), + $this->getTableName($delete), $alias ); @@ -354,7 +368,7 @@ class QueryBuilder implements IQueryBuilder { */ public function update($update = null, $alias = null) { $this->queryBuilder->update( - $this->helper->quoteColumnName($update), + $this->getTableName($update), $alias ); @@ -382,7 +396,7 @@ class QueryBuilder implements IQueryBuilder { */ public function insert($insert = null) { $this->queryBuilder->insert( - $this->helper->quoteColumnName($insert) + $this->getTableName($insert) ); return $this; @@ -405,7 +419,7 @@ class QueryBuilder implements IQueryBuilder { */ public function from($from, $alias = null) { $this->queryBuilder->from( - $this->helper->quoteColumnName($from), + $this->getTableName($from), $alias ); @@ -432,7 +446,7 @@ class QueryBuilder implements IQueryBuilder { public function join($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->join( $fromAlias, - $this->helper->quoteColumnName($join), + $this->getTableName($join), $alias, $condition ); @@ -460,7 +474,7 @@ class QueryBuilder implements IQueryBuilder { public function innerJoin($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->innerJoin( $fromAlias, - $this->helper->quoteColumnName($join), + $this->getTableName($join), $alias, $condition ); @@ -488,7 +502,7 @@ class QueryBuilder implements IQueryBuilder { public function leftJoin($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->leftJoin( $fromAlias, - $this->helper->quoteColumnName($join), + $this->getTableName($join), $alias, $condition ); @@ -516,7 +530,7 @@ class QueryBuilder implements IQueryBuilder { public function rightJoin($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->rightJoin( $fromAlias, - $this->helper->quoteColumnName($join), + $this->getTableName($join), $alias, $condition ); @@ -984,4 +998,16 @@ class QueryBuilder implements IQueryBuilder { public function createFunction($call) { return new QueryFunction($call); } + + /** + * @param string $table + * @return string + */ + private function getTableName($table) { + if ($this->automaticTablePrefix === false || strpos($table, '*PREFIX*') === 0) { + return $this->helper->quoteColumnName($table); + } + + return $this->helper->quoteColumnName('*PREFIX*' . $table); + } } diff --git a/lib/private/files/cache/storage.php b/lib/private/files/cache/storage.php index ebef245f399..338d8308281 100644 --- a/lib/private/files/cache/storage.php +++ b/lib/private/files/cache/storage.php @@ -43,9 +43,10 @@ class Storage { /** * @param \OC\Files\Storage\Storage|string $storage + * @param bool $isAvailable * @throws \RuntimeException */ - public function __construct($storage) { + public function __construct($storage, $isAvailable = true) { if ($storage instanceof \OC\Files\Storage\Storage) { $this->storageId = $storage->getId(); } else { @@ -53,17 +54,14 @@ class Storage { } $this->storageId = self::adjustStorageId($this->storageId); - $sql = 'SELECT `numeric_id` FROM `*PREFIX*storages` WHERE `id` = ?'; - $result = \OC_DB::executeAudited($sql, array($this->storageId)); - if ($row = $result->fetchRow()) { + if ($row = self::getStorageById($this->storageId)) { $this->numericId = $row['numeric_id']; } else { $connection = \OC_DB::getConnection(); - if ($connection->insertIfNotExist('*PREFIX*storages', ['id' => $this->storageId])) { + if ($connection->insertIfNotExist('*PREFIX*storages', ['id' => $this->storageId, 'available' => $isAvailable])) { $this->numericId = \OC_DB::insertid('*PREFIX*storages'); } else { - $result = \OC_DB::executeAudited($sql, array($this->storageId)); - if ($row = $result->fetchRow()) { + if ($row = self::getStorageById($this->storageId)) { $this->numericId = $row['numeric_id']; } else { throw new \RuntimeException('Storage could neither be inserted nor be selected from the database'); @@ -73,6 +71,16 @@ class Storage { } /** + * @param string $storageId + * @return array|null + */ + public static function getStorageById($storageId) { + $sql = 'SELECT * FROM `*PREFIX*storages` WHERE `id` = ?'; + $result = \OC_DB::executeAudited($sql, array($storageId)); + return $result->fetchRow(); + } + + /** * Adjusts the storage id to use md5 if too long * @param string $storageId storage id * @return string unchanged $storageId if its length is less than 64 characters, @@ -120,9 +128,7 @@ class Storage { public static function getNumericStorageId($storageId) { $storageId = self::adjustStorageId($storageId); - $sql = 'SELECT `numeric_id` FROM `*PREFIX*storages` WHERE `id` = ?'; - $result = \OC_DB::executeAudited($sql, array($storageId)); - if ($row = $result->fetchRow()) { + if ($row = self::getStorageById($storageId)) { return $row['numeric_id']; } else { return null; @@ -130,6 +136,28 @@ class Storage { } /** + * @return array|null [ available, last_checked ] + */ + public function getAvailability() { + if ($row = self::getStorageById($this->storageId)) { + return [ + 'available' => $row['available'], + 'last_checked' => $row['last_checked'] + ]; + } else { + return null; + } + } + + /** + * @param bool $isAvailable + */ + public function setAvailability($isAvailable) { + $sql = 'UPDATE `*PREFIX*storages` SET `available` = ?, `last_checked` = ? WHERE `id` = ?'; + \OC_DB::executeAudited($sql, array($isAvailable, time(), $this->storageId)); + } + + /** * Check if a string storage id is known * * @param string $storageId diff --git a/lib/private/files/mount/mountpoint.php b/lib/private/files/mount/mountpoint.php index 2871bbd9083..5e4949aa9dd 100644 --- a/lib/private/files/mount/mountpoint.php +++ b/lib/private/files/mount/mountpoint.php @@ -29,6 +29,7 @@ namespace OC\Files\Mount; use \OC\Files\Filesystem; use OC\Files\Storage\StorageFactory; use OC\Files\Storage\Storage; +use OC\Files\Storage\Wrapper\Wrapper; use OCP\Files\Mount\IMountPoint; class MountPoint implements IMountPoint { @@ -92,7 +93,11 @@ class MountPoint implements IMountPoint { $this->mountPoint = $mountpoint; if ($storage instanceof Storage) { $this->class = get_class($storage); - $this->storage = $this->loader->wrap($this, $storage); + $this->storage = $storage; + // only wrap if not already wrapped + if (!($this->storage instanceof Wrapper)) { + $this->storage = $this->loader->wrap($this, $this->storage); + } } else { // Update old classes to new namespace if (strpos($storage, 'OC_Filestorage_') !== false) { diff --git a/lib/private/files/objectstore/objectstorestorage.php b/lib/private/files/objectstore/objectstorestorage.php index 24398649727..a85553186ae 100644 --- a/lib/private/files/objectstore/objectstorestorage.php +++ b/lib/private/files/objectstore/objectstorestorage.php @@ -24,6 +24,7 @@ namespace OC\Files\ObjectStore; +use Icewind\Streams\IteratorDirectory; use OCP\Files\ObjectStore\IObjectStore; class ObjectStoreStorage extends \OC\Files\Storage\Common { @@ -216,9 +217,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common { $files[] = $file['name']; } - \OC\Files\Stream\Dir::register('objectstore' . $path . '/', $files); - - return opendir('fakedir://objectstore' . $path . '/'); + return IteratorDirectory::wrap($files); } catch (\Exception $e) { \OCP\Util::writeLog('objectstore', $e->getMessage(), \OCP\Util::ERROR); return false; diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index 78f35ad4a6f..a5ed5fd3996 100644 --- a/lib/private/files/storage/common.php +++ b/lib/private/files/storage/common.php @@ -404,6 +404,11 @@ abstract class Common implements Storage { return implode('/', $output); } + /** + * Test a storage for availability + * + * @return bool + */ public function test() { if ($this->stat('')) { return true; @@ -650,4 +655,18 @@ abstract class Common implements Storage { public function changeLock($path, $type, ILockingProvider $provider) { $provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type); } + + /** + * @return array [ available, last_checked ] + */ + public function getAvailability() { + return $this->getStorageCache()->getAvailability(); + } + + /** + * @param bool $isAvailable + */ + public function setAvailability($isAvailable) { + $this->getStorageCache()->setAvailability($isAvailable); + } } diff --git a/lib/private/files/storage/dav.php b/lib/private/files/storage/dav.php index 24cf3c29209..6e89dcccbcd 100644 --- a/lib/private/files/storage/dav.php +++ b/lib/private/files/storage/dav.php @@ -38,7 +38,7 @@ namespace OC\Files\Storage; use Exception; use OC\Files\Filesystem; use OC\Files\Stream\Close; -use OC\Files\Stream\Dir; +use Icewind\Streams\IteratorDirectory; use OC\MemCache\ArrayCache; use OCP\Constants; use OCP\Files; @@ -211,8 +211,7 @@ class DAV extends Common { $file = basename($file); $content[] = $file; } - Dir::register($id, $content); - return opendir('fakedir://' . $id); + return IteratorDirectory::wrap($content); } catch (ClientHttpException $e) { if ($e->getHttpStatus() === 404) { $this->statCache->clear($path . '/'); diff --git a/lib/private/files/storage/local.php b/lib/private/files/storage/local.php index b7272b7d1f0..3676fe69131 100644 --- a/lib/private/files/storage/local.php +++ b/lib/private/files/storage/local.php @@ -35,7 +35,6 @@ */ namespace OC\Files\Storage; - /** * for local filestore, we only have to map the paths */ diff --git a/lib/private/files/storage/wrapper/availability.php b/lib/private/files/storage/wrapper/availability.php new file mode 100644 index 00000000000..37319a8f7d1 --- /dev/null +++ b/lib/private/files/storage/wrapper/availability.php @@ -0,0 +1,462 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@karoshi.org.uk> + * + * @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 OC\Files\Storage\Wrapper; + +/** + * Availability checker for storages + * + * Throws a StorageNotAvailableException for storages with known failures + */ +class Availability extends Wrapper { + const RECHECK_TTL_SEC = 600; // 10 minutes + + /** + * @return bool + */ + private function updateAvailability() { + try { + $result = $this->test(); + } catch (\Exception $e) { + $result = false; + } + $this->setAvailability($result); + return $result; + } + + /** + * @return bool + */ + private function isAvailable() { + $availability = $this->getAvailability(); + if (!$availability['available']) { + // trigger a recheck if TTL reached + if ((time() - $availability['last_checked']) > self::RECHECK_TTL_SEC) { + return $this->updateAvailability(); + } + } + return $availability['available']; + } + + /** + * @throws \OCP\Files\StorageNotAvailableException + */ + private function checkAvailability() { + if (!$this->isAvailable()) { + throw new \OCP\Files\StorageNotAvailableException(); + } + } + + /** {@inheritdoc} */ + public function mkdir($path) { + $this->checkAvailability(); + try { + return parent::mkdir($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function rmdir($path) { + $this->checkAvailability(); + try { + return parent::rmdir($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function opendir($path) { + $this->checkAvailability(); + try { + return parent::opendir($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function is_dir($path) { + $this->checkAvailability(); + try { + return parent::is_dir($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function is_file($path) { + $this->checkAvailability(); + try { + return parent::is_file($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function stat($path) { + $this->checkAvailability(); + try { + return parent::stat($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function filetype($path) { + $this->checkAvailability(); + try { + return parent::filetype($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function filesize($path) { + $this->checkAvailability(); + try { + return parent::filesize($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function isCreatable($path) { + $this->checkAvailability(); + try { + return parent::isCreatable($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function isReadable($path) { + $this->checkAvailability(); + try { + return parent::isReadable($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function isUpdatable($path) { + $this->checkAvailability(); + try { + return parent::isUpdatable($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function isDeletable($path) { + $this->checkAvailability(); + try { + return parent::isDeletable($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function isSharable($path) { + $this->checkAvailability(); + try { + return parent::isSharable($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function getPermissions($path) { + $this->checkAvailability(); + try { + return parent::getPermissions($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function file_exists($path) { + $this->checkAvailability(); + try { + return parent::file_exists($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function filemtime($path) { + $this->checkAvailability(); + try { + return parent::filemtime($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function file_get_contents($path) { + $this->checkAvailability(); + try { + return parent::file_get_contents($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function file_put_contents($path, $data) { + $this->checkAvailability(); + try { + return parent::file_put_contents($path, $data); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function unlink($path) { + $this->checkAvailability(); + try { + return parent::unlink($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function rename($path1, $path2) { + $this->checkAvailability(); + try { + return parent::rename($path1, $path2); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function copy($path1, $path2) { + $this->checkAvailability(); + try { + return parent::copy($path1, $path2); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function fopen($path, $mode) { + $this->checkAvailability(); + try { + return parent::fopen($path, $mode); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function getMimeType($path) { + $this->checkAvailability(); + try { + return parent::getMimeType($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function hash($type, $path, $raw = false) { + $this->checkAvailability(); + try { + return parent::hash($type, $path, $raw); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function free_space($path) { + $this->checkAvailability(); + try { + return parent::free_space($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function search($query) { + $this->checkAvailability(); + try { + return parent::search($query); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function touch($path, $mtime = null) { + $this->checkAvailability(); + try { + return parent::touch($path, $mtime); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function getLocalFile($path) { + $this->checkAvailability(); + try { + return parent::getLocalFile($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function getLocalFolder($path) { + $this->checkAvailability(); + try { + return parent::getLocalFolder($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function hasUpdated($path, $time) { + $this->checkAvailability(); + try { + return parent::hasUpdated($path, $time); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function getOwner($path) { + $this->checkAvailability(); + try { + return parent::getOwner($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function getETag($path) { + $this->checkAvailability(); + try { + return parent::getETag($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function getDirectDownload($path) { + $this->checkAvailability(); + try { + return parent::getDirectDownload($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + $this->checkAvailability(); + try { + return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + $this->checkAvailability(); + try { + return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } + + /** {@inheritdoc} */ + public function getMetaData($path) { + $this->checkAvailability(); + try { + return parent::getMetaData($path); + } catch (\OCP\Files\StorageNotAvailableException $e) { + $this->setAvailability(false); + throw $e; + } + } +} diff --git a/lib/private/files/storage/wrapper/encryption.php b/lib/private/files/storage/wrapper/encryption.php index 51aa13065c1..4ba9b21ddb4 100644 --- a/lib/private/files/storage/wrapper/encryption.php +++ b/lib/private/files/storage/wrapper/encryption.php @@ -127,12 +127,11 @@ class Encryption extends Wrapper { $info = $this->getCache()->get($path); if (isset($this->unencryptedSize[$fullPath])) { $size = $this->unencryptedSize[$fullPath]; + // update file cache + $info['encrypted'] = true; + $info['size'] = $size; + $this->getCache()->put($path, $info); - if (isset($info['fileid'])) { - $info['encrypted'] = true; - $info['size'] = $size; - $this->getCache()->put($path, $info); - } return $size; } diff --git a/lib/private/files/storage/wrapper/wrapper.php b/lib/private/files/storage/wrapper/wrapper.php index d1414880beb..b43dd4fe142 100644 --- a/lib/private/files/storage/wrapper/wrapper.php +++ b/lib/private/files/storage/wrapper/wrapper.php @@ -498,6 +498,24 @@ class Wrapper implements \OC\Files\Storage\Storage { } /** + * Get availability of the storage + * + * @return array [ available, last_checked ] + */ + public function getAvailability() { + return $this->storage->getAvailability(); + } + + /** + * Set availability of the storage + * + * @param bool $isAvailable + */ + public function setAvailability($isAvailable) { + $this->storage->setAvailability($isAvailable); + } + + /** * @param string $path the path of the target folder * @param string $fileName the name of the file itself * @return void diff --git a/lib/private/files/view.php b/lib/private/files/view.php index cb3c05d2bca..9afa9d40b20 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -642,10 +642,10 @@ class View { } $run = true; - if ($this->shouldEmitHooks() && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) { + if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) { // if it was a rename from a part file to a regular file it was a write and not a rename operation $this->emit_file_hooks_pre($exists, $path2, $run); - } elseif ($this->shouldEmitHooks()) { + } elseif ($this->shouldEmitHooks($path1)) { \OC_Hook::emit( Filesystem::CLASSNAME, Filesystem::signal_rename, array( @@ -1087,6 +1087,11 @@ class View { return true; } $fullPath = $this->getAbsolutePath($path); + + if ($fullPath === $defaultRoot) { + return true; + } + return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/'); } @@ -1354,7 +1359,7 @@ class View { // if sharing was disabled for the user we remove the share permissions if (\OCP\Util::isSharingDisabledForUser()) { - $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; + $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; } $files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount); @@ -1656,11 +1661,11 @@ class View { } // verify database - e.g. mysql only 3-byte chars - if (preg_match('%^(?: + if (preg_match('%(?: \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 -)*$%xs', $fileName)) { +)%xs', $fileName)) { throw new InvalidPathException($l10n->t('4-byte characters are not supported in file names')); } diff --git a/lib/private/installer.php b/lib/private/installer.php index 37af8d0edcb..392dc1c0817 100644 --- a/lib/private/installer.php +++ b/lib/private/installer.php @@ -107,6 +107,10 @@ class OC_Installer{ } $extractDir .= '/' . $info['id']; + if(!file_exists($extractDir)) { + OC_Helper::rmdirr($basedir); + throw new \Exception($l->t("Archive does not contain a directory named %s", $info['id'])); + } OC_Helper::copyr($extractDir, $basedir); //remove temporary files diff --git a/lib/private/ocs.php b/lib/private/ocs.php index 6d166f8adb0..bb1aabf8f18 100644 --- a/lib/private/ocs.php +++ b/lib/private/ocs.php @@ -28,6 +28,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ +use OCP\API; /** * Class to handle open collaboration services API requests @@ -64,8 +65,7 @@ class OC_OCS { } } if ($data === false) { - echo self::generateXml('', 'fail', 400, 'Bad request. Please provide a valid '.$key); - exit(); + throw new \OC\OCS\Exception(new OC_OCS_Result(null, 400, 'Bad request. Please provide a valid '.$key)); } else { // NOTE: Is the raw type necessary? It might be a little risky without sanitization if ($type == 'raw') return $data; @@ -78,23 +78,12 @@ class OC_OCS { } public static function notFound() { - if($_SERVER['REQUEST_METHOD'] == 'GET') { - $method='get'; - }elseif($_SERVER['REQUEST_METHOD'] == 'PUT') { - $method='put'; - }elseif($_SERVER['REQUEST_METHOD'] == 'POST') { - $method='post'; - }else{ - echo('internal server error: method not supported'); - exit(); - } - - $format = self::readData($method, 'format', 'text', ''); + $format = OC_API::requestedFormat(); $txt='Invalid query, please check the syntax. API specifications are here:' .' http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n"; $txt.=OC_OCS::getDebugOutput(); - echo(OC_OCS::generateXml($format, 'failed', 999, $txt)); + OC_API::respond(new OC_OCS_Result(null, API::RESPOND_UNKNOWN_ERROR, $txt), $format); } /** @@ -110,130 +99,4 @@ class OC_OCS { if(isset($_POST)) foreach($_POST as $key=>$value) $txt.='post parameter: '.$key.'->'.$value."\n"; return($txt); } - - - /** - * generates the xml or json response for the API call from an multidimenional data array. - * @param string $format - * @param string $status - * @param string $statuscode - * @param string $message - * @param array $data - * @param string $tag - * @param string $tagattribute - * @param int $dimension - * @param int|string $itemscount - * @param int|string $itemsperpage - * @return string xml/json - */ - public static function generateXml($format, $status, $statuscode, - $message, $data=array(), $tag='', $tagattribute='', $dimension=-1, $itemscount='', $itemsperpage='') { - if($format=='json') { - $json=array(); - $json['status']=$status; - $json['statuscode']=$statuscode; - $json['message']=$message; - $json['totalitems']=$itemscount; - $json['itemsperpage']=$itemsperpage; - $json['data']=$data; - return(json_encode($json)); - }else{ - $txt=''; - $writer = xmlwriter_open_memory(); - xmlwriter_set_indent( $writer, 2 ); - xmlwriter_start_document($writer ); - xmlwriter_start_element($writer, 'ocs'); - xmlwriter_start_element($writer, 'meta'); - xmlwriter_write_element($writer, 'status', $status); - xmlwriter_write_element($writer, 'statuscode', $statuscode); - xmlwriter_write_element($writer, 'message', $message); - if($itemscount<>'') xmlwriter_write_element($writer, 'totalitems', $itemscount); - if(!empty($itemsperpage)) xmlwriter_write_element($writer, 'itemsperpage', $itemsperpage); - xmlwriter_end_element($writer); - if($dimension=='0') { - // 0 dimensions - xmlwriter_write_element($writer, 'data', $data); - - }elseif($dimension=='1') { - xmlwriter_start_element($writer, 'data'); - foreach($data as $key=>$entry) { - xmlwriter_write_element($writer, $key, $entry); - } - xmlwriter_end_element($writer); - - }elseif($dimension=='2') { - xmlwriter_start_element($writer, 'data'); - foreach($data as $entry) { - xmlwriter_start_element($writer, $tag); - if(!empty($tagattribute)) { - xmlwriter_write_attribute($writer, 'details', $tagattribute); - } - foreach($entry as $key=>$value) { - if(is_array($value)) { - foreach($value as $k=>$v) { - xmlwriter_write_element($writer, $k, $v); - } - } else { - xmlwriter_write_element($writer, $key, $value); - } - } - xmlwriter_end_element($writer); - } - xmlwriter_end_element($writer); - - }elseif($dimension=='3') { - xmlwriter_start_element($writer, 'data'); - foreach($data as $entrykey=>$entry) { - xmlwriter_start_element($writer, $tag); - if(!empty($tagattribute)) { - xmlwriter_write_attribute($writer, 'details', $tagattribute); - } - foreach($entry as $key=>$value) { - if(is_array($value)) { - xmlwriter_start_element($writer, $entrykey); - foreach($value as $k=>$v) { - xmlwriter_write_element($writer, $k, $v); - } - xmlwriter_end_element($writer); - } else { - xmlwriter_write_element($writer, $key, $value); - } - } - xmlwriter_end_element($writer); - } - xmlwriter_end_element($writer); - }elseif($dimension=='dynamic') { - xmlwriter_start_element($writer, 'data'); - OC_OCS::toxml($writer, $data, 'comment'); - xmlwriter_end_element($writer); - } - - xmlwriter_end_element($writer); - - xmlwriter_end_document( $writer ); - $txt.=xmlwriter_output_memory( $writer ); - unset($writer); - return($txt); - } - } - - /** - * @param resource $writer - * @param array $data - * @param string $node - */ - public static function toXml($writer, $data, $node) { - foreach($data as $key => $value) { - if (is_numeric($key)) { - $key = $node; - } - if (is_array($value)) { - xmlwriter_start_element($writer, $key); - OC_OCS::toxml($writer, $value, $node); - xmlwriter_end_element($writer); - }else{ - xmlwriter_write_element($writer, $key, $value); - } - } - } } diff --git a/lib/private/ocs/cloud.php b/lib/private/ocs/cloud.php index f662bde2858..8f4f1769e9c 100644 --- a/lib/private/ocs/cloud.php +++ b/lib/private/ocs/cloud.php @@ -3,6 +3,7 @@ * @author Bart Visscher <bartv@thisnet.nl> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <rmccorkell@karoshi.org.uk> + * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Tom Needham <tom@owncloud.com> * @@ -36,12 +37,8 @@ class OC_OCS_Cloud { 'edition' => OC_Util::getEditionString(), ); - $result['capabilities'] = array( - 'core' => array( - 'pollinterval' => OC_Config::getValue('pollinterval', 60), - ), - ); - + $result['capabilities'] = \OC::$server->getCapabilitiesManager()->getCapabilities(); + return new OC_OCS_Result($result); } diff --git a/lib/private/ocs/corecapabilities.php b/lib/private/ocs/corecapabilities.php new file mode 100644 index 00000000000..6b620ab0a84 --- /dev/null +++ b/lib/private/ocs/corecapabilities.php @@ -0,0 +1,56 @@ +<?php +/** + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @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 OC\OCS; + +use OCP\Capabilities\ICapability; +use OCP\IConfig; + +/** + * Class Capabilities + * + * @package OC\OCS + */ +class CoreCapabilities implements ICapability { + + /** @var IConfig */ + private $config; + + /** + * @param IConfig $config + */ + public function __construct(IConfig $config) { + $this->config = $config; + } + + /** + * Return this classes capabilities + * + * @return array + */ + public function getCapabilities() { + return [ + 'core' => [ + 'pollinterval' => $this->config->getSystemValue('pollinterval', 60) + ] + ]; + } +} diff --git a/lib/private/ocs/exception.php b/lib/private/ocs/exception.php new file mode 100644 index 00000000000..93bee773771 --- /dev/null +++ b/lib/private/ocs/exception.php @@ -0,0 +1,34 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @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 OC\OCS; + +class Exception extends \Exception { + + public function __construct(\OC_OCS_Result $result) { + $this->result = $result; + } + + public function getResult() { + return $this->result; + } + +} diff --git a/lib/private/ocs/result.php b/lib/private/ocs/result.php index 1ee2982ac4a..c4b0fbf33f3 100644 --- a/lib/private/ocs/result.php +++ b/lib/private/ocs/result.php @@ -27,7 +27,23 @@ class OC_OCS_Result{ - protected $data, $message, $statusCode, $items, $perPage; + /** @var array */ + protected $data; + + /** @var null|string */ + protected $message; + + /** @var int */ + protected $statusCode; + + /** @var integer */ + protected $items; + + /** @var integer */ + protected $perPage; + + /** @var array */ + private $headers = []; /** * create the OCS_Result object @@ -106,5 +122,32 @@ class OC_OCS_Result{ return ($this->statusCode == 100); } + /** + * Adds a new header to the response + * @param string $name The name of the HTTP header + * @param string $value The value, null will delete it + * @return $this + */ + public function addHeader($name, $value) { + $name = trim($name); // always remove leading and trailing whitespace + // to be able to reliably check for security + // headers + + if(is_null($value)) { + unset($this->headers[$name]); + } else { + $this->headers[$name] = $value; + } + + return $this; + } + + /** + * Returns the set headers + * @return array the headers + */ + public function getHeaders() { + return $this->headers; + } } diff --git a/lib/private/security/crypto.php b/lib/private/security/crypto.php index bca0f08090d..9bae1d6992c 100644 --- a/lib/private/security/crypto.php +++ b/lib/private/security/crypto.php @@ -23,11 +23,10 @@ namespace OC\Security; -use Crypt_AES; -use Crypt_Hash; +use phpseclib\Crypt\AES; +use phpseclib\Crypt\Hash; use OCP\Security\ICrypto; use OCP\Security\ISecureRandom; -use OCP\Security\StringUtils; use OCP\IConfig; /** @@ -41,7 +40,7 @@ use OCP\IConfig; * @package OC\Security */ class Crypto implements ICrypto { - /** @var Crypt_AES $cipher */ + /** @var AES $cipher */ private $cipher; /** @var int */ private $ivLength = 16; @@ -50,8 +49,12 @@ class Crypto implements ICrypto { /** @var ISecureRandom */ private $random; + /** + * @param IConfig $config + * @param ISecureRandom $random + */ function __construct(IConfig $config, ISecureRandom $random) { - $this->cipher = new Crypt_AES(); + $this->cipher = new AES(); $this->config = $config; $this->random = $random; } @@ -69,7 +72,7 @@ class Crypto implements ICrypto { // Append an "a" behind the password and hash it to prevent reusing the same password as for encryption $password = hash('sha512', $password . 'a'); - $hash = new Crypt_Hash('sha512'); + $hash = new Hash('sha512'); $hash->setKey($password); return $hash->hash($message); } @@ -119,7 +122,7 @@ class Crypto implements ICrypto { $this->cipher->setIV($iv); - if(!StringUtils::equals($this->calculateHMAC($parts[0].$parts[1], $password), $hmac)) { + if(!\OCP\Security\StringUtils::equals($this->calculateHMAC($parts[0].$parts[1], $password), $hmac)) { throw new \Exception('HMAC does not match.'); } diff --git a/lib/private/server.php b/lib/private/server.php index 12981fe7f19..618431ff2d4 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -75,6 +75,7 @@ class Server extends SimpleContainer implements IServerContainer { * @param string $webRoot */ public function __construct($webRoot) { + parent::__construct(); $this->webRoot = $webRoot; $this->registerService('ContactsManager', function ($c) { @@ -449,6 +450,14 @@ class Server extends SimpleContainer implements IServerContainer { $c->getURLGenerator(), \OC::$configDir); }); + $this->registerService('CapabilitiesManager', function (Server $c) { + $manager = new \OC\CapabilitiesManager(); + $manager->registerCapability(function() use ($c) { + return new \OC\OCS\CoreCapabilities($c->getConfig()); + }); + return $manager; + }); + } /** @@ -945,4 +954,13 @@ class Server extends SimpleContainer implements IServerContainer { public function getMimeTypeDetector() { return $this->query('MimeTypeDetector'); } + + /** + * Get the manager of all the capabilities + * + * @return \OC\CapabilitiesManager + */ + public function getCapabilitiesManager() { + return $this->query('CapabilitiesManager'); + } } diff --git a/lib/private/setup.php b/lib/private/setup.php index 870480feaa0..8f1ae389e45 100644 --- a/lib/private/setup.php +++ b/lib/private/setup.php @@ -39,6 +39,8 @@ use bantu\IniGetWrapper\IniGetWrapper; use Exception; use OCP\IConfig; use OCP\IL10N; +use OCP\ILogger; +use OCP\Security\ISecureRandom; class Setup { /** @var \OCP\IConfig */ @@ -49,6 +51,10 @@ class Setup { protected $l10n; /** @var \OC_Defaults */ protected $defaults; + /** @var ILogger */ + protected $logger; + /** @var ISecureRandom */ + protected $random; /** * @param IConfig $config @@ -58,11 +64,16 @@ class Setup { function __construct(IConfig $config, IniGetWrapper $iniWrapper, IL10N $l10n, - \OC_Defaults $defaults) { + \OC_Defaults $defaults, + ILogger $logger, + ISecureRandom $random + ) { $this->config = $config; $this->iniWrapper = $iniWrapper; $this->l10n = $l10n; $this->defaults = $defaults; + $this->logger = $logger; + $this->random = $random; } static $dbSetupClasses = array( @@ -78,7 +89,7 @@ class Setup { * @param string $name * @return bool */ - public function class_exists($name) { + protected function class_exists($name) { return class_exists($name); } @@ -87,11 +98,20 @@ class Setup { * @param string $name * @return bool */ - public function is_callable($name) { + protected function is_callable($name) { return is_callable($name); } /** + * Wrapper around \PDO::getAvailableDrivers + * + * @return array + */ + protected function getAvailableDbDriversForPdo() { + return \PDO::getAvailableDrivers(); + } + + /** * Get the available and supported databases of this instance * * @param bool $allowAllDatabases @@ -106,8 +126,8 @@ class Setup { 'name' => 'SQLite' ), 'mysql' => array( - 'type' => 'function', - 'call' => 'mysql_connect', + 'type' => 'pdo', + 'call' => 'mysql', 'name' => 'MySQL/MariaDB' ), 'pgsql' => array( @@ -136,10 +156,15 @@ class Setup { foreach($configuredDatabases as $database) { if(array_key_exists($database, $availableDatabases)) { $working = false; - if($availableDatabases[$database]['type'] === 'class') { - $working = $this->class_exists($availableDatabases[$database]['call']); - } elseif ($availableDatabases[$database]['type'] === 'function') { - $working = $this->is_callable($availableDatabases[$database]['call']); + $type = $availableDatabases[$database]['type']; + $call = $availableDatabases[$database]['call']; + + if($type === 'class') { + $working = $this->class_exists($call); + } elseif ($type === 'function') { + $working = $this->is_callable($call); + } elseif($type === 'pdo') { + $working = in_array($call, $this->getAvailableDbDriversForPdo(), TRUE); } if($working) { $supportedDatabases[$database] = $availableDatabases[$database]['name']; @@ -249,7 +274,8 @@ class Setup { $class = self::$dbSetupClasses[$dbType]; /** @var \OC\Setup\AbstractDatabase $dbSetup */ - $dbSetup = new $class($l, 'db_structure.xml'); + $dbSetup = new $class($l, 'db_structure.xml', $this->config, + $this->logger, $this->random); $error = array_merge($error, $dbSetup->validate($options)); // validate the data directory @@ -284,9 +310,9 @@ class Setup { } //generate a random salt that is used to salt the local user passwords - $salt = \OC::$server->getSecureRandom()->getLowStrengthGenerator()->generate(30); + $salt = $this->random->getLowStrengthGenerator()->generate(30); // generate a secret - $secret = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(48); + $secret = $this->random->getMediumStrengthGenerator()->generate(48); //write the config file $this->config->setSystemValues([ @@ -351,7 +377,7 @@ class Setup { //try to write logtimezone if (date_default_timezone_get()) { - \OC_Config::setValue('logtimezone', date_default_timezone_get()); + $config->setSystemValue('logtimezone', date_default_timezone_get()); } //and we are done @@ -389,7 +415,9 @@ class Setup { * @throws \OC\HintException If .htaccess does not include the current version */ public static function updateHtaccess() { - $setupHelper = new \OC\Setup(\OC::$server->getConfig(), \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'), new \OC_Defaults()); + $setupHelper = new \OC\Setup(\OC::$server->getConfig(), \OC::$server->getIniWrapper(), + \OC::$server->getL10N('lib'), new \OC_Defaults(), \OC::$server->getLogger(), + \OC::$server->getSecureRandom()); if(!$setupHelper->isCurrentHtaccess()) { throw new \OC\HintException('.htaccess file has the wrong version. Please upload the correct version. Maybe you forgot to replace it after updating?'); } diff --git a/lib/private/setup/abstractdatabase.php b/lib/private/setup/abstractdatabase.php index 13daf1782fc..1ec853c3b02 100644 --- a/lib/private/setup/abstractdatabase.php +++ b/lib/private/setup/abstractdatabase.php @@ -22,22 +22,39 @@ */ namespace OC\Setup; +use OCP\IConfig; +use OCP\ILogger; +use OCP\Security\ISecureRandom; + abstract class AbstractDatabase { - /** - * @var \OC_L10N - */ + /** @var \OC_L10N */ protected $trans; + /** @var string */ protected $dbDefinitionFile; - protected $dbuser; - protected $dbpassword; - protected $dbname; - protected $dbhost; - protected $tableprefix; + /** @var string */ + protected $dbUser; + /** @var string */ + protected $dbPassword; + /** @var string */ + protected $dbName; + /** @var string */ + protected $dbHost; + /** @var string */ + protected $tablePrefix; + /** @var IConfig */ + protected $config; + /** @var ILogger */ + protected $logger; + /** @var ISecureRandom */ + protected $random; - public function __construct($trans, $dbDefinitionFile) { + public function __construct($trans, $dbDefinitionFile, IConfig $config, ILogger $logger, ISecureRandom $random) { $this->trans = $trans; $this->dbDefinitionFile = $dbDefinitionFile; + $this->config = $config; + $this->logger = $logger; + $this->random = $random; } public function validate($config) { @@ -61,17 +78,17 @@ abstract class AbstractDatabase { $dbHost = !empty($config['dbhost']) ? $config['dbhost'] : 'localhost'; $dbTablePrefix = isset($config['dbtableprefix']) ? $config['dbtableprefix'] : 'oc_'; - \OC_Config::setValues([ + $this->config->setSystemValues([ 'dbname' => $dbName, 'dbhost' => $dbHost, 'dbtableprefix' => $dbTablePrefix, ]); - $this->dbuser = $dbUser; - $this->dbpassword = $dbPass; - $this->dbname = $dbName; - $this->dbhost = $dbHost; - $this->tableprefix = $dbTablePrefix; + $this->dbUser = $dbUser; + $this->dbPassword = $dbPass; + $this->dbName = $dbName; + $this->dbHost = $dbHost; + $this->tablePrefix = $dbTablePrefix; } abstract public function setupDatabase($userName); diff --git a/lib/private/setup/mysql.php b/lib/private/setup/mysql.php index 9cf102393b8..5597592f21e 100644 --- a/lib/private/setup/mysql.php +++ b/lib/private/setup/mysql.php @@ -23,110 +23,139 @@ */ namespace OC\Setup; +use OC\DB\ConnectionFactory; +use OCP\IDBConnection; + class MySQL extends AbstractDatabase { public $dbprettyname = 'MySQL/MariaDB'; public function setupDatabase($username) { //check if the database user has admin right - $connection = @mysql_connect($this->dbhost, $this->dbuser, $this->dbpassword); - if(!$connection) { - throw new \OC\DatabaseSetupException($this->trans->t('MySQL/MariaDB username and/or password not valid'), - $this->trans->t('You need to enter either an existing account or the administrator.')); - } - //user already specified in config - $oldUser=\OC_Config::getValue('dbuser', false); - - //we don't have a dbuser specified in config - if($this->dbuser!=$oldUser) { - //add prefix to the admin username to prevent collisions - $adminUser=substr('oc_'.$username, 0, 16); - - $i = 1; - while(true) { - //this should be enough to check for admin rights in mysql - $query="SELECT user FROM mysql.user WHERE user='$adminUser'"; - - $result = mysql_query($query, $connection); + $connection = $this->connect(); - //current dbuser has admin rights - if($result) { - //new dbuser does not exist - if(mysql_num_rows($result) === 0) { - //use the admin login data for the new database user - $this->dbuser=$adminUser; - - //create a random password so we don't need to store the admin password in the config file - $this->dbpassword=\OC_Util::generateRandomBytes(30); - - $this->createDBUser($connection); - - break; - } else { - //repeat with different username - $length=strlen((string)$i); - $adminUser=substr('oc_'.$username, 0, 16 - $length).$i; - $i++; - } - } else { - break; - } - }; - - \OC_Config::setValues([ - 'dbuser' => $this->dbuser, - 'dbpassword' => $this->dbpassword, - ]); - } + $this->createSpecificUser($username, $connection); //create the database $this->createDatabase($connection); //fill the database if needed - $query='select count(*) from information_schema.tables' - ." where table_schema='".$this->dbname."' AND table_name = '".$this->tableprefix."users';"; - $result = mysql_query($query, $connection); - if($result) { - $row=mysql_fetch_row($result); - } + $query='select count(*) from information_schema.tables where table_schema=? AND table_name = ?'; + $result = $connection->executeQuery($query, [$this->dbName, $this->tablePrefix.'users']); + $row = $result->fetch(); if(!$result or $row[0]==0) { \OC_DB::createDbFromStructure($this->dbDefinitionFile); } - mysql_close($connection); } + /** + * @param \OC\DB\Connection $connection + */ private function createDatabase($connection) { - $name = $this->dbname; - $user = $this->dbuser; - //we cant use OC_BD functions here because we need to connect as the administrative user. - $query = "CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET utf8 COLLATE utf8_bin;"; - $result = mysql_query($query, $connection); - if(!$result) { - $entry = $this->trans->t('DB Error: "%s"', array(mysql_error($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; - \OCP\Util::writeLog('setup.mysql', $entry, \OCP\Util::WARN); - } - $query="GRANT ALL PRIVILEGES ON `$name` . * TO '$user'"; + try{ + $name = $this->dbName; + $user = $this->dbUser; + //we cant use OC_BD functions here because we need to connect as the administrative user. + $query = "CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET utf8 COLLATE utf8_bin;"; + $connection->executeUpdate($query); - //this query will fail if there aren't the right permissions, ignore the error - mysql_query($query, $connection); + //this query will fail if there aren't the right permissions, ignore the error + $query="GRANT ALL PRIVILEGES ON `$name` . * TO '$user'"; + $connection->executeUpdate($query); + } catch (\Exception $ex) { + $this->logger->error('Database creation failed: {error}', [ + 'app' => 'mysql.setup', + 'error' => $ex->getMessage() + ]); + } } + /** + * @param IDbConnection $connection + * @throws \OC\DatabaseSetupException + */ private function createDBUser($connection) { - $name = $this->dbuser; - $password = $this->dbpassword; + $name = $this->dbUser; + $password = $this->dbPassword; // we need to create 2 accounts, one for global use and one for local user. if we don't specify the local one, // the anonymous user would take precedence when there is one. $query = "CREATE USER '$name'@'localhost' IDENTIFIED BY '$password'"; - $result = mysql_query($query, $connection); - if (!$result) { - throw new \OC\DatabaseSetupException($this->trans->t("MySQL/MariaDB user '%s'@'localhost' exists already.", array($name)), - $this->trans->t("Drop this user from MySQL/MariaDB", array($name))); - } + $connection->executeUpdate($query); $query = "CREATE USER '$name'@'%' IDENTIFIED BY '$password'"; - $result = mysql_query($query, $connection); - if (!$result) { - throw new \OC\DatabaseSetupException($this->trans->t("MySQL/MariaDB user '%s'@'%%' already exists", array($name)), - $this->trans->t("Drop this user from MySQL/MariaDB.")); + $connection->executeUpdate($query); + } + + /** + * @return \OC\DB\Connection + * @throws \OC\DatabaseSetupException + */ + private function connect() { + $type = 'mysql'; + $connectionParams = array( + 'host' => $this->dbHost, + 'user' => $this->dbUser, + 'password' => $this->dbPassword, + 'tablePrefix' => $this->tablePrefix, + ); + $cf = new ConnectionFactory(); + return $cf->getConnection($type, $connectionParams); + } + + /** + * @param $username + * @param IDBConnection $connection + * @return array + */ + private function createSpecificUser($username, $connection) { + try { + //user already specified in config + $oldUser = $this->config->getSystemValue('dbuser', false); + + //we don't have a dbuser specified in config + if ($this->dbUser !== $oldUser) { + //add prefix to the admin username to prevent collisions + $adminUser = substr('oc_' . $username, 0, 16); + + $i = 1; + while (true) { + //this should be enough to check for admin rights in mysql + $query = 'SELECT user FROM mysql.user WHERE user=?'; + $result = $connection->executeQuery($query, [$adminUser]); + + //current dbuser has admin rights + if ($result) { + $data = $result->fetchAll(); + //new dbuser does not exist + if (count($data) === 0) { + //use the admin login data for the new database user + $this->dbUser = $adminUser; + + //create a random password so we don't need to store the admin password in the config file + $this->dbPassword = $this->random->getMediumStrengthGenerator()->generate(30); + + $this->createDBUser($connection); + + break; + } else { + //repeat with different username + $length = strlen((string)$i); + $adminUser = substr('oc_' . $username, 0, 16 - $length) . $i; + $i++; + } + } else { + break; + } + }; + } + } catch (\Exception $ex) { + $this->logger->error('Specific user creation failed: {error}', [ + 'app' => 'mysql.setup', + 'error' => $ex->getMessage() + ]); } + + $this->config->setSystemValues([ + 'dbuser' => $this->dbUser, + 'dbpassword' => $this->dbPassword, + ]); } } diff --git a/lib/private/setup/oci.php b/lib/private/setup/oci.php index d46d5529da0..1e1eb1ff54e 100644 --- a/lib/private/setup/oci.php +++ b/lib/private/setup/oci.php @@ -38,10 +38,10 @@ class OCI extends AbstractDatabase { $this->dbtablespace = 'USERS'; } // allow empty hostname for oracle - $this->dbhost = $config['dbhost']; + $this->dbHost = $config['dbhost']; \OC_Config::setValues([ - 'dbhost' => $this->dbhost, + 'dbhost' => $this->dbHost, 'dbtablespace' => $this->dbtablespace, ]); } @@ -58,8 +58,8 @@ class OCI extends AbstractDatabase { } public function setupDatabase($username) { - $e_host = addslashes($this->dbhost); - $e_dbname = addslashes($this->dbname); + $e_host = addslashes($this->dbHost); + $e_dbname = addslashes($this->dbName); //check if the database user has admin right if ($e_host == '') { $easy_connect_string = $e_dbname; // use dbname as easy connect name @@ -67,7 +67,7 @@ class OCI extends AbstractDatabase { $easy_connect_string = '//'.$e_host.'/'.$e_dbname; } \OCP\Util::writeLog('setup oracle', 'connect string: ' . $easy_connect_string, \OCP\Util::DEBUG); - $connection = @oci_connect($this->dbuser, $this->dbpassword, $easy_connect_string); + $connection = @oci_connect($this->dbUser, $this->dbPassword, $easy_connect_string); if(!$connection) { $errorMessage = $this->getLastError(); if ($errorMessage) { @@ -103,23 +103,23 @@ class OCI extends AbstractDatabase { //use the admin login data for the new database user //add prefix to the oracle user name to prevent collisions - $this->dbuser='oc_'.$username; + $this->dbUser='oc_'.$username; //create a new password so we don't need to store the admin config in the config file - $this->dbpassword=\OC_Util::generateRandomBytes(30); + $this->dbPassword=\OC_Util::generateRandomBytes(30); //oracle passwords are treated as identifiers: // must start with alphanumeric char // needs to be shortened to 30 bytes, as the two " needed to escape the identifier count towards the identifier length. - $this->dbpassword=substr($this->dbpassword, 0, 30); + $this->dbPassword=substr($this->dbPassword, 0, 30); $this->createDBUser($connection); } } \OC_Config::setValues([ - 'dbuser' => $this->dbuser, - 'dbname' => $this->dbname, - 'dbpassword' => $this->dbpassword, + 'dbuser' => $this->dbUser, + 'dbname' => $this->dbName, + 'dbpassword' => $this->dbPassword, ]); //create the database not necessary, oracle implies user = schema @@ -131,26 +131,26 @@ class OCI extends AbstractDatabase { oci_close($connection); // connect to the oracle database (schema=$this->dbuser) an check if the schema needs to be filled - $this->dbuser = \OC_Config::getValue('dbuser'); + $this->dbUser = \OC_Config::getValue('dbuser'); //$this->dbname = \OC_Config::getValue('dbname'); - $this->dbpassword = \OC_Config::getValue('dbpassword'); + $this->dbPassword = \OC_Config::getValue('dbpassword'); - $e_host = addslashes($this->dbhost); - $e_dbname = addslashes($this->dbname); + $e_host = addslashes($this->dbHost); + $e_dbname = addslashes($this->dbName); if ($e_host == '') { $easy_connect_string = $e_dbname; // use dbname as easy connect name } else { $easy_connect_string = '//'.$e_host.'/'.$e_dbname; } - $connection = @oci_connect($this->dbuser, $this->dbpassword, $easy_connect_string); + $connection = @oci_connect($this->dbUser, $this->dbPassword, $easy_connect_string); if(!$connection) { throw new \OC\DatabaseSetupException($this->trans->t('Oracle username and/or password not valid'), $this->trans->t('You need to enter either an existing account or the administrator.')); } $query = "SELECT count(*) FROM user_tables WHERE table_name = :un"; $stmt = oci_parse($connection, $query); - $un = $this->tableprefix.'users'; + $un = $this->tablePrefix.'users'; oci_bind_by_name($stmt, ':un', $un); if (!$stmt) { $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; @@ -171,8 +171,8 @@ class OCI extends AbstractDatabase { * @param resource $connection */ private function createDBUser($connection) { - $name = $this->dbuser; - $password = $this->dbpassword; + $name = $this->dbUser; + $password = $this->dbPassword; $query = "SELECT * FROM all_users WHERE USERNAME = :un"; $stmt = oci_parse($connection, $query); if (!$stmt) { diff --git a/lib/private/setup/postgresql.php b/lib/private/setup/postgresql.php index c8fd3b98fe4..319b6676ef8 100644 --- a/lib/private/setup/postgresql.php +++ b/lib/private/setup/postgresql.php @@ -27,9 +27,9 @@ class PostgreSQL extends AbstractDatabase { public $dbprettyname = 'PostgreSQL'; public function setupDatabase($username) { - $e_host = addslashes($this->dbhost); - $e_user = addslashes($this->dbuser); - $e_password = addslashes($this->dbpassword); + $e_host = addslashes($this->dbHost); + $e_user = addslashes($this->dbUser); + $e_password = addslashes($this->dbPassword); // Fix database with port connection if(strpos($e_host, ':')) { @@ -43,7 +43,7 @@ class PostgreSQL extends AbstractDatabase { $connection = @pg_connect($connection_string); if(!$connection) { // Try if we can connect to the DB with the specified name - $e_dbname = addslashes($this->dbname); + $e_dbname = addslashes($this->dbName); $connection_string = "host='$e_host' dbname='$e_dbname' user='$e_user' port='$port' password='$e_password'"; $connection = @pg_connect($connection_string); @@ -51,7 +51,7 @@ class PostgreSQL extends AbstractDatabase { throw new \OC\DatabaseSetupException($this->trans->t('PostgreSQL username and/or password not valid'), $this->trans->t('You need to enter either an existing account or the administrator.')); } - $e_user = pg_escape_string($this->dbuser); + $e_user = pg_escape_string($this->dbUser); //check for roles creation rights in postgresql $query="SELECT 1 FROM pg_roles WHERE rolcreaterole=TRUE AND rolname='$e_user'"; $result = pg_query($connection, $query); @@ -59,16 +59,16 @@ class PostgreSQL extends AbstractDatabase { //use the admin login data for the new database user //add prefix to the postgresql user name to prevent collisions - $this->dbuser='oc_'.$username; + $this->dbUser='oc_'.$username; //create a new password so we don't need to store the admin config in the config file - $this->dbpassword=\OC_Util::generateRandomBytes(30); + $this->dbPassword=\OC_Util::generateRandomBytes(30); $this->createDBUser($connection); } \OC_Config::setValues([ - 'dbuser' => $this->dbuser, - 'dbpassword' => $this->dbpassword, + 'dbuser' => $this->dbUser, + 'dbpassword' => $this->dbPassword, ]); //create the database @@ -78,13 +78,13 @@ class PostgreSQL extends AbstractDatabase { pg_close($connection); // connect to the ownCloud database (dbname=$this->dbname) and check if it needs to be filled - $this->dbuser = \OC_Config::getValue('dbuser'); - $this->dbpassword = \OC_Config::getValue('dbpassword'); + $this->dbUser = \OC_Config::getValue('dbuser'); + $this->dbPassword = \OC_Config::getValue('dbpassword'); - $e_host = addslashes($this->dbhost); - $e_dbname = addslashes($this->dbname); - $e_user = addslashes($this->dbuser); - $e_password = addslashes($this->dbpassword); + $e_host = addslashes($this->dbHost); + $e_dbname = addslashes($this->dbName); + $e_user = addslashes($this->dbUser); + $e_password = addslashes($this->dbPassword); // Fix database with port connection if(strpos($e_host, ':')) { @@ -99,7 +99,7 @@ class PostgreSQL extends AbstractDatabase { throw new \OC\DatabaseSetupException($this->trans->t('PostgreSQL username and/or password not valid'), $this->trans->t('You need to enter either an existing account or the administrator.')); } - $query = "select count(*) FROM pg_class WHERE relname='".$this->tableprefix."users' limit 1"; + $query = "select count(*) FROM pg_class WHERE relname='".$this->tablePrefix."users' limit 1"; $result = pg_query($connection, $query); if($result) { $row = pg_fetch_row($result); @@ -111,8 +111,8 @@ class PostgreSQL extends AbstractDatabase { private function createDatabase($connection) { //we cant use OC_BD functions here because we need to connect as the administrative user. - $e_name = pg_escape_string($this->dbname); - $e_user = pg_escape_string($this->dbuser); + $e_name = pg_escape_string($this->dbName); + $e_user = pg_escape_string($this->dbUser); $query = "select datname from pg_database where datname = '$e_name'"; $result = pg_query($connection, $query); if(!$result) { @@ -137,8 +137,8 @@ class PostgreSQL extends AbstractDatabase { } private function createDBUser($connection) { - $e_name = pg_escape_string($this->dbuser); - $e_password = pg_escape_string($this->dbpassword); + $e_name = pg_escape_string($this->dbUser); + $e_password = pg_escape_string($this->dbPassword); $query = "select * from pg_roles where rolname='$e_name';"; $result = pg_query($connection, $query); if(!$result) { diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 40fcc59f219..9aea4677b5b 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -1218,7 +1218,7 @@ class Share extends Constants { $qb = $connection->getQueryBuilder(); $qb->select('uid_owner') - ->from('*PREFIX*share') + ->from('share') ->where($qb->expr()->eq('id', $qb->createParameter('shareId'))) ->setParameter(':shareId', $shareId); $result = $qb->execute(); @@ -1269,7 +1269,7 @@ class Share extends Constants { self::verifyPassword($password); $qb = $connection->getQueryBuilder(); - $qb->update('*PREFIX*share') + $qb->update('share') ->set('share_with', $qb->createParameter('pass')) ->where($qb->expr()->eq('id', $qb->createParameter('shareId'))) ->setParameter(':pass', is_null($password) ? null : \OC::$server->getHasher()->hash($password)) diff --git a/lib/private/template.php b/lib/private/template.php index ca689729f80..e7acc778de3 100644 --- a/lib/private/template.php +++ b/lib/private/template.php @@ -222,9 +222,9 @@ class OC_Template extends \OC\Template\Base { /** * print error page using Exception details - * @param Exception $exception + * @param Exception|Error $exception */ - public static function printExceptionErrorPage(Exception $exception) { + public static function printExceptionErrorPage($exception) { $request = \OC::$server->getRequest(); $content = new \OC_Template('', 'exception', 'error', false); $content->assign('errorClass', get_class($exception)); diff --git a/lib/private/util.php b/lib/private/util.php index 39d64952dc6..501dbf5c4c5 100644 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -143,6 +143,14 @@ class OC_Util { return $storage; }); + // install storage availability wrapper, before most other wrappers + \OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) { + if (!$storage->isLocal()) { + return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]); + } + return $storage; + }); + \OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) { // set up quota for home storages, even for other users // which can happen when using sharing @@ -567,7 +575,8 @@ class OC_Util { } $webServerRestart = false; - $setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'), new \OC_Defaults()); + $setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'), + new \OC_Defaults(), \OC::$server->getLogger(), \OC::$server->getSecureRandom()); $availableDatabases = $setup->getSupportedDatabases(); if (empty($availableDatabases)) { $errors[] = array( diff --git a/lib/public/activity/iextension.php b/lib/public/activity/iextension.php index 19d1d2e83a0..5d9fe3329ef 100644 --- a/lib/public/activity/iextension.php +++ b/lib/public/activity/iextension.php @@ -38,6 +38,8 @@ namespace OCP\Activity; * @since 8.0.0 */ interface IExtension { + const METHOD_STREAM = 'stream'; + const METHOD_MAIL = 'email'; const PRIORITY_VERYLOW = 10; const PRIORITY_LOW = 20; @@ -50,8 +52,13 @@ interface IExtension { * If no additional types are to be added false is to be returned * * @param string $languageCode - * @return array|false + * @return array|false Array "stringID of the type" => "translated string description for the setting" + * or Array "stringID of the type" => [ + * 'desc' => "translated string description for the setting" + * 'methods' => [self::METHOD_*], + * ] * @since 8.0.0 + * @changed 8.2.0 - Added support to allow limiting notifications to certain methods */ public function getNotificationTypes($languageCode); diff --git a/lib/public/activity/imanager.php b/lib/public/activity/imanager.php index cadb37da03b..0f5dccd8ba1 100644 --- a/lib/public/activity/imanager.php +++ b/lib/public/activity/imanager.php @@ -81,9 +81,15 @@ interface IManager { /** * Will return additional notification types as specified by other apps + * * @param string $languageCode - * @return array + * @return array Array "stringID of the type" => "translated string description for the setting" + * or Array "stringID of the type" => [ + * 'desc' => "translated string description for the setting" + * 'methods' => [\OCP\Activity\IExtension::METHOD_*], + * ] * @since 8.0.0 + * @changed 8.2.0 - Added support to allow limiting notifications to certain methods */ function getNotificationTypes($languageCode); diff --git a/lib/public/appframework/http/contentsecuritypolicy.php b/lib/public/appframework/http/contentsecuritypolicy.php index 9c7218dc8ba..ee36f7aac17 100644 --- a/lib/public/appframework/http/contentsecuritypolicy.php +++ b/lib/public/appframework/http/contentsecuritypolicy.php @@ -63,6 +63,7 @@ class ContentSecurityPolicy { /** @var array Domains from which images can get loaded */ private $allowedImageDomains = [ '\'self\'', + 'data:', ]; /** @var array Domains to which connections can be done */ private $allowedConnectDomains = [ diff --git a/lib/public/appframework/http/ocsresponse.php b/lib/public/appframework/http/ocsresponse.php index 52d3c2fa665..2e788a52bb9 100644 --- a/lib/public/appframework/http/ocsresponse.php +++ b/lib/public/appframework/http/ocsresponse.php @@ -41,38 +41,26 @@ class OCSResponse extends Response { private $format; private $statuscode; private $message; - private $tag; - private $tagattribute; - private $dimension; private $itemscount; private $itemsperpage; /** * generates the xml or json response for the API call from an multidimenional data array. * @param string $format - * @param string $status - * @param string $statuscode + * @param int $statuscode * @param string $message * @param array $data - * @param string $tag - * @param string $tagattribute - * @param int $dimension * @param int|string $itemscount * @param int|string $itemsperpage * @since 8.1.0 */ - public function __construct($format, $status, $statuscode, $message, - $data=[], $tag='', $tagattribute='', - $dimension=-1, $itemscount='', + public function __construct($format, $statuscode, $message, + $data=[], $itemscount='', $itemsperpage='') { $this->format = $format; - $this->setStatus($status); $this->statuscode = $statuscode; $this->message = $message; $this->data = $data; - $this->tag = $tag; - $this->tagattribute = $tagattribute; - $this->dimension = $dimension; $this->itemscount = $itemscount; $this->itemsperpage = $itemsperpage; @@ -93,11 +81,11 @@ class OCSResponse extends Response { * @since 8.1.0 */ public function render() { - return OC_OCS::generateXml( - $this->format, $this->getStatus(), $this->statuscode, $this->message, - $this->data, $this->tag, $this->tagattribute, $this->dimension, - $this->itemscount, $this->itemsperpage - ); + $r = new \OC_OCS_Result($this->data, $this->statuscode, $this->message); + $r->setTotalItems($this->itemscount); + $r->setItemsPerPage($this->itemsperpage); + + return \OC_API::renderResult($r, $this->format); } diff --git a/lib/public/appframework/iappcontainer.php b/lib/public/appframework/iappcontainer.php index 64b1082aa97..7338dbd858d 100644 --- a/lib/public/appframework/iappcontainer.php +++ b/lib/public/appframework/iappcontainer.php @@ -86,4 +86,11 @@ interface IAppContainer extends IContainer { */ function log($message, $level); + /** + * Register a capability + * + * @param string $serviceName e.g. 'OCA\Files\Capabilities' + * @since 8.2.0 + */ + public function registerCapability($serviceName); } diff --git a/lib/public/appframework/ocscontroller.php b/lib/public/appframework/ocscontroller.php index 602731fe761..55ba518020a 100644 --- a/lib/public/appframework/ocscontroller.php +++ b/lib/public/appframework/ocscontroller.php @@ -42,7 +42,7 @@ abstract class OCSController extends ApiController { * constructor of the controller * @param string $appName the name of the app * @param IRequest $request an instance of the request - * @param string $corsMethods comma seperated string of HTTP verbs which + * @param string $corsMethods comma separated string of HTTP verbs which * should be allowed for websites or webapps when calling your API, defaults to * 'PUT, POST, GET, DELETE, PATCH' * @param string $corsAllowedHeaders comma seperated string of HTTP headers @@ -80,13 +80,9 @@ abstract class OCSController extends ApiController { } $params = [ - 'status' => 'OK', 'statuscode' => 100, 'message' => 'OK', 'data' => [], - 'tag' => '', - 'tagattribute' => '', - 'dimension' => 'dynamic', 'itemscount' => '', 'itemsperpage' => '' ]; @@ -96,9 +92,8 @@ abstract class OCSController extends ApiController { } return new OCSResponse( - $format, $params['status'], $params['statuscode'], - $params['message'], $params['data'], $params['tag'], - $params['tagattribute'], $params['dimension'], + $format, $params['statuscode'], + $params['message'], $params['data'], $params['itemscount'], $params['itemsperpage'] ); } diff --git a/lib/public/capabilities/icapability.php b/lib/public/capabilities/icapability.php new file mode 100644 index 00000000000..71a56128d26 --- /dev/null +++ b/lib/public/capabilities/icapability.php @@ -0,0 +1,46 @@ +<?php +/** + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @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 OCP\Capabilities; + +/** + * Minimal interface that has to be implemented for a class to be considered + * a capability. + * + * In an application use: + * $this->getContainer()->registerCapability('OCA\MY_APP\Capabilities'); + * To register capabilities. + * + * The class 'OCA\MY_APP\Capabilities' must then implement ICapability + * + * @since 8.2.0 + */ +interface ICapability { + + /** + * Function an app uses to return the capabilities + * + * @return array Array containing the apps capabilities + * @since 8.2.0 + */ + public function getCapabilities(); +} + diff --git a/lib/public/db/querybuilder/iquerybuilder.php b/lib/public/db/querybuilder/iquerybuilder.php index 09d5d199bef..3fc07af1a47 100644 --- a/lib/public/db/querybuilder/iquerybuilder.php +++ b/lib/public/db/querybuilder/iquerybuilder.php @@ -27,6 +27,15 @@ namespace OCP\DB\QueryBuilder; */ interface IQueryBuilder { /** + * Enable/disable automatic prefixing of table names with the oc_ prefix + * + * @param bool $enabled If set to true table names will be prefixed with the + * owncloud database prefix automatically. + * @since 8.2.0 + */ + public function automaticTablePrefix($enabled); + + /** * Gets an ExpressionBuilder used for object-oriented construction of query expressions. * This producer method is intended for convenient inline usage. Example: * diff --git a/lib/public/files/storage.php b/lib/public/files/storage.php index 41218996382..ac3603e48d4 100644 --- a/lib/public/files/storage.php +++ b/lib/public/files/storage.php @@ -439,4 +439,24 @@ interface Storage { * @since 8.1.0 */ public function changeLock($path, $type, ILockingProvider $provider); + + /** + * Test a storage for availability + * + * @since 8.2.0 + * @return bool + */ + public function test(); + + /** + * @since 8.2.0 + * @return array [ available, last_checked ] + */ + public function getAvailability(); + + /** + * @since 8.2.0 + * @param bool $isAvailable + */ + public function setAvailability($isAvailable); } diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index f3165db33da..ab1729da255 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -438,4 +438,5 @@ interface IServerContainer { * @since 8.2.0 */ public function getMimeTypeDetector(); + } diff --git a/lib/repair/cleantags.php b/lib/repair/cleantags.php index 2bda1047081..d16a49fbca7 100644 --- a/lib/repair/cleantags.php +++ b/lib/repair/cleantags.php @@ -65,8 +65,8 @@ class CleanTags extends BasicEmitter implements RepairStep { protected function deleteOrphanFileEntries() { $this->deleteOrphanEntries( '%d tags for delete files have been removed.', - '*PREFIX*vcategory_to_object', 'objid', - '*PREFIX*filecache', 'fileid', 'path_hash' + 'vcategory_to_object', 'objid', + 'filecache', 'fileid', 'path_hash' ); } @@ -76,8 +76,8 @@ class CleanTags extends BasicEmitter implements RepairStep { protected function deleteOrphanTagEntries() { $this->deleteOrphanEntries( '%d tag entries for deleted tags have been removed.', - '*PREFIX*vcategory_to_object', 'categoryid', - '*PREFIX*vcategory', 'id', 'uid' + 'vcategory_to_object', 'categoryid', + 'vcategory', 'id', 'uid' ); } @@ -87,8 +87,8 @@ class CleanTags extends BasicEmitter implements RepairStep { protected function deleteOrphanCategoryEntries() { $this->deleteOrphanEntries( '%d tags with no entries have been removed.', - '*PREFIX*vcategory', 'id', - '*PREFIX*vcategory_to_object', 'categoryid', 'type' + 'vcategory', 'id', + 'vcategory_to_object', 'categoryid', 'type' ); } diff --git a/lib/repair/filletags.php b/lib/repair/filletags.php index f1bb2c896c4..40072209982 100644 --- a/lib/repair/filletags.php +++ b/lib/repair/filletags.php @@ -42,7 +42,7 @@ class FillETags extends BasicEmitter implements \OC\RepairStep { public function run() { $qb = $this->connection->getQueryBuilder(); - $qb->update('*PREFIX*filecache') + $qb->update('filecache') ->set('etag', $qb->expr()->literal('xxx')) ->where($qb->expr()->eq('etag', $qb->expr()->literal(''))) ->orWhere($qb->expr()->isNull('etag')); diff --git a/ocs/v1.php b/ocs/v1.php index 2829cf08c57..c5c18d20b8a 100644 --- a/ocs/v1.php +++ b/ocs/v1.php @@ -56,5 +56,7 @@ try { } catch (MethodNotAllowedException $e) { OC_API::setContentType(); OC_Response::setStatus(405); +} catch (\OC\OCS\Exception $ex) { + OC_API::respond($ex->getResult(), OC_API::requestedFormat()); } diff --git a/ocs/v2.php b/ocs/v2.php new file mode 100644 index 00000000000..b2e3b259727 --- /dev/null +++ b/ocs/v2.php @@ -0,0 +1,22 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @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/> + * + */ + +require_once 'v1.php'; diff --git a/settings/admin.php b/settings/admin.php index 3021a8d4d97..8410a6671ef 100644 --- a/settings/admin.php +++ b/settings/admin.php @@ -223,7 +223,7 @@ $formsAndMore = array_merge($formsAndMore, $formsMap); $formsAndMore[] = ['anchor' => 'backgroundjobs', 'section-name' => $l->t('Cron')]; $formsAndMore[] = ['anchor' => 'mail_general_settings', 'section-name' => $l->t('Email server')]; $formsAndMore[] = ['anchor' => 'log-section', 'section-name' => $l->t('Log')]; -$formsAndMore[] = ['anchor' => 'server-status', 'section-name' => $l->t('Server Status')]; +$formsAndMore[] = ['anchor' => 'server-status', 'section-name' => $l->t('Server status')]; $formsAndMore[] = ['anchor' => 'admin-tips', 'section-name' => $l->t('Tips & tricks')]; if ($updaterAppPanel) { $formsAndMore[] = ['anchor' => 'updater', 'section-name' => $l->t('Updates')]; diff --git a/settings/application.php b/settings/application.php index 8da835c18d2..155cc39d041 100644 --- a/settings/application.php +++ b/settings/application.php @@ -107,7 +107,8 @@ class Application extends App { $c->query('AppName'), $c->query('Request'), $c->query('CertificateManager'), - $c->query('L10N') + $c->query('L10N'), + $c->query('IAppManager') ); }); $container->registerService('GroupsController', function(IContainer $c) { diff --git a/settings/controller/certificatecontroller.php b/settings/controller/certificatecontroller.php index ea20b7c587f..92d0961efb7 100644 --- a/settings/controller/certificatecontroller.php +++ b/settings/controller/certificatecontroller.php @@ -21,6 +21,7 @@ namespace OC\Settings\Controller; +use OCP\App\IAppManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; @@ -36,20 +37,25 @@ class CertificateController extends Controller { private $certificateManager; /** @var IL10N */ private $l10n; + /** @var IAppManager */ + private $appManager; /** * @param string $appName * @param IRequest $request * @param ICertificateManager $certificateManager * @param IL10N $l10n + * @param IAppManager $appManager */ public function __construct($appName, IRequest $request, ICertificateManager $certificateManager, - IL10N $l10n) { + IL10N $l10n, + IAppManager $appManager) { parent::__construct($appName, $request); $this->certificateManager = $certificateManager; $this->l10n = $l10n; + $this->appManager = $appManager; } /** @@ -60,6 +66,11 @@ class CertificateController extends Controller { * @return array */ public function addPersonalRootCertificate() { + + if ($this->isCertificateImportAllowed() === false) { + return new DataResponse('Individual certificate management disabled', Http::STATUS_FORBIDDEN); + } + $file = $this->request->getUploadedFile('rootcert_import'); if(empty($file)) { return new DataResponse(['message' => 'No file uploaded'], Http::STATUS_UNPROCESSABLE_ENTITY); @@ -92,8 +103,29 @@ class CertificateController extends Controller { * @return DataResponse */ public function removePersonalRootCertificate($certificateIdentifier) { + + if ($this->isCertificateImportAllowed() === false) { + return new DataResponse('Individual certificate management disabled', Http::STATUS_FORBIDDEN); + } + $this->certificateManager->removeCertificate($certificateIdentifier); return new DataResponse(); } + /** + * check if certificate import is allowed + * + * @return bool + */ + protected function isCertificateImportAllowed() { + $externalStorageEnabled = $this->appManager->isEnabledForUser('files_external'); + if ($externalStorageEnabled) { + $backends = \OC_Mount_Config::getPersonalBackends(); + if (!empty($backends)) { + return true; + } + } + return false; + } + } diff --git a/settings/controller/checksetupcontroller.php b/settings/controller/checksetupcontroller.php index f849e3ed565..33f94fe4238 100644 --- a/settings/controller/checksetupcontroller.php +++ b/settings/controller/checksetupcontroller.php @@ -128,7 +128,7 @@ class CheckSetupController extends Controller { /** * Check if the used SSL lib is outdated. Older OpenSSL and NSS versions do * have multiple bugs which likely lead to problems in combination with - * functionalities required by ownCloud such as SNI. + * functionality required by ownCloud such as SNI. * * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546 * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172 @@ -175,6 +175,40 @@ class CheckSetupController extends Controller { return ''; } + + /* + * Whether the php version is still supported (at time of release) + * according to: https://secure.php.net/supported-versions.php + * + * @return array + */ + private function isPhpSupported() { + $eol = false; + + //PHP 5.4 is EOL on 14 Sep 2015 + if (version_compare(PHP_VERSION, '5.5.0') === -1) { + $eol = true; + } + + return ['eol' => $eol, 'version' => PHP_VERSION]; + } + + /* + * Check if the reverse proxy configuration is working as expected + * + * @return bool + */ + private function forwardedForHeadersWorking() { + $trustedProxies = $this->config->getSystemValue('trusted_proxies', []); + $remoteAddress = $this->request->getRemoteAddress(); + + if (is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) { + return false; + } + + // either not enabled or working correctly + return true; + } /** * @return DataResponse @@ -189,6 +223,9 @@ class CheckSetupController extends Controller { 'isUrandomAvailable' => $this->isUrandomAvailable(), 'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'), 'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(), + 'phpSupported' => $this->isPhpSupported(), + 'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(), + 'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'), ] ); } diff --git a/settings/css/settings.css b/settings/css/settings.css index e0fe9b446be..0af63821627 100644 --- a/settings/css/settings.css +++ b/settings/css/settings.css @@ -386,6 +386,12 @@ table.grid td.date{ display: inline-block; } +#encryptionAPI li { + list-style-type: initial; + margin-left: 20px; + padding: 5px 0; +} + .mail_settings p label:first-child { display: inline-block; width: 300px; diff --git a/settings/js/admin.js b/settings/js/admin.js index 8f705b9048d..3e17d7cc182 100644 --- a/settings/js/admin.js +++ b/settings/js/admin.js @@ -85,6 +85,13 @@ $(document).ready(function(){ }); }); + $('#shareapiExpireAfterNDays').change(function() { + var value = $(this).val(); + if (value <= 0) { + $(this).val("1"); + } + }); + $('#shareAPI input:not(#excludedGroups)').change(function() { var value = $(this).val(); if ($(this).attr('type') === 'checkbox') { diff --git a/settings/js/personal.js b/settings/js/personal.js index ac18f525809..9e4dd54090d 100644 --- a/settings/js/personal.js +++ b/settings/js/personal.js @@ -234,6 +234,20 @@ $(document).ready(function () { var uploadparms = { done: function (e, data) { avatarResponseHandler(data.result); + }, + fail: function (e, data){ + var msg = data.jqXHR.statusText + ' (' + data.jqXHR.status + ')'; + if (!_.isUndefined(data.jqXHR.responseJSON) && + !_.isUndefined(data.jqXHR.responseJSON.data) && + !_.isUndefined(data.jqXHR.responseJSON.data.message) + ) { + msg = data.jqXHR.responseJSON.data.message; + } + avatarResponseHandler({ + data: { + message: t('settings', 'An error occurred: {message}', { message: msg }) + } + }); } }; @@ -247,7 +261,25 @@ $(document).ready(function () { OC.dialogs.filepicker( t('settings', "Select a profile picture"), function (path) { - $.post(OC.generateUrl('/avatar/'), {path: path}, avatarResponseHandler); + $.ajax({ + type: "POST", + url: OC.generateUrl('/avatar/'), + data: { path: path } + }).done(avatarResponseHandler) + .fail(function(jqXHR, status){ + var msg = jqXHR.statusText + ' (' + jqXHR.status + ')'; + if (!_.isUndefined(jqXHR.responseJSON) && + !_.isUndefined(jqXHR.responseJSON.data) && + !_.isUndefined(jqXHR.responseJSON.data.message) + ) { + msg = jqXHR.responseJSON.data.message; + } + avatarResponseHandler({ + data: { + message: t('settings', 'An error occurred: {message}', { message: msg }) + } + }); + }); }, false, ["image/png", "image/jpeg"] diff --git a/settings/js/users/filter.js b/settings/js/users/filter.js index 72f2cfc6d24..339d6ad5ec7 100644 --- a/settings/js/users/filter.js +++ b/settings/js/users/filter.js @@ -7,16 +7,13 @@ /** * @brief this object takes care of the filter functionality on the user * management page - * @param jQuery input element that works as the user text input field - * @param object the UserList object + * @param {UserList} userList the UserList object + * @param {GroupList} groupList the GroupList object */ -function UserManagementFilter(filterInput, userList, groupList) { - this.filterInput = filterInput; +function UserManagementFilter (userList, groupList) { this.userList = userList; this.groupList = groupList; - this.filterGroups = false; - this.thread = undefined; - this.oldval = this.filterInput.val(); + this.oldFilter = ''; this.init(); } @@ -24,39 +21,23 @@ function UserManagementFilter(filterInput, userList, groupList) { /** * @brief sets up when the filter action shall be triggered */ -UserManagementFilter.prototype.init = function() { - var umf = this; - this.filterInput.keyup(function(e) { - //we want to react on any printable letter, plus on modifying stuff like - //Backspace and Delete. extended https://stackoverflow.com/a/12467610 - var valid = - e.keyCode === 0 || e.keyCode === 8 || // like ö or ж; backspace - e.keyCode === 9 || e.keyCode === 46 || // tab; delete - e.keyCode === 32 || // space - (e.keyCode > 47 && e.keyCode < 58) || // number keys - (e.keyCode > 64 && e.keyCode < 91) || // letter keys - (e.keyCode > 95 && e.keyCode < 112) || // numpad keys - (e.keyCode > 185 && e.keyCode < 193) || // ;=,-./` (in order) - (e.keyCode > 218 && e.keyCode < 223); // [\]' (in order) - - //besides the keys, the value must have been changed compared to last - //time - if(valid && umf.oldVal !== umf.getPattern()) { - umf.run(); - } - - umf.oldVal = umf.getPattern(); - }); +UserManagementFilter.prototype.init = function () { + OC.Plugins.register('OCA.Search', this); }; /** * @brief the filter action needs to be done, here the accurate steps are being * taken care of */ -UserManagementFilter.prototype.run = _.debounce(function() { +UserManagementFilter.prototype.run = _.debounce(function (filter) { + if (filter === this.oldFilter) { + return; + } + this.oldFilter = filter; + this.userList.filter = filter; this.userList.empty(); this.userList.update(GroupList.getCurrentGID()); - if(this.filterGroups) { + if (this.groupList.filterGroups) { // user counts are being updated nevertheless this.groupList.empty(); } @@ -69,12 +50,12 @@ UserManagementFilter.prototype.run = _.debounce(function() { * @brief returns the filter String * @returns string */ -UserManagementFilter.prototype.getPattern = function() { +UserManagementFilter.prototype.getPattern = function () { var input = this.filterInput.val(), html = $('html'), isIE8or9 = html.hasClass('lte9'); // FIXME - TODO - once support for IE8 and IE9 is dropped - if(isIE8or9 && input == this.filterInput.attr('placeholder')) { + if (isIE8or9 && input == this.filterInput.attr('placeholder')) { input = ''; } return input; @@ -84,10 +65,14 @@ UserManagementFilter.prototype.getPattern = function() { * @brief adds reset functionality to an HTML element * @param jQuery the jQuery representation of that element */ -UserManagementFilter.prototype.addResetButton = function(button) { +UserManagementFilter.prototype.addResetButton = function (button) { var umf = this; - button.click(function(){ + button.click(function () { umf.filterInput.val(''); umf.run(); }); }; + +UserManagementFilter.prototype.attach = function (search) { + search.setFilter('settings', this.run.bind(this)); +}; diff --git a/settings/js/users/groups.js b/settings/js/users/groups.js index d205e915508..322db6c1b45 100644 --- a/settings/js/users/groups.js +++ b/settings/js/users/groups.js @@ -12,6 +12,8 @@ var GroupList; GroupList = { activeGID: '', everyoneGID: '_everyone', + filter: '', + filterGroups: false, addGroup: function (gid, usercount) { var $li = $userGroupList.find('.isgroup:last-child').clone(); @@ -145,8 +147,8 @@ GroupList = { $.get( OC.generateUrl('/settings/users/groups'), { - pattern: filter.getPattern(), - filterGroups: filter.filterGroups ? 1 : 0, + pattern: this.filter, + filterGroups: this.filterGroups ? 1 : 0, sortGroups: $sortGroupBy }, function (result) { diff --git a/settings/js/users/users.js b/settings/js/users/users.js index 3b25bcd5b5f..6f29d6fe25b 100644 --- a/settings/js/users/users.js +++ b/settings/js/users/users.js @@ -8,13 +8,13 @@ var $userList; var $userListBody; -var filter; var UserList = { availableGroups: [], offset: 0, usersToLoad: 10, //So many users will be loaded when user scrolls down currentGid: '', + filter: '', /** * Initializes the user list @@ -229,7 +229,7 @@ var UserList = { return aa.length - bb.length; }, preSortSearchString: function(a, b) { - var pattern = filter.getPattern(); + var pattern = this.filter; if(typeof pattern === 'undefined') { return undefined; } @@ -398,7 +398,7 @@ var UserList = { gid = ''; } UserList.currentGid = gid; - var pattern = filter.getPattern(); + var pattern = this.filter; $.get( OC.generateUrl('/settings/users/users'), { offset: UserList.offset, limit: limit, gid: gid, pattern: pattern }, @@ -612,7 +612,7 @@ $(document).ready(function () { UserList.initDeleteHandling(); // Implements User Search - filter = new UserManagementFilter($('#usersearchform input'), UserList, GroupList); + OCA.Search.users= new UserManagementFilter(UserList, GroupList); UserList.doSort(); UserList.availableGroups = $userList.data('groups'); diff --git a/settings/l10n/ar.js b/settings/l10n/ar.js index 111af05f050..a4e9705f3ac 100644 --- a/settings/l10n/ar.js +++ b/settings/l10n/ar.js @@ -95,7 +95,6 @@ OC.L10N.register( "Upload new" : "رفع الان", "Select new from Files" : "اختر جديد من الملفات ", "Remove image" : "إزالة الصورة", - "Either png or jpg. Ideally square but you will be able to crop it." : "سواء png او jpg. بامكانك قص الصورة ", "Your avatar is provided by your original account." : "صورتك الرمزية يتم توفيرها عن طريق حسابك الاصلي.", "Cancel" : "الغاء", "Choose as profile image" : "اختر صورة الملف الشخصي", diff --git a/settings/l10n/ar.json b/settings/l10n/ar.json index 1564605d7e0..1bd8a54f022 100644 --- a/settings/l10n/ar.json +++ b/settings/l10n/ar.json @@ -93,7 +93,6 @@ "Upload new" : "رفع الان", "Select new from Files" : "اختر جديد من الملفات ", "Remove image" : "إزالة الصورة", - "Either png or jpg. Ideally square but you will be able to crop it." : "سواء png او jpg. بامكانك قص الصورة ", "Your avatar is provided by your original account." : "صورتك الرمزية يتم توفيرها عن طريق حسابك الاصلي.", "Cancel" : "الغاء", "Choose as profile image" : "اختر صورة الملف الشخصي", diff --git a/settings/l10n/ast.js b/settings/l10n/ast.js index 09b417233f5..af3beefd277 100644 --- a/settings/l10n/ast.js +++ b/settings/l10n/ast.js @@ -138,7 +138,6 @@ OC.L10N.register( "Upload new" : "Xubir otra", "Select new from Files" : "Esbillar otra dende Ficheros", "Remove image" : "Desaniciar imaxe", - "Either png or jpg. Ideally square but you will be able to crop it." : "Ficheru PNG o JPG. Preferiblemente cuadráu, pero vas poder retayalu.", "Your avatar is provided by your original account." : "L'avatar ta proporcionáu pola to cuenta orixinal.", "Cancel" : "Encaboxar", "Choose as profile image" : "Esbillar como imaxe de perfil", diff --git a/settings/l10n/ast.json b/settings/l10n/ast.json index a2f71a157b1..b78e4342d0d 100644 --- a/settings/l10n/ast.json +++ b/settings/l10n/ast.json @@ -136,7 +136,6 @@ "Upload new" : "Xubir otra", "Select new from Files" : "Esbillar otra dende Ficheros", "Remove image" : "Desaniciar imaxe", - "Either png or jpg. Ideally square but you will be able to crop it." : "Ficheru PNG o JPG. Preferiblemente cuadráu, pero vas poder retayalu.", "Your avatar is provided by your original account." : "L'avatar ta proporcionáu pola to cuenta orixinal.", "Cancel" : "Encaboxar", "Choose as profile image" : "Esbillar como imaxe de perfil", diff --git a/settings/l10n/az.js b/settings/l10n/az.js index 23805b8c88b..eb6dc71bc61 100644 --- a/settings/l10n/az.js +++ b/settings/l10n/az.js @@ -191,7 +191,6 @@ OC.L10N.register( "Upload new" : "Yenisini yüklə", "Select new from Files" : "Fayllardan yenisini seç", "Remove image" : "Şəkili sil", - "Either png or jpg. Ideally square but you will be able to crop it." : "Həmçinin png yada jpg. İdealda sahədir ancaq, siz onu kəsə bilərsiniz.", "Your avatar is provided by your original account." : "Sizin avatar sizin original hesab tərəfindən təqdim edilib.", "Cancel" : "Dayandır", "Choose as profile image" : "Profayl şəklini seçin", diff --git a/settings/l10n/az.json b/settings/l10n/az.json index 84ad2602e15..68ed9c3926a 100644 --- a/settings/l10n/az.json +++ b/settings/l10n/az.json @@ -189,7 +189,6 @@ "Upload new" : "Yenisini yüklə", "Select new from Files" : "Fayllardan yenisini seç", "Remove image" : "Şəkili sil", - "Either png or jpg. Ideally square but you will be able to crop it." : "Həmçinin png yada jpg. İdealda sahədir ancaq, siz onu kəsə bilərsiniz.", "Your avatar is provided by your original account." : "Sizin avatar sizin original hesab tərəfindən təqdim edilib.", "Cancel" : "Dayandır", "Choose as profile image" : "Profayl şəklini seçin", diff --git a/settings/l10n/bg_BG.js b/settings/l10n/bg_BG.js index 148fd9ba723..13ac183a120 100644 --- a/settings/l10n/bg_BG.js +++ b/settings/l10n/bg_BG.js @@ -191,7 +191,6 @@ OC.L10N.register( "Upload new" : "Качи нов", "Select new from Files" : "Избери нов от Файловете", "Remove image" : "Премахни изображението", - "Either png or jpg. Ideally square but you will be able to crop it." : "Png или jpg. Най-добре в квадратни размери, но ще имаш възможност да го изрежеш.", "Your avatar is provided by your original account." : "Твоят аватар е взет от оригиналния ти профил.", "Cancel" : "Отказ", "Choose as profile image" : "Избери като аватар", diff --git a/settings/l10n/bg_BG.json b/settings/l10n/bg_BG.json index 3df09bddd72..639998e8410 100644 --- a/settings/l10n/bg_BG.json +++ b/settings/l10n/bg_BG.json @@ -189,7 +189,6 @@ "Upload new" : "Качи нов", "Select new from Files" : "Избери нов от Файловете", "Remove image" : "Премахни изображението", - "Either png or jpg. Ideally square but you will be able to crop it." : "Png или jpg. Най-добре в квадратни размери, но ще имаш възможност да го изрежеш.", "Your avatar is provided by your original account." : "Твоят аватар е взет от оригиналния ти профил.", "Cancel" : "Отказ", "Choose as profile image" : "Избери като аватар", diff --git a/settings/l10n/bs.js b/settings/l10n/bs.js index fd219c3a1c5..bcf27bd82fa 100644 --- a/settings/l10n/bs.js +++ b/settings/l10n/bs.js @@ -159,7 +159,6 @@ OC.L10N.register( "Upload new" : "Učitaj novu", "Select new from Files" : "Odaberi novu iz datoteka", "Remove image" : "Ukloni sliku", - "Either png or jpg. Ideally square but you will be able to crop it." : "Ili png ili jpg. Idealno bi bilo da je kvadratna, ali moći ćete je obrezati", "Your avatar is provided by your original account." : "Vaš avatar je isporučen od strane vašeg izvornog računa.", "Cancel" : "Odustani", "Choose as profile image" : "Izaberi kao sliku profila", diff --git a/settings/l10n/bs.json b/settings/l10n/bs.json index 776be894a47..5a8f070ad6a 100644 --- a/settings/l10n/bs.json +++ b/settings/l10n/bs.json @@ -157,7 +157,6 @@ "Upload new" : "Učitaj novu", "Select new from Files" : "Odaberi novu iz datoteka", "Remove image" : "Ukloni sliku", - "Either png or jpg. Ideally square but you will be able to crop it." : "Ili png ili jpg. Idealno bi bilo da je kvadratna, ali moći ćete je obrezati", "Your avatar is provided by your original account." : "Vaš avatar je isporučen od strane vašeg izvornog računa.", "Cancel" : "Odustani", "Choose as profile image" : "Izaberi kao sliku profila", diff --git a/settings/l10n/ca.js b/settings/l10n/ca.js index 42d623fe264..23ea923f42e 100644 --- a/settings/l10n/ca.js +++ b/settings/l10n/ca.js @@ -3,14 +3,13 @@ OC.L10N.register( { "APCu" : "APCu", "Redis" : "Redis", - "Security & setup warnings" : "Seguretat i configuració de alertes", + "Security & setup warnings" : "Avisos de seguretat i configuració", "Sharing" : "Compartir", "Server-side encryption" : "Xifrat del costat del servidor", "External Storage" : "Emmagatzemament extern", "Cron" : "Cron", "Email server" : "Servidor de correu electrònic", "Log" : "Registre", - "Server Status" : "Estat del servidor", "Tips & tricks" : "Consells i trucs", "Updates" : "Actualitzacions", "Authentication error" : "Error d'autenticació", @@ -215,7 +214,6 @@ OC.L10N.register( "Upload new" : "Puja'n una de nova", "Select new from Files" : "Selecciona'n una de nova dels fitxers", "Remove image" : "Elimina imatge", - "Either png or jpg. Ideally square but you will be able to crop it." : "Pot ser png o jpg. Idealment quadrada, però podreu retallar-la.", "Your avatar is provided by your original account." : "El vostre compte original proporciona l'avatar.", "Cancel" : "Cancel·la", "Choose as profile image" : "Selecciona com a imatge de perfil", diff --git a/settings/l10n/ca.json b/settings/l10n/ca.json index cb172fa503a..1d6b383d1d2 100644 --- a/settings/l10n/ca.json +++ b/settings/l10n/ca.json @@ -1,14 +1,13 @@ { "translations": { "APCu" : "APCu", "Redis" : "Redis", - "Security & setup warnings" : "Seguretat i configuració de alertes", + "Security & setup warnings" : "Avisos de seguretat i configuració", "Sharing" : "Compartir", "Server-side encryption" : "Xifrat del costat del servidor", "External Storage" : "Emmagatzemament extern", "Cron" : "Cron", "Email server" : "Servidor de correu electrònic", "Log" : "Registre", - "Server Status" : "Estat del servidor", "Tips & tricks" : "Consells i trucs", "Updates" : "Actualitzacions", "Authentication error" : "Error d'autenticació", @@ -213,7 +212,6 @@ "Upload new" : "Puja'n una de nova", "Select new from Files" : "Selecciona'n una de nova dels fitxers", "Remove image" : "Elimina imatge", - "Either png or jpg. Ideally square but you will be able to crop it." : "Pot ser png o jpg. Idealment quadrada, però podreu retallar-la.", "Your avatar is provided by your original account." : "El vostre compte original proporciona l'avatar.", "Cancel" : "Cancel·la", "Choose as profile image" : "Selecciona com a imatge de perfil", diff --git a/settings/l10n/cs_CZ.js b/settings/l10n/cs_CZ.js index 87ca48a0e9e..482c62b012d 100644 --- a/settings/l10n/cs_CZ.js +++ b/settings/l10n/cs_CZ.js @@ -10,7 +10,7 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "Emailový server", "Log" : "Záznam", - "Server Status" : "Stav serveru", + "Server status" : "Stav serveru", "Tips & tricks" : "Tipy a triky", "Updates" : "Aktualizace", "Authentication error" : "Chyba přihlášení", @@ -31,7 +31,9 @@ OC.L10N.register( "Unable to change password" : "Změna hesla se nezdařila", "Enabled" : "Povoleno", "Not enabled" : "Vypnuto", + "installing and updating apps via the app store or Federated Cloud Sharing" : "Instalovat a aktualizovat aplikace pomocí obchodu nebo Sdíleného Cloudového Úložiště", "Federated Cloud Sharing" : "Propojené cloudové sdílení", + "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL používá zastaralou %s verzi (%s). Aktualizujte prosím svůj operační systém, jinak funkce jako %s nemusí spolehlivě pracovat.", "A problem occurred, please check your log files (Error: %s)" : "Došlo k chybě, zkontrolujte prosím log (Chyba: %s)", "Migration Completed" : "Migrace dokončena", "Group already exists." : "Skupina již existuje.", @@ -76,6 +78,7 @@ OC.L10N.register( "Uninstalling ...." : "Probíhá odinstalace ...", "Error while uninstalling app" : "Chyba při odinstalaci aplikace", "Uninstall" : "Odinstalovat", + "An error occurred: {message}" : "Nastala chyba: {message}", "Select a profile picture" : "Vyberte profilový obrázek", "Very weak password" : "Velmi slabé heslo", "Weak password" : "Slabé heslo", @@ -178,6 +181,8 @@ OC.L10N.register( "More" : "Více", "Less" : "Méně", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Soubor logu je větší než 100 MB. Jeho stažení zabere nějaký čas!", + "Transactional File Locking is enabled." : "Transakční zamykání souboru je povoleno.", + "Transactional File Locking is disabled." : "Transakční zamykání souboru není povoleno.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "Je použita databáze SQLite. Pro větší instalace doporučujeme přejít na robustnější databázi.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Obzvláště při používání klientské aplikace pro synchronizaci s desktopem není SQLite doporučeno.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Pro migraci na jinou databázi lze použít aplikaci pro příkazový řádek: 'occ db:convert-type', nebo nahlédněte do <a target=\"_blank\" href=\"%s\">dokumentace ↗</a>.", @@ -187,8 +192,6 @@ OC.L10N.register( "Improving the config.php" : "Vylepšení souboru config.php", "Theming" : "Vzhledy", "Hardening and security guidance" : "Průvodce vylepšením bezpečnosti", - "Transactional File Locking is enabled." : "Transakční zamykání souboru je povoleno.", - "Transactional File Locking is disabled." : "Transakční zamykání souboru není povoleno.", "Version" : "Verze", "Developer documentation" : "Vývojářská dokumentace", "Experimental applications ahead" : "Experimentální aplikace v pořadí", @@ -236,7 +239,7 @@ OC.L10N.register( "Upload new" : "Nahrát nový", "Select new from Files" : "Vyberte nový ze souborů", "Remove image" : "Odebrat obrázek", - "Either png or jpg. Ideally square but you will be able to crop it." : "png nebo jpg, nejlépe čtvercový, ale budete mít možnost jej oříznout.", + "Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB." : "Buď png nebo jpg. Nejlépe čtvercový, ale budete mít možnost jej oříznout. Soubor nesmí být větší než 20 MB.", "Your avatar is provided by your original account." : "Váš avatar je poskytován Vaším původním účtem.", "Cancel" : "Zrušit", "Choose as profile image" : "Vybrat jako profilový obrázek", diff --git a/settings/l10n/cs_CZ.json b/settings/l10n/cs_CZ.json index 523be1eaba6..630bf5e6353 100644 --- a/settings/l10n/cs_CZ.json +++ b/settings/l10n/cs_CZ.json @@ -8,7 +8,7 @@ "Cron" : "Cron", "Email server" : "Emailový server", "Log" : "Záznam", - "Server Status" : "Stav serveru", + "Server status" : "Stav serveru", "Tips & tricks" : "Tipy a triky", "Updates" : "Aktualizace", "Authentication error" : "Chyba přihlášení", @@ -29,7 +29,9 @@ "Unable to change password" : "Změna hesla se nezdařila", "Enabled" : "Povoleno", "Not enabled" : "Vypnuto", + "installing and updating apps via the app store or Federated Cloud Sharing" : "Instalovat a aktualizovat aplikace pomocí obchodu nebo Sdíleného Cloudového Úložiště", "Federated Cloud Sharing" : "Propojené cloudové sdílení", + "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL používá zastaralou %s verzi (%s). Aktualizujte prosím svůj operační systém, jinak funkce jako %s nemusí spolehlivě pracovat.", "A problem occurred, please check your log files (Error: %s)" : "Došlo k chybě, zkontrolujte prosím log (Chyba: %s)", "Migration Completed" : "Migrace dokončena", "Group already exists." : "Skupina již existuje.", @@ -74,6 +76,7 @@ "Uninstalling ...." : "Probíhá odinstalace ...", "Error while uninstalling app" : "Chyba při odinstalaci aplikace", "Uninstall" : "Odinstalovat", + "An error occurred: {message}" : "Nastala chyba: {message}", "Select a profile picture" : "Vyberte profilový obrázek", "Very weak password" : "Velmi slabé heslo", "Weak password" : "Slabé heslo", @@ -176,6 +179,8 @@ "More" : "Více", "Less" : "Méně", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Soubor logu je větší než 100 MB. Jeho stažení zabere nějaký čas!", + "Transactional File Locking is enabled." : "Transakční zamykání souboru je povoleno.", + "Transactional File Locking is disabled." : "Transakční zamykání souboru není povoleno.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "Je použita databáze SQLite. Pro větší instalace doporučujeme přejít na robustnější databázi.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Obzvláště při používání klientské aplikace pro synchronizaci s desktopem není SQLite doporučeno.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Pro migraci na jinou databázi lze použít aplikaci pro příkazový řádek: 'occ db:convert-type', nebo nahlédněte do <a target=\"_blank\" href=\"%s\">dokumentace ↗</a>.", @@ -185,8 +190,6 @@ "Improving the config.php" : "Vylepšení souboru config.php", "Theming" : "Vzhledy", "Hardening and security guidance" : "Průvodce vylepšením bezpečnosti", - "Transactional File Locking is enabled." : "Transakční zamykání souboru je povoleno.", - "Transactional File Locking is disabled." : "Transakční zamykání souboru není povoleno.", "Version" : "Verze", "Developer documentation" : "Vývojářská dokumentace", "Experimental applications ahead" : "Experimentální aplikace v pořadí", @@ -234,7 +237,7 @@ "Upload new" : "Nahrát nový", "Select new from Files" : "Vyberte nový ze souborů", "Remove image" : "Odebrat obrázek", - "Either png or jpg. Ideally square but you will be able to crop it." : "png nebo jpg, nejlépe čtvercový, ale budete mít možnost jej oříznout.", + "Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB." : "Buď png nebo jpg. Nejlépe čtvercový, ale budete mít možnost jej oříznout. Soubor nesmí být větší než 20 MB.", "Your avatar is provided by your original account." : "Váš avatar je poskytován Vaším původním účtem.", "Cancel" : "Zrušit", "Choose as profile image" : "Vybrat jako profilový obrázek", diff --git a/settings/l10n/da.js b/settings/l10n/da.js index 27d146c46d6..8aa7e3f7b59 100644 --- a/settings/l10n/da.js +++ b/settings/l10n/da.js @@ -10,7 +10,7 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "E-mailserver", "Log" : "Log", - "Server Status" : "Serverstatus", + "Server status" : "ServerStatus", "Tips & tricks" : "Tips & tricks", "Updates" : "Opdateringer", "Authentication error" : "Adgangsfejl", @@ -31,7 +31,9 @@ OC.L10N.register( "Unable to change password" : "Kunne ikke ændre kodeord", "Enabled" : "Aktiveret", "Not enabled" : "Slået fra", + "installing and updating apps via the app store or Federated Cloud Sharing" : "installation og opdatering af apps via app-butikken eller Federated Cloud Sharing", "Federated Cloud Sharing" : "Sammensluttet Cloud deling", + "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL bruger en forældet %s version (%s). Husk at opdatere dit styresystem ellers vil funktioner såsom %s ikke fungere pålideligt.", "A problem occurred, please check your log files (Error: %s)" : "Der opstod en fejl - tjek venligst dine logfiler (fejl: %s)", "Migration Completed" : "Overflytning blev fuldført", "Group already exists." : "Gruppen findes allerede.", @@ -76,6 +78,7 @@ OC.L10N.register( "Uninstalling ...." : "Afinstallerer...", "Error while uninstalling app" : "Fejl under afinstallering af app", "Uninstall" : "Afinstallér", + "An error occurred: {message}" : "En fejl opstod:{message}", "Select a profile picture" : "Vælg et profilbillede", "Very weak password" : "Meget svagt kodeord", "Weak password" : "Svagt kodeord", @@ -178,6 +181,8 @@ OC.L10N.register( "More" : "Mere", "Less" : "Mindre", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Logfilen er større end 100 MB. Det kan tage en del tid at hente den!", + "Transactional File Locking is enabled." : "Transaktionel fillåsning er slået til.", + "Transactional File Locking is disabled." : "Transaktionel fillåsning er slået fra.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite bruges som database. Til større installationer anbefaler vi at skifte til en anden database-backend.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Brug af SQLite frarådes især når skrivebordsklienten anvendes til filsynkronisering.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "For at migrere til en anden database, så brug kommandolinjeværktøjet: \"occ db:convert-type\" eller se <a target=\"_blank\" href=\"%s\">dokumentationen ↗</a>.", @@ -187,8 +192,6 @@ OC.L10N.register( "Improving the config.php" : "Forbedring af config.php", "Theming" : "Temaer", "Hardening and security guidance" : "Modstanddygtighed og sikkerheds vejledning", - "Transactional File Locking is enabled." : "Transaktionel fillåsning er slået til.", - "Transactional File Locking is disabled." : "Transaktionel fillåsning er slået fra.", "Version" : "Version", "Developer documentation" : "Dokumentation for udviklere", "Experimental applications ahead" : "Kommende eksperimentale programmer", @@ -236,7 +239,6 @@ OC.L10N.register( "Upload new" : "Upload nyt", "Select new from Files" : "Vælg nyt fra Filer", "Remove image" : "Fjern billede", - "Either png or jpg. Ideally square but you will be able to crop it." : "Enten png eller jpg. Ideelt kvadratisk men du har mulighed for at beskære det. ", "Your avatar is provided by your original account." : "Din avatar kommer fra din oprindelige konto.", "Cancel" : "Annuller", "Choose as profile image" : "Vælg som profilbillede", diff --git a/settings/l10n/da.json b/settings/l10n/da.json index 9b343d50b28..eef597ae56f 100644 --- a/settings/l10n/da.json +++ b/settings/l10n/da.json @@ -8,7 +8,7 @@ "Cron" : "Cron", "Email server" : "E-mailserver", "Log" : "Log", - "Server Status" : "Serverstatus", + "Server status" : "ServerStatus", "Tips & tricks" : "Tips & tricks", "Updates" : "Opdateringer", "Authentication error" : "Adgangsfejl", @@ -29,7 +29,9 @@ "Unable to change password" : "Kunne ikke ændre kodeord", "Enabled" : "Aktiveret", "Not enabled" : "Slået fra", + "installing and updating apps via the app store or Federated Cloud Sharing" : "installation og opdatering af apps via app-butikken eller Federated Cloud Sharing", "Federated Cloud Sharing" : "Sammensluttet Cloud deling", + "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL bruger en forældet %s version (%s). Husk at opdatere dit styresystem ellers vil funktioner såsom %s ikke fungere pålideligt.", "A problem occurred, please check your log files (Error: %s)" : "Der opstod en fejl - tjek venligst dine logfiler (fejl: %s)", "Migration Completed" : "Overflytning blev fuldført", "Group already exists." : "Gruppen findes allerede.", @@ -74,6 +76,7 @@ "Uninstalling ...." : "Afinstallerer...", "Error while uninstalling app" : "Fejl under afinstallering af app", "Uninstall" : "Afinstallér", + "An error occurred: {message}" : "En fejl opstod:{message}", "Select a profile picture" : "Vælg et profilbillede", "Very weak password" : "Meget svagt kodeord", "Weak password" : "Svagt kodeord", @@ -176,6 +179,8 @@ "More" : "Mere", "Less" : "Mindre", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Logfilen er større end 100 MB. Det kan tage en del tid at hente den!", + "Transactional File Locking is enabled." : "Transaktionel fillåsning er slået til.", + "Transactional File Locking is disabled." : "Transaktionel fillåsning er slået fra.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite bruges som database. Til større installationer anbefaler vi at skifte til en anden database-backend.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Brug af SQLite frarådes især når skrivebordsklienten anvendes til filsynkronisering.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "For at migrere til en anden database, så brug kommandolinjeværktøjet: \"occ db:convert-type\" eller se <a target=\"_blank\" href=\"%s\">dokumentationen ↗</a>.", @@ -185,8 +190,6 @@ "Improving the config.php" : "Forbedring af config.php", "Theming" : "Temaer", "Hardening and security guidance" : "Modstanddygtighed og sikkerheds vejledning", - "Transactional File Locking is enabled." : "Transaktionel fillåsning er slået til.", - "Transactional File Locking is disabled." : "Transaktionel fillåsning er slået fra.", "Version" : "Version", "Developer documentation" : "Dokumentation for udviklere", "Experimental applications ahead" : "Kommende eksperimentale programmer", @@ -234,7 +237,6 @@ "Upload new" : "Upload nyt", "Select new from Files" : "Vælg nyt fra Filer", "Remove image" : "Fjern billede", - "Either png or jpg. Ideally square but you will be able to crop it." : "Enten png eller jpg. Ideelt kvadratisk men du har mulighed for at beskære det. ", "Your avatar is provided by your original account." : "Din avatar kommer fra din oprindelige konto.", "Cancel" : "Annuller", "Choose as profile image" : "Vælg som profilbillede", diff --git a/settings/l10n/de.js b/settings/l10n/de.js index 4d3d3e09b74..2f0127cf578 100644 --- a/settings/l10n/de.js +++ b/settings/l10n/de.js @@ -10,7 +10,6 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "E-Mail-Server", "Log" : "Log", - "Server Status" : "Serverstatus", "Tips & tricks" : "Tipps & Tricks", "Updates" : "Updates", "Authentication error" : "Authentifizierungsfehler", @@ -178,6 +177,8 @@ OC.L10N.register( "More" : "Mehr", "Less" : "Weniger", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Die Logdatei ist größer als 100 MB. Es kann etwas Zeit beanspruchen, sie herunterzuladen!", + "Transactional File Locking is enabled." : "Dateisperren bei Transaktionen sind aktiviert.", + "Transactional File Locking is disabled." : "Dateisperren bei Transaktionen sind deaktiviert.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite wird als Datenbank verwendet. Bei größeren Installationen wird empfohlen, auf ein anderes Datenbank-Backend zu wechseln.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Insbesondere bei der Nutzung des Desktop Clients zur Dateisynchronisierung wird vom Einsatz von SQLite abgeraten.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Um zu einer anderen Datenbank zu migrieren, benutze bitte die Kommandozeile: 'occ db:convert-type', oder in die <a target=\"_blank\" href=\"%s\">Dokumentation ↗</a> schauen.", @@ -187,8 +188,6 @@ OC.L10N.register( "Improving the config.php" : "Die config.php optimieren", "Theming" : "Themes verwenden", "Hardening and security guidance" : "Systemhärtung und Sicherheitsempfehlungen", - "Transactional File Locking is enabled." : "Dateisperren bei Transaktionen sind aktiviert.", - "Transactional File Locking is disabled." : "Dateisperren bei Transaktionen sind deaktiviert.", "Version" : "Version", "Developer documentation" : "Dokumentation für Entwickler", "Experimental applications ahead" : "Experimentelle Apps nachfolgend", @@ -236,7 +235,6 @@ OC.L10N.register( "Upload new" : "Neues hochladen", "Select new from Files" : "Neues aus den Dateien wählen", "Remove image" : "Bild entfernen", - "Either png or jpg. Ideally square but you will be able to crop it." : "Entweder PNG oder JPG. Im Idealfall quadratisch, Du kannst es aber beschneiden.", "Your avatar is provided by your original account." : "Dein Avatar wird von Deinem ursprünglichen Konto bereitgestellt.", "Cancel" : "Abbrechen", "Choose as profile image" : "Als Profilbild wählen", diff --git a/settings/l10n/de.json b/settings/l10n/de.json index e1bfd5ae679..5850b609eab 100644 --- a/settings/l10n/de.json +++ b/settings/l10n/de.json @@ -8,7 +8,6 @@ "Cron" : "Cron", "Email server" : "E-Mail-Server", "Log" : "Log", - "Server Status" : "Serverstatus", "Tips & tricks" : "Tipps & Tricks", "Updates" : "Updates", "Authentication error" : "Authentifizierungsfehler", @@ -176,6 +175,8 @@ "More" : "Mehr", "Less" : "Weniger", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Die Logdatei ist größer als 100 MB. Es kann etwas Zeit beanspruchen, sie herunterzuladen!", + "Transactional File Locking is enabled." : "Dateisperren bei Transaktionen sind aktiviert.", + "Transactional File Locking is disabled." : "Dateisperren bei Transaktionen sind deaktiviert.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite wird als Datenbank verwendet. Bei größeren Installationen wird empfohlen, auf ein anderes Datenbank-Backend zu wechseln.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Insbesondere bei der Nutzung des Desktop Clients zur Dateisynchronisierung wird vom Einsatz von SQLite abgeraten.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Um zu einer anderen Datenbank zu migrieren, benutze bitte die Kommandozeile: 'occ db:convert-type', oder in die <a target=\"_blank\" href=\"%s\">Dokumentation ↗</a> schauen.", @@ -185,8 +186,6 @@ "Improving the config.php" : "Die config.php optimieren", "Theming" : "Themes verwenden", "Hardening and security guidance" : "Systemhärtung und Sicherheitsempfehlungen", - "Transactional File Locking is enabled." : "Dateisperren bei Transaktionen sind aktiviert.", - "Transactional File Locking is disabled." : "Dateisperren bei Transaktionen sind deaktiviert.", "Version" : "Version", "Developer documentation" : "Dokumentation für Entwickler", "Experimental applications ahead" : "Experimentelle Apps nachfolgend", @@ -234,7 +233,6 @@ "Upload new" : "Neues hochladen", "Select new from Files" : "Neues aus den Dateien wählen", "Remove image" : "Bild entfernen", - "Either png or jpg. Ideally square but you will be able to crop it." : "Entweder PNG oder JPG. Im Idealfall quadratisch, Du kannst es aber beschneiden.", "Your avatar is provided by your original account." : "Dein Avatar wird von Deinem ursprünglichen Konto bereitgestellt.", "Cancel" : "Abbrechen", "Choose as profile image" : "Als Profilbild wählen", diff --git a/settings/l10n/de_DE.js b/settings/l10n/de_DE.js index aa2d887a914..3c28e3cb52f 100644 --- a/settings/l10n/de_DE.js +++ b/settings/l10n/de_DE.js @@ -10,7 +10,6 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "E-Mail-Server", "Log" : "Log", - "Server Status" : "Serverstatus", "Tips & tricks" : "Tipps & Tricks", "Updates" : "Updates", "Authentication error" : "Authentifizierungsfehler", @@ -178,6 +177,8 @@ OC.L10N.register( "More" : "Mehr", "Less" : "Weniger", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Die Logdatei ist größer als 100 MB. Es kann etwas Zeit beanspruchen, sie herunterzuladen!", + "Transactional File Locking is enabled." : "Dateisperren bei Transaktionen sind aktiviert.", + "Transactional File Locking is disabled." : "Dateisperren bei Transaktionen sind deaktiviert.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite wird als Datenbank verwendet. Bei größeren Installationen wird empfohlen, auf ein anderes Datenbank-Backend zu wechseln.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Insbesondere bei der Nutzung des Desktop Clients zur Dateisynchronisierung wird vom Einsatz von SQLite abgeraten.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Um auf eine andere Datenbank zu migrieren, benutzen Sie bitte die Kommandozeile: „occ db:convert-type“ oder konsultieren Sie die <a target=\"_blank\" href=\"%s\">Dokumentation ↗</a>.", @@ -187,8 +188,6 @@ OC.L10N.register( "Improving the config.php" : "Die config.php optimieren", "Theming" : "Themes verwenden", "Hardening and security guidance" : "Systemhärtung und Sicherheitsempfehlungen", - "Transactional File Locking is enabled." : "Dateisperren bei Transaktionen sind aktiviert.", - "Transactional File Locking is disabled." : "Dateisperren bei Transaktionen sind deaktiviert.", "Version" : "Version", "Developer documentation" : "Dokumentation für Entwickler", "Experimental applications ahead" : "Experimentelle Apps nachfolgend", @@ -236,7 +235,6 @@ OC.L10N.register( "Upload new" : "Neues hochladen", "Select new from Files" : "Neues aus Dateien wählen", "Remove image" : "Bild entfernen", - "Either png or jpg. Ideally square but you will be able to crop it." : "Entweder PNG oder JPG. Im Idealfall quadratisch, Sie können es aber beschneiden.", "Your avatar is provided by your original account." : "Ihr Avatar wird von Ihrem Ursprungskonto verwendet.", "Cancel" : "Abbrechen", "Choose as profile image" : "Als Profilbild wählen", diff --git a/settings/l10n/de_DE.json b/settings/l10n/de_DE.json index e064a689a4a..e912d745253 100644 --- a/settings/l10n/de_DE.json +++ b/settings/l10n/de_DE.json @@ -8,7 +8,6 @@ "Cron" : "Cron", "Email server" : "E-Mail-Server", "Log" : "Log", - "Server Status" : "Serverstatus", "Tips & tricks" : "Tipps & Tricks", "Updates" : "Updates", "Authentication error" : "Authentifizierungsfehler", @@ -176,6 +175,8 @@ "More" : "Mehr", "Less" : "Weniger", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Die Logdatei ist größer als 100 MB. Es kann etwas Zeit beanspruchen, sie herunterzuladen!", + "Transactional File Locking is enabled." : "Dateisperren bei Transaktionen sind aktiviert.", + "Transactional File Locking is disabled." : "Dateisperren bei Transaktionen sind deaktiviert.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite wird als Datenbank verwendet. Bei größeren Installationen wird empfohlen, auf ein anderes Datenbank-Backend zu wechseln.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Insbesondere bei der Nutzung des Desktop Clients zur Dateisynchronisierung wird vom Einsatz von SQLite abgeraten.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Um auf eine andere Datenbank zu migrieren, benutzen Sie bitte die Kommandozeile: „occ db:convert-type“ oder konsultieren Sie die <a target=\"_blank\" href=\"%s\">Dokumentation ↗</a>.", @@ -185,8 +186,6 @@ "Improving the config.php" : "Die config.php optimieren", "Theming" : "Themes verwenden", "Hardening and security guidance" : "Systemhärtung und Sicherheitsempfehlungen", - "Transactional File Locking is enabled." : "Dateisperren bei Transaktionen sind aktiviert.", - "Transactional File Locking is disabled." : "Dateisperren bei Transaktionen sind deaktiviert.", "Version" : "Version", "Developer documentation" : "Dokumentation für Entwickler", "Experimental applications ahead" : "Experimentelle Apps nachfolgend", @@ -234,7 +233,6 @@ "Upload new" : "Neues hochladen", "Select new from Files" : "Neues aus Dateien wählen", "Remove image" : "Bild entfernen", - "Either png or jpg. Ideally square but you will be able to crop it." : "Entweder PNG oder JPG. Im Idealfall quadratisch, Sie können es aber beschneiden.", "Your avatar is provided by your original account." : "Ihr Avatar wird von Ihrem Ursprungskonto verwendet.", "Cancel" : "Abbrechen", "Choose as profile image" : "Als Profilbild wählen", diff --git a/settings/l10n/el.js b/settings/l10n/el.js index cb425992190..9c7cf9afd4b 100644 --- a/settings/l10n/el.js +++ b/settings/l10n/el.js @@ -10,7 +10,6 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "Διακομιστής Email", "Log" : "Καταγραφές", - "Server Status" : "Κατάσταση διακομιστή", "Tips & tricks" : "Συμβουλές & τεχνάσματα", "Updates" : "Ενημερώσεις", "Authentication error" : "Σφάλμα πιστοποίησης", @@ -178,6 +177,8 @@ OC.L10N.register( "More" : "Περισσότερα", "Less" : "Λιγότερα", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Το αρχείο ιστορικού είναι μεγαλύτερο από 100ΜΒ. Η λήψη του ίσως πάρει λίγη ώρα!", + "Transactional File Locking is enabled." : "Το μεταβατικό κλείδωμα αρχείων είναι ενεργοποιημένο.", + "Transactional File Locking is disabled." : "Το μεταβατικό κλείδωμα αρχείων είναι απενεργοποιημένο.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "Ως βάση δεδομένων χρησιμοποιείται η SQLite. Για μεγαλύτερες εγκαταστάσεις συνιστούμε να επιλέξετε ένα διαφορετικό σύστημα υποστήριξης βάσης δεδομένων.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Ειδικά όταν χρησιμοποιείτε τον πελάτη για συγχρονισμό στον υπολογιστή σας, δεν συνίσταται η χρήση της SQLite.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Για να μετακινηθείτε σε άλλη βάση δεδομένων χρησιμοποιήσετε το εργαλείο στη γραμμή εντολών:'occ db:convert-type', ή ανατρέξτε <a target=\"_blank\" href=\"%s\"> στις οδηγίες ↗ </a>.", @@ -187,8 +188,6 @@ OC.L10N.register( "Improving the config.php" : "Βελτίωση του config.php", "Theming" : "Θέματα", "Hardening and security guidance" : "Οδηγίες ασφάλειας και θωράκισης", - "Transactional File Locking is enabled." : "Το μεταβατικό κλείδωμα αρχείων είναι ενεργοποιημένο.", - "Transactional File Locking is disabled." : "Το μεταβατικό κλείδωμα αρχείων είναι απενεργοποιημένο.", "Version" : "Έκδοση", "Developer documentation" : "Τεκμηρίωση προγραμματιστή", "Experimental applications ahead" : "Πειραματικές εφαρμογές", @@ -235,7 +234,6 @@ OC.L10N.register( "Upload new" : "Μεταφόρτωση νέου", "Select new from Files" : "Επιλογή νέου από τα Αρχεία", "Remove image" : "Αφαίρεση εικόνας", - "Either png or jpg. Ideally square but you will be able to crop it." : "Είτε png ή jpg. Ιδανικά τετράγωνη αλλά θα είστε σε θέση να την περικόψετε.", "Your avatar is provided by your original account." : "Το άβατάρ σας παρέχεται από τον αρχικό σας λογαριασμό.", "Cancel" : "Άκυρο", "Choose as profile image" : "Επιλογή εικόνας προφίλ", diff --git a/settings/l10n/el.json b/settings/l10n/el.json index 962d6f3f60b..5c08bef36d0 100644 --- a/settings/l10n/el.json +++ b/settings/l10n/el.json @@ -8,7 +8,6 @@ "Cron" : "Cron", "Email server" : "Διακομιστής Email", "Log" : "Καταγραφές", - "Server Status" : "Κατάσταση διακομιστή", "Tips & tricks" : "Συμβουλές & τεχνάσματα", "Updates" : "Ενημερώσεις", "Authentication error" : "Σφάλμα πιστοποίησης", @@ -176,6 +175,8 @@ "More" : "Περισσότερα", "Less" : "Λιγότερα", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Το αρχείο ιστορικού είναι μεγαλύτερο από 100ΜΒ. Η λήψη του ίσως πάρει λίγη ώρα!", + "Transactional File Locking is enabled." : "Το μεταβατικό κλείδωμα αρχείων είναι ενεργοποιημένο.", + "Transactional File Locking is disabled." : "Το μεταβατικό κλείδωμα αρχείων είναι απενεργοποιημένο.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "Ως βάση δεδομένων χρησιμοποιείται η SQLite. Για μεγαλύτερες εγκαταστάσεις συνιστούμε να επιλέξετε ένα διαφορετικό σύστημα υποστήριξης βάσης δεδομένων.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Ειδικά όταν χρησιμοποιείτε τον πελάτη για συγχρονισμό στον υπολογιστή σας, δεν συνίσταται η χρήση της SQLite.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Για να μετακινηθείτε σε άλλη βάση δεδομένων χρησιμοποιήσετε το εργαλείο στη γραμμή εντολών:'occ db:convert-type', ή ανατρέξτε <a target=\"_blank\" href=\"%s\"> στις οδηγίες ↗ </a>.", @@ -185,8 +186,6 @@ "Improving the config.php" : "Βελτίωση του config.php", "Theming" : "Θέματα", "Hardening and security guidance" : "Οδηγίες ασφάλειας και θωράκισης", - "Transactional File Locking is enabled." : "Το μεταβατικό κλείδωμα αρχείων είναι ενεργοποιημένο.", - "Transactional File Locking is disabled." : "Το μεταβατικό κλείδωμα αρχείων είναι απενεργοποιημένο.", "Version" : "Έκδοση", "Developer documentation" : "Τεκμηρίωση προγραμματιστή", "Experimental applications ahead" : "Πειραματικές εφαρμογές", @@ -233,7 +232,6 @@ "Upload new" : "Μεταφόρτωση νέου", "Select new from Files" : "Επιλογή νέου από τα Αρχεία", "Remove image" : "Αφαίρεση εικόνας", - "Either png or jpg. Ideally square but you will be able to crop it." : "Είτε png ή jpg. Ιδανικά τετράγωνη αλλά θα είστε σε θέση να την περικόψετε.", "Your avatar is provided by your original account." : "Το άβατάρ σας παρέχεται από τον αρχικό σας λογαριασμό.", "Cancel" : "Άκυρο", "Choose as profile image" : "Επιλογή εικόνας προφίλ", diff --git a/settings/l10n/en_GB.js b/settings/l10n/en_GB.js index da437fa2a25..a64b8a8bd0a 100644 --- a/settings/l10n/en_GB.js +++ b/settings/l10n/en_GB.js @@ -8,7 +8,6 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "Email server", "Log" : "Log", - "Server Status" : "Server Status", "Tips & tricks" : "Tips & tricks", "Updates" : "Updates", "Authentication error" : "Authentication error", @@ -229,7 +228,6 @@ OC.L10N.register( "Upload new" : "Upload new", "Select new from Files" : "Select new from Files", "Remove image" : "Remove image", - "Either png or jpg. Ideally square but you will be able to crop it." : "Either png or jpg. Ideally square but you will be able to crop it.", "Your avatar is provided by your original account." : "Your avatar is provided by your original account.", "Cancel" : "Cancel", "Choose as profile image" : "Choose as profile image", diff --git a/settings/l10n/en_GB.json b/settings/l10n/en_GB.json index 6983ecaac1c..ca340dce2ee 100644 --- a/settings/l10n/en_GB.json +++ b/settings/l10n/en_GB.json @@ -6,7 +6,6 @@ "Cron" : "Cron", "Email server" : "Email server", "Log" : "Log", - "Server Status" : "Server Status", "Tips & tricks" : "Tips & tricks", "Updates" : "Updates", "Authentication error" : "Authentication error", @@ -227,7 +226,6 @@ "Upload new" : "Upload new", "Select new from Files" : "Select new from Files", "Remove image" : "Remove image", - "Either png or jpg. Ideally square but you will be able to crop it." : "Either png or jpg. Ideally square but you will be able to crop it.", "Your avatar is provided by your original account." : "Your avatar is provided by your original account.", "Cancel" : "Cancel", "Choose as profile image" : "Choose as profile image", diff --git a/settings/l10n/eo.js b/settings/l10n/eo.js index 4feb4c4b835..196fcff74d2 100644 --- a/settings/l10n/eo.js +++ b/settings/l10n/eo.js @@ -101,7 +101,6 @@ OC.L10N.register( "Upload new" : "Alŝuti novan", "Select new from Files" : "Elekti novan el dosieroj", "Remove image" : "Forigi bildon", - "Either png or jpg. Ideally square but you will be able to crop it." : "Aŭ PNG aŭ JPG. Prefere ĝi kvadratu, sed vi povos stuci ĝin.", "Cancel" : "Nuligi", "Choose as profile image" : "Elekti kiel profilan bildon", "Language" : "Lingvo", diff --git a/settings/l10n/eo.json b/settings/l10n/eo.json index d0e71295ff0..791dca9b499 100644 --- a/settings/l10n/eo.json +++ b/settings/l10n/eo.json @@ -99,7 +99,6 @@ "Upload new" : "Alŝuti novan", "Select new from Files" : "Elekti novan el dosieroj", "Remove image" : "Forigi bildon", - "Either png or jpg. Ideally square but you will be able to crop it." : "Aŭ PNG aŭ JPG. Prefere ĝi kvadratu, sed vi povos stuci ĝin.", "Cancel" : "Nuligi", "Choose as profile image" : "Elekti kiel profilan bildon", "Language" : "Lingvo", diff --git a/settings/l10n/es.js b/settings/l10n/es.js index f6ad23bfe92..e8836f169c9 100644 --- a/settings/l10n/es.js +++ b/settings/l10n/es.js @@ -10,7 +10,7 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "Servidor de correo electrónico", "Log" : "Registro", - "Server Status" : "Estado del servidor", + "Server status" : "Estado del servidor", "Tips & tricks" : "Sugerencias y trucos", "Updates" : "Actualizaciones", "Authentication error" : "Error de autenticación", @@ -31,7 +31,9 @@ OC.L10N.register( "Unable to change password" : "No se ha podido cambiar la contraseña", "Enabled" : "Habilitado", "Not enabled" : "No habilitado", + "installing and updating apps via the app store or Federated Cloud Sharing" : "instalando y actualizando aplicaciones via app store o Nube compartida Federada", "Federated Cloud Sharing" : "Compartido en Cloud Federado", + "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL está usando una versión desactualizada %s (%s). Por favor, actualice su sistema operativo o funciones tales como %s no funcionará de forma fiable.", "A problem occurred, please check your log files (Error: %s)" : "Ocurrió un problema, por favor verifique los archivos de registro (Error: %s)", "Migration Completed" : "Migración finalizada", "Group already exists." : "El grupo ya existe.", @@ -76,6 +78,7 @@ OC.L10N.register( "Uninstalling ...." : "Desinstalando...", "Error while uninstalling app" : "Error al desinstalar la aplicación", "Uninstall" : "Desinstalar", + "An error occurred: {message}" : "Ocurrió un error: {message}", "Select a profile picture" : "Seleccionar una imagen de perfil", "Very weak password" : "Contraseña muy débil", "Weak password" : "Contraseña débil", @@ -178,6 +181,8 @@ OC.L10N.register( "More" : "Más", "Less" : "Menos", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "El archivo de registro es mayor de 100 MB. Descargarlo puede tardar.", + "Transactional File Locking is enabled." : "Bloqueo de archivos transaccional está habilitado.", + "Transactional File Locking is disabled." : "Bloqueo de archivos transaccional está deshabilitado.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "Se utiliza SQLite como base de datos. Para instalaciones mas grandes se recomiende cambiar a otro sistema de base de datos. ", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "El uso de SQLite está desaconsejado especialmente cuando se usa el cliente de escritorio para sincronizar los ficheros.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Para migrar a otra base de datos use la herramienta de línea de comandos: 'occ db:convert-type', o consulte la <a target=\"_blank\" href=\"%s\">documentación ↗</a>.", @@ -187,8 +192,6 @@ OC.L10N.register( "Improving the config.php" : "Mejorar el config.php", "Theming" : "Personalizar el tema", "Hardening and security guidance" : "Guía de protección y seguridad", - "Transactional File Locking is enabled." : "Bloqueo de archivos transaccional está habilitado.", - "Transactional File Locking is disabled." : "Bloqueo de archivos transaccional está deshabilitado.", "Version" : "Versión", "Developer documentation" : "Documentación de desarrollador", "Experimental applications ahead" : "Aplicaciones experimentales más adelante", @@ -236,7 +239,7 @@ OC.L10N.register( "Upload new" : "Subir otra", "Select new from Files" : "Seleccionar otra desde Archivos", "Remove image" : "Borrar imagen", - "Either png or jpg. Ideally square but you will be able to crop it." : "Archivo PNG o JPG. Preferiblemente cuadrado, pero tendrás la posibilidad de recortarlo.", + "Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB." : "Sea en png o jpg. Preferiblemente que su forma sea cuadrada, pero tendrá la posibilidad de recortarlo. El archivo no puede exceder los 20 MB.", "Your avatar is provided by your original account." : "Su avatar es proporcionado por su cuenta original.", "Cancel" : "Cancelar", "Choose as profile image" : "Seleccionar como imagen de perfil", diff --git a/settings/l10n/es.json b/settings/l10n/es.json index 79139bc4cbd..69573d70a10 100644 --- a/settings/l10n/es.json +++ b/settings/l10n/es.json @@ -8,7 +8,7 @@ "Cron" : "Cron", "Email server" : "Servidor de correo electrónico", "Log" : "Registro", - "Server Status" : "Estado del servidor", + "Server status" : "Estado del servidor", "Tips & tricks" : "Sugerencias y trucos", "Updates" : "Actualizaciones", "Authentication error" : "Error de autenticación", @@ -29,7 +29,9 @@ "Unable to change password" : "No se ha podido cambiar la contraseña", "Enabled" : "Habilitado", "Not enabled" : "No habilitado", + "installing and updating apps via the app store or Federated Cloud Sharing" : "instalando y actualizando aplicaciones via app store o Nube compartida Federada", "Federated Cloud Sharing" : "Compartido en Cloud Federado", + "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL está usando una versión desactualizada %s (%s). Por favor, actualice su sistema operativo o funciones tales como %s no funcionará de forma fiable.", "A problem occurred, please check your log files (Error: %s)" : "Ocurrió un problema, por favor verifique los archivos de registro (Error: %s)", "Migration Completed" : "Migración finalizada", "Group already exists." : "El grupo ya existe.", @@ -74,6 +76,7 @@ "Uninstalling ...." : "Desinstalando...", "Error while uninstalling app" : "Error al desinstalar la aplicación", "Uninstall" : "Desinstalar", + "An error occurred: {message}" : "Ocurrió un error: {message}", "Select a profile picture" : "Seleccionar una imagen de perfil", "Very weak password" : "Contraseña muy débil", "Weak password" : "Contraseña débil", @@ -176,6 +179,8 @@ "More" : "Más", "Less" : "Menos", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "El archivo de registro es mayor de 100 MB. Descargarlo puede tardar.", + "Transactional File Locking is enabled." : "Bloqueo de archivos transaccional está habilitado.", + "Transactional File Locking is disabled." : "Bloqueo de archivos transaccional está deshabilitado.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "Se utiliza SQLite como base de datos. Para instalaciones mas grandes se recomiende cambiar a otro sistema de base de datos. ", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "El uso de SQLite está desaconsejado especialmente cuando se usa el cliente de escritorio para sincronizar los ficheros.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Para migrar a otra base de datos use la herramienta de línea de comandos: 'occ db:convert-type', o consulte la <a target=\"_blank\" href=\"%s\">documentación ↗</a>.", @@ -185,8 +190,6 @@ "Improving the config.php" : "Mejorar el config.php", "Theming" : "Personalizar el tema", "Hardening and security guidance" : "Guía de protección y seguridad", - "Transactional File Locking is enabled." : "Bloqueo de archivos transaccional está habilitado.", - "Transactional File Locking is disabled." : "Bloqueo de archivos transaccional está deshabilitado.", "Version" : "Versión", "Developer documentation" : "Documentación de desarrollador", "Experimental applications ahead" : "Aplicaciones experimentales más adelante", @@ -234,7 +237,7 @@ "Upload new" : "Subir otra", "Select new from Files" : "Seleccionar otra desde Archivos", "Remove image" : "Borrar imagen", - "Either png or jpg. Ideally square but you will be able to crop it." : "Archivo PNG o JPG. Preferiblemente cuadrado, pero tendrás la posibilidad de recortarlo.", + "Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB." : "Sea en png o jpg. Preferiblemente que su forma sea cuadrada, pero tendrá la posibilidad de recortarlo. El archivo no puede exceder los 20 MB.", "Your avatar is provided by your original account." : "Su avatar es proporcionado por su cuenta original.", "Cancel" : "Cancelar", "Choose as profile image" : "Seleccionar como imagen de perfil", diff --git a/settings/l10n/es_AR.js b/settings/l10n/es_AR.js index dbcf81e88ce..fa4a506fab0 100644 --- a/settings/l10n/es_AR.js +++ b/settings/l10n/es_AR.js @@ -108,7 +108,6 @@ OC.L10N.register( "Upload new" : "Subir nuevo", "Select new from Files" : "Seleccionar nuevo desde archivos", "Remove image" : "Remover imagen", - "Either png or jpg. Ideally square but you will be able to crop it." : "Sólo png o jpg. Lo ideal que sea cuadrada sino luego podrás recortarlo.", "Your avatar is provided by your original account." : "Su avatar es proveído por su cuenta original.", "Cancel" : "Cancelar", "Choose as profile image" : "Elegir como imagen de perfil", diff --git a/settings/l10n/es_AR.json b/settings/l10n/es_AR.json index 45249889091..e8098e575c5 100644 --- a/settings/l10n/es_AR.json +++ b/settings/l10n/es_AR.json @@ -106,7 +106,6 @@ "Upload new" : "Subir nuevo", "Select new from Files" : "Seleccionar nuevo desde archivos", "Remove image" : "Remover imagen", - "Either png or jpg. Ideally square but you will be able to crop it." : "Sólo png o jpg. Lo ideal que sea cuadrada sino luego podrás recortarlo.", "Your avatar is provided by your original account." : "Su avatar es proveído por su cuenta original.", "Cancel" : "Cancelar", "Choose as profile image" : "Elegir como imagen de perfil", diff --git a/settings/l10n/es_MX.js b/settings/l10n/es_MX.js index 29b7706b6f2..0453a78f842 100644 --- a/settings/l10n/es_MX.js +++ b/settings/l10n/es_MX.js @@ -80,7 +80,6 @@ OC.L10N.register( "Upload new" : "Subir otra", "Select new from Files" : "Seleccionar otra desde Archivos", "Remove image" : "Borrar imagen", - "Either png or jpg. Ideally square but you will be able to crop it." : "Archivo PNG o JPG. Preferiblemente cuadrado, pero tendrás la posibilidad de recortarlo.", "Your avatar is provided by your original account." : "Su avatar es proporcionado por su cuenta original.", "Cancel" : "Cancelar", "Choose as profile image" : "Seleccionar como imagen de perfil", diff --git a/settings/l10n/es_MX.json b/settings/l10n/es_MX.json index 6e163f2c629..5bba876327a 100644 --- a/settings/l10n/es_MX.json +++ b/settings/l10n/es_MX.json @@ -78,7 +78,6 @@ "Upload new" : "Subir otra", "Select new from Files" : "Seleccionar otra desde Archivos", "Remove image" : "Borrar imagen", - "Either png or jpg. Ideally square but you will be able to crop it." : "Archivo PNG o JPG. Preferiblemente cuadrado, pero tendrás la posibilidad de recortarlo.", "Your avatar is provided by your original account." : "Su avatar es proporcionado por su cuenta original.", "Cancel" : "Cancelar", "Choose as profile image" : "Seleccionar como imagen de perfil", diff --git a/settings/l10n/et_EE.js b/settings/l10n/et_EE.js index 8ff9a761956..f150878c1a2 100644 --- a/settings/l10n/et_EE.js +++ b/settings/l10n/et_EE.js @@ -10,7 +10,6 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "E-kirjade server", "Log" : "Logi", - "Server Status" : "Serveri olek", "Tips & tricks" : "Nõuanded ja trikid", "Updates" : "Uuendused", "Authentication error" : "Autentimise viga", @@ -198,7 +197,6 @@ OC.L10N.register( "Upload new" : "Laadi uus üles", "Select new from Files" : "Vali failidest uus", "Remove image" : "Eemalda pilt", - "Either png or jpg. Ideally square but you will be able to crop it." : "Kas png või jpg. Võimalikult ruudukujuline, kuid sul on võimalus seda veel lõigata.", "Your avatar is provided by your original account." : "Sinu avatari pakub sinu algne konto.", "Cancel" : "Loobu", "Choose as profile image" : "Vali profiilipildiks", diff --git a/settings/l10n/et_EE.json b/settings/l10n/et_EE.json index 7281a11573c..91badcd9fef 100644 --- a/settings/l10n/et_EE.json +++ b/settings/l10n/et_EE.json @@ -8,7 +8,6 @@ "Cron" : "Cron", "Email server" : "E-kirjade server", "Log" : "Logi", - "Server Status" : "Serveri olek", "Tips & tricks" : "Nõuanded ja trikid", "Updates" : "Uuendused", "Authentication error" : "Autentimise viga", @@ -196,7 +195,6 @@ "Upload new" : "Laadi uus üles", "Select new from Files" : "Vali failidest uus", "Remove image" : "Eemalda pilt", - "Either png or jpg. Ideally square but you will be able to crop it." : "Kas png või jpg. Võimalikult ruudukujuline, kuid sul on võimalus seda veel lõigata.", "Your avatar is provided by your original account." : "Sinu avatari pakub sinu algne konto.", "Cancel" : "Loobu", "Choose as profile image" : "Vali profiilipildiks", diff --git a/settings/l10n/eu.js b/settings/l10n/eu.js index 94ea7e6312a..9075fe73299 100644 --- a/settings/l10n/eu.js +++ b/settings/l10n/eu.js @@ -169,7 +169,6 @@ OC.L10N.register( "Upload new" : "Igo berria", "Select new from Files" : "Hautatu berria Fitxategietatik", "Remove image" : "Irudia ezabatu", - "Either png or jpg. Ideally square but you will be able to crop it." : "png edo jpg. Hobe karratua baina mozteko aukera izango duzu.", "Your avatar is provided by your original account." : "Zure jatorrizko kontuak ezarri du zure avatar.", "Cancel" : "Ezeztatu", "Choose as profile image" : "Profil irudi bezala aukeratu", diff --git a/settings/l10n/eu.json b/settings/l10n/eu.json index ce14381a46c..631adb886cb 100644 --- a/settings/l10n/eu.json +++ b/settings/l10n/eu.json @@ -167,7 +167,6 @@ "Upload new" : "Igo berria", "Select new from Files" : "Hautatu berria Fitxategietatik", "Remove image" : "Irudia ezabatu", - "Either png or jpg. Ideally square but you will be able to crop it." : "png edo jpg. Hobe karratua baina mozteko aukera izango duzu.", "Your avatar is provided by your original account." : "Zure jatorrizko kontuak ezarri du zure avatar.", "Cancel" : "Ezeztatu", "Choose as profile image" : "Profil irudi bezala aukeratu", diff --git a/settings/l10n/fa.js b/settings/l10n/fa.js index 15dc40a68b0..ca8d2b41081 100644 --- a/settings/l10n/fa.js +++ b/settings/l10n/fa.js @@ -123,7 +123,6 @@ OC.L10N.register( "Upload new" : "بارگذاری جدید", "Select new from Files" : "انتخاب جدید از میان فایل ها", "Remove image" : "تصویر پاک شود", - "Either png or jpg. Ideally square but you will be able to crop it." : "هردوی jpg و png ها مربع گونه میباشند. با این حال شما میتوانید آنها را برش بزنید.", "Cancel" : "منصرف شدن", "Choose as profile image" : "یک تصویر پروفایل انتخاب کنید", "Language" : "زبان", diff --git a/settings/l10n/fa.json b/settings/l10n/fa.json index 56d135690fc..9dc390ea8cc 100644 --- a/settings/l10n/fa.json +++ b/settings/l10n/fa.json @@ -121,7 +121,6 @@ "Upload new" : "بارگذاری جدید", "Select new from Files" : "انتخاب جدید از میان فایل ها", "Remove image" : "تصویر پاک شود", - "Either png or jpg. Ideally square but you will be able to crop it." : "هردوی jpg و png ها مربع گونه میباشند. با این حال شما میتوانید آنها را برش بزنید.", "Cancel" : "منصرف شدن", "Choose as profile image" : "یک تصویر پروفایل انتخاب کنید", "Language" : "زبان", diff --git a/settings/l10n/fi_FI.js b/settings/l10n/fi_FI.js index c5399c6149f..34baad85135 100644 --- a/settings/l10n/fi_FI.js +++ b/settings/l10n/fi_FI.js @@ -10,7 +10,7 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "Sähköpostipalvelin", "Log" : "Loki", - "Server Status" : "Palvelimen tila", + "Server status" : "Palvelimen tila", "Tips & tricks" : "Vinkit", "Updates" : "Päivitykset", "Authentication error" : "Tunnistautumisvirhe", @@ -32,6 +32,7 @@ OC.L10N.register( "Enabled" : "Käytössä", "Not enabled" : "Ei käytössä", "Federated Cloud Sharing" : "Federoitu pilvijakaminen", + "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL käyttää vanhentunutta %s-versiota (%s). Päivitä käyttöjärjestelmäsi tai ominaisuudet kuten %s eivät toimi luotettavasti.", "A problem occurred, please check your log files (Error: %s)" : "Tapahtui virhe, tarkista lokitiedostot (Virhe: %s)", "Migration Completed" : "Migraatio valmistui", "Group already exists." : "Ryhmä on jo olemassa.", @@ -76,6 +77,7 @@ OC.L10N.register( "Uninstalling ...." : "Poistetaan asennusta....", "Error while uninstalling app" : "Virhe sovellusta poistaessa", "Uninstall" : "Poista asennus", + "An error occurred: {message}" : "Tapahtui virhe: {message}", "Select a profile picture" : "Valitse profiilikuva", "Very weak password" : "Erittäin heikko salasana", "Weak password" : "Heikko salasana", @@ -169,6 +171,8 @@ OC.L10N.register( "More" : "Enemmän", "Less" : "Vähemmän", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Lokitiedosto on kooltaan yli 100 megatavua. Sen lataaminen saattaa kestää hetken.", + "Transactional File Locking is enabled." : "Transaktiopohjainen tiedostolukitus käytössä.", + "Transactional File Locking is disabled." : "Transaktiopohjainen tiedostolukitus on pois käytöstä.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLitea käytetään tietokantana. Suuria asennuksia varten on suositeltavaa vaihtaa muuhun tietokantaan.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Varsinkin työpöytäsovelluksen tiedostosynkronointia käyttäessä SQLiten käyttö ei ole suositeltavaa.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Käytä komentorivityökalua toiseen tietokantaan migraation yhteydessä: 'occ db:convert-type', tai <a target=\"_blank\" href=\"%s\">lue toki myös dokumentaatio ↗</a>.", @@ -178,8 +182,6 @@ OC.L10N.register( "Improving the config.php" : "Config.php-tiedoston parantaminen", "Theming" : "Teemojen käyttö", "Hardening and security guidance" : "Turvaamis- ja tietoturvaopas", - "Transactional File Locking is enabled." : "Transaktiopohjainen tiedostolukitus käytössä.", - "Transactional File Locking is disabled." : "Transaktiopohjainen tiedostolukitus on pois käytöstä.", "Version" : "Versio", "Developer documentation" : "Kehittäjädokumentaatio", "Experimental applications ahead" : "Kokeellisia sovelluksia edessä", @@ -227,7 +229,6 @@ OC.L10N.register( "Upload new" : "Lähetä uusi", "Select new from Files" : "Valitse uusi tiedostoista", "Remove image" : "Poista kuva", - "Either png or jpg. Ideally square but you will be able to crop it." : "Joko png- tai jpg-kuva. Mieluiten neliö, voit kuitenkin rajata kuvaa.", "Your avatar is provided by your original account." : "Avatar-kuvasi pohjautuu alkuperäiseen tiliisi.", "Cancel" : "Peru", "Choose as profile image" : "Valitse profiilikuvaksi", diff --git a/settings/l10n/fi_FI.json b/settings/l10n/fi_FI.json index cd20309e2b7..a0c610c94a8 100644 --- a/settings/l10n/fi_FI.json +++ b/settings/l10n/fi_FI.json @@ -8,7 +8,7 @@ "Cron" : "Cron", "Email server" : "Sähköpostipalvelin", "Log" : "Loki", - "Server Status" : "Palvelimen tila", + "Server status" : "Palvelimen tila", "Tips & tricks" : "Vinkit", "Updates" : "Päivitykset", "Authentication error" : "Tunnistautumisvirhe", @@ -30,6 +30,7 @@ "Enabled" : "Käytössä", "Not enabled" : "Ei käytössä", "Federated Cloud Sharing" : "Federoitu pilvijakaminen", + "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL käyttää vanhentunutta %s-versiota (%s). Päivitä käyttöjärjestelmäsi tai ominaisuudet kuten %s eivät toimi luotettavasti.", "A problem occurred, please check your log files (Error: %s)" : "Tapahtui virhe, tarkista lokitiedostot (Virhe: %s)", "Migration Completed" : "Migraatio valmistui", "Group already exists." : "Ryhmä on jo olemassa.", @@ -74,6 +75,7 @@ "Uninstalling ...." : "Poistetaan asennusta....", "Error while uninstalling app" : "Virhe sovellusta poistaessa", "Uninstall" : "Poista asennus", + "An error occurred: {message}" : "Tapahtui virhe: {message}", "Select a profile picture" : "Valitse profiilikuva", "Very weak password" : "Erittäin heikko salasana", "Weak password" : "Heikko salasana", @@ -167,6 +169,8 @@ "More" : "Enemmän", "Less" : "Vähemmän", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Lokitiedosto on kooltaan yli 100 megatavua. Sen lataaminen saattaa kestää hetken.", + "Transactional File Locking is enabled." : "Transaktiopohjainen tiedostolukitus käytössä.", + "Transactional File Locking is disabled." : "Transaktiopohjainen tiedostolukitus on pois käytöstä.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLitea käytetään tietokantana. Suuria asennuksia varten on suositeltavaa vaihtaa muuhun tietokantaan.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Varsinkin työpöytäsovelluksen tiedostosynkronointia käyttäessä SQLiten käyttö ei ole suositeltavaa.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Käytä komentorivityökalua toiseen tietokantaan migraation yhteydessä: 'occ db:convert-type', tai <a target=\"_blank\" href=\"%s\">lue toki myös dokumentaatio ↗</a>.", @@ -176,8 +180,6 @@ "Improving the config.php" : "Config.php-tiedoston parantaminen", "Theming" : "Teemojen käyttö", "Hardening and security guidance" : "Turvaamis- ja tietoturvaopas", - "Transactional File Locking is enabled." : "Transaktiopohjainen tiedostolukitus käytössä.", - "Transactional File Locking is disabled." : "Transaktiopohjainen tiedostolukitus on pois käytöstä.", "Version" : "Versio", "Developer documentation" : "Kehittäjädokumentaatio", "Experimental applications ahead" : "Kokeellisia sovelluksia edessä", @@ -225,7 +227,6 @@ "Upload new" : "Lähetä uusi", "Select new from Files" : "Valitse uusi tiedostoista", "Remove image" : "Poista kuva", - "Either png or jpg. Ideally square but you will be able to crop it." : "Joko png- tai jpg-kuva. Mieluiten neliö, voit kuitenkin rajata kuvaa.", "Your avatar is provided by your original account." : "Avatar-kuvasi pohjautuu alkuperäiseen tiliisi.", "Cancel" : "Peru", "Choose as profile image" : "Valitse profiilikuvaksi", diff --git a/settings/l10n/fr.js b/settings/l10n/fr.js index 2cbde857389..5c772570ba0 100644 --- a/settings/l10n/fr.js +++ b/settings/l10n/fr.js @@ -10,7 +10,6 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "Serveur e-mail", "Log" : "Log", - "Server Status" : "Statut du serveur", "Tips & tricks" : "Trucs et astuces", "Updates" : "Mises à jour", "Authentication error" : "Erreur d'authentification", @@ -31,7 +30,9 @@ OC.L10N.register( "Unable to change password" : "Impossible de modifier le mot de passe", "Enabled" : "Activées", "Not enabled" : "Désactivées", + "installing and updating apps via the app store or Federated Cloud Sharing" : "le partage Federated Cloud ou l'installation et la mise à jour d'applications par l'app store", "Federated Cloud Sharing" : "Federated Cloud Sharing", + "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL utilise %s %s, qui est une version obsolète. Veuillez mettre à jour votre système d'exploitation, ou des fonctionnalités telles que %s ne fonctionneront pas correctement.", "A problem occurred, please check your log files (Error: %s)" : "Une erreur est survenue, veuillez vérifier vos fichiers de log (Erreur: %s)", "Migration Completed" : "Migration terminée", "Group already exists." : "Ce groupe existe déjà.", @@ -178,6 +179,8 @@ OC.L10N.register( "More" : "Plus", "Less" : "Moins", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "La taille du fichier journal excède 100 Mo. Le télécharger peut prendre un certain temps!", + "Transactional File Locking is enabled." : "Le verrouillage transactionnel de fichiers est activé", + "Transactional File Locking is disabled." : "Le verrouillage transactionnel de fichiers est désactivé", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite est actuellement utilisé comme gestionnaire de base de données. Pour des installations plus volumineuses, nous vous conseillons d'utiliser un autre gestionnaire de base de données.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "L'utilisation de SQLite est particulièrement déconseillée si vous utilisez le client de bureau pour synchroniser vos données.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Pour migrer vers un autre type de base de données, utilisez la ligne de commande : 'occ db:convert-type' ou consultez la <a target=\"_blank\" href=\"%s\">documentation ↗</a>.", @@ -187,8 +190,6 @@ OC.L10N.register( "Improving the config.php" : "Amélioration du config.php ", "Theming" : "Personnalisation de l'apparence", "Hardening and security guidance" : "Guide pour le renforcement et la sécurité", - "Transactional File Locking is enabled." : "Le verrouillage transactionnel de fichiers est activé", - "Transactional File Locking is disabled." : "Le verrouillage transactionnel de fichiers est désactivé", "Version" : "Version", "Developer documentation" : "Documentation pour développeurs", "Experimental applications ahead" : "Attention! Applications expérimentales", @@ -235,7 +236,6 @@ OC.L10N.register( "Upload new" : "Nouvelle depuis votre ordinateur", "Select new from Files" : "Nouvelle depuis les Fichiers", "Remove image" : "Supprimer l'image", - "Either png or jpg. Ideally square but you will be able to crop it." : "Format png ou jpg. Idéalement carrée, mais vous pourrez la recadrer.", "Your avatar is provided by your original account." : "Votre avatar est fourni par votre compte original.", "Cancel" : "Annuler", "Choose as profile image" : "Choisir en tant que photo de profil ", diff --git a/settings/l10n/fr.json b/settings/l10n/fr.json index 85a45b36867..62a39a64852 100644 --- a/settings/l10n/fr.json +++ b/settings/l10n/fr.json @@ -8,7 +8,6 @@ "Cron" : "Cron", "Email server" : "Serveur e-mail", "Log" : "Log", - "Server Status" : "Statut du serveur", "Tips & tricks" : "Trucs et astuces", "Updates" : "Mises à jour", "Authentication error" : "Erreur d'authentification", @@ -29,7 +28,9 @@ "Unable to change password" : "Impossible de modifier le mot de passe", "Enabled" : "Activées", "Not enabled" : "Désactivées", + "installing and updating apps via the app store or Federated Cloud Sharing" : "le partage Federated Cloud ou l'installation et la mise à jour d'applications par l'app store", "Federated Cloud Sharing" : "Federated Cloud Sharing", + "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL utilise %s %s, qui est une version obsolète. Veuillez mettre à jour votre système d'exploitation, ou des fonctionnalités telles que %s ne fonctionneront pas correctement.", "A problem occurred, please check your log files (Error: %s)" : "Une erreur est survenue, veuillez vérifier vos fichiers de log (Erreur: %s)", "Migration Completed" : "Migration terminée", "Group already exists." : "Ce groupe existe déjà.", @@ -176,6 +177,8 @@ "More" : "Plus", "Less" : "Moins", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "La taille du fichier journal excède 100 Mo. Le télécharger peut prendre un certain temps!", + "Transactional File Locking is enabled." : "Le verrouillage transactionnel de fichiers est activé", + "Transactional File Locking is disabled." : "Le verrouillage transactionnel de fichiers est désactivé", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite est actuellement utilisé comme gestionnaire de base de données. Pour des installations plus volumineuses, nous vous conseillons d'utiliser un autre gestionnaire de base de données.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "L'utilisation de SQLite est particulièrement déconseillée si vous utilisez le client de bureau pour synchroniser vos données.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Pour migrer vers un autre type de base de données, utilisez la ligne de commande : 'occ db:convert-type' ou consultez la <a target=\"_blank\" href=\"%s\">documentation ↗</a>.", @@ -185,8 +188,6 @@ "Improving the config.php" : "Amélioration du config.php ", "Theming" : "Personnalisation de l'apparence", "Hardening and security guidance" : "Guide pour le renforcement et la sécurité", - "Transactional File Locking is enabled." : "Le verrouillage transactionnel de fichiers est activé", - "Transactional File Locking is disabled." : "Le verrouillage transactionnel de fichiers est désactivé", "Version" : "Version", "Developer documentation" : "Documentation pour développeurs", "Experimental applications ahead" : "Attention! Applications expérimentales", @@ -233,7 +234,6 @@ "Upload new" : "Nouvelle depuis votre ordinateur", "Select new from Files" : "Nouvelle depuis les Fichiers", "Remove image" : "Supprimer l'image", - "Either png or jpg. Ideally square but you will be able to crop it." : "Format png ou jpg. Idéalement carrée, mais vous pourrez la recadrer.", "Your avatar is provided by your original account." : "Votre avatar est fourni par votre compte original.", "Cancel" : "Annuler", "Choose as profile image" : "Choisir en tant que photo de profil ", diff --git a/settings/l10n/gl.js b/settings/l10n/gl.js index b128436b82b..34bc57bfbf2 100644 --- a/settings/l10n/gl.js +++ b/settings/l10n/gl.js @@ -10,7 +10,7 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "Servidor de correo", "Log" : "Rexistro", - "Server Status" : "Estado do servidor", + "Server status" : "Estado do servidor", "Tips & tricks" : "Trucos e consellos", "Updates" : "Actualizacións", "Authentication error" : "Produciuse un erro de autenticación", @@ -78,6 +78,7 @@ OC.L10N.register( "Uninstalling ...." : "Desinstalando ...", "Error while uninstalling app" : "Produciuse un erro ao desinstalar o aplicatvo", "Uninstall" : "Desinstalar", + "An error occurred: {message}" : "Produciuse un erro: {message}", "Select a profile picture" : "Seleccione unha imaxe para o perfil", "Very weak password" : "Contrasinal moi feble", "Weak password" : "Contrasinal feble", @@ -180,6 +181,8 @@ OC.L10N.register( "More" : "Máis", "Less" : "Menos", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "O ficheiro de rexistro é maior de 100 MB. Pódelle levar un anaco descargalo!", + "Transactional File Locking is enabled." : "O bloque transaccional de ficheiros está activado.", + "Transactional File Locking is disabled." : "O bloque transaccional de ficheiros está desactivado.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "Empregase SQLite como base de datos. Para instalacións grandes recomendámoslle que cambie a unha infraestrutura de base de datos diferente.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Concretamente, se emprega o cliente de escritorio para sincronización, desaconsellámoslle o uso de SQLite", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Para migrar cara outra base de datos, empregue a ferramenta en liña de ordes: «occ db:convert-type», ou vexa a <a target=\"_blank\" href=\"%s\">documentación ↗</a>.", @@ -189,8 +192,6 @@ OC.L10N.register( "Improving the config.php" : "Mellorando o config.php", "Theming" : "Tematización", "Hardening and security guidance" : "Orientacións sobre fortificación e seguridade", - "Transactional File Locking is enabled." : "O bloque transaccional de ficheiros está activado.", - "Transactional File Locking is disabled." : "O bloque transaccional de ficheiros está desactivado.", "Version" : "Versión", "Developer documentation" : "Documentación do desenvolvedor", "Experimental applications ahead" : "Ante as aplicacións experimentais", @@ -238,7 +239,7 @@ OC.L10N.register( "Upload new" : "Novo envío", "Select new from Files" : "Seleccionar outra nova desde Ficheiros", "Remove image" : "Retirar a imaxe", - "Either png or jpg. Ideally square but you will be able to crop it." : "Calquera png ou jpg. É preferíbel que sexa cadrada, mais poderá recortala.", + "Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB." : "Calquera png ou jpg. É preferíbel que sexa cadrada, mais poderá recortala. O ficheiro non pode exceder do tamaño máximo de 20MB.", "Your avatar is provided by your original account." : "O seu avatar é fornecido pola súa conta orixinal.", "Cancel" : "Cancelar", "Choose as profile image" : "Escolla unha imaxe para o perfil", diff --git a/settings/l10n/gl.json b/settings/l10n/gl.json index 44f769a64cb..43d8f93a0cc 100644 --- a/settings/l10n/gl.json +++ b/settings/l10n/gl.json @@ -8,7 +8,7 @@ "Cron" : "Cron", "Email server" : "Servidor de correo", "Log" : "Rexistro", - "Server Status" : "Estado do servidor", + "Server status" : "Estado do servidor", "Tips & tricks" : "Trucos e consellos", "Updates" : "Actualizacións", "Authentication error" : "Produciuse un erro de autenticación", @@ -76,6 +76,7 @@ "Uninstalling ...." : "Desinstalando ...", "Error while uninstalling app" : "Produciuse un erro ao desinstalar o aplicatvo", "Uninstall" : "Desinstalar", + "An error occurred: {message}" : "Produciuse un erro: {message}", "Select a profile picture" : "Seleccione unha imaxe para o perfil", "Very weak password" : "Contrasinal moi feble", "Weak password" : "Contrasinal feble", @@ -178,6 +179,8 @@ "More" : "Máis", "Less" : "Menos", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "O ficheiro de rexistro é maior de 100 MB. Pódelle levar un anaco descargalo!", + "Transactional File Locking is enabled." : "O bloque transaccional de ficheiros está activado.", + "Transactional File Locking is disabled." : "O bloque transaccional de ficheiros está desactivado.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "Empregase SQLite como base de datos. Para instalacións grandes recomendámoslle que cambie a unha infraestrutura de base de datos diferente.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Concretamente, se emprega o cliente de escritorio para sincronización, desaconsellámoslle o uso de SQLite", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Para migrar cara outra base de datos, empregue a ferramenta en liña de ordes: «occ db:convert-type», ou vexa a <a target=\"_blank\" href=\"%s\">documentación ↗</a>.", @@ -187,8 +190,6 @@ "Improving the config.php" : "Mellorando o config.php", "Theming" : "Tematización", "Hardening and security guidance" : "Orientacións sobre fortificación e seguridade", - "Transactional File Locking is enabled." : "O bloque transaccional de ficheiros está activado.", - "Transactional File Locking is disabled." : "O bloque transaccional de ficheiros está desactivado.", "Version" : "Versión", "Developer documentation" : "Documentación do desenvolvedor", "Experimental applications ahead" : "Ante as aplicacións experimentais", @@ -236,7 +237,7 @@ "Upload new" : "Novo envío", "Select new from Files" : "Seleccionar outra nova desde Ficheiros", "Remove image" : "Retirar a imaxe", - "Either png or jpg. Ideally square but you will be able to crop it." : "Calquera png ou jpg. É preferíbel que sexa cadrada, mais poderá recortala.", + "Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB." : "Calquera png ou jpg. É preferíbel que sexa cadrada, mais poderá recortala. O ficheiro non pode exceder do tamaño máximo de 20MB.", "Your avatar is provided by your original account." : "O seu avatar é fornecido pola súa conta orixinal.", "Cancel" : "Cancelar", "Choose as profile image" : "Escolla unha imaxe para o perfil", diff --git a/settings/l10n/hr.js b/settings/l10n/hr.js index 257acdf06ec..e32b8aa6608 100644 --- a/settings/l10n/hr.js +++ b/settings/l10n/hr.js @@ -136,7 +136,6 @@ OC.L10N.register( "Upload new" : "Učitajte novu", "Select new from Files" : "Odaberite novu iz datoteka", "Remove image" : "Uklonite sliku", - "Either png or jpg. Ideally square but you will be able to crop it." : "Ili png ili jpg. Idealno bi bilo da je kvadratna, ali moći ćete je obrezati", "Your avatar is provided by your original account." : "Vaš avatar je isporučen od strane vašeg izvornog računa", "Cancel" : "Odustanite", "Choose as profile image" : "Odaberite kao sliku profila", diff --git a/settings/l10n/hr.json b/settings/l10n/hr.json index 23d1a9c4b02..6cee1ef82aa 100644 --- a/settings/l10n/hr.json +++ b/settings/l10n/hr.json @@ -134,7 +134,6 @@ "Upload new" : "Učitajte novu", "Select new from Files" : "Odaberite novu iz datoteka", "Remove image" : "Uklonite sliku", - "Either png or jpg. Ideally square but you will be able to crop it." : "Ili png ili jpg. Idealno bi bilo da je kvadratna, ali moći ćete je obrezati", "Your avatar is provided by your original account." : "Vaš avatar je isporučen od strane vašeg izvornog računa", "Cancel" : "Odustanite", "Choose as profile image" : "Odaberite kao sliku profila", diff --git a/settings/l10n/hu_HU.js b/settings/l10n/hu_HU.js index 23ffd4adf4c..4ce16798ef8 100644 --- a/settings/l10n/hu_HU.js +++ b/settings/l10n/hu_HU.js @@ -141,7 +141,6 @@ OC.L10N.register( "Upload new" : "Új feltöltése", "Select new from Files" : "Új kiválasztása a Fájlokból", "Remove image" : "A kép eltávolítása", - "Either png or jpg. Ideally square but you will be able to crop it." : "A kép png vagy jpg formátumban legyen. Legjobb, ha négyzet alakú, de később még átszabható.", "Your avatar is provided by your original account." : "A kép az eredeti bejelentkezési adatai alapján lett beállítva.", "Cancel" : "Mégsem", "Choose as profile image" : "Válasszuk ki profilképnek", diff --git a/settings/l10n/hu_HU.json b/settings/l10n/hu_HU.json index fdb1c9d3ee1..b3c60fd5f3e 100644 --- a/settings/l10n/hu_HU.json +++ b/settings/l10n/hu_HU.json @@ -139,7 +139,6 @@ "Upload new" : "Új feltöltése", "Select new from Files" : "Új kiválasztása a Fájlokból", "Remove image" : "A kép eltávolítása", - "Either png or jpg. Ideally square but you will be able to crop it." : "A kép png vagy jpg formátumban legyen. Legjobb, ha négyzet alakú, de később még átszabható.", "Your avatar is provided by your original account." : "A kép az eredeti bejelentkezési adatai alapján lett beállítva.", "Cancel" : "Mégsem", "Choose as profile image" : "Válasszuk ki profilképnek", diff --git a/settings/l10n/id.js b/settings/l10n/id.js index 20e07bfb90f..083c068e14a 100644 --- a/settings/l10n/id.js +++ b/settings/l10n/id.js @@ -10,7 +10,6 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "Server email", "Log" : "Log", - "Server Status" : "Status Server", "Tips & tricks" : "Tips & trik", "Updates" : "Pembaruan", "Authentication error" : "Terjadi kesalahan saat otentikasi", @@ -178,6 +177,8 @@ OC.L10N.register( "More" : "Lainnya", "Less" : "Ciutkan", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Berkas log lebih besar dari 100MB. Pengunduhan ini memerlukan beberapa saat!", + "Transactional File Locking is enabled." : "Penguncian Berkas Transaksional diaktifkan.", + "Transactional File Locking is disabled." : "Penguncian Berkas Transaksional dinonaktifkan.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite digunakan sebagai basis data. Untuk instalasi yang lebih besar, kami menyarankan untuk beralih ke backend basis data yang berbeda.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Terutama saat menggunakan klien desktop untuk sinkronisasi berkas, penggunaan SQLite tidak disarankan.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Untuk migrasi ke basis data lainnya, gunakan alat baris perintah: 'occ db:convert-type', atau lihat <a target=\"_blank\" href=\"%s\">dokumentasi ↗</a>.", @@ -187,8 +188,6 @@ OC.L10N.register( "Improving the config.php" : "Memperbaiki config.php", "Theming" : "Tema", "Hardening and security guidance" : "Panduan Keselamatan dan Keamanan", - "Transactional File Locking is enabled." : "Penguncian Berkas Transaksional diaktifkan.", - "Transactional File Locking is disabled." : "Penguncian Berkas Transaksional dinonaktifkan.", "Version" : "Versi", "Developer documentation" : "Dokumentasi pengembang", "Experimental applications ahead" : "Aplikasi percobaan terdepan", @@ -236,7 +235,6 @@ OC.L10N.register( "Upload new" : "Unggah baru", "Select new from Files" : "Pilih baru dari Berkas", "Remove image" : "Hapus gambar", - "Either png or jpg. Ideally square but you will be able to crop it." : "Boleh png atau jpg. Idealnya berbentuk persegi tetapi jika tidak, Anda bisa memotongnya nanti.", "Your avatar is provided by your original account." : "Avatar disediakan oleh akun asli Anda.", "Cancel" : "Batal", "Choose as profile image" : "Pilih sebagai gambar profil", diff --git a/settings/l10n/id.json b/settings/l10n/id.json index c150230b468..f2464e75a17 100644 --- a/settings/l10n/id.json +++ b/settings/l10n/id.json @@ -8,7 +8,6 @@ "Cron" : "Cron", "Email server" : "Server email", "Log" : "Log", - "Server Status" : "Status Server", "Tips & tricks" : "Tips & trik", "Updates" : "Pembaruan", "Authentication error" : "Terjadi kesalahan saat otentikasi", @@ -176,6 +175,8 @@ "More" : "Lainnya", "Less" : "Ciutkan", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Berkas log lebih besar dari 100MB. Pengunduhan ini memerlukan beberapa saat!", + "Transactional File Locking is enabled." : "Penguncian Berkas Transaksional diaktifkan.", + "Transactional File Locking is disabled." : "Penguncian Berkas Transaksional dinonaktifkan.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite digunakan sebagai basis data. Untuk instalasi yang lebih besar, kami menyarankan untuk beralih ke backend basis data yang berbeda.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Terutama saat menggunakan klien desktop untuk sinkronisasi berkas, penggunaan SQLite tidak disarankan.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Untuk migrasi ke basis data lainnya, gunakan alat baris perintah: 'occ db:convert-type', atau lihat <a target=\"_blank\" href=\"%s\">dokumentasi ↗</a>.", @@ -185,8 +186,6 @@ "Improving the config.php" : "Memperbaiki config.php", "Theming" : "Tema", "Hardening and security guidance" : "Panduan Keselamatan dan Keamanan", - "Transactional File Locking is enabled." : "Penguncian Berkas Transaksional diaktifkan.", - "Transactional File Locking is disabled." : "Penguncian Berkas Transaksional dinonaktifkan.", "Version" : "Versi", "Developer documentation" : "Dokumentasi pengembang", "Experimental applications ahead" : "Aplikasi percobaan terdepan", @@ -234,7 +233,6 @@ "Upload new" : "Unggah baru", "Select new from Files" : "Pilih baru dari Berkas", "Remove image" : "Hapus gambar", - "Either png or jpg. Ideally square but you will be able to crop it." : "Boleh png atau jpg. Idealnya berbentuk persegi tetapi jika tidak, Anda bisa memotongnya nanti.", "Your avatar is provided by your original account." : "Avatar disediakan oleh akun asli Anda.", "Cancel" : "Batal", "Choose as profile image" : "Pilih sebagai gambar profil", diff --git a/settings/l10n/it.js b/settings/l10n/it.js index 3258ab1a9a2..c7a2105f7cf 100644 --- a/settings/l10n/it.js +++ b/settings/l10n/it.js @@ -10,7 +10,7 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "Server di posta", "Log" : "Log", - "Server Status" : "Stato del server", + "Server status" : "Stato del server", "Tips & tricks" : "Suggerimenti e trucchi", "Updates" : "Aggiornamenti", "Authentication error" : "Errore di autenticazione", @@ -31,7 +31,9 @@ OC.L10N.register( "Unable to change password" : "Impossibile cambiare la password", "Enabled" : "Abilitata", "Not enabled" : "Non abilitata", + "installing and updating apps via the app store or Federated Cloud Sharing" : "installazione e aggiornamento delle applicazioni tramite il negozio delle applicazioni o condivisione cloud federata", "Federated Cloud Sharing" : "Condivisione cloud federata", + "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL utilizza una versione %s datata (%s). Aggiorna il tuo sistema operativo o funzionalità come %s non funzioneranno correttamente.", "A problem occurred, please check your log files (Error: %s)" : "Si è verificato un problema, controlla i tuoi file di log (Errore: %s)", "Migration Completed" : "Migrazione completata", "Group already exists." : "Il gruppo esiste già.", @@ -76,6 +78,7 @@ OC.L10N.register( "Uninstalling ...." : "Disinstallazione...", "Error while uninstalling app" : "Errore durante la disinstallazione dell'applicazione", "Uninstall" : "Disinstalla", + "An error occurred: {message}" : "Si è verificato un errore: {message}", "Select a profile picture" : "Seleziona un'immagine del profilo", "Very weak password" : "Password molto debole", "Weak password" : "Password debole", @@ -178,6 +181,8 @@ OC.L10N.register( "More" : "Altro", "Less" : "Meno", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Il file di log è più grande di 100MB. Scaricarlo potrebbe richiedere del tempo!", + "Transactional File Locking is enabled." : "Il blocco file transazionale è abilitato.", + "Transactional File Locking is disabled." : "Il blocco file transazionale è disabilitato.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite è utilizzato come database. Per installazioni più grandi consigliamo di passare a un motore di database diverso.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "In particolar modo, quando si utilizza il client desktop per la sincronizzazione dei file, l'uso di SQLite è sconsigliato.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Per migrare a un altro database, usa lo strumento da riga di comando: 'occ db:convert-type', o leggi la <a target=\"_blank\" href=\"%s\">documentazione ↗</a>.", @@ -187,8 +192,6 @@ OC.L10N.register( "Improving the config.php" : "Ottimizzare il config.php", "Theming" : "Temi", "Hardening and security guidance" : "Guida alla messa in sicurezza", - "Transactional File Locking is enabled." : "Il blocco file transazionale è abilitato.", - "Transactional File Locking is disabled." : "Il blocco file transazionale è disabilitato.", "Version" : "Versione", "Developer documentation" : "Documentazione dello sviluppatore", "Experimental applications ahead" : "Prima le applicazioni sperimentali", @@ -236,7 +239,7 @@ OC.L10N.register( "Upload new" : "Carica nuova", "Select new from Files" : "Seleziona nuova da file", "Remove image" : "Rimuovi immagine", - "Either png or jpg. Ideally square but you will be able to crop it." : "Sia png che jpg. Preferibilmente quadrata, ma potrai ritagliarla.", + "Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB." : "Sia png che jpg. Preferibilmente quadrata, ma potrai ritagliarla. Il file non può superare la dimensione massima di 20 MB.", "Your avatar is provided by your original account." : "Il tuo avatar è ottenuto dal tuo account originale.", "Cancel" : "Annulla", "Choose as profile image" : "Scegli come immagine del profilo", diff --git a/settings/l10n/it.json b/settings/l10n/it.json index ae2c56ba7eb..c7bd9878087 100644 --- a/settings/l10n/it.json +++ b/settings/l10n/it.json @@ -8,7 +8,7 @@ "Cron" : "Cron", "Email server" : "Server di posta", "Log" : "Log", - "Server Status" : "Stato del server", + "Server status" : "Stato del server", "Tips & tricks" : "Suggerimenti e trucchi", "Updates" : "Aggiornamenti", "Authentication error" : "Errore di autenticazione", @@ -29,7 +29,9 @@ "Unable to change password" : "Impossibile cambiare la password", "Enabled" : "Abilitata", "Not enabled" : "Non abilitata", + "installing and updating apps via the app store or Federated Cloud Sharing" : "installazione e aggiornamento delle applicazioni tramite il negozio delle applicazioni o condivisione cloud federata", "Federated Cloud Sharing" : "Condivisione cloud federata", + "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL utilizza una versione %s datata (%s). Aggiorna il tuo sistema operativo o funzionalità come %s non funzioneranno correttamente.", "A problem occurred, please check your log files (Error: %s)" : "Si è verificato un problema, controlla i tuoi file di log (Errore: %s)", "Migration Completed" : "Migrazione completata", "Group already exists." : "Il gruppo esiste già.", @@ -74,6 +76,7 @@ "Uninstalling ...." : "Disinstallazione...", "Error while uninstalling app" : "Errore durante la disinstallazione dell'applicazione", "Uninstall" : "Disinstalla", + "An error occurred: {message}" : "Si è verificato un errore: {message}", "Select a profile picture" : "Seleziona un'immagine del profilo", "Very weak password" : "Password molto debole", "Weak password" : "Password debole", @@ -176,6 +179,8 @@ "More" : "Altro", "Less" : "Meno", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Il file di log è più grande di 100MB. Scaricarlo potrebbe richiedere del tempo!", + "Transactional File Locking is enabled." : "Il blocco file transazionale è abilitato.", + "Transactional File Locking is disabled." : "Il blocco file transazionale è disabilitato.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite è utilizzato come database. Per installazioni più grandi consigliamo di passare a un motore di database diverso.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "In particolar modo, quando si utilizza il client desktop per la sincronizzazione dei file, l'uso di SQLite è sconsigliato.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Per migrare a un altro database, usa lo strumento da riga di comando: 'occ db:convert-type', o leggi la <a target=\"_blank\" href=\"%s\">documentazione ↗</a>.", @@ -185,8 +190,6 @@ "Improving the config.php" : "Ottimizzare il config.php", "Theming" : "Temi", "Hardening and security guidance" : "Guida alla messa in sicurezza", - "Transactional File Locking is enabled." : "Il blocco file transazionale è abilitato.", - "Transactional File Locking is disabled." : "Il blocco file transazionale è disabilitato.", "Version" : "Versione", "Developer documentation" : "Documentazione dello sviluppatore", "Experimental applications ahead" : "Prima le applicazioni sperimentali", @@ -234,7 +237,7 @@ "Upload new" : "Carica nuova", "Select new from Files" : "Seleziona nuova da file", "Remove image" : "Rimuovi immagine", - "Either png or jpg. Ideally square but you will be able to crop it." : "Sia png che jpg. Preferibilmente quadrata, ma potrai ritagliarla.", + "Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB." : "Sia png che jpg. Preferibilmente quadrata, ma potrai ritagliarla. Il file non può superare la dimensione massima di 20 MB.", "Your avatar is provided by your original account." : "Il tuo avatar è ottenuto dal tuo account originale.", "Cancel" : "Annulla", "Choose as profile image" : "Scegli come immagine del profilo", diff --git a/settings/l10n/ja.js b/settings/l10n/ja.js index 6d36075e713..41ee4af3aca 100644 --- a/settings/l10n/ja.js +++ b/settings/l10n/ja.js @@ -10,7 +10,6 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "メールサーバー", "Log" : "ログ", - "Server Status" : "サーバーステータス", "Tips & tricks" : "Tips & tricks", "Updates" : "アップデート", "Authentication error" : "認証エラー", @@ -219,7 +218,6 @@ OC.L10N.register( "Upload new" : "新たにアップロード", "Select new from Files" : "新しいファイルを選択", "Remove image" : "画像を削除", - "Either png or jpg. Ideally square but you will be able to crop it." : "pngまたはjpg形式。正方形が理想ですが、切り取って加工することもできます。", "Your avatar is provided by your original account." : "あなたのアバターは、あなたのオリジナルのアカウントで提供されています。", "Cancel" : "キャンセル", "Choose as profile image" : "プロファイル画像として選択", diff --git a/settings/l10n/ja.json b/settings/l10n/ja.json index 771c4787b98..6533043c799 100644 --- a/settings/l10n/ja.json +++ b/settings/l10n/ja.json @@ -8,7 +8,6 @@ "Cron" : "Cron", "Email server" : "メールサーバー", "Log" : "ログ", - "Server Status" : "サーバーステータス", "Tips & tricks" : "Tips & tricks", "Updates" : "アップデート", "Authentication error" : "認証エラー", @@ -217,7 +216,6 @@ "Upload new" : "新たにアップロード", "Select new from Files" : "新しいファイルを選択", "Remove image" : "画像を削除", - "Either png or jpg. Ideally square but you will be able to crop it." : "pngまたはjpg形式。正方形が理想ですが、切り取って加工することもできます。", "Your avatar is provided by your original account." : "あなたのアバターは、あなたのオリジナルのアカウントで提供されています。", "Cancel" : "キャンセル", "Choose as profile image" : "プロファイル画像として選択", diff --git a/settings/l10n/ko.js b/settings/l10n/ko.js index dd7a09bff39..9ea90377bc1 100644 --- a/settings/l10n/ko.js +++ b/settings/l10n/ko.js @@ -10,7 +10,7 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "이메일 서버", "Log" : "로그", - "Server Status" : "서버 상태", + "Server status" : "서버 상태", "Tips & tricks" : "팁과 추가 정보", "Updates" : "업데이트", "Authentication error" : "인증 오류", @@ -31,7 +31,9 @@ OC.L10N.register( "Unable to change password" : "암호를 변경할 수 없음", "Enabled" : "활성", "Not enabled" : "비활성", + "installing and updating apps via the app store or Federated Cloud Sharing" : "앱 스토어 및 연합 클라우드 공유로 앱 설치 및 업데이트", "Federated Cloud Sharing" : "클라우드 연합 공유", + "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL이 오래된 %s 버전을 사용하고 있습니다(%s). 운영 체제나 기능을 업데이트하지 않으면 %s 등을 안정적으로 사용할 수 없습니다.", "A problem occurred, please check your log files (Error: %s)" : "문제가 발생하였습니다. 로그 파일을 참조하십시오(오류: %s)", "Migration Completed" : "이전 완료됨", "Group already exists." : "그룹이 이미 존재합니다.", @@ -76,6 +78,7 @@ OC.L10N.register( "Uninstalling ...." : "제거 하는 중 ....", "Error while uninstalling app" : "앱을 제거하는 중 오류 발생", "Uninstall" : "제거", + "An error occurred: {message}" : "오류 발생: {message}", "Select a profile picture" : "프로필 사진 선택", "Very weak password" : "매우 약한 암호", "Weak password" : "약한 암호", @@ -178,6 +181,8 @@ OC.L10N.register( "More" : "더 중요함", "Less" : "덜 중요함", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "로그 파일이 100MB보다 큽니다. 다운로드하는 데 시간이 걸릴 수 있습니다!", + "Transactional File Locking is enabled." : "트랜잭션 기반 파일 잠금이 활성화 되었습니다.", + "Transactional File Locking is disabled." : "트랜잭션 기반 파일 잠금이 비활성화 되었습니다.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "데이터베이스로 SQLite를 사용하고 있습니다. 대규모의 파일을 관리하려고 한다면 다른 데이터베이스 백엔드로 전환할 것을 권장합니다.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "특히 파일 동기화를 위해 데스크톱 클라이언트를 사용할 예정이면, SQLite를 사용하지 않는 것이 좋습니다.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "다른 데이터베이스로 이전하려면 다음 명령을 사용하십시오: 'occ db:convert-type' 다른 명령을 알아보려면 <a target=\"_blank\" href=\"%s\">문서 ↗</a>를 참고하십시오.", @@ -187,8 +192,6 @@ OC.L10N.register( "Improving the config.php" : "config.php 개선", "Theming" : "테마 꾸미기", "Hardening and security guidance" : "보안 강화 지침", - "Transactional File Locking is enabled." : "트랜잭션 기반 파일 잠금이 활성화 되었습니다.", - "Transactional File Locking is disabled." : "트랜잭션 기반 파일 잠금이 비활성화 되었습니다.", "Version" : "버전", "Developer documentation" : "개발자 문서", "Experimental applications ahead" : "실험적인 앱 사용 예정", @@ -236,7 +239,7 @@ OC.L10N.register( "Upload new" : "새로 업로드", "Select new from Files" : "파일에서 선택", "Remove image" : "그림 삭제", - "Either png or jpg. Ideally square but you will be able to crop it." : "png나 jpg를 사용하십시오. 정사각형 형태가 가장 좋지만 잘라낼 수 있습니다.", + "Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB." : "PNG나 JPG 파일입니다. 정사각형을 사용하는 것이 가장 좋지만 잘라낼 수 있습니다. 파일 최대 크기는 20MB입니다.", "Your avatar is provided by your original account." : "원본 계정의 아바타를 사용합니다.", "Cancel" : "취소", "Choose as profile image" : "프로필 이미지로 사용", diff --git a/settings/l10n/ko.json b/settings/l10n/ko.json index 955396bff17..429d75dc93c 100644 --- a/settings/l10n/ko.json +++ b/settings/l10n/ko.json @@ -8,7 +8,7 @@ "Cron" : "Cron", "Email server" : "이메일 서버", "Log" : "로그", - "Server Status" : "서버 상태", + "Server status" : "서버 상태", "Tips & tricks" : "팁과 추가 정보", "Updates" : "업데이트", "Authentication error" : "인증 오류", @@ -29,7 +29,9 @@ "Unable to change password" : "암호를 변경할 수 없음", "Enabled" : "활성", "Not enabled" : "비활성", + "installing and updating apps via the app store or Federated Cloud Sharing" : "앱 스토어 및 연합 클라우드 공유로 앱 설치 및 업데이트", "Federated Cloud Sharing" : "클라우드 연합 공유", + "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL이 오래된 %s 버전을 사용하고 있습니다(%s). 운영 체제나 기능을 업데이트하지 않으면 %s 등을 안정적으로 사용할 수 없습니다.", "A problem occurred, please check your log files (Error: %s)" : "문제가 발생하였습니다. 로그 파일을 참조하십시오(오류: %s)", "Migration Completed" : "이전 완료됨", "Group already exists." : "그룹이 이미 존재합니다.", @@ -74,6 +76,7 @@ "Uninstalling ...." : "제거 하는 중 ....", "Error while uninstalling app" : "앱을 제거하는 중 오류 발생", "Uninstall" : "제거", + "An error occurred: {message}" : "오류 발생: {message}", "Select a profile picture" : "프로필 사진 선택", "Very weak password" : "매우 약한 암호", "Weak password" : "약한 암호", @@ -176,6 +179,8 @@ "More" : "더 중요함", "Less" : "덜 중요함", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "로그 파일이 100MB보다 큽니다. 다운로드하는 데 시간이 걸릴 수 있습니다!", + "Transactional File Locking is enabled." : "트랜잭션 기반 파일 잠금이 활성화 되었습니다.", + "Transactional File Locking is disabled." : "트랜잭션 기반 파일 잠금이 비활성화 되었습니다.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "데이터베이스로 SQLite를 사용하고 있습니다. 대규모의 파일을 관리하려고 한다면 다른 데이터베이스 백엔드로 전환할 것을 권장합니다.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "특히 파일 동기화를 위해 데스크톱 클라이언트를 사용할 예정이면, SQLite를 사용하지 않는 것이 좋습니다.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "다른 데이터베이스로 이전하려면 다음 명령을 사용하십시오: 'occ db:convert-type' 다른 명령을 알아보려면 <a target=\"_blank\" href=\"%s\">문서 ↗</a>를 참고하십시오.", @@ -185,8 +190,6 @@ "Improving the config.php" : "config.php 개선", "Theming" : "테마 꾸미기", "Hardening and security guidance" : "보안 강화 지침", - "Transactional File Locking is enabled." : "트랜잭션 기반 파일 잠금이 활성화 되었습니다.", - "Transactional File Locking is disabled." : "트랜잭션 기반 파일 잠금이 비활성화 되었습니다.", "Version" : "버전", "Developer documentation" : "개발자 문서", "Experimental applications ahead" : "실험적인 앱 사용 예정", @@ -234,7 +237,7 @@ "Upload new" : "새로 업로드", "Select new from Files" : "파일에서 선택", "Remove image" : "그림 삭제", - "Either png or jpg. Ideally square but you will be able to crop it." : "png나 jpg를 사용하십시오. 정사각형 형태가 가장 좋지만 잘라낼 수 있습니다.", + "Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB." : "PNG나 JPG 파일입니다. 정사각형을 사용하는 것이 가장 좋지만 잘라낼 수 있습니다. 파일 최대 크기는 20MB입니다.", "Your avatar is provided by your original account." : "원본 계정의 아바타를 사용합니다.", "Cancel" : "취소", "Choose as profile image" : "프로필 이미지로 사용", diff --git a/settings/l10n/lt_LT.js b/settings/l10n/lt_LT.js index 08676dd9156..bf13c23f2af 100644 --- a/settings/l10n/lt_LT.js +++ b/settings/l10n/lt_LT.js @@ -78,7 +78,6 @@ OC.L10N.register( "Upload new" : "Įkelti naują", "Select new from Files" : "Pasirinkti naują iš failų", "Remove image" : "Pašalinti paveikslėlį", - "Either png or jpg. Ideally square but you will be able to crop it." : "Arba png arba jpg. Geriausia kvadratinį, bet galėsite jį apkarpyti.", "Cancel" : "Atšaukti", "Choose as profile image" : "Pasirinkite profilio paveiksliuką", "Language" : "Kalba", diff --git a/settings/l10n/lt_LT.json b/settings/l10n/lt_LT.json index bff0df74f08..fe387dabec5 100644 --- a/settings/l10n/lt_LT.json +++ b/settings/l10n/lt_LT.json @@ -76,7 +76,6 @@ "Upload new" : "Įkelti naują", "Select new from Files" : "Pasirinkti naują iš failų", "Remove image" : "Pašalinti paveikslėlį", - "Either png or jpg. Ideally square but you will be able to crop it." : "Arba png arba jpg. Geriausia kvadratinį, bet galėsite jį apkarpyti.", "Cancel" : "Atšaukti", "Choose as profile image" : "Pasirinkite profilio paveiksliuką", "Language" : "Kalba", diff --git a/settings/l10n/mk.js b/settings/l10n/mk.js index 1695d5d5aeb..f4ce5676543 100644 --- a/settings/l10n/mk.js +++ b/settings/l10n/mk.js @@ -126,7 +126,6 @@ OC.L10N.register( "Upload new" : "Префрли нова", "Select new from Files" : "Одбери нова од датотеките", "Remove image" : "Отстрани ја фотографијата", - "Either png or jpg. Ideally square but you will be able to crop it." : "Мора де биде png или jpg. Идеално квадрат, но ќе бидете во можност да ја исечете.", "Your avatar is provided by your original account." : "Вашиот аватар е креиран со вашата оригинална сметка", "Cancel" : "Откажи", "Choose as profile image" : "Одбери фотографија за профилот", diff --git a/settings/l10n/mk.json b/settings/l10n/mk.json index 5a5db7ccc7e..c93ae59e35c 100644 --- a/settings/l10n/mk.json +++ b/settings/l10n/mk.json @@ -124,7 +124,6 @@ "Upload new" : "Префрли нова", "Select new from Files" : "Одбери нова од датотеките", "Remove image" : "Отстрани ја фотографијата", - "Either png or jpg. Ideally square but you will be able to crop it." : "Мора де биде png или jpg. Идеално квадрат, но ќе бидете во можност да ја исечете.", "Your avatar is provided by your original account." : "Вашиот аватар е креиран со вашата оригинална сметка", "Cancel" : "Откажи", "Choose as profile image" : "Одбери фотографија за профилот", diff --git a/settings/l10n/nb_NO.js b/settings/l10n/nb_NO.js index 805f1f0c328..4fffe8d620f 100644 --- a/settings/l10n/nb_NO.js +++ b/settings/l10n/nb_NO.js @@ -10,7 +10,6 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "E-postserver", "Log" : "Logg", - "Server Status" : "Server-status", "Tips & tricks" : "Tips og triks", "Updates" : "Oppdateringer", "Authentication error" : "Autentiseringsfeil", @@ -178,6 +177,8 @@ OC.L10N.register( "More" : "Mer", "Less" : "Mindre", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Loggfilen er over 100 MB, nedlastingen kan ta en stund!", + "Transactional File Locking is enabled." : "Transaksjonsbasert fil-låsing er aktivert.", + "Transactional File Locking is disabled." : "Transaksjonsbasert fil-låsing er deaktivert.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite brukes som database. For større installasjoner anbefaler vi å bytte til en annen database-server.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "SQLite er spesielt frarådet om man bruker desktopklienten til filsynkronisering", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "For å migrere til en annen database, bruk kommandolinjeverktøyet: 'occ db:convert-type', eller les i <a target=\"_blank\" href=\"%s\">dokumentasjonen ↗</a>.", @@ -187,8 +188,6 @@ OC.L10N.register( "Improving the config.php" : "Forbedring av config.php", "Theming" : "Temaer", "Hardening and security guidance" : "Herding og sikkerhetsveiledning", - "Transactional File Locking is enabled." : "Transaksjonsbasert fil-låsing er aktivert.", - "Transactional File Locking is disabled." : "Transaksjonsbasert fil-låsing er deaktivert.", "Version" : "Versjon", "Developer documentation" : "Utviklerdokumentasjon", "Experimental applications ahead" : "Eksperimentelle applikasjoner forut", @@ -235,7 +234,6 @@ OC.L10N.register( "Upload new" : "Last opp nytt", "Select new from Files" : "Velg nytt fra Filer", "Remove image" : "Fjern bilde", - "Either png or jpg. Ideally square but you will be able to crop it." : "Enten png eller jpg. Helst kvadratisk, men du kan beskjære det.", "Your avatar is provided by your original account." : "Avataren din kommer fra din opprinnelige konto.", "Cancel" : "Avbryt", "Choose as profile image" : "Velg som profilbilde", diff --git a/settings/l10n/nb_NO.json b/settings/l10n/nb_NO.json index 3e92a23ec75..17a4bfce9f4 100644 --- a/settings/l10n/nb_NO.json +++ b/settings/l10n/nb_NO.json @@ -8,7 +8,6 @@ "Cron" : "Cron", "Email server" : "E-postserver", "Log" : "Logg", - "Server Status" : "Server-status", "Tips & tricks" : "Tips og triks", "Updates" : "Oppdateringer", "Authentication error" : "Autentiseringsfeil", @@ -176,6 +175,8 @@ "More" : "Mer", "Less" : "Mindre", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Loggfilen er over 100 MB, nedlastingen kan ta en stund!", + "Transactional File Locking is enabled." : "Transaksjonsbasert fil-låsing er aktivert.", + "Transactional File Locking is disabled." : "Transaksjonsbasert fil-låsing er deaktivert.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite brukes som database. For større installasjoner anbefaler vi å bytte til en annen database-server.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "SQLite er spesielt frarådet om man bruker desktopklienten til filsynkronisering", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "For å migrere til en annen database, bruk kommandolinjeverktøyet: 'occ db:convert-type', eller les i <a target=\"_blank\" href=\"%s\">dokumentasjonen ↗</a>.", @@ -185,8 +186,6 @@ "Improving the config.php" : "Forbedring av config.php", "Theming" : "Temaer", "Hardening and security guidance" : "Herding og sikkerhetsveiledning", - "Transactional File Locking is enabled." : "Transaksjonsbasert fil-låsing er aktivert.", - "Transactional File Locking is disabled." : "Transaksjonsbasert fil-låsing er deaktivert.", "Version" : "Versjon", "Developer documentation" : "Utviklerdokumentasjon", "Experimental applications ahead" : "Eksperimentelle applikasjoner forut", @@ -233,7 +232,6 @@ "Upload new" : "Last opp nytt", "Select new from Files" : "Velg nytt fra Filer", "Remove image" : "Fjern bilde", - "Either png or jpg. Ideally square but you will be able to crop it." : "Enten png eller jpg. Helst kvadratisk, men du kan beskjære det.", "Your avatar is provided by your original account." : "Avataren din kommer fra din opprinnelige konto.", "Cancel" : "Avbryt", "Choose as profile image" : "Velg som profilbilde", diff --git a/settings/l10n/nl.js b/settings/l10n/nl.js index 2898cf5d5a6..51aadc8c161 100644 --- a/settings/l10n/nl.js +++ b/settings/l10n/nl.js @@ -10,7 +10,6 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "E-mailserver", "Log" : "Log", - "Server Status" : "Server Status", "Tips & tricks" : "Tips & trucs", "Updates" : "Updates", "Authentication error" : "Authenticatie fout", @@ -178,6 +177,8 @@ OC.L10N.register( "More" : "Meer", "Less" : "Minder", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Het logbestand is groter dan 100MB. Downloaden kost even tijd!", + "Transactional File Locking is enabled." : "Transactionele File Locking is ingeschakeld.", + "Transactional File Locking is disabled." : "Transactionele File Locking is uitgeschakeld.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite wordt gebruikt als database. Voor grotere installaties adviseren we om te schakelen naar een andere database engine.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Vooral wanneer de desktop client wordt gebruik voor bestandssynchronisatie wordt gebruik van sqlite afgeraden.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Om te migreren naar een andere database moet u de commandoregel tool gebruiken: 'occ db:convert-type', lees de <a target=\"_blank\" href=\"%s\">documentatie ↗</a>.", @@ -187,8 +188,6 @@ OC.L10N.register( "Improving the config.php" : "config.php verbeteren", "Theming" : "Thema's", "Hardening and security guidance" : "Hardening en security advies", - "Transactional File Locking is enabled." : "Transactionele File Locking is ingeschakeld.", - "Transactional File Locking is disabled." : "Transactionele File Locking is uitgeschakeld.", "Version" : "Versie", "Developer documentation" : "Ontwikkelaarsdocumentatie", "Experimental applications ahead" : "Experimentele applicaties vooraan", @@ -235,7 +234,6 @@ OC.L10N.register( "Upload new" : "Upload een nieuwe", "Select new from Files" : "Selecteer een nieuwe vanuit bestanden", "Remove image" : "Afbeelding verwijderen", - "Either png or jpg. Ideally square but you will be able to crop it." : "Of png, of jpg. Bij voorkeur vierkant, maar u kunt de afbeelding bijsnijden.", "Your avatar is provided by your original account." : "Uw avatar is verstrekt door uw originele account.", "Cancel" : "Annuleer", "Choose as profile image" : "Kies als profielafbeelding", diff --git a/settings/l10n/nl.json b/settings/l10n/nl.json index 5f4b4dc208b..4e184bdcb2e 100644 --- a/settings/l10n/nl.json +++ b/settings/l10n/nl.json @@ -8,7 +8,6 @@ "Cron" : "Cron", "Email server" : "E-mailserver", "Log" : "Log", - "Server Status" : "Server Status", "Tips & tricks" : "Tips & trucs", "Updates" : "Updates", "Authentication error" : "Authenticatie fout", @@ -176,6 +175,8 @@ "More" : "Meer", "Less" : "Minder", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Het logbestand is groter dan 100MB. Downloaden kost even tijd!", + "Transactional File Locking is enabled." : "Transactionele File Locking is ingeschakeld.", + "Transactional File Locking is disabled." : "Transactionele File Locking is uitgeschakeld.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite wordt gebruikt als database. Voor grotere installaties adviseren we om te schakelen naar een andere database engine.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Vooral wanneer de desktop client wordt gebruik voor bestandssynchronisatie wordt gebruik van sqlite afgeraden.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Om te migreren naar een andere database moet u de commandoregel tool gebruiken: 'occ db:convert-type', lees de <a target=\"_blank\" href=\"%s\">documentatie ↗</a>.", @@ -185,8 +186,6 @@ "Improving the config.php" : "config.php verbeteren", "Theming" : "Thema's", "Hardening and security guidance" : "Hardening en security advies", - "Transactional File Locking is enabled." : "Transactionele File Locking is ingeschakeld.", - "Transactional File Locking is disabled." : "Transactionele File Locking is uitgeschakeld.", "Version" : "Versie", "Developer documentation" : "Ontwikkelaarsdocumentatie", "Experimental applications ahead" : "Experimentele applicaties vooraan", @@ -233,7 +232,6 @@ "Upload new" : "Upload een nieuwe", "Select new from Files" : "Selecteer een nieuwe vanuit bestanden", "Remove image" : "Afbeelding verwijderen", - "Either png or jpg. Ideally square but you will be able to crop it." : "Of png, of jpg. Bij voorkeur vierkant, maar u kunt de afbeelding bijsnijden.", "Your avatar is provided by your original account." : "Uw avatar is verstrekt door uw originele account.", "Cancel" : "Annuleer", "Choose as profile image" : "Kies als profielafbeelding", diff --git a/settings/l10n/nn_NO.js b/settings/l10n/nn_NO.js index 3d93e9839fc..fb3c45debb4 100644 --- a/settings/l10n/nn_NO.js +++ b/settings/l10n/nn_NO.js @@ -67,7 +67,6 @@ OC.L10N.register( "Upload new" : "Last opp ny", "Select new from Files" : "Vel ny frå Filer", "Remove image" : "Fjern bilete", - "Either png or jpg. Ideally square but you will be able to crop it." : "Anten PNG eller JPG. Helst kvadratisk, men du får moglegheita til å beskjera det.", "Cancel" : "Avbryt", "Choose as profile image" : "Vel som profilbilete", "Language" : "Språk", diff --git a/settings/l10n/nn_NO.json b/settings/l10n/nn_NO.json index fa2e96fdc9e..b1bcc62ea32 100644 --- a/settings/l10n/nn_NO.json +++ b/settings/l10n/nn_NO.json @@ -65,7 +65,6 @@ "Upload new" : "Last opp ny", "Select new from Files" : "Vel ny frå Filer", "Remove image" : "Fjern bilete", - "Either png or jpg. Ideally square but you will be able to crop it." : "Anten PNG eller JPG. Helst kvadratisk, men du får moglegheita til å beskjera det.", "Cancel" : "Avbryt", "Choose as profile image" : "Vel som profilbilete", "Language" : "Språk", diff --git a/settings/l10n/oc.js b/settings/l10n/oc.js index a22ac790df6..af849ff4a65 100644 --- a/settings/l10n/oc.js +++ b/settings/l10n/oc.js @@ -223,7 +223,6 @@ OC.L10N.register( "Upload new" : "Novèla dempuèi vòstre ordenador", "Select new from Files" : "Novèla dempuèi los Fichièrs", "Remove image" : "Suprimir l'imatge", - "Either png or jpg. Ideally square but you will be able to crop it." : "Format png o jpg. Idealament carrada, mas la poiretz requadrar.", "Your avatar is provided by your original account." : "Vòstre avatar es provesit per vòstre compte original.", "Cancel" : "Anullar", "Choose as profile image" : "Causir en tant que fòto de perfil", diff --git a/settings/l10n/oc.json b/settings/l10n/oc.json index 201a2e6e75c..6921ed2bebd 100644 --- a/settings/l10n/oc.json +++ b/settings/l10n/oc.json @@ -221,7 +221,6 @@ "Upload new" : "Novèla dempuèi vòstre ordenador", "Select new from Files" : "Novèla dempuèi los Fichièrs", "Remove image" : "Suprimir l'imatge", - "Either png or jpg. Ideally square but you will be able to crop it." : "Format png o jpg. Idealament carrada, mas la poiretz requadrar.", "Your avatar is provided by your original account." : "Vòstre avatar es provesit per vòstre compte original.", "Cancel" : "Anullar", "Choose as profile image" : "Causir en tant que fòto de perfil", diff --git a/settings/l10n/pl.js b/settings/l10n/pl.js index d5e7bb45bab..1b89a64339f 100644 --- a/settings/l10n/pl.js +++ b/settings/l10n/pl.js @@ -179,7 +179,6 @@ OC.L10N.register( "Upload new" : "Wczytaj nowe", "Select new from Files" : "Wybierz nowe z plików", "Remove image" : "Usuń zdjęcie", - "Either png or jpg. Ideally square but you will be able to crop it." : "Png lub jpg. Idealnie kwadratowy, ale będzie można je przyciąć.", "Your avatar is provided by your original account." : "Twój awatar jest ustawiony jako domyślny.", "Cancel" : "Anuluj", "Choose as profile image" : "Wybierz zdjęcie profilu", diff --git a/settings/l10n/pl.json b/settings/l10n/pl.json index 85475b35803..1424924907a 100644 --- a/settings/l10n/pl.json +++ b/settings/l10n/pl.json @@ -177,7 +177,6 @@ "Upload new" : "Wczytaj nowe", "Select new from Files" : "Wybierz nowe z plików", "Remove image" : "Usuń zdjęcie", - "Either png or jpg. Ideally square but you will be able to crop it." : "Png lub jpg. Idealnie kwadratowy, ale będzie można je przyciąć.", "Your avatar is provided by your original account." : "Twój awatar jest ustawiony jako domyślny.", "Cancel" : "Anuluj", "Choose as profile image" : "Wybierz zdjęcie profilu", diff --git a/settings/l10n/pt_BR.js b/settings/l10n/pt_BR.js index 42d901f1b37..3e77c6a3772 100644 --- a/settings/l10n/pt_BR.js +++ b/settings/l10n/pt_BR.js @@ -10,7 +10,7 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "Servidor de Email", "Log" : "Registro", - "Server Status" : "Status do Servidor", + "Server status" : "Status do servidor", "Tips & tricks" : "Dicas & Truques", "Updates" : "Atualizações", "Authentication error" : "Erro de autenticação", @@ -78,6 +78,7 @@ OC.L10N.register( "Uninstalling ...." : "Desinstalando ...", "Error while uninstalling app" : "Erro enquanto desinstalava aplicativo", "Uninstall" : "Desinstalar", + "An error occurred: {message}" : "Ocorreu um erro: {message}", "Select a profile picture" : "Selecione uma imagem para o perfil", "Very weak password" : "Senha muito fraca", "Weak password" : "Senha fraca", @@ -180,6 +181,8 @@ OC.L10N.register( "More" : "Mais", "Less" : "Menos", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "O arquivo de log é maior que 100 MB. Baixar esse arquivo requer algum tempo!", + "Transactional File Locking is enabled." : "Bloqueio de Arquivo Transacional está habilitado.", + "Transactional File Locking is disabled." : "Bloqueio de Arquivo Transacional está desabilitado.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite é usada como base de dados. Para instalações maiores recomendamos mudar para um backend de banco de dados diferente.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Especialmente quando se utiliza o cliente de desktop para sincronização de arquivos o uso de SQLite é desencorajado.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Para migrar para outro banco de dados usar a ferramenta de linha de comando: 'db occ: converter-type', verifique a <a target=\"_blank\" href=\"%s\">documentação ↗</a>.", @@ -189,8 +192,6 @@ OC.L10N.register( "Improving the config.php" : "Melhorando o config.php", "Theming" : "Elaborar um tema", "Hardening and security guidance" : "Orientações de proteção e segurança", - "Transactional File Locking is enabled." : "Bloqueio de Arquivo Transacional está habilitado.", - "Transactional File Locking is disabled." : "Bloqueio de Arquivo Transacional está desabilitado.", "Version" : "Versão", "Developer documentation" : "Documentação do desenvolvedor", "Experimental applications ahead" : "Aplicações experimentais à frente", @@ -238,7 +239,7 @@ OC.L10N.register( "Upload new" : "Enviar nova foto", "Select new from Files" : "Selecinar uma nova dos Arquivos", "Remove image" : "Remover imagem", - "Either png or jpg. Ideally square but you will be able to crop it." : "Ou png ou jpg. O ideal é quadrado, mas você vai ser capaz de cortá-la.", + "Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB." : "Ou png ou jpg. Idealmente quadrada, mas você será capaz de cortá-la. O arquivo não pode exceder o tamanho máximo de 20 MB.", "Your avatar is provided by your original account." : "Seu avatar é fornecido por sua conta original.", "Cancel" : "Cancelar", "Choose as profile image" : "Escolha como imagem para o perfil", diff --git a/settings/l10n/pt_BR.json b/settings/l10n/pt_BR.json index 3274851cfa5..73a9cf67b58 100644 --- a/settings/l10n/pt_BR.json +++ b/settings/l10n/pt_BR.json @@ -8,7 +8,7 @@ "Cron" : "Cron", "Email server" : "Servidor de Email", "Log" : "Registro", - "Server Status" : "Status do Servidor", + "Server status" : "Status do servidor", "Tips & tricks" : "Dicas & Truques", "Updates" : "Atualizações", "Authentication error" : "Erro de autenticação", @@ -76,6 +76,7 @@ "Uninstalling ...." : "Desinstalando ...", "Error while uninstalling app" : "Erro enquanto desinstalava aplicativo", "Uninstall" : "Desinstalar", + "An error occurred: {message}" : "Ocorreu um erro: {message}", "Select a profile picture" : "Selecione uma imagem para o perfil", "Very weak password" : "Senha muito fraca", "Weak password" : "Senha fraca", @@ -178,6 +179,8 @@ "More" : "Mais", "Less" : "Menos", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "O arquivo de log é maior que 100 MB. Baixar esse arquivo requer algum tempo!", + "Transactional File Locking is enabled." : "Bloqueio de Arquivo Transacional está habilitado.", + "Transactional File Locking is disabled." : "Bloqueio de Arquivo Transacional está desabilitado.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite é usada como base de dados. Para instalações maiores recomendamos mudar para um backend de banco de dados diferente.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Especialmente quando se utiliza o cliente de desktop para sincronização de arquivos o uso de SQLite é desencorajado.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Para migrar para outro banco de dados usar a ferramenta de linha de comando: 'db occ: converter-type', verifique a <a target=\"_blank\" href=\"%s\">documentação ↗</a>.", @@ -187,8 +190,6 @@ "Improving the config.php" : "Melhorando o config.php", "Theming" : "Elaborar um tema", "Hardening and security guidance" : "Orientações de proteção e segurança", - "Transactional File Locking is enabled." : "Bloqueio de Arquivo Transacional está habilitado.", - "Transactional File Locking is disabled." : "Bloqueio de Arquivo Transacional está desabilitado.", "Version" : "Versão", "Developer documentation" : "Documentação do desenvolvedor", "Experimental applications ahead" : "Aplicações experimentais à frente", @@ -236,7 +237,7 @@ "Upload new" : "Enviar nova foto", "Select new from Files" : "Selecinar uma nova dos Arquivos", "Remove image" : "Remover imagem", - "Either png or jpg. Ideally square but you will be able to crop it." : "Ou png ou jpg. O ideal é quadrado, mas você vai ser capaz de cortá-la.", + "Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB." : "Ou png ou jpg. Idealmente quadrada, mas você será capaz de cortá-la. O arquivo não pode exceder o tamanho máximo de 20 MB.", "Your avatar is provided by your original account." : "Seu avatar é fornecido por sua conta original.", "Cancel" : "Cancelar", "Choose as profile image" : "Escolha como imagem para o perfil", diff --git a/settings/l10n/pt_PT.js b/settings/l10n/pt_PT.js index d2ab73ce4ed..0e40df28081 100644 --- a/settings/l10n/pt_PT.js +++ b/settings/l10n/pt_PT.js @@ -212,7 +212,6 @@ OC.L10N.register( "Upload new" : "Carregar novo", "Select new from Files" : "Seleccionar novo a partir dos ficheiros", "Remove image" : "Remover imagem", - "Either png or jpg. Ideally square but you will be able to crop it." : "Apenas png ou jpg. Idealmente quadrada, mas poderá corta-la depois.", "Your avatar is provided by your original account." : "O seu avatar é fornecido pela sua conta original.", "Cancel" : "Cancelar", "Choose as profile image" : "Escolha uma fotografia de perfil", diff --git a/settings/l10n/pt_PT.json b/settings/l10n/pt_PT.json index 1ab8808031e..bcdcd1bdd97 100644 --- a/settings/l10n/pt_PT.json +++ b/settings/l10n/pt_PT.json @@ -210,7 +210,6 @@ "Upload new" : "Carregar novo", "Select new from Files" : "Seleccionar novo a partir dos ficheiros", "Remove image" : "Remover imagem", - "Either png or jpg. Ideally square but you will be able to crop it." : "Apenas png ou jpg. Idealmente quadrada, mas poderá corta-la depois.", "Your avatar is provided by your original account." : "O seu avatar é fornecido pela sua conta original.", "Cancel" : "Cancelar", "Choose as profile image" : "Escolha uma fotografia de perfil", diff --git a/settings/l10n/ro.js b/settings/l10n/ro.js index a50e73b0ce5..b969fae1b60 100644 --- a/settings/l10n/ro.js +++ b/settings/l10n/ro.js @@ -104,7 +104,6 @@ OC.L10N.register( "Upload new" : "Încarcă una nouă", "Select new from Files" : "Selectează una din Fișiere", "Remove image" : "Înlătură imagine", - "Either png or jpg. Ideally square but you will be able to crop it." : "Doar png sau jpg de formă pătrată. ", "Cancel" : "Anulare", "Choose as profile image" : "Alege drept imagine de profil", "Language" : "Limba", diff --git a/settings/l10n/ro.json b/settings/l10n/ro.json index 0ceb936877f..83c2572901d 100644 --- a/settings/l10n/ro.json +++ b/settings/l10n/ro.json @@ -102,7 +102,6 @@ "Upload new" : "Încarcă una nouă", "Select new from Files" : "Selectează una din Fișiere", "Remove image" : "Înlătură imagine", - "Either png or jpg. Ideally square but you will be able to crop it." : "Doar png sau jpg de formă pătrată. ", "Cancel" : "Anulare", "Choose as profile image" : "Alege drept imagine de profil", "Language" : "Limba", diff --git a/settings/l10n/ru.js b/settings/l10n/ru.js index 64df7664ee4..1aae8b2a97b 100644 --- a/settings/l10n/ru.js +++ b/settings/l10n/ru.js @@ -10,7 +10,6 @@ OC.L10N.register( "Cron" : "Cron (планировщик задач)", "Email server" : "Почтовый сервер", "Log" : "Журнал", - "Server Status" : "Статус сервера", "Tips & tricks" : "Советы и трюки", "Updates" : "Обновления", "Authentication error" : "Ошибка аутентификации", @@ -178,6 +177,8 @@ OC.L10N.register( "More" : "Больше", "Less" : "Меньше", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Лог-файл - больше 100 мб. Его скачивание может занять некоторое время!", + "Transactional File Locking is enabled." : "Транзакционная блокировка файлов включена.", + "Transactional File Locking is disabled." : "Транзакционная блокировка файлов отключена.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "В качестве базы данных используется SQLite. Для больших установок мы рекомендуем переключиться на другую серверную базу данных.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Особенно вызывает сомнение использование SQLite при синхронизации файлов с использование клиента для ПК.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Для перехода на другую базу данных используйте команду: 'occ db:convert-type' или ознакомьтесь с <a target=\"_blank\" href=\"%s\">документацией ↗</a>.", @@ -187,8 +188,6 @@ OC.L10N.register( "Improving the config.php" : "Улучшение config.php", "Theming" : "Темы оформления", "Hardening and security guidance" : "Руководство по безопасности и защите", - "Transactional File Locking is enabled." : "Транзакционная блокировка файлов включена.", - "Transactional File Locking is disabled." : "Транзакционная блокировка файлов отключена.", "Version" : "Версия", "Developer documentation" : "Документация для разработчиков", "Experimental applications ahead" : "Экспериментальные приложения", @@ -235,7 +234,6 @@ OC.L10N.register( "Upload new" : "Загрузить новый", "Select new from Files" : "Выберите новый из файлов", "Remove image" : "Удалить аватар", - "Either png or jpg. Ideally square but you will be able to crop it." : "Допустимые форматы: png и jpg. Если изображение не квадратное, то вам будет предложено обрезать его.", "Your avatar is provided by your original account." : "Будет использован аватар вашей оригинальной учетной записи.", "Cancel" : "Отмена", "Choose as profile image" : "Установить как аватар", diff --git a/settings/l10n/ru.json b/settings/l10n/ru.json index 7dc52767c7b..ef73320cbf7 100644 --- a/settings/l10n/ru.json +++ b/settings/l10n/ru.json @@ -8,7 +8,6 @@ "Cron" : "Cron (планировщик задач)", "Email server" : "Почтовый сервер", "Log" : "Журнал", - "Server Status" : "Статус сервера", "Tips & tricks" : "Советы и трюки", "Updates" : "Обновления", "Authentication error" : "Ошибка аутентификации", @@ -176,6 +175,8 @@ "More" : "Больше", "Less" : "Меньше", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Лог-файл - больше 100 мб. Его скачивание может занять некоторое время!", + "Transactional File Locking is enabled." : "Транзакционная блокировка файлов включена.", + "Transactional File Locking is disabled." : "Транзакционная блокировка файлов отключена.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "В качестве базы данных используется SQLite. Для больших установок мы рекомендуем переключиться на другую серверную базу данных.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Особенно вызывает сомнение использование SQLite при синхронизации файлов с использование клиента для ПК.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Для перехода на другую базу данных используйте команду: 'occ db:convert-type' или ознакомьтесь с <a target=\"_blank\" href=\"%s\">документацией ↗</a>.", @@ -185,8 +186,6 @@ "Improving the config.php" : "Улучшение config.php", "Theming" : "Темы оформления", "Hardening and security guidance" : "Руководство по безопасности и защите", - "Transactional File Locking is enabled." : "Транзакционная блокировка файлов включена.", - "Transactional File Locking is disabled." : "Транзакционная блокировка файлов отключена.", "Version" : "Версия", "Developer documentation" : "Документация для разработчиков", "Experimental applications ahead" : "Экспериментальные приложения", @@ -233,7 +232,6 @@ "Upload new" : "Загрузить новый", "Select new from Files" : "Выберите новый из файлов", "Remove image" : "Удалить аватар", - "Either png or jpg. Ideally square but you will be able to crop it." : "Допустимые форматы: png и jpg. Если изображение не квадратное, то вам будет предложено обрезать его.", "Your avatar is provided by your original account." : "Будет использован аватар вашей оригинальной учетной записи.", "Cancel" : "Отмена", "Choose as profile image" : "Установить как аватар", diff --git a/settings/l10n/sk_SK.js b/settings/l10n/sk_SK.js index b09ea77607e..67457a3021c 100644 --- a/settings/l10n/sk_SK.js +++ b/settings/l10n/sk_SK.js @@ -10,7 +10,6 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "Email server", "Log" : "Záznam", - "Server Status" : "Stav servera", "Tips & tricks" : "Tipy a triky", "Updates" : "Aktualizácie", "Authentication error" : "Chyba autentifikácie", @@ -165,6 +164,8 @@ OC.L10N.register( "More" : "Viac", "Less" : "Menej", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Súbor protokolu je väčší ako 100 MB. Jeho sťahovanie môže chvíľu trvať!", + "Transactional File Locking is enabled." : "Transakčné zamykanie súboru je povolené.", + "Transactional File Locking is disabled." : "Transakčné zamykanie súboru je zakázané.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "Ako databáza je použitá SQLite. Pre veľké inštalácie odporúčame prejsť na inú databázu.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Najmä pri používaní aplikácie na synchronizáciu s desktopom nie je databáza SQLite doporučená.", "How to do backups" : "Ako vytvárať zálohy", @@ -173,8 +174,6 @@ OC.L10N.register( "Improving the config.php" : "Zlepšenie config.php", "Theming" : "Vzhľady tém", "Hardening and security guidance" : "Sprievodca vylepšením bezpečnosti", - "Transactional File Locking is enabled." : "Transakčné zamykanie súboru je povolené.", - "Transactional File Locking is disabled." : "Transakčné zamykanie súboru je zakázané.", "Version" : "Verzia", "Developer documentation" : "Dokumentácia vývojára", "Experimental applications ahead" : "Experimentálna aplikácia v poradí", @@ -221,7 +220,6 @@ OC.L10N.register( "Upload new" : "Nahrať nový", "Select new from Files" : "Vyberte nový zo súborov", "Remove image" : "Zmazať obrázok", - "Either png or jpg. Ideally square but you will be able to crop it." : "Formát súboru png alebo jpg. V ideálnom prípade štvorec, ale budete mať možnosť ho orezať.", "Your avatar is provided by your original account." : "Váš avatar je použitý z pôvodného účtu.", "Cancel" : "Zrušiť", "Choose as profile image" : "Vybrať ako avatara", diff --git a/settings/l10n/sk_SK.json b/settings/l10n/sk_SK.json index 0d11a98c6e1..f08db3ba0ad 100644 --- a/settings/l10n/sk_SK.json +++ b/settings/l10n/sk_SK.json @@ -8,7 +8,6 @@ "Cron" : "Cron", "Email server" : "Email server", "Log" : "Záznam", - "Server Status" : "Stav servera", "Tips & tricks" : "Tipy a triky", "Updates" : "Aktualizácie", "Authentication error" : "Chyba autentifikácie", @@ -163,6 +162,8 @@ "More" : "Viac", "Less" : "Menej", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Súbor protokolu je väčší ako 100 MB. Jeho sťahovanie môže chvíľu trvať!", + "Transactional File Locking is enabled." : "Transakčné zamykanie súboru je povolené.", + "Transactional File Locking is disabled." : "Transakčné zamykanie súboru je zakázané.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "Ako databáza je použitá SQLite. Pre veľké inštalácie odporúčame prejsť na inú databázu.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Najmä pri používaní aplikácie na synchronizáciu s desktopom nie je databáza SQLite doporučená.", "How to do backups" : "Ako vytvárať zálohy", @@ -171,8 +172,6 @@ "Improving the config.php" : "Zlepšenie config.php", "Theming" : "Vzhľady tém", "Hardening and security guidance" : "Sprievodca vylepšením bezpečnosti", - "Transactional File Locking is enabled." : "Transakčné zamykanie súboru je povolené.", - "Transactional File Locking is disabled." : "Transakčné zamykanie súboru je zakázané.", "Version" : "Verzia", "Developer documentation" : "Dokumentácia vývojára", "Experimental applications ahead" : "Experimentálna aplikácia v poradí", @@ -219,7 +218,6 @@ "Upload new" : "Nahrať nový", "Select new from Files" : "Vyberte nový zo súborov", "Remove image" : "Zmazať obrázok", - "Either png or jpg. Ideally square but you will be able to crop it." : "Formát súboru png alebo jpg. V ideálnom prípade štvorec, ale budete mať možnosť ho orezať.", "Your avatar is provided by your original account." : "Váš avatar je použitý z pôvodného účtu.", "Cancel" : "Zrušiť", "Choose as profile image" : "Vybrať ako avatara", diff --git a/settings/l10n/sl.js b/settings/l10n/sl.js index e4fc35cd30a..b1ccf3058a3 100644 --- a/settings/l10n/sl.js +++ b/settings/l10n/sl.js @@ -10,7 +10,6 @@ OC.L10N.register( "Cron" : "Periodično opravilo", "Email server" : "Poštni strežnik", "Log" : "Dnevnik", - "Server Status" : "Stanje strežnika", "Tips & tricks" : "Nasveti in triki", "Updates" : "Posodobitve", "Authentication error" : "Napaka med overjanjem", @@ -208,7 +207,6 @@ OC.L10N.register( "Upload new" : "Pošlji novo", "Select new from Files" : "Izberi novo iz menija datotek", "Remove image" : "Odstrani sliko", - "Either png or jpg. Ideally square but you will be able to crop it." : "Slika je lahko png ali jpg. Slika naj bo kvadratna, ni pa to pogoj, saj jo bo mogoče obrezati.", "Your avatar is provided by your original account." : "Podoba je podana v izvornem računu.", "Cancel" : "Prekliči", "Choose as profile image" : "Izberi kot sliko profila", diff --git a/settings/l10n/sl.json b/settings/l10n/sl.json index 3d01ae0bc55..09dc9ad37f1 100644 --- a/settings/l10n/sl.json +++ b/settings/l10n/sl.json @@ -8,7 +8,6 @@ "Cron" : "Periodično opravilo", "Email server" : "Poštni strežnik", "Log" : "Dnevnik", - "Server Status" : "Stanje strežnika", "Tips & tricks" : "Nasveti in triki", "Updates" : "Posodobitve", "Authentication error" : "Napaka med overjanjem", @@ -206,7 +205,6 @@ "Upload new" : "Pošlji novo", "Select new from Files" : "Izberi novo iz menija datotek", "Remove image" : "Odstrani sliko", - "Either png or jpg. Ideally square but you will be able to crop it." : "Slika je lahko png ali jpg. Slika naj bo kvadratna, ni pa to pogoj, saj jo bo mogoče obrezati.", "Your avatar is provided by your original account." : "Podoba je podana v izvornem računu.", "Cancel" : "Prekliči", "Choose as profile image" : "Izberi kot sliko profila", diff --git a/settings/l10n/sr.js b/settings/l10n/sr.js index ea20901670c..4a48261c8de 100644 --- a/settings/l10n/sr.js +++ b/settings/l10n/sr.js @@ -10,7 +10,6 @@ OC.L10N.register( "Cron" : "Крон", "Email server" : "Сервер е-поште", "Log" : "Бележење", - "Server Status" : "Стање сервера", "Tips & tricks" : "Савети и трикови", "Updates" : "Ажурирања", "Authentication error" : "Грешка при провери идентитета", @@ -178,6 +177,8 @@ OC.L10N.register( "More" : "Више", "Less" : "Мање", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Фајл записника је већи од 100МБ. Преузимање може потрајати!", + "Transactional File Locking is enabled." : "Трансакционо закључавање фајла је укључено.", + "Transactional File Locking is disabled." : "Трансакционо закључавање фајла је искључено.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite се користи као базе података. За веће инсталације препоручујемо да се пребаците на други модел базе података.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Нарочито ако се користи клијент програм у графичком окружењу, коришћење СКуЛајта није препоручљиво.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "За миграцију на другу базу користите командну линију: 'occ db:convert-type', или погледајте <a target=\"_blank\" href=\"%s\">документацију ↗</a>.", @@ -187,8 +188,6 @@ OC.L10N.register( "Improving the config.php" : "Побољшање фајла поставки", "Theming" : "Теме", "Hardening and security guidance" : "Ојачавање система и безбедносне препоруке", - "Transactional File Locking is enabled." : "Трансакционо закључавање фајла је укључено.", - "Transactional File Locking is disabled." : "Трансакционо закључавање фајла је искључено.", "Version" : "Верзија", "Developer documentation" : "Програмерска документација", "Experimental applications ahead" : "Експериментална апликација", @@ -236,7 +235,6 @@ OC.L10N.register( "Upload new" : "Отпреми нову", "Select new from Files" : "Изабери нову из фајлова", "Remove image" : "Уклони слику", - "Either png or jpg. Ideally square but you will be able to crop it." : "Или ПНГ или ЈПГ. Идеално би било квадратну али моћи ћете да је опсечете.", "Your avatar is provided by your original account." : "Ваш аватар је добијен са оригиналног налога.", "Cancel" : "Одустани", "Choose as profile image" : "Изаберите слику профила", diff --git a/settings/l10n/sr.json b/settings/l10n/sr.json index 7ba90354f39..4dc73a2df7a 100644 --- a/settings/l10n/sr.json +++ b/settings/l10n/sr.json @@ -8,7 +8,6 @@ "Cron" : "Крон", "Email server" : "Сервер е-поште", "Log" : "Бележење", - "Server Status" : "Стање сервера", "Tips & tricks" : "Савети и трикови", "Updates" : "Ажурирања", "Authentication error" : "Грешка при провери идентитета", @@ -176,6 +175,8 @@ "More" : "Више", "Less" : "Мање", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Фајл записника је већи од 100МБ. Преузимање може потрајати!", + "Transactional File Locking is enabled." : "Трансакционо закључавање фајла је укључено.", + "Transactional File Locking is disabled." : "Трансакционо закључавање фајла је искључено.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite се користи као базе података. За веће инсталације препоручујемо да се пребаците на други модел базе података.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Нарочито ако се користи клијент програм у графичком окружењу, коришћење СКуЛајта није препоручљиво.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "За миграцију на другу базу користите командну линију: 'occ db:convert-type', или погледајте <a target=\"_blank\" href=\"%s\">документацију ↗</a>.", @@ -185,8 +186,6 @@ "Improving the config.php" : "Побољшање фајла поставки", "Theming" : "Теме", "Hardening and security guidance" : "Ојачавање система и безбедносне препоруке", - "Transactional File Locking is enabled." : "Трансакционо закључавање фајла је укључено.", - "Transactional File Locking is disabled." : "Трансакционо закључавање фајла је искључено.", "Version" : "Верзија", "Developer documentation" : "Програмерска документација", "Experimental applications ahead" : "Експериментална апликација", @@ -234,7 +233,6 @@ "Upload new" : "Отпреми нову", "Select new from Files" : "Изабери нову из фајлова", "Remove image" : "Уклони слику", - "Either png or jpg. Ideally square but you will be able to crop it." : "Или ПНГ или ЈПГ. Идеално би било квадратну али моћи ћете да је опсечете.", "Your avatar is provided by your original account." : "Ваш аватар је добијен са оригиналног налога.", "Cancel" : "Одустани", "Choose as profile image" : "Изаберите слику профила", diff --git a/settings/l10n/sv.js b/settings/l10n/sv.js index dc6e5a5393b..703add8fac2 100644 --- a/settings/l10n/sv.js +++ b/settings/l10n/sv.js @@ -167,7 +167,6 @@ OC.L10N.register( "Upload new" : "Ladda upp ny", "Select new from Files" : "Välj ny från filer", "Remove image" : "Radera bild", - "Either png or jpg. Ideally square but you will be able to crop it." : "Antingen png eller jpg. Helst fyrkantig, men du kommer att kunna beskära den.", "Your avatar is provided by your original account." : "Din avatar tillhandahålls av ditt ursprungliga konto.", "Cancel" : "Avbryt", "Choose as profile image" : "Välj som profilbild", diff --git a/settings/l10n/sv.json b/settings/l10n/sv.json index 94401b23ded..c7d0a8eba44 100644 --- a/settings/l10n/sv.json +++ b/settings/l10n/sv.json @@ -165,7 +165,6 @@ "Upload new" : "Ladda upp ny", "Select new from Files" : "Välj ny från filer", "Remove image" : "Radera bild", - "Either png or jpg. Ideally square but you will be able to crop it." : "Antingen png eller jpg. Helst fyrkantig, men du kommer att kunna beskära den.", "Your avatar is provided by your original account." : "Din avatar tillhandahålls av ditt ursprungliga konto.", "Cancel" : "Avbryt", "Choose as profile image" : "Välj som profilbild", diff --git a/settings/l10n/th_TH.js b/settings/l10n/th_TH.js index 0966a3ac11b..7eef1519b89 100644 --- a/settings/l10n/th_TH.js +++ b/settings/l10n/th_TH.js @@ -10,7 +10,7 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "อีเมลเซิร์ฟเวอร์", "Log" : "บันทึกการเปลี่ยนแปลง", - "Server Status" : "สถานะของเซิร์ฟเวอร์", + "Server status" : "สถานะเซิฟเวอร์", "Tips & tricks" : "เคล็ดลับและเทคนิค", "Updates" : "อัพเดท", "Authentication error" : "เกิดข้อผิดพลาดในสิทธิ์การเข้าใช้งาน", @@ -78,6 +78,7 @@ OC.L10N.register( "Uninstalling ...." : "กำลังถอนการติดตั้ง ...", "Error while uninstalling app" : "เกิดข้อผิดพลาดขณะถอนการติดตั้งแอพพลิเคชัน", "Uninstall" : "ถอนการติดตั้ง", + "An error occurred: {message}" : "เกิดข้อผิดพลาด: {message}", "Select a profile picture" : "เลือกรูปภาพโปรไฟล์", "Very weak password" : "รหัสผ่านระดับต่ำมาก", "Weak password" : "รหัสผ่านระดับต่ำ", @@ -180,6 +181,8 @@ OC.L10N.register( "More" : "มาก", "Less" : "น้อย", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "ไฟล์บันทึกมีขนาดใหญ่กว่า 100 เมกะไบต์ มันอาจจะใช้เวลาดาวน์โหลดนาน!", + "Transactional File Locking is enabled." : "เปิดใช้งานล็อคไฟล์ขณะทำธุรกรรม", + "Transactional File Locking is disabled." : "ปิดใช้งานล็อคไฟล์ขณะทำธุรกรรม", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "จะใช้ SQLite เป็นฐานข้อมูล สำหรับการติดตั้งขนาดใหญ่เราขอแนะนำเพื่อสลับไปยังฐานข้อมูลแบ็กเอนด์ที่แตกต่างกัน", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "โดยเฉพาะอย่างยิ่งเมื่อใช้ไคลเอนต์เดสก์ทอปสำหรับการผสานข้อมูลโดย SQLite", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "การโยกย้ายไปยังฐานข้อมูลอื่นใช้เครื่องมือบรรทัดคำสั่ง: 'occ db:convert-type' หรือดูที่ <a target=\"_blank\" href=\"%s\">เอกสาร</a>", @@ -189,8 +192,6 @@ OC.L10N.register( "Improving the config.php" : "ปรับปรุงไฟล์ config.php", "Theming" : "ชุดรูปแบบ", "Hardening and security guidance" : "คำแนะนำการรักษาความปลอดภัย", - "Transactional File Locking is enabled." : "เปิดใช้งานล็อคไฟล์ขณะทำธุรกรรม", - "Transactional File Locking is disabled." : "ปิดใช้งานล็อคไฟล์ขณะทำธุรกรรม", "Version" : "รุ่น", "Developer documentation" : "เอกสารสำหรับนักพัฒนา", "Experimental applications ahead" : "การใช้งานก่อนการทดลอง", @@ -238,7 +239,7 @@ OC.L10N.register( "Upload new" : "อัพโหลดใหม่", "Select new from Files" : "เลือกใหม่จากไฟล์", "Remove image" : "ลบรูปภาพ", - "Either png or jpg. Ideally square but you will be able to crop it." : "ไฟล์ PNG และ JPG ควรจะเป็นสี่เหลี่ยม แต่คุณจะสามารถที่จะครอบตัดมัน", + "Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB." : "การครอบตัดรูปภาพ .PNG หรือ .JPG ให้รูปเป็นสี่เหลี่ยม ไฟล์ต้องมีขนาดไม่เกิน 20 เมกะไบต์", "Your avatar is provided by your original account." : "อวตาลของคุณถูกระบุโดยบัญชีเดิมของคุณ", "Cancel" : "ยกเลิก", "Choose as profile image" : "เลือกรูปภาพโปรไฟล์", diff --git a/settings/l10n/th_TH.json b/settings/l10n/th_TH.json index 78d04cf3f61..8659b55d528 100644 --- a/settings/l10n/th_TH.json +++ b/settings/l10n/th_TH.json @@ -8,7 +8,7 @@ "Cron" : "Cron", "Email server" : "อีเมลเซิร์ฟเวอร์", "Log" : "บันทึกการเปลี่ยนแปลง", - "Server Status" : "สถานะของเซิร์ฟเวอร์", + "Server status" : "สถานะเซิฟเวอร์", "Tips & tricks" : "เคล็ดลับและเทคนิค", "Updates" : "อัพเดท", "Authentication error" : "เกิดข้อผิดพลาดในสิทธิ์การเข้าใช้งาน", @@ -76,6 +76,7 @@ "Uninstalling ...." : "กำลังถอนการติดตั้ง ...", "Error while uninstalling app" : "เกิดข้อผิดพลาดขณะถอนการติดตั้งแอพพลิเคชัน", "Uninstall" : "ถอนการติดตั้ง", + "An error occurred: {message}" : "เกิดข้อผิดพลาด: {message}", "Select a profile picture" : "เลือกรูปภาพโปรไฟล์", "Very weak password" : "รหัสผ่านระดับต่ำมาก", "Weak password" : "รหัสผ่านระดับต่ำ", @@ -178,6 +179,8 @@ "More" : "มาก", "Less" : "น้อย", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "ไฟล์บันทึกมีขนาดใหญ่กว่า 100 เมกะไบต์ มันอาจจะใช้เวลาดาวน์โหลดนาน!", + "Transactional File Locking is enabled." : "เปิดใช้งานล็อคไฟล์ขณะทำธุรกรรม", + "Transactional File Locking is disabled." : "ปิดใช้งานล็อคไฟล์ขณะทำธุรกรรม", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "จะใช้ SQLite เป็นฐานข้อมูล สำหรับการติดตั้งขนาดใหญ่เราขอแนะนำเพื่อสลับไปยังฐานข้อมูลแบ็กเอนด์ที่แตกต่างกัน", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "โดยเฉพาะอย่างยิ่งเมื่อใช้ไคลเอนต์เดสก์ทอปสำหรับการผสานข้อมูลโดย SQLite", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "การโยกย้ายไปยังฐานข้อมูลอื่นใช้เครื่องมือบรรทัดคำสั่ง: 'occ db:convert-type' หรือดูที่ <a target=\"_blank\" href=\"%s\">เอกสาร</a>", @@ -187,8 +190,6 @@ "Improving the config.php" : "ปรับปรุงไฟล์ config.php", "Theming" : "ชุดรูปแบบ", "Hardening and security guidance" : "คำแนะนำการรักษาความปลอดภัย", - "Transactional File Locking is enabled." : "เปิดใช้งานล็อคไฟล์ขณะทำธุรกรรม", - "Transactional File Locking is disabled." : "ปิดใช้งานล็อคไฟล์ขณะทำธุรกรรม", "Version" : "รุ่น", "Developer documentation" : "เอกสารสำหรับนักพัฒนา", "Experimental applications ahead" : "การใช้งานก่อนการทดลอง", @@ -236,7 +237,7 @@ "Upload new" : "อัพโหลดใหม่", "Select new from Files" : "เลือกใหม่จากไฟล์", "Remove image" : "ลบรูปภาพ", - "Either png or jpg. Ideally square but you will be able to crop it." : "ไฟล์ PNG และ JPG ควรจะเป็นสี่เหลี่ยม แต่คุณจะสามารถที่จะครอบตัดมัน", + "Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB." : "การครอบตัดรูปภาพ .PNG หรือ .JPG ให้รูปเป็นสี่เหลี่ยม ไฟล์ต้องมีขนาดไม่เกิน 20 เมกะไบต์", "Your avatar is provided by your original account." : "อวตาลของคุณถูกระบุโดยบัญชีเดิมของคุณ", "Cancel" : "ยกเลิก", "Choose as profile image" : "เลือกรูปภาพโปรไฟล์", diff --git a/settings/l10n/tr.js b/settings/l10n/tr.js index bd6e254d3ec..a6afbd1f65a 100644 --- a/settings/l10n/tr.js +++ b/settings/l10n/tr.js @@ -10,7 +10,6 @@ OC.L10N.register( "Cron" : "Cron", "Email server" : "E-posta sunucusu", "Log" : "Günlük", - "Server Status" : "Sunucu Durumu", "Tips & tricks" : "İpuçları ve hileler", "Updates" : "Güncellemeler", "Authentication error" : "Kimlik doğrulama hatası", @@ -178,6 +177,8 @@ OC.L10N.register( "More" : "Daha fazla", "Less" : "Daha az", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Günlük dosyası 100 MB'dan daha büyük. İndirmek zaman alabilir!", + "Transactional File Locking is enabled." : "İşlemsel Dosya Kilidi etkin.", + "Transactional File Locking is disabled." : "İşlemsel Dosya Kilidi devre dışı.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "Veritabanı olarak SQLite kullanılıyor. Daha büyük kurulumlar için farklı bir veritabanı arka ucuna geçmenizi öneriyoruz.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Özellikle dosya eşitleme için masaüstü istemcisi kullanılırken SQLite kullanımı önerilmez.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Başka bir veritabanına geçmek için komut satırı aracını kullanın: 'occ db:convert-type' veya <a target=\"_blank\" href=\"%s\">belgelendirmeye ↗</a> bakın.", @@ -187,8 +188,6 @@ OC.L10N.register( "Improving the config.php" : "config.php iyileştirme", "Theming" : "Tema", "Hardening and security guidance" : "Sağlamlaştırma ve güvenlik rehberliği", - "Transactional File Locking is enabled." : "İşlemsel Dosya Kilidi etkin.", - "Transactional File Locking is disabled." : "İşlemsel Dosya Kilidi devre dışı.", "Version" : "Sürüm", "Developer documentation" : "Geliştirici belgelendirmesi", "Experimental applications ahead" : "İlerideki deneysel uygulamalar", @@ -235,7 +234,6 @@ OC.L10N.register( "Upload new" : "Yeni yükle", "Select new from Files" : "Dosyalardan seç", "Remove image" : "Resmi kaldır", - "Either png or jpg. Ideally square but you will be able to crop it." : "PNG veya JPG. Genellikle karedir ancak kesebileceksiniz.", "Your avatar is provided by your original account." : "Görüntü resminiz, özgün hesabınız tarafından sağlanıyor.", "Cancel" : "İptal", "Choose as profile image" : "Profil resmi olarak seç", diff --git a/settings/l10n/tr.json b/settings/l10n/tr.json index f4a8fb93b57..79923ef74f0 100644 --- a/settings/l10n/tr.json +++ b/settings/l10n/tr.json @@ -8,7 +8,6 @@ "Cron" : "Cron", "Email server" : "E-posta sunucusu", "Log" : "Günlük", - "Server Status" : "Sunucu Durumu", "Tips & tricks" : "İpuçları ve hileler", "Updates" : "Güncellemeler", "Authentication error" : "Kimlik doğrulama hatası", @@ -176,6 +175,8 @@ "More" : "Daha fazla", "Less" : "Daha az", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Günlük dosyası 100 MB'dan daha büyük. İndirmek zaman alabilir!", + "Transactional File Locking is enabled." : "İşlemsel Dosya Kilidi etkin.", + "Transactional File Locking is disabled." : "İşlemsel Dosya Kilidi devre dışı.", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "Veritabanı olarak SQLite kullanılıyor. Daha büyük kurulumlar için farklı bir veritabanı arka ucuna geçmenizi öneriyoruz.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Özellikle dosya eşitleme için masaüstü istemcisi kullanılırken SQLite kullanımı önerilmez.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Başka bir veritabanına geçmek için komut satırı aracını kullanın: 'occ db:convert-type' veya <a target=\"_blank\" href=\"%s\">belgelendirmeye ↗</a> bakın.", @@ -185,8 +186,6 @@ "Improving the config.php" : "config.php iyileştirme", "Theming" : "Tema", "Hardening and security guidance" : "Sağlamlaştırma ve güvenlik rehberliği", - "Transactional File Locking is enabled." : "İşlemsel Dosya Kilidi etkin.", - "Transactional File Locking is disabled." : "İşlemsel Dosya Kilidi devre dışı.", "Version" : "Sürüm", "Developer documentation" : "Geliştirici belgelendirmesi", "Experimental applications ahead" : "İlerideki deneysel uygulamalar", @@ -233,7 +232,6 @@ "Upload new" : "Yeni yükle", "Select new from Files" : "Dosyalardan seç", "Remove image" : "Resmi kaldır", - "Either png or jpg. Ideally square but you will be able to crop it." : "PNG veya JPG. Genellikle karedir ancak kesebileceksiniz.", "Your avatar is provided by your original account." : "Görüntü resminiz, özgün hesabınız tarafından sağlanıyor.", "Cancel" : "İptal", "Choose as profile image" : "Profil resmi olarak seç", diff --git a/settings/l10n/uk.js b/settings/l10n/uk.js index 093e9a57e57..51a7e526365 100644 --- a/settings/l10n/uk.js +++ b/settings/l10n/uk.js @@ -220,7 +220,6 @@ OC.L10N.register( "Upload new" : "Завантажити нове", "Select new from Files" : "Обрати із завантажених файлів", "Remove image" : "Видалити зображення", - "Either png or jpg. Ideally square but you will be able to crop it." : "Допустимі формати: png і jpg. Якщо зображення не квадратне, то ви зможете його обрізати.", "Your avatar is provided by your original account." : "Буде використано аватар вашого оригінального облікового запису.", "Cancel" : "Відмінити", "Choose as profile image" : "Обрати зображенням облікового запису", diff --git a/settings/l10n/uk.json b/settings/l10n/uk.json index d63210e88e3..29002db8d02 100644 --- a/settings/l10n/uk.json +++ b/settings/l10n/uk.json @@ -218,7 +218,6 @@ "Upload new" : "Завантажити нове", "Select new from Files" : "Обрати із завантажених файлів", "Remove image" : "Видалити зображення", - "Either png or jpg. Ideally square but you will be able to crop it." : "Допустимі формати: png і jpg. Якщо зображення не квадратне, то ви зможете його обрізати.", "Your avatar is provided by your original account." : "Буде використано аватар вашого оригінального облікового запису.", "Cancel" : "Відмінити", "Choose as profile image" : "Обрати зображенням облікового запису", diff --git a/settings/l10n/zh_CN.js b/settings/l10n/zh_CN.js index f769e00142d..e8703fa98ff 100644 --- a/settings/l10n/zh_CN.js +++ b/settings/l10n/zh_CN.js @@ -7,7 +7,6 @@ OC.L10N.register( "Cron" : "计划任务", "Email server" : "电子邮件服务器", "Log" : "日志", - "Server Status" : "服务器状态", "Tips & tricks" : "技巧提示", "Updates" : "更新", "Authentication error" : "认证错误", @@ -160,7 +159,6 @@ OC.L10N.register( "Upload new" : "上传新的", "Select new from Files" : "从文件中选择一个新的", "Remove image" : "移除图片", - "Either png or jpg. Ideally square but you will be able to crop it." : "png 或 jpg。正方形比较理想但你也可以之后对其进行裁剪。", "Your avatar is provided by your original account." : "您的头像由您的原始账户所提供。", "Cancel" : "取消", "Choose as profile image" : "用作头像", diff --git a/settings/l10n/zh_CN.json b/settings/l10n/zh_CN.json index d70e7045458..140962eabad 100644 --- a/settings/l10n/zh_CN.json +++ b/settings/l10n/zh_CN.json @@ -5,7 +5,6 @@ "Cron" : "计划任务", "Email server" : "电子邮件服务器", "Log" : "日志", - "Server Status" : "服务器状态", "Tips & tricks" : "技巧提示", "Updates" : "更新", "Authentication error" : "认证错误", @@ -158,7 +157,6 @@ "Upload new" : "上传新的", "Select new from Files" : "从文件中选择一个新的", "Remove image" : "移除图片", - "Either png or jpg. Ideally square but you will be able to crop it." : "png 或 jpg。正方形比较理想但你也可以之后对其进行裁剪。", "Your avatar is provided by your original account." : "您的头像由您的原始账户所提供。", "Cancel" : "取消", "Choose as profile image" : "用作头像", diff --git a/settings/l10n/zh_TW.js b/settings/l10n/zh_TW.js index e023bded57f..69298cb94e5 100644 --- a/settings/l10n/zh_TW.js +++ b/settings/l10n/zh_TW.js @@ -113,7 +113,6 @@ OC.L10N.register( "Upload new" : "上傳新的", "Select new from Files" : "從已上傳的檔案中選一個", "Remove image" : "移除圖片", - "Either png or jpg. Ideally square but you will be able to crop it." : "可以使用 png 或 jpg 格式,最好是方形的,但是您之後也可以裁剪它", "Your avatar is provided by your original account." : "您的圖像是由您原來的帳號所提供的。", "Cancel" : "取消", "Choose as profile image" : "設定為大頭貼", diff --git a/settings/l10n/zh_TW.json b/settings/l10n/zh_TW.json index 307ae041b63..2d7122fd2bc 100644 --- a/settings/l10n/zh_TW.json +++ b/settings/l10n/zh_TW.json @@ -111,7 +111,6 @@ "Upload new" : "上傳新的", "Select new from Files" : "從已上傳的檔案中選一個", "Remove image" : "移除圖片", - "Either png or jpg. Ideally square but you will be able to crop it." : "可以使用 png 或 jpg 格式,最好是方形的,但是您之後也可以裁剪它", "Your avatar is provided by your original account." : "您的圖像是由您原來的帳號所提供的。", "Cancel" : "取消", "Choose as profile image" : "設定為大頭貼", diff --git a/settings/personal.php b/settings/personal.php index 8823102e01a..203c9f68af8 100644 --- a/settings/personal.php +++ b/settings/personal.php @@ -104,6 +104,17 @@ $clients = array( 'ios' => $config->getSystemValue('customclient_ios', $defaults->getiOSClientUrl()) ); +// only show root certificate import if external storages are enabled +$enableCertImport = false; +$externalStorageEnabled = \OC::$server->getAppManager()->isEnabledForUser('files_external'); +if ($externalStorageEnabled) { + $backends = OC_Mount_Config::getPersonalBackends(); + if (!empty($backends)) { + $enableCertImport = true; + } +} + + // Return template $tmpl = new OC_Template( 'settings', 'personal', 'user'); $tmpl->assign('usage', OC_Helper::humanFileSize($storageInfo['used'])); @@ -120,6 +131,7 @@ $tmpl->assign('displayName', OC_User::getDisplayName()); $tmpl->assign('enableAvatars', $config->getSystemValue('enable_avatars', true)); $tmpl->assign('avatarChangeSupported', OC_User::canUserChangeAvatar(OC_User::getUser())); $tmpl->assign('certs', $certificateManager->listCertificates()); +$tmpl->assign('showCertificates', $enableCertImport); $tmpl->assign('urlGenerator', $urlGenerator); // Get array of group ids for this user @@ -157,7 +169,11 @@ $formsMap = array_map(function($form){ $formsAndMore = array_merge($formsAndMore, $formsMap); // add bottom hardcoded forms from the template -$formsAndMore[]= array( 'anchor' => 'ssl-root-certificates', 'section-name' => $l->t('SSL root certificates') ); +if($enableCertImport) { + $formsAndMore[]= array( 'anchor' => 'ssl-root-certificates', 'section-name' => $l->t('SSL root certificates') ); +} + + $tmpl->assign('forms', $formsAndMore); $tmpl->printPage(); diff --git a/settings/templates/admin.php b/settings/templates/admin.php index a55071bdf84..ff8a2f0c953 100644 --- a/settings/templates/admin.php +++ b/settings/templates/admin.php @@ -326,10 +326,17 @@ if ($_['cronErrors']) { </p> <div id="EncryptionWarning" class="warning hidden"> - <?php p($l->t('Encryption is a one way process. Once encryption is enabled, all files from that point forward will be encrypted on the server and it will not be possible to disable encryption at a later date. This is the final warning: Do you really want to enable encryption?')) ?> - <input type="button" + <p><?php p($l->t('Please read carefully before activating server-side encryption: ')); ?></p> + <ul> + <li><?php p($l->t('Server-side encryption is a one way process. Once encryption is enabled, all files from that point forward will be encrypted on the server and it will not be possible to disable encryption at a later date')); ?></li> + <li><?php p($l->t('Anyone who has privileged access to your ownCloud server can decrypt your files either by intercepting requests or reading out user passwords which are stored in plain text session files. Server-side encryption does therefore not protect against malicious administrators but is useful for protecting your data on externally hosted storage.')); ?></li> + <li><?php p($l->t('Depending on the actual encryption module the general file size is increased (by 35%% or more when using the default module)')); ?></li> + <li><?php p($l->t('You should regularly backup all encryption keys to prevent permanent data loss (data/<user>/files_encryption and data/files_encryption)')); ?></li> + </ul> + + <p><?php p($l->t('This is the final warning: Do you really want to enable encryption?')) ?> <input type="button" id="reallyEnableEncryption" - value="<?php p($l->t("Enable encryption")); ?>" /> + value="<?php p($l->t("Enable encryption")); ?>" /></p> </div> <div id="EncryptionSettingsArea" class="<?php if (!$_['encryptionEnabled']) p('hidden'); ?>"> @@ -507,6 +514,19 @@ if ($_['cronErrors']) { <?php endif; ?> </div> +<div class="section" id="server-status"> + <h2><?php p($l->t('Server status'));?></h2> + <ul> + <li> + <?php if ($_['fileLockingEnabled']) { + p($l->t('Transactional File Locking is enabled.')); + } else { + p($l->t('Transactional File Locking is disabled.')); + } ?> + </li> + </ul> +</div> + <div class="section" id="admin-tips"> <h2><?php p($l->t('Tips & tricks'));?></h2> <ul> @@ -528,18 +548,6 @@ if ($_['cronErrors']) { <li><a target="_blank" href="<?php p(link_to_docs('admin-security')); ?>"><?php p($l->t('Hardening and security guidance'));?> ↗</a></li> </ul> </div> -<div class="section" id="server-status"> - <h2><?php p($l->t('Server Status'));?></h2> - <ul> - <li> - <?php if ($_['fileLockingEnabled']) { - p($l->t('Transactional File Locking is enabled.')); - } else { - p($l->t('Transactional File Locking is disabled.')); - } ?> - </li> - </ul> -</div> <div class="section"> <h2><?php p($l->t('Version'));?></h2> diff --git a/settings/templates/personal.php b/settings/templates/personal.php index 02ee261cd1d..490133c9f25 100644 --- a/settings/templates/personal.php +++ b/settings/templates/personal.php @@ -159,7 +159,7 @@ if($_['passwordChangeSupported']) { <input type="file" class="hidden" name="files[]" id="uploadavatar"> <div class="inlineblock button" id="selectavatar"><?php p($l->t('Select new from Files')); ?></div> <div class="inlineblock button" id="removeavatar"><?php p($l->t('Remove image')); ?></div><br> - <?php p($l->t('Either png or jpg. Ideally square but you will be able to crop it.')); ?> + <?php p($l->t('Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB.')); ?> <?php else: ?> <?php p($l->t('Your avatar is provided by your original account.')); ?> <?php endif; ?> @@ -205,6 +205,7 @@ if($_['passwordChangeSupported']) { <?php } };?> +<?php if($_['showCertificates']) : ?> <div id="ssl-root-certificates" class="section"> <h2><?php p($l->t('SSL root certificates')); ?></h2> <table id="sslCertificate" class="grid"> @@ -242,6 +243,7 @@ if($_['passwordChangeSupported']) { <input type="button" id="rootcert_import_button" value="<?php p($l->t('Import root certificate')); ?>"/> </form> </div> +<?php endif; ?> <div class="section"> <h2><?php p($l->t('Version'));?></h2> diff --git a/settings/templates/users/part.createuser.php b/settings/templates/users/part.createuser.php index 9d9886f694c..0fc5a2bdeaa 100644 --- a/settings/templates/users/part.createuser.php +++ b/settings/templates/users/part.createuser.php @@ -31,7 +31,4 @@ alt="<?php p($l->t('Enter the recovery password in order to recover the users files during password change'))?>"/> </div> <?php endif; ?> - <form autocomplete="off" id="usersearchform"> - <input type="text" class="input userFilter" placeholder="<?php p($l->t('Search Users')); ?>" /> - </form> </div> diff --git a/tests/core/avatar/avatarcontrollertest.php b/tests/core/avatar/avatarcontrollertest.php index 0a85fbb27f1..952e013bb8f 100644 --- a/tests/core/avatar/avatarcontrollertest.php +++ b/tests/core/avatar/avatarcontrollertest.php @@ -23,7 +23,6 @@ namespace OC\Core\Avatar; use OC; use OC\Core\Application; use OCP\AppFramework\IAppContainer; -use OCP\Security\ISecureRandom; use OC\Files\Filesystem; use OCP\AppFramework\Http; use OCP\Image; @@ -264,7 +263,7 @@ class AvatarControllerTest extends \Test\TestCase { $view->file_put_contents('avatar_upload', file_get_contents(OC::$SERVERROOT.'/tests/data/testimage.jpg')); //Create request return - $reqRet = ['error' => [0], 'tmp_name' => [$fileName]]; + $reqRet = ['error' => [0], 'tmp_name' => [$fileName], 'size' => [filesize(OC::$SERVERROOT.'/tests/data/testimage.jpg')]]; $this->container['Request']->method('getUploadedFile')->willReturn($reqRet); $response = $this->avatarController->postAvatar(null); @@ -303,7 +302,7 @@ class AvatarControllerTest extends \Test\TestCase { $view->file_put_contents('avatar_upload', file_get_contents(OC::$SERVERROOT.'/tests/data/testimage.gif')); //Create request return - $reqRet = ['error' => [0], 'tmp_name' => [$fileName]]; + $reqRet = ['error' => [0], 'tmp_name' => [$fileName], 'size' => filesize(OC::$SERVERROOT.'/tests/data/testimage.gif')]; $this->container['Request']->method('getUploadedFile')->willReturn($reqRet); $response = $this->avatarController->postAvatar(null); @@ -330,7 +329,7 @@ class AvatarControllerTest extends \Test\TestCase { } /** - * Test invalid crop argment + * Test invalid crop argument */ public function testPostCroppedAvatarInvalidCrop() { $response = $this->avatarController->postCroppedAvatar([]); @@ -372,4 +371,18 @@ class AvatarControllerTest extends \Test\TestCase { $this->assertEquals('success', $response->getData()['status']); } + /** + * Check for proper reply on proper crop argument + */ + public function testFileTooBig() { + $fileName = OC::$SERVERROOT.'/tests/data/testimage.jpg'; + //Create request return + $reqRet = ['error' => [0], 'tmp_name' => [$fileName], 'size' => [21*1024*1024]]; + $this->container['Request']->method('getUploadedFile')->willReturn($reqRet); + + $response = $this->avatarController->postAvatar(null); + + $this->assertEquals('File is too big', $response->getData()['data']['message']); + } + } diff --git a/tests/lib/activitymanager.php b/tests/lib/activitymanager.php index a35daeaf494..28caf575948 100644 --- a/tests/lib/activitymanager.php +++ b/tests/lib/activitymanager.php @@ -41,6 +41,9 @@ class Test_ActivityManager extends \Test\TestCase { $this->config ); + $this->activityManager->registerConsumer(function() { + return new NoOpConsumer(); + }); $this->activityManager->registerExtension(function() { return new NoOpExtension(); }); @@ -49,6 +52,40 @@ class Test_ActivityManager extends \Test\TestCase { }); } + public function testGetConsumers() { + $consumers = $this->invokePrivate($this->activityManager, 'getConsumers'); + + $this->assertNotEmpty($consumers); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetConsumersInvalidConsumer() { + $this->activityManager->registerConsumer(function() { + return new StdClass(); + }); + + $this->invokePrivate($this->activityManager, 'getConsumers'); + } + + public function testGetExtensions() { + $extensions = $this->invokePrivate($this->activityManager, 'getExtensions'); + + $this->assertNotEmpty($extensions); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetExtensionsInvalidExtension() { + $this->activityManager->registerExtension(function() { + return new StdClass(); + }); + + $this->invokePrivate($this->activityManager, 'getExtensions'); + } + public function testNotificationTypes() { $result = $this->activityManager->getNotificationTypes('en'); $this->assertTrue(is_array($result)); @@ -328,3 +365,9 @@ class NoOpExtension implements \OCP\Activity\IExtension { return false; } } + +class NoOpConsumer implements \OCP\Activity\IConsumer { + + public function receive($app, $subject, $subjectParams, $message, $messageParams, $file, $link, $affectedUser, $type, $priority) { + } +} diff --git a/tests/lib/appframework/controller/ControllerTest.php b/tests/lib/appframework/controller/ControllerTest.php index ccc373f4d59..0d7716da411 100644 --- a/tests/lib/appframework/controller/ControllerTest.php +++ b/tests/lib/appframework/controller/ControllerTest.php @@ -177,7 +177,7 @@ class ControllerTest extends \Test\TestCase { 'test' => 'something', 'Cache-Control' => 'no-cache, must-revalidate', 'Content-Type' => 'application/json; charset=utf-8', - 'Content-Security-Policy' => "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'", + 'Content-Security-Policy' => "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'", ]; $response = $this->controller->customDataResponse(array('hi')); diff --git a/tests/lib/appframework/controller/OCSControllerTest.php b/tests/lib/appframework/controller/OCSControllerTest.php index 11a9d45eb92..92b092cf0e9 100644 --- a/tests/lib/appframework/controller/OCSControllerTest.php +++ b/tests/lib/appframework/controller/OCSControllerTest.php @@ -69,9 +69,11 @@ class OCSControllerTest extends \Test\TestCase { $expected = "<?xml version=\"1.0\"?>\n" . "<ocs>\n" . " <meta>\n" . - " <status>OK</status>\n" . + " <status>failure</status>\n" . " <statuscode>400</statuscode>\n" . " <message>OK</message>\n" . + " <totalitems></totalitems>\n" . + " <itemsperpage></itemsperpage>\n" . " </meta>\n" . " <data>\n" . " <test>hi</test>\n" . @@ -99,9 +101,11 @@ class OCSControllerTest extends \Test\TestCase { $expected = "<?xml version=\"1.0\"?>\n" . "<ocs>\n" . " <meta>\n" . - " <status>OK</status>\n" . + " <status>failure</status>\n" . " <statuscode>400</statuscode>\n" . " <message>OK</message>\n" . + " <totalitems></totalitems>\n" . + " <itemsperpage></itemsperpage>\n" . " </meta>\n" . " <data>\n" . " <test>hi</test>\n" . @@ -126,8 +130,8 @@ class OCSControllerTest extends \Test\TestCase { $this->getMock('\OCP\Security\ISecureRandom'), $this->getMock('\OCP\IConfig') )); - $expected = '{"status":"OK","statuscode":400,"message":"OK",' . - '"totalitems":"","itemsperpage":"","data":{"test":"hi"}}'; + $expected = '{"ocs":{"meta":{"status":"failure","statuscode":400,"message":"OK",' . + '"totalitems":"","itemsperpage":""},"data":{"test":"hi"}}}'; $params = [ 'data' => [ 'test' => 'hi' diff --git a/tests/lib/appframework/http/ContentSecurityPolicyTest.php b/tests/lib/appframework/http/ContentSecurityPolicyTest.php index 18d71df483f..082c032a420 100644 --- a/tests/lib/appframework/http/ContentSecurityPolicyTest.php +++ b/tests/lib/appframework/http/ContentSecurityPolicyTest.php @@ -28,19 +28,19 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDefault() { - $defaultPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $defaultPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->assertSame($defaultPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyScriptDomainValid() { - $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyScriptDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com www.owncloud.org 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com www.owncloud.org 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.org'); @@ -48,7 +48,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowScriptDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.com'); @@ -56,7 +56,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowScriptDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.org'); @@ -64,7 +64,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowScriptDomainMultipleStacked() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.org')->disallowScriptDomain('www.owncloud.com'); @@ -72,14 +72,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyScriptAllowInline() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->allowInlineScript(true); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyScriptAllowInlineWithDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); $this->contentSecurityPolicy->allowInlineScript(true); @@ -87,7 +87,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyScriptDisallowInlineAndEval() { - $expectedPolicy = "default-src 'none';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->allowInlineScript(false); $this->contentSecurityPolicy->allowEvalScript(false); @@ -95,14 +95,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyStyleDomainValid() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyStyleDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com www.owncloud.org 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com www.owncloud.org 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.org'); @@ -110,7 +110,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowStyleDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.com'); @@ -118,7 +118,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowStyleDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.org'); @@ -126,7 +126,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowStyleDomainMultipleStacked() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.org')->disallowStyleDomain('www.owncloud.com'); @@ -134,35 +134,35 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyStyleAllowInline() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->allowInlineStyle(true); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyStyleAllowInlineWithDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyStyleDisallowInline() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->allowInlineStyle(false); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyImageDomainValid() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' www.owncloud.com;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: www.owncloud.com;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyImageDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' www.owncloud.com www.owncloud.org;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: www.owncloud.com www.owncloud.org;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.org'); @@ -170,7 +170,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowImageDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.com'); @@ -178,7 +178,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowImageDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' www.owncloud.com;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: www.owncloud.com;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.org'); @@ -186,7 +186,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowImageDomainMultipleStakes() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.org')->disallowImageDomain('www.owncloud.com'); @@ -194,14 +194,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyFontDomainValid() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self' www.owncloud.com;connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self' www.owncloud.com;connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyFontDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self' www.owncloud.com www.owncloud.org;connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self' www.owncloud.com www.owncloud.org;connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.org'); @@ -209,7 +209,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowFontDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.com'); @@ -217,7 +217,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowFontDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self' www.owncloud.com;connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self' www.owncloud.com;connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.org'); @@ -225,7 +225,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowFontDomainMultipleStakes() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.org')->disallowFontDomain('www.owncloud.com'); @@ -233,14 +233,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyConnectDomainValid() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self' www.owncloud.com;media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self' www.owncloud.com;media-src 'self'"; $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyConnectDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self' www.owncloud.com www.owncloud.org;media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self' www.owncloud.com www.owncloud.org;media-src 'self'"; $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.org'); @@ -248,7 +248,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowConnectDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.com'); @@ -256,7 +256,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowConnectDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self' www.owncloud.com;media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self' www.owncloud.com;media-src 'self'"; $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.org'); @@ -264,7 +264,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowConnectDomainMultipleStakes() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.org')->disallowConnectDomain('www.owncloud.com'); @@ -272,14 +272,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyMediaDomainValid() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self' www.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self' www.owncloud.com"; $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyMediaDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self' www.owncloud.com www.owncloud.org"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self' www.owncloud.com www.owncloud.org"; $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.org'); @@ -287,7 +287,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowMediaDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.com'); @@ -295,7 +295,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowMediaDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self' www.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self' www.owncloud.com"; $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.org'); @@ -303,7 +303,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowMediaDomainMultipleStakes() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.org')->disallowMediaDomain('www.owncloud.com'); @@ -311,14 +311,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyObjectDomainValid() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self';object-src www.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';object-src www.owncloud.com"; $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyObjectDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self';object-src www.owncloud.com www.owncloud.org"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';object-src www.owncloud.com www.owncloud.org"; $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.org'); @@ -326,7 +326,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowObjectDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.com'); @@ -334,7 +334,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowObjectDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self';object-src www.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';object-src www.owncloud.com"; $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.org'); @@ -342,7 +342,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowObjectDomainMultipleStakes() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.org')->disallowObjectDomain('www.owncloud.com'); @@ -350,14 +350,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetAllowedFrameDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self';frame-src www.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';frame-src www.owncloud.com"; $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyFrameDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self';frame-src www.owncloud.com www.owncloud.org"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';frame-src www.owncloud.com www.owncloud.org"; $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.org'); @@ -365,7 +365,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowFrameDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.com'); @@ -373,7 +373,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowFrameDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self';frame-src www.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';frame-src www.owncloud.com"; $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.org'); @@ -381,7 +381,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowFrameDomainMultipleStakes() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.org')->disallowFrameDomain('www.owncloud.com'); @@ -389,14 +389,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetAllowedChildSrcDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self';child-src child.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';child-src child.owncloud.com"; $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyChildSrcValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self';child-src child.owncloud.com child.owncloud.org"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';child-src child.owncloud.com child.owncloud.org"; $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.com'); $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.org'); @@ -404,7 +404,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowChildSrcDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.com'); @@ -412,7 +412,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowChildSrcDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self';child-src www.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';child-src www.owncloud.com"; $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org'); @@ -420,7 +420,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowChildSrcDomainMultipleStakes() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org')->disallowChildSrcDomain('www.owncloud.com'); @@ -428,7 +428,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testConfigureStacked() { - $expectedPolicy = "default-src 'none';script-src 'self' script.owncloud.org;style-src 'self' style.owncloud.org;img-src 'self' img.owncloud.org;font-src 'self' font.owncloud.org;connect-src 'self' connect.owncloud.org;media-src 'self' media.owncloud.org;object-src objects.owncloud.org;frame-src frame.owncloud.org;child-src child.owncloud.org"; + $expectedPolicy = "default-src 'none';script-src 'self' script.owncloud.org;style-src 'self' style.owncloud.org;img-src 'self' data: img.owncloud.org;font-src 'self' font.owncloud.org;connect-src 'self' connect.owncloud.org;media-src 'self' media.owncloud.org;object-src objects.owncloud.org;frame-src frame.owncloud.org;child-src child.owncloud.org"; $this->contentSecurityPolicy->allowInlineStyle(false) ->allowEvalScript(false) diff --git a/tests/lib/appframework/http/DataResponseTest.php b/tests/lib/appframework/http/DataResponseTest.php index ca0582e10e5..2b7817c28e9 100644 --- a/tests/lib/appframework/http/DataResponseTest.php +++ b/tests/lib/appframework/http/DataResponseTest.php @@ -68,7 +68,7 @@ class DataResponseTest extends \Test\TestCase { $expectedHeaders = [ 'Cache-Control' => 'no-cache, must-revalidate', - 'Content-Security-Policy' => "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'", + 'Content-Security-Policy' => "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'", ]; $expectedHeaders = array_merge($expectedHeaders, $headers); diff --git a/tests/lib/appframework/http/OCSResponseTest.php b/tests/lib/appframework/http/OCSResponseTest.php index 111dc7ad0a3..1ca3e330bad 100644 --- a/tests/lib/appframework/http/OCSResponseTest.php +++ b/tests/lib/appframework/http/OCSResponseTest.php @@ -47,14 +47,13 @@ class OCSResponseTest extends \Test\TestCase { public function testRender() { $response = new OCSResponse( - 'xml', 'status', 2, 'message', ['test' => 'hi'], 'tag', 'abc', - 'dynamic', 3, 4 + 'xml', 2, 'message', ['test' => 'hi'], 3, 4 ); $out = $response->render(); $expected = "<?xml version=\"1.0\"?>\n" . "<ocs>\n" . " <meta>\n" . - " <status>status</status>\n" . + " <status>failure</status>\n" . " <statuscode>2</statuscode>\n" . " <message>message</message>\n" . " <totalitems>3</totalitems>\n" . diff --git a/tests/lib/appframework/http/RequestTest.php b/tests/lib/appframework/http/RequestTest.php index 6e86f3d7041..10a9e486c97 100644 --- a/tests/lib/appframework/http/RequestTest.php +++ b/tests/lib/appframework/http/RequestTest.php @@ -1156,4 +1156,99 @@ class RequestTest extends \Test\TestCase { $this->assertSame($expectedUri, $request->getRequestUri()); } + public function testPassesCSRFCheckWithGet() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'get' => [ + 'requesttoken' => 'MyStoredRequestToken', + ], + 'requesttoken' => 'MyStoredRequestToken', + ], + $this->secureRandom, + $this->config, + $this->stream + ]) + ->getMock(); + + $this->assertTrue($request->passesCSRFCheck()); + } + + public function testPassesCSRFCheckWithPost() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'post' => [ + 'requesttoken' => 'MyStoredRequestToken', + ], + 'requesttoken' => 'MyStoredRequestToken', + ], + $this->secureRandom, + $this->config, + $this->stream + ]) + ->getMock(); + + $this->assertTrue($request->passesCSRFCheck()); + } + + public function testPassesCSRFCheckWithHeader() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'server' => [ + 'HTTP_REQUESTTOKEN' => 'MyStoredRequestToken', + ], + 'requesttoken' => 'MyStoredRequestToken', + ], + $this->secureRandom, + $this->config, + $this->stream + ]) + ->getMock(); + + $this->assertTrue($request->passesCSRFCheck()); + } + + public function testPassesCSRFCheckWithInvalidToken() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'server' => [ + 'HTTP_REQUESTTOKEN' => 'MyInvalidSentToken', + ], + 'requesttoken' => 'MyStoredRequestToken', + ], + $this->secureRandom, + $this->config, + $this->stream + ]) + ->getMock(); + + $this->assertFalse($request->passesCSRFCheck()); + } + + public function testPassesCSRFCheckWithoutTokenFail() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [], + $this->secureRandom, + $this->config, + $this->stream + ]) + ->getMock(); + + $this->assertFalse($request->passesCSRFCheck()); + } + } diff --git a/tests/lib/appframework/http/ResponseTest.php b/tests/lib/appframework/http/ResponseTest.php index c8b79fbd8b6..61dd95e5948 100644 --- a/tests/lib/appframework/http/ResponseTest.php +++ b/tests/lib/appframework/http/ResponseTest.php @@ -58,7 +58,7 @@ class ResponseTest extends \Test\TestCase { $this->childResponse->setHeaders($expected); $headers = $this->childResponse->getHeaders(); - $expected['Content-Security-Policy'] = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'"; + $expected['Content-Security-Policy'] = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; $this->assertEquals($expected, $headers); } diff --git a/tests/lib/appframework/routing/RoutingTest.php b/tests/lib/appframework/routing/RoutingTest.php index 4ee3ed58807..51c191fdfb7 100644 --- a/tests/lib/appframework/routing/RoutingTest.php +++ b/tests/lib/appframework/routing/RoutingTest.php @@ -120,7 +120,8 @@ class RoutingTest extends \Test\TestCase } // route mocks - $route = $this->mockRoute($verb, $controllerName, $actionName, $requirements, $defaults); + $container = new DIContainer('app1'); + $route = $this->mockRoute($container, $verb, $controllerName, $actionName, $requirements, $defaults); // router mock $router = $this->getMock("\OC\Route\Router", array('create')); @@ -133,7 +134,6 @@ class RoutingTest extends \Test\TestCase ->will($this->returnValue($route)); // load route configuration - $container = new DIContainer('app1'); $config = new RouteConfig($container, $router, $routes); $config->register(); @@ -151,11 +151,12 @@ class RoutingTest extends \Test\TestCase $router = $this->getMock("\OC\Route\Router", array('create')); // route mocks - $indexRoute = $this->mockRoute('GET', $controllerName, 'index'); - $showRoute = $this->mockRoute('GET', $controllerName, 'show'); - $createRoute = $this->mockRoute('POST', $controllerName, 'create'); - $updateRoute = $this->mockRoute('PUT', $controllerName, 'update'); - $destroyRoute = $this->mockRoute('DELETE', $controllerName, 'destroy'); + $container = new DIContainer('app1'); + $indexRoute = $this->mockRoute($container, 'GET', $controllerName, 'index'); + $showRoute = $this->mockRoute($container, 'GET', $controllerName, 'show'); + $createRoute = $this->mockRoute($container, 'POST', $controllerName, 'create'); + $updateRoute = $this->mockRoute($container, 'PUT', $controllerName, 'update'); + $destroyRoute = $this->mockRoute($container, 'DELETE', $controllerName, 'destroy'); $urlWithParam = $url . '/{' . $paramName . '}'; @@ -191,21 +192,28 @@ class RoutingTest extends \Test\TestCase ->will($this->returnValue($destroyRoute)); // load route configuration - $container = new DIContainer('app1'); $config = new RouteConfig($container, $router, $yaml); $config->register(); } /** + * @param DIContainer $container * @param string $verb * @param string $controllerName * @param string $actionName + * @param array $requirements + * @param array $defaults * @return \PHPUnit_Framework_MockObject_MockObject */ - private function mockRoute($verb, $controllerName, $actionName, array $requirements=array(), array $defaults=array()) - { - $container = new DIContainer('app1'); + private function mockRoute( + DIContainer $container, + $verb, + $controllerName, + $actionName, + array $requirements=array(), + array $defaults=array() + ) { $route = $this->getMock("\OC\Route\Route", array('method', 'action', 'requirements', 'defaults'), array(), '', false); $route ->expects($this->exactly(1)) diff --git a/tests/lib/appframework/utility/SimpleContainerTest.php b/tests/lib/appframework/utility/SimpleContainerTest.php index d544faf273e..e21e0ca13f2 100644 --- a/tests/lib/appframework/utility/SimpleContainerTest.php +++ b/tests/lib/appframework/utility/SimpleContainerTest.php @@ -136,8 +136,7 @@ class SimpleContainerTest extends \Test\TestCase { } - public function tesOverrideService() { - $this->container->registerParameter('test', 'abc'); + public function testOverrideService() { $this->container->registerService( 'Test\AppFramework\Utility\IInterfaceConstructor', function ($c) { return $c->query('Test\AppFramework\Utility\ClassSimpleConstructor'); @@ -147,10 +146,9 @@ class SimpleContainerTest extends \Test\TestCase { return $c->query('Test\AppFramework\Utility\ClassEmptyConstructor'); }); $object = $this->container->query( - 'Test\AppFramework\Utility\ClassInterfaceConstructor' + 'Test\AppFramework\Utility\IInterfaceConstructor' ); $this->assertTrue($object instanceof ClassEmptyConstructor); - $this->assertEquals('abc', $object->test); } public function testRegisterAliasParamter() { @@ -161,10 +159,15 @@ class SimpleContainerTest extends \Test\TestCase { public function testRegisterAliasService() { $this->container->registerService('test', function() { - return 'abc'; - }); + return new \StdClass; + }, true); $this->container->registerAlias('test1', 'test'); - $this->assertEquals('abc', $this->container->query('test1')); + $this->assertSame( + $this->container->query('test'), $this->container->query('test')); + $this->assertSame( + $this->container->query('test1'), $this->container->query('test1')); + $this->assertSame( + $this->container->query('test'), $this->container->query('test1')); } public function sanitizeNameProvider() { @@ -195,5 +198,25 @@ class SimpleContainerTest extends \Test\TestCase { ); } + public function testRegisterFactory() { + $this->container->registerService('test', function() { + return new \StdClass(); + }, false); + $this->assertNotSame( + $this->container->query('test'), $this->container->query('test')); + } + + public function testRegisterAliasFactory() { + $this->container->registerService('test', function() { + return new \StdClass(); + }, false); + $this->container->registerAlias('test1', 'test'); + $this->assertNotSame( + $this->container->query('test'), $this->container->query('test')); + $this->assertNotSame( + $this->container->query('test1'), $this->container->query('test1')); + $this->assertNotSame( + $this->container->query('test'), $this->container->query('test1')); + } } diff --git a/tests/lib/capabilitiesmanager.php b/tests/lib/capabilitiesmanager.php new file mode 100644 index 00000000000..b5dac80ee51 --- /dev/null +++ b/tests/lib/capabilitiesmanager.php @@ -0,0 +1,164 @@ +<?php +/** + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @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 Test; + +use OC\CapabilitiesManager; + +class CapabilitiesManagerTest extends TestCase { + + /** + * Test no capabilities + */ + public function testNoCapabilities() { + $manager = new \OC\CapabilitiesManager(); + $res = $manager->getCapabilities(); + $this->assertEmpty($res); + } + + /** + * Test a valid capabilitie + */ + public function testValidCapability() { + $manager = new \OC\CapabilitiesManager(); + + $manager->registerCapability(function() { + return new SimpleCapability(); + }); + + $res = $manager->getCapabilities(); + $this->assertEquals(['foo' => 1], $res); + } + + /** + * Test that we need something that implents ICapability + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The given Capability (Test\NoCapability) does not implement the ICapability interface + */ + public function testNoICapability() { + $manager = new \OC\CapabilitiesManager(); + + $manager->registerCapability(function() { + return new NoCapability(); + }); + + $res = $manager->getCapabilities(); + $this->assertEquals([], $res); + } + + /** + * Test a bunch of merged Capabilities + */ + public function testMergedCapabilities() { + $manager = new \OC\CapabilitiesManager(); + + $manager->registerCapability(function() { + return new SimpleCapability(); + }); + $manager->registerCapability(function() { + return new SimpleCapability2(); + }); + $manager->registerCapability(function() { + return new SimpleCapability3(); + }); + + $res = $manager->getCapabilities(); + $expected = [ + 'foo' => 1, + 'bar' => [ + 'x' => 1, + 'y' => 2 + ] + ]; + + $this->assertEquals($expected, $res); + } + + /** + * Test deep identical capabilities + */ + public function testDeepIdenticalCapabilities() { + $manager = new \OC\CapabilitiesManager(); + + $manager->registerCapability(function() { + return new DeepCapability(); + }); + $manager->registerCapability(function() { + return new DeepCapability(); + }); + + $res = $manager->getCapabilities(); + $expected = [ + 'foo' => [ + 'bar' => [ + 'baz' => true + ] + ] + ]; + + $this->assertEquals($expected, $res); + } +} + +class SimpleCapability implements \OCP\Capabilities\ICapability { + public function getCapabilities() { + return [ + 'foo' => 1 + ]; + } +} + +class SimpleCapability2 implements \OCP\Capabilities\ICapability { + public function getCapabilities() { + return [ + 'bar' => ['x' => 1] + ]; + } +} + +class SimpleCapability3 implements \OCP\Capabilities\ICapability { + public function getCapabilities() { + return [ + 'bar' => ['y' => 2] + ]; + } +} + +class NoCapability { + public function getCapabilities() { + return [ + 'baz' => 'z' + ]; + } +} + +class DeepCapability implements \OCP\Capabilities\ICapability { + public function getCapabilities() { + return [ + 'foo' => [ + 'bar' => [ + 'baz' => true + ] + ] + ]; + } +} + diff --git a/tests/lib/connector/sabre/requesttest/auth.php b/tests/lib/connector/sabre/requesttest/auth.php new file mode 100644 index 00000000000..7cab4da5264 --- /dev/null +++ b/tests/lib/connector/sabre/requesttest/auth.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright (c) 2015 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Connector\Sabre\RequestTest; + +use Sabre\DAV\Auth\Backend\BackendInterface; + +class Auth implements BackendInterface { + /** + * @var string + */ + private $user; + + /** + * @var string + */ + private $password; + + /** + * Auth constructor. + * + * @param string $user + * @param string $password + */ + public function __construct($user, $password) { + $this->user = $user; + $this->password = $password; + } + + + /** + * Authenticates the user based on the current request. + * + * If authentication is successful, true must be returned. + * If authentication fails, an exception must be thrown. + * + * @param \Sabre\DAV\Server $server + * @param string $realm + * @return bool + */ + function authenticate(\Sabre\DAV\Server $server, $realm) { + $userSession = \OC::$server->getUserSession(); + $result = $userSession->login($this->user, $this->password); + if ($result) { + //we need to pass the user name, which may differ from login name + $user = $userSession->getUser()->getUID(); + \OC_Util::setupFS($user); + //trigger creation of user home and /files folder + \OC::$server->getUserFolder($user); + } + return $result; + } + + /** + * Returns information about the currently logged in username. + * + * If nobody is currently logged in, this method should return null. + * + * @return string|null + */ + function getCurrentUser() { + return $this->user; + } +} diff --git a/tests/lib/connector/sabre/requesttest/exceptionplugin.php b/tests/lib/connector/sabre/requesttest/exceptionplugin.php new file mode 100644 index 00000000000..2b9e5d6d46d --- /dev/null +++ b/tests/lib/connector/sabre/requesttest/exceptionplugin.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright (c) 2015 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Connector\Sabre\RequestTest; + +use Sabre\DAV\Exception; + +class ExceptionPlugin extends \OC\Connector\Sabre\ExceptionLoggerPlugin { + /** + * @var \Exception[] + */ + protected $exceptions = []; + + public function logException(\Exception $ex) { + $exceptionClass = get_class($ex); + if (!isset($this->nonFatalExceptions[$exceptionClass])) { + $this->exceptions[] = $ex; + } + } + + /** + * @return \Exception[] + */ + public function getExceptions() { + return $this->exceptions; + } +} diff --git a/tests/lib/connector/sabre/requesttest/requesttest.php b/tests/lib/connector/sabre/requesttest/requesttest.php new file mode 100644 index 00000000000..c7739aefcd7 --- /dev/null +++ b/tests/lib/connector/sabre/requesttest/requesttest.php @@ -0,0 +1,163 @@ +<?php +/** + * Copyright (c) 2015 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Connector\Sabre\RequestTest; + +use OC\Connector\Sabre\Server; +use OC\Connector\Sabre\ServerFactory; +use OC\Files\Mount\MountPoint; +use OC\Files\Storage\Temporary; +use OC\Files\View; +use OCP\IUser; +use Sabre\HTTP\Request; +use Test\TestCase; + +abstract class RequestTest extends TestCase { + /** + * @var \OC_User_Dummy + */ + protected $userBackend; + + /** + * @var \OCP\Files\Config\IMountProvider[] + */ + protected $mountProviders; + + /** + * @var \OC\Connector\Sabre\ServerFactory + */ + protected $serverFactory; + + protected function getStream($string) { + $stream = fopen('php://temp', 'r+'); + fwrite($stream, $string); + fseek($stream, 0); + return $stream; + } + + /** + * @param $userId + * @param $storages + * @return \OCP\Files\Config\IMountProvider + */ + protected function getMountProvider($userId, $storages) { + $mounts = []; + foreach ($storages as $mountPoint => $storage) { + $mounts[] = new MountPoint($storage, $mountPoint); + } + $provider = $this->getMock('\OCP\Files\Config\IMountProvider'); + $provider->expects($this->any()) + ->method('getMountsForUser') + ->will($this->returnCallback(function (IUser $user) use ($userId, $mounts) { + if ($user->getUID() === $userId) { + return $mounts; + } else { + return []; + } + })); + return $provider; + } + + protected function setUp() { + parent::setUp(); + $this->userBackend = new \OC_User_Dummy(); + \OC::$server->getUserManager()->registerBackend($this->userBackend); + + $this->serverFactory = new ServerFactory( + \OC::$server->getConfig(), + \OC::$server->getLogger(), + \OC::$server->getDatabaseConnection(), + \OC::$server->getUserSession(), + \OC::$server->getMountManager(), + \OC::$server->getTagManager() + ); + } + + protected function tearDown() { + parent::tearDown(); + \OC::$server->getUserManager()->removeBackend($this->userBackend); + } + + protected function setupUser($name, $password) { + $this->userBackend->createUser($name, $password); + \OC::$server->getMountProviderCollection()->registerProvider($this->getMountProvider($name, [ + '/' . $name => new Temporary() + ])); + $this->loginAsUser($name); + return new View('/' . $name . '/files'); + } + + /** + * @param \OC\Files\View $view the view to run the webdav server against + * @param string $user + * @param string $password + * @param string $method + * @param string $url + * @param resource|string|null $body + * @param array|null $headers + * @return \Sabre\HTTP\Response + */ + protected function request($view, $user, $password, $method, $url, $body = null, $headers = null) { + if (is_string($body)) { + $body = $this->getStream($body); + } + $this->logout(); + $exceptionPlugin = new ExceptionPlugin('webdav', null); + $server = $this->getSabreServer($view, $user, $password, $exceptionPlugin); + $request = new Request($method, $url, $headers, $body); + + // since sabre catches all exceptions we need to save them and throw them from outside the sabre server + + $originalServer = $_SERVER; + + if (is_array($headers)) { + foreach ($headers as $header => $value) { + $_SERVER['HTTP_' . strtoupper(str_replace('-', '_', $header))] = $value; + } + } + + $result = $this->makeRequest($server, $request); + + foreach ($exceptionPlugin->getExceptions() as $exception) { + throw $exception; + } + $_SERVER = $originalServer; + return $result; + } + + /** + * @param Server $server + * @param Request $request + * @return \Sabre\HTTP\Response + */ + protected function makeRequest(Server $server, Request $request) { + $sapi = new Sapi($request); + $server->sapi = $sapi; + $server->httpRequest = $request; + $server->exec(); + return $sapi->getResponse(); + } + + /** + * @param View $view + * @param string $user + * @param string $password + * @param ExceptionPlugin $exceptionPlugin + * @return Server + */ + protected function getSabreServer(View $view, $user, $password, ExceptionPlugin $exceptionPlugin) { + $authBackend = new Auth($user, $password); + + $server = $this->serverFactory->createServer('/', 'dummy', $authBackend, function () use ($view) { + return $view; + }); + $server->addPlugin($exceptionPlugin); + + return $server; + } +} diff --git a/tests/lib/connector/sabre/requesttest/sapi.php b/tests/lib/connector/sabre/requesttest/sapi.php new file mode 100644 index 00000000000..7072b8bd286 --- /dev/null +++ b/tests/lib/connector/sabre/requesttest/sapi.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright (c) 2015 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Connector\Sabre\RequestTest; + +use Sabre\HTTP\Request; +use Sabre\HTTP\Response; + +class Sapi { + /** + * @var \Sabre\HTTP\Request + */ + private $request; + + /** + * @var \Sabre\HTTP\Response + */ + private $response; + + /** + * This static method will create a new Request object, based on the + * current PHP request. + * + * @return \Sabre\HTTP\Request + */ + public function getRequest() { + return $this->request; + } + + public function __construct(Request $request) { + $this->request = $request; + } + + /** + * @param \Sabre\HTTP\Response $response + * @return void + */ + public function sendResponse(Response $response) { + $this->response = $response; + } + + /** + * @return \Sabre\HTTP\Response + */ + public function getResponse() { + return $this->response; + } +} diff --git a/tests/lib/connector/sabre/requesttest/uploadtest.php b/tests/lib/connector/sabre/requesttest/uploadtest.php new file mode 100644 index 00000000000..2cc912d07f2 --- /dev/null +++ b/tests/lib/connector/sabre/requesttest/uploadtest.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright (c) 2015 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Connector\Sabre\RequestTest; + +class UploadTest extends RequestTest { + public function testBasicUpload() { + $user = $this->getUniqueID(); + $view = $this->setupUser($user, 'pass'); + + $this->assertFalse($view->file_exists('foo.txt')); + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt', 'asd'); + + $this->assertEquals(201, $response->getStatus()); + $this->assertTrue($view->file_exists('foo.txt')); + $this->assertEquals('asd', $view->file_get_contents('foo.txt')); + } + + public function testUploadOverWrite() { + $user = $this->getUniqueID(); + $view = $this->setupUser($user, 'pass'); + + $view->file_put_contents('foo.txt', 'bar'); + + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt', 'asd'); + + $this->assertEquals(204, $response->getStatus()); + $this->assertEquals('asd', $view->file_get_contents('foo.txt')); + } + + public function testChunkedUpload() { + $user = $this->getUniqueID(); + $view = $this->setupUser($user, 'pass'); + + $this->assertFalse($view->file_exists('foo.txt')); + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']); + + $this->assertEquals(201, $response->getStatus()); + $this->assertFalse($view->file_exists('foo.txt')); + + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']); + + $this->assertEquals(201, $response->getStatus()); + $this->assertTrue($view->file_exists('foo.txt')); + + $this->assertEquals('asdbar', $view->file_get_contents('foo.txt')); + } + + public function testChunkedUploadOverWrite() { + $user = $this->getUniqueID(); + $view = $this->setupUser($user, 'pass'); + + $view->file_put_contents('foo.txt', 'bar'); + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']); + + $this->assertEquals(201, $response->getStatus()); + $this->assertEquals('bar', $view->file_get_contents('foo.txt')); + + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']); + + $this->assertEquals(201, $response->getStatus()); + + $this->assertEquals('asdbar', $view->file_get_contents('foo.txt')); + } + + public function testChunkedUploadOutOfOrder() { + $user = $this->getUniqueID(); + $view = $this->setupUser($user, 'pass'); + + $this->assertFalse($view->file_exists('foo.txt')); + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']); + + $this->assertEquals(201, $response->getStatus()); + $this->assertFalse($view->file_exists('foo.txt')); + + $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']); + + $this->assertEquals(201, $response->getStatus()); + $this->assertTrue($view->file_exists('foo.txt')); + + $this->assertEquals('asdbar', $view->file_get_contents('foo.txt')); + } +} diff --git a/tests/lib/db/querybuilder/querybuildertest.php b/tests/lib/db/querybuilder/querybuildertest.php index 02e516b7386..75e62ba944e 100644 --- a/tests/lib/db/querybuilder/querybuildertest.php +++ b/tests/lib/db/querybuilder/querybuildertest.php @@ -253,8 +253,8 @@ class QueryBuilderTest extends \Test\TestCase { public function dataDelete() { return [ - ['data', null, ['table' => '`data`', 'alias' => null], '`data`'], - ['data', 't', ['table' => '`data`', 'alias' => 't'], '`data` t'], + ['data', null, ['table' => '`*PREFIX*data`', 'alias' => null], '`*PREFIX*data`'], + ['data', 't', ['table' => '`*PREFIX*data`', 'alias' => 't'], '`*PREFIX*data` t'], ]; } @@ -282,8 +282,8 @@ class QueryBuilderTest extends \Test\TestCase { public function dataUpdate() { return [ - ['data', null, ['table' => '`data`', 'alias' => null], '`data`'], - ['data', 't', ['table' => '`data`', 'alias' => 't'], '`data` t'], + ['data', null, ['table' => '`*PREFIX*data`', 'alias' => null], '`*PREFIX*data`'], + ['data', 't', ['table' => '`*PREFIX*data`', 'alias' => 't'], '`*PREFIX*data` t'], ]; } @@ -311,7 +311,7 @@ class QueryBuilderTest extends \Test\TestCase { public function dataInsert() { return [ - ['data', ['table' => '`data`'], '`data`'], + ['data', ['table' => '`*PREFIX*data`'], '`*PREFIX*data`'], ]; } @@ -338,16 +338,16 @@ class QueryBuilderTest extends \Test\TestCase { public function dataFrom() { return [ - ['data', null, null, null, [['table' => '`data`', 'alias' => null]], '`data`'], - ['data', 't', null, null, [['table' => '`data`', 'alias' => 't']], '`data` t'], + ['data', null, null, null, [['table' => '`*PREFIX*data`', 'alias' => null]], '`*PREFIX*data`'], + ['data', 't', null, null, [['table' => '`*PREFIX*data`', 'alias' => 't']], '`*PREFIX*data` t'], ['data1', null, 'data2', null, [ - ['table' => '`data1`', 'alias' => null], - ['table' => '`data2`', 'alias' => null] - ], '`data1`, `data2`'], + ['table' => '`*PREFIX*data1`', 'alias' => null], + ['table' => '`*PREFIX*data2`', 'alias' => null] + ], '`*PREFIX*data1`, `*PREFIX*data2`'], ['data', 't1', 'data', 't2', [ - ['table' => '`data`', 'alias' => 't1'], - ['table' => '`data`', 'alias' => 't2'] - ], '`data` t1, `data` t2'], + ['table' => '`*PREFIX*data`', 'alias' => 't1'], + ['table' => '`*PREFIX*data`', 'alias' => 't2'] + ], '`*PREFIX*data` t1, `*PREFIX*data` t2'], ]; } @@ -382,18 +382,18 @@ class QueryBuilderTest extends \Test\TestCase { return [ [ 'd1', 'data2', null, null, - ['d1' => [['joinType' => 'inner', 'joinTable' => '`data2`', 'joinAlias' => null, 'joinCondition' => null]]], - '`data1` d1 INNER JOIN `data2` ON ' + ['d1' => [['joinType' => 'inner', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => null, 'joinCondition' => null]]], + '`*PREFIX*data1` d1 INNER JOIN `*PREFIX*data2` ON ' ], [ 'd1', 'data2', 'd2', null, - ['d1' => [['joinType' => 'inner', 'joinTable' => '`data2`', 'joinAlias' => 'd2', 'joinCondition' => null]]], - '`data1` d1 INNER JOIN `data2` d2 ON ' + ['d1' => [['joinType' => 'inner', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => 'd2', 'joinCondition' => null]]], + '`*PREFIX*data1` d1 INNER JOIN `*PREFIX*data2` d2 ON ' ], [ 'd1', 'data2', 'd2', 'd1.`field1` = d2.`field2`', - ['d1' => [['joinType' => 'inner', 'joinTable' => '`data2`', 'joinAlias' => 'd2', 'joinCondition' => 'd1.`field1` = d2.`field2`']]], - '`data1` d1 INNER JOIN `data2` d2 ON d1.`field1` = d2.`field2`' + ['d1' => [['joinType' => 'inner', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => 'd2', 'joinCondition' => 'd1.`field1` = d2.`field2`']]], + '`*PREFIX*data1` d1 INNER JOIN `*PREFIX*data2` d2 ON d1.`field1` = d2.`field2`' ], ]; @@ -463,18 +463,18 @@ class QueryBuilderTest extends \Test\TestCase { return [ [ 'd1', 'data2', null, null, - ['d1' => [['joinType' => 'left', 'joinTable' => '`data2`', 'joinAlias' => null, 'joinCondition' => null]]], - '`data1` d1 LEFT JOIN `data2` ON ' + ['d1' => [['joinType' => 'left', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => null, 'joinCondition' => null]]], + '`*PREFIX*data1` d1 LEFT JOIN `*PREFIX*data2` ON ' ], [ 'd1', 'data2', 'd2', null, - ['d1' => [['joinType' => 'left', 'joinTable' => '`data2`', 'joinAlias' => 'd2', 'joinCondition' => null]]], - '`data1` d1 LEFT JOIN `data2` d2 ON ' + ['d1' => [['joinType' => 'left', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => 'd2', 'joinCondition' => null]]], + '`*PREFIX*data1` d1 LEFT JOIN `*PREFIX*data2` d2 ON ' ], [ 'd1', 'data2', 'd2', 'd1.`field1` = d2.`field2`', - ['d1' => [['joinType' => 'left', 'joinTable' => '`data2`', 'joinAlias' => 'd2', 'joinCondition' => 'd1.`field1` = d2.`field2`']]], - '`data1` d1 LEFT JOIN `data2` d2 ON d1.`field1` = d2.`field2`' + ['d1' => [['joinType' => 'left', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => 'd2', 'joinCondition' => 'd1.`field1` = d2.`field2`']]], + '`*PREFIX*data1` d1 LEFT JOIN `*PREFIX*data2` d2 ON d1.`field1` = d2.`field2`' ], ]; } @@ -513,18 +513,18 @@ class QueryBuilderTest extends \Test\TestCase { return [ [ 'd1', 'data2', null, null, - ['d1' => [['joinType' => 'right', 'joinTable' => '`data2`', 'joinAlias' => null, 'joinCondition' => null]]], - '`data1` d1 RIGHT JOIN `data2` ON ' + ['d1' => [['joinType' => 'right', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => null, 'joinCondition' => null]]], + '`*PREFIX*data1` d1 RIGHT JOIN `*PREFIX*data2` ON ' ], [ 'd1', 'data2', 'd2', null, - ['d1' => [['joinType' => 'right', 'joinTable' => '`data2`', 'joinAlias' => 'd2', 'joinCondition' => null]]], - '`data1` d1 RIGHT JOIN `data2` d2 ON ' + ['d1' => [['joinType' => 'right', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => 'd2', 'joinCondition' => null]]], + '`*PREFIX*data1` d1 RIGHT JOIN `*PREFIX*data2` d2 ON ' ], [ 'd1', 'data2', 'd2', 'd1.`field1` = d2.`field2`', - ['d1' => [['joinType' => 'right', 'joinTable' => '`data2`', 'joinAlias' => 'd2', 'joinCondition' => 'd1.`field1` = d2.`field2`']]], - '`data1` d1 RIGHT JOIN `data2` d2 ON d1.`field1` = d2.`field2`' + ['d1' => [['joinType' => 'right', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => 'd2', 'joinCondition' => 'd1.`field1` = d2.`field2`']]], + '`*PREFIX*data1` d1 RIGHT JOIN `*PREFIX*data2` d2 ON d1.`field1` = d2.`field2`' ], ]; } @@ -591,7 +591,7 @@ class QueryBuilderTest extends \Test\TestCase { ); $this->assertSame( - 'UPDATE `data` SET ' . $expectedQuery, + 'UPDATE `*PREFIX*data` SET ' . $expectedQuery, $this->queryBuilder->getSQL() ); } @@ -774,7 +774,7 @@ class QueryBuilderTest extends \Test\TestCase { ); $this->assertSame( - 'INSERT INTO `data` ' . $expectedQuery, + 'INSERT INTO `*PREFIX*data` ' . $expectedQuery, $this->queryBuilder->getSQL() ); } @@ -799,7 +799,7 @@ class QueryBuilderTest extends \Test\TestCase { ); $this->assertSame( - 'INSERT INTO `data` ' . $expectedQuery, + 'INSERT INTO `*PREFIX*data` ' . $expectedQuery, $this->queryBuilder->getSQL() ); } @@ -996,4 +996,34 @@ class QueryBuilderTest extends \Test\TestCase { $this->queryBuilder->getSQL() ); } + + public function dataGetTableName() { + return [ + ['*PREFIX*table', null, '`*PREFIX*table`'], + ['*PREFIX*table', true, '`*PREFIX*table`'], + ['*PREFIX*table', false, '`*PREFIX*table`'], + + ['table', null, '`*PREFIX*table`'], + ['table', true, '`*PREFIX*table`'], + ['table', false, '`table`'], + ]; + } + + /** + * @dataProvider dataGetTableName + * + * @param string $tableName + * @param bool $automatic + * @param string $expected + */ + public function testGetTableName($tableName, $automatic, $expected) { + if ($automatic !== null) { + $this->queryBuilder->automaticTablePrefix($automatic); + } + + $this->assertSame( + $expected, + $this->invokePrivate($this->queryBuilder, 'getTableName', [$tableName]) + ); + } } diff --git a/tests/lib/files/mount/mountpoint.php b/tests/lib/files/mount/mountpoint.php index 29610e6058d..d758c1b8d4d 100644 --- a/tests/lib/files/mount/mountpoint.php +++ b/tests/lib/files/mount/mountpoint.php @@ -70,4 +70,25 @@ class MountPoint extends \Test\TestCase { // storage wrapper never called $this->assertFalse($called); } + + public function testWrappedStorage() { + $storage = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Wrapper') + ->disableOriginalConstructor() + ->getMock(); + + $loader = $this->getMock('\OCP\Files\Storage\IStorageFactory'); + $loader->expects($this->never()) + ->method('getInstance'); + $loader->expects($this->never()) + ->method('wrap'); + + $mountPoint = new \OC\Files\Mount\MountPoint( + $storage, + '/mountpoint', + null, + $loader + ); + + $this->assertEquals($storage, $mountPoint->getStorage()); + } } diff --git a/tests/lib/files/pathverificationtest.php b/tests/lib/files/pathverificationtest.php index b59aceba7b1..13fccd310f3 100644 --- a/tests/lib/files/pathverificationtest.php +++ b/tests/lib/files/pathverificationtest.php @@ -83,6 +83,10 @@ class PathVerification extends \Test\TestCase { return [ // this is the monkey emoji - http://en.wikipedia.org/w/index.php?title=%F0%9F%90%B5&redirect=no ['🐵'], + ['🐵.txt'], + ['txt.💩'], + ['💩🐵.txt'], + ['💩🐵'], ]; } diff --git a/tests/lib/files/storage/wrapper/availability.php b/tests/lib/files/storage/wrapper/availability.php new file mode 100644 index 00000000000..9b394df8ca3 --- /dev/null +++ b/tests/lib/files/storage/wrapper/availability.php @@ -0,0 +1,149 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@karoshi.org.uk> + * + * @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 Test\Files\Storage\Wrapper; + +class Availability extends \Test\TestCase { + protected function getWrapperInstance() { + $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary') + ->disableOriginalConstructor() + ->getMock(); + $wrapper = new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]); + return [$storage, $wrapper]; + } + + /** + * Storage is available + */ + public function testAvailable() { + list($storage, $wrapper) = $this->getWrapperInstance(); + $storage->expects($this->once()) + ->method('getAvailability') + ->willReturn(['available' => true, 'last_checked' => 0]); + $storage->expects($this->never()) + ->method('test'); + $storage->expects($this->once()) + ->method('mkdir'); + + $wrapper->mkdir('foobar'); + } + + /** + * Storage marked unavailable, TTL not expired + * + * @expectedException \OCP\Files\StorageNotAvailableException + */ + public function testUnavailable() { + list($storage, $wrapper) = $this->getWrapperInstance(); + $storage->expects($this->once()) + ->method('getAvailability') + ->willReturn(['available' => false, 'last_checked' => time()]); + $storage->expects($this->never()) + ->method('test'); + $storage->expects($this->never()) + ->method('mkdir'); + + $wrapper->mkdir('foobar'); + } + + /** + * Storage marked unavailable, TTL expired + */ + public function testUnavailableRecheck() { + list($storage, $wrapper) = $this->getWrapperInstance(); + $storage->expects($this->once()) + ->method('getAvailability') + ->willReturn(['available' => false, 'last_checked' => 0]); + $storage->expects($this->once()) + ->method('test') + ->willReturn(true); + $storage->expects($this->once()) + ->method('setAvailability') + ->with($this->equalTo(true)); + $storage->expects($this->once()) + ->method('mkdir'); + + $wrapper->mkdir('foobar'); + } + + /** + * Storage marked available, but throws StorageNotAvailableException + * + * @expectedException \OCP\Files\StorageNotAvailableException + */ + public function testAvailableThrowStorageNotAvailable() { + list($storage, $wrapper) = $this->getWrapperInstance(); + $storage->expects($this->once()) + ->method('getAvailability') + ->willReturn(['available' => true, 'last_checked' => 0]); + $storage->expects($this->never()) + ->method('test'); + $storage->expects($this->once()) + ->method('mkdir') + ->will($this->throwException(new \OCP\Files\StorageNotAvailableException())); + $storage->expects($this->once()) + ->method('setAvailability') + ->with($this->equalTo(false)); + + $wrapper->mkdir('foobar'); + } + + /** + * Storage available, but call fails + * Method failure does not indicate storage unavailability + */ + public function testAvailableFailure() { + list($storage, $wrapper) = $this->getWrapperInstance(); + $storage->expects($this->once()) + ->method('getAvailability') + ->willReturn(['available' => true, 'last_checked' => 0]); + $storage->expects($this->never()) + ->method('test'); + $storage->expects($this->once()) + ->method('mkdir') + ->willReturn(false); + $storage->expects($this->never()) + ->method('setAvailability'); + + $wrapper->mkdir('foobar'); + } + + /** + * Storage available, but throws exception + * Standard exception does not indicate storage unavailability + * + * @expectedException \Exception + */ + public function testAvailableThrow() { + list($storage, $wrapper) = $this->getWrapperInstance(); + $storage->expects($this->once()) + ->method('getAvailability') + ->willReturn(['available' => true, 'last_checked' => 0]); + $storage->expects($this->never()) + ->method('test'); + $storage->expects($this->once()) + ->method('mkdir') + ->will($this->throwException(new \Exception())); + $storage->expects($this->never()) + ->method('setAvailability'); + + $wrapper->mkdir('foobar'); + } +} diff --git a/tests/lib/files/storage/wrapper/encryption.php b/tests/lib/files/storage/wrapper/encryption.php index 612cf827975..c49e6bb0d1f 100644 --- a/tests/lib/files/storage/wrapper/encryption.php +++ b/tests/lib/files/storage/wrapper/encryption.php @@ -261,10 +261,12 @@ class Encryption extends \Test\Files\Storage\Storage { ->expects($this->once()) ->method('copyKeys') ->willReturn($copyKeysReturn); - $this->cache->expects($this->once()) + $this->cache->expects($this->atLeastOnce()) ->method('put') - ->with($this->anything(), ['encrypted' => true]) - ->willReturn(true); + ->willReturnCallback(function($path, $data) { + $this->assertArrayHasKey('encrypted', $data); + $this->assertTrue($data['encrypted']); + }); } else { $this->cache->expects($this->never())->method('put'); $this->keyStore->expects($this->never())->method('copyKeys'); diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 382c033f19c..bb42f385fc5 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -44,8 +44,22 @@ class View extends \Test\TestCase { * @var \OC\Files\Storage\Storage[] $storages */ private $storages = array(); + + /** + * @var string + */ private $user; + /** + * @var \OCP\IUser + */ + private $userObject; + + /** + * @var \OCP\IGroup + */ + private $groupObject; + /** @var \OC\Files\Storage\Storage */ private $tempStorage; @@ -57,10 +71,15 @@ class View extends \Test\TestCase { \OC_User::useBackend(new \OC_User_Dummy()); //login - \OC_User::createUser('test', 'test'); + $userManager = \OC::$server->getUserManager(); + $groupManager = \OC::$server->getGroupManager(); + $this->user = 'test'; + $this->userObject = $userManager->createUser('test', 'test'); - $this->loginAsUser('test'); - $this->user = \OC_User::getUser(); + $this->groupObject = $groupManager->createGroup('group1'); + $this->groupObject->addUser($this->userObject); + + $this->loginAsUser($this->user); // clear mounts but somehow keep the root storage // that was initialized above... \OC\Files\Filesystem::clearMounts(); @@ -81,6 +100,10 @@ class View extends \Test\TestCase { } $this->logout(); + + $this->userObject->delete(); + $this->groupObject->delete(); + parent::tearDown(); } @@ -208,6 +231,40 @@ class View extends \Test\TestCase { $this->assertEquals(4, count($folderContent)); } + public function sharingDisabledPermissionProvider() { + return [ + ['no', '', true], + ['yes', 'group1', false], + ]; + } + + /** + * @dataProvider sharingDisabledPermissionProvider + */ + public function testRemoveSharePermissionWhenSharingDisabledForUser($excludeGroups, $excludeGroupsList, $expectedShareable) { + $appConfig = \OC::$server->getAppConfig(); + $oldExcludeGroupsFlag = $appConfig->getValue('core', 'shareapi_exclude_groups', 'no'); + $oldExcludeGroupsList = $appConfig->getValue('core', 'shareapi_exclude_groups_list', ''); + $appConfig->setValue('core', 'shareapi_exclude_groups', $excludeGroups); + $appConfig->setValue('core', 'shareapi_exclude_groups_list', $excludeGroupsList); + + $storage1 = $this->getTestStorage(); + $storage2 = $this->getTestStorage(); + \OC\Files\Filesystem::mount($storage1, array(), '/'); + \OC\Files\Filesystem::mount($storage2, array(), '/mount'); + + $view = new \OC\Files\View('/'); + + $folderContent = $view->getDirectoryContent(''); + $this->assertEquals($expectedShareable, $folderContent[0]->isShareable()); + + $folderContent = $view->getDirectoryContent('mount'); + $this->assertEquals($expectedShareable, $folderContent[0]->isShareable()); + + $appConfig->setValue('core', 'shareapi_exclude_groups', $oldExcludeGroupsFlag); + $appConfig->setValue('core', 'shareapi_exclude_groups_list', $oldExcludeGroupsList); + } + function testCacheIncompleteFolder() { $storage1 = $this->getTestStorage(false); \OC\Files\Filesystem::clearMounts(); @@ -1329,7 +1386,9 @@ class View extends \Test\TestCase { ['/foo/files/bar', '/foo', true], ['/foo', '/foo', false], ['/foo', '/files/foo', true], - ['/foo', 'filesfoo', false] + ['/foo', 'filesfoo', false], + ['', '/foo/files', true], + ['', '/foo/files/bar.txt', true] ]; } diff --git a/tests/lib/repair/cleantags.php b/tests/lib/repair/cleantags.php index 2f6d0879642..a511daa03d6 100644 --- a/tests/lib/repair/cleantags.php +++ b/tests/lib/repair/cleantags.php @@ -40,13 +40,13 @@ class CleanTags extends \Test\TestCase { protected function cleanUpTables() { $qb = $this->connection->getQueryBuilder(); - $qb->delete('*PREFIX*vcategory') + $qb->delete('vcategory') ->execute(); - $qb->delete('*PREFIX*vcategory_to_object') + $qb->delete('vcategory_to_object') ->execute(); - $qb->delete('*PREFIX*filecache') + $qb->delete('filecache') ->execute(); } @@ -61,20 +61,20 @@ class CleanTags extends \Test\TestCase { $this->addTagEntry(9999999, $cat3, 'contacts'); // Retained $this->addTagEntry($this->getFileID(), $cat3 + 1, 'files'); // Deleted: Category is NULL - $this->assertEntryCount('*PREFIX*vcategory_to_object', 4, 'Assert tag entries count before repair step'); - $this->assertEntryCount('*PREFIX*vcategory', 4, 'Assert tag categories count before repair step'); + $this->assertEntryCount('vcategory_to_object', 4, 'Assert tag entries count before repair step'); + $this->assertEntryCount('vcategory', 4, 'Assert tag categories count before repair step'); self::invokePrivate($this->repair, 'deleteOrphanFileEntries'); - $this->assertEntryCount('*PREFIX*vcategory_to_object', 3, 'Assert tag entries count after cleaning file entries'); - $this->assertEntryCount('*PREFIX*vcategory', 4, 'Assert tag categories count after cleaning file entries'); + $this->assertEntryCount('vcategory_to_object', 3, 'Assert tag entries count after cleaning file entries'); + $this->assertEntryCount('vcategory', 4, 'Assert tag categories count after cleaning file entries'); self::invokePrivate($this->repair, 'deleteOrphanTagEntries'); - $this->assertEntryCount('*PREFIX*vcategory_to_object', 2, 'Assert tag entries count after cleaning tag entries'); - $this->assertEntryCount('*PREFIX*vcategory', 4, 'Assert tag categories count after cleaning tag entries'); + $this->assertEntryCount('vcategory_to_object', 2, 'Assert tag entries count after cleaning tag entries'); + $this->assertEntryCount('vcategory', 4, 'Assert tag categories count after cleaning tag entries'); self::invokePrivate($this->repair, 'deleteOrphanCategoryEntries'); - $this->assertEntryCount('*PREFIX*vcategory_to_object', 2, 'Assert tag entries count after cleaning category entries'); - $this->assertEntryCount('*PREFIX*vcategory', 2, 'Assert tag categories count after cleaning category entries'); + $this->assertEntryCount('vcategory_to_object', 2, 'Assert tag entries count after cleaning category entries'); + $this->assertEntryCount('vcategory', 2, 'Assert tag categories count after cleaning category entries'); } /** @@ -100,7 +100,7 @@ class CleanTags extends \Test\TestCase { */ protected function addTagCategory($category, $type) { $qb = $this->connection->getQueryBuilder(); - $qb->insert('*PREFIX*vcategory') + $qb->insert('vcategory') ->values([ 'uid' => $qb->createNamedParameter('TestRepairCleanTags'), 'category' => $qb->createNamedParameter($category), @@ -108,7 +108,7 @@ class CleanTags extends \Test\TestCase { ]) ->execute(); - return (int) $this->getLastInsertID('*PREFIX*vcategory', 'id'); + return (int) $this->getLastInsertID('vcategory', 'id'); } /** @@ -119,7 +119,7 @@ class CleanTags extends \Test\TestCase { */ protected function addTagEntry($objectId, $category, $type) { $qb = $this->connection->getQueryBuilder(); - $qb->insert('*PREFIX*vcategory_to_object') + $qb->insert('vcategory_to_object') ->values([ 'objid' => $qb->createNamedParameter($objectId, \PDO::PARAM_INT), 'categoryid' => $qb->createNamedParameter($category, \PDO::PARAM_INT), @@ -141,21 +141,21 @@ class CleanTags extends \Test\TestCase { // We create a new file entry and delete it after the test again $fileName = $this->getUniqueID('TestRepairCleanTags', 12); - $qb->insert('*PREFIX*filecache') + $qb->insert('filecache') ->values([ 'path' => $qb->createNamedParameter($fileName), 'path_hash' => $qb->createNamedParameter(md5($fileName)), ]) ->execute(); $fileName = $this->getUniqueID('TestRepairCleanTags', 12); - $qb->insert('*PREFIX*filecache') + $qb->insert('filecache') ->values([ 'path' => $qb->createNamedParameter($fileName), 'path_hash' => $qb->createNamedParameter(md5($fileName)), ]) ->execute(); - $this->createdFile = (int) $this->getLastInsertID('*PREFIX*filecache', 'fileid'); + $this->createdFile = (int) $this->getLastInsertID('filecache', 'fileid'); return $this->createdFile; } diff --git a/tests/lib/server.php b/tests/lib/server.php index cf0ad8265bf..9c5c83ceb5c 100644 --- a/tests/lib/server.php +++ b/tests/lib/server.php @@ -1,6 +1,9 @@ <?php /** * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Lukas Reschke <lukas@owncloud.com> + * @author Roeland Jago Douma <roeland@famdouma.nl> + * @author Thomas Müller <thomas.mueller@tmit.eu> * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 @@ -48,6 +51,7 @@ class Server extends \Test\TestCase { ['AvatarManager', '\OC\AvatarManager'], ['AvatarManager', '\OCP\IAvatarManager'], + ['CapabilitiesManager', '\OC\CapabilitiesManager'], ['ContactsManager', '\OC\ContactsManager'], ['ContactsManager', '\OCP\Contacts\IManager'], ['Crypto', '\OC\Security\Crypto'], diff --git a/tests/lib/setup.php b/tests/lib/setup.php index d07eaa40ee0..72c84520056 100644 --- a/tests/lib/setup.php +++ b/tests/lib/setup.php @@ -20,6 +20,10 @@ class Test_OC_Setup extends \Test\TestCase { private $defaults; /** @var \OC\Setup | PHPUnit_Framework_MockObject_MockObject */ protected $setupClass; + /** @var \OCP\ILogger | PHPUnit_Framework_MockObject_MockObject */ + protected $logger; + /** @var \OCP\Security\ISecureRandom | PHPUnit_Framework_MockObject_MockObject */ + protected $random; protected function setUp() { parent::setUp(); @@ -28,9 +32,11 @@ class Test_OC_Setup extends \Test\TestCase { $this->iniWrapper = $this->getMock('\bantu\IniGetWrapper\IniGetWrapper'); $this->l10n = $this->getMock('\OCP\IL10N'); $this->defaults = $this->getMock('\OC_Defaults'); + $this->logger = $this->getMock('\OCP\ILogger'); + $this->random = $this->getMock('\OCP\Security\ISecureRandom'); $this->setupClass = $this->getMock('\OC\Setup', - ['class_exists', 'is_callable'], - [$this->config, $this->iniWrapper, $this->l10n, $this->defaults]); + ['class_exists', 'is_callable', 'getAvailableDbDriversForPdo'], + [$this->config, $this->iniWrapper, $this->l10n, $this->defaults, $this->logger, $this->random]); } public function testGetSupportedDatabasesWithOneWorking() { @@ -45,9 +51,13 @@ class Test_OC_Setup extends \Test\TestCase { ->method('class_exists') ->will($this->returnValue(true)); $this->setupClass - ->expects($this->exactly(2)) + ->expects($this->once()) ->method('is_callable') ->will($this->returnValue(false)); + $this->setupClass + ->expects($this->once()) + ->method('getAvailableDbDriversForPdo') + ->will($this->returnValue([])); $result = $this->setupClass->getSupportedDatabases(); $expectedResult = array( 'sqlite' => 'SQLite' @@ -68,9 +78,13 @@ class Test_OC_Setup extends \Test\TestCase { ->method('class_exists') ->will($this->returnValue(false)); $this->setupClass - ->expects($this->exactly(3)) + ->expects($this->exactly(2)) ->method('is_callable') ->will($this->returnValue(false)); + $this->setupClass + ->expects($this->once()) + ->method('getAvailableDbDriversForPdo') + ->will($this->returnValue([])); $result = $this->setupClass->getSupportedDatabases(); $this->assertSame(array(), $result); @@ -88,9 +102,13 @@ class Test_OC_Setup extends \Test\TestCase { ->method('class_exists') ->will($this->returnValue(true)); $this->setupClass - ->expects($this->exactly(3)) + ->expects($this->exactly(2)) ->method('is_callable') ->will($this->returnValue(true)); + $this->setupClass + ->expects($this->once()) + ->method('getAvailableDbDriversForPdo') + ->will($this->returnValue(['mysql'])); $result = $this->setupClass->getSupportedDatabases(); $expectedResult = array( 'sqlite' => 'SQLite', diff --git a/tests/lib/share/searchresultsorter.php b/tests/lib/share/searchresultsorter.php index 97ef0f9478a..d91110f7a9a 100644 --- a/tests/lib/share/searchresultsorter.php +++ b/tests/lib/share/searchresultsorter.php @@ -37,11 +37,4 @@ class Test_Share_Search extends \Test\TestCase { $this->assertTrue($result[2]['foobar'] === 'Bicyclerepairwoman'); $this->assertTrue($result[3]['foobar'] === 'woot'); } - - /** - * @expectedException PHPUnit_Framework_Error - */ - public function testSortWrongLog() { - $sorter = new \OC\Share\SearchResultSorter('foo', 'bar', 'UTF-8', 'foobar'); - } } diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php index b6d3e16826d..ef0d9822085 100644 --- a/tests/lib/share/share.php +++ b/tests/lib/share/share.php @@ -1288,7 +1288,7 @@ class Test_Share extends \Test\TestCase { // Find the share ID in the db $qb = $connection->getQueryBuilder(); $qb->select('id') - ->from('*PREFIX*share') + ->from('share') ->where($qb->expr()->eq('item_type', $qb->createParameter('type'))) ->andWhere($qb->expr()->eq('item_source', $qb->createParameter('source'))) ->andWhere($qb->expr()->eq('uid_owner', $qb->createParameter('owner'))) @@ -1309,7 +1309,7 @@ class Test_Share extends \Test\TestCase { // Fetch the hash from the database $qb = $connection->getQueryBuilder(); $qb->select('share_with') - ->from('*PREFIX*share') + ->from('share') ->where($qb->expr()->eq('id', $qb->createParameter('id'))) ->setParameter('id', $id); $hash = $qb->execute()->fetch()['share_with']; diff --git a/tests/ocs/response.php b/tests/ocs/response.php new file mode 100644 index 00000000000..919915a7c78 --- /dev/null +++ b/tests/ocs/response.php @@ -0,0 +1,42 @@ +<?php +use OCP\AppFramework\Http; + +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @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/> + * + */ + +class OcsResponseTest extends \Test\TestCase { + + /** + * @dataProvider providesStatusCodes + */ + public function testStatusCodeMapper($expected, $sc) { + $result = OC_API::mapStatusCodes($sc); + $this->assertEquals($expected, $result); + } + + public function providesStatusCodes() { + return [ + [Http::STATUS_OK, 100], + [Http::STATUS_BAD_REQUEST, 104], + [Http::STATUS_BAD_REQUEST, 1000], + [201, 201], + ]; + } +} diff --git a/tests/settings/controller/CertificateControllerTest.php b/tests/settings/controller/CertificateControllerTest.php index b6981195034..023d7753cca 100644 --- a/tests/settings/controller/CertificateControllerTest.php +++ b/tests/settings/controller/CertificateControllerTest.php @@ -21,6 +21,7 @@ namespace OC\Settings\Controller; +use OCP\App\IAppManager; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\IRequest; @@ -41,6 +42,8 @@ class CertificateControllerTest extends \Test\TestCase { private $certificateManager; /** @var IL10N */ private $l10n; + /** @var IAppManager */ + private $appManager; public function setUp() { parent::setUp(); @@ -48,13 +51,21 @@ class CertificateControllerTest extends \Test\TestCase { $this->request = $this->getMock('\OCP\IRequest'); $this->certificateManager = $this->getMock('\OCP\ICertificateManager'); $this->l10n = $this->getMock('\OCP\IL10N'); - - $this->certificateController = new CertificateController( - 'settings', - $this->request, - $this->certificateManager, - $this->l10n - ); + $this->appManager = $this->getMock('OCP\App\IAppManager'); + + $this->certificateController = $this->getMockBuilder('OC\Settings\Controller\CertificateController') + ->setConstructorArgs( + [ + 'settings', + $this->request, + $this->certificateManager, + $this->l10n, + $this->appManager + ] + )->setMethods(['isCertificateImportAllowed'])->getMock(); + + $this->certificateController->expects($this->any()) + ->method('isCertificateImportAllowed')->willReturn(true); } public function testAddPersonalRootCertificateWithEmptyFile() { diff --git a/tests/settings/controller/CheckSetupControllerTest.php b/tests/settings/controller/CheckSetupControllerTest.php index 6096aae8652..414b1b91e17 100644 --- a/tests/settings/controller/CheckSetupControllerTest.php +++ b/tests/settings/controller/CheckSetupControllerTest.php @@ -31,11 +31,24 @@ use OC_Util; use Test\TestCase; /** + * Mock version_compare + * @param string $version1 + * @param string $version2 + * @return int + */ +function version_compare($version1, $version2) { + return CheckSetupControllerTest::$version_compare; +} + +/** * Class CheckSetupControllerTest * * @package OC\Settings\Controller */ class CheckSetupControllerTest extends TestCase { + /** @var int */ + public static $version_compare; + /** @var CheckSetupController */ private $checkSetupController; /** @var IRequest */ @@ -209,6 +222,66 @@ class CheckSetupControllerTest extends TestCase { ); } + public function testIsPhpSupportedFalse() { + self::$version_compare = -1; + + $this->assertEquals( + ['eol' => true, 'version' => PHP_VERSION], + self::invokePrivate($this->checkSetupController, 'isPhpSupported') + ); + } + + public function testIsPhpSupportedTrue() { + self::$version_compare = 0; + + $this->assertEquals( + ['eol' => false, 'version' => PHP_VERSION], + self::invokePrivate($this->checkSetupController, 'isPhpSupported') + ); + + + self::$version_compare = 1; + + $this->assertEquals( + ['eol' => false, 'version' => PHP_VERSION], + self::invokePrivate($this->checkSetupController, 'isPhpSupported') + ); + } + + public function testForwardedForHeadersWorkingFalse() { + $this->config->expects($this->once()) + ->method('getSystemValue') + ->with('trusted_proxies', []) + ->willReturn(['1.2.3.4']); + $this->request->expects($this->once()) + ->method('getRemoteAddress') + ->willReturn('1.2.3.4'); + + $this->assertFalse( + self::invokePrivate( + $this->checkSetupController, + 'forwardedForHeadersWorking' + ) + ); + } + + public function testForwardedForHeadersWorkingTrue() { + $this->config->expects($this->once()) + ->method('getSystemValue') + ->with('trusted_proxies', []) + ->willReturn(['1.2.3.4']); + $this->request->expects($this->once()) + ->method('getRemoteAddress') + ->willReturn('4.3.2.1'); + + $this->assertTrue( + self::invokePrivate( + $this->checkSetupController, + 'forwardedForHeadersWorking' + ) + ); + } + public function testCheck() { $this->config->expects($this->at(0)) ->method('getSystemValue') @@ -218,6 +291,14 @@ class CheckSetupControllerTest extends TestCase { ->method('getSystemValue') ->with('memcache.local', null) ->will($this->returnValue('SomeProvider')); + $this->config->expects($this->at(2)) + ->method('getSystemValue') + ->with('trusted_proxies', []) + ->willReturn(['1.2.3.4']); + + $this->request->expects($this->once()) + ->method('getRemoteAddress') + ->willReturn('4.3.2.1'); $client = $this->getMockBuilder('\OCP\Http\Client\IClient') ->disableOriginalConstructor()->getMock(); @@ -244,6 +325,11 @@ class CheckSetupControllerTest extends TestCase { ->method('linkToDocs') ->with('admin-security') ->willReturn('https://doc.owncloud.org/server/8.1/admin_manual/configuration_server/hardening.html'); + self::$version_compare = -1; + $this->urlGenerator->expects($this->at(2)) + ->method('linkToDocs') + ->with('admin-reverse-proxy') + ->willReturn('reverse-proxy-doc-link'); $expected = new DataResponse( [ @@ -254,6 +340,12 @@ class CheckSetupControllerTest extends TestCase { 'isUrandomAvailable' => self::invokePrivate($this->checkSetupController, 'isUrandomAvailable'), 'securityDocs' => 'https://doc.owncloud.org/server/8.1/admin_manual/configuration_server/hardening.html', 'isUsedTlsLibOutdated' => '', + 'phpSupported' => [ + 'eol' => true, + 'version' => PHP_VERSION + ], + 'forwardedForHeadersWorking' => true, + 'reverseProxyDocs' => 'reverse-proxy-doc-link', ] ); $this->assertEquals($expected, $this->checkSetupController->check()); diff --git a/version.php b/version.php index 7ccd2e6b548..a115f4b26be 100644 --- a/version.php +++ b/version.php @@ -22,7 +22,7 @@ // We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades // between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel // when updating major/minor version number. -$OC_Version=array(8, 2, 0, 3); +$OC_Version=array(8, 2, 0, 4); // The human readable string $OC_VersionString='8.2 pre alpha'; |