diff options
25 files changed, 682 insertions, 72 deletions
diff --git a/apps/dav/lib/comments/commentnode.php b/apps/dav/lib/comments/commentnode.php index d3cd53bceb1..339abc6382d 100644 --- a/apps/dav/lib/comments/commentnode.php +++ b/apps/dav/lib/comments/commentnode.php @@ -24,9 +24,11 @@ namespace OCA\DAV\Comments; use OCP\Comments\IComment; use OCP\Comments\ICommentsManager; +use OCP\Comments\MessageTooLongException; use OCP\ILogger; use OCP\IUserManager; use OCP\IUserSession; +use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\MethodNotAllowed; use Sabre\DAV\PropPatch; @@ -168,6 +170,7 @@ class CommentNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties { * * @param $propertyValue * @return bool + * @throws BadRequest * @throws Forbidden */ public function updateComment($propertyValue) { @@ -178,6 +181,10 @@ class CommentNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties { return true; } catch (\Exception $e) { $this->logger->logException($e, ['app' => 'dav/comments']); + if($e instanceof MessageTooLongException) { + $msg = 'Message exceeds allowed character limit of '; + throw new BadRequest($msg . IComment::MAX_MESSAGE_LENGTH, 0, $e); + } return false; } } diff --git a/apps/dav/lib/comments/commentsplugin.php b/apps/dav/lib/comments/commentsplugin.php index 56d94cc33e9..7abf6e71ee5 100644 --- a/apps/dav/lib/comments/commentsplugin.php +++ b/apps/dav/lib/comments/commentsplugin.php @@ -242,6 +242,9 @@ class CommentsPlugin extends ServerPlugin { return $comment; } catch (\InvalidArgumentException $e) { throw new BadRequest('Invalid input values', 0, $e); + } catch (\OCP\Comments\MessageTooLongException $e) { + $msg = 'Message exceeds allowed character limit of '; + throw new BadRequest($msg . \OCP\Comments\IComment::MAX_MESSAGE_LENGTH, 0, $e); } } diff --git a/apps/dav/tests/unit/comments/commentnode.php b/apps/dav/tests/unit/comments/commentnode.php index 8d1bf06ab60..8ebc5c2ff2c 100644 --- a/apps/dav/tests/unit/comments/commentnode.php +++ b/apps/dav/tests/unit/comments/commentnode.php @@ -22,6 +22,8 @@ namespace OCA\DAV\Tests\Unit\Comments; use OCA\DAV\Comments\CommentNode; +use OCP\Comments\IComment; +use OCP\Comments\MessageTooLongException; class CommentsNode extends \Test\TestCase { @@ -199,6 +201,43 @@ class CommentsNode extends \Test\TestCase { } /** + * @expectedException \Sabre\DAV\Exception\BadRequest + * @expectedExceptionMessage Message exceeds allowed character limit of + */ + public function testUpdateCommentMessageTooLongException() { + $user = $this->getMock('\OCP\IUser'); + + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('alice')); + + $this->userSession->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($user)); + + $this->comment->expects($this->once()) + ->method('setMessage') + ->will($this->throwException(new MessageTooLongException())); + + $this->comment->expects($this->any()) + ->method('getActorType') + ->will($this->returnValue('users')); + + $this->comment->expects($this->any()) + ->method('getActorId') + ->will($this->returnValue('alice')); + + $this->commentsManager->expects($this->never()) + ->method('save'); + + $this->logger->expects($this->once()) + ->method('logException'); + + // imagine 'foo' has >1k characters. comment is mocked anyway. + $this->node->updateComment('foo'); + } + + /** * @expectedException \Sabre\DAV\Exception\Forbidden */ public function testUpdateForbiddenByUser() { diff --git a/apps/dav/tests/unit/comments/commentsplugin.php b/apps/dav/tests/unit/comments/commentsplugin.php index 9822137bbea..d6f489f5e80 100644 --- a/apps/dav/tests/unit/comments/commentsplugin.php +++ b/apps/dav/tests/unit/comments/commentsplugin.php @@ -23,6 +23,7 @@ namespace OCA\DAV\Tests\Unit\Comments; use OC\Comments\Comment; use OCA\DAV\Comments\CommentsPlugin as CommentsPluginImplementation; +use OCP\Comments\IComment; use Sabre\DAV\Exception\NotFound; class CommentsPlugin extends \Test\TestCase { @@ -506,6 +507,98 @@ class CommentsPlugin extends \Test\TestCase { } /** + * @expectedException \Sabre\DAV\Exception\BadRequest + * @expectedExceptionMessage Message exceeds allowed character limit of + */ + public function testCreateCommentMessageTooLong() { + $commentData = [ + 'actorType' => 'users', + 'verb' => 'comment', + 'message' => str_pad('', IComment::MAX_MESSAGE_LENGTH + 1, 'x'), + ]; + + $comment = new Comment([ + 'objectType' => 'files', + 'objectId' => '42', + 'actorType' => 'users', + 'actorId' => 'alice', + 'verb' => 'comment', + ]); + $comment->setId('23'); + + $path = 'comments/files/42'; + + $requestData = json_encode($commentData); + + $user = $this->getMock('OCP\IUser'); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('alice')); + + $node = $this->getMockBuilder('\OCA\DAV\Comments\EntityCollection') + ->disableOriginalConstructor() + ->getMock(); + $node->expects($this->once()) + ->method('getName') + ->will($this->returnValue('files')); + $node->expects($this->once()) + ->method('getId') + ->will($this->returnValue('42')); + + $node->expects($this->never()) + ->method('setReadMarker'); + + $this->commentsManager->expects($this->once()) + ->method('create') + ->with('users', 'alice', 'files', '42') + ->will($this->returnValue($comment)); + + $this->userSession->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($user)); + + // technically, this is a shortcut. Inbetween EntityTypeCollection would + // be returned, but doing it exactly right would not be really + // unit-testing like, as it would require to haul in a lot of other + // things. + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->with('/' . $path) + ->will($this->returnValue($node)); + + $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface') + ->disableOriginalConstructor() + ->getMock(); + + $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') + ->disableOriginalConstructor() + ->getMock(); + + $request->expects($this->once()) + ->method('getPath') + ->will($this->returnValue('/' . $path)); + + $request->expects($this->once()) + ->method('getBodyAsString') + ->will($this->returnValue($requestData)); + + $request->expects($this->once()) + ->method('getHeader') + ->with('Content-Type') + ->will($this->returnValue('application/json')); + + $response->expects($this->never()) + ->method('setHeader'); + + $this->server->expects($this->any()) + ->method('getRequestUri') + ->will($this->returnValue($path)); + $this->plugin->initialize($this->server); + + $this->plugin->httpPost($request, $response); + } + + /** * @expectedException \Sabre\DAV\Exception\ReportNotSupported */ public function testOnReportInvalidNode() { diff --git a/apps/federatedfilesharing/lib/federatedshareprovider.php b/apps/federatedfilesharing/lib/federatedshareprovider.php index 0825a0e69bc..0e6089bde07 100644 --- a/apps/federatedfilesharing/lib/federatedshareprovider.php +++ b/apps/federatedfilesharing/lib/federatedshareprovider.php @@ -220,6 +220,8 @@ class FederatedShareProvider implements IShareProvider { $qb->update('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) ->execute(); return $share; diff --git a/apps/federation/controller/settingscontroller.php b/apps/federation/controller/settingscontroller.php index e5e46606f12..3adb6fced66 100644 --- a/apps/federation/controller/settingscontroller.php +++ b/apps/federation/controller/settingscontroller.php @@ -26,7 +26,6 @@ use OCA\Federation\TrustedServers; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; -use OCP\IConfig; use OCP\IL10N; use OCP\IRequest; diff --git a/apps/federation/lib/trustedservers.php b/apps/federation/lib/trustedservers.php index e3ce8228cc3..340accfdbdf 100644 --- a/apps/federation/lib/trustedservers.php +++ b/apps/federation/lib/trustedservers.php @@ -23,6 +23,7 @@ namespace OCA\Federation; +use OC\HintException; use OCP\AppFramework\Http; use OCP\BackgroundJob\IJobList; use OCP\Http\Client\IClientService; @@ -202,34 +203,33 @@ class TrustedServers { public function isOwnCloudServer($url) { $isValidOwnCloud = false; $client = $this->httpClientService->newClient(); - try { - $result = $client->get( - $url . '/status.php', - [ - 'timeout' => 3, - 'connect_timeout' => 3, - ] - ); - if ($result->getStatusCode() === Http::STATUS_OK) { - $isValidOwnCloud = $this->checkOwnCloudVersion($result->getBody()); - } - } catch (\Exception $e) { - $this->logger->error($e->getMessage(), ['app' => 'federation']); - return false; + $result = $client->get( + $url . '/status.php', + [ + 'timeout' => 3, + 'connect_timeout' => 3, + ] + ); + if ($result->getStatusCode() === Http::STATUS_OK) { + $isValidOwnCloud = $this->checkOwnCloudVersion($result->getBody()); } + return $isValidOwnCloud; } /** * check if ownCloud version is >= 9.0 * - * @param $statusphp + * @param $status * @return bool */ - protected function checkOwnCloudVersion($statusphp) { - $decoded = json_decode($statusphp, true); + protected function checkOwnCloudVersion($status) { + $decoded = json_decode($status, true); if (!empty($decoded) && isset($decoded['version'])) { - return version_compare($decoded['version'], '9.0.0', '>='); + if (!version_compare($decoded['version'], '9.0.0', '>=')) { + throw new HintException('Remote server version is too low. ownCloud 9.0 is required.'); + } + return true; } return false; } diff --git a/apps/federation/middleware/addservermiddleware.php b/apps/federation/middleware/addservermiddleware.php index cd9ccff4403..10abd9db704 100644 --- a/apps/federation/middleware/addservermiddleware.php +++ b/apps/federation/middleware/addservermiddleware.php @@ -58,7 +58,7 @@ class AddServerMiddleware extends Middleware { if ($exception instanceof HintException) { $message = $exception->getHint(); } else { - $message = $this->l->t('Unknown error'); + $message = $exception->getMessage(); } return new JSONResponse( diff --git a/apps/federation/tests/lib/trustedserverstest.php b/apps/federation/tests/lib/trustedserverstest.php index e57391ed198..130a0e3bb22 100644 --- a/apps/federation/tests/lib/trustedserverstest.php +++ b/apps/federation/tests/lib/trustedserverstest.php @@ -23,6 +23,7 @@ namespace OCA\Federation\Tests\lib; +use OC\HintException; use OCA\Federation\DbHandler; use OCA\Federation\TrustedServers; use OCP\BackgroundJob\IJobList; @@ -282,41 +283,50 @@ class TrustedServersTest extends TestCase { ]; } + /** + * @expectedException \Exception + * @expectedExceptionMessage simulated exception + */ public function testIsOwnCloudServerFail() { $server = 'server1'; $this->httpClientService->expects($this->once())->method('newClient') ->willReturn($this->httpClient); - $this->logger->expects($this->once())->method('error') - ->with('simulated exception', ['app' => 'federation']); - $this->httpClient->expects($this->once())->method('get')->with($server . '/status.php') ->willReturnCallback(function () { throw new \Exception('simulated exception'); }); - $this->assertFalse($this->trustedServers->isOwnCloudServer($server)); - + $this->trustedServers->isOwnCloudServer($server); } /** * @dataProvider dataTestCheckOwnCloudVersion - * - * @param $statusphp - * @param $expected */ - public function testCheckOwnCloudVersion($statusphp, $expected) { - $this->assertSame($expected, - $this->invokePrivate($this->trustedServers, 'checkOwnCloudVersion', [$statusphp]) - ); + public function testCheckOwnCloudVersion($status) { + $this->assertTrue($this->invokePrivate($this->trustedServers, 'checkOwnCloudVersion', [$status])); } public function dataTestCheckOwnCloudVersion() { return [ - ['{"version":"8.4.0"}', false], - ['{"version":"9.0.0"}', true], - ['{"version":"9.1.0"}', true] + ['{"version":"9.0.0"}'], + ['{"version":"9.1.0"}'] + ]; + } + + /** + * @dataProvider dataTestCheckOwnCloudVersionTooLow + * @expectedException \OC\HintException + * @expectedExceptionMessage Remote server version is too low. ownCloud 9.0 is required. + */ + public function testCheckOwnCloudVersionTooLow($status) { + $this->invokePrivate($this->trustedServers, 'checkOwnCloudVersion', [$status]); + } + + public function dataTestCheckOwnCloudVersionTooLow() { + return [ + ['{"version":"8.2.3"}'], ]; } diff --git a/apps/federation/tests/middleware/addservermiddlewaretest.php b/apps/federation/tests/middleware/addservermiddlewaretest.php index a94d907ae76..49e34cc73d6 100644 --- a/apps/federation/tests/middleware/addservermiddlewaretest.php +++ b/apps/federation/tests/middleware/addservermiddlewaretest.php @@ -27,6 +27,7 @@ use OC\HintException; use OCA\Federation\Middleware\AddServerMiddleware; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\ILogger; use Test\TestCase; class AddServerMiddlewareTest extends TestCase { @@ -93,7 +94,7 @@ class AddServerMiddlewareTest extends TestCase { public function dataTestAfterException() { return [ [new HintException('message', 'hint'), 'message', 'hint'], - [new \Exception('message'), 'message', 'Unknown error'], + [new \Exception('message'), 'message', 'message'], ]; } diff --git a/apps/files/appinfo/register_command.php b/apps/files/appinfo/register_command.php index 4aaf49df9e2..0ec2fe1a584 100644 --- a/apps/files/appinfo/register_command.php +++ b/apps/files/appinfo/register_command.php @@ -21,5 +21,11 @@ * */ -$application->add(new OCA\Files\Command\Scan(\OC::$server->getUserManager())); -$application->add(new OCA\Files\Command\DeleteOrphanedFiles(\OC::$server->getDatabaseConnection())); +$dbConnection = \OC::$server->getDatabaseConnection(); +$userManager = OC::$server->getUserManager(); +$shareManager = \OC::$server->getShareManager(); + +/** @var Symfony\Component\Console\Application $application */ +$application->add(new OCA\Files\Command\Scan($userManager)); +$application->add(new OCA\Files\Command\DeleteOrphanedFiles($dbConnection)); +$application->add(new OCA\Files\Command\TransferOwnership($userManager, $shareManager)); diff --git a/apps/files/command/transferownership.php b/apps/files/command/transferownership.php new file mode 100644 index 00000000000..4cc2f34c3a3 --- /dev/null +++ b/apps/files/command/transferownership.php @@ -0,0 +1,225 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\Files\Command; + +use OC\Files\Filesystem; +use OC\Files\View; +use OCP\Files\FileInfo; +use OCP\Files\Folder; +use OCP\IUserManager; +use OCP\Share\IManager; +use OCP\Share\IShare; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class TransferOwnership extends Command { + + /** @var IUserManager $userManager */ + private $userManager; + + /** @var IManager */ + private $shareManager; + + /** @var FileInfo[] */ + private $allFiles = []; + + /** @var FileInfo[] */ + private $encryptedFiles = []; + + /** @var IShare[] */ + private $shares = []; + + /** @var string */ + private $sourceUser; + + /** @var string */ + private $destinationUser; + + /** @var string */ + private $finalTarget; + + public function __construct(IUserManager $userManager, IManager $shareManager) { + $this->userManager = $userManager; + $this->shareManager = $shareManager; + parent::__construct(); + } + + protected function configure() { + $this + ->setName('files:transfer-ownership') + ->setDescription('All files and folders are move to another user - shares are moved as well.') + ->addArgument( + 'source-user', + InputArgument::REQUIRED, + 'owner of files which shall be moved' + ) + ->addArgument( + 'destination-user', + InputArgument::REQUIRED, + 'user who will be the new owner of the files' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $this->sourceUser = $input->getArgument('source-user'); + $this->destinationUser = $input->getArgument('destination-user'); + if (!$this->userManager->userExists($this->sourceUser)) { + $output->writeln("<error>Unknown source user $this->sourceUser</error>"); + return; + } + if (!$this->userManager->userExists($this->destinationUser)) { + $output->writeln("<error>Unknown destination user $this->destinationUser</error>"); + return; + } + + $date = date('c'); + $this->finalTarget = "$this->destinationUser/files/transferred from $this->sourceUser on $date"; + + // setup filesystem + Filesystem::initMountPoints($this->sourceUser); + Filesystem::initMountPoints($this->destinationUser); + + // analyse source folder + $this->analyse($output); + + // collect all the shares + $this->collectUsersShares($output); + + // transfer the files + $this->transfer($output); + + // restore the shares + $this->restoreShares($output); + } + + private function walkFiles(View $view, $path, \Closure $callBack) { + foreach ($view->getDirectoryContent($path) as $fileInfo) { + if (!$callBack($fileInfo)) { + return; + } + if ($fileInfo->getType() === FileInfo::TYPE_FOLDER) { + $this->walkFiles($view, $fileInfo->getPath(), $callBack); + } + } + } + + /** + * @param OutputInterface $output + * @throws \Exception + */ + protected function analyse(OutputInterface $output) { + $view = new View(); + $output->writeln("Analysing files of $this->sourceUser ..."); + $progress = new ProgressBar($output); + $progress->start(); + $self = $this; + $this->walkFiles($view, "$this->sourceUser/files", + function (FileInfo $fileInfo) use ($progress, $self) { + if ($fileInfo->getType() === FileInfo::TYPE_FOLDER) { + return true; + } + $progress->advance(); + $this->allFiles[] = $fileInfo; + if ($fileInfo->isEncrypted()) { + $this->encryptedFiles[] = $fileInfo; + } + return true; + }); + $progress->finish(); + $output->writeln(''); + + // no file is allowed to be encrypted + if (!empty($this->encryptedFiles)) { + $output->writeln("<error>Some files are encrypted - please decrypt them first</error>"); + foreach($this->encryptedFiles as $encryptedFile) { + /** @var FileInfo $encryptedFile */ + $output->writeln(" " . $encryptedFile->getPath()); + } + throw new \Exception('Execution terminated.'); + } + + } + + /** + * @param OutputInterface $output + */ + private function collectUsersShares(OutputInterface $output) { + $output->writeln("Collecting all share information for files and folder of $this->sourceUser ..."); + + $progress = new ProgressBar($output, count($this->shares)); + foreach([\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE] as $shareType) { + $offset = 0; + while (true) { + $sharePage = $this->shareManager->getSharesBy($this->sourceUser, $shareType, null, true, 50, $offset); + $progress->advance(count($sharePage)); + if (empty($sharePage)) { + break; + } + $this->shares = array_merge($this->shares, $sharePage); + $offset += 50; + } + } + + $progress->finish(); + $output->writeln(''); + } + + /** + * @param OutputInterface $output + */ + protected function transfer(OutputInterface $output) { + $view = new View(); + $output->writeln("Transferring files to $this->finalTarget ..."); + $view->rename("$this->sourceUser/files", $this->finalTarget); + // because the files folder is moved away we need to recreate it + $view->mkdir("$this->sourceUser/files"); + } + + /** + * @param OutputInterface $output + */ + private function restoreShares(OutputInterface $output) { + $output->writeln("Restoring shares ..."); + $progress = new ProgressBar($output, count($this->shares)); + + foreach($this->shares as $share) { + if ($share->getSharedWith() === $this->destinationUser) { + $this->shareManager->deleteShare($share); + } else { + if ($share->getShareOwner() === $this->sourceUser) { + $share->setShareOwner($this->destinationUser); + } + if ($share->getSharedBy() === $this->sourceUser) { + $share->setSharedBy($this->destinationUser); + } + + $this->shareManager->updateShare($share); + } + $progress->advance(); + } + $progress->finish(); + $output->writeln(''); + } +} diff --git a/apps/updatenotification/appinfo/app.php b/apps/updatenotification/appinfo/app.php new file mode 100644 index 00000000000..d5e973be528 --- /dev/null +++ b/apps/updatenotification/appinfo/app.php @@ -0,0 +1,39 @@ +<?php +/** + * @author Lukas Reschke <lukas@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +if(\OC::$server->getConfig()->getSystemValue('updatechecker', true) === true) { + $updater = new \OC\Updater( + \OC::$server->getHTTPHelper(), + \OC::$server->getConfig(), + \OC::$server->getIntegrityCodeChecker() + ); + $updateChecker = new \OCA\UpdateNotification\UpdateChecker( + $updater + ); + + $userObject = \OC::$server->getUserSession()->getUser(); + if($userObject !== null) { + if(\OC::$server->getGroupManager()->isAdmin($userObject->getUID()) && $updateChecker->getUpdateState() !== []) { + \OCP\Util::addScript('updatenotification', 'notification'); + OC_Hook::connect('\OCP\Config', 'js', $updateChecker, 'getJavaScript'); + } + } +} diff --git a/apps/updatenotification/appinfo/info.xml b/apps/updatenotification/appinfo/info.xml new file mode 100644 index 00000000000..0bfdd861a2f --- /dev/null +++ b/apps/updatenotification/appinfo/info.xml @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<info> + <id>updatenotification</id> + <name>Update notification</name> + <description>Displays update notifications for ownCloud.</description> + <licence>AGPL</licence> + <author>Lukas Reschke</author> + <version>0.1.0</version> + <default_enable/> + <dependencies> + <owncloud min-version="9.0" max-version="9.0" /> + </dependencies> +</info> diff --git a/core/js/update-notification.js b/apps/updatenotification/js/notification.js index 42baa7f4c28..9d22bcb2309 100644 --- a/core/js/update-notification.js +++ b/apps/updatenotification/js/notification.js @@ -15,8 +15,8 @@ */ $(document).ready(function(){ var head = $('html > head'), - version = head.data('update-version'), - docLink = head.data('update-link'), + version = oc_updateState.updateVersion, + docLink = oc_updateState.updateLink, text = t('core', '{version} is available. Get more information on how to update.', {version: version}), element = $('<a>').attr('href', docLink).text(text); diff --git a/apps/updatenotification/lib/updatechecker.php b/apps/updatenotification/lib/updatechecker.php new file mode 100644 index 00000000000..965e21617e7 --- /dev/null +++ b/apps/updatenotification/lib/updatechecker.php @@ -0,0 +1,67 @@ +<?php +/** + * @author Lukas Reschke <lukas@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\UpdateNotification; + +use OC\Updater; + +class UpdateChecker { + /** @var Updater */ + private $updater; + + /** + * @param Updater $updater + */ + public function __construct(Updater $updater) { + $this->updater = $updater; + } + + /** + * @return array + */ + public function getUpdateState() { + $data = $this->updater->check(); + $result = []; + + if(isset($data['version']) && $data['version'] !== '' && $data['version'] !== []) { + $result['updateAvailable'] = true; + $result['updateVersion'] = $data['versionstring']; + if(substr($data['web'], 0, 8) === 'https://') { + $result['updateLink'] = $data['web']; + } + + return $result; + } + + return []; + } + + /** + * @param array $data + */ + public function getJavaScript(array $data) { + $data['array']['oc_updateState'] = json_encode([ + 'updateAvailable' => true, + 'updateVersion' => $this->getUpdateState()['updateVersion'], + 'updateLink' => isset($this->getUpdateState()['updateLink']) ? $this->getUpdateState()['updateLink'] : '', + ]); + } +} diff --git a/apps/updatenotification/tests/UpdateCheckerTest.php b/apps/updatenotification/tests/UpdateCheckerTest.php new file mode 100644 index 00000000000..9591758c4c9 --- /dev/null +++ b/apps/updatenotification/tests/UpdateCheckerTest.php @@ -0,0 +1,86 @@ +<?php +/** + * @author Lukas Reschke <lukas@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\UpdateNotification\Tests; + +use OC\Updater; +use OCA\UpdateNotification\UpdateChecker; +use Test\TestCase; + +class UpdateCheckerTest extends TestCase { + /** @var Updater */ + private $updater; + /** @var UpdateChecker */ + private $updateChecker; + + public function setUp() { + parent::setUp(); + + $this->updater = $this->getMockBuilder('\OC\Updater') + ->disableOriginalConstructor()->getMock(); + $this->updateChecker = new UpdateChecker($this->updater); + } + + public function testGetUpdateStateWithUpdateAndInvalidLink() { + $this->updater + ->expects($this->once()) + ->method('check') + ->willReturn([ + 'version' => 123, + 'versionstring' => 'ownCloud 123', + 'web'=> 'javascript:alert(1)', + ]); + + $expected = [ + 'updateAvailable' => true, + 'updateVersion' => 'ownCloud 123', + ]; + $this->assertSame($expected, $this->updateChecker->getUpdateState()); + } + + public function testGetUpdateStateWithUpdateAndValidLink() { + $this->updater + ->expects($this->once()) + ->method('check') + ->willReturn([ + 'version' => 123, + 'versionstring' => 'ownCloud 123', + 'web'=> 'https://owncloud.org/myUrl', + ]); + + $expected = [ + 'updateAvailable' => true, + 'updateVersion' => 'ownCloud 123', + 'updateLink' => 'https://owncloud.org/myUrl', + ]; + $this->assertSame($expected, $this->updateChecker->getUpdateState()); + } + + public function testGetUpdateStateWithoutUpdate() { + $this->updater + ->expects($this->once()) + ->method('check') + ->willReturn([]); + + $expected = []; + $this->assertSame($expected, $this->updateChecker->getUpdateState()); + } +} diff --git a/build/integration/features/provisioning-v1.feature b/build/integration/features/provisioning-v1.feature index af177b713dd..04a706f387b 100644 --- a/build/integration/features/provisioning-v1.feature +++ b/build/integration/features/provisioning-v1.feature @@ -291,6 +291,7 @@ Feature: provisioning | files_versions | | provisioning_api | | systemtags | + | updatenotification | Scenario: get app info Given As an "admin" diff --git a/core/shipped.json b/core/shipped.json index b74f2f28c47..d15f3ba3ca3 100644 --- a/core/shipped.json +++ b/core/shipped.json @@ -31,7 +31,7 @@ "sharepoint", "systemtags", "templateeditor", - "updater", + "updatenotification", "user_external", "user_ldap", "user_shibboleth", diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php index 7905f5b7f3a..f79defe100e 100644 --- a/core/templates/layout.user.php +++ b/core/templates/layout.user.php @@ -2,11 +2,7 @@ <!--[if lte IE 8]><html class="ng-csp ie ie8 lte9 lte8" data-placeholder-focus="false" lang="<?php p($_['language']); ?>" ><![endif]--> <!--[if IE 9]><html class="ng-csp ie ie9 lte9" data-placeholder-focus="false" lang="<?php p($_['language']); ?>" ><![endif]--> <!--[if (gt IE 9)|!(IE)]><!--><html class="ng-csp" data-placeholder-focus="false" lang="<?php p($_['language']); ?>" ><!--<![endif]--> - <head data-user="<?php p($_['user_uid']); ?>" data-user-displayname="<?php p($_['user_displayname']); ?>" data-requesttoken="<?php p($_['requesttoken']); ?>" - <?php if ($_['updateAvailable']): ?> - data-update-version="<?php p($_['updateVersion']); ?>" data-update-link="<?php p($_['updateLink']); ?>" - <?php endif; ?> - > + <head data-user="<?php p($_['user_uid']); ?>" data-user-displayname="<?php p($_['user_displayname']); ?>" data-requesttoken="<?php p($_['requesttoken']); ?>"> <meta charset="utf-8"> <title> <?php diff --git a/lib/private/comments/comment.php b/lib/private/comments/comment.php index 36189d9523b..27e63c98555 100644 --- a/lib/private/comments/comment.php +++ b/lib/private/comments/comment.php @@ -22,6 +22,7 @@ namespace OC\Comments; use OCP\Comments\IComment; use OCP\Comments\IllegalIDChangeException; +use OCP\Comments\MessageTooLongException; class Comment implements IComment { @@ -184,13 +185,18 @@ class Comment implements IComment { * * @param string $message * @return IComment + * @throws MessageTooLongException * @since 9.0.0 */ public function setMessage($message) { if(!is_string($message)) { throw new \InvalidArgumentException('String expected.'); } - $this->data['message'] = trim($message); + $message = trim($message); + if(mb_strlen($message, 'UTF-8') > IComment::MAX_MESSAGE_LENGTH) { + throw new MessageTooLongException('Comment message must not exceed ' . IComment::MAX_MESSAGE_LENGTH . ' characters'); + } + $this->data['message'] = $message; return $this; } diff --git a/lib/private/templatelayout.php b/lib/private/templatelayout.php index bc66c0dfb1e..7166b23d4c2 100644 --- a/lib/private/templatelayout.php +++ b/lib/private/templatelayout.php @@ -70,31 +70,6 @@ class TemplateLayout extends \OC_Template { $this->assign('bodyid', 'body-user'); } - // Update notification - if($this->config->getSystemValue('updatechecker', true) === true && - \OC_User::isAdminUser(\OC_User::getUser())) { - $updater = new \OC\Updater( - \OC::$server->getHTTPHelper(), - \OC::$server->getConfig(), - \OC::$server->getIntegrityCodeChecker(), - \OC::$server->getLogger() - ); - $data = $updater->check(); - - if(isset($data['version']) && $data['version'] != '' and $data['version'] !== Array()) { - $this->assign('updateAvailable', true); - $this->assign('updateVersion', $data['versionstring']); - if(substr($data['web'], 0, 8) === 'https://') { - $this->assign('updateLink', $data['web']); - } - \OCP\Util::addScript('core', 'update-notification'); - } else { - $this->assign('updateAvailable', false); // No update available or not an admin user - } - } else { - $this->assign('updateAvailable', false); // Update check is disabled - } - // Code integrity notification $integrityChecker = \OC::$server->getIntegrityCodeChecker(); if(!$integrityChecker->hasPassedCheck()) { diff --git a/lib/public/comments/icomment.php b/lib/public/comments/icomment.php index e695b5193f2..a7f4b4c6171 100644 --- a/lib/public/comments/icomment.php +++ b/lib/public/comments/icomment.php @@ -29,6 +29,7 @@ namespace OCP\Comments; * @since 9.0.0 */ interface IComment { + const MAX_MESSAGE_LENGTH = 1000; /** * returns the ID of the comment @@ -119,8 +120,12 @@ interface IComment { /** * sets the message of the comment and returns itself * + * When the given message length exceeds MAX_MESSAGE_LENGTH an + * MessageTooLongException shall be thrown. + * * @param string $message * @return IComment + * @throws MessageTooLongException * @since 9.0.0 */ public function setMessage($message); diff --git a/lib/public/comments/messagetoolongexception.php b/lib/public/comments/messagetoolongexception.php new file mode 100644 index 00000000000..054cecf320f --- /dev/null +++ b/lib/public/comments/messagetoolongexception.php @@ -0,0 +1,27 @@ +<?php +/** + * @author Arthur Schiwon <blizzz@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +namespace OCP\Comments; + +/** + * Exception thrown when a comment message exceeds the allowed character limit + * @since 9.0.0 + */ +class MessageTooLongException extends \OverflowException {} diff --git a/tests/lib/comments/comment.php b/tests/lib/comments/comment.php index e6f9c941c96..9b3f2ab166e 100644 --- a/tests/lib/comments/comment.php +++ b/tests/lib/comments/comment.php @@ -2,6 +2,7 @@ namespace Test\Comments; +use OCP\Comments\IComment; use Test\TestCase; class Test_Comments_Comment extends TestCase @@ -107,6 +108,15 @@ class Test_Comments_Comment extends TestCase $comment->$setter($type, $id); } + /** + * @expectedException \OCP\Comments\MessageTooLongException + */ + public function testSetUberlongMessage() { + $comment = new \OC\Comments\Comment(); + $msg = str_pad('', IComment::MAX_MESSAGE_LENGTH + 1, 'x'); + $comment->setMessage($msg); + } + } |