Skip to content

Commit c41982e

Browse files
Merge pull request #34081 from nextcloud/bugfix/noid/add-global-shortcuts-setting
Add global setting to disable keyboad shortcuts
2 parents 4c1f061 + df57b51 commit c41982e

16 files changed

Lines changed: 179 additions & 14 deletions

apps/theming/lib/AppInfo/Application.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,15 @@
2525
namespace OCA\Theming\AppInfo;
2626

2727
use OCA\Theming\Capabilities;
28+
use OCA\Theming\Listener\BeforePreferenceListener;
2829
use OCA\Theming\Listener\BeforeTemplateRenderedListener;
2930
use OCP\AppFramework\App;
3031
use OCP\AppFramework\Bootstrap\IBootContext;
3132
use OCP\AppFramework\Bootstrap\IBootstrap;
3233
use OCP\AppFramework\Bootstrap\IRegistrationContext;
3334
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
35+
use OCP\Config\BeforePreferenceDeletedEvent;
36+
use OCP\Config\BeforePreferenceSetEvent;
3437

3538
class Application extends App implements IBootstrap {
3639
public const APP_ID = 'theming';
@@ -42,6 +45,8 @@ public function __construct() {
4245
public function register(IRegistrationContext $context): void {
4346
$context->registerCapability(Capabilities::class);
4447
$context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
48+
$context->registerEventListener(BeforePreferenceSetEvent::class, BeforePreferenceListener::class);
49+
$context->registerEventListener(BeforePreferenceDeletedEvent::class, BeforePreferenceListener::class);
4550
}
4651

4752
public function boot(IBootContext $context): void {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
7+
*
8+
* @author Joas Schilling <coding@schilljs.com>
9+
*
10+
* @license GNU AGPL version 3 or any later version
11+
*
12+
* This program is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU Affero General Public License as
14+
* published by the Free Software Foundation, either version 3 of the
15+
* License, or (at your option) any later version.
16+
*
17+
* This program is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU Affero General Public License for more details.
21+
*
22+
* You should have received a copy of the GNU Affero General Public License
23+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24+
*
25+
*/
26+
namespace OCA\Theming\Listener;
27+
28+
use OCA\Theming\AppInfo\Application;
29+
use OCP\Config\BeforePreferenceDeletedEvent;
30+
use OCP\Config\BeforePreferenceSetEvent;
31+
use OCP\EventDispatcher\Event;
32+
use OCP\EventDispatcher\IEventListener;
33+
34+
class BeforePreferenceListener implements IEventListener {
35+
public function handle(Event $event): void {
36+
if (!$event instanceof BeforePreferenceSetEvent
37+
&& !$event instanceof BeforePreferenceDeletedEvent) {
38+
return;
39+
}
40+
41+
if ($event->getAppId() !== Application::APP_ID) {
42+
return;
43+
}
44+
45+
if ($event->getConfigKey() !== 'shortcuts_disabled') {
46+
return;
47+
}
48+
49+
if ($event instanceof BeforePreferenceSetEvent) {
50+
$event->setValid($event->getConfigValue() === 'yes');
51+
return;
52+
}
53+
54+
$event->setValid(true);
55+
}
56+
}

apps/theming/lib/Listener/BeforeTemplateRenderedListener.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
use OCA\Theming\Service\BackgroundService;
3030
use OCA\Theming\Service\JSDataService;
3131
use OCA\Theming\Service\ThemeInjectionService;
32+
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
33+
use OCP\AppFramework\Http\TemplateResponse;
3234
use OCP\AppFramework\Services\IInitialState;
3335
use OCP\EventDispatcher\Event;
3436
use OCP\EventDispatcher\IEventListener;
@@ -64,6 +66,17 @@ public function handle(Event $event): void {
6466
fn () => $this->container->get(JSDataService::class),
6567
);
6668

69+
/** @var BeforeTemplateRenderedEvent $event */
70+
if ($event->getResponse()->getRenderAs() === TemplateResponse::RENDER_AS_USER) {
71+
$this->initialState->provideLazyInitialState('shortcutsDisabled', function () {
72+
if ($this->userSession->getUser()) {
73+
$uid = $this->userSession->getUser()->getUID();
74+
return $this->config->getUserValue($uid, Application::APP_ID, 'shortcuts_disabled', 'no') === 'yes';
75+
}
76+
return false;
77+
});
78+
}
79+
6780
$this->themeInjectionService->injectHeaders();
6881

6982
$user = $this->userSession->getUser();

apps/theming/src/UserThemes.vue

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323

2424
<template>
2525
<section>
26-
<NcSettingsSection class="theming" :title="t('theming', 'Appearance and accessibility')">
26+
<NcSettingsSection :title="t('theming', 'Appearance and accessibility')"
27+
:limit-width="false"
28+
class="theming">
2729
<p v-html="description" />
2830
<p v-html="descriptionDetail" />
2931

@@ -48,6 +50,18 @@
4850
@change="changeFont" />
4951
</div>
5052
</NcSettingsSection>
53+
54+
<NcSettingsSection :title="t('theming', 'Keyboard shortcuts')">
55+
<p>{{ t('theming', 'In some cases keyboard shortcuts can interfer with accessibility tools. In order to allow focusing on your tool correctly you can disable all keyboard shortcuts here. This will also disable all available shortcuts in apps.') }}</p>
56+
<NcCheckboxRadioSwitch class="theming__preview-toggle"
57+
:checked.sync="shortcutsDisabled"
58+
name="shortcuts_disabled"
59+
type="switch"
60+
@change="changeShortcutsDisabled">
61+
{{ t('theming', 'Disable all keyboard shortcuts') }}
62+
</NcCheckboxRadioSwitch>
63+
</NcSettingsSection>
64+
5165
<NcSettingsSection :title="t('theming', 'Background')"
5266
class="background">
5367
<p>{{ t('theming', 'Set a custom background') }}</p>
@@ -63,6 +77,7 @@
6377
import { generateOcsUrl, imagePath } from '@nextcloud/router'
6478
import { loadState } from '@nextcloud/initial-state'
6579
import axios from '@nextcloud/axios'
80+
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch'
6681
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection'
6782
6883
import BackgroundSettings from './components/BackgroundSettings.vue'
@@ -72,6 +87,7 @@ import { getBackgroundUrl } from '../src/helpers/getBackgroundUrl.js'
7287
7388
const availableThemes = loadState('theming', 'themes', [])
7489
const enforceTheme = loadState('theming', 'enforceTheme', '')
90+
const shortcutsDisabled = loadState('theming', 'shortcutsDisabled', false)
7591
7692
const background = loadState('theming', 'background')
7793
const backgroundVersion = loadState('theming', 'backgroundVersion')
@@ -84,6 +100,7 @@ export default {
84100
name: 'UserThemes',
85101
components: {
86102
ItemPreview,
103+
NcCheckboxRadioSwitch,
87104
NcSettingsSection,
88105
BackgroundSettings,
89106
},
@@ -92,6 +109,7 @@ export default {
92109
return {
93110
availableThemes,
94111
enforceTheme,
112+
shortcutsDisabled,
95113
background,
96114
themingDefaultBackground,
97115
}
@@ -151,9 +169,17 @@ export default {
151169
return '<a target="_blank" href="https://nextcloud.com/design" rel="noreferrer nofollow">'
152170
},
153171
},
172+
154173
mounted() {
155174
this.updateGlobalStyles()
156175
},
176+
177+
watch: {
178+
shortcutsDisabled(newState) {
179+
this.changeShortcutsDisabled(newState)
180+
},
181+
},
182+
157183
methods: {
158184
updateBackground(data) {
159185
this.background = (data.type === 'custom' || data.type === 'default') ? data.type : data.value
@@ -212,6 +238,29 @@ export default {
212238
this.selectItem(enabled, id)
213239
},
214240
241+
async changeShortcutsDisabled(newState) {
242+
if (newState) {
243+
await axios({
244+
url: generateOcsUrl('apps/provisioning_api/api/v1/config/users/{appId}/{configKey}', {
245+
appId: 'theming',
246+
configKey: 'shortcuts_disabled',
247+
}),
248+
data: {
249+
configValue: 'yes',
250+
},
251+
method: 'POST',
252+
})
253+
} else {
254+
await axios({
255+
url: generateOcsUrl('apps/provisioning_api/api/v1/config/users/{appId}/{configKey}', {
256+
appId: 'theming',
257+
configKey: 'shortcuts_disabled',
258+
}),
259+
method: 'DELETE',
260+
})
261+
}
262+
},
263+
215264
updateBodyAttributes() {
216265
const enabledThemesIDs = this.themes.filter(theme => theme.enabled === true).map(theme => theme.id)
217266
const enabledFontsIDs = this.fonts.filter(font => font.enabled === true).map(font => font.id)

core/src/OCP/accessibility.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
3+
*
4+
* @author Joas Schilling <coding@schilljs.com>
5+
*
6+
* @license AGPL-3.0-or-later
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as
10+
* published by the Free Software Foundation, either version 3 of the
11+
* License, or (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License
19+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
23+
import { loadState } from '@nextcloud/initial-state'
24+
25+
export default {
26+
/**
27+
* @return {boolean} Whether the user opted-out of shortcuts so that they should not be registered
28+
*/
29+
disableKeyboardShortcuts() {
30+
return loadState('theming', 'shortcutsDisabled', false)
31+
},
32+
}

core/src/OCP/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ import * as AppConfig from './appconfig'
2828
import * as Comments from './comments'
2929
import * as WhatsNew from './whatsnew'
3030

31+
import Accessibility from './accessibility'
3132
import Collaboration from './collaboration'
3233
import Loader from './loader'
3334
import Toast from './toast'
3435

3536
/** @namespace OCP */
3637
export default {
38+
Accessibility,
3739
AppConfig,
3840
Collaboration,
3941
Comments,

core/src/components/HeaderMenu.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export default {
8181
handler: this.closeMenu,
8282
middleware: this.clickOutsideMiddleware,
8383
},
84+
shortcutsDisabled: OCP.Accessibility.disableKeyboardShortcuts(),
8485
}
8586
},
8687
@@ -144,6 +145,10 @@ export default {
144145
},
145146
146147
onKeyDown(event) {
148+
if (this.shortcutsDisabled) {
149+
return
150+
}
151+
147152
// If opened and escape pressed, close
148153
if (event.key === 'Escape' && this.opened) {
149154
event.preventDefault()

core/src/views/UnifiedSearch.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,10 @@ export default {
334334
},
335335
336336
mounted() {
337+
if (OCP.Accessibility.disableKeyboardShortcuts()) {
338+
return
339+
}
340+
337341
document.addEventListener('keydown', (event) => {
338342
// if not already opened, allows us to trigger default browser on second keydown
339343
if (event.ctrlKey && event.key === 'f' && !this.open) {

dist/core-main.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/core-main.js.LICENSE.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -427,10 +427,9 @@
427427
*/
428428

429429
/**
430-
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
430+
* @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
431431
*
432-
* @author John Molakvoæ <skjnldsv@protonmail.com>
433-
* @author Julius Härtl <jus@bitgrid.net>
432+
* @author Joas Schilling <coding@schilljs.com>
434433
*
435434
* @license AGPL-3.0-or-later
436435
*

0 commit comments

Comments
 (0)