Skip to content

Commit

Permalink
Merge pull request #34612 from nextcloud/stable25-fix-background-appd…
Browse files Browse the repository at this point in the history
…ata-scrope

[stable25] Scope the appdata theming storage for global and users
  • Loading branch information
PVince81 authored Oct 14, 2022
2 parents b99e478 + d094884 commit e487d82
Show file tree
Hide file tree
Showing 14 changed files with 173 additions and 58 deletions.
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);
}
}
}
5 changes: 0 additions & 5 deletions apps/theming/lib/Listener/BeforeTemplateRenderedListener.php
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();
} 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

0 comments on commit e487d82

Please sign in to comment.