A bit more elegant. Plus it will allow us to also write a proper @nextcloud/theming package. To make life easier down the line for all. Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl> Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>tags/v20.0.0beta1
] | ] | ||||
); | ); | ||||
$linkToJs = \OC::$server->getURLGenerator()->linkToRoute( | |||||
'theming.Theming.getJavascript', | |||||
[ | |||||
'v' => \OC::$server->getConfig()->getAppValue('theming', 'cachebuster', '0'), | |||||
] | |||||
); | |||||
\OCP\Util::addHeader( | |||||
'script', | |||||
[ | |||||
'src' => $linkToJs, | |||||
'nonce' => \OC::$server->getContentSecurityPolicyNonceManager()->getNonce() | |||||
], '' | |||||
); | |||||
\OCP\Util::addScript('theming', 'theming'); |
'url' => '/image/{key}', | 'url' => '/image/{key}', | ||||
'verb' => 'GET', | 'verb' => 'GET', | ||||
], | ], | ||||
[ | |||||
'name' => 'Theming#getJavascript', | |||||
'url' => '/js/theming', | |||||
'verb' => 'GET', | |||||
], | |||||
[ | [ | ||||
'name' => 'Theming#getManifest', | 'name' => 'Theming#getManifest', | ||||
'url' => '/manifest/{app}', | 'url' => '/manifest/{app}', |
OCA.Theming = OCP.InitialState.loadState('theming', 'data') |
namespace OCA\Theming\AppInfo; | namespace OCA\Theming\AppInfo; | ||||
use OCA\Theming\Service\JSDataService; | |||||
use OCP\AppFramework\IAppContainer; | |||||
use OCP\IInitialStateService; | |||||
class Application extends \OCP\AppFramework\App { | class Application extends \OCP\AppFramework\App { | ||||
public const APP_ID = 'theming'; | |||||
public function __construct() { | public function __construct() { | ||||
parent::__construct('theming', []); | |||||
parent::__construct(self::APP_ID); | |||||
$container = $this->getContainer(); | |||||
$this->registerInitialState($container); | |||||
} | |||||
private function registerInitialState(IAppContainer $container) { | |||||
/** @var IInitialStateService $initialState */ | |||||
$initialState = $container->query(IInitialStateService::class); | |||||
$initialState->provideLazyInitialState(self::APP_ID, 'data', function () use ($container) { | |||||
/** @var JSDataService $data */ | |||||
$data = $container->query(JSDataService::class); | |||||
return $data; | |||||
}); | |||||
} | } | ||||
} | } |
use OC\Template\SCSSCacher; | use OC\Template\SCSSCacher; | ||||
use OCA\Theming\ImageManager; | use OCA\Theming\ImageManager; | ||||
use OCA\Theming\ThemingDefaults; | use OCA\Theming\ThemingDefaults; | ||||
use OCA\Theming\Util; | |||||
use OCP\App\IAppManager; | use OCP\App\IAppManager; | ||||
use OCP\AppFramework\Controller; | use OCP\AppFramework\Controller; | ||||
use OCP\AppFramework\Http; | use OCP\AppFramework\Http; | ||||
use OCP\AppFramework\Http\DataDownloadResponse; | |||||
use OCP\AppFramework\Http\DataResponse; | use OCP\AppFramework\Http\DataResponse; | ||||
use OCP\AppFramework\Http\FileDisplayResponse; | use OCP\AppFramework\Http\FileDisplayResponse; | ||||
use OCP\AppFramework\Http\NotFoundResponse; | use OCP\AppFramework\Http\NotFoundResponse; | ||||
class ThemingController extends Controller { | class ThemingController extends Controller { | ||||
/** @var ThemingDefaults */ | /** @var ThemingDefaults */ | ||||
private $themingDefaults; | private $themingDefaults; | ||||
/** @var Util */ | |||||
private $util; | |||||
/** @var IL10N */ | /** @var IL10N */ | ||||
private $l10n; | private $l10n; | ||||
/** @var IConfig */ | /** @var IConfig */ | ||||
* @param IRequest $request | * @param IRequest $request | ||||
* @param IConfig $config | * @param IConfig $config | ||||
* @param ThemingDefaults $themingDefaults | * @param ThemingDefaults $themingDefaults | ||||
* @param Util $util | |||||
* @param IL10N $l | * @param IL10N $l | ||||
* @param ITempManager $tempManager | * @param ITempManager $tempManager | ||||
* @param IAppData $appData | * @param IAppData $appData | ||||
IRequest $request, | IRequest $request, | ||||
IConfig $config, | IConfig $config, | ||||
ThemingDefaults $themingDefaults, | ThemingDefaults $themingDefaults, | ||||
Util $util, | |||||
IL10N $l, | IL10N $l, | ||||
ITempManager $tempManager, | ITempManager $tempManager, | ||||
IAppData $appData, | IAppData $appData, | ||||
parent::__construct($appName, $request); | parent::__construct($appName, $request); | ||||
$this->themingDefaults = $themingDefaults; | $this->themingDefaults = $themingDefaults; | ||||
$this->util = $util; | |||||
$this->l10n = $l; | $this->l10n = $l; | ||||
$this->config = $config; | $this->config = $config; | ||||
$this->tempManager = $tempManager; | $this->tempManager = $tempManager; | ||||
} | } | ||||
} | } | ||||
/** | |||||
* @NoCSRFRequired | |||||
* @PublicPage | |||||
* @NoSameSiteCookieRequired | |||||
* | |||||
* @return DataDownloadResponse | |||||
*/ | |||||
public function getJavascript() { | |||||
$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0'); | |||||
$responseJS = '(function() { | |||||
OCA.Theming = { | |||||
name: ' . json_encode($this->themingDefaults->getName()) . ', | |||||
url: ' . json_encode($this->themingDefaults->getBaseUrl()) . ', | |||||
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) . ' | |||||
}; | |||||
})();'; | |||||
$response = new DataDownloadResponse($responseJS, 'javascript', 'text/javascript'); | |||||
$response->cacheFor(3600); | |||||
return $response; | |||||
} | |||||
/** | /** | ||||
* @NoCSRFRequired | * @NoCSRFRequired | ||||
* @PublicPage | * @PublicPage |
<?php | |||||
declare(strict_types=1); | |||||
/** | |||||
* @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> | |||||
* | |||||
* @author Roeland Jago Douma <roeland@famdouma.nl> | |||||
* | |||||
* @license GNU AGPL version 3 or any later version | |||||
* | |||||
* This program is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU Affero General Public License as | |||||
* published by the Free Software Foundation, either version 3 of the | |||||
* License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU Affero General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Affero General Public License | |||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
namespace OCA\Theming\Service; | |||||
use OCA\Theming\AppInfo\Application; | |||||
use OCA\Theming\ThemingDefaults; | |||||
use OCA\Theming\Util; | |||||
use OCP\IConfig; | |||||
class JSDataService implements \JsonSerializable { | |||||
/** @var ThemingDefaults */ | |||||
private $themingDefaults; | |||||
/** @var Util */ | |||||
private $util; | |||||
/** @var IConfig */ | |||||
private $appConfig; | |||||
public function __construct( | |||||
ThemingDefaults $themingDefaults, | |||||
Util $util, | |||||
IConfig $appConfig | |||||
) { | |||||
$this->themingDefaults = $themingDefaults; | |||||
$this->util = $util; | |||||
$this->appConfig = $appConfig; | |||||
} | |||||
public function jsonSerialize() { | |||||
return [ | |||||
'name' => $this->themingDefaults->getName(), | |||||
'url' => $this->themingDefaults->getBaseUrl(), | |||||
'slogan' => $this->themingDefaults->getSlogan(), | |||||
'color' => $this->themingDefaults->getColorPrimary(), | |||||
'imprintUrl' => $this->themingDefaults->getImprintUrl(), | |||||
'privacyUrl' => $this->themingDefaults->getPrivacyUrl(), | |||||
'inverted' => $this->util->invertTextColor($this->themingDefaults->getColorPrimary()), | |||||
'cacheBuster' => $this->appConfig->getAppValue(Application::class, 'cachebuster', '0'), | |||||
]; | |||||
} | |||||
} |
use OCA\Theming\Controller\ThemingController; | use OCA\Theming\Controller\ThemingController; | ||||
use OCA\Theming\ImageManager; | use OCA\Theming\ImageManager; | ||||
use OCA\Theming\ThemingDefaults; | use OCA\Theming\ThemingDefaults; | ||||
use OCA\Theming\Util; | |||||
use OCP\App\IAppManager; | use OCP\App\IAppManager; | ||||
use OCP\AppFramework\Http; | use OCP\AppFramework\Http; | ||||
use OCP\AppFramework\Http\DataResponse; | use OCP\AppFramework\Http\DataResponse; | ||||
private $config; | private $config; | ||||
/** @var ThemingDefaults|\PHPUnit_Framework_MockObject_MockObject */ | /** @var ThemingDefaults|\PHPUnit_Framework_MockObject_MockObject */ | ||||
private $themingDefaults; | private $themingDefaults; | ||||
/** @var Util */ | |||||
private $util; | |||||
/** @var \OCP\AppFramework\Utility\ITimeFactory */ | /** @var \OCP\AppFramework\Utility\ITimeFactory */ | ||||
private $timeFactory; | private $timeFactory; | ||||
/** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */ | /** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */ | ||||
$this->l10n = $this->createMock(L10N::class); | $this->l10n = $this->createMock(L10N::class); | ||||
$this->appData = $this->createMock(IAppData::class); | $this->appData = $this->createMock(IAppData::class); | ||||
$this->appManager = $this->createMock(IAppManager::class); | $this->appManager = $this->createMock(IAppManager::class); | ||||
$this->util = new Util($this->config, $this->appManager, $this->appData); | |||||
$this->tempManager = \OC::$server->getTempManager(); | $this->tempManager = \OC::$server->getTempManager(); | ||||
$this->scssCacher = $this->createMock(SCSSCacher::class); | $this->scssCacher = $this->createMock(SCSSCacher::class); | ||||
$this->urlGenerator = $this->createMock(IURLGenerator::class); | $this->urlGenerator = $this->createMock(IURLGenerator::class); | ||||
$this->request, | $this->request, | ||||
$this->config, | $this->config, | ||||
$this->themingDefaults, | $this->themingDefaults, | ||||
$this->util, | |||||
$this->l10n, | $this->l10n, | ||||
$this->tempManager, | $this->tempManager, | ||||
$this->appData, | $this->appData, | ||||
$this->assertEquals($response, $actual); | $this->assertEquals($response, $actual); | ||||
} | } | ||||
public function testGetJavascript() { | |||||
$this->themingDefaults | |||||
->expects($this->at(0)) | |||||
->method('getName') | |||||
->willReturn(""); | |||||
$this->themingDefaults | |||||
->expects($this->at(1)) | |||||
->method('getBaseUrl') | |||||
->willReturn(""); | |||||
$this->themingDefaults | |||||
->expects($this->at(2)) | |||||
->method('getSlogan') | |||||
->willReturn(""); | |||||
$this->themingDefaults | |||||
->expects($this->at(3)) | |||||
->method('getColorPrimary') | |||||
->willReturn("#000"); | |||||
$expectedResponse = '(function() { | |||||
OCA.Theming = { | |||||
name: "", | |||||
url: "", | |||||
slogan: "", | |||||
color: "#000", | |||||
imprintUrl: null, | |||||
privacyUrl: null, | |||||
inverted: false, | |||||
cacheBuster: null | |||||
}; | |||||
})();'; | |||||
$expected = new Http\DataDownloadResponse($expectedResponse, 'javascript', 'text/javascript'); | |||||
$expected->cacheFor(3600); | |||||
@$this->assertEquals($expected, $this->themingController->getJavascript()); | |||||
} | |||||
public function testGetJavascriptInverted() { | |||||
$this->themingDefaults | |||||
->expects($this->at(0)) | |||||
->method('getName') | |||||
->willReturn("Nextcloud"); | |||||
$this->themingDefaults | |||||
->expects($this->at(1)) | |||||
->method('getBaseUrl') | |||||
->willReturn("nextcloudurl"); | |||||
$this->themingDefaults | |||||
->expects($this->at(2)) | |||||
->method('getSlogan') | |||||
->willReturn("awesome"); | |||||
$this->themingDefaults | |||||
->expects($this->any()) | |||||
->method('getColorPrimary') | |||||
->willReturn("#ffffff"); | |||||
$expectedResponse = '(function() { | |||||
OCA.Theming = { | |||||
name: "Nextcloud", | |||||
url: "nextcloudurl", | |||||
slogan: "awesome", | |||||
color: "#ffffff", | |||||
imprintUrl: null, | |||||
privacyUrl: null, | |||||
inverted: true, | |||||
cacheBuster: null | |||||
}; | |||||
})();'; | |||||
$expected = new Http\DataDownloadResponse($expectedResponse, 'javascript', 'text/javascript'); | |||||
$expected->cacheFor(3600); | |||||
@$this->assertEquals($expected, $this->themingController->getJavascript()); | |||||
} | |||||
public function testGetManifest() { | public function testGetManifest() { | ||||
$this->config | $this->config | ||||
->expects($this->once()) | ->expects($this->once()) |