Move into nested docroot

This commit is contained in:
Rob Davies 2017-02-13 15:31:17 +00:00
parent 83a0d3a149
commit c8b70abde9
13405 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: fallback
label: 'Fallback date format'
locked: true
pattern: 'D, m/d/Y - H:i'

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: html_date
label: 'HTML Date'
locked: true
pattern: Y-m-d

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: html_datetime
label: 'HTML Datetime'
locked: true
pattern: 'Y-m-d\TH:i:sO'

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: html_month
label: 'HTML Month'
locked: true
pattern: Y-m

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: html_time
label: 'HTML Time'
locked: true
pattern: 'H:i:s'

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: html_week
label: 'HTML Week'
locked: true
pattern: Y-\WW

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: html_year
label: 'HTML Year'
locked: true
pattern: 'Y'

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: html_yearless_date
label: 'HTML Yearless date'
locked: true
pattern: m-d

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: long
label: 'Default long date'
locked: false
pattern: 'l, F j, Y - H:i'

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: medium
label: 'Default medium date'
locked: false
pattern: 'D, m/d/Y - H:i'

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: short
label: 'Default short date'
locked: false
pattern: 'm/d/Y - H:i'

View file

@ -0,0 +1 @@
filetransfer_default:

View file

@ -0,0 +1,3 @@
threshold:
requirements_warning: 172800
requirements_error: 1209600

View file

@ -0,0 +1,9 @@
country:
default: ''
first_day: 0
timezone:
default: ''
user:
configurable: true
warn: false
default: 0

View file

@ -0,0 +1,3 @@
context:
lines_leading: 2
lines_trailing: 2

View file

@ -0,0 +1,5 @@
allow_insecure_uploads: false
default_scheme: 'public'
path:
temporary: ''
temporary_maximum_age: 21600

View file

@ -0,0 +1 @@
jpeg_quality: 75

View file

@ -0,0 +1 @@
toolkit: gd

View file

@ -0,0 +1 @@
error_level: hide

View file

@ -0,0 +1,2 @@
interface:
default: 'php_mail'

View file

@ -0,0 +1,2 @@
message: '@site is currently under maintenance. We should be back shortly. Thank you for your patience.'
langcode: en

View file

@ -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

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: admin
label: Administration
description: 'Administrative task links'
locked: true

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: footer
label: Footer
description: 'Site information links'
locked: true

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: main
label: 'Main navigation'
description: 'Site section links'
locked: true

View file

@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
id: tools
label: Tools
description: 'User tool links, often added by modules'
locked: true

View file

@ -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

View file

@ -0,0 +1,6 @@
channel:
description: ''
items:
limit: 10
view_mode: rss
langcode: en

View file

@ -0,0 +1,12 @@
uuid: ''
name: 'Drupal'
mail: ''
slogan: ''
page:
403: ''
404: ''
front: /user/login
admin_compact_mode: false
weight_select_max: 100
langcode: en
default_langcode: en

View file

@ -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

View file

@ -0,0 +1,2 @@
admin: ''
default: stark

View file

@ -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

View file

@ -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%;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -0,0 +1,13 @@
/**
* @file
* Inline items.
*/
.container-inline div,
.container-inline label {
display: inline-block;
}
/* Details contents always need to be rendered as block. */
.container-inline .details-wrapper {
display: block;
}

View file

@ -0,0 +1,10 @@
/**
* @file
* Collapsible details.
*
* @see collapse.js
*/
.js details:not([open]) .details-wrapper {
display: none;
}

View file

@ -0,0 +1,9 @@
/**
* @file
* Fieldgroup border reset.
*/
.fieldgroup {
border-width: 0;
padding: 0;
}

View file

@ -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;
}

View file

@ -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: "";
}

View file

@ -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;
}

View file

@ -0,0 +1,8 @@
/**
* @file
* Utility class to prevent text wrapping.
*/
.nowrap {
white-space: nowrap;
}

View file

@ -0,0 +1,8 @@
/*
* @file
* Contain positioned elements.
*/
.position-container {
position: relative;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

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;
}

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;
}

View file

@ -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

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);

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);

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));

View file

@ -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

View file

@ -0,0 +1,13 @@
# The menu_settings migration is in the menu_ui module.
id: d6_menu
label: Menus
migration_tags:
- Drupal 6
source:
plugin: menu
process:
id: menu_name
label: title
description: description
destination:
plugin: entity:menu

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,29 @@
id: d7_global_theme_settings
label: D7 global theme settings
migration_tags:
- Drupal 7
source:
plugin: variable
variables:
- theme_settings
process:
'features/logo': theme_settings/toggle_logo
'features/name': theme_settings/toggle_name
'features/slogan': theme_settings/toggle_slogan
'features/node_user_picture': theme_settings/toggle_node_user_picture
'features/comment_user_picture': theme_settings/toggle_comment_user_picture
'features/comment_user_verification': theme_settings/toggle_comment_user_verification
'features/favicon': theme_settings/toggle_favicon
'logo/use_default': theme_settings/default_logo
'logo/path': theme_settings/logo_path
'favicon/use_default': theme_settings/default_favicon
'favicon/path': theme_settings/favicon_path
'favicon/mimetype': theme_settings/favicon_mimetype
# Ignore settings not present in Drupal 8
# theme_settings/logo_upload
# theme_settings/favicon_upload
# theme_settings/toggle_main_menu
# theme_settings/toggle_secondary_menu
destination:
plugin: config
config_name: system.theme.global

View file

@ -0,0 +1,20 @@
id: d7_menu
label: Menus
migration_tags:
- Drupal 7
source:
plugin: menu
process:
id:
plugin: static_map
bypass: true
source: menu_name
map:
main-menu: main
management: admin
navigation: tools
user-menu: account
label: title
description: description
destination:
plugin: entity:menu

View file

@ -0,0 +1,12 @@
id: d7_system_authorize
migration_tags:
- Drupal 7
source:
plugin: variable
variables:
- authorize_filetransfer_default
process:
filetransfer_default: authorize_filetransfer_default
destination:
plugin: config
config_name: system.authorize

View file

@ -0,0 +1,15 @@
id: d7_system_cron
label: Drupal 7 cron settings
migration_tags:
- Drupal 7
source:
plugin: variable
variables:
- cron_threshold_warning
- cron_threshold_error
process:
'threshold/requirements_warning': cron_threshold_warning
'threshold/requirements_error': cron_threshold_error
destination:
plugin: config
config_name: system.cron

View file

@ -0,0 +1,22 @@
id: d7_system_date
migration_tags:
- Drupal 7
source:
plugin: variable
variables:
- site_default_country
- date_first_day
- date_default_timezone
- configurable_timezones
- empty_timezone_message
- user_default_timezone
process:
'country/default': site_default_country
first_day: date_first_day
'timezone/default': date_default_timezone
'timezone/user/configurable': configurable_timezones
'timezone/user/warn': empty_timezone_message
'timezone/user/default': user_default_timezone
destination:
plugin: config
config_name: system.date

View file

@ -0,0 +1,20 @@
id: d7_system_file
label: Drupal 7 file system configuration
migration_tags:
- Drupal 7
source:
plugin: variable
variables:
- allow_insecure_uploads
- file_temporary_path
process:
allow_insecure_uploads:
plugin: static_map
source: allow_insecure_uploads
map:
0: FALSE
1: TRUE
'path/temporary': file_temporary_path
destination:
plugin: config
config_name: system.file

View file

@ -0,0 +1,17 @@
id: d7_system_mail
migration_tags:
- Drupal 7
source:
plugin: variable
variables:
- mail_system
process:
'interface/default':
plugin: static_map
source: 'mail_system/default-system'
map:
DefaultMailSystem: php_mail
MailTestCase: test_mail_collector
destination:
plugin: config
config_name: system.mail

View file

@ -0,0 +1,19 @@
id: d7_system_performance
label: Drupal 7 performance configuration
migration_tags:
- Drupal 7
source:
plugin: variable
variables:
- preprocess_css
- preprocess_js
- cache_lifetime
- 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

View file

@ -0,0 +1,14 @@
id: system_image
label: Image toolkit configuration
migration_tags:
- Drupal 6
- Drupal 7
source:
plugin: variable
variables:
- image_toolkit
process:
toolkit: image_toolkit
destination:
plugin: config
config_name: system.image

View file

@ -0,0 +1,14 @@
id: system_image_gd
label: Image quality configuration
migration_tags:
- Drupal 6
- Drupal 7
source:
plugin: variable
variables:
- image_jpeg_quality
process:
jpeg_quality: image_jpeg_quality
destination:
plugin: config
config_name: system.image.gd

View file

@ -0,0 +1,22 @@
id: system_logging
label: System logging
migration_tags:
- Drupal 6
- Drupal 7
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

View file

@ -0,0 +1,14 @@
id: system_maintenance
label: Maintenance page configuration
migration_tags:
- Drupal 6
- Drupal 7
source:
plugin: variable
variables:
- site_offline_message
process:
message: site_offline_message
destination:
plugin: config
config_name: system.maintenance

View file

@ -0,0 +1,16 @@
id: system_rss
label: RSS configuration
migration_tags:
- Drupal 6
- Drupal 7
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

View file

@ -0,0 +1,42 @@
id: system_site
label: Site configuration
migration_tags:
- Drupal 6
- Drupal 7
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

View file

@ -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);
}
}

View file

@ -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();
}
}
}

View file

@ -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();
}

View file

@ -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;
}
}

View file

@ -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'] : '';
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace Drupal\system\Controller;
use Drupal\Core\Access\CsrfRequestHeaderAccessCheck;
use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;
/**
* Returns responses for CSRF token routes.
*/
class CsrfTokenController implements ContainerInjectionInterface {
/**
* The CSRF token generator.
*
* @var \Drupal\Core\Access\CsrfTokenGenerator
*/
protected $tokenGenerator;
/**
* Constructs a new CsrfTokenController object.
*
* @param \Drupal\Core\Access\CsrfTokenGenerator $token_generator
* The CSRF token generator.
*/
public function __construct(CsrfTokenGenerator $token_generator) {
$this->tokenGenerator = $token_generator;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('csrf_token')
);
}
/**
* Returns a CSRF protecting session token.
*
* @return \Symfony\Component\HttpFoundation\Response
* The response object.
*/
public function csrfToken() {
return new Response($this->tokenGenerator->get(CsrfRequestHeaderAccessCheck::TOKEN_KEY), 200, ['Content-Type' => 'text/plain']);
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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.'),
];
}
}

View file

@ -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 $menu_link_tree
* 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('administration 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;
}
}

View file

@ -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);
}
}

View file

@ -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->query->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->query->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();
}
}

View file

@ -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);
}
}

View file

@ -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');
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

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);
}
}

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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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();
}
}

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');
}
}
}

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