Commit 3ed46905 authored by Dries's avatar Dries

- Patch #1710512 by n3or: Convert update settings to configuration system.

parent 18d21ed0
......@@ -1902,7 +1902,7 @@ function install_configure_form_submit($form, &$form_state) {
// Add the site maintenance account's email address to the list of
// addresses to be notified when updates are available, if selected.
if ($form_state['values']['update_status_module'][2]) {
variable_set('update_notify_emails', array($form_state['values']['account']['mail']));
config('update.settings')->set('notification.emails', array($form_state['values']['account']['mail']))->save();
}
}
......
......@@ -65,6 +65,7 @@ function testUpdateAccess() {
* Tests that requirements warnings and errors are correctly displayed.
*/
function testRequirements() {
$update_script_test_config = config('update_script_test.settings');
$this->drupalLogin($this->update_user);
// If there are no requirements warnings or errors, we expect to be able to
......@@ -81,7 +82,7 @@ function testRequirements() {
// First, run this test with pending updates to make sure they can be run
// successfully.
variable_set('update_script_test_requirement_type', REQUIREMENT_WARNING);
$update_script_test_config->set('requirement_type', REQUIREMENT_WARNING)->save();
drupal_set_installed_schema_version('update_script_test', drupal_get_installed_schema_version('update_script_test') - 1);
$this->drupalGet($this->update_url, array('external' => TRUE));
$this->assertText('This is a requirements warning provided by the update_script_test module.');
......@@ -106,7 +107,7 @@ function testRequirements() {
// If there is a requirements error, it should be displayed even after
// clicking the link to proceed (since the problem that triggered the error
// has not been fixed).
variable_set('update_script_test_requirement_type', REQUIREMENT_ERROR);
$update_script_test_config->set('requirement_type', REQUIREMENT_ERROR)->save();
$this->drupalGet($this->update_url, array('external' => TRUE));
$this->assertText('This is a requirements error provided by the update_script_test module.');
$this->clickLink('try again');
......
......@@ -13,7 +13,7 @@ function update_script_test_requirements($phase) {
if ($phase == 'update') {
// Set a requirements warning or error when the test requests it.
$requirement_type = variable_get('update_script_test_requirement_type');
$requirement_type = config('update_script_test.settings')->get('requirement_type');
switch ($requirement_type) {
case REQUIREMENT_WARNING:
$requirements['update_script_test'] = array(
......
......@@ -40,7 +40,7 @@ function testNoReleasesAvailable() {
'hidden' => FALSE,
),
);
variable_set('update_test_system_info', $system_info);
config('update_test.settings')->set('system_info', $system_info)->save();
$this->refreshUpdateStatus(array('drupal' => '0', 'aaa_update_test' => 'no-releases'));
$this->drupalGet('admin/reports/updates');
// Cannot use $this->standardTests() because we need to check for the
......@@ -68,7 +68,7 @@ function testUpdateContribBasic() {
'hidden' => FALSE,
),
);
variable_set('update_test_system_info', $system_info);
config('update_test.settings')->set('system_info', $system_info)->save();
$this->refreshUpdateStatus(
array(
'drupal' => '0',
......@@ -127,7 +127,7 @@ function testUpdateContribOrder() {
'hidden' => FALSE,
),
);
variable_set('update_test_system_info', $system_info);
config('update_test.settings')->set('system_info', $system_info)->save();
$this->refreshUpdateStatus(array('drupal' => '0', '#all' => '1_0'));
$this->standardTests();
// We're expecting the report to say all projects are up to date.
......@@ -184,7 +184,7 @@ function testUpdateBaseThemeSecurityUpdate() {
'hidden' => FALSE,
),
);
variable_set('update_test_system_info', $system_info);
config('update_test.settings')->set('system_info', $system_info)->save();
$xml_mapping = array(
'drupal' => '0',
'update_test_subtheme' => '1_0',
......@@ -199,6 +199,7 @@ function testUpdateBaseThemeSecurityUpdate() {
* Tests that disabled themes are only shown when desired.
*/
function testUpdateShowDisabledThemes() {
$update_settings = config('update.settings');
// Make sure all the update_test_* themes are disabled.
db_update('system')
->fields(array('status' => 0))
......@@ -229,8 +230,8 @@ function testUpdateShowDisabledThemes() {
// total number of attempts made in the test may exceed the default value
// of update_max_fetch_attempts. Therefore this variable is set very high
// to avoid test failures in those cases.
variable_set('update_max_fetch_attempts', 99999);
variable_set('update_test_system_info', $system_info);
$update_settings->set('fetch.max_attempts', 99999)->save();
config('update_test.settings')->set('system_info', $system_info)->save();
$xml_mapping = array(
'drupal' => '0',
'update_test_subtheme' => '1_0',
......@@ -239,7 +240,7 @@ function testUpdateShowDisabledThemes() {
$base_theme_project_link = l(t('Update test base theme'), 'http://example.com/project/update_test_basetheme');
$sub_theme_project_link = l(t('Update test subtheme'), 'http://example.com/project/update_test_subtheme');
foreach (array(TRUE, FALSE) as $check_disabled) {
variable_set('update_check_disabled', $check_disabled);
$update_settings->set('check.disabled_extensions', $check_disabled)->save();
$this->refreshUpdateStatus($xml_mapping);
// In neither case should we see the "Themes" heading for enabled themes.
$this->assertNoText(t('Themes'));
......@@ -280,7 +281,7 @@ function testUpdateBrokenFetchURL() {
'hidden' => FALSE,
),
);
variable_set('update_test_system_info', $system_info);
config('update_test.settings')->set('system_info', $system_info)->save();
$xml_mapping = array(
'drupal' => '0',
......@@ -322,6 +323,7 @@ function testUpdateBrokenFetchURL() {
*/
function testHookUpdateStatusAlter() {
variable_set('allow_authorize_operations', TRUE);
$update_test_config = config('update_test.settings');
$update_admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer software updates'));
$this->drupalLogin($update_admin_user);
......@@ -335,13 +337,13 @@ function testHookUpdateStatusAlter() {
'hidden' => FALSE,
),
);
variable_set('update_test_system_info', $system_info);
$update_test_config->set('system_info', $system_info)->save();
$update_status = array(
'aaa_update_test' => array(
'status' => UPDATE_NOT_SECURE,
),
);
variable_set('update_test_update_status', $update_status);
$update_test_config->set('update_status', $update_status)->save();
$this->refreshUpdateStatus(
array(
'drupal' => '0',
......@@ -355,19 +357,19 @@ function testHookUpdateStatusAlter() {
// Visit the reports page again without the altering and make sure the
// status is back to normal.
variable_set('update_test_update_status', array());
$update_test_config->set('update_status', array())->save();
$this->drupalGet('admin/reports/updates');
$this->assertRaw('<h3>' . t('Modules') . '</h3>');
$this->assertNoText(t('Security update required!'));
$this->assertRaw(l(t('AAA Update test'), 'http://example.com/project/aaa_update_test'), t('Link to aaa_update_test project appears.'));
// Turn the altering back on and visit the Update manager UI.
variable_set('update_test_update_status', $update_status);
$update_test_config->set('update_status', $update_status)->save();
$this->drupalGet('admin/modules/update');
$this->assertText(t('Security update'));
// Turn the altering back off and visit the Update manager UI.
variable_set('update_test_update_status', array());
$update_test_config->set('update_status', array())->save();
$this->drupalGet('admin/modules/update');
$this->assertNoText(t('Security update'));
}
......
......@@ -83,7 +83,7 @@ function testDatestampMismatch() {
'datestamp' => '1000000000',
),
);
variable_set('update_test_system_info', $system_info);
config('update_test.settings')->set('system_info', $system_info)->save();
$this->refreshUpdateStatus(array('drupal' => 'dev'));
$this->assertNoText(t('2001-Sep-'));
$this->assertText(t('Up to date'));
......@@ -96,8 +96,8 @@ function testDatestampMismatch() {
*/
function testModulePageRunCron() {
$this->setSystemInfo7_0();
variable_set('update_fetch_url', url('update-test', array('absolute' => TRUE)));
variable_set('update_test_xml_map', array('drupal' => '0'));
config('update.settings')->set('fetch.url', url('update-test', array('absolute' => TRUE)))->save();
config('update_test.settings')->set('xml_map', array('drupal' => '0'))->save();
$this->cronRun();
$this->drupalGet('admin/modules');
......@@ -110,8 +110,8 @@ function testModulePageRunCron() {
function testModulePageUpToDate() {
$this->setSystemInfo7_0();
// Instead of using refreshUpdateStatus(), set these manually.
variable_set('update_fetch_url', url('update-test', array('absolute' => TRUE)));
variable_set('update_test_xml_map', array('drupal' => '0'));
config('update.settings')->set('fetch.url', url('update-test', array('absolute' => TRUE)))->save();
config('update_test.settings')->set('xml_map', array('drupal' => '0'))->save();
$this->drupalGet('admin/reports/updates');
$this->clickLink(t('Check manually'));
......@@ -127,8 +127,8 @@ function testModulePageUpToDate() {
function testModulePageRegularUpdate() {
$this->setSystemInfo7_0();
// Instead of using refreshUpdateStatus(), set these manually.
variable_set('update_fetch_url', url('update-test', array('absolute' => TRUE)));
variable_set('update_test_xml_map', array('drupal' => '1'));
config('update.settings')->set('fetch.url', url('update-test', array('absolute' => TRUE)))->save();
config('update_test.settings')->set('xml_map', array('drupal' => '1'))->save();
$this->drupalGet('admin/reports/updates');
$this->clickLink(t('Check manually'));
......@@ -144,8 +144,8 @@ function testModulePageRegularUpdate() {
function testModulePageSecurityUpdate() {
$this->setSystemInfo7_0();
// Instead of using refreshUpdateStatus(), set these manually.
variable_set('update_fetch_url', url('update-test', array('absolute' => TRUE)));
variable_set('update_test_xml_map', array('drupal' => '2-sec'));
config('update.settings')->set('fetch.url', url('update-test', array('absolute' => TRUE)))->save();
config('update_test.settings')->set('xml_map', array('drupal' => '2-sec'))->save();
$this->drupalGet('admin/reports/updates');
$this->clickLink(t('Check manually'));
......@@ -219,6 +219,6 @@ protected function setSystemInfo7_0() {
'version' => '7.0',
),
);
variable_set('update_test_system_info', $setting);
config('update_test.settings')->set('system_info', $setting)->save();
}
}
......@@ -43,9 +43,9 @@ class UpdateTestBase extends WebTestBase {
protected function refreshUpdateStatus($xml_map, $url = 'update-test') {
// Tell the Update Manager module to fetch from the URL provided by
// update_test module.
variable_set('update_fetch_url', url($url, array('absolute' => TRUE)));
config('update.settings')->set('fetch.url', url($url, array('absolute' => TRUE)))->save();
// Save the map for update_test_mock_page() to use.
variable_set('update_test_xml_map', $xml_map);
config('update_test.settings')->set('xml_map', $xml_map)->save();
// Manually check the update status.
$this->drupalGet('admin/reports/updates/check');
}
......
......@@ -75,9 +75,11 @@ function testUpdateManagerCoreSecurityUpdateMessages() {
'version' => '7.0',
),
);
variable_set('update_test_system_info', $setting);
variable_set('update_fetch_url', url('update-test', array('absolute' => TRUE)));
variable_set('update_test_xml_map', array('drupal' => '2-sec'));
config('update_test.settings')
->set('system_info', $setting)
->set('xml_map', array('drupal' => '2-sec'))
->save();
config('update.settings')->set('fetch.url', url('update-test', array('absolute' => TRUE)))->save();
// Initialize the update status.
$this->drupalGet('admin/reports/updates');
......
......@@ -42,16 +42,16 @@ function update_test_menu() {
/**
* Implements hook_system_info_alter().
*
* Checks the 'update_test_system_info' variable and sees if we need to alter
* the system info for the given $file based on the setting. The setting is
* expected to be a nested associative array. If the key '#all' is defined, its
* subarray will include .info keys and values for all modules and themes on the
* system. Otherwise, the settings array is keyed by the module or theme short
* name ($file->name) and the subarrays contain settings just for that module or
* theme.
* Checks the 'update_test.settings:system_info' configuration and sees if we
* need to alter the system info for the given $file based on the setting. The
* setting is expected to be a nested associative array. If the key '#all' is
* defined, its subarray will include .info keys and values for all modules and
* themes on the system. Otherwise, the settings array is keyed by the module or
* theme short name ($file->name) and the subarrays contain settings just for
* that module or theme.
*/
function update_test_system_info_alter(&$info, $file) {
$setting = variable_get('update_test_system_info', array());
$setting = config('update_test.settings')->get('system_info');
foreach (array('#all', $file->name) as $id) {
if (!empty($setting[$id])) {
foreach ($setting[$id] as $key => $value) {
......@@ -64,15 +64,16 @@ function update_test_system_info_alter(&$info, $file) {
/**
* Implements hook_update_status_alter().
*
* Checks the 'update_test_update_status' variable and sees if we need to alter
* the update status for the given project based on the setting. The setting is
* expected to be a nested associative array. If the key '#all' is defined, its
* subarray will include .info keys and values for all modules and themes on the
* system. Otherwise, the settings array is keyed by the module or theme short
* name and the subarrays contain settings just for that module or theme.
* Checks the 'update_test.settings:update_status' configuration and sees if we
* need to alter the update status for the given project based on the setting.
* The setting is expected to be a nested associative array. If the key '#all'
* is defined, its subarray will include .info keys and values for all modules
* and themes on the system. Otherwise, the settings array is keyed by the
* module or theme short name and the subarrays contain settings just for that
* module or theme.
*/
function update_test_update_status_alter(&$projects) {
$setting = variable_get('update_test_update_status', array());
$setting = config('update_test.settings')->get('update_status');
if (!empty($setting)) {
foreach ($projects as $project_name => &$project) {
foreach (array('#all', $project_name) as $id) {
......@@ -108,7 +109,7 @@ function update_test_update_status_alter(&$projects) {
* @see update_test_menu()
*/
function update_test_mock_page($project_name) {
$xml_map = variable_get('update_test_xml_map', FALSE);
$xml_map = config('update_test.settings')->get('xml_map');
if (isset($xml_map[$project_name])) {
$availability_scenario = $xml_map[$project_name];
}
......
......@@ -83,7 +83,7 @@ function hook_update_projects_alter(&$projects) {
* @see update_calculate_project_data()
*/
function hook_update_status_alter(&$projects) {
$settings = variable_get('update_advanced_project_settings', array());
$settings = config('update_advanced.settings')->get('projects');
foreach ($projects as $project => $project_info) {
if (isset($settings[$project]) && isset($settings[$project]['check']) &&
($settings[$project]['check'] == 'never' ||
......
......@@ -64,7 +64,7 @@ function update_get_projects() {
$theme_data = system_rebuild_theme_data();
update_process_info_list($projects, $module_data, 'module', TRUE);
update_process_info_list($projects, $theme_data, 'theme', TRUE);
if (variable_get('update_check_disabled', FALSE)) {
if (config('update.settings')->get('check.disabled_extensions')) {
update_process_info_list($projects, $module_data, 'module', FALSE);
update_process_info_list($projects, $theme_data, 'theme', FALSE);
}
......
......@@ -107,7 +107,7 @@ function update_fetch_data_finished($success, $results) {
*/
function _update_fetch_data() {
$queue = queue('update_fetch_tasks');
$end = time() + variable_get('update_max_fetch_time', UPDATE_MAX_FETCH_TIME);
$end = time() + config('update.settings')->get('fetch.timeout');
while (time() < $end && ($item = $queue->claimItem())) {
_update_process_fetch_task($item->data);
$queue->deleteItem($item);
......@@ -128,6 +128,7 @@ function _update_fetch_data() {
*/
function _update_process_fetch_task($project) {
global $base_url;
$update_config = config('update.settings');
$fail = &drupal_static(__FUNCTION__, array());
// This can be in the middle of a long-running batch, so REQUEST_TIME won't
// necessarily be valid.
......@@ -140,7 +141,7 @@ function _update_process_fetch_task($project) {
}
}
$max_fetch_attempts = variable_get('update_max_fetch_attempts', UPDATE_MAX_FETCH_ATTEMPTS);
$max_fetch_attempts = $update_config->get('fetch.max_attempts');
$success = FALSE;
$available = array();
......@@ -177,7 +178,7 @@ function _update_process_fetch_task($project) {
}
}
$frequency = variable_get('update_check_frequency', 1);
$frequency = $update_config->get('check.interval_days');
$cid = 'available_releases::' . $project_name;
_update_cache_set($cid, $available, $now + (60 * 60 * 24 * $frequency));
......@@ -328,7 +329,16 @@ function _update_build_fetch_url($project, $site_key = '') {
* @see _update_build_fetch_url()
*/
function _update_get_fetch_url_base($project) {
return isset($project['info']['project status url']) ? $project['info']['project status url'] : variable_get('update_fetch_url', UPDATE_DEFAULT_URL);
if (isset($project['info']['project status url'])) {
$url = $project['info']['project status url'];
}
else {
$url = config('update.settings')->get('fetch.url');
if (empty($url)) {
$url = UPDATE_DEFAULT_URL;
}
}
return $url;
}
/**
......@@ -341,10 +351,11 @@ function _update_get_fetch_url_base($project) {
* @see update_requirements()
*/
function _update_cron_notify() {
$update_config = config('update.settings');
module_load_install('update');
$status = update_requirements('runtime');
$params = array();
$notify_all = (variable_get('update_notification_threshold', 'all') == 'all');
$notify_all = ($update_config->get('notification.threshold') == 'all');
foreach (array('core', 'contrib') as $report_type) {
$type = 'update_' . $report_type;
if (isset($status[$type]['severity'])
......@@ -353,7 +364,7 @@ function _update_cron_notify() {
}
}
if (!empty($params)) {
$notify_list = variable_get('update_notify_emails', '');
$notify_list = $update_config->get('notification.emails');
if (!empty($notify_list)) {
$default_language = language_default();
foreach ($notify_list as $target) {
......
......@@ -77,20 +77,10 @@ function update_install() {
* Implements hook_uninstall().
*/
function update_uninstall() {
// Clear any variables that might be in use
$variables = array(
'update_check_frequency',
'update_fetch_url',
'update_last_check',
'update_last_email_notification',
'update_notification_threshold',
'update_notify_emails',
'update_max_fetch_attempts',
'update_max_fetch_time',
);
foreach ($variables as $variable) {
variable_del($variable);
}
// @todo D8: Convert to new state storage.
variable_del('update_last_check');
variable_del('update_last_email_notification');
$queue = queue('update_fetch_tasks');
$queue->deleteQueue();
}
......@@ -157,3 +147,18 @@ function _update_requirement_check($project, $type) {
$requirement['value'] = l($requirement_label, update_manager_access() ? 'admin/reports/updates/update' : 'admin/reports/updates');
return $requirement;
}
/**
* Moves update settings from variables to config.
*/
function update_update_8000() {
update_variables_to_config('update.settings', array(
'update_check_disabled' => 'check.disabled_extensions',
'update_check_frequency' => 'check.interval_days',
'update_fetch_url' => 'fetch.url',
'update_max_fetch_attempts' => 'fetch.max_attempts',
'update_max_fetch_time' => 'fetch.timeout',
'update_notify_emails' => 'notification.emails',
'update_notification_threshold' => 'notification.threshold',
));
}
......@@ -63,16 +63,6 @@
*/
const UPDATE_FETCH_PENDING = -4;
/**
* Maximum number of attempts to fetch available update data from a given host.
*/
const UPDATE_MAX_FETCH_ATTEMPTS = 2;
/**
* Maximum number of seconds to try fetching available update data at a time.
*/
const UPDATE_MAX_FETCH_TIME = 5;
/**
* Implements hook_help().
*/
......@@ -292,7 +282,8 @@ function update_theme() {
* Implements hook_cron().
*/
function update_cron() {
$frequency = variable_get('update_check_frequency', 1);
$update_config = config('update.settings');
$frequency = $update_config->get('check.interval_days');
$interval = 60 * 60 * 24 * $frequency;
if ((REQUEST_TIME - variable_get('update_last_check', 0)) > $interval) {
// If the configured update interval has elapsed, we want to invalidate
......@@ -530,7 +521,7 @@ function update_mail($key, &$message, $params) {
$message['body'][] = t('You can automatically install your missing updates using the Update manager:', array(), array('langcode' => $langcode)) . "\n" . url('admin/reports/updates/update', array('absolute' => TRUE, 'language' => $language));
}
$settings_url = url('admin/reports/updates/settings', array('absolute' => TRUE));
if (variable_get('update_notification_threshold', 'all') == 'all') {
if (config('update.settings')->get('notification.threshold') == 'all') {
$message['body'][] = t('Your site is currently configured to send these emails when any updates are available. To get notified only for security updates, !url.', array('!url' => $settings_url));
}
else {
......
......@@ -44,7 +44,7 @@ function theme_update_report($variables) {
$header = array();
$rows = array();
$notification_level = variable_get('update_notification_threshold', 'all');
$notification_level = config('update.settings')->get('notification.threshold');
// Create an array of status values keyed by module or theme name, since
// we'll need this while generating the report if we have to cross reference
......
......@@ -12,11 +12,12 @@
* @see update_settings_submit()
* @ingroup forms
*/
function update_settings($form) {
function update_settings($form, &$form_state) {
$config = config('update.settings');
$form['update_check_frequency'] = array(
'#type' => 'radios',
'#title' => t('Check for updates'),
'#default_value' => variable_get('update_check_frequency', 1),
'#default_value' => $config->get('check.interval_days'),
'#options' => array(
'1' => t('Daily'),
'7' => t('Weekly'),
......@@ -27,22 +28,22 @@ function update_settings($form) {
$form['update_check_disabled'] = array(
'#type' => 'checkbox',
'#title' => t('Check for updates of disabled modules and themes'),
'#default_value' => variable_get('update_check_disabled', FALSE),
'#default_value' => $config->get('check.disabled_extensions'),
);
$notify_emails = variable_get('update_notify_emails', array());
$notification_emails = $config->get('notification.emails');
$form['update_notify_emails'] = array(
'#type' => 'textarea',
'#title' => t('E-mail addresses to notify when updates are available'),
'#rows' => 4,
'#default_value' => implode("\n", $notify_emails),
'#default_value' => implode("\n", $notification_emails),
'#description' => t('Whenever your site checks for available updates and finds new releases, it can notify a list of users via e-mail. Put each address on a separate line. If blank, no e-mails will be sent.'),
);
$form['update_notification_threshold'] = array(
'#type' => 'radios',
'#title' => t('E-mail notification threshold'),
'#default_value' => variable_get('update_notification_threshold', 'all'),
'#default_value' => $config->get('notification.threshold'),
'#options' => array(
'all' => t('All newer versions'),
'security' => t('Only security updates'),
......@@ -50,14 +51,7 @@ function update_settings($form) {
'#description' => t('You can choose to send e-mail only if a security update is available, or to be notified about all newer versions. If there are updates available of Drupal core or any of your installed modules and themes, your site will always print a message on the <a href="@status_report">status report</a> page, and will also display an error message on administration pages if there is a security update.', array('@status_report' => url('admin/reports/status')))
);
$form = system_settings_form($form);
// Custom validation callback for the email notification setting.
$form['#validate'][] = 'update_settings_validate';
// We need to call our own submit callback first, not the one from
// system_settings_form(), so that we can process and save the emails.
unset($form['#submit']);
return $form;
return system_config_form($form, $form_state);
}
/**
......@@ -68,6 +62,7 @@ function update_settings($form) {
* @see update_settings_submit()
*/
function update_settings_validate($form, &$form_state) {
$form_state['notify_emails'] = array();
if (!empty($form_state['values']['update_notify_emails'])) {
$valid = array();
$invalid = array();
......@@ -106,23 +101,18 @@ function update_settings_validate($form, &$form_state) {
* @see update_settings_validate()
*/
function update_settings_submit($form, $form_state) {
$op = $form_state['values']['op'];
if (empty($form_state['notify_emails'])) {
variable_del('update_notify_emails');
}
else {
variable_set('update_notify_emails', $form_state['notify_emails']);
}
unset($form_state['notify_emails']);
unset($form_state['values']['update_notify_emails']);
$config = config('update.settings');
// See if the update_check_disabled setting is being changed, and if so,
// invalidate all cached update status data.
$check_disabled = variable_get('update_check_disabled', FALSE);
if ($form_state['values']['update_check_disabled'] != $check_disabled) {
if ($form_state['values']['update_check_disabled'] != $config->get('check.disabled_extensions')) {
_update_cache_clear();
}
system_settings_form_submit($form, $form_state);
$config
->set('check.disabled_extensions', $form_state['values']['update_check_disabled'])
->set('check.interval_days', $form_state['values']['update_check_frequency'])
->set('notification.emails', $form_state['notify_emails'])
->set('notification.threshold', $form_state['values']['update_notification_threshold'])
->save();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment