Skip to content

Commit

Permalink
feat(theming): Allow to configure default apps and app order in front…
Browse files Browse the repository at this point in the history
…end settings

* Also add API for setting the value using ajax.
* Add cypress tests for app order and defaul apps

Signed-off-by: Ferdinand Thiessen <[email protected]>
  • Loading branch information
susnux committed Oct 19, 2023
1 parent 363d9eb commit e9d4036
Show file tree
Hide file tree
Showing 17 changed files with 1,048 additions and 48 deletions.
5 changes: 5 additions & 0 deletions apps/theming/appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
*/
return [
'routes' => [
[
'name' => 'Theming#updateAppMenu',
'url' => '/ajax/updateAppMenu',
'verb' => 'PUT',
],
[
'name' => 'Theming#updateStylesheet',
'url' => '/ajax/updateStylesheet',
Expand Down
43 changes: 43 additions & 0 deletions apps/theming/lib/Controller/ThemingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
*/
namespace OCA\Theming\Controller;

use InvalidArgumentException;
use OCA\Theming\ImageManager;
use OCA\Theming\Service\ThemesService;
use OCA\Theming\ThemingDefaults;
Expand Down Expand Up @@ -180,6 +181,47 @@ public function updateStylesheet($setting, $value) {
]);
}

/**
* @AuthorizedAdminSetting(settings=OCA\Theming\Settings\Admin)
* @param string $setting
* @param mixed $value
* @return DataResponse
* @throws NotPermittedException
*/
public function updateAppMenu($setting, $value) {
$error = null;
switch ($setting) {
case 'defaultApps':
if (is_array($value)) {
try {
$this->appManager->setDefaultApps($value);
} catch (InvalidArgumentException $e) {
$error = $this->l10n->t('Invalid app given');
}
} else {
$error = $this->l10n->t('Invalid type for setting "defaultApp" given');
}
break;
default:
$error = $this->l10n->t('Invalid setting key');
}
if ($error !== null) {
return new DataResponse([
'data' => [
'message' => $error,
],
'status' => 'error'
], Http::STATUS_BAD_REQUEST);
}

return new DataResponse([
'data' => [
'message' => $this->l10n->t('Saved'),
],
'status' => 'success'
]);
}

/**
* Check that a string is a valid http/https url
*/
Expand Down Expand Up @@ -299,6 +341,7 @@ public function undo(string $setting): DataResponse {
*/
public function undoAll(): DataResponse {
$this->themingDefaults->undoAll();
$this->appManager->setDefaultApps([]);

return new DataResponse(
[
Expand Down
38 changes: 36 additions & 2 deletions apps/theming/lib/Listener/BeforePreferenceListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,34 @@
namespace OCA\Theming\Listener;

use OCA\Theming\AppInfo\Application;
use OCP\App\IAppManager;
use OCP\Config\BeforePreferenceDeletedEvent;
use OCP\Config\BeforePreferenceSetEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;

class BeforePreferenceListener implements IEventListener {
public function __construct(
private IAppManager $appManager,
) {
}

public function handle(Event $event): void {
if (!$event instanceof BeforePreferenceSetEvent
&& !$event instanceof BeforePreferenceDeletedEvent) {
// Invalid event type
return;
}

if ($event->getAppId() !== Application::APP_ID) {
return;
switch ($event->getAppId()) {
case Application::APP_ID: $this->handleThemingValues($event); break;
case 'core': $this->handleCoreValues($event); break;
}
}

private function handleThemingValues(BeforePreferenceSetEvent|BeforePreferenceDeletedEvent $event): void {
if ($event->getConfigKey() !== 'shortcuts_disabled') {
// Not allowed config key
return;
}

Expand All @@ -53,4 +64,27 @@ public function handle(Event $event): void {

$event->setValid(true);
}

private function handleCoreValues(BeforePreferenceSetEvent|BeforePreferenceDeletedEvent $event): void {
if ($event->getConfigKey() !== 'apporder') {
// Not allowed config key
return;
}

if ($event instanceof BeforePreferenceDeletedEvent) {
$event->setValid(true);
return;
}

$value = json_decode($event->getConfigValue(), true, flags:JSON_THROW_ON_ERROR);
if (is_array(($value))) {
foreach ($value as $appName => $order) {
if (!$this->appManager->isEnabledForUser($appName) || !is_array($order) || empty($order) || !is_numeric($order[key($order)])) {
// Invalid config value, refuse the change
return;
}
}
}
$event->setValid(true);
}
}
35 changes: 12 additions & 23 deletions apps/theming/lib/Settings/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,28 +40,16 @@
use OCP\Util;

class Admin implements IDelegatedSettings {
private string $appName;
private IConfig $config;
private IL10N $l;
private ThemingDefaults $themingDefaults;
private IInitialState $initialState;
private IURLGenerator $urlGenerator;
private ImageManager $imageManager;

public function __construct(string $appName,
IConfig $config,
IL10N $l,
ThemingDefaults $themingDefaults,
IInitialState $initialState,
IURLGenerator $urlGenerator,
ImageManager $imageManager) {
$this->appName = $appName;
$this->config = $config;
$this->l = $l;
$this->themingDefaults = $themingDefaults;
$this->initialState = $initialState;
$this->urlGenerator = $urlGenerator;
$this->imageManager = $imageManager;
public function __construct(
private string $appName,
private IConfig $config,
private IL10N $l,
private ThemingDefaults $themingDefaults,
private IInitialState $initialState,
private IURLGenerator $urlGenerator,
private ImageManager $imageManager,
) {
}

/**
Expand All @@ -80,7 +68,7 @@ public function getForm(): TemplateResponse {
$carry[$key] = $this->imageManager->getSupportedUploadImageFormats($key);
return $carry;
}, []);

$this->initialState->provideInitialState('adminThemingParameters', [
'isThemable' => $themable,
'notThemableErrorMessage' => $errorMessage,
Expand All @@ -89,6 +77,7 @@ public function getForm(): TemplateResponse {
'slogan' => $this->themingDefaults->getSlogan(),
'color' => $this->themingDefaults->getDefaultColorPrimary(),
'logoMime' => $this->config->getAppValue(Application::APP_ID, 'logoMime', ''),
'allowedMimeTypes' => $allowedMimeTypes,
'backgroundMime' => $this->config->getAppValue(Application::APP_ID, 'backgroundMime', ''),
'logoheaderMime' => $this->config->getAppValue(Application::APP_ID, 'logoheaderMime', ''),
'faviconMime' => $this->config->getAppValue(Application::APP_ID, 'faviconMime', ''),
Expand All @@ -98,7 +87,7 @@ public function getForm(): TemplateResponse {
'docUrlIcons' => $this->urlGenerator->linkToDocs('admin-theming-icons'),
'canThemeIcons' => $this->imageManager->shouldReplaceIcons(),
'userThemingDisabled' => $this->themingDefaults->isUserThemingDisabled(),
'allowedMimeTypes' => $allowedMimeTypes,
'defaultApps' => array_filter(explode(',', $this->config->getSystemValueString('defaultapp', ''))),
]);

Util::addScript($this->appName, 'admin-theming');
Expand Down
30 changes: 14 additions & 16 deletions apps/theming/lib/Settings/Personal.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use OCA\Theming\ITheme;
use OCA\Theming\Service\ThemesService;
use OCA\Theming\ThemingDefaults;
use OCP\App\IAppManager;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\IConfig;
Expand All @@ -36,22 +37,15 @@

class Personal implements ISettings {

protected string $appName;
private IConfig $config;
private ThemesService $themesService;
private IInitialState $initialStateService;
private ThemingDefaults $themingDefaults;

public function __construct(string $appName,
IConfig $config,
ThemesService $themesService,
IInitialState $initialStateService,
ThemingDefaults $themingDefaults) {
$this->appName = $appName;
$this->config = $config;
$this->themesService = $themesService;
$this->initialStateService = $initialStateService;
$this->themingDefaults = $themingDefaults;
public function __construct(
protected string $appName,
private string $userId,
private IConfig $config,
private ThemesService $themesService,
private IInitialState $initialStateService,
private ThemingDefaults $themingDefaults,
private IAppManager $appManager,
) {
}

public function getForm(): TemplateResponse {
Expand All @@ -74,9 +68,13 @@ public function getForm(): TemplateResponse {
});
}

// Get the default app enforced by admin
$forcedDefaultApp = $this->appManager->getDefaultAppForUser(null, false);

$this->initialStateService->provideInitialState('themes', array_values($themes));
$this->initialStateService->provideInitialState('enforceTheme', $enforcedTheme);
$this->initialStateService->provideInitialState('isUserThemingDisabled', $this->themingDefaults->isUserThemingDisabled());
$this->initialStateService->provideInitialState('enforcedDefaultApp', $forcedDefaultApp);

Util::addScript($this->appName, 'personal-theming');

Expand Down
7 changes: 7 additions & 0 deletions apps/theming/src/AdminTheming.vue
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
</a>
</div>
</NcSettingsSection>
<AppMenuSection :default-apps.sync="defaultApps" />
</section>
</template>

Expand All @@ -118,6 +119,7 @@ import CheckboxField from './components/admin/CheckboxField.vue'
import ColorPickerField from './components/admin/ColorPickerField.vue'
import FileInputField from './components/admin/FileInputField.vue'
import TextField from './components/admin/TextField.vue'
import AppMenuSection from './components/admin/AppMenuSection.vue'
const {
backgroundMime,
Expand All @@ -136,6 +138,7 @@ const {
slogan,
url,
userThemingDisabled,
defaultApps,
} = loadState('theming', 'adminThemingParameters')
const textFields = [
Expand Down Expand Up @@ -247,6 +250,7 @@ export default {
name: 'AdminTheming',
components: {
AppMenuSection,
CheckboxField,
ColorPickerField,
FileInputField,
Expand All @@ -259,6 +263,8 @@ export default {
'update:theming',
],
textFields,
data() {
return {
textFields,
Expand All @@ -267,6 +273,7 @@ export default {
advancedTextFields,
advancedFileInputFields,
userThemingField,
defaultApps,
canThemeIcons,
docUrl,
Expand Down
6 changes: 4 additions & 2 deletions apps/theming/src/UserThemes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
{{ t('theming', 'Disable all keyboard shortcuts') }}
</NcCheckboxRadioSwitch>
</NcSettingsSection>

<UserAppMenuSection />
</section>
</template>

Expand All @@ -87,15 +89,14 @@ import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.
import BackgroundSettings from './components/BackgroundSettings.vue'
import ItemPreview from './components/ItemPreview.vue'
import UserAppMenuSection from './components/UserAppMenuSection.vue'
const availableThemes = loadState('theming', 'themes', [])
const enforceTheme = loadState('theming', 'enforceTheme', '')
const shortcutsDisabled = loadState('theming', 'shortcutsDisabled', false)
const isUserThemingDisabled = loadState('theming', 'isUserThemingDisabled')
console.debug('Available themes', availableThemes)
export default {
name: 'UserThemes',
Expand All @@ -104,6 +105,7 @@ export default {
NcCheckboxRadioSwitch,
NcSettingsSection,
BackgroundSettings,
UserAppMenuSection,
},
data() {
Expand Down
Loading

0 comments on commit e9d4036

Please sign in to comment.