caore d8 files
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: fallback
|
||||
label: 'Fallback date format'
|
||||
locked: true
|
||||
pattern: 'D, m/d/Y - H:i'
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: html_date
|
||||
label: 'HTML Date'
|
||||
locked: true
|
||||
pattern: Y-m-d
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: html_datetime
|
||||
label: 'HTML Datetime'
|
||||
locked: true
|
||||
pattern: 'Y-m-d\TH:i:sO'
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: html_month
|
||||
label: 'HTML Month'
|
||||
locked: true
|
||||
pattern: Y-m
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: html_time
|
||||
label: 'HTML Time'
|
||||
locked: true
|
||||
pattern: 'H:i:s'
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: html_week
|
||||
label: 'HTML Week'
|
||||
locked: true
|
||||
pattern: Y-\WW
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: html_year
|
||||
label: 'HTML Year'
|
||||
locked: true
|
||||
pattern: 'Y'
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: html_yearless_date
|
||||
label: 'HTML Yearless date'
|
||||
locked: true
|
||||
pattern: m-d
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: long
|
||||
label: 'Default long date'
|
||||
locked: false
|
||||
pattern: 'l, F j, Y - H:i'
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: medium
|
||||
label: 'Default medium date'
|
||||
locked: false
|
||||
pattern: 'D, m/d/Y - H:i'
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: short
|
||||
label: 'Default short date'
|
||||
locked: false
|
||||
pattern: 'm/d/Y - H:i'
|
||||
@@ -0,0 +1 @@
|
||||
filetransfer_default:
|
||||
@@ -0,0 +1,3 @@
|
||||
threshold:
|
||||
requirements_warning: 172800
|
||||
requirements_error: 1209600
|
||||
@@ -0,0 +1,9 @@
|
||||
country:
|
||||
default: ''
|
||||
first_day: 0
|
||||
timezone:
|
||||
default: ''
|
||||
user:
|
||||
configurable: true
|
||||
warn: false
|
||||
default: 0
|
||||
@@ -0,0 +1,3 @@
|
||||
context:
|
||||
lines_leading: 2
|
||||
lines_trailing: 2
|
||||
@@ -0,0 +1,5 @@
|
||||
allow_insecure_uploads: false
|
||||
default_scheme: 'public'
|
||||
path:
|
||||
temporary: ''
|
||||
temporary_maximum_age: 21600
|
||||
@@ -0,0 +1 @@
|
||||
jpeg_quality: 75
|
||||
@@ -0,0 +1 @@
|
||||
toolkit: gd
|
||||
@@ -0,0 +1 @@
|
||||
error_level: hide
|
||||
@@ -0,0 +1,2 @@
|
||||
interface:
|
||||
default: 'php_mail'
|
||||
@@ -0,0 +1,2 @@
|
||||
message: '@site is currently under maintenance. We should be back shortly. Thank you for your patience.'
|
||||
langcode: en
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: account
|
||||
label: 'User account menu'
|
||||
description: 'Links related to the active user account'
|
||||
locked: true
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: admin
|
||||
label: Administration
|
||||
description: 'Administrative task links'
|
||||
locked: true
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: footer
|
||||
label: Footer
|
||||
description: 'Site information links'
|
||||
locked: true
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: main
|
||||
label: 'Main navigation'
|
||||
description: 'Site section links'
|
||||
locked: true
|
||||
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: tools
|
||||
label: Tools
|
||||
description: 'User tool links, often added by modules'
|
||||
locked: true
|
||||
@@ -0,0 +1,15 @@
|
||||
cache:
|
||||
page:
|
||||
max_age: 0
|
||||
css:
|
||||
preprocess: true
|
||||
gzip: true
|
||||
fast_404:
|
||||
enabled: true
|
||||
paths: '/\.(?:txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i'
|
||||
exclude_paths: '/\/(?:styles|imagecache)\//'
|
||||
html: '<!DOCTYPE html><html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>'
|
||||
js:
|
||||
preprocess: true
|
||||
gzip: true
|
||||
stale_file_threshold: 2592000
|
||||
@@ -0,0 +1,6 @@
|
||||
channel:
|
||||
description: ''
|
||||
items:
|
||||
limit: 10
|
||||
view_mode: rss
|
||||
langcode: en
|
||||
@@ -0,0 +1,12 @@
|
||||
uuid: ''
|
||||
name: ''
|
||||
mail: ''
|
||||
slogan: ''
|
||||
page:
|
||||
403: ''
|
||||
404: ''
|
||||
front: /user/login
|
||||
admin_compact_mode: false
|
||||
weight_select_max: 100
|
||||
langcode: en
|
||||
default_langcode: en
|
||||
@@ -0,0 +1,14 @@
|
||||
favicon:
|
||||
mimetype: image/vnd.microsoft.icon
|
||||
path: ''
|
||||
url: ''
|
||||
use_default: true
|
||||
features:
|
||||
comment_user_picture: true
|
||||
comment_user_verification: true
|
||||
favicon: true
|
||||
node_user_picture: true
|
||||
logo:
|
||||
path: ''
|
||||
url: ''
|
||||
use_default: true
|
||||
@@ -0,0 +1,2 @@
|
||||
admin: ''
|
||||
default: stark
|
||||
@@ -0,0 +1,357 @@
|
||||
# Schema for the configuration files of the System module.
|
||||
|
||||
system.site:
|
||||
type: config_object
|
||||
label: 'Site information'
|
||||
mapping:
|
||||
uuid:
|
||||
type: string
|
||||
label: 'Site UUID'
|
||||
name:
|
||||
type: label
|
||||
label: 'Site name'
|
||||
mail:
|
||||
type: email
|
||||
label: 'Email address'
|
||||
slogan:
|
||||
type: label
|
||||
label: 'Slogan'
|
||||
page:
|
||||
type: mapping
|
||||
label: 'Pages'
|
||||
mapping:
|
||||
403:
|
||||
type: path
|
||||
label: 'Default 403 (access denied) page'
|
||||
404:
|
||||
type: path
|
||||
label: 'Default 404 (not found) page'
|
||||
front:
|
||||
type: path
|
||||
label: 'Default front page'
|
||||
admin_compact_mode:
|
||||
type: boolean
|
||||
label: 'Compact mode'
|
||||
weight_select_max:
|
||||
type: integer
|
||||
label: 'Weight element maximum value'
|
||||
default_langcode:
|
||||
type: string
|
||||
label: 'Site default language code'
|
||||
mail_notification:
|
||||
type: string
|
||||
label: 'Notification email address'
|
||||
|
||||
system.maintenance:
|
||||
type: config_object
|
||||
label: 'Maintenance mode'
|
||||
mapping:
|
||||
message:
|
||||
type: text
|
||||
label: 'Message to display when in maintenance mode'
|
||||
|
||||
system.authorize:
|
||||
type: config_object
|
||||
label: 'Authorize settings'
|
||||
mapping:
|
||||
filetransfer_default:
|
||||
type: string
|
||||
label: 'Default file transfer protocol'
|
||||
|
||||
system.cron:
|
||||
type: config_object
|
||||
label: 'Cron settings'
|
||||
mapping:
|
||||
threshold:
|
||||
type: mapping
|
||||
label: 'Thresholds'
|
||||
mapping:
|
||||
requirements_warning:
|
||||
type: integer
|
||||
label: 'Requirements warning period'
|
||||
requirements_error:
|
||||
type: integer
|
||||
label: 'Requirements error period'
|
||||
|
||||
system.date:
|
||||
type: config_object
|
||||
label: 'Date settings'
|
||||
mapping:
|
||||
first_day:
|
||||
type: integer
|
||||
label: 'First day of week'
|
||||
country:
|
||||
type: mapping
|
||||
label: 'Country'
|
||||
mapping:
|
||||
default:
|
||||
type: string
|
||||
label: 'Default country'
|
||||
timezone:
|
||||
type: mapping
|
||||
label: 'Time zone settings'
|
||||
mapping:
|
||||
default:
|
||||
type: string
|
||||
label: 'Default time zone'
|
||||
user:
|
||||
type: mapping
|
||||
label: 'User'
|
||||
mapping:
|
||||
configurable:
|
||||
type: boolean
|
||||
label: 'Users may set their own time zone'
|
||||
default:
|
||||
type: integer
|
||||
label: 'Time zone for new users'
|
||||
warn:
|
||||
type: boolean
|
||||
label: 'Remind users at login if their time zone is not set'
|
||||
|
||||
system.diff:
|
||||
type: config_object
|
||||
label: 'Diff settings'
|
||||
mapping:
|
||||
context:
|
||||
type: mapping
|
||||
label: 'Context'
|
||||
mapping:
|
||||
lines_leading:
|
||||
type: integer
|
||||
label: 'Number of leading lines in a diff'
|
||||
lines_trailing:
|
||||
type: integer
|
||||
label: 'Number of trailing lines in a diff'
|
||||
|
||||
system.logging:
|
||||
type: config_object
|
||||
label: 'Logging settings'
|
||||
mapping:
|
||||
error_level:
|
||||
type: string
|
||||
label: 'Error messages to display'
|
||||
|
||||
system.performance:
|
||||
type: config_object
|
||||
label: 'Performance settings'
|
||||
mapping:
|
||||
cache:
|
||||
type: mapping
|
||||
label: 'Caching'
|
||||
mapping:
|
||||
page:
|
||||
type: mapping
|
||||
label: 'Page caching'
|
||||
mapping:
|
||||
max_age:
|
||||
type: integer
|
||||
label: 'Max age'
|
||||
css:
|
||||
type: mapping
|
||||
label: 'CSS performance settings'
|
||||
mapping:
|
||||
preprocess:
|
||||
type: boolean
|
||||
label: 'Aggregate CSS files'
|
||||
gzip:
|
||||
type: boolean
|
||||
label: 'Compress CSS files'
|
||||
fast_404:
|
||||
type: mapping
|
||||
label: 'Fast 404 settings'
|
||||
mapping:
|
||||
enabled:
|
||||
type: boolean
|
||||
label: 'Fast 404 enabled'
|
||||
paths:
|
||||
type: string
|
||||
label: 'Regular expression to match'
|
||||
exclude_paths:
|
||||
type: string
|
||||
label: 'Regular expression to not match'
|
||||
html:
|
||||
type: string
|
||||
label: 'Fast 404 page html'
|
||||
js:
|
||||
type: mapping
|
||||
label: 'JavaScript performance settings'
|
||||
mapping:
|
||||
preprocess:
|
||||
type: boolean
|
||||
label: 'JavaScript preprocess'
|
||||
gzip:
|
||||
type: boolean
|
||||
label: 'Compress JavaScript files.'
|
||||
response:
|
||||
type: mapping
|
||||
label: 'Response performance settings'
|
||||
mapping:
|
||||
gzip:
|
||||
type: boolean
|
||||
label: 'Compress cached pages'
|
||||
stale_file_threshold:
|
||||
type: integer
|
||||
label: 'Stale file threshold'
|
||||
|
||||
system.rss:
|
||||
type: config_object
|
||||
label: 'Feed settings'
|
||||
mapping:
|
||||
channel:
|
||||
type: mapping
|
||||
label: 'Feed channel'
|
||||
mapping:
|
||||
description:
|
||||
type: text
|
||||
label: 'Feed description'
|
||||
items:
|
||||
type: mapping
|
||||
label: 'Feed items'
|
||||
mapping:
|
||||
limit:
|
||||
type: integer
|
||||
label: 'Feed item limit'
|
||||
view_mode:
|
||||
type: string
|
||||
label: 'Feed content'
|
||||
|
||||
system.theme:
|
||||
type: config_object
|
||||
label: 'Theme settings'
|
||||
mapping:
|
||||
admin:
|
||||
type: string
|
||||
label: 'Administration theme'
|
||||
default:
|
||||
type: string
|
||||
label: 'Default theme'
|
||||
|
||||
system.menu.*:
|
||||
type: config_entity
|
||||
label: 'Menu'
|
||||
mapping:
|
||||
id:
|
||||
type: string
|
||||
label: 'ID'
|
||||
label:
|
||||
type: label
|
||||
label: 'Label'
|
||||
description:
|
||||
type: label
|
||||
label: 'Menu description'
|
||||
locked:
|
||||
type: boolean
|
||||
label: ''
|
||||
|
||||
system.action.*:
|
||||
type: config_entity
|
||||
label: 'System action'
|
||||
mapping:
|
||||
id:
|
||||
type: string
|
||||
label: 'ID'
|
||||
label:
|
||||
type: label
|
||||
label: 'Label'
|
||||
type:
|
||||
type: string
|
||||
label: 'Type'
|
||||
plugin:
|
||||
type: string
|
||||
label: 'Plugin'
|
||||
configuration:
|
||||
type: action.configuration.[%parent.plugin]
|
||||
|
||||
system.file:
|
||||
type: config_object
|
||||
label: 'File system'
|
||||
mapping:
|
||||
allow_insecure_uploads:
|
||||
type: boolean
|
||||
label: 'Allow insecure uploads'
|
||||
default_scheme:
|
||||
type: string
|
||||
label: 'Default download method'
|
||||
path:
|
||||
type: mapping
|
||||
label: 'Path settings'
|
||||
mapping:
|
||||
temporary:
|
||||
type: string
|
||||
label: 'Temporary directory'
|
||||
temporary_maximum_age:
|
||||
type: integer
|
||||
label: 'Maximum age for temporary files'
|
||||
|
||||
system.image:
|
||||
type: config_object
|
||||
label: 'Image settings'
|
||||
mapping:
|
||||
toolkit:
|
||||
type: string
|
||||
label: 'Toolkit'
|
||||
|
||||
system.image.gd:
|
||||
type: config_object
|
||||
label: 'Image settings'
|
||||
mapping:
|
||||
jpeg_quality:
|
||||
type: integer
|
||||
label: 'JPEG quality'
|
||||
|
||||
system.mail:
|
||||
type: config_object
|
||||
label: 'Mail system'
|
||||
mapping:
|
||||
interface:
|
||||
type: sequence
|
||||
label: 'Interfaces'
|
||||
sequence:
|
||||
type: string
|
||||
label: 'Interface'
|
||||
|
||||
system.theme.global:
|
||||
type: theme_settings
|
||||
label: 'Theme global settings'
|
||||
|
||||
block.settings.system_branding_block:
|
||||
type: block_settings
|
||||
label: 'Branding block'
|
||||
mapping:
|
||||
use_site_logo:
|
||||
type: boolean
|
||||
label: 'Use site logo'
|
||||
use_site_name:
|
||||
type: boolean
|
||||
label: 'Use site name'
|
||||
use_site_slogan:
|
||||
type: boolean
|
||||
label: 'Use site slogan'
|
||||
|
||||
block.settings.system_menu_block:*:
|
||||
type: block_settings
|
||||
label: 'Menu block'
|
||||
mapping:
|
||||
level:
|
||||
type: integer
|
||||
label: 'Starting level'
|
||||
depth:
|
||||
type: integer
|
||||
label: 'Maximum number of levels'
|
||||
|
||||
block.settings.local_tasks_block:
|
||||
type: block_settings
|
||||
label: 'Tabs block'
|
||||
mapping:
|
||||
primary:
|
||||
type: boolean
|
||||
label: 'Whether primary tabs are shown'
|
||||
secondary:
|
||||
type: boolean
|
||||
label: 'Whether secondary tabs are shown'
|
||||
|
||||
condition.plugin.request_path:
|
||||
type: condition.plugin
|
||||
mapping:
|
||||
pages:
|
||||
type: string
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @file
|
||||
* Throbber.
|
||||
*/
|
||||
|
||||
.ajax-progress {
|
||||
display: inline-block;
|
||||
padding: 1px 5px 2px 5px;
|
||||
}
|
||||
[dir="rtl"] .ajax-progress {
|
||||
float: right;
|
||||
}
|
||||
.ajax-progress-throbber .throbber {
|
||||
background: transparent url(../../../../misc/throbber-active.gif) no-repeat 0px center;
|
||||
display: inline;
|
||||
padding: 1px 5px 2px;
|
||||
}
|
||||
.ajax-progress-throbber .message {
|
||||
display: inline;
|
||||
padding: 1px 5px 2px;
|
||||
}
|
||||
tr .ajax-progress-throbber .throbber {
|
||||
margin: 0 2px;
|
||||
}
|
||||
.ajax-progress-bar {
|
||||
width: 16em;
|
||||
}
|
||||
|
||||
/* Full screen throbber */
|
||||
.ajax-progress-fullscreen {
|
||||
/* Can't do center:50% middle: 50%, so approximate it for a typical window size. */
|
||||
left: 49%; /* LTR */
|
||||
position: fixed;
|
||||
top: 48.5%;
|
||||
z-index: 1000;
|
||||
background-color: #232323;
|
||||
background-image: url(../../../../misc/loading-small.gif);
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 7px;
|
||||
height: 24px;
|
||||
opacity: 0.9;
|
||||
padding: 4px;
|
||||
width: 24px;
|
||||
}
|
||||
[dir="rtl"] .ajax-progress-fullscreen {
|
||||
left: auto;
|
||||
right: 49%;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @file
|
||||
* Alignment classes for text and block level elements.
|
||||
*/
|
||||
|
||||
.text-align-left {
|
||||
text-align: left;
|
||||
}
|
||||
.text-align-right {
|
||||
text-align: right;
|
||||
}
|
||||
.text-align-center {
|
||||
text-align: center;
|
||||
}
|
||||
.text-align-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alignment classes for block level elements (images, videos, blockquotes, etc.)
|
||||
*/
|
||||
.align-left {
|
||||
float: left;
|
||||
}
|
||||
.align-right {
|
||||
float: right;
|
||||
}
|
||||
.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @file
|
||||
* Visual styles for animated throbber.
|
||||
*
|
||||
* @see autocomplete.js
|
||||
*/
|
||||
|
||||
.js input.form-autocomplete {
|
||||
background-image: url(../../../../misc/throbber-inactive.png);
|
||||
background-position: 100% center; /* LTR */
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.js[dir="rtl"] input.form-autocomplete {
|
||||
background-position: 0% center;
|
||||
}
|
||||
.js input.form-autocomplete.ui-autocomplete-loading {
|
||||
background-image: url(../../../../misc/throbber-active.gif);
|
||||
background-position: 100% center; /* LTR */
|
||||
}
|
||||
.js[dir="rtl"] input.form-autocomplete.ui-autocomplete-loading {
|
||||
background-position: 0% center;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @file
|
||||
* Float clearing.
|
||||
*
|
||||
* Based on the micro clearfix hack by Nicolas Gallagher, with the :before
|
||||
* pseudo selector removed to allow normal top margin collapse.
|
||||
*
|
||||
* @see http://nicolasgallagher.com/micro-clearfix-hack
|
||||
*/
|
||||
|
||||
.clearfix:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @file
|
||||
* Inline items.
|
||||
*/
|
||||
|
||||
.container-inline div,
|
||||
.container-inline label {
|
||||
display: inline;
|
||||
}
|
||||
/* Details contents always need to be rendered as block. */
|
||||
.container-inline .details-wrapper {
|
||||
display: block;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* @file
|
||||
* Collapsible details.
|
||||
*
|
||||
* @see collapse.js
|
||||
*/
|
||||
|
||||
.js details:not([open]) .details-wrapper {
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* @file
|
||||
* Fieldgroup border reset.
|
||||
*/
|
||||
|
||||
.fieldgroup {
|
||||
border-width: 0;
|
||||
padding: 0;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @file
|
||||
* Utility classes to hide elements in different ways.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Hide elements from all users.
|
||||
*
|
||||
* Used for elements which should not be immediately displayed to any user. An
|
||||
* example would be collapsible details that will be expanded with a click
|
||||
* from a user. The effect of this class can be toggled with the jQuery show()
|
||||
* and hide() functions.
|
||||
*/
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide elements visually, but keep them available for screen readers.
|
||||
*
|
||||
* Used for information required for screen reader users to understand and use
|
||||
* the site where visual display is undesirable. Information provided in this
|
||||
* manner should be kept concise, to avoid unnecessary burden on the user.
|
||||
* "!important" is used to prevent unintentional overrides.
|
||||
*/
|
||||
.visually-hidden {
|
||||
position: absolute !important;
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
overflow: hidden;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* The .focusable class extends the .visually-hidden class to allow
|
||||
* the element to be focusable when navigated to via the keyboard.
|
||||
*/
|
||||
.visually-hidden.focusable:active,
|
||||
.visually-hidden.focusable:focus {
|
||||
position: static !important;
|
||||
clip: auto;
|
||||
overflow: visible;
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide visually and from screen readers, but maintain layout.
|
||||
*/
|
||||
.invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @file
|
||||
* Styles for item list.
|
||||
*/
|
||||
|
||||
.item-list__comma-list,
|
||||
.item-list__comma-list li {
|
||||
display: inline;
|
||||
}
|
||||
.item-list__comma-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.item-list__comma-list li:after {
|
||||
content: ", ";
|
||||
}
|
||||
.item-list__comma-list li:last-child:after {
|
||||
content: "";
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @file
|
||||
* Utility classes to assist with Javascript functionality.
|
||||
*/
|
||||
|
||||
/**
|
||||
* For anything you want to hide on page load when JS is enabled, so
|
||||
* that you can use the JS to control visibility and avoid flicker.
|
||||
*/
|
||||
.js .js-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* For anything you want to show on page load only when JS is enabled.
|
||||
*/
|
||||
.js-show {
|
||||
display: none;
|
||||
}
|
||||
.js .js-show {
|
||||
display: block;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* @file
|
||||
* Utility class to prevent text wrapping.
|
||||
*/
|
||||
|
||||
.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* @file
|
||||
* Contain positioned elements.
|
||||
*/
|
||||
|
||||
.position-container {
|
||||
position: relative;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @file
|
||||
* Progress behavior.
|
||||
*
|
||||
* @see progress.js
|
||||
*/
|
||||
|
||||
.progress {
|
||||
position: relative;
|
||||
}
|
||||
.progress__track {
|
||||
background-color: #fff;
|
||||
border: 1px solid;
|
||||
margin-top: 5px;
|
||||
max-width: 100%;
|
||||
min-width: 100px;
|
||||
height: 16px;
|
||||
}
|
||||
.progress__bar {
|
||||
background-color: #000;
|
||||
height: 16px;
|
||||
width: 3%;
|
||||
min-width: 3%;
|
||||
max-width: 100%;
|
||||
}
|
||||
.progress__description,
|
||||
.progress__percentage {
|
||||
color: #555;
|
||||
overflow: hidden;
|
||||
font-size: .875em;
|
||||
margin-top: 0.2em;
|
||||
}
|
||||
.progress__description {
|
||||
float: left; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .progress__description {
|
||||
float: right;
|
||||
}
|
||||
.progress__percentage {
|
||||
float: right; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .progress__percentage {
|
||||
float: left;
|
||||
}
|
||||
.progress--small .progress__track {
|
||||
height: 7px;
|
||||
}
|
||||
.progress--small .progress__bar {
|
||||
height: 7px;
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* @file
|
||||
* Utility class to remove browser styles, especially for button.
|
||||
*/
|
||||
|
||||
.reset-appearance {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
border: 0 none;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
line-height: inherit;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @file
|
||||
* Resizable textareas.
|
||||
*/
|
||||
|
||||
.resize-none {
|
||||
resize: none;
|
||||
}
|
||||
.resize-vertical {
|
||||
resize: vertical;
|
||||
min-height: 2em;
|
||||
}
|
||||
.resize-horizontal {
|
||||
resize: horizontal;
|
||||
max-width: 100%;
|
||||
}
|
||||
.resize-both {
|
||||
resize: both;
|
||||
max-width: 100%;
|
||||
min-height: 2em;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @file
|
||||
* Table header behavior.
|
||||
*
|
||||
* @see tableheader.js
|
||||
*/
|
||||
|
||||
table.sticky-header {
|
||||
background-color: #fff;
|
||||
margin-top: 0;
|
||||
z-index: 500;
|
||||
top: 0;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @file
|
||||
* Table drag behavior.
|
||||
*
|
||||
* @see tabledrag.js
|
||||
*/
|
||||
|
||||
body.drag {
|
||||
cursor: move;
|
||||
}
|
||||
tr.region-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
tr.region-message {
|
||||
color: #999;
|
||||
}
|
||||
tr.region-populated {
|
||||
display: none;
|
||||
}
|
||||
tr.add-new .tabledrag-changed {
|
||||
display: none;
|
||||
}
|
||||
.draggable a.tabledrag-handle {
|
||||
cursor: move;
|
||||
float: left; /* LTR */
|
||||
height: 1.7em;
|
||||
margin-left: -1em; /* LTR */
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
}
|
||||
[dir="rtl"] .draggable a.tabledrag-handle {
|
||||
float: right;
|
||||
margin-right: -1em;
|
||||
margin-left: 0;
|
||||
}
|
||||
a.tabledrag-handle:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
a.tabledrag-handle .handle {
|
||||
background: url(../../../../misc/icons/787878/move.svg) no-repeat 6px 7px;
|
||||
height: 14px;
|
||||
margin: -0.4em 0.5em 0;
|
||||
padding: 0.42em 0.5em;
|
||||
width: 14px;
|
||||
}
|
||||
a.tabledrag-handle:hover .handle,
|
||||
a.tabledrag-handle:focus .handle {
|
||||
background-image: url(../../../../misc/icons/000000/move.svg);
|
||||
}
|
||||
.touchevents .draggable td {
|
||||
padding: 0 10px;
|
||||
}
|
||||
.touchevents .draggable .menu-item__link {
|
||||
display: inline-block;
|
||||
padding: 10px 0;
|
||||
}
|
||||
.touchevents a.tabledrag-handle {
|
||||
height: 44px;
|
||||
width: 40px;
|
||||
}
|
||||
.touchevents a.tabledrag-handle .handle {
|
||||
background-position: 40% 19px; /* LTR */
|
||||
height: 21px;
|
||||
}
|
||||
[dir="rtl"] .touch a.tabledrag-handle .handle {
|
||||
background-position: right 40% top 19px;
|
||||
}
|
||||
.touchevents .draggable.drag a.tabledrag-handle .handle {
|
||||
background-position: 50% -32px;
|
||||
}
|
||||
.tabledrag-toggle-weight-wrapper {
|
||||
text-align: right; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .tabledrag-toggle-weight-wrapper {
|
||||
text-align: left;
|
||||
}
|
||||
.indentation {
|
||||
float: left; /* LTR */
|
||||
height: 1.7em;
|
||||
margin: -0.4em 0.2em -0.4em -0.4em; /* LTR */
|
||||
padding: 0.42em 0 0.42em 0.6em; /* LTR */
|
||||
width: 20px;
|
||||
}
|
||||
[dir="rtl"] .indentation {
|
||||
float: right;
|
||||
margin: -0.4em -0.4em -0.4em 0.2em;
|
||||
padding: 0.42em 0.6em 0.42em 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @file
|
||||
* Table sort indicator.
|
||||
*
|
||||
* @see tablesort-indicator.html.twig
|
||||
*/
|
||||
|
||||
.tablesort {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
background-size: 100%;
|
||||
}
|
||||
.tablesort--asc {
|
||||
background-image: url(../../../../misc/icons/787878/twistie-down.svg);
|
||||
}
|
||||
.tablesort--desc {
|
||||
background-image: url(../../../../misc/icons/787878/twistie-up.svg);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @file
|
||||
* Visual styles for a nested tree child.
|
||||
*/
|
||||
|
||||
div.tree-child {
|
||||
background: url(../../../../misc/tree.png) no-repeat 11px center; /* LTR */
|
||||
}
|
||||
div.tree-child-last {
|
||||
background: url(../../../../misc/tree-bottom.png) no-repeat 11px center; /* LTR */
|
||||
}
|
||||
[dir="rtl"] div.tree-child,
|
||||
[dir="rtl"] div.tree-child-last {
|
||||
background-position: -65px center;
|
||||
}
|
||||
div.tree-child-horizontal {
|
||||
background: url(../../../../misc/tree.png) no-repeat -11px center;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Traditional split diff theming
|
||||
*/
|
||||
table.diff {
|
||||
border-spacing: 4px;
|
||||
margin-bottom: 20px;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
table.diff .diff-context {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
table.diff .diff-deletedline {
|
||||
background-color: #ffa;
|
||||
width: 50%;
|
||||
}
|
||||
table.diff .diff-addedline {
|
||||
background-color: #afa;
|
||||
width: 50%;
|
||||
}
|
||||
table.diff .diffchange {
|
||||
color: #f00;
|
||||
font-weight: bold;
|
||||
}
|
||||
table.diff .diff-marker {
|
||||
width: 1.4em;
|
||||
}
|
||||
table.diff th {
|
||||
padding-right: inherit; /* LTR */
|
||||
}
|
||||
[dir="rtl"] table.diff th {
|
||||
padding-right: 0;
|
||||
padding-left: inherit;
|
||||
}
|
||||
table.diff td div {
|
||||
overflow: auto;
|
||||
padding: 0.1ex 0.5em;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
table.diff td {
|
||||
padding: 0.1ex 0.4em;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Update styles
|
||||
*/
|
||||
.update-results {
|
||||
margin-top: 3em;
|
||||
padding: 0.25em;
|
||||
border: 1px solid #ccc;
|
||||
background: #eee;
|
||||
font-size: smaller;
|
||||
}
|
||||
.update-results h2 {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
.update-results h4 {
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
.update-results .none {
|
||||
color: #888;
|
||||
font-style: italic;
|
||||
}
|
||||
.update-results .failure strong {
|
||||
color: #b63300;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize.php styles
|
||||
*/
|
||||
#edit-submit-connection {
|
||||
clear: both;
|
||||
}
|
||||
#edit-submit-process,
|
||||
.filetransfer {
|
||||
display: none;
|
||||
clear: both;
|
||||
}
|
||||
.js #edit-submit-connection {
|
||||
display: none;
|
||||
}
|
||||
.js #edit-submit-process {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#edit-connection-settings-change-connection-type {
|
||||
margin: 2.6em 0.5em 0 1em; /* LTR */
|
||||
}
|
||||
[dir="rtl"] #edit-connection-settings-change-connection-type {
|
||||
margin-left: 0.5em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme maintenance styles
|
||||
*/
|
||||
.authorize-results__failure {
|
||||
font-weight: bold;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
@@ -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);
|
||||
@@ -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);
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* @file
|
||||
* Module page behaviors.
|
||||
*/
|
||||
|
||||
(function ($, Drupal, debounce) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Filters the module list table by a text input search string.
|
||||
*
|
||||
* Additionally accounts for multiple tables being wrapped in "package" details
|
||||
* elements.
|
||||
*
|
||||
* Text search input: input.table-filter-text
|
||||
* Target table: input.table-filter-text[data-table]
|
||||
* Source text: .table-filter-text-source, .module-name, .module-description
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.tableFilterByText = {
|
||||
attach: function (context, settings) {
|
||||
var $input = $('input.table-filter-text').once('table-filter-text');
|
||||
var $table = $($input.attr('data-table'));
|
||||
var $rowsAndDetails;
|
||||
var $rows;
|
||||
var $details;
|
||||
var searching = false;
|
||||
|
||||
function hidePackageDetails(index, element) {
|
||||
var $packDetails = $(element);
|
||||
var $visibleRows = $packDetails.find('tbody tr:visible');
|
||||
$packDetails.toggle($visibleRows.length > 0);
|
||||
}
|
||||
|
||||
function filterModuleList(e) {
|
||||
var query = $(e.target).val();
|
||||
// Case insensitive expression to find query at the beginning of a word.
|
||||
var re = new RegExp('\\b' + query, 'i');
|
||||
|
||||
function showModuleRow(index, row) {
|
||||
var $row = $(row);
|
||||
var $sources = $row.find('.table-filter-text-source, .module-name, .module-description');
|
||||
var textMatch = $sources.text().search(re) !== -1;
|
||||
$row.closest('tr').toggle(textMatch);
|
||||
}
|
||||
// Search over all rows and packages.
|
||||
$rowsAndDetails.show();
|
||||
|
||||
// Filter if the length of the query is at least 2 characters.
|
||||
if (query.length >= 2) {
|
||||
searching = true;
|
||||
$rows.each(showModuleRow);
|
||||
|
||||
// Note that we first open all <details> to be able to use ':visible'.
|
||||
// Mark the <details> elements that were closed before filtering, so
|
||||
// they can be reclosed when filtering is removed.
|
||||
$details.not('[open]').attr('data-drupal-system-state', 'forced-open');
|
||||
|
||||
// Hide the package <details> if they don't have any visible rows.
|
||||
// Note that we first show() all <details> to be able to use ':visible'.
|
||||
$details.attr('open', true).each(hidePackageDetails);
|
||||
|
||||
Drupal.announce(
|
||||
Drupal.t(
|
||||
'!modules modules are available in the modified list.',
|
||||
{'!modules': $rowsAndDetails.find('tbody tr:visible').length}
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (searching) {
|
||||
searching = false;
|
||||
$rowsAndDetails.show();
|
||||
// Return <details> elements that had been closed before filtering
|
||||
// to a closed state.
|
||||
$details.filter('[data-drupal-system-state="forced-open"]')
|
||||
.removeAttr('data-drupal-system-state')
|
||||
.attr('open', false);
|
||||
}
|
||||
}
|
||||
|
||||
function preventEnterKey(event) {
|
||||
if (event.which === 13) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
if ($table.length) {
|
||||
$rowsAndDetails = $table.find('tr, details');
|
||||
$rows = $table.find('tbody tr');
|
||||
$details = $rowsAndDetails.filter('.package-listing');
|
||||
|
||||
$input.on({
|
||||
keyup: debounce(filterModuleList, 200),
|
||||
keydown: preventEnterKey
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}(jQuery, Drupal, Drupal.debounce));
|
||||
@@ -0,0 +1,21 @@
|
||||
id: d6_date_formats
|
||||
label: Date format configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
source:
|
||||
plugin: variable_multirow
|
||||
variables:
|
||||
- date_format_long
|
||||
- date_format_medium
|
||||
- date_format_short
|
||||
process:
|
||||
id:
|
||||
plugin: static_map
|
||||
source: name
|
||||
map:
|
||||
date_format_long: long
|
||||
date_format_short: short
|
||||
date_format_medium: medium
|
||||
pattern: value
|
||||
destination:
|
||||
plugin: entity:date_format
|
||||
@@ -0,0 +1,16 @@
|
||||
id: d6_system_cron
|
||||
label: Cron settings
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
||||
- cron_threshold_warning
|
||||
- cron_threshold_error
|
||||
- cron_last
|
||||
process:
|
||||
'threshold/requirements_warning': cron_threshold_warning
|
||||
'threshold/requirements_error': cron_threshold_error
|
||||
destination:
|
||||
plugin: config
|
||||
config_name: system.cron
|
||||
@@ -0,0 +1,19 @@
|
||||
id: d6_system_date
|
||||
label: System date configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
||||
- configurable_timezones
|
||||
- date_first_day
|
||||
- date_default_timezone
|
||||
process:
|
||||
'timezone/user/configurable': configurable_timezones
|
||||
first_day: date_first_day
|
||||
'timezone/default':
|
||||
plugin: timezone
|
||||
source: date_default_timezone
|
||||
destination:
|
||||
plugin: config
|
||||
config_name: system.date
|
||||
@@ -0,0 +1,20 @@
|
||||
id: d6_system_file
|
||||
label: File system configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
||||
- file_directory_temp
|
||||
- allow_insecure_uploads
|
||||
process:
|
||||
'path/temporary': file_directory_temp
|
||||
allow_insecure_uploads:
|
||||
plugin: static_map
|
||||
source: allow_insecure_uploads
|
||||
map:
|
||||
0: FALSE
|
||||
1: TRUE
|
||||
destination:
|
||||
plugin: config
|
||||
config_name: system.file
|
||||
@@ -0,0 +1,13 @@
|
||||
id: d6_system_image
|
||||
label: Image toolkit configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
||||
- image_toolkit
|
||||
process:
|
||||
toolkit: image_toolkit
|
||||
destination:
|
||||
plugin: config
|
||||
config_name: system.image
|
||||
@@ -0,0 +1,13 @@
|
||||
id: d6_system_image_gd
|
||||
label: Image quality configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
||||
- image_jpeg_quality
|
||||
process:
|
||||
jpeg_quality: image_jpeg_quality
|
||||
destination:
|
||||
plugin: config
|
||||
config_name: system.image.gd
|
||||
@@ -0,0 +1,21 @@
|
||||
id: d6_system_logging
|
||||
label: System logging
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
||||
- error_level
|
||||
process:
|
||||
error_level:
|
||||
plugin: static_map
|
||||
source: error_level
|
||||
default_value: all
|
||||
map:
|
||||
0: hide
|
||||
1: some
|
||||
2: all
|
||||
3: verbose
|
||||
destination:
|
||||
plugin: config
|
||||
config_name: system.logging
|
||||
@@ -0,0 +1,13 @@
|
||||
id: d6_system_maintenance
|
||||
label: Maintenance page configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
||||
- site_offline_message
|
||||
process:
|
||||
message: site_offline_message
|
||||
destination:
|
||||
plugin: config
|
||||
config_name: system.maintenance
|
||||
@@ -0,0 +1,20 @@
|
||||
id: d6_system_performance
|
||||
label: Performance configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
||||
- preprocess_css
|
||||
- preprocess_js
|
||||
- cache_lifetime
|
||||
- cache
|
||||
- page_compression
|
||||
process:
|
||||
'css/preprocess': preprocess_css
|
||||
'js/preprocess': preprocess_js
|
||||
'cache/page/max_age': cache_lifetime
|
||||
'response/gzip': page_compression
|
||||
destination:
|
||||
plugin: config
|
||||
config_name: system.performance
|
||||
@@ -0,0 +1,15 @@
|
||||
id: d6_system_rss
|
||||
label: RSS configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
||||
- feed_default_items
|
||||
- feed_item_length
|
||||
process:
|
||||
'items/limit': feed_default_items
|
||||
'items/view_mode': feed_item_length
|
||||
destination:
|
||||
plugin: config
|
||||
config_name: system.rss
|
||||
@@ -0,0 +1,41 @@
|
||||
id: d6_system_site
|
||||
label: Site configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
source:
|
||||
plugin: variable
|
||||
constants:
|
||||
slash: '/'
|
||||
variables:
|
||||
- site_name
|
||||
- site_mail
|
||||
- site_slogan
|
||||
- site_frontpage
|
||||
- site_403
|
||||
- site_404
|
||||
- drupal_weight_select_max
|
||||
- admin_compact_mode
|
||||
process:
|
||||
name: site_name
|
||||
mail: site_mail
|
||||
slogan: site_slogan
|
||||
'page/front':
|
||||
plugin: concat
|
||||
source:
|
||||
- constants/slash
|
||||
- site_frontpage
|
||||
'page/403':
|
||||
plugin: concat
|
||||
source:
|
||||
- constants/slash
|
||||
- site_403
|
||||
'page/404':
|
||||
plugin: concat
|
||||
source:
|
||||
- constants/slash
|
||||
- site_404
|
||||
weight_select_max: drupal_weight_select_max
|
||||
admin_compact_mode: admin_compact_mode
|
||||
destination:
|
||||
plugin: config
|
||||
config_name: system.site
|
||||
@@ -0,0 +1,14 @@
|
||||
# The menu_settings migration is in the menu_ui module.
|
||||
id: menu
|
||||
label: Menus
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
- Drupal 7
|
||||
source:
|
||||
plugin: menu
|
||||
process:
|
||||
id: menu_name
|
||||
label: title
|
||||
description: description
|
||||
destination:
|
||||
plugin: entity:menu
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
|
||||
/**
|
||||
* Access check for cron routes.
|
||||
*/
|
||||
class CronAccessCheck implements AccessInterface {
|
||||
|
||||
/**
|
||||
* Checks access.
|
||||
*
|
||||
* @param string $key
|
||||
* The cron key.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public function access($key) {
|
||||
if ($key != \Drupal::state()->get('system.cron_key')) {
|
||||
\Drupal::logger('cron')->notice('Cron could not run because an invalid key was used.');
|
||||
return AccessResult::forbidden()->setCacheMaxAge(0);
|
||||
}
|
||||
elseif (\Drupal::state()->get('system.maintenance_mode')) {
|
||||
\Drupal::logger('cron')->notice('Cron could not run because the site is in maintenance mode.');
|
||||
return AccessResult::forbidden()->setCacheMaxAge(0);
|
||||
}
|
||||
return AccessResult::allowed()->setCacheMaxAge(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Access;
|
||||
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Site\Settings;
|
||||
|
||||
/**
|
||||
* Access check for database update routes.
|
||||
*/
|
||||
class DbUpdateAccessCheck implements AccessInterface {
|
||||
|
||||
/**
|
||||
* Checks access for update routes.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The currently logged in account.
|
||||
*
|
||||
* @return string
|
||||
* A \Drupal\Core\Access\AccessInterface constant value.
|
||||
*/
|
||||
public function access(AccountInterface $account) {
|
||||
// Allow the global variable in settings.php to override the access check.
|
||||
if (Settings::get('update_free_access')) {
|
||||
return AccessResult::allowed()->setCacheMaxAge(0);
|
||||
}
|
||||
|
||||
if ($account->hasPermission('administer software updates')) {
|
||||
return AccessResult::allowed()->cachePerPermissions();
|
||||
}
|
||||
else {
|
||||
return AccessResult::forbidden()->cachePerPermissions();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a action entity.
|
||||
*/
|
||||
interface ActionConfigEntityInterface extends ConfigEntityInterface {
|
||||
|
||||
/**
|
||||
* Returns whether or not this action is configurable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConfigurable();
|
||||
|
||||
/**
|
||||
* Returns the operation type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType();
|
||||
|
||||
/**
|
||||
* Returns the operation plugin.
|
||||
*
|
||||
* @return \Drupal\Core\Action\ActionInterface
|
||||
*/
|
||||
public function getPlugin();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
|
||||
/**
|
||||
* Controller for admin section.
|
||||
*/
|
||||
class AdminController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* Prints a listing of admin tasks, organized by module.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing the listing.
|
||||
*/
|
||||
public function index() {
|
||||
$module_info = system_get_info('module');
|
||||
foreach ($module_info as $module => $info) {
|
||||
$module_info[$module] = new \stdClass();
|
||||
$module_info[$module]->info = $info;
|
||||
}
|
||||
|
||||
uasort($module_info, 'system_sort_modules_by_info_name');
|
||||
$menu_items = array();
|
||||
|
||||
foreach ($module_info as $module => $info) {
|
||||
// Only display a section if there are any available tasks.
|
||||
if ($admin_tasks = system_get_module_admin_tasks($module, $info->info)) {
|
||||
// Sort links by title.
|
||||
uasort($admin_tasks, array('\Drupal\Component\Utility\SortArray', 'sortByTitleElement'));
|
||||
// Move 'Configure permissions' links to the bottom of each section.
|
||||
$permission_key = "user.admin_permissions.$module";
|
||||
if (isset($admin_tasks[$permission_key])) {
|
||||
$permission_task = $admin_tasks[$permission_key];
|
||||
unset($admin_tasks[$permission_key]);
|
||||
$admin_tasks[$permission_key] = $permission_task;
|
||||
}
|
||||
|
||||
$menu_items[$info->info['name']] = array($info->info['description'], $admin_tasks);
|
||||
}
|
||||
}
|
||||
|
||||
$output = array(
|
||||
'#theme' => 'system_admin_index',
|
||||
'#menu_items' => $menu_items,
|
||||
);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Controller;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
|
||||
/**
|
||||
* Controller routines for batch routes.
|
||||
*/
|
||||
class BatchController implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The app root.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* Constructs a new BatchController.
|
||||
*
|
||||
* @param string $root
|
||||
* The app root.
|
||||
*/
|
||||
public function __construct($root) {
|
||||
$this->root = $root;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('app.root')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a system batch page.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request object.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response|array
|
||||
* A \Symfony\Component\HttpFoundation\Response object or render array.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
|
||||
*/
|
||||
public function batchPage(Request $request) {
|
||||
require_once $this->root . '/core/includes/batch.inc';
|
||||
$output = _batch_page($request);
|
||||
|
||||
if ($output === FALSE) {
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
elseif ($output instanceof Response) {
|
||||
return $output;
|
||||
}
|
||||
elseif (isset($output)) {
|
||||
$page = [
|
||||
'#type' => 'page',
|
||||
'#show_messages' => FALSE,
|
||||
'content' => $output,
|
||||
];
|
||||
return $page;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The _title_callback for the system.batch_page.normal route.
|
||||
*
|
||||
* @return string
|
||||
* The page title.
|
||||
*/
|
||||
public function batchPageTitle() {
|
||||
$current_set = _batch_current_set();
|
||||
return !empty($current_set['title']) ? $current_set['title'] : '';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,705 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Controller;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
|
||||
use Drupal\Core\Render\BareHtmlPageRendererInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Drupal\Core\Update\UpdateRegistry;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Controller routines for database update routes.
|
||||
*/
|
||||
class DbUpdateController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The keyvalue expirable factory.
|
||||
*
|
||||
* @var \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface
|
||||
*/
|
||||
protected $keyValueExpirableFactory;
|
||||
|
||||
/**
|
||||
* A cache backend interface.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The state service.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* The bare HTML page renderer.
|
||||
*
|
||||
* @var \Drupal\Core\Render\BareHtmlPageRendererInterface
|
||||
*/
|
||||
protected $bareHtmlPageRenderer;
|
||||
|
||||
/**
|
||||
* The app root.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* The post update registry.
|
||||
*
|
||||
* @var \Drupal\Core\Update\UpdateRegistry
|
||||
*/
|
||||
protected $postUpdateRegistry;
|
||||
|
||||
/**
|
||||
* Constructs a new UpdateController.
|
||||
*
|
||||
* @param string $root
|
||||
* The app root.
|
||||
* @param \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $key_value_expirable_factory
|
||||
* The keyvalue expirable factory.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
|
||||
* A cache backend interface.
|
||||
* @param \Drupal\Core\State\StateInterface $state
|
||||
* The state service.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The current user.
|
||||
* @param \Drupal\Core\Render\BareHtmlPageRendererInterface $bare_html_page_renderer
|
||||
* The bare HTML page renderer.
|
||||
* @param \Drupal\Core\Update\UpdateRegistry $post_update_registry
|
||||
* The post update registry.
|
||||
*/
|
||||
public function __construct($root, KeyValueExpirableFactoryInterface $key_value_expirable_factory, CacheBackendInterface $cache, StateInterface $state, ModuleHandlerInterface $module_handler, AccountInterface $account, BareHtmlPageRendererInterface $bare_html_page_renderer, UpdateRegistry $post_update_registry) {
|
||||
$this->root = $root;
|
||||
$this->keyValueExpirableFactory = $key_value_expirable_factory;
|
||||
$this->cache = $cache;
|
||||
$this->state = $state;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->account = $account;
|
||||
$this->bareHtmlPageRenderer = $bare_html_page_renderer;
|
||||
$this->postUpdateRegistry = $post_update_registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('app.root'),
|
||||
$container->get('keyvalue.expirable'),
|
||||
$container->get('cache.default'),
|
||||
$container->get('state'),
|
||||
$container->get('module_handler'),
|
||||
$container->get('current_user'),
|
||||
$container->get('bare_html_page_renderer'),
|
||||
$container->get('update.post_update_registry')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a database update page.
|
||||
*
|
||||
* @param string $op
|
||||
* The update operation to perform. Can be any of the below:
|
||||
* - info
|
||||
* - selection
|
||||
* - run
|
||||
* - results
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request object.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* A response object object.
|
||||
*/
|
||||
public function handle($op, Request $request) {
|
||||
require_once $this->root . '/core/includes/install.inc';
|
||||
require_once $this->root . '/core/includes/update.inc';
|
||||
|
||||
drupal_load_updates();
|
||||
update_fix_compatibility();
|
||||
|
||||
if ($request->query->get('continue')) {
|
||||
$_SESSION['update_ignore_warnings'] = TRUE;
|
||||
}
|
||||
|
||||
$regions = array();
|
||||
$requirements = update_check_requirements();
|
||||
$severity = drupal_requirements_severity($requirements);
|
||||
if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && empty($_SESSION['update_ignore_warnings']))) {
|
||||
$regions['sidebar_first'] = $this->updateTasksList('requirements');
|
||||
$output = $this->requirements($severity, $requirements, $request);
|
||||
}
|
||||
else {
|
||||
switch ($op) {
|
||||
case 'selection':
|
||||
$regions['sidebar_first'] = $this->updateTasksList('selection');
|
||||
$output = $this->selection($request);
|
||||
break;
|
||||
|
||||
case 'run':
|
||||
$regions['sidebar_first'] = $this->updateTasksList('run');
|
||||
$output = $this->triggerBatch($request);
|
||||
break;
|
||||
|
||||
case 'info':
|
||||
$regions['sidebar_first'] = $this->updateTasksList('info');
|
||||
$output = $this->info($request);
|
||||
break;
|
||||
|
||||
case 'results':
|
||||
$regions['sidebar_first'] = $this->updateTasksList('results');
|
||||
$output = $this->results($request);
|
||||
break;
|
||||
|
||||
// Regular batch ops : defer to batch processing API.
|
||||
default:
|
||||
require_once $this->root . '/core/includes/batch.inc';
|
||||
$regions['sidebar_first'] = $this->updateTasksList('run');
|
||||
$output = _batch_page($request);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($output instanceof Response) {
|
||||
return $output;
|
||||
}
|
||||
$title = isset($output['#title']) ? $output['#title'] : $this->t('Drupal database update');
|
||||
|
||||
return $this->bareHtmlPageRenderer->renderBarePage($output, $title, 'maintenance_page', $regions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the info database update page.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @return array
|
||||
* A render array.
|
||||
*/
|
||||
protected function info(Request $request) {
|
||||
// Change query-strings on css/js files to enforce reload for all users.
|
||||
_drupal_flush_css_js();
|
||||
// Flush the cache of all data for the update status module.
|
||||
$this->keyValueExpirableFactory->get('update')->deleteAll();
|
||||
$this->keyValueExpirableFactory->get('update_available_release')->deleteAll();
|
||||
|
||||
$build['info_header'] = array(
|
||||
'#markup' => '<p>' . $this->t('Use this utility to update your database whenever a new release of Drupal or a module is installed.') . '</p><p>' . $this->t('For more detailed information, see the <a href="https://www.drupal.org/upgrade">upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.') . '</p>',
|
||||
);
|
||||
|
||||
$info[] = $this->t("<strong>Back up your code</strong>. Hint: when backing up module code, do not leave that backup in the 'modules' or 'sites/*/modules' directories as this may confuse Drupal's auto-discovery mechanism.");
|
||||
$info[] = $this->t('Put your site into <a href=":url">maintenance mode</a>.', array(
|
||||
':url' => Url::fromRoute('system.site_maintenance_mode')->toString(TRUE)->getGeneratedUrl(),
|
||||
));
|
||||
$info[] = $this->t('<strong>Back up your database</strong>. This process will change your database values and in case of emergency you may need to revert to a backup.');
|
||||
$info[] = $this->t('Install your new files in the appropriate location, as described in the handbook.');
|
||||
$build['info'] = array(
|
||||
'#theme' => 'item_list',
|
||||
'#list_type' => 'ol',
|
||||
'#items' => $info,
|
||||
);
|
||||
$build['info_footer'] = array(
|
||||
'#markup' => '<p>' . $this->t('When you have performed the steps above, you may proceed.') . '</p>',
|
||||
);
|
||||
|
||||
$build['link'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Continue'),
|
||||
'#attributes' => array('class' => array('button', 'button--primary')),
|
||||
// @todo Revisit once https://www.drupal.org/node/2548095 is in.
|
||||
'#url' => Url::fromUri('base://selection'),
|
||||
);
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a list of available database updates.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @return array
|
||||
* A render array.
|
||||
*/
|
||||
protected function selection(Request $request) {
|
||||
// Make sure there is no stale theme registry.
|
||||
$this->cache->deleteAll();
|
||||
|
||||
$count = 0;
|
||||
$incompatible_count = 0;
|
||||
$build['start'] = array(
|
||||
'#tree' => TRUE,
|
||||
'#type' => 'details',
|
||||
);
|
||||
|
||||
// Ensure system.module's updates appear first.
|
||||
$build['start']['system'] = array();
|
||||
|
||||
$starting_updates = array();
|
||||
$incompatible_updates_exist = FALSE;
|
||||
$updates_per_module = [];
|
||||
foreach (['update', 'post_update'] as $update_type) {
|
||||
switch ($update_type) {
|
||||
case 'update':
|
||||
$updates = update_get_update_list();
|
||||
break;
|
||||
case 'post_update':
|
||||
$updates = $this->postUpdateRegistry->getPendingUpdateInformation();
|
||||
break;
|
||||
}
|
||||
foreach ($updates as $module => $update) {
|
||||
if (!isset($update['start'])) {
|
||||
$build['start'][$module] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => $module . ' module',
|
||||
'#markup' => $update['warning'],
|
||||
'#prefix' => '<div class="messages messages--warning">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
$incompatible_updates_exist = TRUE;
|
||||
continue;
|
||||
}
|
||||
if (!empty($update['pending'])) {
|
||||
$updates_per_module += [$module => []];
|
||||
$updates_per_module[$module] = array_merge($updates_per_module[$module], $update['pending']);
|
||||
$build['start'][$module] = array(
|
||||
'#type' => 'hidden',
|
||||
'#value' => $update['start'],
|
||||
);
|
||||
// Store the previous items in order to merge normal updates and
|
||||
// post_update functions together.
|
||||
$build['start'][$module] = array(
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $updates_per_module[$module],
|
||||
'#title' => $module . ' module',
|
||||
);
|
||||
|
||||
if ($update_type === 'update') {
|
||||
$starting_updates[$module] = $update['start'];
|
||||
}
|
||||
}
|
||||
if (isset($update['pending'])) {
|
||||
$count = $count + count($update['pending']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find and label any incompatible updates.
|
||||
foreach (update_resolve_dependencies($starting_updates) as $data) {
|
||||
if (!$data['allowed']) {
|
||||
$incompatible_updates_exist = TRUE;
|
||||
$incompatible_count++;
|
||||
$module_update_key = $data['module'] . '_updates';
|
||||
if (isset($build['start'][$module_update_key]['#items'][$data['number']])) {
|
||||
if ($data['missing_dependencies']) {
|
||||
$text = $this->t('This update will been skipped due to the following missing dependencies:') . '<em>' . implode(', ', $data['missing_dependencies']) . '</em>';
|
||||
}
|
||||
else {
|
||||
$text = $this->t("This update will be skipped due to an error in the module's code.");
|
||||
}
|
||||
$build['start'][$module_update_key]['#items'][$data['number']] .= '<div class="warning">' . $text . '</div>';
|
||||
}
|
||||
// Move the module containing this update to the top of the list.
|
||||
$build['start'] = array($module_update_key => $build['start'][$module_update_key]) + $build['start'];
|
||||
}
|
||||
}
|
||||
|
||||
// Warn the user if any updates were incompatible.
|
||||
if ($incompatible_updates_exist) {
|
||||
drupal_set_message($this->t('Some of the pending updates cannot be applied because their dependencies were not met.'), 'warning');
|
||||
}
|
||||
|
||||
if (empty($count)) {
|
||||
drupal_set_message($this->t('No pending updates.'));
|
||||
unset($build);
|
||||
$build['links'] = array(
|
||||
'#theme' => 'links',
|
||||
'#links' => $this->helpfulLinks($request),
|
||||
);
|
||||
|
||||
// No updates to run, so caches won't get flushed later. Clear them now.
|
||||
drupal_flush_all_caches();
|
||||
}
|
||||
else {
|
||||
$build['help'] = array(
|
||||
'#markup' => '<p>' . $this->t('The version of Drupal you are updating from has been automatically detected.') . '</p>',
|
||||
'#weight' => -5,
|
||||
);
|
||||
if ($incompatible_count) {
|
||||
$build['start']['#title'] = $this->formatPlural(
|
||||
$count,
|
||||
'1 pending update (@number_applied to be applied, @number_incompatible skipped)',
|
||||
'@count pending updates (@number_applied to be applied, @number_incompatible skipped)',
|
||||
array('@number_applied' => $count - $incompatible_count, '@number_incompatible' => $incompatible_count)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$build['start']['#title'] = $this->formatPlural($count, '1 pending update', '@count pending updates');
|
||||
}
|
||||
// @todo Simplify with https://www.drupal.org/node/2548095
|
||||
$base_url = str_replace('/update.php', '', $request->getBaseUrl());
|
||||
$url = (new Url('system.db_update', array('op' => 'run')))->setOption('base_url', $base_url);
|
||||
$build['link'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Apply pending updates'),
|
||||
'#attributes' => array('class' => array('button', 'button--primary')),
|
||||
'#weight' => 5,
|
||||
'#url' => $url,
|
||||
'#access' => $url->access($this->currentUser()),
|
||||
);
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays results of the update script with any accompanying errors.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @return array
|
||||
* A render array.
|
||||
*/
|
||||
protected function results(Request $request) {
|
||||
// @todo Simplify with https://www.drupal.org/node/2548095
|
||||
$base_url = str_replace('/update.php', '', $request->getBaseUrl());
|
||||
|
||||
// Report end result.
|
||||
$dblog_exists = $this->moduleHandler->moduleExists('dblog');
|
||||
if ($dblog_exists && $this->account->hasPermission('access site reports')) {
|
||||
$log_message = $this->t('All errors have been <a href=":url">logged</a>.', array(
|
||||
':url' => Url::fromRoute('dblog.overview')->setOption('base_url', $base_url)->toString(TRUE)->getGeneratedUrl(),
|
||||
));
|
||||
}
|
||||
else {
|
||||
$log_message = $this->t('All errors have been logged.');
|
||||
}
|
||||
|
||||
if (!empty($_SESSION['update_success'])) {
|
||||
$message = '<p>' . $this->t('Updates were attempted. If you see no failures below, you may proceed happily back to your <a href=":url">site</a>. Otherwise, you may need to update your database manually.', array(':url' => Url::fromRoute('<front>')->setOption('base_url', $base_url)->toString(TRUE)->getGeneratedUrl())) . ' ' . $log_message . '</p>';
|
||||
}
|
||||
else {
|
||||
$last = reset($_SESSION['updates_remaining']);
|
||||
list($module, $version) = array_pop($last);
|
||||
$message = '<p class="error">' . $this->t('The update process was aborted prematurely while running <strong>update #@version in @module.module</strong>.', array(
|
||||
'@version' => $version,
|
||||
'@module' => $module,
|
||||
)) . ' ' . $log_message;
|
||||
if ($dblog_exists) {
|
||||
$message .= ' ' . $this->t('You may need to check the <code>watchdog</code> database table manually.');
|
||||
}
|
||||
$message .= '</p>';
|
||||
}
|
||||
|
||||
if (Settings::get('update_free_access')) {
|
||||
$message .= '<p>' . $this->t("<strong>Reminder: don't forget to set the <code>\$settings['update_free_access']</code> value in your <code>settings.php</code> file back to <code>FALSE</code>.</strong>") . '</p>';
|
||||
}
|
||||
|
||||
$build['message'] = array(
|
||||
'#markup' => $message,
|
||||
);
|
||||
$build['links'] = array(
|
||||
'#theme' => 'links',
|
||||
'#links' => $this->helpfulLinks($request),
|
||||
);
|
||||
|
||||
// Output a list of info messages.
|
||||
if (!empty($_SESSION['update_results'])) {
|
||||
$all_messages = array();
|
||||
foreach ($_SESSION['update_results'] as $module => $updates) {
|
||||
if ($module != '#abort') {
|
||||
$module_has_message = FALSE;
|
||||
$info_messages = array();
|
||||
foreach ($updates as $name => $queries) {
|
||||
$messages = array();
|
||||
foreach ($queries as $query) {
|
||||
// If there is no message for this update, don't show anything.
|
||||
if (empty($query['query'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($query['success']) {
|
||||
$messages[] = array(
|
||||
'#wrapper_attributes' => array('class' => array('success')),
|
||||
'#markup' => $query['query'],
|
||||
);
|
||||
}
|
||||
else {
|
||||
$messages[] = array(
|
||||
'#wrapper_attributes' => array('class' => array('failure')),
|
||||
'#markup' => '<strong>' . $this->t('Failed:') . '</strong> ' . $query['query'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($messages) {
|
||||
$module_has_message = TRUE;
|
||||
if (is_numeric($name)) {
|
||||
$title = $this->t('Update #@count', ['@count' => $name]);
|
||||
}
|
||||
else {
|
||||
$title = $this->t('Update @name', ['@name' => trim($name, '_')]);
|
||||
}
|
||||
$info_messages[] = array(
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $messages,
|
||||
'#title' => $title,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// If there were any messages then prefix them with the module name
|
||||
// and add it to the global message list.
|
||||
if ($module_has_message) {
|
||||
$all_messages[] = array(
|
||||
'#type' => 'container',
|
||||
'#prefix' => '<h3>' . $this->t('@module module', array('@module' => $module)) . '</h3>',
|
||||
'#children' => $info_messages,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($all_messages) {
|
||||
$build['query_messages'] = array(
|
||||
'#type' => 'container',
|
||||
'#children' => $all_messages,
|
||||
'#attributes' => array('class' => array('update-results')),
|
||||
'#prefix' => '<h2>' . $this->t('The following updates returned messages:') . '</h2>',
|
||||
);
|
||||
}
|
||||
}
|
||||
unset($_SESSION['update_results']);
|
||||
unset($_SESSION['update_success']);
|
||||
unset($_SESSION['update_ignore_warnings']);
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a list of requirement errors or warnings.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @return array
|
||||
* A render array.
|
||||
*/
|
||||
public function requirements($severity, array $requirements, Request $request) {
|
||||
$options = $severity == REQUIREMENT_WARNING ? array('continue' => 1) : array();
|
||||
// @todo Revisit once https://www.drupal.org/node/2548095 is in. Something
|
||||
// like Url::fromRoute('system.db_update')->setOptions() should then be
|
||||
// possible.
|
||||
$try_again_url = Url::fromUri($request->getUriForPath(''))->setOptions(['query' => $options])->toString(TRUE)->getGeneratedUrl();
|
||||
|
||||
$build['status_report'] = array(
|
||||
'#theme' => 'status_report',
|
||||
'#requirements' => $requirements,
|
||||
'#suffix' => $this->t('Check the messages and <a href=":url">try again</a>.', array(':url' => $try_again_url))
|
||||
);
|
||||
|
||||
$build['#title'] = $this->t('Requirements problem');
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the update task list render array.
|
||||
*
|
||||
* @param string $active
|
||||
* The active task.
|
||||
* Can be one of 'requirements', 'info', 'selection', 'run', 'results'.
|
||||
*
|
||||
* @return array
|
||||
* A render array.
|
||||
*/
|
||||
protected function updateTasksList($active = NULL) {
|
||||
// Default list of tasks.
|
||||
$tasks = array(
|
||||
'requirements' => $this->t('Verify requirements'),
|
||||
'info' => $this->t('Overview'),
|
||||
'selection' => $this->t('Review updates'),
|
||||
'run' => $this->t('Run updates'),
|
||||
'results' => $this->t('Review log'),
|
||||
);
|
||||
|
||||
$task_list = array(
|
||||
'#theme' => 'maintenance_task_list',
|
||||
'#items' => $tasks,
|
||||
'#active' => $active,
|
||||
);
|
||||
return $task_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the database update batch process.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request object.
|
||||
*/
|
||||
protected function triggerBatch(Request $request) {
|
||||
$maintenance_mode = $this->state->get('system.maintenance_mode', FALSE);
|
||||
// Store the current maintenance mode status in the session so that it can
|
||||
// be restored at the end of the batch.
|
||||
$_SESSION['maintenance_mode'] = $maintenance_mode;
|
||||
// During the update, always put the site into maintenance mode so that
|
||||
// in-progress schema changes do not affect visiting users.
|
||||
if (empty($maintenance_mode)) {
|
||||
$this->state->set('system.maintenance_mode', TRUE);
|
||||
}
|
||||
|
||||
$operations = array();
|
||||
|
||||
// Resolve any update dependencies to determine the actual updates that will
|
||||
// be run and the order they will be run in.
|
||||
$start = $this->getModuleUpdates();
|
||||
$updates = update_resolve_dependencies($start);
|
||||
|
||||
// Store the dependencies for each update function in an array which the
|
||||
// batch API can pass in to the batch operation each time it is called. (We
|
||||
// do not store the entire update dependency array here because it is
|
||||
// potentially very large.)
|
||||
$dependency_map = array();
|
||||
foreach ($updates as $function => $update) {
|
||||
$dependency_map[$function] = !empty($update['reverse_paths']) ? array_keys($update['reverse_paths']) : array();
|
||||
}
|
||||
|
||||
// Determine updates to be performed.
|
||||
foreach ($updates as $function => $update) {
|
||||
if ($update['allowed']) {
|
||||
// Set the installed version of each module so updates will start at the
|
||||
// correct place. (The updates are already sorted, so we can simply base
|
||||
// this on the first one we come across in the above foreach loop.)
|
||||
if (isset($start[$update['module']])) {
|
||||
drupal_set_installed_schema_version($update['module'], $update['number'] - 1);
|
||||
unset($start[$update['module']]);
|
||||
}
|
||||
$operations[] = array('update_do_one', array($update['module'], $update['number'], $dependency_map[$function]));
|
||||
}
|
||||
}
|
||||
|
||||
$post_updates = $this->postUpdateRegistry->getPendingUpdateFunctions();
|
||||
|
||||
if ($post_updates) {
|
||||
// Now we rebuild all caches and after that execute the hook_post_update()
|
||||
// functions.
|
||||
$operations[] = ['drupal_flush_all_caches', []];
|
||||
foreach ($post_updates as $function) {
|
||||
$operations[] = ['update_invoke_post_update', [$function]];
|
||||
}
|
||||
}
|
||||
|
||||
$batch['operations'] = $operations;
|
||||
$batch += array(
|
||||
'title' => $this->t('Updating'),
|
||||
'init_message' => $this->t('Starting updates'),
|
||||
'error_message' => $this->t('An unrecoverable error has occurred. You can find the error message below. It is advised to copy it to the clipboard for reference.'),
|
||||
'finished' => array('\Drupal\system\Controller\DbUpdateController', 'batchFinished'),
|
||||
);
|
||||
batch_set($batch);
|
||||
|
||||
// @todo Revisit once https://www.drupal.org/node/2548095 is in.
|
||||
return batch_process(Url::fromUri('base://results'), Url::fromUri('base://start'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes the update process and stores the results for eventual display.
|
||||
*
|
||||
* After the updates run, all caches are flushed. The update results are
|
||||
* stored into the session (for example, to be displayed on the update results
|
||||
* page in update.php). Additionally, if the site was off-line, now that the
|
||||
* update process is completed, the site is set back online.
|
||||
*
|
||||
* @param $success
|
||||
* Indicate that the batch API tasks were all completed successfully.
|
||||
* @param array $results
|
||||
* An array of all the results that were updated in update_do_one().
|
||||
* @param array $operations
|
||||
* A list of all the operations that had not been completed by the batch API.
|
||||
*/
|
||||
public static function batchFinished($success, $results, $operations) {
|
||||
// No updates to run, so caches won't get flushed later. Clear them now.
|
||||
drupal_flush_all_caches();
|
||||
|
||||
$_SESSION['update_results'] = $results;
|
||||
$_SESSION['update_success'] = $success;
|
||||
$_SESSION['updates_remaining'] = $operations;
|
||||
|
||||
// Now that the update is done, we can put the site back online if it was
|
||||
// previously not in maintenance mode.
|
||||
if (empty($_SESSION['maintenance_mode'])) {
|
||||
\Drupal::state()->set('system.maintenance_mode', FALSE);
|
||||
}
|
||||
unset($_SESSION['maintenance_mode']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides links to the homepage and administration pages.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @return array
|
||||
* An array of links.
|
||||
*/
|
||||
protected function helpfulLinks(Request $request) {
|
||||
// @todo Simplify with https://www.drupal.org/node/2548095
|
||||
$base_url = str_replace('/update.php', '', $request->getBaseUrl());
|
||||
$links['front'] = array(
|
||||
'title' => $this->t('Front page'),
|
||||
'url' => Url::fromRoute('<front>')->setOption('base_url', $base_url),
|
||||
);
|
||||
if ($this->account->hasPermission('access administration pages')) {
|
||||
$links['admin-pages'] = array(
|
||||
'title' => $this->t('Administration pages'),
|
||||
'url' => Url::fromRoute('system.admin')->setOption('base_url', $base_url),
|
||||
);
|
||||
}
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves module updates.
|
||||
*
|
||||
* @return array
|
||||
* The module updates that can be performed.
|
||||
*/
|
||||
protected function getModuleUpdates() {
|
||||
$return = array();
|
||||
$updates = update_get_update_list();
|
||||
foreach ($updates as $module => $update) {
|
||||
$return[$module] = $update['start'];
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Controller;
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Component\Utility\Tags;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Entity\EntityAutocompleteMatcher;
|
||||
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
|
||||
/**
|
||||
* Defines a route controller for entity autocomplete form elements.
|
||||
*/
|
||||
class EntityAutocompleteController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The autocomplete matcher for entity references.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityAutocompleteMatcher
|
||||
*/
|
||||
protected $matcher;
|
||||
|
||||
/**
|
||||
* The key value store.
|
||||
*
|
||||
* @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
|
||||
*/
|
||||
protected $keyValue;
|
||||
|
||||
/**
|
||||
* Constructs a EntityAutocompleteController object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityAutocompleteMatcher $matcher
|
||||
* The autocomplete matcher for entity references.
|
||||
* @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $key_value
|
||||
* The key value factory.
|
||||
*/
|
||||
public function __construct(EntityAutocompleteMatcher $matcher, KeyValueStoreInterface $key_value) {
|
||||
$this->matcher = $matcher;
|
||||
$this->keyValue = $key_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.autocomplete_matcher'),
|
||||
$container->get('keyvalue')->get('entity_autocomplete')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Autocomplete the label of an entity.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request object that contains the typed tags.
|
||||
* @param string $target_type
|
||||
* The ID of the target entity type.
|
||||
* @param string $selection_handler
|
||||
* The plugin ID of the entity reference selection handler.
|
||||
* @param string $selection_settings_key
|
||||
* The hashed key of the key/value entry that holds the selection handler
|
||||
* settings.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
* The matched entity labels as a JSON response.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
|
||||
* Thrown if the selection settings key is not found in the key/value store
|
||||
* or if it does not match the stored data.
|
||||
*/
|
||||
public function handleAutocomplete(Request $request, $target_type, $selection_handler, $selection_settings_key) {
|
||||
$matches = array();
|
||||
// Get the typed string from the URL, if it exists.
|
||||
if ($input = $request->query->get('q')) {
|
||||
$typed_string = Tags::explode($input);
|
||||
$typed_string = Unicode::strtolower(array_pop($typed_string));
|
||||
|
||||
// Selection settings are passed in as a hashed key of a serialized array
|
||||
// stored in the key/value store.
|
||||
$selection_settings = $this->keyValue->get($selection_settings_key, FALSE);
|
||||
if ($selection_settings !== FALSE) {
|
||||
$selection_settings_hash = Crypt::hmacBase64(serialize($selection_settings) . $target_type . $selection_handler, Settings::getHashSalt());
|
||||
if ($selection_settings_hash !== $selection_settings_key) {
|
||||
// Disallow access when the selection settings hash does not match the
|
||||
// passed-in key.
|
||||
throw new AccessDeniedHttpException('Invalid selection settings key.');
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Disallow access when the selection settings key is not found in the
|
||||
// key/value store.
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
|
||||
$matches = $this->matcher->getMatches($target_type, $selection_handler, $selection_settings, $typed_string);
|
||||
}
|
||||
|
||||
return new JsonResponse($matches);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
|
||||
/**
|
||||
* Controller for default HTTP 4xx responses.
|
||||
*/
|
||||
class Http4xxController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The default 401 content.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing the message to display for 401 pages.
|
||||
*/
|
||||
public function on401() {
|
||||
return [
|
||||
'#markup' => $this->t('Please log in to access this page.'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The default 403 content.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing the message to display for 404 pages.
|
||||
*/
|
||||
public function on403() {
|
||||
return [
|
||||
'#markup' => $this->t('You are not authorized to access this page.'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The default 404 content.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing the message to display for 404 pages.
|
||||
*/
|
||||
public function on404() {
|
||||
return [
|
||||
'#markup' => $this->t('The requested page could not be found.'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Controller;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Core\Form\FormBuilderInterface;
|
||||
use Drupal\Core\Menu\MenuLinkTreeInterface;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\Core\Theme\ThemeAccessCheck;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\system\SystemManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Returns responses for System routes.
|
||||
*/
|
||||
class SystemController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The entity query factory object.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
protected $queryFactory;
|
||||
|
||||
/**
|
||||
* System Manager Service.
|
||||
*
|
||||
* @var \Drupal\system\SystemManager
|
||||
*/
|
||||
protected $systemManager;
|
||||
|
||||
/**
|
||||
* The theme access checker service.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeAccessCheck
|
||||
*/
|
||||
protected $themeAccess;
|
||||
|
||||
/**
|
||||
* The form builder service.
|
||||
*
|
||||
* @var \Drupal\Core\Form\FormBuilderInterface
|
||||
*/
|
||||
protected $formBuilder;
|
||||
|
||||
/**
|
||||
* The theme handler service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* The menu link tree service.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuLinkTreeInterface
|
||||
*/
|
||||
protected $menuLinkTree;
|
||||
|
||||
/**
|
||||
* Constructs a new SystemController.
|
||||
*
|
||||
* @param \Drupal\system\SystemManager $systemManager
|
||||
* System manager service.
|
||||
* @param \Drupal\Core\Entity\Query\QueryFactory $queryFactory
|
||||
* The entity query object.
|
||||
* @param \Drupal\Core\Theme\ThemeAccessCheck $theme_access
|
||||
* The theme access checker service.
|
||||
* @param \Drupal\Core\Form\FormBuilderInterface $form_builder
|
||||
* The form builder.
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
* @param \Drupal\Core\Menu\MenuLinkTreeInterface
|
||||
* The menu link tree service.
|
||||
*/
|
||||
public function __construct(SystemManager $systemManager, QueryFactory $queryFactory, ThemeAccessCheck $theme_access, FormBuilderInterface $form_builder, ThemeHandlerInterface $theme_handler, MenuLinkTreeInterface $menu_link_tree) {
|
||||
$this->systemManager = $systemManager;
|
||||
$this->queryFactory = $queryFactory;
|
||||
$this->themeAccess = $theme_access;
|
||||
$this->formBuilder = $form_builder;
|
||||
$this->themeHandler = $theme_handler;
|
||||
$this->menuLinkTree = $menu_link_tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('system.manager'),
|
||||
$container->get('entity.query'),
|
||||
$container->get('access_check.theme'),
|
||||
$container->get('form_builder'),
|
||||
$container->get('theme_handler'),
|
||||
$container->get('menu.link_tree')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the administration overview page.
|
||||
*
|
||||
* @param string $link_id
|
||||
* The ID of the administrative path link for which to display child links.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array of the administration overview page.
|
||||
*/
|
||||
public function overview($link_id) {
|
||||
// Check for status report errors.
|
||||
if ($this->systemManager->checkRequirements() && $this->currentUser()->hasPermission('administer site configuration')) {
|
||||
drupal_set_message($this->t('One or more problems were detected with your Drupal installation. Check the <a href=":status">status report</a> for more information.', array(':status' => $this->url('system.status'))), 'error');
|
||||
}
|
||||
// Load all menu links below it.
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->setRoot($link_id)->excludeRoot()->setTopLevelOnly()->onlyEnabledLinks();
|
||||
$tree = $this->menuLinkTree->load(NULL, $parameters);
|
||||
$manipulators = array(
|
||||
array('callable' => 'menu.default_tree_manipulators:checkAccess'),
|
||||
array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
|
||||
);
|
||||
$tree = $this->menuLinkTree->transform($tree, $manipulators);
|
||||
$tree_access_cacheability = new CacheableMetadata();
|
||||
$blocks = array();
|
||||
foreach ($tree as $key => $element) {
|
||||
$tree_access_cacheability = $tree_access_cacheability->merge(CacheableMetadata::createFromObject($element->access));
|
||||
|
||||
// Only render accessible links.
|
||||
if (!$element->access->isAllowed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$link = $element->link;
|
||||
$block['title'] = $link->getTitle();
|
||||
$block['description'] = $link->getDescription();
|
||||
$block['content'] = array(
|
||||
'#theme' => 'admin_block_content',
|
||||
'#content' => $this->systemManager->getAdminBlock($link),
|
||||
);
|
||||
|
||||
if (!empty($block['content']['#content'])) {
|
||||
$blocks[$key] = $block;
|
||||
}
|
||||
}
|
||||
|
||||
if ($blocks) {
|
||||
ksort($blocks);
|
||||
$build = [
|
||||
'#theme' => 'admin_page',
|
||||
'#blocks' => $blocks,
|
||||
];
|
||||
$tree_access_cacheability->applyTo($build);
|
||||
return $build;
|
||||
}
|
||||
else {
|
||||
$build = [
|
||||
'#markup' => $this->t('You do not have any administrative items.'),
|
||||
];
|
||||
$tree_access_cacheability->applyTo($build);
|
||||
return $build;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the admin menu is in compact mode or not.
|
||||
*
|
||||
* @param string $mode
|
||||
* Valid values are 'on' and 'off'.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse
|
||||
*/
|
||||
public function compactPage($mode) {
|
||||
user_cookie_save(array('admin_compact_mode' => ($mode == 'on')));
|
||||
return $this->redirect('<front>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a single block from the administration menu as a page.
|
||||
*/
|
||||
public function systemAdminMenuBlockPage() {
|
||||
return $this->systemManager->getBlockContents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a theme listing.
|
||||
*
|
||||
* @return string
|
||||
* An HTML string of the theme listing page.
|
||||
*
|
||||
* @todo Move into ThemeController.
|
||||
*/
|
||||
public function themesPage() {
|
||||
$config = $this->config('system.theme');
|
||||
// Get all available themes.
|
||||
$themes = $this->themeHandler->rebuildThemeData();
|
||||
uasort($themes, 'system_sort_modules_by_info_name');
|
||||
|
||||
$theme_default = $config->get('default');
|
||||
$theme_groups = array('installed' => array(), 'uninstalled' => array());
|
||||
$admin_theme = $config->get('admin');
|
||||
$admin_theme_options = array();
|
||||
|
||||
foreach ($themes as &$theme) {
|
||||
if (!empty($theme->info['hidden'])) {
|
||||
continue;
|
||||
}
|
||||
$theme->is_default = ($theme->getName() == $theme_default);
|
||||
$theme->is_admin = ($theme->getName() == $admin_theme || ($theme->is_default && $admin_theme == '0'));
|
||||
|
||||
// Identify theme screenshot.
|
||||
$theme->screenshot = NULL;
|
||||
// Create a list which includes the current theme and all its base themes.
|
||||
if (isset($themes[$theme->getName()]->base_themes)) {
|
||||
$theme_keys = array_keys($themes[$theme->getName()]->base_themes);
|
||||
$theme_keys[] = $theme->getName();
|
||||
}
|
||||
else {
|
||||
$theme_keys = array($theme->getName());
|
||||
}
|
||||
// Look for a screenshot in the current theme or in its closest ancestor.
|
||||
foreach (array_reverse($theme_keys) as $theme_key) {
|
||||
if (isset($themes[$theme_key]) && file_exists($themes[$theme_key]->info['screenshot'])) {
|
||||
$theme->screenshot = array(
|
||||
'uri' => $themes[$theme_key]->info['screenshot'],
|
||||
'alt' => $this->t('Screenshot for @theme theme', array('@theme' => $theme->info['name'])),
|
||||
'title' => $this->t('Screenshot for @theme theme', array('@theme' => $theme->info['name'])),
|
||||
'attributes' => array('class' => array('screenshot')),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($theme->status)) {
|
||||
// Ensure this theme is compatible with this version of core.
|
||||
$theme->incompatible_core = !isset($theme->info['core']) || ($theme->info['core'] != \DRUPAL::CORE_COMPATIBILITY);
|
||||
// Require the 'content' region to make sure the main page
|
||||
// content has a common place in all themes.
|
||||
$theme->incompatible_region = !isset($theme->info['regions']['content']);
|
||||
$theme->incompatible_php = version_compare(phpversion(), $theme->info['php']) < 0;
|
||||
// Confirm that all base themes are available.
|
||||
$theme->incompatible_base = (isset($theme->info['base theme']) && !($theme->base_themes === array_filter($theme->base_themes)));
|
||||
// Confirm that the theme engine is available.
|
||||
$theme->incompatible_engine = isset($theme->info['engine']) && !isset($theme->owner);
|
||||
}
|
||||
$theme->operations = array();
|
||||
if (!empty($theme->status) || !$theme->incompatible_core && !$theme->incompatible_php && !$theme->incompatible_base && !$theme->incompatible_engine) {
|
||||
// Create the operations links.
|
||||
$query['theme'] = $theme->getName();
|
||||
if ($this->themeAccess->checkAccess($theme->getName())) {
|
||||
$theme->operations[] = array(
|
||||
'title' => $this->t('Settings'),
|
||||
'url' => Url::fromRoute('system.theme_settings_theme', ['theme' => $theme->getName()]),
|
||||
'attributes' => array('title' => $this->t('Settings for @theme theme', array('@theme' => $theme->info['name']))),
|
||||
);
|
||||
}
|
||||
if (!empty($theme->status)) {
|
||||
if (!$theme->is_default) {
|
||||
$theme_uninstallable = TRUE;
|
||||
if ($theme->getName() == $admin_theme) {
|
||||
$theme_uninstallable = FALSE;
|
||||
}
|
||||
// Check it isn't the base of theme of an installed theme.
|
||||
foreach ($theme->required_by as $themename => $dependency) {
|
||||
if (!empty($themes[$themename]->status)) {
|
||||
$theme_uninstallable = FALSE;
|
||||
}
|
||||
}
|
||||
if ($theme_uninstallable) {
|
||||
$theme->operations[] = array(
|
||||
'title' => $this->t('Uninstall'),
|
||||
'url' => Url::fromRoute('system.theme_uninstall'),
|
||||
'query' => $query,
|
||||
'attributes' => array('title' => $this->t('Uninstall @theme theme', array('@theme' => $theme->info['name']))),
|
||||
);
|
||||
}
|
||||
$theme->operations[] = array(
|
||||
'title' => $this->t('Set as default'),
|
||||
'url' => Url::fromRoute('system.theme_set_default'),
|
||||
'query' => $query,
|
||||
'attributes' => array('title' => $this->t('Set @theme as default theme', array('@theme' => $theme->info['name']))),
|
||||
);
|
||||
}
|
||||
$admin_theme_options[$theme->getName()] = $theme->info['name'];
|
||||
}
|
||||
else {
|
||||
$theme->operations[] = array(
|
||||
'title' => $this->t('Install'),
|
||||
'url' => Url::fromRoute('system.theme_install'),
|
||||
'query' => $query,
|
||||
'attributes' => array('title' => $this->t('Install @theme theme', array('@theme' => $theme->info['name']))),
|
||||
);
|
||||
$theme->operations[] = array(
|
||||
'title' => $this->t('Install and set as default'),
|
||||
'url' => Url::fromRoute('system.theme_set_default'),
|
||||
'query' => $query,
|
||||
'attributes' => array('title' => $this->t('Install @theme as default theme', array('@theme' => $theme->info['name']))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Add notes to default and administration theme.
|
||||
$theme->notes = array();
|
||||
if ($theme->is_default) {
|
||||
$theme->notes[] = $this->t('default theme');
|
||||
}
|
||||
if ($theme->is_admin) {
|
||||
$theme->notes[] = $this->t('admin theme');
|
||||
}
|
||||
|
||||
// Sort installed and uninstalled themes into their own groups.
|
||||
$theme_groups[$theme->status ? 'installed' : 'uninstalled'][] = $theme;
|
||||
}
|
||||
|
||||
// There are two possible theme groups.
|
||||
$theme_group_titles = array(
|
||||
'installed' => $this->formatPlural(count($theme_groups['installed']), 'Installed theme', 'Installed themes'),
|
||||
);
|
||||
if (!empty($theme_groups['uninstalled'])) {
|
||||
$theme_group_titles['uninstalled'] = $this->formatPlural(count($theme_groups['uninstalled']), 'Uninstalled theme', 'Uninstalled themes');
|
||||
}
|
||||
|
||||
uasort($theme_groups['installed'], 'system_sort_themes');
|
||||
$this->moduleHandler()->alter('system_themes_page', $theme_groups);
|
||||
|
||||
$build = array();
|
||||
$build[] = array(
|
||||
'#theme' => 'system_themes_page',
|
||||
'#theme_groups' => $theme_groups,
|
||||
'#theme_group_titles' => $theme_group_titles,
|
||||
);
|
||||
$build[] = $this->formBuilder->getForm('Drupal\system\Form\ThemeAdminForm', $admin_theme_options);
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Controller;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\system\SystemManager;
|
||||
|
||||
/**
|
||||
* Returns responses for System Info routes.
|
||||
*/
|
||||
class SystemInfoController implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* System Manager Service.
|
||||
*
|
||||
* @var \Drupal\system\SystemManager
|
||||
*/
|
||||
protected $systemManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('system.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a SystemInfoController object.
|
||||
*
|
||||
* @param \Drupal\system\SystemManager $systemManager
|
||||
* System manager service.
|
||||
*/
|
||||
public function __construct(SystemManager $systemManager) {
|
||||
$this->systemManager = $systemManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the site status report.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing a list of system requirements for the Drupal
|
||||
* installation and whether this installation meets the requirements.
|
||||
*/
|
||||
public function status() {
|
||||
$requirements = $this->systemManager->listRequirements();
|
||||
return array('#theme' => 'status_report', '#requirements' => $requirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of phpinfo().
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* A response object to be sent to the client.
|
||||
*/
|
||||
public function php() {
|
||||
if (function_exists('phpinfo')) {
|
||||
ob_start();
|
||||
phpinfo();
|
||||
$output = ob_get_clean();
|
||||
}
|
||||
else {
|
||||
$output = t('The phpinfo() function has been disabled for security reasons. For more information, visit <a href=":phpinfo">Enabling and disabling phpinfo()</a> handbook page.', array(':phpinfo' => 'https://www.drupal.org/node/243993'));
|
||||
}
|
||||
return new Response($output);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Controller;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Config\PreExistingConfigException;
|
||||
use Drupal\Core\Config\UnmetDependenciesException;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
|
||||
/**
|
||||
* Controller for theme handling.
|
||||
*/
|
||||
class ThemeController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The theme handler service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* Constructs a new ThemeController.
|
||||
*
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
*/
|
||||
public function __construct(ThemeHandlerInterface $theme_handler, ConfigFactoryInterface $config_factory) {
|
||||
$this->themeHandler = $theme_handler;
|
||||
$this->configFactory = $config_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('theme_handler'),
|
||||
$container->get('config.factory')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstalls a theme.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* A request object containing a theme name and a valid token.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse
|
||||
* Redirects back to the appearance admin page.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
|
||||
* Throws access denied when no theme or token is set in the request or when
|
||||
* the token is invalid.
|
||||
*/
|
||||
public function uninstall(Request $request) {
|
||||
$theme = $request->get('theme');
|
||||
$config = $this->config('system.theme');
|
||||
|
||||
if (isset($theme)) {
|
||||
// Get current list of themes.
|
||||
$themes = $this->themeHandler->listInfo();
|
||||
|
||||
// Check if the specified theme is one recognized by the system.
|
||||
if (!empty($themes[$theme])) {
|
||||
// Do not uninstall the default or admin theme.
|
||||
if ($theme === $config->get('default') || $theme === $config->get('admin')) {
|
||||
drupal_set_message($this->t('%theme is the default theme and cannot be uninstalled.', array('%theme' => $themes[$theme]->info['name'])), 'error');
|
||||
}
|
||||
else {
|
||||
$this->themeHandler->uninstall(array($theme));
|
||||
drupal_set_message($this->t('The %theme theme has been uninstalled.', array('%theme' => $themes[$theme]->info['name'])));
|
||||
}
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
|
||||
}
|
||||
|
||||
return $this->redirect('system.themes_page');
|
||||
}
|
||||
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a theme.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* A request object containing a theme name and a valid token.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse
|
||||
* Redirects back to the appearance admin page.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
|
||||
* Throws access denied when no theme or token is set in the request or when
|
||||
* the token is invalid.
|
||||
*/
|
||||
public function install(Request $request) {
|
||||
$theme = $request->get('theme');
|
||||
|
||||
if (isset($theme)) {
|
||||
try {
|
||||
if ($this->themeHandler->install(array($theme))) {
|
||||
$themes = $this->themeHandler->listInfo();
|
||||
drupal_set_message($this->t('The %theme theme has been installed.', array('%theme' => $themes[$theme]->info['name'])));
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
|
||||
}
|
||||
}
|
||||
catch (PreExistingConfigException $e) {
|
||||
$config_objects = $e->flattenConfigObjects($e->getConfigObjects());
|
||||
drupal_set_message(
|
||||
$this->formatPlural(
|
||||
count($config_objects),
|
||||
'Unable to install @extension, %config_names already exists in active configuration.',
|
||||
'Unable to install @extension, %config_names already exist in active configuration.',
|
||||
array(
|
||||
'%config_names' => implode(', ', $config_objects),
|
||||
'@extension' => $theme,
|
||||
)),
|
||||
'error'
|
||||
);
|
||||
}
|
||||
catch (UnmetDependenciesException $e) {
|
||||
drupal_set_message($e->getTranslatedMessage($this->getStringTranslation(), $theme), 'error');
|
||||
}
|
||||
|
||||
return $this->redirect('system.themes_page');
|
||||
}
|
||||
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default theme.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* A request object containing a theme name.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse
|
||||
* Redirects back to the appearance admin page.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
|
||||
* Throws access denied when no theme is set in the request.
|
||||
*/
|
||||
public function setDefaultTheme(Request $request) {
|
||||
$config = $this->configFactory->getEditable('system.theme');
|
||||
$theme = $request->query->get('theme');
|
||||
|
||||
if (isset($theme)) {
|
||||
// Get current list of themes.
|
||||
$themes = $this->themeHandler->listInfo();
|
||||
|
||||
// Check if the specified theme is one recognized by the system.
|
||||
// Or try to install the theme.
|
||||
if (isset($themes[$theme]) || $this->themeHandler->install(array($theme))) {
|
||||
$themes = $this->themeHandler->listInfo();
|
||||
|
||||
// Set the default theme.
|
||||
$config->set('default', $theme)->save();
|
||||
|
||||
// The status message depends on whether an admin theme is currently in
|
||||
// use: a value of 0 means the admin theme is set to be the default
|
||||
// theme.
|
||||
$admin_theme = $config->get('admin');
|
||||
if ($admin_theme != 0 && $admin_theme != $theme) {
|
||||
drupal_set_message($this->t('Please note that the administration theme is still set to the %admin_theme theme; consequently, the theme on this page remains unchanged. All non-administrative sections of the site, however, will show the selected %selected_theme theme by default.', array(
|
||||
'%admin_theme' => $themes[$admin_theme]->info['name'],
|
||||
'%selected_theme' => $themes[$theme]->info['name'],
|
||||
)));
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('%theme is now the default theme.', array('%theme' => $themes[$theme]->info['name'])));
|
||||
}
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
|
||||
}
|
||||
|
||||
return $this->redirect('system.themes_page');
|
||||
|
||||
}
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Controller;
|
||||
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
|
||||
/**
|
||||
* Provides a callback for finding out a timezone name.
|
||||
*/
|
||||
class TimezoneController {
|
||||
|
||||
/**
|
||||
* Retrieve a JSON object containing a time zone name given a timezone
|
||||
* abbreviation.
|
||||
*
|
||||
* @param string $abbreviation
|
||||
* Time zone abbreviation.
|
||||
* @param int $offset
|
||||
* Offset from GMT in seconds. Defaults to -1 which means that first found
|
||||
* time zone corresponding to abbr is returned. Otherwise exact offset is
|
||||
* searched and only if not found then the first time zone with any offset
|
||||
* is returned.
|
||||
* @param null|bool $is_daylight_saving_time
|
||||
* Daylight saving time indicator. If abbr does not exist then the time
|
||||
* zone is searched solely by offset and isdst.
|
||||
*
|
||||
* @return JsonResponse
|
||||
* The timezone name in JsonResponse object.
|
||||
*/
|
||||
public function getTimezone($abbreviation = '', $offset = -1, $is_daylight_saving_time = NULL) {
|
||||
// An abbreviation of "0" passed in the callback arguments should be
|
||||
// interpreted as the empty string.
|
||||
$abbreviation = $abbreviation ? $abbreviation : '';
|
||||
$timezone = timezone_name_from_abbr($abbreviation, intval($offset), $is_daylight_saving_time);
|
||||
return new JsonResponse($timezone);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\CronInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Controller for Cron handling.
|
||||
*/
|
||||
class CronController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The cron service.
|
||||
*
|
||||
* @var \Drupal\Core\CronInterface
|
||||
*/
|
||||
protected $cron;
|
||||
|
||||
/**
|
||||
* Constructs a CronController object.
|
||||
*
|
||||
* @param \Drupal\Core\CronInterface $cron
|
||||
* The cron service.
|
||||
*/
|
||||
public function __construct(CronInterface $cron) {
|
||||
$this->cron = $cron;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static($container->get('cron'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run Cron once.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* A Symfony response object.
|
||||
*/
|
||||
public function run() {
|
||||
$this->cron->run();
|
||||
|
||||
// HTTP 204 is "No content", meaning "I did what you asked and we're done."
|
||||
return new Response('', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run cron manually.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse
|
||||
* A Symfony direct response object.
|
||||
*/
|
||||
public function runManually() {
|
||||
if ($this->cron->run()) {
|
||||
drupal_set_message($this->t('Cron ran successfully.'));
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('Cron run failed.'), 'error');
|
||||
}
|
||||
|
||||
return $this->redirect('system.status');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Defines the access control handler for the date format entity type.
|
||||
*
|
||||
* @see \Drupal\system\Entity\DateFormat
|
||||
*/
|
||||
class DateFormatAccessControlHandler extends EntityAccessControlHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
|
||||
// There are no restrictions on viewing a date format.
|
||||
if ($operation == 'view') {
|
||||
return AccessResult::allowed();
|
||||
}
|
||||
// Locked date formats cannot be updated or deleted.
|
||||
elseif (in_array($operation, array('update', 'delete'))) {
|
||||
if ($entity->isLocked()) {
|
||||
return AccessResult::forbidden()->addCacheableDependency($entity);
|
||||
}
|
||||
else {
|
||||
return parent::checkAccess($entity, $operation, $account)->addCacheableDependency($entity);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::checkAccess($entity, $operation, $account);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
use Drupal\Core\Datetime\DateFormatterInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a class to build a listing of date format entities.
|
||||
*
|
||||
* @see \Drupal\system\Entity\DateFormat
|
||||
*/
|
||||
class DateFormatListBuilder extends ConfigEntityListBuilder {
|
||||
|
||||
/**
|
||||
* The date formatter service.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\DateFormatterInterface
|
||||
*/
|
||||
protected $dateFormatter;
|
||||
|
||||
/**
|
||||
* Constructs a new DateFormatListBuilder object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage class.
|
||||
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
|
||||
* The date formatter service.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, DateFormatterInterface $date_formatter) {
|
||||
parent::__construct($entity_type, $storage);
|
||||
|
||||
$this->dateFormatter = $date_formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('entity.manager')->getStorage($entity_type->id()),
|
||||
$container->get('date.formatter')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['label'] = t('Name');
|
||||
$header['pattern'] = t('Pattern');
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
$row['label'] = $entity->label();
|
||||
$row['pattern'] = $this->dateFormatter->format(REQUEST_TIME, $entity->id());
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\system\MenuInterface;
|
||||
|
||||
/**
|
||||
* Defines the Menu configuration entity class.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "menu",
|
||||
* label = @Translation("Menu"),
|
||||
* handlers = {
|
||||
* "access" = "Drupal\system\MenuAccessControlHandler"
|
||||
* },
|
||||
* admin_permission = "administer menu",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "label"
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "label",
|
||||
* "description",
|
||||
* "locked",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class Menu extends ConfigEntityBase implements MenuInterface {
|
||||
|
||||
/**
|
||||
* The menu machine name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The human-readable name of the menu entity.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* The menu description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* The locked status of this menu.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $locked = FALSE;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isLocked() {
|
||||
return (bool) $this->locked;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Routing\RouteSubscriberBase;
|
||||
use Drupal\Core\Routing\RoutingEvents;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Adds the _admin_route option to each admin route.
|
||||
*/
|
||||
class AdminRouteSubscriber extends RouteSubscriberBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function alterRoutes(RouteCollection $collection) {
|
||||
foreach ($collection->all() as $route) {
|
||||
if (strpos($route->getPath(), '/admin') === 0 && !$route->hasOption('_admin_route')) {
|
||||
$route->setOption('_admin_route', TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events = parent::getSubscribedEvents();
|
||||
|
||||
// Use a lower priority than \Drupal\field_ui\Routing\RouteSubscriber or
|
||||
// \Drupal\views\EventSubscriber\RouteSubscriber to ensure we add the option
|
||||
// to their routes.
|
||||
$events[RoutingEvents::ALTER] = array('onAlterRoutes', -200);
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
|
||||
use Drupal\Core\Config\ConfigCrudEvent;
|
||||
use Drupal\Core\Config\ConfigEvents;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* A subscriber invalidating cache tags when system config objects are saved.
|
||||
*/
|
||||
class ConfigCacheTag implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* The cache tags invalidator.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
|
||||
*/
|
||||
protected $cacheTagsInvalidator;
|
||||
|
||||
/**
|
||||
* Constructs a ConfigCacheTag object.
|
||||
*
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
* @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cache_tags_invalidator
|
||||
* The cache tags invalidator.
|
||||
*/
|
||||
public function __construct(ThemeHandlerInterface $theme_handler, CacheTagsInvalidatorInterface $cache_tags_invalidator) {
|
||||
$this->themeHandler = $theme_handler;
|
||||
$this->cacheTagsInvalidator = $cache_tags_invalidator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate cache tags when particular system config objects are saved.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigCrudEvent $event
|
||||
* The Event to process.
|
||||
*/
|
||||
public function onSave(ConfigCrudEvent $event) {
|
||||
// Changing the site settings may mean a different route is selected for the
|
||||
// front page. Additionally a change to the site name or similar must
|
||||
// invalidate the render cache since this could be used anywhere.
|
||||
if ($event->getConfig()->getName() === 'system.site') {
|
||||
$this->cacheTagsInvalidator->invalidateTags(['route_match', 'rendered']);
|
||||
}
|
||||
|
||||
// Theme configuration and global theme settings.
|
||||
if (in_array($event->getConfig()->getName(), ['system.theme', 'system.theme.global'], TRUE)) {
|
||||
$this->cacheTagsInvalidator->invalidateTags(['rendered']);
|
||||
}
|
||||
|
||||
// Theme-specific settings, check if this matches a theme settings
|
||||
// configuration object, in that case, clear the rendered cache tag.
|
||||
foreach (array_keys($this->themeHandler->listInfo()) as $theme_name) {
|
||||
if ($theme_name == $event->getConfig()->getName()) {
|
||||
$this->cacheTagsInvalidator->invalidateTags(['rendered']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events[ConfigEvents::SAVE][] = ['onSave'];
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
/**
|
||||
* System file controller.
|
||||
*/
|
||||
class FileDownloadController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* Handles private file transfers.
|
||||
*
|
||||
* Call modules that implement hook_file_download() to find out if a file is
|
||||
* accessible and what headers it should be transferred with. If one or more
|
||||
* modules returned headers the download will start with the returned headers.
|
||||
* If a module returns -1 an AccessDeniedHttpException will be thrown. If the
|
||||
* file exists but no modules responded an AccessDeniedHttpException will be
|
||||
* thrown. If the file does not exist a NotFoundHttpException will be thrown.
|
||||
*
|
||||
* @see hook_file_download()
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request object.
|
||||
* @param string $scheme
|
||||
* The file scheme, defaults to 'private'.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||
* The transferred file as response.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
* Thrown when the requested file does not exist.
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
|
||||
* Thrown when the user does not have access to the file.
|
||||
*/
|
||||
public function download(Request $request, $scheme = 'private') {
|
||||
$target = $request->query->get('file');
|
||||
// Merge remaining path arguments into relative file path.
|
||||
$uri = $scheme . '://' . $target;
|
||||
|
||||
if (file_stream_wrapper_valid_scheme($scheme) && file_exists($uri)) {
|
||||
// Let other modules provide headers and controls access to the file.
|
||||
$headers = $this->moduleHandler()->invokeAll('file_download', array($uri));
|
||||
|
||||
foreach ($headers as $result) {
|
||||
if ($result == -1) {
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
}
|
||||
|
||||
if (count($headers)) {
|
||||
// \Drupal\Core\EventSubscriber\FinishResponseSubscriber::onRespond()
|
||||
// sets response as not cacheable if the Cache-Control header is not
|
||||
// already modified. We pass in FALSE for non-private schemes for the
|
||||
// $public parameter to make sure we don't change the headers.
|
||||
return new BinaryFileResponse($uri, 200, $headers, $scheme !== 'private');
|
||||
}
|
||||
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Form;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\CronInterface;
|
||||
use Drupal\Core\Datetime\DateFormatterInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Configure cron settings for this site.
|
||||
*/
|
||||
class CronForm extends FormBase {
|
||||
|
||||
/**
|
||||
* Stores the state storage service.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* The cron service.
|
||||
*
|
||||
* @var \Drupal\Core\CronInterface
|
||||
*/
|
||||
protected $cron;
|
||||
|
||||
/**
|
||||
* The date formatter service.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\DateFormatterInterface
|
||||
*/
|
||||
protected $dateFormatter;
|
||||
|
||||
/**
|
||||
* The module handler service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* Constructs a CronForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The factory for configuration objects.
|
||||
* @param \Drupal\Core\State\StateInterface $state
|
||||
* The state key value store.
|
||||
* @param \Drupal\Core\CronInterface $cron
|
||||
* The cron service.
|
||||
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
|
||||
* The date formatter service.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler service.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, StateInterface $state, CronInterface $cron, DateFormatterInterface $date_formatter, ModuleHandlerInterface $module_handler) {
|
||||
$this->state = $state;
|
||||
$this->cron = $cron;
|
||||
$this->dateFormatter = $date_formatter;
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('config.factory'),
|
||||
$container->get('state'),
|
||||
$container->get('cron'),
|
||||
$container->get('date.formatter'),
|
||||
$container->get('module_handler')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'system_cron_settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['description'] = array(
|
||||
'#markup' => '<p>' . t('Cron takes care of running periodic tasks like checking for updates and indexing content for search.') . '</p>',
|
||||
);
|
||||
$form['run'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Run cron'),
|
||||
);
|
||||
$status = '<p>' . $this->t('Last run: %time ago.', array('%time' => $this->dateFormatter->formatTimeDiffSince($this->state->get('system.cron_last')))) . '</p>';
|
||||
$form['status'] = array(
|
||||
'#markup' => $status,
|
||||
);
|
||||
|
||||
$cron_url = $this->url('system.cron', array('key' => $this->state->get('system.cron_key')), array('absolute' => TRUE));
|
||||
$form['cron_url'] = array(
|
||||
'#markup' => '<p>' . t('To run cron from outside the site, go to <a href=":cron">@cron</a>', array(':cron' => $cron_url, '@cron' => $cron_url)) . '</p>',
|
||||
);
|
||||
|
||||
if (!$this->moduleHandler->moduleExists('automated_cron')) {
|
||||
$form['cron'] = array(
|
||||
'#markup' => $this->t('Enable the <em>Automated Cron</em> module to allow cron execution at the end of a server response.'),
|
||||
);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs cron and reloads the page.
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// Run cron manually from Cron form.
|
||||
if ($this->cron->run()) {
|
||||
drupal_set_message(t('Cron ran successfully.'));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Cron run failed.'), 'error');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Form;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides a form for adding a date format.
|
||||
*/
|
||||
class DateFormatAddForm extends DateFormatFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = t('Add format');
|
||||
return $actions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Form;
|
||||
|
||||
use Drupal\Core\Datetime\DateFormatterInterface;
|
||||
use Drupal\Core\Entity\EntityDeleteForm;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Builds a form to delete a date format.
|
||||
*/
|
||||
class DateFormatDeleteForm extends EntityDeleteForm {
|
||||
|
||||
/**
|
||||
* The date formatter service.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\DateFormatterInterface
|
||||
*/
|
||||
protected $dateFormatter;
|
||||
|
||||
/**
|
||||
* Constructs an DateFormatDeleteForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
|
||||
* The date formatter service.
|
||||
*/
|
||||
public function __construct(DateFormatterInterface $date_formatter) {
|
||||
$this->dateFormatter = $date_formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('date.formatter')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return t('Are you sure you want to delete the format %name : %format?', array(
|
||||
'%name' => $this->entity->label(),
|
||||
'%format' => $this->dateFormatter->format(REQUEST_TIME, $this->entity->id()))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Form;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides a form for editing a date format.
|
||||
*/
|
||||
class DateFormatEditForm extends DateFormatFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
$now = t('Displayed as %date', array('%date' => $this->dateFormatter->format(REQUEST_TIME, $this->entity->id())));
|
||||
$form['date_format_pattern']['#field_suffix'] = ' <small data-drupal-date-formatter="preview">' . $now . '</small>';
|
||||
$form['date_format_pattern']['#default_value'] = $this->entity->getPattern();
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = t('Save format');
|
||||
unset($actions['delete']);
|
||||
return $actions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Form;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
|
||||
use Drupal\Core\Datetime\DateFormatterInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
|
||||
/**
|
||||
* Provides a base form for date formats.
|
||||
*/
|
||||
abstract class DateFormatFormBase extends EntityForm {
|
||||
|
||||
/**
|
||||
* The date formatter service.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\DateFormatterInterface
|
||||
*/
|
||||
protected $dateFormatter;
|
||||
|
||||
/**
|
||||
* The date format storage.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
|
||||
*/
|
||||
protected $dateFormatStorage;
|
||||
|
||||
/**
|
||||
* Constructs a new date format form.
|
||||
*
|
||||
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
|
||||
* The date service.
|
||||
* @param \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $date_format_storage
|
||||
* The date format storage.
|
||||
*/
|
||||
public function __construct(DateFormatterInterface $date_formatter, ConfigEntityStorageInterface $date_format_storage) {
|
||||
$date = new DrupalDateTime();
|
||||
|
||||
$this->dateFormatter = $date_formatter;
|
||||
$this->dateFormatStorage = $date_format_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('date.formatter'),
|
||||
$container->get('entity.manager')->getStorage('date_format')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for an existing date format.
|
||||
*
|
||||
* @param string|int $entity_id
|
||||
* The entity ID.
|
||||
* @param array $element
|
||||
* The form element.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if this format already exists, FALSE otherwise.
|
||||
*/
|
||||
public function exists($entity_id, array $element) {
|
||||
return (bool) $this->dateFormatStorage
|
||||
->getQuery()
|
||||
->condition('id', $element['#field_prefix'] . $entity_id)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => 'Name',
|
||||
'#maxlength' => 100,
|
||||
'#description' => t('Name of the date format'),
|
||||
'#default_value' => $this->entity->label(),
|
||||
);
|
||||
|
||||
$form['id'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#description' => t('A unique machine-readable name. Can only contain lowercase letters, numbers, and underscores.'),
|
||||
'#disabled' => !$this->entity->isNew(),
|
||||
'#default_value' => $this->entity->id(),
|
||||
'#machine_name' => array(
|
||||
'exists' => array($this, 'exists'),
|
||||
'replace_pattern' => '([^a-z0-9_]+)|(^custom$)',
|
||||
'error' => $this->t('The machine-readable name must be unique, and can only contain lowercase letters, numbers, and underscores. Additionally, it can not be the reserved word "custom".'),
|
||||
),
|
||||
);
|
||||
$form['date_format_pattern'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Format string'),
|
||||
'#maxlength' => 100,
|
||||
'#description' => $this->t('A user-defined date format. See the <a href="http://php.net/manual/function.date.php">PHP manual</a> for available options.'),
|
||||
'#required' => TRUE,
|
||||
'#attributes' => [
|
||||
'data-drupal-date-formatter' => 'source',
|
||||
],
|
||||
'#field_suffix' => ' <small class="js-hide" data-drupal-date-formatter="preview">' . $this->t('Displayed as %date_format', ['%date_format' => '']) . '</small>',
|
||||
);
|
||||
|
||||
$form['langcode'] = array(
|
||||
'#type' => 'language_select',
|
||||
'#title' => t('Language'),
|
||||
'#languages' => LanguageInterface::STATE_ALL,
|
||||
'#default_value' => $this->entity->language()->getId(),
|
||||
);
|
||||
$form['#attached']['drupalSettings']['dateFormats'] = $this->dateFormatter->getSampleDateFormats();
|
||||
$form['#attached']['library'][] = 'system/drupal.system.date';
|
||||
return parent::form($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::validateForm($form, $form_state);
|
||||
|
||||
// The machine name field should already check to see if the requested
|
||||
// machine name is available.
|
||||
$pattern = trim($form_state->getValue('date_format_pattern'));
|
||||
foreach ($this->dateFormatStorage->loadMultiple() as $format) {
|
||||
if ($format->getPattern() == $pattern && ($format->id() == $this->entity->id())) {
|
||||
drupal_set_message(t('The existing format/name combination has not been altered.'));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$form_state->setValue('pattern', trim($form_state->getValue('date_format_pattern')));
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$status = $this->entity->save();
|
||||
if ($status == SAVED_UPDATED) {
|
||||
drupal_set_message(t('Custom date format updated.'));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Custom date format added.'));
|
||||
}
|
||||
$form_state->setRedirectUrl($this->entity->urlInfo('collection'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Form;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Datetime\DateFormatterInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\StreamWrapper\PrivateStream;
|
||||
use Drupal\Core\StreamWrapper\PublicStream;
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
|
||||
use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Configure file system settings for this site.
|
||||
*/
|
||||
class FileSystemForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* The date formatter service.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\DateFormatterInterface
|
||||
*/
|
||||
protected $dateFormatter;
|
||||
|
||||
/**
|
||||
* The stream wrapper manager.
|
||||
*
|
||||
* @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface
|
||||
*/
|
||||
protected $streamWrapperManager;
|
||||
|
||||
/**
|
||||
* Constructs a FileSystemForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The factory for configuration objects.
|
||||
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
|
||||
* The date formatter service.
|
||||
* @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager
|
||||
* The stream wrapper manager.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, DateFormatterInterface $date_formatter, StreamWrapperManagerInterface $stream_wrapper_manager) {
|
||||
parent::__construct($config_factory);
|
||||
$this->dateFormatter = $date_formatter;
|
||||
$this->streamWrapperManager = $stream_wrapper_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static (
|
||||
$container->get('config.factory'),
|
||||
$container->get('date.formatter'),
|
||||
$container->get('stream_wrapper_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'system_file_system_settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['system.file'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$config = $this->config('system.file');
|
||||
$form['file_public_path'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Public file system path'),
|
||||
'#markup' => PublicStream::basePath(),
|
||||
'#description' => t('A local file system path where public files will be stored. This directory must exist and be writable by Drupal. This directory must be relative to the Drupal installation directory and be accessible over the web. This must be changed in settings.php'),
|
||||
);
|
||||
|
||||
$form['file_public_base_url'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Public file base URL'),
|
||||
'#markup' => PublicStream::baseUrl(),
|
||||
'#description' => t('The base URL that will be used for public file URLs. This can be changed in settings.php'),
|
||||
);
|
||||
|
||||
$form['file_private_path'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Private file system path'),
|
||||
'#markup' => (PrivateStream::basePath() ? PrivateStream::basePath() : t('Not set')),
|
||||
'#description' => t('An existing local file system path for storing private files. It should be writable by Drupal and not accessible over the web. This must be changed in settings.php'),
|
||||
);
|
||||
|
||||
$form['file_temporary_path'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Temporary directory'),
|
||||
'#default_value' => $config->get('path.temporary'),
|
||||
'#maxlength' => 255,
|
||||
'#description' => t('A local file system path where temporary files will be stored. This directory should not be accessible over the web.'),
|
||||
'#after_build' => array('system_check_directory'),
|
||||
);
|
||||
// Any visible, writeable wrapper can potentially be used for the files
|
||||
// directory, including a remote file system that integrates with a CDN.
|
||||
$options = $this->streamWrapperManager->getDescriptions(StreamWrapperInterface::WRITE_VISIBLE);
|
||||
|
||||
if (!empty($options)) {
|
||||
$form['file_default_scheme'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Default download method'),
|
||||
'#default_value' => $config->get('default_scheme'),
|
||||
'#options' => $options,
|
||||
'#description' => t('This setting is used as the preferred download method. The use of public files is more efficient, but does not provide any access control.'),
|
||||
);
|
||||
}
|
||||
|
||||
$intervals = array(0, 21600, 43200, 86400, 604800, 2419200, 7776000);
|
||||
$period = array_combine($intervals, array_map(array($this->dateFormatter, 'formatInterval'), $intervals));
|
||||
$period[0] = t('Never');
|
||||
$form['temporary_maximum_age'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Delete orphaned files after'),
|
||||
'#default_value' => $config->get('temporary_maximum_age'),
|
||||
'#options' => $period,
|
||||
'#description' => t('Orphaned files are not referenced from any content but remain in the file system and may appear in administrative listings. <strong>Warning:</strong> If enabled, orphaned files will be permanently deleted and may not be recoverable.'),
|
||||
);
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$config = $this->config('system.file')
|
||||
->set('path.temporary', $form_state->getValue('file_temporary_path'))
|
||||
->set('temporary_maximum_age', $form_state->getValue('temporary_maximum_age'));
|
||||
|
||||
if ($form_state->hasValue('file_default_scheme')) {
|
||||
$config->set('default_scheme', $form_state->getValue('file_default_scheme'));
|
||||
}
|
||||
$config->save();
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Form;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\ImageToolkit\ImageToolkitManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Configures image toolkit settings for this site.
|
||||
*/
|
||||
class ImageToolkitForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* An array containing currently available toolkits.
|
||||
*
|
||||
* @var \Drupal\Core\ImageToolkit\ImageToolkitInterface[]
|
||||
*/
|
||||
protected $availableToolkits = array();
|
||||
|
||||
/**
|
||||
* Constructs a ImageToolkitForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The factory for configuration objects.
|
||||
* @param \Drupal\Core\ImageToolkit\ImageToolkitManager $manager
|
||||
* The image toolkit plugin manager.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, ImageToolkitManager $manager) {
|
||||
parent::__construct($config_factory);
|
||||
|
||||
foreach ($manager->getAvailableToolkits() as $id => $definition) {
|
||||
$this->availableToolkits[$id] = $manager->createInstance($id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('config.factory'),
|
||||
$container->get('image.toolkit.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'system_image_toolkit_settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['system.image'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$current_toolkit = $this->config('system.image')->get('toolkit');
|
||||
|
||||
$form['image_toolkit'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('Select an image processing toolkit'),
|
||||
'#default_value' => $current_toolkit,
|
||||
'#options' => array(),
|
||||
);
|
||||
|
||||
// If we have more than one image toolkit, allow the user to select the one
|
||||
// to use, and load each of the toolkits' settings form.
|
||||
foreach ($this->availableToolkits as $id => $toolkit) {
|
||||
$definition = $toolkit->getPluginDefinition();
|
||||
$form['image_toolkit']['#options'][$id] = $definition['title'];
|
||||
$form['image_toolkit_settings'][$id] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('@toolkit settings', array('@toolkit' => $definition['title'])),
|
||||
'#open' => TRUE,
|
||||
'#tree' => TRUE,
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
':radio[name="image_toolkit"]' => array('value' => $id),
|
||||
),
|
||||
),
|
||||
);
|
||||
$form['image_toolkit_settings'][$id] += $toolkit->buildConfigurationForm(array(), $form_state);
|
||||
}
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::validateForm($form, $form_state);
|
||||
|
||||
// Call the form validation handler for each of the toolkits.
|
||||
foreach ($this->availableToolkits as $toolkit) {
|
||||
$toolkit->validateConfigurationForm($form, $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->config('system.image')
|
||||
->set('toolkit', $form_state->getValue('image_toolkit'))
|
||||
->save();
|
||||
|
||||
// Call the form submit handler for each of the toolkits.
|
||||
foreach ($this->availableToolkits as $toolkit) {
|
||||
$toolkit->submitConfigurationForm($form, $form_state);
|
||||
}
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Form;
|
||||
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Configure logging settings for this site.
|
||||
*/
|
||||
class LoggingForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'system_logging_settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['system.logging'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$config = $this->config('system.logging');
|
||||
$form['error_level'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Error messages to display'),
|
||||
'#default_value' => $config->get('error_level'),
|
||||
'#options' => array(
|
||||
ERROR_REPORTING_HIDE => t('None'),
|
||||
ERROR_REPORTING_DISPLAY_SOME => t('Errors and warnings'),
|
||||
ERROR_REPORTING_DISPLAY_ALL => t('All messages'),
|
||||
ERROR_REPORTING_DISPLAY_VERBOSE => t('All messages, with backtrace information'),
|
||||
),
|
||||
'#description' => t('It is recommended that sites running on production environments do not display any errors.'),
|
||||
);
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->config('system.logging')
|
||||
->set('error_level', $form_state->getValue('error_level'))
|
||||
->save();
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Form;
|
||||
|
||||
use Drupal\Core\Config\PreExistingConfigException;
|
||||
use Drupal\Core\Config\UnmetDependenciesException;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Extension\ModuleInstallerInterface;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Builds a confirmation form for enabling modules with dependencies.
|
||||
*/
|
||||
class ModulesListConfirmForm extends ConfirmFormBase {
|
||||
|
||||
/**
|
||||
* The module handler service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The expirable key value store.
|
||||
*
|
||||
* @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
|
||||
*/
|
||||
protected $keyValueExpirable;
|
||||
|
||||
/**
|
||||
* An associative list of modules to enable or disable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $modules = array();
|
||||
|
||||
/**
|
||||
* The module installer.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleInstallerInterface
|
||||
*/
|
||||
protected $moduleInstaller;
|
||||
|
||||
/**
|
||||
* Constructs a ModulesListConfirmForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param \Drupal\Core\Extension\ModuleInstallerInterface $module_installer
|
||||
* The module installer.
|
||||
* @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $key_value_expirable
|
||||
* The key value expirable factory.
|
||||
*/
|
||||
public function __construct(ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, KeyValueStoreExpirableInterface $key_value_expirable) {
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->moduleInstaller = $module_installer;
|
||||
$this->keyValueExpirable = $key_value_expirable;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('module_handler'),
|
||||
$container->get('module_installer'),
|
||||
$container->get('keyvalue.expirable')->get('module_list')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Some required modules must be enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('system.modules_list');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Continue');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->t('Would you like to continue with the above?');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'system_modules_confirm_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$account = $this->currentUser()->id();
|
||||
$this->modules = $this->keyValueExpirable->get($account);
|
||||
|
||||
// Redirect to the modules list page if the key value store is empty.
|
||||
if (!$this->modules) {
|
||||
return $this->redirect('system.modules_list');
|
||||
}
|
||||
|
||||
$items = $this->buildMessageList();
|
||||
$form['message'] = array(
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $items,
|
||||
);
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the message list for the confirmation form.
|
||||
*
|
||||
* @return \Drupal\Component\Render\MarkupInterface[]
|
||||
* Array of markup for the list of messages on the form.
|
||||
*
|
||||
* @see \Drupal\system\Form\ModulesListForm::buildModuleList()
|
||||
*/
|
||||
protected function buildMessageList() {
|
||||
$items = [];
|
||||
if (!empty($this->modules['dependencies'])) {
|
||||
// Display a list of required modules that have to be installed as well
|
||||
// but were not manually selected.
|
||||
foreach ($this->modules['dependencies'] as $module => $dependencies) {
|
||||
$items[] = $this->formatPlural(count($dependencies), 'You must enable the @required module to install @module.', 'You must enable the @required modules to install @module.', [
|
||||
'@module' => $this->modules['install'][$module],
|
||||
// It is safe to implode this because module names are not translated
|
||||
// markup and so will not be double-escaped.
|
||||
'@required' => implode(', ', $dependencies),
|
||||
]);
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// Remove the key value store entry.
|
||||
$account = $this->currentUser()->id();
|
||||
$this->keyValueExpirable->delete($account);
|
||||
|
||||
if (!empty($this->modules['install'])) {
|
||||
// Don't catch the exception that this can throw for missing dependencies:
|
||||
// the form doesn't allow modules with unmet dependencies, so the only way
|
||||
// this can happen is if the filesystem changed between form display and
|
||||
// submit, in which case the user has bigger problems.
|
||||
try {
|
||||
// Install the given modules.
|
||||
$this->moduleInstaller->install(array_keys($this->modules['install']));
|
||||
}
|
||||
catch (PreExistingConfigException $e) {
|
||||
$config_objects = $e->flattenConfigObjects($e->getConfigObjects());
|
||||
drupal_set_message(
|
||||
$this->formatPlural(
|
||||
count($config_objects),
|
||||
'Unable to install @extension, %config_names already exists in active configuration.',
|
||||
'Unable to install @extension, %config_names already exist in active configuration.',
|
||||
array(
|
||||
'%config_names' => implode(', ', $config_objects),
|
||||
'@extension' => $this->modules['install'][$e->getExtension()]
|
||||
)),
|
||||
'error'
|
||||
);
|
||||
return;
|
||||
}
|
||||
catch (UnmetDependenciesException $e) {
|
||||
drupal_set_message(
|
||||
$e->getTranslatedMessage($this->getStringTranslation(), $this->modules['install'][$e->getExtension()]),
|
||||
'error'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$module_names = array_values($this->modules['install']);
|
||||
drupal_set_message($this->formatPlural(count($module_names), 'Module %name has been enabled.', '@count modules have been enabled: %names.', array(
|
||||
'%name' => $module_names[0],
|
||||
'%names' => implode(', ', $module_names),
|
||||
)));
|
||||
}
|
||||
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\system\Form;
|
||||
|
||||
/**
|
||||
* Builds a confirmation form for enabling experimental modules.
|
||||
*/
|
||||
class ModulesListExperimentalConfirmForm extends ModulesListConfirmForm {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you wish to enable experimental modules?');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'system_modules_experimental_confirm_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildMessageList() {
|
||||
drupal_set_message($this->t('<a href=":url">Experimental modules</a> are provided for testing purposes only. Use at your own risk.', [':url' => 'https://www.drupal.org/core/experimental']), 'warning');
|
||||
|
||||
$items = parent::buildMessageList();
|
||||
// Add the list of experimental modules after any other messages.
|
||||
$items[] = $this->t('The following modules are experimental: @modules', ['@modules' => implode(', ', array_values($this->modules['experimental']))]);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user