~ 6 min read

Drupal 8 module development #3 – adding a settings page – revision

share this story on
Another post in the series of Drupal 8 module development articles. This time we'll add a settings page to our module.

In the previous article we introduced the configuration system and showed how to create a settings form, integrate with the configuration management system and enable our module to save it’s configuration data using this system.

As things still change pretty quickly in the Drupal 8 arena some of the items in the previous post are not valid anymore so while I’ve updated the code for our Global Redirect module on drupal/git, I wanted to post the revised and full and up-to-date (for now :)) version of the settings form:

/**
 * @file
 * This is the GlobalRedirect admin include which provides an interface to global redirect to change some of the default settings
 * Contains \Drupal\globalredirect\Form\GlobalredirectSettingsForm.
 */

namespace Drupal\globalredirect\Form;

use Drupal\Core\Form\ConfigFormBase;

/**
 * Defines a form to configure module settings.
 */
class GlobalredirectSettingsForm extends ConfigFormBase {

  /**
   * {@inheritdoc}
   */
  public function getFormID() {
    return 'globalredirect_settings';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, array &$form_state) {

  	// Get all settings
  	$config = $this->config('globalredirect.settings');
  	$settings = $config->get();

    $form['settings'] = array(
    	'#tree' => TRUE,
  	);

  	$form['settings']['deslash'] = array(
  		'#type' => 'checkbox',
  		'#title' => $this->t('Deslash'),
  		'#description' => $this->t('If enabled, this option will remove the trailing slash from requests. This stops requests such as `example.com/node/1/` failing to match the corresponding alias and can cause duplicate content. On the other hand, if you require certain requests to have a trailing slash, this feature can cause problems so may need to be disabled.'),
  		'#default_value' => $settings['deslash'],
  	);

	  $form['settings']['nonclean_to_clean'] = array(
	    '#type' => 'checkbox',
	    '#title' => $this->t('Non-clean to Clean'),
	    '#description' => $this->t('If enabled, this option will redirect from non-clean to clean URL (if Clean URL\'s are enabled). This will stop, for example, node 1  existing on both `example.com/node/1` AND `example.com?q=node/1`.'),
	    '#default_value' => $settings['nonclean_to_clean'],
	  );

	  $form['settings']['trailing_zero'] = array(
	    '#type' => 'radios',
	    '#title' => $this->t('Remove Trailing Zero Argument'),
	    '#description' => $this->t('If enabled, any instance of "/0" will be trimmed from the right of the URL. This stops duplicate pages such as "taxonomy/term/1" and "taxonomy/term/1/0" where 0 is the default depth. There is an option of limiting this feature to taxonomy term pages ONLY or allowing it to effect any page. **By default this feature is disabled to avoid any unexpected behavior. Also of note, the trailing /0 "depth modifier" was removed from Drupal 7.**'),
	    '#options' => array(
	      0 => $this->t('Disabled'),
	      1 => $this->t('Enabled for all pages'),
	      2 => $this->t('Enabled for taxonomy term pages only'),
	    ),
	    '#default_value' => $settings['trailing_zero'],
	  );

	  $form['settings']['menu_check'] = array(
	    '#type' => 'checkbox',
	    '#title' => $this->t('Menu Access Checking'),
	    '#description' => $this->t('If enabled, the module will check the user has access to the page before redirecting. This helps to stop redirection on protected pages and avoids giving away _secret_ URL\'s. **By default this feature is disabled to avoid any unexpected behavior**'),
	    '#default_value' => $settings['menu_check'],
	  );

	  $form['settings']['case_sensitive_urls'] = array(
	    '#type' => 'checkbox',
	    '#title' => $this->t('Case Sensitive URL Checking'),
	    '#description' => $this->t('If enabled, the module will compare the current URL to the alias stored in the system. If there are any differences in case then the user will be redirected to the correct URL.'),
	    '#default_value' => $settings['case_sensitive_urls'],
	  );


	  $form['settings']['language_redirect'] = array(
	    '#type' => 'checkbox',
	    '#title' => $this->t('Language Path Checking'),
	    '#description' => $this->t('If enabled, the module will check that the page being viewed matches the language in the URL or the system default. For example, viewing a French node while the site is in English will cause a redirect to the English node.'),
	    '#default_value' => $settings['language_redirect'],
	  );


	  $form['settings']['canonical'] = array(
	    '#type' => 'checkbox',
	    '#title' => $this->t('Add Canonical Link'),
	    '#description' => $this->t('If enabled, will add a [canonical link](http://enginx.com/blog/drupal-8-module-development-3-adding-settings-page-revision/!canonical) to each page.', array('!canonical' => 'http://googlewebmastercentral.blogspot.com/2009/02/specify-your-canonical.html')),
	    '#default_value' => $settings['canonical'],
	  );


	  $form['settings']['content_location_header'] = array(
	    '#type' => 'checkbox',
	    '#title' => $this->t('Set Content Location Header'),
	    '#description' => $this->t('If enabled, will add a [Content-Location](http://enginx.com/blog/drupal-8-module-development-3-adding-settings-page-revision/!canonical) header.', array('!canonical' => 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.14')),
	    '#default_value' => $settings['content_location_header'],
	  );


	  $form['settings']['term_path_handler'] = array(
	    '#type' => 'checkbox',
	    '#title' => $this->t('Taxonomy Term Path Handler'),
	    '#description' => $this->t('If enabled, any request to a taxonomy/term/[tid] page will check that the correct path is being used for the term\'s vocabulary.'),
	    '#default_value' => $settings['term_path_handler'],
	  );

	  $form['settings']['frontpage_redirect'] = array(
	    '#type' => 'checkbox',
	    '#title' => $this->t('Frontpage Redirect Handler'),
	    '#description' => $this->t('If enabled, any request to the frontpage path will redirect to the site root.
	                         Whatever you set as the path of the front page on the !link settings page will redirect to the site root (e.g. "node" or "node/1" and also its alias (e.g. in case you have set "node/1" as your home page but that page also has an alias "home")).', array(
	      '!link' => l($this->t('Site Information'), 'admin/settings/site-information'),
	    )),
	    '#default_value' => $settings['frontpage_redirect'],
	  );

	  $form['settings']['ignore_admin_path'] = array(
	    '#type' => 'checkbox',
	    '#title' => $this->t('Ignore Admin Path'),
	    '#description' => $this->t('If enabled, any request to the admin section of the site will be ignored by Global Redirect.
	                         This is useful if you are experiencing problems with Global Redirect and want to protect the admin section of your website. NOTE: This may not be desirable if you are using path aliases for certain admin URLs.'),
	    '#default_value' => $settings['ignore_admin_path'],
	  );

	  $form['buttons']['reset'] = array(
	    '#type' => 'submit',
	    '#submit' => array(array($this, 'submitResetDefaults')),
	    '#value' => t('Reset to defaults'),
	  );

    return parent::buildForm($form, $form_state);
  }

  /**
   * Compares the submitted settings to the defaults and unsets any that are equal. This was we only store overrides.
   */
  public function submitForm(array &$form, array &$form_state) {

  	// Get config factory
  	$config = $this->configFactory->get('globalredirect.settings');

  	$form_values = $form_state['values']['settings'];

    $config
      ->set('deslash', $form_values['deslash'])
      ->set('nonclean_to_clean', $form_values['nonclean_to_clean'])
      ->set('trailing_zero', $form_values['trailing_zero'])
      ->set('menu_check', $form_values['menu_check'])
      ->set('case_sensitive_urls', $form_values['case_sensitive_urls'])
      ->set('language_redirect', $form_values['language_redirect'])
      ->set('canonical', $form_values['canonical'])
      ->set('content_location_header', $form_values['content_location_header'])
      ->set('term_path_handler', $form_values['term_path_handler'])
      ->set('frontpage_redirect', $form_values['frontpage_redirect'])
      ->set('ignore_admin_path', $form_values['ignore_admin_path'])
      ->save();

    parent::submitForm($form, $form_state);

  }



  /**
   * Clears the caches.
   */
  public function submitResetDefaults(array &$form, array &$form_state) {
  	$config = $this->configFactory->get('globalredirect.settings');

    // Get config factory
  	$settingsDefault = $this->getDefaultSettings();

    $config
      ->set('deslash', $settingsDefault['deslash'])
      ->set('nonclean_to_clean', $settingsDefault['nonclean_to_clean'])
      ->set('trailing_zero', $settingsDefault['trailing_zero'])
      ->set('menu_check', $settingsDefault['menu_check'])
      ->set('case_sensitive_urls', $settingsDefault['case_sensitive_urls'])
      ->set('language_redirect', $settingsDefault['language_redirect'])
      ->set('canonical', $settingsDefault['canonical'])
      ->set('content_location_header', $settingsDefault['content_location_header'])
      ->set('term_path_handler', $settingsDefault['term_path_handler'])
      ->set('frontpage_redirect', $settingsDefault['frontpage_redirect'])
      ->set('ignore_admin_path', $settingsDefault['ignore_admin_path'])
      ->save();

    parent::submitForm($form, $form_state);

  }


  /**
   * Returns an associative array of default settings
   * @return array
   */
  public function getDefaultSettings() {

    $defaults = array(
      'deslash' => 1,
      'nonclean_to_clean' => 1,
      'trailing_zero' => 0,
      'menu_check' => 0,
      'case_sensitive_urls' => 1,
      'language_redirect' => 0,
      'canonical' => 0,
      'content_location_header' => 0,
      'term_path_handler' => 1,
      'frontpage_redirect' => 1,
      'ignore_admin_path' => 1,
    );

    return $defaults;

  }

}

With credits and thanks to Matt who pointed this out and was generous enough to comment on the previous blog and update me on this change.