keyboard_shortcuts-1.0.0/src/Form/KeyboardShortcutsSettingsForm.php
src/Form/KeyboardShortcutsSettingsForm.php
<?php
namespace Drupal\keyboard_shortcuts\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\user\Entity\Role;
class KeyboardShortcutsSettingsForm extends ConfigFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'keyboard_shortcuts';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return [
'keyboard_shortcuts.settings',
];
}
/**
* Get a descriptive help text.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
*/
protected function getDescription() {
$description = $this->t(<<< QQ
<b>Modifier keys</b>
<p>You can use the following modifier keywords: <em>shift</em>,
<em>ctrl</em>, <em>alt</em>, or <em>meta</em>.</p>
<p>You can use substitute keywords <em>option</em> for <em>alt</em> and
<em>command</em> for <em>meta</em>.</p>
<p>Other special keywords are: <em>backspace</em>, <em>tab</em>,
<em>enter</em>, <em>return</em>,
<em>capslock</em>, <em>esc</em>, <em>escape</em>, <em>space</em>,
<em>pageup</em>, <em>pagedown</em>, <em>end</em>, <em>home</em>,
<em>left</em>, <em>up</em>, <em>right</em>, <em>down</em>, <em>ins</em>,
<em>del</em> and <em>plus</em>.</p>
<p>Any other key you should be able to reference directly by it's
character: a-z, /, $, *, or =.</p>
<b>Single key</b>
<p>Just type in the character or special key names (see above).</p>
<b>Combination of keys</b>
<p>Combine several keys using a '+' sign (ctrl+s).</p>
<b>Sequence of keys</b>
<p>Define a sequence of keys using a space (d ctrl+s).</p>
<b>Tokens</b>
<p>You can use '[node:]' and '[user:]' tokens in the action argument
fields. (e.g. [node:edit-url])</p>
<b>Note</b>
<p>Some browsers block standard shortcuts like for example (ctrl+n or
command+n). It is advised to use combinations that don't overwrite
default browser behavior.</p>
QQ);
return $description;
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
$config = $this->config('keyboard_shortcuts.settings');
$roles = Role::loadMultiple();
array_walk($roles, function(&$role) {
$role = $role->label();
});
$form['#title'] = $this->t('Keyboard shortcut settings');
$form['#tree'] = true;
$form['shortcuts'] = [
'#type' => 'fieldset',
'#title' => $this->t('Shortcuts'),
'#collapsed' => FALSE,
'#prefix' => '<div id="shortcuts-wrapper">',
'#suffix' => '</div>',
];
$shortcuts = $form_state->getValue('shortcuts') ?? $config->get('shortcuts') ?? [];
if (empty($shortcuts)) {
$shortcuts[] = $this->getNewShortcut();
}
foreach ($shortcuts as $delta => $shortcut) {
$auto_label = (empty($shortcut['keystroke']) ? 'Undefined shortcut' : $shortcut['keystroke']);
$label = empty($shortcut['label']) ? $auto_label : $shortcut['label'];
$form['shortcuts'][$delta] = [
'#type' => 'details',
'#collapsed' => !empty($shortcut['keystroke']),
'#collapsible' => TRUE,
'#title' => ($label != $auto_label ? $label . ' (' . $auto_label . ')' : $label),
];
$form['shortcuts'][$delta]['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label') . ' ' . $delta,
'#description' => $this->t('Give the shortcut a label (used in the gui).'),
'#default_value' => $label,
'#required' => true,
];
$form['shortcuts'][$delta]['keystroke'] = [
'#type' => 'textfield',
'#title' => $this->t('Keystroke(s)'),
'#description' => $this->t('Give the keystroke(s) to associate to this shortcut. Separate multiple keystrokes with a comma. Leave empty to remove this keystroke entry.'),
'#default_value' => $shortcut['keystroke'] ?? '',
'#required' => true,
];
$form['shortcuts'][$delta]['roles'] = [
'#type' => 'checkboxes',
'#title' => $this->t('Roles'),
'#options' => $roles,
'#description' => $this->t('Select which roles are allowed to use this keystroke. Select none to disable this check.'),
'#default_value' => $shortcut['roles'] ?? [],
];
$form['shortcuts'][$delta]['enable_during_input'] = [
'#type' => 'checkbox',
'#title' => $this->t('Enable during input'),
'#description' => $this->t('Enable the use of the shortcut inside of inputs (textarea, text, select, editable content)'),
'#default_value' => $shortcut['enable_during_input'] ?? 1,
];
$form['shortcuts'][$delta]['prevent_default'] = [
'#type' => 'checkbox',
'#title' => $this->t('Prevent default'),
'#description' => $this->t('Prevent default behaviour of the shortcut keystroke (eg. Ctrl+S, normally saves the webpage).'),
'#default_value' => $shortcut['prevent_default'] ?? 1,
];
$form['shortcuts'][$delta]['action'] = [
'#type' => 'select',
'#title' => $this->t('Action'),
'#description' => $this->t('Give the action to be executed'),
'#default_value' => $shortcut['action'] ?? 'redirect',
'#options' => [
'trigger' => 'trigger',
'modal' => 'modal',
'redirect' => 'redirect',
'callback' => 'callback',
],
'#required' => true,
];
$form['shortcuts'][$delta]['arguments'] = [
'#type' => 'fieldset',
'#collapsed' => FALSE,
'#collapsible' => FALSE,
'#title' => $this->t('Action settings'),
];
$arguments = $shortcut['arguments'] ?? [];
// Trigger arguments
$form['shortcuts'][$delta]['arguments']['type'] = [
'#type' => 'select',
'#title' => $this->t('Type'),
'#default_value' => $arguments['type'] ?? '',
'#options' => [
'click' => 'click',
'mouseleave' => 'mouseleave',
'mouseenter' => 'mouseenter',
],
'#states' => [
'visible' => [
':input[name="shortcuts['.$delta.'][action]"]' => ['value' => 'trigger'],
],
],
];
$form['shortcuts'][$delta]['arguments']['selector'] = [
'#type' => 'textfield',
'#title' => $this->t('Selector'),
'#description' => $this->t('Enter a jQuery style element selector. You can use tokens (see below).'),
'#default_value' => $arguments['selector'] ?? '',
'#states' => [
'visible' => [
':input[name="shortcuts['.$delta.'][action]"]' => ['value' => 'trigger'],
],
],
];
// 'redirect':
$form['shortcuts'][$delta]['arguments']['url'] = [
'#type' => 'textfield',
'#title' => $this->t('URL'),
'#description' => $this->t('Enter a url to redirect to. You can use tokens (see below).'),
'#default_value' => $arguments['url'] ?? '',
'#states' => [
'visible' => [
':input[name="shortcuts['.$delta.'][action]"]' => [
['value' => 'redirect'],
['value' => 'modal'],
],
],
],
];
// 'callback':
$form['shortcuts'][$delta]['arguments']['callback'] = [
'#type' => 'textfield',
'#title' => $this->t('Callback'),
'#description' => $this->t('The javascript callback to execute when the shortcut is pressed.'),
'#default_value' => $arguments['callback'] ?? '',
'#states' => [
'visible' => [
':input[name="shortcuts['.$delta.'][action]"]' => ['value' => 'callback'],
],
],
];
$form['shortcuts'][$delta]['remove'] = [
'#type' => 'submit',
'#value' => $this->t('Remove'),
'#submit' => ['::removeCallback'],
'#name' => 'remove-shortcut-' . $delta,
'#ajax' => [
'callback' => '::ajaxCallback',
'wrapper' => 'shortcuts-wrapper',
],
];
}
$form['shortcuts']['actions']['add'] = [
'#type' => 'submit',
'#value' => $this->t('Add shortcut'),
'#submit' => ['::addCallback'],
'#ajax' => [
'callback' => '::ajaxCallback',
'wrapper' => 'shortcuts-wrapper',
],
];
$form_state->setCached(FALSE);
$form['info'] = [
'#type' => 'item',
'#description' => $this->getDescription(),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$config = $this->config('keyboard_shortcuts.settings');
$shortcuts = $form_state->getValue('shortcuts');
// Filter out any empty keystrokes.
$shortcuts = array_filter($shortcuts, function ($shortcut ) {
return !empty($shortcut['keystroke']);
});
// Don't save the add more button.
unset($shortcuts['actions']);
// Clean up the arguments we don't need.
array_walk($shortcuts, function(&$shortcut) {
$arguments = [
'' => [],
'trigger' => ['url', 'callback'],
'modal' => ['callback', 'type', 'selector'],
'redirect' => ['callback', 'type', 'selector'],
'callback' => ['url', 'type', 'selector'],
][$shortcut['action'] ?? ''];
foreach($arguments as $argument) {
unset($shortcut['arguments'][$argument]);
}
// Don't save the remove button.
unset($shortcut['remove']);
});
$config->set('shortcuts', array_values($shortcuts));
$config->save();
parent::submitForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$element = $form_state->getTriggeringElement();
if (isset($element['#array_parents'][2]) && $element['#array_parents'][2] == 'remove') {
$form_state->clearErrors();
}
}
public function addCallback(array &$form, FormStateInterface $form_state) {
$shortcuts = $form_state->getValue('shortcuts');
unset($shortcuts['actions']);
$shortcuts[] = $this->getNewShortcut();
$form_state->setValue('shortcuts', $shortcuts);
$form_state->setRebuild();
}
public function ajaxCallback(array &$form, FormStateInterface $form_state) {
return $form['shortcuts'];
}
public function removeCallback(array &$form, FormStateInterface $form_state) {
$shortcuts = $form_state->getValue('shortcuts');
unset($shortcuts['actions']);
$element = $form_state->getTriggeringElement();
$index = $element['#array_parents'][1];
unset($shortcuts[$index]);
$form_state->setValue('shortcuts', $shortcuts);
$form_state->setRebuild();
}
protected function getNewShortcut() {
return [
'label' => $this->t('New shortcut'),
'keystroke' => '',
'roles' => [],
'enable_during_input' => 1,
'prevent_default' => 1,
'action' => 'redirect',
'arguments' => [
'type' => '',
'selector' => '',
'url' => '',
'callback' => '',
],
];
}
}
