diff options
52 files changed, 1604 insertions, 204 deletions
diff --git a/.drone.yml b/.drone.yml index a3c3870dcdd..92dde90bf6d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -301,7 +301,7 @@ pipeline: when: matrix: TESTS: integration-sharees-features - codecov: + nodb-codecov: image: nextcloudci/php7.0:php7.0-2 commands: - TEST_SELECTION=NODB ./autotest.sh sqlite @@ -310,12 +310,22 @@ pipeline: - sh -c "if [ '$DRONE_BUILD_EVENT' != 'pull_request' ]; then bash codecov.sh -B $DRONE_BRANCH -C $DRONE_COMMIT -t 117641e2-a9e8-4b7b-984b-ae872d9b05f5 -f tests/autotest-clover-sqlite.xml; fi" when: matrix: - TESTS: codecov - + TESTS: nodb-codecov + db-codecov: + image: nextcloudci/php7.0:php7.0-2 + commands: + - TEST_SELECTION=QUICKDB ./autotest.sh sqlite + - wget https://codecov.io/bash -O codecov.sh + - sh -c "if [ '$DRONE_BUILD_EVENT' = 'pull_request' ]; then bash codecov.sh -B $DRONE_BRANCH -C $DRONE_COMMIT -P $DRONE_PULL_REQUEST -t 117641e2-a9e8-4b7b-984b-ae872d9b05f5 -f tests/autotest-clover-sqlite.xml; fi" + - sh -c "if [ '$DRONE_BUILD_EVENT' != 'pull_request' ]; then bash codecov.sh -B $DRONE_BRANCH -C $DRONE_COMMIT -t 117641e2-a9e8-4b7b-984b-ae872d9b05f5 -f tests/autotest-clover-sqlite.xml; fi" + when: + matrix: + TESTS: db-codecov matrix: include: - TESTS: signed-off-check - - TESTS: codecov + - TESTS: nodb-codecov + - TESTS: db-codecov - TESTS: integration-capabilities_features - TESTS: integration-federation_features - TESTS: integration-auth diff --git a/README.md b/README.md index 2c0f11b43bd..cd6122894af 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Nextcloud -[](https://drone.weasel.rocks/nextcloud/server) +[](https://drone.nextcloud.com/nextcloud/server) [](https://webchat.freenode.net/?channels=nextcloud) [](https://webchat.freenode.net/?channels=nextcloud-dev) diff --git a/apps/comments/appinfo/app.php b/apps/comments/appinfo/app.php index 00085cf9148..df41bdfa32d 100644 --- a/apps/comments/appinfo/app.php +++ b/apps/comments/appinfo/app.php @@ -32,6 +32,7 @@ $eventDispatcher->addListener( \OCP\Util::addScript('comments', 'commentsummarymodel'); \OCP\Util::addScript('comments', 'commentstabview'); \OCP\Util::addScript('comments', 'filesplugin'); + \OCP\Util::addScript('comments', 'activitytabviewplugin'); \OCP\Util::addStyle('comments', 'comments'); } ); diff --git a/apps/comments/css/comments.css b/apps/comments/css/comments.css index 7f64f1cf7db..103564c5faf 100644 --- a/apps/comments/css/comments.css +++ b/apps/comments/css/comments.css @@ -59,35 +59,39 @@ line-height: 32px; } +#activityTabView li.comment.collapsed .activitymessage, #commentsTabView .comment.collapsed .message { white-space: pre-wrap; } +#activityTabView li.comment.collapsed .activitymessage, #commentsTabView .comment.collapsed .message { max-height: 70px; overflow: hidden; } +#activityTabView li.comment .message-overlay, #commentsTabView .comment .message-overlay { display: none; } +#activityTabView li.comment.collapsed .message-overlay, #commentsTabView .comment.collapsed .message-overlay { display: block; - position: absolute; + position: absolute; z-index: 2; - height: 50px; - pointer-events: none; + height: 50px; + pointer-events: none; left: 0; right: 0; - bottom: 0; + bottom: 0; background: -moz-linear-gradient(rgba(255,255,255,0), rgba(255,255,255,1)); background: -webkit-linear-gradient(rgba(255,255,255,0), rgba(255,255,255,1)); background: -o-linear-gradient(rgba(255,255,255,0), rgba(255,255,255,1)); background: -ms-linear-gradient(rgba(255,255,255,0), rgba(255,255,255,1)); background: linear-gradient(rgba(255,255,255,0), rgba(255,255,255,1)); filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#00FFFFFF', endColorstr='#FFFFFFFF'); - background-repeat: no-repeat; + background-repeat: no-repeat; } #commentsTabView .authorRow>div { diff --git a/apps/comments/js/activitytabviewplugin.js b/apps/comments/js/activitytabviewplugin.js new file mode 100644 index 00000000000..ca3253bd137 --- /dev/null +++ b/apps/comments/js/activitytabviewplugin.js @@ -0,0 +1,59 @@ +/* + * @author Joas Schilling <coding@schilljs.com> + * Copyright (c) 2016 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + */ + +(function() { + OCA.Comments.ActivityTabViewPlugin = { + + /** + * Prepare activity for display + * + * @param {OCA.Activity.ActivityModel} model for this activity + * @param {jQuery} $el jQuery handle for this activity + * @param {string} view The view that displayes this activity + */ + prepareModelForDisplay: function (model, $el, view) { + if (model.get('app') !== 'comments' || model.get('type') !== 'comments') { + return; + } + + if (view === 'ActivityTabView') { + $el.addClass('comment'); + if (this._isLong(model.get('message_prepared'))) { + $el.addClass('collapsed'); + var $overlay = $('<div>').addClass('message-overlay'); + $el.find('.activitymessage').after($overlay); + $el.on('click', this._onClickCollapsedComment); + } + } + }, + + /* + * Copy of CommentsTabView._onClickComment() + */ + _onClickCollapsedComment: function(ev) { + var $row = $(ev.target); + if (!$row.is('.comment')) { + $row = $row.closest('.comment'); + } + $row.removeClass('collapsed'); + }, + + /* + * Copy of CommentsTabView._isLong() + */ + _isLong: function(message) { + return message.length > 250 || (message.match(/\n/g) || []).length > 1; + } + }; + + +})(); + +OC.Plugins.register('OCA.Activity.RenderingPlugins', OCA.Comments.ActivityTabViewPlugin); diff --git a/apps/comments/lib/Activity/Extension.php b/apps/comments/lib/Activity/Extension.php index 0a7503c1915..6bf7bc9ac0b 100644 --- a/apps/comments/lib/Activity/Extension.php +++ b/apps/comments/lib/Activity/Extension.php @@ -159,7 +159,7 @@ class Extension implements IExtension { } return (string) $l->t('%1$s commented', $params); case self::ADD_COMMENT_MESSAGE: - return $this->convertParameterToComment($params[0], 120); + return $this->convertParameterToComment($params[0]); } return false; @@ -196,7 +196,6 @@ class Extension implements IExtension { try { return strip_tags($user) === $this->activityManager->getCurrentUserId(); } catch (\UnexpectedValueException $e) { - // FIXME this is awkward, but we have no access to the current user in emails return false; } } @@ -247,14 +246,15 @@ class Extension implements IExtension { public function getNavigation() { $l = $this->getL10N(); return [ - 'apps' => [], - 'top' => [ + 'apps' => [ self::APP_NAME => [ 'id' => self::APP_NAME, + 'icon' => 'icon-comment', 'name' => (string) $l->t('Comments'), 'url' => $this->URLGenerator->linkToRoute('activity.Activities.showList', ['filter' => self::APP_NAME]), ], ], + 'top' => [], ]; } @@ -300,21 +300,12 @@ class Extension implements IExtension { * @param string $parameter * @return string */ - protected function convertParameterToComment($parameter, $maxLength = 0) { + protected function convertParameterToComment($parameter) { if (preg_match('/^\<parameter\>(\d*)\<\/parameter\>$/', $parameter, $matches)) { try { $comment = $this->commentsManager->get((int) $matches[1]); $message = $comment->getMessage(); $message = str_replace("\n", '<br />', str_replace(['<', '>'], ['<', '>'], $message)); - - if ($maxLength && isset($message[$maxLength + 20])) { - $findSpace = strpos($message, ' ', $maxLength); - if ($findSpace !== false && $findSpace < $maxLength + 20) { - return substr($message, 0, $findSpace) . '…'; - } - return substr($message, 0, $maxLength + 20) . '…'; - } - return $message; } catch (NotFoundException $e) { return ''; diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php index dd5f958ed4c..aa5bacea5bb 100644 --- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php @@ -329,8 +329,7 @@ class FilesPlugin extends ServerPlugin { }); } - if ($node instanceof \OCA\DAV\Connector\Sabre\Node - || $node instanceof \OCA\DAV\Files\FilesHome) { + if ($node instanceof \OCA\DAV\Connector\Sabre\Node) { $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function() use ($node) { return $this->config->getSystemValue('data-fingerprint', ''); }); diff --git a/apps/dav/lib/Files/FilesHome.php b/apps/dav/lib/Files/FilesHome.php index a4fb7d285a6..9c8f9835d7c 100644 --- a/apps/dav/lib/Files/FilesHome.php +++ b/apps/dav/lib/Files/FilesHome.php @@ -23,11 +23,9 @@ namespace OCA\DAV\Files; use OCA\DAV\Connector\Sabre\Directory; use Sabre\DAV\Exception\Forbidden; -use Sabre\DAV\ICollection; -use Sabre\DAV\SimpleCollection; use Sabre\HTTP\URLUtil; -class FilesHome implements ICollection { +class FilesHome extends Directory { /** * @var array @@ -41,30 +39,13 @@ class FilesHome implements ICollection { */ public function __construct($principalInfo) { $this->principalInfo = $principalInfo; - } - - function createFile($name, $data = null) { - return $this->impl()->createFile($name, $data); - } - - function createDirectory($name) { - $this->impl()->createDirectory($name); - } - - function getChild($name) { - return $this->impl()->getChild($name); - } - - function getChildren() { - return $this->impl()->getChildren(); - } - - function childExists($name) { - return $this->impl()->childExists($name); + $view = \OC\Files\Filesystem::getView(); + $rootInfo = $view->getFileInfo(''); + parent::__construct($view, $rootInfo); } function delete() { - $this->impl()->delete(); + throw new Forbidden('Permission denied to delete home folder'); } function getName() { @@ -75,30 +56,4 @@ class FilesHome implements ICollection { function setName($name) { throw new Forbidden('Permission denied to rename this folder'); } - - /** - * Returns the last modification time, as a unix timestamp - * - * @return int - */ - function getLastModified() { - return $this->impl()->getLastModified(); - } - - /** - * @return Directory - */ - private function impl() { - // - // TODO: we need to mount filesystem of the give user - // - $user = \OC::$server->getUserSession()->getUser(); - if ($this->getName() !== $user->getUID()) { - return new SimpleCollection($this->getName()); - } - $view = \OC\Files\Filesystem::getView(); - $rootInfo = $view->getFileInfo(''); - $impl = new Directory($view, $rootInfo); - return $impl; - } } diff --git a/apps/dav/lib/Files/RootCollection.php b/apps/dav/lib/Files/RootCollection.php index 10459aa8ada..57802d19573 100644 --- a/apps/dav/lib/Files/RootCollection.php +++ b/apps/dav/lib/Files/RootCollection.php @@ -22,7 +22,8 @@ namespace OCA\DAV\Files; use Sabre\DAVACL\AbstractPrincipalCollection; -use Sabre\DAVACL\IPrincipal; +use Sabre\HTTP\URLUtil; +use Sabre\DAV\SimpleCollection; class RootCollection extends AbstractPrincipalCollection { @@ -34,9 +35,17 @@ class RootCollection extends AbstractPrincipalCollection { * supplied by the authentication backend. * * @param array $principalInfo - * @return IPrincipal + * @return INode */ function getChildForPrincipal(array $principalInfo) { + list(,$name) = URLUtil::splitPath($principalInfo['uri']); + $user = \OC::$server->getUserSession()->getUser(); + if ($name !== $user->getUID()) { + // a user is only allowed to see their own home contents, so in case another collection + // is accessed, we return a simple empty collection for now + // in the future this could be considered to be used for accessing shared files + return new SimpleCollection($name); + } return new FilesHome($principalInfo); } diff --git a/apps/dav/lib/Upload/UploadHome.php b/apps/dav/lib/Upload/UploadHome.php index 296eb2df3a9..df458e8bc4b 100644 --- a/apps/dav/lib/Upload/UploadHome.php +++ b/apps/dav/lib/Upload/UploadHome.php @@ -30,7 +30,7 @@ use Sabre\DAV\ICollection; class UploadHome implements ICollection { /** - * FilesHome constructor. + * UploadHome constructor. * * @param array $principalInfo */ diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php index e2d63868af0..282a5b2f626 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php @@ -217,58 +217,6 @@ class FilesPluginTest extends TestCase { $this->assertEquals([self::SIZE_PROPERTYNAME], $propFind->get404Properties()); } - public function testGetPropertiesForFileHome() { - /** @var \OCA\DAV\Files\FilesHome | \PHPUnit_Framework_MockObject_MockObject $node */ - $node = $this->getMockBuilder('\OCA\DAV\Files\FilesHome') - ->disableOriginalConstructor() - ->getMock(); - - $propFind = new PropFind( - '/dummyPath', - array( - self::GETETAG_PROPERTYNAME, - self::FILEID_PROPERTYNAME, - self::INTERNAL_FILEID_PROPERTYNAME, - self::SIZE_PROPERTYNAME, - self::PERMISSIONS_PROPERTYNAME, - self::DOWNLOADURL_PROPERTYNAME, - self::OWNER_ID_PROPERTYNAME, - self::OWNER_DISPLAY_NAME_PROPERTYNAME, - self::DATA_FINGERPRINT_PROPERTYNAME, - ), - 0 - ); - - $user = $this->getMockBuilder('\OC\User\User') - ->disableOriginalConstructor()->getMock(); - $user->expects($this->never())->method('getUID'); - $user->expects($this->never())->method('getDisplayName'); - - $this->plugin->handleGetProperties( - $propFind, - $node - ); - - $this->assertEquals(null, $propFind->get(self::GETETAG_PROPERTYNAME)); - $this->assertEquals(null, $propFind->get(self::FILEID_PROPERTYNAME)); - $this->assertEquals(null, $propFind->get(self::INTERNAL_FILEID_PROPERTYNAME)); - $this->assertEquals(null, $propFind->get(self::SIZE_PROPERTYNAME)); - $this->assertEquals(null, $propFind->get(self::PERMISSIONS_PROPERTYNAME)); - $this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME)); - $this->assertEquals(null, $propFind->get(self::OWNER_ID_PROPERTYNAME)); - $this->assertEquals(null, $propFind->get(self::OWNER_DISPLAY_NAME_PROPERTYNAME)); - $this->assertEquals(['{DAV:}getetag', - '{http://owncloud.org/ns}id', - '{http://owncloud.org/ns}fileid', - '{http://owncloud.org/ns}size', - '{http://owncloud.org/ns}permissions', - '{http://owncloud.org/ns}downloadURL', - '{http://owncloud.org/ns}owner-id', - '{http://owncloud.org/ns}owner-display-name' - ], $propFind->get404Properties()); - $this->assertEquals('my_fingerprint', $propFind->get(self::DATA_FINGERPRINT_PROPERTYNAME)); - } - public function testGetPropertiesStorageNotAvailable() { /** @var \OCA\DAV\Connector\Sabre\File | \PHPUnit_Framework_MockObject_MockObject $node */ $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); diff --git a/apps/files/lib/Activity.php b/apps/files/lib/Activity.php index b9cc98cf7ea..5fc59f119e1 100644 --- a/apps/files/lib/Activity.php +++ b/apps/files/lib/Activity.php @@ -104,7 +104,7 @@ class Activity implements IExtension { $l = $this->getL10N($languageCode); return [ self::TYPE_SHARE_CREATED => (string) $l->t('A new file or folder has been <strong>created</strong>'), - self::TYPE_SHARE_CHANGED => (string) $l->t('A file or folder has been <strong>changed</strong>'), + self::TYPE_SHARE_CHANGED => (string) $l->t('A file or folder has been <strong>changed</strong> or <strong>renamed</strong>'), self::TYPE_FAVORITES => [ 'desc' => (string) $l->t('Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>'), 'methods' => [self::METHOD_STREAM], @@ -189,6 +189,14 @@ class Activity implements IExtension { return (string) $l->t('You restored %1$s', $params); case 'restored_by': return (string) $l->t('%2$s restored %1$s', $params); + case 'renamed_self': + return (string) $l->t('You renamed %2$s to %1$s', $params); + case 'renamed_by': + return (string) $l->t('%2$s renamed %3$s to %1$s', $params); + case 'moved_self': + return (string) $l->t('You moved %2$s to %1$s', $params); + case 'moved_by': + return (string) $l->t('%2$s moved %3$s to %1$s', $params); default: return false; @@ -209,6 +217,10 @@ class Activity implements IExtension { return (string) $l->t('Deleted by %2$s', $params); case 'restored_by': return (string) $l->t('Restored by %2$s', $params); + case 'renamed_by': + return (string) $l->t('Renamed by %2$s', $params); + case 'moved_by': + return (string) $l->t('Moved by %2$s', $params); default: return false; @@ -242,6 +254,19 @@ class Activity implements IExtension { 0 => 'file', 1 => 'username', ]; + case 'renamed_self': + case 'moved_self': + return [ + 0 => 'file', + 1 => 'file', + ]; + case 'renamed_by': + case 'moved_by': + return [ + 0 => 'file', + 1 => 'username', + 2 => 'file', + ]; } } @@ -306,6 +331,7 @@ class Activity implements IExtension { 'top' => [ self::FILTER_FAVORITES => [ 'id' => self::FILTER_FAVORITES, + 'icon' => 'icon-favorite', 'name' => (string) $this->l->t('Favorites'), 'url' => $this->URLGenerator->linkToRoute('activity.Activities.showList', ['filter' => self::FILTER_FAVORITES]), ], @@ -313,6 +339,7 @@ class Activity implements IExtension { 'apps' => [ self::FILTER_FILES => [ 'id' => self::FILTER_FILES, + 'icon' => 'icon-files-dark', 'name' => (string) $this->l->t('Files'), 'url' => $this->URLGenerator->linkToRoute('activity.Activities.showList', ['filter' => self::FILTER_FILES]), ], diff --git a/apps/files_sharing/lib/Activity.php b/apps/files_sharing/lib/Activity.php index f69f86175f8..4d57cc8c7b2 100644 --- a/apps/files_sharing/lib/Activity.php +++ b/apps/files_sharing/lib/Activity.php @@ -429,6 +429,7 @@ class Activity implements IExtension { 'top' => [ self::FILTER_SHARES => [ 'id' => self::FILTER_SHARES, + 'icon' => 'icon-share', 'name' => (string) $l->t('Shares'), 'url' => $this->URLGenerator->linkToRoute('activity.Activities.showList', ['filter' => self::FILTER_SHARES]), ], diff --git a/apps/files_sharing/tests/EtagPropagationTest.php b/apps/files_sharing/tests/EtagPropagationTest.php index 977f98f0497..4aba9e29113 100644 --- a/apps/files_sharing/tests/EtagPropagationTest.php +++ b/apps/files_sharing/tests/EtagPropagationTest.php @@ -34,7 +34,7 @@ use OC\Files\View; /** * Class EtagPropagationTest * - * @group DB + * @group SLOWDB * * @package OCA\Files_Sharing\Tests */ diff --git a/apps/files_sharing/tests/GroupEtagPropagationTest.php b/apps/files_sharing/tests/GroupEtagPropagationTest.php index 0ff3a4c01c6..eeb3c06bc59 100644 --- a/apps/files_sharing/tests/GroupEtagPropagationTest.php +++ b/apps/files_sharing/tests/GroupEtagPropagationTest.php @@ -28,7 +28,7 @@ use OC\Files\Filesystem; use OC\Files\View; /** - * @group DB + * @group SLOWDB * * @package OCA\Files_Sharing\Tests */ diff --git a/apps/files_sharing/tests/SharedMountTest.php b/apps/files_sharing/tests/SharedMountTest.php index 7427304eb14..5f769852dc8 100644 --- a/apps/files_sharing/tests/SharedMountTest.php +++ b/apps/files_sharing/tests/SharedMountTest.php @@ -31,7 +31,7 @@ namespace OCA\Files_Sharing\Tests; /** * Class SharedMountTest * - * @group DB + * @group SLOWDB */ class SharedMountTest extends TestCase { diff --git a/apps/systemtags/lib/Activity/Extension.php b/apps/systemtags/lib/Activity/Extension.php index c7342bd2af7..9fac2ddbc62 100644 --- a/apps/systemtags/lib/Activity/Extension.php +++ b/apps/systemtags/lib/Activity/Extension.php @@ -98,7 +98,7 @@ class Extension implements IExtension { public function getTypeIcon($type) { switch ($type) { case self::APP_NAME: - return false; + return 'icon-tag'; } return false; diff --git a/autotest.sh b/autotest.sh index 2dfa961a1a2..eca3d81c048 100755 --- a/autotest.sh +++ b/autotest.sh @@ -293,11 +293,14 @@ function execute_tests { export XDEBUG_CONFIG=$_XDEBUG_CONFIG fi GROUP='' + if [ "$TEST_SELECTION" == "QUICKDB" ]; then + GROUP='--group DB --exclude-group=SLOWDB' + fi if [ "$TEST_SELECTION" == "DB" ]; then - GROUP='--group DB' + GROUP='--group DB,SLOWDB' fi if [ "$TEST_SELECTION" == "NODB" ]; then - GROUP='--exclude-group DB' + GROUP='--exclude-group DB,SLOWDB' fi COVER='' diff --git a/core/css/icons.css b/core/css/icons.css index 45069a23c44..50dcf51fd3a 100644 --- a/core/css/icons.css +++ b/core/css/icons.css @@ -282,6 +282,10 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- background-image: url('../img/actions/sound-off.svg?v=1'); } +.icon-favorite { + background-image: url('../img/actions/star-dark.svg?v=1'); +} + .icon-star, .icon-starred:hover, .icon-starred:focus { @@ -365,6 +369,9 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-files { background-image: url('../img/places/files.svg?v=1'); } +.icon-files-dark { + background-image: url('../img/places/files-dark.svg?v=1'); +} .icon-file, .icon-filetype-text { background-image: url('../img/filetypes/text.svg?v=1'); diff --git a/core/img/actions/star-dark.svg b/core/img/actions/star-dark.svg new file mode 100644 index 00000000000..6edb7ea5000 --- /dev/null +++ b/core/img/actions/star-dark.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="22" width="22"><path d="M11.017.06l2.946 7.384L22 8.077l-6.11 5.082L17.77 21l-6.72-4.242-6.876 4.213 1.957-7.703L0 8.03l7.932-.52z"/></svg> diff --git a/core/img/places/files-dark.svg b/core/img/places/files-dark.svg new file mode 100644 index 00000000000..dfcad56018a --- /dev/null +++ b/core/img/places/files-dark.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="32" width="32"><path d="M2.917 5.015c-.5 0-.92.42-.92.92v22.16c0 .516.402.92.92.92h26.157a.91.91 0 0 0 .92-.92V9.938c0-.5-.418-.926-.92-.926H15.997l-4-4z" fill-rule="evenodd"/></svg>
\ No newline at end of file diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 2fa4df4d9e9..0ff46de07de 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -107,6 +107,7 @@ return array( 'OCP\\Files\\FileNameTooLongException' => $baseDir . '/lib/public/Files/FileNameTooLongException.php', 'OCP\\Files\\Folder' => $baseDir . '/lib/public/Files/Folder.php', 'OCP\\Files\\ForbiddenException' => $baseDir . '/lib/public/Files/ForbiddenException.php', + 'OCP\\Files\\IAppData' => $baseDir . '/lib/public/Files/IAppData.php', 'OCP\\Files\\IHomeStorage' => $baseDir . '/lib/public/Files/IHomeStorage.php', 'OCP\\Files\\IMimeTypeDetector' => $baseDir . '/lib/public/Files/IMimeTypeDetector.php', 'OCP\\Files\\IMimeTypeLoader' => $baseDir . '/lib/public/Files/IMimeTypeLoader.php', @@ -123,6 +124,9 @@ return array( 'OCP\\Files\\NotPermittedException' => $baseDir . '/lib/public/Files/NotPermittedException.php', 'OCP\\Files\\ObjectStore\\IObjectStore' => $baseDir . '/lib/public/Files/ObjectStore/IObjectStore.php', 'OCP\\Files\\ReservedWordException' => $baseDir . '/lib/public/Files/ReservedWordException.php', + 'OCP\\Files\\SimpleFS\\ISimpleFile' => $baseDir . '/lib/public/Files/SimpleFS/ISimpleFile.php', + 'OCP\\Files\\SimpleFS\\ISimpleFolder' => $baseDir . '/lib/public/Files/SimpleFS/ISimpleFolder.php', + 'OCP\\Files\\SimpleFS\\ISimpleRoot' => $baseDir . '/lib/public/Files/SimpleFS/ISimpleRoot.php', 'OCP\\Files\\Storage' => $baseDir . '/lib/public/Files/Storage.php', 'OCP\\Files\\StorageAuthException' => $baseDir . '/lib/public/Files/StorageAuthException.php', 'OCP\\Files\\StorageBadConfigException' => $baseDir . '/lib/public/Files/StorageBadConfigException.php', @@ -459,6 +463,8 @@ return array( 'OC\\Encryption\\Manager' => $baseDir . '/lib/private/Encryption/Manager.php', 'OC\\Encryption\\Update' => $baseDir . '/lib/private/Encryption/Update.php', 'OC\\Encryption\\Util' => $baseDir . '/lib/private/Encryption/Util.php', + 'OC\\Files\\AppData\\AppData' => $baseDir . '/lib/private/Files/AppData/AppData.php', + 'OC\\Files\\AppData\\Factory' => $baseDir . '/lib/private/Files/AppData/Factory.php', 'OC\\Files\\Cache\\Cache' => $baseDir . '/lib/private/Files/Cache/Cache.php', 'OC\\Files\\Cache\\CacheEntry' => $baseDir . '/lib/private/Files/Cache/CacheEntry.php', 'OC\\Files\\Cache\\FailedCache' => $baseDir . '/lib/private/Files/Cache/FailedCache.php', @@ -500,6 +506,8 @@ return array( 'OC\\Files\\ObjectStore\\NoopScanner' => $baseDir . '/lib/private/Files/ObjectStore/NoopScanner.php', 'OC\\Files\\ObjectStore\\ObjectStoreStorage' => $baseDir . '/lib/private/Files/ObjectStore/ObjectStoreStorage.php', 'OC\\Files\\ObjectStore\\Swift' => $baseDir . '/lib/private/Files/ObjectStore/Swift.php', + 'OC\\Files\\SimpleFS\\SimpleFile' => $baseDir . '/lib/private/Files/SimpleFS/SimpleFile.php', + 'OC\\Files\\SimpleFS\\SimpleFolder' => $baseDir . '/lib/private/Files/SimpleFS/SimpleFolder.php', 'OC\\Files\\Storage\\Common' => $baseDir . '/lib/private/Files/Storage/Common.php', 'OC\\Files\\Storage\\CommonTest' => $baseDir . '/lib/private/Files/Storage/CommonTest.php', 'OC\\Files\\Storage\\DAV' => $baseDir . '/lib/private/Files/Storage/DAV.php', @@ -638,6 +646,8 @@ return array( 'OC\\Repair\\FillETags' => $baseDir . '/lib/private/Repair/FillETags.php', 'OC\\Repair\\InnoDB' => $baseDir . '/lib/private/Repair/InnoDB.php', 'OC\\Repair\\MoveUpdaterStepFile' => $baseDir . '/lib/private/Repair/MoveUpdaterStepFile.php', + 'OC\\Repair\\NC11\\MoveAvatars' => $baseDir . '/lib/private/Repair/NC11/MoveAvatars.php', + 'OC\\Repair\\NC11\\MoveAvatarsBackgroundJob' => $baseDir . '/lib/private/Repair/NC11/MoveAvatarBackgroundJob.php', 'OC\\Repair\\OldGroupMembershipShares' => $baseDir . '/lib/private/Repair/OldGroupMembershipShares.php', 'OC\\Repair\\Preview' => $baseDir . '/lib/private/Repair/Preview.php', 'OC\\Repair\\RemoveGetETagEntries' => $baseDir . '/lib/private/Repair/RemoveGetETagEntries.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index ad493bfb041..24058a22edb 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -137,6 +137,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\Files\\FileNameTooLongException' => __DIR__ . '/../../..' . '/lib/public/Files/FileNameTooLongException.php', 'OCP\\Files\\Folder' => __DIR__ . '/../../..' . '/lib/public/Files/Folder.php', 'OCP\\Files\\ForbiddenException' => __DIR__ . '/../../..' . '/lib/public/Files/ForbiddenException.php', + 'OCP\\Files\\IAppData' => __DIR__ . '/../../..' . '/lib/public/Files/IAppData.php', 'OCP\\Files\\IHomeStorage' => __DIR__ . '/../../..' . '/lib/public/Files/IHomeStorage.php', 'OCP\\Files\\IMimeTypeDetector' => __DIR__ . '/../../..' . '/lib/public/Files/IMimeTypeDetector.php', 'OCP\\Files\\IMimeTypeLoader' => __DIR__ . '/../../..' . '/lib/public/Files/IMimeTypeLoader.php', @@ -153,6 +154,9 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\Files\\NotPermittedException' => __DIR__ . '/../../..' . '/lib/public/Files/NotPermittedException.php', 'OCP\\Files\\ObjectStore\\IObjectStore' => __DIR__ . '/../../..' . '/lib/public/Files/ObjectStore/IObjectStore.php', 'OCP\\Files\\ReservedWordException' => __DIR__ . '/../../..' . '/lib/public/Files/ReservedWordException.php', + 'OCP\\Files\\SimpleFS\\ISimpleFile' => __DIR__ . '/../../..' . '/lib/public/Files/SimpleFS/ISimpleFile.php', + 'OCP\\Files\\SimpleFS\\ISimpleFolder' => __DIR__ . '/../../..' . '/lib/public/Files/SimpleFS/ISimpleFolder.php', + 'OCP\\Files\\SimpleFS\\ISimpleRoot' => __DIR__ . '/../../..' . '/lib/public/Files/SimpleFS/ISimpleRoot.php', 'OCP\\Files\\Storage' => __DIR__ . '/../../..' . '/lib/public/Files/Storage.php', 'OCP\\Files\\StorageAuthException' => __DIR__ . '/../../..' . '/lib/public/Files/StorageAuthException.php', 'OCP\\Files\\StorageBadConfigException' => __DIR__ . '/../../..' . '/lib/public/Files/StorageBadConfigException.php', @@ -489,6 +493,8 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Encryption\\Manager' => __DIR__ . '/../../..' . '/lib/private/Encryption/Manager.php', 'OC\\Encryption\\Update' => __DIR__ . '/../../..' . '/lib/private/Encryption/Update.php', 'OC\\Encryption\\Util' => __DIR__ . '/../../..' . '/lib/private/Encryption/Util.php', + 'OC\\Files\\AppData\\AppData' => __DIR__ . '/../../..' . '/lib/private/Files/AppData/AppData.php', + 'OC\\Files\\AppData\\Factory' => __DIR__ . '/../../..' . '/lib/private/Files/AppData/Factory.php', 'OC\\Files\\Cache\\Cache' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Cache.php', 'OC\\Files\\Cache\\CacheEntry' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/CacheEntry.php', 'OC\\Files\\Cache\\FailedCache' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/FailedCache.php', @@ -530,6 +536,8 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Files\\ObjectStore\\NoopScanner' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/NoopScanner.php', 'OC\\Files\\ObjectStore\\ObjectStoreStorage' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/ObjectStoreStorage.php', 'OC\\Files\\ObjectStore\\Swift' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/Swift.php', + 'OC\\Files\\SimpleFS\\SimpleFile' => __DIR__ . '/../../..' . '/lib/private/Files/SimpleFS/SimpleFile.php', + 'OC\\Files\\SimpleFS\\SimpleFolder' => __DIR__ . '/../../..' . '/lib/private/Files/SimpleFS/SimpleFolder.php', 'OC\\Files\\Storage\\Common' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Common.php', 'OC\\Files\\Storage\\CommonTest' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/CommonTest.php', 'OC\\Files\\Storage\\DAV' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/DAV.php', @@ -668,6 +676,8 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Repair\\FillETags' => __DIR__ . '/../../..' . '/lib/private/Repair/FillETags.php', 'OC\\Repair\\InnoDB' => __DIR__ . '/../../..' . '/lib/private/Repair/InnoDB.php', 'OC\\Repair\\MoveUpdaterStepFile' => __DIR__ . '/../../..' . '/lib/private/Repair/MoveUpdaterStepFile.php', + 'OC\\Repair\\NC11\\MoveAvatars' => __DIR__ . '/../../..' . '/lib/private/Repair/NC11/MoveAvatars.php', + 'OC\\Repair\\NC11\\MoveAvatarsBackgroundJob' => __DIR__ . '/../../..' . '/lib/private/Repair/NC11/MoveAvatarBackgroundJob.php', 'OC\\Repair\\OldGroupMembershipShares' => __DIR__ . '/../../..' . '/lib/private/Repair/OldGroupMembershipShares.php', 'OC\\Repair\\Preview' => __DIR__ . '/../../..' . '/lib/private/Repair/Preview.php', 'OC\\Repair\\RemoveGetETagEntries' => __DIR__ . '/../../..' . '/lib/private/Repair/RemoveGetETagEntries.php', diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php index 20351d1321c..b6f8d8f458d 100644 --- a/lib/private/AppFramework/DependencyInjection/DIContainer.php +++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php @@ -46,6 +46,7 @@ use OC\AppFramework\Utility\SimpleContainer; use OC\Core\Middleware\TwoFactorMiddleware; use OCP\AppFramework\IApi; use OCP\AppFramework\IAppContainer; +use OCP\Files\IAppData; class DIContainer extends SimpleContainer implements IAppContainer { @@ -164,6 +165,10 @@ class DIContainer extends SimpleContainer implements IAppContainer { return $this->getServer()->getHTTPClientService(); }); + $this->registerService(IAppData::class, function (SimpleContainer $c) { + return $this->getServer()->getAppDataDir($c->query('AppName')); + }); + $this->registerService('OCP\\IGroupManager', function($c) { return $this->getServer()->getGroupManager(); }); diff --git a/lib/private/Avatar.php b/lib/private/Avatar.php index 9e8bd0136c2..c3a068701df 100644 --- a/lib/private/Avatar.php +++ b/lib/private/Avatar.php @@ -29,10 +29,10 @@ namespace OC; use OC\User\User; -use OCP\Files\Folder; -use OCP\Files\File; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; +use OCP\Files\SimpleFS\ISimpleFile; +use OCP\Files\SimpleFS\ISimpleFolder; use OCP\IAvatar; use OCP\IConfig; use OCP\IImage; @@ -45,7 +45,7 @@ use OCP\ILogger; */ class Avatar implements IAvatar { - /** @var Folder */ + /** @var ISimpleFolder */ private $folder; /** @var IL10N */ private $l; @@ -59,13 +59,13 @@ class Avatar implements IAvatar { /** * constructor * - * @param Folder $folder The folder where the avatars are + * @param ISimpleFolder $folder The folder where the avatars are * @param IL10N $l * @param User $user * @param ILogger $logger * @param IConfig $config */ - public function __construct(Folder $folder, + public function __construct(ISimpleFolder $folder, IL10N $l, $user, ILogger $logger, @@ -98,7 +98,8 @@ class Avatar implements IAvatar { * @return bool */ public function exists() { - return $this->folder->nodeExists('avatar.jpg') || $this->folder->nodeExists('avatar.png'); + + return $this->folder->fileExists('avatar.jpg') || $this->folder->fileExists('avatar.png'); } /** @@ -170,15 +171,15 @@ class Avatar implements IAvatar { } try { - $file = $this->folder->get($path); + $file = $this->folder->getFile($path); } catch (NotFoundException $e) { if ($size <= 0) { throw new NotFoundException; } $avatar = new OC_Image(); - /** @var File $file */ - $file = $this->folder->get('avatar.' . $ext); + /** @var ISimpleFile $file */ + $file = $this->folder->getFile('avatar.' . $ext); $avatar->loadFromData($file->getContent()); if ($size !== -1) { $avatar->resize($size); @@ -201,9 +202,9 @@ class Avatar implements IAvatar { * @throws NotFoundException */ private function getExtension() { - if ($this->folder->nodeExists('avatar.jpg')) { + if ($this->folder->fileExists('avatar.jpg')) { return 'jpg'; - } elseif ($this->folder->nodeExists('avatar.png')) { + } elseif ($this->folder->fileExists('avatar.png')) { return 'png'; } throw new NotFoundException; diff --git a/lib/private/AvatarManager.php b/lib/private/AvatarManager.php index df3247b8f00..b8c6c2a1eb6 100644 --- a/lib/private/AvatarManager.php +++ b/lib/private/AvatarManager.php @@ -27,13 +27,12 @@ namespace OC; -use OCP\Files\Folder; +use OCP\Files\IAppData; use OCP\Files\NotFoundException; use OCP\IAvatarManager; use OCP\IConfig; use OCP\ILogger; use OCP\IUserManager; -use OCP\Files\IRootFolder; use OCP\IL10N; /** @@ -44,8 +43,8 @@ class AvatarManager implements IAvatarManager { /** @var IUserManager */ private $userManager; - /** @var IRootFolder */ - private $rootFolder; + /** @var IAppData */ + private $appData; /** @var IL10N */ private $l; @@ -60,19 +59,19 @@ class AvatarManager implements IAvatarManager { * AvatarManager constructor. * * @param IUserManager $userManager - * @param IRootFolder $rootFolder + * @param IAppData $appData * @param IL10N $l * @param ILogger $logger * @param IConfig $config */ public function __construct( IUserManager $userManager, - IRootFolder $rootFolder, + IAppData $appData, IL10N $l, ILogger $logger, IConfig $config) { $this->userManager = $userManager; - $this->rootFolder = $rootFolder; + $this->appData = $appData; $this->l = $l; $this->logger = $logger; $this->config = $config; @@ -95,20 +94,12 @@ class AvatarManager implements IAvatarManager { // sanitize userID - fixes casing issue (needed for the filesystem stuff that is done below) $userId = $user->getUID(); - /* - * Fix for #22119 - * Basically we do not want to copy the skeleton folder. - * - * For unit test purposes this is ignored when run in PHPUnit. - */ - if(!defined('PHPUNIT_RUN')) { - \OC\Files\Filesystem::initMountPoints($userId); + try { + $folder = $this->appData->getFolder($userId); + } catch (NotFoundException $e) { + $folder = $this->appData->newFolder($userId); } - $dir = '/' . $userId; - /** @var Folder $folder */ - $folder = $this->rootFolder->get($dir); - return new Avatar($folder, $this->l, $user, $this->logger, $this->config); } } diff --git a/lib/private/Files/AppData/AppData.php b/lib/private/Files/AppData/AppData.php new file mode 100644 index 00000000000..270e834b8e5 --- /dev/null +++ b/lib/private/Files/AppData/AppData.php @@ -0,0 +1,131 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.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\Files\AppData; + +use OC\Files\SimpleFS\SimpleFolder; +use OCP\Files\IAppData; +use OCP\Files\IRootFolder; +use OCP\Files\Folder; +use OC\SystemConfig; +use OCP\Files\Node; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; + +class AppData implements IAppData { + + /** @var IRootFolder */ + private $rootFolder; + + /** @var SystemConfig */ + private $config; + + /** @var string */ + private $appId; + + /** @var Folder */ + private $folder; + + /** + * AppData constructor. + * + * @param IRootFolder $rootFolder + * @param SystemConfig $systemConfig + * @param string $appId + */ + public function __construct(IRootFolder $rootFolder, + SystemConfig $systemConfig, + $appId) { + + $this->rootFolder = $rootFolder; + $this->config = $systemConfig; + $this->appId = $appId; + } + + /** + * @return Folder + * @throws \RuntimeException + */ + private function getAppDataFolder() { + if ($this->folder === null) { + $instanceId = $this->config->getValue('instanceid', null); + if ($instanceId === null) { + throw new \RuntimeException('no instance id!'); + } + + $name = 'appdata_' . $instanceId; + + try { + $appDataFolder = $this->rootFolder->get($name); + } catch (NotFoundException $e) { + try { + $appDataFolder = $this->rootFolder->newFolder($name); + } catch (NotPermittedException $e) { + throw new \RuntimeException('Could not get appdata folder'); + } + } + + try { + $appDataFolder = $appDataFolder->get($this->appId); + } catch (NotFoundException $e) { + try { + $appDataFolder = $appDataFolder->newFolder($this->appId); + } catch (NotPermittedException $e) { + throw new \RuntimeException('Could not get appdata folder for ' . $this->appId); + } + } + + $this->folder = $appDataFolder; + } + + return $this->folder; + } + + public function getFolder($name) { + $node = $this->getAppDataFolder()->get($name); + + /** @var Folder $node */ + return new SimpleFolder($node); + } + + public function newFolder($name) { + $folder = $this->getAppDataFolder()->newFolder($name); + + return new SimpleFolder($folder); + } + + public function getDirectoryListing() { + $listing = $this->getAppDataFolder()->getDirectoryListing(); + + $fileListing = array_map(function(Node $folder) { + if ($folder instanceof Folder) { + return new SimpleFolder($folder); + } + return null; + }, $listing); + + $fileListing = array_filter($fileListing); + + return array_values($fileListing); + } +} diff --git a/lib/private/Files/AppData/Factory.php b/lib/private/Files/AppData/Factory.php new file mode 100644 index 00000000000..85c75733796 --- /dev/null +++ b/lib/private/Files/AppData/Factory.php @@ -0,0 +1,50 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.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\Files\AppData; + +use OC\SystemConfig; +use OCP\Files\IRootFolder; + +class Factory { + + /** @var IRootFolder */ + private $rootFolder; + + /** @var SystemConfig */ + private $config; + + public function __construct(IRootFolder $rootFolder, + SystemConfig $systemConfig) { + + $this->rootFolder = $rootFolder; + $this->config = $systemConfig; + } + + /** + * @param string $appId + * @return AppData + */ + public function get($appId) { + return new AppData($this->rootFolder, $this->config, $appId); + } +} diff --git a/lib/private/Files/SimpleFS/SimpleFile.php b/lib/private/Files/SimpleFS/SimpleFile.php new file mode 100644 index 00000000000..5eadfd98b60 --- /dev/null +++ b/lib/private/Files/SimpleFS/SimpleFile.php @@ -0,0 +1,115 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.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\Files\SimpleFS; + +use OCP\Files\File; +use OCP\Files\NotPermittedException; +use OCP\Files\SimpleFS\ISimpleFile; + +class SimpleFile implements ISimpleFile { + + /** @var File $file */ + private $file; + + /** + * File constructor. + * + * @param File $file + */ + public function __construct(File $file) { + $this->file = $file; + } + + /** + * Get the name + * + * @return string + */ + public function getName() { + return $this->file->getName(); + } + + /** + * Get the size in bytes + * + * @return int + */ + public function getSize() { + return $this->file->getSize(); + } + + /** + * Get the ETag + * + * @return string + */ + public function getETag() { + return $this->file->getEtag(); + } + + /** + * Get the last modification time + * + * @return int + */ + public function getMTime() { + return $this->file->getMTime(); + } + + /** + * Get the content + * + * @return string + */ + public function getContent() { + return $this->file->getContent(); + } + + /** + * Overwrite the file + * + * @param string $data + * @throws NotPermittedException + */ + public function putContent($data) { + $this->file->putContent($data); + } + + /** + * Delete the file + * + * @throws NotPermittedException + */ + public function delete() { + $this->file->delete(); + } + + /** + * Get the MimeType + * + * @return string + */ + public function getMimeType() { + return $this->file->getMimeType(); + } +} diff --git a/lib/private/Files/SimpleFS/SimpleFolder.php b/lib/private/Files/SimpleFS/SimpleFolder.php new file mode 100644 index 00000000000..5b55fe0f157 --- /dev/null +++ b/lib/private/Files/SimpleFS/SimpleFolder.php @@ -0,0 +1,87 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.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\Files\SimpleFS; + +use OCP\Files\File; +use OCP\Files\Folder; +use OCP\Files\Node; +use OCP\Files\NotFoundException; +use OCP\Files\SimpleFS\ISimpleFolder; + +class SimpleFolder implements ISimpleFolder { + + /** @var Folder */ + private $folder; + + /** + * Folder constructor. + * + * @param Folder $folder + */ + public function __construct(Folder $folder) { + $this->folder = $folder; + } + + public function getName() { + return $this->folder->getName(); + } + + public function getDirectoryListing() { + $listing = $this->folder->getDirectoryListing(); + + $fileListing = array_map(function(Node $file) { + if ($file instanceof File) { + return new SimpleFile($file); + } + return null; + }, $listing); + + $fileListing = array_filter($fileListing); + + return array_values($fileListing); + } + + public function delete() { + $this->folder->delete(); + } + + public function fileExists($name) { + return $this->folder->nodeExists($name); + } + + public function getFile($name) { + $file = $this->folder->get($name); + + if (!($file instanceof File)) { + throw new NotFoundException(); + } + + return new SimpleFile($file); + } + + public function newFile($name) { + $file = $this->folder->newFile($name); + + return new SimpleFile($file); + } +} diff --git a/lib/private/Repair.php b/lib/private/Repair.php index bf441d03c35..2ba118b9c37 100644 --- a/lib/private/Repair.php +++ b/lib/private/Repair.php @@ -36,6 +36,7 @@ use OC\Repair\CleanTags; use OC\Repair\Collation; use OC\Repair\DropOldJobs; use OC\Repair\MoveUpdaterStepFile; +use OC\Repair\NC11\MoveAvatars; use OC\Repair\OldGroupMembershipShares; use OC\Repair\RemoveGetETagEntries; use OC\Repair\RemoveOldShares; @@ -149,6 +150,10 @@ class Repair implements IOutput{ \OC::$server->getGroupManager() ), new MoveUpdaterStepFile(\OC::$server->getConfig()), + new MoveAvatars( + \OC::$server->getJobList(), + \OC::$server->getSystemConfig() + ), ]; } diff --git a/lib/private/Repair/NC11/MoveAvatarBackgroundJob.php b/lib/private/Repair/NC11/MoveAvatarBackgroundJob.php new file mode 100644 index 00000000000..993235146c9 --- /dev/null +++ b/lib/private/Repair/NC11/MoveAvatarBackgroundJob.php @@ -0,0 +1,104 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.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\Repair\NC11; + +use OC\BackgroundJob\QueuedJob; +use OCP\Files\File; +use OCP\Files\Folder; +use OCP\Files\IAppData; +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; +use OCP\ILogger; +use OCP\IUser; +use OCP\IUserManager; + +class MoveAvatarsBackgroundJob extends QueuedJob { + + /** @var IUserManager */ + private $userManager; + + /** @var IRootFolder */ + private $rootFolder; + + /** @var IAppData */ + private $appData; + + /** @var ILogger */ + private $logger; + + /** + * MoveAvatars constructor. + */ + public function __construct() { + $this->userManager = \OC::$server->getUserManager(); + $this->rootFolder = \OC::$server->getRootFolder(); + $this->logger = \OC::$server->getLogger(); + $this->appData = \OC::$server->getAppDataDir('avatar'); + } + + public function run($arguments) { + $this->logger->info('Started migrating avatars to AppData folder'); + $this->moveAvatars(); + $this->logger->info('All avatars migrated to AppData folder'); + } + + private function moveAvatars() { + $counter = 0; + $this->userManager->callForAllUsers(function (IUser $user) use ($counter) { + if ($user->getLastLogin() !== 0) { + $uid = $user->getUID(); + + \OC\Files\Filesystem::initMountPoints($uid); + /** @var Folder $userFolder */ + $userFolder = $this->rootFolder->get($uid); + + try { + $userData = $this->appData->getFolder($uid); + } catch (NotFoundException $e) { + $userData = $this->appData->newFolder($uid); + } + + + $regex = '/^avatar\.([0-9]+\.)?(jpg|png)$/'; + $avatars = $userFolder->getDirectoryListing(); + + foreach ($avatars as $avatar) { + /** @var File $avatar */ + if (preg_match($regex, $avatar->getName())) { + /* + * This is not the most effective but it is the most abstract way + * to handle this. Avatars should be small anyways. + */ + $newAvatar = $userData->newFile($avatar->getName()); + $newAvatar->putContent($avatar->getContent()); + $avatar->delete(); + } + } + } + $counter++; + if ($counter % 100) { + $this->logger->info('{amount} avatars migrated', ['amount' => $counter]); + } + }); + } +} diff --git a/lib/private/Repair/NC11/MoveAvatars.php b/lib/private/Repair/NC11/MoveAvatars.php new file mode 100644 index 00000000000..44402b1be4f --- /dev/null +++ b/lib/private/Repair/NC11/MoveAvatars.php @@ -0,0 +1,64 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.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\Repair\NC11; + +use OC\SystemConfig; +use OCP\BackgroundJob\IJobList; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class MoveAvatars implements IRepairStep { + + /** @var IJobList */ + private $jobList; + + /** @var SystemConfig */ + private $systemConfig; + + /** + * MoveAvatars constructor. + * + * @param IJobList $jobList + * @param SystemConfig $systemConfig + */ + public function __construct(IJobList $jobList, + SystemConfig $systemConfig) { + $this->jobList = $jobList; + $this->systemConfig = $systemConfig; + } + + /** + * @return string + */ + public function getName() { + return 'Add mover avatar background job'; + } + + public function run(IOutput $output) { + if ($this->systemConfig->getValue('enable_avatars', true) === false) { + $output->info('Avatars are disabled'); + } else { + $this->jobList->add(MoveAvatarsBackgroundJob::class); + } + } +} diff --git a/lib/private/Server.php b/lib/private/Server.php index 494387ab6ca..b49e94b554e 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -359,7 +359,7 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService('AvatarManager', function (Server $c) { return new AvatarManager( $c->getUserManager(), - $c->getRootFolder(), + $c->getAppDataDir('avatar'), $c->getL10N('lib'), $c->getLogger(), $c->getConfig() @@ -742,6 +742,12 @@ class Server extends ServerContainer implements IServerContainer { ); return $manager; }); + $this->registerService(\OC\Files\AppData\Factory::class, function (Server $c) { + return new \OC\Files\AppData\Factory( + $c->getRootFolder(), + $c->getSystemConfig() + ); + }); } /** @@ -876,6 +882,7 @@ class Server extends ServerContainer implements IServerContainer { * Returns an app-specific view in ownClouds data directory * * @return \OCP\Files\Folder + * @deprecated since 9.2.0 use IAppData */ public function getAppFolder() { $dir = '/' . \OC_App::getCurrentApp(); @@ -1456,4 +1463,13 @@ class Server extends ServerContainer implements IServerContainer { public function getSettingsManager() { return $this->query('SettingsManager'); } + + /** + * @return \OCP\Files\IAppData + */ + public function getAppDataDir($app) { + /** @var \OC\Files\AppData\Factory $factory */ + $factory = $this->query(\OC\Files\AppData\Factory::class); + return $factory->get($app); + } } diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php index d964212f3bb..c3d2d1d6ad4 100644 --- a/lib/private/legacy/app.php +++ b/lib/private/legacy/app.php @@ -662,15 +662,16 @@ class OC_App { * Read all app metadata from the info.xml file * * @param string $appId id of the app or the path of the info.xml file - * @param boolean $path (optional) + * @param bool $path + * @param string $lang * @return array|null * @note all data is read from info.xml, not just pre-defined fields */ - public static function getAppInfo($appId, $path = false) { + public static function getAppInfo($appId, $path = false, $lang = null) { if ($path) { $file = $appId; } else { - if (isset(self::$appInfo[$appId])) { + if ($lang === null && isset(self::$appInfo[$appId])) { return self::$appInfo[$appId]; } $appPath = self::getAppPath($appId); @@ -684,7 +685,7 @@ class OC_App { $data = $parser->parse($file); if (is_array($data)) { - $data = OC_App::parseAppInfo($data); + $data = OC_App::parseAppInfo($data, $lang); } if(isset($data['ocsid'])) { $storedId = \OC::$server->getConfig()->getAppValue($appId, 'ocsid'); @@ -693,7 +694,9 @@ class OC_App { } } - self::$appInfo[$appId] = $data; + if ($lang === null) { + self::$appInfo[$appId] = $data; + } return $data; } @@ -843,11 +846,12 @@ class OC_App { //we don't want to show configuration for these $blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps(); $appList = array(); + $langCode = \OC::$server->getL10N('core')->getLanguageCode(); foreach ($installedApps as $app) { if (array_search($app, $blacklist) === false) { - $info = OC_App::getAppInfo($app); + $info = OC_App::getAppInfo($app, false, $langCode); if (!is_array($info)) { \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR); continue; @@ -1327,13 +1331,69 @@ class OC_App { } } + protected static function findBestL10NOption($options, $lang) { + $fallback = $similarLangFallback = $englishFallback = false; + + $lang = strtolower($lang); + $similarLang = $lang; + if (strpos($similarLang, '_')) { + // For "de_DE" we want to find "de" and the other way around + $similarLang = substr($lang, 0, strpos($lang, '_')); + } + + foreach ($options as $option) { + if (is_array($option)) { + if ($fallback === false) { + $fallback = $option['@value']; + } + + if (!isset($option['@attributes']['lang'])) { + continue; + } + + $attributeLang = strtolower($option['@attributes']['lang']); + if ($attributeLang === $lang) { + return $option['@value']; + } + + if ($attributeLang === $similarLang) { + $similarLangFallback = $option['@value']; + } else if (strpos($attributeLang, $similarLang . '_') === 0) { + if ($similarLangFallback === false) { + $similarLangFallback = $option['@value']; + } + } + } else { + $englishFallback = $option; + } + } + + if ($similarLangFallback !== false) { + return $similarLangFallback; + } else if ($englishFallback !== false) { + return $englishFallback; + } + return (string) $fallback; + } + /** * parses the app data array and enhanced the 'description' value * * @param array $data the app data + * @param string $lang * @return array improved app data */ - public static function parseAppInfo(array $data) { + public static function parseAppInfo(array $data, $lang = null) { + + if ($lang && isset($data['name']) && is_array($data['name'])) { + $data['name'] = self::findBestL10NOption($data['name'], $lang); + } + if ($lang && isset($data['summary']) && is_array($data['summary'])) { + $data['summary'] = self::findBestL10NOption($data['summary'], $lang); + } + if ($lang && isset($data['description']) && is_array($data['description'])) { + $data['description'] = self::findBestL10NOption($data['description'], $lang); + } // just modify the description if it is available // otherwise this will create a $data element with an empty 'description' diff --git a/lib/private/legacy/util.php b/lib/private/legacy/util.php index cb52949779f..b8f3a93ba50 100644 --- a/lib/private/legacy/util.php +++ b/lib/private/legacy/util.php @@ -311,10 +311,20 @@ class OC_Util { * * @param String $userId * @param \OCP\Files\Folder $userDirectory + * @throws \RuntimeException */ public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) { - $skeletonDirectory = \OCP\Config::getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton'); + $skeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton'); + $instanceId = \OC::$server->getConfig()->getSystemValue('instanceid', ''); + + if ($instanceId === null) { + throw new \RuntimeException('no instance id!'); + } + $appdata = 'appdata_' . $instanceId; + if ($userId === $appdata) { + throw new \RuntimeException('username is reserved name: ' . $appdata); + } if (!empty($skeletonDirectory)) { \OCP\Util::writeLog( diff --git a/lib/public/AppFramework/Http/FileDisplayResponse.php b/lib/public/AppFramework/Http/FileDisplayResponse.php index 22171e2b379..03a6fbec2dd 100644 --- a/lib/public/AppFramework/Http/FileDisplayResponse.php +++ b/lib/public/AppFramework/Http/FileDisplayResponse.php @@ -23,7 +23,6 @@ namespace OCP\AppFramework\Http; use OCP\AppFramework\Http; -use OCP\Files\File; /** * Class FileDisplayResponse @@ -33,18 +32,18 @@ use OCP\Files\File; */ class FileDisplayResponse extends Response implements ICallbackResponse { - /** @var File */ + /** @var \OCP\Files\File|\OCP\Files\SimpleFS\ISimpleFile */ private $file; /** * FileDisplayResponse constructor. * - * @param File $file + * @param \OCP\Files\File|\OCP\Files\SimpleFS\ISimpleFile $file * @param int $statusCode * @param array $headers * @since 9.2.0 */ - public function __construct(File $file, $statusCode=Http::STATUS_OK, + public function __construct($file, $statusCode=Http::STATUS_OK, $headers=[]) { $this->file = $file; $this->setStatus($statusCode); diff --git a/lib/public/Files/IAppData.php b/lib/public/Files/IAppData.php new file mode 100644 index 00000000000..92e54fee366 --- /dev/null +++ b/lib/public/Files/IAppData.php @@ -0,0 +1,36 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.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 OCP\Files; + +use OCP\Files\SimpleFS\ISimpleRoot; + +/** + * Interface IAppData + * + * @package OCP\Files + * @since 9.2.0 + * @internal This interface is experimental and might change for NC12 + */ +interface IAppData extends ISimpleRoot { + +} diff --git a/lib/public/Files/SimpleFS/ISimpleFile.php b/lib/public/Files/SimpleFS/ISimpleFile.php new file mode 100644 index 00000000000..efd682e7855 --- /dev/null +++ b/lib/public/Files/SimpleFS/ISimpleFile.php @@ -0,0 +1,100 @@ +<?php +/** + * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.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 OCP\Files\SimpleFS; + +use OCP\Files\NotPermittedException; + +/** + * Interface ISimpleFile + * + * @package OCP\Files\SimpleFS + * @since 9.2.0 + * @internal This interface is experimental and might change for NC12 + */ +interface ISimpleFile { + + /** + * Get the name + * + * @return string + * @since 9.2.0 + */ + public function getName(); + + /** + * Get the size in bytes + * + * @return int + * @since 9.2.0 + */ + public function getSize(); + + /** + * Get the ETag + * + * @return string + * @since 9.2.0 + */ + public function getETag(); + + /** + * Get the last modification time + * + * @return int + * @since 9.2.0 + */ + public function getMTime(); + + /** + * Get the content + * + * @return string + * @since 9.2.0 + */ + public function getContent(); + + /** + * Overwrite the file + * + * @param string $data + * @throws NotPermittedException + * @since 9.2.0 + */ + public function putContent($data); + + /** + * Delete the file + * + * @throws NotPermittedException + * @since 9.2.0 + */ + public function delete(); + + /** + * Get the MimeType + * + * @return string + * @since 9.2.0 + */ + public function getMimeType(); +} diff --git a/lib/public/Files/SimpleFS/ISimpleFolder.php b/lib/public/Files/SimpleFS/ISimpleFolder.php new file mode 100644 index 00000000000..406bb631159 --- /dev/null +++ b/lib/public/Files/SimpleFS/ISimpleFolder.php @@ -0,0 +1,88 @@ +<?php +/** + * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.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 OCP\Files\SimpleFS; + +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; + +/** + * Interface ISimpleFolder + * + * @package OCP\Files\SimpleFS + * @since 9.2.0 + * @internal This interface is experimental and might change for NC12 + */ +interface ISimpleFolder { + /** + * Get all the files in a folder + * + * @return ISimpleFile[] + * @since 9.2.0 + */ + public function getDirectoryListing(); + + /** + * Check if a file with $name exists + * + * @param string $name + * @return bool + * @since 9.2.0 + */ + public function fileExists($name); + + /** + * Get the file named $name from the folder + * + * @param string $name + * @return ISimpleFile + * @throws NotFoundException + * @since 9.2.0 + */ + public function getFile($name); + + /** + * Creates a new file with $name in the folder + * + * @param string $name + * @return ISimpleFile + * @throws NotPermittedException + * @since 9.2.0 + */ + public function newFile($name); + + /** + * Remove the folder and all the files in it + * + * @throws NotPermittedException + * @since 9.2.0 + */ + public function delete(); + + /** + * Get the folder name + * + * @return string + * @since 9.2.0 + */ + public function getName(); +} diff --git a/lib/public/Files/SimpleFS/ISimpleRoot.php b/lib/public/Files/SimpleFS/ISimpleRoot.php new file mode 100644 index 00000000000..c2f9d4ff05d --- /dev/null +++ b/lib/public/Files/SimpleFS/ISimpleRoot.php @@ -0,0 +1,67 @@ +<?php +/** + * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.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 OCP\Files\SimpleFS; + +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; + +/** + * Interface ISimpleRoot + * + * @package OCP\Files\SimpleFS + * @since 9.2.0 + * @internal This interface is experimental and might change for NC12 + */ +interface ISimpleRoot { + /** + * Get the folder with name $name + * + * @param string $name + * @return ISimpleFolder + * @throws NotFoundException + * @throws \RuntimeException + * @since 9.2.0 + */ + public function getFolder($name); + + /** + * Get all the Folders + * + * @return ISimpleFolder[] + * @throws NotFoundException + * @throws \RuntimeException + * @since 9.2.0 + */ + public function getDirectoryListing(); + + /** + * Create a new folder named $name + * + * @param string $name + * @return ISimpleFolder + * @throws NotPermittedException + * @throws \RuntimeException + * @since 9.2.0 + */ + public function newFolder($name); +} diff --git a/lib/public/IServerContainer.php b/lib/public/IServerContainer.php index b736af2899a..354e39bd8f9 100644 --- a/lib/public/IServerContainer.php +++ b/lib/public/IServerContainer.php @@ -115,6 +115,7 @@ interface IServerContainer { * * @return \OCP\Files\Folder * @since 6.0.0 + * @deprecated since 9.2.0 use IAppData */ public function getAppFolder(); diff --git a/settings/js/apps.js b/settings/js/apps.js index 99246f323fe..5119b35178e 100644 --- a/settings/js/apps.js +++ b/settings/js/apps.js @@ -183,6 +183,10 @@ OC.Settings.Apps = OC.Settings.Apps || { app.previewAsIcon = true; } + if (_.isArray(app.author)) { + app.author = app.author.join(', '); + } + var html = template(app); if (selector) { selector.html(html); @@ -513,6 +517,9 @@ OC.Settings.Apps = OC.Settings.Apps || { // Author Name apps = apps.concat(_.filter(OC.Settings.Apps.State.apps, function (app) { + if (_.isArray(app.author)) { + return app.author.join(', ').toLowerCase().indexOf(query) !== -1; + } return app.author.toLowerCase().indexOf(query) !== -1; })); diff --git a/tests/lib/AvatarManagerTest.php b/tests/lib/AvatarManagerTest.php index 0ad998af6d5..8ccc51d12e0 100644 --- a/tests/lib/AvatarManagerTest.php +++ b/tests/lib/AvatarManagerTest.php @@ -26,8 +26,8 @@ namespace Test; use OC\Avatar; use OC\AvatarManager; -use OCP\Files\Folder; -use OCP\Files\IRootFolder; +use OCP\Files\IAppData; +use OCP\Files\SimpleFS\ISimpleFolder; use OCP\IConfig; use OCP\IL10N; use OCP\ILogger; @@ -40,8 +40,8 @@ use OCP\IUserManager; class AvatarManagerTest extends \Test\TestCase { /** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */ private $userManager; - /** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */ - private $rootFolder; + /** @var IAppData|\PHPUnit_Framework_MockObject_MockObject */ + private $appData; /** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */ private $l10n; /** @var ILogger|\PHPUnit_Framework_MockObject_MockObject */ @@ -55,14 +55,14 @@ class AvatarManagerTest extends \Test\TestCase { parent::setUp(); $this->userManager = $this->createMock(IUserManager::class); - $this->rootFolder = $this->createMock(IRootFolder::class); + $this->appData = $this->createMock(IAppData::class); $this->l10n = $this->createMock(IL10N::class); $this->logger = $this->createMock(ILogger::class); $this->config = $this->createMock(IConfig::class); $this->avatarManager = new AvatarManager( $this->userManager, - $this->rootFolder, + $this->appData, $this->l10n, $this->logger, $this->config @@ -94,11 +94,11 @@ class AvatarManagerTest extends \Test\TestCase { ->method('get') ->with('valid-user') ->willReturn($user); - $folder = $this->createMock(Folder::class); - $this->rootFolder + $folder = $this->createMock(ISimpleFolder::class); + $this->appData ->expects($this->once()) - ->method('get') - ->with('/valid-user') + ->method('getFolder') + ->with('valid-user') ->willReturn($folder); $expected = new Avatar($folder, $this->l10n, $user, $this->logger, $this->config);; diff --git a/tests/lib/AvatarTest.php b/tests/lib/AvatarTest.php index 7f012c895fd..cea3f9bed1a 100644 --- a/tests/lib/AvatarTest.php +++ b/tests/lib/AvatarTest.php @@ -8,6 +8,8 @@ namespace Test; +use OC\Files\SimpleFS\SimpleFolder; +use OC\User\User; use OCP\Files\File; use OCP\Files\Folder; use OCP\IConfig; @@ -30,11 +32,11 @@ class AvatarTest extends \Test\TestCase { public function setUp() { parent::setUp(); - $this->folder = $this->createMock(Folder::class); + $this->folder = $this->createMock(SimpleFolder::class); /** @var \OCP\IL10N | \PHPUnit_Framework_MockObject_MockObject $l */ $l = $this->createMock(IL10N::class); $l->method('t')->will($this->returnArgument(0)); - $this->user = $this->getMockBuilder('OC\User\User')->disableOriginalConstructor()->getMock(); + $this->user = $this->createMock(User::class); $this->config = $this->createMock(IConfig::class); $this->avatar = new \OC\Avatar( @@ -51,7 +53,7 @@ class AvatarTest extends \Test\TestCase { } public function testGetAvatarSizeMatch() { - $this->folder->method('nodeExists') + $this->folder->method('fileExists') ->will($this->returnValueMap([ ['avatar.jpg', true], ['avatar.128.jpg', true], @@ -61,13 +63,13 @@ class AvatarTest extends \Test\TestCase { $file = $this->createMock(File::class); $file->method('getContent')->willReturn($expected->data()); - $this->folder->method('get')->with('avatar.128.jpg')->willReturn($file); + $this->folder->method('getFile')->with('avatar.128.jpg')->willReturn($file); $this->assertEquals($expected->data(), $this->avatar->get(128)->data()); } public function testGetAvatarSizeMinusOne() { - $this->folder->method('nodeExists') + $this->folder->method('fileExists') ->will($this->returnValueMap([ ['avatar.jpg', true], ])); @@ -76,13 +78,13 @@ class AvatarTest extends \Test\TestCase { $file = $this->createMock(File::class); $file->method('getContent')->willReturn($expected->data()); - $this->folder->method('get')->with('avatar.jpg')->willReturn($file); + $this->folder->method('getFile')->with('avatar.jpg')->willReturn($file); $this->assertEquals($expected->data(), $this->avatar->get(-1)->data()); } public function testGetAvatarNoSizeMatch() { - $this->folder->method('nodeExists') + $this->folder->method('fileExists') ->will($this->returnValueMap([ ['avatar.png', true], ['avatar.32.png', false], @@ -95,7 +97,7 @@ class AvatarTest extends \Test\TestCase { $file = $this->createMock(File::class); $file->method('getContent')->willReturn($expected->data()); - $this->folder->method('get') + $this->folder->method('getFile') ->will($this->returnCallback( function($path) use ($file) { if ($path === 'avatar.png') { @@ -126,7 +128,7 @@ class AvatarTest extends \Test\TestCase { } public function testExiststJPG() { - $this->folder->method('nodeExists') + $this->folder->method('fileExists') ->will($this->returnValueMap([ ['avatar.jpg', true], ['avatar.png', false], @@ -135,7 +137,7 @@ class AvatarTest extends \Test\TestCase { } public function testExistsPNG() { - $this->folder->method('nodeExists') + $this->folder->method('fileExists') ->will($this->returnValueMap([ ['avatar.jpg', false], ['avatar.png', true], diff --git a/tests/lib/Files/AppData/AppDataTest.php b/tests/lib/Files/AppData/AppDataTest.php new file mode 100644 index 00000000000..3247ce7ba99 --- /dev/null +++ b/tests/lib/Files/AppData/AppDataTest.php @@ -0,0 +1,121 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.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\Files\AppData; + +use OC\Files\AppData\AppData; +use OC\SystemConfig; +use OCP\Files\File; +use OCP\Files\Folder; +use OCP\Files\IAppData; +use OCP\Files\IRootFolder; +use OCP\Files\Node; +use OCP\Files\SimpleFS\ISimpleFolder; + +class AppDataTest extends \Test\TestCase { + /** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */ + private $rootFolder; + + /** @var SystemConfig|\PHPUnit_Framework_MockObject_MockObject */ + private $systemConfig; + + /** @var IAppData */ + private $appData; + + public function setUp() { + parent::setUp(); + + $this->rootFolder = $this->createMock(IRootFolder::class); + $this->systemConfig = $this->createMock(SystemConfig::class); + $this->appData = new AppData($this->rootFolder, $this->systemConfig, 'myApp'); + + $this->systemConfig->expects($this->any()) + ->method('getValue') + ->with('instanceid', null) + ->willReturn('iid'); + } + + private function setupAppFolder() { + $dataFolder = $this->createMock(Folder::class); + $appFolder = $this->createMock(Folder::class); + + $this->rootFolder->expects($this->once()) + ->method('get') + ->with($this->equalTo('appdata_iid')) + ->willReturn($dataFolder); + $dataFolder->expects($this->once()) + ->method('get') + ->with($this->equalTo('myApp')) + ->willReturn($appFolder); + + return [$dataFolder, $appFolder]; + } + + public function testGetFolder() { + $folders = $this->setupAppFolder(); + $appFolder = $folders[1]; + + $folder = $this->createMock(Folder::class); + + $appFolder->expects($this->once()) + ->method('get') + ->with($this->equalTo('folder')) + ->willReturn($folder); + + $result = $this->appData->getFolder('folder'); + $this->assertInstanceOf(ISimpleFolder::class, $result); + } + + public function testNewFolder() { + $folders = $this->setupAppFolder(); + $appFolder = $folders[1]; + + $folder = $this->createMock(Folder::class); + + $appFolder->expects($this->once()) + ->method('newFolder') + ->with($this->equalTo('folder')) + ->willReturn($folder); + + $result = $this->appData->newFolder('folder'); + $this->assertInstanceOf(ISimpleFolder::class, $result); + } + + public function testGetDirectoryListing() { + $folders = $this->setupAppFolder(); + $appFolder = $folders[1]; + + $file = $this->createMock(File::class); + $folder = $this->createMock(Folder::class); + $node = $this->createMock(Node::class); + + $appFolder->expects($this->once()) + ->method('getDirectoryListing') + ->willReturn([$file, $folder, $node]); + + $result = $this->appData->getDirectoryListing(); + + $this->assertCount(1, $result); + $this->assertInstanceOf(ISimpleFolder::class, $result[0]); + } + +} diff --git a/tests/lib/Files/AppData/FactoryTest.php b/tests/lib/Files/AppData/FactoryTest.php new file mode 100644 index 00000000000..75999c8c7da --- /dev/null +++ b/tests/lib/Files/AppData/FactoryTest.php @@ -0,0 +1,55 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.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\Files\AppData; + +use OC\Files\AppData\Factory; +use OC\SystemConfig; +use OCP\Files\IRootFolder; + +class FactoryTest extends \Test\TestCase { + /** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */ + private $rootFolder; + + /** @var SystemConfig|\PHPUnit_Framework_MockObject_MockObject */ + private $systemConfig; + + /** @var Factory */ + private $factory; + + public function setUp() { + parent::setUp(); + + $this->rootFolder = $this->createMock(IRootFolder::class); + $this->systemConfig = $this->createMock(SystemConfig::class); + $this->factory = new Factory($this->rootFolder, $this->systemConfig); + } + + public function testGet() { + $this->rootFolder->expects($this->never()) + ->method($this->anything()); + $this->systemConfig->expects($this->never()) + ->method($this->anything()); + + $this->factory->get('foo'); + } +} diff --git a/tests/lib/Files/SimpleFS/SimpleFileTest.php b/tests/lib/Files/SimpleFS/SimpleFileTest.php new file mode 100644 index 00000000000..4e623eafa22 --- /dev/null +++ b/tests/lib/Files/SimpleFS/SimpleFileTest.php @@ -0,0 +1,104 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.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\File\SimpleFS; + +use OC\Files\SimpleFS\SimpleFile; +use OCP\Files\File; + +class SimpleFileTest extends \Test\TestCase { + /** @var File|\PHPUnit_Framework_MockObject_MockObject */ + private $file; + + /** @var SimpleFile */ + private $simpleFile; + + public function setUp() { + parent::setUp(); + + $this->file = $this->createMock(File::class); + $this->simpleFile = new SimpleFile($this->file); + } + + public function testGetName() { + $this->file->expects($this->once()) + ->method('getName') + ->willReturn('myname'); + + $this->assertEquals('myname', $this->simpleFile->getName()); + } + + public function testGetSize() { + $this->file->expects($this->once()) + ->method('getSize') + ->willReturn(42); + + $this->assertEquals(42, $this->simpleFile->getSize()); + } + + public function testGetETag() { + $this->file->expects($this->once()) + ->method('getETag') + ->willReturn('etag'); + + $this->assertEquals('etag', $this->simpleFile->getETag()); + } + + public function testGetMTime() { + $this->file->expects($this->once()) + ->method('getMTime') + ->willReturn(101); + + $this->assertEquals(101, $this->simpleFile->getMTime()); + } + + public function testGetContent() { + $this->file->expects($this->once()) + ->method('getContent') + ->willReturn('foo'); + + $this->assertEquals('foo', $this->simpleFile->getContent()); + } + + public function testPutContent() { + $this->file->expects($this->once()) + ->method('putContent') + ->with($this->equalTo('bar')); + + $this->simpleFile->putContent('bar'); + } + + public function testDelete() { + $this->file->expects($this->once()) + ->method('delete'); + + $this->simpleFile->delete(); + } + + public function testGetMimeType() { + $this->file->expects($this->once()) + ->method('getMimeType') + ->willReturn('app/awesome'); + + $this->assertEquals('app/awesome', $this->simpleFile->getMimeType()); + } +} diff --git a/tests/lib/Files/SimpleFS/SimpleFolderTest.php b/tests/lib/Files/SimpleFS/SimpleFolderTest.php new file mode 100644 index 00000000000..d86c705d880 --- /dev/null +++ b/tests/lib/Files/SimpleFS/SimpleFolderTest.php @@ -0,0 +1,138 @@ +<?php +/** + * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.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\File\SimpleFS; + +use OC\Files\SimpleFS\SimpleFolder; +use OCP\Files\File; +use OCP\Files\Folder; +use OCP\Files\Node; +use OCP\Files\NotFoundException; +use OCP\Files\SimpleFS\ISimpleFile; + +class SimpleFolderTest extends \Test\TestCase { + /** @var Folder|\PHPUnit_Framework_MockObject_MockObject */ + private $folder; + + /** @var SimpleFolder */ + private $simpleFolder; + + public function setUp() { + parent::setUp(); + + $this->folder = $this->createMock(Folder::class); + $this->simpleFolder = new SimpleFolder($this->folder); + } + + public function testGetName() { + $this->folder->expects($this->once()) + ->method('getName') + ->willReturn('myname'); + + $this->assertEquals('myname', $this->simpleFolder->getName()); + } + + public function testDelete() { + $this->folder->expects($this->once()) + ->method('delete'); + + $this->simpleFolder->delete(); + } + + public function dataFileExists() { + return [ + [true], + [false], + ]; + } + + /** + * @dataProvider dataFileExists + * @param bool $exists + */ + public function testFileExists($exists) { + $this->folder->expects($this->once()) + ->method('nodeExists') + ->with($this->equalTo('file')) + ->willReturn($exists); + + $this->assertEquals($exists, $this->simpleFolder->fileExists('file')); + } + + public function dataGetFile() { + return [ + [File::class, false], + [Folder::class, true], + [Node::class, true], + ]; + } + + /** + * @dataProvider dataGetFile + * @param string $class + * @param bool $exception + */ + public function testGetFile($class, $exception) { + $node = $this->createMock($class); + + $this->folder->expects($this->once()) + ->method('get') + ->with($this->equalTo('file')) + ->willReturn($node); + + try { + $result = $this->simpleFolder->getFile('file'); + $this->assertFalse($exception); + $this->assertInstanceOf(ISimpleFile::class, $result); + } catch (NotFoundException $e) { + $this->assertTrue($exception); + } + } + + public function testNewFile() { + $file = $this->createMock(File::class); + + $this->folder->expects($this->once()) + ->method('newFile') + ->with($this->equalTo('file')) + ->willReturn($file); + + $result = $this->simpleFolder->newFile('file'); + $this->assertInstanceOf(ISimpleFile::class, $result); + } + + public function testGetDirectoryListing() { + $file = $this->createMock(File::class); + $folder = $this->createMock(Folder::class); + $node = $this->createMock(Node::class); + + $this->folder->expects($this->once()) + ->method('getDirectoryListing') + ->willReturn([$file, $folder, $node]); + + $result = $this->simpleFolder->getDirectoryListing(); + + $this->assertCount(1, $result); + $this->assertInstanceOf(ISimpleFile::class, $result[0]); + } + +} diff --git a/tests/lib/TestCase.php b/tests/lib/TestCase.php index 0389ef5d46b..7ccff382357 100644 --- a/tests/lib/TestCase.php +++ b/tests/lib/TestCase.php @@ -435,8 +435,10 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase { return true; } $annotations = $this->getAnnotations(); - if (isset($annotations['class']['group']) && in_array('DB', $annotations['class']['group'])) { - return true; + if (isset($annotations['class']['group'])) { + if(in_array('DB', $annotations['class']['group']) || in_array('SLOWDB', $annotations['class']['group']) ) { + return true; + } } return false; diff --git a/tests/phpunit-autotest.xml b/tests/phpunit-autotest.xml index 301f63a375d..3a82a56ebbf 100644 --- a/tests/phpunit-autotest.xml +++ b/tests/phpunit-autotest.xml @@ -29,10 +29,15 @@ <directory suffix=".php">../apps/files_trashbin/tests</directory> <directory suffix=".php">../apps/files_versions/tests</directory> <directory suffix=".php">../apps/provisioning_api/tests</directory> + <directory suffix=".php">../apps/systemtags/tests</directory> + <directory suffix=".php">../apps/theming/tests</directory> + <directory suffix=".php">../apps/twofactor_backupcodes/tests</directory> <directory suffix=".php">../apps/updatenotification/tests</directory> <directory suffix=".php">../apps/user_ldap/tests</directory> + <directory suffix=".php">../apps/workflowengine/tests</directory> <directory suffix=".php">../tests</directory> <directory suffix=".php">../build</directory> + <directory suffix=".php">../lib/composer</directory> </exclude> </whitelist> </filter> diff --git a/version.php b/version.php index 01a822708e3..96725a6bb4d 100644 --- a/version.php +++ b/version.php @@ -25,7 +25,7 @@ // We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades // between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel // when updating major/minor version number. -$OC_Version = array(9, 2, 0, 3); +$OC_Version = array(9, 2, 0, 4); // The human readable string $OC_VersionString = '11.0 alpha'; |