diff options
39 files changed, 1494 insertions, 130 deletions
diff --git a/apps/files/js/gotoplugin.js b/apps/files/js/gotoplugin.js index 69ec64b0266..4793420ed2d 100644 --- a/apps/files/js/gotoplugin.js +++ b/apps/files/js/gotoplugin.js @@ -40,6 +40,7 @@ type: OCA.Files.FileActions.TYPE_DROPDOWN, actionHandler: function (fileName, context) { var fileModel = context.fileInfoModel; + OC.Apps.hideAppSidebar($('.detailsView')); OCA.Files.App.setActiveView('files', {silent: true}); OCA.Files.App.fileList.changeDirectory(fileModel.get('path'), true, true).then(function() { OCA.Files.App.fileList.scrollTo(fileModel.get('name')); diff --git a/core/css/inputs.scss b/core/css/inputs.scss index e76e5bea150..13a164e13f2 100644 --- a/core/css/inputs.scss +++ b/core/css/inputs.scss @@ -206,7 +206,6 @@ input { height: 1px; overflow: hidden; + label { - padding: 6px 0; user-select: none; } &:disabled + label, @@ -220,8 +219,8 @@ input { width: 12px; vertical-align: middle; border-radius: 50%; - margin: 6px; - margin-top: -2px; + margin: 3px; + margin-top: 1px; border: 1px solid nc-lighten($color-main-text, 53%); } &:not(:disabled):not(:checked) + label:hover:before, diff --git a/lib/base.php b/lib/base.php index 1db6b84c5fb..3ca4775dbe2 100644 --- a/lib/base.php +++ b/lib/base.php @@ -935,14 +935,15 @@ class OC { // emergency app disabling if ($requestPath === '/disableapp' && $request->getMethod() === 'POST' - && ((string)$request->getParam('appid')) !== '' + && ((array)$request->getParam('appid')) !== '' ) { \OCP\JSON::callCheck(); \OCP\JSON::checkAdminUser(); - $appId = (string)$request->getParam('appid'); - $appId = \OC_App::cleanAppId($appId); - - \OC_App::disable($appId); + $appIds = (array)$request->getParam('appid'); + foreach($appIds as $appId) { + $appId = \OC_App::cleanAppId($appId); + \OC_App::disable($appId); + } \OC_JSON::success(); exit(); } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 9dea4d10fb2..23aff9df870 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -318,6 +318,12 @@ return array( 'OC\\AppFramework\\Utility\\TimeFactory' => $baseDir . '/lib/private/AppFramework/Utility/TimeFactory.php', 'OC\\AppHelper' => $baseDir . '/lib/private/AppHelper.php', 'OC\\App\\AppManager' => $baseDir . '/lib/private/App/AppManager.php', + 'OC\\App\\AppStore\\Bundles\\Bundle' => $baseDir . '/lib/private/App/AppStore/Bundles/Bundle.php', + 'OC\\App\\AppStore\\Bundles\\BundleFetcher' => $baseDir . '/lib/private/App/AppStore/Bundles/BundleFetcher.php', + 'OC\\App\\AppStore\\Bundles\\CoreBundle' => $baseDir . '/lib/private/App/AppStore/Bundles/CoreBundle.php', + 'OC\\App\\AppStore\\Bundles\\EnterpriseBundle' => $baseDir . '/lib/private/App/AppStore/Bundles/EnterpriseBundle.php', + 'OC\\App\\AppStore\\Bundles\\GroupwareBundle' => $baseDir . '/lib/private/App/AppStore/Bundles/GroupwareBundle.php', + 'OC\\App\\AppStore\\Bundles\\SocialSharingBundle' => $baseDir . '/lib/private/App/AppStore/Bundles/SocialSharingBundle.php', 'OC\\App\\AppStore\\Fetcher\\AppFetcher' => $baseDir . '/lib/private/App/AppStore/Fetcher/AppFetcher.php', 'OC\\App\\AppStore\\Fetcher\\CategoryFetcher' => $baseDir . '/lib/private/App/AppStore/Fetcher/CategoryFetcher.php', 'OC\\App\\AppStore\\Fetcher\\Fetcher' => $baseDir . '/lib/private/App/AppStore/Fetcher/Fetcher.php', @@ -727,6 +733,7 @@ return array( 'OC\\Repair\\NC11\\FixMountStorages' => $baseDir . '/lib/private/Repair/NC11/FixMountStorages.php', 'OC\\Repair\\NC11\\MoveAvatars' => $baseDir . '/lib/private/Repair/NC11/MoveAvatars.php', 'OC\\Repair\\NC11\\MoveAvatarsBackgroundJob' => $baseDir . '/lib/private/Repair/NC11/MoveAvatarsBackgroundJob.php', + 'OC\\Repair\\NC12\\InstallCoreBundle' => $baseDir . '/lib/private/Repair/NC12/InstallCoreBundle.php', 'OC\\Repair\\NC12\\UpdateLanguageCodes' => $baseDir . '/lib/private/Repair/NC12/UpdateLanguageCodes.php', 'OC\\Repair\\OldGroupMembershipShares' => $baseDir . '/lib/private/Repair/OldGroupMembershipShares.php', 'OC\\Repair\\RemoveRootShares' => $baseDir . '/lib/private/Repair/RemoveRootShares.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 11d949de34a..709d59ff3d0 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -348,6 +348,12 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\AppFramework\\Utility\\TimeFactory' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/TimeFactory.php', 'OC\\AppHelper' => __DIR__ . '/../../..' . '/lib/private/AppHelper.php', 'OC\\App\\AppManager' => __DIR__ . '/../../..' . '/lib/private/App/AppManager.php', + 'OC\\App\\AppStore\\Bundles\\Bundle' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Bundles/Bundle.php', + 'OC\\App\\AppStore\\Bundles\\BundleFetcher' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Bundles/BundleFetcher.php', + 'OC\\App\\AppStore\\Bundles\\CoreBundle' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Bundles/CoreBundle.php', + 'OC\\App\\AppStore\\Bundles\\EnterpriseBundle' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Bundles/EnterpriseBundle.php', + 'OC\\App\\AppStore\\Bundles\\GroupwareBundle' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Bundles/GroupwareBundle.php', + 'OC\\App\\AppStore\\Bundles\\SocialSharingBundle' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Bundles/SocialSharingBundle.php', 'OC\\App\\AppStore\\Fetcher\\AppFetcher' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Fetcher/AppFetcher.php', 'OC\\App\\AppStore\\Fetcher\\CategoryFetcher' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Fetcher/CategoryFetcher.php', 'OC\\App\\AppStore\\Fetcher\\Fetcher' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Fetcher/Fetcher.php', @@ -757,6 +763,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Repair\\NC11\\FixMountStorages' => __DIR__ . '/../../..' . '/lib/private/Repair/NC11/FixMountStorages.php', 'OC\\Repair\\NC11\\MoveAvatars' => __DIR__ . '/../../..' . '/lib/private/Repair/NC11/MoveAvatars.php', 'OC\\Repair\\NC11\\MoveAvatarsBackgroundJob' => __DIR__ . '/../../..' . '/lib/private/Repair/NC11/MoveAvatarsBackgroundJob.php', + 'OC\\Repair\\NC12\\InstallCoreBundle' => __DIR__ . '/../../..' . '/lib/private/Repair/NC12/InstallCoreBundle.php', 'OC\\Repair\\NC12\\UpdateLanguageCodes' => __DIR__ . '/../../..' . '/lib/private/Repair/NC12/UpdateLanguageCodes.php', 'OC\\Repair\\OldGroupMembershipShares' => __DIR__ . '/../../..' . '/lib/private/Repair/OldGroupMembershipShares.php', 'OC\\Repair\\RemoveRootShares' => __DIR__ . '/../../..' . '/lib/private/Repair/RemoveRootShares.php', diff --git a/lib/private/App/AppStore/Bundles/Bundle.php b/lib/private/App/AppStore/Bundles/Bundle.php new file mode 100644 index 00000000000..47efc4e0cce --- /dev/null +++ b/lib/private/App/AppStore/Bundles/Bundle.php @@ -0,0 +1,59 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @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\App\AppStore\Bundles; + +use OCP\IL10N; + +abstract class Bundle { + /** @var IL10N */ + protected $l10n; + + /** + * @param IL10N $l10n + */ + public function __construct(IL10N $l10n) { + $this->l10n = $l10n; + } + + /** + * Get the identifier of the bundle + * + * @return string + */ + public final function getIdentifier() { + return substr(strrchr(get_class($this), '\\'), 1); + } + + /** + * Get the name of the bundle + * + * @return string + */ + public abstract function getName(); + + /** + * Get the list of app identifiers in the bundle + * + * @return array + */ + public abstract function getAppIdentifiers(); +} diff --git a/lib/private/App/AppStore/Bundles/BundleFetcher.php b/lib/private/App/AppStore/Bundles/BundleFetcher.php new file mode 100644 index 00000000000..01cd4d6a518 --- /dev/null +++ b/lib/private/App/AppStore/Bundles/BundleFetcher.php @@ -0,0 +1,80 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @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\App\AppStore\Bundles; + +use OCP\IL10N; + +class BundleFetcher { + /** @var IL10N */ + private $l10n; + + /** + * @param IL10N $l10n + */ + public function __construct(IL10N $l10n) { + $this->l10n = $l10n; + } + + /** + * @return Bundle[] + */ + public function getBundles() { + return [ + new EnterpriseBundle($this->l10n), + new GroupwareBundle($this->l10n), + new SocialSharingBundle($this->l10n), + ]; + } + + /** + * Bundles that should be installed by default after installation + * + * @return Bundle[] + */ + public function getDefaultInstallationBundle() { + return [ + new CoreBundle($this->l10n), + ]; + } + + /** + * Get the bundle with the specified identifier + * + * @param string $identifier + * @return Bundle + * @throws \BadMethodCallException If the bundle does not exist + */ + public function getBundleByIdentifier($identifier) { + /** @var Bundle[] $bundles */ + $bundles = array_merge( + $this->getBundles(), + $this->getDefaultInstallationBundle() + ); + foreach($bundles as $bundle) { + if($bundle->getIdentifier() === $identifier) { + return $bundle; + } + } + + throw new \BadMethodCallException('Bundle with specified identifier does not exist'); + } +} diff --git a/lib/private/App/AppStore/Bundles/CoreBundle.php b/lib/private/App/AppStore/Bundles/CoreBundle.php new file mode 100644 index 00000000000..a87292b9ec9 --- /dev/null +++ b/lib/private/App/AppStore/Bundles/CoreBundle.php @@ -0,0 +1,42 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @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\App\AppStore\Bundles; + +class CoreBundle extends Bundle { + + /** + * {@inheritDoc} + */ + public function getName() { + return 'Core bundle'; + } + + /** + * {@inheritDoc} + */ + public function getAppIdentifiers() { + return [ + 'bruteforcesettings', + ]; + } + +} diff --git a/lib/private/App/AppStore/Bundles/EnterpriseBundle.php b/lib/private/App/AppStore/Bundles/EnterpriseBundle.php new file mode 100644 index 00000000000..6d43a6210fa --- /dev/null +++ b/lib/private/App/AppStore/Bundles/EnterpriseBundle.php @@ -0,0 +1,47 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @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\App\AppStore\Bundles; + +class EnterpriseBundle extends Bundle { + + /** + * {@inheritDoc} + */ + public function getName() { + return (string)$this->l10n->t('Enterprise bundle'); + } + + /** + * {@inheritDoc} + */ + public function getAppIdentifiers() { + return [ + 'admin_audit', + 'user_ldap', + 'files_retention', + 'files_automatedtagging', + 'user_saml', + 'files_accesscontrol', + ]; + } + +} diff --git a/lib/private/App/AppStore/Bundles/GroupwareBundle.php b/lib/private/App/AppStore/Bundles/GroupwareBundle.php new file mode 100644 index 00000000000..7e7414f69c7 --- /dev/null +++ b/lib/private/App/AppStore/Bundles/GroupwareBundle.php @@ -0,0 +1,44 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @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\App\AppStore\Bundles; + +class GroupwareBundle extends Bundle { + + /** + * {@inheritDoc} + */ + public function getName() { + return (string)$this->l10n->t('Groupware bundle'); + } + + /** + * {@inheritDoc} + */ + public function getAppIdentifiers() { + return [ + 'calendar', + 'contacts', + 'spreed', + ]; + } + +} diff --git a/lib/private/App/AppStore/Bundles/SocialSharingBundle.php b/lib/private/App/AppStore/Bundles/SocialSharingBundle.php new file mode 100644 index 00000000000..8da84e8d1ef --- /dev/null +++ b/lib/private/App/AppStore/Bundles/SocialSharingBundle.php @@ -0,0 +1,46 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @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\App\AppStore\Bundles; + +class SocialSharingBundle extends Bundle { + + /** + * {@inheritDoc} + */ + public function getName() { + return (string)$this->l10n->t('Social sharing bundle'); + } + + /** + * {@inheritDoc} + */ + public function getAppIdentifiers() { + return [ + 'socialsharing_twitter', + 'socialsharing_googleplus', + 'socialsharing_facebook', + 'socialsharing_email', + 'socialsharing_diaspora', + ]; + } + +} diff --git a/lib/private/Installer.php b/lib/private/Installer.php index 0d6030d5744..8702f264e54 100644 --- a/lib/private/Installer.php +++ b/lib/private/Installer.php @@ -42,6 +42,8 @@ namespace OC; use Doctrine\DBAL\Exception\TableExistsException; +use OC\App\AppManager; +use OC\App\AppStore\Bundles\Bundle; use OC\App\AppStore\Fetcher\AppFetcher; use OC\App\CodeChecker\CodeChecker; use OC\App\CodeChecker\EmptyCheck; @@ -50,7 +52,9 @@ use OC\Archive\TAR; use OC_App; use OC_DB; use OC_Helper; +use OCP\App\IAppManager; use OCP\Http\Client\IClientService; +use OCP\IConfig; use OCP\ILogger; use OCP\ITempManager; use phpseclib\File\X509; @@ -67,21 +71,26 @@ class Installer { private $tempManager; /** @var ILogger */ private $logger; + /** @var IConfig */ + private $config; /** * @param AppFetcher $appFetcher * @param IClientService $clientService * @param ITempManager $tempManager * @param ILogger $logger + * @param IConfig $config */ public function __construct(AppFetcher $appFetcher, IClientService $clientService, ITempManager $tempManager, - ILogger $logger) { + ILogger $logger, + IConfig $config) { $this->appFetcher = $appFetcher; $this->clientService = $clientService; $this->tempManager = $tempManager; $this->logger = $logger; + $this->config = $config; } /** @@ -109,6 +118,7 @@ class Installer { } } + \OC_App::registerAutoloading($appId, $basedir); \OC_App::setupBackgroundJobs($info['background-jobs']); //run appinfo/install.php @@ -420,6 +430,27 @@ class Installer { } /** + * Installs the app within the bundle and marks the bundle as installed + * + * @param Bundle $bundle + * @throws \Exception If app could not get installed + */ + public function installAppBundle(Bundle $bundle) { + $appIds = $bundle->getAppIdentifiers(); + foreach($appIds as $appId) { + if(!$this->isDownloaded($appId)) { + $this->downloadApp($appId); + } + $this->installApp($appId); + $app = new OC_App(); + $app->enable($appId); + } + $bundles = json_decode($this->config->getAppValue('core', 'installed.bundles', json_encode([])), true); + $bundles[] = $bundle->getIdentifier(); + $this->config->setAppValue('core', 'installed.bundles', json_encode($bundles)); + } + + /** * Installs shipped apps * * This function installs all apps found in the 'apps' directory that should be enabled by default; diff --git a/lib/private/Repair.php b/lib/private/Repair.php index e808774ec93..65e0342905a 100644 --- a/lib/private/Repair.php +++ b/lib/private/Repair.php @@ -30,12 +30,14 @@ namespace OC; +use OC\App\AppStore\Bundles\BundleFetcher; use OC\Repair\CleanTags; use OC\Repair\Collation; use OC\Repair\MoveUpdaterStepFile; use OC\Repair\NC11\CleanPreviews; use OC\Repair\NC11\FixMountStorages; use OC\Repair\NC11\MoveAvatars; +use OC\Repair\NC12\InstallCoreBundle; use OC\Repair\NC12\UpdateLanguageCodes; use OC\Repair\OldGroupMembershipShares; use OC\Repair\RemoveRootShares; @@ -136,6 +138,11 @@ class Repair implements IOutput{ ), new FixMountStorages(\OC::$server->getDatabaseConnection()), new UpdateLanguageCodes(\OC::$server->getDatabaseConnection(), \OC::$server->getConfig()), + new InstallCoreBundle( + \OC::$server->query(BundleFetcher::class), + \OC::$server->getConfig(), + \OC::$server->query(Installer::class) + ) ]; } diff --git a/lib/private/Repair/NC12/InstallCoreBundle.php b/lib/private/Repair/NC12/InstallCoreBundle.php new file mode 100644 index 00000000000..38583b09a89 --- /dev/null +++ b/lib/private/Repair/NC12/InstallCoreBundle.php @@ -0,0 +1,78 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @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\NC12; + +use OC\App\AppStore\Bundles\BundleFetcher; +use OC\Installer; +use OCP\IConfig; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class InstallCoreBundle implements IRepairStep { + /** @var BundleFetcher */ + private $bundleFetcher; + /** @var IConfig */ + private $config; + /** @var Installer */ + private $installer; + + /** + * @param BundleFetcher $bundleFetcher + * @param IConfig $config + * @param Installer $installer + */ + public function __construct(BundleFetcher $bundleFetcher, + IConfig $config, + Installer $installer) { + $this->bundleFetcher = $bundleFetcher; + $this->config = $config; + $this->installer = $installer; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'Install new core bundle components'; + } + + /** + * {@inheritdoc} + */ + public function run(IOutput $output) { + $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); + + if (version_compare($versionFromBeforeUpdate, '12.0.0.14', '>')) { + return; + } + + $defaultBundle = $this->bundleFetcher->getDefaultInstallationBundle(); + foreach($defaultBundle as $bundle) { + try { + $this->installer->installAppBundle($bundle); + $output->info('Successfully installed core app bundle.'); + } catch (\Exception $e) { + $output->warning('Could not install core app bundle: ' . $e->getMessage()); + } + } + } +} diff --git a/lib/private/Server.php b/lib/private/Server.php index 7724feb551b..b05e05660b0 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -43,6 +43,7 @@ namespace OC; use bantu\IniGetWrapper\IniGetWrapper; use OC\App\AppManager; +use OC\App\AppStore\Bundles\BundleFetcher; use OC\App\AppStore\Fetcher\AppFetcher; use OC\App\AppStore\Fetcher\CategoryFetcher; use OC\AppFramework\Http\Request; @@ -816,7 +817,12 @@ class Server extends ServerContainer implements IServerContainer { ); }); $this->registerAlias('MimeTypeLoader', \OCP\Files\IMimeTypeLoader::class); - + $this->registerService(BundleFetcher::class, function () { + return new BundleFetcher($this->getL10N('lib')); + }); + $this->registerService(AppFetcher::class, function() { + return $this->getAppFetcher(); + }); $this->registerService(\OCP\Notification\IManager::class, function (Server $c) { return new Manager( $c->query(IValidator::class) diff --git a/lib/private/Setup.php b/lib/private/Setup.php index e2806efad48..b1cf289d9aa 100644 --- a/lib/private/Setup.php +++ b/lib/private/Setup.php @@ -41,6 +41,7 @@ namespace OC; use bantu\IniGetWrapper\IniGetWrapper; use Exception; +use OC\App\AppStore\Bundles\BundleFetcher; use OCP\Defaults; use OCP\IL10N; use OCP\ILogger; @@ -63,11 +64,12 @@ class Setup { /** * @param SystemConfig $config * @param IniGetWrapper $iniWrapper + * @param IL10N $l10n * @param Defaults $defaults * @param ILogger $logger * @param ISecureRandom $random */ - function __construct(SystemConfig $config, + public function __construct(SystemConfig $config, IniGetWrapper $iniWrapper, IL10N $l10n, Defaults $defaults, @@ -364,8 +366,22 @@ class Setup { $group =\OC::$server->getGroupManager()->createGroup('admin'); $group->addUser($user); - //guess what this does + // Install shipped apps and specified app bundles Installer::installShippedApps(); + $installer = new Installer( + \OC::$server->getAppFetcher(), + \OC::$server->getHTTPClientService(), + \OC::$server->getTempManager(), + \OC::$server->getLogger(), + \OC::$server->getConfig() + ); + $bundleFetcher = new BundleFetcher(\OC::$server->getL10N('lib')); + $defaultInstallationBundles = $bundleFetcher->getDefaultInstallationBundle(); + foreach($defaultInstallationBundles as $bundle) { + try { + $installer->installAppBundle($bundle); + } catch (Exception $e) {} + } // create empty file in data dir, so we can later find // out that this is indeed an ownCloud data directory diff --git a/lib/private/Updater.php b/lib/private/Updater.php index 4427e4c48dc..c080ee0eb43 100644 --- a/lib/private/Updater.php +++ b/lib/private/Updater.php @@ -243,11 +243,11 @@ class Updater extends BasicEmitter { } // update all shipped apps - $disabledApps = $this->checkAppsRequirements(); + $this->checkAppsRequirements(); $this->doAppUpgrade(); // upgrade appstore apps - $this->upgradeAppStoreApps($disabledApps); + $this->upgradeAppStoreApps(\OC::$server->getAppManager()->getInstalledApps()); // install new shipped apps on upgrade OC_App::loadApps('authentication'); @@ -441,7 +441,8 @@ class Updater extends BasicEmitter { \OC::$server->getAppFetcher(), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), - $this->log + $this->log, + \OC::$server->getConfig() ); if (Installer::isUpdateAvailable($app, \OC::$server->getAppFetcher())) { $this->emit('\OC\Updater', 'upgradeAppStoreApp', [$app]); diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php index 3800b8b770e..111da7d0d40 100644 --- a/lib/private/legacy/app.php +++ b/lib/private/legacy/app.php @@ -365,7 +365,8 @@ class OC_App { \OC::$server->getAppFetcher(), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), - \OC::$server->getLogger() + \OC::$server->getLogger(), + \OC::$server->getConfig() ); $isDownloaded = $installer->isDownloaded($appId); @@ -427,7 +428,8 @@ class OC_App { \OC::$server->getAppFetcher(), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), - \OC::$server->getLogger() + \OC::$server->getLogger(), + \OC::$server->getConfig() ); return $installer->removeApp($app); } diff --git a/lib/private/legacy/util.php b/lib/private/legacy/util.php index 9516a67af48..d49599cb8a1 100644 --- a/lib/private/legacy/util.php +++ b/lib/private/legacy/util.php @@ -1388,6 +1388,12 @@ class OC_Util { if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) { return false; } + + // detect part files + if (preg_match('/' . \OCP\Files\FileInfo::BLACKLIST_FILES_REGEX . '/', $trimmed) !== 0) { + return false; + } + foreach (str_split($trimmed) as $char) { if (strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) { return false; diff --git a/settings/Controller/AppSettingsController.php b/settings/Controller/AppSettingsController.php index 7be6c2bf562..ac77b2e7dd6 100644 --- a/settings/Controller/AppSettingsController.php +++ b/settings/Controller/AppSettingsController.php @@ -27,6 +27,7 @@ namespace OC\Settings\Controller; +use OC\App\AppStore\Bundles\BundleFetcher; use OC\App\AppStore\Fetcher\AppFetcher; use OC\App\AppStore\Fetcher\CategoryFetcher; use OC\App\AppStore\Version\VersionParser; @@ -50,6 +51,7 @@ class AppSettingsController extends Controller { const CAT_ENABLED = 0; const CAT_DISABLED = 1; const CAT_ALL_INSTALLED = 2; + const CAT_APP_BUNDLES = 3; /** @var \OCP\IL10N */ private $l10n; @@ -65,6 +67,8 @@ class AppSettingsController extends Controller { private $appFetcher; /** @var IFactory */ private $l10nFactory; + /** @var BundleFetcher */ + private $bundleFetcher; /** * @param string $appName @@ -76,6 +80,7 @@ class AppSettingsController extends Controller { * @param CategoryFetcher $categoryFetcher * @param AppFetcher $appFetcher * @param IFactory $l10nFactory + * @param BundleFetcher $bundleFetcher */ public function __construct($appName, IRequest $request, @@ -85,7 +90,8 @@ class AppSettingsController extends Controller { IAppManager $appManager, CategoryFetcher $categoryFetcher, AppFetcher $appFetcher, - IFactory $l10nFactory) { + IFactory $l10nFactory, + BundleFetcher $bundleFetcher) { parent::__construct($appName, $request); $this->l10n = $l10n; $this->config = $config; @@ -94,6 +100,7 @@ class AppSettingsController extends Controller { $this->categoryFetcher = $categoryFetcher; $this->appFetcher = $appFetcher; $this->l10nFactory = $l10nFactory; + $this->bundleFetcher = $bundleFetcher; } /** @@ -120,18 +127,14 @@ class AppSettingsController extends Controller { return $templateResponse; } - /** - * Get all available categories - * - * @return JSONResponse - */ - public function listCategories() { + private function getAllCategories() { $currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2); $formattedCategories = [ ['id' => self::CAT_ALL_INSTALLED, 'ident' => 'installed', 'displayName' => (string)$this->l10n->t('Your apps')], ['id' => self::CAT_ENABLED, 'ident' => 'enabled', 'displayName' => (string)$this->l10n->t('Enabled apps')], ['id' => self::CAT_DISABLED, 'ident' => 'disabled', 'displayName' => (string)$this->l10n->t('Disabled apps')], + ['id' => self::CAT_APP_BUNDLES, 'ident' => 'app-bundles', 'displayName' => (string)$this->l10n->t('App bundles')], ]; $categories = $this->categoryFetcher->get(); foreach($categories as $category) { @@ -142,7 +145,16 @@ class AppSettingsController extends Controller { ]; } - return new JSONResponse($formattedCategories); + return $formattedCategories; + } + + /** + * Get all available categories + * + * @return JSONResponse + */ + public function listCategories() { + return new JSONResponse($this->getAllCategories()); } /** @@ -334,6 +346,41 @@ class AppSettingsController extends Controller { return ($a < $b) ? -1 : 1; }); break; + case 'app-bundles': + $bundles = $this->bundleFetcher->getBundles(); + $apps = []; + foreach($bundles as $bundle) { + $newCategory = true; + $allApps = $appClass->listAllApps(); + $categories = $this->getAllCategories(); + foreach($categories as $singleCategory) { + $newApps = $this->getAppsForCategory($singleCategory['id']); + foreach($allApps as $app) { + foreach($newApps as $key => $newApp) { + if($app['id'] === $newApp['id']) { + unset($newApps[$key]); + } + } + } + $allApps = array_merge($allApps, $newApps); + } + + foreach($bundle->getAppIdentifiers() as $identifier) { + foreach($allApps as $app) { + if($app['id'] === $identifier) { + if($newCategory) { + $app['newCategory'] = true; + $app['categoryName'] = $bundle->getName(); + } + $app['bundleId'] = $bundle->getIdentifier(); + $newCategory = false; + $apps[] = $app; + continue; + } + } + } + } + break; default: $apps = $this->getAppsForCategory($category); diff --git a/settings/ajax/disableapp.php b/settings/ajax/disableapp.php index 8edd1c1453e..9b76236a15b 100644 --- a/settings/ajax/disableapp.php +++ b/settings/ajax/disableapp.php @@ -36,8 +36,9 @@ if (!array_key_exists('appid', $_POST)) { exit; } -$appId = (string)$_POST['appid']; -$appId = OC_App::cleanAppId($appId); - -OC_App::disable($appId); +$appIds = (array)$_POST['appid']; +foreach($appIds as $appId) { + $appId = OC_App::cleanAppId($appId); + OC_App::disable($appId); +} OC_JSON::success(); diff --git a/settings/ajax/enableapp.php b/settings/ajax/enableapp.php index b6d62671a63..4c4fa0be666 100644 --- a/settings/ajax/enableapp.php +++ b/settings/ajax/enableapp.php @@ -36,13 +36,20 @@ if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay } $groups = isset($_POST['groups']) ? (array)$_POST['groups'] : null; +$appIds = isset($_POST['appIds']) ? (array)$_POST['appIds'] : []; try { - $app = new OC_App(); - $appId = (string)$_POST['appid']; - $appId = OC_App::cleanAppId($appId); - $app->enable($appId, $groups); - OC_JSON::success(['data' => ['update_required' => \OC_App::shouldUpgrade($appId)]]); + $updateRequired = false; + foreach($appIds as $appId) { + $app = new OC_App(); + $appId = OC_App::cleanAppId($appId); + $app->enable($appId, $groups); + if(\OC_App::shouldUpgrade($appId)) { + $updateRequired = true; + } + } + + OC_JSON::success(['data' => ['update_required' => $updateRequired]]); } catch (Exception $e) { \OCP\Util::writeLog('core', $e->getMessage(), \OCP\Util::ERROR); OC_JSON::error(array("data" => array("message" => $e->getMessage()) )); diff --git a/settings/ajax/updateapp.php b/settings/ajax/updateapp.php index 3020f828577..bcf8e149140 100644 --- a/settings/ajax/updateapp.php +++ b/settings/ajax/updateapp.php @@ -44,7 +44,8 @@ try { \OC::$server->getAppFetcher(), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), - \OC::$server->getLogger() + \OC::$server->getLogger(), + \OC::$server->getConfig() ); $result = $installer->updateAppstoreApp($appId); $config->setSystemValue('maintenance', false); diff --git a/settings/css/settings.css b/settings/css/settings.css index 7e91877773e..0a1d4e046fe 100644 --- a/settings/css/settings.css +++ b/settings/css/settings.css @@ -519,13 +519,37 @@ input.userFilter {width: 200px;} /* APPS */ +/* Bundle header */ +#apps-list .apps-header { + display: table-row; + position: relative; +} +#apps-list .apps-header div { + display: table-cell; + height: 70px; +} +#apps-list .apps-header h2 { + display: table-cell; + position: absolute; + padding-left: 6px; + padding-top: 15px; +} +#apps-list .apps-header h2 .enable { + position: relative; + top: -1px; + margin-left: 12px; +} +#apps-list .apps-header h2 + .section { + margin-top: 50px; +} + #app-content > svg.app-filter { float: left; height: 0; width: 0; } -#app-category-disabled { +#app-category-app-bundles { margin-bottom: 20px; } @@ -558,6 +582,10 @@ span.version { border-radius: 3px; padding: 3px 6px; } +.app-level a { + padding: 10px; + white-space: nowrap; +} .app-level .official { border-color: #37ce02; background-position: left center; @@ -737,6 +765,7 @@ form.section { display: table; width: 100%; height: auto; + margin-bottom: 100px; } #apps-list.installed .section { diff --git a/settings/js/apps.js b/settings/js/apps.js index 3326886951f..6bad2cc842c 100644 --- a/settings/js/apps.js +++ b/settings/js/apps.js @@ -29,6 +29,7 @@ OC.Settings.Apps = OC.Settings.Apps || { State: { currentCategory: null, + currentCategoryElements: null, apps: null, $updateNotification: null, availableUpdates: 0 @@ -90,14 +91,15 @@ OC.Settings.Apps = OC.Settings.Apps || { }), { type:'GET', success: function (apps) { + OC.Settings.Apps.State.currentCategoryElements = apps.apps; var appListWithIndex = _.indexBy(apps.apps, 'id'); OC.Settings.Apps.State.apps = appListWithIndex; var appList = _.map(appListWithIndex, function(app) { // default values for missing fields return _.extend({level: 0}, app); }); - var source - if (categoryId === 'enabled' || categoryId === 'disabled' || categoryId === 'installed') { + var source; + if (categoryId === 'enabled' || categoryId === 'disabled' || categoryId === 'installed' || categoryId === 'app-bundles') { source = $("#app-template-installed").html(); $('#apps-list').addClass('installed'); } else { @@ -107,17 +109,19 @@ OC.Settings.Apps = OC.Settings.Apps || { var template = Handlebars.compile(source); if (appList.length) { - appList.sort(function(a,b) { - if (a.active !== b.active) { - return (a.active ? -1 : 1) - } else { - var levelDiff = b.level - a.level; - if (levelDiff === 0) { - return OC.Util.naturalSortCompare(a.name, b.name); + if(categoryId !== 'app-bundles') { + appList.sort(function (a, b) { + if (a.active !== b.active) { + return (a.active ? -1 : 1) + } else { + var levelDiff = b.level - a.level; + if (levelDiff === 0) { + return OC.Util.naturalSortCompare(a.name, b.name); + } + return levelDiff; } - return levelDiff; - } - }); + }); + } var firstExperimental = false; _.each(appList, function(app) { @@ -303,56 +307,126 @@ OC.Settings.Apps = OC.Settings.Apps || { return $.get(OC.generateUrl('apps/files')); }, - enableApp:function(appId, active, element, groups) { + enableAppBundle:function(bundleId, active, element, groups) { + if (OC.PasswordConfirmation.requiresPasswordConfirmation()) { + OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this.enableAppBundle, this, bundleId, active, element, groups)); + return; + } + + var apps = OC.Settings.Apps.State.currentCategoryElements; + var appsToEnable = []; + apps.forEach(function(app) { + if(app['bundleId'] === bundleId) { + if(app['active'] === false) { + appsToEnable.push(app['id']); + } + } + }); + + OC.Settings.Apps.enableApp(appsToEnable, false, groups); + }, + + /** + * @param {string[]} appId + * @param {boolean} active + * @param {array} groups + */ + enableApp:function(appId, active, groups) { if (OC.PasswordConfirmation.requiresPasswordConfirmation()) { - OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this.enableApp, this, appId, active, element, groups)); + OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this.enableApp, this, appId, active, groups)); return; } + var elements = []; + appId.forEach(function(appId) { + elements.push($('#app-'+appId+' .enable')); + }); + var self = this; - OC.Settings.Apps.hideErrorMessage(appId); + appId.forEach(function(appId) { + OC.Settings.Apps.hideErrorMessage(appId); + }); groups = groups || []; - var appItem = $('div#app-'+appId+''); + var appItems = []; + appId.forEach(function(appId) { + appItems.push($('div#app-'+appId+'')); + }); + if(active && !groups.length) { - element.val(t('settings','Disabling app …')); + elements.forEach(function(element) { + element.val(t('settings','Disabling app …')); + }); $.post(OC.filePath('settings','ajax','disableapp.php'),{appid:appId},function(result) { if(!result || result.status !== 'success') { if (result.data && result.data.message) { OC.Settings.Apps.showErrorMessage(appId, result.data.message); - appItem.data('errormsg', result.data.message); + appItems.forEach(function(appItem) { + appItem.data('errormsg', result.data.message); + }) } else { OC.Settings.Apps.showErrorMessage(appId, t('settings', 'Error while disabling app')); - appItem.data('errormsg', t('settings', 'Error while disabling app')); + appItems.forEach(function(appItem) { + appItem.data('errormsg', t('settings', 'Error while disabling app')); + }); } - element.val(t('settings','Disable')); - appItem.addClass('appwarning'); + elements.forEach(function(element) { + element.val(t('settings','Disable')); + }); + appItems.forEach(function(appItem) { + appItem.addClass('appwarning'); + }); } else { OC.Settings.Apps.rebuildNavigation(); - appItem.data('active',false); - appItem.data('groups', ''); - element.data('active',false); - appItem.removeClass('active'); - element.val(t('settings','Enable')); - element.parent().find(".groups-enable").hide(); - element.parent().find('#group_select').hide().val(null); + appItems.forEach(function(appItem) { + appItem.data('active', false); + appItem.data('groups', ''); + }); + elements.forEach(function(element) { + element.data('active', false); + }); + appItems.forEach(function(appItem) { + appItem.removeClass('active'); + }); + elements.forEach(function(element) { + element.val(t('settings', 'Enable')); + element.parent().find(".groups-enable").hide(); + element.parent().find('#group_select').hide().val(null); + }); OC.Settings.Apps.State.apps[appId].active = false; } },'json'); } else { // TODO: display message to admin to not refresh the page! // TODO: lock UI to prevent further operations - element.val(t('settings','Enabling app …')); - $.post(OC.filePath('settings','ajax','enableapp.php'),{appid: appId, groups: groups},function(result) { + elements.forEach(function(element) { + element.val(t('settings', 'Enabling app …')); + }); + + var appIdArray = []; + if( typeof appId === 'string' ) { + appIdArray = [appId]; + } else { + appIdArray = appId; + } + $.post(OC.filePath('settings','ajax','enableapp.php'),{appIds: appIdArray, groups: groups},function(result) { if(!result || result.status !== 'success') { if (result.data && result.data.message) { OC.Settings.Apps.showErrorMessage(appId, result.data.message); - appItem.data('errormsg', result.data.message); + appItems.forEach(function(appItem) { + appItem.data('errormsg', result.data.message); + }); } else { OC.Settings.Apps.showErrorMessage(appId, t('settings', 'Error while enabling app')); - appItem.data('errormsg', t('settings', 'Error while disabling app')); + appItems.forEach(function(appItem) { + appItem.data('errormsg', t('settings', 'Error while disabling app')); + }); } - element.val(t('settings','Enable')); - appItem.addClass('appwarning'); + elements.forEach(function(element) { + element.val(t('settings', 'Enable')); + }); + appItems.forEach(function(appItem) { + appItem.addClass('appwarning'); + }); } else { self._checkServerHealth().done(function() { if (result.data.update_required) { @@ -364,24 +438,40 @@ OC.Settings.Apps = OC.Settings.Apps || { } OC.Settings.Apps.rebuildNavigation(); - appItem.data('active',true); - element.data('active',true); - appItem.addClass('active'); - element.val(t('settings','Disable')); + appItems.forEach(function(appItem) { + appItem.data('active', true); + }); + elements.forEach(function(element) { + element.data('active', true); + }); + appItems.forEach(function(appItem) { + appItem.addClass('active'); + }); + elements.forEach(function(element) { + element.val(t('settings', 'Disable')); + }); var app = OC.Settings.Apps.State.apps[appId]; app.active = true; if (OC.Settings.Apps.isType(app, 'filesystem') || OC.Settings.Apps.isType(app, 'prelogin') || OC.Settings.Apps.isType(app, 'authentication') || OC.Settings.Apps.isType(app, 'logging')) { - element.parent().find(".groups-enable").prop('checked', true); - element.parent().find(".groups-enable").hide(); - element.parent().find('#group_select').hide().val(null); + elements.forEach(function(element) { + element.parent().find(".groups-enable").prop('checked', true); + element.parent().find(".groups-enable").hide(); + element.parent().find('#group_select').hide().val(null); + }); } else { - element.parent().find("#groups-enable").show(); + elements.forEach(function(element) { + element.parent().find("#groups-enable").show(); + }); if (groups) { - appItem.data('groups', JSON.stringify(groups)); + appItems.forEach(function(appItem) { + appItem.data('groups', JSON.stringify(groups)); + }); } else { - appItem.data('groups', ''); + appItems.forEach(function(appItem) { + appItem.data('groups', ''); + }); } } }).fail(function() { @@ -391,26 +481,40 @@ OC.Settings.Apps = OC.Settings.Apps || { appId, t('settings', 'Error: this app cannot be enabled because it makes the server unstable') ); - appItem.data('errormsg', t('settings', 'Error while enabling app')); - element.val(t('settings','Enable')); - appItem.addClass('appwarning'); + appItems.forEach(function(appItem) { + appItem.data('errormsg', t('settings', 'Error while enabling app')); + }); + elements.forEach(function(element) { + element.val(t('settings', 'Enable')); + }); + appItems.forEach(function(appItem) { + appItem.addClass('appwarning'); + }); }).fail(function() { OC.Settings.Apps.showErrorMessage( appId, t('settings', 'Error: could not disable broken app') ); - appItem.data('errormsg', t('settings', 'Error while disabling broken app')); - element.val(t('settings','Enable')); + appItems.forEach(function(appItem) { + appItem.data('errormsg', t('settings', 'Error while disabling broken app')); + }); + elements.forEach(function(element) { + element.val(t('settings', 'Enable')); + }); }); }); } },'json') .fail(function() { OC.Settings.Apps.showErrorMessage(appId, t('settings', 'Error while enabling app')); - appItem.data('errormsg', t('settings', 'Error while enabling app')); - appItem.data('active',false); - appItem.addClass('appwarning'); - element.val(t('settings','Enable')); + appItems.forEach(function(appItem) { + appItem.data('errormsg', t('settings', 'Error while enabling app')); + appItem.data('active', false); + appItem.addClass('appwarning'); + }); + elements.forEach(function(element) { + element.val(t('settings', 'Enable')); + }); }); } }, @@ -774,10 +878,17 @@ OC.Settings.Apps = OC.Settings.Apps || { $(document).on('click', '#apps-list input.enable', function () { var appId = $(this).data('appid'); + var bundleId = $(this).data('bundleid'); var element = $(this); var active = $(this).data('active'); - OC.Settings.Apps.enableApp(appId, active, element); + var category = $('#app-navigation').attr('data-category'); + if(bundleId) { + OC.Settings.Apps.enableAppBundle(bundleId, active, element); + element.val(t('settings', 'Enable all')); + } else { + OC.Settings.Apps.enableApp([appId], active); + } }); $(document).on('click', '#apps-list input.uninstall', function () { @@ -805,7 +916,7 @@ OC.Settings.Apps = OC.Settings.Apps || { var appId = element.data('appid'); if (appId) { - OC.Settings.Apps.enableApp(appId, false, element, groups); + OC.Settings.Apps.enableApp([appId], false, groups); OC.Settings.Apps.State.apps[appId].groups = groups; } }); diff --git a/settings/templates/apps.php b/settings/templates/apps.php index 310513722cf..260b042c078 100644 --- a/settings/templates/apps.php +++ b/settings/templates/apps.php @@ -29,8 +29,17 @@ script( <?php endif; ?> </script> - <script id="app-template-installed" type="text/x-handlebars"> +{{#if newCategory}} +<div class="apps-header"> + <div class="app-image"></div> + <h2>{{categoryName}} <input class="enable" type="submit" data-bundleid="{{bundleId}}" data-active="true" value="<?php p($l->t('Enable all'));?>"/></h2> + <div class="app-version"></div> + <div class="app-level"></div> + <div class="app-groups"></div> + <div class="actions"> </div> +</div> +{{/if}} <div class="section" id="app-{{id}}"> <div class="app-image app-image-icon"></div> <div class="app-name"> diff --git a/tests/Settings/Controller/AppSettingsControllerTest.php b/tests/Settings/Controller/AppSettingsControllerTest.php index 14dc33ca191..9633c771596 100644 --- a/tests/Settings/Controller/AppSettingsControllerTest.php +++ b/tests/Settings/Controller/AppSettingsControllerTest.php @@ -22,6 +22,7 @@ namespace Tests\Settings\Controller; +use OC\App\AppStore\Bundles\BundleFetcher; use OC\App\AppStore\Fetcher\AppFetcher; use OC\App\AppStore\Fetcher\CategoryFetcher; use OC\Settings\Controller\AppSettingsController; @@ -60,6 +61,8 @@ class AppSettingsControllerTest extends TestCase { private $appFetcher; /** @var IFactory|\PHPUnit_Framework_MockObject_MockObject */ private $l10nFactory; + /** @var BundleFetcher|\PHPUnit_Framework_MockObject_MockObject */ + private $bundleFetcher; public function setUp() { parent::setUp(); @@ -75,6 +78,7 @@ class AppSettingsControllerTest extends TestCase { $this->categoryFetcher = $this->createMock(CategoryFetcher::class); $this->appFetcher = $this->createMock(AppFetcher::class); $this->l10nFactory = $this->createMock(IFactory::class); + $this->bundleFetcher = $this->createMock(BundleFetcher::class); $this->appSettingsController = new AppSettingsController( 'settings', @@ -85,7 +89,8 @@ class AppSettingsControllerTest extends TestCase { $this->appManager, $this->categoryFetcher, $this->appFetcher, - $this->l10nFactory + $this->l10nFactory, + $this->bundleFetcher ); } @@ -107,6 +112,11 @@ class AppSettingsControllerTest extends TestCase { 'displayName' => 'Disabled apps', ], [ + 'id' => 3, + 'ident' => 'app-bundles', + 'displayName' => 'App bundles', + ], + [ 'id' => 'auth', 'ident' => 'auth', 'displayName' => 'Authentication & authorization', diff --git a/tests/acceptance/features/app-files.feature b/tests/acceptance/features/app-files.feature index 7adc618e02e..6779b37e145 100644 --- a/tests/acceptance/features/app-files.feature +++ b/tests/acceptance/features/app-files.feature @@ -1,5 +1,28 @@ Feature: app-files + Scenario: viewing a favorite file in its folder closes the details view + Given I am logged in + And I mark "welcome.txt" as favorite + And I see that "welcome.txt" is marked as favorite + And I open the "Favorites" section + And I open the details view for "welcome.txt" + And I see that the details view for "Favorites" section is open + When I view "welcome.txt" in folder + Then I see that the current section is "All files" + And I see that the details view is closed + + Scenario: viewing a favorite file in its folder does not prevent opening the details view in "All files" section + Given I am logged in + And I mark "welcome.txt" as favorite + And I see that "welcome.txt" is marked as favorite + And I open the "Favorites" section + And I open the details view for "welcome.txt" + And I see that the details view for "Favorites" section is open + And I view "welcome.txt" in folder + And I see that the current section is "All files" + When I open the details view for "welcome.txt" + Then I see that the details view for "All files" section is open + Scenario: set a password to a shared link Given I am logged in And I share the link for "welcome.txt" diff --git a/tests/acceptance/features/bootstrap/FilesAppContext.php b/tests/acceptance/features/bootstrap/FilesAppContext.php index 7e7f592a44e..bc926fbe52f 100644 --- a/tests/acceptance/features/bootstrap/FilesAppContext.php +++ b/tests/acceptance/features/bootstrap/FilesAppContext.php @@ -28,6 +28,55 @@ class FilesAppContext implements Context, ActorAwareInterface { use ActorAware; /** + * @return array + */ + public static function sections() { + return [ "All files" => "files", + "Recent" => "recent", + "Favorites" => "favorites", + "Shared with you" => "sharingin", + "Shared with others" => "sharingout", + "Shared by link" => "sharinglinks", + "Tags" => "systemtagsfilter", + "Deleted files" => "trashbin" ]; + } + + /** + * @return Locator + */ + public static function appNavigation() { + return Locator::forThe()->id("app-navigation")-> + describedAs("App navigation"); + } + + /** + * @return Locator + */ + public static function appNavigationSectionItemFor($sectionText) { + return Locator::forThe()->xpath("//li[normalize-space() = '$sectionText']")-> + descendantOf(self::appNavigation())-> + describedAs($sectionText . " section item in App Navigation"); + } + + /** + * @return Locator + */ + public static function appNavigationCurrentSectionItem() { + return Locator::forThe()->css(".active")->descendantOf(self::appNavigation())-> + describedAs("Current section item in App Navigation"); + } + + /** + * @return Locator + */ + public static function mainViewForSection($section) { + $sectionId = self::sections()[$section]; + + return Locator::forThe()->id("app-content-$sectionId")-> + describedAs("Main view for section $section in Files app"); + } + + /** * @return Locator */ public static function currentSectionMainView() { @@ -38,6 +87,15 @@ class FilesAppContext implements Context, ActorAwareInterface { /** * @return Locator */ + public static function detailsViewForSection($section) { + return Locator::forThe()->xpath("/preceding-sibling::*[position() = 1 and @id = 'app-sidebar']")-> + descendantOf(self::mainViewForSection($section))-> + describedAs("Details view for section $section in Files app"); + } + + /** + * @return Locator + */ public static function currentSectionDetailsView() { return Locator::forThe()->xpath("/preceding-sibling::*[position() = 1 and @id = 'app-sidebar']")-> descendantOf(self::currentSectionMainView())-> @@ -96,12 +154,88 @@ class FilesAppContext implements Context, ActorAwareInterface { /** * @return Locator */ + public static function favoriteActionForFile($fileName) { + return Locator::forThe()->css(".action-favorite")->descendantOf(self::rowForFile($fileName))-> + describedAs("Favorite action for file $fileName in Files app"); + } + + /** + * @return Locator + */ + public static function favoritedStateIconForFile($fileName) { + return Locator::forThe()->content("Favorited")->descendantOf(self::favoriteActionForFile($fileName))-> + describedAs("Favorited state icon for file $fileName in Files app"); + } + + /** + * @return Locator + */ + public static function mainLinkForFile($fileName) { + return Locator::forThe()->css(".name")->descendantOf(self::rowForFile($fileName))-> + describedAs("Main link for file $fileName in Files app"); + } + + /** + * @return Locator + */ public static function shareActionForFile($fileName) { return Locator::forThe()->css(".action-share")->descendantOf(self::rowForFile($fileName))-> describedAs("Share action for file $fileName in Files app"); } /** + * @return Locator + */ + public static function fileActionsMenuButtonForFile($fileName) { + return Locator::forThe()->css(".action-menu")->descendantOf(self::rowForFile($fileName))-> + describedAs("File actions menu button for file $fileName in Files app"); + } + + /** + * @return Locator + */ + public static function fileActionsMenu() { + return Locator::forThe()->css(".fileActionsMenu")-> + describedAs("File actions menu in Files app"); + } + + /** + * @return Locator + */ + public static function viewFileInFolderMenuItem() { + return self::fileActionsMenuItemFor("View in folder"); + } + + /** + * @return Locator + */ + private static function fileActionsMenuItemFor($itemText) { + return Locator::forThe()->content($itemText)->descendantOf(self::fileActionsMenu())-> + describedAs($itemText . " item in file actions menu in Files app"); + } + + /** + * @Given I open the :section section + */ + public function iOpenTheSection($section) { + $this->actor->find(self::appNavigationSectionItemFor($section), 10)->click(); + } + + /** + * @Given I open the details view for :fileName + */ + public function iOpenTheDetailsViewFor($fileName) { + $this->actor->find(self::mainLinkForFile($fileName), 10)->click(); + } + + /** + * @Given I mark :fileName as favorite + */ + public function iMarkAsFavorite($fileName) { + $this->actor->find(self::favoriteActionForFile($fileName), 10)->click(); + } + + /** * @Given I share the link for :fileName */ public function iShareTheLinkFor($fileName) { @@ -118,6 +252,15 @@ class FilesAppContext implements Context, ActorAwareInterface { } /** + * @When I view :fileName in folder + */ + public function iViewInFolder($fileName) { + $this->actor->find(self::fileActionsMenuButtonForFile($fileName), 10)->click(); + + $this->actor->find(self::viewFileInFolderMenuItem(), 2)->click(); + } + + /** * @When I protect the shared link with the password :password */ public function iProtectTheSharedLinkWithThePassword($password) { @@ -136,6 +279,53 @@ class FilesAppContext implements Context, ActorAwareInterface { } /** + * @Then I see that the current section is :section + */ + public function iSeeThatTheCurrentSectionIs($section) { + PHPUnit_Framework_Assert::assertEquals($this->actor->find(self::appNavigationCurrentSectionItem(), 10)->getText(), $section); + } + + /** + * @Then I see that the details view for :section section is open + */ + public function iSeeThatTheDetailsViewForSectionIsOpen($section) { + PHPUnit_Framework_Assert::assertTrue( + $this->actor->find(self::detailsViewForSection($section), 10)->isVisible()); + + $otherSections = self::sections(); + unset($otherSections[$section]); + + $this->assertDetailsViewForSectionsAreClosed($otherSections); + } + + /** + * @Then I see that the details view is closed + */ + public function iSeeThatTheDetailsViewIsClosed() { + PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::currentSectionMainView(), 10)); + + $this->assertDetailsViewForSectionsAreClosed(self::sections()); + } + + private function assertDetailsViewForSectionsAreClosed($sections) { + foreach ($sections as $section => $id) { + try { + PHPUnit_Framework_Assert::assertFalse( + $this->actor->find(self::detailsViewForSection($section))->isVisible(), + "Details view for section $section is open but it should be closed"); + } catch (NoSuchElementException $exception) { + } + } + } + + /** + * @Then I see that :fileName is marked as favorite + */ + public function iSeeThatIsMarkedAsFavorite($fileName) { + PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::favoritedStateIconForFile($fileName), 10)); + } + + /** * @Then I see that the working icon for password protect is shown */ public function iSeeThatTheWorkingIconForPasswordProtectIsShown() { diff --git a/tests/lib/App/AppStore/Bundles/BundleBase.php b/tests/lib/App/AppStore/Bundles/BundleBase.php new file mode 100644 index 00000000000..23af1cda927 --- /dev/null +++ b/tests/lib/App/AppStore/Bundles/BundleBase.php @@ -0,0 +1,60 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @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\App\AppStore\Bundles; + +use OC\App\AppStore\Bundles\Bundle; +use OCP\IL10N; +use Test\TestCase; + +abstract class BundleBase extends TestCase { + /** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */ + protected $l10n; + /** @var Bundle */ + protected $bundle; + /** @var string */ + protected $bundleIdentifier; + /** @var string */ + protected $bundleName; + /** @var array */ + protected $bundleAppIds; + + public function setUp() { + parent::setUp(); + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->method('t') + ->will($this->returnCallback(function ($text, $parameters = []) { + return vsprintf($text, $parameters); + })); + } + + public function testGetIdentifier() { + $this->assertSame($this->bundleIdentifier, $this->bundle->getIdentifier()); + } + + public function testGetName() { + $this->assertSame($this->bundleName, $this->bundle->getName()); + } + + public function testGetAppIdentifiers() { + $this->assertSame($this->bundleAppIds, $this->bundle->getAppIdentifiers()); + } +} diff --git a/tests/lib/App/AppStore/Bundles/BundleFetcherTest.php b/tests/lib/App/AppStore/Bundles/BundleFetcherTest.php new file mode 100644 index 00000000000..71f9820fc72 --- /dev/null +++ b/tests/lib/App/AppStore/Bundles/BundleFetcherTest.php @@ -0,0 +1,78 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @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\App\AppStore\Bundles; + +use OC\App\AppStore\Bundles\BundleFetcher; +use OC\App\AppStore\Bundles\CoreBundle; +use OC\App\AppStore\Bundles\EnterpriseBundle; +use OC\App\AppStore\Bundles\GroupwareBundle; +use OC\App\AppStore\Bundles\SocialSharingBundle; +use OCP\IL10N; +use Test\TestCase; + +class BundleFetcherTest extends TestCase { + /** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */ + private $l10n; + /** @var BundleFetcher */ + private $bundleFetcher; + + public function setUp() { + parent::setUp(); + + $this->l10n = $this->createMock(IL10N::class); + + $this->bundleFetcher = new BundleFetcher( + $this->l10n + ); + } + + public function testGetBundles() { + $expected = [ + new EnterpriseBundle($this->l10n), + new GroupwareBundle($this->l10n), + new SocialSharingBundle($this->l10n), + ]; + $this->assertEquals($expected, $this->bundleFetcher->getBundles()); + } + + public function testGetDefaultInstallationBundle() { + $expected = [ + new CoreBundle($this->l10n), + ]; + $this->assertEquals($expected, $this->bundleFetcher->getDefaultInstallationBundle()); + } + + public function testGetBundleByIdentifier() { + $this->assertEquals(new EnterpriseBundle($this->l10n), $this->bundleFetcher->getBundleByIdentifier('EnterpriseBundle')); + $this->assertEquals(new CoreBundle($this->l10n), $this->bundleFetcher->getBundleByIdentifier('CoreBundle')); + $this->assertEquals(new GroupwareBundle($this->l10n), $this->bundleFetcher->getBundleByIdentifier('GroupwareBundle')); + } + + /** + * @expectedException \BadMethodCallException + * @expectedExceptionMessage Bundle with specified identifier does not exist + */ + public function testGetBundleByIdentifierWithException() { + $this->bundleFetcher->getBundleByIdentifier('NotExistingBundle'); + } + +} diff --git a/tests/lib/App/AppStore/Bundles/CoreBundleTest.php b/tests/lib/App/AppStore/Bundles/CoreBundleTest.php new file mode 100644 index 00000000000..235e2ec84fe --- /dev/null +++ b/tests/lib/App/AppStore/Bundles/CoreBundleTest.php @@ -0,0 +1,36 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @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\App\AppStore\Bundles; + +use OC\App\AppStore\Bundles\CoreBundle; + +class CoreBundleTest extends BundleBase { + public function setUp() { + parent::setUp(); + $this->bundle = new CoreBundle($this->l10n); + $this->bundleIdentifier = 'CoreBundle'; + $this->bundleName = 'Core bundle'; + $this->bundleAppIds = [ + 'bruteforcesettings', + ]; + } +} diff --git a/tests/lib/App/AppStore/Bundles/EnterpriseBundleTest.php b/tests/lib/App/AppStore/Bundles/EnterpriseBundleTest.php new file mode 100644 index 00000000000..e75486b3ed5 --- /dev/null +++ b/tests/lib/App/AppStore/Bundles/EnterpriseBundleTest.php @@ -0,0 +1,41 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @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\App\AppStore\Bundles; + +use OC\App\AppStore\Bundles\EnterpriseBundle; + +class EnterpriseBundleTest extends BundleBase { + public function setUp() { + parent::setUp(); + $this->bundle = new EnterpriseBundle($this->l10n); + $this->bundleIdentifier = 'EnterpriseBundle'; + $this->bundleName = 'Enterprise bundle'; + $this->bundleAppIds = [ + 'admin_audit', + 'user_ldap', + 'files_retention', + 'files_automatedtagging', + 'user_saml', + 'files_accesscontrol', + ]; + } +} diff --git a/tests/lib/App/AppStore/Bundles/GroupwareBundleTest.php b/tests/lib/App/AppStore/Bundles/GroupwareBundleTest.php new file mode 100644 index 00000000000..f2f9dcc5ccc --- /dev/null +++ b/tests/lib/App/AppStore/Bundles/GroupwareBundleTest.php @@ -0,0 +1,38 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @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\App\AppStore\Bundles; + +use OC\App\AppStore\Bundles\GroupwareBundle; + +class GroupwareBundleTest extends BundleBase { + public function setUp() { + parent::setUp(); + $this->bundle = new GroupwareBundle($this->l10n); + $this->bundleIdentifier = 'GroupwareBundle'; + $this->bundleName = 'Groupware bundle'; + $this->bundleAppIds = [ + 'calendar', + 'contacts', + 'spreed', + ]; + } +} diff --git a/tests/lib/App/AppStore/Bundles/SocialSharingBundleTest.php b/tests/lib/App/AppStore/Bundles/SocialSharingBundleTest.php new file mode 100644 index 00000000000..02ea0eb6ae5 --- /dev/null +++ b/tests/lib/App/AppStore/Bundles/SocialSharingBundleTest.php @@ -0,0 +1,40 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @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\App\AppStore\Bundles; + +use OC\App\AppStore\Bundles\SocialSharingBundle; + +class SocialSharingBundleTest extends BundleBase { + public function setUp() { + parent::setUp(); + $this->bundle = new SocialSharingBundle($this->l10n); + $this->bundleIdentifier = 'SocialSharingBundle'; + $this->bundleName = 'Social sharing bundle'; + $this->bundleAppIds = [ + 'socialsharing_twitter', + 'socialsharing_googleplus', + 'socialsharing_facebook', + 'socialsharing_email', + 'socialsharing_diaspora', + ]; + } +} diff --git a/tests/lib/InstallerTest.php b/tests/lib/InstallerTest.php index d1923970588..a31c8826bd9 100644 --- a/tests/lib/InstallerTest.php +++ b/tests/lib/InstallerTest.php @@ -9,11 +9,13 @@ namespace Test; +use OC\App\AppStore\Bundles\Bundle; use OC\App\AppStore\Fetcher\AppFetcher; use OC\Archive\ZIP; use OC\Installer; use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; +use OCP\IConfig; use OCP\ILogger; use OCP\ITempManager; @@ -29,6 +31,8 @@ class InstallerTest extends TestCase { private $tempManager; /** @var ILogger|\PHPUnit_Framework_MockObject_MockObject */ private $logger; + /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ + private $config; /** @var Installer */ private $installer; @@ -40,11 +44,13 @@ class InstallerTest extends TestCase { $this->clientService = $this->createMock(IClientService::class); $this->tempManager = $this->createMock(ITempManager::class); $this->logger = $this->createMock(ILogger::class); + $this->config = $this->createMock(IConfig::class); $this->installer = new Installer( $this->appFetcher, $this->clientService, $this->tempManager, - $this->logger + $this->logger, + $this->config ); $config = \OC::$server->getConfig(); @@ -54,7 +60,8 @@ class InstallerTest extends TestCase { \OC::$server->getAppFetcher(), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), - \OC::$server->getLogger() + \OC::$server->getLogger(), + $config ); $installer->removeApp(self::$appid); } @@ -64,7 +71,8 @@ class InstallerTest extends TestCase { \OC::$server->getAppFetcher(), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), - \OC::$server->getLogger() + \OC::$server->getLogger(), + \OC::$server->getConfig() ); $installer->removeApp(self::$appid); \OC::$server->getConfig()->setSystemValue('appstoreenabled', $this->appstore); @@ -86,7 +94,8 @@ class InstallerTest extends TestCase { \OC::$server->getAppFetcher(), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), - \OC::$server->getLogger() + \OC::$server->getLogger(), + \OC::$server->getConfig() ); $installer->installApp(self::$appid); $isInstalled = Installer::isInstalled(self::$appid); diff --git a/tests/lib/Repair/NC12/InstallCoreBundleTest.php b/tests/lib/Repair/NC12/InstallCoreBundleTest.php new file mode 100644 index 00000000000..3a72934df86 --- /dev/null +++ b/tests/lib/Repair/NC12/InstallCoreBundleTest.php @@ -0,0 +1,144 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @author Lukas Reschke <lukas@statuscode.ch> + * + * @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\Repair\NC12; + +use OC\App\AppStore\Bundles\Bundle; +use OC\App\AppStore\Bundles\BundleFetcher; +use OC\Installer; +use OC\Repair\NC12\InstallCoreBundle; +use OCP\IConfig; +use OCP\Migration\IOutput; +use Test\TestCase; + + +class InstallCoreBundleTest extends TestCase { + /** @var BundleFetcher|\PHPUnit_Framework_MockObject_MockObject */ + private $bundleFetcher; + /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ + private $config; + /** @var Installer|\PHPUnit_Framework_MockObject_MockObject */ + private $installer; + /** @var InstallCoreBundle */ + private $installCoreBundle; + + public function setUp() { + parent::setUp(); + $this->bundleFetcher = $this->createMock(BundleFetcher::class); + $this->config = $this->createMock(IConfig::class); + $this->installer = $this->createMock(Installer::class); + + $this->installCoreBundle = new InstallCoreBundle( + $this->bundleFetcher, + $this->config, + $this->installer + ); + } + + public function testGetName() { + $this->assertSame('Install new core bundle components', $this->installCoreBundle->getName()); + } + + public function testRunOlder() { + $this->config + ->expects($this->once()) + ->method('getSystemValue') + ->with('version', '0.0.0') + ->willReturn('12.0.0.15'); + $this->bundleFetcher + ->expects($this->never()) + ->method('getDefaultInstallationBundle'); + /** @var IOutput|\PHPUnit_Framework_MockObject_MockObject $output */ + $output = $this->createMock(IOutput::class); + $output + ->expects($this->never()) + ->method('info'); + $output + ->expects($this->never()) + ->method('warning'); + + $this->installCoreBundle->run($output); + } + + public function testRunWithException() { + $this->config + ->expects($this->once()) + ->method('getSystemValue') + ->with('version', '0.0.0') + ->willReturn('12.0.0.14'); + $bundle = $this->createMock(Bundle::class); + $this->bundleFetcher + ->expects($this->once()) + ->method('getDefaultInstallationBundle') + ->willReturn([ + $bundle, + ]); + $this->installer + ->expects($this->once()) + ->method('installAppBundle') + ->with($bundle) + ->willThrowException(new \Exception('ExceptionText')); + /** @var IOutput|\PHPUnit_Framework_MockObject_MockObject $output */ + $output = $this->createMock(IOutput::class); + $output + ->expects($this->never()) + ->method('info'); + $output + ->expects($this->once()) + ->method('warning') + ->with('Could not install core app bundle: ExceptionText'); + + $this->installCoreBundle->run($output); + } + + public function testRun() { + $this->config + ->expects($this->once()) + ->method('getSystemValue') + ->with('version', '0.0.0') + ->willReturn('12.0.0.14'); + $bundle = $this->createMock(Bundle::class); + $this->bundleFetcher + ->expects($this->once()) + ->method('getDefaultInstallationBundle') + ->willReturn([ + $bundle, + ]); + $this->installer + ->expects($this->once()) + ->method('installAppBundle') + ->with($bundle); + /** @var IOutput|\PHPUnit_Framework_MockObject_MockObject $output */ + $output = $this->createMock(IOutput::class); + $output + ->expects($this->once()) + ->method('info') + ->with('Successfully installed core app bundle.'); + $output + ->expects($this->never()) + ->method('warning'); + + $this->installCoreBundle->run($output); + } + +} diff --git a/tests/lib/UtilTest.php b/tests/lib/UtilTest.php index 278e6cfd4ce..39a29742e4f 100644 --- a/tests/lib/UtilTest.php +++ b/tests/lib/UtilTest.php @@ -204,41 +204,51 @@ class UtilTest extends \Test\TestCase { } public function filenameValidationProvider() { - return array( + return [ // valid names - array('boringname', true), - array('something.with.extension', true), - array('now with spaces', true), - array('.a', true), - array('..a', true), - array('.dotfile', true), - array('single\'quote', true), - array(' spaces before', true), - array('spaces after ', true), - array('allowed chars including the crazy ones $%&_-^@!,()[]{}=;#', true), - array('汉字也能用', true), - array('und Ümläüte sind auch willkommen', true), + ['boringname', true], + ['something.with.extension', true], + ['now with spaces', true], + ['.a', true], + ['..a', true], + ['.dotfile', true], + ['single\'quote', true], + [' spaces before', true], + ['spaces after ', true], + ['allowed chars including the crazy ones $%&_-^@!,()[]{}=;#', true], + ['汉字也能用', true], + ['und Ümläüte sind auch willkommen', true], // disallowed names - array('', false), - array(' ', false), - array('.', false), - array('..', false), - array('back\\slash', false), - array('sl/ash', false), - array('lt<lt', true), - array('gt>gt', true), - array('col:on', true), - array('double"quote', true), - array('pi|pe', true), - array('dont?ask?questions?', true), - array('super*star', true), - array('new\nline', false), + ['', false], + [' ', false], + ['.', false], + ['..', false], + ['back\\slash', false], + ['sl/ash', false], + ['lt<lt', true], + ['gt>gt', true], + ['col:on', true], + ['double"quote', true], + ['pi|pe', true], + ['dont?ask?questions?', true], + ['super*star', true], + ['new\nline', false], + // better disallow these to avoid unexpected trimming to have side effects - array(' ..', false), - array('.. ', false), - array('. ', false), - array(' .', false), - ); + [' ..', false], + ['.. ', false], + ['. ', false], + [' .', false], + + // part files not allowed + ['.part', false], + ['notallowed.part', false], + ['neither.filepart', false], + + // part in the middle is ok + ['super movie part one.mkv', true], + ['super.movie.part.mkv', true], + ]; } /** diff --git a/version.php b/version.php index 0d1d327cb7f..011c693d7b3 100644 --- a/version.php +++ b/version.php @@ -26,7 +26,7 @@ // between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel // when updating major/minor version number. -$OC_Version = array(12, 0, 0, 14); +$OC_Version = array(12, 0, 0, 15); // The human readable string $OC_VersionString = '12.0 alpha'; |