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

feat: Allow to configure the app menu order in the frontend #40844

Merged
merged 4 commits into from Oct 19, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions apps/theming/appinfo/routes.php
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
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
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;
susnux marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
$event->setValid(true);
}
}
35 changes: 12 additions & 23 deletions apps/theming/lib/Settings/Admin.php
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
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
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
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