Newer
Older
/**
* @file
* Configuration system that lets administrators modify the workings of the site.
*/
Alex Bronstein
committed
use Drupal\Component\Render\PlainTextOutput;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Asset\AttachedAssetsInterface;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Queue\QueueGarbageCollectionInterface;
Alex Pott
committed
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Extension\Extension;
catch
committed
use Drupal\Core\Extension\ExtensionDiscovery;
Dries Buytaert
committed
use Drupal\Core\Form\FormStateInterface;
Alex Pott
committed
use Drupal\Core\KeyValueStore\KeyValueDatabaseExpirableFactory;
use Drupal\Core\PageCache\RequestPolicyInterface;
catch
committed
use Drupal\Core\PhpStorage\PhpStorageFactory;
Angie Byron
committed
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Routing\StackedRouteMatchInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Menu\MenuTreeParameters;
catch
committed
use Drupal\Core\Extension\ModuleHandler;
use Drupal\Core\Url;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\user\UserInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use GuzzleHttp\Exception\RequestException;
Angie Byron
committed
/**
* New users will be set to the default time zone at registration.
*
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
* Use \Drupal\user\UserInterface::TIMEZONE_DEFAULT instead.
Gabor Hojtsy
committed
*
* @see https://www.drupal.org/node/2831620
Angie Byron
committed
*/
const DRUPAL_USER_TIMEZONE_DEFAULT = 0;
Angie Byron
committed
/**
* New users will get an empty time zone at registration.
*
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
* Use \Drupal\user\UserInterface::TIMEZONE_EMPTY instead.
Gabor Hojtsy
committed
*
* @see https://www.drupal.org/node/2831620
Angie Byron
committed
*/
const DRUPAL_USER_TIMEZONE_EMPTY = 1;
Angie Byron
committed
/**
* New users will select their own timezone at registration.
*
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
* Use \Drupal\user\UserInterface::TIMEZONE_SELECT instead.
Gabor Hojtsy
committed
*
* @see https://www.drupal.org/node/2831620
Angie Byron
committed
*/
const DRUPAL_USER_TIMEZONE_SELECT = 2;
Angie Byron
committed
Dries Buytaert
committed
/**
Dries Buytaert
committed
* Disabled option on forms and settings
*/
const DRUPAL_DISABLED = 0;
Dries Buytaert
committed
/**
* Optional option on forms and settings
*/
const DRUPAL_OPTIONAL = 1;
Dries Buytaert
committed
/**
* Required option on forms and settings
*/
const DRUPAL_REQUIRED = 2;
Dries Buytaert
committed
Dries Buytaert
committed
/**
* Return only visible regions.
Dries Buytaert
committed
*
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
* Use \Drupal\block\BlockRepositoryInterface::REGIONS_VISIBLE instead.
*
Dries Buytaert
committed
* @see system_region_list()
Gabor Hojtsy
committed
* @see https://www.drupal.org/node/2831620
Dries Buytaert
committed
*/
const REGIONS_VISIBLE = 'visible';
Dries Buytaert
committed
/**
* Return all regions.
Dries Buytaert
committed
*
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
* Use \Drupal\block\BlockRepositoryInterface::REGIONS_ALL instead.
*
Dries Buytaert
committed
* @see system_region_list()
Gabor Hojtsy
committed
* @see https://www.drupal.org/node/2831620
Dries Buytaert
committed
*/
const REGIONS_ALL = 'all';
Dries Buytaert
committed
Dries Buytaert
committed
* Implements hook_help().
Angie Byron
committed
function system_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.system':
Dries Buytaert
committed
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The System module is integral to the site: it provides user interfaces for many core systems and settings, as well as the basic administrative menu structure. For more information, see the <a href=":system">online documentation for the System module</a>.', [':system' => 'https://www.drupal.org/documentation/modules/system']) . '</p>';
Dries Buytaert
committed
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Managing modules') . '</dt>';
$output .= '<dd>' . t('Users with appropriate permission can install and uninstall modules from the <a href=":modules">Extend page</a>. Depending on which distribution or installation profile you choose when you install your site, several modules are installed and others are provided but not installed. Each module provides a discrete set of features; modules may be installed or uninstalled depending on the needs of the site. Many additional modules contributed by members of the Drupal community are available for download from the <a href=":drupal-modules">Drupal.org module page</a>. Note that uninstalling a module is a destructive action: when you uninstall a module, you will permanently lose all data connected to the module.', [':modules' => \Drupal::url('system.modules_list'), ':drupal-modules' => 'https://www.drupal.org/project/modules']) . '</dd>';
Dries Buytaert
committed
$output .= '<dt>' . t('Managing themes') . '</dt>';
$output .= '<dd>' . t('Users with appropriate permission can install and uninstall themes on the <a href=":themes">Appearance page</a>. Themes determine the design and presentation of your site. Depending on which distribution or installation profile you choose when you install your site, a default theme is installed, and possibly a different theme for administration pages. Other themes are provided but not installed, and additional contributed themes are available at the <a href=":drupal-themes">Drupal.org theme page</a>.', [':themes' => \Drupal::url('system.themes_page'), ':drupal-themes' => 'https://www.drupal.org/project/themes']) . '</dd>';
Angie Byron
committed
$output .= '<dt>' . t('Disabling drag-and-drop functionality') . '</dt>';
Angie Byron
committed
$output .= '<dd>' . t('The default drag-and-drop user interface for ordering tables in the administrative interface presents a challenge for some users, including users of screen readers and other assistive technology. The drag-and-drop interface can be disabled in a table by clicking a link labeled "Show row weights" above the table. The replacement interface allows users to order the table by choosing numerical weights instead of dragging table rows.') . '</dd>';
$output .= '<dt>' . t('Configuring basic site settings') . '</dt>';
$output .= '<dd>' . t('The System module provides pages for managing basic site configuration, including <a href=":date-time-settings">Date and time formats</a> and <a href=":site-info">Basic site settings</a> (site name, email address to send mail from, home page, and error pages). Additional configuration pages are listed on the main <a href=":config">Configuration page</a>.', [':date-time-settings' => \Drupal::url('entity.date_format.collection'), ':site-info' => \Drupal::url('system.site_information_settings'), ':config' => \Drupal::url('system.admin_config')]) . '</dd>';
Angie Byron
committed
$output .= '<dt>' . t('Checking site status') . '</dt>';
$output .= '<dd>' . t('The <a href=":status">Status report</a> provides an overview of the configuration, status, and health of your site. Review this report to make sure there are not any problems to address, and to find information about the software your site and web server are using.', [':status' => \Drupal::url('system.status')]) . '</dd>';
Angie Byron
committed
$output .= '<dt>' . t('Using maintenance mode') . '</dt>';
$output .= '<dd>' . t('When you are performing site maintenance, you can prevent non-administrative users (including anonymous visitors) from viewing your site by putting it in <a href=":maintenance-mode">Maintenance mode</a>. This will prevent unauthorized users from making changes to the site while you are performing maintenance, or from seeing a broken site while updates are in progress.', [':maintenance-mode' => \Drupal::url('system.site_maintenance_mode')]) . '</dd>';
$output .= '<dt>' . t('Configuring for performance') . '</dt>';
$output .= '<dd>' . t('On the <a href=":performance-page">Performance page</a>, the site can be configured to aggregate CSS and JavaScript files, making the total request size smaller. Note that, for small- to medium-sized websites, the <a href=":page-cache">Internal Page Cache module</a> should be installed so that pages are efficiently cached and reused for anonymous users. Finally, for websites of all sizes, the <a href=":dynamic-page-cache">Dynamic Page Cache module</a> should also be installed so that the non-personalized parts of pages are efficiently cached (for all users).', [':performance-page' => \Drupal::url('system.performance_settings'), ':page-cache' => (\Drupal::moduleHandler()->moduleExists('page_cache')) ? \Drupal::url('help.page', ['name' => 'page_cache']) : '#', ':dynamic-page-cache' => (\Drupal::moduleHandler()->moduleExists('dynamic_page_cache')) ? \Drupal::url('help.page', ['name' => 'dynamic_page_cache']) : '#']) . '</dd>';
Angie Byron
committed
$output .= '<dt>' . t('Configuring cron') . '</dt>';
$output .= '<dd>' . t('In order for the site and its modules to continue to operate well, a set of routine administrative operations must run on a regular basis; these operations are known as <em>cron</em> tasks. On the <a href=":cron">Cron page</a>, you can configure cron to run periodically as part of server responses by installing the <em>Automated Cron</em> module, or you can turn this off and trigger cron from an outside process on your web server. You can verify the status of cron tasks by visiting the <a href=":status">Status report page</a>. For more information, see the <a href=":handbook">online documentation for configuring cron jobs</a>.', [':status' => \Drupal::url('system.status'), ':handbook' => 'https://www.drupal.org/cron', ':cron' => \Drupal::url('system.cron_settings')]) . '</dd>';
Angie Byron
committed
$output .= '<dt>' . t('Configuring the file system') . '</dt>';
$output .= '<dd>' . t('Your site has several file directories, which are used to store and process uploaded and generated files. The <em>public</em> file directory, which is configured in your settings.php file, is the default place for storing uploaded files. Links to files in this directory contain the direct file URL, so when the files are requested, the web server will send them directly without invoking your site code. This means that the files can be downloaded by anyone with the file URL, so requests are not access-controlled but they are efficient. The <em>private</em> file directory, also configured in your settings.php file and ideally located outside the site web root, is access controlled. Links to files in this directory are not direct, so requests to these files are mediated by your site code. This means that your site can check file access permission for each file before deciding to fulfill the request, so the requests are more secure, but less efficient. You should only use the private storage for files that need access control, not for files like your site logo and background images used on every page. The <em>temporary</em> file directory is used internally by your site code for various operations, and is configured on the <a href=":file-system">File system settings</a> page. You can also see the configured public and private file directories on this page, and choose whether public or private should be the default for uploaded files.', [':file-system' => \Drupal::url('system.file_system_settings')]) . '</dd>';
Angie Byron
committed
$output .= '<dt>' . t('Configuring the image toolkit') . '</dt>';
$output .= '<dd>' . t('On the <a href=":toolkit">Image toolkit page</a>, you can select and configure the PHP toolkit used to manipulate images. Depending on which distribution or installation profile you choose when you install your site, the GD2 toolkit and possibly others are included; other toolkits may be provided by contributed modules.', [':toolkit' => \Drupal::url('system.image_toolkit_settings')]) . '</dd>';
$output .= '</dl>';
Dries Buytaert
committed
return $output;
case 'system.admin_index':
return '<p>' . t('This page shows you all available administration tasks for each module.') . '</p>';
case 'system.themes_page':
$output = '<p>' . t('Set and configure the default theme for your website. Alternative <a href=":themes">themes</a> are available.', [':themes' => 'https://www.drupal.org/project/themes']) . '</p>';
if (\Drupal::moduleHandler()->moduleExists('block')) {
$output .= '<p>' . t('You can place blocks for each theme on the <a href=":blocks">block layout</a> page.', [':blocks' => \Drupal::url('block.admin_display')]) . '</p>';
}
return $output;
case 'system.theme_settings_theme':
$theme_list = \Drupal::service('theme_handler')->listInfo();
Angie Byron
committed
$theme = $theme_list[$route_match->getParameter('theme')];
return '<p>' . t('These options control the display settings for the %name theme. When your site is displayed using this theme, these settings will be used.', ['%name' => $theme->info['name']]) . '</p>';
case 'system.theme_settings':
return '<p>' . t('Control default display settings for your site, across all themes. Use theme-specific settings to override these defaults.') . '</p>';
case 'system.modules_list':
$output = '<p>' . t('Download additional <a href=":modules">contributed modules</a> to extend your site\'s functionality.', [':modules' => 'https://www.drupal.org/project/modules']) . '</p>';
if (!\Drupal::moduleHandler()->moduleExists('update')) {
$output .= '<p>' . t('Regularly review available updates to maintain a secure and current site. Always run the <a href=":update-php">update script</a> each time a module is updated. Enable the <a href=":update-manager">Update Manager module</a> to update and install modules and themes.', [':update-php' => \Drupal::url('system.db_update'), ':update-manager' => \Drupal::url('system.modules_list', [], ['fragment' => 'module-update'])]) . '</p>';
Dries Buytaert
committed
}
Dries Buytaert
committed
return $output;
case 'system.modules_uninstall':
Dries Buytaert
committed
return '<p>' . t('The uninstall process removes all data related to a module.') . '</p>';
case 'entity.block.edit_form':
if (($block = $route_match->getParameter('block')) && $block->getPluginId() == 'system_powered_by_block') {
return '<p>' . t('The <em>Powered by Drupal</em> block is an optional link to the home page of the Drupal project. While there is absolutely no requirement that sites feature this link, it may be used to show support for Drupal.') . '</p>';
Gábor Hojtsy
committed
}
Gábor Hojtsy
committed
break;
case 'block.admin_add':
Angie Byron
committed
if ($route_match->getParameter('plugin_id') == 'system_powered_by_block') {
return '<p>' . t('The <em>Powered by Drupal</em> block is an optional link to the home page of the Drupal project. While there is absolutely no requirement that sites feature this link, it may be used to show support for Drupal.') . '</p>';
}
break;
case 'system.site_maintenance_mode':
if (\Drupal::currentUser()->id() == 1) {
return '<p>' . t('Use maintenance mode when making major updates, particularly if the updates could disrupt visitors or the update process. Examples include upgrading, importing or exporting content, modifying a theme, modifying content types, and making backups.') . '</p>';
Dries Buytaert
committed
}
Angie Byron
committed
break;
case 'system.status':
return '<p>' . t("Here you can find a short overview of your site's parameters as well as any problems detected with your installation. It may be useful to copy and paste this information into support requests filed on Drupal.org's support forums and project issue queues. Before filing a support request, ensure that your web server meets the <a href=\":system-requirements\">system requirements.</a>", [':system-requirements' => 'https://www.drupal.org/requirements']) . '</p>';
Dries Buytaert
committed
}
/**
Dries Buytaert
committed
* Implements hook_theme().
*/
Dries Buytaert
committed
function system_theme() {
return array_merge(drupal_common_theme(), [
Angie Byron
committed
// Normally theme suggestion templates are only picked up when they are in
// themes. We explicitly define theme suggestions here so that the block
// templates in core/modules/system/templates are picked up.
'block__system_branding_block' => [
'render element' => 'elements',
Angie Byron
committed
'base hook' => 'block',
],
'block__system_messages_block' => [
'base hook' => 'block',
],
'block__system_menu_block' => [
'render element' => 'elements',
'base hook' => 'block',
],
'system_themes_page' => [
'variables' => [
'theme_groups' => [],
'theme_group_titles' => [],
],
'file' => 'system.admin.inc',
],
'system_config_form' => [
'render element' => 'form',
],
'confirm_form' => [
'render element' => 'form',
],
'system_modules_details' => [
'render element' => 'form',
'file' => 'system.admin.inc',
],
'system_modules_uninstall' => [
'render element' => 'form',
'file' => 'system.admin.inc',
],
'status_report_page' => [
'variables' => [
'counters' => [],
'general_info' => [],
'requirements' => NULL,
],
],
'status_report' => [
'variables' => [
'grouped_requirements' => NULL,
'requirements' => NULL,
],
],
'status_report_grouped' => [
'variables' => [
'grouped_requirements' => NULL,
'requirements' => NULL,
],
],
'status_report_counter' => [
'variables' => ['amount' => NULL, 'text' => NULL, 'severity' => NULL],
],
'status_report_general_info' => [
'variables' => [
'drupal' => [],
'cron' => [],
'database_system' => [],
'database_system_version' => [],
'php' => [],
'php_memory_limit' => [],
'webserver' => [],
],
],
'admin_page' => [
'variables' => ['blocks' => NULL],
'file' => 'system.admin.inc',
],
'admin_block' => [
'variables' => ['block' => NULL, 'attributes' => []],
'file' => 'system.admin.inc',
],
'admin_block_content' => [
'variables' => ['content' => NULL],
'file' => 'system.admin.inc',
],
'system_admin_index' => [
'variables' => ['menu_items' => NULL],
'file' => 'system.admin.inc',
],
'entity_add_list' => [
'variables' => [
'bundles' => [],
'add_bundle_message' => NULL,
],
'template' => 'entity-add-list',
],
Lee Rowlands
committed
'off_canvas_page_wrapper' => [
'variables' => ['children' => NULL],
],
]);
Dries Buytaert
committed
}
Dries Buytaert
committed
* Implements hook_hook_info().
*/
function system_hook_info() {
$hooks['token_info'] = [
'group' => 'tokens',
];
$hooks['token_info_alter'] = [
'group' => 'tokens',
];
$hooks['tokens'] = [
'group' => 'tokens',
];
$hooks['tokens_alter'] = [
Angie Byron
committed
'group' => 'tokens',
];
Angie Byron
committed
return $hooks;
}
/**
* Implements hook_theme_suggestions_HOOK().
*/
function system_theme_suggestions_html(array $variables) {
$path_args = explode('/', trim(\Drupal::service('path.current')->getPath(), '/'));
return theme_get_suggestions($path_args, 'html');
}
/**
* Implements hook_theme_suggestions_HOOK().
*/
function system_theme_suggestions_page(array $variables) {
$path_args = explode('/', trim(\Drupal::service('path.current')->getPath(), '/'));
return theme_get_suggestions($path_args, 'page');
}
/**
* Implements hook_theme_suggestions_HOOK().
*/
function system_theme_suggestions_maintenance_page(array $variables) {
$suggestions = [];
// Dead databases will show error messages so supplying this template will
// allow themers to override the page and the content completely.
$offline = defined('MAINTENANCE_MODE');
try {
\Drupal::service('path.matcher')->isFrontPage();
}
catch (Exception $e) {
// The database is not yet available.
$offline = TRUE;
}
if ($offline) {
$suggestions[] = 'maintenance_page__offline';
}
return $suggestions;
}
/**
* Implements hook_theme_suggestions_HOOK().
*/
function system_theme_suggestions_region(array $variables) {
$suggestions = [];
if (!empty($variables['elements']['#region'])) {
$suggestions[] = 'region__' . $variables['elements']['#region'];
}
return $suggestions;
}
/**
* Implements hook_theme_suggestions_HOOK().
*/
function system_theme_suggestions_field(array $variables) {
$suggestions = [];
$element = $variables['element'];
$suggestions[] = 'field__' . $element['#field_type'];
$suggestions[] = 'field__' . $element['#field_name'];
$suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#bundle'];
$suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name'];
$suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name'] . '__' . $element['#bundle'];
return $suggestions;
}
/**
* Prepares variables for the list of available bundles.
*
* Default template: entity-add-list.html.twig.
*
* @param array $variables
* An associative array containing:
* - bundles: An array of bundles with the label, description, add_link keys.
* - add_bundle_message: The message shown when there are no bundles. Only
* available if the entity type uses bundle entities.
*/
function template_preprocess_entity_add_list(&$variables) {
foreach ($variables['bundles'] as $bundle_name => $bundle_info) {
$variables['bundles'][$bundle_name]['description'] = [
'#markup' => $bundle_info['description'],
];
}
}
/**
Angie Byron
committed
* @defgroup authorize Authorized operations
* @{
* Functions to run operations with elevated privileges via authorize.php.
*
Angie Byron
committed
* Because of the Update manager functionality included in Drupal core, there
* is a mechanism for running operations with elevated file system privileges,
* the top-level authorize.php script. This script runs at a reduced Drupal
* bootstrap level so that it is not reliant on the entire site being
* functional. The operations use a FileTransfer class to manipulate code
* installed on the system as the user that owns the files, not the user that
* the httpd is running as.
*
* The first setup is to define a callback function that should be authorized
* to run with the elevated privileges. This callback should take a
* FileTransfer as its first argument, although you can define an array of
* other arguments it should be invoked with. The callback should be placed in
* a separate .inc file that will be included by authorize.php.
*
* To run the operation, certain data must be saved into the SESSION, and then
* the flow of control should be redirected to the authorize.php script. There
* are two ways to do this, either to call system_authorized_run() directly,
* or to call system_authorized_init() and then redirect to authorize.php,
* using the URL from system_authorized_get_url(). Redirecting yourself is
* necessary when your authorized operation is being triggered by a form
* submit handler, since calling redirecting in a submit handler is a bad
* idea, and you should instead use $form_state->setRedirect().
Angie Byron
committed
*
* Once the SESSION is setup for the operation and the user is redirected to
* authorize.php, they will be prompted for their connection credentials (core
* provides FTP and SSH by default, although other connection classes can be
* added via contributed modules). With valid credentials, authorize.php will
* instantiate the appropriate FileTransfer object, and then invoke the
* desired operation passing in that object. The authorize.php script can act
* as a Batch API processing page, if the operation requires a batch.
*
* @see authorize.php
Alex Pott
committed
* @see \Drupal\Core\FileTransfer\FileTransfer
* @see hook_filetransfer_info()
Angie Byron
committed
*/
/**
* Setup a given callback to run via authorize.php with elevated privileges.
*
Jennifer Hodgdon
committed
* To use authorize.php, certain variables must be stashed into $_SESSION. This
* function sets up all the necessary $_SESSION variables. The calling function
* should then redirect to authorize.php, using the full path returned by
* system_authorized_get_url(). That initiates the workflow that will eventually
* lead to the callback being invoked. The callback will be invoked at a low
* bootstrap level, without all modules being invoked, so it needs to be careful
* not to assume any code exists. Example (system_authorized_run()):
* @code
* system_authorized_init($callback, $file, $arguments, $page_title);
* return new RedirectResponse(system_authorized_get_url()->toString());
Jennifer Hodgdon
committed
* @endcode
* Example (update_manager_install_form_submit()):
* @code
* system_authorized_init('update_authorize_run_install',
* drupal_get_path('module', 'update') . '/update.authorize.inc',
* $arguments, t('Update manager'));
* $form_state->setRedirectUrl(system_authorized_get_url());
Jennifer Hodgdon
committed
* @endcode
*
* @param $callback
Jennifer Hodgdon
committed
* The name of the function to invoke once the user authorizes the operation.
* @param $file
* The full path to the file where the callback function is implemented.
* @param $arguments
* Optional array of arguments to pass into the callback when it is invoked.
* Note that the first argument to the callback is always the FileTransfer
* object created by authorize.php when the user authorizes the operation.
* @param $page_title
* Optional string to use as the page title once redirected to authorize.php.
* @return
Angie Byron
committed
* Nothing, this function just initializes variables in the user's session.
*/
function system_authorized_init($callback, $file, $arguments = [], $page_title = NULL) {
// First, figure out what file transfer backends the site supports, and put
// all of those in the SESSION so that authorize.php has access to all of
// them via the class autoloader, even without a full bootstrap.
$_SESSION['authorize_filetransfer_info'] = drupal_get_filetransfer_info();
// Now, define the callback to invoke.
$_SESSION['authorize_operation'] = [
'callback' => $callback,
'file' => $file,
'arguments' => $arguments,
];
if (isset($page_title)) {
Angie Byron
committed
$_SESSION['authorize_page_title'] = $page_title;
}
Angie Byron
committed
}
Angie Byron
committed
/**
* Return the URL for the authorize.php script.
Angie Byron
committed
*
* @param array $options
* Optional array of options to set on the \Drupal\Core\Url object.
* @return \Drupal\Core\Url
Angie Byron
committed
* The full URL to authorize.php, using HTTPS if available.
Jennifer Hodgdon
committed
*
* @see system_authorized_init()
Angie Byron
committed
*/
function system_authorized_get_url(array $options = []) {
Angie Byron
committed
// core/authorize.php is an unrouted URL, so using the base: scheme is
// the correct usage for this case.
Angie Byron
committed
$url = Url::fromUri('base:core/authorize.php');
$url_options = $url->getOptions();
$url->setOptions($options + $url_options);
return $url;
}
Angie Byron
committed
/**
* Returns the URL for the authorize.php script when it is processing a batch.
*
* @param array $options
* Optional array of options to set on the \Drupal\Core\Url object.
*
* @return \Drupal\Core\Url
Angie Byron
committed
*/
function system_authorized_batch_processing_url(array $options = []) {
$options['query'] = ['batch' => '1'];
return system_authorized_get_url($options);
Angie Byron
committed
}
Angie Byron
committed
/**
* Setup and invoke an operation using authorize.php.
*
Dries Buytaert
committed
* @see system_authorized_init()
Angie Byron
committed
*/
function system_authorized_run($callback, $file, $arguments = [], $page_title = NULL) {
Angie Byron
committed
system_authorized_init($callback, $file, $arguments, $page_title);
return new RedirectResponse(system_authorized_get_url()->toString());
Angie Byron
committed
}
Angie Byron
committed
/**
* Use authorize.php to run batch_process().
*
* @see batch_process()
*/
function system_authorized_batch_process() {
$finish_url = system_authorized_get_url();
Angie Byron
committed
$process_url = system_authorized_batch_processing_url();
Angie Byron
committed
return batch_process($finish_url->setAbsolute()->toString(), $process_url);
Angie Byron
committed
}
Angie Byron
committed
/**
* @} End of "defgroup authorize".
*/
/**
Dries Buytaert
committed
* Implements hook_updater_info().
*/
function system_updater_info() {
return [
'module' => [
'class' => 'Drupal\Core\Updater\Module',
'name' => t('Update modules'),
'weight' => 0,
],
'theme' => [
'class' => 'Drupal\Core\Updater\Theme',
'name' => t('Update themes'),
'weight' => 0,
],
];
}
Dries Buytaert
committed
/**
* Implements hook_filetransfer_info().
Dries Buytaert
committed
*/
function system_filetransfer_info() {
$backends = [];
Dries Buytaert
committed
Angie Byron
committed
// This is the default, will be available on most systems.
if (function_exists('ftp_connect')) {
$backends['ftp'] = [
Dries Buytaert
committed
'title' => t('FTP'),
'class' => 'Drupal\Core\FileTransfer\FTP',
Dries Buytaert
committed
'weight' => 0,
];
Dries Buytaert
committed
}
Dries Buytaert
committed
// SSH2 lib connection is only available if the proper PHP extension is
// installed.
if (function_exists('ssh2_connect')) {
$backends['ssh'] = [
Dries Buytaert
committed
'title' => t('SSH'),
'class' => 'Drupal\Core\FileTransfer\SSH',
Dries Buytaert
committed
'weight' => 20,
];
Dries Buytaert
committed
}
return $backends;
}
/**
* Implements hook_page_attachments().
*
* @see template_preprocess_maintenance_page()
* @see \Drupal\Core\EventSubscriber\ActiveLinkResponseFilter
*/
function system_page_attachments(array &$page) {
// Ensure the same CSS is loaded in template_preprocess_maintenance_page().
$page['#attached']['library'][] = 'system/base';
if (\Drupal::service('router.admin_context')->isAdminRoute()) {
$page['#attached']['library'][] = 'system/admin';
}
// Attach libraries used by this theme.
$active_theme = \Drupal::theme()->getActiveTheme();
foreach ($active_theme->getLibraries() as $library) {
$page['#attached']['library'][] = $library;
}
// Attach favicon.
if (theme_get_setting('features.favicon')) {
$favicon = theme_get_setting('favicon.url');
$type = theme_get_setting('favicon.mimetype');
$page['#attached']['html_head_link'][][] = [
'rel' => 'shortcut icon',
'href' => UrlHelper::stripDangerousProtocols($favicon),
'type' => $type,
];
}
// Get the major Drupal version.
list($version,) = explode('.', \Drupal::VERSION);
// Attach default meta tags.
$meta_default = [
// Make sure the Content-Type comes first because the IE browser may be
// vulnerable to XSS via encoding attacks from any content that comes
// before this META tag, such as a TITLE tag.
'system_meta_content_type' => [
'#attributes' => [
'charset' => 'utf-8',
],
// Security: This always has to be output first.
'#weight' => -1000,
],
// Show Drupal and the major version number in the META GENERATOR tag.
'system_meta_generator' => [
'#type' => 'html_tag',
'#tag' => 'meta',
'#attributes' => [
'name' => 'Generator',
Jess
committed
'content' => 'Drupal ' . $version . ' (https://www.drupal.org)',
],
],
// Attach default mobile meta tags for responsive design.
'MobileOptimized' => [
'#attributes' => [
'name' => 'MobileOptimized',
'content' => 'width',
],
],
'HandheldFriendly' => [
'#attributes' => [
'name' => 'HandheldFriendly',
'content' => 'true',
],
],
'viewport' => [
'#attributes' => [
'name' => 'viewport',
'content' => 'width=device-width, initial-scale=1.0',
],
],
];
foreach ($meta_default as $key => $value) {
$page['#attached']['html_head'][] = [$value, $key];
}
// Handle setting the "active" class on links by:
// - loading the active-link library if the current user is authenticated;
// - applying a response filter if the current user is anonymous.
// @see \Drupal\Core\Link
// @see \Drupal\Core\Utility\LinkGenerator::generate()
// @see template_preprocess_links()
// @see \Drupal\Core\EventSubscriber\ActiveLinkResponseFilter
if (\Drupal::currentUser()->isAuthenticated()) {
$page['#attached']['library'][] = 'core/drupal.active-link';
Dries Buytaert
committed
}
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
/**
* Implements hook_js_settings_build().
*
* Sets values for the core/drupal.ajax library, which just depends on the
* active theme but no other request-dependent values.
*/
function system_js_settings_build(&$settings, AttachedAssetsInterface $assets) {
// Generate the values for the core/drupal.ajax library.
// We need to send ajaxPageState settings for core/drupal.ajax if:
// - ajaxPageState is being loaded in this Response, in which case it will
// already exist at $settings['ajaxPageState'] (because the core/drupal.ajax
// library definition specifies a placeholder 'ajaxPageState' setting).
// - core/drupal.ajax already has been loaded and hence this is an AJAX
// Response in which we must send the list of extra asset libraries that are
// being added in this AJAX Response.
/** @var \Drupal\Core\Asset\LibraryDependencyResolver $library_dependency_resolver */
$library_dependency_resolver = \Drupal::service('library.dependency_resolver');
if (isset($settings['ajaxPageState']) || in_array('core/drupal.ajax', $library_dependency_resolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()))) {
// Provide the page with information about the theme that's used, so that
// a later AJAX request can be rendered using the same theme.
// @see \Drupal\Core\Theme\AjaxBasePageNegotiator
$theme_key = \Drupal::theme()->getActiveTheme()->getName();
$settings['ajaxPageState']['theme'] = $theme_key;
}
}
/**
* Implements hook_js_settings_alter().
*
* Sets values which depend on the current request, like core/drupalSettings
* as well as theme_token ajax state.
function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) {
// As this is being output in the final response always use the master
// request.
$request = \Drupal::requestStack()->getMasterRequest();
$current_query = $request->query->all();
// Let output path processors set a prefix.
/** @var \Drupal\Core\PathProcessor\OutboundPathProcessorInterface $path_processor */
$path_processor = \Drupal::service('path_processor_manager');
$options = ['prefix' => ''];
$path_processor->processOutbound('/', $options);
$pathPrefix = $options['prefix'];
$route_match = \Drupal::routeMatch();
if ($route_match instanceof StackedRouteMatchInterface) {
$route_match = $route_match->getMasterRouteMatch();
}
$current_path = $route_match->getRouteName() ? Url::fromRouteMatch($route_match)->getInternalPath() : '';
$current_path_is_admin = \Drupal::service('router.admin_context')->isAdminRoute($route_match->getRouteObject());
$path_settings = [
'baseUrl' => $request->getBaseUrl() . '/',
'pathPrefix' => $pathPrefix,
'currentPath' => $current_path,
'currentPathIsAdmin' => $current_path_is_admin,
'isFront' => \Drupal::service('path.matcher')->isFrontPage(),
'currentLanguage' => \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_URL)->getId(),
];
if (!empty($current_query)) {
ksort($current_query);
$path_settings['currentQuery'] = (object) $current_query;
}
// Only set core/drupalSettings values that haven't been set already.
foreach ($path_settings as $key => $value) {
if (!isset($settings['path'][$key])) {
$settings['path'][$key] = $value;
}
}
if (!isset($settings['pluralDelimiter'])) {
$settings['pluralDelimiter'] = LOCALE_PLURAL_DELIMITER;
// Add the theme token to ajaxPageState, ensuring the database is available
Alex Pott
committed
// before doing so. Also add the loaded libraries to ajaxPageState.
/** @var \Drupal\Core\Asset\LibraryDependencyResolver $library_dependency_resolver */
$library_dependency_resolver = \Drupal::service('library.dependency_resolver');
if (isset($settings['ajaxPageState']) || in_array('core/drupal.ajax', $library_dependency_resolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()))) {
if (!defined('MAINTENANCE_MODE')) {
// The theme token is only validated when the theme requested is not the
// default, so don't generate it unless necessary.
// @see \Drupal\Core\Theme\AjaxBasePageNegotiator::determineActiveTheme()
$active_theme_key = \Drupal::theme()->getActiveTheme()->getName();
if ($active_theme_key !== \Drupal::service('theme_handler')->getDefault()) {
$settings['ajaxPageState']['theme_token'] = \Drupal::csrfToken()
->get($active_theme_key);
}
}
Alex Pott
committed
// Provide the page with information about the individual asset libraries
// used, information not otherwise available when aggregation is enabled.
$minimal_libraries = $library_dependency_resolver->getMinimalRepresentativeSubset(array_merge(
$assets->getLibraries(),
$assets->getAlreadyLoadedLibraries()
));
sort($minimal_libraries);
$settings['ajaxPageState']['libraries'] = implode(',', $minimal_libraries);
}
}
/**
* Implements hook_form_alter().
*/
function system_form_alter(&$form, FormStateInterface $form_state) {
// If the page that's being built is cacheable, set the 'immutable' flag, to
// ensure that when the form is used, a new form build ID is generated when
// appropriate, to prevent information disclosure.
// Note: This code just wants to know whether cache response headers are set,
// not whether page_cache module will be active.
// \Drupal\Core\EventSubscriber\FinishResponseSubscriber::onRespond will
// send those headers, in case $request_policy->check($request) succeeds. In
// that case we need to ensure that the immutable flag is sot, so future POST
// request won't take over the form state of another user.
/** @var \Drupal\Core\PageCache\RequestPolicyInterface $request_policy */
$request_policy = \Drupal::service('page_cache_request_policy');
$request = \Drupal::requestStack()->getCurrentRequest();
$request_is_cacheable = $request_policy->check($request) === RequestPolicyInterface::ALLOW;
if ($request_is_cacheable) {
$form_state->addBuildInfo('immutable', TRUE);
}
}
* Implements hook_form_FORM_ID_alter() for \Drupal\user\AccountForm.
Dries Buytaert
committed
function system_form_user_form_alter(&$form, FormStateInterface $form_state) {
if (\Drupal::config('system.date')->get('timezone.user.configurable')) {
catch
committed
system_user_timezone($form, $form_state);
Angie Byron
committed
}
}
/**
* Implements hook_form_FORM_ID_alter() for \Drupal\user\RegisterForm.
Angie Byron
committed
*/
Dries Buytaert
committed
function system_form_user_register_form_alter(&$form, FormStateInterface $form_state) {
$config = \Drupal::config('system.date');
if ($config->get('timezone.user.configurable') && $config->get('timezone.user.default') == DRUPAL_USER_TIMEZONE_SELECT) {
system_user_timezone($form, $form_state);
/**
* Implements hook_ENTITY_TYPE_presave() for user entities.
*/
function system_user_presave(UserInterface $account) {
$config = \Drupal::config('system.date');
if ($config->get('timezone.user.configurable') && !$account->getTimeZone() && !$config->get('timezone.user.default')) {
$account->timezone = $config->get('timezone.default');
}
}
Angie Byron
committed
/**
Dries Buytaert
committed
* Implements hook_user_login().
Angie Byron
committed
*/
function system_user_login(UserInterface $account) {
$config = \Drupal::config('system.date');
Angie Byron
committed
// If the user has a NULL time zone, notify them to set a time zone.
if (!$account->getTimezone() && $config->get('timezone.user.configurable') && $config->get('timezone.user.warn')) {
drupal_set_message(t('Configure your <a href=":user-edit">account time zone setting</a>.', [':user-edit' => $account->url('edit-form', ['query' => \Drupal::destination()->getAsArray(), 'fragment' => 'edit-timezone'])]));
Angie Byron
committed
}
}
/**
* Add the time zone field to the user edit and register forms.
*/
Dries Buytaert
committed
function system_user_timezone(&$form, FormStateInterface $form_state) {
$user = \Drupal::currentUser();
Dries Buytaert
committed
$account = $form_state->getFormObject()->getEntity();
$form['timezone'] = [
'#type' => 'details',
Angie Byron
committed
'#title' => t('Locale settings'),
Angie Byron
committed
'#open' => TRUE,
Angie Byron
committed
'#weight' => 6,
];
$form['timezone']['timezone'] = [
Angie Byron
committed
'#type' => 'select',
'#title' => t('Time zone'),
'#default_value' => $account->getTimezone() ? $account->getTimezone() : \Drupal::config('system.date')->get('timezone.default'),
'#options' => system_time_zones($account->id() != $user->id(), TRUE),
Angie Byron
committed
'#description' => t('Select the desired local time and time zone. Dates and times throughout this site will be displayed using this time zone.'),
];
Angie Byron
committed
$user_input = $form_state->getUserInput();
if (!$account->getTimezone() && $account->id() == $user->id() && empty($user_input['timezone'])) {
Angie Byron
committed
$form['timezone']['#attached']['library'][] = 'core/drupal.timezone';
$form['timezone']['timezone']['#attributes'] = ['class' => ['timezone-detect']];
Angie Byron
committed
}
}
Gábor Hojtsy
committed
/**
Angie Byron
committed
* Implements hook_preprocess_HOOK() for block templates.
Dries Buytaert
committed
*/
Dries Buytaert
committed
function system_preprocess_block(&$variables) {
switch ($variables['base_plugin_id']) {
Angie Byron
committed
case 'system_branding_block':
$variables['site_logo'] = '';
if ($variables['content']['site_logo']['#access'] && $variables['content']['site_logo']['#uri']) {
$variables['site_logo'] = $variables['content']['site_logo']['#uri'];
}
$variables['site_name'] = '';
if ($variables['content']['site_name']['#access'] && $variables['content']['site_name']['#markup']) {
$variables['site_name'] = $variables['content']['site_name']['#markup'];
}
$variables['site_slogan'] = '';
if ($variables['content']['site_slogan']['#access'] && $variables['content']['site_slogan']['#markup']) {
$variables['site_slogan'] = [
'#markup' => $variables['content']['site_slogan']['#markup'],
];
Angie Byron
committed
}
break;
Dries Buytaert
committed
case 'system_powered_by_block':
Alex Pott
committed
$variables['attributes']['role'] = 'complementary';
Dries Buytaert
committed
break;
Dries Buytaert
committed
}
Gábor Hojtsy
committed
}
Dries Buytaert
committed
/**
* Checks the existence of the directory specified in $form_element.
*
* This function is called from the system_settings form to check all core
* file directories (file_public_path, file_private_path, file_temporary_path).
Dries Buytaert
committed
*
* @param $form_element
* The form element containing the name of the directory to check.
Alex Pott
committed
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
Dries Buytaert
committed
*/
Alex Pott
committed
function system_check_directory($form_element, FormStateInterface $form_state) {
$directory = $form_element['#value'];
Angie Byron
committed
if (strlen($directory) == 0) {
return $form_element;
}
Angie Byron
committed
$logger = \Drupal::logger('file system');
if (!is_dir($directory) && !drupal_mkdir($directory, NULL, TRUE)) {
// If the directory does not exists and cannot be created.
$form_state->setErrorByName($form_element['#parents'][0], t('The directory %directory does not exist and could not be created.', ['%directory' => $directory]));
$logger->error('The directory %directory does not exist and could not be created.', ['%directory' => $directory]);
}
if (is_dir($directory) && !is_writable($directory) && !drupal_chmod($directory)) {
// If the directory is not writable and cannot be made so.
$form_state->setErrorByName($form_element['#parents'][0], t('The directory %directory exists but is not writable and could not be made writable.', ['%directory' => $directory]));
$logger->error('The directory %directory exists but is not writable and could not be made writable.', ['%directory' => $directory]);
}
Angie Byron
committed
elseif (is_dir($directory)) {
if ($form_element['#name'] == 'file_public_path') {
// Create public .htaccess file.
Dries Buytaert
committed
file_save_htaccess($directory, FALSE);
}
else {
// Create private .htaccess file.
Dries Buytaert
committed
file_save_htaccess($directory);
}
}
Dries Buytaert
committed
return $form_element;
}
Angie Byron
committed
/**
Dries Buytaert
committed
* Returns an array of information about enabled modules or themes.
Angie Byron
committed
*
Dries Buytaert
committed
* This function returns the contents of the .info.yml file for each installed
* module or theme.
Angie Byron
committed
*
* @param $type
* Either 'module' or 'theme'.
Dries Buytaert
committed
* @param $name
* (optional) The name of a module or theme whose information shall be
* returned. If omitted, all records for the provided $type will be returned.
* If $name does not exist in the provided $type or is not enabled, an empty
* array will be returned.
Dries Buytaert
committed
*
Angie Byron
committed
* @return
Dries Buytaert
committed
* An associative array of module or theme information keyed by name, or only
* information for $name, if given. If no records are available, an empty
* array is returned.
Angie Byron
committed
*
* @see system_rebuild_module_data()
* @see \Drupal\Core\Extension\ThemeHandlerInterface::rebuildThemeData()
Angie Byron
committed
*/
Dries Buytaert
committed
function system_get_info($type, $name = NULL) {
if ($type == 'module') {
Alex Bronstein
committed
$info = &drupal_static(__FUNCTION__);
if (!isset($info)) {
if ($cache = \Drupal::cache()->get('system.module.info')) {
$info = $cache->data;
}
else {
$data = system_rebuild_module_data();
foreach (\Drupal::moduleHandler()->getModuleList() as $module => $filename) {
if (isset($data[$module])) {
$info[$module] = $data[$module]->info;
}
}
// Store the module information in cache. This cache is cleared by
// calling system_rebuild_module_data(), for example, when listing
// modules, (un)installing modules, importing configuration, updating
// the site and when flushing all the caches.
\Drupal::cache()->set('system.module.info', $info);
Angie Byron
committed
}
Dries Buytaert
committed
}
else {
$info = [];
$list = system_list($type);
foreach ($list as $shortname => $item) {
if (!empty($item->status)) {
$info[$shortname] = $item->info;
}
Dries Buytaert
committed
}
}
if (isset($name)) {
return isset($info[$name]) ? $info[$name] : [];
Angie Byron
committed
}
return $info;
}
* Helper function to scan and collect module .info.yml data.