diff -r 07239de796bb -r e756a8c72c3d cms/drupal/modules/system/system.admin.inc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cms/drupal/modules/system/system.admin.inc Fri Sep 08 12:04:06 2017 +0200 @@ -0,0 +1,3236 @@ +status report for more information.', array('@status' => url('admin/reports/status'))), 'error'); + } + $blocks = array(); + if ($admin = db_query("SELECT menu_name, mlid FROM {menu_links} WHERE link_path = 'admin/config' AND module = 'system'")->fetchAssoc()) { + $result = db_query(" + SELECT m.*, ml.* + FROM {menu_links} ml + INNER JOIN {menu_router} m ON ml.router_path = m.path + WHERE ml.link_path <> 'admin/help' AND menu_name = :menu_name AND ml.plid = :mlid AND hidden = 0", $admin, array('fetch' => PDO::FETCH_ASSOC)); + foreach ($result as $item) { + _menu_link_translate($item); + if (!$item['access']) { + continue; + } + // The link description, either derived from 'description' in hook_menu() + // or customized via menu module is used as title attribute. + if (!empty($item['localized_options']['attributes']['title'])) { + $item['description'] = $item['localized_options']['attributes']['title']; + unset($item['localized_options']['attributes']['title']); + } + $block = $item; + $block['content'] = ''; + $block['content'] .= theme('admin_block_content', array('content' => system_admin_menu_block($item))); + if (!empty($block['content'])) { + $block['show'] = TRUE; + } + + // Prepare for sorting as in function _menu_tree_check_access(). + // The weight is offset so it is always positive, with a uniform 5-digits. + $blocks[(50000 + $item['weight']) . ' ' . $item['title'] . ' ' . $item['mlid']] = $block; + } + } + if ($blocks) { + ksort($blocks); + return theme('admin_page', array('blocks' => $blocks)); + } + else { + return t('You do not have any administrative items.'); + } +} + +/** + * Provide a single block from the administration menu as a page. + * + * This function is often a destination for these blocks. + * For example, 'admin/structure/types' needs to have a destination to be valid + * in the Drupal menu system, but too much information there might be + * hidden, so we supply the contents of the block. + * + * @return + * The output HTML. + */ +function system_admin_menu_block_page() { + $item = menu_get_item(); + if ($content = system_admin_menu_block($item)) { + $output = theme('admin_block_content', array('content' => $content)); + } + else { + $output = t('You do not have any administrative items.'); + } + return $output; +} + +/** + * Menu callback; prints a listing of admin tasks, organized by module. + */ +function system_admin_index() { + $module_info = system_get_info('module'); + foreach ($module_info as $module => $info) { + $module_info[$module] = new stdClass(); + $module_info[$module]->info = $info; + } + uasort($module_info, 'system_sort_modules_by_info_name'); + $menu_items = array(); + + foreach ($module_info as $module => $info) { + // Only display a section if there are any available tasks. + if ($admin_tasks = system_get_module_admin_tasks($module, $info->info)) { + // Sort links by title. + uasort($admin_tasks, 'drupal_sort_title'); + // Move 'Configure permissions' links to the bottom of each section. + $permission_key = "admin/people/permissions#module-$module"; + if (isset($admin_tasks[$permission_key])) { + $permission_task = $admin_tasks[$permission_key]; + unset($admin_tasks[$permission_key]); + $admin_tasks[$permission_key] = $permission_task; + } + + $menu_items[$info->info['name']] = array($info->info['description'], $admin_tasks); + } + } + return theme('system_admin_index', array('menu_items' => $menu_items)); +} + +/** + * Displays the configuration overview page. + * + * This menu callback implementation is a legacy function that used to display + * the configuration overview page at admin/config. It is currently unused and + * will be removed in Drupal 8. The page at admin/config is now generated by + * system_admin_config_page(). + * + * @deprecated + * @see system_admin_config_page() + */ +function system_settings_overview() { + // Check database setup if necessary + if (function_exists('db_check_setup') && empty($_POST)) { + db_check_setup(); + } + + $item = menu_get_item('admin/config'); + $content = system_admin_menu_block($item); + + $output = theme('admin_block_content', array('content' => $content)); + + return $output; +} + +/** + * Menu callback; displays a listing of all themes. + */ +function system_themes_page() { + // Get current list of themes. + $themes = system_rebuild_theme_data(); + uasort($themes, 'system_sort_modules_by_info_name'); + + $theme_default = variable_get('theme_default', 'bartik'); + $theme_groups = array(); + + foreach ($themes as &$theme) { + if (!empty($theme->info['hidden'])) { + continue; + } + $admin_theme_options[$theme->name] = $theme->info['name']; + $theme->is_default = ($theme->name == $theme_default); + + // Identify theme screenshot. + $theme->screenshot = NULL; + // Create a list which includes the current theme and all its base themes. + if (isset($themes[$theme->name]->base_themes)) { + $theme_keys = array_keys($themes[$theme->name]->base_themes); + $theme_keys[] = $theme->name; + } + else { + $theme_keys = array($theme->name); + } + // Look for a screenshot in the current theme or in its closest ancestor. + foreach (array_reverse($theme_keys) as $theme_key) { + if (isset($themes[$theme_key]) && file_exists($themes[$theme_key]->info['screenshot'])) { + $theme->screenshot = array( + 'path' => $themes[$theme_key]->info['screenshot'], + 'alt' => t('Screenshot for !theme theme', array('!theme' => $theme->info['name'])), + 'title' => t('Screenshot for !theme theme', array('!theme' => $theme->info['name'])), + 'attributes' => array('class' => array('screenshot')), + ); + break; + } + } + + if (empty($theme->status)) { + // Ensure this theme is compatible with this version of core. + // Require the 'content' region to make sure the main page + // content has a common place in all themes. + $theme->incompatible_core = !isset($theme->info['core']) || ($theme->info['core'] != DRUPAL_CORE_COMPATIBILITY) || (!isset($theme->info['regions']['content'])); + $theme->incompatible_php = version_compare(phpversion(), $theme->info['php']) < 0; + } + $query['token'] = drupal_get_token('system-theme-operation-link'); + $theme->operations = array(); + if (!empty($theme->status) || !$theme->incompatible_core && !$theme->incompatible_php) { + // Create the operations links. + $query['theme'] = $theme->name; + if (drupal_theme_access($theme)) { + $theme->operations[] = array( + 'title' => t('Settings'), + 'href' => 'admin/appearance/settings/' . $theme->name, + 'attributes' => array('title' => t('Settings for !theme theme', array('!theme' => $theme->info['name']))), + ); + } + if (!empty($theme->status)) { + if (!$theme->is_default) { + $theme->operations[] = array( + 'title' => t('Disable'), + 'href' => 'admin/appearance/disable', + 'query' => $query, + 'attributes' => array('title' => t('Disable !theme theme', array('!theme' => $theme->info['name']))), + ); + $theme->operations[] = array( + 'title' => t('Set default'), + 'href' => 'admin/appearance/default', + 'query' => $query, + 'attributes' => array('title' => t('Set !theme as default theme', array('!theme' => $theme->info['name']))), + ); + } + } + else { + $theme->operations[] = array( + 'title' => t('Enable'), + 'href' => 'admin/appearance/enable', + 'query' => $query, + 'attributes' => array('title' => t('Enable !theme theme', array('!theme' => $theme->info['name']))), + ); + $theme->operations[] = array( + 'title' => t('Enable and set default'), + 'href' => 'admin/appearance/default', + 'query' => $query, + 'attributes' => array('title' => t('Enable !theme as default theme', array('!theme' => $theme->info['name']))), + ); + } + } + + // Add notes to default and administration theme. + $theme->notes = array(); + $theme->classes = array(); + if ($theme->is_default) { + $theme->classes[] = 'theme-default'; + $theme->notes[] = t('default theme'); + } + + // Sort enabled and disabled themes into their own groups. + $theme_groups[$theme->status ? 'enabled' : 'disabled'][] = $theme; + } + + // There are two possible theme groups. + $theme_group_titles = array( + 'enabled' => format_plural(count($theme_groups['enabled']), 'Enabled theme', 'Enabled themes'), + ); + if (!empty($theme_groups['disabled'])) { + $theme_group_titles['disabled'] = format_plural(count($theme_groups['disabled']), 'Disabled theme', 'Disabled themes'); + } + + uasort($theme_groups['enabled'], 'system_sort_themes'); + drupal_alter('system_themes_page', $theme_groups); + + $admin_form = drupal_get_form('system_themes_admin_form', $admin_theme_options); + return theme('system_themes_page', array('theme_groups' => $theme_groups, 'theme_group_titles' => $theme_group_titles)) . drupal_render($admin_form); +} + +/** + * Form to select the administration theme. + * + * @ingroup forms + * @see system_themes_admin_form_submit() + */ +function system_themes_admin_form($form, &$form_state, $theme_options) { + // Administration theme settings. + $form['admin_theme'] = array( + '#type' => 'fieldset', + '#title' => t('Administration theme'), + ); + $form['admin_theme']['admin_theme'] = array( + '#type' => 'select', + '#options' => array(0 => t('Default theme')) + $theme_options, + '#title' => t('Administration theme'), + '#description' => t('Choose "Default theme" to always use the same theme as the rest of the site.'), + '#default_value' => variable_get('admin_theme', 0), + ); + $form['admin_theme']['node_admin_theme'] = array( + '#type' => 'checkbox', + '#title' => t('Use the administration theme when editing or creating content'), + '#default_value' => variable_get('node_admin_theme', '0'), + ); + $form['admin_theme']['actions'] = array('#type' => 'actions'); + $form['admin_theme']['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + ); + return $form; +} + +/** + * Process system_themes_admin_form form submissions. + */ +function system_themes_admin_form_submit($form, &$form_state) { + drupal_set_message(t('The configuration options have been saved.')); + variable_set('admin_theme', $form_state['values']['admin_theme']); + variable_set('node_admin_theme', $form_state['values']['node_admin_theme']); +} + +/** + * Menu callback; Enables a theme. + */ +function system_theme_enable() { + if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) { + $theme = $_REQUEST['theme']; + // Get current list of themes. + $themes = list_themes(); + + // Check if the specified theme is one recognized by the system. + if (!empty($themes[$theme])) { + theme_enable(array($theme)); + drupal_set_message(t('The %theme theme has been enabled.', array('%theme' => $themes[$theme]->info['name']))); + } + else { + drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error'); + } + drupal_goto('admin/appearance'); + } + return MENU_ACCESS_DENIED; +} + +/** + * Menu callback; Disables a theme. + */ +function system_theme_disable() { + if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) { + $theme = $_REQUEST['theme']; + // Get current list of themes. + $themes = list_themes(); + + // Check if the specified theme is one recognized by the system. + if (!empty($themes[$theme])) { + if ($theme == variable_get('theme_default', 'bartik')) { + // Don't disable the default theme. + drupal_set_message(t('%theme is the default theme and cannot be disabled.', array('%theme' => $themes[$theme]->info['name'])), 'error'); + } + else { + theme_disable(array($theme)); + drupal_set_message(t('The %theme theme has been disabled.', array('%theme' => $themes[$theme]->info['name']))); + } + } + else { + drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error'); + } + drupal_goto('admin/appearance'); + } + return MENU_ACCESS_DENIED; +} + +/** + * Menu callback; Set the default theme. + */ +function system_theme_default() { + if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) { + $theme = $_REQUEST['theme']; + // Get current list of themes. + $themes = list_themes(); + + // Check if the specified theme is one recognized by the system. + if (!empty($themes[$theme])) { + // Enable the theme if it is currently disabled. + if (empty($themes[$theme]->status)) { + theme_enable(array($theme)); + } + // Set the default theme. + variable_set('theme_default', $theme); + + // Rebuild the menu. This duplicates the menu_rebuild() in theme_enable(). + // However, modules must know the current default theme in order to use + // this information in hook_menu() or hook_menu_alter() implementations, + // and doing the variable_set() before the theme_enable() could result + // in a race condition where the theme is default but not enabled. + menu_rebuild(); + + // The status message depends on whether an admin theme is currently in use: + // a value of 0 means the admin theme is set to be the default theme. + $admin_theme = variable_get('admin_theme', 0); + if ($admin_theme != 0 && $admin_theme != $theme) { + drupal_set_message(t('Please note that the administration theme is still set to the %admin_theme theme; consequently, the theme on this page remains unchanged. All non-administrative sections of the site, however, will show the selected %selected_theme theme by default.', array( + '%admin_theme' => $themes[$admin_theme]->info['name'], + '%selected_theme' => $themes[$theme]->info['name'], + ))); + } + else { + drupal_set_message(t('%theme is now the default theme.', array('%theme' => $themes[$theme]->info['name']))); + } + } + else { + drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error'); + } + drupal_goto('admin/appearance'); + } + return MENU_ACCESS_DENIED; +} + +/** + * Form builder; display theme configuration for entire site and individual themes. + * + * @param $key + * A theme name. + * @return + * The form structure. + * @ingroup forms + * @see system_theme_settings_submit() + */ +function system_theme_settings($form, &$form_state, $key = '') { + // Default settings are defined in theme_get_setting() in includes/theme.inc + if ($key) { + $var = 'theme_' . $key . '_settings'; + $themes = list_themes(); + $features = $themes[$key]->info['features']; + } + else { + $var = 'theme_settings'; + } + + $form['var'] = array('#type' => 'hidden', '#value' => $var); + + // Toggle settings + $toggles = array( + 'logo' => t('Logo'), + 'name' => t('Site name'), + 'slogan' => t('Site slogan'), + 'node_user_picture' => t('User pictures in posts'), + 'comment_user_picture' => t('User pictures in comments'), + 'comment_user_verification' => t('User verification status in comments'), + 'favicon' => t('Shortcut icon'), + 'main_menu' => t('Main menu'), + 'secondary_menu' => t('Secondary menu'), + ); + + // Some features are not always available + $disabled = array(); + if (!variable_get('user_pictures', 0)) { + $disabled['toggle_node_user_picture'] = TRUE; + $disabled['toggle_comment_user_picture'] = TRUE; + } + if (!module_exists('comment')) { + $disabled['toggle_comment_user_picture'] = TRUE; + $disabled['toggle_comment_user_verification'] = TRUE; + } + + $form['theme_settings'] = array( + '#type' => 'fieldset', + '#title' => t('Toggle display'), + '#description' => t('Enable or disable the display of certain page elements.'), + ); + foreach ($toggles as $name => $title) { + if ((!$key) || in_array($name, $features)) { + $form['theme_settings']['toggle_' . $name] = array('#type' => 'checkbox', '#title' => $title, '#default_value' => theme_get_setting('toggle_' . $name, $key)); + // Disable checkboxes for features not supported in the current configuration. + if (isset($disabled['toggle_' . $name])) { + $form['theme_settings']['toggle_' . $name]['#disabled'] = TRUE; + } + } + } + + if (!element_children($form['theme_settings'])) { + // If there is no element in the theme settings fieldset then do not show + // it -- but keep it in the form if another module wants to alter. + $form['theme_settings']['#access'] = FALSE; + } + + // Logo settings + if ((!$key) || in_array('logo', $features)) { + $form['logo'] = array( + '#type' => 'fieldset', + '#title' => t('Logo image settings'), + '#description' => t('If toggled on, the following logo will be displayed.'), + '#attributes' => array('class' => array('theme-settings-bottom')), + ); + $form['logo']['default_logo'] = array( + '#type' => 'checkbox', + '#title' => t('Use the default logo'), + '#default_value' => theme_get_setting('default_logo', $key), + '#tree' => FALSE, + '#description' => t('Check here if you want the theme to use the logo supplied with it.') + ); + $form['logo']['settings'] = array( + '#type' => 'container', + '#states' => array( + // Hide the logo settings when using the default logo. + 'invisible' => array( + 'input[name="default_logo"]' => array('checked' => TRUE), + ), + ), + ); + $form['logo']['settings']['logo_path'] = array( + '#type' => 'textfield', + '#title' => t('Path to custom logo'), + '#description' => t('The path to the file you would like to use as your logo file instead of the default logo.'), + '#default_value' => theme_get_setting('logo_path', $key), + ); + $form['logo']['settings']['logo_upload'] = array( + '#type' => 'file', + '#title' => t('Upload logo image'), + '#maxlength' => 40, + '#description' => t("If you don't have direct file access to the server, use this field to upload your logo.") + ); + } + + if ((!$key) || in_array('favicon', $features)) { + $form['favicon'] = array( + '#type' => 'fieldset', + '#title' => t('Shortcut icon settings'), + '#description' => t("Your shortcut icon, or 'favicon', is displayed in the address bar and bookmarks of most browsers."), + ); + $form['favicon']['default_favicon'] = array( + '#type' => 'checkbox', + '#title' => t('Use the default shortcut icon.'), + '#default_value' => theme_get_setting('default_favicon', $key), + '#description' => t('Check here if you want the theme to use the default shortcut icon.') + ); + $form['favicon']['settings'] = array( + '#type' => 'container', + '#states' => array( + // Hide the favicon settings when using the default favicon. + 'invisible' => array( + 'input[name="default_favicon"]' => array('checked' => TRUE), + ), + ), + ); + $form['favicon']['settings']['favicon_path'] = array( + '#type' => 'textfield', + '#title' => t('Path to custom icon'), + '#description' => t('The path to the image file you would like to use as your custom shortcut icon.'), + '#default_value' => theme_get_setting('favicon_path', $key), + ); + $form['favicon']['settings']['favicon_upload'] = array( + '#type' => 'file', + '#title' => t('Upload icon image'), + '#description' => t("If you don't have direct file access to the server, use this field to upload your shortcut icon.") + ); + } + + // Inject human-friendly values for logo and favicon. + foreach (array('logo' => 'logo.png', 'favicon' => 'favicon.ico') as $type => $default) { + if (isset($form[$type]['settings'][$type . '_path'])) { + $element = &$form[$type]['settings'][$type . '_path']; + + // If path is a public:// URI, display the path relative to the files + // directory; stream wrappers are not end-user friendly. + $original_path = $element['#default_value']; + $friendly_path = NULL; + if (file_uri_scheme($original_path) == 'public') { + $friendly_path = file_uri_target($original_path); + $element['#default_value'] = $friendly_path; + } + } + } + + if ($key) { + // Call engine-specific settings. + $function = $themes[$key]->prefix . '_engine_settings'; + if (function_exists($function)) { + $form['engine_specific'] = array( + '#type' => 'fieldset', + '#title' => t('Theme-engine-specific settings'), + '#description' => t('These settings only exist for the themes based on the %engine theme engine.', array('%engine' => $themes[$key]->prefix)), + ); + $function($form, $form_state); + } + + // Create a list which includes the current theme and all its base themes. + if (isset($themes[$key]->base_themes)) { + $theme_keys = array_keys($themes[$key]->base_themes); + $theme_keys[] = $key; + } + else { + $theme_keys = array($key); + } + + // Save the name of the current theme (if any), so that we can temporarily + // override the current theme and allow theme_get_setting() to work + // without having to pass the theme name to it. + $default_theme = !empty($GLOBALS['theme_key']) ? $GLOBALS['theme_key'] : NULL; + $GLOBALS['theme_key'] = $key; + + // Process the theme and all its base themes. + foreach ($theme_keys as $theme) { + // Include the theme-settings.php file. + $filename = DRUPAL_ROOT . '/' . str_replace("/$theme.info", '', $themes[$theme]->filename) . '/theme-settings.php'; + if (file_exists($filename)) { + require_once $filename; + } + + // Call theme-specific settings. + $function = $theme . '_form_system_theme_settings_alter'; + if (function_exists($function)) { + $function($form, $form_state); + } + } + + // Restore the original current theme. + if (isset($default_theme)) { + $GLOBALS['theme_key'] = $default_theme; + } + else { + unset($GLOBALS['theme_key']); + } + } + + $form = system_settings_form($form); + // We don't want to call system_settings_form_submit(), so change #submit. + array_pop($form['#submit']); + $form['#submit'][] = 'system_theme_settings_submit'; + $form['#validate'][] = 'system_theme_settings_validate'; + return $form; +} + +/** + * Validator for the system_theme_settings() form. + */ +function system_theme_settings_validate($form, &$form_state) { + // Handle file uploads. + $validators = array('file_validate_is_image' => array()); + + // Check for a new uploaded logo. + $file = file_save_upload('logo_upload', $validators); + if (isset($file)) { + // File upload was attempted. + if ($file) { + // Put the temporary file in form_values so we can save it on submit. + $form_state['values']['logo_upload'] = $file; + } + else { + // File upload failed. + form_set_error('logo_upload', t('The logo could not be uploaded.')); + } + } + + $validators = array('file_validate_extensions' => array('ico png gif jpg jpeg apng svg')); + + // Check for a new uploaded favicon. + $file = file_save_upload('favicon_upload', $validators); + if (isset($file)) { + // File upload was attempted. + if ($file) { + // Put the temporary file in form_values so we can save it on submit. + $form_state['values']['favicon_upload'] = $file; + } + else { + // File upload failed. + form_set_error('favicon_upload', t('The favicon could not be uploaded.')); + } + } + + // If the user provided a path for a logo or favicon file, make sure a file + // exists at that path. + if (!empty($form_state['values']['logo_path'])) { + $path = _system_theme_settings_validate_path($form_state['values']['logo_path']); + if (!$path) { + form_set_error('logo_path', t('The custom logo path is invalid.')); + } + } + if (!empty($form_state['values']['favicon_path'])) { + $path = _system_theme_settings_validate_path($form_state['values']['favicon_path']); + if (!$path) { + form_set_error('favicon_path', t('The custom favicon path is invalid.')); + } + } +} + +/** + * Helper function for the system_theme_settings form. + * + * Attempts to validate normal system paths, paths relative to the public files + * directory, or stream wrapper URIs. If the given path is any of the above, + * returns a valid path or URI that the theme system can display. + * + * @param $path + * A path relative to the Drupal root or to the public files directory, or + * a stream wrapper URI. + * @return mixed + * A valid path that can be displayed through the theme system, or FALSE if + * the path could not be validated. + */ +function _system_theme_settings_validate_path($path) { + // Absolute local file paths are invalid. + if (drupal_realpath($path) == $path) { + return FALSE; + } + // A path relative to the Drupal root or a fully qualified URI is valid. + if (is_file($path)) { + return $path; + } + // Prepend 'public://' for relative file paths within public filesystem. + if (file_uri_scheme($path) === FALSE) { + $path = 'public://' . $path; + } + if (is_file($path)) { + return $path; + } + return FALSE; +} + +/** + * Process system_theme_settings form submissions. + */ +function system_theme_settings_submit($form, &$form_state) { + // Exclude unnecessary elements before saving. + form_state_values_clean($form_state); + + $values = $form_state['values']; + + // Extract the name of the theme from the submitted form values, then remove + // it from the array so that it is not saved as part of the variable. + $key = $values['var']; + unset($values['var']); + + // If the user uploaded a new logo or favicon, save it to a permanent location + // and use it in place of the default theme-provided file. + if (!empty($values['logo_upload'])) { + $file = $values['logo_upload']; + unset($values['logo_upload']); + $filename = file_unmanaged_copy($file->uri); + $values['default_logo'] = 0; + $values['logo_path'] = $filename; + $values['toggle_logo'] = 1; + } + if (!empty($values['favicon_upload'])) { + $file = $values['favicon_upload']; + unset($values['favicon_upload']); + $filename = file_unmanaged_copy($file->uri); + $values['default_favicon'] = 0; + $values['favicon_path'] = $filename; + $values['toggle_favicon'] = 1; + } + + // If the user entered a path relative to the system files directory for + // a logo or favicon, store a public:// URI so the theme system can handle it. + if (!empty($values['logo_path'])) { + $values['logo_path'] = _system_theme_settings_validate_path($values['logo_path']); + } + if (!empty($values['favicon_path'])) { + $values['favicon_path'] = _system_theme_settings_validate_path($values['favicon_path']); + } + + if (empty($values['default_favicon']) && !empty($values['favicon_path'])) { + $values['favicon_mimetype'] = file_get_mimetype($values['favicon_path']); + } + + variable_set($key, $values); + drupal_set_message(t('The configuration options have been saved.')); + + cache_clear_all(); +} + +/** + * Recursively check compatibility. + * + * @param $incompatible + * An associative array which at the end of the check contains all + * incompatible files as the keys, their values being TRUE. + * @param $files + * The set of files that will be tested. + * @param $file + * The file at which the check starts. + * @return + * Returns TRUE if an incompatible file is found, NULL (no return value) + * otherwise. + */ +function _system_is_incompatible(&$incompatible, $files, $file) { + if (isset($incompatible[$file->name])) { + return TRUE; + } + // Recursively traverse required modules, looking for incompatible modules. + foreach ($file->requires as $requires) { + if (isset($files[$requires]) && _system_is_incompatible($incompatible, $files, $files[$requires])) { + $incompatible[$file->name] = TRUE; + return TRUE; + } + } +} + +/** + * Menu callback; provides module enable/disable interface. + * + * The list of modules gets populated by module.info files, which contain each + * module's name, description, and information about which modules it requires. + * See drupal_parse_info_file() for information on module.info descriptors. + * + * Dependency checking is performed to ensure that a module: + * - can not be enabled if there are disabled modules it requires. + * - can not be disabled if there are enabled modules which depend on it. + * + * @param $form_state + * An associative array containing the current state of the form. + * + * @return + * The form array. + * + * @ingroup forms + * @see theme_system_modules() + * @see system_modules_submit() + */ +function system_modules($form, $form_state = array()) { + // Get current list of modules. + $files = system_rebuild_module_data(); + + // Remove hidden modules from display list. + $visible_files = $files; + foreach ($visible_files as $filename => $file) { + if (!empty($file->info['hidden'])) { + unset($visible_files[$filename]); + } + } + + uasort($visible_files, 'system_sort_modules_by_info_name'); + + // If the modules form was submitted, then system_modules_submit() runs first + // and if there are unfilled required modules, then $form_state['storage'] is + // filled, triggering a rebuild. In this case we need to display a + // confirmation form. + if (!empty($form_state['storage'])) { + return system_modules_confirm_form($visible_files, $form_state['storage']); + } + + $modules = array(); + $form['modules'] = array('#tree' => TRUE); + + // Used when checking if module implements a help page. + $help_arg = module_exists('help') ? drupal_help_arg() : FALSE; + + // Used when displaying modules that are required by the installation profile. + require_once DRUPAL_ROOT . '/includes/install.inc'; + $distribution_name = check_plain(drupal_install_profile_distribution_name()); + + // Iterate through each of the modules. + foreach ($visible_files as $filename => $module) { + $extra = array(); + $extra['enabled'] = (bool) $module->status; + if (!empty($module->info['required'] )) { + $extra['disabled'] = TRUE; + $extra['required_by'][] = $distribution_name . (!empty($module->info['explanation']) ? ' ('. $module->info['explanation'] .')' : ''); + } + + // If this module requires other modules, add them to the array. + foreach ($module->requires as $requires => $v) { + if (!isset($files[$requires])) { + $extra['requires'][$requires] = t('@module (missing)', array('@module' => drupal_ucfirst($requires))); + $extra['disabled'] = TRUE; + } + // Only display visible modules. + elseif (isset($visible_files[$requires])) { + $requires_name = $files[$requires]->info['name']; + // Disable this module if it is incompatible with the dependency's version. + if ($incompatible_version = drupal_check_incompatibility($v, str_replace(DRUPAL_CORE_COMPATIBILITY . '-', '', $files[$requires]->info['version']))) { + $extra['requires'][$requires] = t('@module (incompatible with version @version)', array( + '@module' => $requires_name . $incompatible_version, + '@version' => $files[$requires]->info['version'], + )); + $extra['disabled'] = TRUE; + } + // Disable this module if the dependency is incompatible with this + // version of Drupal core. + elseif ($files[$requires]->info['core'] != DRUPAL_CORE_COMPATIBILITY) { + $extra['requires'][$requires] = t('@module (incompatible with this version of Drupal core)', array( + '@module' => $requires_name, + )); + $extra['disabled'] = TRUE; + } + elseif ($files[$requires]->status) { + $extra['requires'][$requires] = t('@module (enabled)', array('@module' => $requires_name)); + } + else { + $extra['requires'][$requires] = t('@module (disabled)', array('@module' => $requires_name)); + } + } + } + // Generate link for module's help page, if there is one. + if ($help_arg && $module->status && in_array($filename, module_implements('help'))) { + if (module_invoke($filename, 'help', "admin/help#$filename", $help_arg)) { + $extra['links']['help'] = array( + '#type' => 'link', + '#title' => t('Help'), + '#href' => "admin/help/$filename", + '#options' => array('attributes' => array('class' => array('module-link', 'module-link-help'), 'title' => t('Help'))), + ); + } + } + // Generate link for module's permission, if the user has access to it. + if ($module->status && user_access('administer permissions') && in_array($filename, module_implements('permission'))) { + $extra['links']['permissions'] = array( + '#type' => 'link', + '#title' => t('Permissions'), + '#href' => 'admin/people/permissions', + '#options' => array('fragment' => 'module-' . $filename, 'attributes' => array('class' => array('module-link', 'module-link-permissions'), 'title' => t('Configure permissions'))), + ); + } + // Generate link for module's configuration page, if the module provides + // one. + if ($module->status && isset($module->info['configure'])) { + $configure_link = menu_get_item($module->info['configure']); + if ($configure_link['access']) { + $extra['links']['configure'] = array( + '#type' => 'link', + '#title' => t('Configure'), + '#href' => $configure_link['href'], + '#options' => array('attributes' => array('class' => array('module-link', 'module-link-configure'), 'title' => $configure_link['description'])), + ); + } + } + + // If this module is required by other modules, list those, and then make it + // impossible to disable this one. + foreach ($module->required_by as $required_by => $v) { + // Hidden modules are unset already. + if (isset($visible_files[$required_by])) { + if ($files[$required_by]->status == 1 && $module->status == 1) { + $extra['required_by'][] = t('@module (enabled)', array('@module' => $files[$required_by]->info['name'])); + $extra['disabled'] = TRUE; + } + else { + $extra['required_by'][] = t('@module (disabled)', array('@module' => $files[$required_by]->info['name'])); + } + } + } + $form['modules'][$module->info['package']][$filename] = _system_modules_build_row($module->info, $extra); + } + + // Add basic information to the fieldsets. + foreach (element_children($form['modules']) as $package) { + $form['modules'][$package] += array( + '#type' => 'fieldset', + '#title' => t($package), + '#collapsible' => TRUE, + '#theme' => 'system_modules_fieldset', + '#header' => array( + array('data' => t('Enabled'), 'class' => array('checkbox')), + t('Name'), + t('Version'), + t('Description'), + array('data' => t('Operations'), 'colspan' => 3), + ), + // Ensure that the "Core" package fieldset comes first. + '#weight' => $package == 'Core' ? -10 : NULL, + ); + } + + // Lastly, sort all fieldsets by title. + uasort($form['modules'], 'element_sort_by_title'); + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + ); + $form['#action'] = url('admin/modules/list/confirm'); + + return $form; +} + +/** + * Array sorting callback; sorts modules or themes by their name. + */ +function system_sort_modules_by_info_name($a, $b) { + return strcasecmp($a->info['name'], $b->info['name']); +} + +/** + * Sorts themes by their names, with the default theme listed first. + * + * Callback for uasort() within system_themes_page(). + * + * @see system_sort_modules_by_info_name(). + */ +function system_sort_themes($a, $b) { + if ($a->is_default) { + return -1; + } + if ($b->is_default) { + return 1; + } + return strcasecmp($a->info['name'], $b->info['name']); +} + +/** + * Build a table row for the system modules page. + */ +function _system_modules_build_row($info, $extra) { + // Add in the defaults. + $extra += array( + 'requires' => array(), + 'required_by' => array(), + 'disabled' => FALSE, + 'enabled' => FALSE, + 'links' => array(), + ); + $form = array( + '#tree' => TRUE, + ); + // Set the basic properties. + $form['name'] = array( + '#markup' => $info['name'], + ); + $form['description'] = array( + '#markup' => t($info['description']), + ); + $form['version'] = array( + '#markup' => $info['version'], + ); + $form['#requires'] = $extra['requires']; + $form['#required_by'] = $extra['required_by']; + + // Check the compatibilities. + $compatible = TRUE; + $status_short = ''; + $status_long = ''; + + // Initialize empty arrays of long and short reasons explaining why the + // module is incompatible. + // Add each reason as a separate element in both the arrays. + $reasons_short = array(); + $reasons_long = array(); + + // Check the core compatibility. + if (!isset($info['core']) || $info['core'] != DRUPAL_CORE_COMPATIBILITY) { + $compatible = FALSE; + $reasons_short[] = t('Incompatible with this version of Drupal core.'); + $reasons_long[] = t('This version is not compatible with Drupal !core_version and should be replaced.', array('!core_version' => DRUPAL_CORE_COMPATIBILITY)); + } + + // Ensure this module is compatible with the currently installed version of PHP. + if (version_compare(phpversion(), $info['php']) < 0) { + $compatible = FALSE; + $reasons_short[] = t('Incompatible with this version of PHP'); + $php_required = $info['php']; + if (substr_count($info['php'], '.') < 2) { + $php_required .= '.*'; + } + $reasons_long[] = t('This module requires PHP version @php_required and is incompatible with PHP version !php_version.', array('@php_required' => $php_required, '!php_version' => phpversion())); + } + + // If this module is compatible, present a checkbox indicating + // this module may be installed. Otherwise, show a big red X. + if ($compatible) { + $form['enable'] = array( + '#type' => 'checkbox', + '#title' => t('Enable'), + '#default_value' => $extra['enabled'], + ); + if ($extra['disabled']) { + $form['enable']['#disabled'] = TRUE; + } + } + else { + $status_short = implode(' ', $reasons_short); + $status_long = implode(' ', $reasons_long); + $form['enable'] = array( + '#markup' => theme('image', array('path' => 'misc/watchdog-error.png', 'alt' => $status_short, 'title' => $status_short)), + ); + $form['description']['#markup'] .= theme('system_modules_incompatible', array('message' => $status_long)); + } + + // Build operation links. + foreach (array('help', 'permissions', 'configure') as $key) { + $form['links'][$key] = (isset($extra['links'][$key]) ? $extra['links'][$key] : array()); + } + + return $form; +} + +/** + * Display confirmation form for required modules. + * + * @param $modules + * Array of module file objects as returned from system_rebuild_module_data(). + * @param $storage + * The contents of $form_state['storage']; an array with two + * elements: the list of required modules and the list of status + * form field values from the previous screen. + * @ingroup forms + */ +function system_modules_confirm_form($modules, $storage) { + $items = array(); + + $form['validation_modules'] = array('#type' => 'value', '#value' => $modules); + $form['status']['#tree'] = TRUE; + + foreach ($storage['more_required'] as $info) { + $t_argument = array( + '@module' => $info['name'], + '@required' => implode(', ', $info['requires']), + ); + $items[] = format_plural(count($info['requires']), 'You must enable the @required module to install @module.', 'You must enable the @required modules to install @module.', $t_argument); + } + + foreach ($storage['missing_modules'] as $name => $info) { + $t_argument = array( + '@module' => $name, + '@depends' => implode(', ', $info['depends']), + ); + $items[] = format_plural(count($info['depends']), 'The @module module is missing, so the following module will be disabled: @depends.', 'The @module module is missing, so the following modules will be disabled: @depends.', $t_argument); + } + + $form['text'] = array('#markup' => theme('item_list', array('items' => $items))); + + if ($form) { + // Set some default form values + $form = confirm_form( + $form, + t('Some required modules must be enabled'), + 'admin/modules', + t('Would you like to continue with the above?'), + t('Continue'), + t('Cancel')); + return $form; + } +} + +/** + * Submit callback; handles modules form submission. + */ +function system_modules_submit($form, &$form_state) { + include_once DRUPAL_ROOT . '/includes/install.inc'; + + // Builds list of modules. + $modules = array(); + // If we're not coming from the confirmation form, build the list of modules. + if (empty($form_state['storage'])) { + // If we're not coming from the confirmation form, build the module list. + foreach ($form_state['values']['modules'] as $group_name => $group) { + foreach ($group as $module => $enabled) { + $modules[$module] = array('group' => $group_name, 'enabled' => $enabled['enable']); + } + } + } + else { + // If we are coming from the confirmation form, fetch + // the modules out of $form_state. + $modules = $form_state['storage']['modules']; + } + + // Collect data for all modules to be able to determine dependencies. + $files = system_rebuild_module_data(); + + // Sorts modules by weight. + $sort = array(); + foreach (array_keys($modules) as $module) { + $sort[$module] = $files[$module]->sort; + } + array_multisort($sort, $modules); + + // Makes sure all required modules are set to be enabled. + $more_required = array(); + $missing_modules = array(); + foreach ($modules as $name => $module) { + if ($module['enabled']) { + // Checks that all dependencies are set to be enabled. Stores the ones + // that are not in $dependencies variable so that the user can be alerted + // in the confirmation form that more modules need to be enabled. + $dependencies = array(); + foreach (array_keys($files[$name]->requires) as $required) { + if (empty($modules[$required]['enabled'])) { + if (isset($files[$required])) { + $dependencies[] = $files[$required]->info['name']; + $modules[$required]['enabled'] = TRUE; + } + else { + $missing_modules[$required]['depends'][] = $name; + $modules[$name]['enabled'] = FALSE; + } + } + } + + // Stores additional modules that need to be enabled in $more_required. + if (!empty($dependencies)) { + $more_required[$name] = array( + 'name' => $files[$name]->info['name'], + 'requires' => $dependencies, + ); + } + } + } + + // Redirects to confirmation form if more modules need to be enabled. + if ((!empty($more_required) || !empty($missing_modules)) && !isset($form_state['values']['confirm'])) { + $form_state['storage'] = array( + 'more_required' => $more_required, + 'modules' => $modules, + 'missing_modules' => $missing_modules, + ); + $form_state['rebuild'] = TRUE; + return; + } + + // Invokes hook_requirements('install'). If failures are detected, makes sure + // the dependent modules aren't installed either. + foreach ($modules as $name => $module) { + // Only invoke hook_requirements() on modules that are going to be installed. + if ($module['enabled'] && drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED) { + if (!drupal_check_module($name)) { + $modules[$name]['enabled'] = FALSE; + foreach (array_keys($files[$name]->required_by) as $required_by) { + $modules[$required_by]['enabled'] = FALSE; + } + } + } + } + + // Initializes array of actions. + $actions = array( + 'enable' => array(), + 'disable' => array(), + 'install' => array(), + ); + + // Builds arrays of modules that need to be enabled, disabled, and installed. + foreach ($modules as $name => $module) { + if ($module['enabled']) { + if (drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED) { + $actions['install'][] = $name; + $actions['enable'][] = $name; + } + elseif (!module_exists($name)) { + $actions['enable'][] = $name; + } + } + elseif (module_exists($name)) { + $actions['disable'][] = $name; + } + } + + // Gets list of modules prior to install process, unsets $form_state['storage'] + // so we don't get redirected back to the confirmation form. + $pre_install_list = module_list(); + unset($form_state['storage']); + + // Reverse the 'enable' list, to order dependencies before dependents. + krsort($actions['enable']); + + // Installs, enables, and disables modules. + module_enable($actions['enable'], FALSE); + module_disable($actions['disable'], FALSE); + + // Gets module list after install process, flushes caches and displays a + // message if there are changes. + $post_install_list = module_list(TRUE); + if ($pre_install_list != $post_install_list) { + drupal_flush_all_caches(); + drupal_set_message(t('The configuration options have been saved.')); + } + + $form_state['redirect'] = 'admin/modules'; +} + +/** + * Uninstall functions + */ + +/** + * Builds a form of currently disabled modules. + * + * @ingroup forms + * @see system_modules_uninstall_validate() + * @see system_modules_uninstall_submit() + * @param $form_state['values'] + * Submitted form values. + * @return + * A form array representing the currently disabled modules. + */ +function system_modules_uninstall($form, $form_state = NULL) { + // Make sure the install API is available. + include_once DRUPAL_ROOT . '/includes/install.inc'; + + // Display the confirm form if any modules have been submitted. + if (!empty($form_state['storage']) && $confirm_form = system_modules_uninstall_confirm_form($form_state['storage'])) { + return $confirm_form; + } + + // Get a list of disabled, installed modules. + $all_modules = system_rebuild_module_data(); + $disabled_modules = array(); + foreach ($all_modules as $name => $module) { + if (empty($module->status) && $module->schema_version > SCHEMA_UNINSTALLED) { + $disabled_modules[$name] = $module; + } + } + + // Only build the rest of the form if there are any modules available to + // uninstall. + if (!empty($disabled_modules)) { + $profile = drupal_get_profile(); + uasort($disabled_modules, 'system_sort_modules_by_info_name'); + $form['uninstall'] = array('#tree' => TRUE); + foreach ($disabled_modules as $module) { + $module_name = $module->info['name'] ? $module->info['name'] : $module->name; + $form['modules'][$module->name]['#module_name'] = $module_name; + $form['modules'][$module->name]['name']['#markup'] = $module_name; + $form['modules'][$module->name]['description']['#markup'] = t($module->info['description']); + $form['uninstall'][$module->name] = array( + '#type' => 'checkbox', + '#title' => t('Uninstall @module module', array('@module' => $module_name)), + '#title_display' => 'invisible', + ); + // All modules which depend on this one must be uninstalled first, before + // we can allow this module to be uninstalled. (The installation profile + // is excluded from this list.) + foreach (array_keys($module->required_by) as $dependent) { + if ($dependent != $profile && drupal_get_installed_schema_version($dependent) != SCHEMA_UNINSTALLED) { + $dependent_name = isset($all_modules[$dependent]->info['name']) ? $all_modules[$dependent]->info['name'] : $dependent; + $form['modules'][$module->name]['#required_by'][] = $dependent_name; + $form['uninstall'][$module->name]['#disabled'] = TRUE; + } + } + } + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Uninstall'), + ); + $form['#action'] = url('admin/modules/uninstall/confirm'); + } + else { + $form['modules'] = array(); + } + + return $form; +} + +/** + * Confirm uninstall of selected modules. + * + * @ingroup forms + * @param $storage + * An associative array of modules selected to be uninstalled. + * @return + * A form array representing modules to confirm. + */ +function system_modules_uninstall_confirm_form($storage) { + // Nothing to build. + if (empty($storage)) { + return; + } + + // Construct the hidden form elements and list items. + foreach (array_filter($storage['uninstall']) as $module => $value) { + $info = drupal_parse_info_file(drupal_get_path('module', $module) . '/' . $module . '.info'); + $uninstall[] = $info['name']; + $form['uninstall'][$module] = array('#type' => 'hidden', + '#value' => 1, + ); + } + + // Display a confirm form if modules have been selected. + if (isset($uninstall)) { + $form['#confirmed'] = TRUE; + $form['uninstall']['#tree'] = TRUE; + $form['modules'] = array('#markup' => '
' . t('The following modules will be completely uninstalled from your site, and all data from these modules will be lost!') . '
' . theme('item_list', array('items' => $uninstall))); + $form = confirm_form( + $form, + t('Confirm uninstall'), + 'admin/modules/uninstall', + t('Would you like to continue with uninstalling the above?'), + t('Uninstall'), + t('Cancel')); + return $form; + } +} + +/** + * Validates the submitted uninstall form. + */ +function system_modules_uninstall_validate($form, &$form_state) { + // Form submitted, but no modules selected. + if (!count(array_filter($form_state['values']['uninstall']))) { + drupal_set_message(t('No modules selected.'), 'error'); + drupal_goto('admin/modules/uninstall'); + } +} + +/** + * Processes the submitted uninstall form. + */ +function system_modules_uninstall_submit($form, &$form_state) { + // Make sure the install API is available. + include_once DRUPAL_ROOT . '/includes/install.inc'; + + if (!empty($form['#confirmed'])) { + // Call the uninstall routine for each selected module. + $modules = array_keys($form_state['values']['uninstall']); + drupal_uninstall_modules($modules); + drupal_set_message(t('The selected modules have been uninstalled.')); + + $form_state['redirect'] = 'admin/modules/uninstall'; + } + else { + $form_state['storage'] = $form_state['values']; + $form_state['rebuild'] = TRUE; + } +} + +/** + * Menu callback. Display blocked IP addresses. + * + * @param $default_ip + * Optional IP address to be passed on to drupal_get_form() for + * use as the default value of the IP address form field. + */ +function system_ip_blocking($default_ip = '') { + $rows = array(); + $header = array(t('Blocked IP addresses'), t('Operations')); + $result = db_query('SELECT * FROM {blocked_ips}'); + foreach ($result as $ip) { + $rows[] = array( + $ip->ip, + l(t('delete'), "admin/config/people/ip-blocking/delete/$ip->iid"), + ); + } + + $build['system_ip_blocking_form'] = drupal_get_form('system_ip_blocking_form', $default_ip); + + $build['system_ip_blocking_table'] = array( + '#theme' => 'table', + '#header' => $header, + '#rows' => $rows, + '#empty' => t('No blocked IP addresses available.'), + ); + + return $build; +} + +/** + * Define the form for blocking IP addresses. + * + * @ingroup forms + * @see system_ip_blocking_form_validate() + * @see system_ip_blocking_form_submit() + */ +function system_ip_blocking_form($form, $form_state, $default_ip) { + $form['ip'] = array( + '#title' => t('IP address'), + '#type' => 'textfield', + '#size' => 48, + '#maxlength' => 40, + '#default_value' => $default_ip, + '#description' => t('Enter a valid IP address.'), + ); + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Add'), + ); + $form['#submit'][] = 'system_ip_blocking_form_submit'; + $form['#validate'][] = 'system_ip_blocking_form_validate'; + return $form; +} + +function system_ip_blocking_form_validate($form, &$form_state) { + $ip = trim($form_state['values']['ip']); + if (db_query("SELECT * FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField()) { + form_set_error('ip', t('This IP address is already blocked.')); + } + elseif ($ip == ip_address()) { + form_set_error('ip', t('You may not block your own IP address.')); + } + elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) { + form_set_error('ip', t('Enter a valid IP address.')); + } +} + +function system_ip_blocking_form_submit($form, &$form_state) { + $ip = trim($form_state['values']['ip']); + db_insert('blocked_ips') + ->fields(array('ip' => $ip)) + ->execute(); + drupal_set_message(t('The IP address %ip has been blocked.', array('%ip' => $ip))); + $form_state['redirect'] = 'admin/config/people/ip-blocking'; + return; +} + +/** + * IP deletion confirm page. + * + * @see system_ip_blocking_delete_submit() + */ +function system_ip_blocking_delete($form, &$form_state, $iid) { + $form['blocked_ip'] = array( + '#type' => 'value', + '#value' => $iid, + ); + return confirm_form($form, t('Are you sure you want to delete %ip?', array('%ip' => $iid['ip'])), 'admin/config/people/ip-blocking', t('This action cannot be undone.'), t('Delete'), t('Cancel')); +} + +/** + * Process system_ip_blocking_delete form submissions. + */ +function system_ip_blocking_delete_submit($form, &$form_state) { + $blocked_ip = $form_state['values']['blocked_ip']; + db_delete('blocked_ips') + ->condition('iid', $blocked_ip['iid']) + ->execute(); + watchdog('user', 'Deleted %ip', array('%ip' => $blocked_ip['ip'])); + drupal_set_message(t('The IP address %ip was deleted.', array('%ip' => $blocked_ip['ip']))); + $form_state['redirect'] = 'admin/config/people/ip-blocking'; +} + +/** + * Form builder; The general site information form. + * + * @ingroup forms + * @see system_settings_form() + */ +function system_site_information_settings() { + $form['site_information'] = array( + '#type' => 'fieldset', + '#title' => t('Site details'), + ); + $form['site_information']['site_name'] = array( + '#type' => 'textfield', + '#title' => t('Site name'), + '#default_value' => variable_get('site_name', 'Drupal'), + '#required' => TRUE + ); + $form['site_information']['site_slogan'] = array( + '#type' => 'textfield', + '#title' => t('Slogan'), + '#default_value' => variable_get('site_slogan', ''), + '#description' => t("How this is used depends on your site's theme."), + ); + $form['site_information']['site_mail'] = array( + '#type' => 'textfield', + '#title' => t('E-mail address'), + '#default_value' => variable_get('site_mail', ini_get('sendmail_from')), + '#description' => t("The From address in automated e-mails sent during registration and new password requests, and other notifications. (Use an address ending in your site's domain to help prevent this e-mail being flagged as spam.)"), + '#required' => TRUE, + ); + $form['front_page'] = array( + '#type' => 'fieldset', + '#title' => t('Front page'), + ); + $form['front_page']['default_nodes_main'] = array( + '#type' => 'select', '#title' => t('Number of posts on front page'), + '#default_value' => variable_get('default_nodes_main', 10), + '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)), + '#description' => t('The maximum number of posts displayed on overview pages such as the front page.') + ); + $form['front_page']['site_frontpage'] = array( + '#type' => 'textfield', + '#title' => t('Default front page'), + '#default_value' => (variable_get('site_frontpage')!='node'?drupal_get_path_alias(variable_get('site_frontpage', 'node')):''), + '#size' => 40, + '#description' => t('Optionally, specify a relative URL to display as the front page. Leave blank to display the default content feed.'), + '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='), + ); + $form['error_page'] = array( + '#type' => 'fieldset', + '#title' => t('Error pages'), + ); + $form['error_page']['site_403'] = array( + '#type' => 'textfield', + '#title' => t('Default 403 (access denied) page'), + '#default_value' => variable_get('site_403', ''), + '#size' => 40, + '#description' => t('This page is displayed when the requested document is denied to the current user. Leave blank to display a generic "access denied" page.'), + '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=') + ); + $form['error_page']['site_404'] = array( + '#type' => 'textfield', + '#title' => t('Default 404 (not found) page'), + '#default_value' => variable_get('site_404', ''), + '#size' => 40, + '#description' => t('This page is displayed when no other content matches the requested document. Leave blank to display a generic "page not found" page.'), + '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=') + ); + + $form['#validate'][] = 'system_site_information_settings_validate'; + + return system_settings_form($form); +} + +/** + * Validates the submitted site-information form. + */ +function system_site_information_settings_validate($form, &$form_state) { + // Validate the e-mail address. + if ($error = user_validate_mail($form_state['values']['site_mail'])) { + form_set_error('site_mail', $error); + } + // Check for empty front page path. + if (empty($form_state['values']['site_frontpage'])) { + // Set to default "node". + form_set_value($form['front_page']['site_frontpage'], 'node', $form_state); + } + else { + // Get the normal path of the front page. + form_set_value($form['front_page']['site_frontpage'], drupal_get_normal_path($form_state['values']['site_frontpage']), $form_state); + } + // Validate front page path. + if (!drupal_valid_path($form_state['values']['site_frontpage'])) { + form_set_error('site_frontpage', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_frontpage']))); + } + // Get the normal paths of both error pages. + if (!empty($form_state['values']['site_403'])) { + form_set_value($form['error_page']['site_403'], drupal_get_normal_path($form_state['values']['site_403']), $form_state); + } + if (!empty($form_state['values']['site_404'])) { + form_set_value($form['error_page']['site_404'], drupal_get_normal_path($form_state['values']['site_404']), $form_state); + } + // Validate 403 error path. + if (!empty($form_state['values']['site_403']) && !drupal_valid_path($form_state['values']['site_403'])) { + form_set_error('site_403', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_403']))); + } + // Validate 404 error path. + if (!empty($form_state['values']['site_404']) && !drupal_valid_path($form_state['values']['site_404'])) { + form_set_error('site_404', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_404']))); + } +} + +/** + * Form builder; Cron form. + * + * @see system_settings_form() + * @ingroup forms + */ +function system_cron_settings() { + global $base_url; + $form['description'] = array( + '#markup' => '' . t('Cron takes care of running periodic tasks like checking for updates and indexing content for search.') . '
', + ); + $form['run'] = array( + '#type' => 'submit', + '#value' => t('Run cron'), + '#submit' => array('system_run_cron_submit'), + ); + $status = '' . t('Last run: %cron-last ago.', array('%cron-last' => format_interval(REQUEST_TIME - variable_get('cron_last')),)) . '
'; + $form['status'] = array( + '#markup' => $status, + ); + + $form['cron_url'] = array( + '#markup' => '' . t('To run cron from outside the site, go to !cron', array('!cron' => url($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => variable_get('cron_key', 'drupal')))))) . '
', + ); + + $form['cron'] = array( + '#type' => 'fieldset', + ); + $form['cron']['cron_safe_threshold'] = array( + '#type' => 'select', + '#title' => t('Run cron every'), + '#description' => t('More information about setting up scheduled tasks can be found by reading the cron tutorial on drupal.org.', array('@url' => url('http://drupal.org/cron'))), + '#default_value' => variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD), + '#options' => array(0 => t('Never')) + drupal_map_assoc(array(3600, 10800, 21600, 43200, 86400, 604800), 'format_interval'), + ); + + return system_settings_form($form); +} + +/** + * Submit callback; run cron. + * + * @ingroup forms + */ +function system_run_cron_submit($form, &$form_state) { + // Run cron manually from Cron form. + if (drupal_cron_run()) { + drupal_set_message(t('Cron run successfully.')); + } + else { + drupal_set_message(t('Cron run failed.'), 'error'); + } + + drupal_goto('admin/config/system/cron'); +} + +/** + * Form builder; Configure error reporting settings. + * + * @ingroup forms + * @see system_settings_form() + */ +function system_logging_settings() { + $form['error_level'] = array( + '#type' => 'radios', + '#title' => t('Error messages to display'), + '#default_value' => variable_get('error_level', ERROR_REPORTING_DISPLAY_ALL), + '#options' => array( + ERROR_REPORTING_HIDE => t('None'), + ERROR_REPORTING_DISPLAY_SOME => t('Errors and warnings'), + ERROR_REPORTING_DISPLAY_ALL => t('All messages'), + ), + '#description' => t('It is recommended that sites running on production environments do not display any errors.'), + ); + + return system_settings_form($form); +} + +/** + * Form builder; Configure site performance settings. + * + * @ingroup forms + * @see system_settings_form() + */ +function system_performance_settings() { + drupal_add_js(drupal_get_path('module', 'system') . '/system.js'); + + $form['clear_cache'] = array( + '#type' => 'fieldset', + '#title' => t('Clear cache'), + ); + + $form['clear_cache']['clear'] = array( + '#type' => 'submit', + '#value' => t('Clear all caches'), + '#submit' => array('system_clear_cache_submit'), + ); + + $form['caching'] = array( + '#type' => 'fieldset', + '#title' => t('Caching'), + ); + + $cache = variable_get('cache', 0); + $form['caching']['cache'] = array( + '#type' => 'checkbox', + '#title' => t('Cache pages for anonymous users'), + '#default_value' => $cache, + '#weight' => -2, + ); + $period = drupal_map_assoc(array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400), 'format_interval'); + $period[0] = '<' . t('none') . '>'; + $form['caching']['cache_lifetime'] = array( + '#type' => 'select', + '#title' => t('Minimum cache lifetime'), + '#default_value' => variable_get('cache_lifetime', 0), + '#options' => $period, + '#description' => t('Cached pages will not be re-created until at least this much time has elapsed.') + ); + $form['caching']['page_cache_maximum_age'] = array( + '#type' => 'select', + '#title' => t('Expiration of cached pages'), + '#default_value' => variable_get('page_cache_maximum_age', 0), + '#options' => $period, + '#description' => t('The maximum time an external cache can use an old version of a page.') + ); + + $directory = 'public://'; + $is_writable = is_dir($directory) && is_writable($directory); + $disabled = !$is_writable; + $disabled_message = ''; + if (!$is_writable) { + $disabled_message = ' ' . t('Set up the public files directory to make these optimizations available.', array('!file-system' => url('admin/config/media/file-system'))); + } + + $form['bandwidth_optimization'] = array( + '#type' => 'fieldset', + '#title' => t('Bandwidth optimization'), + '#description' => t('External resources can be optimized automatically, which can reduce both the size and number of requests made to your website.') . $disabled_message, + ); + + $js_hide = $cache ? '' : ' class="js-hide"'; + $form['bandwidth_optimization']['page_compression'] = array( + '#type' => 'checkbox', + '#title' => t('Compress cached pages.'), + '#default_value' => variable_get('page_compression', TRUE), + '#prefix' => 'example.com/user
instead of example.com/?q=user
.'),
+ );
+ $form = system_settings_form($form);
+ if ($conflict) {
+ // $form_state['redirect'] needs to be set to the non-clean URL,
+ // otherwise the setting is not saved.
+ $form_state['redirect'] = url('', array('query' => array('q' => '/admin/config/search/clean-urls')));
+ }
+ }
+ // Show the clean URLs test form.
+ else {
+ drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
+
+ $form_state['redirect'] = url('admin/config/search/clean-urls');
+ $form['clean_url_description'] = array(
+ '#type' => 'markup',
+ '#markup' => '' . t('Use URLs like example.com/user
instead of example.com/?q=user
.'),
+ );
+ // Explain why the user is seeing this page and what to expect after
+ // clicking the 'Run the clean URL test' button.
+ $form['clean_url_test_result'] = array(
+ '#type' => 'markup',
+ '#markup' => '
' . t('Clean URLs cannot be enabled. If you are directed to this page or to a Page not found (404) error after testing for clean URLs, see the online handbook.', array('@handbook' => 'http://drupal.org/node/15365')) . '
', + ); + $form['actions'] = array( + '#type' => 'actions', + 'clean_url_test' => array( + '#type' => 'submit', + '#value' => t('Run the clean URL test'), + ), + ); + $form['clean_url_test_execute'] = array( + '#type' => 'hidden', + '#value' => 1, + ); + } + + return $form; +} + +/** + * Menu callback: displays the site status report. Can also be used as a pure check. + * + * @param $check + * If true, only returns a boolean whether there are system status errors. + */ +function system_status($check = FALSE) { + // Load .install files + include_once DRUPAL_ROOT . '/includes/install.inc'; + drupal_load_updates(); + + // Check run-time requirements and status information. + $requirements = module_invoke_all('requirements', 'runtime'); + usort($requirements, '_system_sort_requirements'); + + if ($check) { + return drupal_requirements_severity($requirements) == REQUIREMENT_ERROR; + } + // MySQL import might have set the uid of the anonymous user to autoincrement + // value. Let's try fixing it. See http://drupal.org/node/204411 + db_update('users') + ->expression('uid', 'uid - uid') + ->condition('name', '') + ->condition('pass', '') + ->condition('status', 0) + ->execute(); + return theme('status_report', array('requirements' => $requirements)); +} + +/** + * Menu callback: run cron manually. + */ +function system_run_cron() { + // Run cron manually + if (drupal_cron_run()) { + drupal_set_message(t('Cron ran successfully.')); + } + else { + drupal_set_message(t('Cron run failed.'), 'error'); + } + + drupal_goto('admin/reports/status'); +} + +/** + * Menu callback: return information about PHP. + */ +function system_php() { + phpinfo(); + drupal_exit(); +} + +/** + * Default page callback for batches. + */ +function system_batch_page() { + require_once DRUPAL_ROOT . '/includes/batch.inc'; + $output = _batch_page(); + + if ($output === FALSE) { + drupal_access_denied(); + } + elseif (isset($output)) { + // Force a page without blocks or messages to + // display a list of collected messages later. + drupal_set_page_content($output); + $page = element_info('page'); + $page['#show_messages'] = FALSE; + return $page; + } +} + +/** + * Returns HTML for an administrative block for display. + * + * @param $variables + * An associative array containing: + * - block: An array containing information about the block: + * - show: A Boolean whether to output the block. Defaults to FALSE. + * - title: The block's title. + * - content: (optional) Formatted content for the block. + * - description: (optional) Description of the block. Only output if + * 'content' is not set. + * + * @ingroup themeable + */ +function theme_admin_block($variables) { + $block = $variables['block']; + $output = ''; + + // Don't display the block if it has no content to display. + if (empty($block['show'])) { + return $output; + } + + $output .= '' . $severity['icon'] . ' | ' . $requirement['title'] . ' | ' . $requirement['value'] . ' |
' . $requirement['description'] . ' | ||
' . $severity['icon'] . ' | ' . $requirement['title'] . ' | ' . $requirement['value'] . ' |