diff options
author | Arthur Schiwon <blizzz@arthur-schiwon.de> | 2016-06-11 16:34:43 +0300 |
---|---|---|
committer | Arthur Schiwon <blizzz@arthur-schiwon.de> | 2016-06-11 16:34:43 +0300 |
commit | 42c66efea5ef512d3a3442112f820168e6499265 (patch) | |
tree | 97ef44632d653656608e71e096fd537bbd609936 | |
parent | 75f37f550bb7895757325d3f9a3215bcc4471065 (diff) | |
parent | 52a0c939ab8674857bbfe9a9fb0ee7308eee960e (diff) |
Merge branch 'master' of https://github.com/owncloud/core into downstream-160611
109 files changed, 2151 insertions, 219 deletions
diff --git a/apps/comments/l10n/bg_BG.js b/apps/comments/l10n/bg_BG.js index e63aedf80db..c5fbba69f7b 100644 --- a/apps/comments/l10n/bg_BG.js +++ b/apps/comments/l10n/bg_BG.js @@ -1,8 +1,19 @@ OC.L10N.register( "comments", { + "Type in a new comment..." : "Напиши нов коментар...", + "Delete comment" : "Изтрий коментар", "Cancel" : "Отказ", + "Edit comment" : "Редактирай коментра", + "[Deleted user]" : "[Изтрит потребител]", + "Comments" : "Коментари", + "No other comments available" : "Няма други коментари", + "More comments..." : "Още коментари...", "Save" : "Запазване", - "Comment" : "Коментар" + "Allowed characters {count} of {max}" : "Позволени символи {count} от {max}", + "{count} unread comments" : "{count} нечетени коментари", + "Comment" : "Коментар", + "<strong>Comments</strong> for files <em>(always listed in stream)</em>" : "<strong>Коментари</strong> на файлове <em>(винаги изписвани в stream-а)</em>", + "You commented" : "Вие коментирахте" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/comments/l10n/bg_BG.json b/apps/comments/l10n/bg_BG.json index 78ad0b57d4c..64f516861ca 100644 --- a/apps/comments/l10n/bg_BG.json +++ b/apps/comments/l10n/bg_BG.json @@ -1,6 +1,17 @@ { "translations": { + "Type in a new comment..." : "Напиши нов коментар...", + "Delete comment" : "Изтрий коментар", "Cancel" : "Отказ", + "Edit comment" : "Редактирай коментра", + "[Deleted user]" : "[Изтрит потребител]", + "Comments" : "Коментари", + "No other comments available" : "Няма други коментари", + "More comments..." : "Още коментари...", "Save" : "Запазване", - "Comment" : "Коментар" + "Allowed characters {count} of {max}" : "Позволени символи {count} от {max}", + "{count} unread comments" : "{count} нечетени коментари", + "Comment" : "Коментар", + "<strong>Comments</strong> for files <em>(always listed in stream)</em>" : "<strong>Коментари</strong> на файлове <em>(винаги изписвани в stream-а)</em>", + "You commented" : "Вие коментирахте" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/dav/appinfo/database.xml b/apps/dav/appinfo/database.xml index f79ea07ae76..9578526a705 100644 --- a/apps/dav/appinfo/database.xml +++ b/apps/dav/appinfo/database.xml @@ -272,6 +272,12 @@ CREATE TABLE calendarobjects ( <type>text</type> <length>255</length> </field> + <field> + <comments>0 - public, 1 - private, 2 - confidential</comments> + <name>classification</name> + <type>integer</type> + <default>0</default> + </field> <index> <name>calobjects_index</name> <unique>true</unique> diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml index ca456b03089..26e37e6bb86 100644 --- a/apps/dav/appinfo/info.xml +++ b/apps/dav/appinfo/info.xml @@ -5,7 +5,7 @@ <description>ownCloud WebDAV endpoint</description> <licence>AGPL</licence> <author>owncloud.org</author> - <version>0.2.4</version> + <version>0.2.5</version> <default_enable/> <types> <filesystem/> @@ -20,4 +20,9 @@ <background-jobs> <job>OCA\DAV\CardDAV\Sync\SyncJob</job> </background-jobs> + <repair-steps> + <post-migration> + <job>OCA\DAV\Migration\Classification</job> + </post-migration> + </repair-steps> </info> diff --git a/apps/dav/appinfo/v1/publicwebdav.php b/apps/dav/appinfo/v1/publicwebdav.php index c6c319aa36d..261a4d4b96d 100644 --- a/apps/dav/appinfo/v1/publicwebdav.php +++ b/apps/dav/appinfo/v1/publicwebdav.php @@ -66,7 +66,6 @@ $server = $serverFactory->createServer($baseuri, $requestUri, $authBackend, func $share = $authBackend->getShare(); $owner = $share->getShareOwner(); - $isWritable = $share->getPermissions() & (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_CREATE); $isReadable = $share->getPermissions() & \OCP\Constants::PERMISSION_READ; $fileId = $share->getNodeId(); @@ -74,11 +73,9 @@ $server = $serverFactory->createServer($baseuri, $requestUri, $authBackend, func return false; } - if (!$isWritable) { - \OC\Files\Filesystem::addStorageWrapper('readonly', function ($mountPoint, $storage) { - return new \OC\Files\Storage\Wrapper\PermissionsMask(array('storage' => $storage, 'mask' => \OCP\Constants::PERMISSION_READ + \OCP\Constants::PERMISSION_SHARE)); - }); - } + \OC\Files\Filesystem::addStorageWrapper('sharePermissions', function ($mountPoint, $storage) use ($share) { + return new \OC\Files\Storage\Wrapper\PermissionsMask(array('storage' => $storage, 'mask' => $share->getPermissions() | \OCP\Constants::PERMISSION_SHARE)); + }); OC_Util::setupFS($owner); $ownerView = \OC\Files\Filesystem::getView(); diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php index ba0ef421f97..9e0d2da4e17 100644 --- a/apps/dav/lib/AppInfo/Application.php +++ b/apps/dav/lib/AppInfo/Application.php @@ -31,10 +31,12 @@ use OCA\DAV\CardDAV\SyncService; use OCA\DAV\Connector\Sabre\Principal; use OCA\DAV\DAV\GroupPrincipalBackend; use OCA\DAV\HookManager; +use OCA\DAV\Migration\Classification; use \OCP\AppFramework\App; use OCP\AppFramework\IAppContainer; use OCP\Contacts\IManager; use OCP\IUser; +use Sabre\VObject\Reader; use Symfony\Component\EventDispatcher\GenericEvent; class Application extends App { @@ -106,6 +108,14 @@ class Application extends App { $g ); }); + + $container->registerService('OCA\DAV\Migration\Classification', function ($c) { + /** @var IAppContainer $c */ + return new Classification( + $c->query('CalDavBackend'), + $c->getServer()->getUserManager() + ); + }); } /** diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 64fdf0f7ebe..ce494082976 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -37,10 +37,11 @@ use Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp; use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet; use Sabre\DAV; use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\PropPatch; use Sabre\HTTP\URLUtil; use Sabre\VObject\DateTimeParser; use Sabre\VObject\Reader; -use Sabre\VObject\RecurrenceIterator; +use Sabre\VObject\Recur\EventIterator; /** * Class CalDavBackend @@ -61,6 +62,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription */ const MAX_DATE = '2038-01-01'; + const CLASSIFICATION_PUBLIC = 0; + const CLASSIFICATION_PRIVATE = 1; + const CLASSIFICATION_CONFIDENTIAL = 2; + /** * List of CalDAV properties, and how they map to database field names * Add your own properties by simply adding on to this array. @@ -395,10 +400,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * * Read the PropPatch documentation for more info and examples. * - * @param \Sabre\DAV\PropPatch $propPatch + * @param PropPatch $propPatch * @return void */ - function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) { + function updateCalendar($calendarId, PropPatch $propPatch) { $supportedProperties = array_keys($this->propertyMap); $supportedProperties[] = '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp'; @@ -484,7 +489,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription */ function getCalendarObjects($calendarId) { $query = $this->db->getQueryBuilder(); - $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'componenttype']) + $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'componenttype', 'classification']) ->from('calendarobjects') ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId))); $stmt = $query->execute(); @@ -499,6 +504,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription 'calendarid' => $row['calendarid'], 'size' => (int)$row['size'], 'component' => strtolower($row['componenttype']), + 'classification'=> (int)$row['classification'] ]; } @@ -524,7 +530,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription function getCalendarObject($calendarId, $objectUri) { $query = $this->db->getQueryBuilder(); - $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype']) + $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype', 'classification']) ->from('calendarobjects') ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId))) ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri))); @@ -542,6 +548,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription 'size' => (int)$row['size'], 'calendardata' => $this->readBlob($row['calendardata']), 'component' => strtolower($row['componenttype']), + 'classification'=> (int)$row['classification'] ]; } @@ -559,7 +566,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription */ function getMultipleCalendarObjects($calendarId, array $uris) { $query = $this->db->getQueryBuilder(); - $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype']) + $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype', 'classification']) ->from('calendarobjects') ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId))) ->andWhere($query->expr()->in('uri', $query->createParameter('uri'))) @@ -579,6 +586,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription 'size' => (int)$row['size'], 'calendardata' => $this->readBlob($row['calendardata']), 'component' => strtolower($row['componenttype']), + 'classification' => (int)$row['classification'] ]; } @@ -618,6 +626,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription 'componenttype' => $query->createNamedParameter($extraData['componentType']), 'firstoccurence' => $query->createNamedParameter($extraData['firstOccurence']), 'lastoccurence' => $query->createNamedParameter($extraData['lastOccurence']), + 'classification' => $query->createNamedParameter($extraData['classification']), 'uid' => $query->createNamedParameter($extraData['uid']), ]) ->execute(); @@ -657,6 +666,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription ->set('componenttype', $query->createNamedParameter($extraData['componentType'])) ->set('firstoccurence', $query->createNamedParameter($extraData['firstOccurence'])) ->set('lastoccurence', $query->createNamedParameter($extraData['lastOccurence'])) + ->set('classification', $query->createNamedParameter($extraData['classification'])) ->set('uid', $query->createNamedParameter($extraData['uid'])) ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId))) ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri))) @@ -668,6 +678,23 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription } /** + * @param int $calendarObjectId + * @param int $classification + */ + public function setClassification($calendarObjectId, $classification) { + if (!in_array($classification, [ + self::CLASSIFICATION_PUBLIC, self::CLASSIFICATION_PRIVATE, self::CLASSIFICATION_CONFIDENTIAL + ])) { + throw new \InvalidArgumentException(); + } + $query = $this->db->getQueryBuilder(); + $query->update('calendarobjects') + ->set('classification', $query->createNamedParameter($classification)) + ->where($query->expr()->eq('id', $query->createNamedParameter($calendarObjectId))) + ->execute(); + } + + /** * Deletes an existing calendar object. * * The object uri is only the basename, or filename and not a full path. @@ -1086,10 +1113,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * Read the PropPatch documentation for more info and examples. * * @param mixed $subscriptionId - * @param \Sabre\DAV\PropPatch $propPatch + * @param PropPatch $propPatch * @return void */ - function updateSubscription($subscriptionId, DAV\PropPatch $propPatch) { + function updateSubscription($subscriptionId, PropPatch $propPatch) { $supportedProperties = array_keys($this->subscriptionPropertyMap); $supportedProperties[] = '{http://calendarserver.org/ns/}source'; @@ -1280,14 +1307,15 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * @param string $calendarData * @return array */ - protected function getDenormalizedData($calendarData) { + public function getDenormalizedData($calendarData) { $vObject = Reader::read($calendarData); $componentType = null; $component = null; - $firstOccurence = null; - $lastOccurence = null; + $firstOccurrence = null; + $lastOccurrence = null; $uid = null; + $classification = self::CLASSIFICATION_PUBLIC; foreach($vObject->getComponents() as $component) { if ($component->name!=='VTIMEZONE') { $componentType = $component->name; @@ -1299,27 +1327,27 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component'); } if ($componentType === 'VEVENT' && $component->DTSTART) { - $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); + $firstOccurrence = $component->DTSTART->getDateTime()->getTimeStamp(); // Finding the last occurrence is a bit harder if (!isset($component->RRULE)) { if (isset($component->DTEND)) { - $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); + $lastOccurrence = $component->DTEND->getDateTime()->getTimeStamp(); } elseif (isset($component->DURATION)) { $endDate = clone $component->DTSTART->getDateTime(); $endDate->add(DateTimeParser::parse($component->DURATION->getValue())); - $lastOccurence = $endDate->getTimeStamp(); + $lastOccurrence = $endDate->getTimeStamp(); } elseif (!$component->DTSTART->hasTime()) { $endDate = clone $component->DTSTART->getDateTime(); $endDate->modify('+1 day'); - $lastOccurence = $endDate->getTimeStamp(); + $lastOccurrence = $endDate->getTimeStamp(); } else { - $lastOccurence = $firstOccurence; + $lastOccurrence = $firstOccurrence; } } else { - $it = new RecurrenceIterator($vObject, (string)$component->UID); + $it = new EventIterator($vObject, (string)$component->UID); $maxDate = new \DateTime(self::MAX_DATE); if ($it->isInfinite()) { - $lastOccurence = $maxDate->getTimeStamp(); + $lastOccurrence = $maxDate->getTimeStamp(); } else { $end = $it->getDtEnd(); while($it->valid() && $end < $maxDate) { @@ -1327,19 +1355,31 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $it->next(); } - $lastOccurence = $end->getTimeStamp(); + $lastOccurrence = $end->getTimeStamp(); } } } + if ($component->CLASS) { + $classification = CalDavBackend::CLASSIFICATION_PRIVATE; + switch ($component->CLASS->getValue()) { + case 'PUBLIC': + $classification = CalDavBackend::CLASSIFICATION_PUBLIC; + break; + case 'CONFIDENTIAL': + $classification = CalDavBackend::CLASSIFICATION_CONFIDENTIAL; + break; + } + } return [ - 'etag' => md5($calendarData), - 'size' => strlen($calendarData), - 'componentType' => $componentType, - 'firstOccurence' => is_null($firstOccurence) ? null : max(0, $firstOccurence), - 'lastOccurence' => $lastOccurence, - 'uid' => $uid, + 'etag' => md5($calendarData), + 'size' => strlen($calendarData), + 'componentType' => $componentType, + 'firstOccurence' => is_null($firstOccurrence) ? null : max(0, $firstOccurrence), + 'lastOccurence' => $lastOccurrence, + 'uid' => $uid, + 'classification' => $classification ]; } diff --git a/apps/dav/lib/CalDAV/Calendar.php b/apps/dav/lib/CalDAV/Calendar.php index 73b3957a9b0..785bb5699e2 100644 --- a/apps/dav/lib/CalDAV/Calendar.php +++ b/apps/dav/lib/CalDAV/Calendar.php @@ -26,6 +26,7 @@ use OCA\DAV\DAV\Sharing\IShareable; use OCP\IL10N; use Sabre\CalDAV\Backend\BackendInterface; use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\NotFound; use Sabre\DAV\PropPatch; class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { @@ -162,6 +163,78 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { parent::propPatch($propPatch); } + function getChild($name) { + + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name); + + if (!$obj) { + throw new NotFound('Calendar object not found'); + } + + if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { + throw new NotFound('Calendar object not found'); + } + + $obj['acl'] = $this->getChildACL(); + + return new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj); + + } + + function getChildren() { + + $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); + $children = []; + foreach ($objs as $obj) { + if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { + continue; + } + $obj['acl'] = $this->getChildACL(); + $children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj); + } + return $children; + + } + + function getMultipleChildren(array $paths) { + + $objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths); + $children = []; + foreach ($objs as $obj) { + if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { + continue; + } + $obj['acl'] = $this->getChildACL(); + $children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj); + } + return $children; + + } + + function childExists($name) { + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name); + if (!$obj) { + return false; + } + if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { + return false; + } + + return true; + } + + function calendarQuery(array $filters) { + + $uris = $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters); + if ($this->isShared()) { + return array_filter($uris, function ($uri) { + return $this->childExists($uri); + }); + } + + return $uris; + } + private function canWrite() { if (isset($this->calendarInfo['{http://owncloud.org/ns}read-only'])) { return !$this->calendarInfo['{http://owncloud.org/ns}read-only']; @@ -169,4 +242,8 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { return true; } + private function isShared() { + return isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal']); + } + } diff --git a/apps/dav/lib/CalDAV/CalendarObject.php b/apps/dav/lib/CalDAV/CalendarObject.php new file mode 100644 index 00000000000..b4a58b52093 --- /dev/null +++ b/apps/dav/lib/CalDAV/CalendarObject.php @@ -0,0 +1,92 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + + +namespace OCA\DAV\CalDAV; + + +use Sabre\VObject\Component; +use Sabre\VObject\Property; +use Sabre\VObject\Reader; + +class CalendarObject extends \Sabre\CalDAV\CalendarObject { + + /** + * @inheritdoc + */ + function get() { + $data = parent::get(); + if ($this->isShared() && $this->objectData['classification'] === CalDavBackend::CLASSIFICATION_CONFIDENTIAL) { + return $this->createConfidentialObject($data); + } + return $data; + } + + private function isShared() { + return isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal']); + } + + /** + * @param string $calData + * @return string + */ + private static function createConfidentialObject($calData) { + + $vObject = Reader::read($calData); + + /** @var Component $vElement */ + $vElement = null; + if(isset($vObject->VEVENT)) { + $vElement = $vObject->VEVENT; + } + if(isset($vObject->VJOURNAL)) { + $vElement = $vObject->VJOURNAL; + } + if(isset($vObject->VTODO)) { + $vElement = $vObject->VTODO; + } + if(!is_null($vElement)) { + foreach ($vElement->children as &$property) { + /** @var Property $property */ + switch($property->name) { + case 'CREATED': + case 'DTSTART': + case 'RRULE': + case 'DURATION': + case 'DTEND': + case 'CLASS': + case 'UID': + break; + case 'SUMMARY': + $property->setValue('Busy'); + break; + default: + $vElement->__unset($property->name); + unset($property); + break; + } + } + } + + return $vObject->serialize(); + } + +} diff --git a/apps/dav/lib/Connector/PublicAuth.php b/apps/dav/lib/Connector/PublicAuth.php index 2716ca29107..4e63ca1d29e 100644 --- a/apps/dav/lib/Connector/PublicAuth.php +++ b/apps/dav/lib/Connector/PublicAuth.php @@ -31,13 +31,14 @@ use OCP\IRequest; use OCP\ISession; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager; +use Sabre\DAV\Auth\Backend\AbstractBasic; /** * Class PublicAuth * * @package OCA\DAV\Connector */ -class PublicAuth extends \Sabre\DAV\Auth\Backend\AbstractBasic { +class PublicAuth extends AbstractBasic { /** @var \OCP\Share\IShare */ private $share; @@ -62,6 +63,10 @@ class PublicAuth extends \Sabre\DAV\Auth\Backend\AbstractBasic { $this->request = $request; $this->shareManager = $shareManager; $this->session = $session; + + // setup realm + $defaults = new \OC_Defaults(); + $this->realm = $defaults->getName(); } /** @@ -99,7 +104,7 @@ class PublicAuth extends \Sabre\DAV\Auth\Backend\AbstractBasic { if (in_array('XMLHttpRequest', explode(',', $this->request->getHeader('X-Requested-With')))) { // do not re-authenticate over ajax, use dummy auth name to prevent browser popup http_response_code(401); - header('WWW-Authenticate', 'DummyBasic real="ownCloud"'); + header('WWW-Authenticate','DummyBasic realm="' . $this->realm . '"'); throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls'); } return false; diff --git a/apps/dav/lib/Connector/Sabre/Auth.php b/apps/dav/lib/Connector/Sabre/Auth.php index 27900cc1cad..653da10bc3c 100644 --- a/apps/dav/lib/Connector/Sabre/Auth.php +++ b/apps/dav/lib/Connector/Sabre/Auth.php @@ -74,6 +74,10 @@ class Auth extends AbstractBasic { $this->twoFactorManager = $twoFactorManager; $this->request = $request; $this->principalPrefix = $principalPrefix; + + // setup realm + $defaults = new \OC_Defaults(); + $this->realm = $defaults->getName(); } /** diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php index dc47416cca8..0a2e6713cb4 100644 --- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php @@ -42,6 +42,7 @@ use \Sabre\HTTP\RequestInterface; use \Sabre\HTTP\ResponseInterface; use OCP\Files\StorageNotAvailableException; use OCP\IConfig; +use OCP\IRequest; class FilesPlugin extends ServerPlugin { @@ -96,19 +97,28 @@ class FilesPlugin extends ServerPlugin { private $config; /** + * @var IRequest + */ + private $request; + + /** * @param Tree $tree * @param View $view + * @param IConfig $config + * @param IRequest $request * @param bool $isPublic * @param bool $downloadAttachment */ public function __construct(Tree $tree, View $view, IConfig $config, + IRequest $request, $isPublic = false, $downloadAttachment = true) { $this->tree = $tree; $this->fileView = $view; $this->config = $config; + $this->request = $request; $this->isPublic = $isPublic; $this->downloadAttachment = $downloadAttachment; } @@ -225,7 +235,18 @@ class FilesPlugin extends ServerPlugin { // adds a 'Content-Disposition: attachment' header if ($this->downloadAttachment) { - $response->addHeader('Content-Disposition', 'attachment'); + $filename = $node->getName(); + if ($this->request->isUserAgent( + [ + \OC\AppFramework\Http\Request::USER_AGENT_IE, + \OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME, + \OC\AppFramework\Http\Request::USER_AGENT_FREEBOX, + ])) { + $response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"'); + } else { + $response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename) + . '; filename="' . rawurlencode($filename) . '"'); + } } if ($node instanceof \OCA\DAV\Connector\Sabre\File) { diff --git a/apps/dav/lib/Connector/Sabre/ServerFactory.php b/apps/dav/lib/Connector/Sabre/ServerFactory.php index b193bfc76c7..c5b4f6a9352 100644 --- a/apps/dav/lib/Connector/Sabre/ServerFactory.php +++ b/apps/dav/lib/Connector/Sabre/ServerFactory.php @@ -100,10 +100,9 @@ class ServerFactory { $server->setBaseUri($baseUri); // Load plugins - $defaults = new \OC_Defaults(); $server->addPlugin(new \OCA\DAV\Connector\Sabre\MaintenancePlugin($this->config)); $server->addPlugin(new \OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin($this->config)); - $server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $defaults->getName())); + $server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend)); // FIXME: The following line is a workaround for legacy components relying on being able to send a GET to / $server->addPlugin(new \OCA\DAV\Connector\Sabre\DummyGetResponsePlugin()); $server->addPlugin(new \OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin('webdav', $this->logger)); @@ -144,6 +143,7 @@ class ServerFactory { $objectTree, $view, $this->config, + $this->request, false, !$this->config->getSystemValue('debug', false) ) diff --git a/apps/dav/lib/Migration/Classification.php b/apps/dav/lib/Migration/Classification.php new file mode 100644 index 00000000000..b793f790af5 --- /dev/null +++ b/apps/dav/lib/Migration/Classification.php @@ -0,0 +1,93 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + + +namespace OCA\DAV\Migration; + +use OCA\DAV\CalDAV\CalDavBackend; +use OCP\IUser; +use OCP\IUserManager; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class Classification implements IRepairStep { + + /** @var CalDavBackend */ + private $calDavBackend; + + /** @var IUserManager */ + private $userManager; + + /** + * Classification constructor. + * + * @param CalDavBackend $calDavBackend + */ + public function __construct(CalDavBackend $calDavBackend, IUserManager $userManager) { + $this->calDavBackend = $calDavBackend; + $this->userManager = $userManager; + } + + /** + * @param IUser $user + */ + public function runForUser($user) { + $principal = 'principals/users/' . $user->getUID(); + $calendars = $this->calDavBackend->getCalendarsForUser($principal); + foreach ($calendars as $calendar) { + $objects = $this->calDavBackend->getCalendarObjects($calendar['id']); + foreach ($objects as $object) { + $calObject = $this->calDavBackend->getCalendarObject($calendar['id'], $object['uri']); + $classification = $this->extractClassification($calObject['calendardata']); + $this->calDavBackend->setClassification($object['id'], $classification); + } + } + } + + /** + * @param $calendarData + * @return integer + * @throws \Sabre\DAV\Exception\BadRequest + */ + protected function extractClassification($calendarData) { + return $this->calDavBackend->getDenormalizedData($calendarData)['classification']; + } + + /** + * @inheritdoc + */ + public function getName() { + return 'Fix classification for calendar objects'; + } + + /** + * @inheritdoc + */ + public function run(IOutput $output) { + $output->startProgress(); + $this->userManager->callForAllUsers(function($user) use ($output) { + /** @var IUser $user */ + $output->advance(1, $user->getDisplayName()); + $this->runForUser($user); + }); + $output->finishProgress(); + } +} diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php index 179558e97ae..e150f441b82 100644 --- a/apps/dav/lib/Server.php +++ b/apps/dav/lib/Server.php @@ -141,6 +141,7 @@ class Server { $this->server->tree, $view, \OC::$server->getConfig(), + $this->request, false, !\OC::$server->getConfig()->getSystemValue('debug', false) ) diff --git a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php new file mode 100644 index 00000000000..49e5e5a2bcc --- /dev/null +++ b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackendTest.php @@ -0,0 +1,163 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\DAV\Tests\unit\CalDAV; + +use DateTime; +use DateTimeZone; +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CalDAV\Calendar; +use OCA\DAV\Connector\Sabre\Principal; +use OCP\IL10N; +use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet; +use Sabre\DAV\PropPatch; +use Sabre\DAV\Xml\Property\Href; +use Sabre\DAVACL\IACL; +use Test\TestCase; + +/** + * Class CalDavBackendTest + * + * @group DB + * + * @package OCA\DAV\Tests\unit\CalDAV + */ +abstract class AbstractCalDavBackendTest extends TestCase { + + /** @var CalDavBackend */ + protected $backend; + + /** @var Principal | \PHPUnit_Framework_MockObject_MockObject */ + protected $principal; + + const UNIT_TEST_USER = 'principals/users/caldav-unit-test'; + const UNIT_TEST_USER1 = 'principals/users/caldav-unit-test1'; + const UNIT_TEST_GROUP = 'principals/groups/caldav-unit-test-group'; + + public function setUp() { + parent::setUp(); + + $this->principal = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Principal') + ->disableOriginalConstructor() + ->setMethods(['getPrincipalByPath', 'getGroupMembership']) + ->getMock(); + $this->principal->expects($this->any())->method('getPrincipalByPath') + ->willReturn([ + 'uri' => 'principals/best-friend' + ]); + $this->principal->expects($this->any())->method('getGroupMembership') + ->withAnyParameters() + ->willReturn([self::UNIT_TEST_GROUP]); + + $db = \OC::$server->getDatabaseConnection(); + $this->backend = new CalDavBackend($db, $this->principal); + + $this->tearDown(); + } + + public function tearDown() { + parent::tearDown(); + + if (is_null($this->backend)) { + return; + } + $books = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER); + foreach ($books as $book) { + $this->backend->deleteCalendar($book['id']); + } + $subscriptions = $this->backend->getSubscriptionsForUser(self::UNIT_TEST_USER); + foreach ($subscriptions as $subscription) { + $this->backend->deleteSubscription($subscription['id']); + } + } + + protected function createTestCalendar() { + $this->backend->createCalendar(self::UNIT_TEST_USER, 'Example', [ + '{http://apple.com/ns/ical/}calendar-color' => '#1C4587FF' + ]); + $calendars = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER); + $this->assertEquals(1, count($calendars)); + $this->assertEquals(self::UNIT_TEST_USER, $calendars[0]['principaluri']); + /** @var SupportedCalendarComponentSet $components */ + $components = $calendars[0]['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']; + $this->assertEquals(['VEVENT','VTODO'], $components->getValue()); + $color = $calendars[0]['{http://apple.com/ns/ical/}calendar-color']; + $this->assertEquals('#1C4587FF', $color); + $this->assertEquals('Example', $calendars[0]['uri']); + $this->assertEquals('Example', $calendars[0]['{DAV:}displayname']); + $calendarId = $calendars[0]['id']; + + return $calendarId; + } + + protected function createEvent($calendarId, $start = '20130912T130000Z', $end = '20130912T140000Z') { + + $calData = <<<EOD +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:ownCloud Calendar +BEGIN:VEVENT +CREATED;VALUE=DATE-TIME:20130910T125139Z +UID:47d15e3ec8 +LAST-MODIFIED;VALUE=DATE-TIME:20130910T125139Z +DTSTAMP;VALUE=DATE-TIME:20130910T125139Z +SUMMARY:Test Event +DTSTART;VALUE=DATE-TIME:$start +DTEND;VALUE=DATE-TIME:$end +CLASS:PUBLIC +END:VEVENT +END:VCALENDAR +EOD; + $uri0 = $this->getUniqueID('event'); + $this->backend->createCalendarObject($calendarId, $uri0, $calData); + + return $uri0; + } + + protected function assertAcl($principal, $privilege, $acl) { + foreach($acl as $a) { + if ($a['principal'] === $principal && $a['privilege'] === $privilege) { + $this->assertTrue(true); + return; + } + } + $this->fail("ACL does not contain $principal / $privilege"); + } + + protected function assertNotAcl($principal, $privilege, $acl) { + foreach($acl as $a) { + if ($a['principal'] === $principal && $a['privilege'] === $privilege) { + $this->fail("ACL contains $principal / $privilege"); + return; + } + } + $this->assertTrue(true); + } + + protected function assertAccess($shouldHaveAcl, $principal, $privilege, $acl) { + if ($shouldHaveAcl) { + $this->assertAcl($principal, $privilege, $acl); + } else { + $this->assertNotAcl($principal, $privilege, $acl); + } + } +} diff --git a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php index c3e32e436d9..977bdf15c8e 100644 --- a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php +++ b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php @@ -26,13 +26,10 @@ use DateTime; use DateTimeZone; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\Calendar; -use OCA\DAV\Connector\Sabre\Principal; use OCP\IL10N; -use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet; use Sabre\DAV\PropPatch; use Sabre\DAV\Xml\Property\Href; use Sabre\DAVACL\IACL; -use Test\TestCase; /** * Class CalDavBackendTest @@ -41,54 +38,7 @@ use Test\TestCase; * * @package OCA\DAV\Tests\unit\CalDAV */ -class CalDavBackendTest extends TestCase { - - /** @var CalDavBackend */ - private $backend; - - /** @var Principal | \PHPUnit_Framework_MockObject_MockObject */ - private $principal; - - const UNIT_TEST_USER = 'principals/users/caldav-unit-test'; - const UNIT_TEST_USER1 = 'principals/users/caldav-unit-test1'; - const UNIT_TEST_GROUP = 'principals/groups/caldav-unit-test-group'; - - public function setUp() { - parent::setUp(); - - $this->principal = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Principal') - ->disableOriginalConstructor() - ->setMethods(['getPrincipalByPath', 'getGroupMembership']) - ->getMock(); - $this->principal->expects($this->any())->method('getPrincipalByPath') - ->willReturn([ - 'uri' => 'principals/best-friend' - ]); - $this->principal->expects($this->any())->method('getGroupMembership') - ->withAnyParameters() - ->willReturn([self::UNIT_TEST_GROUP]); - - $db = \OC::$server->getDatabaseConnection(); - $this->backend = new CalDavBackend($db, $this->principal); - - $this->tearDown(); - } - - public function tearDown() { - parent::tearDown(); - - if (is_null($this->backend)) { - return; - } - $books = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER); - foreach ($books as $book) { - $this->backend->deleteCalendar($book['id']); - } - $subscriptions = $this->backend->getSubscriptionsForUser(self::UNIT_TEST_USER); - foreach ($subscriptions as $subscription) { - $this->backend->deleteSubscription($subscription['id']); - } - } +class CalDavBackendTest extends AbstractCalDavBackendTest { public function testCalendarOperations() { @@ -232,6 +182,7 @@ EOD; $calendarObjects = $this->backend->getCalendarObjects($calendarId); $this->assertEquals(1, count($calendarObjects)); $this->assertEquals($calendarId, $calendarObjects[0]['calendarid']); + $this->assertArrayHasKey('classification', $calendarObjects[0]); // get the cards $calendarObject = $this->backend->getCalendarObject($calendarId, $uri); @@ -241,6 +192,7 @@ EOD; $this->assertArrayHasKey('lastmodified', $calendarObject); $this->assertArrayHasKey('etag', $calendarObject); $this->assertArrayHasKey('size', $calendarObject); + $this->assertArrayHasKey('classification', $calendarObject); $this->assertEquals($calData, $calendarObject['calendardata']); // update the card @@ -310,6 +262,7 @@ EOD; $this->assertArrayHasKey('lastmodified', $card); $this->assertArrayHasKey('etag', $card); $this->assertArrayHasKey('size', $card); + $this->assertArrayHasKey('classification', $card); $this->assertEquals($calData, $card['calendardata']); } @@ -363,49 +316,6 @@ EOD; ]; } - private function createTestCalendar() { - $this->backend->createCalendar(self::UNIT_TEST_USER, 'Example', [ - '{http://apple.com/ns/ical/}calendar-color' => '#1C4587FF' - ]); - $calendars = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER); - $this->assertEquals(1, count($calendars)); - $this->assertEquals(self::UNIT_TEST_USER, $calendars[0]['principaluri']); - /** @var SupportedCalendarComponentSet $components */ - $components = $calendars[0]['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']; - $this->assertEquals(['VEVENT','VTODO'], $components->getValue()); - $color = $calendars[0]['{http://apple.com/ns/ical/}calendar-color']; - $this->assertEquals('#1C4587FF', $color); - $this->assertEquals('Example', $calendars[0]['uri']); - $this->assertEquals('Example', $calendars[0]['{DAV:}displayname']); - $calendarId = $calendars[0]['id']; - - return $calendarId; - } - - private function createEvent($calendarId, $start = '20130912T130000Z', $end = '20130912T140000Z') { - - $calData = <<<EOD -BEGIN:VCALENDAR -VERSION:2.0 -PRODID:ownCloud Calendar -BEGIN:VEVENT -CREATED;VALUE=DATE-TIME:20130910T125139Z -UID:47d15e3ec8 -LAST-MODIFIED;VALUE=DATE-TIME:20130910T125139Z -DTSTAMP;VALUE=DATE-TIME:20130910T125139Z -SUMMARY:Test Event -DTSTART;VALUE=DATE-TIME:$start -DTEND;VALUE=DATE-TIME:$end -CLASS:PUBLIC -END:VEVENT -END:VCALENDAR -EOD; - $uri0 = $this->getUniqueID('event'); - $this->backend->createCalendarObject($calendarId, $uri0, $calData); - - return $uri0; - } - public function testSyncSupport() { $calendarId = $this->createTestCalendar(); @@ -464,43 +374,20 @@ EOD; /** * @dataProvider providesCalDataForGetDenormalizedData */ - public function testGetDenormalizedData($expectedFirstOccurance, $calData) { - $actual = $this->invokePrivate($this->backend, 'getDenormalizedData', [$calData]); - $this->assertEquals($expectedFirstOccurance, $actual['firstOccurence']); + public function testGetDenormalizedData($expected, $key, $calData) { + $actual = $this->backend->getDenormalizedData($calData); + $this->assertEquals($expected, $actual[$key]); } public function providesCalDataForGetDenormalizedData() { return [ - [0, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:413F269B-B51B-46B1-AFB6-40055C53A4DC\r\nDTSTAMP:20160309T095056Z\r\nDTSTART;VALUE=DATE:16040222\r\nDTEND;VALUE=DATE:16040223\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:SUMMARY\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"], - [null, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:413F269B-B51B-46B1-AFB6-40055C53A4DC\r\nDTSTAMP:20160309T095056Z\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:SUMMARY\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"] + 'first occurrence before unix epoch starts' => [0, 'firstOccurence', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:413F269B-B51B-46B1-AFB6-40055C53A4DC\r\nDTSTAMP:20160309T095056Z\r\nDTSTART;VALUE=DATE:16040222\r\nDTEND;VALUE=DATE:16040223\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:SUMMARY\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"], + 'no first occurrence because yearly' => [null, 'firstOccurence', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:413F269B-B51B-46B1-AFB6-40055C53A4DC\r\nDTSTAMP:20160309T095056Z\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:SUMMARY\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"], + 'CLASS:PRIVATE' => [CalDavBackend::CLASSIFICATION_PRIVATE, 'classification', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//dmfs.org//mimedir.icalendar//EN\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nX-LIC-LOCATION:Europe/Berlin\r\nBEGIN:DAYLIGHT\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nTZNAME:CEST\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nTZNAME:CET\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTART;TZID=Europe/Berlin:20160419T130000\r\nSUMMARY:Test\r\nCLASS:PRIVATE\r\nTRANSP:OPAQUE\r\nSTATUS:CONFIRMED\r\nDTEND;TZID=Europe/Berlin:20160419T140000\r\nLAST-MODIFIED:20160419T074202Z\r\nDTSTAMP:20160419T074202Z\r\nCREATED:20160419T074202Z\r\nUID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310\r\nEND:VEVENT\r\nEND:VCALENDAR"], + 'CLASS:PUBLIC' => [CalDavBackend::CLASSIFICATION_PUBLIC, 'classification', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//dmfs.org//mimedir.icalendar//EN\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nX-LIC-LOCATION:Europe/Berlin\r\nBEGIN:DAYLIGHT\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nTZNAME:CEST\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nTZNAME:CET\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTART;TZID=Europe/Berlin:20160419T130000\r\nSUMMARY:Test\r\nCLASS:PUBLIC\r\nTRANSP:OPAQUE\r\nSTATUS:CONFIRMED\r\nDTEND;TZID=Europe/Berlin:20160419T140000\r\nLAST-MODIFIED:20160419T074202Z\r\nDTSTAMP:20160419T074202Z\r\nCREATED:20160419T074202Z\r\nUID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310\r\nEND:VEVENT\r\nEND:VCALENDAR"], + 'CLASS:CONFIDENTIAL' => [CalDavBackend::CLASSIFICATION_CONFIDENTIAL, 'classification', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//dmfs.org//mimedir.icalendar//EN\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nX-LIC-LOCATION:Europe/Berlin\r\nBEGIN:DAYLIGHT\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nTZNAME:CEST\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nTZNAME:CET\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTART;TZID=Europe/Berlin:20160419T130000\r\nSUMMARY:Test\r\nCLASS:CONFIDENTIAL\r\nTRANSP:OPAQUE\r\nSTATUS:CONFIRMED\r\nDTEND;TZID=Europe/Berlin:20160419T140000\r\nLAST-MODIFIED:20160419T074202Z\r\nDTSTAMP:20160419T074202Z\r\nCREATED:20160419T074202Z\r\nUID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310\r\nEND:VEVENT\r\nEND:VCALENDAR"], + 'no class set -> public' => [CalDavBackend::CLASSIFICATION_PUBLIC, 'classification', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//dmfs.org//mimedir.icalendar//EN\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nX-LIC-LOCATION:Europe/Berlin\r\nBEGIN:DAYLIGHT\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nTZNAME:CEST\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nTZNAME:CET\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTART;TZID=Europe/Berlin:20160419T130000\r\nSUMMARY:Test\r\nTRANSP:OPAQUE\r\nDTEND;TZID=Europe/Berlin:20160419T140000\r\nLAST-MODIFIED:20160419T074202Z\r\nDTSTAMP:20160419T074202Z\r\nCREATED:20160419T074202Z\r\nUID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310\r\nEND:VEVENT\r\nEND:VCALENDAR"], + 'unknown class -> private' => [CalDavBackend::CLASSIFICATION_PRIVATE, 'classification', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//dmfs.org//mimedir.icalendar//EN\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nX-LIC-LOCATION:Europe/Berlin\r\nBEGIN:DAYLIGHT\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nTZNAME:CEST\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nTZNAME:CET\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTART;TZID=Europe/Berlin:20160419T130000\r\nSUMMARY:Test\r\nCLASS:VERTRAULICH\r\nTRANSP:OPAQUE\r\nSTATUS:CONFIRMED\r\nDTEND;TZID=Europe/Berlin:20160419T140000\r\nLAST-MODIFIED:20160419T074202Z\r\nDTSTAMP:20160419T074202Z\r\nCREATED:20160419T074202Z\r\nUID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310\r\nEND:VEVENT\r\nEND:VCALENDAR"], ]; } - - private function assertAcl($principal, $privilege, $acl) { - foreach($acl as $a) { - if ($a['principal'] === $principal && $a['privilege'] === $privilege) { - $this->assertTrue(true); - return; - } - } - $this->fail("ACL does not contain $principal / $privilege"); - } - - private function assertNotAcl($principal, $privilege, $acl) { - foreach($acl as $a) { - if ($a['principal'] === $principal && $a['privilege'] === $privilege) { - $this->fail("ACL contains $principal / $privilege"); - return; - } - } - $this->assertTrue(true); - } - - private function assertAccess($shouldHaveAcl, $principal, $privilege, $acl) { - if ($shouldHaveAcl) { - $this->assertAcl($principal, $privilege, $acl); - } else { - $this->assertNotAcl($principal, $privilege, $acl); - } - } } diff --git a/apps/dav/tests/unit/CalDAV/CalendarTest.php b/apps/dav/tests/unit/CalDAV/CalendarTest.php index 73d85e82bbc..56a2d4fcba7 100644 --- a/apps/dav/tests/unit/CalDAV/CalendarTest.php +++ b/apps/dav/tests/unit/CalDAV/CalendarTest.php @@ -27,6 +27,7 @@ use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\Calendar; use OCP\IL10N; use Sabre\DAV\PropPatch; +use Sabre\VObject\Reader; use Test\TestCase; class CalendarTest extends TestCase { @@ -189,4 +190,153 @@ class CalendarTest extends TestCase { 'birthday calendar' => [false, false, false, BirthdayService::BIRTHDAY_CALENDAR_URI] ]; } + + /** + * @dataProvider providesConfidentialClassificationData + * @param $expectedChildren + * @param $isShared + */ + public function testPrivateClassification($expectedChildren, $isShared) { + + $calObject0 = ['uri' => 'event-0', 'classification' => CalDavBackend::CLASSIFICATION_PUBLIC]; + $calObject1 = ['uri' => 'event-1', 'classification' => CalDavBackend::CLASSIFICATION_CONFIDENTIAL]; + $calObject2 = ['uri' => 'event-2', 'classification' => CalDavBackend::CLASSIFICATION_PRIVATE]; + + /** @var \PHPUnit_Framework_MockObject_MockObject | CalDavBackend $backend */ + $backend = $this->getMockBuilder('OCA\DAV\CalDAV\CalDavBackend')->disableOriginalConstructor()->getMock(); + $backend->expects($this->any())->method('getCalendarObjects')->willReturn([ + $calObject0, $calObject1, $calObject2 + ]); + $backend->expects($this->any())->method('getMultipleCalendarObjects') + ->with(666, ['event-0', 'event-1', 'event-2']) + ->willReturn([ + $calObject0, $calObject1, $calObject2 + ]); + $backend->expects($this->any())->method('getCalendarObject') + ->willReturn($calObject2)->with(666, 'event-2'); + + $calendarInfo = [ + 'principaluri' => 'user2', + 'id' => 666, + 'uri' => 'cal', + ]; + + if ($isShared) { + $calendarInfo['{http://owncloud.org/ns}owner-principal'] = 'user1'; + + } + $c = new Calendar($backend, $calendarInfo, $this->l10n); + $children = $c->getChildren(); + $this->assertEquals($expectedChildren, count($children)); + $children = $c->getMultipleChildren(['event-0', 'event-1', 'event-2']); + $this->assertEquals($expectedChildren, count($children)); + + $this->assertEquals(!$isShared, $c->childExists('event-2')); + } + + /** + * @dataProvider providesConfidentialClassificationData + * @param $expectedChildren + * @param $isShared + */ + public function testConfidentialClassification($expectedChildren, $isShared) { + $start = '20160609'; + $end = '20160610'; + + $calData = <<<EOD +BEGIN:VCALENDAR +PRODID:-//ownCloud calendar v1.2.2 +BEGIN:VEVENT +CREATED:20160602T133732 +DTSTAMP:20160602T133732 +LAST-MODIFIED:20160602T133732 +UID:wej2z68l9h +SUMMARY:Test Event +LOCATION:Somewhere ... +ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CUTYPE=INDIVIDUAL;CN=de + epdiver:MAILTO:thomas.mueller@tmit.eu +ORGANIZER;CN=deepdiver:MAILTO:thomas.mueller@tmit.eu +DESCRIPTION:maybe .... +DTSTART;TZID=Europe/Berlin;VALUE=DATE:$start +DTEND;TZID=Europe/Berlin;VALUE=DATE:$end +RRULE:FREQ=DAILY +BEGIN:VALARM +ACTION:AUDIO +TRIGGER:-PT15M +END:VALARM +END:VEVENT +BEGIN:VTIMEZONE +TZID:Europe/Berlin +BEGIN:DAYLIGHT +DTSTART:19810329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:MESZ +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +END:DAYLIGHT +BEGIN:STANDARD +DTSTART:19961027T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +TZNAME:MEZ +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +END:STANDARD +END:VTIMEZONE +END:VCALENDAR +EOD; + + $calObject0 = ['uri' => 'event-0', 'classification' => CalDavBackend::CLASSIFICATION_PUBLIC]; + $calObject1 = ['uri' => 'event-1', 'classification' => CalDavBackend::CLASSIFICATION_CONFIDENTIAL, 'calendardata' => $calData]; + $calObject2 = ['uri' => 'event-2', 'classification' => CalDavBackend::CLASSIFICATION_PRIVATE]; + + /** @var \PHPUnit_Framework_MockObject_MockObject | CalDavBackend $backend */ + $backend = $this->getMockBuilder('OCA\DAV\CalDAV\CalDavBackend')->disableOriginalConstructor()->getMock(); + $backend->expects($this->any())->method('getCalendarObjects')->willReturn([ + $calObject0, $calObject1, $calObject2 + ]); + $backend->expects($this->any())->method('getMultipleCalendarObjects') + ->with(666, ['event-0', 'event-1', 'event-2']) + ->willReturn([ + $calObject0, $calObject1, $calObject2 + ]); + $backend->expects($this->any())->method('getCalendarObject') + ->willReturn($calObject1)->with(666, 'event-1'); + + $calendarInfo = [ + 'principaluri' => 'user2', + 'id' => 666, + 'uri' => 'cal', + ]; + + if ($isShared) { + $calendarInfo['{http://owncloud.org/ns}owner-principal'] = 'user1'; + + } + $c = new Calendar($backend, $calendarInfo, $this->l10n); + + // test private event + $privateEvent = $c->getChild('event-1'); + $calData = $privateEvent->get(); + $event = Reader::read($calData); + + $this->assertEquals($start, $event->VEVENT->DTSTART->getValue()); + $this->assertEquals($end, $event->VEVENT->DTEND->getValue()); + + if ($isShared) { + $this->assertEquals('Busy', $event->VEVENT->SUMMARY->getValue()); + $this->assertArrayNotHasKey('ATTENDEE', $event->VEVENT); + $this->assertArrayNotHasKey('LOCATION', $event->VEVENT); + $this->assertArrayNotHasKey('DESCRIPTION', $event->VEVENT); + $this->assertArrayNotHasKey('ORGANIZER', $event->VEVENT); + } else { + $this->assertEquals('Test Event', $event->VEVENT->SUMMARY->getValue()); + } + } + + public function providesConfidentialClassificationData() { + return [ + [3, false], + [2, true] + ]; + } } diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php index 80f284e470e..2b3f3e15d1a 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php @@ -73,6 +73,11 @@ class FilesPluginTest extends TestCase { */ private $config; + /** + * @var \OCP\IRequest | \PHPUnit_Framework_MockObject_MockObject + */ + private $request; + public function setUp() { parent::setUp(); $this->server = $this->getMockBuilder('\Sabre\DAV\Server') @@ -88,11 +93,13 @@ class FilesPluginTest extends TestCase { $this->config->expects($this->any())->method('getSystemValue') ->with($this->equalTo('data-fingerprint'), $this->equalTo('')) ->willReturn('my_fingerprint'); + $this->request = $this->getMock('\OCP\IRequest'); $this->plugin = new FilesPlugin( $this->tree, $this->view, - $this->config + $this->config, + $this->request ); $this->plugin->initialize($this->server); } @@ -268,6 +275,7 @@ class FilesPluginTest extends TestCase { $this->tree, $this->view, $this->config, + $this->getMock('\OCP\IRequest'), true); $this->plugin->initialize($this->server); @@ -484,4 +492,60 @@ class FilesPluginTest extends TestCase { $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); } + + public function downloadHeadersProvider() { + return [ + [ + false, + 'attachment; filename*=UTF-8\'\'somefile.xml; filename="somefile.xml"' + ], + [ + true, + 'attachment; filename="somefile.xml"' + ], + ]; + } + + /** + * @dataProvider downloadHeadersProvider + */ + public function testDownloadHeaders($isClumsyAgent, $contentDispositionHeader) { + $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface') + ->disableOriginalConstructor() + ->getMock(); + $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') + ->disableOriginalConstructor() + ->getMock(); + + $request + ->expects($this->once()) + ->method('getPath') + ->will($this->returnValue('test/somefile.xml')); + + $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File') + ->disableOriginalConstructor() + ->getMock(); + $node + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue('somefile.xml')); + + $this->tree + ->expects($this->once()) + ->method('getNodeForPath') + ->with('test/somefile.xml') + ->will($this->returnValue($node)); + + $this->request + ->expects($this->once()) + ->method('isUserAgent') + ->will($this->returnValue($isClumsyAgent)); + + $response + ->expects($this->once()) + ->method('addHeader') + ->with('Content-Disposition', $contentDispositionHeader); + + $this->plugin->httpGet($request, $response); + } } diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php index 41d44efd89c..baf4259b215 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php @@ -343,7 +343,8 @@ class FilesReportPluginTest extends \Test\TestCase { new \OCA\DAV\Connector\Sabre\FilesPlugin( $this->tree, $this->view, - $config + $config, + $this->getMock('\OCP\IRequest') ) ); $this->plugin->initialize($this->server); diff --git a/apps/dav/tests/unit/Migration/ClassificationTest.php b/apps/dav/tests/unit/Migration/ClassificationTest.php new file mode 100644 index 00000000000..5c7fa627226 --- /dev/null +++ b/apps/dav/tests/unit/Migration/ClassificationTest.php @@ -0,0 +1,75 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\DAV\Tests\unit\DAV\Migration; + +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\Migration\Classification; +use OCA\DAV\Tests\unit\CalDAV\AbstractCalDavBackendTest; +use OCP\IUser; + +/** + * Class ClassificationTest + * + * @group DB + * + * @package OCA\DAV\Tests\unit\DAV + */ +class ClassificationTest extends AbstractCalDavBackendTest { + + /** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\IUserManager */ + private $userManager; + + public function setUp() { + parent::setUp(); + + $this->userManager = $this->getMockBuilder('OCP\IUserManager') + ->disableOriginalConstructor()->getMock(); + } + + public function test() { + // setup data + $calendarId = $this->createTestCalendar(); + $eventUri = $this->createEvent($calendarId, '20130912T130000Z', '20130912T140000Z'); + $object = $this->backend->getCalendarObject($calendarId, $eventUri); + + // assert proper classification + $this->assertEquals(CalDavBackend::CLASSIFICATION_PUBLIC, $object['classification']); + $this->backend->setClassification($object['id'], CalDavBackend::CLASSIFICATION_CONFIDENTIAL); + $object = $this->backend->getCalendarObject($calendarId, $eventUri); + $this->assertEquals(CalDavBackend::CLASSIFICATION_CONFIDENTIAL, $object['classification']); + + // run migration + $c = new Classification($this->backend, $this->userManager); + + /** @var IUser | \PHPUnit_Framework_MockObject_MockObject $user */ + $user = $this->getMockBuilder('OCP\IUser') + ->disableOriginalConstructor() + ->getMock(); + $user->expects($this->once())->method('getUID')->willReturn('caldav-unit-test'); + + $c->runForUser($user); + + // assert classification after migration + $object = $this->backend->getCalendarObject($calendarId, $eventUri); + $this->assertEquals(CalDavBackend::CLASSIFICATION_PUBLIC, $object['classification']); + } +} diff --git a/apps/encryption/l10n/pl.js b/apps/encryption/l10n/pl.js index 164a4a36d57..17f73e4fdb7 100644 --- a/apps/encryption/l10n/pl.js +++ b/apps/encryption/l10n/pl.js @@ -27,6 +27,8 @@ OC.L10N.register( "Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Nie można odszyfrować tego pliku, prawdopodobnie jest to plik udostępniony. Poproś właściciela pliku o ponowne udostępnianie pliku Tobie.", "The share will expire on %s." : "Ten zasób wygaśnie %s", "Cheers!" : "Dzięki!", + "Enable recovery key" : "Włącz klucz odzyskiwania", + "Disable recovery key" : "Wyłącz klucz odzyskiwania", "Recovery key password" : "Hasło klucza odzyskiwania", "Repeat recovery key password" : "Powtórz hasło klucza odzyskiwania", "Change recovery key password:" : "Zmień hasło klucza odzyskiwania", diff --git a/apps/encryption/l10n/pl.json b/apps/encryption/l10n/pl.json index 2bd108ba4c6..f1ef3faf44d 100644 --- a/apps/encryption/l10n/pl.json +++ b/apps/encryption/l10n/pl.json @@ -25,6 +25,8 @@ "Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Nie można odszyfrować tego pliku, prawdopodobnie jest to plik udostępniony. Poproś właściciela pliku o ponowne udostępnianie pliku Tobie.", "The share will expire on %s." : "Ten zasób wygaśnie %s", "Cheers!" : "Dzięki!", + "Enable recovery key" : "Włącz klucz odzyskiwania", + "Disable recovery key" : "Wyłącz klucz odzyskiwania", "Recovery key password" : "Hasło klucza odzyskiwania", "Repeat recovery key password" : "Powtórz hasło klucza odzyskiwania", "Change recovery key password:" : "Zmień hasło klucza odzyskiwania", diff --git a/apps/federatedfilesharing/l10n/hu_HU.js b/apps/federatedfilesharing/l10n/hu_HU.js index 0f0ffdd1295..0a4f26cb20d 100644 --- a/apps/federatedfilesharing/l10n/hu_HU.js +++ b/apps/federatedfilesharing/l10n/hu_HU.js @@ -1,10 +1,14 @@ OC.L10N.register( "federatedfilesharing", { + "Federated sharing" : "Egyesített megosztás", "Invalid Federated Cloud ID" : "Érvénytelen Egyesített Felhő Azonosító", "Sharing %s failed, because this item is already shared with %s" : "%s megosztása nem sikerült, mert ez már meg van osztva vele: %s", "Not allowed to create a federated share with the same user" : "Azonos felhasználóval nem lehet létrehozni egyesített megosztást", + "File is already shared with %s" : "Fájl már megosztva vele: %s", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "%s megosztása sikertelen, mert %s nem található; talán a szerver jelenleg nem elérhető.", + "You received \"/%3$s\" as a remote share from %1$s (on behalf of %2$s)" : "Kapott egy távoli megosztást: \"/%3$s\", innen: %1$s (%2$s nevében)", + "You received \"/%3$s\" as a remote share from %1$s" : "Kapott egy távoli megosztást: \"/%3$s\", innen: %1$s", "Accept" : "Elfogadás", "Decline" : "Elutasítás", "Share with me through my #ownCloud Federated Cloud ID, see %s" : "Ossza meg velem az #ownCloud Egyesített Felhő Azonosító segítségével, lásd %s", diff --git a/apps/federatedfilesharing/l10n/hu_HU.json b/apps/federatedfilesharing/l10n/hu_HU.json index 24e25b6aa4c..77a12ece1a8 100644 --- a/apps/federatedfilesharing/l10n/hu_HU.json +++ b/apps/federatedfilesharing/l10n/hu_HU.json @@ -1,8 +1,12 @@ { "translations": { + "Federated sharing" : "Egyesített megosztás", "Invalid Federated Cloud ID" : "Érvénytelen Egyesített Felhő Azonosító", "Sharing %s failed, because this item is already shared with %s" : "%s megosztása nem sikerült, mert ez már meg van osztva vele: %s", "Not allowed to create a federated share with the same user" : "Azonos felhasználóval nem lehet létrehozni egyesített megosztást", + "File is already shared with %s" : "Fájl már megosztva vele: %s", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "%s megosztása sikertelen, mert %s nem található; talán a szerver jelenleg nem elérhető.", + "You received \"/%3$s\" as a remote share from %1$s (on behalf of %2$s)" : "Kapott egy távoli megosztást: \"/%3$s\", innen: %1$s (%2$s nevében)", + "You received \"/%3$s\" as a remote share from %1$s" : "Kapott egy távoli megosztást: \"/%3$s\", innen: %1$s", "Accept" : "Elfogadás", "Decline" : "Elutasítás", "Share with me through my #ownCloud Federated Cloud ID, see %s" : "Ossza meg velem az #ownCloud Egyesített Felhő Azonosító segítségével, lásd %s", diff --git a/apps/federation/l10n/pt_PT.js b/apps/federation/l10n/pt_PT.js index 38ec142b689..c227ab490b7 100644 --- a/apps/federation/l10n/pt_PT.js +++ b/apps/federation/l10n/pt_PT.js @@ -6,7 +6,7 @@ OC.L10N.register( "No ownCloud server found" : "Nenhum servidor ownCloud encontrado", "Could not add server" : "Não foi possível adicionar servidor", "Federation" : "Federação", - "ownCloud Federation allows you to connect with other trusted ownClouds to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "Federação ownCloud permite-lhe conectar-se com outros ownClouds de confiança para partilhar directórios. Por exemplo, isto será utilizado para auto-completar utilizadores externos para partilhas federadas.", + "ownCloud Federation allows you to connect with other trusted ownClouds to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "A Federação ownCloud permite-lhe conectar-se com outras ownClouds de confiança para partilhar directorias. Por exemplo, isto será utilizado para auto-completar utilizadores externos para partilhas federadas.", "Add server automatically once a federated share was created successfully" : "Adicionar o servidor automaticamente assim que uma partilha federada tenha sido criada com sucesso", "Trusted ownCloud Servers" : "Servidores ownCloud de confiança", "+ Add ownCloud server" : "+ Adicionar servidor ownCloud", diff --git a/apps/federation/l10n/pt_PT.json b/apps/federation/l10n/pt_PT.json index 796a2127170..2316af9c253 100644 --- a/apps/federation/l10n/pt_PT.json +++ b/apps/federation/l10n/pt_PT.json @@ -4,7 +4,7 @@ "No ownCloud server found" : "Nenhum servidor ownCloud encontrado", "Could not add server" : "Não foi possível adicionar servidor", "Federation" : "Federação", - "ownCloud Federation allows you to connect with other trusted ownClouds to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "Federação ownCloud permite-lhe conectar-se com outros ownClouds de confiança para partilhar directórios. Por exemplo, isto será utilizado para auto-completar utilizadores externos para partilhas federadas.", + "ownCloud Federation allows you to connect with other trusted ownClouds to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "A Federação ownCloud permite-lhe conectar-se com outras ownClouds de confiança para partilhar directorias. Por exemplo, isto será utilizado para auto-completar utilizadores externos para partilhas federadas.", "Add server automatically once a federated share was created successfully" : "Adicionar o servidor automaticamente assim que uma partilha federada tenha sido criada com sucesso", "Trusted ownCloud Servers" : "Servidores ownCloud de confiança", "+ Add ownCloud server" : "+ Adicionar servidor ownCloud", diff --git a/apps/federation/l10n/ro.js b/apps/federation/l10n/ro.js index e3035955143..19f37f329a1 100644 --- a/apps/federation/l10n/ro.js +++ b/apps/federation/l10n/ro.js @@ -4,6 +4,12 @@ OC.L10N.register( "Server added to the list of trusted ownClouds" : "Server adăugat la lista serverelor ownCloud de încredere", "Server is already in the list of trusted servers." : "Serverul este deja pe lista celor de încredere.", "No ownCloud server found" : "Nu s-a găsit niciun server ownCloud", - "Could not add server" : "Nu s-a putut adăuga serverul" + "Could not add server" : "Nu s-a putut adăuga serverul", + "Federation" : "Federare", + "ownCloud Federation allows you to connect with other trusted ownClouds to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "Federarea ownCloud îți permite să te conectezi la alte servere ownCloud de încredere pentru a partaja baza de utilizatori. De exemplu, va permite completarea automată a numelor utilizatorilor externi pentru partajarea federată.", + "Add server automatically once a federated share was created successfully" : "Adaugă serverul automat odată ce elementul partajat federat a fost creat cu succes", + "Trusted ownCloud Servers" : "Servere ownCloud de încredere", + "+ Add ownCloud server" : "+ Adaugă server ownCloud", + "ownCloud Server" : "Server ownCloud" }, "nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));"); diff --git a/apps/federation/l10n/ro.json b/apps/federation/l10n/ro.json index 75c514fd8c8..0acb4ebd962 100644 --- a/apps/federation/l10n/ro.json +++ b/apps/federation/l10n/ro.json @@ -2,6 +2,12 @@ "Server added to the list of trusted ownClouds" : "Server adăugat la lista serverelor ownCloud de încredere", "Server is already in the list of trusted servers." : "Serverul este deja pe lista celor de încredere.", "No ownCloud server found" : "Nu s-a găsit niciun server ownCloud", - "Could not add server" : "Nu s-a putut adăuga serverul" + "Could not add server" : "Nu s-a putut adăuga serverul", + "Federation" : "Federare", + "ownCloud Federation allows you to connect with other trusted ownClouds to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "Federarea ownCloud îți permite să te conectezi la alte servere ownCloud de încredere pentru a partaja baza de utilizatori. De exemplu, va permite completarea automată a numelor utilizatorilor externi pentru partajarea federată.", + "Add server automatically once a federated share was created successfully" : "Adaugă serverul automat odată ce elementul partajat federat a fost creat cu succes", + "Trusted ownCloud Servers" : "Servere ownCloud de încredere", + "+ Add ownCloud server" : "+ Adaugă server ownCloud", + "ownCloud Server" : "Server ownCloud" },"pluralForm" :"nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));" }
\ No newline at end of file diff --git a/apps/federation/lib/DAV/FedAuth.php b/apps/federation/lib/DAV/FedAuth.php index bb1041adcdf..21c0d61487c 100644 --- a/apps/federation/lib/DAV/FedAuth.php +++ b/apps/federation/lib/DAV/FedAuth.php @@ -36,6 +36,10 @@ class FedAuth extends AbstractBasic { public function __construct(DbHandler $db) { $this->db = $db; $this->principalPrefix = 'principals/system/'; + + // setup realm + $defaults = new \OC_Defaults(); + $this->realm = $defaults->getName(); } /** diff --git a/apps/files/l10n/ar.js b/apps/files/l10n/ar.js index a9093c45a96..ae180fca235 100644 --- a/apps/files/l10n/ar.js +++ b/apps/files/l10n/ar.js @@ -41,6 +41,12 @@ OC.L10N.register( "Pending" : "قيد الانتظار", "Unable to determine date" : "تعذر تحديد التاريخ", "This operation is forbidden" : "هذة العملية ممنوعة ", + "Could not rename \"{fileName}\"" : "إعادة تسمية الملف \"{fileName}\" لم تنجح", + "Could not create file \"{file}\"" : "لا يمكن إنشاء الملف\"{file}\"", + "Could not create file \"{file}\" because it already exists" : "لا يمكن إنشاء الملف \"{file}\" فهو موجود بالفعل", + "Could not create folder \"{dir}\"" : "لا يمكن إنشاء المجلد \"{dir}\"", + "Could not create folder \"{dir}\" because it already exists" : "لا يمكن إنشاء المجلد \"{dir}\" فهو موجود بالفعل", + "Error deleting file \"{fileName}\"." : "خطأ أثناء حذف الملف \"{fileName}\".", "No entries in this folder match '{filter}'" : "لا يوجد مدخلات في هذا المجلد تتوافق مع '{filter}'", "Name" : "اسم", "Size" : "حجم", diff --git a/apps/files/l10n/ar.json b/apps/files/l10n/ar.json index fe07f30c033..257aa4f1bb4 100644 --- a/apps/files/l10n/ar.json +++ b/apps/files/l10n/ar.json @@ -39,6 +39,12 @@ "Pending" : "قيد الانتظار", "Unable to determine date" : "تعذر تحديد التاريخ", "This operation is forbidden" : "هذة العملية ممنوعة ", + "Could not rename \"{fileName}\"" : "إعادة تسمية الملف \"{fileName}\" لم تنجح", + "Could not create file \"{file}\"" : "لا يمكن إنشاء الملف\"{file}\"", + "Could not create file \"{file}\" because it already exists" : "لا يمكن إنشاء الملف \"{file}\" فهو موجود بالفعل", + "Could not create folder \"{dir}\"" : "لا يمكن إنشاء المجلد \"{dir}\"", + "Could not create folder \"{dir}\" because it already exists" : "لا يمكن إنشاء المجلد \"{dir}\" فهو موجود بالفعل", + "Error deleting file \"{fileName}\"." : "خطأ أثناء حذف الملف \"{fileName}\".", "No entries in this folder match '{filter}'" : "لا يوجد مدخلات في هذا المجلد تتوافق مع '{filter}'", "Name" : "اسم", "Size" : "حجم", diff --git a/apps/files/l10n/de.js b/apps/files/l10n/de.js index 068cbe85376..0866279a435 100644 --- a/apps/files/l10n/de.js +++ b/apps/files/l10n/de.js @@ -21,6 +21,7 @@ OC.L10N.register( "Invalid directory." : "Ungültiges Verzeichnis.", "Files" : "Dateien", "All files" : "Alle Dateien", + "File could not be found" : "Datei konnte nicht gefunden werden", "Home" : "Home", "Close" : "Schließen", "Favorites" : "Favoriten", diff --git a/apps/files/l10n/de.json b/apps/files/l10n/de.json index 2169d228295..0f2939217d9 100644 --- a/apps/files/l10n/de.json +++ b/apps/files/l10n/de.json @@ -19,6 +19,7 @@ "Invalid directory." : "Ungültiges Verzeichnis.", "Files" : "Dateien", "All files" : "Alle Dateien", + "File could not be found" : "Datei konnte nicht gefunden werden", "Home" : "Home", "Close" : "Schließen", "Favorites" : "Favoriten", diff --git a/apps/files/l10n/de_DE.js b/apps/files/l10n/de_DE.js index 9a349357a99..6a2f0ab1f59 100644 --- a/apps/files/l10n/de_DE.js +++ b/apps/files/l10n/de_DE.js @@ -21,6 +21,7 @@ OC.L10N.register( "Invalid directory." : "Ungültiges Verzeichnis.", "Files" : "Dateien", "All files" : "Alle Dateien", + "File could not be found" : "Datei konnte nicht gefunden werden", "Home" : "Zuhause", "Close" : "Schließen", "Favorites" : "Favoriten", diff --git a/apps/files/l10n/de_DE.json b/apps/files/l10n/de_DE.json index 9df557e8fa4..047299f9510 100644 --- a/apps/files/l10n/de_DE.json +++ b/apps/files/l10n/de_DE.json @@ -19,6 +19,7 @@ "Invalid directory." : "Ungültiges Verzeichnis.", "Files" : "Dateien", "All files" : "Alle Dateien", + "File could not be found" : "Datei konnte nicht gefunden werden", "Home" : "Zuhause", "Close" : "Schließen", "Favorites" : "Favoriten", diff --git a/apps/files/l10n/en_GB.js b/apps/files/l10n/en_GB.js index ec8b821c3e7..82ed2364dd6 100644 --- a/apps/files/l10n/en_GB.js +++ b/apps/files/l10n/en_GB.js @@ -21,6 +21,7 @@ OC.L10N.register( "Invalid directory." : "Invalid directory.", "Files" : "Files", "All files" : "All files", + "File could not be found" : "File could not be found", "Home" : "Home", "Close" : "Close", "Favorites" : "Favourites", diff --git a/apps/files/l10n/en_GB.json b/apps/files/l10n/en_GB.json index 6dae8ba41b7..98b3d2000cb 100644 --- a/apps/files/l10n/en_GB.json +++ b/apps/files/l10n/en_GB.json @@ -19,6 +19,7 @@ "Invalid directory." : "Invalid directory.", "Files" : "Files", "All files" : "All files", + "File could not be found" : "File could not be found", "Home" : "Home", "Close" : "Close", "Favorites" : "Favourites", diff --git a/apps/files/l10n/fi_FI.js b/apps/files/l10n/fi_FI.js index 325955a5492..8bf1c47612a 100644 --- a/apps/files/l10n/fi_FI.js +++ b/apps/files/l10n/fi_FI.js @@ -21,6 +21,7 @@ OC.L10N.register( "Invalid directory." : "Virheellinen kansio.", "Files" : "Tiedostot", "All files" : "Kaikki tiedostot", + "File could not be found" : "TIedostoa ei löytynyt", "Home" : "Koti", "Close" : "Sulje", "Favorites" : "Suosikit", diff --git a/apps/files/l10n/fi_FI.json b/apps/files/l10n/fi_FI.json index 966ef7074e4..dc8d168ffb5 100644 --- a/apps/files/l10n/fi_FI.json +++ b/apps/files/l10n/fi_FI.json @@ -19,6 +19,7 @@ "Invalid directory." : "Virheellinen kansio.", "Files" : "Tiedostot", "All files" : "Kaikki tiedostot", + "File could not be found" : "TIedostoa ei löytynyt", "Home" : "Koti", "Close" : "Sulje", "Favorites" : "Suosikit", diff --git a/apps/files/l10n/it.js b/apps/files/l10n/it.js index 7d85f952ed4..b560e6623ca 100644 --- a/apps/files/l10n/it.js +++ b/apps/files/l10n/it.js @@ -21,6 +21,7 @@ OC.L10N.register( "Invalid directory." : "Cartella non valida.", "Files" : "File", "All files" : "Tutti i file", + "File could not be found" : "Il file non può essere trovato", "Home" : "Home", "Close" : "Chiudi", "Favorites" : "Preferiti", diff --git a/apps/files/l10n/it.json b/apps/files/l10n/it.json index ec7003f7382..253fc5198aa 100644 --- a/apps/files/l10n/it.json +++ b/apps/files/l10n/it.json @@ -19,6 +19,7 @@ "Invalid directory." : "Cartella non valida.", "Files" : "File", "All files" : "Tutti i file", + "File could not be found" : "Il file non può essere trovato", "Home" : "Home", "Close" : "Chiudi", "Favorites" : "Preferiti", diff --git a/apps/files/l10n/pl.js b/apps/files/l10n/pl.js index cb243f6bf4d..a366c6a967c 100644 --- a/apps/files/l10n/pl.js +++ b/apps/files/l10n/pl.js @@ -32,6 +32,8 @@ OC.L10N.register( "Could not get result from server." : "Nie można uzyskać wyniku z serwera.", "Uploading..." : "Wgrywanie....", "..." : "...", + "{seconds} second{plural_s} left" : "Pozostało sekund: {seconds}", + "{seconds}s" : "{seconds} s", "File upload is in progress. Leaving the page now will cancel the upload." : "Wysyłanie pliku jest w toku. Jeśli opuścisz tę stronę, wysyłanie zostanie przerwane.", "Actions" : "Akcje", "Download" : "Pobierz", diff --git a/apps/files/l10n/pl.json b/apps/files/l10n/pl.json index 60323d0e757..05b65b2f84a 100644 --- a/apps/files/l10n/pl.json +++ b/apps/files/l10n/pl.json @@ -30,6 +30,8 @@ "Could not get result from server." : "Nie można uzyskać wyniku z serwera.", "Uploading..." : "Wgrywanie....", "..." : "...", + "{seconds} second{plural_s} left" : "Pozostało sekund: {seconds}", + "{seconds}s" : "{seconds} s", "File upload is in progress. Leaving the page now will cancel the upload." : "Wysyłanie pliku jest w toku. Jeśli opuścisz tę stronę, wysyłanie zostanie przerwane.", "Actions" : "Akcje", "Download" : "Pobierz", diff --git a/apps/files/l10n/pt_BR.js b/apps/files/l10n/pt_BR.js index d8a7097161d..7bc2a94deb1 100644 --- a/apps/files/l10n/pt_BR.js +++ b/apps/files/l10n/pt_BR.js @@ -21,6 +21,7 @@ OC.L10N.register( "Invalid directory." : "Diretório inválido.", "Files" : "Arquivos", "All files" : "Todos os arquivos", + "File could not be found" : "O arquivo não foi encontrado", "Home" : "Home", "Close" : "Fechar", "Favorites" : "Favoritos", diff --git a/apps/files/l10n/pt_BR.json b/apps/files/l10n/pt_BR.json index f5fa97a94af..f16c8c374a6 100644 --- a/apps/files/l10n/pt_BR.json +++ b/apps/files/l10n/pt_BR.json @@ -19,6 +19,7 @@ "Invalid directory." : "Diretório inválido.", "Files" : "Arquivos", "All files" : "Todos os arquivos", + "File could not be found" : "O arquivo não foi encontrado", "Home" : "Home", "Close" : "Fechar", "Favorites" : "Favoritos", diff --git a/apps/files/l10n/sq.js b/apps/files/l10n/sq.js index c1485c154d7..b104afdd723 100644 --- a/apps/files/l10n/sq.js +++ b/apps/files/l10n/sq.js @@ -21,6 +21,7 @@ OC.L10N.register( "Invalid directory." : "Drejtori e pavlefshme.", "Files" : "Kartela", "All files" : "Krejt kartelat", + "File could not be found" : "Kartela s’u gjet dot", "Home" : "Kreu", "Close" : "Mbylle", "Favorites" : "Të parapëlqyera", diff --git a/apps/files/l10n/sq.json b/apps/files/l10n/sq.json index 9b40e9f7c9d..80e5f104fa6 100644 --- a/apps/files/l10n/sq.json +++ b/apps/files/l10n/sq.json @@ -19,6 +19,7 @@ "Invalid directory." : "Drejtori e pavlefshme.", "Files" : "Kartela", "All files" : "Krejt kartelat", + "File could not be found" : "Kartela s’u gjet dot", "Home" : "Kreu", "Close" : "Mbylle", "Favorites" : "Të parapëlqyera", diff --git a/apps/files_external/l10n/pl.js b/apps/files_external/l10n/pl.js index 8c3e394627e..00ae59b27c6 100644 --- a/apps/files_external/l10n/pl.js +++ b/apps/files_external/l10n/pl.js @@ -16,6 +16,7 @@ OC.L10N.register( "Saved" : "Zapisano", "Username" : "Nazwa użytkownika", "Password" : "Hasło", + "Credentials required" : "Wymagane poświadczenia", "Save" : "Zapisz", "Storage with id \"%i\" not found" : "Id magazynu nie został znaleziony", "Invalid mount point" : "Nieprawidłowy punkt montowania", diff --git a/apps/files_external/l10n/pl.json b/apps/files_external/l10n/pl.json index 8e00fc31863..235de7e9d10 100644 --- a/apps/files_external/l10n/pl.json +++ b/apps/files_external/l10n/pl.json @@ -14,6 +14,7 @@ "Saved" : "Zapisano", "Username" : "Nazwa użytkownika", "Password" : "Hasło", + "Credentials required" : "Wymagane poświadczenia", "Save" : "Zapisz", "Storage with id \"%i\" not found" : "Id magazynu nie został znaleziony", "Invalid mount point" : "Nieprawidłowy punkt montowania", diff --git a/apps/files_sharing/l10n/pl.js b/apps/files_sharing/l10n/pl.js index ebdfe2aa738..e1bec669533 100644 --- a/apps/files_sharing/l10n/pl.js +++ b/apps/files_sharing/l10n/pl.js @@ -25,6 +25,9 @@ OC.L10N.register( "Invalid ownCloud url" : "Błędny adres URL", "Shared by" : "Udostępniane przez", "Sharing" : "Udostępnianie", + "Public link sharing is disabled by the administrator" : "Udostępnianie linków publicznych zostało zablokowane przez twojego administratora", + "Public upload disabled by the administrator" : "Publiczne wczytywanie zostało zablokowane przez twojego administratora", + "Public upload is only possible for publicly shared folders" : "Publiczne wczytywanie jest możliwe wyłącznie do katalogów publicznych", "A file or folder has been <strong>shared</strong>" : "Plik lub folder stał się <strong>współdzielony</strong>", "You shared %1$s with %2$s" : "Współdzielisz %1$s z %2$s", "You shared %1$s with group %2$s" : "Współdzielisz %1$s z grupą %2$s", diff --git a/apps/files_sharing/l10n/pl.json b/apps/files_sharing/l10n/pl.json index f5de5dd368c..6190871b16f 100644 --- a/apps/files_sharing/l10n/pl.json +++ b/apps/files_sharing/l10n/pl.json @@ -23,6 +23,9 @@ "Invalid ownCloud url" : "Błędny adres URL", "Shared by" : "Udostępniane przez", "Sharing" : "Udostępnianie", + "Public link sharing is disabled by the administrator" : "Udostępnianie linków publicznych zostało zablokowane przez twojego administratora", + "Public upload disabled by the administrator" : "Publiczne wczytywanie zostało zablokowane przez twojego administratora", + "Public upload is only possible for publicly shared folders" : "Publiczne wczytywanie jest możliwe wyłącznie do katalogów publicznych", "A file or folder has been <strong>shared</strong>" : "Plik lub folder stał się <strong>współdzielony</strong>", "You shared %1$s with %2$s" : "Współdzielisz %1$s z %2$s", "You shared %1$s with group %2$s" : "Współdzielisz %1$s z grupą %2$s", diff --git a/apps/files_sharing/l10n/ro.js b/apps/files_sharing/l10n/ro.js index 2481f77bcc6..5cddb6e8cdf 100644 --- a/apps/files_sharing/l10n/ro.js +++ b/apps/files_sharing/l10n/ro.js @@ -12,12 +12,28 @@ OC.L10N.register( "Shared with others" : "Partajat cu alții", "Shared by link" : "Partajat prin link", "Nothing shared with you yet" : "Nimic nu e partajat cu tine încă", + "Files and folders others share with you will show up here" : "Fișierele și directoarele partajate cu tine vor apărea aici", "Nothing shared yet" : "Nimic partajat încă", + "Files and folders you share will show up here" : "Fișierele și directoarele pe care le partajezi vor apărea aici", + "No shared links" : "Nicio legătură partajată", + "Files and folders you share by link will show up here" : "Fișierele și directoarele pe care le partajezi prin legături vor apărea aici", + "Remote share" : "Element partajat la distanță", + "Remote share password" : "Parolă element partajat la distanță", "Cancel" : "Anulare", + "Add remote share" : "Adaugă element partajat la distanță", + "You can upload into this folder" : "Poți încărca în acest director", "No ownCloud installation (7 or higher) found at {remote}" : "Nu s-a găsit nicio instanță ownCloud (versiunea 7 sau mai mare) la {remote}", "Invalid ownCloud url" : "URL ownCloud invalid", "Shared by" : "impartite in ", "Sharing" : "Partajare", + "Share API is disabled" : "API-ul de partajare este dezactivat", + "Wrong share ID, share doesn't exist" : "ID greșit al elementului partajat, acesta nu există", + "Could not delete share" : "Nu s-a putut șterge elementul partajat", + "Please specify a file or folder path" : "Specifică un fișier sau o cale către un director", + "Wrong path, file/folder doesn't exist" : "Cale greșită, fișierul/directorul nu există", + "Please specify a valid user" : "Specifică un utilizator valid", + "Please specify a valid group" : "Specifică un grup valid", + "Invalid date, date format must be YYYY-MM-DD" : "Dată invalidă, formatul trebuie să fie AAAA-LL-ZZ", "Not a directory" : "Nu este un director", "Could not lock path" : "Calea nu a putut fi blocată", "Cannot increase permissions" : "Nu se pot extinde permisiunile", @@ -26,6 +42,9 @@ OC.L10N.register( "You shared %1$s with group %2$s" : "Ai partajat %1$s cu grupul %2$s", "You shared %1$s via link" : "Ai partajat %1$s prin legătură", "%2$s shared %1$s with you" : "%2$s a partajat %1$s cu tine", + "Shared with %2$s" : "Partajat cu %2$s", + "Shared with %3$s by %2$s" : "Partajat de %2$s cu %3$s", + "Shared with group %2$s" : "Partajat cu grupul %2$s", "Shares" : "Partajări", "This share is password-protected" : "Această partajare este protejată cu parolă", "The password is wrong. Try again." : "Parola este incorectă. Încercaţi din nou.", @@ -38,6 +57,7 @@ OC.L10N.register( "sharing is disabled" : "Partajare este oprită", "Add to your ownCloud" : "Adaugă propriul tău ownCloud", "Download" : "Descarcă", - "Download %s" : "Descarcă %s" + "Download %s" : "Descarcă %s", + "Direct link" : "Legătură directă" }, "nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));"); diff --git a/apps/files_sharing/l10n/ro.json b/apps/files_sharing/l10n/ro.json index e3f9450bcb5..6d0d87037db 100644 --- a/apps/files_sharing/l10n/ro.json +++ b/apps/files_sharing/l10n/ro.json @@ -10,12 +10,28 @@ "Shared with others" : "Partajat cu alții", "Shared by link" : "Partajat prin link", "Nothing shared with you yet" : "Nimic nu e partajat cu tine încă", + "Files and folders others share with you will show up here" : "Fișierele și directoarele partajate cu tine vor apărea aici", "Nothing shared yet" : "Nimic partajat încă", + "Files and folders you share will show up here" : "Fișierele și directoarele pe care le partajezi vor apărea aici", + "No shared links" : "Nicio legătură partajată", + "Files and folders you share by link will show up here" : "Fișierele și directoarele pe care le partajezi prin legături vor apărea aici", + "Remote share" : "Element partajat la distanță", + "Remote share password" : "Parolă element partajat la distanță", "Cancel" : "Anulare", + "Add remote share" : "Adaugă element partajat la distanță", + "You can upload into this folder" : "Poți încărca în acest director", "No ownCloud installation (7 or higher) found at {remote}" : "Nu s-a găsit nicio instanță ownCloud (versiunea 7 sau mai mare) la {remote}", "Invalid ownCloud url" : "URL ownCloud invalid", "Shared by" : "impartite in ", "Sharing" : "Partajare", + "Share API is disabled" : "API-ul de partajare este dezactivat", + "Wrong share ID, share doesn't exist" : "ID greșit al elementului partajat, acesta nu există", + "Could not delete share" : "Nu s-a putut șterge elementul partajat", + "Please specify a file or folder path" : "Specifică un fișier sau o cale către un director", + "Wrong path, file/folder doesn't exist" : "Cale greșită, fișierul/directorul nu există", + "Please specify a valid user" : "Specifică un utilizator valid", + "Please specify a valid group" : "Specifică un grup valid", + "Invalid date, date format must be YYYY-MM-DD" : "Dată invalidă, formatul trebuie să fie AAAA-LL-ZZ", "Not a directory" : "Nu este un director", "Could not lock path" : "Calea nu a putut fi blocată", "Cannot increase permissions" : "Nu se pot extinde permisiunile", @@ -24,6 +40,9 @@ "You shared %1$s with group %2$s" : "Ai partajat %1$s cu grupul %2$s", "You shared %1$s via link" : "Ai partajat %1$s prin legătură", "%2$s shared %1$s with you" : "%2$s a partajat %1$s cu tine", + "Shared with %2$s" : "Partajat cu %2$s", + "Shared with %3$s by %2$s" : "Partajat de %2$s cu %3$s", + "Shared with group %2$s" : "Partajat cu grupul %2$s", "Shares" : "Partajări", "This share is password-protected" : "Această partajare este protejată cu parolă", "The password is wrong. Try again." : "Parola este incorectă. Încercaţi din nou.", @@ -36,6 +55,7 @@ "sharing is disabled" : "Partajare este oprită", "Add to your ownCloud" : "Adaugă propriul tău ownCloud", "Download" : "Descarcă", - "Download %s" : "Descarcă %s" + "Download %s" : "Descarcă %s", + "Direct link" : "Legătură directă" },"pluralForm" :"nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));" }
\ No newline at end of file diff --git a/apps/files_sharing/lib/API/Share20OCS.php b/apps/files_sharing/lib/API/Share20OCS.php index b10c51ce2c9..3d6a715be99 100644 --- a/apps/files_sharing/lib/API/Share20OCS.php +++ b/apps/files_sharing/lib/API/Share20OCS.php @@ -100,15 +100,8 @@ class Share20OCS { */ protected function formatShare(\OCP\Share\IShare $share) { $sharedBy = $this->userManager->get($share->getSharedBy()); - // for federated shares the owner can be a remote user, in this - // case we use the initiator - if ($this->userManager->userExists($share->getShareOwner())) { - $shareOwner = $this->userManager->get($share->getShareOwner()); - $localUser = $share->getShareOwner(); - } else { - $shareOwner = $this->userManager->get($share->getSharedBy()); - $localUser = $share->getSharedBy(); - } + $shareOwner = $this->userManager->get($share->getShareOwner()); + $result = [ 'id' => $share->getId(), 'share_type' => $share->getShareType(), @@ -123,8 +116,16 @@ class Share20OCS { 'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(), ]; - $node = $share->getNode(); - $result['path'] = $this->rootFolder->getUserFolder($localUser)->getRelativePath($node->getPath()); + $userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); + $nodes = $userFolder->getById($share->getNodeId()); + + if (empty($nodes)) { + throw new NotFoundException(); + } + + $node = $nodes[0]; + + $result['path'] = $userFolder->getRelativePath($node->getPath()); if ($node instanceOf \OCP\Files\Folder) { $result['item_type'] = 'folder'; } else { @@ -536,7 +537,6 @@ class Share20OCS { $shares = array_merge($shares, $federatedShares); } - $formatted = []; foreach ($shares as $share) { try { diff --git a/apps/files_sharing/tests/API/Share20OCSTest.php b/apps/files_sharing/tests/API/Share20OCSTest.php index 02b16d7bf88..b760a0f47a0 100644 --- a/apps/files_sharing/tests/API/Share20OCSTest.php +++ b/apps/files_sharing/tests/API/Share20OCSTest.php @@ -433,8 +433,12 @@ class Share20OCSTest extends \Test\TestCase { ->method('getRelativePath') ->will($this->returnArgument(0)); + $userFolder->method('getById') + ->with($share->getNodeId()) + ->willReturn([$share->getNode()]); + $this->rootFolder->method('getUserFolder') - ->with($share->getShareOwner()) + ->with($this->currentUser->getUID()) ->willReturn($userFolder); $this->urlGenerator @@ -2006,8 +2010,19 @@ class Share20OCSTest extends \Test\TestCase { ->willReturn('myLink'); - $this->rootFolder->method('getUserFolder')->with($share->getShareOwner())->will($this->returnSelf()); - $this->rootFolder->method('getRelativePath')->will($this->returnArgument(0)); + $this->rootFolder->method('getUserFolder') + ->with($this->currentUser->getUID()) + ->will($this->returnSelf()); + + if (!$exception) { + $this->rootFolder->method('getById') + ->with($share->getNodeId()) + ->willReturn([$share->getNode()]); + + $this->rootFolder->method('getRelativePath') + ->with($share->getNode()->getPath()) + ->will($this->returnArgument(0)); + } try { $result = $this->invokePrivate($this->ocs, 'formatShare', [$share]); diff --git a/apps/files_sharing/tests/ApiTest.php b/apps/files_sharing/tests/ApiTest.php index f44c346236e..058b0c4758c 100644 --- a/apps/files_sharing/tests/ApiTest.php +++ b/apps/files_sharing/tests/ApiTest.php @@ -764,8 +764,7 @@ class ApiTest extends TestCase { // we should get exactly one result $this->assertCount(1, $data); - $expectedPath = $this->folder . $this->subfolder; - $this->assertEquals($expectedPath, $data[0]['path']); + $this->assertEquals($this->subfolder, $data[0]['path']); $this->shareManager->deleteShare($share2); $this->shareManager->deleteShare($share1); @@ -801,6 +800,9 @@ class ApiTest extends TestCase { ->setPermissions(1); $share3 = $this->shareManager->createShare($share3); + /* + * Test as recipient + */ $request = $this->createRequest(['path' => '/', 'subfiles' => 'true']); $ocs = $this->createOCS($request, self::TEST_FILES_SHARING_API_USER3); $result = $ocs->getShares(); @@ -811,9 +813,37 @@ class ApiTest extends TestCase { // we should get exactly one result $this->assertCount(1, $data); + $this->assertEquals($this->subsubfolder, $data[0]['path']); - $expectedPath = $this->folder . $this->subfolder . $this->subsubfolder; - $this->assertEquals($expectedPath, $data[0]['path']); + /* + * Test for first owner/initiator + */ + $request = $this->createRequest([]); + $ocs = $this->createOCS($request, self::TEST_FILES_SHARING_API_USER1); + $result = $ocs->getShares(); + $this->assertTrue($result->succeeded()); + + // test should return one share within $this->folder + $data = $result->getData(); + + // we should get exactly one result + $this->assertCount(1, $data); + $this->assertEquals($this->folder . $this->subfolder, $data[0]['path']); + + /* + * Test for second initiator + */ + $request = $this->createRequest([]); + $ocs = $this->createOCS($request, self::TEST_FILES_SHARING_API_USER2); + $result = $ocs->getShares(); + $this->assertTrue($result->succeeded()); + + // test should return one share within $this->folder + $data = $result->getData(); + + // we should get exactly one result + $this->assertCount(1, $data); + $this->assertEquals($this->subfolder . $this->subsubfolder, $data[0]['path']); $this->shareManager->deleteShare($share1); $this->shareManager->deleteShare($share2); @@ -922,8 +952,7 @@ class ApiTest extends TestCase { // we should get exactly one result $this->assertCount(1, $data); - $expectedPath = $this->folder.$this->subfolder.$this->filename; - $this->assertEquals($expectedPath, $data[0]['path']); + $this->assertEquals($this->filename, $data[0]['path']); $this->shareManager->deleteShare($share1); $this->shareManager->deleteShare($share2); diff --git a/apps/systemtags/l10n/hu_HU.js b/apps/systemtags/l10n/hu_HU.js index 13d1705ff1c..783b90ece8a 100644 --- a/apps/systemtags/l10n/hu_HU.js +++ b/apps/systemtags/l10n/hu_HU.js @@ -21,6 +21,7 @@ OC.L10N.register( "%1$s assigned system tag %3$s to %2$s" : "%1$s hozzárendelte ezt a rendszer címkét: %3$s neki: %2$s", "You unassigned system tag %3$s from %2$s" : "%3$s rendszer címke hozzárendelést elvette tőle: %2$s", "%1$s unassigned system tag %3$s from %2$s" : "%1$s elvette ezt a rendszer címkét %3$s tőle: %2$s", + "%s (restricted)" : "%s (korlátozott)", "%s (invisible)" : "%s (láthatatlan)", "No files in here" : "Itt nincsenek fájlok", "No entries found in this folder" : "Nincsenek bejegyzések ebben a könyvtárban", diff --git a/apps/systemtags/l10n/hu_HU.json b/apps/systemtags/l10n/hu_HU.json index 6408b3a5314..f89da5d3dfd 100644 --- a/apps/systemtags/l10n/hu_HU.json +++ b/apps/systemtags/l10n/hu_HU.json @@ -19,6 +19,7 @@ "%1$s assigned system tag %3$s to %2$s" : "%1$s hozzárendelte ezt a rendszer címkét: %3$s neki: %2$s", "You unassigned system tag %3$s from %2$s" : "%3$s rendszer címke hozzárendelést elvette tőle: %2$s", "%1$s unassigned system tag %3$s from %2$s" : "%1$s elvette ezt a rendszer címkét %3$s tőle: %2$s", + "%s (restricted)" : "%s (korlátozott)", "%s (invisible)" : "%s (láthatatlan)", "No files in here" : "Itt nincsenek fájlok", "No entries found in this folder" : "Nincsenek bejegyzések ebben a könyvtárban", diff --git a/apps/updatenotification/l10n/hu_HU.js b/apps/updatenotification/l10n/hu_HU.js index 1df38a6746f..cce4549f01e 100644 --- a/apps/updatenotification/l10n/hu_HU.js +++ b/apps/updatenotification/l10n/hu_HU.js @@ -1,8 +1,11 @@ OC.L10N.register( "updatenotification", { + "Update notifications" : "Frissítési értesítés", "{version} is available. Get more information on how to update." : "{version} rendelkezésre áll. További információ a frissítéshez.", "Updated channel" : "Frissített csatorna", + "ownCloud core" : "ownCloud mag", + "Update for %1$s to version %2$s is available." : "%1$s frissíthető %2$s verzióra.", "Updater" : "Frissítéskezelő", "A new version is available: %s" : "Új verzió érhető el: %s", "Open updater" : "Frissítő megnyitása", diff --git a/apps/updatenotification/l10n/hu_HU.json b/apps/updatenotification/l10n/hu_HU.json index eb953669ab1..952b499d52a 100644 --- a/apps/updatenotification/l10n/hu_HU.json +++ b/apps/updatenotification/l10n/hu_HU.json @@ -1,6 +1,9 @@ { "translations": { + "Update notifications" : "Frissítési értesítés", "{version} is available. Get more information on how to update." : "{version} rendelkezésre áll. További információ a frissítéshez.", "Updated channel" : "Frissített csatorna", + "ownCloud core" : "ownCloud mag", + "Update for %1$s to version %2$s is available." : "%1$s frissíthető %2$s verzióra.", "Updater" : "Frissítéskezelő", "A new version is available: %s" : "Új verzió érhető el: %s", "Open updater" : "Frissítő megnyitása", diff --git a/apps/user_ldap/l10n/pl.js b/apps/user_ldap/l10n/pl.js index de8f1f49ad3..a75b38fc12f 100644 --- a/apps/user_ldap/l10n/pl.js +++ b/apps/user_ldap/l10n/pl.js @@ -31,6 +31,7 @@ OC.L10N.register( "Confirm Deletion" : "Potwierdź usunięcie", "Mappings cleared successfully!" : "Mapowanie wyczyszczone!", "Error while clearing the mappings." : "Błąd podczas czyszczenia mapowania.", + "Mode switch" : "Przełącznik trybów", "Select attributes" : "Wybierz atrybuty", "_%s group found_::_%s groups found_" : ["%s znaleziona grupa","%s znalezionych grup","%s znalezionych grup"], "_%s user found_::_%s users found_" : ["%s znaleziony użytkownik","%s znalezionych użytkowników","%s znalezionych użytkowników"], diff --git a/apps/user_ldap/l10n/pl.json b/apps/user_ldap/l10n/pl.json index 5a853c3abed..472148fa3b0 100644 --- a/apps/user_ldap/l10n/pl.json +++ b/apps/user_ldap/l10n/pl.json @@ -29,6 +29,7 @@ "Confirm Deletion" : "Potwierdź usunięcie", "Mappings cleared successfully!" : "Mapowanie wyczyszczone!", "Error while clearing the mappings." : "Błąd podczas czyszczenia mapowania.", + "Mode switch" : "Przełącznik trybów", "Select attributes" : "Wybierz atrybuty", "_%s group found_::_%s groups found_" : ["%s znaleziona grupa","%s znalezionych grup","%s znalezionych grup"], "_%s user found_::_%s users found_" : ["%s znaleziony użytkownik","%s znalezionych użytkowników","%s znalezionych użytkowników"], diff --git a/apps/user_ldap/lib/Group_LDAP.php b/apps/user_ldap/lib/Group_LDAP.php index 7c12613f34d..14d86fb0619 100644 --- a/apps/user_ldap/lib/Group_LDAP.php +++ b/apps/user_ldap/lib/Group_LDAP.php @@ -535,7 +535,7 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface { } if(isset($this->cachedGroupsByMember[$uid])) { - $groups[] = $this->cachedGroupsByMember[$uid]; + $groups = array_merge($groups, $this->cachedGroupsByMember[$uid]); } else { $groupsByMember = array_values($this->getGroupsByMember($uid)); $groupsByMember = $this->access->ownCloudGroupNames($groupsByMember); diff --git a/apps/user_ldap/tests/Group_LDAPTest.php b/apps/user_ldap/tests/Group_LDAPTest.php index 556c4b0b394..35d525068a6 100644 --- a/apps/user_ldap/tests/Group_LDAPTest.php +++ b/apps/user_ldap/tests/Group_LDAPTest.php @@ -454,4 +454,57 @@ class Group_LDAPTest extends \Test\TestCase { $groupBackend->getUserGroups('userX'); } + public function testGetGroupsByMember() { + $access = $this->getAccessMock(); + + $access->connection->expects($this->any()) + ->method('__get') + ->will($this->returnCallback(function($name) { + if($name === 'useMemberOfToDetectMembership') { + return 0; + } else if($name === 'ldapDynamicGroupMemberURL') { + return ''; + } else if($name === 'ldapNestedGroups') { + return false; + } + return 1; + })); + + $dn = 'cn=userX,dc=foobar'; + + $access->connection->hasPrimaryGroups = false; + + $access->expects($this->exactly(2)) + ->method('username2dn') + ->will($this->returnValue($dn)); + + $access->expects($this->never()) + ->method('readAttribute') + ->with($dn, 'memberOf'); + + $group1 = [ + 'cn' => 'group1', + 'dn' => ['cn=group1,ou=groups,dc=domain,dc=com'], + ]; + $group2 = [ + 'cn' => 'group2', + 'dn' => ['cn=group2,ou=groups,dc=domain,dc=com'], + ]; + + $access->expects($this->once()) + ->method('ownCloudGroupNames') + ->with([$group1, $group2]) + ->will($this->returnValue(['group1', 'group2'])); + + $access->expects($this->once()) + ->method('fetchListOfGroups') + ->will($this->returnValue([$group1, $group2])); + + $groupBackend = new GroupLDAP($access); + $groups = $groupBackend->getUserGroups('userX'); + $this->assertEquals(['group1', 'group2'], $groups); + + $groupsAgain = $groupBackend->getUserGroups('userX'); + $this->assertEquals(['group1', 'group2'], $groupsAgain); + } } diff --git a/build/integration/features/webdav-related.feature b/build/integration/features/webdav-related.feature index f4d40615fa7..14ff505463c 100644 --- a/build/integration/features/webdav-related.feature +++ b/build/integration/features/webdav-related.feature @@ -82,7 +82,7 @@ Feature: webdav-related And As an "admin" When Downloading file "/welcome.txt" Then The following headers should be set - |Content-Disposition|attachment| + |Content-Disposition|attachment; filename*=UTF-8''welcome.txt; filename="welcome.txt"| |Content-Security-Policy|default-src 'none';| |X-Content-Type-Options |nosniff| |X-Download-Options|noopen| @@ -97,7 +97,7 @@ Feature: webdav-related And As an "admin" When Downloading file "/welcome.txt" Then The following headers should be set - |Content-Disposition|attachment| + |Content-Disposition|attachment; filename*=UTF-8''welcome.txt; filename="welcome.txt"| |Content-Security-Policy|default-src 'none';| |X-Content-Type-Options |nosniff| |X-Download-Options|noopen| diff --git a/core/Command/Maintenance/Install.php b/core/Command/Maintenance/Install.php index b1b63b9b3bd..12a61d6341a 100644 --- a/core/Command/Maintenance/Install.php +++ b/core/Command/Maintenance/Install.php @@ -106,7 +106,12 @@ class Install extends Command { $dbUser = $input->getOption('database-user'); $dbPass = $input->getOption('database-pass'); $dbName = $input->getOption('database-name'); - $dbHost = $input->getOption('database-host'); + if ($db === 'oci') { + // an empty hostname needs to be read from the raw parameters + $dbHost = $input->getParameterOption('--database-host', ''); + } else { + $dbHost = $input->getOption('database-host'); + } $dbTablePrefix = 'oc_'; if ($input->hasParameterOption('--database-table-prefix')) { $dbTablePrefix = (string) $input->getOption('database-table-prefix'); diff --git a/core/Controller/LoginController.php b/core/Controller/LoginController.php index c64f58ae2cc..7806e1de904 100644 --- a/core/Controller/LoginController.php +++ b/core/Controller/LoginController.php @@ -171,6 +171,7 @@ class LoginController extends Controller { * @return RedirectResponse */ public function tryLogin($user, $password, $redirect_url) { + $originalUser = $user; // TODO: Add all the insane error handling /* @var $loginResult IUser */ $loginResult = $this->userManager->checkPassword($user, $password); @@ -186,8 +187,8 @@ class LoginController extends Controller { $this->session->set('loginMessages', [ ['invalidpassword'] ]); - // Read current user and append if possible - $args = !is_null($user) ? ['user' => $user] : []; + // Read current user and append if possible - we need to return the unmodified user otherwise we will leak the login name + $args = !is_null($user) ? ['user' => $originalUser] : []; return new RedirectResponse($this->urlGenerator->linkToRoute('core.login.showLoginForm', $args)); } // TODO: remove password checks from above and let the user session handle failures diff --git a/core/l10n/de.js b/core/l10n/de.js index 177376c3802..52e1639efff 100644 --- a/core/l10n/de.js +++ b/core/l10n/de.js @@ -298,6 +298,7 @@ OC.L10N.register( "Thank you for your patience." : "Vielen Dank für Deine Geduld.", "Two-step verification" : "Bestätigung in zwei Schritten", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "Die erweiterte Sicherheit wurde für dich Konto aktiviert. Bitte authentifiziere dich mit einem zweiten Faktor. ", + "Cancel login" : "Anmelden abbrechen", "Please authenticate using the selected factor." : "Bitte authentifiziere dich mit dem ausgewählten zweiten Faktor. ", "An error occured while verifying the token" : "Es ist ein Fehler bei der Verifizierung des Tokens aufgetreten", "You are accessing the server from an untrusted domain." : "Du greifst von einer nicht vertrauenswürdigen Domain auf den Server zu.", diff --git a/core/l10n/de.json b/core/l10n/de.json index a66abe70162..d713c507248 100644 --- a/core/l10n/de.json +++ b/core/l10n/de.json @@ -296,6 +296,7 @@ "Thank you for your patience." : "Vielen Dank für Deine Geduld.", "Two-step verification" : "Bestätigung in zwei Schritten", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "Die erweiterte Sicherheit wurde für dich Konto aktiviert. Bitte authentifiziere dich mit einem zweiten Faktor. ", + "Cancel login" : "Anmelden abbrechen", "Please authenticate using the selected factor." : "Bitte authentifiziere dich mit dem ausgewählten zweiten Faktor. ", "An error occured while verifying the token" : "Es ist ein Fehler bei der Verifizierung des Tokens aufgetreten", "You are accessing the server from an untrusted domain." : "Du greifst von einer nicht vertrauenswürdigen Domain auf den Server zu.", diff --git a/core/l10n/de_DE.js b/core/l10n/de_DE.js index f10a73798d6..9e379c2c025 100644 --- a/core/l10n/de_DE.js +++ b/core/l10n/de_DE.js @@ -298,6 +298,7 @@ OC.L10N.register( "Thank you for your patience." : "Vielen Dank für Ihre Geduld.", "Two-step verification" : "Bestätigung in zwei Schritten", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "Die erweiterte Sicherheit wurde für Ihr Konto aktiviert. Bitte authentifizieren Sie sich mit einem zweiten Faktor. ", + "Cancel login" : "Anmelden abbrechen", "Please authenticate using the selected factor." : "Bitte authentifizieren Sie sich mit dem ausgewählten zweiten Faktor. ", "An error occured while verifying the token" : "Es ist ein Fehler bei der Verifizierung des Tokens aufgetreten", "You are accessing the server from an untrusted domain." : "Sie greifen von einer nicht vertrauenswürdigen Domain auf den Server zu.", diff --git a/core/l10n/de_DE.json b/core/l10n/de_DE.json index 3b6267dca62..bcdf1f52795 100644 --- a/core/l10n/de_DE.json +++ b/core/l10n/de_DE.json @@ -296,6 +296,7 @@ "Thank you for your patience." : "Vielen Dank für Ihre Geduld.", "Two-step verification" : "Bestätigung in zwei Schritten", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "Die erweiterte Sicherheit wurde für Ihr Konto aktiviert. Bitte authentifizieren Sie sich mit einem zweiten Faktor. ", + "Cancel login" : "Anmelden abbrechen", "Please authenticate using the selected factor." : "Bitte authentifizieren Sie sich mit dem ausgewählten zweiten Faktor. ", "An error occured while verifying the token" : "Es ist ein Fehler bei der Verifizierung des Tokens aufgetreten", "You are accessing the server from an untrusted domain." : "Sie greifen von einer nicht vertrauenswürdigen Domain auf den Server zu.", diff --git a/core/l10n/en_GB.js b/core/l10n/en_GB.js index 6f5859aae3e..76045cf033d 100644 --- a/core/l10n/en_GB.js +++ b/core/l10n/en_GB.js @@ -298,6 +298,7 @@ OC.L10N.register( "Thank you for your patience." : "Thank you for your patience.", "Two-step verification" : "Two-step verification", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "Enhanced security has been enabled for your account. Please authenticate using a second factor.", + "Cancel login" : "Cancel login", "Please authenticate using the selected factor." : "Please authenticate using the selected factor.", "An error occured while verifying the token" : "An error occured while verifying the token", "You are accessing the server from an untrusted domain." : "You are accessing the server from an untrusted domain.", diff --git a/core/l10n/en_GB.json b/core/l10n/en_GB.json index 7aedc2a730f..d71ff3f8ada 100644 --- a/core/l10n/en_GB.json +++ b/core/l10n/en_GB.json @@ -296,6 +296,7 @@ "Thank you for your patience." : "Thank you for your patience.", "Two-step verification" : "Two-step verification", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "Enhanced security has been enabled for your account. Please authenticate using a second factor.", + "Cancel login" : "Cancel login", "Please authenticate using the selected factor." : "Please authenticate using the selected factor.", "An error occured while verifying the token" : "An error occured while verifying the token", "You are accessing the server from an untrusted domain." : "You are accessing the server from an untrusted domain.", diff --git a/core/l10n/fi_FI.js b/core/l10n/fi_FI.js index d15457022a2..d491cf213fc 100644 --- a/core/l10n/fi_FI.js +++ b/core/l10n/fi_FI.js @@ -187,6 +187,7 @@ OC.L10N.register( "Warning" : "Varoitus", "Error while sending notification" : "Virhe ilmoitusta lähettäessä", "Non-existing tag #{tag}" : "Ei olemassa oleva tunniste #{tag}", + "restricted" : "rajoitettu", "invisible" : "näkymätön", "({scope})" : "({scope})", "Delete" : "Poista", @@ -289,6 +290,7 @@ OC.L10N.register( "Thank you for your patience." : "Kiitos kärsivällisyydestäsi.", "Two-step verification" : "Kaksivaiheinen vahvistus", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "Tililläsi on käytössä lisäturvatoimia. Tunnistaudu käyttäen kaksivaiheista vahvistusta.", + "Cancel login" : "Peru kirjautuminen", "Please authenticate using the selected factor." : "Tunnistaudu käyttäen valittua vahvistusta.", "You are accessing the server from an untrusted domain." : "Olet yhteydessä palvelimeen epäluotettavasta verkko-osoitteesta.", "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. An example configuration is provided in config/config.sample.php." : "Ota yhteys ylläpitoon. Jos olet tämän asennuksen ylläpitäjä, määritä \"trusted_domains\"-asetus config/config.php-tiedostossa. Esimerkkimääritys on tarjolla tiedostossa config/config.sample.php.", diff --git a/core/l10n/fi_FI.json b/core/l10n/fi_FI.json index 14565b0b46e..e3f7fb28a8c 100644 --- a/core/l10n/fi_FI.json +++ b/core/l10n/fi_FI.json @@ -185,6 +185,7 @@ "Warning" : "Varoitus", "Error while sending notification" : "Virhe ilmoitusta lähettäessä", "Non-existing tag #{tag}" : "Ei olemassa oleva tunniste #{tag}", + "restricted" : "rajoitettu", "invisible" : "näkymätön", "({scope})" : "({scope})", "Delete" : "Poista", @@ -287,6 +288,7 @@ "Thank you for your patience." : "Kiitos kärsivällisyydestäsi.", "Two-step verification" : "Kaksivaiheinen vahvistus", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "Tililläsi on käytössä lisäturvatoimia. Tunnistaudu käyttäen kaksivaiheista vahvistusta.", + "Cancel login" : "Peru kirjautuminen", "Please authenticate using the selected factor." : "Tunnistaudu käyttäen valittua vahvistusta.", "You are accessing the server from an untrusted domain." : "Olet yhteydessä palvelimeen epäluotettavasta verkko-osoitteesta.", "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. An example configuration is provided in config/config.sample.php." : "Ota yhteys ylläpitoon. Jos olet tämän asennuksen ylläpitäjä, määritä \"trusted_domains\"-asetus config/config.php-tiedostossa. Esimerkkimääritys on tarjolla tiedostossa config/config.sample.php.", diff --git a/core/l10n/it.js b/core/l10n/it.js index 7daff3e0dad..6769455e669 100644 --- a/core/l10n/it.js +++ b/core/l10n/it.js @@ -298,6 +298,7 @@ OC.L10N.register( "Thank you for your patience." : "Grazie per la pazienza.", "Two-step verification" : "Verifica in due fasi", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "La sicurezza migliorata è stata abilitata per il tuo account. Autenticati utilizzando un secondo fattore.", + "Cancel login" : "Annulla l'accesso", "Please authenticate using the selected factor." : "Autentica utilizzando il fattore selezionato.", "An error occured while verifying the token" : "Si è verificato un errore durante la verifica del token", "You are accessing the server from an untrusted domain." : "Stai accedendo al server da un dominio non attendibile.", diff --git a/core/l10n/it.json b/core/l10n/it.json index 9d403e4f724..ac6d16d90b9 100644 --- a/core/l10n/it.json +++ b/core/l10n/it.json @@ -296,6 +296,7 @@ "Thank you for your patience." : "Grazie per la pazienza.", "Two-step verification" : "Verifica in due fasi", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "La sicurezza migliorata è stata abilitata per il tuo account. Autenticati utilizzando un secondo fattore.", + "Cancel login" : "Annulla l'accesso", "Please authenticate using the selected factor." : "Autentica utilizzando il fattore selezionato.", "An error occured while verifying the token" : "Si è verificato un errore durante la verifica del token", "You are accessing the server from an untrusted domain." : "Stai accedendo al server da un dominio non attendibile.", diff --git a/core/l10n/pt_BR.js b/core/l10n/pt_BR.js index a0ed6ee2aba..e793cab7836 100644 --- a/core/l10n/pt_BR.js +++ b/core/l10n/pt_BR.js @@ -298,6 +298,7 @@ OC.L10N.register( "Thank you for your patience." : "Obrigado pela sua paciência.", "Two-step verification" : "Verificação em dois passos", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "Segurança reforçada foi ativada para sua conta. Por favor autenticar usando um segundo fator.", + "Cancel login" : "Cancelar o login", "Please authenticate using the selected factor." : "Por favor autenticar usando o fator selecionado.", "An error occured while verifying the token" : "Ocorreu um erro ao verificar o token", "You are accessing the server from an untrusted domain." : "Você está acessando o servidor a partir de um domínio não confiável.", diff --git a/core/l10n/pt_BR.json b/core/l10n/pt_BR.json index 8642980d766..c6a944e1775 100644 --- a/core/l10n/pt_BR.json +++ b/core/l10n/pt_BR.json @@ -296,6 +296,7 @@ "Thank you for your patience." : "Obrigado pela sua paciência.", "Two-step verification" : "Verificação em dois passos", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "Segurança reforçada foi ativada para sua conta. Por favor autenticar usando um segundo fator.", + "Cancel login" : "Cancelar o login", "Please authenticate using the selected factor." : "Por favor autenticar usando o fator selecionado.", "An error occured while verifying the token" : "Ocorreu um erro ao verificar o token", "You are accessing the server from an untrusted domain." : "Você está acessando o servidor a partir de um domínio não confiável.", diff --git a/core/l10n/ro.js b/core/l10n/ro.js index 7b4cf2c8957..e73ec5cf79f 100644 --- a/core/l10n/ro.js +++ b/core/l10n/ro.js @@ -8,23 +8,37 @@ OC.L10N.register( "Unknown filetype" : "Tip fișier necunoscut", "Invalid image" : "Imagine invalidă", "An error occurred. Please contact your admin." : "A apărut o eroare. Te rugăm să contactezi administratorul.", + "No temporary profile picture available, try again" : "Nu este disponibilă nicio imagine temporară a profilului, încearcă din nou", "%s password reset" : "%s resetare parola", "Couldn't send reset email. Please contact your administrator." : "Expedierea email-ului de resetare a eşuat. Vă rugăm să contactaţi administratorul dvs.", "Error loading tags" : "Eroare la încărcarea etichetelor", "Tag already exists" : "Eticheta deja există", + "Error deleting tag(s)" : "Eroare la ștergerea etichetei(-lor)", "Error tagging" : "Eroare la etichetare", "Error untagging" : "Eroare la scoaterea etichetei", + "Error favoriting" : "Eroare la adăugarea la favorite", + "Error unfavoriting" : "Eroare la scoaterea de la favorite", "Couldn't send mail to following users: %s " : "Nu s-a putut trimite mesajul către următorii utilizatori: %s", "Preparing update" : "Se pregătește actualizarea", + "[%d / %d]: %s" : "[%d / %d]: %s", "Repair warning: " : "Alerte reparare:", "Repair error: " : "Eroare de reparare:", + "[%d / %d]: Checking table %s" : "[%d / %d]: Se verifică tabela %s", "Turned on maintenance mode" : "Modul mentenanță a fost activat", "Turned off maintenance mode" : "Modul mentenanță a fost dezactivat", + "Maintenance mode is kept active" : "Modul de mentenanță este menținut activ", "Updating database schema" : "Se actualizează schema bazei de date", "Updated database" : "Bază de date actualizată", "Checking whether the database schema can be updated (this can take a long time depending on the database size)" : "Se verifică dacă schema bazei de date poate fi actualizată (această verificare poate lua mult timp în funcție de mărimea bazei de date)", + "Checking updates of apps" : "Se verifică actualizările aplicațiilor", "Updated \"%s\" to %s" : "\"%s\" a fost actualizat până la %s", + "Set log level to debug" : "Setează nivelul de logare la \"debug\"", + "Reset log level" : "Resetează nivelul de logare", + "Starting code integrity check" : "Începe verificarea integrității codului", + "Finished code integrity check" : "Verificarea integrității codului a fost finalizată", + "%s (3rdparty)" : "%s (terță parte)", "%s (incompatible)" : "%s (incompatibil)", + "Following apps have been disabled: %s" : "Următoarele aplicații au fost dezactivate: %s", "Already up to date" : "Deja actualizat", "Sunday" : "Duminică", "Monday" : "Luni", @@ -71,9 +85,11 @@ OC.L10N.register( "Oct." : "Oct.", "Nov." : "Noi.", "Dec." : "Dec.", + "<a href=\"{docUrl}\">There were problems with the code integrity check. More information…</a>" : "<a href=\"{docUrl}\">Au apărut probleme la verificarea integrității codului. Mai multe informații…</a>", "Settings" : "Setări", "Problem loading page, reloading in 5 seconds" : "A apărut o problemă la încărcarea paginii, se reîncearcă în 5 secunde", "Saving..." : "Se salvează...", + "Dismiss" : "Înlătură", "seconds ago" : "secunde în urmă", "I know what I'm doing" : "Eu știu ce fac", "Password can not be changed. Please contact your administrator." : "Parola nu poate fi modificată. Vă rugăm să contactați administratorul dvs.", @@ -135,24 +151,39 @@ OC.L10N.register( "access control" : "control acces", "Could not unshare" : "Nu s-a putut elimina partajarea", "Share details could not be loaded for this item." : "Nu s-au putut încărca detaliile de partajare pentru acest element.", + "An error occurred. Please try again" : "A apărut o eroare. Încearcă din nou", "Share" : "Partajează", "Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Partajează cu persoane din alte instanțe ownCloud folosind sintaxa nume_utilizator@exemplu.com/owncloud", "Share with users…" : "Partajează cu utilizatori...", + "Share with users or groups…" : "Partajează cu utilizatori sau grupuri...", + "Error removing share" : "Eroare la înlăturarea elementului partajat", "Warning" : "Atenție", + "Error while sending notification" : "Eroare la trimiterea notificării", + "Non-existing tag #{tag}" : "Etichetă inexistentă #{tag}", + "restricted" : "restricționat", "invisible" : "invizibil", "Delete" : "Șterge", "Rename" : "Redenumește", + "Collaborative tags" : "Etichete colaborative", "The object type is not specified." : "Tipul obiectului nu este specificat.", "Enter new" : "Introducere nou", "Add" : "Adaugă", "Edit tags" : "Editează etichete", + "No tags selected for deletion." : "Nu au fost selectate etichete pentru ștergere.", "unknown text" : "text necunoscut", + "Hello world!" : "Hello world!", "sunny" : "însorit", + "Hello {name}" : "Salut {name}", "new" : "nou", + "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "Actualizarea este în progres, părăsirea acestei pagini ar putea duce la întreruperea procesului în unele medii.", + "Updating to {version}" : "Actualizare la {version}", "An error occurred." : "A apărut o eroare.", "Please reload the page." : "Te rugăm să reîncarci pagina.", "The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud community</a>." : "Actualizarea a eșuat! Raportați problema către <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">comunitatea ownCloud</a>.", + "The update was successful. There were warnings." : "Actualizarea nu a avut loc cu succes. Au existat avertismente.", "The update was successful. Redirecting you to ownCloud now." : "Actualizare reușită. Ești redirecționat către ownCloud.", + "Searching other places" : "Se caută în alte locuri", + "No search results in other folders" : "Nu există rezultate ale căutării în alte directoare", "Personal" : "Personal", "Users" : "Utilizatori", "Apps" : "Aplicații", @@ -160,8 +191,12 @@ OC.L10N.register( "Help" : "Ajutor", "Access forbidden" : "Acces restricționat", "File not found" : "Fișierul nu a fost găsit", + "The specified document has not been found on the server." : "Documentul specificat nu a fost găsit pe server.", + "You can click here to return to %s." : "Poți da click aici pentru a te întoarce la %s.", "The share will expire on %s." : "Partajarea va expira în data de %s.", "Cheers!" : "Noroc!", + "Internal Server Error" : "Eroare internă a serverului", + "The server encountered an internal error and was unable to complete your request." : "Serverul a întâmpinat o eroare și nu îți poate îndeplini cererea.", "Technical details" : "Detalii tehnice", "Type: %s" : "Tip: %s", "Code: %s" : "Cod: %s", @@ -190,16 +225,35 @@ OC.L10N.register( "See the documentation" : "Vezi documentația", "Log out" : "Ieșire", "Search" : "Căutare", + "Please contact your administrator." : "Contactează-ți administratorul.", "An internal error occurred." : "A apărut o eroare internă.", + "Please try again or contact your administrator." : "Încearcă din nou sau contactează-ți administratorul.", "Username or email" : "Nume de utilizator sau adresă email", "Log in" : "Autentificare", + "Wrong password. Reset it?" : "Parolă greșită. O resetezi?", + "Wrong password." : "Parolă greșită.", + "Stay logged in" : "Rămâi autentificat", "Alternative Logins" : "Conectări alternative", "Use the following link to reset your password: {link}" : "Folosește următorul link pentru a reseta parola: {link}", "New password" : "Noua parolă", "New Password" : "Noua parolă", "Reset password" : "Resetează parola", "Thank you for your patience." : "Îți mulțumim pentru răbdare.", + "Two-step verification" : "Verificare în doi pași", + "Cancel login" : "Anulează autentificarea", + "Please authenticate using the selected factor." : "Autentifică-te folosind factorul ales.", + "An error occured while verifying the token" : "A apărut o eroare la verificarea jetonului", "You are accessing the server from an untrusted domain." : "Accesezi serverul dintr-un domeniu care nu a fost configurat ca fiind de încredere.", - "Start update" : "Începe actualizarea" + "Add \"%s\" as trusted domain" : "Adaugă \"%s\" ca domeniu de încredere", + "App update required" : "E necesară o actualizare a aplicației", + "%s will be updated to version %s" : "%s va fi actualizat la versiunea %s", + "These apps will be updated:" : "Aceste aplicații vor fi actualizate:", + "These incompatible apps will be disabled:" : "Aceste aplicații incompatibile vor fi dezactivate:", + "The theme %s has been disabled." : "Tema %s a fost dezactivată.", + "Start update" : "Începe actualizarea", + "Detailed logs" : "Loguri detaliate", + "Update needed" : "E necesară actualizarea", + "Please use the command line updater because you have a big instance." : "Folosește actualizarea din linia de comandă deoarece ai o instanță mare.", + "This %s instance is currently in maintenance mode, which may take a while." : "Instanța %s este acum în modul de mentenanță, ceea ce ar putea dura o vreme." }, "nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));"); diff --git a/core/l10n/ro.json b/core/l10n/ro.json index cea43ae3142..c154312dab6 100644 --- a/core/l10n/ro.json +++ b/core/l10n/ro.json @@ -6,23 +6,37 @@ "Unknown filetype" : "Tip fișier necunoscut", "Invalid image" : "Imagine invalidă", "An error occurred. Please contact your admin." : "A apărut o eroare. Te rugăm să contactezi administratorul.", + "No temporary profile picture available, try again" : "Nu este disponibilă nicio imagine temporară a profilului, încearcă din nou", "%s password reset" : "%s resetare parola", "Couldn't send reset email. Please contact your administrator." : "Expedierea email-ului de resetare a eşuat. Vă rugăm să contactaţi administratorul dvs.", "Error loading tags" : "Eroare la încărcarea etichetelor", "Tag already exists" : "Eticheta deja există", + "Error deleting tag(s)" : "Eroare la ștergerea etichetei(-lor)", "Error tagging" : "Eroare la etichetare", "Error untagging" : "Eroare la scoaterea etichetei", + "Error favoriting" : "Eroare la adăugarea la favorite", + "Error unfavoriting" : "Eroare la scoaterea de la favorite", "Couldn't send mail to following users: %s " : "Nu s-a putut trimite mesajul către următorii utilizatori: %s", "Preparing update" : "Se pregătește actualizarea", + "[%d / %d]: %s" : "[%d / %d]: %s", "Repair warning: " : "Alerte reparare:", "Repair error: " : "Eroare de reparare:", + "[%d / %d]: Checking table %s" : "[%d / %d]: Se verifică tabela %s", "Turned on maintenance mode" : "Modul mentenanță a fost activat", "Turned off maintenance mode" : "Modul mentenanță a fost dezactivat", + "Maintenance mode is kept active" : "Modul de mentenanță este menținut activ", "Updating database schema" : "Se actualizează schema bazei de date", "Updated database" : "Bază de date actualizată", "Checking whether the database schema can be updated (this can take a long time depending on the database size)" : "Se verifică dacă schema bazei de date poate fi actualizată (această verificare poate lua mult timp în funcție de mărimea bazei de date)", + "Checking updates of apps" : "Se verifică actualizările aplicațiilor", "Updated \"%s\" to %s" : "\"%s\" a fost actualizat până la %s", + "Set log level to debug" : "Setează nivelul de logare la \"debug\"", + "Reset log level" : "Resetează nivelul de logare", + "Starting code integrity check" : "Începe verificarea integrității codului", + "Finished code integrity check" : "Verificarea integrității codului a fost finalizată", + "%s (3rdparty)" : "%s (terță parte)", "%s (incompatible)" : "%s (incompatibil)", + "Following apps have been disabled: %s" : "Următoarele aplicații au fost dezactivate: %s", "Already up to date" : "Deja actualizat", "Sunday" : "Duminică", "Monday" : "Luni", @@ -69,9 +83,11 @@ "Oct." : "Oct.", "Nov." : "Noi.", "Dec." : "Dec.", + "<a href=\"{docUrl}\">There were problems with the code integrity check. More information…</a>" : "<a href=\"{docUrl}\">Au apărut probleme la verificarea integrității codului. Mai multe informații…</a>", "Settings" : "Setări", "Problem loading page, reloading in 5 seconds" : "A apărut o problemă la încărcarea paginii, se reîncearcă în 5 secunde", "Saving..." : "Se salvează...", + "Dismiss" : "Înlătură", "seconds ago" : "secunde în urmă", "I know what I'm doing" : "Eu știu ce fac", "Password can not be changed. Please contact your administrator." : "Parola nu poate fi modificată. Vă rugăm să contactați administratorul dvs.", @@ -133,24 +149,39 @@ "access control" : "control acces", "Could not unshare" : "Nu s-a putut elimina partajarea", "Share details could not be loaded for this item." : "Nu s-au putut încărca detaliile de partajare pentru acest element.", + "An error occurred. Please try again" : "A apărut o eroare. Încearcă din nou", "Share" : "Partajează", "Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Partajează cu persoane din alte instanțe ownCloud folosind sintaxa nume_utilizator@exemplu.com/owncloud", "Share with users…" : "Partajează cu utilizatori...", + "Share with users or groups…" : "Partajează cu utilizatori sau grupuri...", + "Error removing share" : "Eroare la înlăturarea elementului partajat", "Warning" : "Atenție", + "Error while sending notification" : "Eroare la trimiterea notificării", + "Non-existing tag #{tag}" : "Etichetă inexistentă #{tag}", + "restricted" : "restricționat", "invisible" : "invizibil", "Delete" : "Șterge", "Rename" : "Redenumește", + "Collaborative tags" : "Etichete colaborative", "The object type is not specified." : "Tipul obiectului nu este specificat.", "Enter new" : "Introducere nou", "Add" : "Adaugă", "Edit tags" : "Editează etichete", + "No tags selected for deletion." : "Nu au fost selectate etichete pentru ștergere.", "unknown text" : "text necunoscut", + "Hello world!" : "Hello world!", "sunny" : "însorit", + "Hello {name}" : "Salut {name}", "new" : "nou", + "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "Actualizarea este în progres, părăsirea acestei pagini ar putea duce la întreruperea procesului în unele medii.", + "Updating to {version}" : "Actualizare la {version}", "An error occurred." : "A apărut o eroare.", "Please reload the page." : "Te rugăm să reîncarci pagina.", "The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud community</a>." : "Actualizarea a eșuat! Raportați problema către <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">comunitatea ownCloud</a>.", + "The update was successful. There were warnings." : "Actualizarea nu a avut loc cu succes. Au existat avertismente.", "The update was successful. Redirecting you to ownCloud now." : "Actualizare reușită. Ești redirecționat către ownCloud.", + "Searching other places" : "Se caută în alte locuri", + "No search results in other folders" : "Nu există rezultate ale căutării în alte directoare", "Personal" : "Personal", "Users" : "Utilizatori", "Apps" : "Aplicații", @@ -158,8 +189,12 @@ "Help" : "Ajutor", "Access forbidden" : "Acces restricționat", "File not found" : "Fișierul nu a fost găsit", + "The specified document has not been found on the server." : "Documentul specificat nu a fost găsit pe server.", + "You can click here to return to %s." : "Poți da click aici pentru a te întoarce la %s.", "The share will expire on %s." : "Partajarea va expira în data de %s.", "Cheers!" : "Noroc!", + "Internal Server Error" : "Eroare internă a serverului", + "The server encountered an internal error and was unable to complete your request." : "Serverul a întâmpinat o eroare și nu îți poate îndeplini cererea.", "Technical details" : "Detalii tehnice", "Type: %s" : "Tip: %s", "Code: %s" : "Cod: %s", @@ -188,16 +223,35 @@ "See the documentation" : "Vezi documentația", "Log out" : "Ieșire", "Search" : "Căutare", + "Please contact your administrator." : "Contactează-ți administratorul.", "An internal error occurred." : "A apărut o eroare internă.", + "Please try again or contact your administrator." : "Încearcă din nou sau contactează-ți administratorul.", "Username or email" : "Nume de utilizator sau adresă email", "Log in" : "Autentificare", + "Wrong password. Reset it?" : "Parolă greșită. O resetezi?", + "Wrong password." : "Parolă greșită.", + "Stay logged in" : "Rămâi autentificat", "Alternative Logins" : "Conectări alternative", "Use the following link to reset your password: {link}" : "Folosește următorul link pentru a reseta parola: {link}", "New password" : "Noua parolă", "New Password" : "Noua parolă", "Reset password" : "Resetează parola", "Thank you for your patience." : "Îți mulțumim pentru răbdare.", + "Two-step verification" : "Verificare în doi pași", + "Cancel login" : "Anulează autentificarea", + "Please authenticate using the selected factor." : "Autentifică-te folosind factorul ales.", + "An error occured while verifying the token" : "A apărut o eroare la verificarea jetonului", "You are accessing the server from an untrusted domain." : "Accesezi serverul dintr-un domeniu care nu a fost configurat ca fiind de încredere.", - "Start update" : "Începe actualizarea" + "Add \"%s\" as trusted domain" : "Adaugă \"%s\" ca domeniu de încredere", + "App update required" : "E necesară o actualizare a aplicației", + "%s will be updated to version %s" : "%s va fi actualizat la versiunea %s", + "These apps will be updated:" : "Aceste aplicații vor fi actualizate:", + "These incompatible apps will be disabled:" : "Aceste aplicații incompatibile vor fi dezactivate:", + "The theme %s has been disabled." : "Tema %s a fost dezactivată.", + "Start update" : "Începe actualizarea", + "Detailed logs" : "Loguri detaliate", + "Update needed" : "E necesară actualizarea", + "Please use the command line updater because you have a big instance." : "Folosește actualizarea din linia de comandă deoarece ai o instanță mare.", + "This %s instance is currently in maintenance mode, which may take a while." : "Instanța %s este acum în modul de mentenanță, ceea ce ar putea dura o vreme." },"pluralForm" :"nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));" }
\ No newline at end of file diff --git a/core/l10n/ru.js b/core/l10n/ru.js index 398fe84160f..619d007c851 100644 --- a/core/l10n/ru.js +++ b/core/l10n/ru.js @@ -298,6 +298,7 @@ OC.L10N.register( "Thank you for your patience." : "Спасибо за терпение.", "Two-step verification" : "Двухшаговая проверка", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "Для вашей учётной записи включена повышенная безопасность. Пожалуйста, аутентифицируйтесь через второй фактор.", + "Cancel login" : "Отменить вход", "Please authenticate using the selected factor." : "Пожалуйста, аутентифицируйтесь выбранным фактором.", "An error occured while verifying the token" : "При проверке токена возникла ошибка.", "You are accessing the server from an untrusted domain." : "Вы пытаетесь получить доступ к серверу с недоверенного домена.", diff --git a/core/l10n/ru.json b/core/l10n/ru.json index fb4bf2a1052..9fe8d22feae 100644 --- a/core/l10n/ru.json +++ b/core/l10n/ru.json @@ -296,6 +296,7 @@ "Thank you for your patience." : "Спасибо за терпение.", "Two-step verification" : "Двухшаговая проверка", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "Для вашей учётной записи включена повышенная безопасность. Пожалуйста, аутентифицируйтесь через второй фактор.", + "Cancel login" : "Отменить вход", "Please authenticate using the selected factor." : "Пожалуйста, аутентифицируйтесь выбранным фактором.", "An error occured while verifying the token" : "При проверке токена возникла ошибка.", "You are accessing the server from an untrusted domain." : "Вы пытаетесь получить доступ к серверу с недоверенного домена.", diff --git a/core/l10n/sq.js b/core/l10n/sq.js index 06da819b5a0..049f33d3355 100644 --- a/core/l10n/sq.js +++ b/core/l10n/sq.js @@ -298,6 +298,7 @@ OC.L10N.register( "Thank you for your patience." : "Ju faleminderit për durimin.", "Two-step verification" : "Verifikim dyhapësh", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "Siguria e zgjeruar është aktivizuar për llogarinë tuaj. Ju lutemi, bëni mirëfilltësimin duke përdorur një faktor të dytë.", + "Cancel login" : "Anuloje hyrjen", "Please authenticate using the selected factor." : "Ju lutemi, bëni mirëfilltësimin duke përdorur faktorin e përzgjedhur.", "An error occured while verifying the token" : "Ndodhi një gabim gjatë verifikimit të token-it", "You are accessing the server from an untrusted domain." : "Po hyni në shërbyes nga një përkatësi jo e besuar.", diff --git a/core/l10n/sq.json b/core/l10n/sq.json index 91d40b34e97..d6dee599bf7 100644 --- a/core/l10n/sq.json +++ b/core/l10n/sq.json @@ -296,6 +296,7 @@ "Thank you for your patience." : "Ju faleminderit për durimin.", "Two-step verification" : "Verifikim dyhapësh", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "Siguria e zgjeruar është aktivizuar për llogarinë tuaj. Ju lutemi, bëni mirëfilltësimin duke përdorur një faktor të dytë.", + "Cancel login" : "Anuloje hyrjen", "Please authenticate using the selected factor." : "Ju lutemi, bëni mirëfilltësimin duke përdorur faktorin e përzgjedhur.", "An error occured while verifying the token" : "Ndodhi një gabim gjatë verifikimit të token-it", "You are accessing the server from an untrusted domain." : "Po hyni në shërbyes nga një përkatësi jo e besuar.", diff --git a/lib/l10n/pl.js b/lib/l10n/pl.js index 3124a335261..1a36e960779 100644 --- a/lib/l10n/pl.js +++ b/lib/l10n/pl.js @@ -30,6 +30,7 @@ OC.L10N.register( "_%n minute ago_::_%n minutes ago_" : ["%n minute temu","%n minut temu","%n minut temu"], "seconds ago" : "sekund temu", "Empty filename is not allowed" : "Pusta nazwa nie jest dozwolona.", + "4-byte characters are not supported in file names" : "Znaki 4-bajtowe są niedozwolone w nazwach plików", "File name contains at least one invalid character" : "Nazwa pliku zawiera co najmniej jeden nieprawidłowy znak", "File name is too long" : "Nazwa pliku zbyt długa", "App directory already exists" : "Katalog aplikacji już isnieje", diff --git a/lib/l10n/pl.json b/lib/l10n/pl.json index bbb4cc7b5ea..048530779e1 100644 --- a/lib/l10n/pl.json +++ b/lib/l10n/pl.json @@ -28,6 +28,7 @@ "_%n minute ago_::_%n minutes ago_" : ["%n minute temu","%n minut temu","%n minut temu"], "seconds ago" : "sekund temu", "Empty filename is not allowed" : "Pusta nazwa nie jest dozwolona.", + "4-byte characters are not supported in file names" : "Znaki 4-bajtowe są niedozwolone w nazwach plików", "File name contains at least one invalid character" : "Nazwa pliku zawiera co najmniej jeden nieprawidłowy znak", "File name is too long" : "Nazwa pliku zbyt długa", "App directory already exists" : "Katalog aplikacji już isnieje", diff --git a/lib/l10n/pt_PT.js b/lib/l10n/pt_PT.js index 196009d71d8..f069531895d 100644 --- a/lib/l10n/pt_PT.js +++ b/lib/l10n/pt_PT.js @@ -18,12 +18,12 @@ OC.L10N.register( "Following platforms are supported: %s" : "São suportadas as seguintes plataformas: %s", "ownCloud %s or higher is required." : "É necessário ownCloud %s ou superior.", "ownCloud %s or lower is required." : "É necessário ownCloud %s ou inferior.", - "Unknown filetype" : "Ficheiro desconhecido", + "Unknown filetype" : "Tipo de ficheiro desconhecido", "Invalid image" : "Imagem inválida", "today" : "hoje", "yesterday" : "ontem", "_%n day ago_::_%n days ago_" : ["%n dia atrás","%n dias atrás"], - "last month" : "ultímo mês", + "last month" : "ultimo mês", "last year" : "ano passado", "_%n year ago_::_%n years ago_" : ["%n ano atrás","%n anos atrás"], "seconds ago" : "Minutos atrás", diff --git a/lib/l10n/pt_PT.json b/lib/l10n/pt_PT.json index 5dc215f41b8..55492d0f14c 100644 --- a/lib/l10n/pt_PT.json +++ b/lib/l10n/pt_PT.json @@ -16,12 +16,12 @@ "Following platforms are supported: %s" : "São suportadas as seguintes plataformas: %s", "ownCloud %s or higher is required." : "É necessário ownCloud %s ou superior.", "ownCloud %s or lower is required." : "É necessário ownCloud %s ou inferior.", - "Unknown filetype" : "Ficheiro desconhecido", + "Unknown filetype" : "Tipo de ficheiro desconhecido", "Invalid image" : "Imagem inválida", "today" : "hoje", "yesterday" : "ontem", "_%n day ago_::_%n days ago_" : ["%n dia atrás","%n dias atrás"], - "last month" : "ultímo mês", + "last month" : "ultimo mês", "last year" : "ano passado", "_%n year ago_::_%n years ago_" : ["%n ano atrás","%n anos atrás"], "seconds ago" : "Minutos atrás", diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php index f84c8a41f17..4e48ee149ef 100644 --- a/lib/private/AppConfig.php +++ b/lib/private/AppConfig.php @@ -273,7 +273,9 @@ class AppConfig implements IAppConfig { ->from('appconfig'); $result = $sql->execute(); - while ($row = $result->fetch()) { + // we are going to store the result in memory anyway + $rows = $result->fetchAll(); + foreach ($rows as $row) { if (!isset($this->cache[$row['appid']])) { $this->cache[$row['appid']] = []; } diff --git a/lib/private/DB/ConnectionFactory.php b/lib/private/DB/ConnectionFactory.php index 0856d8d19c0..6a096e504d8 100644 --- a/lib/private/DB/ConnectionFactory.php +++ b/lib/private/DB/ConnectionFactory.php @@ -104,6 +104,10 @@ class ConnectionFactory { break; case 'oci': $eventManager->addEventSubscriber(new OracleSessionInit); + // the driverOptions are unused in dbal and need to be mapped to the parameters + if (isset($additionalConnectionParams['driverOptions'])) { + $additionalConnectionParams = array_merge($additionalConnectionParams, $additionalConnectionParams['driverOptions']); + } break; case 'sqlite3': $journalMode = $additionalConnectionParams['sqlite.journal_mode']; diff --git a/lib/private/Encryption/File.php b/lib/private/Encryption/File.php index 2bd9fb02100..3573ff19c6e 100644 --- a/lib/private/Encryption/File.php +++ b/lib/private/Encryption/File.php @@ -22,6 +22,8 @@ namespace OC\Encryption; +use OC\Cache\CappedMemoryCache; + class File implements \OCP\Encryption\IFile { /** @var Util */ @@ -36,6 +38,7 @@ class File implements \OCP\Encryption\IFile { public function __construct(Util $util) { $this->util = $util; + $this->cache = new CappedMemoryCache(); } diff --git a/lib/private/Files/Cache/Propagator.php b/lib/private/Files/Cache/Propagator.php index b998c6bcfae..52bb4dfdfca 100644 --- a/lib/private/Files/Cache/Propagator.php +++ b/lib/private/Files/Cache/Propagator.php @@ -29,6 +29,10 @@ use OCP\IDBConnection; * Propagate etags and mtimes within the storage */ class Propagator implements IPropagator { + private $inBatch = false; + + private $batch = []; + /** * @var \OC\Files\Storage\Storage */ @@ -59,6 +63,13 @@ class Propagator implements IPropagator { $parents = $this->getParents($internalPath); + if ($this->inBatch) { + foreach ($parents as $parent) { + $this->addToBatch($parent, $time, $sizeDifference); + } + return; + } + $parentHashes = array_map('md5', $parents); $etag = uniqid(); // since we give all folders the same etag we don't ask the storage for the etag @@ -68,7 +79,7 @@ class Propagator implements IPropagator { }, $parentHashes); $builder->update('filecache') - ->set('mtime', $builder->createFunction('GREATEST(`mtime`, ' . $builder->createNamedParameter($time) . ')')) + ->set('mtime', $builder->createFunction('GREATEST(`mtime`, ' . $builder->createNamedParameter($time, IQueryBuilder::PARAM_INT) . ')')) ->set('etag', $builder->createNamedParameter($etag, IQueryBuilder::PARAM_STR)) ->where($builder->expr()->eq('storage', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT))) ->andWhere($builder->expr()->in('path_hash', $hashParams)); @@ -98,4 +109,79 @@ class Propagator implements IPropagator { } return $parents; } + + /** + * Mark the beginning of a propagation batch + * + * Note that not all cache setups support propagation in which case this will be a noop + * + * Batching for cache setups that do support it has to be explicit since the cache state is not fully consistent + * before the batch is committed. + */ + public function beginBatch() { + $this->inBatch = true; + } + + private function addToBatch($internalPath, $time, $sizeDifference) { + if (!isset($this->batch[$internalPath])) { + $this->batch[$internalPath] = [ + 'hash' => md5($internalPath), + 'time' => $time, + 'size' => $sizeDifference + ]; + } else { + $this->batch[$internalPath]['size'] += $sizeDifference; + if ($time > $this->batch[$internalPath]['time']) { + $this->batch[$internalPath]['time'] = $time; + } + } + } + + /** + * Commit the active propagation batch + */ + public function commitBatch() { + if (!$this->inBatch) { + throw new \BadMethodCallException('Not in batch'); + } + $this->inBatch = false; + + $this->connection->beginTransaction(); + + $query = $this->connection->getQueryBuilder(); + $storageId = (int)$this->storage->getStorageCache()->getNumericId(); + + $query->update('filecache') + ->set('mtime', $query->createFunction('GREATEST(`mtime`, ' . $query->createParameter('time') . ')')) + ->set('etag', $query->expr()->literal(uniqid())) + ->where($query->expr()->eq('storage', $query->expr()->literal($storageId, IQueryBuilder::PARAM_INT))) + ->andWhere($query->expr()->eq('path_hash', $query->createParameter('hash'))); + + $sizeQuery = $this->connection->getQueryBuilder(); + $sizeQuery->update('filecache') + ->set('size', $sizeQuery->createFunction('`size` + ' . $sizeQuery->createParameter('size'))) + ->where($query->expr()->eq('storage', $query->expr()->literal($storageId, IQueryBuilder::PARAM_INT))) + ->andWhere($query->expr()->eq('path_hash', $query->createParameter('hash'))) + ->andWhere($sizeQuery->expr()->gt('size', $sizeQuery->expr()->literal(-1, IQueryBuilder::PARAM_INT))); + + foreach ($this->batch as $item) { + $query->setParameter('time', $item['time'], IQueryBuilder::PARAM_INT); + $query->setParameter('hash', $item['hash']); + + $query->execute(); + + if ($item['size']) { + $sizeQuery->setParameter('size', $item['size'], IQueryBuilder::PARAM_INT); + $sizeQuery->setParameter('hash', $item['hash']); + + $sizeQuery->execute(); + } + } + + $this->batch = []; + + $this->connection->commit(); + } + + } diff --git a/lib/private/Files/Utils/Scanner.php b/lib/private/Files/Utils/Scanner.php index 8beba116fe1..e4e5e353f9f 100644 --- a/lib/private/Files/Utils/Scanner.php +++ b/lib/private/Files/Utils/Scanner.php @@ -138,7 +138,9 @@ class Scanner extends PublicEmitter { $this->triggerPropagator($storage, $path); }); + $storage->getPropagator()->beginBatch(); $scanner->backgroundScan(); + $storage->getPropagator()->commitBatch(); } } @@ -187,12 +189,14 @@ class Scanner extends PublicEmitter { $this->db->beginTransaction(); } try { + $storage->getPropagator()->beginBatch(); $scanner->scan($relativePath, \OC\Files\Cache\Scanner::SCAN_RECURSIVE, \OC\Files\Cache\Scanner::REUSE_ETAG | \OC\Files\Cache\Scanner::REUSE_SIZE); $cache = $storage->getCache(); if ($cache instanceof Cache) { // only re-calculate for the root folder we scanned, anything below that is taken care of by the scanner $cache->correctFolderSize($relativePath); } + $storage->getPropagator()->commitBatch(); } catch (StorageNotAvailableException $e) { $this->logger->error('Storage ' . $storage->getId() . ' not available'); $this->logger->logException($e); diff --git a/lib/private/Repair.php b/lib/private/Repair.php index 4869db77497..bb2967d7e6e 100644 --- a/lib/private/Repair.php +++ b/lib/private/Repair.php @@ -29,11 +29,13 @@ namespace OC; use OC\Repair\AssetCache; +use OC\Repair\AvatarPermissions; use OC\Repair\CleanTags; use OC\Repair\Collation; use OC\Repair\DropOldJobs; use OC\Repair\OldGroupMembershipShares; use OC\Repair\RemoveGetETagEntries; +use OC\Repair\RemoveOldShares; use OC\Repair\SharePropagation; use OC\Repair\SqliteAutoincrement; use OC\Repair\DropOldTables; @@ -132,6 +134,8 @@ class Repair implements IOutput{ new UpdateOutdatedOcsIds(\OC::$server->getConfig()), new RepairInvalidShares(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection()), new SharePropagation(\OC::$server->getConfig()), + new RemoveOldShares(\OC::$server->getDatabaseConnection()), + new AvatarPermissions(\OC::$server->getDatabaseConnection()), ]; } diff --git a/lib/private/Repair/AvatarPermissions.php b/lib/private/Repair/AvatarPermissions.php new file mode 100644 index 00000000000..d23479f5ba8 --- /dev/null +++ b/lib/private/Repair/AvatarPermissions.php @@ -0,0 +1,115 @@ +<?php +/** + * @author Roeland Jago Douma <rullzer@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +namespace OC\Repair; + +use Doctrine\DBAL\Platforms\OraclePlatform; +use OCP\IDBConnection; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +/** + * Class AvatarPermissions + * + * @package OC\Repair + */ +class AvatarPermissions implements IRepairStep { + /** @var IDBConnection */ + private $connection; + + /** + * AvatarPermissions constructor. + * + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->connection = $connection; + } + + /** + * @return string + */ + public function getName() { + return 'Fix permissions so avatars can be stored again'; + } + + /** + * @param IOutput $output + */ + public function run(IOutput $output) { + $output->startProgress(2); + $this->fixUserRootPermissions(); + $output->advance(); + $this->fixAvatarPermissions(); + $output->finishProgress(); + } + + /** + * Make sure all user roots have permissions 23 (all but share) + */ + protected function fixUserRootPermissions() { + $qb = $this->connection->getQueryBuilder(); + $qb2 = $this->connection->getQueryBuilder(); + + $qb->select('numeric_id') + ->from('storages') + ->where($qb->expr()->like('id', $qb2->createParameter('like'))); + + if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) { + // '' is null on oracle + $path = $qb2->expr()->isNull('path'); + } else { + $path = $qb2->expr()->eq('path', $qb2->createNamedParameter('')); + } + + $qb2->update('filecache') + ->set('permissions', $qb2->createNamedParameter(23)) + ->where($path) + ->andWhere($qb2->expr()->in('storage', $qb2->createFunction($qb->getSQL()))) + ->andWhere($qb2->expr()->neq('permissions', $qb2->createNamedParameter(23))) + ->setParameter('like', 'home::%'); + + + $qb2->execute(); + } + + /** + * Make sure all avatar files in the user roots have permission 27 + */ + protected function fixAvatarPermissions() { + $qb = $this->connection->getQueryBuilder(); + $qb2 = $this->connection->getQueryBuilder(); + + $qb->select('numeric_id') + ->from('storages') + ->where($qb->expr()->like('id', $qb2->createParameter('like'))); + + $qb2->update('filecache') + ->set('permissions', $qb2->createNamedParameter(27)) + ->where($qb2->expr()->like('path', $qb2->createNamedParameter('avatar.%'))) + ->andWhere($qb2->expr()->in('storage', $qb2->createFunction($qb->getSQL()))) + ->andWhere($qb2->expr()->neq('permissions', $qb2->createNamedParameter(27))) + ->setParameter('like', 'home::%'); + + $qb2->execute(); + } + +} + diff --git a/lib/private/Repair/RemoveOldShares.php b/lib/private/Repair/RemoveOldShares.php new file mode 100644 index 00000000000..2c05d97b15c --- /dev/null +++ b/lib/private/Repair/RemoveOldShares.php @@ -0,0 +1,103 @@ +<?php +/** + * @author Roeland Jago Douma <rullzer@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +namespace OC\Repair; + +use OCP\IDBConnection; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +/** + * Class RemoveOldShares + * + * @package OC\Repair + */ +class RemoveOldShares implements IRepairStep { + + /** @var IDBConnection */ + protected $connection; + + /** + * RemoveOldCalendarShares constructor. + * + * @param IDBConnection $db + */ + public function __construct(IDBConnection $connection) { + $this->connection = $connection; + } + + /** + * @return string + */ + public function getName() { + return 'Remove old (< 9.0) calendar/contact shares'; + } + + /** + * @param IOutput $output + */ + public function run(IOutput $output) { + $output->startProgress(4); + + $this->removeCalendarShares($output); + $this->removeContactShares($output); + + $output->finishProgress(); + } + + /** + * @param IOutput $output + */ + private function removeCalendarShares(IOutput $output) { + $qb = $this->connection->getQueryBuilder(); + $qb->delete('share') + ->where($qb->expr()->eq('item_type', $qb->createNamedParameter('calendar'))); + $qb->execute(); + + $output->advance(); + + $qb = $this->connection->getQueryBuilder(); + $qb->delete('share') + ->where($qb->expr()->eq('item_type', $qb->createNamedParameter('event'))); + $qb->execute(); + + $output->advance(); + } + + /** + * @param IOutput $output + */ + private function removeContactShares(IOutput $output) { + $qb = $this->connection->getQueryBuilder(); + $qb->delete('share') + ->where($qb->expr()->eq('item_type', $qb->createNamedParameter('contact'))); + $qb->execute(); + + $output->advance(); + + $qb = $this->connection->getQueryBuilder(); + $qb->delete('share') + ->where($qb->expr()->eq('item_type', $qb->createNamedParameter('addressbook'))); + $qb->execute(); + + $output->advance(); + } +} + diff --git a/lib/private/legacy/json.php b/lib/private/legacy/json.php index d201d69723e..1dde63602b1 100644 --- a/lib/private/legacy/json.php +++ b/lib/private/legacy/json.php @@ -64,7 +64,9 @@ class OC_JSON{ * @deprecated Use annotation based ACLs from the AppFramework instead */ public static function checkLoggedIn() { - if( !OC_User::isLoggedIn()) { + $twoFactorAuthManger = \OC::$server->getTwoFactorAuthManager(); + if( !OC_User::isLoggedIn() + || $twoFactorAuthManger->needsSecondFactor()) { $l = \OC::$server->getL10N('lib'); http_response_code(\OCP\AppFramework\Http::STATUS_UNAUTHORIZED); self::error(array( 'data' => array( 'message' => $l->t('Authentication error'), 'error' => 'authentication_error' ))); diff --git a/lib/private/legacy/util.php b/lib/private/legacy/util.php index a863348566e..65d00c16388 100644 --- a/lib/private/legacy/util.php +++ b/lib/private/legacy/util.php @@ -970,6 +970,11 @@ class OC_Util { ); exit(); } + // Redirect to index page if 2FA challenge was not solved yet + if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor()) { + header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php')); + exit(); + } } /** diff --git a/lib/public/Files/Cache/IPropagator.php b/lib/public/Files/Cache/IPropagator.php index 5494ec9a54e..541135b9e60 100644 --- a/lib/public/Files/Cache/IPropagator.php +++ b/lib/public/Files/Cache/IPropagator.php @@ -28,6 +28,25 @@ namespace OCP\Files\Cache; */ interface IPropagator { /** + * Mark the beginning of a propagation batch + * + * Note that not all cache setups support propagation in which case this will be a noop + * + * Batching for cache setups that do support it has to be explicit since the cache state is not fully consistent + * before the batch is committed. + * + * @since 9.1.0 + */ + public function beginBatch(); + + /** + * Commit the active propagation batch + * + * @since 9.1.0 + */ + public function commitBatch(); + + /** * @param string $internalPath * @param int $time * @since 9.0.0 diff --git a/lib/public/Migration/IRepairStep.php b/lib/public/Migration/IRepairStep.php index 3394b70a6ff..84619128b4e 100644 --- a/lib/public/Migration/IRepairStep.php +++ b/lib/public/Migration/IRepairStep.php @@ -39,8 +39,9 @@ interface IRepairStep { * Run repair step. * Must throw exception on error. * - * @since 9.1.0 + * @param IOutput $output * @throws \Exception in case of failure + * @since 9.1.0 */ public function run(IOutput $output); diff --git a/settings/l10n/en_GB.js b/settings/l10n/en_GB.js index 443ca4adc38..bf3bf387fa6 100644 --- a/settings/l10n/en_GB.js +++ b/settings/l10n/en_GB.js @@ -137,6 +137,7 @@ OC.L10N.register( "The Read-Only config has been enabled. This prevents setting some configurations via the web-interface. Furthermore, the file needs to be made writable manually for every update." : "The Read-Only config has been enabled. This prevents setting some configurations via the web-interface. Furthermore, the file needs to be made writable manually for every update.", "PHP is apparently setup to strip inline doc blocks. This will make several core apps inaccessible." : "PHP is apparently setup to strip inline doc blocks. This will make several core apps inaccessible.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.", + "Your database does not run with \"READ COMMITED\" transaction isolation level. This can cause problems when multiple actions are executed in parallel." : "Your database does not run with \"READ COMMITED\" transaction isolation level. This can cause problems when multiple actions are executed in parallel.", "Your server is running on Microsoft Windows. We highly recommend Linux for optimal user experience." : "Your server is running on Microsoft Windows. We highly recommend Linux for optimal user experience.", "%1$s below version %2$s is installed, for stability and performance reasons we recommend updating to a newer %1$s version." : "%1$s below version %2$s is installed, for stability and performance reasons we recommend updating to a newer %1$s version.", "The PHP module 'fileinfo' is missing. We strongly recommend to enable this module to get best results with mime-type detection." : "The PHP module 'fileinfo' is missing. We strongly recommend enabling this module to get best results with mime-type detection.", @@ -266,6 +267,7 @@ OC.L10N.register( "Current password" : "Current password", "New password" : "New password", "Change password" : "Change password", + "These are the web, desktop and mobile clients currently logged in to your ownCloud." : "These are the web, desktop and mobile clients currently logged in to your ownCloud.", "Browser" : "Browser", "Most recent activity" : "Most recent activity", "You've linked these devices." : "You've linked these devices.", diff --git a/settings/l10n/en_GB.json b/settings/l10n/en_GB.json index 838710e4073..9ab2f0e5a96 100644 --- a/settings/l10n/en_GB.json +++ b/settings/l10n/en_GB.json @@ -135,6 +135,7 @@ "The Read-Only config has been enabled. This prevents setting some configurations via the web-interface. Furthermore, the file needs to be made writable manually for every update." : "The Read-Only config has been enabled. This prevents setting some configurations via the web-interface. Furthermore, the file needs to be made writable manually for every update.", "PHP is apparently setup to strip inline doc blocks. This will make several core apps inaccessible." : "PHP is apparently setup to strip inline doc blocks. This will make several core apps inaccessible.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.", + "Your database does not run with \"READ COMMITED\" transaction isolation level. This can cause problems when multiple actions are executed in parallel." : "Your database does not run with \"READ COMMITED\" transaction isolation level. This can cause problems when multiple actions are executed in parallel.", "Your server is running on Microsoft Windows. We highly recommend Linux for optimal user experience." : "Your server is running on Microsoft Windows. We highly recommend Linux for optimal user experience.", "%1$s below version %2$s is installed, for stability and performance reasons we recommend updating to a newer %1$s version." : "%1$s below version %2$s is installed, for stability and performance reasons we recommend updating to a newer %1$s version.", "The PHP module 'fileinfo' is missing. We strongly recommend to enable this module to get best results with mime-type detection." : "The PHP module 'fileinfo' is missing. We strongly recommend enabling this module to get best results with mime-type detection.", @@ -264,6 +265,7 @@ "Current password" : "Current password", "New password" : "New password", "Change password" : "Change password", + "These are the web, desktop and mobile clients currently logged in to your ownCloud." : "These are the web, desktop and mobile clients currently logged in to your ownCloud.", "Browser" : "Browser", "Most recent activity" : "Most recent activity", "You've linked these devices." : "You've linked these devices.", diff --git a/settings/templates/apps.php b/settings/templates/apps.php index ecb00fb27c3..d3c14332693 100644 --- a/settings/templates/apps.php +++ b/settings/templates/apps.php @@ -91,8 +91,22 @@ script( <a id="adminDocumentation" class="appslink" href="{{documentation.admin}}" target="_blank" rel="noreferrer"><?php p($l->t('Admin documentation'));?> ↗</a> </span> {{/if}} + + {{#if documentation.developer}} + <span class="developerDocumentation"> + <a id="developerDocumentation" class="appslink" href="{{documentation.developer}}" target="_blank" rel="noreferrer"><?php p($l->t('Developer documentation'));?> ↗</a> + </span> + {{/if}} </p> {{/if}} + + {{#if website}} + <a id="userDocumentation" class="appslink" href="{{website}}" target="_blank" rel="noreferrer"><?php p($l->t('Visit website'));?> ↗</a> + {{/if}} + + {{#if bugs}} + <a id="adminDocumentation" class="appslink" href="{{bugs}}" target="_blank" rel="noreferrer"><?php p($l->t('Report a bug'));?> ↗</a> + {{/if}} </div><!-- end app-description-container --> <div class="app-description-toggle-show" role="link"><?php p($l->t("Show description …"));?></div> <div class="app-description-toggle-hide hidden" role="link"><?php p($l->t("Hide description …"));?></div> diff --git a/tests/Core/Controller/LoginControllerTest.php b/tests/Core/Controller/LoginControllerTest.php index ea9d6a44148..d6fa772d38b 100644 --- a/tests/Core/Controller/LoginControllerTest.php +++ b/tests/Core/Controller/LoginControllerTest.php @@ -29,6 +29,7 @@ use OCP\IConfig; use OCP\IRequest; use OCP\ISession; use OCP\IURLGenerator; +use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; use Test\TestCase; @@ -36,19 +37,19 @@ use Test\TestCase; class LoginControllerTest extends TestCase { /** @var LoginController */ private $loginController; - /** @var IRequest */ + /** @var IRequest | \PHPUnit_Framework_MockObject_MockObject */ private $request; - /** @var IUserManager */ + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject */ private $userManager; - /** @var IConfig */ + /** @var IConfig | \PHPUnit_Framework_MockObject_MockObject */ private $config; - /** @var ISession */ + /** @var ISession | \PHPUnit_Framework_MockObject_MockObject */ private $session; - /** @var IUserSession */ + /** @var IUserSession | \PHPUnit_Framework_MockObject_MockObject */ private $userSession; - /** @var IURLGenerator */ + /** @var IURLGenerator | \PHPUnit_Framework_MockObject_MockObject */ private $urlGenerator; - /** @var Manager */ + /** @var Manager | \PHPUnit_Framework_MockObject_MockObject */ private $twoFactorManager; public function setUp() { @@ -296,6 +297,7 @@ class LoginControllerTest extends TestCase { } public function testLoginWithValidCredentials() { + /** @var IUser | \PHPUnit_Framework_MockObject_MockObject $user */ $user = $this->getMock('\OCP\IUser'); $password = 'secret'; $indexPageUrl = 'some url'; @@ -323,6 +325,7 @@ class LoginControllerTest extends TestCase { } public function testLoginWithValidCredentialsAndRedirectUrl() { + /** @var IUser | \PHPUnit_Framework_MockObject_MockObject $user */ $user = $this->getMock('\OCP\IUser'); $user->expects($this->any()) ->method('getUID') @@ -352,6 +355,7 @@ class LoginControllerTest extends TestCase { } public function testLoginWithTwoFactorEnforced() { + /** @var IUser | \PHPUnit_Framework_MockObject_MockObject $user */ $user = $this->getMock('\OCP\IUser'); $user->expects($this->any()) ->method('getUID') @@ -380,8 +384,36 @@ class LoginControllerTest extends TestCase { ->with('core.TwoFactorChallenge.selectChallenge') ->will($this->returnValue($challengeUrl)); - $expected = new \OCP\AppFramework\Http\RedirectResponse($challengeUrl); + $expected = new RedirectResponse($challengeUrl); $this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', $password, null)); } + public function testToNotLeakLoginName() { + /** @var IUser | \PHPUnit_Framework_MockObject_MockObject $user */ + $user = $this->getMock('\OCP\IUser'); + $user->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('john')); + + $this->userManager->expects($this->exactly(2)) + ->method('checkPassword') + ->withConsecutive( + ['john@doe.com', 'just wrong'], + ['john', 'just wrong'] + ) + ->willReturn(false); + + $this->userManager->expects($this->once()) + ->method('getByEmail') + ->with('john@doe.com') + ->willReturn([$user]); + + $this->urlGenerator->expects($this->once()) + ->method('linkToRoute') + ->with('core.login.showLoginForm', ['user' => 'john@doe.com']) + ->will($this->returnValue('')); + + $expected = new RedirectResponse(''); + $this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', 'just wrong', null)); + } } diff --git a/tests/lib/Files/Cache/PropagatorTest.php b/tests/lib/Files/Cache/PropagatorTest.php new file mode 100644 index 00000000000..402b29c8c3e --- /dev/null +++ b/tests/lib/Files/Cache/PropagatorTest.php @@ -0,0 +1,125 @@ +<?php +/** + * Copyright (c) 2012 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\Files\Cache; + +use OC\Files\Storage\Temporary; +use OCP\Files\Cache\ICacheEntry; +use OCP\Files\Storage\IStorage; +use Test\TestCase; + +/** + * @group DB + */ +class PropagatorTest extends TestCase { + /** @var IStorage */ + private $storage; + + public function setUp() { + parent::setUp(); + $this->storage = new Temporary(); + $this->storage->mkdir('foo/bar'); + $this->storage->file_put_contents('foo/bar/file.txt', 'bar'); + $this->storage->getScanner()->scan(''); + } + + /** + * @param $paths + * @return ICacheEntry[] + */ + private function getFileInfos($paths) { + $values = array_map(function ($path) { + return $this->storage->getCache()->get($path); + }, $paths); + return array_combine($paths, $values); + } + + public function testEtagPropagation() { + $paths = ['', 'foo', 'foo/bar']; + $oldInfos = $this->getFileInfos($paths); + $this->storage->getPropagator()->propagateChange('foo/bar/file.txt', time()); + $newInfos = $this->getFileInfos($paths); + + foreach ($oldInfos as $i => $oldInfo) { + $this->assertNotEquals($oldInfo->getEtag(), $newInfos[$i]->getEtag()); + } + } + + public function testTimePropagation() { + $paths = ['', 'foo', 'foo/bar']; + $oldTime = time() - 200; + $targetTime = time() - 100; + $now = time(); + $cache = $this->storage->getCache(); + $cache->put('', ['mtime' => $now]); + $cache->put('foo', ['mtime' => $now]); + $cache->put('foo/bar', ['mtime' => $oldTime]); + $cache->put('foo/bar/file.txt', ['mtime' => $oldTime]); + $this->storage->getPropagator()->propagateChange('foo/bar/file.txt', $targetTime); + $newInfos = $this->getFileInfos($paths); + + $this->assertEquals($targetTime, $newInfos['foo/bar']->getMTime()); + + // dont lower mtimes + $this->assertEquals($now, $newInfos['foo']->getMTime()); + $this->assertEquals($now, $newInfos['']->getMTime()); + } + + public function testSizePropagation() { + $paths = ['', 'foo', 'foo/bar']; + $oldInfos = $this->getFileInfos($paths); + $this->storage->getPropagator()->propagateChange('foo/bar/file.txt', time(), 10); + $newInfos = $this->getFileInfos($paths); + + foreach ($oldInfos as $i => $oldInfo) { + $this->assertEquals($oldInfo->getSize() + 10, $newInfos[$i]->getSize()); + } + } + + public function testBatchedPropagation() { + $this->storage->mkdir('foo/baz'); + $this->storage->mkdir('asd'); + $this->storage->file_put_contents('asd/file.txt', 'bar'); + $this->storage->file_put_contents('foo/baz/file.txt', 'bar'); + $this->storage->getScanner()->scan(''); + + $paths = ['', 'foo', 'foo/bar', 'asd', 'foo/baz']; + + $oldInfos = $this->getFileInfos($paths); + $propagator = $this->storage->getPropagator(); + + $propagator->beginBatch(); + $propagator->propagateChange('asd/file.txt', time(), 10); + $propagator->propagateChange('foo/bar/file.txt', time(), 2); + + $newInfos = $this->getFileInfos($paths); + + // no changes until we finish the batch + foreach ($oldInfos as $i => $oldInfo) { + $this->assertEquals($oldInfo->getSize(), $newInfos[$i]->getSize()); + $this->assertEquals($oldInfo->getEtag(), $newInfos[$i]->getEtag()); + $this->assertEquals($oldInfo->getMTime(), $newInfos[$i]->getMTime()); + } + + $propagator->commitBatch(); + + $newInfos = $this->getFileInfos($paths); + + foreach ($oldInfos as $i => $oldInfo) { + if ($oldInfo->getPath() !== 'foo/baz') { + $this->assertNotEquals($oldInfo->getEtag(), $newInfos[$i]->getEtag()); + } + } + + $this->assertEquals($oldInfos['']->getSize() + 12, $newInfos['']->getSize()); + $this->assertEquals($oldInfos['asd']->getSize() + 10, $newInfos['asd']->getSize()); + $this->assertEquals($oldInfos['foo']->getSize() + 2, $newInfos['foo']->getSize()); + $this->assertEquals($oldInfos['foo/bar']->getSize() + 2, $newInfos['foo/bar']->getSize()); + $this->assertEquals($oldInfos['foo/baz']->getSize(), $newInfos['foo/baz']->getSize()); + } +} diff --git a/tests/lib/Repair/AvatarPermissionsTest.php b/tests/lib/Repair/AvatarPermissionsTest.php new file mode 100644 index 00000000000..e3f582dc512 --- /dev/null +++ b/tests/lib/Repair/AvatarPermissionsTest.php @@ -0,0 +1,189 @@ +<?php +/** + * @author Roeland Jago Douma <rullzer@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +namespace Test\Repair; + +/** + * Test for fixing the userRoot and avatar permissions + * + * @group DB + * + * @see \OC\Repair\AvatarPermissionsTest + */ +class AvatarPermissionsTest extends \Test\TestCase { + + /** @var \OC\Repair\AvatarPermissions */ + protected $repair; + + /** @var \OCP\IDBConnection */ + protected $connection; + + protected function setUp() { + parent::setUp(); + + $this->connection = \OC::$server->getDatabaseConnection(); + $this->repair = new \OC\Repair\AvatarPermissions($this->connection); + $this->cleanUpTables(); + } + + protected function tearDown() { + $this->cleanUpTables(); + + parent::tearDown(); + } + + protected function cleanUpTables() { + $qb = $this->connection->getQueryBuilder(); + $qb->delete('filecache')->execute(); + $qb->delete('storages')->execute(); + } + + public function dataFixUserRootPermissions() { + return [ + ['home::user', '', 0, 23], + ['home::user', 'foo', 0, 0], + ['home::user', 'avatar.jpg', 0, 0], + ['ABC::user', '', 0, 0], + ['ABC::user', 'foo', 0, 0], + ]; + } + + /** + * @dataProvider dataFixUserRootPermissions + * + * @param string $storageId + * @param string $path + * @param int $permissionsBefore + * @param int $permissionsAfter + */ + public function testFixUserRootPermissions($storageId, $path, $permissionsBefore, $permissionsAfter) { + $userStorage = $this->addStorage($storageId); + $userHome = $this->addFileCacheEntry($userStorage, $path, $permissionsBefore); + + $this->invokePrivate($this->repair, 'fixUserRootPermissions', []); + + $this->verifyPermissions($userHome, $permissionsAfter); + } + + public function dataFixAvatarPermissions() { + return [ + ['home::user', '', 0, 0], + ['home::user', 'avatar.jpg', 0, 27], + ['home::user', 'avatar.png', 0, 27], + ['home::user', 'avatar.32.png', 0, 27], + ['home::user', 'mine.txt', 0, 0], + ['ABC::user', '', 0, 0], + ['ABC::user', 'avatar.jpg', 0, 0], + ['ABC::user', 'avatar.png', 0, 0], + ['ABC::user', 'avatar.32.png', 0, 0], + ['ABC::user', 'mine.txt', 0, 0], + ]; + } + + /** + * @dataProvider dataFixAvatarPermissions + * + * @param string $storageId + * @param string $path + * @param int $permissionsBefore + * @param int $permissionsAfter + */ + public function testFixAvatarPermissions($storageId, $path, $permissionsBefore, $permissionsAfter) { + $userStorage = $this->addStorage($storageId); + $userHome = $this->addFileCacheEntry($userStorage, $path, $permissionsBefore); + + $this->invokePrivate($this->repair, 'fixAvatarPermissions', []); + + $this->verifyPermissions($userHome, $permissionsAfter); + } + + /** + * Add a new storage + * + * @param string $id + * @return int The numeric id + */ + protected function addStorage($id) { + $qb = $this->connection->getQueryBuilder(); + + $qb->insert('storages') + ->values([ + 'id' => $qb->createNamedParameter($id) + ]); + + $qb->execute(); + + return $qb->getLastInsertId(); + } + + /** + * Add a filecache entry + * + * @param int $storage + * @param string $path + * @param int $permissions + * + * @return int The fileid + */ + protected function addFileCacheEntry($storage, $path, $permissions) { + $qb = $this->connection->getQueryBuilder(); + + $qb->insert('filecache') + ->values([ + 'path' => $qb->createNamedParameter($path), + 'path_hash' => $qb->createNamedParameter(md5($path)), + 'parent' => $qb->createNamedParameter(42), + 'mimetype' => $qb->createNamedParameter(23), + 'mimepart' => $qb->createNamedParameter(32), + 'size' => $qb->createNamedParameter(16), + 'mtime' => $qb->createNamedParameter(1), + 'storage_mtime' => $qb->createNamedParameter(2), + 'encrypted' => $qb->createNamedParameter(0), + 'unencrypted_size' => $qb->createNamedParameter(0), + 'storage' => $qb->createNamedParameter($storage), + 'permissions' => $qb->createNamedParameter($permissions), + ]); + + $qb->execute(); + + return $qb->getLastInsertId(); + } + + /** + * @param int $fileId + * @param int $permissions + */ + protected function verifyPermissions($fileId, $permissions) { + $qb = $this->connection->getQueryBuilder(); + + $qb->select('permissions') + ->from('filecache') + ->where($qb->expr()->eq('fileid', $qb->createNamedParameter($fileId))); + + $cursor = $qb->execute(); + + $data = $cursor->fetch(); + $cursor->closeCursor(); + + $this->assertSame($permissions, (int)$data['permissions']); + } + + +} diff --git a/tests/lib/Repair/RemoveOldSharesTest.php b/tests/lib/Repair/RemoveOldSharesTest.php new file mode 100644 index 00000000000..ac30585bdc5 --- /dev/null +++ b/tests/lib/Repair/RemoveOldSharesTest.php @@ -0,0 +1,160 @@ +<?php +/** + * @author Roeland Jago Douma <rullzer@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +namespace Test\Repair; + +use OC\Repair\RemoveOldShares; +use OCP\IDBConnection; +use OCP\Migration\IOutput; + +/** + * Class RemoveOldSharesTest + * + * @package Test\Repair + * @group DB + */ +class RemoveOldSharesTest extends \Test\TestCase { + + /** @var RemoveOldShares */ + protected $repair; + + /** @var IDBConnection */ + protected $connection; + + /** @var IOutput */ + private $outputMock; + + protected function setUp() { + parent::setUp(); + + $this->outputMock = $this->getMockBuilder('\OCP\Migration\IOutput') + ->disableOriginalConstructor() + ->getMock(); + + $this->connection = \OC::$server->getDatabaseConnection(); + $this->repair = new RemoveOldShares($this->connection); + } + + protected function tearDown() { + $qb = $this->connection->getQueryBuilder(); + $qb->delete('share'); + $qb->execute(); + + return parent::tearDown(); + } + + public function testRun() { + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->values([ + 'share_type' => $qb->createNamedParameter(0), + 'share_with' => $qb->createNamedParameter('foo'), + 'uid_owner' => $qb->createNamedParameter('owner'), + 'item_type' => $qb->createNamedParameter('file'), + 'item_source' => $qb->createNamedParameter(42), + 'item_target' => $qb->createNamedParameter('/target'), + 'file_source' => $qb->createNamedParameter(42), + 'file_target' => $qb->createNamedParameter('/target'), + 'permissions' => $qb->createNamedParameter(1), + ]); + $qb->execute(); + + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->values([ + 'share_type' => $qb->createNamedParameter(0), + 'share_with' => $qb->createNamedParameter('foo'), + 'uid_owner' => $qb->createNamedParameter('owner'), + 'item_type' => $qb->createNamedParameter('calendar'), + 'item_source' => $qb->createNamedParameter(42), + 'item_target' => $qb->createNamedParameter('/target'), + 'file_source' => $qb->createNamedParameter(42), + 'file_target' => $qb->createNamedParameter('/target'), + 'permissions' => $qb->createNamedParameter(1), + ]); + $qb->execute(); + + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->values([ + 'share_type' => $qb->createNamedParameter(0), + 'share_with' => $qb->createNamedParameter('foo'), + 'uid_owner' => $qb->createNamedParameter('owner'), + 'item_type' => $qb->createNamedParameter('event'), + 'item_source' => $qb->createNamedParameter(42), + 'item_target' => $qb->createNamedParameter('/target'), + 'file_source' => $qb->createNamedParameter(42), + 'file_target' => $qb->createNamedParameter('/target'), + 'permissions' => $qb->createNamedParameter(1), + ]); + $qb->execute(); + + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->values([ + 'share_type' => $qb->createNamedParameter(0), + 'share_with' => $qb->createNamedParameter('foo'), + 'uid_owner' => $qb->createNamedParameter('owner'), + 'item_type' => $qb->createNamedParameter('contact'), + 'item_source' => $qb->createNamedParameter(42), + 'item_target' => $qb->createNamedParameter('/target'), + 'file_source' => $qb->createNamedParameter(42), + 'file_target' => $qb->createNamedParameter('/target'), + 'permissions' => $qb->createNamedParameter(1), + ]); + $qb->execute(); + + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->values([ + 'share_type' => $qb->createNamedParameter(0), + 'share_with' => $qb->createNamedParameter('foo'), + 'uid_owner' => $qb->createNamedParameter('owner'), + 'item_type' => $qb->createNamedParameter('addressbook'), + 'item_source' => $qb->createNamedParameter(42), + 'item_target' => $qb->createNamedParameter('/target'), + 'file_source' => $qb->createNamedParameter(42), + 'file_target' => $qb->createNamedParameter('/target'), + 'permissions' => $qb->createNamedParameter(1), + ]); + $qb->execute(); + + $qb = $this->connection->getQueryBuilder(); + $qb->selectAlias($qb->createFunction('COUNT(*)'), 'count') + ->from('share'); + + $cursor = $qb->execute(); + $data = $cursor->fetchAll(); + $cursor->closeCursor(); + $this->assertEquals(5, $data[0]['count']); + + $this->repair->run($this->outputMock); + + $qb = $this->connection->getQueryBuilder(); + $qb->select('*') + ->from('share'); + + $cursor = $qb->execute(); + $data = $cursor->fetchAll(); + $cursor->closeCursor(); + $this->assertCount(1, $data); + $this->assertEquals('file', $data[0]['item_type']); + } +} |