diff options
77 files changed, 914 insertions, 514 deletions
diff --git a/apps/dav/appinfo/v1/caldav.php b/apps/dav/appinfo/v1/caldav.php index 301ea4b5486..bd9de775f44 100644 --- a/apps/dav/appinfo/v1/caldav.php +++ b/apps/dav/appinfo/v1/caldav.php @@ -84,7 +84,7 @@ if ($debugging) { $server->addPlugin(new \Sabre\DAV\Sync\Plugin()); $server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin()); $server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin()); -$server->addPlugin(new \OCA\DAV\CalDAV\Schedule\IMipPlugin( \OC::$server->getMailer(), \OC::$server->getLogger())); +$server->addPlugin(new \OCA\DAV\CalDAV\Schedule\IMipPlugin( \OC::$server->getMailer(), \OC::$server->getLogger(), new \OC\AppFramework\Utility\TimeFactory())); $server->addPlugin(new ExceptionLoggerPlugin('caldav', \OC::$server->getLogger())); // And off we go! diff --git a/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php b/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php index c21edb45848..8e1d7e2563d 100644 --- a/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php +++ b/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php @@ -1,8 +1,10 @@ <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. + * @copyright Copyright (c) 2017, Georg Ehrke * * @author Thomas Müller <thomas.mueller@tmit.eu> + * @author Georg Ehrke <oc.list@georgehrke.com> * * @license AGPL-3.0 * @@ -21,10 +23,15 @@ */ namespace OCA\DAV\CalDAV\Schedule; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\ILogger; use OCP\Mail\IMailer; +use Sabre\VObject\Component\VCalendar; +use Sabre\VObject\DateTimeParser; use Sabre\VObject\ITip; use Sabre\CalDAV\Schedule\IMipPlugin as SabreIMipPlugin; +use Sabre\VObject\Recur\EventIterator; + /** * iMIP handler. * @@ -47,15 +54,23 @@ class IMipPlugin extends SabreIMipPlugin { /** @var ILogger */ private $logger; + /** @var ITimeFactory */ + private $timeFactory; + + const MAX_DATE = '2038-01-01'; + /** * Creates the email handler. * * @param IMailer $mailer + * @param ILogger $logger + * @param ITimeFactory $timeFactory */ - function __construct(IMailer $mailer, ILogger $logger) { + function __construct(IMailer $mailer, ILogger $logger, ITimeFactory $timeFactory) { parent::__construct(''); $this->mailer = $mailer; $this->logger = $logger; + $this->timeFactory = $timeFactory; } /** @@ -85,6 +100,11 @@ class IMipPlugin extends SabreIMipPlugin { return; } + // don't send out mails for events that already took place + if ($this->isEventInThePast($iTipMessage->message)) { + return; + } + $sender = substr($iTipMessage->sender, 7); $recipient = substr($iTipMessage->recipient, 7); @@ -125,4 +145,49 @@ class IMipPlugin extends SabreIMipPlugin { } } + /** + * check if event took place in the past already + * @param VCalendar $vObject + * @return bool + */ + private function isEventInThePast(VCalendar $vObject) { + $component = $vObject->VEVENT; + + $firstOccurrence = $component->DTSTART->getDateTime()->getTimeStamp(); + // Finding the last occurrence is a bit harder + if (!isset($component->RRULE)) { + if (isset($component->DTEND)) { + $lastOccurrence = $component->DTEND->getDateTime()->getTimeStamp(); + } elseif (isset($component->DURATION)) { + $endDate = clone $component->DTSTART->getDateTime(); + // $component->DTEND->getDateTime() returns DateTimeImmutable + $endDate = $endDate->add(DateTimeParser::parse($component->DURATION->getValue())); + $lastOccurrence = $endDate->getTimeStamp(); + } elseif (!$component->DTSTART->hasTime()) { + $endDate = clone $component->DTSTART->getDateTime(); + // $component->DTSTART->getDateTime() returns DateTimeImmutable + $endDate = $endDate->modify('+1 day'); + $lastOccurrence = $endDate->getTimeStamp(); + } else { + $lastOccurrence = $firstOccurrence; + } + } else { + $it = new EventIterator($vObject, (string)$component->UID); + $maxDate = new \DateTime(self::MAX_DATE); + if ($it->isInfinite()) { + $lastOccurrence = $maxDate->getTimestamp(); + } else { + $end = $it->getDtEnd(); + while($it->valid() && $end < $maxDate) { + $end = $it->getDtEnd(); + $it->next(); + + } + $lastOccurrence = $end->getTimestamp(); + } + } + + $currentTime = $this->timeFactory->getTime(); + return $lastOccurrence < $currentTime; + } } diff --git a/apps/dav/lib/Connector/Sabre/CachingTree.php b/apps/dav/lib/Connector/Sabre/CachingTree.php new file mode 100644 index 00000000000..80c8311fed3 --- /dev/null +++ b/apps/dav/lib/Connector/Sabre/CachingTree.php @@ -0,0 +1,39 @@ +<?php +/** + * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Connector\Sabre; + +use Sabre\DAV\Tree; + +class CachingTree extends Tree { + /** + * Store a node in the cache + * + * @param Node $node + * @param null|string $path + */ + public function cacheNode(Node $node, $path = null) { + if (is_null($path)) { + $path = $node->getPath(); + } + $this->cache[trim($path, '/')] = $node; + } +} diff --git a/apps/dav/lib/Connector/Sabre/ObjectTree.php b/apps/dav/lib/Connector/Sabre/ObjectTree.php index 3371c655f29..a44b25c24cb 100644 --- a/apps/dav/lib/Connector/Sabre/ObjectTree.php +++ b/apps/dav/lib/Connector/Sabre/ObjectTree.php @@ -39,7 +39,7 @@ use OCP\Files\StorageInvalidException; use OCP\Files\StorageNotAvailableException; use OCP\Lock\LockedException; -class ObjectTree extends \Sabre\DAV\Tree { +class ObjectTree extends CachingTree { /** * @var \OC\Files\View @@ -97,10 +97,6 @@ class ObjectTree extends \Sabre\DAV\Tree { return $path; } - public function cacheNode(Node $node) { - $this->cache[trim($node->getPath(), '/')] = $node; - } - /** * Returns the INode object for the requested path * diff --git a/apps/dav/lib/Connector/Sabre/Server.php b/apps/dav/lib/Connector/Sabre/Server.php index 9915ffdba03..686b0ea7604 100644 --- a/apps/dav/lib/Connector/Sabre/Server.php +++ b/apps/dav/lib/Connector/Sabre/Server.php @@ -33,6 +33,7 @@ namespace OCA\DAV\Connector\Sabre; * @see \Sabre\DAV\Server */ class Server extends \Sabre\DAV\Server { + /** @var CachingTree $tree */ /** * @see \Sabre\DAV\Server diff --git a/apps/dav/lib/Files/FileSearchBackend.php b/apps/dav/lib/Files/FileSearchBackend.php index 0d837807fd8..515e7ed0121 100644 --- a/apps/dav/lib/Files/FileSearchBackend.php +++ b/apps/dav/lib/Files/FileSearchBackend.php @@ -26,6 +26,7 @@ use OC\Files\Search\SearchComparison; use OC\Files\Search\SearchOrder; use OC\Files\Search\SearchQuery; use OC\Files\View; +use OCA\DAV\Connector\Sabre\CachingTree; use OCA\DAV\Connector\Sabre\Directory; use OCA\DAV\Connector\Sabre\FilesPlugin; use OCA\DAV\Connector\Sabre\TagsPlugin; @@ -39,7 +40,6 @@ use OCP\Files\Search\ISearchQuery; use OCP\IUser; use OCP\Share\IManager; use Sabre\DAV\Exception\NotFound; -use Sabre\DAV\Tree; use SearchDAV\Backend\ISearchBackend; use SearchDAV\Backend\SearchPropertyDefinition; use SearchDAV\Backend\SearchResult; @@ -49,7 +49,7 @@ use SearchDAV\XML\Operator; use SearchDAV\XML\Order; class FileSearchBackend implements ISearchBackend { - /** @var Tree */ + /** @var CachingTree */ private $tree; /** @var IUser */ @@ -67,14 +67,14 @@ class FileSearchBackend implements ISearchBackend { /** * FileSearchBackend constructor. * - * @param Tree $tree + * @param CachingTree $tree * @param IUser $user * @param IRootFolder $rootFolder * @param IManager $shareManager * @param View $view * @internal param IRootFolder $rootFolder */ - public function __construct(Tree $tree, IUser $user, IRootFolder $rootFolder, IManager $shareManager, View $view) { + public function __construct(CachingTree $tree, IUser $user, IRootFolder $rootFolder, IManager $shareManager, View $view) { $this->tree = $tree; $this->user = $user; $this->rootFolder = $rootFolder; @@ -157,10 +157,13 @@ class FileSearchBackend implements ISearchBackend { return array_map(function (Node $node) { if ($node instanceof Folder) { - return new SearchResult(new \OCA\DAV\Connector\Sabre\Directory($this->view, $node, $this->tree, $this->shareManager), $this->getHrefForNode($node)); + $davNode = new \OCA\DAV\Connector\Sabre\Directory($this->view, $node, $this->tree, $this->shareManager); } else { - return new SearchResult(new \OCA\DAV\Connector\Sabre\File($this->view, $node, $this->shareManager), $this->getHrefForNode($node)); + $davNode = new \OCA\DAV\Connector\Sabre\File($this->view, $node, $this->shareManager); } + $path = $this->getHrefForNode($node); + $this->tree->cacheNode($davNode, $path); + return new SearchResult($davNode, $path); }, $results); } diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php index 896af09f1c0..ac0abc8b4eb 100644 --- a/apps/dav/lib/Server.php +++ b/apps/dav/lib/Server.php @@ -28,6 +28,7 @@ */ namespace OCA\DAV; +use OC\AppFramework\Utility\TimeFactory; use OCA\DAV\CalDAV\Schedule\IMipPlugin; use OCA\DAV\CardDAV\ImageExportPlugin; use OCA\DAV\CardDAV\PhotoCache; @@ -35,6 +36,7 @@ use OCA\DAV\Comments\CommentsPlugin; use OCA\DAV\Connector\Sabre\Auth; use OCA\DAV\Connector\Sabre\BearerAuth; use OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin; +use OCA\DAV\Connector\Sabre\CachingTree; use OCA\DAV\Connector\Sabre\CommentPropertiesPlugin; use OCA\DAV\Connector\Sabre\CopyEtagHeaderPlugin; use OCA\DAV\Connector\Sabre\DavAclPlugin; @@ -73,9 +75,10 @@ class Server { $logger = \OC::$server->getLogger(); $mailer = \OC::$server->getMailer(); $dispatcher = \OC::$server->getEventDispatcher(); + $timezone = new TimeFactory(); $root = new RootCollection(); - $this->server = new \OCA\DAV\Connector\Sabre\Server($root); + $this->server = new \OCA\DAV\Connector\Sabre\Server(new CachingTree($root)); // Add maintenance plugin $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\MaintenancePlugin(\OC::$server->getConfig())); @@ -134,7 +137,7 @@ class Server { $this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin()); $this->server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin()); $this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin()); - $this->server->addPlugin(new IMipPlugin($mailer, $logger)); + $this->server->addPlugin(new IMipPlugin($mailer, $logger, $timezone)); $this->server->addPlugin(new \Sabre\CalDAV\Subscriptions\Plugin()); $this->server->addPlugin(new \Sabre\CalDAV\Notifications\Plugin()); $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OC::$server->getRequest())); diff --git a/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php b/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php index 2b9d10c9e81..56eb00406da 100644 --- a/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php +++ b/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php @@ -1,9 +1,11 @@ <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. + * @copyright Copyright (c) 2017, Georg Ehrke * * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> + * @author Georg Ehrke <oc.list@georgehrke.com> * * @license AGPL-3.0 * @@ -25,6 +27,7 @@ namespace OCA\DAV\Tests\unit\CalDAV\Schedule; use OC\Mail\Mailer; use OCA\DAV\CalDAV\Schedule\IMipPlugin; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\ILogger; use Sabre\VObject\Component\VCalendar; use Sabre\VObject\ITip\Message; @@ -40,8 +43,10 @@ class IMipPluginTest extends TestCase { $mailer->expects($this->once())->method('send'); /** @var ILogger | \PHPUnit_Framework_MockObject_MockObject $logger */ $logger = $this->getMockBuilder('OC\Log')->disableOriginalConstructor()->getMock(); + $timeFactory = $this->getMockBuilder(ITimeFactory::class)->disableOriginalConstructor()->getMock(); + $timeFactory->method('getTime')->willReturn(1); - $plugin = new IMipPlugin($mailer, $logger); + $plugin = new IMipPlugin($mailer, $logger, $timeFactory); $message = new Message(); $message->method = 'REQUEST'; $message->message = new VCalendar(); @@ -49,6 +54,7 @@ class IMipPluginTest extends TestCase { 'UID' => $message->uid, 'SEQUENCE' => $message->sequence, 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2017-01-01 00:00:00') // 1483228800 ]); $message->sender = 'mailto:gandalf@wiz.ard'; $message->recipient = 'mailto:frodo@hobb.it'; @@ -69,8 +75,10 @@ class IMipPluginTest extends TestCase { $mailer->method('send')->willThrowException(new \Exception()); /** @var ILogger | \PHPUnit_Framework_MockObject_MockObject $logger */ $logger = $this->getMockBuilder('OC\Log')->disableOriginalConstructor()->getMock(); + $timeFactory = $this->getMockBuilder(ITimeFactory::class)->disableOriginalConstructor()->getMock(); + $timeFactory->method('getTime')->willReturn(1); - $plugin = new IMipPlugin($mailer, $logger); + $plugin = new IMipPlugin($mailer, $logger, $timeFactory); $message = new Message(); $message->method = 'REQUEST'; $message->message = new VCalendar(); @@ -78,6 +86,7 @@ class IMipPluginTest extends TestCase { 'UID' => $message->uid, 'SEQUENCE' => $message->sequence, 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2017-01-01 00:00:00') // 1483228800 ]); $message->sender = 'mailto:gandalf@wiz.ard'; $message->recipient = 'mailto:frodo@hobb.it'; @@ -90,4 +99,57 @@ class IMipPluginTest extends TestCase { $this->assertEquals('text/calendar; charset=UTF-8; method=REQUEST', $mailMessage->getSwiftMessage()->getContentType()); } + /** + * @dataProvider dataNoMessageSendForPastEvents + */ + public function testNoMessageSendForPastEvents($veventParams, $expectsMail) { + $mailMessage = new \OC\Mail\Message(new \Swift_Message()); + /** @var Mailer | \PHPUnit_Framework_MockObject_MockObject $mailer */ + $mailer = $this->getMockBuilder('OC\Mail\Mailer')->disableOriginalConstructor()->getMock(); + $mailer->method('createMessage')->willReturn($mailMessage); + if ($expectsMail) { + $mailer->expects($this->once())->method('send'); + } else { + $mailer->expects($this->never())->method('send'); + } + /** @var ILogger | \PHPUnit_Framework_MockObject_MockObject $logger */ + $logger = $this->getMockBuilder('OC\Log')->disableOriginalConstructor()->getMock(); + $timeFactory = $this->getMockBuilder(ITimeFactory::class)->disableOriginalConstructor()->getMock(); + $timeFactory->method('getTime')->willReturn(1496912528); + + $plugin = new IMipPlugin($mailer, $logger, $timeFactory); + $message = new Message(); + $message->method = 'REQUEST'; + $message->message = new VCalendar(); + $message->message->add('VEVENT', array_merge([ + 'UID' => 'uid1337', + 'SEQUENCE' => 42, + 'SUMMARY' => 'Fellowship meeting', + ], $veventParams)); + $message->sender = 'mailto:gandalf@wiz.ard'; + $message->recipient = 'mailto:frodo@hobb.it'; + + $plugin->schedule($message); + + if ($expectsMail) { + $this->assertEquals('1.1', $message->getScheduleStatus()); + } else { + $this->assertEquals(false, $message->getScheduleStatus()); + } + } + + public function dataNoMessageSendForPastEvents() { + return [ + [['DTSTART' => new \DateTime('2017-01-01 00:00:00')], false], + [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DTEND' => new \DateTime('2017-01-01 00:00:00')], false], + [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DTEND' => new \DateTime('2017-12-31 00:00:00')], true], + [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DURATION' => 'P1D'], false], + [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DURATION' => 'P52W'], true], + [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DTEND' => new \DateTime('2017-01-01 00:00:00'), 'RRULE' => 'FREQ=WEEKLY'], true], + [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DTEND' => new \DateTime('2017-01-01 00:00:00'), 'RRULE' => 'FREQ=WEEKLY;COUNT=3'], false], + [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DTEND' => new \DateTime('2017-01-01 00:00:00'), 'RRULE' => 'FREQ=WEEKLY;UNTIL=20170301T000000Z'], false], + [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DTEND' => new \DateTime('2017-01-01 00:00:00'), 'RRULE' => 'FREQ=WEEKLY;COUNT=33'], true], + [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DTEND' => new \DateTime('2017-01-01 00:00:00'), 'RRULE' => 'FREQ=WEEKLY;UNTIL=20171001T000000Z'], true], + ]; + } } diff --git a/apps/dav/tests/unit/Files/FileSearchBackendTest.php b/apps/dav/tests/unit/Files/FileSearchBackendTest.php index 7de92c59763..28b6f082712 100644 --- a/apps/dav/tests/unit/Files/FileSearchBackendTest.php +++ b/apps/dav/tests/unit/Files/FileSearchBackendTest.php @@ -24,6 +24,7 @@ namespace OCA\DAV\Tests\Files; use OC\Files\Search\SearchComparison; use OC\Files\Search\SearchQuery; use OC\Files\View; +use OCA\DAV\Connector\Sabre\CachingTree; use OCA\DAV\Connector\Sabre\Directory; use OCA\DAV\Connector\Sabre\File; use OCA\DAV\Connector\Sabre\FilesPlugin; @@ -34,7 +35,6 @@ use OCP\Files\IRootFolder; use OCP\Files\Search\ISearchComparison; use OCP\IUser; use OCP\Share\IManager; -use Sabre\DAV\Tree; use SearchDAV\XML\BasicSearch; use SearchDAV\XML\Literal; use SearchDAV\XML\Operator; @@ -42,7 +42,7 @@ use SearchDAV\XML\Scope; use Test\TestCase; class FileSearchBackendTest extends TestCase { - /** @var Tree|\PHPUnit_Framework_MockObject_MockObject */ + /** @var CachingTree|\PHPUnit_Framework_MockObject_MockObject */ private $tree; /** @var IUser */ @@ -74,7 +74,7 @@ class FileSearchBackendTest extends TestCase { ->method('getUID') ->willReturn('test'); - $this->tree = $this->getMockBuilder(Tree::class) + $this->tree = $this->getMockBuilder(CachingTree::class) ->disableOriginalConstructor() ->getMock(); diff --git a/apps/files/l10n/da.js b/apps/files/l10n/da.js index f7d90b6af9b..c22801a26f6 100644 --- a/apps/files/l10n/da.js +++ b/apps/files/l10n/da.js @@ -16,6 +16,8 @@ OC.L10N.register( "Not enough free space, you are uploading {size1} but only {size2} is left" : "Der er ikke tilstrækkeligt friplads. Du uplaoder {size1} men der er kun {size2} tilbage", "Target folder \"{dir}\" does not exist any more" : "Destinations mappen \"{dir}\" eksistere ikke længere", "Not enough free space" : "Ikke nok fri plads", + "Uploading …" : "Uploader...", + "…" : "...", "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} af {totalSize} ({bitrate})", "Actions" : "Handlinger", "Download" : "Hent", @@ -100,7 +102,9 @@ OC.L10N.register( "A file has been added to or removed from your <strong>favorites</strong>" : "En fil er blevet tilføjet eller fjernet fra dine <strong>favoritter</strong>", "A file or folder has been <strong>changed</strong> or <strong>renamed</strong>" : "En fil eller mappe er blevet <strong>ændret</strong> eller <strong>omdøbt</strong>", "A new file or folder has been <strong>created</strong>" : "En ny fil eller mapper er blevet <strong>oprettet</strong>", + "A file or folder has been <strong>deleted</strong>" : "En fil eller mappe er blevet <strong>slettet</strong>", "Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>" : "Begræns noter om oprettelse og ændringer af dine <strong>favorit filer</strong> <em>(Kun streaming)</em>", + "A file or folder has been <strong>restored</strong>" : "En fil eller mappe er blevet <strong>gendannet</strong>", "Unlimited" : "Ubegrænset", "Upload (max. %s)" : "Upload (max. %s)", "File handling" : "Filhåndtering", @@ -115,6 +119,7 @@ OC.L10N.register( "Show hidden files" : "Vis skjulte filer", "WebDAV" : "WebDAV", "Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">access your Files via WebDAV</a>" : "Brug denne adresse til at <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">tilgå dine filer via WebDAV</a>", + "Uploading @" : "Uploader @", "No files in here" : "Her er ingen filer", "Upload some content or sync with your devices!" : "Overfør indhold eller synkronisér med dine enheder!", "No entries found in this folder" : "Der blev ikke fundet poster i denne mappe", diff --git a/apps/files/l10n/da.json b/apps/files/l10n/da.json index dc40721380e..9f279f3d095 100644 --- a/apps/files/l10n/da.json +++ b/apps/files/l10n/da.json @@ -14,6 +14,8 @@ "Not enough free space, you are uploading {size1} but only {size2} is left" : "Der er ikke tilstrækkeligt friplads. Du uplaoder {size1} men der er kun {size2} tilbage", "Target folder \"{dir}\" does not exist any more" : "Destinations mappen \"{dir}\" eksistere ikke længere", "Not enough free space" : "Ikke nok fri plads", + "Uploading …" : "Uploader...", + "…" : "...", "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} af {totalSize} ({bitrate})", "Actions" : "Handlinger", "Download" : "Hent", @@ -98,7 +100,9 @@ "A file has been added to or removed from your <strong>favorites</strong>" : "En fil er blevet tilføjet eller fjernet fra dine <strong>favoritter</strong>", "A file or folder has been <strong>changed</strong> or <strong>renamed</strong>" : "En fil eller mappe er blevet <strong>ændret</strong> eller <strong>omdøbt</strong>", "A new file or folder has been <strong>created</strong>" : "En ny fil eller mapper er blevet <strong>oprettet</strong>", + "A file or folder has been <strong>deleted</strong>" : "En fil eller mappe er blevet <strong>slettet</strong>", "Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>" : "Begræns noter om oprettelse og ændringer af dine <strong>favorit filer</strong> <em>(Kun streaming)</em>", + "A file or folder has been <strong>restored</strong>" : "En fil eller mappe er blevet <strong>gendannet</strong>", "Unlimited" : "Ubegrænset", "Upload (max. %s)" : "Upload (max. %s)", "File handling" : "Filhåndtering", @@ -113,6 +117,7 @@ "Show hidden files" : "Vis skjulte filer", "WebDAV" : "WebDAV", "Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">access your Files via WebDAV</a>" : "Brug denne adresse til at <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">tilgå dine filer via WebDAV</a>", + "Uploading @" : "Uploader @", "No files in here" : "Her er ingen filer", "Upload some content or sync with your devices!" : "Overfør indhold eller synkronisér med dine enheder!", "No entries found in this folder" : "Der blev ikke fundet poster i denne mappe", diff --git a/apps/files/l10n/en_GB.js b/apps/files/l10n/en_GB.js index 7862757e38c..4736520eb43 100644 --- a/apps/files/l10n/en_GB.js +++ b/apps/files/l10n/en_GB.js @@ -16,6 +16,8 @@ OC.L10N.register( "Not enough free space, you are uploading {size1} but only {size2} is left" : "Not enough free space, you are uploading {size1} but only {size2} is left", "Target folder \"{dir}\" does not exist any more" : "Target folder \"{dir}\" does not exist any more", "Not enough free space" : "Not enough free space", + "Uploading …" : "Uploading …", + "…" : "…", "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} of {totalSize} ({bitrate})", "Actions" : "Actions", "Download" : "Download", @@ -117,6 +119,7 @@ OC.L10N.register( "Show hidden files" : "Show hidden files", "WebDAV" : "WebDAV", "Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">access your Files via WebDAV</a>" : "Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">access your Files via WebDAV</a>", + "Uploading @" : "Uploading @", "No files in here" : "No files in here", "Upload some content or sync with your devices!" : "Upload some content or sync with your devices!", "No entries found in this folder" : "No entries found in this folder", diff --git a/apps/files/l10n/en_GB.json b/apps/files/l10n/en_GB.json index 4c93aa6dace..f346459ac85 100644 --- a/apps/files/l10n/en_GB.json +++ b/apps/files/l10n/en_GB.json @@ -14,6 +14,8 @@ "Not enough free space, you are uploading {size1} but only {size2} is left" : "Not enough free space, you are uploading {size1} but only {size2} is left", "Target folder \"{dir}\" does not exist any more" : "Target folder \"{dir}\" does not exist any more", "Not enough free space" : "Not enough free space", + "Uploading …" : "Uploading …", + "…" : "…", "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} of {totalSize} ({bitrate})", "Actions" : "Actions", "Download" : "Download", @@ -115,6 +117,7 @@ "Show hidden files" : "Show hidden files", "WebDAV" : "WebDAV", "Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">access your Files via WebDAV</a>" : "Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">access your Files via WebDAV</a>", + "Uploading @" : "Uploading @", "No files in here" : "No files in here", "Upload some content or sync with your devices!" : "Upload some content or sync with your devices!", "No entries found in this folder" : "No entries found in this folder", diff --git a/apps/files/l10n/lt_LT.js b/apps/files/l10n/lt_LT.js index 472dcc80b25..309f508aa20 100644 --- a/apps/files/l10n/lt_LT.js +++ b/apps/files/l10n/lt_LT.js @@ -16,6 +16,8 @@ OC.L10N.register( "Not enough free space, you are uploading {size1} but only {size2} is left" : "Nepakanka laisvos vietos. Jūs bandote įkelti {size1} dydžio bylą, bet liko tik {size2} vietos", "Target folder \"{dir}\" does not exist any more" : "Paskirties aplanko \"{dir}\" daugiau nebėra", "Not enough free space" : "Trūksta laisvos vietos", + "Uploading …" : "Įkeliama ...", + "…" : "...", "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} iš {totalSize} ({bitrate})", "Actions" : "Veiksmai", "Download" : "Atsisiųsti", @@ -117,6 +119,7 @@ OC.L10N.register( "Show hidden files" : "Rodyti paslėptus failus", "WebDAV" : "WebDAV", "Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">access your Files via WebDAV</a>" : "Naudokite šį adresą <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\"> norėdami pasiekti failus per WebDAV</a>", + "Uploading @" : "Įkeliama @", "No files in here" : "Čia nėra failų", "Upload some content or sync with your devices!" : "Įkelkite kokį nors turinį, arba sinchronizuokite su savo įrenginiais!", "No entries found in this folder" : "Nerasta įrašų šiame aplanke", diff --git a/apps/files/l10n/lt_LT.json b/apps/files/l10n/lt_LT.json index bdd60647db0..fe6ae055f63 100644 --- a/apps/files/l10n/lt_LT.json +++ b/apps/files/l10n/lt_LT.json @@ -14,6 +14,8 @@ "Not enough free space, you are uploading {size1} but only {size2} is left" : "Nepakanka laisvos vietos. Jūs bandote įkelti {size1} dydžio bylą, bet liko tik {size2} vietos", "Target folder \"{dir}\" does not exist any more" : "Paskirties aplanko \"{dir}\" daugiau nebėra", "Not enough free space" : "Trūksta laisvos vietos", + "Uploading …" : "Įkeliama ...", + "…" : "...", "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} iš {totalSize} ({bitrate})", "Actions" : "Veiksmai", "Download" : "Atsisiųsti", @@ -115,6 +117,7 @@ "Show hidden files" : "Rodyti paslėptus failus", "WebDAV" : "WebDAV", "Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">access your Files via WebDAV</a>" : "Naudokite šį adresą <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\"> norėdami pasiekti failus per WebDAV</a>", + "Uploading @" : "Įkeliama @", "No files in here" : "Čia nėra failų", "Upload some content or sync with your devices!" : "Įkelkite kokį nors turinį, arba sinchronizuokite su savo įrenginiais!", "No entries found in this folder" : "Nerasta įrašų šiame aplanke", diff --git a/apps/files_external/tests/Service/StoragesServiceTest.php b/apps/files_external/tests/Service/StoragesServiceTest.php index 2776f24d5ab..056a03d24c8 100644 --- a/apps/files_external/tests/Service/StoragesServiceTest.php +++ b/apps/files_external/tests/Service/StoragesServiceTest.php @@ -22,10 +22,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OCA\Files_External\Tests\Service; use \OC\Files\Filesystem; +use OCA\Files_External\Lib\Auth\InvalidAuth; +use OCA\Files_External\Lib\Backend\InvalidBackend; use OCA\Files_External\NotFoundException; use OCA\Files_External\Lib\StorageConfig; use OCA\Files_External\Service\BackendService; @@ -368,28 +371,24 @@ abstract class StoragesServiceTest extends \Test\TestCase { $this->assertEquals($priority, $storage->getPriority()); } - /** - * @expectedException \InvalidArgumentException - */ public function testCreateStorageInvalidClass() { - $this->service->createStorage( + $storage = $this->service->createStorage( 'mount', 'identifier:\OC\Not\A\Backend', 'identifier:\Auth\Mechanism', [] ); + $this->assertInstanceOf(InvalidBackend::class, $storage->getBackend()); } - /** - * @expectedException \InvalidArgumentException - */ public function testCreateStorageInvalidAuthMechanismClass() { - $this->service->createStorage( + $storage = $this->service->createStorage( 'mount', 'identifier:\OCA\Files_External\Lib\Backend\SMB', 'identifier:\Not\An\Auth\Mechanism', [] ); + $this->assertInstanceOf(InvalidAuth::class, $storage->getAuthMechanism()); } public function testGetStoragesBackendNotVisible() { diff --git a/apps/sharebymail/lib/ShareByMailProvider.php b/apps/sharebymail/lib/ShareByMailProvider.php index e5ae64ba120..9a0678f5fe7 100644 --- a/apps/sharebymail/lib/ShareByMailProvider.php +++ b/apps/sharebymail/lib/ShareByMailProvider.php @@ -384,8 +384,7 @@ class ShareByMailProvider implements IShareProvider { $message = $this->mailer->createMessage(); - $emailTemplate = $this->mailer->createEMailTemplate(); - $emailTemplate->setMetaData('sharebymail.RecipientNotification', [ + $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [ 'filename' => $filename, 'link' => $link, 'initiator' => $initiatorDisplayName, @@ -462,8 +461,7 @@ class ShareByMailProvider implements IShareProvider { $message = $this->mailer->createMessage(); - $emailTemplate = $this->mailer->createEMailTemplate(); - $emailTemplate->setMetaData('sharebymail.RecipientPasswordNotification', [ + $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientPasswordNotification', [ 'filename' => $filename, 'password' => $password, 'initiator' => $initiatorDisplayName, @@ -530,8 +528,7 @@ class ShareByMailProvider implements IShareProvider { $bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]); $message = $this->mailer->createMessage(); - $emailTemplate = $this->mailer->createEMailTemplate(); - $emailTemplate->setMetaData('sharebymail.OwnerPasswordNotification', [ + $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.OwnerPasswordNotification', [ 'filename' => $filename, 'password' => $password, 'initiator' => $initiatorDisplayName, diff --git a/apps/theming/css/theming.scss b/apps/theming/css/theming.scss index 22e3bdc0d95..f36ff69e42b 100644 --- a/apps/theming/css/theming.scss +++ b/apps/theming/css/theming.scss @@ -10,7 +10,6 @@ color: $color-primary-text; } -/* invert header icons on bright background */ @if (lightness($color-primary) > 55) { .searchbox input[type="search"] { background: transparent url('../../../core/img/actions/search.svg') no-repeat 6px center; @@ -29,6 +28,36 @@ #appmenu .icon-more-white { background-image: url('../../../core/img/actions/more.svg'); } + + #body-login { + + input, + #alternative-logins li a { + border: 1px solid nc-lighten($color-primary-text, 50%); + } + input.primary, + #alternative-logins li a { + background-color: $color-primary; + } + a, + label, + p, + #alternative-logins legend { + color: $color-primary-text !important; + } + input[type='checkbox'].checkbox--white + label:before { + border-color: nc-darken($color-primary-element, 40%) !important; + } + input[type='checkbox'].checkbox--white:not(:disabled):not(:checked) + label:hover:before, + input[type='checkbox'].checkbox--white:focus + label:before { + border-color: nc-darken($color-primary-element, 30%) !important; + } + input[type='checkbox'].checkbox--white:checked + label:before { + border-color: nc-darken($color-primary-element, 30%) !important; + background-image: url('../../../core/img/actions/checkbox-mark.svg'); + background-color: nc-darken($color-primary-element, 30%) !important; + } + } } /* Colorized svg images */ @@ -59,8 +88,10 @@ background-color: $color-primary; } -input.primary { - background-color: $color-primary; +input.primary, +#alternative-logins li a { + background-color: $color-primary-element; + border: 1px solid $color-primary-text; color: $color-primary-text; } @@ -82,37 +113,6 @@ input.primary { color: $color-primary-text !important; } - @if (lightness($color-primary) > 50) { - #submit { - border-color: nc-darken($color-primary, 20%); - background-color: nc-darken($color-primary, 20%); - } - #submit:hover { - border-color: nc-darken($color-primary, 10%); - background-color: nc-darken($color-primary, 10%); - } - input[type='checkbox'].checkbox--white + label:before { - border-color: nc-darken($color-primary, 40%) !important; - } - input[type='checkbox'].checkbox--white:not(:disabled):not(:checked) + label:hover:before, - input[type='checkbox'].checkbox--white:focus + label:before { - border-color: nc-darken($color-primary, 30%) !important; - } - input[type='checkbox'].checkbox--white:checked + label:before { - border-color: nc-darken($color-primary, 30%) !important; - background-image: url('../../../core/img/actions/checkbox-mark.svg'); - background-color: nc-darken($color-primary, 30%) !important; - } - } @else { - #submit { - border-color: nc-lighten($color-primary, 20%); - background-color: nc-lighten($color-primary, 20%); - } - #submit:hover { - border-color: nc-lighten($color-primary, 10%); - background-color: nc-lighten($color-primary, 10%); - } - } } } diff --git a/apps/theming/l10n/lt_LT.js b/apps/theming/l10n/lt_LT.js index 7f645a20852..dd895804418 100644 --- a/apps/theming/l10n/lt_LT.js +++ b/apps/theming/l10n/lt_LT.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Loading preview…" : "Įkeliama peržiūra…", "Saved" : "Įrašyta", + "Admin" : "Administravimas", "a safe home for all your data" : "saugūs namai visiems jūsų duomenims", "The given name is too long" : "Nurodytas pavadinimas yra per ilgas", "The given web address is too long" : "Nurodytas adresas yra per ilgas", @@ -11,7 +12,9 @@ OC.L10N.register( "No file uploaded" : "Neįkeltas joks failas", "Unsupported image type" : "Nepalaikomas paveikslo tipas", "You are already using a custom theme" : "Jūs jau naudojate tinkintą temą", + "Theming" : "Tema", "Name" : "Pavadinimas", + "Reset to default" : "Atstatyti į numatytąją", "Web address" : "Saityno adresas", "Web address https://…" : "Saityno adresas https://…", "Slogan" : "Šūkis", @@ -20,6 +23,8 @@ OC.L10N.register( "Upload new logo" : "Įkelti naują logotipą", "Login image" : "Prisijungimo paveikslas", "Upload new login background" : "Įkelti naują prisijungimo foną", - "Remove background image" : "Šalinti foninį paveikslą" + "Remove background image" : "Šalinti foninį paveikslą", + "reset to default" : "atstatyta į numatytąją", + "Log in image" : "Prisijungimo vaizdas" }, "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);"); diff --git a/apps/theming/l10n/lt_LT.json b/apps/theming/l10n/lt_LT.json index d9b0eee99f1..74a192b259c 100644 --- a/apps/theming/l10n/lt_LT.json +++ b/apps/theming/l10n/lt_LT.json @@ -1,6 +1,7 @@ { "translations": { "Loading preview…" : "Įkeliama peržiūra…", "Saved" : "Įrašyta", + "Admin" : "Administravimas", "a safe home for all your data" : "saugūs namai visiems jūsų duomenims", "The given name is too long" : "Nurodytas pavadinimas yra per ilgas", "The given web address is too long" : "Nurodytas adresas yra per ilgas", @@ -9,7 +10,9 @@ "No file uploaded" : "Neįkeltas joks failas", "Unsupported image type" : "Nepalaikomas paveikslo tipas", "You are already using a custom theme" : "Jūs jau naudojate tinkintą temą", + "Theming" : "Tema", "Name" : "Pavadinimas", + "Reset to default" : "Atstatyti į numatytąją", "Web address" : "Saityno adresas", "Web address https://…" : "Saityno adresas https://…", "Slogan" : "Šūkis", @@ -18,6 +21,8 @@ "Upload new logo" : "Įkelti naują logotipą", "Login image" : "Prisijungimo paveikslas", "Upload new login background" : "Įkelti naują prisijungimo foną", - "Remove background image" : "Šalinti foninį paveikslą" + "Remove background image" : "Šalinti foninį paveikslą", + "reset to default" : "atstatyta į numatytąją", + "Log in image" : "Prisijungimo vaizdas" },"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);" }
\ No newline at end of file diff --git a/apps/theming/lib/ThemingDefaults.php b/apps/theming/lib/ThemingDefaults.php index b10dc4b7ea8..8200957edc0 100644 --- a/apps/theming/lib/ThemingDefaults.php +++ b/apps/theming/lib/ThemingDefaults.php @@ -238,6 +238,7 @@ class ThemingDefaults extends \OC_Defaults { } $variables['color-primary'] = $this->getColorPrimary(); $variables['color-primary-text'] = $colorPrimaryText; + $variables['color-primary-element'] = $this->util->elementColor($this->getColorPrimary()); } if ($this->config->getAppValue('theming', 'backgroundMime', null) === 'backgroundColor') { diff --git a/apps/theming/tests/ThemingDefaultsTest.php b/apps/theming/tests/ThemingDefaultsTest.php index cb827773470..c6d1fec91dd 100644 --- a/apps/theming/tests/ThemingDefaultsTest.php +++ b/apps/theming/tests/ThemingDefaultsTest.php @@ -507,8 +507,10 @@ class ThemingDefaultsTest extends TestCase { $this->config->expects($this->at(7))->method('getAppValue')->with('theming', 'color', null)->willReturn($this->defaults->getColorPrimary()); $this->config->expects($this->at(8))->method('getAppValue')->with('theming', 'color', $this->defaults->getColorPrimary())->willReturn($this->defaults->getColorPrimary()); $this->config->expects($this->at(9))->method('getAppValue')->with('theming', 'color', $this->defaults->getColorPrimary())->willReturn($this->defaults->getColorPrimary()); + $this->config->expects($this->at(10))->method('getAppValue')->with('theming', 'color', $this->defaults->getColorPrimary())->willReturn($this->defaults->getColorPrimary()); $this->util->expects($this->any())->method('invertTextColor')->with($this->defaults->getColorPrimary())->willReturn(false); + $this->util->expects($this->any())->method('elementColor')->with($this->defaults->getColorPrimary())->willReturn('#aaaaaa'); $this->cache->expects($this->once())->method('get')->with('getScssVariables')->willReturn(null); $folder = $this->createMock(ISimpleFolder::class); $file = $this->createMock(ISimpleFile::class); @@ -538,7 +540,8 @@ class ThemingDefaultsTest extends TestCase { 'image-login-background' => "'absolute-custom-background?v=0'", 'color-primary' => $this->defaults->getColorPrimary(), 'color-primary-text' => '#ffffff', - 'image-login-plain' => 'false' + 'image-login-plain' => 'false', + 'color-primary-element' => '#aaaaaa' ]; $this->assertEquals($expected, $this->template->getScssVariables()); diff --git a/apps/user_ldap/lib/Connection.php b/apps/user_ldap/lib/Connection.php index 10fbea7174b..440f5d2444e 100644 --- a/apps/user_ldap/lib/Connection.php +++ b/apps/user_ldap/lib/Connection.php @@ -528,12 +528,13 @@ class Connection extends LDAPUtility { } } + $isOverrideMainServer = ($this->configuration->ldapOverrideMainServer + || $this->getFromCache('overrideMainServer')); + $isBackupHost = (trim($this->configuration->ldapBackupHost) !== ""); $bindStatus = false; $error = -1; try { - if (!$this->configuration->ldapOverrideMainServer - && !$this->getFromCache('overrideMainServer') - ) { + if (!$isOverrideMainServer) { $this->doConnect($this->configuration->ldapHost, $this->configuration->ldapPort); $bindStatus = $this->bind(); @@ -543,26 +544,26 @@ class Connection extends LDAPUtility { if($bindStatus === true) { return $bindStatus; } - } catch (\OC\ServerNotAvailableException $e) { - if(trim($this->configuration->ldapBackupHost) === "") { + } catch (ServerNotAvailableException $e) { + if(!$isBackupHost) { throw $e; } } //if LDAP server is not reachable, try the Backup (Replica!) Server - if( $error !== 0 - || $this->configuration->ldapOverrideMainServer - || $this->getFromCache('overrideMainServer')) - { + if($isBackupHost && ($error !== 0 || $isOverrideMainServer)) { $this->doConnect($this->configuration->ldapBackupHost, $this->configuration->ldapBackupPort); $bindStatus = $this->bind(); - if($bindStatus && $error === -1 && !$this->getFromCache('overrideMainServer')) { + $error = $this->ldap->isResource($this->ldapConnectionRes) ? + $this->ldap->errno($this->ldapConnectionRes) : -1; + if($bindStatus && $error === 0 && !$this->getFromCache('overrideMainServer')) { //when bind to backup server succeeded and failed to main server, //skip contacting him until next cache refresh $this->writeToCache('overrideMainServer', true); } } + return $bindStatus; } return null; @@ -578,16 +579,23 @@ class Connection extends LDAPUtility { if ($host === '') { return false; } + $this->ldapConnectionRes = $this->ldap->connect($host, $port); - if($this->ldap->setOption($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) { - if($this->ldap->setOption($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) { - if($this->configuration->ldapTLS) { - $this->ldap->startTls($this->ldapConnectionRes); - } + + if(!$this->ldap->setOption($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) { + throw new ServerNotAvailableException('Could not set required LDAP Protocol version.'); + } + + if(!$this->ldap->setOption($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) { + throw new ServerNotAvailableException('Could not disable LDAP referrals.'); + } + + if($this->configuration->ldapTLS) { + if(!$this->ldap->startTls($this->ldapConnectionRes)) { + throw new ServerNotAvailableException('Start TLS failed, when connecting to LDAP host ' . $host . '.'); } - } else { - throw new \OC\ServerNotAvailableException('Could not set required LDAP Protocol version.'); } + return true; } @@ -595,17 +603,10 @@ class Connection extends LDAPUtility { * Binds to LDAP */ public function bind() { - static $getConnectionResourceAttempt = false; if(!$this->configuration->ldapConfigurationActive) { return false; } - if($getConnectionResourceAttempt) { - $getConnectionResourceAttempt = false; - return false; - } - $getConnectionResourceAttempt = true; $cr = $this->getConnectionResource(); - $getConnectionResourceAttempt = false; if(!$this->ldap->isResource($cr)) { return false; } @@ -613,10 +614,17 @@ class Connection extends LDAPUtility { $this->configuration->ldapAgentName, $this->configuration->ldapAgentPassword); if(!$ldapLogin) { + $errno = $this->ldap->errno($cr); + \OCP\Util::writeLog('user_ldap', - 'Bind failed: ' . $this->ldap->errno($cr) . ': ' . $this->ldap->error($cr), + 'Bind failed: ' . $errno . ': ' . $this->ldap->error($cr), \OCP\Util::WARN); - $this->ldapConnectionRes = null; + + // Set to failure mode, if LDAP error code is not LDAP_SUCCESS or LDAP_INVALID_CREDENTIALS + if($errno !== 0x00 && $errno !== 0x31) { + $this->ldapConnectionRes = null; + } + return false; } return true; diff --git a/apps/user_ldap/lib/Wizard.php b/apps/user_ldap/lib/Wizard.php index 9d4da9cbf3f..7376507d134 100644 --- a/apps/user_ldap/lib/Wizard.php +++ b/apps/user_ldap/lib/Wizard.php @@ -419,7 +419,7 @@ class Wizard extends LDAPUtility { * @throws \Exception */ public function fetchGroups($dbKey, $confKey) { - $obclasses = array('posixGroup', 'group', 'zimbraDistributionList', 'groupOfNames'); + $obclasses = array('posixGroup', 'group', 'zimbraDistributionList', 'groupOfNames', 'groupOfUniqueNames'); $filterParts = array(); foreach($obclasses as $obclass) { diff --git a/apps/user_ldap/tests/ConnectionTest.php b/apps/user_ldap/tests/ConnectionTest.php index e013773b7d9..87ebc8d9ad3 100644 --- a/apps/user_ldap/tests/ConnectionTest.php +++ b/apps/user_ldap/tests/ConnectionTest.php @@ -111,6 +111,10 @@ class ConnectionTest extends \Test\TestCase { ->method('connect') ->will($this->returnValue('ldapResource')); + $this->ldap->expects($this->any()) + ->method('errno') + ->will($this->returnValue(0)); + // Not called often enough? Then, the fallback to the backup server is broken. $this->connection->expects($this->exactly(4)) ->method('getFromCache') @@ -138,4 +142,98 @@ class ConnectionTest extends \Test\TestCase { $this->connection->init(); } + public function testBindWithInvalidCredentials() { + // background: Bind with invalid credentials should return false + // and not throw a ServerNotAvailableException. + + $host = 'ldap://nixda.ldap'; + $config = [ + 'ldapConfigurationActive' => true, + 'ldapHost' => $host, + 'ldapPort' => 389, + 'ldapBackupHost' => '', + 'ldapAgentName' => 'user', + 'ldapAgentPassword' => 'password' + ]; + + $this->connection->setIgnoreValidation(true); + $this->connection->setConfiguration($config); + + $this->ldap->expects($this->any()) + ->method('isResource') + ->will($this->returnValue(true)); + + $this->ldap->expects($this->any()) + ->method('setOption') + ->will($this->returnValue(true)); + + $this->ldap->expects($this->any()) + ->method('connect') + ->will($this->returnValue('ldapResource')); + + $this->ldap->expects($this->exactly(2)) + ->method('bind') + ->will($this->returnValue(false)); + + // LDAP_INVALID_CREDENTIALS + $this->ldap->expects($this->any()) + ->method('errno') + ->will($this->returnValue(0x31)); + + try { + $this->assertFalse($this->connection->bind(), 'Connection::bind() should not return true with invalid credentials.'); + } catch (\OC\ServerNotAvailableException $e) { + $this->fail('Failed asserting that exception of type "OC\ServerNotAvailableException" is not thrown.'); + } + } + + public function testStartTlsNegotiationFailure() { + // background: If Start TLS negotiation fails, + // a ServerNotAvailableException should be thrown. + + $host = 'ldap://nixda.ldap'; + $port = 389; + $config = [ + 'ldapConfigurationActive' => true, + 'ldapHost' => $host, + 'ldapPort' => $port, + 'ldapTLS' => true, + 'ldapBackupHost' => '', + 'ldapAgentName' => 'user', + 'ldapAgentPassword' => 'password' + ]; + + $this->connection->setIgnoreValidation(true); + $this->connection->setConfiguration($config); + + $this->ldap->expects($this->any()) + ->method('isResource') + ->will($this->returnValue(true)); + + $this->ldap->expects($this->any()) + ->method('connect') + ->will($this->returnValue('ldapResource')); + + $this->ldap->expects($this->any()) + ->method('setOption') + ->will($this->returnValue(true)); + + $this->ldap->expects($this->any()) + ->method('bind') + ->will($this->returnValue(true)); + + $this->ldap->expects($this->any()) + ->method('errno') + ->will($this->returnValue(0)); + + $this->ldap->expects($this->any()) + ->method('startTls') + ->will($this->returnValue(false)); + + $this->expectException(\OC\ServerNotAvailableException::class); + $this->expectExceptionMessage('Start TLS failed, when connecting to LDAP host ' . $host . '.'); + + $this->connection->init(); + } + } diff --git a/core/Controller/ClientFlowLoginController.php b/core/Controller/ClientFlowLoginController.php index bec81a89d53..5767c9e1c62 100644 --- a/core/Controller/ClientFlowLoginController.php +++ b/core/Controller/ClientFlowLoginController.php @@ -193,6 +193,7 @@ class ClientFlowLoginController extends Controller { 'urlGenerator' => $this->urlGenerator, 'stateToken' => $stateToken, 'serverHost' => $this->request->getServerHost(), + 'oauthState' => $this->session->get('oauth.state'), ], 'guest' ); diff --git a/core/Controller/LoginController.php b/core/Controller/LoginController.php index 47c14cbeaf4..462b9091d4f 100644 --- a/core/Controller/LoginController.php +++ b/core/Controller/LoginController.php @@ -184,12 +184,12 @@ class LoginController extends Controller { } // OpenGraph Support: http://ogp.me/ - Util::addHeader('meta', ['property' => "og:title", 'content' => Util::sanitizeHTML($this->defaults->getName())]); - Util::addHeader('meta', ['property' => "og:description", 'content' => Util::sanitizeHTML($this->defaults->getSlogan())]); - Util::addHeader('meta', ['property' => "og:site_name", 'content' => Util::sanitizeHTML($this->defaults->getName())]); - Util::addHeader('meta', ['property' => "og:url", 'content' => $this->urlGenerator->getAbsoluteURL('')]); - Util::addHeader('meta', ['property' => "og:type", 'content' => "website"]); - Util::addHeader('meta', ['property' => "og:image", 'content' => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core','favicon-touch.png'))]); + Util::addHeader('meta', ['property' => 'og:title', 'content' => Util::sanitizeHTML($this->defaults->getName())]); + Util::addHeader('meta', ['property' => 'og:description', 'content' => Util::sanitizeHTML($this->defaults->getSlogan())]); + Util::addHeader('meta', ['property' => 'og:site_name', 'content' => Util::sanitizeHTML($this->defaults->getName())]); + Util::addHeader('meta', ['property' => 'og:url', 'content' => $this->urlGenerator->getAbsoluteURL('/')]); + Util::addHeader('meta', ['property' => 'og:type', 'content' => 'website']); + Util::addHeader('meta', ['property' => 'og:image', 'content' => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core','favicon-touch.png'))]); return new TemplateResponse( $this->appName, 'login', $parameters, 'guest' diff --git a/core/Controller/LostController.php b/core/Controller/LostController.php index 45423b41e8b..4c59b0a1c00 100644 --- a/core/Controller/LostController.php +++ b/core/Controller/LostController.php @@ -305,8 +305,7 @@ class LostController extends Controller { $link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', array('userId' => $user->getUID(), 'token' => $token)); - $emailTemplate = $this->mailer->createEMailTemplate(); - $emailTemplate->setMetaData('core.ResetPassword', [ + $emailTemplate = $this->mailer->createEMailTemplate('core.ResetPassword', [ 'link' => $link, ]); diff --git a/core/css/guest.css b/core/css/guest.css index 8909495b540..1081b95fb36 100644 --- a/core/css/guest.css +++ b/core/css/guest.css @@ -400,8 +400,23 @@ form .warning input[type='checkbox']+label { } /* Alternative Logins */ -#alternative-logins legend { margin-bottom:10px; } -#alternative-logins li { height:40px; display:inline-block; white-space:nowrap; } +#alternative-logins legend { + margin-bottom: 10px; +} +#alternative-logins li { + height: 40px; + white-space: nowrap; + padding: 05px; +} +#alternative-logins li a { + width: 100%; + display: inline-block; + text-align: center; + box-sizing: border-box; + background-color: #0082c9; + color: white; + border-radius: 3px; +} /* fixes for update page TODO should be fixed some time in a proper way */ /* this is just for an error while updating the ownCloud instance */ diff --git a/core/css/inputs.scss b/core/css/inputs.scss index 10e3fcef015..5a7da4f50c9 100644 --- a/core/css/inputs.scss +++ b/core/css/inputs.scss @@ -50,7 +50,7 @@ textarea, &:focus, &.active { /* active class used for multiselect */ - border-color: $color-primary; + border-color: $color-primary-element; outline: none; } &:active { @@ -66,21 +66,21 @@ textarea, } /* Primary action button, use sparingly */ &.primary { - border: 1px solid #fff; - background-color: $color-primary; + background-color: $color-primary-element; + border: 1px solid $color-primary-text; color: $color-primary-text; cursor: pointer; &:not(:disabled) { &:hover, &:focus { - background-color: rgba($color-primary, .85); + background-color: rgba($color-primary-element, .85); } &:active { - background-color: rgba($color-primary, .7); + background-color: rgba($color-primary-element, .7); } } &:disabled { - background-color: rgba($color-primary, .7); + background-color: rgba($color-primary-element, .7); color: nc-lighten($color-main-text, 73%); } } @@ -225,15 +225,15 @@ input { } &:not(:disabled):not(:checked) + label:hover:before, &:focus + label:before { - border-color: $color-primary; + border-color: $color-primary-element; } &:checked + label:before, &.checkbox:indeterminate + label:before { /* ^ :indeterminate have a strange behavior on radio, so we respecified the checkbox class again to be safe */ box-shadow: inset 0px 0px 0px 2px $color-main-background; - background-color: $color-primary; - border-color: $color-primary + background-color: $color-primary-element; + border-color: $color-primary-element; } &:disabled + label:before { border: 1px solid nc-lighten($color-main-text, 53%); diff --git a/core/css/jquery-ui-fixes.scss b/core/css/jquery-ui-fixes.scss index cf27c1561f0..87167100813 100644 --- a/core/css/jquery-ui-fixes.scss +++ b/core/css/jquery-ui-fixes.scss @@ -190,6 +190,6 @@ .ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid transparent; background: inherit; - color: $color-primary; + color: $color-primary-element; } } diff --git a/core/css/variables.scss b/core/css/variables.scss index 5b7783a888a..3a135f91c86 100644 --- a/core/css/variables.scss +++ b/core/css/variables.scss @@ -5,6 +5,7 @@ $color-primary-text: #ffffff; $color-error: #e9322d; $color-warning: #ffcc44; $color-success: #46ba61; +$color-primary-element: $color-primary; @function nc-darken($color, $value) { @return darken($color, $value); diff --git a/core/l10n/da.js b/core/l10n/da.js index afdd8af6d9b..8133d3228bb 100644 --- a/core/l10n/da.js +++ b/core/l10n/da.js @@ -219,6 +219,8 @@ OC.L10N.register( "The specified document has not been found on the server." : "Det angivne dokument blev ikke fundet på serveren.", "You can click here to return to %s." : "Du kan klikke her for at gå tilbage til %s.", "Internal Server Error" : "Intern serverfejl", + "The server was unable to complete your request." : "Serveren var ikke i stand til at udføre din forespørgsel.", + "If this happens again, please send the technical details below to the server administrator." : "Send venligst detaljerne nedenunder til serveradministratoren, hvis dette sker igen.", "More details can be found in the server log." : "Flere detaljer kan fås i serverloggen.", "Technical details" : "Tekniske detaljer", "Remote Address: %s" : "Fjernadresse: %s", @@ -269,6 +271,8 @@ OC.L10N.register( "Log in" : "Log ind", "Stay logged in" : "Forbliv logget ind", "Alternative Logins" : "Alternative logins", + "Account access" : "Konto adgang", + "You are about to grant %s access to your %s account." : "Du er ved at tildele %s adgang til din %s konto.", "App token" : "App token", "Alternative login using app token" : "Alternativ login ved brug af \"app token\"", "Redirecting …" : "Viderstiller", @@ -280,6 +284,9 @@ OC.L10N.register( "Cancel log in" : "Annullér login", "Use backup code" : "Benyt backup-kode", "Error while validating your second factor" : "Fejl i valideringen af din anden faktor login", + "Access through untrusted domain" : "Tilgå gennem et utroværdigt domæne.", + "Please contact your administrator. If you are an administrator, edit the \"trusted_domains\" setting in config/config.php like the example in config.sample.php." : "Kontakt venligst din administrator. Hvis du er administratoren af denne server, skal du konfigurerer \"trusted-domains\" i filen config/config.php. Et eksempel på hvordan kan findes i filen config/config.sample.php", + "Depending on your configuration, this button could also work to trust the domain:" : "Denne knap kan også virke for at godkende domænet, afhængig af din konfiguration.", "Add \"%s\" as trusted domain" : "Tilføj \"%s\" som et troværdigt domæne", "App update required" : "Opdatering af app påkræves", "%s will be updated to version %s" : "%s vil blive opdateret til version %s", diff --git a/core/l10n/da.json b/core/l10n/da.json index e6c972e4fd6..ce2760f1bbc 100644 --- a/core/l10n/da.json +++ b/core/l10n/da.json @@ -217,6 +217,8 @@ "The specified document has not been found on the server." : "Det angivne dokument blev ikke fundet på serveren.", "You can click here to return to %s." : "Du kan klikke her for at gå tilbage til %s.", "Internal Server Error" : "Intern serverfejl", + "The server was unable to complete your request." : "Serveren var ikke i stand til at udføre din forespørgsel.", + "If this happens again, please send the technical details below to the server administrator." : "Send venligst detaljerne nedenunder til serveradministratoren, hvis dette sker igen.", "More details can be found in the server log." : "Flere detaljer kan fås i serverloggen.", "Technical details" : "Tekniske detaljer", "Remote Address: %s" : "Fjernadresse: %s", @@ -267,6 +269,8 @@ "Log in" : "Log ind", "Stay logged in" : "Forbliv logget ind", "Alternative Logins" : "Alternative logins", + "Account access" : "Konto adgang", + "You are about to grant %s access to your %s account." : "Du er ved at tildele %s adgang til din %s konto.", "App token" : "App token", "Alternative login using app token" : "Alternativ login ved brug af \"app token\"", "Redirecting …" : "Viderstiller", @@ -278,6 +282,9 @@ "Cancel log in" : "Annullér login", "Use backup code" : "Benyt backup-kode", "Error while validating your second factor" : "Fejl i valideringen af din anden faktor login", + "Access through untrusted domain" : "Tilgå gennem et utroværdigt domæne.", + "Please contact your administrator. If you are an administrator, edit the \"trusted_domains\" setting in config/config.php like the example in config.sample.php." : "Kontakt venligst din administrator. Hvis du er administratoren af denne server, skal du konfigurerer \"trusted-domains\" i filen config/config.php. Et eksempel på hvordan kan findes i filen config/config.sample.php", + "Depending on your configuration, this button could also work to trust the domain:" : "Denne knap kan også virke for at godkende domænet, afhængig af din konfiguration.", "Add \"%s\" as trusted domain" : "Tilføj \"%s\" som et troværdigt domæne", "App update required" : "Opdatering af app påkræves", "%s will be updated to version %s" : "%s vil blive opdateret til version %s", diff --git a/core/l10n/en_GB.js b/core/l10n/en_GB.js index 7797bd75244..e06d3005054 100644 --- a/core/l10n/en_GB.js +++ b/core/l10n/en_GB.js @@ -219,6 +219,8 @@ OC.L10N.register( "The specified document has not been found on the server." : "The specified document has not been found on the server.", "You can click here to return to %s." : "You can click here to return to %s.", "Internal Server Error" : "Internal Server Error", + "The server was unable to complete your request." : "The server was unable to complete your request.", + "If this happens again, please send the technical details below to the server administrator." : "If this happens again, please send the technical details below to the server administrator.", "More details can be found in the server log." : "More details can be found in the server log.", "Technical details" : "Technical details", "Remote Address: %s" : "Remote Address: %s", @@ -282,6 +284,9 @@ OC.L10N.register( "Cancel log in" : "Cancel log in", "Use backup code" : "Use backup code", "Error while validating your second factor" : "Error while validating your second factor", + "Access through untrusted domain" : "Access through untrusted domain", + "Please contact your administrator. If you are an administrator, edit the \"trusted_domains\" setting in config/config.php like the example in config.sample.php." : "Please contact your administrator. If you are an administrator, edit the \"trusted_domains\" setting in config/config.php like the example in config.sample.php.", + "Depending on your configuration, this button could also work to trust the domain:" : "Depending on your configuration, this button could also work to trust the domain:", "Add \"%s\" as trusted domain" : "Add \"%s\" as a trusted domain", "App update required" : "App update required", "%s will be updated to version %s" : "%s will be updated to version %s", diff --git a/core/l10n/en_GB.json b/core/l10n/en_GB.json index 2d49edb2ed7..e16bc07be03 100644 --- a/core/l10n/en_GB.json +++ b/core/l10n/en_GB.json @@ -217,6 +217,8 @@ "The specified document has not been found on the server." : "The specified document has not been found on the server.", "You can click here to return to %s." : "You can click here to return to %s.", "Internal Server Error" : "Internal Server Error", + "The server was unable to complete your request." : "The server was unable to complete your request.", + "If this happens again, please send the technical details below to the server administrator." : "If this happens again, please send the technical details below to the server administrator.", "More details can be found in the server log." : "More details can be found in the server log.", "Technical details" : "Technical details", "Remote Address: %s" : "Remote Address: %s", @@ -280,6 +282,9 @@ "Cancel log in" : "Cancel log in", "Use backup code" : "Use backup code", "Error while validating your second factor" : "Error while validating your second factor", + "Access through untrusted domain" : "Access through untrusted domain", + "Please contact your administrator. If you are an administrator, edit the \"trusted_domains\" setting in config/config.php like the example in config.sample.php." : "Please contact your administrator. If you are an administrator, edit the \"trusted_domains\" setting in config/config.php like the example in config.sample.php.", + "Depending on your configuration, this button could also work to trust the domain:" : "Depending on your configuration, this button could also work to trust the domain:", "Add \"%s\" as trusted domain" : "Add \"%s\" as a trusted domain", "App update required" : "App update required", "%s will be updated to version %s" : "%s will be updated to version %s", diff --git a/core/l10n/lt_LT.js b/core/l10n/lt_LT.js index 8acf6e60830..c54b1bf9163 100644 --- a/core/l10n/lt_LT.js +++ b/core/l10n/lt_LT.js @@ -219,6 +219,7 @@ OC.L10N.register( "The specified document has not been found on the server." : "Ieškotas dokumentas nerastas sistemoje.", "You can click here to return to %s." : "Paspauskite čia norėdami grįžti į %s.", "Internal Server Error" : "Vidinė serverio klaida", + "The server was unable to complete your request." : "Serveriui nepavyko įvykdyti jūsų užklausos.", "More details can be found in the server log." : "Detalesnė informacija yra sistemos žurnale.", "Technical details" : "Techniniai duomenys", "Remote Address: %s" : "Nuotolinis adresas: %s", @@ -269,6 +270,7 @@ OC.L10N.register( "Log in" : "Prisijungti", "Stay logged in" : "Likti prisijungus", "Alternative Logins" : "Alternatyvūs prisijungimai", + "Account access" : "Paskyros prieiga", "App token" : "Išorinės sistemos įskiepio kodas", "Alternative login using app token" : "Alternatyvus prisijungimas naudojant išorinės sistemos kodą", "Redirecting …" : "Nukreipiama...", diff --git a/core/l10n/lt_LT.json b/core/l10n/lt_LT.json index 0b627565c5d..90b3b243bf9 100644 --- a/core/l10n/lt_LT.json +++ b/core/l10n/lt_LT.json @@ -217,6 +217,7 @@ "The specified document has not been found on the server." : "Ieškotas dokumentas nerastas sistemoje.", "You can click here to return to %s." : "Paspauskite čia norėdami grįžti į %s.", "Internal Server Error" : "Vidinė serverio klaida", + "The server was unable to complete your request." : "Serveriui nepavyko įvykdyti jūsų užklausos.", "More details can be found in the server log." : "Detalesnė informacija yra sistemos žurnale.", "Technical details" : "Techniniai duomenys", "Remote Address: %s" : "Nuotolinis adresas: %s", @@ -267,6 +268,7 @@ "Log in" : "Prisijungti", "Stay logged in" : "Likti prisijungus", "Alternative Logins" : "Alternatyvūs prisijungimai", + "Account access" : "Paskyros prieiga", "App token" : "Išorinės sistemos įskiepio kodas", "Alternative login using app token" : "Alternatyvus prisijungimas naudojant išorinės sistemos kodą", "Redirecting …" : "Nukreipiama...", diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index e5fd3a88b0c..425eeb6ce7b 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -385,6 +385,7 @@ return array( 'OC\\Command\\CallableJob' => $baseDir . '/lib/private/Command/CallableJob.php', 'OC\\Command\\ClosureJob' => $baseDir . '/lib/private/Command/ClosureJob.php', 'OC\\Command\\CommandJob' => $baseDir . '/lib/private/Command/CommandJob.php', + 'OC\\Command\\CronBus' => $baseDir . '/lib/private/Command/CronBus.php', 'OC\\Command\\FileAccess' => $baseDir . '/lib/private/Command/FileAccess.php', 'OC\\Command\\QueueBus' => $baseDir . '/lib/private/Command/QueueBus.php', 'OC\\Comments\\Comment' => $baseDir . '/lib/private/Comments/Comment.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 652d6de99be..68262fafb0d 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -415,6 +415,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Command\\CallableJob' => __DIR__ . '/../../..' . '/lib/private/Command/CallableJob.php', 'OC\\Command\\ClosureJob' => __DIR__ . '/../../..' . '/lib/private/Command/ClosureJob.php', 'OC\\Command\\CommandJob' => __DIR__ . '/../../..' . '/lib/private/Command/CommandJob.php', + 'OC\\Command\\CronBus' => __DIR__ . '/../../..' . '/lib/private/Command/CronBus.php', 'OC\\Command\\FileAccess' => __DIR__ . '/../../..' . '/lib/private/Command/FileAccess.php', 'OC\\Command\\QueueBus' => __DIR__ . '/../../..' . '/lib/private/Command/QueueBus.php', 'OC\\Comments\\Comment' => __DIR__ . '/../../..' . '/lib/private/Comments/Comment.php', diff --git a/lib/l10n/en_GB.js b/lib/l10n/en_GB.js index b3041727416..6dfb50a219e 100644 --- a/lib/l10n/en_GB.js +++ b/lib/l10n/en_GB.js @@ -176,6 +176,7 @@ OC.L10N.register( "Username must not consist of dots only" : "Username must not consist of dots only", "A valid password must be provided" : "A valid password must be provided", "The username is already being used" : "The username is already being used", + "Could not create user" : "Could not create user", "User disabled" : "User disabled", "Login canceled by app" : "Login cancelled by app", "No app name specified" : "No app name specified", diff --git a/lib/l10n/en_GB.json b/lib/l10n/en_GB.json index 6db6480af0a..ed6e69c454f 100644 --- a/lib/l10n/en_GB.json +++ b/lib/l10n/en_GB.json @@ -174,6 +174,7 @@ "Username must not consist of dots only" : "Username must not consist of dots only", "A valid password must be provided" : "A valid password must be provided", "The username is already being used" : "The username is already being used", + "Could not create user" : "Could not create user", "User disabled" : "User disabled", "Login canceled by app" : "Login cancelled by app", "No app name specified" : "No app name specified", diff --git a/lib/private/Authentication/TwoFactorAuth/Manager.php b/lib/private/Authentication/TwoFactorAuth/Manager.php index 88ca4ba6084..fd0d5914d02 100644 --- a/lib/private/Authentication/TwoFactorAuth/Manager.php +++ b/lib/private/Authentication/TwoFactorAuth/Manager.php @@ -293,7 +293,6 @@ class Manager { return false; } } catch (InvalidTokenException $e) { - return true; } } diff --git a/lib/private/Command/AsyncBus.php b/lib/private/Command/AsyncBus.php index fb3cbee7240..2dffc9c784d 100644 --- a/lib/private/Command/AsyncBus.php +++ b/lib/private/Command/AsyncBus.php @@ -24,17 +24,11 @@ namespace OC\Command; use OCP\Command\IBus; use OCP\Command\ICommand; -use SuperClosure\Serializer; /** * Asynchronous command bus that uses the background job system as backend */ -class AsyncBus implements IBus { - /** - * @var \OCP\BackgroundJob\IJobList - */ - private $jobList; - +abstract class AsyncBus implements IBus { /** * List of traits for command which require sync execution * @@ -43,26 +37,26 @@ class AsyncBus implements IBus { private $syncTraits = []; /** - * @param \OCP\BackgroundJob\IJobList $jobList - */ - public function __construct($jobList) { - $this->jobList = $jobList; - } - - /** * Schedule a command to be fired * * @param \OCP\Command\ICommand | callable $command */ public function push($command) { if ($this->canRunAsync($command)) { - $this->jobList->add($this->getJobClass($command), $this->serializeCommand($command)); + $this->queueCommand($command); } else { $this->runCommand($command); } } /** + * Queue a command in the bus + * + * @param \OCP\Command\ICommand | callable $command + */ + abstract protected function queueCommand($command); + + /** * Require all commands using a trait to be run synchronous * * @param string $trait @@ -84,37 +78,6 @@ class AsyncBus implements IBus { /** * @param \OCP\Command\ICommand | callable $command - * @return string - */ - private function getJobClass($command) { - if ($command instanceof \Closure) { - return 'OC\Command\ClosureJob'; - } else if (is_callable($command)) { - return 'OC\Command\CallableJob'; - } else if ($command instanceof ICommand) { - return 'OC\Command\CommandJob'; - } else { - throw new \InvalidArgumentException('Invalid command'); - } - } - - /** - * @param \OCP\Command\ICommand | callable $command - * @return string - */ - private function serializeCommand($command) { - if ($command instanceof \Closure) { - $serializer = new Serializer(); - return $serializer->serialize($command); - } else if (is_callable($command) or $command instanceof ICommand) { - return serialize($command); - } else { - throw new \InvalidArgumentException('Invalid command'); - } - } - - /** - * @param \OCP\Command\ICommand | callable $command * @return bool */ private function canRunAsync($command) { diff --git a/lib/private/Command/CronBus.php b/lib/private/Command/CronBus.php new file mode 100644 index 00000000000..9bde4d88242 --- /dev/null +++ b/lib/private/Command/CronBus.php @@ -0,0 +1,75 @@ +<?php +/** + * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Command; + +use OCP\Command\ICommand; +use SuperClosure\Serializer; + +class CronBus extends AsyncBus { + /** + * @var \OCP\BackgroundJob\IJobList + */ + private $jobList; + + + /** + * @param \OCP\BackgroundJob\IJobList $jobList + */ + public function __construct($jobList) { + $this->jobList = $jobList; + } + + protected function queueCommand($command) { + $this->jobList->add($this->getJobClass($command), $this->serializeCommand($command)); + } + + /** + * @param \OCP\Command\ICommand | callable $command + * @return string + */ + private function getJobClass($command) { + if ($command instanceof \Closure) { + return 'OC\Command\ClosureJob'; + } else if (is_callable($command)) { + return 'OC\Command\CallableJob'; + } else if ($command instanceof ICommand) { + return 'OC\Command\CommandJob'; + } else { + throw new \InvalidArgumentException('Invalid command'); + } + } + + /** + * @param \OCP\Command\ICommand | callable $command + * @return string + */ + private function serializeCommand($command) { + if ($command instanceof \Closure) { + $serializer = new Serializer(); + return $serializer->serialize($command); + } else if (is_callable($command) or $command instanceof ICommand) { + return serialize($command); + } else { + throw new \InvalidArgumentException('Invalid command'); + } + } +} diff --git a/lib/private/Group/Backend.php b/lib/private/Group/Backend.php index 1e8d62f5e42..001d8d9da66 100644 --- a/lib/private/Group/Backend.php +++ b/lib/private/Group/Backend.php @@ -38,6 +38,7 @@ abstract class Backend implements \OCP\GroupInterface { self::REMOVE_FROM_GOUP => 'removeFromGroup', self::COUNT_USERS => 'countUsersInGroup', self::GROUP_DETAILS => 'getGroupDetails', + self::IS_ADMIN => 'isAdmin', ]; /** diff --git a/lib/private/Group/Manager.php b/lib/private/Group/Manager.php index 6d4f5a091c6..15d83380acf 100644 --- a/lib/private/Group/Manager.php +++ b/lib/private/Group/Manager.php @@ -288,6 +288,11 @@ class Manager extends PublicEmitter implements IGroupManager { * @return bool if admin */ public function isAdmin($userId) { + foreach ($this->backends as $backend) { + if ($backend->implementsActions(\OC\Group\Backend::IS_ADMIN) && $backend->isAdmin($userId)) { + return true; + } + } return $this->isInGroup($userId, 'admin'); } diff --git a/lib/private/Log.php b/lib/private/Log.php index 0d291218096..d93b29414e6 100644 --- a/lib/private/Log.php +++ b/lib/private/Log.php @@ -65,6 +65,7 @@ class Log implements ILogger { 'completeLogin', 'login', 'checkPassword', + 'checkPasswordNoLogging', 'loginWithPassword', 'updatePrivateKeyPassword', 'validateUserPass', @@ -82,14 +83,19 @@ class Log implements ILogger { 'solveChallenge', 'verifyChallenge', - //ICrypto + // ICrypto 'calculateHMAC', 'encrypt', 'decrypt', - //LoginController + // LoginController 'tryLogin', 'confirmPassword', + + // LDAP + 'bind', + 'areCredentialsValid', + 'invokeLDAPMethod', ]; /** @@ -97,7 +103,7 @@ class Log implements ILogger { * @param SystemConfig $config the system config object * @param null $normalizer */ - public function __construct($logger=null, SystemConfig $config=null, $normalizer = null) { + public function __construct($logger = null, SystemConfig $config = null, $normalizer = null) { // FIXME: Add this for backwards compatibility, should be fixed at some point probably if($config === null) { $config = \OC::$server->getSystemConfig(); diff --git a/lib/private/Mail/EMailTemplate.php b/lib/private/Mail/EMailTemplate.php index 32cb01f30b3..da3341b6709 100644 --- a/lib/private/Mail/EMailTemplate.php +++ b/lib/private/Mail/EMailTemplate.php @@ -342,24 +342,18 @@ EOF; * @param Defaults $themingDefaults * @param IURLGenerator $urlGenerator * @param IL10N $l10n + * @param string $emailId + * @param array $data */ public function __construct(Defaults $themingDefaults, IURLGenerator $urlGenerator, - IL10N $l10n) { + IL10N $l10n, + $emailId, + array $data) { $this->themingDefaults = $themingDefaults; $this->urlGenerator = $urlGenerator; $this->l10n = $l10n; $this->htmlBody .= $this->head; - } - - /** - * Set meta data of an email - * - * @param string $emailId - * @param array $data - * @since 12.0.3 - */ - public function setMetaData($emailId, array $data = []) { $this->emailId = $emailId; $this->data = $data; } diff --git a/lib/private/Mail/Mailer.php b/lib/private/Mail/Mailer.php index d232587df63..45405157d26 100644 --- a/lib/private/Mail/Mailer.php +++ b/lib/private/Mail/Mailer.php @@ -26,6 +26,7 @@ use OCP\Defaults; use OCP\IConfig; use OCP\IL10N; use OCP\IURLGenerator; +use OCP\Mail\IEMailTemplate; use OCP\Mail\IMailer; use OCP\ILogger; @@ -89,21 +90,33 @@ class Mailer implements IMailer { return new Message(new \Swift_Message()); } - public function createEMailTemplate() { + /** + * Creates a new email template object + * + * @param string $emailId + * @param array $data + * @return IEMailTemplate + * @since 12.0.0 + */ + public function createEMailTemplate($emailId, array $data = []) { $class = $this->config->getSystemValue('mail_template_class', ''); if ($class !== '' && class_exists($class) && is_a($class, EMailTemplate::class, true)) { return new $class( $this->defaults, $this->urlGenerator, - $this->l10n + $this->l10n, + $emailId, + $data ); } return new EMailTemplate( $this->defaults, $this->urlGenerator, - $this->l10n + $this->l10n, + $emailId, + $data ); } diff --git a/lib/private/Server.php b/lib/private/Server.php index 18f09eb30b7..3a4fac175e8 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -39,6 +39,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC; use bantu\IniGetWrapper\IniGetWrapper; @@ -51,7 +52,7 @@ use OC\AppFramework\Http\Request; use OC\AppFramework\Utility\SimpleContainer; use OC\AppFramework\Utility\TimeFactory; use OC\Authentication\LoginCredentials\Store; -use OC\Command\AsyncBus; +use OC\Command\CronBus; use OC\Contacts\ContactsMenu\ActionFactory; use OC\Diagnostics\EventLogger; use OC\Diagnostics\NullEventLogger; @@ -140,7 +141,7 @@ class Server extends ServerContainer implements IServerContainer { parent::__construct(); $this->webRoot = $webRoot; - $this->registerService(\OCP\IServerContainer::class, function(IServerContainer $c) { + $this->registerService(\OCP\IServerContainer::class, function (IServerContainer $c) { return $c; }); @@ -150,7 +151,6 @@ class Server extends ServerContainer implements IServerContainer { $this->registerAlias(IActionFactory::class, ActionFactory::class); - $this->registerService(\OCP\IPreview::class, function (Server $c) { return new PreviewManager( $c->getConfig(), @@ -257,8 +257,8 @@ class Server extends ServerContainer implements IServerContainer { }); $this->registerAlias('SystemTagObjectMapper', \OCP\SystemTag\ISystemTagObjectMapper::class); - $this->registerService(\OCP\Files\IRootFolder::class, function(Server $c) { - return new LazyRoot(function() use ($c) { + $this->registerService(\OCP\Files\IRootFolder::class, function (Server $c) { + return new LazyRoot(function () use ($c) { return $c->query('RootFolder'); }); }); @@ -296,7 +296,7 @@ class Server extends ServerContainer implements IServerContainer { }); $this->registerAlias('GroupManager', \OCP\IGroupManager::class); - $this->registerService(Store::class, function(Server $c) { + $this->registerService(Store::class, function (Server $c) { $session = $c->getSession(); if (\OC::$server->getSystemConfig()->getValue('installed', false)) { $tokenProvider = $c->query('OC\Authentication\Token\IProvider'); @@ -544,7 +544,7 @@ class Server extends ServerContainer implements IServerContainer { }); $this->registerAlias('Search', \OCP\ISearch::class); - $this->registerService(\OC\Security\RateLimiting\Limiter::class, function($c) { + $this->registerService(\OC\Security\RateLimiting\Limiter::class, function ($c) { return new \OC\Security\RateLimiting\Limiter( $this->getUserSession(), $this->getRequest(), @@ -552,7 +552,7 @@ class Server extends ServerContainer implements IServerContainer { $c->query(\OC\Security\RateLimiting\Backend\IBackend::class) ); }); - $this->registerService(\OC\Security\RateLimiting\Backend\IBackend::class, function($c) { + $this->registerService(\OC\Security\RateLimiting\Backend\IBackend::class, function ($c) { return new \OC\Security\RateLimiting\Backend\MemoryCache( $this->getMemCacheFactory(), new \OC\AppFramework\Utility\TimeFactory() @@ -686,7 +686,7 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService(\OCP\Files\Config\IMountProviderCollection::class, function (Server $c) { $loader = \OC\Files\Filesystem::getLoader(); $mountCache = $c->query('UserMountCache'); - $manager = new \OC\Files\Config\MountProviderCollection($loader, $mountCache); + $manager = new \OC\Files\Config\MountProviderCollection($loader, $mountCache); // builtin providers @@ -703,13 +703,24 @@ class Server extends ServerContainer implements IServerContainer { return new IniGetWrapper(); }); $this->registerService('AsyncCommandBus', function (Server $c) { - $jobList = $c->getJobList(); - return new AsyncBus($jobList); + $busClass = $c->getConfig()->getSystemValue('commandbus'); + if ($busClass) { + list($app, $class) = explode('::', $busClass, 2); + if ($c->getAppManager()->isInstalled($app)) { + \OC_App::loadApp($app); + return $c->query($class); + } else { + throw new ServiceUnavailableException("The app providing the command bus ($app) is not enabled"); + } + } else { + $jobList = $c->getJobList(); + return new CronBus($jobList); + } }); $this->registerService('TrustedDomainHelper', function ($c) { return new TrustedDomainHelper($this->getConfig()); }); - $this->registerService('Throttler', function(Server $c) { + $this->registerService('Throttler', function (Server $c) { return new Throttler( $c->getDatabaseConnection(), new TimeFactory(), @@ -720,7 +731,7 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService('IntegrityCodeChecker', function (Server $c) { // IConfig and IAppManager requires a working database. This code // might however be called when ownCloud is not yet setup. - if(\OC::$server->getSystemConfig()->getValue('installed', false)) { + if (\OC::$server->getSystemConfig()->getValue('installed', false)) { $config = $c->getConfig(); $appManager = $c->getAppManager(); } else { @@ -729,13 +740,13 @@ class Server extends ServerContainer implements IServerContainer { } return new Checker( - new EnvironmentHelper(), - new FileAccessHelper(), - new AppLocator(), - $config, - $c->getMemCacheFactory(), - $appManager, - $c->getTempManager() + new EnvironmentHelper(), + new FileAccessHelper(), + new AppLocator(), + $config, + $c->getMemCacheFactory(), + $appManager, + $c->getTempManager() ); }); $this->registerService(\OCP\IRequest::class, function ($c) { @@ -785,10 +796,10 @@ class Server extends ServerContainer implements IServerContainer { }); $this->registerAlias('Mailer', \OCP\Mail\IMailer::class); - $this->registerService('LDAPProvider', function(Server $c) { + $this->registerService('LDAPProvider', function (Server $c) { $config = $c->getConfig(); $factoryClass = $config->getSystemValue('ldapProviderFactory', null); - if(is_null($factoryClass)) { + if (is_null($factoryClass)) { throw new \Exception('ldapProviderFactory not set'); } /** @var \OCP\LDAP\ILDAPProviderFactory $factory */ @@ -854,7 +865,7 @@ class Server extends ServerContainer implements IServerContainer { }); $this->registerAlias('CapabilitiesManager', \OC\CapabilitiesManager::class); - $this->registerService(\OCP\Comments\ICommentsManager::class, function(Server $c) { + $this->registerService(\OCP\Comments\ICommentsManager::class, function (Server $c) { $config = $c->getConfig(); $factoryClass = $config->getSystemValue('comments.managerFactory', '\OC\Comments\ManagerFactory'); /** @var \OCP\Comments\ICommentsManagerFactory $factory */ @@ -863,7 +874,7 @@ class Server extends ServerContainer implements IServerContainer { }); $this->registerAlias('CommentsManager', \OCP\Comments\ICommentsManager::class); - $this->registerService('ThemingDefaults', function(Server $c) { + $this->registerService('ThemingDefaults', function (Server $c) { /* * Dark magic for autoloader. * If we do a class_exists it will try to load the class which will @@ -889,7 +900,7 @@ class Server extends ServerContainer implements IServerContainer { } return new \OC_Defaults(); }); - $this->registerService(SCSSCacher::class, function(Server $c) { + $this->registerService(SCSSCacher::class, function (Server $c) { /** @var Factory $cacheFactory */ $cacheFactory = $c->query(Factory::class); return new SCSSCacher( @@ -949,14 +960,14 @@ class Server extends ServerContainer implements IServerContainer { }); $this->registerAlias('ContentSecurityPolicyManager', \OCP\Security\IContentSecurityPolicyManager::class); - $this->registerService('ContentSecurityPolicyNonceManager', function(Server $c) { + $this->registerService('ContentSecurityPolicyNonceManager', function (Server $c) { return new ContentSecurityPolicyNonceManager( $c->getCsrfTokenManager(), $c->getRequest() ); }); - $this->registerService(\OCP\Share\IManager::class, function(Server $c) { + $this->registerService(\OCP\Share\IManager::class, function (Server $c) { $config = $c->getConfig(); $factoryClass = $config->getSystemValue('sharing.managerFactory', '\OC\Share20\ProviderFactory'); /** @var \OCP\Share\IProviderFactory $factory */ @@ -983,7 +994,7 @@ class Server extends ServerContainer implements IServerContainer { }); $this->registerAlias('ShareManager', \OCP\Share\IManager::class); - $this->registerService('SettingsManager', function(Server $c) { + $this->registerService('SettingsManager', function (Server $c) { $manager = new \OC\Settings\Manager( $c->getLogger(), $c->getDatabaseConnection(), @@ -1011,7 +1022,7 @@ class Server extends ServerContainer implements IServerContainer { }); $this->registerService('LockdownManager', function (Server $c) { - return new LockdownManager(function() use ($c) { + return new LockdownManager(function () use ($c) { return $c->getSession(); }); }); @@ -1047,11 +1058,11 @@ class Server extends ServerContainer implements IServerContainer { }); $this->registerAlias('Defaults', \OCP\Defaults::class); - $this->registerService(\OCP\ISession::class, function(SimpleContainer $c) { + $this->registerService(\OCP\ISession::class, function (SimpleContainer $c) { return $c->query(\OCP\IUserSession::class)->getSession(); }); - $this->registerService(IShareHelper::class, function(Server $c) { + $this->registerService(IShareHelper::class, function (Server $c) { return new ShareHelper( $c->query(\OCP\Share\IManager::class) ); diff --git a/lib/private/Setup/OCI.php b/lib/private/Setup/OCI.php index 3051987917c..9fd85b7247d 100644 --- a/lib/private/Setup/OCI.php +++ b/lib/private/Setup/OCI.php @@ -28,6 +28,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Setup; class OCI extends AbstractDatabase { @@ -46,201 +47,49 @@ class OCI extends AbstractDatabase { $this->dbHost = $config['dbhost']; $this->config->setValues([ - 'dbhost' => $this->dbHost, - 'dbtablespace' => $this->dbtablespace, + 'dbhost' => $this->dbHost, + 'dbtablespace' => $this->dbtablespace, ]); } public function validate($config) { $errors = array(); - if(empty($config['dbuser']) && empty($config['dbname'])) { + if (empty($config['dbuser']) && empty($config['dbname'])) { $errors[] = $this->trans->t("%s enter the database username and name.", array($this->dbprettyname)); - } else if(empty($config['dbuser'])) { + } else if (empty($config['dbuser'])) { $errors[] = $this->trans->t("%s enter the database username.", array($this->dbprettyname)); - } else if(empty($config['dbname'])) { + } else if (empty($config['dbname'])) { $errors[] = $this->trans->t("%s enter the database name.", array($this->dbprettyname)); } return $errors; } public function setupDatabase($username) { - $e_host = addslashes($this->dbHost); - // casting to int to avoid malicious input - $e_port = (int)$this->dbPort; - $e_dbname = addslashes($this->dbName); - //check if the database user has admin right - if ($e_host == '') { - $easy_connect_string = $e_dbname; // use dbname as easy connect name - } else { - $easy_connect_string = '//'.$e_host.(!empty($e_port) ? ":{$e_port}" : "").'/'.$e_dbname; - } - $this->logger->debug('connect string: ' . $easy_connect_string, ['app' => 'setup.oci']); - $connection = @oci_connect($this->dbUser, $this->dbPassword, $easy_connect_string); - if(!$connection) { + try { + $this->connect(); + } catch (\Exception $e) { $errorMessage = $this->getLastError(); if ($errorMessage) { throw new \OC\DatabaseSetupException($this->trans->t('Oracle connection could not be established'), - $errorMessage.' Check environment: ORACLE_HOME='.getenv('ORACLE_HOME') - .' ORACLE_SID='.getenv('ORACLE_SID') - .' LD_LIBRARY_PATH='.getenv('LD_LIBRARY_PATH') - .' NLS_LANG='.getenv('NLS_LANG') - .' tnsnames.ora is '.(is_readable(getenv('ORACLE_HOME').'/network/admin/tnsnames.ora')?'':'not ').'readable'); + $errorMessage . ' Check environment: ORACLE_HOME=' . getenv('ORACLE_HOME') + . ' ORACLE_SID=' . getenv('ORACLE_SID') + . ' LD_LIBRARY_PATH=' . getenv('LD_LIBRARY_PATH') + . ' NLS_LANG=' . getenv('NLS_LANG') + . ' tnsnames.ora is ' . (is_readable(getenv('ORACLE_HOME') . '/network/admin/tnsnames.ora') ? '' : 'not ') . 'readable'); } throw new \OC\DatabaseSetupException($this->trans->t('Oracle username and/or password not valid'), - 'Check environment: ORACLE_HOME='.getenv('ORACLE_HOME') - .' ORACLE_SID='.getenv('ORACLE_SID') - .' LD_LIBRARY_PATH='.getenv('LD_LIBRARY_PATH') - .' NLS_LANG='.getenv('NLS_LANG') - .' tnsnames.ora is '.(is_readable(getenv('ORACLE_HOME').'/network/admin/tnsnames.ora')?'':'not ').'readable'); - } - //check for roles creation rights in oracle - - $query='SELECT count(*) FROM user_role_privs, role_sys_privs' - ." WHERE user_role_privs.granted_role = role_sys_privs.role AND privilege = 'CREATE ROLE'"; - $stmt = oci_parse($connection, $query); - if (!$stmt) { - $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; - $this->logger->warning($entry, ['app' => 'setup.oci']); - } - $result = oci_execute($stmt); - if($result) { - $row = oci_fetch_row($stmt); - - if ($row[0] > 0) { - //use the admin login data for the new database user - - //add prefix to the oracle user name to prevent collisions - $this->dbUser='oc_'.$username; - //create a new password so we don't need to store the admin config in the config file - $this->dbPassword = \OC::$server->getSecureRandom()->generate(30, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS); - - //oracle passwords are treated as identifiers: - // must start with alphanumeric char - // needs to be shortened to 30 bytes, as the two " needed to escape the identifier count towards the identifier length. - $this->dbPassword=substr($this->dbPassword, 0, 30); - - $this->createDBUser($connection); - } + 'Check environment: ORACLE_HOME=' . getenv('ORACLE_HOME') + . ' ORACLE_SID=' . getenv('ORACLE_SID') + . ' LD_LIBRARY_PATH=' . getenv('LD_LIBRARY_PATH') + . ' NLS_LANG=' . getenv('NLS_LANG') + . ' tnsnames.ora is ' . (is_readable(getenv('ORACLE_HOME') . '/network/admin/tnsnames.ora') ? '' : 'not ') . 'readable'); } $this->config->setValues([ - 'dbuser' => $this->dbUser, - 'dbname' => $this->dbName, - 'dbpassword' => $this->dbPassword, + 'dbuser' => $this->dbUser, + 'dbname' => $this->dbName, + 'dbpassword' => $this->dbPassword, ]); - - //create the database not necessary, oracle implies user = schema - //$this->createDatabase($this->dbname, $this->dbuser, $connection); - - //FIXME check tablespace exists: select * from user_tablespaces - - // the connection to dbname=oracle is not needed anymore - oci_close($connection); - - // connect to the oracle database (schema=$this->dbuser) an check if the schema needs to be filled - $this->dbUser = $this->config->getValue('dbuser'); - //$this->dbname = \OC_Config::getValue('dbname'); - $this->dbPassword = $this->config->getValue('dbpassword'); - - $e_host = addslashes($this->dbHost); - $e_dbname = addslashes($this->dbName); - - if ($e_host == '') { - $easy_connect_string = $e_dbname; // use dbname as easy connect name - } else { - $easy_connect_string = '//' . $e_host . (!empty($e_port) ? ":{$e_port}" : "") . '/' . $e_dbname; - } - $connection = @oci_connect($this->dbUser, $this->dbPassword, $easy_connect_string); - if(!$connection) { - throw new \OC\DatabaseSetupException($this->trans->t('Oracle username and/or password not valid'), - $this->trans->t('You need to enter details of an existing account.')); - } - $query = "SELECT count(*) FROM user_tables WHERE table_name = :un"; - $stmt = oci_parse($connection, $query); - $un = $this->tablePrefix.'users'; - oci_bind_by_name($stmt, ':un', $un); - if (!$stmt) { - $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; - $this->logger->warning( $entry, ['app' => 'setup.oci']); - } - oci_execute($stmt); - } - - /** - * @param resource $connection - */ - private function createDBUser($connection) { - $name = $this->dbUser; - $password = $this->dbPassword; - $query = "SELECT * FROM all_users WHERE USERNAME = :un"; - $stmt = oci_parse($connection, $query); - if (!$stmt) { - $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; - $this->logger->warning($entry, ['app' => 'setup.oci']); - } - oci_bind_by_name($stmt, ':un', $name); - $result = oci_execute($stmt); - if(!$result) { - $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; - $this->logger->warning($entry, ['app' => 'setup.oci']); - } - - if(! oci_fetch_row($stmt)) { - //user does not exists let's create it :) - //password must start with alphabetic character in oracle - $query = 'CREATE USER '.$name.' IDENTIFIED BY "'.$password.'" DEFAULT TABLESPACE '.$this->dbtablespace; - $stmt = oci_parse($connection, $query); - if (!$stmt) { - $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; - $this->logger->warning($entry, ['app' => 'setup.oci']); - - } - //oci_bind_by_name($stmt, ':un', $name); - $result = oci_execute($stmt); - if(!$result) { - $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s", name: %s, password: %s', - array($query, $name, $password)) . '<br />'; - $this->logger->warning($entry, ['app' => 'setup.oci']); - - } - } else { // change password of the existing role - $query = "ALTER USER :un IDENTIFIED BY :pw"; - $stmt = oci_parse($connection, $query); - if (!$stmt) { - $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; - $this->logger->warning($entry, ['app' => 'setup.oci']); - } - oci_bind_by_name($stmt, ':un', $name); - oci_bind_by_name($stmt, ':pw', $password); - $result = oci_execute($stmt); - if(!$result) { - $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; - $this->logger->warning($entry, ['app' => 'setup.oci']); - } - } - // grant necessary roles - $query = 'GRANT CREATE SESSION, CREATE TABLE, CREATE SEQUENCE, CREATE TRIGGER, UNLIMITED TABLESPACE TO '.$name; - $stmt = oci_parse($connection, $query); - if (!$stmt) { - $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; - $this->logger->warning($entry, ['app' => 'setup.oci']); - } - $result = oci_execute($stmt); - if(!$result) { - $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s", name: %s, password: %s', - array($query, $name, $password)) . '<br />'; - $this->logger->warning($entry, ['app' => 'setup.oci']); - } } /** diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index 46e3bd75c5c..886a319b1e7 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -696,8 +696,7 @@ class Manager implements IManager { $message = $this->mailer->createMessage(); - $emailTemplate = $this->mailer->createEMailTemplate(); - $emailTemplate->setMetaData('files_sharing.RecipientNotification', [ + $emailTemplate = $this->mailer->createEMailTemplate('files_sharing.RecipientNotification', [ 'filename' => $filename, 'link' => $link, 'initiator' => $initiatorDisplayName, diff --git a/lib/private/Template/CSSResourceLocator.php b/lib/private/Template/CSSResourceLocator.php index 32dabb48c0d..1b4050e4ae0 100644 --- a/lib/private/Template/CSSResourceLocator.php +++ b/lib/private/Template/CSSResourceLocator.php @@ -117,38 +117,18 @@ class CSSResourceLocator extends ResourceLocator { parent::append($root, $file, $webRoot, $throw); } else { if (!$webRoot) { - $tmpRoot = realpath($root); - /* - * traverse the potential web roots upwards in the path - * - * example: - * - root: /srv/www/apps/myapp - * - available mappings: ['/srv/www'] - * - * First we check if a mapping for /srv/www/apps/myapp is available, - * then /srv/www/apps, /srv/www/apps, /srv/www, ... until we find a - * valid web root - */ - do { - if (isset($this->mapping[$tmpRoot])) { - $webRoot = $this->mapping[$tmpRoot]; - break; - } - - if ($tmpRoot === '/') { - $webRoot = ''; - $this->logger->error('ResourceLocator can not find a web root (root: {root}, file: {file}, webRoot: {webRoot}, throw: {throw})', [ - 'app' => 'lib', - 'root' => $root, - 'file' => $file, - 'webRoot' => $webRoot, - 'throw' => $throw ? 'true' : 'false' - ]); - break; - } - $tmpRoot = dirname($tmpRoot); - } while(true); - + $webRoot = $this->findWebRoot($root); + + if (!$webRoot) { + $webRoot = ''; + $this->logger->error('ResourceLocator can not find a web root (root: {root}, file: {file}, webRoot: {webRoot}, throw: {throw})', [ + 'app' => 'lib', + 'root' => $root, + 'file' => $file, + 'webRoot' => $webRoot, + 'throw' => $throw ? 'true' : 'false' + ]); + } } if ($throw && $tmpRoot === '/') { diff --git a/lib/private/Template/ResourceLocator.php b/lib/private/Template/ResourceLocator.php index f721906e12b..2127161f28c 100755 --- a/lib/private/Template/ResourceLocator.php +++ b/lib/private/Template/ResourceLocator.php @@ -107,6 +107,50 @@ abstract class ResourceLocator { } /** + * Attempt to find the webRoot + * + * traverse the potential web roots upwards in the path + * + * example: + * - root: /srv/www/apps/myapp + * - available mappings: ['/srv/www'] + * + * First we check if a mapping for /srv/www/apps/myapp is available, + * then /srv/www/apps, /srv/www/apps, /srv/www, ... until we find a + * valid web root + * + * @param string $root + * @return string|null The web root or null on failure + */ + protected function findWebRoot($root) { + $webRoot = null; + $tmpRoot = $root; + + while ($webRoot === null) { + if (isset($this->mapping[$tmpRoot])) { + $webRoot = $this->mapping[$tmpRoot]; + break; + } + + if ($tmpRoot === '/') { + break; + } + + $tmpRoot = dirname($tmpRoot); + } + + if (!$webRoot) { + $realpath = realpath($root); + + if ($realpath && ($realpath !== $root)) { + return $this->findWebRoot($realpath); + } + } + + return $webRoot; + } + + /** * append the $file resource at $root * * @param string $root path to check @@ -125,38 +169,18 @@ abstract class ResourceLocator { } if (!$webRoot) { - $tmpRoot = realpath($root); - /* - * traverse the potential web roots upwards in the path - * - * example: - * - root: /srv/www/apps/myapp - * - available mappings: ['/srv/www'] - * - * First we check if a mapping for /srv/www/apps/myapp is available, - * then /srv/www/apps, /srv/www/apps, /srv/www, ... until we find a - * valid web root - */ - do { - if (isset($this->mapping[$tmpRoot])) { - $webRoot = $this->mapping[$tmpRoot]; - break; - } - - if ($tmpRoot === '/') { - $webRoot = ''; - $this->logger->error('ResourceLocator can not find a web root (root: {root}, file: {file}, webRoot: {webRoot}, throw: {throw})', [ - 'app' => 'lib', - 'root' => $root, - 'file' => $file, - 'webRoot' => $webRoot, - 'throw' => $throw ? 'true' : 'false' - ]); - break; - } - $tmpRoot = dirname($tmpRoot); - } while(true); - + $webRoot = $this->findWebRoot($root); + + if (!$webRoot) { + $webRoot = ''; + $this->logger->error('ResourceLocator can not find a web root (root: {root}, file: {file}, webRoot: {webRoot}, throw: {throw})', [ + 'app' => 'lib', + 'root' => $root, + 'file' => $file, + 'webRoot' => $webRoot, + 'throw' => $throw ? 'true' : 'false' + ]); + } } $this->resources[] = array($root, $webRoot, $file); diff --git a/lib/public/GroupInterface.php b/lib/public/GroupInterface.php index 97837e50b16..f6ef237a333 100644 --- a/lib/public/GroupInterface.php +++ b/lib/public/GroupInterface.php @@ -51,6 +51,10 @@ interface GroupInterface { //OBSOLETE const GET_DISPLAYNAME = 0x00010000; const COUNT_USERS = 0x00100000; const GROUP_DETAILS = 0x01000000; + /** + * @since 13.0.0 + */ + const IS_ADMIN = 0x10000000; /** * Check if backend implements actions diff --git a/lib/public/Mail/IEMailTemplate.php b/lib/public/Mail/IEMailTemplate.php index 2ff4a486f75..6c71c0c0473 100644 --- a/lib/public/Mail/IEMailTemplate.php +++ b/lib/public/Mail/IEMailTemplate.php @@ -51,12 +51,6 @@ namespace OCP\Mail; * @since 12.0.0 */ interface IEMailTemplate { - /** - * Set meta data of an email - * - * @since 12.0.3 - */ - public function setMetaData($emailId, array $data = []); /** * Adds a header to the email diff --git a/lib/public/Mail/IMailer.php b/lib/public/Mail/IMailer.php index af16a8a239a..c283d346745 100644 --- a/lib/public/Mail/IMailer.php +++ b/lib/public/Mail/IMailer.php @@ -57,10 +57,12 @@ interface IMailer { /** * Creates a new email template object * + * @param string $emailId + * @param array $data * @return IEMailTemplate - * @since 12.0.0 + * @since 12.0.0 Parameters added in 12.0.3 */ - public function createEMailTemplate(); + public function createEMailTemplate($emailId, array $data = []); /** * Send the specified message. Also sets the from address to the value defined in config.php diff --git a/settings/Controller/MailSettingsController.php b/settings/Controller/MailSettingsController.php index 44cece1d658..974a95618ad 100644 --- a/settings/Controller/MailSettingsController.php +++ b/settings/Controller/MailSettingsController.php @@ -147,9 +147,7 @@ class MailSettingsController extends Controller { try { $displayName = $this->userSession->getUser()->getDisplayName(); - $template = $this->mailer->createEMailTemplate(); - - $template->setMetaData('settings.TestEmail', [ + $template = $this->mailer->createEMailTemplate('settings.TestEmail', [ 'displayname' => $displayName, ]); diff --git a/settings/Controller/UsersController.php b/settings/Controller/UsersController.php index cb191441c9a..a78c1b9466b 100644 --- a/settings/Controller/UsersController.php +++ b/settings/Controller/UsersController.php @@ -437,7 +437,9 @@ class UsersController extends Controller { ); } - $password = $this->secureRandom->generate(32); + $password = $this->secureRandom->generate(30); + // Make sure we pass the password_policy + $password .= $this->secureRandom->generate(2, '$!.,;:-~+*[]{}()'); $generatePasswordResetToken = true; } diff --git a/settings/Hooks.php b/settings/Hooks.php index f97145da0c0..6f537796517 100644 --- a/settings/Hooks.php +++ b/settings/Hooks.php @@ -117,8 +117,7 @@ class Hooks { $this->activityManager->publish($event); if ($user->getEMailAddress() !== null) { - $template = $this->mailer->createEMailTemplate(); - $template->setMetaData('settings.PasswordChanged', [ + $template = $this->mailer->createEMailTemplate('settings.PasswordChanged', [ 'displayname' => $user->getDisplayName(), 'emailAddress' => $user->getEMailAddress(), 'instanceUrl' => $instanceUrl, @@ -188,8 +187,7 @@ class Hooks { if ($oldMailAddress !== null) { - $template = $this->mailer->createEMailTemplate(); - $template->setMetaData('settings.EmailChanged', [ + $template = $this->mailer->createEMailTemplate('settings.EmailChanged', [ 'displayname' => $user->getDisplayName(), 'newEMailAddress' => $user->getEMailAddress(), 'oldEMailAddress' => $oldMailAddress, diff --git a/settings/Mailer/NewUserMailHelper.php b/settings/Mailer/NewUserMailHelper.php index c4c09653370..9fc6a4a3c70 100644 --- a/settings/Mailer/NewUserMailHelper.php +++ b/settings/Mailer/NewUserMailHelper.php @@ -21,7 +21,6 @@ namespace OC\Settings\Mailer; -use OC\Mail\EMailTemplate; use OCP\Mail\IEMailTemplate; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Defaults; @@ -96,7 +95,7 @@ class NewUserMailHelper { /** * @param IUser $user * @param bool $generatePasswordResetToken - * @return EMailTemplate + * @return IEMailTemplate */ public function generateTemplate(IUser $user, $generatePasswordResetToken = false) { if ($generatePasswordResetToken) { @@ -114,11 +113,18 @@ class NewUserMailHelper { } else { $link = $this->urlGenerator->getAbsoluteURL('/'); } - - $emailTemplate = $this->mailer->createEMailTemplate(); - $emailTemplate->addHeader(); $displayName = $user->getDisplayName(); $userId = $user->getUID(); + + $emailTemplate = $this->mailer->createEMailTemplate('settings.Welcome', [ + 'link' => $link, + 'displayname' => $displayName, + 'userid' => $userId, + 'instancename' => $this->themingDefaults->getName(), + 'resetTokenGenerated' => $generatePasswordResetToken, + ]); + + $emailTemplate->addHeader(); if ($displayName === $userId) { $emailTemplate->addHeading($this->l10n->t('Welcome aboard')); } else { @@ -139,14 +145,6 @@ class NewUserMailHelper { ); $emailTemplate->addFooter(); - $emailTemplate->setMetaData('settings.Welcome', [ - 'link' => $link, - 'displayname' => $displayName, - 'userid' => $userId, - 'instancename' => $this->themingDefaults->getName(), - 'resetTokenGenerated' => $generatePasswordResetToken, - ]); - return $emailTemplate; } diff --git a/settings/l10n/lt_LT.js b/settings/l10n/lt_LT.js index fd94a0a9d38..32c0af4116f 100644 --- a/settings/l10n/lt_LT.js +++ b/settings/l10n/lt_LT.js @@ -5,9 +5,15 @@ OC.L10N.register( "You changed your password" : "Jūs pakeitėte savo slaptažodį", "Your password was reset by an administrator" : "Administratorius atstatė jūsų slaptažodį", "{actor} changed your email address" : "{actor} pakeitė jūsų el. pašto adresą", + "You changed your email address" : "Jūs pakeitėte savo elektroninio pašto adresą", + "Your email address was changed by an administrator" : "Administratorius pakeitė jūsų elektroninio pašto adresą", "Security" : "Saugumas", + "You successfully logged in using two-factor authentication (%1$s)" : "Jūs sėkmingai prisijungėte, panaudodami dviejų faktorių tapatybės nustatymą (%1$s)", + "A login attempt using two-factor authentication failed (%1$s)" : "Nepavyko prisijungti, panaudojant dviejų faktorių tapatybės nustatymą (%1$s)", "Your <strong>password</strong> or <strong>email</strong> was modified" : "Jūsų <strong>slaptažodis</strong> ar <strong>el. paštas</strong> buvo pakeisti", "Your apps" : "Jūsų programėlės", + "Enabled apps" : "Įjungtos programėlės", + "Disabled apps" : "Išjungtos programėlės", "Wrong password" : "Neteisingas slaptažodis", "Saved" : "Įrašyta", "No user supplied" : "Nepateiktas naudotojas", @@ -15,22 +21,32 @@ OC.L10N.register( "Authentication error" : "Tapatybės nustatymo klaida", "Wrong admin recovery password. Please check the password and try again." : "Netinkamas administratoriaus atkūrimo slaptažodis. Prašome pasitikrinti ir bandyti vėl.", "A problem occurred, please check your log files (Error: %s)" : "Atsirado problema, prašome patikrinti savo žurnalo failus (Klaida: %s)", + "Migration Completed" : "Migracija baigta", "Group already exists." : "Grupė jau yra.", "Unable to add group." : "Nepavyko pridėti grupės.", "Unable to delete group." : "Nepavyko ištrinti grupės.", "Invalid SMTP password." : "Neteisingas SMTP slaptažodis.", + "Email setting test" : "El. pašto nustatymo testas", + "Email could not be sent. Check your mail server log" : "El. laiškas nebuvo išsiųstas. Peržiūrėkite savo pašto serverio žurnalą.", + "A problem occurred while sending the email. Please revise your settings. (Error: %s)" : "Įvyko klaida išsiunčiant laišką. Prašome peržiūrėkite savo nustatymus. (Klaida: %s)", + "You need to set your user email before being able to send test emails." : "Jūs turite nurodyti elektroninio pašto adresą, kad galėtumėte siųsti testinius el. laiškus.", "Invalid mail address" : "Neteisingas pašto adresas", + "No valid group selected" : "Pasirinkta neteisinga grupė", "A user with that name already exists." : "Toks naudotojas jau yra.", + "To send a password link to the user an email address is required." : "Norint persiųsti slaptažodžio nuorodą, būtinas el. pašto adresas.", "Unable to create user." : "Nepavyko sukurti naudotojo.", "Unable to delete user." : "Nepavyko ištrinti naudotojo.", "Error while enabling user." : "Klaida įjungiant naudotoją.", "Error while disabling user." : "Klaida išjungiant naudotoją.", "Settings saved" : "Nustatymai įrašyti", "Unable to change full name" : "Nepavyko pakeisti pilno vardo", + "Unable to change email address" : "Nepavyko pakeisti el. pašto adresą", "Your full name has been changed." : "Pilnas vardas pakeistas.", + "Forbidden" : "Uždrausta", "Invalid user" : "Neteisingas naudotojas", "Unable to change mail address" : "Nepavyko pakeisti el. pašto adresą", "Email saved" : "El. paštas įrašytas", + "%1$s changed your password on %2$s." : "%1$s pakeitė jūsų slaptažodį %2$s", "The new email address is %s" : "Naujasis el. pašto adresas yra %s", "Your username is: %s" : "Jūsų naudotojo vardas yra: %s", "Your %s account was created" : "Jūsų paskyra %s sukurta", diff --git a/settings/l10n/lt_LT.json b/settings/l10n/lt_LT.json index bb988e23139..7ac504f3f40 100644 --- a/settings/l10n/lt_LT.json +++ b/settings/l10n/lt_LT.json @@ -3,9 +3,15 @@ "You changed your password" : "Jūs pakeitėte savo slaptažodį", "Your password was reset by an administrator" : "Administratorius atstatė jūsų slaptažodį", "{actor} changed your email address" : "{actor} pakeitė jūsų el. pašto adresą", + "You changed your email address" : "Jūs pakeitėte savo elektroninio pašto adresą", + "Your email address was changed by an administrator" : "Administratorius pakeitė jūsų elektroninio pašto adresą", "Security" : "Saugumas", + "You successfully logged in using two-factor authentication (%1$s)" : "Jūs sėkmingai prisijungėte, panaudodami dviejų faktorių tapatybės nustatymą (%1$s)", + "A login attempt using two-factor authentication failed (%1$s)" : "Nepavyko prisijungti, panaudojant dviejų faktorių tapatybės nustatymą (%1$s)", "Your <strong>password</strong> or <strong>email</strong> was modified" : "Jūsų <strong>slaptažodis</strong> ar <strong>el. paštas</strong> buvo pakeisti", "Your apps" : "Jūsų programėlės", + "Enabled apps" : "Įjungtos programėlės", + "Disabled apps" : "Išjungtos programėlės", "Wrong password" : "Neteisingas slaptažodis", "Saved" : "Įrašyta", "No user supplied" : "Nepateiktas naudotojas", @@ -13,22 +19,32 @@ "Authentication error" : "Tapatybės nustatymo klaida", "Wrong admin recovery password. Please check the password and try again." : "Netinkamas administratoriaus atkūrimo slaptažodis. Prašome pasitikrinti ir bandyti vėl.", "A problem occurred, please check your log files (Error: %s)" : "Atsirado problema, prašome patikrinti savo žurnalo failus (Klaida: %s)", + "Migration Completed" : "Migracija baigta", "Group already exists." : "Grupė jau yra.", "Unable to add group." : "Nepavyko pridėti grupės.", "Unable to delete group." : "Nepavyko ištrinti grupės.", "Invalid SMTP password." : "Neteisingas SMTP slaptažodis.", + "Email setting test" : "El. pašto nustatymo testas", + "Email could not be sent. Check your mail server log" : "El. laiškas nebuvo išsiųstas. Peržiūrėkite savo pašto serverio žurnalą.", + "A problem occurred while sending the email. Please revise your settings. (Error: %s)" : "Įvyko klaida išsiunčiant laišką. Prašome peržiūrėkite savo nustatymus. (Klaida: %s)", + "You need to set your user email before being able to send test emails." : "Jūs turite nurodyti elektroninio pašto adresą, kad galėtumėte siųsti testinius el. laiškus.", "Invalid mail address" : "Neteisingas pašto adresas", + "No valid group selected" : "Pasirinkta neteisinga grupė", "A user with that name already exists." : "Toks naudotojas jau yra.", + "To send a password link to the user an email address is required." : "Norint persiųsti slaptažodžio nuorodą, būtinas el. pašto adresas.", "Unable to create user." : "Nepavyko sukurti naudotojo.", "Unable to delete user." : "Nepavyko ištrinti naudotojo.", "Error while enabling user." : "Klaida įjungiant naudotoją.", "Error while disabling user." : "Klaida išjungiant naudotoją.", "Settings saved" : "Nustatymai įrašyti", "Unable to change full name" : "Nepavyko pakeisti pilno vardo", + "Unable to change email address" : "Nepavyko pakeisti el. pašto adresą", "Your full name has been changed." : "Pilnas vardas pakeistas.", + "Forbidden" : "Uždrausta", "Invalid user" : "Neteisingas naudotojas", "Unable to change mail address" : "Nepavyko pakeisti el. pašto adresą", "Email saved" : "El. paštas įrašytas", + "%1$s changed your password on %2$s." : "%1$s pakeitė jūsų slaptažodį %2$s", "The new email address is %s" : "Naujasis el. pašto adresas yra %s", "Your username is: %s" : "Jūsų naudotojo vardas yra: %s", "Your %s account was created" : "Jūsų paskyra %s sukurta", diff --git a/settings/l10n/zh_CN.js b/settings/l10n/zh_CN.js index 9c5f0b87db7..7547ce7d4a1 100644 --- a/settings/l10n/zh_CN.js +++ b/settings/l10n/zh_CN.js @@ -249,6 +249,7 @@ OC.L10N.register( "You need to migrate your encryption keys from the old encryption (ownCloud <= 8.0) to the new one." : "您需要从旧版本 (ownCloud<=8.0) 迁移您的加密密钥.", "Start migration" : "开始迁移", "Security & setup warnings" : "安全及设置警告", + "It's important for the security and performance of your instance that everything is configured correctly. To help you with that we are doing some automatic checks. Please see the Tips & Tricks section and the documentation for more information." : "你的每项配置对于实例的安全性和性能都至关重要。 为了帮助您,我们正在做一些自动检查。 有关详细信息,请参阅提示与技巧部分和文档。", "PHP does not seem to be setup properly to query system environment variables. The test with getenv(\"PATH\") only returns an empty response." : "PHP 的设置似乎有问题, 无法获取系统环境变量. 使用 getenv(\\\"PATH\\\") 测试时仅返回空结果.", "Please check the <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">installation documentation ↗</a> for PHP configuration notes and the PHP configuration of your server, especially when using php-fpm." : "请检查 <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">安装文档 ↗</a> 中关于 PHP 配置的说明并在您的服务器中进行配置, 尤其是使用 php-fpm 时.", "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." : "已启用只读配置. 这将阻止在 Web 界面中进行设置. 此外, 每次更新后该文件需要手动设置为可写入.", diff --git a/settings/l10n/zh_CN.json b/settings/l10n/zh_CN.json index a430f0c89ad..e43c4fe308e 100644 --- a/settings/l10n/zh_CN.json +++ b/settings/l10n/zh_CN.json @@ -247,6 +247,7 @@ "You need to migrate your encryption keys from the old encryption (ownCloud <= 8.0) to the new one." : "您需要从旧版本 (ownCloud<=8.0) 迁移您的加密密钥.", "Start migration" : "开始迁移", "Security & setup warnings" : "安全及设置警告", + "It's important for the security and performance of your instance that everything is configured correctly. To help you with that we are doing some automatic checks. Please see the Tips & Tricks section and the documentation for more information." : "你的每项配置对于实例的安全性和性能都至关重要。 为了帮助您,我们正在做一些自动检查。 有关详细信息,请参阅提示与技巧部分和文档。", "PHP does not seem to be setup properly to query system environment variables. The test with getenv(\"PATH\") only returns an empty response." : "PHP 的设置似乎有问题, 无法获取系统环境变量. 使用 getenv(\\\"PATH\\\") 测试时仅返回空结果.", "Please check the <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">installation documentation ↗</a> for PHP configuration notes and the PHP configuration of your server, especially when using php-fpm." : "请检查 <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">安装文档 ↗</a> 中关于 PHP 配置的说明并在您的服务器中进行配置, 尤其是使用 php-fpm 时.", "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." : "已启用只读配置. 这将阻止在 Web 界面中进行设置. 此外, 每次更新后该文件需要手动设置为可写入.", diff --git a/settings/l10n/zh_TW.js b/settings/l10n/zh_TW.js index 097f73abfdc..50056b924c3 100644 --- a/settings/l10n/zh_TW.js +++ b/settings/l10n/zh_TW.js @@ -20,7 +20,9 @@ OC.L10N.register( "No user supplied" : "未提供使用者", "Unable to change password" : "無法修改密碼", "Authentication error" : "認證錯誤", + "Please provide an admin recovery password; otherwise, all user data will be lost." : "請提供一個admin管理者恢復密碼,否則將會失去所有使用者資料。", "Wrong admin recovery password. Please check the password and try again." : "錯誤的管理者還原密碼", + "Backend doesn't support password change, but the user's encryption key was updated." : "後端不支援變更密碼,但使用者的加密金鑰已經更新。", "installing and updating apps via the app store or Federated Cloud Sharing" : "透過應用程式中心或是聯盟式雲端分享來安裝、更新應用程式", "Federated Cloud Sharing" : "聯盟式雲端分享", "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL 使用的 %s 版本已經過期 (%s),請您更新您的作業系統,否則功能如 %s 可能無法正常運作", @@ -39,10 +41,13 @@ OC.L10N.register( "Invalid mail address" : "無效的 email 地址", "No valid group selected" : "無效的群組", "A user with that name already exists." : "同名的使用者已經存在", + "To send a password link to the user an email address is required." : "向使用者送出密碼連結需要提供一組E-mail信箱。", "Unable to create user." : "無法建立使用者", "Unable to delete user." : "無法移除使用者", "Error while enabling user." : "啟用用戶時發生錯誤", "Error while disabling user." : "停用用戶時發生錯誤", + "In order to verify your Twitter account, post the following tweet on Twitter (please make sure to post it without any line breaks):" : "為了驗證您的推特帳號,請在推特上發佈下列推文(請確認推文內容沒有斷行):", + "In order to verify your Website, store the following content in your web-root at '.well-known/CloudIdVerificationCode.txt' (please make sure that the complete text is in one line):" : "為了驗證您的網站,請將下列內容存至您網站,路徑為:well-known/CloudIdVerificationCode.txt\n(請確保全文以一行的格式儲存)", "Settings saved" : "設定已存檔", "Unable to change full name" : "無法變更全名", "Unable to change email address" : "無法變更email地址", @@ -51,6 +56,7 @@ OC.L10N.register( "Invalid user" : "無效的使用者", "Unable to change mail address" : "無法更改 email 地址", "Email saved" : "Email 已儲存", + "%1$s changed your password on %2$s." : "%1$s在%2$s時更改了您的密碼", "Your password on %s was changed." : "你的密碼在 %s 已變更。", "Your password on %s was reset by an administrator." : "您的密碼在 %s 已被管理員重設。", "Password changed for %s" : "%s 的密碼已變更。", @@ -61,6 +67,10 @@ OC.L10N.register( "Your email address on %s was changed by an administrator." : "你的email地址在 %s 已被管理員變更。", "Email address changed for %s" : "%s 的email地址已變更。", "The new email address is %s" : "新的email地址為 %s", + "Email address for %1$s changed on %2$s" : "%1$s的Email信箱在%2$s已經變更。", + "Welcome aboard" : "歡迎加入", + "Welcome aboard %s" : "%s,歡迎您加入", + "You have now an %s account, you can add, protect, and share your data." : "您現在已經是%s帳戶,你可以新增,維護且分享您的檔案了。", "Your username is: %s" : "你的使用者名稱為: %s", "Set your password" : "設定您的密碼", "Go to %s" : "前往 %s", @@ -82,6 +92,7 @@ OC.L10N.register( "_You have %n app update pending_::_You have %n app updates pending_" : ["%n 個應用程式尚未更新"], "No apps found for your version" : "沒有找到適合您的版本的應用程式", "The app will be downloaded from the app store" : "將會從應用程式商店下載這個應用程式", + "Official apps are developed by and within the community. They offer central functionality and are ready for production use." : "官方應用程序是由社區內部和內部開發的。 它們提供核心功能,並可在正式成品使用。", "Approved apps are developed by trusted developers and have passed a cursory security check. They are actively maintained in an open code repository and their maintainers deem them to be stable for casual to normal use." : "審查通過的應用程式經由可信任的開發人員所設計,並且經過一連串的安全測試,他們在開放的程式庫中維護這些應用程式,而且確保這些應用程式能穩定運作", "This app is not checked for security issues and is new or known to be unstable. Install at your own risk." : "這個新應用程式並沒有經過安全檢測,可能會是不穩定的,如果您要安裝的話,風險自行負責。", "Disabling app …" : "停用應用程式中 ...", diff --git a/settings/l10n/zh_TW.json b/settings/l10n/zh_TW.json index 4f6d723d5b0..098f2f05297 100644 --- a/settings/l10n/zh_TW.json +++ b/settings/l10n/zh_TW.json @@ -18,7 +18,9 @@ "No user supplied" : "未提供使用者", "Unable to change password" : "無法修改密碼", "Authentication error" : "認證錯誤", + "Please provide an admin recovery password; otherwise, all user data will be lost." : "請提供一個admin管理者恢復密碼,否則將會失去所有使用者資料。", "Wrong admin recovery password. Please check the password and try again." : "錯誤的管理者還原密碼", + "Backend doesn't support password change, but the user's encryption key was updated." : "後端不支援變更密碼,但使用者的加密金鑰已經更新。", "installing and updating apps via the app store or Federated Cloud Sharing" : "透過應用程式中心或是聯盟式雲端分享來安裝、更新應用程式", "Federated Cloud Sharing" : "聯盟式雲端分享", "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL 使用的 %s 版本已經過期 (%s),請您更新您的作業系統,否則功能如 %s 可能無法正常運作", @@ -37,10 +39,13 @@ "Invalid mail address" : "無效的 email 地址", "No valid group selected" : "無效的群組", "A user with that name already exists." : "同名的使用者已經存在", + "To send a password link to the user an email address is required." : "向使用者送出密碼連結需要提供一組E-mail信箱。", "Unable to create user." : "無法建立使用者", "Unable to delete user." : "無法移除使用者", "Error while enabling user." : "啟用用戶時發生錯誤", "Error while disabling user." : "停用用戶時發生錯誤", + "In order to verify your Twitter account, post the following tweet on Twitter (please make sure to post it without any line breaks):" : "為了驗證您的推特帳號,請在推特上發佈下列推文(請確認推文內容沒有斷行):", + "In order to verify your Website, store the following content in your web-root at '.well-known/CloudIdVerificationCode.txt' (please make sure that the complete text is in one line):" : "為了驗證您的網站,請將下列內容存至您網站,路徑為:well-known/CloudIdVerificationCode.txt\n(請確保全文以一行的格式儲存)", "Settings saved" : "設定已存檔", "Unable to change full name" : "無法變更全名", "Unable to change email address" : "無法變更email地址", @@ -49,6 +54,7 @@ "Invalid user" : "無效的使用者", "Unable to change mail address" : "無法更改 email 地址", "Email saved" : "Email 已儲存", + "%1$s changed your password on %2$s." : "%1$s在%2$s時更改了您的密碼", "Your password on %s was changed." : "你的密碼在 %s 已變更。", "Your password on %s was reset by an administrator." : "您的密碼在 %s 已被管理員重設。", "Password changed for %s" : "%s 的密碼已變更。", @@ -59,6 +65,10 @@ "Your email address on %s was changed by an administrator." : "你的email地址在 %s 已被管理員變更。", "Email address changed for %s" : "%s 的email地址已變更。", "The new email address is %s" : "新的email地址為 %s", + "Email address for %1$s changed on %2$s" : "%1$s的Email信箱在%2$s已經變更。", + "Welcome aboard" : "歡迎加入", + "Welcome aboard %s" : "%s,歡迎您加入", + "You have now an %s account, you can add, protect, and share your data." : "您現在已經是%s帳戶,你可以新增,維護且分享您的檔案了。", "Your username is: %s" : "你的使用者名稱為: %s", "Set your password" : "設定您的密碼", "Go to %s" : "前往 %s", @@ -80,6 +90,7 @@ "_You have %n app update pending_::_You have %n app updates pending_" : ["%n 個應用程式尚未更新"], "No apps found for your version" : "沒有找到適合您的版本的應用程式", "The app will be downloaded from the app store" : "將會從應用程式商店下載這個應用程式", + "Official apps are developed by and within the community. They offer central functionality and are ready for production use." : "官方應用程序是由社區內部和內部開發的。 它們提供核心功能,並可在正式成品使用。", "Approved apps are developed by trusted developers and have passed a cursory security check. They are actively maintained in an open code repository and their maintainers deem them to be stable for casual to normal use." : "審查通過的應用程式經由可信任的開發人員所設計,並且經過一連串的安全測試,他們在開放的程式庫中維護這些應用程式,而且確保這些應用程式能穩定運作", "This app is not checked for security issues and is new or known to be unstable. Install at your own risk." : "這個新應用程式並沒有經過安全檢測,可能會是不穩定的,如果您要安裝的話,風險自行負責。", "Disabling app …" : "停用應用程式中 ...", diff --git a/settings/templates/settings/admin/server.development.notice.php b/settings/templates/settings/admin/server.development.notice.php index f58258fc0ae..d1af82bc9a9 100644 --- a/settings/templates/settings/admin/server.development.notice.php +++ b/settings/templates/settings/admin/server.development.notice.php @@ -1,3 +1,3 @@ <div class="section"> - <p><?php include(__DIR__ . '/../settings.development.notice.php'); ?></p> + <p><?php include(__DIR__ . '/../../settings.development.notice.php'); ?></p> </div> diff --git a/tests/Core/Controller/ClientFlowLoginControllerTest.php b/tests/Core/Controller/ClientFlowLoginControllerTest.php index 1132c0f540c..89253f13038 100644 --- a/tests/Core/Controller/ClientFlowLoginControllerTest.php +++ b/tests/Core/Controller/ClientFlowLoginControllerTest.php @@ -149,6 +149,11 @@ class ClientFlowLoginControllerTest extends TestCase { ->expects($this->once()) ->method('set') ->with('client.flow.state.token', 'StateToken'); + $this->session + ->expects($this->once()) + ->method('get') + ->with('oauth.state') + ->willReturn('OauthStateToken'); $this->defaults ->expects($this->once()) ->method('getName') @@ -168,6 +173,7 @@ class ClientFlowLoginControllerTest extends TestCase { 'urlGenerator' => $this->urlGenerator, 'stateToken' => 'StateToken', 'serverHost' => 'example.com', + 'oauthState' => 'OauthStateToken', ], 'guest' ); @@ -199,6 +205,11 @@ class ClientFlowLoginControllerTest extends TestCase { ->expects($this->once()) ->method('set') ->with('client.flow.state.token', 'StateToken'); + $this->session + ->expects($this->once()) + ->method('get') + ->with('oauth.state') + ->willReturn('OauthStateToken'); $this->defaults ->expects($this->once()) ->method('getName') @@ -218,6 +229,7 @@ class ClientFlowLoginControllerTest extends TestCase { 'urlGenerator' => $this->urlGenerator, 'stateToken' => 'StateToken', 'serverHost' => 'example.com', + 'oauthState' => 'OauthStateToken', ], 'guest' ); diff --git a/tests/Settings/Mailer/NewUserMailHelperTest.php b/tests/Settings/Mailer/NewUserMailHelperTest.php index 40ba9ee1c21..033847e49f6 100644 --- a/tests/Settings/Mailer/NewUserMailHelperTest.php +++ b/tests/Settings/Mailer/NewUserMailHelperTest.php @@ -66,7 +66,9 @@ class NewUserMailHelperTest extends TestCase { $template = new EMailTemplate( $this->defaults, $this->urlGenerator, - $this->l10n + $this->l10n, + 'test.TestTemplate', + [] ); $this->mailer->method('createEMailTemplate') ->will($this->returnValue($template)); diff --git a/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php b/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php index d623edbd9d9..4fa3b3d7e14 100644 --- a/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php +++ b/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php @@ -566,6 +566,8 @@ class ManagerTest extends TestCase { } public function testNeedsSecondFactorInvalidToken() { + $this->prepareNoProviders(); + $user = $this->createMock(IUser::class); $user->method('getUID') ->willReturn('user'); @@ -579,6 +581,8 @@ class ManagerTest extends TestCase { ->with('mysessionid') ->willThrowException(new OC\Authentication\Exceptions\InvalidTokenException()); - $this->assertTrue($this->manager->needsSecondFactor($user)); + $this->config->method('getUserKeys')->willReturn([]); + + $this->assertFalse($this->manager->needsSecondFactor($user)); } } diff --git a/tests/lib/Command/AsyncBusTest.php b/tests/lib/Command/AsyncBusTest.php index 8c1713f1260..da168d66e6d 100644 --- a/tests/lib/Command/AsyncBusTest.php +++ b/tests/lib/Command/AsyncBusTest.php @@ -12,7 +12,6 @@ namespace Test\Command; use OC\Command\FileAccess; use OCP\Command\IBus; use OCP\Command\ICommand; -use Test\BackgroundJob\DummyJobList; use Test\TestCase; class SimpleCommand implements ICommand { @@ -58,7 +57,7 @@ class ThisClosureTest { } } -class AsyncBusTest extends TestCase { +abstract class AsyncBusTest extends TestCase { /** * Basic way to check output from a command * @@ -67,11 +66,6 @@ class AsyncBusTest extends TestCase { public static $lastCommand; /** - * @var \OCP\BackgroundJob\IJobList - */ - private $jobList; - - /** * @var \OCP\Command\IBus */ private $bus; @@ -80,47 +74,60 @@ class AsyncBusTest extends TestCase { self::$lastCommand = 'static'; } + /** + * @return IBus + */ + protected function getBus() { + if (!$this->bus instanceof IBus) { + $this->bus = $this->createBus(); + } + return $this->bus; + } + + /** + * @return IBus + */ + abstract protected function createBus(); + public function setUp() { - $this->jobList = new DummyJobList(); - $this->bus = new \OC\Command\AsyncBus($this->jobList); self::$lastCommand = ''; } public function testSimpleCommand() { $command = new SimpleCommand(); - $this->bus->push($command); + $this->getBus()->push($command); $this->runJobs(); $this->assertEquals('SimpleCommand', self::$lastCommand); } public function testStateFullCommand() { $command = new StateFullCommand('foo'); - $this->bus->push($command); + $this->getBus()->push($command); $this->runJobs(); $this->assertEquals('foo', self::$lastCommand); } public function testStaticCallable() { - $this->bus->push(['\Test\Command\AsyncBusTest', 'DummyCommand']); + $this->getBus()->push(['\Test\Command\AsyncBusTest', 'DummyCommand']); $this->runJobs(); $this->assertEquals('static', self::$lastCommand); } public function testMemberCallable() { $command = new StateFullCommand('bar'); - $this->bus->push([$command, 'handle']); + $this->getBus()->push([$command, 'handle']); $this->runJobs(); $this->assertEquals('bar', self::$lastCommand); } public function testFunctionCallable() { - $this->bus->push('\Test\Command\BasicFunction'); + $this->getBus()->push('\Test\Command\BasicFunction'); $this->runJobs(); $this->assertEquals('function', self::$lastCommand); } public function testClosure() { - $this->bus->push(function () { + $this->getBus()->push(function () { AsyncBusTest::$lastCommand = 'closure'; }); $this->runJobs(); @@ -128,7 +135,7 @@ class AsyncBusTest extends TestCase { } public function testClosureSelf() { - $this->bus->push(function () { + $this->getBus()->push(function () { self::$lastCommand = 'closure-self'; }); $this->runJobs(); @@ -139,14 +146,14 @@ class AsyncBusTest extends TestCase { public function testClosureThis() { // clean class to prevent phpunit putting closure in $this $test = new ThisClosureTest(); - $test->test($this->bus); + $test->test($this->getBus()); $this->runJobs(); $this->assertEquals('closure-this', self::$lastCommand); } public function testClosureBind() { $state = 'bar'; - $this->bus->push(function () use ($state) { + $this->getBus()->push(function () use ($state) { self::$lastCommand = 'closure-' . $state; }); $this->runJobs(); @@ -154,15 +161,15 @@ class AsyncBusTest extends TestCase { } public function testFileFileAccessCommand() { - $this->bus->push(new FilesystemCommand()); + $this->getBus()->push(new FilesystemCommand()); $this->assertEquals('', self::$lastCommand); $this->runJobs(); $this->assertEquals('FileAccess', self::$lastCommand); } public function testFileFileAccessCommandSync() { - $this->bus->requireSync('\OC\Command\FileAccess'); - $this->bus->push(new FilesystemCommand()); + $this->getBus()->requireSync('\OC\Command\FileAccess'); + $this->getBus()->push(new FilesystemCommand()); $this->assertEquals('FileAccess', self::$lastCommand); self::$lastCommand = ''; $this->runJobs(); @@ -170,10 +177,5 @@ class AsyncBusTest extends TestCase { } - private function runJobs() { - $jobs = $this->jobList->getAll(); - foreach ($jobs as $job) { - $job->execute($this->jobList); - } - } + abstract protected function runJobs(); } diff --git a/tests/lib/Command/CronBusTest.php b/tests/lib/Command/CronBusTest.php new file mode 100644 index 00000000000..9bf53ba92d2 --- /dev/null +++ b/tests/lib/Command/CronBusTest.php @@ -0,0 +1,50 @@ +<?php +/** + * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace Test\Command; + +use OC\Command\CronBus; +use Test\BackgroundJob\DummyJobList; + +class CronBusTest extends AsyncBusTest { + /** + * @var \OCP\BackgroundJob\IJobList + */ + private $jobList; + + + public function setUp() { + parent::setUp(); + + $this->jobList = new DummyJobList(); + } + + protected function createBus() { + return new CronBus($this->jobList); + } + + protected function runJobs() { + $jobs = $this->jobList->getAll(); + foreach ($jobs as $job) { + $job->execute($this->jobList); + } + } +} diff --git a/tests/lib/Mail/EMailTemplateTest.php b/tests/lib/Mail/EMailTemplateTest.php index 08af56453ab..b2d08f7e81b 100644 --- a/tests/lib/Mail/EMailTemplateTest.php +++ b/tests/lib/Mail/EMailTemplateTest.php @@ -49,7 +49,9 @@ class EMailTemplateTest extends TestCase { $this->emailTemplate = new EMailTemplate( $this->defaults, $this->urlGenerator, - $this->l10n + $this->l10n, + 'test.TestTemplate', + [] ); } diff --git a/tests/lib/Mail/MailerTest.php b/tests/lib/Mail/MailerTest.php index a7226b9ecd6..f647b966f1f 100644 --- a/tests/lib/Mail/MailerTest.php +++ b/tests/lib/Mail/MailerTest.php @@ -133,6 +133,6 @@ class MailerTest extends TestCase { } public function testCreateEMailTemplate() { - $this->assertSame(EMailTemplate::class, get_class($this->mailer->createEMailTemplate())); + $this->assertSame(EMailTemplate::class, get_class($this->mailer->createEMailTemplate('tests.MailerTest'))); } } diff --git a/tests/lib/TestCase.php b/tests/lib/TestCase.php index c3aa2c73c9e..818b3454c3a 100644 --- a/tests/lib/TestCase.php +++ b/tests/lib/TestCase.php @@ -190,7 +190,9 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase { self::assertEquals([], $errors, "There have been xml parsing errors"); } - \OC\Files\Cache\Storage::getGlobalCache()->clearCache(); + if ($this->IsDatabaseAccessAllowed()) { + \OC\Files\Cache\Storage::getGlobalCache()->clearCache(); + } // tearDown the traits $traits = $this->getTestTraits(); |