Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scope the appdata theming storage for global and users #34599

Merged
merged 5 commits into from
Oct 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions apps/theming/lib/Controller/UserThemeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ public function getBackground(): Http\Response {
* @NoAdminRequired
*/
public function setBackground(string $type = 'default', string $value = ''): JSONResponse {
$currentVersion = (int)$this->config->getUserValue($this->userId, Application::APP_ID, 'backgroundVersion', '0');
$currentVersion = (int)$this->config->getUserValue($this->userId, Application::APP_ID, 'userCacheBuster', '0');

try {
switch ($type) {
case 'shipped':
Expand All @@ -179,14 +180,14 @@ public function setBackground(string $type = 'default', string $value = ''): JSO
} catch (\Throwable $e) {
return new JSONResponse(['error' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
}

$currentVersion++;
$this->config->setUserValue($this->userId, Application::APP_ID, 'backgroundVersion', (string)$currentVersion);
// FIXME replace with user-specific cachebuster increase https://github.com/nextcloud/server/issues/34472
$this->themingDefaults->increaseCacheBuster();
$this->config->setUserValue($this->userId, Application::APP_ID, 'userCacheBuster', (string)$currentVersion);

return new JSONResponse([
'type' => $type,
'value' => $value,
'version' => $this->config->getUserValue($this->userId, Application::APP_ID, 'backgroundVersion', $currentVersion)
'version' => $currentVersion,
]);
}
}
32 changes: 21 additions & 11 deletions apps/theming/lib/ImageManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,13 @@ public function __construct(IConfig $config,
IURLGenerator $urlGenerator,
ICacheFactory $cacheFactory,
ILogger $logger,
ITempManager $tempManager
) {
ITempManager $tempManager) {
$this->config = $config;
$this->appData = $appData;
$this->urlGenerator = $urlGenerator;
$this->cacheFactory = $cacheFactory;
$this->logger = $logger;
$this->tempManager = $tempManager;
$this->appData = $appData;
}

public function getImageUrl(string $key, bool $useSvg = true): string {
Expand Down Expand Up @@ -106,10 +105,12 @@ public function getImageUrlAbsolute(string $key, bool $useSvg = true): string {
*/
public function getImage(string $key, bool $useSvg = true): ISimpleFile {
$logo = $this->config->getAppValue('theming', $key . 'Mime', '');
$folder = $this->appData->getFolder('images');
$folder = $this->getRootFolder()->getFolder('images');

if ($logo === '' || !$folder->fileExists($key)) {
throw new NotFoundException();
}

if (!$useSvg && $this->shouldReplaceIcons()) {
if (!$folder->fileExists($key . '.png')) {
try {
Expand All @@ -127,6 +128,7 @@ public function getImage(string $key, bool $useSvg = true): ISimpleFile {
return $folder->getFile($key . '.png');
}
}

return $folder->getFile($key);
}

Expand Down Expand Up @@ -158,9 +160,9 @@ public function getCustomImages(): array {
public function getCacheFolder(): ISimpleFolder {
$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
try {
$folder = $this->appData->getFolder($cacheBusterValue);
$folder = $this->getRootFolder()->getFolder($cacheBusterValue);
} catch (NotFoundException $e) {
$folder = $this->appData->newFolder($cacheBusterValue);
$folder = $this->getRootFolder()->newFolder($cacheBusterValue);
$this->cleanup();
}
return $folder;
Expand Down Expand Up @@ -202,13 +204,13 @@ public function setCachedImage(string $filename, string $data): ISimpleFile {
public function delete(string $key): void {
/* ignore exceptions, since we don't want to fail hard if something goes wrong during cleanup */
try {
$file = $this->appData->getFolder('images')->getFile($key);
$file = $this->getRootFolder()->getFolder('images')->getFile($key);
$file->delete();
} catch (NotFoundException $e) {
} catch (NotPermittedException $e) {
}
try {
$file = $this->appData->getFolder('images')->getFile($key . '.png');
$file = $this->getRootFolder()->getFolder('images')->getFile($key . '.png');
$file->delete();
} catch (NotFoundException $e) {
} catch (NotPermittedException $e) {
Expand All @@ -219,9 +221,9 @@ public function updateImage(string $key, string $tmpFile): string {
$this->delete($key);

try {
$folder = $this->appData->getFolder('images');
$folder = $this->getRootFolder()->getFolder('images');
} catch (NotFoundException $e) {
$folder = $this->appData->newFolder('images');
$folder = $this->getRootFolder()->newFolder('images');
}

$target = $folder->newFile($key);
Expand Down Expand Up @@ -288,7 +290,7 @@ private function getSupportedUploadImageFormats(string $key): array {
*/
public function cleanup() {
$currentFolder = $this->getCacheFolder();
$folders = $this->appData->getDirectoryListing();
$folders = $this->getRootFolder()->getDirectoryListing();
foreach ($folders as $folder) {
if ($folder->getName() !== 'images' && $folder->getName() !== $currentFolder->getName()) {
$folder->delete();
Expand Down Expand Up @@ -316,4 +318,12 @@ public function shouldReplaceIcons() {
$cache->set('shouldReplaceIcons', $value);
return $value;
}

private function getRootFolder(): ISimpleFolder {
try {
return $this->appData->getFolder('global');
} catch (NotFoundException $e) {
return $this->appData->newFolder('global');
}
}
}
28 changes: 22 additions & 6 deletions apps/theming/lib/Jobs/MigrateBackgroundImages.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
use OCP\Files\AppData\IAppDataFactory;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\IConfig;

class MigrateBackgroundImages extends QueuedJob {
Expand All @@ -57,7 +58,6 @@ protected function run($argument): void {
return;
}

$themingData = $this->appDataFactory->get(Application::APP_ID);
$dashboardData = $this->appDataFactory->get('dashboard');

$userIds = $this->config->getUsersForUserValue('theming', 'background', 'custom');
Expand All @@ -79,11 +79,8 @@ protected function run($argument): void {

// migration
$file = $dashboardData->getFolder($userId)->getFile('background.jpg');
try {
$targetDir = $themingData->getFolder($userId);
} catch (NotFoundException $e) {
$targetDir = $themingData->newFolder($userId);
}
$targetDir = $this->getUserFolder($userId);

if (!$targetDir->fileExists('background.jpg')) {
$targetDir->newFile('background.jpg', $file->getContent());
}
Expand All @@ -104,4 +101,23 @@ protected function run($argument): void {
$this->jobList->add(self::class);
}
}

/**
* Get the root location for users theming data
*/
protected function getUserFolder(string $userId): ISimpleFolder {
$themingData = $this->appDataFactory->get(Application::APP_ID);

try {
$rootFolder = $themingData->getFolder('users');
} catch (NotFoundException $e) {
$rootFolder = $themingData->newFolder('users');
}

try {
return $rootFolder->getFolder($userId);
} catch (NotFoundException $e) {
return $rootFolder->newFolder($userId);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,6 @@ public function handle(Event $event): void {
$this->config->getUserValue($userId, Application::APP_ID, 'background', 'default'),
);

$this->initialState->provideInitialState(
'backgroundVersion',
$this->config->getUserValue($userId, Application::APP_ID, 'backgroundVersion', 0),
);

$this->initialState->provideInitialState(
'themingDefaultBackground',
$this->config->getAppValue('theming', 'backgroundMime', ''),
Expand Down
28 changes: 19 additions & 9 deletions apps/theming/lib/Service/BackgroundService.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,19 +136,19 @@ class BackgroundService {
private string $userId;
private IAppDataFactory $appDataFactory;

public function __construct(
IRootFolder $rootFolder,
IAppDataFactory $appDataFactory,
IConfig $config,
?string $userId
) {
public function __construct(IRootFolder $rootFolder,
IAppData $appData,
IConfig $config,
?string $userId,
IAppDataFactory $appDataFactory) {
if ($userId === null) {
return;
}

$this->rootFolder = $rootFolder;
$this->appData = $appDataFactory->get(Application::APP_ID);
$this->config = $config;
$this->userId = $userId;
$this->appData = $appData;
$this->appDataFactory = $appDataFactory;
}

Expand All @@ -167,12 +167,15 @@ public function setDefaultBackground(): void {
public function setFileBackground($path): void {
$this->config->setUserValue($this->userId, Application::APP_ID, 'background', 'custom');
$userFolder = $this->rootFolder->getUserFolder($this->userId);

/** @var File $file */
$file = $userFolder->get($path);
$image = new \OCP\Image();

if ($image->loadFromFileHandle($file->fopen('r')) === false) {
throw new InvalidArgumentException('Invalid image file');
}

$this->getAppDataFolder()->newFile('background.jpg', $file->fopen('r'));
}

Expand Down Expand Up @@ -207,14 +210,21 @@ public function getBackground(): ?ISimpleFile {
}

/**
* Storing the data in appdata/theming/users/USERID
*
* @return ISimpleFolder
* @throws NotPermittedException
*/
private function getAppDataFolder(): ISimpleFolder {
try {
return $this->appData->getFolder($this->userId);
$rootFolder = $this->appData->getFolder('users');
} catch (NotFoundException $e) {
$rootFolder = $this->appData->newFolder('users');
}
try {
return $rootFolder->getFolder($this->userId);
} catch (NotFoundException $e) {
return $this->appData->newFolder($this->userId);
return $rootFolder->newFolder($this->userId);
}
}
}
30 changes: 26 additions & 4 deletions apps/theming/lib/Service/ThemeInjectionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,35 @@
*/
namespace OCA\Theming\Service;

use OCA\Theming\AppInfo\Application;
use OCA\Theming\Themes\DefaultTheme;
use OCP\IConfig;
use OCP\IURLGenerator;
use OCP\IUserSession;
use OCP\Util;

class ThemeInjectionService {

private IURLGenerator $urlGenerator;
private ThemesService $themesService;
private DefaultTheme $defaultTheme;
private IConfig $config;
private ?string $userId;

public function __construct(IURLGenerator $urlGenerator,
ThemesService $themesService,
DefaultTheme $defaultTheme) {
DefaultTheme $defaultTheme,
IConfig $config,
IUserSession $userSession) {
$this->urlGenerator = $urlGenerator;
$this->themesService = $themesService;
$this->defaultTheme = $defaultTheme;
$this->config = $config;
if ($userSession->getUser() !== null) {
$this->userId = $userSession->getUser()->getUID();

Check notice

Code scanning / Psalm

PossiblyNullReference

Cannot call method getUID on possibly null value
} else {
$this->userId = null;
}
}

public function injectHeaders() {
Expand All @@ -50,13 +63,13 @@ public function injectHeaders() {

// Default theme fallback
$this->addThemeHeader($defaultTheme->getId());

// Themes applied by media queries
foreach($mediaThemes as $theme) {
$this->addThemeHeader($theme->getId(), true, $theme->getMediaQuery());
}

// Themes
// Themes
foreach($this->themesService->getThemes() as $theme) {
// Ignore default theme as already processed first
if ($theme->getId() === $this->defaultTheme->getId()) {
Expand All @@ -68,15 +81,24 @@ public function injectHeaders() {

/**
* Inject theme header into rendered page
*
*
* @param string $themeId the theme ID
* @param bool $plain request the :root syntax
* @param string $media media query to use in the <link> element
*/
private function addThemeHeader(string $themeId, bool $plain = true, string $media = null) {
$cacheBuster = $this->config->getAppValue('theming', 'cachebuster', '0');
if ($this->userId !== null) {
// need to bust the cache for the CSS file when the user background changed as its
// URL is served in those files
$userCacheBuster = $this->config->getUserValue($this->userId, Application::APP_ID, 'userCacheBuster', '0');
$cacheBuster .= $this->userId . '_' . $userCacheBuster;
}

$linkToCSS = $this->urlGenerator->linkToRoute('theming.Theming.getThemeStylesheet', [
'themeId' => $themeId,
'plain' => $plain,
'v' => substr(sha1($cacheBuster), 0, 8),
]);
Util::addHeader('link', [
'rel' => 'stylesheet',
Expand Down
4 changes: 3 additions & 1 deletion apps/theming/lib/Themes/DefaultTheme.php
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,11 @@ public function getCSSVariables(): array {
$user = $this->userSession->getUser();
if ($appManager->isEnabledForUser(Application::APP_ID) && $user !== null) {
$themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', 'default');
$currentVersion = (int)$this->config->getUserValue($user->getUID(), Application::APP_ID, 'userCacheBuster', '0');

if ($themingBackground === 'custom') {
$variables['--image-main-background'] = "url('" . $this->urlGenerator->linkToRouteAbsolute('theming.userTheme.getBackground') . "')";
$cacheBuster = substr(sha1($user->getUID() . '_' . $currentVersion), 0, 8);
$variables['--image-main-background'] = "url('" . $this->urlGenerator->linkToRouteAbsolute('theming.userTheme.getBackground') . "?v=$cacheBuster')";
} elseif (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground])) {
$variables['--image-main-background'] = "url('" . $this->urlGenerator->linkTo(Application::APP_ID, "/img/background/$themingBackground") . "')";
} elseif (substr($themingBackground, 0, 1) === '#') {
Expand Down
4 changes: 1 addition & 3 deletions apps/theming/src/UserThemes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ const enforceTheme = loadState('theming', 'enforceTheme', '')
const shortcutsDisabled = loadState('theming', 'shortcutsDisabled', false)

const background = loadState('theming', 'background')
const backgroundVersion = loadState('theming', 'backgroundVersion')
const themingDefaultBackground = loadState('theming', 'themingDefaultBackground')
const shippedBackgroundList = loadState('theming', 'shippedBackgrounds')

Expand All @@ -109,7 +108,6 @@ export default {
enforceTheme,
shortcutsDisabled,
background,
backgroundVersion,
themingDefaultBackground,
}
},
Expand Down Expand Up @@ -169,10 +167,10 @@ export default {
methods: {
updateBackground(data) {
this.background = (data.type === 'custom' || data.type === 'default') ? data.type : data.value
this.backgroundVersion = data.version
this.updateGlobalStyles()
this.$emit('update:background')
},

updateGlobalStyles() {
// Override primary-invert-if-bright and color-primary-text if background is set
const isBackgroundBright = shippedBackgroundList[this.background]?.theming === 'dark'
Expand Down
Loading