Move into nested docroot
This commit is contained in:
parent
83a0d3a149
commit
c8b70abde9
13405 changed files with 0 additions and 0 deletions
519
web/core/includes/batch.inc
Normal file
519
web/core/includes/batch.inc
Normal file
|
@ -0,0 +1,519 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Batch processing API for processes to run in multiple HTTP requests.
|
||||
*
|
||||
* Note that batches are usually invoked by form submissions, which is
|
||||
* why the core interaction functions of the batch processing API live in
|
||||
* form.inc.
|
||||
*
|
||||
* @see form.inc
|
||||
* @see batch_set()
|
||||
* @see batch_process()
|
||||
* @see batch_get()
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\Timer;
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Core\Batch\Percentage;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
/**
|
||||
* Renders the batch processing page based on the current state of the batch.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request object.
|
||||
*
|
||||
* @see _batch_shutdown()
|
||||
*/
|
||||
function _batch_page(Request $request) {
|
||||
$batch = &batch_get();
|
||||
|
||||
if (!($request_id = $request->query->get('id'))) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Retrieve the current state of the batch.
|
||||
if (!$batch) {
|
||||
$batch = \Drupal::service('batch.storage')->load($request_id);
|
||||
if (!$batch) {
|
||||
drupal_set_message(t('No active batch.'), 'error');
|
||||
return new RedirectResponse(\Drupal::url('<front>', [], ['absolute' => TRUE]));
|
||||
}
|
||||
}
|
||||
|
||||
// Register database update for the end of processing.
|
||||
drupal_register_shutdown_function('_batch_shutdown');
|
||||
|
||||
$build = array();
|
||||
|
||||
// Add batch-specific libraries.
|
||||
foreach ($batch['sets'] as $batch_set) {
|
||||
if (isset($batch_set['library'])) {
|
||||
foreach ($batch_set['library'] as $library) {
|
||||
$build['#attached']['library'][] = $library;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$op = $request->query->get('op', '');
|
||||
switch ($op) {
|
||||
case 'start':
|
||||
case 'do_nojs':
|
||||
// Display the full progress page on startup and on each additional
|
||||
// non-JavaScript iteration.
|
||||
$current_set = _batch_current_set();
|
||||
$build['#title'] = $current_set['title'];
|
||||
$build['content'] = _batch_progress_page();
|
||||
break;
|
||||
|
||||
case 'do':
|
||||
// JavaScript-based progress page callback.
|
||||
return _batch_do();
|
||||
|
||||
case 'finished':
|
||||
// _batch_finished() returns a RedirectResponse.
|
||||
return _batch_finished();
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does one execution pass with JavaScript and returns progress to the browser.
|
||||
*
|
||||
* @see _batch_progress_page_js()
|
||||
* @see _batch_process()
|
||||
*/
|
||||
function _batch_do() {
|
||||
// Perform actual processing.
|
||||
list($percentage, $message, $label) = _batch_process();
|
||||
|
||||
return new JsonResponse(array('status' => TRUE, 'percentage' => $percentage, 'message' => $message, 'label' => $label));
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a batch processing page.
|
||||
*
|
||||
* @see _batch_process()
|
||||
*/
|
||||
function _batch_progress_page() {
|
||||
$batch = &batch_get();
|
||||
|
||||
$current_set = _batch_current_set();
|
||||
|
||||
$new_op = 'do_nojs';
|
||||
|
||||
if (!isset($batch['running'])) {
|
||||
// This is the first page so we return some output immediately.
|
||||
$percentage = 0;
|
||||
$message = $current_set['init_message'];
|
||||
$label = '';
|
||||
$batch['running'] = TRUE;
|
||||
}
|
||||
else {
|
||||
// This is one of the later requests; do some processing first.
|
||||
|
||||
// Error handling: if PHP dies due to a fatal error (e.g. a nonexistent
|
||||
// function), it will output whatever is in the output buffer, followed by
|
||||
// the error message.
|
||||
ob_start();
|
||||
$fallback = $current_set['error_message'] . '<br />' . $batch['error_message'];
|
||||
|
||||
// We strip the end of the page using a marker in the template, so any
|
||||
// additional HTML output by PHP shows up inside the page rather than below
|
||||
// it. While this causes invalid HTML, the same would be true if we didn't,
|
||||
// as content is not allowed to appear after </html> anyway.
|
||||
$bare_html_page_renderer = \Drupal::service('bare_html_page_renderer');
|
||||
$response = $bare_html_page_renderer->renderBarePage(['#markup' => $fallback], $current_set['title'], 'maintenance_page', array(
|
||||
'#show_messages' => FALSE,
|
||||
));
|
||||
|
||||
// Just use the content of the response.
|
||||
$fallback = $response->getContent();
|
||||
|
||||
list($fallback) = explode('<!--partial-->', $fallback);
|
||||
print $fallback;
|
||||
|
||||
// Perform actual processing.
|
||||
list($percentage, $message, $label) = _batch_process($batch);
|
||||
if ($percentage == 100) {
|
||||
$new_op = 'finished';
|
||||
}
|
||||
|
||||
// PHP did not die; remove the fallback output.
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
// Merge required query parameters for batch processing into those provided by
|
||||
// batch_set() or hook_batch_alter().
|
||||
$query_options = $batch['url']->getOption('query');
|
||||
$query_options['id'] = $batch['id'];
|
||||
$query_options['op'] = $new_op;
|
||||
$batch['url']->setOption('query', $query_options);
|
||||
|
||||
$url = $batch['url']->toString(TRUE)->getGeneratedUrl();
|
||||
|
||||
$build = array(
|
||||
'#theme' => 'progress_bar',
|
||||
'#percent' => $percentage,
|
||||
'#message' => array('#markup' => $message),
|
||||
'#label' => $label,
|
||||
'#attached' => array(
|
||||
'html_head' => array(
|
||||
array(
|
||||
array(
|
||||
// Redirect through a 'Refresh' meta tag if JavaScript is disabled.
|
||||
'#tag' => 'meta',
|
||||
'#noscript' => TRUE,
|
||||
'#attributes' => array(
|
||||
'http-equiv' => 'Refresh',
|
||||
'content' => '0; URL=' . $url,
|
||||
),
|
||||
),
|
||||
'batch_progress_meta_refresh',
|
||||
),
|
||||
),
|
||||
// Adds JavaScript code and settings for clients where JavaScript is enabled.
|
||||
'drupalSettings' => [
|
||||
'batch' => [
|
||||
'errorMessage' => $current_set['error_message'] . '<br />' . $batch['error_message'],
|
||||
'initMessage' => $current_set['init_message'],
|
||||
'uri' => $url,
|
||||
],
|
||||
],
|
||||
'library' => array(
|
||||
'core/drupal.batch',
|
||||
),
|
||||
),
|
||||
);
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes sets in a batch.
|
||||
*
|
||||
* If the batch was marked for progressive execution (default), this executes as
|
||||
* many operations in batch sets until an execution time of 1 second has been
|
||||
* exceeded. It will continue with the next operation of the same batch set in
|
||||
* the next request.
|
||||
*
|
||||
* @return array
|
||||
* An array containing a completion value (in percent) and a status message.
|
||||
*/
|
||||
function _batch_process() {
|
||||
$batch = &batch_get();
|
||||
$current_set = &_batch_current_set();
|
||||
// Indicate that this batch set needs to be initialized.
|
||||
$set_changed = TRUE;
|
||||
|
||||
// If this batch was marked for progressive execution (e.g. forms submitted by
|
||||
// \Drupal::formBuilder()->submitForm(), initialize a timer to determine
|
||||
// whether we need to proceed with the same batch phase when a processing time
|
||||
// of 1 second has been exceeded.
|
||||
if ($batch['progressive']) {
|
||||
Timer::start('batch_processing');
|
||||
}
|
||||
|
||||
if (empty($current_set['start'])) {
|
||||
$current_set['start'] = microtime(TRUE);
|
||||
}
|
||||
|
||||
$queue = _batch_queue($current_set);
|
||||
|
||||
while (!$current_set['success']) {
|
||||
// If this is the first time we iterate this batch set in the current
|
||||
// request, we check if it requires an additional file for functions
|
||||
// definitions.
|
||||
if ($set_changed && isset($current_set['file']) && is_file($current_set['file'])) {
|
||||
include_once \Drupal::root() . '/' . $current_set['file'];
|
||||
}
|
||||
|
||||
$task_message = $label = '';
|
||||
// Assume a single pass operation and set the completion level to 1 by
|
||||
// default.
|
||||
$finished = 1;
|
||||
|
||||
if ($item = $queue->claimItem()) {
|
||||
list($callback, $args) = $item->data;
|
||||
|
||||
// Build the 'context' array and execute the function call.
|
||||
$batch_context = array(
|
||||
'sandbox' => &$current_set['sandbox'],
|
||||
'results' => &$current_set['results'],
|
||||
'finished' => &$finished,
|
||||
'message' => &$task_message,
|
||||
);
|
||||
call_user_func_array($callback, array_merge($args, array(&$batch_context)));
|
||||
|
||||
if ($finished >= 1) {
|
||||
// Make sure this step is not counted twice when computing $current.
|
||||
$finished = 0;
|
||||
// Remove the processed operation and clear the sandbox.
|
||||
$queue->deleteItem($item);
|
||||
$current_set['count']--;
|
||||
$current_set['sandbox'] = array();
|
||||
}
|
||||
}
|
||||
|
||||
// When all operations in the current batch set are completed, browse
|
||||
// through the remaining sets, marking them 'successfully processed'
|
||||
// along the way, until we find a set that contains operations.
|
||||
// _batch_next_set() executes form submit handlers stored in 'control'
|
||||
// sets (see \Drupal::service('form_submitter')), which can in turn add new
|
||||
// sets to the batch.
|
||||
$set_changed = FALSE;
|
||||
$old_set = $current_set;
|
||||
while (empty($current_set['count']) && ($current_set['success'] = TRUE) && _batch_next_set()) {
|
||||
$current_set = &_batch_current_set();
|
||||
$current_set['start'] = microtime(TRUE);
|
||||
$set_changed = TRUE;
|
||||
}
|
||||
|
||||
// At this point, either $current_set contains operations that need to be
|
||||
// processed or all sets have been completed.
|
||||
$queue = _batch_queue($current_set);
|
||||
|
||||
// If we are in progressive mode, break processing after 1 second.
|
||||
if ($batch['progressive'] && Timer::read('batch_processing') > 1000) {
|
||||
// Record elapsed wall clock time.
|
||||
$current_set['elapsed'] = round((microtime(TRUE) - $current_set['start']) * 1000, 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($batch['progressive']) {
|
||||
// Gather progress information.
|
||||
|
||||
// Reporting 100% progress will cause the whole batch to be considered
|
||||
// processed. If processing was paused right after moving to a new set,
|
||||
// we have to use the info from the new (unprocessed) set.
|
||||
if ($set_changed && isset($current_set['queue'])) {
|
||||
// Processing will continue with a fresh batch set.
|
||||
$remaining = $current_set['count'];
|
||||
$total = $current_set['total'];
|
||||
$progress_message = $current_set['init_message'];
|
||||
$task_message = '';
|
||||
}
|
||||
else {
|
||||
// Processing will continue with the current batch set.
|
||||
$remaining = $old_set['count'];
|
||||
$total = $old_set['total'];
|
||||
$progress_message = $old_set['progress_message'];
|
||||
}
|
||||
|
||||
// Total progress is the number of operations that have fully run plus the
|
||||
// completion level of the current operation.
|
||||
$current = $total - $remaining + $finished;
|
||||
$percentage = _batch_api_percentage($total, $current);
|
||||
$elapsed = isset($current_set['elapsed']) ? $current_set['elapsed'] : 0;
|
||||
$values = array(
|
||||
'@remaining' => $remaining,
|
||||
'@total' => $total,
|
||||
'@current' => floor($current),
|
||||
'@percentage' => $percentage,
|
||||
'@elapsed' => \Drupal::service('date.formatter')->formatInterval($elapsed / 1000),
|
||||
// If possible, estimate remaining processing time.
|
||||
'@estimate' => ($current > 0) ? \Drupal::service('date.formatter')->formatInterval(($elapsed * ($total - $current) / $current) / 1000) : '-',
|
||||
);
|
||||
$message = strtr($progress_message, $values);
|
||||
if (!empty($task_message)) {
|
||||
$label = $task_message;
|
||||
}
|
||||
|
||||
return array($percentage, $message, $label);
|
||||
}
|
||||
else {
|
||||
// If we are not in progressive mode, the entire batch has been processed.
|
||||
return _batch_finished();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the percent completion for a batch set.
|
||||
*
|
||||
* @param int $total
|
||||
* The total number of operations.
|
||||
* @param int|float $current
|
||||
* The number of the current operation. This may be a floating point number
|
||||
* rather than an integer in the case of a multi-step operation that is not
|
||||
* yet complete; in that case, the fractional part of $current represents the
|
||||
* fraction of the operation that has been completed.
|
||||
*
|
||||
* @return string
|
||||
* The properly formatted percentage, as a string. We output percentages
|
||||
* using the correct number of decimal places so that we never print "100%"
|
||||
* until we are finished, but we also never print more decimal places than
|
||||
* are meaningful.
|
||||
*
|
||||
* @see _batch_process()
|
||||
*/
|
||||
function _batch_api_percentage($total, $current) {
|
||||
return Percentage::format($total, $current);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the batch set being currently processed.
|
||||
*/
|
||||
function &_batch_current_set() {
|
||||
$batch = &batch_get();
|
||||
return $batch['sets'][$batch['current_set']];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the next set in a batch.
|
||||
*
|
||||
* If there is a subsequent set in this batch, assign it as the new set to
|
||||
* process and execute its form submit handler (if defined), which may add
|
||||
* further sets to this batch.
|
||||
*
|
||||
* @return true|null
|
||||
* TRUE if a subsequent set was found in the batch; no value will be returned
|
||||
* if no subsequent set was found.
|
||||
*/
|
||||
function _batch_next_set() {
|
||||
$batch = &batch_get();
|
||||
if (isset($batch['sets'][$batch['current_set'] + 1])) {
|
||||
$batch['current_set']++;
|
||||
$current_set = &_batch_current_set();
|
||||
if (isset($current_set['form_submit']) && ($callback = $current_set['form_submit']) && is_callable($callback)) {
|
||||
// We use our stored copies of $form and $form_state to account for
|
||||
// possible alterations by previous form submit handlers.
|
||||
$complete_form = &$batch['form_state']->getCompleteForm();
|
||||
call_user_func_array($callback, array(&$complete_form, &$batch['form_state']));
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the batch processing.
|
||||
*
|
||||
* Call the 'finished' callback of each batch set to allow custom handling of
|
||||
* the results and resolve page redirection.
|
||||
*/
|
||||
function _batch_finished() {
|
||||
$batch = &batch_get();
|
||||
$batch_finished_redirect = NULL;
|
||||
|
||||
// Execute the 'finished' callbacks for each batch set, if defined.
|
||||
foreach ($batch['sets'] as $batch_set) {
|
||||
if (isset($batch_set['finished'])) {
|
||||
// Check if the set requires an additional file for function definitions.
|
||||
if (isset($batch_set['file']) && is_file($batch_set['file'])) {
|
||||
include_once \Drupal::root() . '/' . $batch_set['file'];
|
||||
}
|
||||
if (is_callable($batch_set['finished'])) {
|
||||
$queue = _batch_queue($batch_set);
|
||||
$operations = $queue->getAllItems();
|
||||
$batch_set_result = call_user_func_array($batch_set['finished'], array($batch_set['success'], $batch_set['results'], $operations, \Drupal::service('date.formatter')->formatInterval($batch_set['elapsed'] / 1000)));
|
||||
// If a batch 'finished' callback requested a redirect after the batch
|
||||
// is complete, save that for later use. If more than one batch set
|
||||
// returned a redirect, the last one is used.
|
||||
if ($batch_set_result instanceof RedirectResponse) {
|
||||
$batch_finished_redirect = $batch_set_result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up the batch table and unset the static $batch variable.
|
||||
if ($batch['progressive']) {
|
||||
\Drupal::service('batch.storage')->delete($batch['id']);
|
||||
foreach ($batch['sets'] as $batch_set) {
|
||||
if ($queue = _batch_queue($batch_set)) {
|
||||
$queue->deleteQueue();
|
||||
}
|
||||
}
|
||||
// Clean-up the session. Not needed for CLI updates.
|
||||
if (isset($_SESSION)) {
|
||||
unset($_SESSION['batches'][$batch['id']]);
|
||||
if (empty($_SESSION['batches'])) {
|
||||
unset($_SESSION['batches']);
|
||||
}
|
||||
}
|
||||
}
|
||||
$_batch = $batch;
|
||||
$batch = NULL;
|
||||
|
||||
// Redirect if needed.
|
||||
if ($_batch['progressive']) {
|
||||
// Revert the 'destination' that was saved in batch_process().
|
||||
if (isset($_batch['destination'])) {
|
||||
\Drupal::request()->query->set('destination', $_batch['destination']);
|
||||
}
|
||||
|
||||
// Determine the target path to redirect to. If a batch 'finished' callback
|
||||
// returned a redirect response object, use that. Otherwise, fall back on
|
||||
// the form redirection.
|
||||
if (isset($batch_finished_redirect)) {
|
||||
return $batch_finished_redirect;
|
||||
}
|
||||
elseif (!isset($_batch['form_state'])) {
|
||||
$_batch['form_state'] = new FormState();
|
||||
}
|
||||
if ($_batch['form_state']->getRedirect() === NULL) {
|
||||
$redirect = $_batch['batch_redirect'] ?: $_batch['source_url'];
|
||||
// Any path with a scheme does not correspond to a route.
|
||||
if (!$redirect instanceof Url) {
|
||||
$options = UrlHelper::parse($redirect);
|
||||
if (parse_url($options['path'], PHP_URL_SCHEME)) {
|
||||
$redirect = Url::fromUri($options['path'], $options);
|
||||
}
|
||||
else {
|
||||
$redirect = \Drupal::pathValidator()->getUrlIfValid($options['path']);
|
||||
if (!$redirect) {
|
||||
// Stay on the same page if the redirect was invalid.
|
||||
$redirect = Url::fromRoute('<current>');
|
||||
}
|
||||
$redirect->setOptions($options);
|
||||
}
|
||||
}
|
||||
$_batch['form_state']->setRedirectUrl($redirect);
|
||||
}
|
||||
|
||||
// Use \Drupal\Core\Form\FormSubmitterInterface::redirectForm() to handle
|
||||
// the redirection logic.
|
||||
$redirect = \Drupal::service('form_submitter')->redirectForm($_batch['form_state']);
|
||||
if (is_object($redirect)) {
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
// If no redirection happened, redirect to the originating page. In case the
|
||||
// form needs to be rebuilt, save the final $form_state for
|
||||
// \Drupal\Core\Form\FormBuilderInterface::buildForm().
|
||||
if ($_batch['form_state']->isRebuilding()) {
|
||||
$_SESSION['batch_form_state'] = $_batch['form_state'];
|
||||
}
|
||||
$callback = $_batch['redirect_callback'];
|
||||
/** @var \Drupal\Core\Url $source_url */
|
||||
$source_url = $_batch['source_url'];
|
||||
if (is_callable($callback)) {
|
||||
$callback($_batch['source_url'], array('query' => array('op' => 'finish', 'id' => $_batch['id'])));
|
||||
}
|
||||
elseif ($callback === NULL) {
|
||||
// Default to RedirectResponse objects when nothing specified.
|
||||
$url = $source_url
|
||||
->setAbsolute()
|
||||
->setOption('query', ['op' => 'finish', 'id' => $_batch['id']]);
|
||||
return new RedirectResponse($url->toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown function: Stores the current batch data for the next request.
|
||||
*
|
||||
* @see _batch_page()
|
||||
* @see drupal_register_shutdown_function()
|
||||
*/
|
||||
function _batch_shutdown() {
|
||||
if ($batch = batch_get()) {
|
||||
\Drupal::service('batch.storage')->update($batch);
|
||||
}
|
||||
}
|
1022
web/core/includes/bootstrap.inc
Normal file
1022
web/core/includes/bootstrap.inc
Normal file
File diff suppressed because it is too large
Load diff
1296
web/core/includes/common.inc
Normal file
1296
web/core/includes/common.inc
Normal file
File diff suppressed because it is too large
Load diff
1049
web/core/includes/database.inc
Normal file
1049
web/core/includes/database.inc
Normal file
File diff suppressed because it is too large
Load diff
575
web/core/includes/entity.inc
Normal file
575
web/core/includes/entity.inc
Normal file
|
@ -0,0 +1,575 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Entity API for handling entities like nodes or users.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
|
||||
/**
|
||||
* Clears the entity render cache for all entity types.
|
||||
*/
|
||||
function entity_render_cache_clear() {
|
||||
$entity_manager = Drupal::entityManager();
|
||||
foreach ($entity_manager->getDefinitions() as $entity_type => $info) {
|
||||
if ($entity_manager->hasHandler($entity_type, 'view_builder')) {
|
||||
$entity_manager->getViewBuilder($entity_type)->resetCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity bundle info.
|
||||
*
|
||||
* @param string|null $entity_type
|
||||
* The entity type whose bundle info should be returned, or NULL for all
|
||||
* bundles info. Defaults to NULL.
|
||||
*
|
||||
* @return array
|
||||
* The bundle info for a specific entity type, or all entity types.
|
||||
*
|
||||
* @deprecated in Drupal 8.x-dev and will be removed before Drupal 9.0.0. Use
|
||||
* \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getBundleInfo() for a
|
||||
* single bundle, or
|
||||
* \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getAllBundleInfo() for
|
||||
* all bundles.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getBundleInfo()
|
||||
* @see \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getAllBundleInfo()
|
||||
*/
|
||||
function entity_get_bundles($entity_type = NULL) {
|
||||
if (isset($entity_type)) {
|
||||
return \Drupal::entityManager()->getBundleInfo($entity_type);
|
||||
}
|
||||
else {
|
||||
return \Drupal::entityManager()->getAllBundleInfo();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an entity from the database.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to load, e.g. node or user.
|
||||
* @param mixed $id
|
||||
* The id of the entity to load.
|
||||
* @param bool $reset
|
||||
* Whether to reset the internal cache for the requested entity type.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface|null
|
||||
* The entity object, or NULL if there is no entity with the given ID.
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x, will be removed before Drupal 9.0.0. Use
|
||||
* The method overriding Entity::load() for the entity type, e.g.
|
||||
* \Drupal\node\Entity\Node::load() if the entity type is known. If the
|
||||
* entity type is variable, use the entity manager service to load the entity
|
||||
* from the entity storage:
|
||||
* @code
|
||||
* \Drupal::entityTypeManager()->getStorage($entity_type)->load($id);
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityInterface::load()
|
||||
* @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::load()
|
||||
* @see \Drupal\Core\Entity\Sql\SqlContentEntityStorage
|
||||
* @see \Drupal\Core\Entity\Query\QueryInterface
|
||||
*/
|
||||
function entity_load($entity_type, $id, $reset = FALSE) {
|
||||
$controller = \Drupal::entityManager()->getStorage($entity_type);
|
||||
if ($reset) {
|
||||
$controller->resetCache(array($id));
|
||||
}
|
||||
return $controller->load($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an entity from the database.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to load, e.g. node or user.
|
||||
* @param int $revision_id
|
||||
* The id of the entity to load.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface|null
|
||||
* The entity object, or NULL if there is no entity with the given revision
|
||||
* id.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. Use
|
||||
* the entity storage's loadRevision() method to load a specific entity
|
||||
* revision:
|
||||
* @code
|
||||
* \Drupal::entityTypeManager()
|
||||
* ->getStorage($entity_type)
|
||||
* ->loadRevision($revision_id);
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::loadRevision()
|
||||
* @see \Drupal\Core\Entity\Sql\SqlContentEntityStorage
|
||||
*/
|
||||
function entity_revision_load($entity_type, $revision_id) {
|
||||
return \Drupal::entityManager()
|
||||
->getStorage($entity_type)
|
||||
->loadRevision($revision_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an entity revision.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to load, e.g. node or user.
|
||||
* @param $revision_id
|
||||
* The revision ID to delete.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. Use
|
||||
* the entity storage's deleteRevision() method to delete a specific entity
|
||||
* revision:
|
||||
* @code
|
||||
* \Drupal::entityTypeManager()
|
||||
* ->getStorage($entity_type)
|
||||
* ->deleteRevision($revision_id);
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::deleteRevision()
|
||||
*/
|
||||
function entity_revision_delete($entity_type, $revision_id) {
|
||||
\Drupal::entityManager()
|
||||
->getStorage($entity_type)
|
||||
->deleteRevision($revision_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads multiple entities from the database.
|
||||
*
|
||||
* This function should be used whenever you need to load more than one entity
|
||||
* from the database. The entities are loaded into memory and will not require
|
||||
* database access if loaded again during the same page request.
|
||||
*
|
||||
* The actual loading is done through a class that has to implement the
|
||||
* \Drupal\Core\Entity\EntityStorageInterface interface. By default,
|
||||
* \Drupal\Core\Entity\Sql\SqlContentEntityStorage is used for content entities
|
||||
* and Drupal\Core\Config\Entity\ConfigEntityStorage for config entities. Entity
|
||||
* types can specify that a different class should be used by setting the
|
||||
* "handlers['storage']" key in the entity plugin annotation. These classes
|
||||
* can either implement the \Drupal\Core\Entity\EntityStorageInterface
|
||||
* interface, or, most commonly, extend the
|
||||
* \Drupal\Core\Entity\Sql\SqlContentEntityStorage class. See
|
||||
* \Drupal\node\Entity\Node and \Drupal\node\NodeStorage for an example.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to load, e.g. node or user.
|
||||
* @param array $ids
|
||||
* (optional) An array of entity IDs. If omitted, all entities are loaded.
|
||||
* @param bool $reset
|
||||
* Whether to reset the internal cache for the requested entity type.
|
||||
*
|
||||
* @return array
|
||||
* An array of entity objects indexed by their IDs.
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x, will be removed before Drupal 9.0.0. Use
|
||||
* The method overriding Entity::loadMultiple() for the entity type, e.g.
|
||||
* \Drupal\node\Entity\Node::loadMultiple() if the entity type is known. If
|
||||
* the entity type is variable, use the entity manager service to load the
|
||||
* entity from the entity storage:
|
||||
* @code
|
||||
* \Drupal::entityTypeManager()->getStorage($entity_type)->loadMultiple($id);
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityInterface::loadMultiple()
|
||||
* @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::loadMultiple()
|
||||
* @see \Drupal\Core\Entity\Sql\SqlContentEntityStorage
|
||||
* @see \Drupal\Core\Entity\Query\QueryInterface
|
||||
*/
|
||||
function entity_load_multiple($entity_type, array $ids = NULL, $reset = FALSE) {
|
||||
$controller = \Drupal::entityManager()->getStorage($entity_type);
|
||||
if ($reset) {
|
||||
$controller->resetCache($ids);
|
||||
}
|
||||
return $controller->loadMultiple($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load entities by their property values.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to load, e.g. node or user.
|
||||
* @param array $values
|
||||
* An associative array where the keys are the property names and the
|
||||
* values are the values those properties must have.
|
||||
*
|
||||
* @return array
|
||||
* An array of entity objects indexed by their IDs. Returns an empty array if
|
||||
* no matching entities are found.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. Use
|
||||
* the entity storage's loadByProperties() method to load an entity by their
|
||||
* property values:
|
||||
* @code
|
||||
* \Drupal::entityTypeManager()
|
||||
* ->getStorage($entity_type)
|
||||
* ->loadByProperties($values);
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::loadByProperties()
|
||||
*/
|
||||
function entity_load_multiple_by_properties($entity_type, array $values) {
|
||||
return \Drupal::entityManager()
|
||||
->getStorage($entity_type)
|
||||
->loadByProperties($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the unchanged, i.e. not modified, entity from the database.
|
||||
*
|
||||
* Unlike entity_load() this function ensures the entity is directly loaded from
|
||||
* the database, thus bypassing any static cache. In particular, this function
|
||||
* is useful to determine changes by comparing the entity being saved to the
|
||||
* stored entity.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type to load, e.g. node or user.
|
||||
* @param $id
|
||||
* The ID of the entity to load.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface|null
|
||||
* The unchanged entity, or FALSE if the entity cannot be loaded.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. Use
|
||||
* the entity storage's loadUnchanged() method to load an unchanged entity:
|
||||
* @code
|
||||
* \Drupal::entityTypeManager()->getStorage($entity_type)->loadUnchanged($id);
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::loadUnchanged()
|
||||
*/
|
||||
function entity_load_unchanged($entity_type, $id) {
|
||||
return \Drupal::entityManager()
|
||||
->getStorage($entity_type)
|
||||
->loadUnchanged($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes multiple entities permanently.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The type of the entity.
|
||||
* @param array $ids
|
||||
* An array of entity IDs of the entities to delete.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. Use
|
||||
* the entity storage's delete() method to delete multiple entities:
|
||||
* @code
|
||||
* $storage_handler = \Drupal::entityTypeManager()->getStorage($entity_type);
|
||||
* $entities = $storage_handler->loadMultiple($ids);
|
||||
* $storage_handler->delete($entities);
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::loadMultiple()
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::delete()
|
||||
*/
|
||||
function entity_delete_multiple($entity_type, array $ids) {
|
||||
$controller = \Drupal::entityManager()->getStorage($entity_type);
|
||||
$entities = $controller->loadMultiple($ids);
|
||||
$controller->delete($entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new entity object, without permanently saving it.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The type of the entity.
|
||||
* @param array $values
|
||||
* (optional) An array of values to set, keyed by property name. If the
|
||||
* entity type has bundles, the bundle key has to be specified.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
* A new entity object.
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x, will be removed before Drupal 9.0.0. Use
|
||||
* The method overriding Entity::create() for the entity type, e.g.
|
||||
* \Drupal\node\Entity\Node::create() if the entity type is known. If the
|
||||
* entity type is variable, use the entity storage's create() method to
|
||||
* construct a new entity:
|
||||
* @code
|
||||
* \Drupal::entityTypeManager()->getStorage($entity_type)->create($values);
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::create()
|
||||
*/
|
||||
function entity_create($entity_type, array $values = array()) {
|
||||
return \Drupal::entityManager()
|
||||
->getStorage($entity_type)
|
||||
->create($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label of an entity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity for which to generate the label.
|
||||
* @param $langcode
|
||||
* (optional) The language code of the language that should be used for
|
||||
* getting the label. If set to NULL, the entity's default language is
|
||||
* used.
|
||||
*
|
||||
* @return string|null
|
||||
* The label of the entity, or NULL if there is no label defined.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. Use
|
||||
* the entity's label() method to get the label of the entity:
|
||||
* @code
|
||||
* $entity->label($langcode);
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityInterface::label()
|
||||
*/
|
||||
function entity_page_label(EntityInterface $entity, $langcode = NULL) {
|
||||
return $entity->label($langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the render array for an entity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity to be rendered.
|
||||
* @param string $view_mode
|
||||
* The view mode that should be used to display the entity.
|
||||
* @param string $langcode
|
||||
* (optional) For which language the entity should be rendered, defaults to
|
||||
* the current content language.
|
||||
* @param bool $reset
|
||||
* (optional) Whether to reset the render cache for the requested entity.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* A render array for the entity.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0.
|
||||
* Use the entity view builder's view() method for creating a render array:
|
||||
* @code
|
||||
* $view_builder = \Drupal::entityTypeManager()
|
||||
* ->getViewBuilder($entity->getEntityTypeId());
|
||||
* return $view_builder->view($entity, $view_mode, $langcode);
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityTypeManagerInterface::getViewBuilder()
|
||||
* @see \Drupal\Core\Entity\EntityViewBuilderInterface::view()
|
||||
*/
|
||||
function entity_view(EntityInterface $entity, $view_mode, $langcode = NULL, $reset = FALSE) {
|
||||
$render_controller = \Drupal::entityManager()->getViewBuilder($entity->getEntityTypeId());
|
||||
if ($reset) {
|
||||
$render_controller->resetCache([$entity]);
|
||||
}
|
||||
return $render_controller->view($entity, $view_mode, $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the render array for the provided entities.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface[] $entities
|
||||
* The entities to be rendered, must be of the same type.
|
||||
* @param string $view_mode
|
||||
* The view mode that should be used to display the entity.
|
||||
* @param string $langcode
|
||||
* (optional) For which language the entity should be rendered, defaults to
|
||||
* the current content language.
|
||||
* @param bool $reset
|
||||
* (optional) Whether to reset the render cache for the requested entities.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* A render array for the entities, indexed by the same keys as the
|
||||
* entities array passed in $entities.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0.
|
||||
* Use the entity view builder's viewMultiple() method for creating a render
|
||||
* array for the provided entities:
|
||||
* @code
|
||||
* $view_builder = \Drupal::entityTypeManager()
|
||||
* ->getViewBuilder($entity->getEntityTypeId());
|
||||
* return $view_builder->viewMultiple($entities, $view_mode, $langcode);
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityTypeManagerInterface::getViewBuilder()
|
||||
* @see \Drupal\Core\Entity\EntityViewBuilderInterface::viewMultiple()
|
||||
*/
|
||||
function entity_view_multiple(array $entities, $view_mode, $langcode = NULL, $reset = FALSE) {
|
||||
$render_controller = \Drupal::entityManager()->getViewBuilder(reset($entities)->getEntityTypeId());
|
||||
if ($reset) {
|
||||
$render_controller->resetCache($entities);
|
||||
}
|
||||
return $render_controller->viewMultiple($entities, $view_mode, $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity view display associated with a bundle and view mode.
|
||||
*
|
||||
* Use this function when assigning suggested display options for a component
|
||||
* in a given view mode. Note that they will only be actually used at render
|
||||
* time if the view mode itself is configured to use dedicated display settings
|
||||
* for the bundle; if not, the 'default' display is used instead.
|
||||
*
|
||||
* The function reads the entity view display from the current configuration, or
|
||||
* returns a ready-to-use empty one if configuration entry exists yet for this
|
||||
* bundle and view mode. This streamlines manipulation of display objects by
|
||||
* always returning a consistent object that reflects the current state of the
|
||||
* configuration.
|
||||
*
|
||||
* Example usage:
|
||||
* - Set the 'body' field to be displayed and the 'field_image' field to be
|
||||
* hidden on article nodes in the 'default' display.
|
||||
* @code
|
||||
* entity_get_display('node', 'article', 'default')
|
||||
* ->setComponent('body', array(
|
||||
* 'type' => 'text_summary_or_trimmed',
|
||||
* 'settings' => array('trim_length' => '200')
|
||||
* 'weight' => 1,
|
||||
* ))
|
||||
* ->removeComponent('field_image')
|
||||
* ->save();
|
||||
* @endcode
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type.
|
||||
* @param string $bundle
|
||||
* The bundle.
|
||||
* @param string $view_mode
|
||||
* The view mode, or 'default' to retrieve the 'default' display object for
|
||||
* this bundle.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\Display\EntityViewDisplayInterface
|
||||
* The entity view display associated with the view mode.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0.
|
||||
* If the display is available in configuration use:
|
||||
* @code
|
||||
* \Drupal::entityTypeManager()
|
||||
* ->getStorage('entity_view_display')
|
||||
* ->load($entity_type . '.' . $bundle . '.' . $view_mode);
|
||||
* @endcode
|
||||
* When the display is not available in configuration, you can create a new
|
||||
* EntityViewDisplay object using:
|
||||
* @code
|
||||
* $values = array(
|
||||
* 'targetEntityType' => $entity_type,
|
||||
* 'bundle' => $bundle,
|
||||
* 'mode' => $view_mode,
|
||||
* 'status' => TRUE,
|
||||
* );
|
||||
* \Drupal::entityTypeManager()
|
||||
* ->getStorage('entity_view_display')
|
||||
* ->create($values);
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::create()
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::load()
|
||||
*/
|
||||
function entity_get_display($entity_type, $bundle, $view_mode) {
|
||||
// Try loading the display from configuration.
|
||||
$display = EntityViewDisplay::load($entity_type . '.' . $bundle . '.' . $view_mode);
|
||||
|
||||
// If not found, create a fresh display object. We do not preemptively create
|
||||
// new entity_view_display configuration entries for each existing entity type
|
||||
// and bundle whenever a new view mode becomes available. Instead,
|
||||
// configuration entries are only created when a display object is explicitly
|
||||
// configured and saved.
|
||||
if (!$display) {
|
||||
$display = EntityViewDisplay::create(array(
|
||||
'targetEntityType' => $entity_type,
|
||||
'bundle' => $bundle,
|
||||
'mode' => $view_mode,
|
||||
'status' => TRUE,
|
||||
));
|
||||
}
|
||||
|
||||
return $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity form display associated with a bundle and form mode.
|
||||
*
|
||||
* The function reads the entity form display object from the current
|
||||
* configuration, or returns a ready-to-use empty one if no configuration entry
|
||||
* exists yet for this bundle and form mode. This streamlines manipulation of
|
||||
* entity form displays by always returning a consistent object that reflects
|
||||
* the current state of the configuration.
|
||||
*
|
||||
* Example usage:
|
||||
* - Set the 'body' field to be displayed with the 'text_textarea_with_summary'
|
||||
* widget and the 'field_image' field to be hidden on article nodes in the
|
||||
* 'default' form mode.
|
||||
* @code
|
||||
* entity_get_form_display('node', 'article', 'default')
|
||||
* ->setComponent('body', array(
|
||||
* 'type' => 'text_textarea_with_summary',
|
||||
* 'weight' => 1,
|
||||
* ))
|
||||
* ->setComponent('field_image', array(
|
||||
* 'type' => 'hidden',
|
||||
* ))
|
||||
* ->save();
|
||||
* @endcode
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type.
|
||||
* @param string $bundle
|
||||
* The bundle.
|
||||
* @param string $form_mode
|
||||
* The form mode.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\Display\EntityFormDisplayInterface
|
||||
* The entity form display associated with the given form mode.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0.
|
||||
* If the entity form display is available in configuration use:
|
||||
* @code
|
||||
* \Drupal::entityTypeManager()
|
||||
* ->getStorage('entity_form_display')
|
||||
* ->load($entity_type . '.' . $bundle . '.' . $form_mode);
|
||||
* @endcode
|
||||
* When the entity form display is not available in configuration, you can
|
||||
* create a new EntityFormDisplay object using:
|
||||
* @code
|
||||
* $values = array(
|
||||
* 'targetEntityType' => $entity_type,
|
||||
* 'bundle' => $bundle,
|
||||
* 'mode' => $form_mode,
|
||||
* 'status' => TRUE,
|
||||
* );
|
||||
* \Drupal::entityTypeManager()
|
||||
* ->getStorage('entity_form_display')
|
||||
* ->create($values);
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::create()
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::load()
|
||||
*/
|
||||
function entity_get_form_display($entity_type, $bundle, $form_mode) {
|
||||
// Try loading the entity from configuration.
|
||||
$entity_form_display = EntityFormDisplay::load($entity_type . '.' . $bundle . '.' . $form_mode);
|
||||
|
||||
// If not found, create a fresh entity object. We do not preemptively create
|
||||
// new entity form display configuration entries for each existing entity type
|
||||
// and bundle whenever a new form mode becomes available. Instead,
|
||||
// configuration entries are only created when an entity form display is
|
||||
// explicitly configured and saved.
|
||||
if (!$entity_form_display) {
|
||||
$entity_form_display = EntityFormDisplay::create(array(
|
||||
'targetEntityType' => $entity_type,
|
||||
'bundle' => $bundle,
|
||||
'mode' => $form_mode,
|
||||
'status' => TRUE,
|
||||
));
|
||||
}
|
||||
|
||||
return $entity_form_display;
|
||||
}
|
326
web/core/includes/errors.inc
Normal file
326
web/core/includes/errors.inc
Normal file
|
@ -0,0 +1,326 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Functions for error handling.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Logger\RfcLogLevel;
|
||||
use Drupal\Core\Render\Markup;
|
||||
use Drupal\Core\Utility\Error;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Maps PHP error constants to watchdog severity levels.
|
||||
*
|
||||
* The error constants are documented at
|
||||
* http://php.net/manual/errorfunc.constants.php
|
||||
*
|
||||
* @ingroup logging_severity_levels
|
||||
*/
|
||||
function drupal_error_levels() {
|
||||
$types = array(
|
||||
E_ERROR => array('Error', RfcLogLevel::ERROR),
|
||||
E_WARNING => array('Warning', RfcLogLevel::WARNING),
|
||||
E_PARSE => array('Parse error', RfcLogLevel::ERROR),
|
||||
E_NOTICE => array('Notice', RfcLogLevel::NOTICE),
|
||||
E_CORE_ERROR => array('Core error', RfcLogLevel::ERROR),
|
||||
E_CORE_WARNING => array('Core warning', RfcLogLevel::WARNING),
|
||||
E_COMPILE_ERROR => array('Compile error', RfcLogLevel::ERROR),
|
||||
E_COMPILE_WARNING => array('Compile warning', RfcLogLevel::WARNING),
|
||||
E_USER_ERROR => array('User error', RfcLogLevel::ERROR),
|
||||
E_USER_WARNING => array('User warning', RfcLogLevel::WARNING),
|
||||
E_USER_NOTICE => array('User notice', RfcLogLevel::NOTICE),
|
||||
E_STRICT => array('Strict warning', RfcLogLevel::DEBUG),
|
||||
E_RECOVERABLE_ERROR => array('Recoverable fatal error', RfcLogLevel::ERROR),
|
||||
E_DEPRECATED => array('Deprecated function', RfcLogLevel::DEBUG),
|
||||
E_USER_DEPRECATED => array('User deprecated function', RfcLogLevel::DEBUG),
|
||||
);
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides custom PHP error handling.
|
||||
*
|
||||
* @param $error_level
|
||||
* The level of the error raised.
|
||||
* @param $message
|
||||
* The error message.
|
||||
* @param $filename
|
||||
* The filename that the error was raised in.
|
||||
* @param $line
|
||||
* The line number the error was raised at.
|
||||
* @param $context
|
||||
* An array that points to the active symbol table at the point the error
|
||||
* occurred.
|
||||
*/
|
||||
function _drupal_error_handler_real($error_level, $message, $filename, $line, $context) {
|
||||
if ($error_level & error_reporting()) {
|
||||
$types = drupal_error_levels();
|
||||
list($severity_msg, $severity_level) = $types[$error_level];
|
||||
$backtrace = debug_backtrace();
|
||||
$caller = Error::getLastCaller($backtrace);
|
||||
|
||||
// We treat recoverable errors as fatal.
|
||||
$recoverable = $error_level == E_RECOVERABLE_ERROR;
|
||||
// As __toString() methods must not throw exceptions (recoverable errors)
|
||||
// in PHP, we allow them to trigger a fatal error by emitting a user error
|
||||
// using trigger_error().
|
||||
$to_string = $error_level == E_USER_ERROR && substr($caller['function'], -strlen('__toString()')) == '__toString()';
|
||||
_drupal_log_error(array(
|
||||
'%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error',
|
||||
// The standard PHP error handler considers that the error messages
|
||||
// are HTML. We mimick this behavior here.
|
||||
'@message' => Markup::create(Xss::filterAdmin($message)),
|
||||
'%function' => $caller['function'],
|
||||
'%file' => $caller['file'],
|
||||
'%line' => $caller['line'],
|
||||
'severity_level' => $severity_level,
|
||||
'backtrace' => $backtrace,
|
||||
'@backtrace_string' => (new \Exception())->getTraceAsString(),
|
||||
), $recoverable || $to_string);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an error should be displayed.
|
||||
*
|
||||
* When in maintenance mode or when error_level is ERROR_REPORTING_DISPLAY_ALL,
|
||||
* all errors should be displayed. For ERROR_REPORTING_DISPLAY_SOME, $error
|
||||
* will be examined to determine if it should be displayed.
|
||||
*
|
||||
* @param $error
|
||||
* Optional error to examine for ERROR_REPORTING_DISPLAY_SOME.
|
||||
*
|
||||
* @return
|
||||
* TRUE if an error should be displayed.
|
||||
*/
|
||||
function error_displayable($error = NULL) {
|
||||
if (defined('MAINTENANCE_MODE')) {
|
||||
return TRUE;
|
||||
}
|
||||
$error_level = _drupal_get_error_level();
|
||||
if ($error_level == ERROR_REPORTING_DISPLAY_ALL || $error_level == ERROR_REPORTING_DISPLAY_VERBOSE) {
|
||||
return TRUE;
|
||||
}
|
||||
if ($error_level == ERROR_REPORTING_DISPLAY_SOME && isset($error)) {
|
||||
return $error['%type'] != 'Notice' && $error['%type'] != 'Strict warning';
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a PHP error or exception and displays an error page in fatal cases.
|
||||
*
|
||||
* @param $error
|
||||
* An array with the following keys: %type, @message, %function, %file,
|
||||
* %line, @backtrace_string, severity_level, and backtrace. All the parameters
|
||||
* are plain-text, with the exception of @message, which needs to be an HTML
|
||||
* string, and backtrace, which is a standard PHP backtrace.
|
||||
* @param bool $fatal
|
||||
* TRUE for:
|
||||
* - An exception is thrown and not caught by something else.
|
||||
* - A recoverable fatal error, which is a fatal error.
|
||||
* Non-recoverable fatal errors cannot be logged by Drupal.
|
||||
*/
|
||||
function _drupal_log_error($error, $fatal = FALSE) {
|
||||
$is_installer = drupal_installation_attempted();
|
||||
|
||||
// Backtrace array is not a valid replacement value for t().
|
||||
$backtrace = $error['backtrace'];
|
||||
unset($error['backtrace']);
|
||||
|
||||
// When running inside the testing framework, we relay the errors
|
||||
// to the tested site by the way of HTTP headers.
|
||||
if (DRUPAL_TEST_IN_CHILD_SITE && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
|
||||
// $number does not use drupal_static as it should not be reset
|
||||
// as it uniquely identifies each PHP error.
|
||||
static $number = 0;
|
||||
$assertion = array(
|
||||
$error['@message'],
|
||||
$error['%type'],
|
||||
array(
|
||||
'function' => $error['%function'],
|
||||
'file' => $error['%file'],
|
||||
'line' => $error['%line'],
|
||||
),
|
||||
);
|
||||
// For non-fatal errors (e.g. PHP notices) _drupal_log_error can be called
|
||||
// multiple times per request. In that case the response is typically
|
||||
// generated outside of the error handler, e.g., in a controller. As a
|
||||
// result it is not possible to use a Response object here but instead the
|
||||
// headers need to be emitted directly.
|
||||
header('X-Drupal-Assertion-' . $number . ': ' . rawurlencode(serialize($assertion)));
|
||||
$number++;
|
||||
}
|
||||
|
||||
$response = new Response();
|
||||
|
||||
// Only call the logger if there is a logger factory available. This can occur
|
||||
// if there is an error while rebuilding the container or during the
|
||||
// installer.
|
||||
if (\Drupal::hasService('logger.factory')) {
|
||||
try {
|
||||
\Drupal::logger('php')->log($error['severity_level'], '%type: @message in %function (line %line of %file) @backtrace_string.', $error);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// We can't log, for example because the database connection is not
|
||||
// available. At least try to log to PHP error log.
|
||||
error_log(strtr('Failed to log error: %type: @message in %function (line %line of %file). @backtrace_string', $error));
|
||||
}
|
||||
}
|
||||
|
||||
// Log fatal errors, so developers can find and debug them.
|
||||
if ($fatal) {
|
||||
error_log(sprintf('%s: %s in %s on line %d %s', $error['%type'], $error['@message'], $error['%file'], $error['%line'], $error['@backtrace_string']));
|
||||
}
|
||||
|
||||
if (PHP_SAPI === 'cli') {
|
||||
if ($fatal) {
|
||||
// When called from CLI, simply output a plain text message.
|
||||
// Should not translate the string to avoid errors producing more errors.
|
||||
$response->setContent(html_entity_decode(strip_tags(SafeMarkup::format('%type: @message in %function (line %line of %file).', $error))) . "\n");
|
||||
$response->send();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (\Drupal::hasRequest() && \Drupal::request()->isXmlHttpRequest()) {
|
||||
if ($fatal) {
|
||||
if (error_displayable($error)) {
|
||||
// When called from JavaScript, simply output the error message.
|
||||
// Should not translate the string to avoid errors producing more errors.
|
||||
$response->setContent(SafeMarkup::format('%type: @message in %function (line %line of %file).', $error));
|
||||
$response->send();
|
||||
}
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Display the message if the current error reporting level allows this type
|
||||
// of message to be displayed, and unconditionally in update.php.
|
||||
$message = '';
|
||||
$class = NULL;
|
||||
if (error_displayable($error)) {
|
||||
$class = 'error';
|
||||
|
||||
// If error type is 'User notice' then treat it as debug information
|
||||
// instead of an error message.
|
||||
// @see debug()
|
||||
if ($error['%type'] == 'User notice') {
|
||||
$error['%type'] = 'Debug';
|
||||
$class = 'status';
|
||||
}
|
||||
|
||||
// Attempt to reduce verbosity by removing DRUPAL_ROOT from the file path
|
||||
// in the message. This does not happen for (false) security.
|
||||
if (\Drupal::hasService('app.root')) {
|
||||
$root_length = strlen(\Drupal::root());
|
||||
if (substr($error['%file'], 0, $root_length) == \Drupal::root()) {
|
||||
$error['%file'] = substr($error['%file'], $root_length + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if verbose error reporting is on.
|
||||
$error_level = _drupal_get_error_level();
|
||||
|
||||
if ($error_level != ERROR_REPORTING_DISPLAY_VERBOSE) {
|
||||
// Without verbose logging, use a simple message.
|
||||
|
||||
// We call SafeMarkup::format() directly here, rather than use t() since
|
||||
// we are in the middle of error handling, and we don't want t() to
|
||||
// cause further errors.
|
||||
$message = SafeMarkup::format('%type: @message in %function (line %line of %file).', $error);
|
||||
}
|
||||
else {
|
||||
// With verbose logging, we will also include a backtrace.
|
||||
|
||||
// First trace is the error itself, already contained in the message.
|
||||
// While the second trace is the error source and also contained in the
|
||||
// message, the message doesn't contain argument values, so we output it
|
||||
// once more in the backtrace.
|
||||
array_shift($backtrace);
|
||||
// Generate a backtrace containing only scalar argument values.
|
||||
$error['@backtrace'] = Error::formatBacktrace($backtrace);
|
||||
$message = SafeMarkup::format('%type: @message in %function (line %line of %file). <pre class="backtrace">@backtrace</pre>', $error);
|
||||
}
|
||||
}
|
||||
|
||||
if ($fatal) {
|
||||
// We fallback to a maintenance page at this point, because the page generation
|
||||
// itself can generate errors.
|
||||
// Should not translate the string to avoid errors producing more errors.
|
||||
$message = 'The website encountered an unexpected error. Please try again later.' . '<br />' . $message;
|
||||
|
||||
if ($is_installer) {
|
||||
// install_display_output() prints the output and ends script execution.
|
||||
$output = array(
|
||||
'#title' => 'Error',
|
||||
'#markup' => $message,
|
||||
);
|
||||
install_display_output($output, $GLOBALS['install_state'], $response->headers->all());
|
||||
exit;
|
||||
}
|
||||
|
||||
$response->setContent($message);
|
||||
$response->setStatusCode(500, '500 Service unavailable (with message)');
|
||||
|
||||
$response->send();
|
||||
// An exception must halt script execution.
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($message) {
|
||||
if (\Drupal::hasService('session')) {
|
||||
// Message display is dependent on sessions being available.
|
||||
drupal_set_message($message, $class, TRUE);
|
||||
}
|
||||
else {
|
||||
print $message;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current error level.
|
||||
*
|
||||
* This function should only be used to get the current error level prior to the
|
||||
* kernel being booted or before Drupal is installed. In all other situations
|
||||
* the following code is preferred:
|
||||
* @code
|
||||
* \Drupal::config('system.logging')->get('error_level');
|
||||
* @endcode
|
||||
*
|
||||
* @return string
|
||||
* The current error level.
|
||||
*/
|
||||
function _drupal_get_error_level() {
|
||||
// Raise the error level to maximum for the installer, so users are able to
|
||||
// file proper bug reports for installer errors. The returned value is
|
||||
// different to the one below, because the installer actually has a
|
||||
// 'config.factory' service, which reads the default 'error_level' value from
|
||||
// System module's default configuration and the default value is not verbose.
|
||||
// @see error_displayable()
|
||||
if (drupal_installation_attempted()) {
|
||||
return ERROR_REPORTING_DISPLAY_VERBOSE;
|
||||
}
|
||||
$error_level = NULL;
|
||||
// Try to get the error level configuration from database. If this fails,
|
||||
// for example if the database connection is not there, try to read it from
|
||||
// settings.php.
|
||||
try {
|
||||
$error_level = \Drupal::config('system.logging')->get('error_level');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$error_level = isset($GLOBALS['config']['system.logging']['error_level']) ? $GLOBALS['config']['system.logging']['error_level'] : ERROR_REPORTING_HIDE;
|
||||
}
|
||||
|
||||
// If there is no container or if it has no config.factory service, we are
|
||||
// possibly in an edge-case error situation while trying to serve a regular
|
||||
// request on a public site, so use the non-verbose default value.
|
||||
return $error_level ?: ERROR_REPORTING_DISPLAY_ALL;
|
||||
}
|
1228
web/core/includes/file.inc
Normal file
1228
web/core/includes/file.inc
Normal file
File diff suppressed because it is too large
Load diff
952
web/core/includes/form.inc
Normal file
952
web/core/includes/form.inc
Normal file
|
@ -0,0 +1,952 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Functions for form and batch generation and processing.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Render\Element\RenderElement;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
/**
|
||||
* Prepares variables for select element templates.
|
||||
*
|
||||
* Default template: select.html.twig.
|
||||
*
|
||||
* It is possible to group options together; to do this, change the format of
|
||||
* $options to an associative array in which the keys are group labels, and the
|
||||
* values are associative arrays in the normal $options format.
|
||||
*
|
||||
* @param $variables
|
||||
* An associative array containing:
|
||||
* - element: An associative array containing the properties of the element.
|
||||
* Properties used: #title, #value, #options, #description, #extra,
|
||||
* #multiple, #required, #name, #attributes, #size.
|
||||
*/
|
||||
function template_preprocess_select(&$variables) {
|
||||
$element = $variables['element'];
|
||||
Element::setAttributes($element, array('id', 'name', 'size'));
|
||||
RenderElement::setAttributes($element, array('form-select'));
|
||||
|
||||
$variables['attributes'] = $element['#attributes'];
|
||||
$variables['options'] = form_select_options($element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an options form element into a structured array for output.
|
||||
*
|
||||
* This function calls itself recursively to obtain the values for each optgroup
|
||||
* within the list of options and when the function encounters an object with
|
||||
* an 'options' property inside $element['#options'].
|
||||
*
|
||||
* @param array $element
|
||||
* An associative array containing the following key-value pairs:
|
||||
* - #multiple: Optional Boolean indicating if the user may select more than
|
||||
* one item.
|
||||
* - #options: An associative array of options to render as HTML. Each array
|
||||
* value can be a string, an array, or an object with an 'option' property:
|
||||
* - A string or integer key whose value is a translated string is
|
||||
* interpreted as a single HTML option element. Do not use placeholders
|
||||
* that sanitize data: doing so will lead to double-escaping. Note that
|
||||
* the key will be visible in the HTML and could be modified by malicious
|
||||
* users, so don't put sensitive information in it.
|
||||
* - A translated string key whose value is an array indicates a group of
|
||||
* options. The translated string is used as the label attribute for the
|
||||
* optgroup. Do not use placeholders to sanitize data: doing so will lead
|
||||
* to double-escaping. The array should contain the options you wish to
|
||||
* group and should follow the syntax of $element['#options'].
|
||||
* - If the function encounters a string or integer key whose value is an
|
||||
* object with an 'option' property, the key is ignored, the contents of
|
||||
* the option property are interpreted as $element['#options'], and the
|
||||
* resulting HTML is added to the output.
|
||||
* - #value: Optional integer, string, or array representing which option(s)
|
||||
* to pre-select when the list is first displayed. The integer or string
|
||||
* must match the key of an option in the '#options' list. If '#multiple' is
|
||||
* TRUE, this can be an array of integers or strings.
|
||||
* @param array|null $choices
|
||||
* (optional) Either an associative array of options in the same format as
|
||||
* $element['#options'] above, or NULL. This parameter is only used internally
|
||||
* and is not intended to be passed in to the initial function call.
|
||||
*
|
||||
* @return mixed[]
|
||||
* A structured, possibly nested, array of options and optgroups for use in a
|
||||
* select form element.
|
||||
* - label: A translated string whose value is the text of a single HTML
|
||||
* option element, or the label attribute for an optgroup.
|
||||
* - options: Optional, array of options for an optgroup.
|
||||
* - selected: A boolean that indicates whether the option is selected when
|
||||
* rendered.
|
||||
* - type: A string that defines the element type. The value can be 'option'
|
||||
* or 'optgroup'.
|
||||
* - value: A string that contains the value attribute for the option.
|
||||
*/
|
||||
function form_select_options($element, $choices = NULL) {
|
||||
if (!isset($choices)) {
|
||||
if (empty($element['#options'])) {
|
||||
return [];
|
||||
}
|
||||
$choices = $element['#options'];
|
||||
}
|
||||
// array_key_exists() accommodates the rare event where $element['#value'] is NULL.
|
||||
// isset() fails in this situation.
|
||||
$value_valid = isset($element['#value']) || array_key_exists('#value', $element);
|
||||
$value_is_array = $value_valid && is_array($element['#value']);
|
||||
// Check if the element is multiple select and no value has been selected.
|
||||
$empty_value = (empty($element['#value']) && !empty($element['#multiple']));
|
||||
$options = [];
|
||||
foreach ($choices as $key => $choice) {
|
||||
if (is_array($choice)) {
|
||||
$options[] = [
|
||||
'type' => 'optgroup',
|
||||
'label' => $key,
|
||||
'options' => form_select_options($element, $choice),
|
||||
];
|
||||
}
|
||||
elseif (is_object($choice) && isset($choice->option)) {
|
||||
$options = array_merge($options, form_select_options($element, $choice->option));
|
||||
}
|
||||
else {
|
||||
$option = [];
|
||||
$key = (string) $key;
|
||||
$empty_choice = $empty_value && $key == '_none';
|
||||
if ($value_valid && ((!$value_is_array && (string) $element['#value'] === $key || ($value_is_array && in_array($key, $element['#value']))) || $empty_choice)) {
|
||||
$option['selected'] = TRUE;
|
||||
}
|
||||
else {
|
||||
$option['selected'] = FALSE;
|
||||
}
|
||||
$option['type'] = 'option';
|
||||
$option['value'] = $key;
|
||||
$option['label'] = $choice;
|
||||
$options[] = $option;
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the indexes of a select element's options matching a given key.
|
||||
*
|
||||
* This function is useful if you need to modify the options that are
|
||||
* already in a form element; for example, to remove choices which are
|
||||
* not valid because of additional filters imposed by another module.
|
||||
* One example might be altering the choices in a taxonomy selector.
|
||||
* To correctly handle the case of a multiple hierarchy taxonomy,
|
||||
* #options arrays can now hold an array of objects, instead of a
|
||||
* direct mapping of keys to labels, so that multiple choices in the
|
||||
* selector can have the same key (and label). This makes it difficult
|
||||
* to manipulate directly, which is why this helper function exists.
|
||||
*
|
||||
* This function does not support optgroups (when the elements of the
|
||||
* #options array are themselves arrays), and will return FALSE if
|
||||
* arrays are found. The caller must either flatten/restore or
|
||||
* manually do their manipulations in this case, since returning the
|
||||
* index is not sufficient, and supporting this would make the
|
||||
* "helper" too complicated and cumbersome to be of any help.
|
||||
*
|
||||
* As usual with functions that can return array() or FALSE, do not
|
||||
* forget to use === and !== if needed.
|
||||
*
|
||||
* @param $element
|
||||
* The select element to search.
|
||||
* @param $key
|
||||
* The key to look for.
|
||||
*
|
||||
* @return
|
||||
* An array of indexes that match the given $key. Array will be
|
||||
* empty if no elements were found. FALSE if optgroups were found.
|
||||
*/
|
||||
function form_get_options($element, $key) {
|
||||
$keys = array();
|
||||
foreach ($element['#options'] as $index => $choice) {
|
||||
if (is_array($choice)) {
|
||||
return FALSE;
|
||||
}
|
||||
elseif (is_object($choice)) {
|
||||
if (isset($choice->option[$key])) {
|
||||
$keys[] = $index;
|
||||
}
|
||||
}
|
||||
elseif ($index == $key) {
|
||||
$keys[] = $index;
|
||||
}
|
||||
}
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for fieldset element templates.
|
||||
*
|
||||
* Default template: fieldset.html.twig.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - element: An associative array containing the properties of the element.
|
||||
* Properties used: #attributes, #children, #description, #id, #title,
|
||||
* #value.
|
||||
*/
|
||||
function template_preprocess_fieldset(&$variables) {
|
||||
$element = $variables['element'];
|
||||
Element::setAttributes($element, array('id'));
|
||||
RenderElement::setAttributes($element);
|
||||
$variables['attributes'] = isset($element['#attributes']) ? $element['#attributes'] : array();
|
||||
$variables['prefix'] = isset($element['#field_prefix']) ? $element['#field_prefix'] : NULL;
|
||||
$variables['suffix'] = isset($element['#field_suffix']) ? $element['#field_suffix'] : NULL;
|
||||
$variables['title_display'] = isset($element['#title_display']) ? $element['#title_display'] : NULL;
|
||||
$variables['children'] = $element['#children'];
|
||||
$variables['required'] = !empty($element['#required']) ? $element['#required'] : NULL;
|
||||
|
||||
if (isset($element['#title']) && $element['#title'] !== '') {
|
||||
$variables['legend']['title'] = ['#markup' => $element['#title']];
|
||||
}
|
||||
|
||||
$variables['legend']['attributes'] = new Attribute();
|
||||
// Add 'visually-hidden' class to legend span.
|
||||
if ($variables['title_display'] == 'invisible') {
|
||||
$variables['legend_span']['attributes'] = new Attribute(array('class' => 'visually-hidden'));
|
||||
}
|
||||
else {
|
||||
$variables['legend_span']['attributes'] = new Attribute();
|
||||
}
|
||||
|
||||
if (!empty($element['#description'])) {
|
||||
$description_id = $element['#attributes']['id'] . '--description';
|
||||
$description_attributes['id'] = $description_id;
|
||||
$variables['description']['attributes'] = new Attribute($description_attributes);
|
||||
$variables['description']['content'] = $element['#description'];
|
||||
|
||||
// Add the description's id to the fieldset aria attributes.
|
||||
$variables['attributes']['aria-describedby'] = $description_id;
|
||||
}
|
||||
|
||||
// Suppress error messages.
|
||||
$variables['errors'] = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for details element templates.
|
||||
*
|
||||
* Default template: details.html.twig.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - element: An associative array containing the properties of the element.
|
||||
* Properties used: #attributes, #children, #open,
|
||||
* #description, #id, #title, #value, #optional.
|
||||
*/
|
||||
function template_preprocess_details(&$variables) {
|
||||
$element = $variables['element'];
|
||||
$variables['attributes'] = $element['#attributes'];
|
||||
$variables['summary_attributes'] = new Attribute();
|
||||
if (!empty($element['#title'])) {
|
||||
$variables['summary_attributes']['role'] = 'button';
|
||||
if (!empty($element['#attributes']['id'])) {
|
||||
$variables['summary_attributes']['aria-controls'] = $element['#attributes']['id'];
|
||||
}
|
||||
$variables['summary_attributes']['aria-expanded'] = !empty($element['#attributes']['open']) ? 'true' : 'false';
|
||||
$variables['summary_attributes']['aria-pressed'] = $variables['summary_attributes']['aria-expanded'];
|
||||
}
|
||||
$variables['title'] = (!empty($element['#title'])) ? $element['#title'] : '';
|
||||
$variables['description'] = (!empty($element['#description'])) ? $element['#description'] : '';
|
||||
$variables['children'] = (isset($element['#children'])) ? $element['#children'] : '';
|
||||
$variables['value'] = (isset($element['#value'])) ? $element['#value'] : '';
|
||||
$variables['required'] = !empty($element['#required']) ? $element['#required'] : NULL;
|
||||
|
||||
// Suppress error messages.
|
||||
$variables['errors'] = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for radios templates.
|
||||
*
|
||||
* Default template: radios.html.twig.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - element: An associative array containing the properties of the element.
|
||||
* Properties used: #title, #value, #options, #description, #required,
|
||||
* #attributes, #children.
|
||||
*/
|
||||
function template_preprocess_radios(&$variables) {
|
||||
$element = $variables['element'];
|
||||
$variables['attributes'] = array();
|
||||
if (isset($element['#id'])) {
|
||||
$variables['attributes']['id'] = $element['#id'];
|
||||
}
|
||||
if (isset($element['#attributes']['title'])) {
|
||||
$variables['attributes']['title'] = $element['#attributes']['title'];
|
||||
}
|
||||
$variables['children'] = $element['#children'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for checkboxes templates.
|
||||
*
|
||||
* Default template: checkboxes.html.twig.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - element: An associative array containing the properties of the element.
|
||||
* Properties used: #children, #attributes.
|
||||
*/
|
||||
function template_preprocess_checkboxes(&$variables) {
|
||||
$element = $variables['element'];
|
||||
$variables['attributes'] = array();
|
||||
if (isset($element['#id'])) {
|
||||
$variables['attributes']['id'] = $element['#id'];
|
||||
}
|
||||
if (isset($element['#attributes']['title'])) {
|
||||
$variables['attributes']['title'] = $element['#attributes']['title'];
|
||||
}
|
||||
$variables['children'] = $element['#children'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for vertical tabs templates.
|
||||
*
|
||||
* Default template: vertical-tabs.html.twig.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - element: An associative array containing the properties and children of
|
||||
* the details element. Properties used: #children.
|
||||
*/
|
||||
function template_preprocess_vertical_tabs(&$variables) {
|
||||
$element = $variables['element'];
|
||||
$variables['children'] = (!empty($element['#children'])) ? $element['#children'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for input templates.
|
||||
*
|
||||
* Default template: input.html.twig.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - element: An associative array containing the properties of the element.
|
||||
* Properties used: #attributes.
|
||||
*/
|
||||
function template_preprocess_input(&$variables) {
|
||||
$element = $variables['element'];
|
||||
// Remove name attribute if empty, for W3C compliance.
|
||||
if (isset($variables['attributes']['name']) && empty((string) $variables['attributes']['name'])) {
|
||||
unset($variables['attributes']['name']);
|
||||
}
|
||||
$variables['children'] = $element['#children'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for form templates.
|
||||
*
|
||||
* Default template: form.html.twig.
|
||||
*
|
||||
* @param $variables
|
||||
* An associative array containing:
|
||||
* - element: An associative array containing the properties of the element.
|
||||
* Properties used: #action, #method, #attributes, #children
|
||||
*/
|
||||
function template_preprocess_form(&$variables) {
|
||||
$element = $variables['element'];
|
||||
if (isset($element['#action'])) {
|
||||
$element['#attributes']['action'] = UrlHelper::stripDangerousProtocols($element['#action']);
|
||||
}
|
||||
Element::setAttributes($element, array('method', 'id'));
|
||||
if (empty($element['#attributes']['accept-charset'])) {
|
||||
$element['#attributes']['accept-charset'] = "UTF-8";
|
||||
}
|
||||
$variables['attributes'] = $element['#attributes'];
|
||||
$variables['children'] = $element['#children'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for textarea templates.
|
||||
*
|
||||
* Default template: textarea.html.twig.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - element: An associative array containing the properties of the element.
|
||||
* Properties used: #title, #value, #description, #rows, #cols,
|
||||
* #placeholder, #required, #attributes, #resizable
|
||||
*/
|
||||
function template_preprocess_textarea(&$variables) {
|
||||
$element = $variables['element'];
|
||||
Element::setAttributes($element, array('id', 'name', 'rows', 'cols', 'placeholder'));
|
||||
RenderElement::setAttributes($element, array('form-textarea'));
|
||||
$variables['wrapper_attributes'] = new Attribute();
|
||||
$variables['attributes'] = new Attribute($element['#attributes']);
|
||||
$variables['value'] = $element['#value'];
|
||||
$variables['resizable'] = !empty($element['#resizable']) ? $element['#resizable'] : NULL;
|
||||
$variables['required'] = !empty($element['#required']) ? $element['#required'] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML for a form element.
|
||||
* Prepares variables for form element templates.
|
||||
*
|
||||
* Default template: form-element.html.twig.
|
||||
*
|
||||
* In addition to the element itself, the DIV contains a label for the element
|
||||
* based on the optional #title_display property, and an optional #description.
|
||||
*
|
||||
* The optional #title_display property can have these values:
|
||||
* - before: The label is output before the element. This is the default.
|
||||
* The label includes the #title and the required marker, if #required.
|
||||
* - after: The label is output after the element. For example, this is used
|
||||
* for radio and checkbox #type elements. If the #title is empty but the field
|
||||
* is #required, the label will contain only the required marker.
|
||||
* - invisible: Labels are critical for screen readers to enable them to
|
||||
* properly navigate through forms but can be visually distracting. This
|
||||
* property hides the label for everyone except screen readers.
|
||||
* - attribute: Set the title attribute on the element to create a tooltip
|
||||
* but output no label element. This is supported only for checkboxes
|
||||
* and radios in
|
||||
* \Drupal\Core\Render\Element\CompositeFormElementTrait::preRenderCompositeFormElement().
|
||||
* It is used where a visual label is not needed, such as a table of
|
||||
* checkboxes where the row and column provide the context. The tooltip will
|
||||
* include the title and required marker.
|
||||
*
|
||||
* If the #title property is not set, then the label and any required marker
|
||||
* will not be output, regardless of the #title_display or #required values.
|
||||
* This can be useful in cases such as the password_confirm element, which
|
||||
* creates children elements that have their own labels and required markers,
|
||||
* but the parent element should have neither. Use this carefully because a
|
||||
* field without an associated label can cause accessibility challenges.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - element: An associative array containing the properties of the element.
|
||||
* Properties used: #title, #title_display, #description, #id, #required,
|
||||
* #children, #type, #name.
|
||||
*/
|
||||
function template_preprocess_form_element(&$variables) {
|
||||
$element = &$variables['element'];
|
||||
|
||||
// This function is invoked as theme wrapper, but the rendered form element
|
||||
// may not necessarily have been processed by
|
||||
// \Drupal::formBuilder()->doBuildForm().
|
||||
$element += array(
|
||||
'#title_display' => 'before',
|
||||
'#wrapper_attributes' => array(),
|
||||
);
|
||||
$variables['attributes'] = $element['#wrapper_attributes'];
|
||||
|
||||
// Add element #id for #type 'item'.
|
||||
if (isset($element['#markup']) && !empty($element['#id'])) {
|
||||
$variables['attributes']['id'] = $element['#id'];
|
||||
}
|
||||
|
||||
// Pass elements #type and #name to template.
|
||||
if (!empty($element['#type'])) {
|
||||
$variables['type'] = $element['#type'];
|
||||
}
|
||||
if (!empty($element['#name'])) {
|
||||
$variables['name'] = $element['#name'];
|
||||
}
|
||||
|
||||
// Pass elements disabled status to template.
|
||||
$variables['disabled'] = !empty($element['#attributes']['disabled']) ? $element['#attributes']['disabled'] : NULL;
|
||||
|
||||
// Suppress error messages.
|
||||
$variables['errors'] = NULL;
|
||||
|
||||
// If #title is not set, we don't display any label.
|
||||
if (!isset($element['#title'])) {
|
||||
$element['#title_display'] = 'none';
|
||||
}
|
||||
|
||||
$variables['title_display'] = $element['#title_display'];
|
||||
|
||||
$variables['prefix'] = isset($element['#field_prefix']) ? $element['#field_prefix'] : NULL;
|
||||
$variables['suffix'] = isset($element['#field_suffix']) ? $element['#field_suffix'] : NULL;
|
||||
|
||||
$variables['description'] = NULL;
|
||||
if (!empty($element['#description'])) {
|
||||
$variables['description_display'] = $element['#description_display'];
|
||||
$description_attributes = [];
|
||||
if (!empty($element['#id'])) {
|
||||
$description_attributes['id'] = $element['#id'] . '--description';
|
||||
}
|
||||
$variables['description']['attributes'] = new Attribute($description_attributes);
|
||||
$variables['description']['content'] = $element['#description'];
|
||||
}
|
||||
|
||||
// Add label_display and label variables to template.
|
||||
$variables['label_display'] = $element['#title_display'];
|
||||
$variables['label'] = array('#theme' => 'form_element_label');
|
||||
$variables['label'] += array_intersect_key($element, array_flip(array('#id', '#required', '#title', '#title_display')));
|
||||
|
||||
$variables['children'] = $element['#children'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for form label templates.
|
||||
*
|
||||
* Form element labels include the #title and a #required marker. The label is
|
||||
* associated with the element itself by the element #id. Labels may appear
|
||||
* before or after elements, depending on form-element.html.twig and
|
||||
* #title_display.
|
||||
*
|
||||
* This function will not be called for elements with no labels, depending on
|
||||
* #title_display. For elements that have an empty #title and are not required,
|
||||
* this function will output no label (''). For required elements that have an
|
||||
* empty #title, this will output the required marker alone within the label.
|
||||
* The label will use the #id to associate the marker with the field that is
|
||||
* required. That is especially important for screenreader users to know
|
||||
* which field is required.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - element: An associative array containing the properties of the element.
|
||||
* Properties used: #required, #title, #id, #value, #description.
|
||||
*/
|
||||
function template_preprocess_form_element_label(&$variables) {
|
||||
$element = $variables['element'];
|
||||
// If title and required marker are both empty, output no label.
|
||||
if (isset($element['#title']) && $element['#title'] !== '') {
|
||||
$variables['title'] = ['#markup' => $element['#title']];
|
||||
}
|
||||
|
||||
// Pass elements title_display to template.
|
||||
$variables['title_display'] = $element['#title_display'];
|
||||
|
||||
// A #for property of a dedicated #type 'label' element as precedence.
|
||||
if (!empty($element['#for'])) {
|
||||
$variables['attributes']['for'] = $element['#for'];
|
||||
// A custom #id allows the referenced form input element to refer back to
|
||||
// the label element; e.g., in the 'aria-labelledby' attribute.
|
||||
if (!empty($element['#id'])) {
|
||||
$variables['attributes']['id'] = $element['#id'];
|
||||
}
|
||||
}
|
||||
// Otherwise, point to the #id of the form input element.
|
||||
elseif (!empty($element['#id'])) {
|
||||
$variables['attributes']['for'] = $element['#id'];
|
||||
}
|
||||
|
||||
// Pass elements required to template.
|
||||
$variables['required'] = !empty($element['#required']) ? $element['#required'] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @defgroup batch Batch operations
|
||||
* @{
|
||||
* Creates and processes batch operations.
|
||||
*
|
||||
* Functions allowing forms processing to be spread out over several page
|
||||
* requests, thus ensuring that the processing does not get interrupted
|
||||
* because of a PHP timeout, while allowing the user to receive feedback
|
||||
* on the progress of the ongoing operations.
|
||||
*
|
||||
* The API is primarily designed to integrate nicely with the Form API
|
||||
* workflow, but can also be used by non-Form API scripts (like update.php)
|
||||
* or even simple page callbacks (which should probably be used sparingly).
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* $batch = array(
|
||||
* 'title' => t('Exporting'),
|
||||
* 'operations' => array(
|
||||
* array('my_function_1', array($account->id(), 'story')),
|
||||
* array('my_function_2', array()),
|
||||
* ),
|
||||
* 'finished' => 'my_finished_callback',
|
||||
* 'file' => 'path_to_file_containing_myfunctions',
|
||||
* );
|
||||
* batch_set($batch);
|
||||
* // Only needed if not inside a form _submit handler.
|
||||
* // Setting redirect in batch_process.
|
||||
* batch_process('node/1');
|
||||
* @endcode
|
||||
*
|
||||
* Note: if the batch 'title', 'init_message', 'progress_message', or
|
||||
* 'error_message' could contain any user input, it is the responsibility of
|
||||
* the code calling batch_set() to sanitize them first with a function like
|
||||
* \Drupal\Component\Utility\SafeMarkup::checkPlain() or
|
||||
* \Drupal\Component\Utility\Xss::filter(). Furthermore, if the batch operation
|
||||
* returns any user input in the 'results' or 'message' keys of $context, it
|
||||
* must also sanitize them first.
|
||||
*
|
||||
* Sample callback_batch_operation():
|
||||
* @code
|
||||
* // Simple and artificial: load a node of a given type for a given user
|
||||
* function my_function_1($uid, $type, &$context) {
|
||||
* // The $context array gathers batch context information about the execution (read),
|
||||
* // as well as 'return values' for the current operation (write)
|
||||
* // The following keys are provided :
|
||||
* // 'results' (read / write): The array of results gathered so far by
|
||||
* // the batch processing, for the current operation to append its own.
|
||||
* // 'message' (write): A text message displayed in the progress page.
|
||||
* // The following keys allow for multi-step operations :
|
||||
* // 'sandbox' (read / write): An array that can be freely used to
|
||||
* // store persistent data between iterations. It is recommended to
|
||||
* // use this instead of $_SESSION, which is unsafe if the user
|
||||
* // continues browsing in a separate window while the batch is processing.
|
||||
* // 'finished' (write): A float number between 0 and 1 informing
|
||||
* // the processing engine of the completion level for the operation.
|
||||
* // 1 (or no value explicitly set) means the operation is finished
|
||||
* // and the batch processing can continue to the next operation.
|
||||
*
|
||||
* $nodes = \Drupal::entityTypeManager()->getStorage('node')
|
||||
* ->loadByProperties(['uid' => $uid, 'type' => $type]);
|
||||
* $node = reset($nodes);
|
||||
* $context['results'][] = $node->id() . ' : ' . SafeMarkup::checkPlain($node->label());
|
||||
* $context['message'] = SafeMarkup::checkPlain($node->label());
|
||||
* }
|
||||
*
|
||||
* // A more advanced example is a multi-step operation that loads all rows,
|
||||
* // five by five.
|
||||
* function my_function_2(&$context) {
|
||||
* if (empty($context['sandbox'])) {
|
||||
* $context['sandbox']['progress'] = 0;
|
||||
* $context['sandbox']['current_id'] = 0;
|
||||
* $context['sandbox']['max'] = db_query('SELECT COUNT(DISTINCT id) FROM {example}')->fetchField();
|
||||
* }
|
||||
* $limit = 5;
|
||||
* $result = db_select('example')
|
||||
* ->fields('example', array('id'))
|
||||
* ->condition('id', $context['sandbox']['current_id'], '>')
|
||||
* ->orderBy('id')
|
||||
* ->range(0, $limit)
|
||||
* ->execute();
|
||||
* foreach ($result as $row) {
|
||||
* $context['results'][] = $row->id . ' : ' . SafeMarkup::checkPlain($row->title);
|
||||
* $context['sandbox']['progress']++;
|
||||
* $context['sandbox']['current_id'] = $row->id;
|
||||
* $context['message'] = SafeMarkup::checkPlain($row->title);
|
||||
* }
|
||||
* if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
|
||||
* $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* Sample callback_batch_finished():
|
||||
* @code
|
||||
* function my_finished_callback($success, $results, $operations) {
|
||||
* // The 'success' parameter means no fatal PHP errors were detected. All
|
||||
* // other error management should be handled using 'results'.
|
||||
* if ($success) {
|
||||
* $message = \Drupal::translation()->formatPlural(count($results), 'One post processed.', '@count posts processed.');
|
||||
* }
|
||||
* else {
|
||||
* $message = t('Finished with an error.');
|
||||
* }
|
||||
* drupal_set_message($message);
|
||||
* // Providing data for the redirected page is done through $_SESSION.
|
||||
* foreach ($results as $result) {
|
||||
* $items[] = t('Loaded node %title.', array('%title' => $result));
|
||||
* }
|
||||
* $_SESSION['my_batch_results'] = $items;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds a new batch.
|
||||
*
|
||||
* Batch operations are added as new batch sets. Batch sets are used to spread
|
||||
* processing (primarily, but not exclusively, forms processing) over several
|
||||
* page requests. This helps to ensure that the processing is not interrupted
|
||||
* due to PHP timeouts, while users are still able to receive feedback on the
|
||||
* progress of the ongoing operations. Combining related operations into
|
||||
* distinct batch sets provides clean code independence for each batch set,
|
||||
* ensuring that two or more batches, submitted independently, can be processed
|
||||
* without mutual interference. Each batch set may specify its own set of
|
||||
* operations and results, produce its own UI messages, and trigger its own
|
||||
* 'finished' callback. Batch sets are processed sequentially, with the progress
|
||||
* bar starting afresh for each new set.
|
||||
*
|
||||
* @param $batch_definition
|
||||
* An associative array defining the batch, with the following elements (all
|
||||
* are optional except as noted):
|
||||
* - operations: (required) Array of operations to be performed, where each
|
||||
* item is an array consisting of the name of an implementation of
|
||||
* callback_batch_operation() and an array of parameter.
|
||||
* Example:
|
||||
* @code
|
||||
* array(
|
||||
* array('callback_batch_operation_1', array($arg1)),
|
||||
* array('callback_batch_operation_2', array($arg2_1, $arg2_2)),
|
||||
* )
|
||||
* @endcode
|
||||
* - title: A safe, translated string to use as the title for the progress
|
||||
* page. Defaults to t('Processing').
|
||||
* - init_message: Message displayed while the processing is initialized.
|
||||
* Defaults to t('Initializing.').
|
||||
* - progress_message: Message displayed while processing the batch. Available
|
||||
* placeholders are @current, @remaining, @total, @percentage, @estimate and
|
||||
* @elapsed. Defaults to t('Completed @current of @total.').
|
||||
* - error_message: Message displayed if an error occurred while processing
|
||||
* the batch. Defaults to t('An error has occurred.').
|
||||
* - finished: Name of an implementation of callback_batch_finished(). This is
|
||||
* executed after the batch has completed. This should be used to perform
|
||||
* any result massaging that may be needed, and possibly save data in
|
||||
* $_SESSION for display after final page redirection.
|
||||
* - file: Path to the file containing the definitions of the 'operations' and
|
||||
* 'finished' functions, for instance if they don't reside in the main
|
||||
* .module file. The path should be relative to base_path(), and thus should
|
||||
* be built using drupal_get_path().
|
||||
* - css: Array of paths to CSS files to be used on the progress page.
|
||||
* - url_options: options passed to the \Drupal\Core\Url object when
|
||||
* constructing redirect URLs for the batch.
|
||||
* - progressive: A Boolean that indicates whether or not the batch needs to
|
||||
* run progressively. TRUE indicates that the batch will run in more than
|
||||
* one run. FALSE (default) indicates that the batch will finish in a single
|
||||
* run.
|
||||
* - queue: An override of the default queue (with name and class fields
|
||||
* optional). An array containing two elements:
|
||||
* - name: Unique identifier for the queue.
|
||||
* - class: The name of a class that implements
|
||||
* \Drupal\Core\Queue\QueueInterface, including the full namespace but not
|
||||
* starting with a backslash. It must have a constructor with two
|
||||
* arguments: $name and a \Drupal\Core\Database\Connection object.
|
||||
* Typically, the class will either be \Drupal\Core\Queue\Batch or
|
||||
* \Drupal\Core\Queue\BatchMemory. Defaults to Batch if progressive is
|
||||
* TRUE, or to BatchMemory if progressive is FALSE.
|
||||
*/
|
||||
function batch_set($batch_definition) {
|
||||
if ($batch_definition) {
|
||||
$batch =& batch_get();
|
||||
|
||||
// Initialize the batch if needed.
|
||||
if (empty($batch)) {
|
||||
$batch = array(
|
||||
'sets' => array(),
|
||||
'has_form_submits' => FALSE,
|
||||
);
|
||||
}
|
||||
|
||||
// Base and default properties for the batch set.
|
||||
$init = array(
|
||||
'sandbox' => array(),
|
||||
'results' => array(),
|
||||
'success' => FALSE,
|
||||
'start' => 0,
|
||||
'elapsed' => 0,
|
||||
);
|
||||
$defaults = array(
|
||||
'title' => t('Processing'),
|
||||
'init_message' => t('Initializing.'),
|
||||
'progress_message' => t('Completed @current of @total.'),
|
||||
'error_message' => t('An error has occurred.'),
|
||||
'css' => array(),
|
||||
);
|
||||
$batch_set = $init + $batch_definition + $defaults;
|
||||
|
||||
// Tweak init_message to avoid the bottom of the page flickering down after
|
||||
// init phase.
|
||||
$batch_set['init_message'] .= '<br/> ';
|
||||
|
||||
// The non-concurrent workflow of batch execution allows us to save
|
||||
// numberOfItems() queries by handling our own counter.
|
||||
$batch_set['total'] = count($batch_set['operations']);
|
||||
$batch_set['count'] = $batch_set['total'];
|
||||
|
||||
// Add the set to the batch.
|
||||
if (empty($batch['id'])) {
|
||||
// The batch is not running yet. Simply add the new set.
|
||||
$batch['sets'][] = $batch_set;
|
||||
}
|
||||
else {
|
||||
// The set is being added while the batch is running. Insert the new set
|
||||
// right after the current one to ensure execution order, and store its
|
||||
// operations in a queue.
|
||||
$index = $batch['current_set'] + 1;
|
||||
$slice1 = array_slice($batch['sets'], 0, $index);
|
||||
$slice2 = array_slice($batch['sets'], $index);
|
||||
$batch['sets'] = array_merge($slice1, array($batch_set), $slice2);
|
||||
_batch_populate_queue($batch, $index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the batch.
|
||||
*
|
||||
* This function is generally not needed in form submit handlers;
|
||||
* Form API takes care of batches that were set during form submission.
|
||||
*
|
||||
* @param \Drupal\Core\Url|string $redirect
|
||||
* (optional) Either path or Url object to redirect to when the batch has
|
||||
* finished processing. Note that to simply force a batch to (conditionally)
|
||||
* redirect to a custom location after it is finished processing but to
|
||||
* otherwise allow the standard form API batch handling to occur, it is not
|
||||
* necessary to call batch_process() and use this parameter. Instead, make
|
||||
* the batch 'finished' callback return an instance of
|
||||
* \Symfony\Component\HttpFoundation\RedirectResponse, which will be used
|
||||
* automatically by the standard batch processing pipeline (and which takes
|
||||
* precedence over this parameter).
|
||||
* @param \Drupal\Core\Url $url
|
||||
* (optional - should only be used for separate scripts like update.php)
|
||||
* URL of the batch processing page.
|
||||
* @param $redirect_callback
|
||||
* (optional) Specify a function to be called to redirect to the progressive
|
||||
* processing page.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse|null
|
||||
* A redirect response if the batch is progressive. No return value otherwise.
|
||||
*/
|
||||
function batch_process($redirect = NULL, Url $url = NULL, $redirect_callback = NULL) {
|
||||
$batch =& batch_get();
|
||||
|
||||
if (isset($batch)) {
|
||||
// Add process information
|
||||
$process_info = array(
|
||||
'current_set' => 0,
|
||||
'progressive' => TRUE,
|
||||
'url' => isset($url) ? $url : Url::fromRoute('system.batch_page.html'),
|
||||
'source_url' => Url::fromRouteMatch(\Drupal::routeMatch()),
|
||||
'batch_redirect' => $redirect,
|
||||
'theme' => \Drupal::theme()->getActiveTheme()->getName(),
|
||||
'redirect_callback' => $redirect_callback,
|
||||
);
|
||||
$batch += $process_info;
|
||||
|
||||
// The batch is now completely built. Allow other modules to make changes
|
||||
// to the batch so that it is easier to reuse batch processes in other
|
||||
// environments.
|
||||
\Drupal::moduleHandler()->alter('batch', $batch);
|
||||
|
||||
// Assign an arbitrary id: don't rely on a serial column in the 'batch'
|
||||
// table, since non-progressive batches skip database storage completely.
|
||||
$batch['id'] = db_next_id();
|
||||
|
||||
// Move operations to a job queue. Non-progressive batches will use a
|
||||
// memory-based queue.
|
||||
foreach ($batch['sets'] as $key => $batch_set) {
|
||||
_batch_populate_queue($batch, $key);
|
||||
}
|
||||
|
||||
// Initiate processing.
|
||||
if ($batch['progressive']) {
|
||||
// Now that we have a batch id, we can generate the redirection link in
|
||||
// the generic error message.
|
||||
/** @var \Drupal\Core\Url $batch_url */
|
||||
$batch_url = $batch['url'];
|
||||
/** @var \Drupal\Core\Url $error_url */
|
||||
$error_url = clone $batch_url;
|
||||
$query_options = $error_url->getOption('query');
|
||||
$query_options['id'] = $batch['id'];
|
||||
$query_options['op'] = 'finished';
|
||||
$error_url->setOption('query', $query_options);
|
||||
|
||||
$batch['error_message'] = t('Please continue to <a href=":error_url">the error page</a>', array(':error_url' => $error_url->toString(TRUE)->getGeneratedUrl()));
|
||||
|
||||
// Clear the way for the redirection to the batch processing page, by
|
||||
// saving and unsetting the 'destination', if there is any.
|
||||
$request = \Drupal::request();
|
||||
if ($request->query->has('destination')) {
|
||||
$batch['destination'] = $request->query->get('destination');
|
||||
$request->query->remove('destination');
|
||||
}
|
||||
|
||||
// Store the batch.
|
||||
\Drupal::service('batch.storage')->create($batch);
|
||||
|
||||
// Set the batch number in the session to guarantee that it will stay alive.
|
||||
$_SESSION['batches'][$batch['id']] = TRUE;
|
||||
|
||||
// Redirect for processing.
|
||||
$query_options = $error_url->getOption('query');
|
||||
$query_options['op'] = 'start';
|
||||
$query_options['id'] = $batch['id'];
|
||||
$batch_url->setOption('query', $query_options);
|
||||
if (($function = $batch['redirect_callback']) && function_exists($function)) {
|
||||
$function($batch_url->toString(), ['query' => $query_options]);
|
||||
}
|
||||
else {
|
||||
return new RedirectResponse($batch_url->setAbsolute()->toString(TRUE)->getGeneratedUrl());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Non-progressive execution: bypass the whole progressbar workflow
|
||||
// and execute the batch in one pass.
|
||||
require_once __DIR__ . '/batch.inc';
|
||||
_batch_process();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current batch.
|
||||
*/
|
||||
function &batch_get() {
|
||||
// Not drupal_static(), because Batch API operates at a lower level than most
|
||||
// use-cases for resetting static variables, and we specifically do not want a
|
||||
// global drupal_static_reset() resetting the batch information. Functions
|
||||
// that are part of the Batch API and need to reset the batch information may
|
||||
// call batch_get() and manipulate the result by reference. Functions that are
|
||||
// not part of the Batch API can also do this, but shouldn't.
|
||||
static $batch = array();
|
||||
return $batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates a job queue with the operations of a batch set.
|
||||
*
|
||||
* Depending on whether the batch is progressive or not, the
|
||||
* Drupal\Core\Queue\Batch or Drupal\Core\Queue\BatchMemory handler classes will
|
||||
* be used. The name and class of the queue are added by reference to the
|
||||
* batch set.
|
||||
*
|
||||
* @param $batch
|
||||
* The batch array.
|
||||
* @param $set_id
|
||||
* The id of the set to process.
|
||||
*/
|
||||
function _batch_populate_queue(&$batch, $set_id) {
|
||||
$batch_set = &$batch['sets'][$set_id];
|
||||
|
||||
if (isset($batch_set['operations'])) {
|
||||
$batch_set += array(
|
||||
'queue' => array(
|
||||
'name' => 'drupal_batch:' . $batch['id'] . ':' . $set_id,
|
||||
'class' => $batch['progressive'] ? 'Drupal\Core\Queue\Batch' : 'Drupal\Core\Queue\BatchMemory',
|
||||
),
|
||||
);
|
||||
|
||||
$queue = _batch_queue($batch_set);
|
||||
$queue->createQueue();
|
||||
foreach ($batch_set['operations'] as $operation) {
|
||||
$queue->createItem($operation);
|
||||
}
|
||||
|
||||
unset($batch_set['operations']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a queue object for a batch set.
|
||||
*
|
||||
* @param $batch_set
|
||||
* The batch set.
|
||||
*
|
||||
* @return
|
||||
* The queue object.
|
||||
*/
|
||||
function _batch_queue($batch_set) {
|
||||
static $queues;
|
||||
|
||||
if (!isset($queues)) {
|
||||
$queues = array();
|
||||
}
|
||||
|
||||
if (isset($batch_set['queue'])) {
|
||||
$name = $batch_set['queue']['name'];
|
||||
$class = $batch_set['queue']['class'];
|
||||
|
||||
if (!isset($queues[$class][$name])) {
|
||||
$queues[$class][$name] = new $class($name, \Drupal::database());
|
||||
}
|
||||
return $queues[$class][$name];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "defgroup batch".
|
||||
*/
|
2205
web/core/includes/install.core.inc
Normal file
2205
web/core/includes/install.core.inc
Normal file
File diff suppressed because it is too large
Load diff
1112
web/core/includes/install.inc
Normal file
1112
web/core/includes/install.inc
Normal file
File diff suppressed because it is too large
Load diff
167
web/core/includes/menu.inc
Normal file
167
web/core/includes/menu.inc
Normal file
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* API for the Drupal menu system.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup menu
|
||||
* @{
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Render\Element;
|
||||
|
||||
|
||||
/**
|
||||
* Prepares variables for single local task link templates.
|
||||
*
|
||||
* Default template: menu-local-task.html.twig.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - element: A render element containing:
|
||||
* - #link: A menu link array with 'title', 'url', and (optionally)
|
||||
* 'localized_options' keys.
|
||||
* - #active: A boolean indicating whether the local task is active.
|
||||
*/
|
||||
function template_preprocess_menu_local_task(&$variables) {
|
||||
$link = $variables['element']['#link'];
|
||||
$link += array(
|
||||
'localized_options' => array(),
|
||||
);
|
||||
$link_text = $link['title'];
|
||||
|
||||
if (!empty($variables['element']['#active'])) {
|
||||
$variables['is_active'] = TRUE;
|
||||
|
||||
// Add text to indicate active tab for non-visual users.
|
||||
$active = SafeMarkup::format('<span class="visually-hidden">@label</span>', array('@label' => t('(active tab)')));
|
||||
$link_text = t('@local-task-title@active', array('@local-task-title' => $link_text, '@active' => $active));
|
||||
}
|
||||
|
||||
$link['localized_options']['set_active_class'] = TRUE;
|
||||
|
||||
$variables['link'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => $link_text,
|
||||
'#url' => $link['url'],
|
||||
'#options' => $link['localized_options'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for single local action link templates.
|
||||
*
|
||||
* Default template: menu-local-action.html.twig.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - element: A render element containing:
|
||||
* - #link: A menu link array with 'title', 'url', and (optionally)
|
||||
* 'localized_options' keys.
|
||||
*/
|
||||
function template_preprocess_menu_local_action(&$variables) {
|
||||
$link = $variables['element']['#link'];
|
||||
$link += array(
|
||||
'localized_options' => array(),
|
||||
);
|
||||
$link['localized_options']['attributes']['class'][] = 'button';
|
||||
$link['localized_options']['attributes']['class'][] = 'button-action';
|
||||
$link['localized_options']['set_active_class'] = TRUE;
|
||||
|
||||
$variables['link'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => $link['title'],
|
||||
'#options' => $link['localized_options'],
|
||||
'#url' => $link['url'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing the names of system-defined (default) menus.
|
||||
*/
|
||||
function menu_list_system_menus() {
|
||||
return array(
|
||||
'tools' => 'Tools',
|
||||
'admin' => 'Administration',
|
||||
'account' => 'User account menu',
|
||||
'main' => 'Main navigation',
|
||||
'footer' => 'Footer menu',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the local tasks (tabs) for the current route.
|
||||
*
|
||||
* @param int $level
|
||||
* The level of tasks you ask for. Primary tasks are 0, secondary are 1.
|
||||
*
|
||||
* @return array
|
||||
* An array containing
|
||||
* - tabs: Local tasks for the requested level.
|
||||
* - route_name: The route name for the current page used to collect the local
|
||||
* tasks.
|
||||
*
|
||||
* @see hook_menu_local_tasks_alter()
|
||||
*
|
||||
* @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.
|
||||
*/
|
||||
function menu_local_tasks($level = 0) {
|
||||
/** @var \Drupal\Core\Menu\LocalTaskManagerInterface $manager */
|
||||
$manager = \Drupal::service('plugin.manager.menu.local_task');
|
||||
return $manager->getLocalTasks(\Drupal::routeMatch()->getRouteName(), $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rendered local tasks at the top level.
|
||||
*
|
||||
* @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.
|
||||
*/
|
||||
function menu_primary_local_tasks() {
|
||||
/** @var \Drupal\Core\Menu\LocalTaskManagerInterface $manager */
|
||||
$manager = \Drupal::service('plugin.manager.menu.local_task');
|
||||
$links = $manager->getLocalTasks(\Drupal::routeMatch()->getRouteName(), 0);
|
||||
// Do not display single tabs.
|
||||
return count(Element::getVisibleChildren($links['tabs'])) > 1 ? $links['tabs'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rendered local tasks at the second level.
|
||||
*
|
||||
* @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.
|
||||
*/
|
||||
function menu_secondary_local_tasks() {
|
||||
/** @var \Drupal\Core\Menu\LocalTaskManagerInterface $manager */
|
||||
$manager = \Drupal::service('plugin.manager.menu.local_task');
|
||||
$links = $manager->getLocalTasks(\Drupal::routeMatch()->getRouteName(), 1);
|
||||
// Do not display single tabs.
|
||||
return count(Element::getVisibleChildren($links['tabs'])) > 1 ? $links['tabs'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a renderable element for the primary and secondary tabs.
|
||||
*/
|
||||
function menu_local_tabs() {
|
||||
$build = array(
|
||||
'#theme' => 'menu_local_tasks',
|
||||
'#primary' => menu_primary_local_tasks(),
|
||||
'#secondary' => menu_secondary_local_tasks(),
|
||||
);
|
||||
return !empty($build['#primary']) || !empty($build['#secondary']) ? $build : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all cached menu data.
|
||||
*
|
||||
* This should be called any time broad changes
|
||||
* might have been made to the router items or menu links.
|
||||
*/
|
||||
function menu_cache_clear_all() {
|
||||
\Drupal::cache('menu')->invalidateAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup menu".
|
||||
*/
|
235
web/core/includes/module.inc
Normal file
235
web/core/includes/module.inc
Normal file
|
@ -0,0 +1,235 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* API for loading and interacting with Drupal modules.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Extension\ExtensionDiscovery;
|
||||
|
||||
/**
|
||||
* Builds a list of installed themes.
|
||||
*
|
||||
* @param $type
|
||||
* The type of list to return:
|
||||
* - theme: All installed themes.
|
||||
*
|
||||
* @return
|
||||
* An associative array of themes, keyed by name.
|
||||
* For $type 'theme', the array values are objects representing the
|
||||
* respective database row, with the 'info' property already unserialized.
|
||||
*
|
||||
* @see \Drupal\Core\Extension\ThemeHandler::listInfo()
|
||||
*/
|
||||
function system_list($type) {
|
||||
$lists = &drupal_static(__FUNCTION__);
|
||||
if ($cached = \Drupal::cache('bootstrap')->get('system_list')) {
|
||||
$lists = $cached->data;
|
||||
}
|
||||
else {
|
||||
$lists = array(
|
||||
'theme' => array(),
|
||||
'filepaths' => array(),
|
||||
);
|
||||
// ThemeHandler maintains the 'system.theme.data' state record.
|
||||
$theme_data = \Drupal::state()->get('system.theme.data', array());
|
||||
foreach ($theme_data as $name => $theme) {
|
||||
$lists['theme'][$name] = $theme;
|
||||
$lists['filepaths'][] = array(
|
||||
'type' => 'theme',
|
||||
'name' => $name,
|
||||
'filepath' => $theme->getPathname(),
|
||||
);
|
||||
}
|
||||
\Drupal::cache('bootstrap')->set('system_list', $lists);
|
||||
}
|
||||
// To avoid a separate database lookup for the filepath, prime the
|
||||
// drupal_get_filename() static cache with all enabled themes.
|
||||
foreach ($lists['filepaths'] as $item) {
|
||||
system_register($item['type'], $item['name'], $item['filepath']);
|
||||
}
|
||||
|
||||
return $lists[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all system_list() caches.
|
||||
*/
|
||||
function system_list_reset() {
|
||||
drupal_static_reset('system_list');
|
||||
drupal_static_reset('system_rebuild_module_data');
|
||||
\Drupal::cache('bootstrap')->delete('system_list');
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an extension in runtime registries for execution.
|
||||
*
|
||||
* @param string $type
|
||||
* The extension type; e.g., 'module' or 'theme'.
|
||||
* @param string $name
|
||||
* The internal name of the extension; e.g., 'node'.
|
||||
* @param string $uri
|
||||
* The relative URI of the primary extension file; e.g.,
|
||||
* 'core/modules/node/node.module'.
|
||||
*/
|
||||
function system_register($type, $name, $uri) {
|
||||
drupal_get_filename($type, $name, $uri);
|
||||
drupal_classloader_register($name, dirname($uri));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a module's installation hooks.
|
||||
*
|
||||
* @param $module
|
||||
* The name of the module (without the .module extension).
|
||||
*
|
||||
* @return
|
||||
* The name of the module's install file, if successful; FALSE otherwise.
|
||||
*/
|
||||
function module_load_install($module) {
|
||||
// Make sure the installation API is available
|
||||
include_once __DIR__ . '/install.inc';
|
||||
|
||||
return module_load_include('install', $module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a module include file.
|
||||
*
|
||||
* Examples:
|
||||
* @code
|
||||
* // Load node.admin.inc from the node module.
|
||||
* module_load_include('inc', 'node', 'node.admin');
|
||||
* // Load content_types.inc from the node module.
|
||||
* module_load_include('inc', 'node', 'content_types');
|
||||
* @endcode
|
||||
*
|
||||
* Do not use this function to load an install file, use module_load_install()
|
||||
* instead. Do not use this function in a global context since it requires
|
||||
* Drupal to be fully bootstrapped, use require_once DRUPAL_ROOT . '/path/file'
|
||||
* instead.
|
||||
*
|
||||
* @param $type
|
||||
* The include file's type (file extension).
|
||||
* @param $module
|
||||
* The module to which the include file belongs.
|
||||
* @param $name
|
||||
* (optional) The base file name (without the $type extension). If omitted,
|
||||
* $module is used; i.e., resulting in "$module.$type" by default.
|
||||
*
|
||||
* @return
|
||||
* The name of the included file, if successful; FALSE otherwise.
|
||||
*
|
||||
* @todo The module_handler service has a loadInclude() method which performs
|
||||
* this same task but only for enabled modules. Figure out a way to move this
|
||||
* functionality entirely into the module_handler while keeping the ability to
|
||||
* load the files of disabled modules.
|
||||
*/
|
||||
function module_load_include($type, $module, $name = NULL) {
|
||||
if (!isset($name)) {
|
||||
$name = $module;
|
||||
}
|
||||
|
||||
if (function_exists('drupal_get_path')) {
|
||||
$file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "/$name.$type";
|
||||
if (is_file($file)) {
|
||||
require_once $file;
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of modules required by core.
|
||||
*/
|
||||
function drupal_required_modules() {
|
||||
$listing = new ExtensionDiscovery(\Drupal::root());
|
||||
$files = $listing->scan('module');
|
||||
$required = array();
|
||||
|
||||
// Unless called by the installer, an installation profile is required and
|
||||
// must always be loaded. drupal_get_profile() also returns the installation
|
||||
// profile in the installer, but only after it has been selected.
|
||||
if ($profile = drupal_get_profile()) {
|
||||
$required[] = $profile;
|
||||
}
|
||||
|
||||
foreach ($files as $name => $file) {
|
||||
$info = \Drupal::service('info_parser')->parse($file->getPathname());
|
||||
if (!empty($info) && !empty($info['required']) && $info['required']) {
|
||||
$required[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
return $required;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets weight of a particular module.
|
||||
*
|
||||
* The weight of uninstalled modules cannot be changed.
|
||||
*
|
||||
* @param string $module
|
||||
* The name of the module (without the .module extension).
|
||||
* @param int $weight
|
||||
* An integer representing the weight of the module.
|
||||
*/
|
||||
function module_set_weight($module, $weight) {
|
||||
$extension_config = \Drupal::configFactory()->getEditable('core.extension');
|
||||
if ($extension_config->get("module.$module") !== NULL) {
|
||||
// Pre-cast the $weight to an integer so that we can save this without using
|
||||
// schema. This is a performance improvement for module installation.
|
||||
$extension_config
|
||||
->set("module.$module", (int) $weight)
|
||||
->set('module', module_config_sort($extension_config->get('module')))
|
||||
->save(TRUE);
|
||||
|
||||
// Prepare the new module list, sorted by weight, including filenames.
|
||||
// @see \Drupal\Core\Extension\ModuleInstaller::install()
|
||||
$module_handler = \Drupal::moduleHandler();
|
||||
$current_module_filenames = $module_handler->getModuleList();
|
||||
$current_modules = array_fill_keys(array_keys($current_module_filenames), 0);
|
||||
$current_modules = module_config_sort(array_merge($current_modules, $extension_config->get('module')));
|
||||
$module_filenames = array();
|
||||
foreach ($current_modules as $name => $weight) {
|
||||
$module_filenames[$name] = $current_module_filenames[$name];
|
||||
}
|
||||
// Update the module list in the extension handler.
|
||||
$module_handler->setModuleList($module_filenames);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the configured list of enabled modules.
|
||||
*
|
||||
* The list of enabled modules is expected to be ordered by weight and name.
|
||||
* The list is always sorted on write to avoid the overhead on read.
|
||||
*
|
||||
* @param array $data
|
||||
* An array of module configuration data.
|
||||
*
|
||||
* @return array
|
||||
* An array of module configuration data sorted by weight and name.
|
||||
*/
|
||||
function module_config_sort($data) {
|
||||
// PHP array sorting functions such as uasort() do not work with both keys and
|
||||
// values at the same time, so we achieve weight and name sorting by computing
|
||||
// strings with both information concatenated (weight first, name second) and
|
||||
// use that as a regular string sort reference list via array_multisort(),
|
||||
// compound of "[sign-as-integer][padded-integer-weight][name]"; e.g., given
|
||||
// two modules and weights (spaces added for clarity):
|
||||
// - Block with weight -5: 0 0000000000000000005 block
|
||||
// - Node with weight 0: 1 0000000000000000000 node
|
||||
$sort = array();
|
||||
foreach ($data as $name => $weight) {
|
||||
// Prefix negative weights with 0, positive weights with 1.
|
||||
// +/- signs cannot be used, since + (ASCII 43) is before - (ASCII 45).
|
||||
$prefix = (int) ($weight >= 0);
|
||||
// The maximum weight is PHP_INT_MAX, so pad all weights to 19 digits.
|
||||
$sort[] = $prefix . sprintf('%019d', abs($weight)) . $name;
|
||||
}
|
||||
array_multisort($sort, SORT_STRING, $data);
|
||||
return $data;
|
||||
}
|
331
web/core/includes/pager.inc
Normal file
331
web/core/includes/pager.inc
Normal file
|
@ -0,0 +1,331 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Functions to aid in presenting database results as a set of pages.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
|
||||
/**
|
||||
* Returns the current page being requested for display within a pager.
|
||||
*
|
||||
* @param int $element
|
||||
* (optional) An integer to distinguish between multiple pagers on one page.
|
||||
*
|
||||
* @return int
|
||||
* The number of the current requested page, within the pager represented by
|
||||
* $element. This is determined from the URL query parameter
|
||||
* \Drupal::request()->query->get('page'), or 0 by default. Note that this
|
||||
* number may differ from the actual page being displayed. For example, if a
|
||||
* search for "example text" brings up three pages of results, but a user
|
||||
* visits search/node/example+text?page=10, this function will return 10,
|
||||
* even though the default pager implementation adjusts for this and still
|
||||
* displays the third page of search results at that URL.
|
||||
*
|
||||
* @see pager_default_initialize()
|
||||
*/
|
||||
function pager_find_page($element = 0) {
|
||||
$page = \Drupal::request()->query->get('page', '');
|
||||
$page_array = explode(',', $page);
|
||||
if (!isset($page_array[$element])) {
|
||||
$page_array[$element] = 0;
|
||||
}
|
||||
return (int) $page_array[$element];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a pager.
|
||||
*
|
||||
* This function sets up the necessary global variables so that the render
|
||||
* system will correctly process #type 'pager' render arrays to output pagers
|
||||
* that correspond to the items being displayed.
|
||||
*
|
||||
* If the items being displayed result from a database query performed using
|
||||
* Drupal's database API, and if you have control over the construction of the
|
||||
* database query, you do not need to call this function directly; instead, you
|
||||
* can simply extend the query object with the 'PagerSelectExtender' extender
|
||||
* before executing it. For example:
|
||||
* @code
|
||||
* $query = db_select('some_table')
|
||||
* ->extend('Drupal\Core\Database\Query\PagerSelectExtender');
|
||||
* @endcode
|
||||
*
|
||||
* However, if you are using a different method for generating the items to be
|
||||
* paged through, then you should call this function in preparation.
|
||||
*
|
||||
* The following example shows how this function can be used in a controller
|
||||
* that invokes an external datastore with an SQL-like syntax:
|
||||
* @code
|
||||
* // First find the total number of items and initialize the pager.
|
||||
* $where = "status = 1";
|
||||
* $total = mymodule_select("SELECT COUNT(*) FROM data " . $where)->result();
|
||||
* $num_per_page = \Drupal::config('mymodule.settings')->get('num_per_page');
|
||||
* $page = pager_default_initialize($total, $num_per_page);
|
||||
*
|
||||
* // Next, retrieve the items for the current page and put them into a
|
||||
* // render array.
|
||||
* $offset = $num_per_page * $page;
|
||||
* $result = mymodule_select("SELECT * FROM data " . $where . " LIMIT %d, %d", $offset, $num_per_page)->fetchAll();
|
||||
* $render = [];
|
||||
* $render[] = [
|
||||
* '#theme' => 'mymodule_results',
|
||||
* '#result' => $result,
|
||||
* ];
|
||||
*
|
||||
* // Finally, add the pager to the render array, and return.
|
||||
* $render[] = ['#type' => 'pager'];
|
||||
* return $render;
|
||||
* @endcode
|
||||
*
|
||||
* A second example involves a controller that invokes an external search
|
||||
* service where the total number of matching results is provided as part of
|
||||
* the returned set (so that we do not need a separate query in order to obtain
|
||||
* this information). Here, we call pager_find_page() to calculate the desired
|
||||
* offset before the search is invoked:
|
||||
* @code
|
||||
* // Perform the query, using the requested offset from pager_find_page().
|
||||
* // This comes from a URL parameter, so here we are assuming that the URL
|
||||
* // parameter corresponds to an actual page of results that will exist
|
||||
* // within the set.
|
||||
* $page = pager_find_page();
|
||||
* $num_per_page = \Drupal::config('mymodule.settings')->get('num_per_page');
|
||||
* $offset = $num_per_page * $page;
|
||||
* $result = mymodule_remote_search($keywords, $offset, $num_per_page);
|
||||
*
|
||||
* // Now that we have the total number of results, initialize the pager.
|
||||
* pager_default_initialize($result->total, $num_per_page);
|
||||
*
|
||||
* // Create a render array with the search results.
|
||||
* $render = [];
|
||||
* $render[] = [
|
||||
* '#theme' => 'search_results',
|
||||
* '#results' => $result->data,
|
||||
* '#type' => 'remote',
|
||||
* ];
|
||||
*
|
||||
* // Finally, add the pager to the render array, and return.
|
||||
* $render[] = ['#type' => 'pager'];
|
||||
* return $render;
|
||||
* @endcode
|
||||
*
|
||||
* @param int $total
|
||||
* The total number of items to be paged.
|
||||
* @param int $limit
|
||||
* The number of items the calling code will display per page.
|
||||
* @param int $element
|
||||
* (optional) An integer to distinguish between multiple pagers on one page.
|
||||
*
|
||||
* @return int
|
||||
* The number of the current page, within the pager represented by $element.
|
||||
* This is determined from the URL query parameter
|
||||
* \Drupal::request()->query->get('page), or 0 by default. However, if a page
|
||||
* that does not correspond to the actual range of the result set was
|
||||
* requested, this function will return the closest page actually within the
|
||||
* result set.
|
||||
*/
|
||||
function pager_default_initialize($total, $limit, $element = 0) {
|
||||
global $pager_page_array, $pager_total, $pager_total_items, $pager_limits;
|
||||
|
||||
$page = pager_find_page($element);
|
||||
|
||||
// We calculate the total of pages as ceil(items / limit).
|
||||
$pager_total_items[$element] = $total;
|
||||
$pager_total[$element] = ceil($pager_total_items[$element] / $limit);
|
||||
$pager_page_array[$element] = max(0, min($page, ((int) $pager_total[$element]) - 1));
|
||||
$pager_limits[$element] = $limit;
|
||||
return $pager_page_array[$element];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a URL query parameter array for pager links.
|
||||
*
|
||||
* @return array
|
||||
* A URL query parameter array that consists of all components of the current
|
||||
* page request except for those pertaining to paging.
|
||||
*/
|
||||
function pager_get_query_parameters() {
|
||||
$query = &drupal_static(__FUNCTION__);
|
||||
if (!isset($query)) {
|
||||
$query = UrlHelper::filterQueryParameters(\Drupal::request()->query->all(), array('page'));
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for pager templates.
|
||||
*
|
||||
* Default template: pager.html.twig.
|
||||
*
|
||||
* Menu callbacks that display paged query results should use #type => pager
|
||||
* to retrieve a pager control so that users can view other results. Format a
|
||||
* list of nearby pages with additional query results.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - pager: A render element containing:
|
||||
* - #tags: An array of labels for the controls in the pager.
|
||||
* - #element: An optional integer to distinguish between multiple pagers on
|
||||
* one page.
|
||||
* - #parameters: An associative array of query string parameters to append
|
||||
* to the pager links.
|
||||
* - #route_parameters: An associative array of the route parameters.
|
||||
* - #quantity: The number of pages in the list.
|
||||
*/
|
||||
function template_preprocess_pager(&$variables) {
|
||||
$element = $variables['pager']['#element'];
|
||||
$parameters = $variables['pager']['#parameters'];
|
||||
$quantity = $variables['pager']['#quantity'];
|
||||
$route_name = $variables['pager']['#route_name'];
|
||||
$route_parameters = isset($variables['pager']['#route_parameters']) ? $variables['pager']['#route_parameters'] : [];
|
||||
global $pager_page_array, $pager_total;
|
||||
|
||||
// Nothing to do if there is only one page.
|
||||
if ($pager_total[$element] <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tags = $variables['pager']['#tags'];
|
||||
|
||||
// Calculate various markers within this pager piece:
|
||||
// Middle is used to "center" pages around the current page.
|
||||
$pager_middle = ceil($quantity / 2);
|
||||
// current is the page we are currently paged to.
|
||||
$pager_current = $pager_page_array[$element] + 1;
|
||||
// first is the first page listed by this pager piece (re quantity).
|
||||
$pager_first = $pager_current - $pager_middle + 1;
|
||||
// last is the last page listed by this pager piece (re quantity).
|
||||
$pager_last = $pager_current + $quantity - $pager_middle;
|
||||
// max is the maximum page number.
|
||||
$pager_max = $pager_total[$element];
|
||||
// End of marker calculations.
|
||||
|
||||
// Prepare for generation loop.
|
||||
$i = $pager_first;
|
||||
if ($pager_last > $pager_max) {
|
||||
// Adjust "center" if at end of query.
|
||||
$i = $i + ($pager_max - $pager_last);
|
||||
$pager_last = $pager_max;
|
||||
}
|
||||
if ($i <= 0) {
|
||||
// Adjust "center" if at start of query.
|
||||
$pager_last = $pager_last + (1 - $i);
|
||||
$i = 1;
|
||||
}
|
||||
// End of generation loop preparation.
|
||||
|
||||
// Create the "first" and "previous" links if we are not on the first page.
|
||||
if ($pager_page_array[$element] > 0) {
|
||||
$items['first'] = array();
|
||||
$options = array(
|
||||
'query' => pager_query_add_page($parameters, $element, 0),
|
||||
);
|
||||
$items['first']['href'] = \Drupal::url($route_name, $route_parameters, $options);
|
||||
if (isset($tags[0])) {
|
||||
$items['first']['text'] = $tags[0];
|
||||
}
|
||||
|
||||
$items['previous'] = array();
|
||||
$options = array(
|
||||
'query' => pager_query_add_page($parameters, $element, $pager_page_array[$element] - 1),
|
||||
);
|
||||
$items['previous']['href'] = \Drupal::url($route_name, $route_parameters, $options);
|
||||
if (isset($tags[1])) {
|
||||
$items['previous']['text'] = $tags[1];
|
||||
}
|
||||
}
|
||||
|
||||
if ($i != $pager_max) {
|
||||
// Add an ellipsis if there are further previous pages.
|
||||
if ($i > 1) {
|
||||
$variables['ellipses']['previous'] = TRUE;
|
||||
}
|
||||
// Now generate the actual pager piece.
|
||||
for (; $i <= $pager_last && $i <= $pager_max; $i++) {
|
||||
$options = array(
|
||||
'query' => pager_query_add_page($parameters, $element, $i - 1),
|
||||
);
|
||||
$items['pages'][$i]['href'] = \Drupal::url($route_name, $route_parameters, $options);
|
||||
if ($i == $pager_current) {
|
||||
$variables['current'] = $i;
|
||||
}
|
||||
}
|
||||
// Add an ellipsis if there are further next pages.
|
||||
if ($i < $pager_max + 1) {
|
||||
$variables['ellipses']['next'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the "next" and "last" links if we are not on the last page.
|
||||
if ($pager_page_array[$element] < ($pager_max - 1)) {
|
||||
$items['next'] = array();
|
||||
$options = array(
|
||||
'query' => pager_query_add_page($parameters, $element, $pager_page_array[$element] + 1),
|
||||
);
|
||||
$items['next']['href'] = \Drupal::url($route_name, $route_parameters, $options);
|
||||
if (isset($tags[3])) {
|
||||
$items['next']['text'] = $tags[3];
|
||||
}
|
||||
|
||||
$items['last'] = array();
|
||||
$options = array(
|
||||
'query' => pager_query_add_page($parameters, $element, $pager_max - 1),
|
||||
);
|
||||
$items['last']['href'] = \Drupal::url($route_name, $route_parameters, $options);
|
||||
if (isset($tags[4])) {
|
||||
$items['last']['text'] = $tags[4];
|
||||
}
|
||||
}
|
||||
|
||||
$variables['items'] = $items;
|
||||
|
||||
// The rendered link needs to play well with any other query parameter used
|
||||
// on the page, like exposed filters, so for the cacheability all query
|
||||
// parameters matter.
|
||||
$variables['#cache']['contexts'][] = 'url.query_args';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL query parameter array of a pager link.
|
||||
*
|
||||
* Adds to or adjusts the 'page' URL query parameter so that if you follow the
|
||||
* link, you'll get page $index for pager $element on the page.
|
||||
*
|
||||
* The 'page' URL query parameter is a comma-delimited string, where each value
|
||||
* is the target content page for the corresponding pager $element. For
|
||||
* instance, if we have 5 pagers on a single page, and we want to have a link
|
||||
* to a page that should display the 6th content page for the 3rd pager, and
|
||||
* the 1st content page for all the other pagers, then the URL query will look
|
||||
* like this: ?page=0,0,5,0,0 (page numbering starts at zero).
|
||||
*
|
||||
* @param array $query
|
||||
* An associative array of URL query parameters to add to.
|
||||
* @param int $element
|
||||
* An integer to distinguish between multiple pagers on one page.
|
||||
* @param int $index
|
||||
* The index of the target page, for the given element, in the pager array.
|
||||
*
|
||||
* @return array
|
||||
* The altered $query parameter array.
|
||||
*/
|
||||
function pager_query_add_page(array $query, $element, $index) {
|
||||
global $pager_page_array;
|
||||
|
||||
// Build the 'page' query parameter. This is built based on the current
|
||||
// page of each pager element (or NULL if the pager is not set), with the
|
||||
// exception of the requested page index for the current element.
|
||||
$max_element = max(array_keys($pager_page_array));
|
||||
$element_pages = [];
|
||||
for ($i = 0; $i <= $max_element; $i++) {
|
||||
$element_pages[] = ($i == $element) ? $index : (isset($pager_page_array[$i]) ? $pager_page_array[$i] : NULL);
|
||||
}
|
||||
$query['page'] = implode(',', $element_pages);
|
||||
|
||||
// Merge the query parameters passed to this function with the parameters
|
||||
// from the current request. In case of collision, the parameters passed into
|
||||
// this function take precedence.
|
||||
if ($current_request_query = pager_get_query_parameters()) {
|
||||
$query = array_merge($current_request_query, $query);
|
||||
}
|
||||
return $query;
|
||||
}
|
237
web/core/includes/schema.inc
Normal file
237
web/core/includes/schema.inc
Normal file
|
@ -0,0 +1,237 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Schema API handling functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup schemaapi
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Indicates that a module has not been installed yet.
|
||||
*/
|
||||
const SCHEMA_UNINSTALLED = -1;
|
||||
|
||||
/**
|
||||
* Returns an array of available schema versions for a module.
|
||||
*
|
||||
* @param string $module
|
||||
* A module name.
|
||||
*
|
||||
* @return array|bool
|
||||
* If the module has updates, an array of available updates sorted by
|
||||
* version. Otherwise, FALSE.
|
||||
*/
|
||||
function drupal_get_schema_versions($module) {
|
||||
$updates = &drupal_static(__FUNCTION__, NULL);
|
||||
if (!isset($updates[$module])) {
|
||||
$updates = array();
|
||||
foreach (\Drupal::moduleHandler()->getModuleList() as $loaded_module => $filename) {
|
||||
$updates[$loaded_module] = array();
|
||||
}
|
||||
|
||||
// Prepare regular expression to match all possible defined hook_update_N().
|
||||
$regexp = '/^(?<module>.+)_update_(?<version>\d+)$/';
|
||||
$functions = get_defined_functions();
|
||||
// Narrow this down to functions ending with an integer, since all
|
||||
// hook_update_N() functions end this way, and there are other
|
||||
// possible functions which match '_update_'. We use preg_grep() here
|
||||
// instead of foreaching through all defined functions, since the loop
|
||||
// through all PHP functions can take significant page execution time
|
||||
// and this function is called on every administrative page via
|
||||
// system_requirements().
|
||||
foreach (preg_grep('/_\d+$/', $functions['user']) as $function) {
|
||||
// If this function is a module update function, add it to the list of
|
||||
// module updates.
|
||||
if (preg_match($regexp, $function, $matches)) {
|
||||
$updates[$matches['module']][] = $matches['version'];
|
||||
}
|
||||
}
|
||||
// Ensure that updates are applied in numerical order.
|
||||
foreach ($updates as &$module_updates) {
|
||||
sort($module_updates, SORT_NUMERIC);
|
||||
}
|
||||
}
|
||||
return empty($updates[$module]) ? FALSE : $updates[$module];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently installed schema version for a module.
|
||||
*
|
||||
* @param string $module
|
||||
* A module name.
|
||||
* @param bool $reset
|
||||
* Set to TRUE after installing or uninstalling an extension.
|
||||
* @param bool $array
|
||||
* Set to TRUE if you want to get information about all modules in the
|
||||
* system.
|
||||
*
|
||||
* @return string|int
|
||||
* The currently installed schema version, or SCHEMA_UNINSTALLED if the
|
||||
* module is not installed.
|
||||
*/
|
||||
function drupal_get_installed_schema_version($module, $reset = FALSE, $array = FALSE) {
|
||||
$versions = &drupal_static(__FUNCTION__, array());
|
||||
|
||||
if ($reset) {
|
||||
$versions = array();
|
||||
}
|
||||
|
||||
if (!$versions) {
|
||||
if (!$versions = \Drupal::keyValue('system.schema')->getAll()) {
|
||||
$versions = array();
|
||||
}
|
||||
}
|
||||
|
||||
if ($array) {
|
||||
return $versions;
|
||||
}
|
||||
else {
|
||||
return isset($versions[$module]) ? $versions[$module] : SCHEMA_UNINSTALLED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the installed version information for a module.
|
||||
*
|
||||
* @param string $module
|
||||
* A module name.
|
||||
* @param string $version
|
||||
* The new schema version.
|
||||
*/
|
||||
function drupal_set_installed_schema_version($module, $version) {
|
||||
\Drupal::keyValue('system.schema')->set($module, $version);
|
||||
// Reset the static cache of module schema versions.
|
||||
drupal_get_installed_schema_version(NULL, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates all tables defined in a module's hook_schema().
|
||||
*
|
||||
* @param string $module
|
||||
* The module for which the tables will be created.
|
||||
*/
|
||||
function drupal_install_schema($module) {
|
||||
$schema = drupal_get_module_schema($module);
|
||||
_drupal_schema_initialize($schema, $module, FALSE);
|
||||
|
||||
foreach ($schema as $name => $table) {
|
||||
db_create_table($name, $table);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all tables defined in a module's hook_schema().
|
||||
*
|
||||
* @param string $module
|
||||
* The module for which the tables will be removed.
|
||||
*/
|
||||
function drupal_uninstall_schema($module) {
|
||||
$schema = drupal_get_module_schema($module);
|
||||
_drupal_schema_initialize($schema, $module, FALSE);
|
||||
|
||||
foreach ($schema as $table) {
|
||||
if (db_table_exists($table['name'])) {
|
||||
db_drop_table($table['name']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a module's schema.
|
||||
*
|
||||
* This function can be used to retrieve a schema specification in
|
||||
* hook_schema(), so it allows you to derive your tables from existing
|
||||
* specifications.
|
||||
*
|
||||
* @param string $module
|
||||
* The module to which the table belongs.
|
||||
* @param string $table
|
||||
* The name of the table. If not given, the module's complete schema
|
||||
* is returned.
|
||||
*/
|
||||
function drupal_get_module_schema($module, $table = NULL) {
|
||||
// Load the .install file to get hook_schema.
|
||||
module_load_install($module);
|
||||
$schema = \Drupal::moduleHandler()->invoke($module, 'schema');
|
||||
|
||||
if (isset($table)) {
|
||||
if (isset($schema[$table])) {
|
||||
return $schema[$table];
|
||||
}
|
||||
return array();
|
||||
}
|
||||
elseif (!empty($schema)) {
|
||||
return $schema;
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills in required default values for table definitions from hook_schema().
|
||||
*
|
||||
* @param array $schema
|
||||
* The schema definition array as it was returned by the module's
|
||||
* hook_schema().
|
||||
* @param string $module
|
||||
* The module for which hook_schema() was invoked.
|
||||
* @param bool $remove_descriptions
|
||||
* (optional) Whether to additionally remove 'description' keys of all tables
|
||||
* and fields to improve performance of serialize() and unserialize().
|
||||
* Defaults to TRUE.
|
||||
*/
|
||||
function _drupal_schema_initialize(&$schema, $module, $remove_descriptions = TRUE) {
|
||||
// Set the name and module key for all tables.
|
||||
foreach ($schema as $name => &$table) {
|
||||
if (empty($table['module'])) {
|
||||
$table['module'] = $module;
|
||||
}
|
||||
if (!isset($table['name'])) {
|
||||
$table['name'] = $name;
|
||||
}
|
||||
if ($remove_descriptions) {
|
||||
unset($table['description']);
|
||||
foreach ($table['fields'] as &$field) {
|
||||
unset($field['description']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Typecasts values to proper datatypes.
|
||||
*
|
||||
* MySQL PDO silently casts, e.g. FALSE and '' to 0, when inserting the value
|
||||
* into an integer column, but PostgreSQL PDO does not. Look up the schema
|
||||
* information and use that to correctly typecast the value.
|
||||
*
|
||||
* @param array $info
|
||||
* An array describing the schema field info.
|
||||
* @param mixed $value
|
||||
* The value to be converted.
|
||||
*
|
||||
* @return mixed
|
||||
* The converted value.
|
||||
*/
|
||||
function drupal_schema_get_field_value(array $info, $value) {
|
||||
// Preserve legal NULL values.
|
||||
if (isset($value) || !empty($info['not null'])) {
|
||||
if ($info['type'] == 'int' || $info['type'] == 'serial') {
|
||||
$value = (int) $value;
|
||||
}
|
||||
elseif ($info['type'] == 'float') {
|
||||
$value = (float) $value;
|
||||
}
|
||||
elseif (!is_array($value)) {
|
||||
$value = (string) $value;
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup schemaapi".
|
||||
*/
|
149
web/core/includes/tablesort.inc
Normal file
149
web/core/includes/tablesort.inc
Normal file
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Functions to aid in the creation of sortable tables.
|
||||
*
|
||||
* All tables created when rendering a '#type' => 'table' have the option of
|
||||
* having column headers that the user can click on to sort the table by that
|
||||
* column.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
|
||||
/**
|
||||
* Initializes the table sort context.
|
||||
*/
|
||||
function tablesort_init($header) {
|
||||
$ts = tablesort_get_order($header);
|
||||
$ts['sort'] = tablesort_get_sort($header);
|
||||
$ts['query'] = tablesort_get_query_parameters();
|
||||
return $ts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a column header.
|
||||
*
|
||||
* If the cell in question is the column header for the current sort criterion,
|
||||
* it gets special formatting. All possible sort criteria become links.
|
||||
*
|
||||
* @param string $cell_content
|
||||
* The cell content to format. Passed by reference.
|
||||
* @param array $cell_attributes
|
||||
* The cell attributes. Passed by reference.
|
||||
* @param array $header
|
||||
* An array of column headers in the format described in '#type' => 'table'.
|
||||
* @param array $ts
|
||||
* The current table sort context as returned from tablesort_init().
|
||||
*/
|
||||
function tablesort_header(&$cell_content, array &$cell_attributes, array $header, array $ts) {
|
||||
// Special formatting for the currently sorted column header.
|
||||
if (isset($cell_attributes['field'])) {
|
||||
$title = t('sort by @s', array('@s' => $cell_content));
|
||||
if ($cell_content == $ts['name']) {
|
||||
// aria-sort is a WAI-ARIA property that indicates if items in a table
|
||||
// or grid are sorted in ascending or descending order. See
|
||||
// http://www.w3.org/TR/wai-aria/states_and_properties#aria-sort
|
||||
$cell_attributes['aria-sort'] = ($ts['sort'] == 'asc') ? 'ascending' : 'descending';
|
||||
$ts['sort'] = (($ts['sort'] == 'asc') ? 'desc' : 'asc');
|
||||
$cell_attributes['class'][] = 'is-active';
|
||||
$tablesort_indicator = array(
|
||||
'#theme' => 'tablesort_indicator',
|
||||
'#style' => $ts['sort'],
|
||||
);
|
||||
$image = drupal_render($tablesort_indicator);
|
||||
}
|
||||
else {
|
||||
// If the user clicks a different header, we want to sort ascending initially.
|
||||
$ts['sort'] = 'asc';
|
||||
$image = '';
|
||||
}
|
||||
$cell_content = \Drupal::l(SafeMarkup::format('@cell_content@image', array('@cell_content' => $cell_content, '@image' => $image)), new Url('<current>', [], [
|
||||
'attributes' => array('title' => $title),
|
||||
'query' => array_merge($ts['query'], array(
|
||||
'sort' => $ts['sort'],
|
||||
'order' => $cell_content,
|
||||
)),
|
||||
]));
|
||||
|
||||
unset($cell_attributes['field'], $cell_attributes['sort']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes a URL query parameter array for table sorting links.
|
||||
*
|
||||
* @return
|
||||
* A URL query parameter array that consists of all components of the current
|
||||
* page request except for those pertaining to table sorting.
|
||||
*/
|
||||
function tablesort_get_query_parameters() {
|
||||
return UrlHelper::filterQueryParameters(\Drupal::request()->query->all(), array('sort', 'order'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the current sort criterion.
|
||||
*
|
||||
* @param $headers
|
||||
* An array of column headers in the format described in '#type' => 'table'.
|
||||
*
|
||||
* @return
|
||||
* An associative array describing the criterion, containing the keys:
|
||||
* - "name": The localized title of the table column.
|
||||
* - "sql": The name of the database field to sort on.
|
||||
*/
|
||||
function tablesort_get_order($headers) {
|
||||
$order = \Drupal::request()->query->get('order', '');
|
||||
foreach ($headers as $header) {
|
||||
if (is_array($header)) {
|
||||
if (isset($header['data']) && $order == $header['data']) {
|
||||
$default = $header;
|
||||
break;
|
||||
}
|
||||
|
||||
if (empty($default) && isset($header['sort']) && ($header['sort'] == 'asc' || $header['sort'] == 'desc')) {
|
||||
$default = $header;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($default)) {
|
||||
$default = reset($headers);
|
||||
if (!is_array($default)) {
|
||||
$default = array('data' => $default);
|
||||
}
|
||||
}
|
||||
|
||||
$default += array('data' => NULL, 'field' => NULL);
|
||||
return array('name' => $default['data'], 'sql' => $default['field']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the current sort direction.
|
||||
*
|
||||
* @param $headers
|
||||
* An array of column headers in the format described in '#type' => 'table'.
|
||||
*
|
||||
* @return
|
||||
* The current sort direction ("asc" or "desc").
|
||||
*/
|
||||
function tablesort_get_sort($headers) {
|
||||
$query = \Drupal::request()->query;
|
||||
if ($query->has('sort')) {
|
||||
return (strtolower($query->get('sort')) == 'desc') ? 'desc' : 'asc';
|
||||
}
|
||||
// The user has not specified a sort. Use the default for the currently sorted
|
||||
// header if specified; otherwise use "asc".
|
||||
else {
|
||||
// Find out which header is currently being sorted.
|
||||
$ts = tablesort_get_order($headers);
|
||||
foreach ($headers as $header) {
|
||||
if (is_array($header) && isset($header['data']) && $header['data'] == $ts['name'] && isset($header['sort'])) {
|
||||
return $header['sort'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return 'asc';
|
||||
}
|
1865
web/core/includes/theme.inc
Normal file
1865
web/core/includes/theme.inc
Normal file
File diff suppressed because it is too large
Load diff
138
web/core/includes/theme.maintenance.inc
Normal file
138
web/core/includes/theme.maintenance.inc
Normal file
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Theming for maintenance pages.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Site\Settings;
|
||||
|
||||
/**
|
||||
* Sets up the theming system for maintenance page.
|
||||
*
|
||||
* Used for site installs, updates and when the site is in maintenance mode.
|
||||
* It also applies when the database is unavailable or bootstrap was not
|
||||
* complete. Seven is always used for the initial install and update
|
||||
* operations. In other cases, Bartik is used, but this can be overridden by
|
||||
* setting a "maintenance_theme" key in the $settings variable in settings.php.
|
||||
*/
|
||||
function _drupal_maintenance_theme() {
|
||||
// If the theme is already set, assume the others are set too, and do nothing.
|
||||
if (\Drupal::theme()->hasActiveTheme()) {
|
||||
return;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/theme.inc';
|
||||
require_once __DIR__ . '/common.inc';
|
||||
require_once __DIR__ . '/unicode.inc';
|
||||
require_once __DIR__ . '/file.inc';
|
||||
require_once __DIR__ . '/module.inc';
|
||||
require_once __DIR__ . '/database.inc';
|
||||
Unicode::check();
|
||||
|
||||
// Install and update pages are treated differently to prevent theming overrides.
|
||||
if (defined('MAINTENANCE_MODE') && (MAINTENANCE_MODE == 'install' || MAINTENANCE_MODE == 'update')) {
|
||||
if (drupal_installation_attempted()) {
|
||||
$custom_theme = $GLOBALS['install_state']['theme'];
|
||||
}
|
||||
else {
|
||||
$custom_theme = Settings::get('maintenance_theme', 'seven');
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Use the maintenance theme if specified, otherwise attempt to use the
|
||||
// default site theme.
|
||||
try {
|
||||
$custom_theme = Settings::get('maintenance_theme', '');
|
||||
if (!$custom_theme) {
|
||||
$config = \Drupal::config('system.theme');
|
||||
$custom_theme = $config->get('default');
|
||||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// Whatever went wrong (often a database connection problem), we are
|
||||
// about to fall back to a sensible theme so there is no need for special
|
||||
// handling.
|
||||
}
|
||||
if (!$custom_theme) {
|
||||
// We have been unable to identify the configured theme, so fall back to
|
||||
// a safe default. Bartik is reasonably user friendly and fairly generic.
|
||||
$custom_theme = 'bartik';
|
||||
}
|
||||
}
|
||||
|
||||
$themes = \Drupal::service('theme_handler')->listInfo();
|
||||
|
||||
// If no themes are installed yet, or if the requested custom theme is not
|
||||
// installed, retrieve all available themes.
|
||||
/** @var \Drupal\Core\Theme\ThemeInitialization $theme_init */
|
||||
$theme_init = \Drupal::service('theme.initialization');
|
||||
$theme_handler = \Drupal::service('theme_handler');
|
||||
if (empty($themes) || !isset($themes[$custom_theme])) {
|
||||
$themes = $theme_handler->rebuildThemeData();
|
||||
$theme_handler->addTheme($themes[$custom_theme]);
|
||||
}
|
||||
|
||||
// \Drupal\Core\Extension\ThemeHandlerInterface::listInfo() triggers a
|
||||
// \Drupal\Core\Extension\ModuleHandler::alter() in maintenance mode, but we
|
||||
// can't let themes alter the .info.yml data until we know a theme's base
|
||||
// themes. So don't set active theme until after
|
||||
// \Drupal\Core\Extension\ThemeHandlerInterface::listInfo() builds its cache.
|
||||
$theme = $custom_theme;
|
||||
|
||||
// Find all our ancestor themes and put them in an array.
|
||||
// @todo This is just a workaround. Find a better way how to handle themes
|
||||
// on maintenance pages, see https://www.drupal.org/node/2322619.
|
||||
// This code is basically a duplicate of
|
||||
// \Drupal\Core\Theme\ThemeInitialization::getActiveThemeByName.
|
||||
$base_themes = [];
|
||||
$ancestor = $theme;
|
||||
while ($ancestor && isset($themes[$ancestor]->base_theme)) {
|
||||
$base_themes[] = $themes[$themes[$ancestor]->base_theme];
|
||||
$ancestor = $themes[$ancestor]->base_theme;
|
||||
if ($ancestor) {
|
||||
// Ensure that the base theme is added and installed.
|
||||
$theme_handler->addTheme($themes[$ancestor]);
|
||||
}
|
||||
}
|
||||
\Drupal::theme()->setActiveTheme($theme_init->getActiveTheme($themes[$custom_theme], $base_themes));
|
||||
// Prime the theme registry.
|
||||
Drupal::service('theme.registry');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for authorize.php operation report templates.
|
||||
*
|
||||
* This report displays the results of an operation run via authorize.php.
|
||||
*
|
||||
* Default template: authorize-report.html.twig.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - messages: An array of result messages.
|
||||
*/
|
||||
function template_preprocess_authorize_report(&$variables) {
|
||||
$messages = [];
|
||||
if (!empty($variables['messages'])) {
|
||||
foreach ($variables['messages'] as $heading => $logs) {
|
||||
$items = [];
|
||||
foreach ($logs as $number => $log_message) {
|
||||
if ($number === '#abort') {
|
||||
continue;
|
||||
}
|
||||
$class = 'authorize-results__' . ($log_message['success'] ? 'success' : 'failure');
|
||||
$items[] = [
|
||||
'#wrapper_attributes' => ['class' => [$class]],
|
||||
'#markup' => $log_message['message'],
|
||||
];
|
||||
}
|
||||
$messages[] = [
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $items,
|
||||
'#title' => $heading,
|
||||
];
|
||||
}
|
||||
}
|
||||
$variables['messages'] = $messages;
|
||||
}
|
111
web/core/includes/unicode.inc
Normal file
111
web/core/includes/unicode.inc
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides Unicode-related conversions and operations.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
|
||||
/**
|
||||
* Returns Unicode library status and errors.
|
||||
*/
|
||||
function unicode_requirements() {
|
||||
$libraries = array(
|
||||
Unicode::STATUS_SINGLEBYTE => t('Standard PHP'),
|
||||
Unicode::STATUS_MULTIBYTE => t('PHP Mbstring Extension'),
|
||||
Unicode::STATUS_ERROR => t('Error'),
|
||||
);
|
||||
$severities = array(
|
||||
Unicode::STATUS_SINGLEBYTE => REQUIREMENT_WARNING,
|
||||
Unicode::STATUS_MULTIBYTE => NULL,
|
||||
Unicode::STATUS_ERROR => REQUIREMENT_ERROR,
|
||||
);
|
||||
$failed_check = Unicode::check();
|
||||
$library = Unicode::getStatus();
|
||||
|
||||
$requirements['unicode'] = array(
|
||||
'title' => t('Unicode library'),
|
||||
'value' => $libraries[$library],
|
||||
'severity' => $severities[$library],
|
||||
);
|
||||
switch ($failed_check) {
|
||||
case 'mb_strlen':
|
||||
$requirements['unicode']['description'] = t('Operations on Unicode strings are emulated on a best-effort basis. Install the <a href="http://php.net/mbstring">PHP mbstring extension</a> for improved Unicode support.');
|
||||
break;
|
||||
|
||||
case 'mbstring.func_overload':
|
||||
$requirements['unicode']['description'] = t('Multibyte string function overloading in PHP is active and must be disabled. Check the php.ini <em>mbstring.func_overload</em> setting. Please refer to the <a href="http://php.net/mbstring">PHP mbstring documentation</a> for more information.');
|
||||
break;
|
||||
|
||||
case 'mbstring.encoding_translation':
|
||||
$requirements['unicode']['description'] = t('Multibyte string input conversion in PHP is active and must be disabled. Check the php.ini <em>mbstring.encoding_translation</em> setting. Please refer to the <a href="http://php.net/mbstring">PHP mbstring documentation</a> for more information.');
|
||||
break;
|
||||
|
||||
case 'mbstring.http_input':
|
||||
$requirements['unicode']['description'] = t('Multibyte string input conversion in PHP is active and must be disabled. Check the php.ini <em>mbstring.http_input</em> setting. Please refer to the <a href="http://php.net/mbstring">PHP mbstring documentation</a> for more information.');
|
||||
break;
|
||||
|
||||
case 'mbstring.http_output':
|
||||
$requirements['unicode']['description'] = t('Multibyte string output conversion in PHP is active and must be disabled. Check the php.ini <em>mbstring.http_output</em> setting. Please refer to the <a href="http://php.net/mbstring">PHP mbstring documentation</a> for more information.');
|
||||
break;
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a new XML parser.
|
||||
*
|
||||
* This is a wrapper around xml_parser_create() which extracts the encoding
|
||||
* from the XML data first and sets the output encoding to UTF-8. This function
|
||||
* should be used instead of xml_parser_create(), because PHP 4's XML parser
|
||||
* doesn't check the input encoding itself. "Starting from PHP 5, the input
|
||||
* encoding is automatically detected, so that the encoding parameter specifies
|
||||
* only the output encoding."
|
||||
*
|
||||
* This is also where unsupported encodings will be converted. Callers should
|
||||
* take this into account: $data might have been changed after the call.
|
||||
*
|
||||
* @param $data
|
||||
* The XML data which will be parsed later.
|
||||
*
|
||||
* @return
|
||||
* An XML parser object or FALSE on error.
|
||||
*
|
||||
* @ingroup php_wrappers
|
||||
*/
|
||||
function drupal_xml_parser_create(&$data) {
|
||||
// Default XML encoding is UTF-8
|
||||
$encoding = 'utf-8';
|
||||
$bom = FALSE;
|
||||
|
||||
// Check for UTF-8 byte order mark (PHP5's XML parser doesn't handle it).
|
||||
if (!strncmp($data, "\xEF\xBB\xBF", 3)) {
|
||||
$bom = TRUE;
|
||||
$data = substr($data, 3);
|
||||
}
|
||||
|
||||
// Check for an encoding declaration in the XML prolog if no BOM was found.
|
||||
if (!$bom && preg_match('/^<\?xml[^>]+encoding="(.+?)"/', $data, $match)) {
|
||||
$encoding = $match[1];
|
||||
}
|
||||
|
||||
// Unsupported encodings are converted here into UTF-8.
|
||||
$php_supported = array('utf-8', 'iso-8859-1', 'us-ascii');
|
||||
if (!in_array(strtolower($encoding), $php_supported)) {
|
||||
$out = Unicode::convertToUtf8($data, $encoding);
|
||||
if ($out !== FALSE) {
|
||||
$encoding = 'utf-8';
|
||||
$data = preg_replace('/^(<\?xml[^>]+encoding)="(.+?)"/', '\\1="utf-8"', $out);
|
||||
}
|
||||
else {
|
||||
\Drupal::logger('php')->warning('Could not convert XML encoding %s to UTF-8.', array('%s' => $encoding));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
$xml_parser = xml_parser_create($encoding);
|
||||
xml_parser_set_option($xml_parser, XML_OPTION_TARGET_ENCODING, 'utf-8');
|
||||
return $xml_parser;
|
||||
}
|
764
web/core/includes/update.inc
Normal file
764
web/core/includes/update.inc
Normal file
|
@ -0,0 +1,764 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Drupal database update API.
|
||||
*
|
||||
* This file contains functions to perform database updates for a Drupal
|
||||
* installation. It is included and used extensively by update.php.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Graph\Graph;
|
||||
use Drupal\Core\Utility\Error;
|
||||
|
||||
/**
|
||||
* Disables any extensions that are incompatible with the current core version.
|
||||
*/
|
||||
function update_fix_compatibility() {
|
||||
$extension_config = \Drupal::configFactory()->getEditable('core.extension');
|
||||
$save = FALSE;
|
||||
foreach (array('module', 'theme') as $type) {
|
||||
foreach ($extension_config->get($type) as $name => $weight) {
|
||||
if (update_check_incompatibility($name, $type)) {
|
||||
$extension_config->clear("$type.$name");
|
||||
$save = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($save) {
|
||||
$extension_config->set('module', module_config_sort($extension_config->get('module')));
|
||||
$extension_config->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the compatibility of a module or theme.
|
||||
*/
|
||||
function update_check_incompatibility($name, $type = 'module') {
|
||||
static $themes, $modules;
|
||||
|
||||
// Store values of expensive functions for future use.
|
||||
if (empty($themes) || empty($modules)) {
|
||||
// We need to do a full rebuild here to make sure the database reflects any
|
||||
// code changes that were made in the filesystem before the update script
|
||||
// was initiated.
|
||||
$themes = \Drupal::service('theme_handler')->rebuildThemeData();
|
||||
$modules = system_rebuild_module_data();
|
||||
}
|
||||
|
||||
if ($type == 'module' && isset($modules[$name])) {
|
||||
$file = $modules[$name];
|
||||
}
|
||||
elseif ($type == 'theme' && isset($themes[$name])) {
|
||||
$file = $themes[$name];
|
||||
}
|
||||
if (!isset($file)
|
||||
|| !isset($file->info['core'])
|
||||
|| $file->info['core'] != \Drupal::CORE_COMPATIBILITY
|
||||
|| version_compare(phpversion(), $file->info['php']) < 0) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the minimum schema requirement has been satisfied.
|
||||
*
|
||||
* @return array
|
||||
* A requirements info array.
|
||||
*/
|
||||
function update_system_schema_requirements() {
|
||||
$requirements = array();
|
||||
|
||||
$system_schema = drupal_get_installed_schema_version('system');
|
||||
|
||||
$requirements['minimum schema']['title'] = 'Minimum schema version';
|
||||
if ($system_schema >= \Drupal::CORE_MINIMUM_SCHEMA_VERSION) {
|
||||
$requirements['minimum schema'] += array(
|
||||
'value' => 'The installed schema version meets the minimum.',
|
||||
'description' => 'Schema version: ' . $system_schema,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$requirements['minimum schema'] += array(
|
||||
'value' => 'The installed schema version does not meet the minimum.',
|
||||
'severity' => REQUIREMENT_ERROR,
|
||||
'description' => 'Your system schema version is ' . $system_schema . '. Updating directly from a schema version prior to 8000 is not supported. You must <a href="https://www.drupal.org/node/2179269">migrate your site to Drupal 8</a> first.',
|
||||
);
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks update requirements and reports errors and (optionally) warnings.
|
||||
*/
|
||||
function update_check_requirements() {
|
||||
// Check requirements of all loaded modules.
|
||||
$requirements = \Drupal::moduleHandler()->invokeAll('requirements', array('update'));
|
||||
$requirements += update_system_schema_requirements();
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a module to a given schema version.
|
||||
*
|
||||
* This function is rarely necessary.
|
||||
*
|
||||
* @param string $module
|
||||
* Name of the module.
|
||||
* @param string $schema_version
|
||||
* The schema version the module should be set to.
|
||||
*/
|
||||
function update_set_schema($module, $schema_version) {
|
||||
\Drupal::keyValue('system.schema')->set($module, $schema_version);
|
||||
// system_list_reset() is in module.inc but that would only be available
|
||||
// once the variable bootstrap is done.
|
||||
require_once __DIR__ . '/module.inc';
|
||||
system_list_reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements callback_batch_operation().
|
||||
*
|
||||
* Performs one update and stores the results for display on the results page.
|
||||
*
|
||||
* If an update function completes successfully, it should return a message
|
||||
* as a string indicating success, for example:
|
||||
* @code
|
||||
* return t('New index added successfully.');
|
||||
* @endcode
|
||||
*
|
||||
* Alternatively, it may return nothing. In that case, no message
|
||||
* will be displayed at all.
|
||||
*
|
||||
* If it fails for whatever reason, it should throw an instance of
|
||||
* Drupal\Core\Utility\UpdateException with an appropriate error message, for
|
||||
* example:
|
||||
* @code
|
||||
* use Drupal\Core\Utility\UpdateException;
|
||||
* throw new UpdateException(t('Description of what went wrong'));
|
||||
* @endcode
|
||||
*
|
||||
* If an exception is thrown, the current update and all updates that depend on
|
||||
* it will be aborted. The schema version will not be updated in this case, and
|
||||
* all the aborted updates will continue to appear on update.php as updates
|
||||
* that have not yet been run.
|
||||
*
|
||||
* If an update function needs to be re-run as part of a batch process, it
|
||||
* should accept the $sandbox array by reference as its first parameter
|
||||
* and set the #finished property to the percentage completed that it is, as a
|
||||
* fraction of 1.
|
||||
*
|
||||
* @param $module
|
||||
* The module whose update will be run.
|
||||
* @param $number
|
||||
* The update number to run.
|
||||
* @param $dependency_map
|
||||
* An array whose keys are the names of all update functions that will be
|
||||
* performed during this batch process, and whose values are arrays of other
|
||||
* update functions that each one depends on.
|
||||
* @param $context
|
||||
* The batch context array.
|
||||
*
|
||||
* @see update_resolve_dependencies()
|
||||
*/
|
||||
function update_do_one($module, $number, $dependency_map, &$context) {
|
||||
$function = $module . '_update_' . $number;
|
||||
|
||||
// If this update was aborted in a previous step, or has a dependency that
|
||||
// was aborted in a previous step, go no further.
|
||||
if (!empty($context['results']['#abort']) && array_intersect($context['results']['#abort'], array_merge($dependency_map, array($function)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ret = array();
|
||||
if (function_exists($function)) {
|
||||
try {
|
||||
$ret['results']['query'] = $function($context['sandbox']);
|
||||
$ret['results']['success'] = TRUE;
|
||||
}
|
||||
// @TODO We may want to do different error handling for different
|
||||
// exception types, but for now we'll just log the exception and
|
||||
// return the message for printing.
|
||||
// @see https://www.drupal.org/node/2564311
|
||||
catch (Exception $e) {
|
||||
watchdog_exception('update', $e);
|
||||
|
||||
$variables = Error::decodeException($e);
|
||||
unset($variables['backtrace']);
|
||||
$ret['#abort'] = array('success' => FALSE, 'query' => t('%type: @message in %function (line %line of %file).', $variables));
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($context['sandbox']['#finished'])) {
|
||||
$context['finished'] = $context['sandbox']['#finished'];
|
||||
unset($context['sandbox']['#finished']);
|
||||
}
|
||||
|
||||
if (!isset($context['results'][$module])) {
|
||||
$context['results'][$module] = array();
|
||||
}
|
||||
if (!isset($context['results'][$module][$number])) {
|
||||
$context['results'][$module][$number] = array();
|
||||
}
|
||||
$context['results'][$module][$number] = array_merge($context['results'][$module][$number], $ret);
|
||||
|
||||
if (!empty($ret['#abort'])) {
|
||||
// Record this function in the list of updates that were aborted.
|
||||
$context['results']['#abort'][] = $function;
|
||||
}
|
||||
|
||||
// Record the schema update if it was completed successfully.
|
||||
if ($context['finished'] == 1 && empty($ret['#abort'])) {
|
||||
drupal_set_installed_schema_version($module, $number);
|
||||
}
|
||||
|
||||
$context['message'] = t('Updating @module', ['@module' => $module]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a single hook_post_update_NAME().
|
||||
*
|
||||
* @param string $function
|
||||
* The function name, that should be executed.
|
||||
* @param array $context
|
||||
* The batch context array.
|
||||
*/
|
||||
function update_invoke_post_update($function, &$context) {
|
||||
$ret = [];
|
||||
|
||||
// If this update was aborted in a previous step, or has a dependency that was
|
||||
// aborted in a previous step, go no further.
|
||||
if (!empty($context['results']['#abort'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
list($module, $name) = explode('_post_update_', $function, 2);
|
||||
module_load_include('php', $module, $module . '.post_update');
|
||||
if (function_exists($function)) {
|
||||
try {
|
||||
$ret['results']['query'] = $function($context['sandbox']);
|
||||
$ret['results']['success'] = TRUE;
|
||||
|
||||
if (!isset($context['sandbox']['#finished']) || (isset($context['sandbox']['#finished']) && $context['sandbox']['#finished'] >= 1)) {
|
||||
\Drupal::service('update.post_update_registry')->registerInvokedUpdates([$function]);
|
||||
}
|
||||
}
|
||||
// @TODO We may want to do different error handling for different exception
|
||||
// types, but for now we'll just log the exception and return the message
|
||||
// for printing.
|
||||
// @see https://www.drupal.org/node/2564311
|
||||
catch (Exception $e) {
|
||||
watchdog_exception('update', $e);
|
||||
|
||||
$variables = Error::decodeException($e);
|
||||
unset($variables['backtrace']);
|
||||
$ret['#abort'] = [
|
||||
'success' => FALSE,
|
||||
'query' => t('%type: @message in %function (line %line of %file).', $variables),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($context['sandbox']['#finished'])) {
|
||||
$context['finished'] = $context['sandbox']['#finished'];
|
||||
unset($context['sandbox']['#finished']);
|
||||
}
|
||||
if (!isset($context['results'][$module][$name])) {
|
||||
$context['results'][$module][$name] = array();
|
||||
}
|
||||
$context['results'][$module][$name] = array_merge($context['results'][$module][$name], $ret);
|
||||
|
||||
if (!empty($ret['#abort'])) {
|
||||
// Record this function in the list of updates that were aborted.
|
||||
$context['results']['#abort'][] = $function;
|
||||
}
|
||||
|
||||
$context['message'] = t('Post updating @module', ['@module' => $module]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all the pending database updates.
|
||||
*
|
||||
* @return
|
||||
* An associative array keyed by module name which contains all information
|
||||
* about database updates that need to be run, and any updates that are not
|
||||
* going to proceed due to missing requirements. The system module will
|
||||
* always be listed first.
|
||||
*
|
||||
* The subarray for each module can contain the following keys:
|
||||
* - start: The starting update that is to be processed. If this does not
|
||||
* exist then do not process any updates for this module as there are
|
||||
* other requirements that need to be resolved.
|
||||
* - warning: Any warnings about why this module can not be updated.
|
||||
* - pending: An array of all the pending updates for the module including
|
||||
* the update number and the description from source code comment for
|
||||
* each update function. This array is keyed by the update number.
|
||||
*/
|
||||
function update_get_update_list() {
|
||||
// Make sure that the system module is first in the list of updates.
|
||||
$ret = array('system' => array());
|
||||
|
||||
$modules = drupal_get_installed_schema_version(NULL, FALSE, TRUE);
|
||||
foreach ($modules as $module => $schema_version) {
|
||||
// Skip uninstalled and incompatible modules.
|
||||
if ($schema_version == SCHEMA_UNINSTALLED || update_check_incompatibility($module)) {
|
||||
continue;
|
||||
}
|
||||
// Display a requirements error if the user somehow has a schema version
|
||||
// from the previous Drupal major version.
|
||||
if ($schema_version < \Drupal::CORE_MINIMUM_SCHEMA_VERSION) {
|
||||
$ret[$module]['warning'] = '<em>' . $module . '</em> module cannot be updated. Its schema version is ' . $schema_version . ', which is from an earlier major release of Drupal. You will need to <a href="https://www.drupal.org/node/2127611">migrate the data for this module</a> instead.';
|
||||
continue;
|
||||
}
|
||||
// Otherwise, get the list of updates defined by this module.
|
||||
$updates = drupal_get_schema_versions($module);
|
||||
if ($updates !== FALSE) {
|
||||
// \Drupal::moduleHandler()->invoke() returns NULL for non-existing hooks,
|
||||
// so if no updates are removed, it will == 0.
|
||||
$last_removed = \Drupal::moduleHandler()->invoke($module, 'update_last_removed');
|
||||
if ($schema_version < $last_removed) {
|
||||
$ret[$module]['warning'] = '<em>' . $module . '</em> module cannot be updated. Its schema version is ' . $schema_version . '. Updates up to and including ' . $last_removed . ' have been removed in this release. In order to update <em>' . $module . '</em> module, you will first <a href="https://www.drupal.org/upgrade">need to upgrade</a> to the last version in which these updates were available.';
|
||||
continue;
|
||||
}
|
||||
|
||||
$updates = array_combine($updates, $updates);
|
||||
foreach (array_keys($updates) as $update) {
|
||||
if ($update == \Drupal::CORE_MINIMUM_SCHEMA_VERSION) {
|
||||
$ret[$module]['warning'] = '<em>' . $module . '</em> module cannot be updated. It contains an update numbered as ' . \Drupal::CORE_MINIMUM_SCHEMA_VERSION . ' which is reserved for the earliest installation of a module in Drupal ' . \Drupal::CORE_COMPATIBILITY . ', before any updates. In order to update <em>' . $module . '</em> module, you will need to install a version of the module with valid updates.';
|
||||
continue 2;
|
||||
}
|
||||
if ($update > $schema_version) {
|
||||
// The description for an update comes from its Doxygen.
|
||||
$func = new ReflectionFunction($module . '_update_' . $update);
|
||||
$description = str_replace(array("\n", '*', '/'), '', $func->getDocComment());
|
||||
$ret[$module]['pending'][$update] = "$update - $description";
|
||||
if (!isset($ret[$module]['start'])) {
|
||||
$ret[$module]['start'] = $update;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isset($ret[$module]['start']) && isset($ret[$module]['pending'])) {
|
||||
$ret[$module]['start'] = $schema_version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($ret['system'])) {
|
||||
unset($ret['system']);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves dependencies in a set of module updates, and orders them correctly.
|
||||
*
|
||||
* This function receives a list of requested module updates and determines an
|
||||
* appropriate order to run them in such that all update dependencies are met.
|
||||
* Any updates whose dependencies cannot be met are included in the returned
|
||||
* array but have the key 'allowed' set to FALSE; the calling function should
|
||||
* take responsibility for ensuring that these updates are ultimately not
|
||||
* performed.
|
||||
*
|
||||
* In addition, the returned array also includes detailed information about the
|
||||
* dependency chain for each update, as provided by the depth-first search
|
||||
* algorithm in Drupal\Component\Graph\Graph::searchAndSort().
|
||||
*
|
||||
* @param $starting_updates
|
||||
* An array whose keys contain the names of modules with updates to be run
|
||||
* and whose values contain the number of the first requested update for that
|
||||
* module.
|
||||
*
|
||||
* @return
|
||||
* An array whose keys are the names of all update functions within the
|
||||
* provided modules that would need to be run in order to fulfill the
|
||||
* request, arranged in the order in which the update functions should be
|
||||
* run. (This includes the provided starting update for each module and all
|
||||
* subsequent updates that are available.) The values are themselves arrays
|
||||
* containing all the keys provided by the
|
||||
* Drupal\Component\Graph\Graph::searchAndSort() algorithm, which encode
|
||||
* detailed information about the dependency chain for this update function
|
||||
* (for example: 'paths', 'reverse_paths', 'weight', and 'component'), as
|
||||
* well as the following additional keys:
|
||||
* - 'allowed': A boolean which is TRUE when the update function's
|
||||
* dependencies are met, and FALSE otherwise. Calling functions should
|
||||
* inspect this value before running the update.
|
||||
* - 'missing_dependencies': An array containing the names of any other
|
||||
* update functions that are required by this one but that are unavailable
|
||||
* to be run. This array will be empty when 'allowed' is TRUE.
|
||||
* - 'module': The name of the module that this update function belongs to.
|
||||
* - 'number': The number of this update function within that module.
|
||||
*
|
||||
* @see \Drupal\Component\Graph\Graph::searchAndSort()
|
||||
*/
|
||||
function update_resolve_dependencies($starting_updates) {
|
||||
// Obtain a dependency graph for the requested update functions.
|
||||
$update_functions = update_get_update_function_list($starting_updates);
|
||||
$graph = update_build_dependency_graph($update_functions);
|
||||
|
||||
// Perform the depth-first search and sort on the results.
|
||||
$graph_object = new Graph($graph);
|
||||
$graph = $graph_object->searchAndSort();
|
||||
uasort($graph, array('Drupal\Component\Utility\SortArray', 'sortByWeightElement'));
|
||||
|
||||
foreach ($graph as $function => &$data) {
|
||||
$module = $data['module'];
|
||||
$number = $data['number'];
|
||||
// If the update function is missing and has not yet been performed, mark
|
||||
// it and everything that ultimately depends on it as disallowed.
|
||||
if (update_is_missing($module, $number, $update_functions) && !update_already_performed($module, $number)) {
|
||||
$data['allowed'] = FALSE;
|
||||
foreach (array_keys($data['paths']) as $dependent) {
|
||||
$graph[$dependent]['allowed'] = FALSE;
|
||||
$graph[$dependent]['missing_dependencies'][] = $function;
|
||||
}
|
||||
}
|
||||
elseif (!isset($data['allowed'])) {
|
||||
$data['allowed'] = TRUE;
|
||||
$data['missing_dependencies'] = array();
|
||||
}
|
||||
// Now that we have finished processing this function, remove it from the
|
||||
// graph if it was not part of the original list. This ensures that we
|
||||
// never try to run any updates that were not specifically requested.
|
||||
if (!isset($update_functions[$module][$number])) {
|
||||
unset($graph[$function]);
|
||||
}
|
||||
}
|
||||
|
||||
return $graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an organized list of update functions for a set of modules.
|
||||
*
|
||||
* @param $starting_updates
|
||||
* An array whose keys contain the names of modules and whose values contain
|
||||
* the number of the first requested update for that module.
|
||||
*
|
||||
* @return
|
||||
* An array containing all the update functions that should be run for each
|
||||
* module, including the provided starting update and all subsequent updates
|
||||
* that are available. The keys of the array contain the module names, and
|
||||
* each value is an ordered array of update functions, keyed by the update
|
||||
* number.
|
||||
*
|
||||
* @see update_resolve_dependencies()
|
||||
*/
|
||||
function update_get_update_function_list($starting_updates) {
|
||||
// Go through each module and find all updates that we need (including the
|
||||
// first update that was requested and any updates that run after it).
|
||||
$update_functions = array();
|
||||
foreach ($starting_updates as $module => $version) {
|
||||
$update_functions[$module] = array();
|
||||
$updates = drupal_get_schema_versions($module);
|
||||
if ($updates !== FALSE) {
|
||||
$max_version = max($updates);
|
||||
if ($version <= $max_version) {
|
||||
foreach ($updates as $update) {
|
||||
if ($update >= $version) {
|
||||
$update_functions[$module][$update] = $module . '_update_' . $update;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $update_functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a graph which encodes the dependencies between module updates.
|
||||
*
|
||||
* This function returns an associative array which contains a "directed graph"
|
||||
* representation of the dependencies between a provided list of update
|
||||
* functions, as well as any outside update functions that they directly depend
|
||||
* on but that were not in the provided list. The vertices of the graph
|
||||
* represent the update functions themselves, and each edge represents a
|
||||
* requirement that the first update function needs to run before the second.
|
||||
* For example, consider this graph:
|
||||
*
|
||||
* system_update_8001 ---> system_update_8002 ---> system_update_8003
|
||||
*
|
||||
* Visually, this indicates that system_update_8001() must run before
|
||||
* system_update_8002(), which in turn must run before system_update_8003().
|
||||
*
|
||||
* The function takes into account standard dependencies within each module, as
|
||||
* shown above (i.e., the fact that each module's updates must run in numerical
|
||||
* order), but also finds any cross-module dependencies that are defined by
|
||||
* modules which implement hook_update_dependencies(), and builds them into the
|
||||
* graph as well.
|
||||
*
|
||||
* @param $update_functions
|
||||
* An organized array of update functions, in the format returned by
|
||||
* update_get_update_function_list().
|
||||
*
|
||||
* @return
|
||||
* A multidimensional array representing the dependency graph, suitable for
|
||||
* passing in to Drupal\Component\Graph\Graph::searchAndSort(), but with extra
|
||||
* information about each update function also included. Each array key
|
||||
* contains the name of an update function, including all update functions
|
||||
* from the provided list as well as any outside update functions which they
|
||||
* directly depend on. Each value is an associative array containing the
|
||||
* following keys:
|
||||
* - 'edges': A representation of any other update functions that immediately
|
||||
* depend on this one. See Drupal\Component\Graph\Graph::searchAndSort() for
|
||||
* more details on the format.
|
||||
* - 'module': The name of the module that this update function belongs to.
|
||||
* - 'number': The number of this update function within that module.
|
||||
*
|
||||
* @see \Drupal\Component\Graph\Graph::searchAndSort()
|
||||
* @see update_resolve_dependencies()
|
||||
*/
|
||||
function update_build_dependency_graph($update_functions) {
|
||||
// Initialize an array that will define a directed graph representing the
|
||||
// dependencies between update functions.
|
||||
$graph = array();
|
||||
|
||||
// Go through each update function and build an initial list of dependencies.
|
||||
foreach ($update_functions as $module => $functions) {
|
||||
$previous_function = NULL;
|
||||
foreach ($functions as $number => $function) {
|
||||
// Add an edge to the directed graph representing the fact that each
|
||||
// update function in a given module must run after the update that
|
||||
// numerically precedes it.
|
||||
if ($previous_function) {
|
||||
$graph[$previous_function]['edges'][$function] = TRUE;
|
||||
}
|
||||
$previous_function = $function;
|
||||
|
||||
// Define the module and update number associated with this function.
|
||||
$graph[$function]['module'] = $module;
|
||||
$graph[$function]['number'] = $number;
|
||||
}
|
||||
}
|
||||
|
||||
// Now add any explicit update dependencies declared by modules.
|
||||
$update_dependencies = update_retrieve_dependencies();
|
||||
foreach ($graph as $function => $data) {
|
||||
if (!empty($update_dependencies[$data['module']][$data['number']])) {
|
||||
foreach ($update_dependencies[$data['module']][$data['number']] as $module => $number) {
|
||||
$dependency = $module . '_update_' . $number;
|
||||
$graph[$dependency]['edges'][$function] = TRUE;
|
||||
$graph[$dependency]['module'] = $module;
|
||||
$graph[$dependency]['number'] = $number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a module update is missing or unavailable.
|
||||
*
|
||||
* @param $module
|
||||
* The name of the module.
|
||||
* @param $number
|
||||
* The number of the update within that module.
|
||||
* @param $update_functions
|
||||
* An organized array of update functions, in the format returned by
|
||||
* update_get_update_function_list(). This should represent all module
|
||||
* updates that are requested to run at the time this function is called.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the provided module update is not installed or is not in the
|
||||
* provided list of updates to run; FALSE otherwise.
|
||||
*/
|
||||
function update_is_missing($module, $number, $update_functions) {
|
||||
return !isset($update_functions[$module][$number]) || !function_exists($update_functions[$module][$number]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a module update has already been performed.
|
||||
*
|
||||
* @param $module
|
||||
* The name of the module.
|
||||
* @param $number
|
||||
* The number of the update within that module.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the database schema indicates that the update has already been
|
||||
* performed; FALSE otherwise.
|
||||
*/
|
||||
function update_already_performed($module, $number) {
|
||||
return $number <= drupal_get_installed_schema_version($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes hook_update_dependencies() in all installed modules.
|
||||
*
|
||||
* This function is similar to \Drupal::moduleHandler()->invokeAll(), with the
|
||||
* main difference that it does not require that a module be enabled to invoke
|
||||
* its hook, only that it be installed. This allows the update system to
|
||||
* properly perform updates even on modules that are currently disabled.
|
||||
*
|
||||
* @return
|
||||
* An array of return values obtained by merging the results of the
|
||||
* hook_update_dependencies() implementations in all installed modules.
|
||||
*
|
||||
* @see \Drupal\Core\Extension\ModuleHandlerInterface::invokeAll()
|
||||
* @see hook_update_dependencies()
|
||||
*/
|
||||
function update_retrieve_dependencies() {
|
||||
$return = array();
|
||||
// Get a list of installed modules, arranged so that we invoke their hooks in
|
||||
// the same order that \Drupal::moduleHandler()->invokeAll() does.
|
||||
foreach (\Drupal::keyValue('system.schema')->getAll() as $module => $schema) {
|
||||
if ($schema == SCHEMA_UNINSTALLED) {
|
||||
// Nothing to upgrade.
|
||||
continue;
|
||||
}
|
||||
$function = $module . '_update_dependencies';
|
||||
// Ensure install file is loaded.
|
||||
module_load_install($module);
|
||||
if (function_exists($function)) {
|
||||
$updated_dependencies = $function();
|
||||
// Each implementation of hook_update_dependencies() returns a
|
||||
// multidimensional, associative array containing some keys that
|
||||
// represent module names (which are strings) and other keys that
|
||||
// represent update function numbers (which are integers). We cannot use
|
||||
// array_merge_recursive() to properly merge these results, since it
|
||||
// treats strings and integers differently. Therefore, we have to
|
||||
// explicitly loop through the expected array structure here and perform
|
||||
// the merge manually.
|
||||
if (isset($updated_dependencies) && is_array($updated_dependencies)) {
|
||||
foreach ($updated_dependencies as $module_name => $module_data) {
|
||||
foreach ($module_data as $update_version => $update_data) {
|
||||
foreach ($update_data as $module_dependency => $update_dependency) {
|
||||
// If there are redundant dependencies declared for the same
|
||||
// update function (so that it is declared to depend on more than
|
||||
// one update from a particular module), record the dependency on
|
||||
// the highest numbered update here, since that automatically
|
||||
// implies the previous ones. For example, if one module's
|
||||
// implementation of hook_update_dependencies() required this
|
||||
// ordering:
|
||||
//
|
||||
// system_update_8002 ---> user_update_8001
|
||||
//
|
||||
// but another module's implementation of the hook required this
|
||||
// one:
|
||||
//
|
||||
// system_update_8003 ---> user_update_8001
|
||||
//
|
||||
// we record the second one, since system_update_8002() is always
|
||||
// guaranteed to run before system_update_8003() anyway (within
|
||||
// an individual module, updates are always run in numerical
|
||||
// order).
|
||||
if (!isset($return[$module_name][$update_version][$module_dependency]) || $update_dependency > $return[$module_name][$update_version][$module_dependency]) {
|
||||
$return[$module_name][$update_version][$module_dependency] = $update_dependency;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace permissions during update.
|
||||
*
|
||||
* This function can replace one permission to several or even delete an old
|
||||
* one.
|
||||
*
|
||||
* @param array $replace
|
||||
* An associative array. The keys are the old permissions the values are lists
|
||||
* of new permissions. If the list is an empty array, the old permission is
|
||||
* removed.
|
||||
*/
|
||||
function update_replace_permissions($replace) {
|
||||
$prefix = 'user.role.';
|
||||
$cut = strlen($prefix);
|
||||
$role_names = \Drupal::service('config.storage')->listAll($prefix);
|
||||
foreach ($role_names as $role_name) {
|
||||
$rid = substr($role_name, $cut);
|
||||
$config = \Drupal::config("user.role.$rid");
|
||||
$permissions = $config->get('permissions') ?: array();
|
||||
foreach ($replace as $old_permission => $new_permissions) {
|
||||
if (($index = array_search($old_permission, $permissions)) !== FALSE) {
|
||||
unset($permissions[$index]);
|
||||
$permissions = array_unique(array_merge($permissions, $new_permissions));
|
||||
}
|
||||
}
|
||||
$config
|
||||
->set('permissions', $permissions)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of languages set up on the site during upgrades.
|
||||
*
|
||||
* @param $flags
|
||||
* (optional) Specifies the state of the languages that have to be returned.
|
||||
* It can be: LanguageInterface::STATE_CONFIGURABLE,
|
||||
* LanguageInterface::STATE_LOCKED, or LanguageInterface::STATE_ALL.
|
||||
*
|
||||
* @return \Drupal\Core\Language\LanguageInterface[]
|
||||
* An associative array of languages, keyed by the language code, ordered by
|
||||
* weight ascending and name ascending.
|
||||
*/
|
||||
function update_language_list($flags = LanguageInterface::STATE_CONFIGURABLE) {
|
||||
|
||||
$languages = &drupal_static(__FUNCTION__);
|
||||
|
||||
// Initialize master language list.
|
||||
if (!isset($languages)) {
|
||||
// Initialize local language list cache.
|
||||
$languages = array();
|
||||
|
||||
// Fill in master language list based on current configuration.
|
||||
$default = \Drupal::languageManager()->getDefaultLanguage();
|
||||
if (\Drupal::languageManager()->isMultilingual() || \Drupal::moduleHandler()->moduleExists('language')) {
|
||||
// Use language module configuration if available. We can not use
|
||||
// entity_load_multiple() because this breaks during updates.
|
||||
$language_entities = \Drupal::configFactory()->listAll('language.entity.');
|
||||
|
||||
// Initialize default property so callers have an easy reference and can
|
||||
// save the same object without data loss.
|
||||
foreach ($language_entities as $langcode_config_name) {
|
||||
$langcode = substr($langcode_config_name, strlen('language.entity.'));
|
||||
$info = \Drupal::config($langcode_config_name)->get();
|
||||
$languages[$langcode] = new Language(array(
|
||||
'default' => ($info['id'] == $default->getId()),
|
||||
'name' => $info['label'],
|
||||
'id' => $info['id'],
|
||||
'direction' => $info['direction'],
|
||||
'locked' => $info['locked'],
|
||||
'weight' => $info['weight'],
|
||||
));
|
||||
}
|
||||
Language::sort($languages);
|
||||
}
|
||||
else {
|
||||
// No language module, so use the default language only.
|
||||
$languages = array($default->getId() => $default);
|
||||
// Add the special languages, they will be filtered later if needed.
|
||||
$languages += \Drupal::languageManager()->getDefaultLockedLanguages($default->getWeight());
|
||||
}
|
||||
}
|
||||
|
||||
// Filter the full list of languages based on the value of the $all flag. By
|
||||
// default we remove the locked languages, but the caller may request for
|
||||
// those languages to be added as well.
|
||||
$filtered_languages = array();
|
||||
|
||||
// Add the site's default language if flagged as allowed value.
|
||||
if ($flags & LanguageInterface::STATE_SITE_DEFAULT) {
|
||||
$default = \Drupal::languageManager()->getDefaultLanguage();
|
||||
// Rename the default language.
|
||||
$default->setName(t("Site's default language (@lang_name)", array('@lang_name' => $default->getName())));
|
||||
$filtered_languages[LanguageInterface::LANGCODE_SITE_DEFAULT] = $default;
|
||||
}
|
||||
|
||||
foreach ($languages as $langcode => $language) {
|
||||
if (($language->isLocked() && !($flags & LanguageInterface::STATE_LOCKED)) || (!$language->isLocked() && !($flags & LanguageInterface::STATE_CONFIGURABLE))) {
|
||||
continue;
|
||||
}
|
||||
$filtered_languages[$langcode] = $language;
|
||||
}
|
||||
|
||||
return $filtered_languages;
|
||||
}
|
57
web/core/includes/utility.inc
Normal file
57
web/core/includes/utility.inc
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Miscellaneous functions.
|
||||
*/
|
||||
|
||||
use Drupal\Core\PhpStorage\PhpStorageFactory;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\DrupalKernel;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Composer\Autoload\ClassLoader;
|
||||
|
||||
/**
|
||||
* Rebuilds all caches even when Drupal itself does not work.
|
||||
*
|
||||
* @param \Composer\Autoload\ClassLoader $class_loader
|
||||
* The class loader.
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @see rebuild.php
|
||||
*/
|
||||
function drupal_rebuild(ClassLoader $class_loader, Request $request) {
|
||||
// Remove Drupal's error and exception handlers; they rely on a working
|
||||
// service container and other subsystems and will only cause a fatal error
|
||||
// that hides the actual error.
|
||||
restore_error_handler();
|
||||
restore_exception_handler();
|
||||
|
||||
// Force kernel to rebuild php cache.
|
||||
PhpStorageFactory::get('twig')->deleteAll();
|
||||
|
||||
// Bootstrap up to where caches exist and clear them.
|
||||
$kernel = new DrupalKernel('prod', $class_loader);
|
||||
$kernel->setSitePath(DrupalKernel::findSitePath($request));
|
||||
|
||||
// Invalidate the container.
|
||||
$kernel->invalidateContainer();
|
||||
|
||||
// Prepare a NULL request.
|
||||
$kernel->prepareLegacyRequest($request);
|
||||
|
||||
foreach (Cache::getBins() as $bin) {
|
||||
$bin->deleteAll();
|
||||
}
|
||||
|
||||
// Disable recording of cached pages.
|
||||
\Drupal::service('page_cache_kill_switch')->trigger();
|
||||
|
||||
drupal_flush_all_caches();
|
||||
|
||||
// Restore Drupal's error and exception handlers.
|
||||
// @see \Drupal\Core\DrupalKernel::boot()
|
||||
set_error_handler('_drupal_error_handler');
|
||||
set_exception_handler('_drupal_exception_handler');
|
||||
}
|
Reference in a new issue