diff --git a/apps/theming/js/settings-admin.js b/apps/theming/js/settings-admin.js index 76d9fb965c..afc218b7ff 100644 --- a/apps/theming/js/settings-admin.js +++ b/apps/theming/js/settings-admin.js @@ -84,7 +84,8 @@ function hideUndoButton(setting, value) { color: '#0082c9', logoMime: '', backgroundMime: '', - imprintUrl: '' + imprintUrl: '', + privacyUrl: '' }; if (value === themingDefaults[setting] || value === '') { diff --git a/apps/theming/lib/Controller/ThemingController.php b/apps/theming/lib/Controller/ThemingController.php index dd7bf4bb04..a834bb1c5a 100644 --- a/apps/theming/lib/Controller/ThemingController.php +++ b/apps/theming/lib/Controller/ThemingController.php @@ -171,6 +171,16 @@ class ThemingController extends Controller { ]); } break; + case 'privacyUrl': + if (strlen($value) > 500) { + return new DataResponse([ + 'data' => [ + 'message' => $this->l10n->t('The given privacy policy address is too long'), + ], + 'status' => 'error' + ]); + } + break; case 'slogan': if (strlen($value) > 500) { return new DataResponse([ @@ -419,6 +429,7 @@ class ThemingController extends Controller { slogan: ' . json_encode($this->themingDefaults->getSlogan()) . ', color: ' . json_encode($this->themingDefaults->getColorPrimary()) . ', imprintUrl: ' . json_encode($this->themingDefaults->getImprintUrl()) . ', + privacyUrl: ' . json_encode($this->themingDefaults->getPrivacyUrl()) . ', inverted: ' . json_encode($this->util->invertTextColor($this->themingDefaults->getColorPrimary())) . ', cacheBuster: ' . json_encode($cacheBusterValue) . ' }; diff --git a/apps/theming/lib/Settings/Admin.php b/apps/theming/lib/Settings/Admin.php index ef296688ed..6a95dd39d4 100644 --- a/apps/theming/lib/Settings/Admin.php +++ b/apps/theming/lib/Settings/Admin.php @@ -85,6 +85,7 @@ class Admin implements ISettings { 'iconDocs' => $this->urlGenerator->linkToDocs('admin-theming-icons'), 'images' => $this->imageManager->getCustomImages(), 'imprintUrl' => $this->themingDefaults->getImprintUrl(), + 'privacyUrl' => $this->themingDefaults->getPrivacyUrl(), ]; return new TemplateResponse('theming', 'settings-admin', $parameters, ''); diff --git a/apps/theming/lib/ThemingDefaults.php b/apps/theming/lib/ThemingDefaults.php index d2f5747124..b61179b681 100644 --- a/apps/theming/lib/ThemingDefaults.php +++ b/apps/theming/lib/ThemingDefaults.php @@ -145,20 +145,41 @@ class ThemingDefaults extends \OC_Defaults { return $this->config->getAppValue('theming', 'imprintUrl', ''); } + public function getPrivacyUrl() { + return $this->config->getAppValue('theming', 'privacyUrl', ''); + } + public function getShortFooter() { $slogan = $this->getSlogan(); $footer = '' .$this->getEntity() . ''. ($slogan !== '' ? ' – ' . $slogan : ''); - $imprintUrl = (string)$this->getImprintUrl(); - if($imprintUrl !== '' - && filter_var($imprintUrl, FILTER_VALIDATE_URL, [ - 'flags' => FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED - ]) - ) { - $footer .= '
' . $this->l->t('Legal notice') . ''; + $links = [ + [ + 'text' => $this->l->t('Legal notice'), + 'url' => (string)$this->getImprintUrl() + ], + [ + 'text' => $this->l->t('Privacy policy'), + 'url' => (string)$this->getPrivacyUrl() + ], + ]; + + $legalLinks = ''; $divider = ''; + foreach($links as $link) { + if($link['url'] !== '' + && filter_var($link['url'], FILTER_VALIDATE_URL, [ + 'flags' => FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED + ]) + ) { + $legalLinks .= $divider . '' . $link['text'] . ''; + $divider = ' · '; + } + } + if($legalLinks !== '' ) { + $footer .= '
' . $legalLinks; } return $footer; diff --git a/apps/theming/templates/settings-admin.php b/apps/theming/templates/settings-admin.php index 26ab78637c..0cc224abc2 100644 --- a/apps/theming/templates/settings-admin.php +++ b/apps/theming/templates/settings-admin.php @@ -96,9 +96,16 @@ style('theming', 'settings-admin');
+
+
+
diff --git a/apps/theming/tests/Controller/ThemingControllerTest.php b/apps/theming/tests/Controller/ThemingControllerTest.php index f196541d37..cd50061c59 100644 --- a/apps/theming/tests/Controller/ThemingControllerTest.php +++ b/apps/theming/tests/Controller/ThemingControllerTest.php @@ -818,6 +818,7 @@ class ThemingControllerTest extends TestCase { slogan: "", color: "#000", imprintUrl: null, + privacyUrl: null, inverted: false, cacheBuster: null }; @@ -853,6 +854,7 @@ class ThemingControllerTest extends TestCase { slogan: "awesome", color: "#ffffff", imprintUrl: null, + privacyUrl: null, inverted: true, cacheBuster: null }; diff --git a/apps/theming/tests/Settings/AdminTest.php b/apps/theming/tests/Settings/AdminTest.php index f7361677d7..5943866edc 100644 --- a/apps/theming/tests/Settings/AdminTest.php +++ b/apps/theming/tests/Settings/AdminTest.php @@ -85,6 +85,10 @@ class AdminTest extends TestCase { ->expects($this->once()) ->method('getImprintUrl') ->willReturn(''); + $this->themingDefaults + ->expects($this->once()) + ->method('getPrivacyUrl') + ->willReturn(''); $this->themingDefaults ->expects($this->once()) ->method('getSlogan') @@ -110,6 +114,7 @@ class AdminTest extends TestCase { 'iconDocs' => null, 'images' => [], 'imprintUrl' => '', + 'privacyUrl' => '', ]; $expected = new TemplateResponse('theming', 'settings-admin', $params, ''); @@ -139,6 +144,10 @@ class AdminTest extends TestCase { ->expects($this->once()) ->method('getImprintUrl') ->willReturn(''); + $this->themingDefaults + ->expects($this->once()) + ->method('getPrivacyUrl') + ->willReturn(''); $this->themingDefaults ->expects($this->once()) ->method('getSlogan') @@ -164,6 +173,7 @@ class AdminTest extends TestCase { 'iconDocs' => '', 'images' => [], 'imprintUrl' => '', + 'privacyUrl' => '', ]; $expected = new TemplateResponse('theming', 'settings-admin', $params, ''); diff --git a/apps/theming/tests/ThemingDefaultsTest.php b/apps/theming/tests/ThemingDefaultsTest.php index b454b21f84..2cd415d98d 100644 --- a/apps/theming/tests/ThemingDefaultsTest.php +++ b/apps/theming/tests/ThemingDefaultsTest.php @@ -195,16 +195,16 @@ class ThemingDefaultsTest extends TestCase { $this->assertEquals('https://example.com/', $this->template->getBaseUrl()); } - public function imprintUrlProvider() { + public function legalUrlProvider() { return [ [ '' ], - [ 'https://example.com/imprint.html'] + [ 'https://example.com/legal.html'] ]; } /** * @param $imprintUrl - * @dataProvider imprintUrlProvider + * @dataProvider legalUrlProvider */ public function testGetImprintURL($imprintUrl) { $this->config @@ -216,6 +216,20 @@ class ThemingDefaultsTest extends TestCase { $this->assertEquals($imprintUrl, $this->template->getImprintUrl()); } + /** + * @param $privacyUrl + * @dataProvider legalUrlProvider + */ + public function testGetPrivacyURL($privacyUrl) { + $this->config + ->expects($this->once()) + ->method('getAppValue') + ->with('theming', 'privacyUrl', '') + ->willReturn($privacyUrl); + + $this->assertEquals($privacyUrl, $this->template->getPrivacyUrl()); + } + public function testGetSloganWithDefault() { $this->config ->expects($this->once()) @@ -238,13 +252,14 @@ class ThemingDefaultsTest extends TestCase { public function testGetShortFooter() { $this->config - ->expects($this->exactly(4)) + ->expects($this->exactly(5)) ->method('getAppValue') ->willReturnMap([ ['theming', 'url', $this->defaults->getBaseUrl(), 'url'], ['theming', 'name', 'Nextcloud', 'Name'], ['theming', 'slogan', $this->defaults->getSlogan(), 'Slogan'], ['theming', 'imprintUrl', '', ''], + ['theming', 'privacyUrl', '', ''], ]); $this->assertEquals('Name – Slogan', $this->template->getShortFooter()); @@ -252,13 +267,14 @@ class ThemingDefaultsTest extends TestCase { public function testGetShortFooterEmptySlogan() { $this->config - ->expects($this->exactly(4)) + ->expects($this->exactly(5)) ->method('getAppValue') ->willReturnMap([ ['theming', 'url', $this->defaults->getBaseUrl(), 'url'], ['theming', 'name', 'Nextcloud', 'Name'], ['theming', 'slogan', $this->defaults->getSlogan(), ''], ['theming', 'imprintUrl', '', ''], + ['theming', 'privacyUrl', '', ''], ]); $this->assertEquals('Name', $this->template->getShortFooter()); @@ -266,13 +282,14 @@ class ThemingDefaultsTest extends TestCase { public function testGetShortFooterImprint() { $this->config - ->expects($this->exactly(4)) + ->expects($this->exactly(5)) ->method('getAppValue') ->willReturnMap([ ['theming', 'url', $this->defaults->getBaseUrl(), 'url'], ['theming', 'name', 'Nextcloud', 'Name'], ['theming', 'slogan', $this->defaults->getSlogan(), 'Slogan'], ['theming', 'imprintUrl', '', 'https://example.com/imprint'], + ['theming', 'privacyUrl', '', ''], ]); $this->l10n @@ -283,26 +300,86 @@ class ThemingDefaultsTest extends TestCase { $this->assertEquals('Name – Slogan
Legal notice', $this->template->getShortFooter()); } - public function invalidImprintUrlProvider() { + public function testGetShortFooterPrivacy() { + $this->config + ->expects($this->exactly(5)) + ->method('getAppValue') + ->willReturnMap([ + ['theming', 'url', $this->defaults->getBaseUrl(), 'url'], + ['theming', 'name', 'Nextcloud', 'Name'], + ['theming', 'slogan', $this->defaults->getSlogan(), 'Slogan'], + ['theming', 'imprintUrl', '', ''], + ['theming', 'privacyUrl', '', 'https://example.com/privacy'], + ]); + + $this->l10n + ->expects($this->any()) + ->method('t') + ->willReturnArgument(0); + + $this->assertEquals('Name – Slogan
Privacy policy', $this->template->getShortFooter()); + } + + public function testGetShortFooterAllLegalLinks() { + $this->config + ->expects($this->exactly(5)) + ->method('getAppValue') + ->willReturnMap([ + ['theming', 'url', $this->defaults->getBaseUrl(), 'url'], + ['theming', 'name', 'Nextcloud', 'Name'], + ['theming', 'slogan', $this->defaults->getSlogan(), 'Slogan'], + ['theming', 'imprintUrl', '', 'https://example.com/imprint'], + ['theming', 'privacyUrl', '', 'https://example.com/privacy'], + ]); + + $this->l10n + ->expects($this->any()) + ->method('t') + ->willReturnArgument(0); + + $this->assertEquals('Name – Slogan
Legal notice · Privacy policy', $this->template->getShortFooter()); + } + + public function invalidLegalUrlProvider() { return [ - ['example.com/imprint'], # missing scheme - ['https:///imprint'], # missing host + ['example.com/legal'], # missing scheme + ['https:///legal'], # missing host ]; } /** * @param $invalidImprintUrl - * @dataProvider invalidImprintUrlProvider + * @dataProvider invalidLegalUrlProvider */ public function testGetShortFooterInvalidImprint($invalidImprintUrl) { $this->config - ->expects($this->exactly(4)) + ->expects($this->exactly(5)) ->method('getAppValue') ->willReturnMap([ ['theming', 'url', $this->defaults->getBaseUrl(), 'url'], ['theming', 'name', 'Nextcloud', 'Name'], ['theming', 'slogan', $this->defaults->getSlogan(), 'Slogan'], ['theming', 'imprintUrl', '', $invalidImprintUrl], + ['theming', 'privacyUrl', '', ''], + ]); + + $this->assertEquals('Name – Slogan', $this->template->getShortFooter()); + } + + /** + * @param $invalidPrivacyUrl + * @dataProvider invalidLegalUrlProvider + */ + public function testGetShortFooterInvalidPrivacy($invalidPrivacyUrl) { + $this->config + ->expects($this->exactly(5)) + ->method('getAppValue') + ->willReturnMap([ + ['theming', 'url', $this->defaults->getBaseUrl(), 'url'], + ['theming', 'name', 'Nextcloud', 'Name'], + ['theming', 'slogan', $this->defaults->getSlogan(), 'Slogan'], + ['theming', 'imprintUrl', '', ''], + ['theming', 'privacyUrl', '', $invalidPrivacyUrl], ]); $this->assertEquals('Name – Slogan', $this->template->getShortFooter());