diff --git a/core/includes/theme.inc b/core/includes/theme.inc index ae8ec15..a1253ea 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1508,6 +1508,7 @@ function theme_enable($theme_list) { $disabled_themes->save(); list_themes(TRUE); + Drupal::service('router.builder')->rebuild(); menu_router_rebuild(); drupal_theme_rebuild(); @@ -1543,6 +1544,7 @@ function theme_disable($theme_list) { $disabled_themes->save(); list_themes(TRUE); + Drupal::service('router.builder')->rebuild(); menu_router_rebuild(); drupal_theme_rebuild(); diff --git a/core/lib/Drupal/Core/Controller/HtmlFormController.php b/core/lib/Drupal/Core/Controller/HtmlFormController.php index 2c2fa2c..8e88b53 100644 --- a/core/lib/Drupal/Core/Controller/HtmlFormController.php +++ b/core/lib/Drupal/Core/Controller/HtmlFormController.php @@ -57,10 +57,12 @@ public function content(Request $request, $_form) { $request->attributes->set('form', array()); $request->attributes->set('form_state', $form_state); $args = $this->container->get('controller_resolver')->getArguments($request, array($form_object, 'buildForm')); - unset($args[0], $args[1]); $request->attributes->remove('form'); $request->attributes->remove('form_state'); - $form_state['build_info']['args'] = $args; + + // Remove $form and $form_state from the arguments, and re-index them. + unset($args[0], $args[1]); + $form_state['build_info']['args'] = array_values($args); $form_id = _drupal_form_id($form_object, $form_state); return drupal_build_form($form_id, $form_state); diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockHiddenRegionTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockHiddenRegionTest.php index 1232420..710c53f 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockHiddenRegionTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockHiddenRegionTest.php @@ -64,6 +64,7 @@ public function testBlockNotInHiddenRegion() { config('system.theme') ->set('default', $theme) ->save(); + \Drupal::service('router.builder')->rebuild(); menu_router_rebuild(); // Ensure that "block_test_theme" is set as the default theme. diff --git a/core/modules/color/lib/Drupal/color/Tests/ColorTest.php b/core/modules/color/lib/Drupal/color/Tests/ColorTest.php index 7dcfb1b..13627cc 100644 --- a/core/modules/color/lib/Drupal/color/Tests/ColorTest.php +++ b/core/modules/color/lib/Drupal/color/Tests/ColorTest.php @@ -48,6 +48,8 @@ function setUp() { ), ); theme_enable(array_keys($this->themes)); + $this->container->get('router.builder')->rebuild(); + menu_router_rebuild(); // Array filled with valid and not valid color values $this->colorTests = array( diff --git a/core/modules/system/lib/Drupal/system/Form/ThemeSettingsForm.php b/core/modules/system/lib/Drupal/system/Form/ThemeSettingsForm.php new file mode 100644 index 0000000..903c4f5 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Form/ThemeSettingsForm.php @@ -0,0 +1,455 @@ +moduleHandler = $module_handler; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('config.factory'), + $container->get('config.context.free'), + $container->get('module_handler') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormID() { + return 'system_theme_settings'; + } + + /** + * {@inheritdoc} + * + * @param string $theme_name + * The theme name. + */ + public function buildForm(array $form, array &$form_state, $theme_name = '') { + $form = parent::buildForm($form, $form_state); + + $themes = list_themes(); + + // Deny access if the theme is disabled or not found. + if (!empty($theme_name) && (empty($themes[$theme_name]) || !$themes[$theme_name]->status)) { + throw new NotFoundHttpException(); + } + + // Default settings are defined in theme_get_setting() in includes/theme.inc + if ($theme_name) { + $var = 'theme_' . $theme_name . '_settings'; + $config_key = $theme_name . '.settings'; + $features = $themes[$theme_name]->info['features']; + } + else { + $var = 'theme_settings'; + $config_key = 'system.theme.global'; + } + + $form['var'] = array( + '#type' => 'hidden', + '#value' => $var + ); + $form['config_key'] = array( + '#type' => 'hidden', + '#value' => $config_key + ); + + // 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 (!user_picture_enabled()) { + $disabled['toggle_node_user_picture'] = TRUE; + $disabled['toggle_comment_user_picture'] = TRUE; + } + if (!$this->moduleHandler->moduleExists('comment')) { + $disabled['toggle_comment_user_picture'] = TRUE; + $disabled['toggle_comment_user_verification'] = TRUE; + } + + $form['theme_settings'] = array( + '#type' => 'details', + '#title' => t('Toggle display'), + '#description' => t('Enable or disable the display of certain page elements.'), + ); + foreach ($toggles as $name => $title) { + if ((!$theme_name) || in_array($name, $features)) { + $form['theme_settings']['toggle_' . $name] = array('#type' => 'checkbox', '#title' => $title, '#default_value' => theme_get_setting('features.' . $name, $theme_name)); + // 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 details then do not show + // it -- but keep it in the form if another module wants to alter. + $form['theme_settings']['#access'] = FALSE; + } + + // Logo settings, only available when file.module is enabled. + if ((!$theme_name) || in_array('logo', $features) && $this->moduleHandler->moduleExists('file')) { + $form['logo'] = array( + '#type' => 'details', + '#title' => t('Logo image settings'), + '#attributes' => array('class' => array('theme-settings-bottom')), + '#states' => array( + // Hide the logo image settings fieldset when logo display is disabled. + 'invisible' => array( + 'input[name="toggle_logo"]' => array('checked' => FALSE), + ), + ), + ); + $form['logo']['default_logo'] = array( + '#type' => 'checkbox', + '#title' => t('Use the default logo supplied by the theme'), + '#default_value' => theme_get_setting('logo.use_default', $theme_name), + '#tree' => FALSE, + ); + $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'), + '#default_value' => theme_get_setting('logo.path', $theme_name), + ); + $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 ((!$theme_name) || in_array('favicon', $features) && $this->moduleHandler->moduleExists('file')) { + $form['favicon'] = array( + '#type' => 'details', + '#title' => t('Shortcut icon settings'), + '#description' => t("Your shortcut icon, or 'favicon', is displayed in the address bar and bookmarks of most browsers."), + '#states' => array( + // Hide the shortcut icon settings fieldset when shortcut icon display + // is disabled. + 'invisible' => array( + 'input[name="toggle_favicon"]' => array('checked' => FALSE), + ), + ), + ); + $form['favicon']['default_favicon'] = array( + '#type' => 'checkbox', + '#title' => t('Use the default shortcut icon supplied by the theme'), + '#default_value' => theme_get_setting('favicon.use_default', $theme_name), + ); + $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'), + '#default_value' => theme_get_setting('favicon.path', $theme_name), + ); + $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 and form element descriptions 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; + } + + // Prepare local file path for description. + if ($original_path && isset($friendly_path)) { + $local_file = strtr($original_path, array('public:/' => variable_get('file_public_path', conf_path() . '/files'))); + } + elseif ($theme_name) { + $local_file = drupal_get_path('theme', $theme_name) . '/' . $default; + } + else { + $local_file = path_to_theme() . '/' . $default; + } + + $element['#description'] = t('Examples: @implicit-public-file (for a file in the public filesystem), @explicit-file, or @local-file.', array( + '@implicit-public-file' => isset($friendly_path) ? $friendly_path : $default, + '@explicit-file' => file_uri_scheme($original_path) !== FALSE ? $original_path : 'public://' . $default, + '@local-file' => $local_file, + )); + } + } + + if ($theme_name) { + // Call engine-specific settings. + $function = $themes[$theme_name]->prefix . '_engine_settings'; + if (function_exists($function)) { + $form['engine_specific'] = array( + '#type' => 'details', + '#title' => t('Theme-engine-specific settings'), + '#description' => t('These settings only exist for the themes based on the %engine theme engine.', array('%engine' => $themes[$theme_name]->prefix)), + ); + $function($form, $form_state); + } + + // 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); + } + + // 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'] = $theme_name; + + // 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.yml", '', $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']); + } + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, array &$form_state) { + parent::validateForm($form, $form_state); + + if ($this->moduleHandler->moduleExists('file')) { + // Handle file uploads. + $validators = array('file_validate_is_image' => array()); + + // Check for a new uploaded logo. + $file = file_save_upload('logo_upload', $validators, FALSE, 0); + 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, FALSE, 0); + 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 ($form_state['values']['logo_path']) { + $path = $this->validatePath($form_state['values']['logo_path']); + if (!$path) { + form_set_error('logo_path', t('The custom logo path is invalid.')); + } + } + if ($form_state['values']['favicon_path']) { + $path = $this->validatePath($form_state['values']['favicon_path']); + if (!$path) { + form_set_error('favicon_path', t('The custom favicon path is invalid.')); + } + } + } + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, array &$form_state) { + parent::submitForm($form, $form_state); + + $config = $this->configFactory->get($form_state['values']['config_key']); + + // Exclude unnecessary elements before saving. + form_state_values_clean($form_state); + unset($form_state['values']['var']); + unset($form_state['values']['config_key']); + + $values = $form_state['values']; + + // 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 ($this->moduleHandler->moduleExists('file')) { + if ($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 ($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'] = $this->validatePath($values['logo_path']); + } + if (!empty($values['favicon_path'])) { + $values['favicon_path'] = $this->validatePath($values['favicon_path']); + } + + if (empty($values['default_favicon']) && !empty($values['favicon_path'])) { + $values['favicon_mimetype'] = file_get_mimetype($values['favicon_path']); + } + } + + theme_settings_convert_to_config($values, $config)->save(); + + Cache::invalidateTags(array('content' => TRUE)); + } + + /** + * 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 string $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. + */ + protected function validatePath($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; + } + +} diff --git a/core/modules/system/lib/Drupal/system/Routing/RouteSubscriber.php b/core/modules/system/lib/Drupal/system/Routing/RouteSubscriber.php new file mode 100644 index 0000000..616955c --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Routing/RouteSubscriber.php @@ -0,0 +1,47 @@ +getRouteCollection(); + foreach (list_themes() as $theme) { + if (!empty($theme->status)) { + $route = new Route('admin/appearance/settings/' . $theme->name, array( + '_form' => '\Drupal\system\Form\ThemeSettingsForm', 'theme_name' => $theme->name), array( + '_permission' => 'administer themes', + )); + $collection->add('system_theme_settings_' . $theme->name, $route); + } + } + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php index b6731ac..afb7e32 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php @@ -338,6 +338,7 @@ function testMenuName() { // Change the menu_name parameter in menu_test.module, then force a menu // rebuild. menu_test_menu_name('changed'); + \Drupal::service('router.builder')->rebuild(); menu_router_rebuild(); $menu_links = entity_load_multiple_by_properties('menu_link', array('router_path' => 'menu_name_test')); diff --git a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php index f636849..d9e2adb 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php @@ -231,6 +231,8 @@ function testSwitchDefaultTheme() { $this->clickLink(t('Set default')); $this->assertEqual(config('system.theme')->get('default'), 'bartik'); + drupal_flush_all_caches(); + // Test the default theme on the secondary links (blocks admin page). $this->drupalGet('admin/structure/block'); $this->assertText('Bartik(' . t('active tab') . ')', 'Default local task on blocks admin page is the default theme.'); diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index 1cfea0c..1e4c13c 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -305,385 +305,6 @@ function system_theme_default() { } /** - * 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_validate() - * @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'; - $config_key = $key . '.settings'; - $themes = list_themes(); - $features = $themes[$key]->info['features']; - } - else { - $var = 'theme_settings'; - $config_key = 'system.theme.global'; - } - - $form['var'] = array('#type' => 'hidden', '#value' => $var); - $form['config_key'] = array('#type' => 'hidden', '#value' => $config_key); - - // 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 (!user_picture_enabled()) { - $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' => 'details', - '#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('features.' . $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 details then do not show - // it -- but keep it in the form if another module wants to alter. - $form['theme_settings']['#access'] = FALSE; - } - - // Logo settings, only available when file.module is enabled. - if ((!$key) || in_array('logo', $features) && module_exists('file')) { - $form['logo'] = array( - '#type' => 'details', - '#title' => t('Logo image settings'), - '#attributes' => array('class' => array('theme-settings-bottom')), - '#states' => array( - // Hide the logo image settings fieldset when logo display is disabled. - 'invisible' => array( - 'input[name="toggle_logo"]' => array('checked' => FALSE), - ), - ), - ); - $form['logo']['default_logo'] = array( - '#type' => 'checkbox', - '#title' => t('Use the default logo supplied by the theme'), - '#default_value' => theme_get_setting('logo.use_default', $key), - '#tree' => FALSE, - ); - $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'), - '#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) && module_exists('file')) { - $form['favicon'] = array( - '#type' => 'details', - '#title' => t('Shortcut icon settings'), - '#description' => t("Your shortcut icon, or 'favicon', is displayed in the address bar and bookmarks of most browsers."), - '#states' => array( - // Hide the shortcut icon settings fieldset when shortcut icon display - // is disabled. - 'invisible' => array( - 'input[name="toggle_favicon"]' => array('checked' => FALSE), - ), - ), - ); - $form['favicon']['default_favicon'] = array( - '#type' => 'checkbox', - '#title' => t('Use the default shortcut icon supplied by the theme'), - '#default_value' => theme_get_setting('favicon.use_default', $key), - ); - $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'), - '#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 and form element descriptions 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; - } - - // Prepare local file path for description. - if ($original_path && isset($friendly_path)) { - $local_file = strtr($original_path, array('public:/' => variable_get('file_public_path', conf_path() . '/files'))); - } - elseif ($key) { - $local_file = drupal_get_path('theme', $key) . '/' . $default; - } - else { - $local_file = path_to_theme() . '/' . $default; - } - - $element['#description'] = t('Examples: @implicit-public-file (for a file in the public filesystem), @explicit-file, or @local-file.', array( - '@implicit-public-file' => isset($friendly_path) ? $friendly_path : $default, - '@explicit-file' => file_uri_scheme($original_path) !== FALSE ? $original_path : 'public://' . $default, - '@local-file' => $local_file, - )); - } - } - - if ($key) { - // Call engine-specific settings. - $function = $themes[$key]->prefix . '_engine_settings'; - if (function_exists($function)) { - $form['engine_specific'] = array( - '#type' => 'details', - '#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.yml", '', $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']); - } - } - - return system_config_form($form, $form_state); -} - -/** - * Validator for the system_theme_settings() form. - */ -function system_theme_settings_validate($form, &$form_state) { - if (module_exists('file')) { - // Handle file uploads. - $validators = array('file_validate_is_image' => array()); - - // Check for a new uploaded logo. - $file = file_save_upload('logo_upload', $validators, FALSE, 0); - 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, FALSE, 0); - 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 ($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 ($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) { - $config = Drupal::config($form_state['values']['config_key']); - - // Exclude unnecessary elements before saving. - form_state_values_clean($form_state); - $key = $form_state['values']['var']; - unset($form_state['values']['var']); - unset($form_state['values']['config_key']); - - $values = $form_state['values']; - - // 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 (module_exists('file')) { - if ($file = $values['logo_upload']) { - unset($values['logo_upload']); - $filename = file_unmanaged_copy($file->getFileUri()); - $values['default_logo'] = 0; - $values['logo_path'] = $filename; - $values['toggle_logo'] = 1; - } - if ($file = $values['favicon_upload']) { - unset($values['favicon_upload']); - $filename = file_unmanaged_copy($file->getFileUri()); - $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']); - } - } - - theme_settings_convert_to_config($values, $config)->save(); - - cache_invalidate_tags(array('content' => TRUE)); -} - -/** * Recursively check compatibility. * * @param $incompatible diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 6a1f766..11db292 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -709,28 +709,24 @@ function system_menu() { $items['admin/appearance/settings'] = array( 'title' => 'Settings', 'description' => 'Configure default and theme specific settings.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('system_theme_settings'), - 'access arguments' => array('administer themes'), + 'route_name' => 'system_theme_settings', 'type' => MENU_LOCAL_TASK, - 'file' => 'system.admin.inc', 'weight' => 100, ); - // Theme configuration subtabs. + // Theme configuration local tasks. $items['admin/appearance/settings/global'] = array( 'title' => 'Global settings', 'type' => MENU_DEFAULT_LOCAL_TASK, ); - foreach (list_themes() as $key => $theme) { - $items['admin/appearance/settings/' . $theme->name] = array( - 'title' => $theme->info['name'], - 'page arguments' => array('system_theme_settings', $theme->name), - 'type' => MENU_LOCAL_TASK, - 'access callback' => '_system_themes_access', - 'access arguments' => array($key), - 'file' => 'system.admin.inc', - ); + foreach (list_themes(TRUE) as $theme) { + if (!empty($theme->status)) { + $items['admin/appearance/settings/' . $theme->name] = array( + 'title' => $theme->info['name'], + 'route_name' => 'system_theme_settings_' . $theme->name, + 'type' => MENU_LOCAL_TASK, + ); + } } // Modules. diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml index 0ba8674..45ce1e5 100644 --- a/core/modules/system/system.routing.yml +++ b/core/modules/system/system.routing.yml @@ -143,3 +143,17 @@ system_admin_index: _content: 'Drupal\system\Controller\AdminController::index' requirements: _permission: 'access administration pages' + +system_theme_settings: + pattern: '/admin/appearance/settings' + defaults: + _form: '\Drupal\system\Form\ThemeSettingsForm' + requirements: + _permission: 'administer themes' + +system_theme_settings_global: + pattern: '/admin/appearance/settings/global' + defaults: + _form: '\Drupal\system\Form\ThemeSettingsForm' + requirements: + _permission: 'administer themes' diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml index 8f0d232..0fad9cd 100644 --- a/core/modules/system/system.services.yml +++ b/core/modules/system/system.services.yml @@ -9,3 +9,7 @@ services: system.manager: class: Drupal\system\SystemManager arguments: ['@module_handler', '@database'] + system.route_subscriber: + class: Drupal\system\Routing\RouteSubscriber + tags: + - { name: event_subscriber }