first commit
This commit is contained in:
@@ -0,0 +1,557 @@
|
||||
<?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::messenger()->addError(t('No active batch.'));
|
||||
return new RedirectResponse(Url::fromRoute('<front>', [], ['absolute' => TRUE])->toString());
|
||||
}
|
||||
}
|
||||
|
||||
// We need to store the updated batch information in the batch storage after
|
||||
// processing the batch. In order for the error page to work correctly this
|
||||
// needs to be done even in case of a PHP fatal error in which case the end of
|
||||
// this function is never reached. Therefore we register a shutdown function
|
||||
// to handle this case. Because with FastCGI and fastcgi_finish_request()
|
||||
// shutdown functions are called after the HTTP connection is closed, updating
|
||||
// the batch information in a shutdown function would lead to race conditions
|
||||
// between consecutive requests if the batch processing continues. In case of
|
||||
// a fatal error the processing stops anyway, so it works even with FastCGI.
|
||||
// However, we must ensure to only update in the shutdown phase in this
|
||||
// particular case we track whether the batch information still needs to be
|
||||
// updated.
|
||||
// @see _batch_shutdown()
|
||||
// @see \Symfony\Component\HttpFoundation\Response::send()
|
||||
drupal_register_shutdown_function('_batch_shutdown');
|
||||
_batch_needs_update(TRUE);
|
||||
|
||||
$build = [];
|
||||
|
||||
// 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();
|
||||
|
||||
$response = $build;
|
||||
break;
|
||||
|
||||
case 'do':
|
||||
// JavaScript-based progress page callback.
|
||||
$response = _batch_do();
|
||||
break;
|
||||
|
||||
case 'finished':
|
||||
// _batch_finished() returns a RedirectResponse.
|
||||
$response = _batch_finished();
|
||||
break;
|
||||
}
|
||||
|
||||
if ($batch) {
|
||||
\Drupal::service('batch.storage')->update($batch);
|
||||
}
|
||||
_batch_needs_update(FALSE);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the batch information needs to be updated in the storage.
|
||||
*
|
||||
* @param bool $new_value
|
||||
* (optional) A new value to set.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the batch information needs to be updated; FALSE otherwise.
|
||||
*/
|
||||
function _batch_needs_update($new_value = NULL) {
|
||||
$needs_update = &drupal_static(__FUNCTION__, FALSE);
|
||||
|
||||
if (isset($new_value)) {
|
||||
$needs_update = $new_value;
|
||||
}
|
||||
|
||||
return $needs_update;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(['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', [
|
||||
'#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 = [
|
||||
'#theme' => 'progress_bar',
|
||||
'#percent' => $percentage,
|
||||
'#message' => ['#markup' => $message],
|
||||
'#label' => $label,
|
||||
'#attached' => [
|
||||
'html_head' => [
|
||||
[
|
||||
[
|
||||
// Redirect through a 'Refresh' meta tag if JavaScript is disabled.
|
||||
'#tag' => 'meta',
|
||||
'#noscript' => TRUE,
|
||||
'#attributes' => [
|
||||
'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' => [
|
||||
'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;
|
||||
$task_message = '';
|
||||
|
||||
// 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 = '';
|
||||
// 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 = [
|
||||
'sandbox' => &$current_set['sandbox'],
|
||||
'results' => &$current_set['results'],
|
||||
'finished' => &$finished,
|
||||
'message' => &$task_message,
|
||||
];
|
||||
call_user_func_array($callback, array_merge($args, [&$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'] = [];
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = [
|
||||
'@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);
|
||||
|
||||
return [$percentage, $message, $task_message];
|
||||
}
|
||||
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();
|
||||
$set_indexes = array_keys($batch['sets']);
|
||||
$current_set_index_key = array_search($batch['current_set'], $set_indexes);
|
||||
if (isset($set_indexes[$current_set_index_key + 1])) {
|
||||
$batch['current_set'] = $set_indexes[$current_set_index_key + 1];
|
||||
$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, [&$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'], [$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'];
|
||||
$_batch['source_url']->mergeOptions(['query' => ['op' => 'finish', 'id' => $_batch['id']]]);
|
||||
if (is_callable($callback)) {
|
||||
$callback($_batch['source_url'], $_batch['source_url']->getOption('query'));
|
||||
}
|
||||
elseif ($callback === NULL) {
|
||||
// Default to RedirectResponse objects when nothing specified.
|
||||
return new RedirectResponse($_batch['source_url']->setAbsolute()->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()) && _batch_needs_update()) {
|
||||
\Drupal::service('batch.storage')->update($batch);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,485 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Entity API for handling entities like nodes or users.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Clears the entity render cache for all entity types.
|
||||
*
|
||||
* @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Instead,
|
||||
* use \Drupal\Core\Entity\EntityViewBuilderInterface::resetCache() on the
|
||||
* required entity types or invalidate specific cache tags.
|
||||
*
|
||||
* @see https://www.drupal.org/node/3000037
|
||||
* @see \Drupal\Core\Entity\EntityViewBuilderInterface::resetCache()
|
||||
* @see \Drupal\Core\Entity\EntityTypeManagerInterface::getDefinitions()
|
||||
*/
|
||||
function entity_render_cache_clear() {
|
||||
@trigger_error(__FUNCTION__ . '() is deprecated. Use \Drupal\Core\Entity\EntityViewBuilderInterface::resetCache() on the required entity types or invalidate specific cache tags instead. See https://www.drupal.org/node/3000037', E_USER_DEPRECATED);
|
||||
$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.0.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getBundleInfo() for a
|
||||
* single bundle, or
|
||||
* \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getAllBundleInfo() for
|
||||
* all bundles.
|
||||
*
|
||||
* @see https://www.drupal.org/node/3051077
|
||||
* @see \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getBundleInfo()
|
||||
* @see \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getAllBundleInfo()
|
||||
*/
|
||||
function entity_get_bundles($entity_type = NULL) {
|
||||
@trigger_error('entity_get_bundles() is deprecated in Drupal 8.0.0 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 https://www.drupal.org/node/3051077', E_USER_DEPRECATED);
|
||||
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.0 and is removed from drupal:9.0.0. Use the
|
||||
* entity type storage's load() method.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2266845
|
||||
*/
|
||||
function entity_load($entity_type, $id, $reset = FALSE) {
|
||||
@trigger_error('entity_load() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the entity type storage\'s load() method. See https://www.drupal.org/node/2266845', E_USER_DEPRECATED);
|
||||
$controller = \Drupal::entityManager()->getStorage($entity_type);
|
||||
if ($reset) {
|
||||
$controller->resetCache([$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 in drupal:8.0.0 and is removed from drupal:9.0.0. Use the
|
||||
* entity type storage's loadRevision() method.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1818376
|
||||
*/
|
||||
function entity_revision_load($entity_type, $revision_id) {
|
||||
@trigger_error('entity_revision_load() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the entity type storage\'s loadRevision() method. See https://www.drupal.org/node/1818376', E_USER_DEPRECATED);
|
||||
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 in drupal:8.0.0 and is removed from drupal:9.0.0. Use the
|
||||
* entity type storage's deleteRevision() method.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1818376
|
||||
*/
|
||||
function entity_revision_delete($entity_type, $revision_id) {
|
||||
@trigger_error('entity_revision_delete() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the entity type storage\'s deleteRevision() method. See https://www.drupal.org/node/1818376', E_USER_DEPRECATED);
|
||||
\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.0 and is removed from drupal:9.0.0. Use the
|
||||
* entity type storage's loadMultiple() method.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2266845
|
||||
*/
|
||||
function entity_load_multiple($entity_type, array $ids = NULL, $reset = FALSE) {
|
||||
@trigger_error('entity_load_multiple() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the entity type storage\'s loadMultiple() method. See https://www.drupal.org/node/2266845', E_USER_DEPRECATED);
|
||||
$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 in drupal:8.0.0 and is removed from drupal:9.0.0. Use the
|
||||
* entity type storage's loadByProperties() method.
|
||||
*
|
||||
* @see https://www.drupal.org/node/3050910
|
||||
*/
|
||||
function entity_load_multiple_by_properties($entity_type, array $values) {
|
||||
@trigger_error('entity_load_multiple_by_properties() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the entity type storage\'s loadByProperties() method. See https://www.drupal.org/node/3050910', E_USER_DEPRECATED);
|
||||
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 in drupal:8.0.0 and is removed from drupal:9.0.0. Use the
|
||||
* entity type storage's loadUnchanged() method.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1935744
|
||||
*/
|
||||
function entity_load_unchanged($entity_type, $id) {
|
||||
@trigger_error('entity_load_unchanged() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the entity type storage\'s loadUnchanged() method. See https://www.drupal.org/node/1935744', E_USER_DEPRECATED);
|
||||
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 in drupal:8.0.0 and is removed from 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()
|
||||
* @see https://www.drupal.org/node/3051072
|
||||
*/
|
||||
function entity_delete_multiple($entity_type, array $ids) {
|
||||
@trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.0.0 and will be removed in drupal:9.0.0. Use the entity storage\'s delete() method to delete multiple entities. @see https://www.drupal.org/node/3051072', E_USER_DEPRECATED);
|
||||
$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.0 and is removed from 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 https://www.drupal.org/node/2266845
|
||||
* @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::create()
|
||||
*/
|
||||
function entity_create($entity_type, array $values = []) {
|
||||
@trigger_error('entity_create() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the create() method of the entity type class directly or \Drupal::entityTypeManager()->getStorage($entity_type)->create($values) instead. See https://www.drupal.org/node/2266845', E_USER_DEPRECATED);
|
||||
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 in drupal:8.0.0 and is removed from drupal:9.0.0. Use the
|
||||
* entity's label() method.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2549923
|
||||
* @see \Drupal\Core\Entity\EntityInterface::label()
|
||||
*/
|
||||
function entity_page_label(EntityInterface $entity, $langcode = NULL) {
|
||||
@trigger_error('entity_page_label() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the entity\'s label() method. See https://www.drupal.org/node/2549923', E_USER_DEPRECATED);
|
||||
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 in drupal:8.0.0 and is removed from 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 https://www.drupal.org/node/3033656
|
||||
* @see \Drupal\Core\Entity\EntityTypeManagerInterface::getViewBuilder()
|
||||
* @see \Drupal\Core\Entity\EntityViewBuilderInterface::view()
|
||||
*/
|
||||
function entity_view(EntityInterface $entity, $view_mode, $langcode = NULL, $reset = FALSE) {
|
||||
@trigger_error('entity_view() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal::entityTypeManager()->getViewBuilder($entity->getEntityTypeId())->view($entity, $view_mode, $langcode) instead. See https://www.drupal.org/node/3033656', E_USER_DEPRECATED);
|
||||
$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 in drupal:8.0.0 and is removed from 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 https://www.drupal.org/node/3033656
|
||||
* @see \Drupal\Core\Entity\EntityTypeManagerInterface::getViewBuilder()
|
||||
* @see \Drupal\Core\Entity\EntityViewBuilderInterface::viewMultiple()
|
||||
*/
|
||||
function entity_view_multiple(array $entities, $view_mode, $langcode = NULL, $reset = FALSE) {
|
||||
@trigger_error('entity_view_multiple() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal::entityTypeManager()->getViewBuilder($entity->getEntityTypeId())->viewMultiple($entities, $view_mode, $langcode) instead. See https://www.drupal.org/node/3033656', E_USER_DEPRECATED);
|
||||
$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 in drupal:8.8.0 and is removed from drupal:9.0.0. Use
|
||||
* EntityDisplayRepositoryInterface::getViewDisplay() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2835616
|
||||
*/
|
||||
function entity_get_display($entity_type, $bundle, $view_mode) {
|
||||
@trigger_error('entity_get_display() is deprecated in drupal:8.8.0. It will be removed before drupal:9.0.0. Use \Drupal::service(\'entity_display.repository\')->getViewDisplay() instead. See https://www.drupal.org/node/2835616', E_USER_DEPRECATED);
|
||||
return \Drupal::service('entity_display.repository')
|
||||
->getViewDisplay($entity_type, $bundle, $view_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(
|
||||
* 'region' => '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 in drupal:8.8.0 and is removed from drupal:9.0.0. Use
|
||||
* EntityDisplayRepositoryInterface::getFormDisplay() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2835616
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::create()
|
||||
* @see \Drupal\Core\Entity\EntityStorageInterface::load()
|
||||
*/
|
||||
function entity_get_form_display($entity_type, $bundle, $form_mode) {
|
||||
@trigger_error('entity_get_form_display() is deprecated in drupal:8.8.0. It will be removed before drupal:9.0.0. Use \Drupal::service(\'entity_display.repository\')->getFormDisplay() instead. See https://www.drupal.org/node/2835616', E_USER_DEPRECATED);
|
||||
return \Drupal::service('entity_display.repository')
|
||||
->getFormDisplay($entity_type, $bundle, $form_mode);
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Functions for error handling.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Installer\InstallerKernel;
|
||||
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 = [
|
||||
E_ERROR => ['Error', RfcLogLevel::ERROR],
|
||||
E_WARNING => ['Warning', RfcLogLevel::WARNING],
|
||||
E_PARSE => ['Parse error', RfcLogLevel::ERROR],
|
||||
E_NOTICE => ['Notice', RfcLogLevel::NOTICE],
|
||||
E_CORE_ERROR => ['Core error', RfcLogLevel::ERROR],
|
||||
E_CORE_WARNING => ['Core warning', RfcLogLevel::WARNING],
|
||||
E_COMPILE_ERROR => ['Compile error', RfcLogLevel::ERROR],
|
||||
E_COMPILE_WARNING => ['Compile warning', RfcLogLevel::WARNING],
|
||||
E_USER_ERROR => ['User error', RfcLogLevel::ERROR],
|
||||
E_USER_WARNING => ['User warning', RfcLogLevel::WARNING],
|
||||
E_USER_NOTICE => ['User notice', RfcLogLevel::NOTICE],
|
||||
E_STRICT => ['Strict warning', RfcLogLevel::DEBUG],
|
||||
E_RECOVERABLE_ERROR => ['Recoverable fatal error', RfcLogLevel::ERROR],
|
||||
E_DEPRECATED => ['Deprecated function', RfcLogLevel::DEBUG],
|
||||
E_USER_DEPRECATED => ['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([
|
||||
'%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);
|
||||
}
|
||||
// If the site is a test site then fail for user deprecations so they can be
|
||||
// caught by the deprecation error handler.
|
||||
elseif (DRUPAL_TEST_IN_CHILD_SITE && $error_level === E_USER_DEPRECATED) {
|
||||
static $seen = [];
|
||||
if (array_search($message, $seen, TRUE) === FALSE) {
|
||||
// Only report each deprecation once. Too many headers can break some
|
||||
// Chrome and web driver testing.
|
||||
$seen[] = $message;
|
||||
$backtrace = debug_backtrace();
|
||||
$caller = Error::getLastCaller($backtrace);
|
||||
_drupal_error_header(
|
||||
Markup::create(Xss::filterAdmin($message)),
|
||||
'User deprecated function',
|
||||
$caller['function'],
|
||||
$caller['file'],
|
||||
$caller['line']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = InstallerKernel::installationAttempted();
|
||||
|
||||
// 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)) {
|
||||
_drupal_error_header($error['@message'], $error['%type'], $error['%function'], $error['%file'], $error['%line']);
|
||||
}
|
||||
|
||||
$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 {
|
||||
// Provide the PHP backtrace to logger implementations.
|
||||
\Drupal::logger('php')->log($error['severity_level'], '%type: @message in %function (line %line of %file) @backtrace_string.', $error + ['backtrace' => $backtrace]);
|
||||
}
|
||||
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(new FormattableMarkup('%type: @message in %function (line %line of %file).', $error))) . "\n");
|
||||
$response->send();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
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(new FormattableMarkup('%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 use \Drupal\Component\Render\FormattableMarkup 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 = new FormattableMarkup('%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 = new FormattableMarkup('%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 = [
|
||||
'#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::messenger()->addMessage($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 (InstallerKernel::installationAttempted()) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds error information to headers so that tests can access it.
|
||||
*
|
||||
* @param $message
|
||||
* The error message.
|
||||
* @param $type
|
||||
* The type of error.
|
||||
* @param $function
|
||||
* The function that emitted the error.
|
||||
* @param $file
|
||||
* The file that emitted the error.
|
||||
* @param $line
|
||||
* The line number in file that emitted the error.
|
||||
*/
|
||||
function _drupal_error_header($message, $type, $function, $file, $line) {
|
||||
// $number does not use drupal_static as it should not be reset
|
||||
// as it uniquely identifies each PHP error.
|
||||
static $number = 0;
|
||||
$assertion = [
|
||||
$message,
|
||||
$type,
|
||||
[
|
||||
'function' => $function,
|
||||
'file' => $file,
|
||||
'line' => $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++;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* API for the Drupal menu system.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup menu
|
||||
* @{
|
||||
*/
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
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 += [
|
||||
'localized_options' => [],
|
||||
];
|
||||
$link_text = $link['title'];
|
||||
|
||||
if (!empty($variables['element']['#active'])) {
|
||||
$variables['is_active'] = TRUE;
|
||||
|
||||
// Add text to indicate active tab for non-visual users.
|
||||
$active = new FormattableMarkup('<span class="visually-hidden">@label</span>', ['@label' => t('(active tab)')]);
|
||||
$link_text = t('@local-task-title@active', ['@local-task-title' => $link_text, '@active' => $active]);
|
||||
}
|
||||
|
||||
$link['localized_options']['set_active_class'] = TRUE;
|
||||
|
||||
$variables['link'] = [
|
||||
'#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 += [
|
||||
'localized_options' => [],
|
||||
];
|
||||
$link['localized_options']['attributes']['class'][] = 'button';
|
||||
$link['localized_options']['attributes']['class'][] = 'button-action';
|
||||
$link['localized_options']['set_active_class'] = TRUE;
|
||||
|
||||
$variables['link'] = [
|
||||
'#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 [
|
||||
'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()
|
||||
* @see https://www.drupal.org/node/2544940
|
||||
*
|
||||
* @deprecated in drupal:8.0.0 and is removed from 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 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Core\Menu\LocalTaskManagerInterface::getLocalTasks() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2874695
|
||||
*/
|
||||
function menu_primary_local_tasks() {
|
||||
@trigger_error(__FUNCTION__ . '() is deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Menu\LocalTaskManagerInterface::getLocalTasks() instead. See https://www.drupal.org/node/2874695', E_USER_DEPRECATED);
|
||||
/** @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 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Core\Menu\LocalTaskManagerInterface::getLocalTasks() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2874695
|
||||
*/
|
||||
function menu_secondary_local_tasks() {
|
||||
@trigger_error(__FUNCTION__ . '() is deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Menu\LocalTaskManagerInterface::getLocalTasks() instead. See https://www.drupal.org/node/2874695', E_USER_DEPRECATED);
|
||||
/** @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.
|
||||
*
|
||||
* @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
|
||||
* local_tasks_block block or inline theming instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2874695
|
||||
*/
|
||||
function menu_local_tabs() {
|
||||
@trigger_error(__FUNCTION__ . '() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use local_tasks_block block or inline theming instead. See https://www.drupal.org/node/2874695', E_USER_DEPRECATED);
|
||||
$build = [
|
||||
'#theme' => 'menu_local_tasks',
|
||||
'#primary' => menu_primary_local_tasks(),
|
||||
'#secondary' => menu_secondary_local_tasks(),
|
||||
];
|
||||
return !empty($build['#primary']) || !empty($build['#secondary']) ? $build : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all cached menu data.
|
||||
*
|
||||
* This should be called any time broad changes
|
||||
* might have been made to the router items or menu links.
|
||||
*
|
||||
* @deprecated in drupal:8.6.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal::cache('menu')->invalidateAll() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2989138
|
||||
*/
|
||||
function menu_cache_clear_all() {
|
||||
@trigger_error("menu_cache_clear_all() is deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0. Use \Drupal::cache('menu')->invalidateAll() instead. See https://www.drupal.org/node/2989138", E_USER_DEPRECATED);
|
||||
\Drupal::cache('menu')->invalidateAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup menu".
|
||||
*/
|
||||
@@ -0,0 +1,240 @@
|
||||
<?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 array
|
||||
* 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.
|
||||
*
|
||||
* @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal::service('theme_handler')->listInfo() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2709919
|
||||
* @see \Drupal\Core\Extension\ThemeHandler::listInfo()
|
||||
*/
|
||||
function system_list($type) {
|
||||
@trigger_error('system_list() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal::service(\'theme_handler\')->listInfo() instead. See https://www.drupal.org/node/2709919', E_USER_DEPRECATED);
|
||||
|
||||
$lists = [
|
||||
'theme' => \Drupal::service('theme_handler')->listInfo(),
|
||||
'filepaths' => [],
|
||||
];
|
||||
foreach ($lists['theme'] as $name => $theme) {
|
||||
$lists['filepaths'][] = [
|
||||
'type' => 'theme',
|
||||
'name' => $name,
|
||||
'filepath' => $theme->getPathname(),
|
||||
];
|
||||
}
|
||||
return $lists[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all system_list() caches.
|
||||
*
|
||||
* @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. There
|
||||
* is no direct replacement. Call each
|
||||
* \Drupal::service('extension.list.TYPE')->reset() as necessary.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2709919
|
||||
*/
|
||||
function system_list_reset() {
|
||||
@trigger_error("system_list_reset() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. There is no direct replacement. Call each \Drupal::service('extension.list.TYPE')->reset() as necessary. See https://www.drupal.org/node/2709919.", E_USER_DEPRECATED);
|
||||
\Drupal::service('extension.list.profile')->reset();
|
||||
\Drupal::service('extension.list.module')->reset();
|
||||
\Drupal::service('extension.list.theme_engine')->reset();
|
||||
\Drupal::service('extension.list.theme')->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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'.
|
||||
*
|
||||
* @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. There is
|
||||
* no replacement for this function. Use the following sequence of code to
|
||||
* achieve the same functionality:
|
||||
* @code
|
||||
* $path = \Drupal::service("extension.list.$type")->getPath($name);
|
||||
* \Drupal::service('class_loader')->addPsr4('Drupal\\' . $name . '\\', \Drupal::root() . '/' . $path . '/src');
|
||||
* @endcode
|
||||
*/
|
||||
function system_register($type, $name, $uri) {
|
||||
@trigger_error('system_register() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. There is no replacement for this function. To achieve the same functionality use this snippet: $path = \Drupal::service("extension.list.$type")->getPath($name); ' . "\\Drupal::service('class_loader')->addPsr4('Drupal\\\\' . \$name . '\\\\', \\Drupal::root() . '/' . \$path . '/src'); See https://www.drupal.org/node/3035275.", E_USER_DEPRECATED);
|
||||
\Drupal::service('class_loader')->addPsr4('Drupal\\' . $name . '\\', \Drupal::root() . '/' . dirname($uri) . '/src');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
|
||||
// Unless called by the installer, an installation profile is required and
|
||||
// must always be loaded.
|
||||
if ($profile = \Drupal::installProfile()) {
|
||||
$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 = [];
|
||||
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 = [];
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Functions to aid in presenting database results as a set of pages.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Template\Attribute;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Component\Utility\Html;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Core\Pager\RequestPagerInterface->findPage() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2779457
|
||||
* @see \Drupal\Core\Pager\PagerParametersInterface::findPage()
|
||||
*/
|
||||
function pager_find_page($element = 0) {
|
||||
@trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Pager\RequestPagerInterface->findPage() instead. See https://www.drupal.org/node/2779457', E_USER_DEPRECATED);
|
||||
/* @var $pager_parameters \Drupal\Core\Pager\PagerParametersInterface */
|
||||
$pager_parameters = \Drupal::service('pager.parameters');
|
||||
return $pager_parameters->findPage($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 = \Drupal::database()->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.
|
||||
*
|
||||
* @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Core\Pager\PagerManagerInterface->defaultInitialize() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2779457
|
||||
* @see \Drupal\Core\Pager\PagerManagerInterface::createPager()
|
||||
*/
|
||||
function pager_default_initialize($total, $limit, $element = 0) {
|
||||
@trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface->createPager() instead. See https://www.drupal.org/node/2779457', E_USER_DEPRECATED);
|
||||
/* @var $pager_manager \Drupal\Core\Pager\PagerManagerInterface */
|
||||
$pager_manager = \Drupal::service('pager.manager');
|
||||
$pager = $pager_manager->createPager($total, $limit, $element);
|
||||
return $pager->getCurrentPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Core\Pager\RequestPagerInterface->getQueryParameters() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2779457
|
||||
* @see \Drupal\Core\Pager\PagerParametersInterface::getQueryParameters()
|
||||
*/
|
||||
function pager_get_query_parameters() {
|
||||
@trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Pager\RequestPagerInterface->getQueryParameters() instead. See https://www.drupal.org/node/2779457', E_USER_DEPRECATED);
|
||||
/* @var $pager_params \Drupal\Core\Pager\PagerParametersInterface */
|
||||
$pager_params = \Drupal::service('pager.parameters');
|
||||
return $pager_params->getQueryParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = empty($variables['pager']['#quantity']) ? 0 : $variables['pager']['#quantity'];
|
||||
$route_name = $variables['pager']['#route_name'];
|
||||
$route_parameters = isset($variables['pager']['#route_parameters']) ? $variables['pager']['#route_parameters'] : [];
|
||||
|
||||
/* @var $pager_manager \Drupal\Core\Pager\PagerManagerInterface */
|
||||
$pager_manager = \Drupal::service('pager.manager');
|
||||
|
||||
$pager = $pager_manager->getPager($element);
|
||||
|
||||
// Nothing to do if there is no pager.
|
||||
if (!isset($pager)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$pager_max = $pager->getTotalPages();
|
||||
|
||||
// Nothing to do if there is only one page.
|
||||
if ($pager_max <= 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_page = $pager->getCurrentPage();
|
||||
// The current pager is the page we are currently paged to.
|
||||
$pager_current = $current_page + 1;
|
||||
// The first pager is the first page listed by this pager piece (re quantity).
|
||||
$pager_first = $pager_current - $pager_middle + 1;
|
||||
// The last is the last page listed by this pager piece (re quantity).
|
||||
$pager_last = $pager_current + $quantity - $pager_middle;
|
||||
// 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 ($current_page > 0) {
|
||||
$items['first'] = [];
|
||||
$items['first']['attributes'] = new Attribute();
|
||||
$options = [
|
||||
'query' => $pager_manager->getUpdatedParameters($parameters, $element, 0),
|
||||
];
|
||||
$items['first']['href'] = Url::fromRoute($route_name, $route_parameters, $options)->toString();
|
||||
if (isset($tags[0])) {
|
||||
$items['first']['text'] = $tags[0];
|
||||
}
|
||||
|
||||
$items['previous'] = [];
|
||||
$items['previous']['attributes'] = new Attribute();
|
||||
$options = [
|
||||
'query' => $pager_manager->getUpdatedParameters($parameters, $element, $current_page - 1),
|
||||
];
|
||||
$items['previous']['href'] = Url::fromRoute($route_name, $route_parameters, $options)->toString();
|
||||
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 = [
|
||||
'query' => $pager_manager->getUpdatedParameters($parameters, $element, $i - 1),
|
||||
];
|
||||
$items['pages'][$i]['href'] = Url::fromRoute($route_name, $route_parameters, $options)->toString();
|
||||
$items['pages'][$i]['attributes'] = new Attribute();
|
||||
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 ($current_page < ($pager_max - 1)) {
|
||||
$items['next'] = [];
|
||||
$items['next']['attributes'] = new Attribute();
|
||||
$options = [
|
||||
'query' => $pager_manager->getUpdatedParameters($parameters, $element, $current_page + 1),
|
||||
];
|
||||
$items['next']['href'] = Url::fromRoute($route_name, $route_parameters, $options)->toString();
|
||||
if (isset($tags[3])) {
|
||||
$items['next']['text'] = $tags[3];
|
||||
}
|
||||
|
||||
$items['last'] = [];
|
||||
$items['last']['attributes'] = new Attribute();
|
||||
$options = [
|
||||
'query' => $pager_manager->getUpdatedParameters($parameters, $element, $pager_max - 1),
|
||||
];
|
||||
$items['last']['href'] = Url::fromRoute($route_name, $route_parameters, $options)->toString();
|
||||
if (isset($tags[4])) {
|
||||
$items['last']['text'] = $tags[4];
|
||||
}
|
||||
}
|
||||
|
||||
$variables['items'] = $items;
|
||||
$variables['heading_id'] = Html::getUniqueId('pagination-heading');
|
||||
|
||||
// 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.
|
||||
*
|
||||
* @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Core\Pager\PagerManagerInterface::getUpdatedParameters() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2779457
|
||||
* @see \Drupal\Core\Pager\PagerManagerInterface::getUpdatedParameters()
|
||||
*/
|
||||
function pager_query_add_page(array $query, $element, $index) {
|
||||
@trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface->getUpdatedParameters() instead. See https://www.drupal.org/node/2779457', E_USER_DEPRECATED);
|
||||
/* @var $pager_manager \Drupal\Core\Pager\PagerManagerInterface */
|
||||
$pager_manager = \Drupal::service('pager.manager');
|
||||
return $pager_manager->getUpdatedParameters($query, $element, $index);
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Schema API handling functions.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema;
|
||||
|
||||
/**
|
||||
* @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 = [];
|
||||
foreach (\Drupal::moduleHandler()->getModuleList() as $loaded_module => $filename) {
|
||||
$updates[$loaded_module] = [];
|
||||
}
|
||||
|
||||
// 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__, []);
|
||||
|
||||
if ($reset) {
|
||||
$versions = [];
|
||||
}
|
||||
|
||||
if (!$versions) {
|
||||
if (!$versions = \Drupal::keyValue('system.schema')->getAll()) {
|
||||
$versions = [];
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
\Drupal::database()->schema()->createTable($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) {
|
||||
$tables = drupal_get_module_schema($module);
|
||||
_drupal_schema_initialize($tables, $module, FALSE);
|
||||
$schema = \Drupal::database()->schema();
|
||||
foreach ($tables as $table) {
|
||||
if ($schema->tableExists($table['name'])) {
|
||||
$schema->dropTable($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 [];
|
||||
}
|
||||
elseif (!empty($schema)) {
|
||||
return $schema;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 data types.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::castValue() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/3051983
|
||||
*/
|
||||
function drupal_schema_get_field_value(array $info, $value) {
|
||||
@trigger_error('drupal_schema_get_field_value() is deprecated in drupal:8.8.0. It will be removed from drupal:9.0.0. Use \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::castValue($info, $value) instead. See https://www.drupal.org/node/3051983', E_USER_DEPRECATED);
|
||||
return SqlContentEntityStorageSchema::castValue($info, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup schemaapi".
|
||||
*/
|
||||
@@ -0,0 +1,112 @@
|
||||
<?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\Core\Utility\TableSort;
|
||||
|
||||
/**
|
||||
* Initializes the table sort context.
|
||||
*
|
||||
* @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Core\Utility\TableSort::getContextFromRequest() instead.
|
||||
*
|
||||
* @see \Drupal\Core\Utility\TableSortInterface::getContextFromRequest()
|
||||
* @see https://www.drupal.org/node/3009182
|
||||
*/
|
||||
function tablesort_init($header) {
|
||||
@trigger_error(__FUNCTION__ . '() is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\Utility\TableSort::getContextFromRequest() instead. See https://www.drupal.org/node/3009182', E_USER_DEPRECATED);
|
||||
return TableSort::getContextFromRequest($header, \Drupal::request());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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().
|
||||
*
|
||||
* @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Core\Utility\TableSort::header() instead.
|
||||
*
|
||||
* @see \Drupal\Core\Utility\TableSortInterface::header()
|
||||
* @see https://www.drupal.org/node/3009182
|
||||
*/
|
||||
function tablesort_header(&$cell_content, array &$cell_attributes, array $header, array $ts) {
|
||||
@trigger_error(__FUNCTION__ . '() is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\Utility\TableSort::header() instead. See https://www.drupal.org/node/3009182', E_USER_DEPRECATED);
|
||||
TableSort::header($cell_content, $cell_attributes, $header, $ts);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Core\Utility\TableSort::getQueryParameters() instead.
|
||||
*
|
||||
* @see \Drupal\Core\Utility\TableSort::getQueryParameters()
|
||||
* @see https://www.drupal.org/node/3009182
|
||||
*/
|
||||
function tablesort_get_query_parameters() {
|
||||
@trigger_error(__FUNCTION__ . '() is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\Utility\TableSort::getQueryParameters() instead. See https://www.drupal.org/node/3009182', E_USER_DEPRECATED);
|
||||
return TableSort::getQueryParameters(\Drupal::request());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Core\Utility\TableSort::getOrder() instead.
|
||||
*
|
||||
* @see \Drupal\Core\Utility\TableSortInterface::getOrder()
|
||||
* @see https://www.drupal.org/node/3009182
|
||||
*/
|
||||
function tablesort_get_order($headers) {
|
||||
@trigger_error(__FUNCTION__ . '() is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\Utility\TableSort::getOrder() instead. See https://www.drupal.org/node/3009182', E_USER_DEPRECATED);
|
||||
return TableSort::getOrder($headers, \Drupal::request());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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").
|
||||
*
|
||||
* @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Core\Utility\TableSort::getSort() instead.
|
||||
*
|
||||
* @see \Drupal\Core\Utility\TableSortInterface::getSort()
|
||||
* @see https://www.drupal.org/node/3009182
|
||||
*/
|
||||
function tablesort_get_sort($headers) {
|
||||
@trigger_error(__FUNCTION__ . '() is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\Utility\TableSort::getSort() instead. See https://www.drupal.org/node/3009182', E_USER_DEPRECATED);
|
||||
return TableSort::getSort($headers, \Drupal::request());
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Theming for maintenance pages.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Installer\InstallerKernel;
|
||||
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';
|
||||
|
||||
// Install and update pages are treated differently to prevent theming overrides.
|
||||
if (defined('MAINTENANCE_MODE') && (MAINTENANCE_MODE == 'install' || MAINTENANCE_MODE == 'update')) {
|
||||
if (InstallerKernel::installationAttempted()) {
|
||||
$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 = \Drupal::service('extension.list.theme')->getList();
|
||||
$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;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides Unicode-related conversions and operations.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
|
||||
/**
|
||||
* Returns Unicode library status and errors.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Moves unicode_requirements() logic to system_requirements().
|
||||
*
|
||||
* @deprecated in drupal:8.4.0 and is removed from drupal:9.0.0.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2884698
|
||||
*/
|
||||
function unicode_requirements() {
|
||||
@trigger_error('unicode_requirements() is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. There is no replacement; system_requirements() now includes the logic instead. See https://www.drupal.org/node/2884698', E_USER_DEPRECATED);
|
||||
|
||||
$libraries = [
|
||||
Unicode::STATUS_SINGLEBYTE => t('Standard PHP'),
|
||||
Unicode::STATUS_MULTIBYTE => t('PHP Mbstring Extension'),
|
||||
Unicode::STATUS_ERROR => t('Error'),
|
||||
];
|
||||
$severities = [
|
||||
Unicode::STATUS_SINGLEBYTE => REQUIREMENT_WARNING,
|
||||
Unicode::STATUS_MULTIBYTE => NULL,
|
||||
Unicode::STATUS_ERROR => REQUIREMENT_ERROR,
|
||||
];
|
||||
$failed_check = Unicode::check();
|
||||
$library = Unicode::getStatus();
|
||||
|
||||
$requirements['unicode'] = [
|
||||
'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
|
||||
*
|
||||
* @deprecated in drupal:8.3.0 and is removed from drupal:9.0.0. Use
|
||||
* xml_parser_create() and
|
||||
* xml_parser_set_option($xml_parser, XML_OPTION_TARGET_ENCODING, 'utf-8')
|
||||
* instead.
|
||||
*/
|
||||
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 = ['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.', ['%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;
|
||||
}
|
||||
@@ -0,0 +1,786 @@
|
||||
<?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\Extension\Exception\UnknownExtensionException;
|
||||
use Drupal\Core\Update\UpdateKernel;
|
||||
use Drupal\Core\Utility\Error;
|
||||
|
||||
/**
|
||||
* Disables any extensions that are incompatible with the current core version.
|
||||
*
|
||||
* @deprecated in Drupal 8.8.5 and is removed from Drupal 9.0.0.
|
||||
*
|
||||
* @see https://www.drupal.org/node/3026100
|
||||
*/
|
||||
function update_fix_compatibility() {
|
||||
@trigger_error(__FUNCTION__ . '() is deprecated in Drupal 8.8.5 and will be removed before Drupal 9.0.0. There is no replacement. See https://www.drupal.org/node/3026100', E_USER_DEPRECATED);
|
||||
// Fix extension objects if the update is being done via Drush 8. In non-Drush
|
||||
// environments this will already be fixed by the UpdateKernel this point.
|
||||
UpdateKernel::fixSerializedExtensionObjects(\Drupal::getContainer());
|
||||
|
||||
$extension_config = \Drupal::configFactory()->getEditable('core.extension');
|
||||
$save = FALSE;
|
||||
foreach (['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 = \Drupal::service('extension.list.module')->reset()->getList();
|
||||
}
|
||||
|
||||
if ($type == 'module' && isset($modules[$name])) {
|
||||
$file = $modules[$name];
|
||||
}
|
||||
elseif ($type == 'theme' && isset($themes[$name])) {
|
||||
$file = $themes[$name];
|
||||
}
|
||||
if (!isset($file)
|
||||
|| $file->info['core_incompatible']
|
||||
|| 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 = [];
|
||||
|
||||
$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'] += [
|
||||
'value' => 'The installed schema version meets the minimum.',
|
||||
'description' => 'Schema version: ' . $system_schema,
|
||||
];
|
||||
}
|
||||
else {
|
||||
$requirements['minimum schema'] += [
|
||||
'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 upgrade your site to Drupal 8 first, see https://www.drupal.org/docs/8/upgrade.',
|
||||
];
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks update requirements and reports errors and (optionally) warnings.
|
||||
*/
|
||||
function update_check_requirements() {
|
||||
// Because this is one of the earliest points in the update process,
|
||||
// detect and fix missing schema versions for modules here to ensure
|
||||
// it runs on all update code paths.
|
||||
_update_fix_missing_schema();
|
||||
|
||||
// Check requirements of all loaded modules.
|
||||
$requirements = \Drupal::moduleHandler()->invokeAll('requirements', ['update']);
|
||||
$requirements += update_system_schema_requirements();
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to detect and fix 'missing' schema information.
|
||||
*
|
||||
* Repairs the case where a module has no schema version recorded.
|
||||
* This has to be done prior to updates being run, otherwise the update
|
||||
* system would detect and attempt to run all historical updates for a
|
||||
* module.
|
||||
*
|
||||
* @todo: remove in a major version after
|
||||
* https://www.drupal.org/project/drupal/issues/3130037 has been fixed.
|
||||
*/
|
||||
function _update_fix_missing_schema() {
|
||||
$versions = \Drupal::keyValue('system.schema')->getAll();
|
||||
$module_handler = \Drupal::moduleHandler();
|
||||
$enabled_modules = $module_handler->getModuleList();
|
||||
|
||||
foreach (array_keys($enabled_modules) as $module) {
|
||||
// All modules should have a recorded schema version, but when they
|
||||
// don't, detect and fix the problem.
|
||||
if (!isset($versions[$module])) {
|
||||
// Ensure the .install file is loaded.
|
||||
module_load_install($module);
|
||||
$all_updates = drupal_get_schema_versions($module);
|
||||
// If the schema version of a module hasn't been recorded, we cannot
|
||||
// know the actual schema version a module is at, because
|
||||
// no updates will ever have been run on the site and it was not set
|
||||
// correctly when the module was installed, so instead set it to
|
||||
// the same as the last update. This means that updates will proceed
|
||||
// again the next time the module is updated and a new update is
|
||||
// added. Updates added in between the module being installed and the
|
||||
// schema version being fixed here (if any have been added) will never
|
||||
// be run, but we have no way to identify which updates these are.
|
||||
if ($all_updates) {
|
||||
$last_update = max($all_updates);
|
||||
}
|
||||
else {
|
||||
$last_update = \Drupal::CORE_MINIMUM_SCHEMA_VERSION;
|
||||
}
|
||||
// If the module implements hook_update_last_removed() use the
|
||||
// value of that if it's higher than the schema versions found so
|
||||
// far.
|
||||
if ($last_removed = $module_handler->invoke($module, 'update_last_removed')) {
|
||||
$last_update = max($last_update, $last_removed);
|
||||
}
|
||||
drupal_set_installed_schema_version($module, $last_update);
|
||||
$args = ['%module' => $module, '%last_update_hook' => $module . '_update_' . $last_update . '()'];
|
||||
\Drupal::messenger()->addWarning(t('Schema information for module %module was missing from the database. You should manually review the module updates and your database to check if any updates have been skipped up to, and including, %last_update_hook.', $args));
|
||||
\Drupal::logger('update')->warning('Schema information for module %module was missing from the database. You should manually review the module updates and your database to check if any updates have been skipped up to, and including, %last_update_hook.', $args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
\Drupal::service('extension.list.profile')->reset();
|
||||
\Drupal::service('extension.list.module')->reset();
|
||||
\Drupal::service('extension.list.theme_engine')->reset();
|
||||
\Drupal::service('extension.list.theme')->reset();
|
||||
drupal_static_reset('drupal_get_installed_schema_version');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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('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, [$function]))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ret = [];
|
||||
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'] = ['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] = [];
|
||||
}
|
||||
if (!isset($context['results'][$module][$number])) {
|
||||
$context['results'][$module][$number] = [];
|
||||
}
|
||||
$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] = [];
|
||||
}
|
||||
$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 = ['system' => []];
|
||||
|
||||
$modules = drupal_get_installed_schema_version(NULL, FALSE, TRUE);
|
||||
/** @var \Drupal\Core\Extension\ExtensionList $extension_list */
|
||||
$extension_list = \Drupal::service('extension.list.module');
|
||||
/** @var array $installed_module_info */
|
||||
$installed_module_info = $extension_list->getAllInstalledInfo();
|
||||
foreach ($modules as $module => $schema_version) {
|
||||
// Skip uninstalled and incompatible modules.
|
||||
try {
|
||||
if ($schema_version == SCHEMA_UNINSTALLED || $extension_list->checkIncompatibility($module)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// It is possible that the system schema has orphaned entries, so the
|
||||
// incompatibility checking might throw an exception.
|
||||
catch (UnknownExtensionException $e) {
|
||||
$args = [
|
||||
'%name' => $module,
|
||||
':url' => 'https://www.drupal.org/node/3137656',
|
||||
];
|
||||
\Drupal::messenger()->addWarning(t('Module %name has an entry in the system.schema key/value storage, but is missing from your site. <a href=":url">More information about this error</a>.', $args));
|
||||
\Drupal::logger('system')->notice('Module %name has an entry in the system.schema key/value storage, but is missing from your site. <a href=":url">More information about this error</a>.', $args);
|
||||
continue;
|
||||
}
|
||||
// There might be orphaned entries for modules that are in the filesystem
|
||||
// but not installed. Also skip those, but warn site admins about it.
|
||||
if (empty($installed_module_info[$module])) {
|
||||
$args = [
|
||||
'%name' => $module,
|
||||
':url' => 'https://www.drupal.org/node/3137656',
|
||||
];
|
||||
\Drupal::messenger()->addWarning(t('Module %name has an entry in the system.schema key/value storage, but is not installed. <a href=":url">More information about this error</a>.', $args));
|
||||
\Drupal::logger('system')->notice('Module %name has an entry in the system.schema key/value storage, but is not installed. <a href=":url">More information about this error</a>.', $args);
|
||||
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) {
|
||||
foreach ($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(["\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, ['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'] = [];
|
||||
}
|
||||
// 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 = [];
|
||||
foreach ($starting_updates as $module => $version) {
|
||||
$update_functions[$module] = [];
|
||||
$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 = [];
|
||||
|
||||
// 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 = [];
|
||||
/** @var \Drupal\Core\Extension\ModuleExtensionList */
|
||||
$extension_list = \Drupal::service('extension.list.module');
|
||||
// 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) {
|
||||
// Skip modules that are entirely missing from the filesystem here, since
|
||||
// module_load_install() will call trigger_error() if invoked on a module
|
||||
// that doesn't exist. There's no way to catch() that, so avoid it entirely.
|
||||
// This can happen when there are orphaned entries in the system.schema k/v
|
||||
// store for modules that have been removed from a site without first being
|
||||
// cleanly uninstalled. We don't care here if the module has been installed
|
||||
// or not, since we'll filter those out in update_get_update_list().
|
||||
if ($schema == SCHEMA_UNINSTALLED || !$extension_list->exists($module)) {
|
||||
// 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') ?: [];
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Miscellaneous functions.
|
||||
*/
|
||||
|
||||
use Drupal\Core\PhpStorage\PhpStorageFactory;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\DrupalKernel;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Rebuilds all caches even when Drupal itself does not work.
|
||||
*
|
||||
* @param $class_loader
|
||||
* The class loader. Normally Composer's ClassLoader, as included by the
|
||||
* front controller, but may also be decorated; e.g.,
|
||||
* \Symfony\Component\ClassLoader\ApcClassLoader, \Symfony\Component\ClassLoader\WinCacheClassLoader, or \Symfony\Component\ClassLoader\XcacheClassLoader
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @see rebuild.php
|
||||
*/
|
||||
function drupal_rebuild($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));
|
||||
$kernel->boot();
|
||||
$kernel->preHandle($request);
|
||||
// Ensure our request includes the session if appropriate.
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
$request->setSession($kernel->getContainer()->get('session'));
|
||||
}
|
||||
|
||||
// Invalidate the container.
|
||||
$kernel->invalidateContainer();
|
||||
|
||||
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 New Issue
Block a user