monster_menus-9.0.x-dev/src/Form/ConfigForm.php
src/Form/ConfigForm.php
<?php
namespace Drupal\monster_menus\Form;
use Drupal\Component\Utility\EmailValidatorInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\RedundantEditableConfigNamesTrait;
use Drupal\Core\State\State;
use Drupal\Core\Url;
use Drupal\monster_menus\Constants;
use Drupal\user\Entity\Role;
use Drupal\user\PermissionHandlerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ConfigForm extends ConfigFormBase {
use RedundantEditableConfigNamesTrait;
/**
* The permission handler.
*
* @var \Drupal\user\PermissionHandlerInterface
*/
protected $permissionHandler;
/**
* The state handler.
*
* @var \Drupal\Core\State\State
*/
protected $state;
/**
* @var \Drupal\Component\Utility\EmailValidatorInterface
*/
private EmailValidatorInterface $emailValidator;
/**
* Constructs a ConfigForm object.
*
* @param ConfigFactoryInterface $config_factory
* The config factory.
* @param \Drupal\user\PermissionHandlerInterface $permission_handler
* The permission handler.
* @param \Drupal\Core\State\State $state
* The state handler.
* @param EmailValidatorInterface $email_validator
* The email validator service.
*/
public function __construct(ConfigFactoryInterface $config_factory, PermissionHandlerInterface $permission_handler, State $state, EmailValidatorInterface $email_validator, TypedConfigManagerInterface $typed_config_manager) {
$this->permissionHandler = $permission_handler;
$this->state = $state;
$this->emailValidator = $email_validator;
parent::__construct($config_factory, $typed_config_manager);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('user.permissions'),
$container->get('state'),
$container->get('email.validator'),
$container->get('config.typed'),
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'mm_admin_config';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Note: When creating form elements that refer to config settings
// containing sub-elements (with dots), change the dots to dashes. For
// instance:
// $form['foo-bar'] = [
// '#type' => 'text',
// '#default_value' => $settings->get('foo.bar')
// ]
$settings = $this->config('monster_menus.settings');
if ($this->currentUser()->hasPermission('administer all menus')) {
$form['general'] = [
'#type' => 'details',
'#title' => $this->t('General'),
];
$form['general']['recycle_auto_empty'] = [
'#type' => 'select',
'#title' => $this->t('Automatic recycle bin deletion interval'),
'#description' => $this->t('Automatically delete content in recycle bins that has been there longer than this amount of time'),
'#config_target' => 'monster_menus.settings:recycle_auto_empty',
'#options' => [
-1 => $this->t('(don\'t use recycle bins)'),
0 => $this->t('(never auto delete)'),
30 * 60 => $this->t('30 minutes'),
60 * 60 => $this->t('1 hour'),
2 * 60 * 60 => $this->t('2 hours'),
6 * 60 * 60 => $this->t('6 hours'),
12 * 60 * 60 => $this->t('12 hours'),
24 * 60 * 60 => $this->t('1 day'),
2 * 24 * 60 * 60 => $this->t('2 days'),
3 * 24 * 60 * 60 => $this->t('3 days'),
7 * 24 * 60 * 60 => $this->t('1 week'),
2 * 7 * 24 * 60 * 60 => $this->t('2 weeks'),
30 * 24 * 60 * 60 => $this->t('30 days'),
60 * 24 * 60 * 60 => $this->t('60 days'),
intval(365 / 4 * 24 * 60 * 60) => $this->t('3 months'),
intval(365 / 2 * 24 * 60 * 60) => $this->t('6 months'),
intval(365 / 4 * 3 * 24 * 60 * 60) => $this->t('9 months'),
365 * 24 * 60 * 60 => $this->t('1 year'),
],
];
$form['general']['access_cache_time'] = [
'#type' => 'select',
'#options' => [
0 => $this->t('(disabled)'),
30 => $this->t('30 seconds'),
60 => $this->t('1 minute'),
120 => $this->t('2 minutes'),
180 => $this->t('3 minutes'),
240 => $this->t('4 minutes'),
300 => $this->t('5 minutes'),
600 => $this->t('10 minutes'),
900 => $this->t('15 minutes'),
1200 => $this->t('20 minutes'),
1800 => $this->t('30 minutes'),
2700 => $this->t('45 minutes'),
3600 => $this->t('1 hour'),
],
'#title' => $this->t('Permissions cache time'),
'#description' => $this->t("Save time by caching the data needed to determine if a given user has access to a page or piece of content. The cache is automatically cleared whenever a piece content or any of its parent pages' permissions are modified. Setting this value too high can lead to lots of data being stored in the cache table."),
'#config_target' => 'monster_menus.settings:access_cache_time',
];
$form['general']['prevent_showpage_removal'] = [
'#type' => 'select',
'#options' => [
Constants::MM_PREVENT_SHOWPAGE_REMOVAL_NONE => $this->t('Do not check'),
Constants::MM_PREVENT_SHOWPAGE_REMOVAL_WARN => $this->t('Show a warning'),
Constants::MM_PREVENT_SHOWPAGE_REMOVAL_HALT => $this->t('Prevent non-admins from moving or renaming'),
],
'#title' => $this->t('Protect dynamic content (hook_mm_showpage_routing) from removal'),
'#description' => $this->t('This option detects when the user is about to either move or rename a page that is referred to (or is the parent of a page referred to) in a hook_mm_showpage_routing() implemention. If the "Prevent" option is chosen, users with the "administer all menus" permission will only see a warning.'),
'#config_target' => 'monster_menus.settings:prevent_showpage_removal',
];
$form['vgroup'] = [
'#type' => 'details',
'#title' => $this->t('Virtual Groups'),
];
$form['vgroup']['vgroup-regen_chunk'] = [
'#type' => 'number',
'#title' => $this->t('Chunk size used to split virtual group regeneration queries'),
'#min' => 1,
'#size' => 5,
'#description' => $this->t('To gain speed when regenerating dirty virtual groups, the separate queries are concatenated into one big query with a UNION. If the maximum SQL query buffer length is being exceeded or the large queries are taking too much memory, this value should be reduced.'),
'#config_target' => 'monster_menus.settings:vgroup.regen_chunk',
];
$form['vgroup']['vgroup-regen_chunks_per_run'] = [
'#type' => 'number',
'#title' => $this->t('Number of concatenated virtual group queries per cron run'),
'#min' => 1,
'#size' => 5,
'#description' => $this->t('If cron is taking too long to complete, this value should be reduced. The total number of dirty virtual groups updated per cron run is this number times the chunk size.'),
'#config_target' => 'monster_menus.settings:vgroup.regen_chunks_per_run',
];
$form['vgroup']['vgroup-errors_email'] = [
'#type' => 'textfield',
'#title' => $this->t('Where to send virtual group warnings'),
'#description' => $this->t('Warnings are generated for any virtual groups that decrease in size too rapidly. This is the e-mail address where warnings are sent. If left blank, the site e-mail address is used.'),
'#maxlength' => 1024,
'#size' => 100,
'#config_target' => 'monster_menus.settings:vgroup.errors_email',
];
$form['vgroup']['vgroup-group_info_message'] = [
'#type' => 'textfield',
'#title' => $this->t('Message displayed in group information for virtual groups'),
'#description' => $this->t('Two variables are available for substitution: @gid is the group ID and @owner is the themed owner of the group.'),
'#maxlength' => 1024,
'#size' => 100,
'#config_target' => 'monster_menus.settings:vgroup.group_info_message',
];
$form['vgroup']['group-group_info_message'] = [
'#type' => 'textfield',
'#title' => $this->t('Message displayed in group information for regular groups'),
'#description' => $this->t('Two variables are available for substitution: @gid is the group ID and @owner is the themed owner of the group.'),
'#maxlength' => 1024,
'#size' => 100,
'#config_target' => 'monster_menus.settings:group.group_info_message',
];
$form['mm_page'] = [
'#type' => 'details',
'#title' => $this->t('Page Display'),
];
$form['mm_page']['pages-hide_empty_pages_in_menu'] = [
'#type' => 'checkbox',
'#title' => $this->t('Hide empty pages in menus'),
'#config_target' => 'monster_menus.settings:pages.hide_empty_pages_in_menu',
];
$form['mm_page']['pages-enable_rss'] = [
'#type' => 'checkbox',
'#title' => $this->t('Allow content creators to control the availability of RSS feeds on a per-page basis'),
'#config_target' => 'monster_menus.settings:pages.enable_rss',
];
$form['mm_node'] = [
'#type' => 'details',
'#title' => $this->t('Node Display'),
];
if (mm_module_exists('comment')) {
$form['mm_node']['comments'] = [
'#type' => 'details',
'#title' => $this->t('Comments'),
];
$form['mm_node']['comments']['comments-show_count_instead'] = [
'#type' => 'checkbox',
'#title' => $this->t('Show comment count instead of full comments'),
'#description' => $this->t('This option takes effect when viewing pages. When checked, nodes having comments show a link with the number of comments. Clicking on the link displays the node by itself, with the comments. If unchecked, all comments are displayed under their nodes, on the same page.'),
'#config_target' => 'monster_menus.settings:comments.show_count_instead',
];
$form['mm_node']['comments']['comments-finegrain_readability'] = [
'#type' => 'checkbox',
'#title' => $this->t('Control comment readability at the node level'),
'#description' => $this->t('This option lets users say who can read comments posted to each node on an individual basis. A default value for new nodes can also be set at the page level.'),
'#config_target' => 'monster_menus.settings:comments.finegrain_readability',
];
$labels = $settings->get('comments.readable_labels');
$labels[] = [];
$form['mm_node']['comments']['comments-readable_labels'] = [
'#prefix' => '<table><tr><th>' . $this->t('Permission') . '</th><th>' . $this->t('Description') . '</th></tr>',
'#suffix' => '</table>',
'#tree' => TRUE,
];
$i = 0;
foreach ($labels as $label) {
$form['mm_node']['comments']['comments-readable_labels'][$i]['perm'] = [
'#type' => 'textfield',
'#prefix' => '<tr><td>',
'#default_value' => $label['perm'] ?? '',
'#size' => 30,
'#suffix' => '</td><td>',
];
$form['mm_node']['comments']['comments-readable_labels'][$i]['desc'] = [
'#type' => 'textfield',
'#suffix' => '</td></tr>',
'#default_value' => $label['desc'] ?? '',
'#size' => 30,
];
$i++;
}
$form['mm_node']['comments']['desc'] = [
'#type' => 'item',
'#input' => FALSE,
'#description' => $this->t('<p><em>Permission</em> is the label appearing on the <a href=":url">Permissions</a> page; these are effectively ANDed with the <em>access comments</em> permission. Example: <em>comments readable by everyone</em></p><p><em>Description</em> is what users see in the list of choices for setting readability at the page/node level, it answers the question, "Who can read comments?" Example: <em>everyone</em></p><p>To remove a row, clear either value. <strong>Changing data in the Permission column or removing rows may affect the readability of comments in existing nodes!</strong> Don\'t forget to update the permissions after making changes here.</p>', [':url' => Url::fromRoute('user.admin_permissions')->toString()]),
];
}
$form['mm_userhome'] = [
'#type' => 'details',
'#title' => $this->t('User Home Pages'),
];
$form['mm_userhome']['user_homepages-enable'] = [
'#type' => 'checkbox',
'#title' => $this->t('Use user home directories'),
'#description' => $this->t('When enabled, each newly-added user gets a personal home page, starting at <a href=":url">/users</a>. Note: If you disable and then re-enable this option, any users created during the time it was disabled will not have home pages.', [':url' => mm_content_get_mmtid_url(mm_content_users_mmtid())->toString()]),
'#config_target' => 'monster_menus.settings:user_homepages.enable',
];
$form['mm_userhome']['user_homepages-virtual'] = [
'#type' => 'checkbox',
'#title' => $this->t('Use virtual user directories'),
'#description' => $this->t('If you have many users, the entire user list at <a href=":url">/users</a> can get very long. This feature will split the users into smaller chunks, based on the letter of the alphabet with which their name begins.', [':url' => mm_content_get_mmtid_url(mm_content_users_mmtid())->toString()]),
'#config_target' => 'monster_menus.settings:user_homepages.virtual',
];
$form['mm_userhome']['user_homepages-default_homepage'] = [
'#type' => 'textarea',
'#title' => $this->t('Default personal homepage message'),
'#description' => $this->t('What users see when viewing their own, empty homepage. Provide some instructions telling them how to create content.'),
'#config_target' => 'monster_menus.settings:user_homepages.default_homepage',
];
$form['mm_username'] = [
'#type' => 'details',
'#title' => $this->t('User Names'),
'#description' => $this->t('These names are displayed in content attribution lines and group membership lists.'),
];
$form['mm_username']['usernames-anon'] = [
'#type' => 'textfield',
'#title' => $this->t('Long name of the Anonymous user'),
'#config_target' => 'monster_menus.settings:usernames.anon',
];
$form['mm_username']['usernames-admin'] = [
'#type' => 'textfield',
'#title' => $this->t('Long name of the Administrator user'),
'#config_target' => 'monster_menus.settings:usernames.admin',
];
$form['mm_username']['usernames-disabled'] = [
'#type' => 'textfield',
'#title' => $this->t('Long name for all disabled users'),
'#config_target' => 'monster_menus.settings:usernames.disabled',
];
$form['mm_nodelist'] = [
'#type' => 'details',
'#title' => $this->t('Node Chooser'),
];
$form['mm_nodelist']['nodes-nodelist_pager_limit'] = [
'#type' => 'select',
'#title' => $this->t('Number of nodes to show per page in the node chooser'),
'#options' => [
10 => 10,
20 => 20,
50 => 50,
100 => 100,
],
'#config_target' => 'monster_menus.settings:nodes.nodelist_pager_limit',
];
$form['mm_sitemap'] = [
'#type' => 'details',
'#title' => $this->t('Site Map'),
];
$form['mm_sitemap']['help'] = [
'#markup' => $this->t('<p>Monster Menus will respond to a request for <code>/-mm-sitemap</code> by generating a standard <code>/sitemap.xml</code> file. You should call this URL periodically, in the same way you do cron.php, but less frequently. Once it has been generated, the <code>sitemap.xml</code> file contains links to any pages that are publicly readable, and not hidden or recycled.</p>'),
];
$form['mm_sitemap']['sitemap-exclude_list'] = [
'#type' => 'textarea',
'#wysiwyg' => FALSE,
'#title' => $this->t('Paths to exclude from the sitemap'),
'#default_value' => join("\n", $settings->get('sitemap.exclude_list')),
'#description' => $this->t('A list of paths, one per line, which should not be part of the <code>sitemap.xml</code>. Do not include leading or trailing slashes. Example: <code>foo/bar/baz</code>'),
'#config_target' => 'monster_menus.settings:sitemap.exclude_list',
];
$form['mm_sitemap']['sitemap-max_level'] = [
'#type' => 'select',
'#options' => [-1 => $this->t('(disabled)'), 0 => $this->t('(Home only)'), 1 => $this->t('1 level'), 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9, 10 => 10, 11 => 11, 12 => 12, 13 => 13, 14 => 14, 15 => 15, 1000 => $this->t('(unlimited)')],
'#title' => $this->t('Number of levels to generate'),
'#description' => $this->t('The maximum depth in the tree to use for the sitemap. Set this too high and, on a large site, your sitemap.xml file may become too large to be useful.'),
'#config_target' => 'monster_menus.settings:sitemap.max_level',
];
}
mm_module_invoke_all_array('mm_config_alter', [&$form, $settings]);
if ($form) {
return parent::buildForm($form, $form_state);
}
$form['msg'] = ['#markup' => $this->t('You are not allowed to change any of the settings.')];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$labels = $form_state->getValue('comments-readable_labels');
$form_state->setValue('comments-readable_labels', array_filter($labels, function ($label) {
return !empty($label['perm']);
}));
foreach (explode(',', $form_state->getValue('vgroup-errors_email')) as $email) {
if ($email && !$this->emailValidator->isValid(trim($email))) {
$form_state->setErrorByName('vgroup-errors_email', $this->t('One or more email addresses were not in the correct format.'));
}
}
$form_state->setValue(['sitemap-exclude_list'], preg_split('{/*\s*[\r\n]+\s*/*}', trim($form_state->getValue('sitemap-exclude_list'), " \r\n/"), -1, PREG_SPLIT_NO_EMPTY));
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Do this one array the old-fashioned way, so that empty rows get deleted.
$this->configFactory()
->getEditable('monster_menus.settings')
->set('comments.readable_labels', $form_state->getValue('comments-readable_labels'))
->save();
parent::submitForm($form, $form_state);
// Re-fetch custom permissions to potentially add new permissions to list.
$this->permissionHandler->getPermissions();
// If comments.finegrain_readability has never been set before, create the
// default settings using the current 'access comments' setting.
if ($form_state->getValue('comments-finegrain_readability') && !$this->state->get('monster_menus.finegrain_comment_readability_ever_set', FALSE)) {
$this->state->set('monster_menus.finegrain_comment_readability_ever_set', TRUE);
foreach (Role::loadMultiple() as $role) {
if ($role->hasPermission('access comments')) {
$role->grantPermission(Constants::MM_COMMENT_READABILITY_DEFAULT);
}
$role->grantPermission('access comments');
$role->trustData()->save();
}
$this->messenger()->addStatus($this->t('Because you enabled the <em>Control comment readability at the node level</em> setting for the first time, the <em>access comments</em> permission has been enabled for all roles. This is necessary in order for this feature to work.'));
$this->messenger()->addStatus($this->t('You should now go to <a href=":url">Permissions</a> to set the roles for each permission you just created.', [':url' => Url::fromRoute('user.admin_permissions', [], ['fragment' => 'module-monster_menus'])->toString()]));
}
}
}
