caore d8 files

This commit is contained in:
Bachir Soussi Chiadmi
2016-09-06 12:44:15 +02:00
commit 58f46e4e8f
12831 changed files with 1451591 additions and 0 deletions
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: fallback
label: 'Fallback date format'
locked: true
pattern: 'D, m/d/Y - H:i'
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: html_date
label: 'HTML Date'
locked: true
pattern: Y-m-d
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: html_datetime
label: 'HTML Datetime'
locked: true
pattern: 'Y-m-d\TH:i:sO'
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: html_month
label: 'HTML Month'
locked: true
pattern: Y-m
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: html_time
label: 'HTML Time'
locked: true
pattern: 'H:i:s'
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: html_week
label: 'HTML Week'
locked: true
pattern: Y-\WW
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: html_year
label: 'HTML Year'
locked: true
pattern: 'Y'
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: html_yearless_date
label: 'HTML Yearless date'
locked: true
pattern: m-d
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: long
label: 'Default long date'
locked: false
pattern: 'l, F j, Y - H:i'
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: medium
label: 'Default medium date'
locked: false
pattern: 'D, m/d/Y - H:i'
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: short
label: 'Default short date'
locked: false
pattern: 'm/d/Y - H:i'
@@ -0,0 +1 @@
filetransfer_default:
@@ -0,0 +1,3 @@
threshold:
requirements_warning: 172800
requirements_error: 1209600
@@ -0,0 +1,9 @@
country:
default: ''
first_day: 0
timezone:
default: ''
user:
configurable: true
warn: false
default: 0
@@ -0,0 +1,3 @@
context:
lines_leading: 2
lines_trailing: 2
@@ -0,0 +1,5 @@
allow_insecure_uploads: false
default_scheme: 'public'
path:
temporary: ''
temporary_maximum_age: 21600
@@ -0,0 +1 @@
jpeg_quality: 75
@@ -0,0 +1 @@
toolkit: gd
@@ -0,0 +1 @@
error_level: hide
@@ -0,0 +1,2 @@
interface:
default: 'php_mail'
@@ -0,0 +1,2 @@
message: '@site is currently under maintenance. We should be back shortly. Thank you for your patience.'
langcode: en
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: account
label: 'User account menu'
description: 'Links related to the active user account'
locked: true
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: admin
label: Administration
description: 'Administrative task links'
locked: true
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: footer
label: Footer
description: 'Site information links'
locked: true
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: main
label: 'Main navigation'
description: 'Site section links'
locked: true
@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: tools
label: Tools
description: 'User tool links, often added by modules'
locked: true
@@ -0,0 +1,15 @@
cache:
page:
max_age: 0
css:
preprocess: true
gzip: true
fast_404:
enabled: true
paths: '/\.(?:txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i'
exclude_paths: '/\/(?:styles|imagecache)\//'
html: '<!DOCTYPE html><html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>'
js:
preprocess: true
gzip: true
stale_file_threshold: 2592000
@@ -0,0 +1,6 @@
channel:
description: ''
items:
limit: 10
view_mode: rss
langcode: en
@@ -0,0 +1,12 @@
uuid: ''
name: ''
mail: ''
slogan: ''
page:
403: ''
404: ''
front: /user/login
admin_compact_mode: false
weight_select_max: 100
langcode: en
default_langcode: en
@@ -0,0 +1,14 @@
favicon:
mimetype: image/vnd.microsoft.icon
path: ''
url: ''
use_default: true
features:
comment_user_picture: true
comment_user_verification: true
favicon: true
node_user_picture: true
logo:
path: ''
url: ''
use_default: true
@@ -0,0 +1,2 @@
admin: ''
default: stark
@@ -0,0 +1,357 @@
# Schema for the configuration files of the System module.
system.site:
type: config_object
label: 'Site information'
mapping:
uuid:
type: string
label: 'Site UUID'
name:
type: label
label: 'Site name'
mail:
type: email
label: 'Email address'
slogan:
type: label
label: 'Slogan'
page:
type: mapping
label: 'Pages'
mapping:
403:
type: path
label: 'Default 403 (access denied) page'
404:
type: path
label: 'Default 404 (not found) page'
front:
type: path
label: 'Default front page'
admin_compact_mode:
type: boolean
label: 'Compact mode'
weight_select_max:
type: integer
label: 'Weight element maximum value'
default_langcode:
type: string
label: 'Site default language code'
mail_notification:
type: string
label: 'Notification email address'
system.maintenance:
type: config_object
label: 'Maintenance mode'
mapping:
message:
type: text
label: 'Message to display when in maintenance mode'
system.authorize:
type: config_object
label: 'Authorize settings'
mapping:
filetransfer_default:
type: string
label: 'Default file transfer protocol'
system.cron:
type: config_object
label: 'Cron settings'
mapping:
threshold:
type: mapping
label: 'Thresholds'
mapping:
requirements_warning:
type: integer
label: 'Requirements warning period'
requirements_error:
type: integer
label: 'Requirements error period'
system.date:
type: config_object
label: 'Date settings'
mapping:
first_day:
type: integer
label: 'First day of week'
country:
type: mapping
label: 'Country'
mapping:
default:
type: string
label: 'Default country'
timezone:
type: mapping
label: 'Time zone settings'
mapping:
default:
type: string
label: 'Default time zone'
user:
type: mapping
label: 'User'
mapping:
configurable:
type: boolean
label: 'Users may set their own time zone'
default:
type: integer
label: 'Time zone for new users'
warn:
type: boolean
label: 'Remind users at login if their time zone is not set'
system.diff:
type: config_object
label: 'Diff settings'
mapping:
context:
type: mapping
label: 'Context'
mapping:
lines_leading:
type: integer
label: 'Number of leading lines in a diff'
lines_trailing:
type: integer
label: 'Number of trailing lines in a diff'
system.logging:
type: config_object
label: 'Logging settings'
mapping:
error_level:
type: string
label: 'Error messages to display'
system.performance:
type: config_object
label: 'Performance settings'
mapping:
cache:
type: mapping
label: 'Caching'
mapping:
page:
type: mapping
label: 'Page caching'
mapping:
max_age:
type: integer
label: 'Max age'
css:
type: mapping
label: 'CSS performance settings'
mapping:
preprocess:
type: boolean
label: 'Aggregate CSS files'
gzip:
type: boolean
label: 'Compress CSS files'
fast_404:
type: mapping
label: 'Fast 404 settings'
mapping:
enabled:
type: boolean
label: 'Fast 404 enabled'
paths:
type: string
label: 'Regular expression to match'
exclude_paths:
type: string
label: 'Regular expression to not match'
html:
type: string
label: 'Fast 404 page html'
js:
type: mapping
label: 'JavaScript performance settings'
mapping:
preprocess:
type: boolean
label: 'JavaScript preprocess'
gzip:
type: boolean
label: 'Compress JavaScript files.'
response:
type: mapping
label: 'Response performance settings'
mapping:
gzip:
type: boolean
label: 'Compress cached pages'
stale_file_threshold:
type: integer
label: 'Stale file threshold'
system.rss:
type: config_object
label: 'Feed settings'
mapping:
channel:
type: mapping
label: 'Feed channel'
mapping:
description:
type: text
label: 'Feed description'
items:
type: mapping
label: 'Feed items'
mapping:
limit:
type: integer
label: 'Feed item limit'
view_mode:
type: string
label: 'Feed content'
system.theme:
type: config_object
label: 'Theme settings'
mapping:
admin:
type: string
label: 'Administration theme'
default:
type: string
label: 'Default theme'
system.menu.*:
type: config_entity
label: 'Menu'
mapping:
id:
type: string
label: 'ID'
label:
type: label
label: 'Label'
description:
type: label
label: 'Menu description'
locked:
type: boolean
label: ''
system.action.*:
type: config_entity
label: 'System action'
mapping:
id:
type: string
label: 'ID'
label:
type: label
label: 'Label'
type:
type: string
label: 'Type'
plugin:
type: string
label: 'Plugin'
configuration:
type: action.configuration.[%parent.plugin]
system.file:
type: config_object
label: 'File system'
mapping:
allow_insecure_uploads:
type: boolean
label: 'Allow insecure uploads'
default_scheme:
type: string
label: 'Default download method'
path:
type: mapping
label: 'Path settings'
mapping:
temporary:
type: string
label: 'Temporary directory'
temporary_maximum_age:
type: integer
label: 'Maximum age for temporary files'
system.image:
type: config_object
label: 'Image settings'
mapping:
toolkit:
type: string
label: 'Toolkit'
system.image.gd:
type: config_object
label: 'Image settings'
mapping:
jpeg_quality:
type: integer
label: 'JPEG quality'
system.mail:
type: config_object
label: 'Mail system'
mapping:
interface:
type: sequence
label: 'Interfaces'
sequence:
type: string
label: 'Interface'
system.theme.global:
type: theme_settings
label: 'Theme global settings'
block.settings.system_branding_block:
type: block_settings
label: 'Branding block'
mapping:
use_site_logo:
type: boolean
label: 'Use site logo'
use_site_name:
type: boolean
label: 'Use site name'
use_site_slogan:
type: boolean
label: 'Use site slogan'
block.settings.system_menu_block:*:
type: block_settings
label: 'Menu block'
mapping:
level:
type: integer
label: 'Starting level'
depth:
type: integer
label: 'Maximum number of levels'
block.settings.local_tasks_block:
type: block_settings
label: 'Tabs block'
mapping:
primary:
type: boolean
label: 'Whether primary tabs are shown'
secondary:
type: boolean
label: 'Whether secondary tabs are shown'
condition.plugin.request_path:
type: condition.plugin
mapping:
pages:
type: string
@@ -0,0 +1,49 @@
/**
* @file
* Throbber.
*/
.ajax-progress {
display: inline-block;
padding: 1px 5px 2px 5px;
}
[dir="rtl"] .ajax-progress {
float: right;
}
.ajax-progress-throbber .throbber {
background: transparent url(../../../../misc/throbber-active.gif) no-repeat 0px center;
display: inline;
padding: 1px 5px 2px;
}
.ajax-progress-throbber .message {
display: inline;
padding: 1px 5px 2px;
}
tr .ajax-progress-throbber .throbber {
margin: 0 2px;
}
.ajax-progress-bar {
width: 16em;
}
/* Full screen throbber */
.ajax-progress-fullscreen {
/* Can't do center:50% middle: 50%, so approximate it for a typical window size. */
left: 49%; /* LTR */
position: fixed;
top: 48.5%;
z-index: 1000;
background-color: #232323;
background-image: url(../../../../misc/loading-small.gif);
background-position: center center;
background-repeat: no-repeat;
border-radius: 7px;
height: 24px;
opacity: 0.9;
padding: 4px;
width: 24px;
}
[dir="rtl"] .ajax-progress-fullscreen {
left: auto;
right: 49%;
}
@@ -0,0 +1,32 @@
/**
* @file
* Alignment classes for text and block level elements.
*/
.text-align-left {
text-align: left;
}
.text-align-right {
text-align: right;
}
.text-align-center {
text-align: center;
}
.text-align-justify {
text-align: justify;
}
/**
* Alignment classes for block level elements (images, videos, blockquotes, etc.)
*/
.align-left {
float: left;
}
.align-right {
float: right;
}
.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
@@ -0,0 +1,22 @@
/**
* @file
* Visual styles for animated throbber.
*
* @see autocomplete.js
*/
.js input.form-autocomplete {
background-image: url(../../../../misc/throbber-inactive.png);
background-position: 100% center; /* LTR */
background-repeat: no-repeat;
}
.js[dir="rtl"] input.form-autocomplete {
background-position: 0% center;
}
.js input.form-autocomplete.ui-autocomplete-loading {
background-image: url(../../../../misc/throbber-active.gif);
background-position: 100% center; /* LTR */
}
.js[dir="rtl"] input.form-autocomplete.ui-autocomplete-loading {
background-position: 0% center;
}
@@ -0,0 +1,15 @@
/**
* @file
* Float clearing.
*
* Based on the micro clearfix hack by Nicolas Gallagher, with the :before
* pseudo selector removed to allow normal top margin collapse.
*
* @see http://nicolasgallagher.com/micro-clearfix-hack
*/
.clearfix:after {
content: "";
display: table;
clear: both;
}
@@ -0,0 +1,13 @@
/**
* @file
* Inline items.
*/
.container-inline div,
.container-inline label {
display: inline;
}
/* Details contents always need to be rendered as block. */
.container-inline .details-wrapper {
display: block;
}
@@ -0,0 +1,10 @@
/**
* @file
* Collapsible details.
*
* @see collapse.js
*/
.js details:not([open]) .details-wrapper {
display: none;
}
@@ -0,0 +1,9 @@
/**
* @file
* Fieldgroup border reset.
*/
.fieldgroup {
border-width: 0;
padding: 0;
}
@@ -0,0 +1,53 @@
/**
* @file
* Utility classes to hide elements in different ways.
*/
/**
* Hide elements from all users.
*
* Used for elements which should not be immediately displayed to any user. An
* example would be collapsible details that will be expanded with a click
* from a user. The effect of this class can be toggled with the jQuery show()
* and hide() functions.
*/
.hidden {
display: none;
}
/**
* Hide elements visually, but keep them available for screen readers.
*
* Used for information required for screen reader users to understand and use
* the site where visual display is undesirable. Information provided in this
* manner should be kept concise, to avoid unnecessary burden on the user.
* "!important" is used to prevent unintentional overrides.
*/
.visually-hidden {
position: absolute !important;
clip: rect(1px, 1px, 1px, 1px);
overflow: hidden;
height: 1px;
width: 1px;
word-wrap: normal;
}
/**
* The .focusable class extends the .visually-hidden class to allow
* the element to be focusable when navigated to via the keyboard.
*/
.visually-hidden.focusable:active,
.visually-hidden.focusable:focus {
position: static !important;
clip: auto;
overflow: visible;
height: auto;
width: auto;
}
/**
* Hide visually and from screen readers, but maintain layout.
*/
.invisible {
visibility: hidden;
}
@@ -0,0 +1,19 @@
/**
* @file
* Styles for item list.
*/
.item-list__comma-list,
.item-list__comma-list li {
display: inline;
}
.item-list__comma-list {
margin: 0;
padding: 0;
}
.item-list__comma-list li:after {
content: ", ";
}
.item-list__comma-list li:last-child:after {
content: "";
}
@@ -0,0 +1,22 @@
/**
* @file
* Utility classes to assist with Javascript functionality.
*/
/**
* For anything you want to hide on page load when JS is enabled, so
* that you can use the JS to control visibility and avoid flicker.
*/
.js .js-hide {
display: none;
}
/**
* For anything you want to show on page load only when JS is enabled.
*/
.js-show {
display: none;
}
.js .js-show {
display: block;
}
@@ -0,0 +1,8 @@
/**
* @file
* Utility class to prevent text wrapping.
*/
.nowrap {
white-space: nowrap;
}
@@ -0,0 +1,8 @@
/*
* @file
* Contain positioned elements.
*/
.position-container {
position: relative;
}
@@ -0,0 +1,51 @@
/**
* @file
* Progress behavior.
*
* @see progress.js
*/
.progress {
position: relative;
}
.progress__track {
background-color: #fff;
border: 1px solid;
margin-top: 5px;
max-width: 100%;
min-width: 100px;
height: 16px;
}
.progress__bar {
background-color: #000;
height: 16px;
width: 3%;
min-width: 3%;
max-width: 100%;
}
.progress__description,
.progress__percentage {
color: #555;
overflow: hidden;
font-size: .875em;
margin-top: 0.2em;
}
.progress__description {
float: left; /* LTR */
}
[dir="rtl"] .progress__description {
float: right;
}
.progress__percentage {
float: right; /* LTR */
}
[dir="rtl"] .progress__percentage {
float: left;
}
.progress--small .progress__track {
height: 7px;
}
.progress--small .progress__bar {
height: 7px;
background-size: 20px 20px;
}
@@ -0,0 +1,15 @@
/*
* @file
* Utility class to remove browser styles, especially for button.
*/
.reset-appearance {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
border: 0 none;
background: transparent;
padding: 0;
margin: 0;
line-height: inherit;
}
@@ -0,0 +1,21 @@
/**
* @file
* Resizable textareas.
*/
.resize-none {
resize: none;
}
.resize-vertical {
resize: vertical;
min-height: 2em;
}
.resize-horizontal {
resize: horizontal;
max-width: 100%;
}
.resize-both {
resize: both;
max-width: 100%;
min-height: 2em;
}
@@ -0,0 +1,13 @@
/**
* @file
* Table header behavior.
*
* @see tableheader.js
*/
table.sticky-header {
background-color: #fff;
margin-top: 0;
z-index: 500;
top: 0;
}
@@ -0,0 +1,88 @@
/**
* @file
* Table drag behavior.
*
* @see tabledrag.js
*/
body.drag {
cursor: move;
}
tr.region-title {
font-weight: bold;
}
tr.region-message {
color: #999;
}
tr.region-populated {
display: none;
}
tr.add-new .tabledrag-changed {
display: none;
}
.draggable a.tabledrag-handle {
cursor: move;
float: left; /* LTR */
height: 1.7em;
margin-left: -1em; /* LTR */
overflow: hidden;
text-decoration: none;
}
[dir="rtl"] .draggable a.tabledrag-handle {
float: right;
margin-right: -1em;
margin-left: 0;
}
a.tabledrag-handle:hover {
text-decoration: none;
}
a.tabledrag-handle .handle {
background: url(../../../../misc/icons/787878/move.svg) no-repeat 6px 7px;
height: 14px;
margin: -0.4em 0.5em 0;
padding: 0.42em 0.5em;
width: 14px;
}
a.tabledrag-handle:hover .handle,
a.tabledrag-handle:focus .handle {
background-image: url(../../../../misc/icons/000000/move.svg);
}
.touchevents .draggable td {
padding: 0 10px;
}
.touchevents .draggable .menu-item__link {
display: inline-block;
padding: 10px 0;
}
.touchevents a.tabledrag-handle {
height: 44px;
width: 40px;
}
.touchevents a.tabledrag-handle .handle {
background-position: 40% 19px; /* LTR */
height: 21px;
}
[dir="rtl"] .touch a.tabledrag-handle .handle {
background-position: right 40% top 19px;
}
.touchevents .draggable.drag a.tabledrag-handle .handle {
background-position: 50% -32px;
}
.tabledrag-toggle-weight-wrapper {
text-align: right; /* LTR */
}
[dir="rtl"] .tabledrag-toggle-weight-wrapper {
text-align: left;
}
.indentation {
float: left; /* LTR */
height: 1.7em;
margin: -0.4em 0.2em -0.4em -0.4em; /* LTR */
padding: 0.42em 0 0.42em 0.6em; /* LTR */
width: 20px;
}
[dir="rtl"] .indentation {
float: right;
margin: -0.4em -0.4em -0.4em 0.2em;
padding: 0.42em 0.6em 0.42em 0;
}
@@ -0,0 +1,19 @@
/**
* @file
* Table sort indicator.
*
* @see tablesort-indicator.html.twig
*/
.tablesort {
width: 16px;
height: 16px;
display: inline-block;
background-size: 100%;
}
.tablesort--asc {
background-image: url(../../../../misc/icons/787878/twistie-down.svg);
}
.tablesort--desc {
background-image: url(../../../../misc/icons/787878/twistie-up.svg);
}
@@ -0,0 +1,18 @@
/**
* @file
* Visual styles for a nested tree child.
*/
div.tree-child {
background: url(../../../../misc/tree.png) no-repeat 11px center; /* LTR */
}
div.tree-child-last {
background: url(../../../../misc/tree-bottom.png) no-repeat 11px center; /* LTR */
}
[dir="rtl"] div.tree-child,
[dir="rtl"] div.tree-child-last {
background-position: -65px center;
}
div.tree-child-horizontal {
background: url(../../../../misc/tree.png) no-repeat -11px center;
}
+389
View File
@@ -0,0 +1,389 @@
/**
* @file
* Styles for administration pages.
*/
/**
* Reusable layout styles.
*/
.layout-container {
margin: 0 1.5em;
}
.layout-container:after {
content: "";
display: table;
clear: both;
}
@media screen and (min-width: 38em) {
.layout-container {
margin: 0 2.5em;
}
.layout-column {
float: left; /* LTR */
box-sizing: border-box;
}
[dir="rtl"] .layout-column {
float: right;
}
.layout-column + .layout-column {
padding-left: 10px; /* LTR */
}
[dir="rtl"] .layout-column + .layout-column {
padding-right: 10px;
padding-left: 0;
}
.layout-column--half {
width: 50%;
}
.layout-column--quarter {
width: 25%;
}
.layout-column--three-quarter {
width: 75%;
}
}
/**
* Panel.
* Used to visually group items together.
*/
.panel {
padding: 5px 5px 15px;
}
.panel__description {
margin: 0 0 3px;
padding: 2px 0 3px 0;
}
/**
* System compact link: to toggle the display of description text.
*/
.compact-link {
margin: 0 0 0.5em 0;
}
/**
* Quick inline admin links.
*/
small .admin-link:before {
content: ' [';
}
small .admin-link:after {
content: ']';
}
/**
* Modules page.
*/
.system-modules thead > tr {
border: 0;
}
.system-modules div.incompatible {
font-weight: bold;
}
.system-modules td.checkbox {
min-width: 25px;
width: 4%;
}
.system-modules td.module {
width: 25%;
}
.system-modules td {
vertical-align: top;
}
.system-modules label,
.system-modules-uninstall label {
color: #1d1d1d;
font-size: 1.15em;
}
.system-modules details {
color: #5c5c5b;
line-height: 20px;
overflow: hidden; /* truncates descriptions if too long */
text-overflow: ellipsis;
white-space: nowrap;
}
.system-modules details[open] {
height: auto;
overflow: visible;
white-space: normal;
}
.system-modules details[open] summary .text {
-webkit-hyphens: auto;
-moz-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
text-transform: none;
}
.system-modules td details a {
color: #5C5C5B;
border: 0px;
}
.system-modules td details {
border: 0;
margin: 0;
height: 20px;
}
.system-modules td details summary {
padding: 0;
text-transform: none;
font-weight: normal;
cursor: default;
}
.system-modules td {
padding-left: 0; /* LTR */
}
[dir="rtl"] .system-modules td {
padding-left: 12px;
padding-right: 0;
}
@media screen and (max-width: 40em) {
.system-modules td.name {
width: 20%;
}
.system-modules td.description {
width: 40%;
}
}
.system-modules .requirements {
padding: 5px 0;
max-width: 490px;
}
.system-modules .links {
overflow: hidden; /* prevents collapse */
}
.system-modules .checkbox {
margin: 0 5px;
}
.system-modules .checkbox .form-item {
margin-bottom: 0;
}
.admin-requirements,
.admin-required {
font-size: 0.9em;
color: #666;
}
.admin-enabled {
color: #080;
}
.admin-missing {
color: #f00;
}
.module-link {
display: block;
padding: 2px 20px;
white-space: nowrap;
margin-top: 2px;
float: left; /* LTR */
}
[dir="rtl"] .module-link {
float: right;
}
.module-link-help {
background: url(../../../misc/icons/787878/questionmark-disc.svg) 0 50% no-repeat; /* LTR */
}
[dir="rtl"] .module-link-help {
background-position: top 50% right 0;
}
.module-link-permissions {
background: url(../../../misc/icons/787878/key.svg) 0 50% no-repeat; /* LTR */
}
[dir="rtl"] .module-link-permissions {
background-position: top 50% right 0;
}
.module-link-configure {
background: url(../../../misc/icons/787878/cog.svg) 0 50% no-repeat; /* LTR */
}
[dir="rtl"] .module-link-configure {
background-position: top 50% right 0;
}
/* Status report. */
.system-status-report__status-title {
position: relative;
vertical-align: top;
width: 25%;
padding: 10px 6px 10px 40px; /* LTR */
box-sizing: border-box;
font-weight: normal;
}
[dir="rtl"] .system-status-report__status-title {
padding: 10px 40px 10px 6px;
}
.system-status-report__status-icon:before {
content: "";
background-repeat: no-repeat;
height: 16px;
width: 16px;
display: block;
position: absolute;
left: 12px; /* LTR */
top: 12px;
}
[dir="rtl"] .system-status-report__status-icon:before {
left: auto;
right: 12px;
}
.system-status-report__status-icon--error:before {
background-image: url(../../../misc/icons/e32700/error.svg);
}
.system-status-report__status-icon--warning:before {
background-image: url(../../../misc/icons/e29700/warning.svg);
}
/**
* Appearance page.
*/
.theme-info__header {
margin-bottom: 0;
font-weight: normal;
}
.theme-default .theme-info__header {
font-weight: bold;
}
.theme-info__description {
margin-top: 0;
}
.system-themes-list {
margin-bottom: 20px;
}
.system-themes-list-uninstalled {
border-top: 1px solid #cdcdcd;
padding-top: 20px;
}
.system-themes-list__header {
margin: 0;
}
.theme-selector {
padding-top: 20px;
}
.theme-selector .screenshot,
.theme-selector .no-screenshot {
border: 1px solid #e0e0d8;
padding: 2px;
vertical-align: bottom;
max-width: 100%;
height: auto;
text-align: center;
}
.theme-default .screenshot {
border: 1px solid #aaa;
}
.system-themes-list-uninstalled .screenshot,
.system-themes-list-uninstalled .no-screenshot {
max-width: 194px;
height: auto;
}
/**
* Theme display without vertical toolbar.
*/
@media screen and (min-width: 45em) {
body:not(.toolbar-vertical) .system-themes-list-installed .screenshot,
body:not(.toolbar-vertical) .system-themes-list-installed .no-screenshot {
float: left; /* LTR */
margin: 0 20px 0 0; /* LTR */
width: 294px;
}
[dir="rtl"] body:not(.toolbar-vertical) .system-themes-list-installed .screenshot,
[dir="rtl"] body:not(.toolbar-vertical) .system-themes-list-installed .no-screenshot {
float: right;
margin: 0 0 0 20px;
}
body:not(.toolbar-vertical) .system-themes-list-installed .system-themes-list__header {
margin-top: 0;
}
body:not(.toolbar-vertical) .system-themes-list-uninstalled .theme-selector {
box-sizing: border-box;
width: 31.25%;
float: left; /* LTR */
padding: 20px 20px 20px 0; /* LTR */
}
[dir="rtl"] body:not(.toolbar-vertical) .system-themes-list-uninstalled .theme-selector {
float: right;
padding: 20px 0 20px 20px;
}
body:not(.toolbar-vertical) .system-themes-list-uninstalled .theme-info {
min-height: 170px;
}
}
/**
* Theme display with vertical toolbar.
*/
@media screen and (min-width: 60em) {
.toolbar-vertical .system-themes-list-installed .screenshot,
.toolbar-vertical .system-themes-list-installed .no-screenshot {
float: left; /* LTR */
margin: 0 20px 0 0; /* LTR */
width: 294px;
}
[dir="rtl"] .toolbar-vertical .system-themes-list-installed .screenshot,
[dir="rtl"] .toolbar-vertical .system-themes-list-installed .no-screenshot {
float: right;
margin: 0 0 0 20px;
}
.toolbar-vertical .system-themes-list-installed .theme-info__header {
margin-top: 0;
}
.toolbar-vertical .system-themes-list-uninstalled .theme-selector {
box-sizing: border-box;
width: 31.25%;
float: left; /* LTR */
padding: 20px 20px 20px 0; /* LTR */
}
[dir="rtl"] .toolbar-vertical .system-themes-list-uninstalled .theme-selector {
float: right;
padding: 20px 0 20px 20px;
}
.toolbar-vertical .system-themes-list-uninstalled .theme-info {
min-height: 170px;
}
}
.system-themes-list-installed .theme-info {
max-width: 940px;
}
.theme-selector .incompatible {
margin-top: 10px;
font-weight: bold;
}
.theme-selector .operations {
margin: 10px 0 0 0;
padding: 0;
}
.theme-selector .operations li {
float: left; /* LTR */
margin: 0;
padding: 0 0.7em;
list-style-type: none;
border-right: 1px solid #cdcdcd; /* LTR */
}
[dir="rtl"] .theme-selector .operations li {
float: right;
border-left: 1px solid #cdcdcd;
border-right: none;
}
.theme-selector .operations li:last-child {
padding: 0 0 0 0.7em; /* LTR */
border-right: none; /* LTR */
}
[dir="rtl"] .theme-selector .operations li:last-child {
padding: 0 0.7em 0 0;
border-left: none;
}
.theme-selector .operations li:first-child {
padding: 0 0.7em 0 0; /* LTR */
}
[dir="rtl"] .theme-selector .operations li:first-child {
padding: 0 0 0 0.7em;
}
.system-themes-admin-form {
clear: left; /* LTR */
}
[dir="rtl"] .system-themes-admin-form {
clear: right;
}
+42
View File
@@ -0,0 +1,42 @@
/**
* Traditional split diff theming
*/
table.diff {
border-spacing: 4px;
margin-bottom: 20px;
table-layout: fixed;
width: 100%;
}
table.diff .diff-context {
background-color: #fafafa;
}
table.diff .diff-deletedline {
background-color: #ffa;
width: 50%;
}
table.diff .diff-addedline {
background-color: #afa;
width: 50%;
}
table.diff .diffchange {
color: #f00;
font-weight: bold;
}
table.diff .diff-marker {
width: 1.4em;
}
table.diff th {
padding-right: inherit; /* LTR */
}
[dir="rtl"] table.diff th {
padding-right: 0;
padding-left: inherit;
}
table.diff td div {
overflow: auto;
padding: 0.1ex 0.5em;
word-wrap: break-word;
}
table.diff td {
padding: 0.1ex 0.4em;
}
@@ -0,0 +1,56 @@
/**
* Update styles
*/
.update-results {
margin-top: 3em;
padding: 0.25em;
border: 1px solid #ccc;
background: #eee;
font-size: smaller;
}
.update-results h2 {
margin-top: 0.25em;
}
.update-results h4 {
margin-bottom: 0.25em;
}
.update-results .none {
color: #888;
font-style: italic;
}
.update-results .failure strong {
color: #b63300;
}
/**
* Authorize.php styles
*/
#edit-submit-connection {
clear: both;
}
#edit-submit-process,
.filetransfer {
display: none;
clear: both;
}
.js #edit-submit-connection {
display: none;
}
.js #edit-submit-process {
display: block;
}
#edit-connection-settings-change-connection-type {
margin: 2.6em 0.5em 0 1em; /* LTR */
}
[dir="rtl"] #edit-connection-settings-change-connection-type {
margin-left: 0.5em;
margin-right: 1em;
}
/**
* Theme maintenance styles
*/
.authorize-results__failure {
font-weight: bold;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

+57
View File
@@ -0,0 +1,57 @@
/**
* @file
* Provides date format preview feature.
*/
(function ($, Drupal, drupalSettings) {
'use strict';
var dateFormats = drupalSettings.dateFormats;
/**
* Display the preview for date format entered.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attach behavior for previewing date formats on input elements.
*/
Drupal.behaviors.dateFormat = {
attach: function (context) {
var $context = $(context);
var $source = $context.find('[data-drupal-date-formatter="source"]').once('dateFormat');
var $target = $context.find('[data-drupal-date-formatter="preview"]').once('dateFormat');
var $preview = $target.find('em');
// All elements have to exist.
if (!$source.length || !$target.length) {
return;
}
/**
* Event handler that replaces date characters with value.
*
* @param {jQuery.Event} e
* The jQuery event triggered.
*/
function dateFormatHandler(e) {
var baseValue = $(e.target).val() || '';
var dateString = baseValue.replace(/\\?(.?)/gi, function (key, value) {
return dateFormats[key] ? dateFormats[key] : value;
});
$preview.html(dateString);
$target.toggleClass('js-hide', !dateString.length);
}
/**
* On given event triggers the date character replacement.
*/
$source.on('keyup.dateFormat change.dateFormat input.dateFormat', dateFormatHandler)
// Initialize preview.
.trigger('keyup');
}
};
})(jQuery, Drupal, drupalSettings);
+81
View File
@@ -0,0 +1,81 @@
/**
* @file
* System behaviors.
*/
(function ($, Drupal, drupalSettings) {
'use strict';
// Cache IDs in an array for ease of use.
var ids = [];
/**
* Attaches field copy behavior from input fields to other input fields.
*
* When a field is filled out, apply its value to other fields that will
* likely use the same value. In the installer this is used to populate the
* administrator email address with the same value as the site email address.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the field copy behavior to an input field.
*/
Drupal.behaviors.copyFieldValue = {
attach: function (context) {
// List of fields IDs on which to bind the event listener.
// Create an array of IDs to use with jQuery.
for (var sourceId in drupalSettings.copyFieldValue) {
if (drupalSettings.copyFieldValue.hasOwnProperty(sourceId)) {
ids.push(sourceId);
}
}
if (ids.length) {
// Listen to value:copy events on all dependent fields.
// We have to use body and not document because of the way jQuery events
// bubble up the DOM tree.
$('body').once('copy-field-values').on('value:copy', this.valueTargetCopyHandler);
// Listen on all source elements.
$('#' + ids.join(', #')).once('copy-field-values').on('blur', this.valueSourceBlurHandler);
}
},
detach: function (context, settings, trigger) {
if (trigger === 'unload' && ids.length) {
$('body').removeOnce('copy-field-values').off('value:copy');
$('#' + ids.join(', #')).removeOnce('copy-field-values').off('blur');
}
},
/**
* Event handler that fill the target element with the specified value.
*
* @param {jQuery.Event} e
* Event object.
* @param {string} value
* Custom value from jQuery trigger.
*/
valueTargetCopyHandler: function (e, value) {
var $target = $(e.target);
if ($target.val() === '') {
$target.val(value);
}
},
/**
* Handler for a Blur event on a source field.
*
* This event handler will trigger a 'value:copy' event on all dependent
* fields.
*
* @param {jQuery.Event} e
* The event triggered.
*/
valueSourceBlurHandler: function (e) {
var value = $(e.target).val();
var targetIds = drupalSettings.copyFieldValue[e.target.id];
$('#' + targetIds.join(', #')).trigger('value:copy', value);
}
};
})(jQuery, Drupal, drupalSettings);
+103
View File
@@ -0,0 +1,103 @@
/**
* @file
* Module page behaviors.
*/
(function ($, Drupal, debounce) {
'use strict';
/**
* Filters the module list table by a text input search string.
*
* Additionally accounts for multiple tables being wrapped in "package" details
* elements.
*
* Text search input: input.table-filter-text
* Target table: input.table-filter-text[data-table]
* Source text: .table-filter-text-source, .module-name, .module-description
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.tableFilterByText = {
attach: function (context, settings) {
var $input = $('input.table-filter-text').once('table-filter-text');
var $table = $($input.attr('data-table'));
var $rowsAndDetails;
var $rows;
var $details;
var searching = false;
function hidePackageDetails(index, element) {
var $packDetails = $(element);
var $visibleRows = $packDetails.find('tbody tr:visible');
$packDetails.toggle($visibleRows.length > 0);
}
function filterModuleList(e) {
var query = $(e.target).val();
// Case insensitive expression to find query at the beginning of a word.
var re = new RegExp('\\b' + query, 'i');
function showModuleRow(index, row) {
var $row = $(row);
var $sources = $row.find('.table-filter-text-source, .module-name, .module-description');
var textMatch = $sources.text().search(re) !== -1;
$row.closest('tr').toggle(textMatch);
}
// Search over all rows and packages.
$rowsAndDetails.show();
// Filter if the length of the query is at least 2 characters.
if (query.length >= 2) {
searching = true;
$rows.each(showModuleRow);
// Note that we first open all <details> to be able to use ':visible'.
// Mark the <details> elements that were closed before filtering, so
// they can be reclosed when filtering is removed.
$details.not('[open]').attr('data-drupal-system-state', 'forced-open');
// Hide the package <details> if they don't have any visible rows.
// Note that we first show() all <details> to be able to use ':visible'.
$details.attr('open', true).each(hidePackageDetails);
Drupal.announce(
Drupal.t(
'!modules modules are available in the modified list.',
{'!modules': $rowsAndDetails.find('tbody tr:visible').length}
)
);
}
else if (searching) {
searching = false;
$rowsAndDetails.show();
// Return <details> elements that had been closed before filtering
// to a closed state.
$details.filter('[data-drupal-system-state="forced-open"]')
.removeAttr('data-drupal-system-state')
.attr('open', false);
}
}
function preventEnterKey(event) {
if (event.which === 13) {
event.preventDefault();
event.stopPropagation();
}
}
if ($table.length) {
$rowsAndDetails = $table.find('tr, details');
$rows = $table.find('tbody tr');
$details = $rowsAndDetails.filter('.package-listing');
$input.on({
keyup: debounce(filterModuleList, 200),
keydown: preventEnterKey
});
}
}
};
}(jQuery, Drupal, Drupal.debounce));
@@ -0,0 +1,21 @@
id: d6_date_formats
label: Date format configuration
migration_tags:
- Drupal 6
source:
plugin: variable_multirow
variables:
- date_format_long
- date_format_medium
- date_format_short
process:
id:
plugin: static_map
source: name
map:
date_format_long: long
date_format_short: short
date_format_medium: medium
pattern: value
destination:
plugin: entity:date_format
@@ -0,0 +1,16 @@
id: d6_system_cron
label: Cron settings
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- cron_threshold_warning
- cron_threshold_error
- cron_last
process:
'threshold/requirements_warning': cron_threshold_warning
'threshold/requirements_error': cron_threshold_error
destination:
plugin: config
config_name: system.cron
@@ -0,0 +1,19 @@
id: d6_system_date
label: System date configuration
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- configurable_timezones
- date_first_day
- date_default_timezone
process:
'timezone/user/configurable': configurable_timezones
first_day: date_first_day
'timezone/default':
plugin: timezone
source: date_default_timezone
destination:
plugin: config
config_name: system.date
@@ -0,0 +1,20 @@
id: d6_system_file
label: File system configuration
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- file_directory_temp
- allow_insecure_uploads
process:
'path/temporary': file_directory_temp
allow_insecure_uploads:
plugin: static_map
source: allow_insecure_uploads
map:
0: FALSE
1: TRUE
destination:
plugin: config
config_name: system.file
@@ -0,0 +1,13 @@
id: d6_system_image
label: Image toolkit configuration
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- image_toolkit
process:
toolkit: image_toolkit
destination:
plugin: config
config_name: system.image
@@ -0,0 +1,13 @@
id: d6_system_image_gd
label: Image quality configuration
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- image_jpeg_quality
process:
jpeg_quality: image_jpeg_quality
destination:
plugin: config
config_name: system.image.gd
@@ -0,0 +1,21 @@
id: d6_system_logging
label: System logging
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- error_level
process:
error_level:
plugin: static_map
source: error_level
default_value: all
map:
0: hide
1: some
2: all
3: verbose
destination:
plugin: config
config_name: system.logging
@@ -0,0 +1,13 @@
id: d6_system_maintenance
label: Maintenance page configuration
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- site_offline_message
process:
message: site_offline_message
destination:
plugin: config
config_name: system.maintenance
@@ -0,0 +1,20 @@
id: d6_system_performance
label: Performance configuration
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- preprocess_css
- preprocess_js
- cache_lifetime
- cache
- page_compression
process:
'css/preprocess': preprocess_css
'js/preprocess': preprocess_js
'cache/page/max_age': cache_lifetime
'response/gzip': page_compression
destination:
plugin: config
config_name: system.performance
@@ -0,0 +1,15 @@
id: d6_system_rss
label: RSS configuration
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- feed_default_items
- feed_item_length
process:
'items/limit': feed_default_items
'items/view_mode': feed_item_length
destination:
plugin: config
config_name: system.rss
@@ -0,0 +1,41 @@
id: d6_system_site
label: Site configuration
migration_tags:
- Drupal 6
source:
plugin: variable
constants:
slash: '/'
variables:
- site_name
- site_mail
- site_slogan
- site_frontpage
- site_403
- site_404
- drupal_weight_select_max
- admin_compact_mode
process:
name: site_name
mail: site_mail
slogan: site_slogan
'page/front':
plugin: concat
source:
- constants/slash
- site_frontpage
'page/403':
plugin: concat
source:
- constants/slash
- site_403
'page/404':
plugin: concat
source:
- constants/slash
- site_404
weight_select_max: drupal_weight_select_max
admin_compact_mode: admin_compact_mode
destination:
plugin: config
config_name: system.site
@@ -0,0 +1,14 @@
# The menu_settings migration is in the menu_ui module.
id: menu
label: Menus
migration_tags:
- Drupal 6
- Drupal 7
source:
plugin: menu
process:
id: menu_name
label: title
description: description
destination:
plugin: entity:menu
@@ -0,0 +1,34 @@
<?php
namespace Drupal\system\Access;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Routing\Access\AccessInterface;
/**
* Access check for cron routes.
*/
class CronAccessCheck implements AccessInterface {
/**
* Checks access.
*
* @param string $key
* The cron key.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function access($key) {
if ($key != \Drupal::state()->get('system.cron_key')) {
\Drupal::logger('cron')->notice('Cron could not run because an invalid key was used.');
return AccessResult::forbidden()->setCacheMaxAge(0);
}
elseif (\Drupal::state()->get('system.maintenance_mode')) {
\Drupal::logger('cron')->notice('Cron could not run because the site is in maintenance mode.');
return AccessResult::forbidden()->setCacheMaxAge(0);
}
return AccessResult::allowed()->setCacheMaxAge(0);
}
}
@@ -0,0 +1,38 @@
<?php
namespace Drupal\system\Access;
use Drupal\Core\Routing\Access\AccessInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Site\Settings;
/**
* Access check for database update routes.
*/
class DbUpdateAccessCheck implements AccessInterface {
/**
* Checks access for update routes.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The currently logged in account.
*
* @return string
* A \Drupal\Core\Access\AccessInterface constant value.
*/
public function access(AccountInterface $account) {
// Allow the global variable in settings.php to override the access check.
if (Settings::get('update_free_access')) {
return AccessResult::allowed()->setCacheMaxAge(0);
}
if ($account->hasPermission('administer software updates')) {
return AccessResult::allowed()->cachePerPermissions();
}
else {
return AccessResult::forbidden()->cachePerPermissions();
}
}
}
@@ -0,0 +1,33 @@
<?php
namespace Drupal\system;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
/**
* Provides an interface defining a action entity.
*/
interface ActionConfigEntityInterface extends ConfigEntityInterface {
/**
* Returns whether or not this action is configurable.
*
* @return bool
*/
public function isConfigurable();
/**
* Returns the operation type.
*
* @return string
*/
public function getType();
/**
* Returns the operation plugin.
*
* @return \Drupal\Core\Action\ActionInterface
*/
public function getPlugin();
}
@@ -0,0 +1,53 @@
<?php
namespace Drupal\system\Controller;
use Drupal\Core\Controller\ControllerBase;
/**
* Controller for admin section.
*/
class AdminController extends ControllerBase {
/**
* Prints a listing of admin tasks, organized by module.
*
* @return array
* A render array containing the listing.
*/
public function index() {
$module_info = system_get_info('module');
foreach ($module_info as $module => $info) {
$module_info[$module] = new \stdClass();
$module_info[$module]->info = $info;
}
uasort($module_info, 'system_sort_modules_by_info_name');
$menu_items = array();
foreach ($module_info as $module => $info) {
// Only display a section if there are any available tasks.
if ($admin_tasks = system_get_module_admin_tasks($module, $info->info)) {
// Sort links by title.
uasort($admin_tasks, array('\Drupal\Component\Utility\SortArray', 'sortByTitleElement'));
// Move 'Configure permissions' links to the bottom of each section.
$permission_key = "user.admin_permissions.$module";
if (isset($admin_tasks[$permission_key])) {
$permission_task = $admin_tasks[$permission_key];
unset($admin_tasks[$permission_key]);
$admin_tasks[$permission_key] = $permission_task;
}
$menu_items[$info->info['name']] = array($info->info['description'], $admin_tasks);
}
}
$output = array(
'#theme' => 'system_admin_index',
'#menu_items' => $menu_items,
);
return $output;
}
}
@@ -0,0 +1,84 @@
<?php
namespace Drupal\system\Controller;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* Controller routines for batch routes.
*/
class BatchController implements ContainerInjectionInterface {
/**
* The app root.
*
* @var string
*/
protected $root;
/**
* Constructs a new BatchController.
*
* @param string $root
* The app root.
*/
public function __construct($root) {
$this->root = $root;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('app.root')
);
}
/**
* Returns a system batch page.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request object.
*
* @return \Symfony\Component\HttpFoundation\Response|array
* A \Symfony\Component\HttpFoundation\Response object or render array.
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
public function batchPage(Request $request) {
require_once $this->root . '/core/includes/batch.inc';
$output = _batch_page($request);
if ($output === FALSE) {
throw new AccessDeniedHttpException();
}
elseif ($output instanceof Response) {
return $output;
}
elseif (isset($output)) {
$page = [
'#type' => 'page',
'#show_messages' => FALSE,
'content' => $output,
];
return $page;
}
}
/**
* The _title_callback for the system.batch_page.normal route.
*
* @return string
* The page title.
*/
public function batchPageTitle() {
$current_set = _batch_current_set();
return !empty($current_set['title']) ? $current_set['title'] : '';
}
}
@@ -0,0 +1,705 @@
<?php
namespace Drupal\system\Controller;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
use Drupal\Core\Render\BareHtmlPageRendererInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Site\Settings;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Update\UpdateRegistry;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
/**
* Controller routines for database update routes.
*/
class DbUpdateController extends ControllerBase {
/**
* The keyvalue expirable factory.
*
* @var \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface
*/
protected $keyValueExpirableFactory;
/**
* A cache backend interface.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $account;
/**
* The bare HTML page renderer.
*
* @var \Drupal\Core\Render\BareHtmlPageRendererInterface
*/
protected $bareHtmlPageRenderer;
/**
* The app root.
*
* @var string
*/
protected $root;
/**
* The post update registry.
*
* @var \Drupal\Core\Update\UpdateRegistry
*/
protected $postUpdateRegistry;
/**
* Constructs a new UpdateController.
*
* @param string $root
* The app root.
* @param \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $key_value_expirable_factory
* The keyvalue expirable factory.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* A cache backend interface.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Session\AccountInterface $account
* The current user.
* @param \Drupal\Core\Render\BareHtmlPageRendererInterface $bare_html_page_renderer
* The bare HTML page renderer.
* @param \Drupal\Core\Update\UpdateRegistry $post_update_registry
* The post update registry.
*/
public function __construct($root, KeyValueExpirableFactoryInterface $key_value_expirable_factory, CacheBackendInterface $cache, StateInterface $state, ModuleHandlerInterface $module_handler, AccountInterface $account, BareHtmlPageRendererInterface $bare_html_page_renderer, UpdateRegistry $post_update_registry) {
$this->root = $root;
$this->keyValueExpirableFactory = $key_value_expirable_factory;
$this->cache = $cache;
$this->state = $state;
$this->moduleHandler = $module_handler;
$this->account = $account;
$this->bareHtmlPageRenderer = $bare_html_page_renderer;
$this->postUpdateRegistry = $post_update_registry;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('app.root'),
$container->get('keyvalue.expirable'),
$container->get('cache.default'),
$container->get('state'),
$container->get('module_handler'),
$container->get('current_user'),
$container->get('bare_html_page_renderer'),
$container->get('update.post_update_registry')
);
}
/**
* Returns a database update page.
*
* @param string $op
* The update operation to perform. Can be any of the below:
* - info
* - selection
* - run
* - results
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request object.
*
* @return \Symfony\Component\HttpFoundation\Response
* A response object object.
*/
public function handle($op, Request $request) {
require_once $this->root . '/core/includes/install.inc';
require_once $this->root . '/core/includes/update.inc';
drupal_load_updates();
update_fix_compatibility();
if ($request->query->get('continue')) {
$_SESSION['update_ignore_warnings'] = TRUE;
}
$regions = array();
$requirements = update_check_requirements();
$severity = drupal_requirements_severity($requirements);
if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && empty($_SESSION['update_ignore_warnings']))) {
$regions['sidebar_first'] = $this->updateTasksList('requirements');
$output = $this->requirements($severity, $requirements, $request);
}
else {
switch ($op) {
case 'selection':
$regions['sidebar_first'] = $this->updateTasksList('selection');
$output = $this->selection($request);
break;
case 'run':
$regions['sidebar_first'] = $this->updateTasksList('run');
$output = $this->triggerBatch($request);
break;
case 'info':
$regions['sidebar_first'] = $this->updateTasksList('info');
$output = $this->info($request);
break;
case 'results':
$regions['sidebar_first'] = $this->updateTasksList('results');
$output = $this->results($request);
break;
// Regular batch ops : defer to batch processing API.
default:
require_once $this->root . '/core/includes/batch.inc';
$regions['sidebar_first'] = $this->updateTasksList('run');
$output = _batch_page($request);
break;
}
}
if ($output instanceof Response) {
return $output;
}
$title = isset($output['#title']) ? $output['#title'] : $this->t('Drupal database update');
return $this->bareHtmlPageRenderer->renderBarePage($output, $title, 'maintenance_page', $regions);
}
/**
* Returns the info database update page.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return array
* A render array.
*/
protected function info(Request $request) {
// Change query-strings on css/js files to enforce reload for all users.
_drupal_flush_css_js();
// Flush the cache of all data for the update status module.
$this->keyValueExpirableFactory->get('update')->deleteAll();
$this->keyValueExpirableFactory->get('update_available_release')->deleteAll();
$build['info_header'] = array(
'#markup' => '<p>' . $this->t('Use this utility to update your database whenever a new release of Drupal or a module is installed.') . '</p><p>' . $this->t('For more detailed information, see the <a href="https://www.drupal.org/upgrade">upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.') . '</p>',
);
$info[] = $this->t("<strong>Back up your code</strong>. Hint: when backing up module code, do not leave that backup in the 'modules' or 'sites/*/modules' directories as this may confuse Drupal's auto-discovery mechanism.");
$info[] = $this->t('Put your site into <a href=":url">maintenance mode</a>.', array(
':url' => Url::fromRoute('system.site_maintenance_mode')->toString(TRUE)->getGeneratedUrl(),
));
$info[] = $this->t('<strong>Back up your database</strong>. This process will change your database values and in case of emergency you may need to revert to a backup.');
$info[] = $this->t('Install your new files in the appropriate location, as described in the handbook.');
$build['info'] = array(
'#theme' => 'item_list',
'#list_type' => 'ol',
'#items' => $info,
);
$build['info_footer'] = array(
'#markup' => '<p>' . $this->t('When you have performed the steps above, you may proceed.') . '</p>',
);
$build['link'] = array(
'#type' => 'link',
'#title' => $this->t('Continue'),
'#attributes' => array('class' => array('button', 'button--primary')),
// @todo Revisit once https://www.drupal.org/node/2548095 is in.
'#url' => Url::fromUri('base://selection'),
);
return $build;
}
/**
* Renders a list of available database updates.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return array
* A render array.
*/
protected function selection(Request $request) {
// Make sure there is no stale theme registry.
$this->cache->deleteAll();
$count = 0;
$incompatible_count = 0;
$build['start'] = array(
'#tree' => TRUE,
'#type' => 'details',
);
// Ensure system.module's updates appear first.
$build['start']['system'] = array();
$starting_updates = array();
$incompatible_updates_exist = FALSE;
$updates_per_module = [];
foreach (['update', 'post_update'] as $update_type) {
switch ($update_type) {
case 'update':
$updates = update_get_update_list();
break;
case 'post_update':
$updates = $this->postUpdateRegistry->getPendingUpdateInformation();
break;
}
foreach ($updates as $module => $update) {
if (!isset($update['start'])) {
$build['start'][$module] = array(
'#type' => 'item',
'#title' => $module . ' module',
'#markup' => $update['warning'],
'#prefix' => '<div class="messages messages--warning">',
'#suffix' => '</div>',
);
$incompatible_updates_exist = TRUE;
continue;
}
if (!empty($update['pending'])) {
$updates_per_module += [$module => []];
$updates_per_module[$module] = array_merge($updates_per_module[$module], $update['pending']);
$build['start'][$module] = array(
'#type' => 'hidden',
'#value' => $update['start'],
);
// Store the previous items in order to merge normal updates and
// post_update functions together.
$build['start'][$module] = array(
'#theme' => 'item_list',
'#items' => $updates_per_module[$module],
'#title' => $module . ' module',
);
if ($update_type === 'update') {
$starting_updates[$module] = $update['start'];
}
}
if (isset($update['pending'])) {
$count = $count + count($update['pending']);
}
}
}
// Find and label any incompatible updates.
foreach (update_resolve_dependencies($starting_updates) as $data) {
if (!$data['allowed']) {
$incompatible_updates_exist = TRUE;
$incompatible_count++;
$module_update_key = $data['module'] . '_updates';
if (isset($build['start'][$module_update_key]['#items'][$data['number']])) {
if ($data['missing_dependencies']) {
$text = $this->t('This update will been skipped due to the following missing dependencies:') . '<em>' . implode(', ', $data['missing_dependencies']) . '</em>';
}
else {
$text = $this->t("This update will be skipped due to an error in the module's code.");
}
$build['start'][$module_update_key]['#items'][$data['number']] .= '<div class="warning">' . $text . '</div>';
}
// Move the module containing this update to the top of the list.
$build['start'] = array($module_update_key => $build['start'][$module_update_key]) + $build['start'];
}
}
// Warn the user if any updates were incompatible.
if ($incompatible_updates_exist) {
drupal_set_message($this->t('Some of the pending updates cannot be applied because their dependencies were not met.'), 'warning');
}
if (empty($count)) {
drupal_set_message($this->t('No pending updates.'));
unset($build);
$build['links'] = array(
'#theme' => 'links',
'#links' => $this->helpfulLinks($request),
);
// No updates to run, so caches won't get flushed later. Clear them now.
drupal_flush_all_caches();
}
else {
$build['help'] = array(
'#markup' => '<p>' . $this->t('The version of Drupal you are updating from has been automatically detected.') . '</p>',
'#weight' => -5,
);
if ($incompatible_count) {
$build['start']['#title'] = $this->formatPlural(
$count,
'1 pending update (@number_applied to be applied, @number_incompatible skipped)',
'@count pending updates (@number_applied to be applied, @number_incompatible skipped)',
array('@number_applied' => $count - $incompatible_count, '@number_incompatible' => $incompatible_count)
);
}
else {
$build['start']['#title'] = $this->formatPlural($count, '1 pending update', '@count pending updates');
}
// @todo Simplify with https://www.drupal.org/node/2548095
$base_url = str_replace('/update.php', '', $request->getBaseUrl());
$url = (new Url('system.db_update', array('op' => 'run')))->setOption('base_url', $base_url);
$build['link'] = array(
'#type' => 'link',
'#title' => $this->t('Apply pending updates'),
'#attributes' => array('class' => array('button', 'button--primary')),
'#weight' => 5,
'#url' => $url,
'#access' => $url->access($this->currentUser()),
);
}
return $build;
}
/**
* Displays results of the update script with any accompanying errors.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return array
* A render array.
*/
protected function results(Request $request) {
// @todo Simplify with https://www.drupal.org/node/2548095
$base_url = str_replace('/update.php', '', $request->getBaseUrl());
// Report end result.
$dblog_exists = $this->moduleHandler->moduleExists('dblog');
if ($dblog_exists && $this->account->hasPermission('access site reports')) {
$log_message = $this->t('All errors have been <a href=":url">logged</a>.', array(
':url' => Url::fromRoute('dblog.overview')->setOption('base_url', $base_url)->toString(TRUE)->getGeneratedUrl(),
));
}
else {
$log_message = $this->t('All errors have been logged.');
}
if (!empty($_SESSION['update_success'])) {
$message = '<p>' . $this->t('Updates were attempted. If you see no failures below, you may proceed happily back to your <a href=":url">site</a>. Otherwise, you may need to update your database manually.', array(':url' => Url::fromRoute('<front>')->setOption('base_url', $base_url)->toString(TRUE)->getGeneratedUrl())) . ' ' . $log_message . '</p>';
}
else {
$last = reset($_SESSION['updates_remaining']);
list($module, $version) = array_pop($last);
$message = '<p class="error">' . $this->t('The update process was aborted prematurely while running <strong>update #@version in @module.module</strong>.', array(
'@version' => $version,
'@module' => $module,
)) . ' ' . $log_message;
if ($dblog_exists) {
$message .= ' ' . $this->t('You may need to check the <code>watchdog</code> database table manually.');
}
$message .= '</p>';
}
if (Settings::get('update_free_access')) {
$message .= '<p>' . $this->t("<strong>Reminder: don't forget to set the <code>\$settings['update_free_access']</code> value in your <code>settings.php</code> file back to <code>FALSE</code>.</strong>") . '</p>';
}
$build['message'] = array(
'#markup' => $message,
);
$build['links'] = array(
'#theme' => 'links',
'#links' => $this->helpfulLinks($request),
);
// Output a list of info messages.
if (!empty($_SESSION['update_results'])) {
$all_messages = array();
foreach ($_SESSION['update_results'] as $module => $updates) {
if ($module != '#abort') {
$module_has_message = FALSE;
$info_messages = array();
foreach ($updates as $name => $queries) {
$messages = array();
foreach ($queries as $query) {
// If there is no message for this update, don't show anything.
if (empty($query['query'])) {
continue;
}
if ($query['success']) {
$messages[] = array(
'#wrapper_attributes' => array('class' => array('success')),
'#markup' => $query['query'],
);
}
else {
$messages[] = array(
'#wrapper_attributes' => array('class' => array('failure')),
'#markup' => '<strong>' . $this->t('Failed:') . '</strong> ' . $query['query'],
);
}
}
if ($messages) {
$module_has_message = TRUE;
if (is_numeric($name)) {
$title = $this->t('Update #@count', ['@count' => $name]);
}
else {
$title = $this->t('Update @name', ['@name' => trim($name, '_')]);
}
$info_messages[] = array(
'#theme' => 'item_list',
'#items' => $messages,
'#title' => $title,
);
}
}
// If there were any messages then prefix them with the module name
// and add it to the global message list.
if ($module_has_message) {
$all_messages[] = array(
'#type' => 'container',
'#prefix' => '<h3>' . $this->t('@module module', array('@module' => $module)) . '</h3>',
'#children' => $info_messages,
);
}
}
}
if ($all_messages) {
$build['query_messages'] = array(
'#type' => 'container',
'#children' => $all_messages,
'#attributes' => array('class' => array('update-results')),
'#prefix' => '<h2>' . $this->t('The following updates returned messages:') . '</h2>',
);
}
}
unset($_SESSION['update_results']);
unset($_SESSION['update_success']);
unset($_SESSION['update_ignore_warnings']);
return $build;
}
/**
* Renders a list of requirement errors or warnings.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return array
* A render array.
*/
public function requirements($severity, array $requirements, Request $request) {
$options = $severity == REQUIREMENT_WARNING ? array('continue' => 1) : array();
// @todo Revisit once https://www.drupal.org/node/2548095 is in. Something
// like Url::fromRoute('system.db_update')->setOptions() should then be
// possible.
$try_again_url = Url::fromUri($request->getUriForPath(''))->setOptions(['query' => $options])->toString(TRUE)->getGeneratedUrl();
$build['status_report'] = array(
'#theme' => 'status_report',
'#requirements' => $requirements,
'#suffix' => $this->t('Check the messages and <a href=":url">try again</a>.', array(':url' => $try_again_url))
);
$build['#title'] = $this->t('Requirements problem');
return $build;
}
/**
* Provides the update task list render array.
*
* @param string $active
* The active task.
* Can be one of 'requirements', 'info', 'selection', 'run', 'results'.
*
* @return array
* A render array.
*/
protected function updateTasksList($active = NULL) {
// Default list of tasks.
$tasks = array(
'requirements' => $this->t('Verify requirements'),
'info' => $this->t('Overview'),
'selection' => $this->t('Review updates'),
'run' => $this->t('Run updates'),
'results' => $this->t('Review log'),
);
$task_list = array(
'#theme' => 'maintenance_task_list',
'#items' => $tasks,
'#active' => $active,
);
return $task_list;
}
/**
* Starts the database update batch process.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request object.
*/
protected function triggerBatch(Request $request) {
$maintenance_mode = $this->state->get('system.maintenance_mode', FALSE);
// Store the current maintenance mode status in the session so that it can
// be restored at the end of the batch.
$_SESSION['maintenance_mode'] = $maintenance_mode;
// During the update, always put the site into maintenance mode so that
// in-progress schema changes do not affect visiting users.
if (empty($maintenance_mode)) {
$this->state->set('system.maintenance_mode', TRUE);
}
$operations = array();
// Resolve any update dependencies to determine the actual updates that will
// be run and the order they will be run in.
$start = $this->getModuleUpdates();
$updates = update_resolve_dependencies($start);
// Store the dependencies for each update function in an array which the
// batch API can pass in to the batch operation each time it is called. (We
// do not store the entire update dependency array here because it is
// potentially very large.)
$dependency_map = array();
foreach ($updates as $function => $update) {
$dependency_map[$function] = !empty($update['reverse_paths']) ? array_keys($update['reverse_paths']) : array();
}
// Determine updates to be performed.
foreach ($updates as $function => $update) {
if ($update['allowed']) {
// Set the installed version of each module so updates will start at the
// correct place. (The updates are already sorted, so we can simply base
// this on the first one we come across in the above foreach loop.)
if (isset($start[$update['module']])) {
drupal_set_installed_schema_version($update['module'], $update['number'] - 1);
unset($start[$update['module']]);
}
$operations[] = array('update_do_one', array($update['module'], $update['number'], $dependency_map[$function]));
}
}
$post_updates = $this->postUpdateRegistry->getPendingUpdateFunctions();
if ($post_updates) {
// Now we rebuild all caches and after that execute the hook_post_update()
// functions.
$operations[] = ['drupal_flush_all_caches', []];
foreach ($post_updates as $function) {
$operations[] = ['update_invoke_post_update', [$function]];
}
}
$batch['operations'] = $operations;
$batch += array(
'title' => $this->t('Updating'),
'init_message' => $this->t('Starting updates'),
'error_message' => $this->t('An unrecoverable error has occurred. You can find the error message below. It is advised to copy it to the clipboard for reference.'),
'finished' => array('\Drupal\system\Controller\DbUpdateController', 'batchFinished'),
);
batch_set($batch);
// @todo Revisit once https://www.drupal.org/node/2548095 is in.
return batch_process(Url::fromUri('base://results'), Url::fromUri('base://start'));
}
/**
* Finishes the update process and stores the results for eventual display.
*
* After the updates run, all caches are flushed. The update results are
* stored into the session (for example, to be displayed on the update results
* page in update.php). Additionally, if the site was off-line, now that the
* update process is completed, the site is set back online.
*
* @param $success
* Indicate that the batch API tasks were all completed successfully.
* @param array $results
* An array of all the results that were updated in update_do_one().
* @param array $operations
* A list of all the operations that had not been completed by the batch API.
*/
public static function batchFinished($success, $results, $operations) {
// No updates to run, so caches won't get flushed later. Clear them now.
drupal_flush_all_caches();
$_SESSION['update_results'] = $results;
$_SESSION['update_success'] = $success;
$_SESSION['updates_remaining'] = $operations;
// Now that the update is done, we can put the site back online if it was
// previously not in maintenance mode.
if (empty($_SESSION['maintenance_mode'])) {
\Drupal::state()->set('system.maintenance_mode', FALSE);
}
unset($_SESSION['maintenance_mode']);
}
/**
* Provides links to the homepage and administration pages.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return array
* An array of links.
*/
protected function helpfulLinks(Request $request) {
// @todo Simplify with https://www.drupal.org/node/2548095
$base_url = str_replace('/update.php', '', $request->getBaseUrl());
$links['front'] = array(
'title' => $this->t('Front page'),
'url' => Url::fromRoute('<front>')->setOption('base_url', $base_url),
);
if ($this->account->hasPermission('access administration pages')) {
$links['admin-pages'] = array(
'title' => $this->t('Administration pages'),
'url' => Url::fromRoute('system.admin')->setOption('base_url', $base_url),
);
}
return $links;
}
/**
* Retrieves module updates.
*
* @return array
* The module updates that can be performed.
*/
protected function getModuleUpdates() {
$return = array();
$updates = update_get_update_list();
foreach ($updates as $module => $update) {
$return[$module] = $update['start'];
}
return $return;
}
}
@@ -0,0 +1,109 @@
<?php
namespace Drupal\system\Controller;
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Tags;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityAutocompleteMatcher;
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
use Drupal\Core\Site\Settings;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* Defines a route controller for entity autocomplete form elements.
*/
class EntityAutocompleteController extends ControllerBase {
/**
* The autocomplete matcher for entity references.
*
* @var \Drupal\Core\Entity\EntityAutocompleteMatcher
*/
protected $matcher;
/**
* The key value store.
*
* @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
*/
protected $keyValue;
/**
* Constructs a EntityAutocompleteController object.
*
* @param \Drupal\Core\Entity\EntityAutocompleteMatcher $matcher
* The autocomplete matcher for entity references.
* @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $key_value
* The key value factory.
*/
public function __construct(EntityAutocompleteMatcher $matcher, KeyValueStoreInterface $key_value) {
$this->matcher = $matcher;
$this->keyValue = $key_value;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.autocomplete_matcher'),
$container->get('keyvalue')->get('entity_autocomplete')
);
}
/**
* Autocomplete the label of an entity.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object that contains the typed tags.
* @param string $target_type
* The ID of the target entity type.
* @param string $selection_handler
* The plugin ID of the entity reference selection handler.
* @param string $selection_settings_key
* The hashed key of the key/value entry that holds the selection handler
* settings.
*
* @return \Symfony\Component\HttpFoundation\JsonResponse
* The matched entity labels as a JSON response.
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
* Thrown if the selection settings key is not found in the key/value store
* or if it does not match the stored data.
*/
public function handleAutocomplete(Request $request, $target_type, $selection_handler, $selection_settings_key) {
$matches = array();
// Get the typed string from the URL, if it exists.
if ($input = $request->query->get('q')) {
$typed_string = Tags::explode($input);
$typed_string = Unicode::strtolower(array_pop($typed_string));
// Selection settings are passed in as a hashed key of a serialized array
// stored in the key/value store.
$selection_settings = $this->keyValue->get($selection_settings_key, FALSE);
if ($selection_settings !== FALSE) {
$selection_settings_hash = Crypt::hmacBase64(serialize($selection_settings) . $target_type . $selection_handler, Settings::getHashSalt());
if ($selection_settings_hash !== $selection_settings_key) {
// Disallow access when the selection settings hash does not match the
// passed-in key.
throw new AccessDeniedHttpException('Invalid selection settings key.');
}
}
else {
// Disallow access when the selection settings key is not found in the
// key/value store.
throw new AccessDeniedHttpException();
}
$matches = $this->matcher->getMatches($target_type, $selection_handler, $selection_settings, $typed_string);
}
return new JsonResponse($matches);
}
}
@@ -0,0 +1,48 @@
<?php
namespace Drupal\system\Controller;
use Drupal\Core\Controller\ControllerBase;
/**
* Controller for default HTTP 4xx responses.
*/
class Http4xxController extends ControllerBase {
/**
* The default 401 content.
*
* @return array
* A render array containing the message to display for 401 pages.
*/
public function on401() {
return [
'#markup' => $this->t('Please log in to access this page.'),
];
}
/**
* The default 403 content.
*
* @return array
* A render array containing the message to display for 404 pages.
*/
public function on403() {
return [
'#markup' => $this->t('You are not authorized to access this page.'),
];
}
/**
* The default 404 content.
*
* @return array
* A render array containing the message to display for 404 pages.
*/
public function on404() {
return [
'#markup' => $this->t('The requested page could not be found.'),
];
}
}
@@ -0,0 +1,339 @@
<?php
namespace Drupal\system\Controller;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Menu\MenuLinkTreeInterface;
use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\Core\Theme\ThemeAccessCheck;
use Drupal\Core\Url;
use Drupal\system\SystemManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Returns responses for System routes.
*/
class SystemController extends ControllerBase {
/**
* The entity query factory object.
*
* @var \Drupal\Core\Entity\Query\QueryFactory
*/
protected $queryFactory;
/**
* System Manager Service.
*
* @var \Drupal\system\SystemManager
*/
protected $systemManager;
/**
* The theme access checker service.
*
* @var \Drupal\Core\Theme\ThemeAccessCheck
*/
protected $themeAccess;
/**
* The form builder service.
*
* @var \Drupal\Core\Form\FormBuilderInterface
*/
protected $formBuilder;
/**
* The theme handler service.
*
* @var \Drupal\Core\Extension\ThemeHandlerInterface
*/
protected $themeHandler;
/**
* The menu link tree service.
*
* @var \Drupal\Core\Menu\MenuLinkTreeInterface
*/
protected $menuLinkTree;
/**
* Constructs a new SystemController.
*
* @param \Drupal\system\SystemManager $systemManager
* System manager service.
* @param \Drupal\Core\Entity\Query\QueryFactory $queryFactory
* The entity query object.
* @param \Drupal\Core\Theme\ThemeAccessCheck $theme_access
* The theme access checker service.
* @param \Drupal\Core\Form\FormBuilderInterface $form_builder
* The form builder.
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
* The theme handler.
* @param \Drupal\Core\Menu\MenuLinkTreeInterface
* The menu link tree service.
*/
public function __construct(SystemManager $systemManager, QueryFactory $queryFactory, ThemeAccessCheck $theme_access, FormBuilderInterface $form_builder, ThemeHandlerInterface $theme_handler, MenuLinkTreeInterface $menu_link_tree) {
$this->systemManager = $systemManager;
$this->queryFactory = $queryFactory;
$this->themeAccess = $theme_access;
$this->formBuilder = $form_builder;
$this->themeHandler = $theme_handler;
$this->menuLinkTree = $menu_link_tree;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('system.manager'),
$container->get('entity.query'),
$container->get('access_check.theme'),
$container->get('form_builder'),
$container->get('theme_handler'),
$container->get('menu.link_tree')
);
}
/**
* Provide the administration overview page.
*
* @param string $link_id
* The ID of the administrative path link for which to display child links.
*
* @return array
* A renderable array of the administration overview page.
*/
public function overview($link_id) {
// Check for status report errors.
if ($this->systemManager->checkRequirements() && $this->currentUser()->hasPermission('administer site configuration')) {
drupal_set_message($this->t('One or more problems were detected with your Drupal installation. Check the <a href=":status">status report</a> for more information.', array(':status' => $this->url('system.status'))), 'error');
}
// Load all menu links below it.
$parameters = new MenuTreeParameters();
$parameters->setRoot($link_id)->excludeRoot()->setTopLevelOnly()->onlyEnabledLinks();
$tree = $this->menuLinkTree->load(NULL, $parameters);
$manipulators = array(
array('callable' => 'menu.default_tree_manipulators:checkAccess'),
array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
);
$tree = $this->menuLinkTree->transform($tree, $manipulators);
$tree_access_cacheability = new CacheableMetadata();
$blocks = array();
foreach ($tree as $key => $element) {
$tree_access_cacheability = $tree_access_cacheability->merge(CacheableMetadata::createFromObject($element->access));
// Only render accessible links.
if (!$element->access->isAllowed()) {
continue;
}
$link = $element->link;
$block['title'] = $link->getTitle();
$block['description'] = $link->getDescription();
$block['content'] = array(
'#theme' => 'admin_block_content',
'#content' => $this->systemManager->getAdminBlock($link),
);
if (!empty($block['content']['#content'])) {
$blocks[$key] = $block;
}
}
if ($blocks) {
ksort($blocks);
$build = [
'#theme' => 'admin_page',
'#blocks' => $blocks,
];
$tree_access_cacheability->applyTo($build);
return $build;
}
else {
$build = [
'#markup' => $this->t('You do not have any administrative items.'),
];
$tree_access_cacheability->applyTo($build);
return $build;
}
}
/**
* Sets whether the admin menu is in compact mode or not.
*
* @param string $mode
* Valid values are 'on' and 'off'.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function compactPage($mode) {
user_cookie_save(array('admin_compact_mode' => ($mode == 'on')));
return $this->redirect('<front>');
}
/**
* Provides a single block from the administration menu as a page.
*/
public function systemAdminMenuBlockPage() {
return $this->systemManager->getBlockContents();
}
/**
* Returns a theme listing.
*
* @return string
* An HTML string of the theme listing page.
*
* @todo Move into ThemeController.
*/
public function themesPage() {
$config = $this->config('system.theme');
// Get all available themes.
$themes = $this->themeHandler->rebuildThemeData();
uasort($themes, 'system_sort_modules_by_info_name');
$theme_default = $config->get('default');
$theme_groups = array('installed' => array(), 'uninstalled' => array());
$admin_theme = $config->get('admin');
$admin_theme_options = array();
foreach ($themes as &$theme) {
if (!empty($theme->info['hidden'])) {
continue;
}
$theme->is_default = ($theme->getName() == $theme_default);
$theme->is_admin = ($theme->getName() == $admin_theme || ($theme->is_default && $admin_theme == '0'));
// Identify theme screenshot.
$theme->screenshot = NULL;
// Create a list which includes the current theme and all its base themes.
if (isset($themes[$theme->getName()]->base_themes)) {
$theme_keys = array_keys($themes[$theme->getName()]->base_themes);
$theme_keys[] = $theme->getName();
}
else {
$theme_keys = array($theme->getName());
}
// Look for a screenshot in the current theme or in its closest ancestor.
foreach (array_reverse($theme_keys) as $theme_key) {
if (isset($themes[$theme_key]) && file_exists($themes[$theme_key]->info['screenshot'])) {
$theme->screenshot = array(
'uri' => $themes[$theme_key]->info['screenshot'],
'alt' => $this->t('Screenshot for @theme theme', array('@theme' => $theme->info['name'])),
'title' => $this->t('Screenshot for @theme theme', array('@theme' => $theme->info['name'])),
'attributes' => array('class' => array('screenshot')),
);
break;
}
}
if (empty($theme->status)) {
// Ensure this theme is compatible with this version of core.
$theme->incompatible_core = !isset($theme->info['core']) || ($theme->info['core'] != \DRUPAL::CORE_COMPATIBILITY);
// Require the 'content' region to make sure the main page
// content has a common place in all themes.
$theme->incompatible_region = !isset($theme->info['regions']['content']);
$theme->incompatible_php = version_compare(phpversion(), $theme->info['php']) < 0;
// Confirm that all base themes are available.
$theme->incompatible_base = (isset($theme->info['base theme']) && !($theme->base_themes === array_filter($theme->base_themes)));
// Confirm that the theme engine is available.
$theme->incompatible_engine = isset($theme->info['engine']) && !isset($theme->owner);
}
$theme->operations = array();
if (!empty($theme->status) || !$theme->incompatible_core && !$theme->incompatible_php && !$theme->incompatible_base && !$theme->incompatible_engine) {
// Create the operations links.
$query['theme'] = $theme->getName();
if ($this->themeAccess->checkAccess($theme->getName())) {
$theme->operations[] = array(
'title' => $this->t('Settings'),
'url' => Url::fromRoute('system.theme_settings_theme', ['theme' => $theme->getName()]),
'attributes' => array('title' => $this->t('Settings for @theme theme', array('@theme' => $theme->info['name']))),
);
}
if (!empty($theme->status)) {
if (!$theme->is_default) {
$theme_uninstallable = TRUE;
if ($theme->getName() == $admin_theme) {
$theme_uninstallable = FALSE;
}
// Check it isn't the base of theme of an installed theme.
foreach ($theme->required_by as $themename => $dependency) {
if (!empty($themes[$themename]->status)) {
$theme_uninstallable = FALSE;
}
}
if ($theme_uninstallable) {
$theme->operations[] = array(
'title' => $this->t('Uninstall'),
'url' => Url::fromRoute('system.theme_uninstall'),
'query' => $query,
'attributes' => array('title' => $this->t('Uninstall @theme theme', array('@theme' => $theme->info['name']))),
);
}
$theme->operations[] = array(
'title' => $this->t('Set as default'),
'url' => Url::fromRoute('system.theme_set_default'),
'query' => $query,
'attributes' => array('title' => $this->t('Set @theme as default theme', array('@theme' => $theme->info['name']))),
);
}
$admin_theme_options[$theme->getName()] = $theme->info['name'];
}
else {
$theme->operations[] = array(
'title' => $this->t('Install'),
'url' => Url::fromRoute('system.theme_install'),
'query' => $query,
'attributes' => array('title' => $this->t('Install @theme theme', array('@theme' => $theme->info['name']))),
);
$theme->operations[] = array(
'title' => $this->t('Install and set as default'),
'url' => Url::fromRoute('system.theme_set_default'),
'query' => $query,
'attributes' => array('title' => $this->t('Install @theme as default theme', array('@theme' => $theme->info['name']))),
);
}
}
// Add notes to default and administration theme.
$theme->notes = array();
if ($theme->is_default) {
$theme->notes[] = $this->t('default theme');
}
if ($theme->is_admin) {
$theme->notes[] = $this->t('admin theme');
}
// Sort installed and uninstalled themes into their own groups.
$theme_groups[$theme->status ? 'installed' : 'uninstalled'][] = $theme;
}
// There are two possible theme groups.
$theme_group_titles = array(
'installed' => $this->formatPlural(count($theme_groups['installed']), 'Installed theme', 'Installed themes'),
);
if (!empty($theme_groups['uninstalled'])) {
$theme_group_titles['uninstalled'] = $this->formatPlural(count($theme_groups['uninstalled']), 'Uninstalled theme', 'Uninstalled themes');
}
uasort($theme_groups['installed'], 'system_sort_themes');
$this->moduleHandler()->alter('system_themes_page', $theme_groups);
$build = array();
$build[] = array(
'#theme' => 'system_themes_page',
'#theme_groups' => $theme_groups,
'#theme_group_titles' => $theme_group_titles,
);
$build[] = $this->formBuilder->getForm('Drupal\system\Form\ThemeAdminForm', $admin_theme_options);
return $build;
}
}
@@ -0,0 +1,71 @@
<?php
namespace Drupal\system\Controller;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\system\SystemManager;
/**
* Returns responses for System Info routes.
*/
class SystemInfoController implements ContainerInjectionInterface {
/**
* System Manager Service.
*
* @var \Drupal\system\SystemManager
*/
protected $systemManager;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('system.manager')
);
}
/**
* Constructs a SystemInfoController object.
*
* @param \Drupal\system\SystemManager $systemManager
* System manager service.
*/
public function __construct(SystemManager $systemManager) {
$this->systemManager = $systemManager;
}
/**
* Displays the site status report.
*
* @return array
* A render array containing a list of system requirements for the Drupal
* installation and whether this installation meets the requirements.
*/
public function status() {
$requirements = $this->systemManager->listRequirements();
return array('#theme' => 'status_report', '#requirements' => $requirements);
}
/**
* Returns the contents of phpinfo().
*
* @return \Symfony\Component\HttpFoundation\Response
* A response object to be sent to the client.
*/
public function php() {
if (function_exists('phpinfo')) {
ob_start();
phpinfo();
$output = ob_get_clean();
}
else {
$output = t('The phpinfo() function has been disabled for security reasons. For more information, visit <a href=":phpinfo">Enabling and disabling phpinfo()</a> handbook page.', array(':phpinfo' => 'https://www.drupal.org/node/243993'));
}
return new Response($output);
}
}
@@ -0,0 +1,193 @@
<?php
namespace Drupal\system\Controller;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\PreExistingConfigException;
use Drupal\Core\Config\UnmetDependenciesException;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* Controller for theme handling.
*/
class ThemeController extends ControllerBase {
/**
* The theme handler service.
*
* @var \Drupal\Core\Extension\ThemeHandlerInterface
*/
protected $themeHandler;
/**
* Constructs a new ThemeController.
*
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
* The theme handler.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
*/
public function __construct(ThemeHandlerInterface $theme_handler, ConfigFactoryInterface $config_factory) {
$this->themeHandler = $theme_handler;
$this->configFactory = $config_factory;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('theme_handler'),
$container->get('config.factory')
);
}
/**
* Uninstalls a theme.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* A request object containing a theme name and a valid token.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* Redirects back to the appearance admin page.
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
* Throws access denied when no theme or token is set in the request or when
* the token is invalid.
*/
public function uninstall(Request $request) {
$theme = $request->get('theme');
$config = $this->config('system.theme');
if (isset($theme)) {
// Get current list of themes.
$themes = $this->themeHandler->listInfo();
// Check if the specified theme is one recognized by the system.
if (!empty($themes[$theme])) {
// Do not uninstall the default or admin theme.
if ($theme === $config->get('default') || $theme === $config->get('admin')) {
drupal_set_message($this->t('%theme is the default theme and cannot be uninstalled.', array('%theme' => $themes[$theme]->info['name'])), 'error');
}
else {
$this->themeHandler->uninstall(array($theme));
drupal_set_message($this->t('The %theme theme has been uninstalled.', array('%theme' => $themes[$theme]->info['name'])));
}
}
else {
drupal_set_message($this->t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
}
return $this->redirect('system.themes_page');
}
throw new AccessDeniedHttpException();
}
/**
* Installs a theme.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* A request object containing a theme name and a valid token.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* Redirects back to the appearance admin page.
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
* Throws access denied when no theme or token is set in the request or when
* the token is invalid.
*/
public function install(Request $request) {
$theme = $request->get('theme');
if (isset($theme)) {
try {
if ($this->themeHandler->install(array($theme))) {
$themes = $this->themeHandler->listInfo();
drupal_set_message($this->t('The %theme theme has been installed.', array('%theme' => $themes[$theme]->info['name'])));
}
else {
drupal_set_message($this->t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
}
}
catch (PreExistingConfigException $e) {
$config_objects = $e->flattenConfigObjects($e->getConfigObjects());
drupal_set_message(
$this->formatPlural(
count($config_objects),
'Unable to install @extension, %config_names already exists in active configuration.',
'Unable to install @extension, %config_names already exist in active configuration.',
array(
'%config_names' => implode(', ', $config_objects),
'@extension' => $theme,
)),
'error'
);
}
catch (UnmetDependenciesException $e) {
drupal_set_message($e->getTranslatedMessage($this->getStringTranslation(), $theme), 'error');
}
return $this->redirect('system.themes_page');
}
throw new AccessDeniedHttpException();
}
/**
* Set the default theme.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* A request object containing a theme name.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* Redirects back to the appearance admin page.
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
* Throws access denied when no theme is set in the request.
*/
public function setDefaultTheme(Request $request) {
$config = $this->configFactory->getEditable('system.theme');
$theme = $request->query->get('theme');
if (isset($theme)) {
// Get current list of themes.
$themes = $this->themeHandler->listInfo();
// Check if the specified theme is one recognized by the system.
// Or try to install the theme.
if (isset($themes[$theme]) || $this->themeHandler->install(array($theme))) {
$themes = $this->themeHandler->listInfo();
// Set the default theme.
$config->set('default', $theme)->save();
// The status message depends on whether an admin theme is currently in
// use: a value of 0 means the admin theme is set to be the default
// theme.
$admin_theme = $config->get('admin');
if ($admin_theme != 0 && $admin_theme != $theme) {
drupal_set_message($this->t('Please note that the administration theme is still set to the %admin_theme theme; consequently, the theme on this page remains unchanged. All non-administrative sections of the site, however, will show the selected %selected_theme theme by default.', array(
'%admin_theme' => $themes[$admin_theme]->info['name'],
'%selected_theme' => $themes[$theme]->info['name'],
)));
}
else {
drupal_set_message($this->t('%theme is now the default theme.', array('%theme' => $themes[$theme]->info['name'])));
}
}
else {
drupal_set_message($this->t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
}
return $this->redirect('system.themes_page');
}
throw new AccessDeniedHttpException();
}
}
@@ -0,0 +1,38 @@
<?php
namespace Drupal\system\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* Provides a callback for finding out a timezone name.
*/
class TimezoneController {
/**
* Retrieve a JSON object containing a time zone name given a timezone
* abbreviation.
*
* @param string $abbreviation
* Time zone abbreviation.
* @param int $offset
* Offset from GMT in seconds. Defaults to -1 which means that first found
* time zone corresponding to abbr is returned. Otherwise exact offset is
* searched and only if not found then the first time zone with any offset
* is returned.
* @param null|bool $is_daylight_saving_time
* Daylight saving time indicator. If abbr does not exist then the time
* zone is searched solely by offset and isdst.
*
* @return JsonResponse
* The timezone name in JsonResponse object.
*/
public function getTimezone($abbreviation = '', $offset = -1, $is_daylight_saving_time = NULL) {
// An abbreviation of "0" passed in the callback arguments should be
// interpreted as the empty string.
$abbreviation = $abbreviation ? $abbreviation : '';
$timezone = timezone_name_from_abbr($abbreviation, intval($offset), $is_daylight_saving_time);
return new JsonResponse($timezone);
}
}
@@ -0,0 +1,70 @@
<?php
namespace Drupal\system;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\CronInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;
/**
* Controller for Cron handling.
*/
class CronController extends ControllerBase {
/**
* The cron service.
*
* @var \Drupal\Core\CronInterface
*/
protected $cron;
/**
* Constructs a CronController object.
*
* @param \Drupal\Core\CronInterface $cron
* The cron service.
*/
public function __construct(CronInterface $cron) {
$this->cron = $cron;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container->get('cron'));
}
/**
* Run Cron once.
*
* @return \Symfony\Component\HttpFoundation\Response
* A Symfony response object.
*/
public function run() {
$this->cron->run();
// HTTP 204 is "No content", meaning "I did what you asked and we're done."
return new Response('', 204);
}
/**
* Run cron manually.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* A Symfony direct response object.
*/
public function runManually() {
if ($this->cron->run()) {
drupal_set_message($this->t('Cron ran successfully.'));
}
else {
drupal_set_message($this->t('Cron run failed.'), 'error');
}
return $this->redirect('system.status');
}
}
@@ -0,0 +1,38 @@
<?php
namespace Drupal\system;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Defines the access control handler for the date format entity type.
*
* @see \Drupal\system\Entity\DateFormat
*/
class DateFormatAccessControlHandler extends EntityAccessControlHandler {
/**
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
// There are no restrictions on viewing a date format.
if ($operation == 'view') {
return AccessResult::allowed();
}
// Locked date formats cannot be updated or deleted.
elseif (in_array($operation, array('update', 'delete'))) {
if ($entity->isLocked()) {
return AccessResult::forbidden()->addCacheableDependency($entity);
}
else {
return parent::checkAccess($entity, $operation, $account)->addCacheableDependency($entity);
}
}
return parent::checkAccess($entity, $operation, $account);
}
}
@@ -0,0 +1,71 @@
<?php
namespace Drupal\system;
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a class to build a listing of date format entities.
*
* @see \Drupal\system\Entity\DateFormat
*/
class DateFormatListBuilder extends ConfigEntityListBuilder {
/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatterInterface
*/
protected $dateFormatter;
/**
* Constructs a new DateFormatListBuilder object.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition.
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
* The entity storage class.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service.
*/
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, DateFormatterInterface $date_formatter) {
parent::__construct($entity_type, $storage);
$this->dateFormatter = $date_formatter;
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
return new static(
$entity_type,
$container->get('entity.manager')->getStorage($entity_type->id()),
$container->get('date.formatter')
);
}
/**
* {@inheritdoc}
*/
public function buildHeader() {
$header['label'] = t('Name');
$header['pattern'] = t('Pattern');
return $header + parent::buildHeader();
}
/**
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
$row['label'] = $entity->label();
$row['pattern'] = $this->dateFormatter->format(REQUEST_TIME, $entity->id());
return $row + parent::buildRow($entity);
}
}
+153
View File
@@ -0,0 +1,153 @@
<?php
namespace Drupal\system\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
use Drupal\system\ActionConfigEntityInterface;
use Drupal\Core\Action\ActionPluginCollection;
use Drupal\Component\Plugin\ConfigurablePluginInterface;
/**
* Defines the configured action entity.
*
* @ConfigEntityType(
* id = "action",
* label = @Translation("Action"),
* admin_permission = "administer actions",
* entity_keys = {
* "id" = "id",
* "label" = "label"
* },
* config_export = {
* "id",
* "label",
* "type",
* "plugin",
* "configuration",
* }
* )
*/
class Action extends ConfigEntityBase implements ActionConfigEntityInterface, EntityWithPluginCollectionInterface {
/**
* The name (plugin ID) of the action.
*
* @var string
*/
protected $id;
/**
* The label of the action.
*
* @var string
*/
protected $label;
/**
* The action type.
*
* @var string
*/
protected $type;
/**
* The configuration of the action.
*
* @var array
*/
protected $configuration = array();
/**
* The plugin ID of the action.
*
* @var string
*/
protected $plugin;
/**
* The plugin collection that stores action plugins.
*
* @var \Drupal\Core\Action\ActionPluginCollection
*/
protected $pluginCollection;
/**
* Encapsulates the creation of the action's LazyPluginCollection.
*
* @return \Drupal\Component\Plugin\LazyPluginCollection
* The action's plugin collection.
*/
protected function getPluginCollection() {
if (!$this->pluginCollection) {
$this->pluginCollection = new ActionPluginCollection(\Drupal::service('plugin.manager.action'), $this->plugin, $this->configuration);
}
return $this->pluginCollection;
}
/**
* {@inheritdoc}
*/
public function getPluginCollections() {
return array('configuration' => $this->getPluginCollection());
}
/**
* {@inheritdoc}
*/
public function getPlugin() {
return $this->getPluginCollection()->get($this->plugin);
}
/**
* {@inheritdoc}
*/
public function setPlugin($plugin_id) {
$this->plugin = $plugin_id;
$this->getPluginCollection()->addInstanceId($plugin_id);
}
/**
* {@inheritdoc}
*/
public function getPluginDefinition() {
return $this->getPlugin()->getPluginDefinition();
}
/**
* {@inheritdoc}
*/
public function execute(array $entities) {
return $this->getPlugin()->executeMultiple($entities);
}
/**
* {@inheritdoc}
*/
public function isConfigurable() {
return $this->getPlugin() instanceof ConfigurablePluginInterface;
}
/**
* {@inheritdoc}
*/
public function getType() {
return $this->type;
}
/**
* {@inheritdoc}
*/
public static function sort(ConfigEntityInterface $a, ConfigEntityInterface $b) {
/** @var \Drupal\system\ActionConfigEntityInterface $a */
/** @var \Drupal\system\ActionConfigEntityInterface $b */
$a_type = $a->getType();
$b_type = $b->getType();
if ($a_type != $b_type) {
return strnatcasecmp($a_type, $b_type);
}
return parent::sort($a, $b);
}
}
+74
View File
@@ -0,0 +1,74 @@
<?php
namespace Drupal\system\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\system\MenuInterface;
/**
* Defines the Menu configuration entity class.
*
* @ConfigEntityType(
* id = "menu",
* label = @Translation("Menu"),
* handlers = {
* "access" = "Drupal\system\MenuAccessControlHandler"
* },
* admin_permission = "administer menu",
* entity_keys = {
* "id" = "id",
* "label" = "label"
* },
* config_export = {
* "id",
* "label",
* "description",
* "locked",
* }
* )
*/
class Menu extends ConfigEntityBase implements MenuInterface {
/**
* The menu machine name.
*
* @var string
*/
protected $id;
/**
* The human-readable name of the menu entity.
*
* @var string
*/
protected $label;
/**
* The menu description.
*
* @var string
*/
protected $description;
/**
* The locked status of this menu.
*
* @var bool
*/
protected $locked = FALSE;
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->description;
}
/**
* {@inheritdoc}
*/
public function isLocked() {
return (bool) $this->locked;
}
}
@@ -0,0 +1,39 @@
<?php
namespace Drupal\system\EventSubscriber;
use Drupal\Core\Routing\RouteSubscriberBase;
use Drupal\Core\Routing\RoutingEvents;
use Symfony\Component\Routing\RouteCollection;
/**
* Adds the _admin_route option to each admin route.
*/
class AdminRouteSubscriber extends RouteSubscriberBase {
/**
* {@inheritdoc}
*/
protected function alterRoutes(RouteCollection $collection) {
foreach ($collection->all() as $route) {
if (strpos($route->getPath(), '/admin') === 0 && !$route->hasOption('_admin_route')) {
$route->setOption('_admin_route', TRUE);
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events = parent::getSubscribedEvents();
// Use a lower priority than \Drupal\field_ui\Routing\RouteSubscriber or
// \Drupal\views\EventSubscriber\RouteSubscriber to ensure we add the option
// to their routes.
$events[RoutingEvents::ALTER] = array('onAlterRoutes', -200);
return $events;
}
}
@@ -0,0 +1,80 @@
<?php
namespace Drupal\system\EventSubscriber;
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
use Drupal\Core\Config\ConfigCrudEvent;
use Drupal\Core\Config\ConfigEvents;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* A subscriber invalidating cache tags when system config objects are saved.
*/
class ConfigCacheTag implements EventSubscriberInterface {
/**
* The theme handler.
*
* @var \Drupal\Core\Extension\ThemeHandlerInterface
*/
protected $themeHandler;
/**
* The cache tags invalidator.
*
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
*/
protected $cacheTagsInvalidator;
/**
* Constructs a ConfigCacheTag object.
*
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
* The theme handler.
* @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cache_tags_invalidator
* The cache tags invalidator.
*/
public function __construct(ThemeHandlerInterface $theme_handler, CacheTagsInvalidatorInterface $cache_tags_invalidator) {
$this->themeHandler = $theme_handler;
$this->cacheTagsInvalidator = $cache_tags_invalidator;
}
/**
* Invalidate cache tags when particular system config objects are saved.
*
* @param \Drupal\Core\Config\ConfigCrudEvent $event
* The Event to process.
*/
public function onSave(ConfigCrudEvent $event) {
// Changing the site settings may mean a different route is selected for the
// front page. Additionally a change to the site name or similar must
// invalidate the render cache since this could be used anywhere.
if ($event->getConfig()->getName() === 'system.site') {
$this->cacheTagsInvalidator->invalidateTags(['route_match', 'rendered']);
}
// Theme configuration and global theme settings.
if (in_array($event->getConfig()->getName(), ['system.theme', 'system.theme.global'], TRUE)) {
$this->cacheTagsInvalidator->invalidateTags(['rendered']);
}
// Theme-specific settings, check if this matches a theme settings
// configuration object, in that case, clear the rendered cache tag.
foreach (array_keys($this->themeHandler->listInfo()) as $theme_name) {
if ($theme_name == $event->getConfig()->getName()) {
$this->cacheTagsInvalidator->invalidateTags(['rendered']);
break;
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[ConfigEvents::SAVE][] = ['onSave'];
return $events;
}
}
@@ -0,0 +1,70 @@
<?php
namespace Drupal\system;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
/**
* System file controller.
*/
class FileDownloadController extends ControllerBase {
/**
* Handles private file transfers.
*
* Call modules that implement hook_file_download() to find out if a file is
* accessible and what headers it should be transferred with. If one or more
* modules returned headers the download will start with the returned headers.
* If a module returns -1 an AccessDeniedHttpException will be thrown. If the
* file exists but no modules responded an AccessDeniedHttpException will be
* thrown. If the file does not exist a NotFoundHttpException will be thrown.
*
* @see hook_file_download()
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
* @param string $scheme
* The file scheme, defaults to 'private'.
*
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
* The transferred file as response.
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
* Thrown when the requested file does not exist.
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
* Thrown when the user does not have access to the file.
*/
public function download(Request $request, $scheme = 'private') {
$target = $request->query->get('file');
// Merge remaining path arguments into relative file path.
$uri = $scheme . '://' . $target;
if (file_stream_wrapper_valid_scheme($scheme) && file_exists($uri)) {
// Let other modules provide headers and controls access to the file.
$headers = $this->moduleHandler()->invokeAll('file_download', array($uri));
foreach ($headers as $result) {
if ($result == -1) {
throw new AccessDeniedHttpException();
}
}
if (count($headers)) {
// \Drupal\Core\EventSubscriber\FinishResponseSubscriber::onRespond()
// sets response as not cacheable if the Cache-Control header is not
// already modified. We pass in FALSE for non-private schemes for the
// $public parameter to make sure we don't change the headers.
return new BinaryFileResponse($uri, 200, $headers, $scheme !== 'private');
}
throw new AccessDeniedHttpException();
}
throw new NotFoundHttpException();
}
}
+132
View File
@@ -0,0 +1,132 @@
<?php
namespace Drupal\system\Form;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\CronInterface;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\State\StateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Configure cron settings for this site.
*/
class CronForm extends FormBase {
/**
* Stores the state storage service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* The cron service.
*
* @var \Drupal\Core\CronInterface
*/
protected $cron;
/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatterInterface
*/
protected $dateFormatter;
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
*/
protected $moduleHandler;
/**
* Constructs a CronForm object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The factory for configuration objects.
* @param \Drupal\Core\State\StateInterface $state
* The state key value store.
* @param \Drupal\Core\CronInterface $cron
* The cron service.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler service.
*/
public function __construct(ConfigFactoryInterface $config_factory, StateInterface $state, CronInterface $cron, DateFormatterInterface $date_formatter, ModuleHandlerInterface $module_handler) {
$this->state = $state;
$this->cron = $cron;
$this->dateFormatter = $date_formatter;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('state'),
$container->get('cron'),
$container->get('date.formatter'),
$container->get('module_handler')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'system_cron_settings';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['description'] = array(
'#markup' => '<p>' . t('Cron takes care of running periodic tasks like checking for updates and indexing content for search.') . '</p>',
);
$form['run'] = array(
'#type' => 'submit',
'#value' => t('Run cron'),
);
$status = '<p>' . $this->t('Last run: %time ago.', array('%time' => $this->dateFormatter->formatTimeDiffSince($this->state->get('system.cron_last')))) . '</p>';
$form['status'] = array(
'#markup' => $status,
);
$cron_url = $this->url('system.cron', array('key' => $this->state->get('system.cron_key')), array('absolute' => TRUE));
$form['cron_url'] = array(
'#markup' => '<p>' . t('To run cron from outside the site, go to <a href=":cron">@cron</a>', array(':cron' => $cron_url, '@cron' => $cron_url)) . '</p>',
);
if (!$this->moduleHandler->moduleExists('automated_cron')) {
$form['cron'] = array(
'#markup' => $this->t('Enable the <em>Automated Cron</em> module to allow cron execution at the end of a server response.'),
);
}
return $form;
}
/**
* Runs cron and reloads the page.
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Run cron manually from Cron form.
if ($this->cron->run()) {
drupal_set_message(t('Cron ran successfully.'));
}
else {
drupal_set_message(t('Cron run failed.'), 'error');
}
}
}
@@ -0,0 +1,21 @@
<?php
namespace Drupal\system\Form;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides a form for adding a date format.
*/
class DateFormatAddForm extends DateFormatFormBase {
/**
* {@inheritdoc}
*/
protected function actions(array $form, FormStateInterface $form_state) {
$actions = parent::actions($form, $form_state);
$actions['submit']['#value'] = t('Add format');
return $actions;
}
}
@@ -0,0 +1,50 @@
<?php
namespace Drupal\system\Form;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\EntityDeleteForm;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Builds a form to delete a date format.
*/
class DateFormatDeleteForm extends EntityDeleteForm {
/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatterInterface
*/
protected $dateFormatter;
/**
* Constructs an DateFormatDeleteForm object.
*
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service.
*/
public function __construct(DateFormatterInterface $date_formatter) {
$this->dateFormatter = $date_formatter;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('date.formatter')
);
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return t('Are you sure you want to delete the format %name : %format?', array(
'%name' => $this->entity->label(),
'%format' => $this->dateFormatter->format(REQUEST_TIME, $this->entity->id()))
);
}
}
@@ -0,0 +1,35 @@
<?php
namespace Drupal\system\Form;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides a form for editing a date format.
*/
class DateFormatEditForm extends DateFormatFormBase {
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state);
$now = t('Displayed as %date', array('%date' => $this->dateFormatter->format(REQUEST_TIME, $this->entity->id())));
$form['date_format_pattern']['#field_suffix'] = ' <small data-drupal-date-formatter="preview">' . $now . '</small>';
$form['date_format_pattern']['#default_value'] = $this->entity->getPattern();
return $form;
}
/**
* {@inheritdoc}
*/
protected function actions(array $form, FormStateInterface $form_state) {
$actions = parent::actions($form, $form_state);
$actions['submit']['#value'] = t('Save format');
unset($actions['delete']);
return $actions;
}
}
@@ -0,0 +1,160 @@
<?php
namespace Drupal\system\Form;
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityForm;
/**
* Provides a base form for date formats.
*/
abstract class DateFormatFormBase extends EntityForm {
/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatterInterface
*/
protected $dateFormatter;
/**
* The date format storage.
*
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
*/
protected $dateFormatStorage;
/**
* Constructs a new date format form.
*
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date service.
* @param \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $date_format_storage
* The date format storage.
*/
public function __construct(DateFormatterInterface $date_formatter, ConfigEntityStorageInterface $date_format_storage) {
$date = new DrupalDateTime();
$this->dateFormatter = $date_formatter;
$this->dateFormatStorage = $date_format_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('date.formatter'),
$container->get('entity.manager')->getStorage('date_format')
);
}
/**
* Checks for an existing date format.
*
* @param string|int $entity_id
* The entity ID.
* @param array $element
* The form element.
*
* @return bool
* TRUE if this format already exists, FALSE otherwise.
*/
public function exists($entity_id, array $element) {
return (bool) $this->dateFormatStorage
->getQuery()
->condition('id', $element['#field_prefix'] . $entity_id)
->execute();
}
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$form['label'] = array(
'#type' => 'textfield',
'#title' => 'Name',
'#maxlength' => 100,
'#description' => t('Name of the date format'),
'#default_value' => $this->entity->label(),
);
$form['id'] = array(
'#type' => 'machine_name',
'#description' => t('A unique machine-readable name. Can only contain lowercase letters, numbers, and underscores.'),
'#disabled' => !$this->entity->isNew(),
'#default_value' => $this->entity->id(),
'#machine_name' => array(
'exists' => array($this, 'exists'),
'replace_pattern' => '([^a-z0-9_]+)|(^custom$)',
'error' => $this->t('The machine-readable name must be unique, and can only contain lowercase letters, numbers, and underscores. Additionally, it can not be the reserved word "custom".'),
),
);
$form['date_format_pattern'] = array(
'#type' => 'textfield',
'#title' => t('Format string'),
'#maxlength' => 100,
'#description' => $this->t('A user-defined date format. See the <a href="http://php.net/manual/function.date.php">PHP manual</a> for available options.'),
'#required' => TRUE,
'#attributes' => [
'data-drupal-date-formatter' => 'source',
],
'#field_suffix' => ' <small class="js-hide" data-drupal-date-formatter="preview">' . $this->t('Displayed as %date_format', ['%date_format' => '']) . '</small>',
);
$form['langcode'] = array(
'#type' => 'language_select',
'#title' => t('Language'),
'#languages' => LanguageInterface::STATE_ALL,
'#default_value' => $this->entity->language()->getId(),
);
$form['#attached']['drupalSettings']['dateFormats'] = $this->dateFormatter->getSampleDateFormats();
$form['#attached']['library'][] = 'system/drupal.system.date';
return parent::form($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
// The machine name field should already check to see if the requested
// machine name is available.
$pattern = trim($form_state->getValue('date_format_pattern'));
foreach ($this->dateFormatStorage->loadMultiple() as $format) {
if ($format->getPattern() == $pattern && ($format->id() == $this->entity->id())) {
drupal_set_message(t('The existing format/name combination has not been altered.'));
continue;
}
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$form_state->setValue('pattern', trim($form_state->getValue('date_format_pattern')));
parent::submitForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$status = $this->entity->save();
if ($status == SAVED_UPDATED) {
drupal_set_message(t('Custom date format updated.'));
}
else {
drupal_set_message(t('Custom date format added.'));
}
$form_state->setRedirectUrl($this->entity->urlInfo('collection'));
}
}
@@ -0,0 +1,153 @@
<?php
namespace Drupal\system\Form;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StreamWrapper\PrivateStream;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Configure file system settings for this site.
*/
class FileSystemForm extends ConfigFormBase {
/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatterInterface
*/
protected $dateFormatter;
/**
* The stream wrapper manager.
*
* @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface
*/
protected $streamWrapperManager;
/**
* Constructs a FileSystemForm object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The factory for configuration objects.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service.
* @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager
* The stream wrapper manager.
*/
public function __construct(ConfigFactoryInterface $config_factory, DateFormatterInterface $date_formatter, StreamWrapperManagerInterface $stream_wrapper_manager) {
parent::__construct($config_factory);
$this->dateFormatter = $date_formatter;
$this->streamWrapperManager = $stream_wrapper_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static (
$container->get('config.factory'),
$container->get('date.formatter'),
$container->get('stream_wrapper_manager')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'system_file_system_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return ['system.file'];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('system.file');
$form['file_public_path'] = array(
'#type' => 'item',
'#title' => t('Public file system path'),
'#markup' => PublicStream::basePath(),
'#description' => t('A local file system path where public files will be stored. This directory must exist and be writable by Drupal. This directory must be relative to the Drupal installation directory and be accessible over the web. This must be changed in settings.php'),
);
$form['file_public_base_url'] = array(
'#type' => 'item',
'#title' => t('Public file base URL'),
'#markup' => PublicStream::baseUrl(),
'#description' => t('The base URL that will be used for public file URLs. This can be changed in settings.php'),
);
$form['file_private_path'] = array(
'#type' => 'item',
'#title' => t('Private file system path'),
'#markup' => (PrivateStream::basePath() ? PrivateStream::basePath() : t('Not set')),
'#description' => t('An existing local file system path for storing private files. It should be writable by Drupal and not accessible over the web. This must be changed in settings.php'),
);
$form['file_temporary_path'] = array(
'#type' => 'textfield',
'#title' => t('Temporary directory'),
'#default_value' => $config->get('path.temporary'),
'#maxlength' => 255,
'#description' => t('A local file system path where temporary files will be stored. This directory should not be accessible over the web.'),
'#after_build' => array('system_check_directory'),
);
// Any visible, writeable wrapper can potentially be used for the files
// directory, including a remote file system that integrates with a CDN.
$options = $this->streamWrapperManager->getDescriptions(StreamWrapperInterface::WRITE_VISIBLE);
if (!empty($options)) {
$form['file_default_scheme'] = array(
'#type' => 'radios',
'#title' => t('Default download method'),
'#default_value' => $config->get('default_scheme'),
'#options' => $options,
'#description' => t('This setting is used as the preferred download method. The use of public files is more efficient, but does not provide any access control.'),
);
}
$intervals = array(0, 21600, 43200, 86400, 604800, 2419200, 7776000);
$period = array_combine($intervals, array_map(array($this->dateFormatter, 'formatInterval'), $intervals));
$period[0] = t('Never');
$form['temporary_maximum_age'] = array(
'#type' => 'select',
'#title' => t('Delete orphaned files after'),
'#default_value' => $config->get('temporary_maximum_age'),
'#options' => $period,
'#description' => t('Orphaned files are not referenced from any content but remain in the file system and may appear in administrative listings. <strong>Warning:</strong> If enabled, orphaned files will be permanently deleted and may not be recoverable.'),
);
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$config = $this->config('system.file')
->set('path.temporary', $form_state->getValue('file_temporary_path'))
->set('temporary_maximum_age', $form_state->getValue('temporary_maximum_age'));
if ($form_state->hasValue('file_default_scheme')) {
$config->set('default_scheme', $form_state->getValue('file_default_scheme'));
}
$config->save();
parent::submitForm($form, $form_state);
}
}
@@ -0,0 +1,126 @@
<?php
namespace Drupal\system\Form;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\ImageToolkit\ImageToolkitManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Configures image toolkit settings for this site.
*/
class ImageToolkitForm extends ConfigFormBase {
/**
* An array containing currently available toolkits.
*
* @var \Drupal\Core\ImageToolkit\ImageToolkitInterface[]
*/
protected $availableToolkits = array();
/**
* Constructs a ImageToolkitForm object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The factory for configuration objects.
* @param \Drupal\Core\ImageToolkit\ImageToolkitManager $manager
* The image toolkit plugin manager.
*/
public function __construct(ConfigFactoryInterface $config_factory, ImageToolkitManager $manager) {
parent::__construct($config_factory);
foreach ($manager->getAvailableToolkits() as $id => $definition) {
$this->availableToolkits[$id] = $manager->createInstance($id);
}
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('image.toolkit.manager')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'system_image_toolkit_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return ['system.image'];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$current_toolkit = $this->config('system.image')->get('toolkit');
$form['image_toolkit'] = array(
'#type' => 'radios',
'#title' => $this->t('Select an image processing toolkit'),
'#default_value' => $current_toolkit,
'#options' => array(),
);
// If we have more than one image toolkit, allow the user to select the one
// to use, and load each of the toolkits' settings form.
foreach ($this->availableToolkits as $id => $toolkit) {
$definition = $toolkit->getPluginDefinition();
$form['image_toolkit']['#options'][$id] = $definition['title'];
$form['image_toolkit_settings'][$id] = array(
'#type' => 'details',
'#title' => $this->t('@toolkit settings', array('@toolkit' => $definition['title'])),
'#open' => TRUE,
'#tree' => TRUE,
'#states' => array(
'visible' => array(
':radio[name="image_toolkit"]' => array('value' => $id),
),
),
);
$form['image_toolkit_settings'][$id] += $toolkit->buildConfigurationForm(array(), $form_state);
}
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
// Call the form validation handler for each of the toolkits.
foreach ($this->availableToolkits as $toolkit) {
$toolkit->validateConfigurationForm($form, $form_state);
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->config('system.image')
->set('toolkit', $form_state->getValue('image_toolkit'))
->save();
// Call the form submit handler for each of the toolkits.
foreach ($this->availableToolkits as $toolkit) {
$toolkit->submitConfigurationForm($form, $form_state);
}
parent::submitForm($form, $form_state);
}
}
@@ -0,0 +1,59 @@
<?php
namespace Drupal\system\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Configure logging settings for this site.
*/
class LoggingForm extends ConfigFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'system_logging_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return ['system.logging'];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('system.logging');
$form['error_level'] = array(
'#type' => 'radios',
'#title' => t('Error messages to display'),
'#default_value' => $config->get('error_level'),
'#options' => array(
ERROR_REPORTING_HIDE => t('None'),
ERROR_REPORTING_DISPLAY_SOME => t('Errors and warnings'),
ERROR_REPORTING_DISPLAY_ALL => t('All messages'),
ERROR_REPORTING_DISPLAY_VERBOSE => t('All messages, with backtrace information'),
),
'#description' => t('It is recommended that sites running on production environments do not display any errors.'),
);
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->config('system.logging')
->set('error_level', $form_state->getValue('error_level'))
->save();
parent::submitForm($form, $form_state);
}
}
@@ -0,0 +1,206 @@
<?php
namespace Drupal\system\Form;
use Drupal\Core\Config\PreExistingConfigException;
use Drupal\Core\Config\UnmetDependenciesException;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Extension\ModuleInstallerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Builds a confirmation form for enabling modules with dependencies.
*/
class ModulesListConfirmForm extends ConfirmFormBase {
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The expirable key value store.
*
* @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
*/
protected $keyValueExpirable;
/**
* An associative list of modules to enable or disable.
*
* @var array
*/
protected $modules = array();
/**
* The module installer.
*
* @var \Drupal\Core\Extension\ModuleInstallerInterface
*/
protected $moduleInstaller;
/**
* Constructs a ModulesListConfirmForm object.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Extension\ModuleInstallerInterface $module_installer
* The module installer.
* @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $key_value_expirable
* The key value expirable factory.
*/
public function __construct(ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, KeyValueStoreExpirableInterface $key_value_expirable) {
$this->moduleHandler = $module_handler;
$this->moduleInstaller = $module_installer;
$this->keyValueExpirable = $key_value_expirable;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('module_handler'),
$container->get('module_installer'),
$container->get('keyvalue.expirable')->get('module_list')
);
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Some required modules must be enabled');
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('system.modules_list');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Continue');
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->t('Would you like to continue with the above?');
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'system_modules_confirm_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$account = $this->currentUser()->id();
$this->modules = $this->keyValueExpirable->get($account);
// Redirect to the modules list page if the key value store is empty.
if (!$this->modules) {
return $this->redirect('system.modules_list');
}
$items = $this->buildMessageList();
$form['message'] = array(
'#theme' => 'item_list',
'#items' => $items,
);
return parent::buildForm($form, $form_state);
}
/**
* Builds the message list for the confirmation form.
*
* @return \Drupal\Component\Render\MarkupInterface[]
* Array of markup for the list of messages on the form.
*
* @see \Drupal\system\Form\ModulesListForm::buildModuleList()
*/
protected function buildMessageList() {
$items = [];
if (!empty($this->modules['dependencies'])) {
// Display a list of required modules that have to be installed as well
// but were not manually selected.
foreach ($this->modules['dependencies'] as $module => $dependencies) {
$items[] = $this->formatPlural(count($dependencies), 'You must enable the @required module to install @module.', 'You must enable the @required modules to install @module.', [
'@module' => $this->modules['install'][$module],
// It is safe to implode this because module names are not translated
// markup and so will not be double-escaped.
'@required' => implode(', ', $dependencies),
]);
}
}
return $items;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Remove the key value store entry.
$account = $this->currentUser()->id();
$this->keyValueExpirable->delete($account);
if (!empty($this->modules['install'])) {
// Don't catch the exception that this can throw for missing dependencies:
// the form doesn't allow modules with unmet dependencies, so the only way
// this can happen is if the filesystem changed between form display and
// submit, in which case the user has bigger problems.
try {
// Install the given modules.
$this->moduleInstaller->install(array_keys($this->modules['install']));
}
catch (PreExistingConfigException $e) {
$config_objects = $e->flattenConfigObjects($e->getConfigObjects());
drupal_set_message(
$this->formatPlural(
count($config_objects),
'Unable to install @extension, %config_names already exists in active configuration.',
'Unable to install @extension, %config_names already exist in active configuration.',
array(
'%config_names' => implode(', ', $config_objects),
'@extension' => $this->modules['install'][$e->getExtension()]
)),
'error'
);
return;
}
catch (UnmetDependenciesException $e) {
drupal_set_message(
$e->getTranslatedMessage($this->getStringTranslation(), $this->modules['install'][$e->getExtension()]),
'error'
);
return;
}
$module_names = array_values($this->modules['install']);
drupal_set_message($this->formatPlural(count($module_names), 'Module %name has been enabled.', '@count modules have been enabled: %names.', array(
'%name' => $module_names[0],
'%names' => implode(', ', $module_names),
)));
}
$form_state->setRedirectUrl($this->getCancelUrl());
}
}
@@ -0,0 +1,37 @@
<?php
namespace Drupal\system\Form;
/**
* Builds a confirmation form for enabling experimental modules.
*/
class ModulesListExperimentalConfirmForm extends ModulesListConfirmForm {
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you wish to enable experimental modules?');
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'system_modules_experimental_confirm_form';
}
/**
* {@inheritdoc}
*/
protected function buildMessageList() {
drupal_set_message($this->t('<a href=":url">Experimental modules</a> are provided for testing purposes only. Use at your own risk.', [':url' => 'https://www.drupal.org/core/experimental']), 'warning');
$items = parent::buildMessageList();
// Add the list of experimental modules after any other messages.
$items[] = $this->t('The following modules are experimental: @modules', ['@modules' => implode(', ', array_values($this->modules['experimental']))]);
return $items;
}
}

Some files were not shown because too many files have changed in this diff Show More