1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
|
<?php
/**
* @file
*/
use Drupal\Component\FileSecurity\FileSecurity;
use Drupal\Core\Extension\Extension;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
/**
* Disabled option on forms and settings.
*/
const DRUPAL_DISABLED = 0;
/**
* Optional option on forms and settings.
*/
const DRUPAL_OPTIONAL = 1;
/**
* Required option on forms and settings.
*/
const DRUPAL_REQUIRED = 2;
/**
* Return only visible regions.
*
* @see system_region_list()
*/
const REGIONS_VISIBLE = 'visible';
/**
* Return all regions.
*
* @see system_region_list()
*/
const REGIONS_ALL = 'all';
/**
* Implements hook_hook_info().
*/
function system_hook_info(): array {
$hooks['token_info'] = [
'group' => 'tokens',
];
$hooks['token_info_alter'] = [
'group' => 'tokens',
];
$hooks['tokens'] = [
'group' => 'tokens',
];
$hooks['tokens_alter'] = [
'group' => 'tokens',
];
return $hooks;
}
/**
* Implements hook_theme_suggestions_HOOK().
*/
function system_theme_suggestions_html(array $variables): array {
$path_args = explode('/', trim(\Drupal::service('path.current')->getPath(), '/'));
return theme_get_suggestions($path_args, 'html');
}
/**
* Implements hook_theme_suggestions_HOOK().
*/
function system_theme_suggestions_page(array $variables): array {
$path_args = explode('/', trim(\Drupal::service('path.current')->getPath(), '/'));
$suggestions = theme_get_suggestions($path_args, 'page');
$supported_http_error_codes = [401, 403, 404];
$exception = \Drupal::requestStack()->getCurrentRequest()->attributes->get('exception');
if ($exception instanceof HttpExceptionInterface && in_array($exception->getStatusCode(), $supported_http_error_codes, TRUE)) {
$suggestions[] = 'page__4xx';
$suggestions[] = 'page__' . $exception->getStatusCode();
}
return $suggestions;
}
/**
* Implements hook_theme_suggestions_HOOK().
*/
function system_theme_suggestions_maintenance_page(array $variables): array {
$suggestions = [];
// Dead databases will show error messages so supplying this template will
// allow themers to override the page and the content completely.
$offline = defined('MAINTENANCE_MODE');
try {
\Drupal::service('path.matcher')->isFrontPage();
}
catch (Exception) {
// The database is not yet available.
$offline = TRUE;
}
if ($offline) {
$suggestions[] = 'maintenance_page__offline';
}
return $suggestions;
}
/**
* Implements hook_theme_suggestions_HOOK().
*/
function system_theme_suggestions_region(array $variables): array {
$suggestions = [];
if (!empty($variables['elements']['#region'])) {
$suggestions[] = 'region__' . $variables['elements']['#region'];
}
return $suggestions;
}
/**
* Implements hook_theme_suggestions_HOOK().
*/
function system_theme_suggestions_field(array $variables): array {
$suggestions = [];
$element = $variables['element'];
$suggestions[] = 'field__' . $element['#field_type'];
$suggestions[] = 'field__' . $element['#field_name'];
$suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#bundle'];
$suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name'];
$suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name'] . '__' . $element['#bundle'];
return $suggestions;
}
/**
* Prepares variables for the list of available bundles.
*
* Default template: entity-add-list.html.twig.
*
* @param array $variables
* An associative array containing:
* - bundles: An array of bundles with the label, description, add_link keys.
* - add_bundle_message: The message shown when there are no bundles. Only
* available if the entity type uses bundle entities.
*/
function template_preprocess_entity_add_list(&$variables): void {
foreach ($variables['bundles'] as $bundle_name => $bundle_info) {
$variables['bundles'][$bundle_name]['description'] = [
'#markup' => $bundle_info['description'],
];
}
}
/**
* @defgroup authorize Authorized operations
* @{
* Functions to run operations with elevated privileges via authorize.php.
*
* Because of the Update manager functionality included in Drupal core, there
* is a mechanism for running operations with elevated file system privileges,
* the top-level authorize.php script. This script runs at a reduced Drupal
* bootstrap level so that it is not reliant on the entire site being
* functional. The operations use a FileTransfer class to manipulate code
* installed on the system as the user that owns the files, not the user that
* the httpd is running as.
*
* The first setup is to define a callback function that should be authorized
* to run with the elevated privileges. This callback should take a
* FileTransfer as its first argument, although you can define an array of
* other arguments it should be invoked with. The callback should be placed in
* a separate .inc file that will be included by authorize.php.
*
* To run the operation, certain data must be saved into the SESSION, and then
* the flow of control should be redirected to the authorize.php script. There
* are two ways to do this, either to call system_authorized_run() directly,
* or to call system_authorized_init() and then redirect to authorize.php,
* using the URL from system_authorized_get_url(). Redirecting yourself is
* necessary when your authorized operation is being triggered by a form
* submit handler, since calling redirecting in a submit handler is a bad
* idea, and you should instead use $form_state->setRedirect().
*
* Once the SESSION is setup for the operation and the user is redirected to
* authorize.php, they will be prompted for their connection credentials (core
* provides FTP and SSH by default, although other connection classes can be
* added via contributed modules). With valid credentials, authorize.php will
* instantiate the appropriate FileTransfer object, and then invoke the
* desired operation passing in that object. The authorize.php script can act
* as a Batch API processing page, if the operation requires a batch.
*
* @see authorize.php
* @see \Drupal\Core\FileTransfer\FileTransfer
* @see hook_filetransfer_info()
*/
/**
* Setup a given callback to run via authorize.php with elevated privileges.
*
* To use authorize.php, certain variables must be stashed in the user's
* session. This function sets up all the necessary session variables. The
* calling function should then redirect to authorize.php, using the full path
* returned by system_authorized_get_url(). That initiates the workflow that
* will eventually lead to the callback being invoked. The callback will be
* invoked at a low bootstrap level, without all modules being invoked, so it
* needs to be careful not to assume any code exists.
* Example (system_authorized_run()):
* @code
* system_authorized_init($callback, $file, $arguments, $page_title);
* return new RedirectResponse(system_authorized_get_url()->toString());
* @endcode
*
* @param callable $callback
* The name of the function to invoke once the user authorizes the operation.
* @param string $file
* The full path to the file where the callback function is implemented.
* @param array $arguments
* Optional array of arguments to pass into the callback when it is invoked.
* Note that the first argument to the callback is always the FileTransfer
* object created by authorize.php when the user authorizes the operation.
* @param string $page_title
* Optional string to use as the page title once redirected to authorize.php.
*/
function system_authorized_init($callback, $file, $arguments = [], $page_title = NULL): void {
@trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
$session = \Drupal::request()->getSession();
// First, figure out what file transfer backends the site supports, and put
// all of those in the SESSION so that authorize.php has access to all of
// them via the class autoloader, even without a full bootstrap.
$session->set('authorize_filetransfer_info', drupal_get_filetransfer_info());
// Now, define the callback to invoke.
$session->set('authorize_operation', [
'callback' => $callback,
'file' => $file,
'arguments' => $arguments,
]);
if (isset($page_title)) {
$session->set('authorize_page_title', $page_title);
}
}
/**
* Return the URL for the authorize.php script.
*
* @param array $options
* Optional array of options to set on the \Drupal\Core\Url object.
*
* @return \Drupal\Core\Url
* The full URL to authorize.php, using HTTPS if available.
*
* @see system_authorized_init()
*/
function system_authorized_get_url(array $options = []) {
@trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
// core/authorize.php is an unrouted URL, so using the base: scheme is
// the correct usage for this case.
$url = Url::fromUri('base:core/authorize.php');
$url_options = $url->getOptions();
$url->setOptions($options + $url_options);
return $url;
}
/**
* Returns the URL for the authorize.php script when it is processing a batch.
*
* @param array $options
* Optional array of options to set on the \Drupal\Core\Url object.
*
* @return \Drupal\Core\Url
* The full URL for the authorize.php script with batch processing options.
*/
function system_authorized_batch_processing_url(array $options = []) {
@trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
$options['query'] = ['batch' => '1'];
return system_authorized_get_url($options);
}
/**
* Setup and invoke an operation using authorize.php.
*
* @see system_authorized_init()
*/
function system_authorized_run($callback, $file, $arguments = [], $page_title = NULL) {
@trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
system_authorized_init($callback, $file, $arguments, $page_title);
return new RedirectResponse(system_authorized_get_url()->toString());
}
/**
* Use authorize.php to run batch_process().
*
* @see batch_process()
*/
function system_authorized_batch_process() {
@trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
$finish_url = system_authorized_get_url();
$process_url = system_authorized_batch_processing_url();
return batch_process($finish_url->setAbsolute()->toString(), $process_url);
}
/**
* Implements hook_preprocess_HOOK() for block templates.
*/
function system_preprocess_block(&$variables): void {
switch ($variables['base_plugin_id']) {
case 'system_branding_block':
$variables['site_logo'] = '';
if ($variables['content']['site_logo']['#access'] && $variables['content']['site_logo']['#uri']) {
$variables['site_logo'] = $variables['content']['site_logo']['#uri'];
}
$variables['site_name'] = '';
if ($variables['content']['site_name']['#access'] && $variables['content']['site_name']['#markup']) {
$variables['site_name'] = $variables['content']['site_name']['#markup'];
}
$variables['site_slogan'] = '';
if ($variables['content']['site_slogan']['#access'] && $variables['content']['site_slogan']['#markup']) {
$variables['site_slogan'] = [
'#markup' => $variables['content']['site_slogan']['#markup'],
];
}
break;
}
}
/**
* Checks the existence of the directory specified in $form_element.
*
* This function is called from the system_settings form to check all core
* file directories (file_public_path, file_private_path, file_temporary_path).
*
* @param array $form_element
* The form element containing the name of the directory to check.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
function system_check_directory($form_element, FormStateInterface $form_state) {
$directory = $form_element['#value'];
if (strlen($directory) == 0) {
return $form_element;
}
$logger = \Drupal::logger('file system');
/** @var \Drupal\Core\File\FileSystemInterface $file_system */
$file_system = \Drupal::service('file_system');
if (!is_dir($directory) && !$file_system->mkdir($directory, NULL, TRUE)) {
// If the directory does not exist and cannot be created.
$form_state->setErrorByName($form_element['#parents'][0], t('The directory %directory does not exist and could not be created.', ['%directory' => $directory]));
$logger->error('The directory %directory does not exist and could not be created.', ['%directory' => $directory]);
}
if (is_dir($directory) && !is_writable($directory) && !$file_system->chmod($directory)) {
// If the directory is not writable and cannot be made so.
$form_state->setErrorByName($form_element['#parents'][0], t('The directory %directory exists but is not writable and could not be made writable.', ['%directory' => $directory]));
$logger->error('The directory %directory exists but is not writable and could not be made writable.', ['%directory' => $directory]);
}
elseif (is_dir($directory)) {
if ($form_element['#name'] == 'file_public_path') {
// Create public .htaccess file.
FileSecurity::writeHtaccess($directory, FALSE);
}
else {
// Create private .htaccess file.
FileSecurity::writeHtaccess($directory);
}
}
return $form_element;
}
/**
* Get a list of available regions from a specified theme.
*
* @param \Drupal\Core\Extension\Extension|string $theme
* A theme extension object, or the name of a theme.
* @param string $show
* Possible values: REGIONS_ALL or REGIONS_VISIBLE. Visible excludes hidden
* regions.
*
* @return string[]
* An array of regions in the form $region['name'] = 'description'.
*/
function system_region_list($theme, $show = REGIONS_ALL): array {
if (!$theme instanceof Extension) {
$themes = \Drupal::service('theme_handler')->listInfo();
if (!isset($themes[$theme])) {
return [];
}
$theme = $themes[$theme];
}
$list = [];
$info = $theme->info;
// If requested, suppress hidden regions. See block_admin_display_form().
foreach ($info['regions'] as $name => $label) {
if ($show == REGIONS_ALL || !isset($info['regions_hidden']) || !in_array($name, $info['regions_hidden'])) {
// phpcs:ignore Drupal.Semantics.FunctionT.NotLiteralString
$list[$name] = t($label);
}
}
return $list;
}
/**
* Sorts themes by their names, with the default theme listed first.
*
* Callback for uasort() within
* \Drupal\system\Controller\SystemController::themesPage().
*
* @see \Drupal\Core\Extension\Extension::sortByName()
*/
function system_sort_themes($a, $b) {
if ($a->is_default) {
return -1;
}
if ($b->is_default) {
return 1;
}
return strcasecmp($a->info['name'], $b->info['name']);
}
/**
* Gets the name of the default region for a given theme.
*
* @param string $theme
* The name of a theme.
*
* @return string
* A string that is the region name.
*/
function system_default_region($theme) {
$regions = array_keys(system_region_list($theme, REGIONS_VISIBLE));
return $regions[0] ?? '';
}
/**
* Determines whether the current user is in compact mode.
*
* Compact mode shows certain administration pages with less description text,
* such as the configuration page and the permissions page.
*
* Whether the user is in compact mode is determined by a cookie, which is set
* for the user by \Drupal\system\Controller\SystemController::compactPage().
*
* If the user does not have the cookie, the default value is given by the
* configuration variable 'system.site.admin_compact_mode', which itself
* defaults to FALSE. This does not have a user interface to set it: it is a
* hidden variable which can be set in the settings.php file.
*
* @return bool
* TRUE when in compact mode, FALSE when in expanded mode.
*/
function system_admin_compact_mode() {
// PHP converts dots into underscores in cookie names to avoid problems with
// its parser, so we use a converted cookie name.
return \Drupal::request()->cookies->get('Drupal_visitor_admin_compact_mode', \Drupal::config('system.site')->get('admin_compact_mode'));
}
/**
* Determines if Claro is the admin theme but not the active theme.
*
* @return bool
* TRUE if Claro is the admin theme but not the active theme.
*/
function _system_is_claro_admin_and_not_active() {
$admin_theme = \Drupal::configFactory()->get('system.theme')->get('admin');
$active_theme = \Drupal::theme()->getActiveTheme()->getName();
return $active_theme !== 'claro' && $admin_theme === 'claro';
}
/**
* Implements hook_preprocess_toolbar().
*/
function system_preprocess_toolbar(array &$variables, $hook, $info): void {
// When Claro is the admin theme, Claro overrides the active theme's if that
// active theme is not Claro. Because of these potential overrides, the
// toolbar cache should be invalidated any time the default or admin theme
// changes.
$variables['#cache']['tags'][] = 'config:system.theme';
// If Claro is the admin theme but not the active theme, still include Claro's
// toolbar preprocessing.
if (_system_is_claro_admin_and_not_active()) {
require_once DRUPAL_ROOT . '/core/themes/claro/claro.theme';
claro_preprocess_toolbar($variables, $hook, $info);
}
}
|