diff options
-rw-r--r-- | apps/theming/lib/Settings/Section.php | 83 | ||||
-rw-r--r-- | apps/theming/tests/ServicesTest.php | 4 | ||||
-rw-r--r-- | apps/theming/tests/Settings/SectionTest.php | 77 | ||||
-rw-r--r-- | core/css/styles.scss | 8 | ||||
-rw-r--r-- | lib/private/Settings/Manager.php | 5 | ||||
-rw-r--r-- | lib/private/Settings/Personal/PersonalInfo.php | 44 | ||||
-rw-r--r-- | lib/private/Settings/Theming/ServerInfo.php | 121 | ||||
-rw-r--r-- | settings/Controller/ServerInfoSettingsController.php | 67 | ||||
-rw-r--r-- | settings/css/_server-info.scss | 99 | ||||
-rw-r--r-- | settings/css/_where-is-your-data.scss | 10 | ||||
-rw-r--r-- | settings/css/settings.scss | 13 | ||||
-rw-r--r-- | settings/img/theming-dark.svg | 1 | ||||
-rw-r--r-- | settings/js/admin.js | 103 | ||||
-rw-r--r-- | settings/routes.php | 1 | ||||
-rw-r--r-- | settings/templates/settings/admin/server-info.php | 114 | ||||
-rw-r--r-- | settings/templates/settings/personal/partials/where-is-your-data.php | 76 | ||||
-rw-r--r-- | settings/templates/settings/personal/personal.info.php | 10 | ||||
-rw-r--r-- | tests/Settings/Controller/ServerInfoSettingsControllerTest.php | 94 | ||||
-rw-r--r-- | tests/lib/Settings/ManagerTest.php | 12 |
19 files changed, 762 insertions, 180 deletions
diff --git a/apps/theming/lib/Settings/Section.php b/apps/theming/lib/Settings/Section.php deleted file mode 100644 index 9ec990ebe48..00000000000 --- a/apps/theming/lib/Settings/Section.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -namespace OCA\Theming\Settings; - -use OCP\IL10N; -use OCP\IURLGenerator; -use OCP\Settings\IIconSection; - -class Section implements IIconSection { - /** @var IL10N */ - private $l; - /** @var IURLGenerator */ - private $url; - - /** - * @param IURLGenerator $url - * @param IL10N $l - */ - public function __construct(IURLGenerator $url, IL10N $l) { - $this->url = $url; - $this->l = $l; - } - - /** - * returns the ID of the section. It is supposed to be a lower case string, - * e.g. 'ldap' - * - * @returns string - */ - public function getID() { - return 'theming'; - } - - /** - * returns the translated name as it should be displayed, e.g. 'LDAP / AD - * integration'. Use the L10N service to translate it. - * - * @return string - */ - public function getName() { - return $this->l->t('Theming'); - } - - /** - * @return int whether the form should be rather on the top or bottom of - * the settings navigation. The sections are arranged in ascending order of - * the priority values. It is required to return a value between 0 and 99. - * - * E.g.: 70 - */ - public function getPriority() { - return 30; - } - - /** - * {@inheritdoc} - */ - public function getIcon() { - return $this->url->imagePath('theming', 'app-dark.svg'); - } -} diff --git a/apps/theming/tests/ServicesTest.php b/apps/theming/tests/ServicesTest.php index a46e28235f5..baf7c6edc88 100644 --- a/apps/theming/tests/ServicesTest.php +++ b/apps/theming/tests/ServicesTest.php @@ -26,13 +26,11 @@ namespace OCA\Theming\Tests; use OCA\Theming\Capabilities; use OCA\Theming\Controller\ThemingController; use OCA\Theming\Settings\Admin; -use OCA\Theming\Settings\Section; use OCA\Theming\ThemingDefaults; use OCA\Theming\Util; use OCP\AppFramework\App; use OCP\Capabilities\ICapability; use OCP\IL10N; -use OCP\Settings\ISection; use OCP\Settings\ISettings; use Test\TestCase; @@ -72,8 +70,6 @@ class ServicesTest extends TestCase { // Settings [Admin::class], [Admin::class, ISettings::class], - [Section::class], - [Section::class, ISection::class], ]; } diff --git a/apps/theming/tests/Settings/SectionTest.php b/apps/theming/tests/Settings/SectionTest.php deleted file mode 100644 index 09abf9e34f5..00000000000 --- a/apps/theming/tests/Settings/SectionTest.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Joas Schilling <coding@schilljs.com> - * @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 OCA\Theming\Tests\Settings; - -use OCA\Theming\Settings\Section; -use OCP\IL10N; -use OCP\IURLGenerator; -use Test\TestCase; - -class SectionTest extends TestCase { - /** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */ - private $url; - /** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */ - private $l; - /** @var Section */ - private $section; - - public function setUp() { - parent::setUp(); - $this->url = $this->createMock(IURLGenerator::class); - $this->l = $this->createMock(IL10N::class); - - $this->section = new Section( - $this->url, - $this->l - ); - } - - public function testGetID() { - $this->assertSame('theming', $this->section->getID()); - } - - public function testGetName() { - $this->l - ->expects($this->once()) - ->method('t') - ->with('Theming') - ->willReturn('Theming'); - - $this->assertSame('Theming', $this->section->getName()); - } - - public function testGetPriority() { - $this->assertSame(30, $this->section->getPriority()); - } - - public function testGetIcon() { - $this->url->expects($this->once()) - ->method('imagePath') - ->with('theming', 'app-dark.svg') - ->willReturn('icon'); - - $this->assertSame('icon', $this->section->getIcon()); - } -} diff --git a/core/css/styles.scss b/core/css/styles.scss index 912648f2757..11c737af52f 100644 --- a/core/css/styles.scss +++ b/core/css/styles.scss @@ -60,11 +60,11 @@ a { * { cursor: pointer; } -} -a.external { - margin: 0 3px; - text-decoration: underline; + &.external { + margin: 0 3px; + text-decoration: underline; + } } input { diff --git a/lib/private/Settings/Manager.php b/lib/private/Settings/Manager.php index 42ec16e223b..3f72557d8ec 100644 --- a/lib/private/Settings/Manager.php +++ b/lib/private/Settings/Manager.php @@ -192,6 +192,7 @@ class Manager implements IManager { 1 => [new Section('server', $this->l->t('Basic settings'), 0, $this->url->imagePath('core', 'actions/settings-dark.svg'))], 5 => [new Section('sharing', $this->l->t('Sharing'), 0, $this->url->imagePath('core', 'actions/share.svg'))], 10 => [new Section('security', $this->l->t('Security'), 0, $this->url->imagePath('core', 'actions/password.svg'))], + 30 => [new Section('theming', $this->l->t('Theming'), 0, $this->url->imagePath('settings', 'theming-dark.svg'))], 50 => [new Section('groupware', $this->l->t('Groupware'), 0, $this->url->imagePath('core', 'places/contacts.svg'))], 98 => [new Section('additional', $this->l->t('Additional settings'), 0, $this->url->imagePath('core', 'actions/settings-dark.svg'))], ]; @@ -237,6 +238,10 @@ class Manager implements IManager { $form = $this->container->query(Admin\Security::class); $forms[$form->getPriority()] = [$form]; } + if ($section === 'theming') { + $form = $this->container->query(Theming\ServerInfo::class); + $forms[$form->getPriority()] = [$form]; + } if ($section === 'sharing') { /** @var ISettings $form */ $form = $this->container->query(Admin\Sharing::class); diff --git a/lib/private/Settings/Personal/PersonalInfo.php b/lib/private/Settings/Personal/PersonalInfo.php index 98991ce6d40..74969806540 100644 --- a/lib/private/Settings/Personal/PersonalInfo.php +++ b/lib/private/Settings/Personal/PersonalInfo.php @@ -26,6 +26,7 @@ namespace OC\Settings\Personal; use OC\Accounts\AccountManager; +use OC\Settings\Theming\ServerInfo; use OCA\FederatedFileSharing\AppInfo\Application; use OCP\App\IAppManager; use OCP\AppFramework\Http\TemplateResponse; @@ -38,6 +39,7 @@ use OCP\IUser; use OCP\IUserManager; use OCP\L10N\IFactory; use OCP\Settings\ISettings; +use OCP\Encryption\IManager as EncryptionManager; class PersonalInfo implements ISettings { @@ -55,14 +57,18 @@ class PersonalInfo implements ISettings { private $l10nFactory; /** @var IL10N */ private $l; + /** @var EncryptionManager */ + private $encryptionManager; /** * @param IConfig $config * @param IUserManager $userManager * @param IGroupManager $groupManager * @param AccountManager $accountManager + * @param IAppManager $appManager * @param IFactory $l10nFactory * @param IL10N $l + * @param EncryptionManager $encryptionManager */ public function __construct( IConfig $config, @@ -71,7 +77,8 @@ class PersonalInfo implements ISettings { AccountManager $accountManager, IAppManager $appManager, IFactory $l10nFactory, - IL10N $l + IL10N $l, + EncryptionManager $encryptionManager ) { $this->config = $config; $this->userManager = $userManager; @@ -80,6 +87,7 @@ class PersonalInfo implements ISettings { $this->appManager = $appManager; $this->l10nFactory = $l10nFactory; $this->l = $l; + $this->encryptionManager = $encryptionManager; } /** @@ -135,13 +143,41 @@ class PersonalInfo implements ISettings { 'twitterScope' => $userData[AccountManager::PROPERTY_TWITTER]['scope'], 'twitterVerification' => $userData[AccountManager::PROPERTY_TWITTER]['verified'], 'groups' => $this->getGroups($user), - ] + $messageParameters + $languageParameters + $localeParameters; - + ] + $this->getWhereIsYourDataParams() + $messageParameters + $languageParameters + $localeParameters; return new TemplateResponse('settings', 'settings/personal/personal.info', $parameters, ''); } /** + * Returns the "where is your data" template params. + * + * @return array + */ + private function getWhereIsYourDataParams(): array { + + $adminContactConfigId = $this->config->getSystemValue(ServerInfo::SETTING_PROVIDER_ADMIN_CONTACT); + $adminContact = $this->userManager->get($adminContactConfigId); + + $params = [ + 'dataLocation' => $this->config->getSystemValue(ServerInfo::SETTING_LOCATION), + 'provider' => $this->config->getSystemValue(ServerInfo::SETTING_PROVIDER), + 'providerLink' => $this->config->getSystemValue(ServerInfo::SETTING_PROVIDER_WEBSITE), + 'providerPrivacyLink' => $this->config->getSystemValue(ServerInfo::SETTING_PROVIDER_PRIVACY_LINK), + 'encryptionEnabled' => $this->encryptionManager->isEnabled(), + 'adminName' => $adminContact !== null ? $adminContact->getDisplayName() : '', + 'adminMail' => $adminContact !== null ? $adminContact->getEMailAddress() : '' + ]; + + $params['show_where_is_your_data_section'] = empty($params['dataLocation']) === false + || empty($params['provider']) === false + || $params['encryptionEnabled'] === true + || empty($params['adminName']) === false; + + return $params; + + } + + /** * @return string the section ID, e.g. 'sharing' * @since 9.1 */ @@ -202,7 +238,7 @@ class PersonalInfo implements ISettings { $userLang = $languages['commonlanguages'][$userLangIndex]; // search in the other languages if ($userLangIndex === false) { - $userLangIndex = array_search($userConfLang, array_column($languages['languages'], 'code')); + $userLangIndex = array_search($userConfLang, array_column($languages['languages'], 'code')); $userLang = $languages['languages'][$userLangIndex]; } // if user language is not available but set somehow: show the actual code as name diff --git a/lib/private/Settings/Theming/ServerInfo.php b/lib/private/Settings/Theming/ServerInfo.php new file mode 100644 index 00000000000..ecd2fc418fc --- /dev/null +++ b/lib/private/Settings/Theming/ServerInfo.php @@ -0,0 +1,121 @@ +<?php +/** + * @copyright Copyright (c) 2018 Michael Weimann <mail@michael-weimann.eu> + * + * @author Michael Weimann <mail@michael-weimann.eu> + * + * @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\Settings\Theming; + +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IConfig; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\Settings\ISettings; + +/** + * This class describes the server info settings. + */ +class ServerInfo implements ISettings { + + const SETTING_LOCATION = 'serverinfo.location'; + const SETTING_PROVIDER = 'serverinfo.provider'; + const SETTING_PROVIDER_WEBSITE = 'serverinfo.provider.website'; + const SETTING_PROVIDER_PRIVACY_LINK = 'serverinfo.provider.privacylink'; + const SETTING_PROVIDER_ADMIN_CONTACT = 'serverinfo.admincontact'; + + /** + * @var IConfig + */ + private $config; + + /** + * @var IGroupManager + */ + private $groupManager; + + /** + * ServerInfo constructor. + * + * @param IConfig $config + * @param IGroupManager $groupManager + */ + public function __construct(IConfig $config, IGroupManager $groupManager) { + $this->config = $config; + $this->groupManager = $groupManager; + } + + /** + * @return TemplateResponse + */ + public function getForm(): TemplateResponse { + $parameters = [ + 'location' => $this->config->getSystemValue(self::SETTING_LOCATION), + 'provider' => $this->config->getSystemValue(self::SETTING_PROVIDER), + 'providerWebsite' => $this->config->getSystemValue(self::SETTING_PROVIDER_WEBSITE), + 'providerPrivacyLink' => $this->config->getSystemValue(self::SETTING_PROVIDER_PRIVACY_LINK), + 'adminUsers' => $this->getAdminListValues(), + 'adminContact' => $this->config->getSystemValue(self::SETTING_PROVIDER_ADMIN_CONTACT), + ]; + return new TemplateResponse('settings', 'settings/admin/server-info', $parameters, ''); + } + + /** + * Returns the admin list values. + * + * @return array[] An array or arrays with the keys 'id' and 'displayName' + */ + private function getAdminListValues(): array { + $adminGroup = $this->groupManager->get('admin'); + $users = $adminGroup->getUsers(); + + $users = array_map(function(IUser $user) { + return [ + 'id' => $user->getUID(), + 'displayName' => $user->getDisplayName() + ]; + }, $users); + + usort($your_data, function(array $a, array $b) { + return strcmp($a['displayName'], $b['displayName']); + }); + + return $users; + } + + /** + * Returns the server info section id. + * + * @return string + */ + public function getSection(): string { + return 'theming'; + } + + /** + * Returns the server info settings priority. + * + * @return int whether the form should be rather on the top or bottom of + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + */ + public function getPriority(): int { + return 10; + } + +} diff --git a/settings/Controller/ServerInfoSettingsController.php b/settings/Controller/ServerInfoSettingsController.php new file mode 100644 index 00000000000..a5c80211712 --- /dev/null +++ b/settings/Controller/ServerInfoSettingsController.php @@ -0,0 +1,67 @@ +<?php +/** + * @copyright Copyright (c) 2018 Michael Weimann <mail@michael-weimann.eu> + * + * @author Michael Weimann <mail@michael-weimann.eu> + * + * @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\Settings\Controller; + +use OC\Settings\Theming\ServerInfo; +use OCP\AppFramework\Controller; +use OCP\IConfig; +use OCP\IRequest; + +/** + * This controller handles server info settings requests. + */ +class ServerInfoSettingsController extends Controller { + + /** + * @var IConfig + */ + private $config; + + /** + * ServerInfoSettingsController constructor. + * + * @param IConfig $config + */ + public function __construct($appName, IRequest $request, IConfig $config) { + parent::__construct($appName, $request); + $this->config = $config; + } + + public function storeServerInfo( + string $location, + string $provider, + string $providerWebsite, + string $providerPrivacyLink, + string $adminContact + ): void { + $configs = [ + ServerInfo::SETTING_LOCATION => $location, + ServerInfo::SETTING_PROVIDER => $provider, + ServerInfo::SETTING_PROVIDER_WEBSITE => $providerWebsite, + ServerInfo::SETTING_PROVIDER_PRIVACY_LINK => $providerPrivacyLink, + ServerInfo::SETTING_PROVIDER_ADMIN_CONTACT => $adminContact + ]; + $this->config->setSystemValues($configs); + } + +} diff --git a/settings/css/_server-info.scss b/settings/css/_server-info.scss new file mode 100644 index 00000000000..1073cab2954 --- /dev/null +++ b/settings/css/_server-info.scss @@ -0,0 +1,99 @@ +/* Copyright (c) 2018 Michael Weimann <mail@michael-weimann.eu + This file is licensed under the Affero General Public License version 3 or later. + See the COPYING-README file. */ + +.server-info-settings { + .label { + display: block; + } + + .form-input { + margin-bottom: 10px; + width: 100%; + } + + .margin-bottom { + margin-bottom: 15px; + } + + .form-actions { + text-align: right; + + .button { + align-items: center; + display: inline-flex; + margin: 0; + transition: background-color 500ms linear; + + .default-label, + .working-label, + .success-label, + .error-label { + align-items: center; + gap: 4px; + } + + .working-label, + .success-label, + .error-label { + display: none; + } + } + + .button-working, + .button-success, + .button-error { + background-color: $color-background-dark; + color: $color-text-lighter; + opacity: 1; + + .default-label { + display: none; + } + } + + .button-working { + .working-label { + display: inline-flex; + } + } + + .button-success { + background-color: $color-success; + border-color: darken($color-success, 10%); + color: $color-primary-text-dark; + + .success-label { + display: inline-flex; + } + } + + .button-error { + background-color: $color-error; + border-color: darken($color-error, 10%); + color: $color-primary-text-dark; + + .error-label { + display: inline-flex; + } + } + } + + @media (min-width: 1000px) { + .label { + display: inline-block; + text-align: right; + width: 175px; + } + + .form-input { + margin-left: 5px; + width: 225px; + } + + .form-actions { + margin-left: 180px; + width: 225px; + } + } +} diff --git a/settings/css/_where-is-your-data.scss b/settings/css/_where-is-your-data.scss new file mode 100644 index 00000000000..fc71bb0a023 --- /dev/null +++ b/settings/css/_where-is-your-data.scss @@ -0,0 +1,10 @@ +/* Copyright (c) 2018 Michael Weimann <mail@michael-weimann.eu + This file is licensed under the Affero General Public License version 3 or later. + See the COPYING-README file. */ + +.where-is-your-data { + // @todo replace by common link style as soon as available + a:not(.icon-info) { + border-bottom: 1px dotted; + } +} diff --git a/settings/css/settings.scss b/settings/css/settings.scss index 126d451fc66..ebd1ff1b0ec 100644 --- a/settings/css/settings.scss +++ b/settings/css/settings.scss @@ -2,6 +2,9 @@ This file is licensed under the Affero General Public License version 3 or later. See the COPYING-README file. */ +@import "server-info"; +@import "where-is-your-data"; + input { &#openid, &#webdav { width: 20em; @@ -98,6 +101,7 @@ input { #personal-settings-avatar-container { display: inline-grid; + grid-row: span 3; grid-template-columns: 1fr; grid-template-rows: 2fr 1fr; vertical-align: top; @@ -178,6 +182,15 @@ select { grid-template-columns: 1fr; #personal-settings-avatar-container { grid-template-rows: 1fr; + + // swap "Where is my data" and "Detail" + *:nth-child(2) { + order: 3; + } + + *:nth-child(3) { + order: 2; + } } .personal-settings-container { grid-template-columns: 1fr 1fr; diff --git a/settings/img/theming-dark.svg b/settings/img/theming-dark.svg new file mode 100644 index 00000000000..adf97966c41 --- /dev/null +++ b/settings/img/theming-dark.svg @@ -0,0 +1 @@ +<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M10.707 11.412l-.587-.587-.03-.03a.513.513 0 0 1-.074-.526L13.07 3.4l-1.5-1.498-.15.15-.708-.706.505-.505a.538.538 0 0 1 .224-.128c.04-.01.05-.01.087-.016h.087c.04.006.05.006.086.016.072.02.134.055.192.1.74.676 1.42 1.415 2.127 2.124a.503.503 0 0 1 .103.556l-3.053 6.87.344.343.49-.49 3.01 3.01a1.192 1.192 0 0 1-1.685 1.686l-3.012-3.01.49-.488zm-.533-10.217a.986.986 0 0 0-1.396 0l-7.582 7.58a.99.99 0 0 0 0 1.398l1.397 1.396a.986.986 0 0 0 1.396 0l7.58-7.583a.988.988 0 0 0 0-1.396l-1.396-1.395z" fill="#000"/></svg> diff --git a/settings/js/admin.js b/settings/js/admin.js index b93c55a8a9c..1c9b5460eab 100644 --- a/settings/js/admin.js +++ b/settings/js/admin.js @@ -313,4 +313,107 @@ $(document).ready(function(){ if (document.getElementById('security-warning') !== null) { setupChecks(); } + + // server info + + var serverInfoForm = $('#server-info-form'); + var serverInfoWorkingTimeoutHandle; + var serverInfoSubmitButton = $('#server-info-submit-button'); + + /** + * Sets the server info submit button state to default. + */ + function setServerInfoButtonDefault() { + serverInfoSubmitButton.removeClass('button-success'); + serverInfoSubmitButton.removeClass('button-error'); + serverInfoSubmitButton.removeClass('button-working'); + } + + /** + * Sets the server info submit button state to working. + */ + function setServerInfoButtonWorking() { + serverInfoSubmitButton.removeClass('button-success'); + serverInfoSubmitButton.removeClass('button-error'); + serverInfoSubmitButton.addClass('button-working'); + } + + /** + * Sets the server info submit button state to success. + */ + function setServerInfoButtonSuccess() { + serverInfoSubmitButton.removeClass('button-error'); + serverInfoSubmitButton.removeClass('button-working'); + serverInfoSubmitButton.addClass('button-success'); + } + + /** + * Sets the server info submit button state to error. + */ + function setServerInfoButtonError() { + serverInfoSubmitButton.removeClass('button-success'); + serverInfoSubmitButton.removeClass('button-working'); + serverInfoSubmitButton.addClass('button-error'); + } + + /** + * Clears the server info working timeout, if present. + */ + function clearServerInfoWorkingTimeout() { + if (serverInfoWorkingTimeoutHandle) { + clearTimeout(serverInfoWorkingTimeoutHandle); + serverInfoWorkingTimeoutHandle = undefined; + } + } + + /** + * Unlocks the server info form, e.g. removing readonly from inputs. + */ + function unlockForm() { + serverInfoForm.find('input, select').prop('readonly', false); + serverInfoSubmitButton.prop('disabled', false); + } + + /** + * Resets the submit button state one of the form elements is changed. + */ + serverInfoForm.find('input, select').on('keyup change', function() { + setServerInfoButtonDefault(); + }); + + /** + * Handles the server info form submit. + */ + serverInfoForm.on('submit', function(event) { + event.stopImmediatePropagation(); + event.preventDefault(); + + serverInfoForm.find('input, select').prop('readonly', true); + serverInfoSubmitButton.prop('disabled', true); + + clearServerInfoWorkingTimeout(); + + // start show spinner only if request takes longer than one second + serverInfoWorkingTimeoutHandle = setTimeout(function() { + setServerInfoButtonWorking(); + }, 1000); + + $.ajax({ + url: OC.generateUrl('/settings/serverinfo'), + type: 'POST', + data: serverInfoForm.serialize(), + success: function() { + clearServerInfoWorkingTimeout(); + setServerInfoButtonSuccess(); + unlockForm(); + serverInfoSubmitButton.blur(); + }, + error: function() { + clearServerInfoWorkingTimeout(); + setServerInfoButtonError(); + unlockForm(); + serverInfoSubmitButton.blur(); + } + }); + }); }); diff --git a/settings/routes.php b/settings/routes.php index 7c8120f9be4..277bcca484f 100644 --- a/settings/routes.php +++ b/settings/routes.php @@ -83,6 +83,7 @@ $application->registerRoutes($this, [ ['name' => 'ChangePassword#changeUserPassword', 'url' => '/settings/users/changepassword', 'verb' => 'POST'], ['name' => 'TwoFactorSettings#index', 'url' => '/settings/api/admin/twofactorauth', 'verb' => 'GET'], ['name' => 'TwoFactorSettings#update', 'url' => '/settings/api/admin/twofactorauth', 'verb' => 'PUT'], + ['name' => 'ServerInfoSettings#storeServerInfo', 'url' => '/settings/serverinfo', 'verb' => 'POST'], ] ]); diff --git a/settings/templates/settings/admin/server-info.php b/settings/templates/settings/admin/server-info.php new file mode 100644 index 00000000000..ac6cf48ee80 --- /dev/null +++ b/settings/templates/settings/admin/server-info.php @@ -0,0 +1,114 @@ +<?php +/** + * @copyright Copyright (c) 2018 Michael Weimann <mail@michael-weimann.eu> + * + * @author Michael Weimann <mail@michael-weimann.eu> + * + * @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/>. + */ + +/** + * This file contains the server info settings template. + */ + +/** @var array $_ */ + +?> + +<div class="section server-info-settings"> + <h2><?php p($l->t('Server info')); ?></h2> + <p class="settings-hint"> + <?php p($l->t('Enter common info about your Nextcloud instance here. These info are visible to all users.')) ?> + </p> + <form id="server-info-form" name="server-info-form"> + <div class="margin-bottom"> + <label class="label" for="location"><?php p($l->t('Server location')); ?></label> + <input + class="form-input" + id="location" + name="location" + type="text" + maxlength="100" + value="<?php p($_['location']); ?>" + placeholder="<?php p($l->t('country')); ?>"> + </div> + <div> + <label class="label" for="provider"><?php p($l->t('Service provider')); ?></label> + <input + class="form-input" + id="provider" + name="provider" + type="text" + maxlength="100" + value="<?php p($_['provider']); ?>" + placeholder="<?php p($l->t('company or person')); ?>"> + </div> + <div> + <label class="label" for="providerWebsite"><?php p($l->t('Provider website')); ?></label> + <input + class="form-input" + id="providerWebsite" + name="providerWebsite" + type="url" + maxlength="200" + value="<?php p($_['providerWebsite']); ?>" + placeholder="<?php p($l->t('link to website')); ?>"> + </div> + <div class="margin-bottom"> + <label class="label" for="providerPrivacyLink"><?php p($l->t('Link to privacy policy')); ?></label> + <input + class="form-input" + id="providerPrivacyLink" + name="providerPrivacyLink" + type="url" + maxlength="200" + value="<?php p($_['providerPrivacyLink']); ?>" + placeholder="<?php p($l->t('link to privacy policy')); ?>"> + </div> + <div class="margin-bottom"> + <label class="label" for="adminContact"><?php p($l->t('Admin contact')); ?></label> + <select class="form-input" name="adminContact" id="adminContact"> + <option value=""><?php p($l->t('choose admin contact')); ?></option> + <?php foreach($_['adminUsers'] as $adminUser): ?> + <option + value="<?php p($adminUser['id']); ?>" + <?php if ($adminUser['id'] === $_['adminContact']): ?>selected="selected"<?php endif; ?>> + <?php p($adminUser['displayName']); ?> + </option> + <?php endforeach; ?> + </select> + </div> + <div class="form-actions"> + <button id="server-info-submit-button" class="button"> + <span class="default-label"> + <?php p($l->t('save')); ?> + </span> + <span class="working-label"> + <span class="icon-loading-small-dark"></span> + <?php p($l->t('saving')); ?> + </span> + <span class="success-label"> + <span class="icon-checkmark-white"></span> + <?php p($l->t('saved')); ?> + </span> + <span class="error-label"> + <span class="icon-error-white"></span> + <?php p($l->t('error saving settings')); ?> + </span> + </button> + </div> + </form> +</div> diff --git a/settings/templates/settings/personal/partials/where-is-your-data.php b/settings/templates/settings/personal/partials/where-is-your-data.php new file mode 100644 index 00000000000..56f261f9c00 --- /dev/null +++ b/settings/templates/settings/personal/partials/where-is-your-data.php @@ -0,0 +1,76 @@ +<div class="personal-settings-setting-box personal-settings-group-box section where-is-your-data"> + <h3> + <?php p($l->t('Where is your data?')); ?> + <a + target="_blank" + rel="noreferrer noopener" + class="icon-info" + title="" + href="https://nextcloud.com/yourdata/" + data-original-title="Open documentation"></a> + </h3> + <?php if (empty($_['dataLocation']) === false): ?> + <div class="personal-info icon-address"> + <p> + <?php echo $l->t('Your data is located in <b>%s</b>.', [$_['dataLocation']]); ?> + </p> + </div> + <?php endif; ?> + + <?php if (empty($_['provider']) === false): ?> + <div class="personal-info icon-home"> + <p> + <?php + if (empty($_['providerLink']) === false) { + echo $l->t('Your provider is %s%s%s.', [ + '<a href="' . $_['providerLink'] . '" target="_blank" title="" rel="noreferrer noopener">', + $_['provider'], + '</a>' + ]); + } else { + echo $l->t('Your provider is %s.', [$_['provider']]); + } + ?> + <?php + if (empty($_['providerPrivacyLink']) === false) { + echo $l->t('Read the %sprivacy policy%s now.', [ + '<a href="' . $_['providerPrivacyLink'] . '" target="_blank" title="" rel="noreferrer noopener">', + '</a>' + ]); + } + ?> + </p> + </div> + <?php endif; ?> + + <?php if ($_['encryptionEnabled'] === true): ?> + <div class="personal-info icon-password"> + <p> + <?php echo $l->t( + 'Your files are encrypted with %sserver side encryption%s.', + [ + '<a href="https://nextcloud.com/blog/encryption-in-nextcloud/" target="_blank" title="" rel="noreferrer noopener">', + '</a>' + ] + ); ?> + </p> + </div> + <?php endif; ?> + + <?php if (empty($_['adminName']) === false): ?> + <div class="personal-info icon-user-admin"> + <p> + <?php echo $l->t( + '%s%s%s is your admin. If you have any issues, %scontact them%s.', + [ + '<a href="mailto:' . $_['adminMail'] . '" target="_blank" title="" rel="noreferrer noopener">', + $_['adminName'], + '</a>', + '<a href="mailto:' . $_['adminMail'] . '" target="_blank" title="" rel="noreferrer noopener">', + '</a>' + ] + ); ?> + </p> + </div> + <?php endif; ?> +</div> diff --git a/settings/templates/settings/personal/personal.info.php b/settings/templates/settings/personal/personal.info.php index d873f6821b8..a6e3eaf345b 100644 --- a/settings/templates/settings/personal/personal.info.php +++ b/settings/templates/settings/personal/personal.info.php @@ -95,6 +95,11 @@ script('settings', [ <progress value="<?php p($_['usage_relative']); ?>" max="100"<?php if($_['usage_relative'] > 80): ?> class="warn" <?php endif; ?>></progress> </div> </div> + <?php + if ($_['show_where_is_your_data_section']) { + include __DIR__ . '/partials/where-is-your-data.php'; + } + ?> </div> <div class="personal-settings-container"> @@ -375,9 +380,4 @@ script('settings', [ </div> <span class="msg"></span> </div> - - <div id="personal-settings-group-container"> - - </div> - </div> diff --git a/tests/Settings/Controller/ServerInfoSettingsControllerTest.php b/tests/Settings/Controller/ServerInfoSettingsControllerTest.php new file mode 100644 index 00000000000..8d2083d0acb --- /dev/null +++ b/tests/Settings/Controller/ServerInfoSettingsControllerTest.php @@ -0,0 +1,94 @@ +<?php +/** + * @copyright Copyright (c) 2018 Michael Weimann <mail@michael-weimann.eu> + * + * @author Michael Weimann <mail@michael-weimann.eu> + * + * @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 Settings\Controller; + +use OC\Settings\Theming\ServerInfo; +use OC\Settings\Controller\ServerInfoSettingsController; +use OCP\IConfig; +use OCP\IRequest; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +/** + * This class provides tests for the server info settings controller. + */ +class ServerInfoSettingsControllerTest extends TestCase { + + /** + * @var IConfig|MockObject + */ + private $config; + + /** + * @var ServerInfoSettingsController + */ + private $controller; + + /** + * Does the test setup. + */ + protected function setUp() { + parent::setUp(); + + $request = $this->getMockBuilder(IRequest::class)->getMock(); + /* @var IRequest|MockObject $request */ + $this->config = $this->getMockBuilder(IConfig::class)->getMock(); + $this->controller = new ServerInfoSettingsController( + 'settings', + $request, + $this->config + ); + } + + /** + * Tests that the handler passes the params to the config. + */ + public function testStoreServerInfo() { + + $location = 'test-location'; + $provider = 'test-provider'; + $providerWebsite = 'https://example.com/'; + $providerPrivacyLink = 'https://example.com/privacy'; + $adminContact = 'testuser'; + + $this->config->expects($this->once()) + ->method('setSystemValues') + ->with([ + ServerInfo::SETTING_LOCATION => $location, + ServerInfo::SETTING_PROVIDER => $provider, + ServerInfo::SETTING_PROVIDER_WEBSITE => $providerWebsite, + ServerInfo::SETTING_PROVIDER_PRIVACY_LINK => $providerPrivacyLink, + ServerInfo::SETTING_PROVIDER_ADMIN_CONTACT => $adminContact, + ]); + + $this->controller->storeServerInfo( + $location, + $provider, + $providerWebsite, + $providerPrivacyLink, + $adminContact + ); + + } + +} diff --git a/tests/lib/Settings/ManagerTest.php b/tests/lib/Settings/ManagerTest.php index b82fb5bc3ca..8323452934f 100644 --- a/tests/lib/Settings/ManagerTest.php +++ b/tests/lib/Settings/ManagerTest.php @@ -72,13 +72,14 @@ class ManagerTest extends TestCase { $this->manager->registerSection('admin', \OCA\WorkflowEngine\Settings\Section::class); - $this->url->expects($this->exactly(6)) + $this->url->expects($this->exactly(7)) ->method('imagePath') ->willReturnMap([ ['settings', 'admin.svg', '0'], ['core', 'actions/settings-dark.svg', '1'], ['core', 'actions/share.svg', '2'], ['core', 'actions/password.svg', '3'], + ['settings', 'theming-dark.svg', '6'], ['core', 'places/contacts.svg', '5'], ['settings', 'help.svg', '4'], ]); @@ -88,6 +89,7 @@ class ManagerTest extends TestCase { 1 => [new Section('server', 'Basic settings', 0, '1')], 5 => [new Section('sharing', 'Sharing', 0, '2')], 10 => [new Section('security', 'Security', 0, '3')], + 30 => [new Section('theming', 'Theming', 0, '6')], 50 => [new Section('groupware', 'Groupware', 0, '5')], 55 => [\OC::$server->query(\OCA\WorkflowEngine\Settings\Section::class)], 98 => [new Section('additional', 'Additional settings', 0, '1')], @@ -124,13 +126,14 @@ class ManagerTest extends TestCase { ->method('t') ->will($this->returnArgument(0)); - $this->url->expects($this->exactly(6)) + $this->url->expects($this->exactly(7)) ->method('imagePath') ->willReturnMap([ ['settings', 'admin.svg', '0'], ['core', 'actions/settings-dark.svg', '1'], ['core', 'actions/share.svg', '2'], ['core', 'actions/password.svg', '3'], + ['settings', 'theming-dark.svg', '6'], ['core', 'places/contacts.svg', '5'], ['settings', 'help.svg', '4'], ]); @@ -140,6 +143,7 @@ class ManagerTest extends TestCase { 1 => [new Section('server', 'Basic settings', 0, '1')], 5 => [new Section('sharing', 'Sharing', 0, '2')], 10 => [new Section('security', 'Security', 0, '3')], + 30 => [new Section('theming', 'Theming', 0, '6')], 50 => [new Section('groupware', 'Groupware', 0, '5')], 98 => [new Section('additional', 'Additional settings', 0, '1')], ], $this->manager->getAdminSections()); @@ -209,7 +213,7 @@ class ManagerTest extends TestCase { $this->manager->registerSection('personal', \OCA\WorkflowEngine\Settings\Section::class); $this->manager->registerSection('admin', \OCA\WorkflowEngine\Settings\Section::class); - $this->url->expects($this->exactly(9)) + $this->url->expects($this->exactly(10)) ->method('imagePath') ->willReturnMap([ ['core', 'actions/info.svg', '1'], @@ -219,6 +223,7 @@ class ManagerTest extends TestCase { ['core', 'actions/settings-dark.svg', '1'], ['core', 'actions/share.svg', '2'], ['core', 'actions/password.svg', '3'], + ['settings', 'theming-dark.svg', '6'], ['core', 'places/contacts.svg', '5'], ['settings', 'help.svg', '4'], ]); @@ -235,6 +240,7 @@ class ManagerTest extends TestCase { 1 => [new Section('server', 'Basic settings', 0, '1')], 5 => [new Section('sharing', 'Sharing', 0, '2')], 10 => [new Section('security', 'Security', 0, '3')], + 30 => [new Section('theming', 'Theming', 0, '6')], 50 => [new Section('groupware', 'Groupware', 0, '5')], 55 => [\OC::$server->query(\OCA\WorkflowEngine\Settings\Section::class)], 98 => [new Section('additional', 'Additional settings', 0, '1')], |