I'm in the process of creating my first D7 module, partly based on the tutorial found here. My module creates (among other things) a public page to show some content, and it also has a configuration page (in the Drupal admin) that allows the user to choose the path at which this page must be displayed (e.g. "mypage" or "content/mymodule" etc).
My problem is that saving this value through the configuration form is not enough: you also have to clear the Menu cache, otherwise the old path remains active, and the new one gives you a "page not found" error.
I need that to occur automatically every time this value is changed (or every time the form is submitted). I tried using menu_rebuild()
in my validation function, but it's obviously not the right place, because it is triggered before the new data is saved, and so the menu is rebuilt with the obsolete data.
How should I proceed if I want the Menu cache to be automatically cleared after the data has been validated and saved?
Here's my implementation of the pages, via hook_menu:
<?php
/**
* Implements hook_menu().
*/
function mymodule_menu() {
$items = array();
// config page
$items['admin/config/services/mymodule'] = array(
'title' => 'mymodule',
'description' => 'Configuration for the mymodule module',
'page callback' => 'drupal_get_form',
'page arguments' => array('mymodule_form'),
'access arguments' => array('access administration pages'),
'type' => MENU_NORMAL_ITEM,
);
// public page
$m_title = variable_get('mymodule_title', 'Mymodule');
$m_title = $m_title ? $m_title : 'Mymodule';
$m_path = variable_get('mymodule_path', 'mymodule');
$m_path = $m_path ? $m_path : 'mymodule';
$items[$m_path] = array(
'title' => $m_title,
'page callback' => '_mymodule_page',
'access arguments' => array('access mymodule content'),
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
/**
* Page callback: mymodule settings
*
* @see mymodule_menu()
*/
function mymodule_form($form, &$form_state) {
$form['mymodule_title'] = array(
'#type' => 'textfield',
'#title' => t('Title displayed on the mymodule public page'),
'#default_value' => variable_get('mymodule_title', 'Mymodule'),
'#required' => TRUE,
);
$form['mymodule_path'] = array(
'#type' => 'textfield',
'#title' => t('Path for the mymodule public page'),
'#default_value' => variable_get('mymodule_path', 'mymodule'),
'#required' => TRUE,
);
$form['#submit'][] = 'mymodule_form_submit';
return system_settings_form($form);
}
/**
* Add a submit handler/function to the form.
*/
function mymodule_form_submit($form, &$form_state) {
drupal_set_message(t('TEST: The form has been submitted.'));
menu_rebuild();
}
/**
* Implements validation from the Form API.
*
* @param $form
* A structured array containing the elements and properties of the form.
* @param $form_state
* An array that stores information about the form's current state
* during processing.
*/
function mymodule_form_validate($form, &$form_state) {
// page path validation
$m_path = $form_state['values']['mymodule_path'];
$m_path_db = variable_get('mymodule_path', '');
if ($m_path_db != $m_path) {
if (drupal_lookup_path('source', $m_path) || menu_get_item($m_path)) {
form_set_error('mymodule_path', t('This path is already used.'));
} else {
// attempting to validate path value
// see: http://drupal.stackexchange.com/questions/167923
$m_path_parts = explode('/', $m_path);
$has_invalid_chars = false;
foreach ($m_path_parts as $key => $part) {
if (urlencode($part) != $part) {
$has_invalid_chars = true;
}
}
if ($has_invalid_chars) {
form_set_error('mymodule_path', t('This path contains invalid characters.'));
}
}
}
}
?>
Edit: more code provided.
variable_get
's second parameter (e.g. 'Mymodule') is the default value to return if the variable is not set. You don't need the other line immediately after it:$m_title = $m_title ? $m_title : 'Mymodule';
because variable_get already does it for you.