Update to Drupal 8.0.0-beta15. For more information, see: https://www.drupal.org/node/2563023
This commit is contained in:
parent
2720a9ec4b
commit
f3791f1da3
1898 changed files with 54300 additions and 11481 deletions
|
@ -342,6 +342,17 @@ block.settings.system_menu_block:*:
|
|||
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:
|
||||
|
|
43
core/modules/system/css/components/action-links.theme.css
Normal file
43
core/modules/system/css/components/action-links.theme.css
Normal file
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* @file
|
||||
* Styles for link buttons and action links.
|
||||
*/
|
||||
|
||||
.action-links {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 1em 0;
|
||||
}
|
||||
[dir="rtl"] .action-links {
|
||||
/* This is required to win over specificity of [dir="rtl"] ul */
|
||||
margin-right: 0;
|
||||
}
|
||||
.action-links li {
|
||||
display: inline-block;
|
||||
margin: 0 0.3em;
|
||||
}
|
||||
.action-links li:first-child {
|
||||
margin-left: 0; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .action-links li:first-child {
|
||||
margin-left: 0.3em;
|
||||
margin-right: 0;
|
||||
}
|
||||
.button-action {
|
||||
display: inline-block;
|
||||
line-height: 160%;
|
||||
padding: 0.2em 0.5em 0.3em;
|
||||
text-decoration: none;
|
||||
}
|
||||
.button-action:before {
|
||||
content: '+';
|
||||
font-weight: 900;
|
||||
margin-left: -0.1em; /* LTR */
|
||||
padding-right: 0.2em; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .button-action:before {
|
||||
margin-left: 0;
|
||||
margin-right: -0.1em;
|
||||
padding-left: 0.2em;
|
||||
padding-right: 0;
|
||||
}
|
45
core/modules/system/css/components/ajax-progress.module.css
Normal file
45
core/modules/system/css/components/ajax-progress.module.css
Normal file
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* @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%;
|
||||
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;
|
||||
}
|
32
core/modules/system/css/components/align.module.css
Normal file
32
core/modules/system/css/components/align.module.css
Normal file
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* @file
|
||||
* Alignment classes for text and block level elements.
|
||||
*/
|
||||
|
||||
.text-align-left {
|
||||
text-align: left;
|
||||
}
|
||||
.text-align-right {
|
||||
text-align: right;
|
||||
}
|
||||
.text-align-center {
|
||||
text-align: center;
|
||||
}
|
||||
.text-align-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alignment classes for block level elements (images, videos, blockquotes, etc.)
|
||||
*/
|
||||
.align-left {
|
||||
float: left;
|
||||
}
|
||||
.align-right {
|
||||
float: right;
|
||||
}
|
||||
.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
|
@ -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;
|
||||
}
|
29
core/modules/system/css/components/breadcrumb.theme.css
Normal file
29
core/modules/system/css/components/breadcrumb.theme.css
Normal file
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* @file
|
||||
* Styles for breadcrumbs.
|
||||
*/
|
||||
|
||||
.breadcrumb {
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
.breadcrumb ol {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
[dir="rtl"] .breadcrumb ol {
|
||||
/* This is required to win over specificity of [dir="rtl"] ol */
|
||||
margin-right: 0;
|
||||
}
|
||||
.breadcrumb li {
|
||||
display: inline;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
/* IE8 does not support :not() and :last-child. */
|
||||
.breadcrumb li:before {
|
||||
content: ' \BB ';
|
||||
}
|
||||
.breadcrumb li:first-child:before {
|
||||
content: none;
|
||||
}
|
15
core/modules/system/css/components/button.theme.css
Normal file
15
core/modules/system/css/components/button.theme.css
Normal file
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for buttons.
|
||||
*/
|
||||
|
||||
.button,
|
||||
.image-button {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
.button:first-child,
|
||||
.image-button:first-child {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
15
core/modules/system/css/components/clearfix.module.css
Normal file
15
core/modules/system/css/components/clearfix.module.css
Normal file
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* @file
|
||||
* Float clearing.
|
||||
*
|
||||
* Based on the micro clearfix hack by Nicolas Gallagher, with the :before
|
||||
* pseudo selector removed to allow normal top margin collapse.
|
||||
*
|
||||
* @see http://nicolasgallagher.com/micro-clearfix-hack
|
||||
*/
|
||||
|
||||
.clearfix:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for collapsible fieldsets.
|
||||
*/
|
||||
|
||||
.collapse-processed > summary {
|
||||
padding-left: 0.5em;
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
.collapse-processed > summary:before {
|
||||
background: url(../../../../misc/menu-expanded.png) 0px 100% no-repeat; /* LTR */
|
||||
content: "";
|
||||
float: left;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
}
|
||||
[dir="rtl"] .collapse-processed > summary:before {
|
||||
background-position: 100% 100%;
|
||||
float: right;
|
||||
}
|
||||
.collapse-processed:not([open]) > summary:before {
|
||||
background-position: 25% 35%; /* LTR */
|
||||
-ms-transform: rotate(-90deg);
|
||||
-webkit-transform: rotate(-90deg);
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
[dir="rtl"] .collapse-processed:not([open]) > summary:before {
|
||||
background-position: 75% 35%;
|
||||
-ms-transform: rotate(90deg);
|
||||
-webkit-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
|
@ -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,20 @@
|
|||
/**
|
||||
* @file
|
||||
* Inline items.
|
||||
*/
|
||||
|
||||
.container-inline label:after,
|
||||
.container-inline .label:after {
|
||||
content: ':';
|
||||
}
|
||||
.form-type-radios .container-inline label:after {
|
||||
content: '';
|
||||
}
|
||||
.form-type-radios .container-inline .form-type-radio {
|
||||
margin: 0 1em;
|
||||
}
|
||||
.container-inline .form-actions,
|
||||
.container-inline.form-actions {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
10
core/modules/system/css/components/details.module.css
Normal file
10
core/modules/system/css/components/details.module.css
Normal file
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* @file
|
||||
* Collapsible details.
|
||||
*
|
||||
* @see collapse.js
|
||||
*/
|
||||
|
||||
.js details:not([open]) .details-wrapper {
|
||||
display: none;
|
||||
}
|
23
core/modules/system/css/components/details.theme.css
Normal file
23
core/modules/system/css/components/details.theme.css
Normal file
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* @file
|
||||
* Collapsible details.
|
||||
*
|
||||
* @see collapse.js
|
||||
* @see http://nicolasgallagher.com/css-background-image-hacks/
|
||||
*/
|
||||
|
||||
details {
|
||||
border: 1px solid #ccc;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
details > .details-wrapper {
|
||||
padding: 0.5em 1.5em;
|
||||
}
|
||||
/* @todo Regression: The summary of uncollapsible details are no longer
|
||||
vertically aligned with the .details-wrapper in browsers without native
|
||||
details support. */
|
||||
summary {
|
||||
cursor: pointer;
|
||||
padding: 0.2em 0.5em;
|
||||
}
|
46
core/modules/system/css/components/exposed-filters.theme.css
Normal file
46
core/modules/system/css/components/exposed-filters.theme.css
Normal file
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for exposed filters.
|
||||
*/
|
||||
|
||||
.exposed-filters .filters {
|
||||
float: left; /* LTR */
|
||||
margin-right: 1em; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .exposed-filters .filters {
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
margin-right: 0;
|
||||
}
|
||||
.exposed-filters .form-item {
|
||||
margin: 0 0 0.1em 0;
|
||||
padding: 0;
|
||||
}
|
||||
.exposed-filters .form-item label {
|
||||
float: left; /* LTR */
|
||||
font-weight: normal;
|
||||
width: 10em;
|
||||
}
|
||||
[dir="rtl"] .exposed-filters .form-item label {
|
||||
float: right;
|
||||
}
|
||||
.exposed-filters .form-select {
|
||||
width: 14em;
|
||||
}
|
||||
/* Current filters */
|
||||
.exposed-filters .current-filters {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.exposed-filters .current-filters .placeholder {
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
}
|
||||
.exposed-filters .additional-filters {
|
||||
float: left; /* LTR */
|
||||
margin-right: 1em; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .exposed-filters .additional-filters {
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
margin-right: 0;
|
||||
}
|
25
core/modules/system/css/components/field.theme.css
Normal file
25
core/modules/system/css/components/field.theme.css
Normal file
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for fields.
|
||||
*/
|
||||
|
||||
.field__label {
|
||||
font-weight: bold;
|
||||
}
|
||||
.field--label-inline .field__label,
|
||||
.field--label-inline .field__items {
|
||||
float: left; /*LTR*/
|
||||
}
|
||||
.field--label-inline .field__label,
|
||||
.field--label-inline > .field__item,
|
||||
.field--label-inline .field__items {
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
[dir="rtl"] .field--label-inline .field__label,
|
||||
[dir="rtl"] .field--label-inline .field__items {
|
||||
padding-left: 0.5em;
|
||||
padding-right: 0;
|
||||
}
|
||||
.field--label-inline .field__label::after {
|
||||
content: ':';
|
||||
}
|
9
core/modules/system/css/components/fieldgroup.module.css
Normal file
9
core/modules/system/css/components/fieldgroup.module.css
Normal file
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* @file
|
||||
* Fieldgroup border reset.
|
||||
*/
|
||||
|
||||
.fieldgroup {
|
||||
border-width: 0;
|
||||
padding: 0;
|
||||
}
|
98
core/modules/system/css/components/form.theme.css
Normal file
98
core/modules/system/css/components/form.theme.css
Normal file
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for form components.
|
||||
*/
|
||||
|
||||
form .field-multiple-table {
|
||||
margin: 0;
|
||||
}
|
||||
form .field-multiple-table .field-multiple-drag {
|
||||
width: 30px;
|
||||
padding-right: 0; /*LTR*/
|
||||
}
|
||||
[dir="rtl"] form .field-multiple-table .field-multiple-drag {
|
||||
padding-left: 0;
|
||||
}
|
||||
form .field-multiple-table .field-multiple-drag .tabledrag-handle {
|
||||
padding-right: .5em; /*LTR*/
|
||||
}
|
||||
[dir="rtl"] form .field-multiple-table .field-multiple-drag .tabledrag-handle {
|
||||
padding-left: .5em;
|
||||
}
|
||||
form .field-add-more-submit {
|
||||
margin: .5em 0 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Markup generated by Form API.
|
||||
*/
|
||||
.form-item,
|
||||
.form-actions {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
tr.odd .form-item,
|
||||
tr.even .form-item {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.form-composite > .fieldset-wrapper > .description,
|
||||
.form-item .description {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
label.option {
|
||||
display: inline;
|
||||
font-weight: normal;
|
||||
}
|
||||
.form-composite > legend,
|
||||
.label {
|
||||
display:inline;
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.form-checkboxes .form-item,
|
||||
.form-radios .form-item {
|
||||
margin-top: 0.4em;
|
||||
margin-bottom: 0.4em;
|
||||
}
|
||||
.form-type-radio .description,
|
||||
.form-type-checkbox .description {
|
||||
margin-left: 2.4em;
|
||||
}
|
||||
.marker {
|
||||
color: #e00;
|
||||
}
|
||||
.form-required:after {
|
||||
content: '';
|
||||
vertical-align: super;
|
||||
display: inline-block;
|
||||
/* Use a background image to prevent screen readers from announcing the text. */
|
||||
background-image: url(../../../../misc/icons/ee0000/required.svg);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 6px 6px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin: 0 0.3em;
|
||||
}
|
||||
abbr.tabledrag-changed,
|
||||
abbr.ajax-changed {
|
||||
border-bottom: none;
|
||||
}
|
||||
.form-item input.error,
|
||||
.form-item textarea.error,
|
||||
.form-item select.error {
|
||||
border: 2px solid red;
|
||||
}
|
||||
|
||||
/* Inline error messages. */
|
||||
.form-item--error-message:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
vertical-align: sub;
|
||||
background: url(../../../../misc/icons/e32700/error.svg) no-repeat;
|
||||
background-size: contain;
|
||||
}
|
53
core/modules/system/css/components/hidden.module.css
Normal file
53
core/modules/system/css/components/hidden.module.css
Normal file
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* @file
|
||||
* Utility classes to hide elements in different ways.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Hide elements from all users.
|
||||
*
|
||||
* Used for elements which should not be immediately displayed to any user. An
|
||||
* example would be collapsible details that will be expanded with a click
|
||||
* from a user. The effect of this class can be toggled with the jQuery show()
|
||||
* and hide() functions.
|
||||
*/
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide elements visually, but keep them available for screen readers.
|
||||
*
|
||||
* Used for information required for screen reader users to understand and use
|
||||
* the site where visual display is undesirable. Information provided in this
|
||||
* manner should be kept concise, to avoid unnecessary burden on the user.
|
||||
* "!important" is used to prevent unintentional overrides.
|
||||
*/
|
||||
.visually-hidden {
|
||||
position: absolute !important;
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
overflow: hidden;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* The .focusable class extends the .visually-hidden class to allow
|
||||
* the element to be focusable when navigated to via the keyboard.
|
||||
*/
|
||||
.visually-hidden.focusable:active,
|
||||
.visually-hidden.focusable:focus {
|
||||
position: static !important;
|
||||
clip: auto;
|
||||
overflow: visible;
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide visually and from screen readers, but maintain layout.
|
||||
*/
|
||||
.invisible {
|
||||
visibility: hidden;
|
||||
}
|
21
core/modules/system/css/components/icons.theme.css
Normal file
21
core/modules/system/css/components/icons.theme.css
Normal file
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for icons.
|
||||
*/
|
||||
|
||||
.icon-help {
|
||||
background: url(../../../../misc/help.png) 0 50% no-repeat; /* LTR */
|
||||
padding: 1px 0 1px 20px; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .icon-help {
|
||||
background-position: 100% 50%;
|
||||
padding: 1px 20px 1px 0;
|
||||
}
|
||||
.feed-icon {
|
||||
background: url(../../../../misc/feed.svg) no-repeat;
|
||||
overflow: hidden;
|
||||
text-indent: -9999px;
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
29
core/modules/system/css/components/inline-form.theme.css
Normal file
29
core/modules/system/css/components/inline-form.theme.css
Normal file
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for inline forms.
|
||||
*/
|
||||
|
||||
.form--inline .form-item {
|
||||
float: left; /* LTR */
|
||||
margin-right: 0.5em; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .form--inline .form-item {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
.form--inline .form-item-separator {
|
||||
margin-top: 2.3em;
|
||||
margin-right: 1em; /* LTR */
|
||||
margin-left: 0.5em; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .form--inline .form-item-separator {
|
||||
margin-right: 0.5em;
|
||||
margin-left: 1em;
|
||||
}
|
||||
.form--inline .form-actions {
|
||||
clear: left; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .form--inline .form-actions {
|
||||
clear: right;
|
||||
}
|
39
core/modules/system/css/components/item-list.theme.css
Normal file
39
core/modules/system/css/components/item-list.theme.css
Normal file
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for item list.
|
||||
*/
|
||||
|
||||
.item-list .title {
|
||||
font-weight: bold;
|
||||
}
|
||||
.item-list ul {
|
||||
margin: 0 0 0.75em 0;
|
||||
padding: 0;
|
||||
}
|
||||
.item-list ul li {
|
||||
margin: 0 0 0.25em 1.5em; /* LTR */
|
||||
padding: 0;
|
||||
}
|
||||
[dir="rtl"] .item-list ul li {
|
||||
margin: 0 1.5em 0.25em 0;
|
||||
}
|
||||
ul.item-list__comma-list {
|
||||
display: inline;
|
||||
}
|
||||
ul.item-list__comma-list li {
|
||||
display: inline;
|
||||
list-style-type: none;
|
||||
}
|
||||
ul.item-list__comma-list,
|
||||
ul.item-list__comma-list li,
|
||||
[dir="rtl"] ul.item-list__comma-list,
|
||||
[dir="rtl"] ul.item-list__comma-list li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
ul.item-list__comma-list li:after {
|
||||
content: ", ";
|
||||
}
|
||||
ul.item-list__comma-list li:last-child:after {
|
||||
content: "";
|
||||
}
|
22
core/modules/system/css/components/js.module.css
Normal file
22
core/modules/system/css/components/js.module.css
Normal file
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* @file
|
||||
* Utility classes to assist with Javascript functionality.
|
||||
*/
|
||||
|
||||
/**
|
||||
* For anything you want to hide on page load when JS is enabled, so
|
||||
* that you can use the JS to control visibility and avoid flicker.
|
||||
*/
|
||||
.js .js-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* For anything you want to show on page load only when JS is enabled.
|
||||
*/
|
||||
.js-show {
|
||||
display: none;
|
||||
}
|
||||
.js .js-show {
|
||||
display: block;
|
||||
}
|
16
core/modules/system/css/components/link.theme.css
Normal file
16
core/modules/system/css/components/link.theme.css
Normal file
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* @file
|
||||
* Style another element as a link.
|
||||
*/
|
||||
|
||||
button.link {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
label button.link {
|
||||
font-weight: bold;
|
||||
}
|
18
core/modules/system/css/components/links.theme.css
Normal file
18
core/modules/system/css/components/links.theme.css
Normal file
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for links.
|
||||
*/
|
||||
|
||||
ul.inline,
|
||||
ul.links.inline {
|
||||
display: inline;
|
||||
padding-left: 0;
|
||||
}
|
||||
ul.inline li {
|
||||
display: inline;
|
||||
list-style-type: none;
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
ul.links a.is-active {
|
||||
color: #000;
|
||||
}
|
34
core/modules/system/css/components/menu.theme.css
Normal file
34
core/modules/system/css/components/menu.theme.css
Normal file
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for menu.
|
||||
*/
|
||||
|
||||
ul.menu {
|
||||
list-style: none outside;
|
||||
margin-left: 1em; /* LTR */
|
||||
padding: 0;
|
||||
text-align: left; /* LTR */
|
||||
}
|
||||
[dir="rtl"] ul.menu {
|
||||
margin-left: 0;
|
||||
margin-right: 1em;
|
||||
text-align: right;
|
||||
}
|
||||
.menu-item--expanded {
|
||||
list-style-image: url(../../../../misc/menu-expanded.png);
|
||||
list-style-type: circle;
|
||||
}
|
||||
.menu-item--collapsed {
|
||||
list-style-image: url(../../../../misc/menu-collapsed.png); /* LTR */
|
||||
list-style-type: disc;
|
||||
}
|
||||
[dir="rtl"] .menu-item--collapsed {
|
||||
list-style-image: url(../../../../misc/menu-collapsed-rtl.png);
|
||||
}
|
||||
.menu-item {
|
||||
padding-top: 0.2em;
|
||||
margin: 0;
|
||||
}
|
||||
ul.menu a.is-active {
|
||||
color: #000;
|
||||
}
|
72
core/modules/system/css/components/messages.theme.css
Normal file
72
core/modules/system/css/components/messages.theme.css
Normal file
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* @file
|
||||
* Styles for system messages.
|
||||
*/
|
||||
|
||||
.messages {
|
||||
background: no-repeat 10px 17px; /* LTR */
|
||||
border: 1px solid;
|
||||
border-width: 1px 1px 1px 0; /* LTR */
|
||||
border-radius: 2px;
|
||||
padding: 15px 20px 15px 35px; /* LTR */
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
[dir="rtl"] .messages {
|
||||
border-width: 1px 0 1px 1px;
|
||||
background-position: right 10px top 17px;
|
||||
padding-left: 20px;
|
||||
padding-right: 35px;
|
||||
text-align: right;
|
||||
}
|
||||
.messages + .messages {
|
||||
margin-top: 1.538em;
|
||||
}
|
||||
.messages__list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.messages__item + .messages__item {
|
||||
margin-top: 0.769em;
|
||||
}
|
||||
/* See .color-success in Seven's colors.css */
|
||||
.messages--status {
|
||||
color: #325e1c;
|
||||
background-color: #f3faef;
|
||||
border-color: #c9e1bd #c9e1bd #c9e1bd transparent; /* LTR */
|
||||
background-image: url(../../../../misc/icons/73b355/check.svg);
|
||||
box-shadow: -8px 0 0 #77b259; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .messages--status {
|
||||
border-color: #c9e1bd transparent #c9e1bd #c9e1bd;
|
||||
box-shadow: 8px 0 0 #77b259;
|
||||
margin-left: 0;
|
||||
}
|
||||
/* See .color-warning in Seven's colors.css */
|
||||
.messages--warning {
|
||||
background-color: #fdf8ed;
|
||||
background-image: url(../../../../misc/icons/e29700/warning.svg);
|
||||
border-color: #f4daa6 #f4daa6 #f4daa6 transparent; /* LTR */
|
||||
color: #734c00;
|
||||
box-shadow: -8px 0 0 #e09600; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .messages--warning {
|
||||
border-color: #f4daa6 transparent #f4daa6 #f4daa6;
|
||||
box-shadow: 8px 0 0 #e09600;
|
||||
}
|
||||
/* See .color-error in Seven's colors.css */
|
||||
.messages--error {
|
||||
background-color: #fcf4f2;
|
||||
color: #a51b00;
|
||||
background-image: url(../../../../misc/icons/e32700/error.svg);
|
||||
border-color: #f9c9bf #f9c9bf #f9c9bf transparent; /* LTR */
|
||||
box-shadow: -8px 0 0 #e62600; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .messages--error {
|
||||
border-color: #f9c9bf transparent #f9c9bf #f9c9bf;
|
||||
box-shadow: 8px 0 0 #e62600;
|
||||
}
|
||||
.messages--error p.error {
|
||||
color: #a51b00;
|
||||
}
|
12
core/modules/system/css/components/more-link.theme.css
Normal file
12
core/modules/system/css/components/more-link.theme.css
Normal file
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* @file
|
||||
* Markup generated by #type 'more_link'.
|
||||
*/
|
||||
|
||||
.more-link {
|
||||
display: block;
|
||||
text-align: right; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .more-link {
|
||||
text-align: left;
|
||||
}
|
8
core/modules/system/css/components/nowrap.module.css
Normal file
8
core/modules/system/css/components/nowrap.module.css
Normal file
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* @file
|
||||
* Utility class to prevent text wrapping.
|
||||
*/
|
||||
|
||||
.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
16
core/modules/system/css/components/pager.theme.css
Normal file
16
core/modules/system/css/components/pager.theme.css
Normal file
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for pager.
|
||||
*/
|
||||
|
||||
.pager__items {
|
||||
clear: both;
|
||||
text-align: center;
|
||||
}
|
||||
.pager__item {
|
||||
display: inline;
|
||||
padding: 0.5em;
|
||||
}
|
||||
.pager__item.is-active {
|
||||
font-weight: bold;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* @file
|
||||
* Contain positioned elements.
|
||||
*/
|
||||
|
||||
.position-container {
|
||||
position: relative;
|
||||
}
|
50
core/modules/system/css/components/progress.module.css
Normal file
50
core/modules/system/css/components/progress.module.css
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* @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: 1.5em;
|
||||
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;
|
||||
}
|
64
core/modules/system/css/components/progress.theme.css
Normal file
64
core/modules/system/css/components/progress.theme.css
Normal file
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for progress bar.
|
||||
*
|
||||
* @see progress.js
|
||||
*/
|
||||
|
||||
.progress__track {
|
||||
border-color: #b3b3b3;
|
||||
border-radius: 10em;
|
||||
background-color: #f2f1eb;
|
||||
background-image: -webkit-linear-gradient(#e7e7df, #f0f0f0);
|
||||
background-image: linear-gradient(#e7e7df, #f0f0f0);
|
||||
box-shadow: inset 0 1px 3px hsla(0, 0%, 0%, 0.16);
|
||||
}
|
||||
.progress__bar {
|
||||
border: 1px #07629a solid;
|
||||
background: #057ec9;
|
||||
background-image:
|
||||
-webkit-linear-gradient( top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15) ),
|
||||
-webkit-linear-gradient( left top,
|
||||
#0094f0 0%,
|
||||
#0094f0 25%,
|
||||
#007ecc 25%,
|
||||
#007ecc 50%,
|
||||
#0094f0 50%,
|
||||
#0094f0 75%,
|
||||
#0094f0 100% );
|
||||
background-image:
|
||||
-webkit-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15)), -webkit-linear-gradient(left top, #0094f0 0%, #0094f0 25%, #007ecc 25%, #007ecc 50%, #0094f0 50%, #0094f0 75%, #0094f0 100%);
|
||||
background-image:
|
||||
linear-gradient( to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15) ),
|
||||
linear-gradient( to right bottom,
|
||||
#0094f0 0%,
|
||||
#0094f0 25%,
|
||||
#007ecc 25%,
|
||||
#007ecc 50%,
|
||||
#0094f0 50%,
|
||||
#0094f0 75%,
|
||||
#0094f0 100% );
|
||||
background-size: 40px 40px;
|
||||
margin-top: -1px;
|
||||
margin-left: -1px;
|
||||
padding: 0 1px;
|
||||
height: 16px;
|
||||
border-radius: 10em;
|
||||
-webkit-animation: animate-stripes 3s linear infinite;
|
||||
-moz-animation: animate-stripes 3s linear infinite;
|
||||
-webkit-transition: width 0.5s ease-out;
|
||||
transition: width 0.5s ease-out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Progress bar animations.
|
||||
*/
|
||||
@-webkit-keyframes animate-stripes {
|
||||
0% {background-position: 0 0, 0 0;} 100% {background-position: 0 0, -80px 0;}
|
||||
}
|
||||
@-ms-keyframes animate-stripes {
|
||||
0% {background-position: 0 0, 0 0;} 100% {background-position: 0 0, -80px 0;}
|
||||
}
|
||||
@keyframes animate-stripes {
|
||||
0% {background-position: 0 0, 0 0;} 100% {background-position: 0 0, -80px 0;}
|
||||
}
|
|
@ -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;
|
||||
}
|
21
core/modules/system/css/components/resize.module.css
Normal file
21
core/modules/system/css/components/resize.module.css
Normal file
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* @file
|
||||
* Resizable textareas.
|
||||
*/
|
||||
|
||||
.resize-none {
|
||||
resize: none;
|
||||
}
|
||||
.resize-vertical {
|
||||
resize: vertical;
|
||||
min-height: 2em;
|
||||
}
|
||||
.resize-horizontal {
|
||||
resize: horizontal;
|
||||
max-width: 100%;
|
||||
}
|
||||
.resize-both {
|
||||
resize: both;
|
||||
max-width: 100%;
|
||||
min-height: 2em;
|
||||
}
|
13
core/modules/system/css/components/sticky-header.module.css
Normal file
13
core/modules/system/css/components/sticky-header.module.css
Normal file
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* @file
|
||||
* Table header behavior.
|
||||
*
|
||||
* @see tableheader.js
|
||||
*/
|
||||
|
||||
table.sticky-header {
|
||||
background-color: #fff;
|
||||
margin-top: 0;
|
||||
z-index: 500;
|
||||
top: 0;
|
||||
}
|
85
core/modules/system/css/components/tabledrag.module.css
Normal file
85
core/modules/system/css/components/tabledrag.module.css
Normal file
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* @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);
|
||||
}
|
||||
.touch .draggable td {
|
||||
padding: 0 10px;
|
||||
}
|
||||
.touch .draggable .menu-item__link {
|
||||
display: inline-block;
|
||||
padding: 10px 0;
|
||||
}
|
||||
.touch a.tabledrag-handle {
|
||||
height: 44px;
|
||||
width: 40px;
|
||||
}
|
||||
.touch a.tabledrag-handle .handle {
|
||||
background-position: 40% 19px;
|
||||
height: 21px;
|
||||
}
|
||||
.touch .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;
|
||||
}
|
14
core/modules/system/css/components/tabledrag.theme.css
Normal file
14
core/modules/system/css/components/tabledrag.theme.css
Normal file
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for table drag.
|
||||
*/
|
||||
|
||||
tr.drag {
|
||||
background-color: #fffff0;
|
||||
}
|
||||
tr.drag-previous {
|
||||
background-color: #ffd;
|
||||
}
|
||||
body div.tabledrag-changed-warning {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
14
core/modules/system/css/components/tableselect.theme.css
Normal file
14
core/modules/system/css/components/tableselect.theme.css
Normal file
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* @file
|
||||
* Table select behavior.
|
||||
*
|
||||
* @see tableselect.js
|
||||
*/
|
||||
|
||||
tr.selected td {
|
||||
background: #ffc;
|
||||
}
|
||||
td.checkbox,
|
||||
th.checkbox {
|
||||
text-align: center;
|
||||
}
|
11
core/modules/system/css/components/tablesort.theme.css
Normal file
11
core/modules/system/css/components/tablesort.theme.css
Normal file
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* @file
|
||||
* Table sort indicator.
|
||||
*/
|
||||
|
||||
th.is-active img {
|
||||
display: inline;
|
||||
}
|
||||
td.is-active {
|
||||
background-color: #ddd;
|
||||
}
|
33
core/modules/system/css/components/tabs.theme.css
Normal file
33
core/modules/system/css/components/tabs.theme.css
Normal file
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for tabs.
|
||||
*/
|
||||
|
||||
div.tabs {
|
||||
margin: 1em 0;
|
||||
}
|
||||
ul.tabs {
|
||||
list-style: none;
|
||||
margin: 0 0 0.5em;
|
||||
padding: 0;
|
||||
}
|
||||
.tabs > li {
|
||||
display: inline-block;
|
||||
margin-right: 0.3em; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .tabs > li {
|
||||
margin-left: 0.3em;
|
||||
margin-right: 0;
|
||||
}
|
||||
.tabs a {
|
||||
display: block;
|
||||
padding: 0.2em 1em;
|
||||
text-decoration: none;
|
||||
}
|
||||
.tabs a.is-active {
|
||||
background-color: #eee;
|
||||
}
|
||||
.tabs a:focus,
|
||||
.tabs a:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
11
core/modules/system/css/components/textarea.theme.css
Normal file
11
core/modules/system/css/components/textarea.theme.css
Normal file
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for a resizable textarea.
|
||||
*/
|
||||
|
||||
.form-textarea-wrapper textarea {
|
||||
display: block;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
18
core/modules/system/css/components/tree-child.module.css
Normal file
18
core/modules/system/css/components/tree-child.module.css
Normal file
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styles for a nested tree child.
|
||||
*/
|
||||
|
||||
div.tree-child {
|
||||
background: url(../../../../misc/tree.png) no-repeat 11px center; /* LTR */
|
||||
}
|
||||
div.tree-child-last {
|
||||
background: url(../../../../misc/tree-bottom.png) no-repeat 11px center; /* LTR */
|
||||
}
|
||||
[dir="rtl"] div.tree-child,
|
||||
[dir="rtl"] div.tree-child-last {
|
||||
background-position: -65px center;
|
||||
}
|
||||
div.tree-child-horizontal {
|
||||
background: url(../../../../misc/tree.png) no-repeat -11px center;
|
||||
}
|
|
@ -208,7 +208,7 @@ small .admin-link:after {
|
|||
display: block;
|
||||
}
|
||||
.system-status-report__status-icon--error:before {
|
||||
background-image: url(../../../misc/icons/ea2800/error.svg);
|
||||
background-image: url(../../../misc/icons/e32700/error.svg);
|
||||
}
|
||||
.system-status-report__status-icon--warning:before {
|
||||
background-image: url(../../../misc/icons/e29700/warning.svg);
|
||||
|
|
|
@ -1,44 +1,3 @@
|
|||
/**
|
||||
* Inline diff metadata
|
||||
*/
|
||||
.diff-inline-metadata {
|
||||
padding:4px;
|
||||
border:1px solid #ddd;
|
||||
background:#fff;
|
||||
margin:0 0 10px;
|
||||
}
|
||||
|
||||
.diff-inline-legend {
|
||||
font-size:11px;
|
||||
}
|
||||
|
||||
.diff-inline-legend span,
|
||||
.diff-inline-legend label {
|
||||
margin-right:5px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline diff markup
|
||||
*/
|
||||
span.diff-deleted {
|
||||
color:#ccc;
|
||||
}
|
||||
span.diff-deleted img {
|
||||
border: solid 2px #ccc;
|
||||
}
|
||||
span.diff-changed {
|
||||
background:#ffb;
|
||||
}
|
||||
span.diff-changed img {
|
||||
border:solid 2px #ffb;
|
||||
}
|
||||
span.diff-added {
|
||||
background:#cfc;
|
||||
}
|
||||
span.diff-added img {
|
||||
border: solid 2px #cfc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traditional split diff theming
|
||||
*/
|
||||
|
@ -48,25 +7,8 @@ table.diff {
|
|||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
table.diff .even, table.diff .odd {
|
||||
background-color: inherit;
|
||||
border: none;
|
||||
}
|
||||
table.diff .diff-prevlink {
|
||||
text-align: left;
|
||||
}
|
||||
table.diff .diff-nextlink {
|
||||
text-align: right;
|
||||
}
|
||||
table.diff .diff-section-title,
|
||||
table.diff .diff-section-title {
|
||||
background-color: #f0f0ff;
|
||||
font-size: 0.83em;
|
||||
font-weight: bold;
|
||||
padding: 0.1em 1em;
|
||||
}
|
||||
table.diff .diff-context {
|
||||
background-color: #fafafa;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
table.diff .diff-deletedline {
|
||||
background-color: #ffa;
|
||||
|
@ -80,13 +22,9 @@ table.diff .diffchange {
|
|||
color: #f00;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.diff .diff-marker {
|
||||
width: 1.4em;
|
||||
}
|
||||
table.diff .diff-content {
|
||||
width: 50%;
|
||||
}
|
||||
table.diff th {
|
||||
padding-right: inherit;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
* 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) {
|
||||
|
@ -30,6 +33,7 @@
|
|||
* Event handler that replaces date characters with value.
|
||||
*
|
||||
* @param {jQuery.Event} e
|
||||
* The jQuery event triggered.
|
||||
*/
|
||||
function dateFormatHandler(e) {
|
||||
var baseValue = $(e.target).val() || '';
|
||||
|
|
|
@ -11,11 +11,16 @@
|
|||
var ids = [];
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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) {
|
||||
|
@ -60,9 +65,11 @@
|
|||
/**
|
||||
* Handler for a Blur event on a source field.
|
||||
*
|
||||
* This event handler will trigger a 'value:copy' event on all dependent fields.
|
||||
* 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();
|
||||
|
|
19
core/modules/system/migration_templates/d6_system_date.yml
Normal file
19
core/modules/system/migration_templates/d6_system_date.yml
Normal file
|
@ -0,0 +1,19 @@
|
|||
id: d6_system_date
|
||||
label: Drupal 6 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
|
13
core/modules/system/migration_templates/menu.yml
Normal file
13
core/modules/system/migration_templates/menu.yml
Normal file
|
@ -0,0 +1,13 @@
|
|||
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
|
|
@ -9,7 +9,6 @@ namespace Drupal\system\Controller;
|
|||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
|
||||
use Drupal\Core\Render\BareHtmlPageRendererInterface;
|
||||
|
@ -61,13 +60,6 @@ class DbUpdateController extends ControllerBase {
|
|||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* The entity definition update manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
|
||||
*/
|
||||
protected $entityDefinitionUpdateManager;
|
||||
|
||||
/**
|
||||
* The bare HTML page renderer.
|
||||
*
|
||||
|
@ -97,19 +89,16 @@ class DbUpdateController extends ControllerBase {
|
|||
* The module handler.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The current user.
|
||||
* @param \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface $entity_definition_update_manager
|
||||
* The entity definition update manager.
|
||||
* @param \Drupal\Core\Render\BareHtmlPageRendererInterface $bare_html_page_renderer
|
||||
* The bare HTML page renderer.
|
||||
*/
|
||||
public function __construct($root, KeyValueExpirableFactoryInterface $key_value_expirable_factory, CacheBackendInterface $cache, StateInterface $state, ModuleHandlerInterface $module_handler, AccountInterface $account, EntityDefinitionUpdateManagerInterface $entity_definition_update_manager, BareHtmlPageRendererInterface $bare_html_page_renderer) {
|
||||
public function __construct($root, KeyValueExpirableFactoryInterface $key_value_expirable_factory, CacheBackendInterface $cache, StateInterface $state, ModuleHandlerInterface $module_handler, AccountInterface $account, BareHtmlPageRendererInterface $bare_html_page_renderer) {
|
||||
$this->root = $root;
|
||||
$this->keyValueExpirableFactory = $key_value_expirable_factory;
|
||||
$this->cache = $cache;
|
||||
$this->state = $state;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->account = $account;
|
||||
$this->entityDefinitionUpdateManager = $entity_definition_update_manager;
|
||||
$this->bareHtmlPageRenderer = $bare_html_page_renderer;
|
||||
}
|
||||
|
||||
|
@ -124,7 +113,6 @@ class DbUpdateController extends ControllerBase {
|
|||
$container->get('state'),
|
||||
$container->get('module_handler'),
|
||||
$container->get('current_user'),
|
||||
$container->get('entity.definition_update_manager'),
|
||||
$container->get('bare_html_page_renderer')
|
||||
);
|
||||
}
|
||||
|
@ -160,13 +148,13 @@ class DbUpdateController extends ControllerBase {
|
|||
$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);
|
||||
$output = $this->requirements($severity, $requirements, $request);
|
||||
}
|
||||
else {
|
||||
switch ($op) {
|
||||
case 'selection':
|
||||
$regions['sidebar_first'] = $this->updateTasksList('selection');
|
||||
$output = $this->selection();
|
||||
$output = $this->selection($request);
|
||||
break;
|
||||
|
||||
case 'run':
|
||||
|
@ -176,12 +164,12 @@ class DbUpdateController extends ControllerBase {
|
|||
|
||||
case 'info':
|
||||
$regions['sidebar_first'] = $this->updateTasksList('info');
|
||||
$output = $this->info();
|
||||
$output = $this->info($request);
|
||||
break;
|
||||
|
||||
case 'results':
|
||||
$regions['sidebar_first'] = $this->updateTasksList('results');
|
||||
$output = $this->results();
|
||||
$output = $this->results($request);
|
||||
break;
|
||||
|
||||
// Regular batch ops : defer to batch processing API.
|
||||
|
@ -204,10 +192,13 @@ class DbUpdateController extends ControllerBase {
|
|||
/**
|
||||
* Returns the info database update page.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @return array
|
||||
* A render array.
|
||||
*/
|
||||
protected function info() {
|
||||
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.
|
||||
|
@ -233,12 +224,12 @@ class DbUpdateController extends ControllerBase {
|
|||
'#markup' => '<p>' . $this->t('When you have performed the steps above, you may proceed.') . '</p>',
|
||||
);
|
||||
|
||||
$url = new Url('system.db_update', array('op' => 'selection'));
|
||||
$build['link'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Continue'),
|
||||
'#attributes' => array('class' => array('button', 'button--primary')),
|
||||
'#url' => $url,
|
||||
// @todo Revisit once https://www.drupal.org/node/2548095 is in.
|
||||
'#url' => Url::fromUri('base://selection'),
|
||||
);
|
||||
return $build;
|
||||
}
|
||||
|
@ -246,10 +237,13 @@ class DbUpdateController extends ControllerBase {
|
|||
/**
|
||||
* Renders a list of available database updates.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @return array
|
||||
* A render array.
|
||||
*/
|
||||
protected function selection() {
|
||||
protected function selection(Request $request) {
|
||||
// Make sure there is no stale theme registry.
|
||||
$this->cache->deleteAll();
|
||||
|
||||
|
@ -320,29 +314,12 @@ class DbUpdateController extends ControllerBase {
|
|||
drupal_set_message($this->t('Some of the pending updates cannot be applied because their dependencies were not met.'), 'warning');
|
||||
}
|
||||
|
||||
// If there are entity definition updates, display their summary.
|
||||
if ($this->entityDefinitionUpdateManager->needsUpdates()) {
|
||||
$entity_build = array();
|
||||
$summary = $this->entityDefinitionUpdateManager->getChangeSummary();
|
||||
foreach ($summary as $entity_type_id => $items) {
|
||||
$entity_update_key = 'entity_type_updates_' . $entity_type_id;
|
||||
$entity_build[$entity_update_key] = array(
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $items,
|
||||
'#title' => $entity_type_id . ' entity type',
|
||||
);
|
||||
$count++;
|
||||
}
|
||||
// Display these above the module updates, since they will be run first.
|
||||
$build['start'] = $entity_build + $build['start'];
|
||||
}
|
||||
|
||||
if (empty($count)) {
|
||||
drupal_set_message($this->t('No pending updates.'));
|
||||
unset($build);
|
||||
$build['links'] = array(
|
||||
'#theme' => 'links',
|
||||
'#links' => $this->helpfulLinks(),
|
||||
'#links' => $this->helpfulLinks($request),
|
||||
);
|
||||
|
||||
// No updates to run, so caches won't get flushed later. Clear them now.
|
||||
|
@ -364,7 +341,9 @@ class DbUpdateController extends ControllerBase {
|
|||
else {
|
||||
$build['start']['#title'] = $this->formatPlural($count, '1 pending update', '@count pending updates');
|
||||
}
|
||||
$url = new Url('system.db_update', array('op' => 'run'));
|
||||
// @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'),
|
||||
|
@ -380,15 +359,21 @@ class DbUpdateController extends ControllerBase {
|
|||
/**
|
||||
* 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() {
|
||||
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')->toString(TRUE)->getGeneratedUrl(),
|
||||
'@url' => Url::fromRoute('dblog.overview')->setOption('base_url', $base_url)->toString(TRUE)->getGeneratedUrl(),
|
||||
));
|
||||
}
|
||||
else {
|
||||
|
@ -396,7 +381,7 @@ class DbUpdateController extends ControllerBase {
|
|||
}
|
||||
|
||||
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>')->toString(TRUE)->getGeneratedUrl())) . ' ' . $log_message . '</p>';
|
||||
$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']);
|
||||
|
@ -420,7 +405,7 @@ class DbUpdateController extends ControllerBase {
|
|||
);
|
||||
$build['links'] = array(
|
||||
'#theme' => 'links',
|
||||
'#links' => $this->helpfulLinks(),
|
||||
'#links' => $this->helpfulLinks($request),
|
||||
);
|
||||
|
||||
// Output a list of info messages.
|
||||
|
@ -492,12 +477,18 @@ class DbUpdateController extends ControllerBase {
|
|||
/**
|
||||
* 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) {
|
||||
public function requirements($severity, array $requirements, Request $request) {
|
||||
$options = $severity == REQUIREMENT_WARNING ? array('continue' => 1) : array();
|
||||
$try_again_url = Url::fromRoute('system.db_update', $options)->toString(TRUE)->getGeneratedUrl();
|
||||
// @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',
|
||||
|
@ -544,13 +535,13 @@ class DbUpdateController extends ControllerBase {
|
|||
* The current request object.
|
||||
*/
|
||||
protected function triggerBatch(Request $request) {
|
||||
// During the update, bring the site offline so that schema changes do not
|
||||
// affect visiting users.
|
||||
$maintenance_mode = $this->config('system.maintenance')->get('enabled');
|
||||
if (isset($maintenance_mode)) {
|
||||
$_SESSION['maintenance_mode'] = $maintenance_mode;
|
||||
}
|
||||
if (empty($_SESSION['maintenance_mode'])) {
|
||||
$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);
|
||||
}
|
||||
|
||||
|
@ -584,16 +575,6 @@ class DbUpdateController extends ControllerBase {
|
|||
}
|
||||
}
|
||||
|
||||
// Lastly, perform entity definition updates, which will update storage
|
||||
// schema if needed. If module update functions need to work with specific
|
||||
// entity schema they should call the entity update service for the specific
|
||||
// update themselves.
|
||||
// @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface::applyEntityUpdate()
|
||||
// @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface::applyFieldUpdate()
|
||||
if ($this->entityDefinitionUpdateManager->needsUpdates()) {
|
||||
$operations[] = array('update_entity_definitions', array());
|
||||
}
|
||||
|
||||
$batch['operations'] = $operations;
|
||||
$batch += array(
|
||||
'title' => $this->t('Updating'),
|
||||
|
@ -603,7 +584,8 @@ class DbUpdateController extends ControllerBase {
|
|||
);
|
||||
batch_set($batch);
|
||||
|
||||
return batch_process('update.php/results', Url::fromRoute('system.db_update', array('op' => 'start')));
|
||||
// @todo Revisit once https://www.drupal.org/node/2548095 is in.
|
||||
return batch_process(Url::fromUri('base://results'), Url::fromUri('base://start'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -630,28 +612,33 @@ class DbUpdateController extends ControllerBase {
|
|||
$_SESSION['updates_remaining'] = $operations;
|
||||
|
||||
// Now that the update is done, we can put the site back online if it was
|
||||
// previously in maintenance mode.
|
||||
if (isset($_SESSION['maintenance_mode'])) {
|
||||
// previously not in maintenance mode.
|
||||
if (empty($_SESSION['maintenance_mode'])) {
|
||||
\Drupal::state()->set('system.maintenance_mode', FALSE);
|
||||
unset($_SESSION['maintenance_mode']);
|
||||
}
|
||||
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() {
|
||||
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>'),
|
||||
'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'),
|
||||
'url' => Url::fromRoute('system.admin')->setOption('base_url', $base_url),
|
||||
);
|
||||
}
|
||||
return $links;
|
||||
|
|
|
@ -68,7 +68,7 @@ class DateFormatListBuilder extends ConfigEntityListBuilder {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
$row['label'] = $this->getLabel($entity);
|
||||
$row['label'] = $entity->label();
|
||||
$row['pattern'] = $this->dateFormatter->format(REQUEST_TIME, $entity->id());
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
|
|
@ -130,12 +130,11 @@ abstract class DateFormatFormBase extends EntityForm {
|
|||
parent::validateForm($form, $form_state);
|
||||
|
||||
// The machine name field should already check to see if the requested
|
||||
// machine name is available. Regardless of machine_name or human readable
|
||||
// name, check to see if the provided pattern exists.
|
||||
// machine name is available.
|
||||
$pattern = trim($form_state->getValue('date_format_pattern'));
|
||||
foreach ($this->dateFormatStorage->loadMultiple() as $format) {
|
||||
if ($format->getPattern() == $pattern && ($this->entity->isNew() || $format->id() != $this->entity->id())) {
|
||||
$form_state->setErrorByName('date_format_pattern', $this->t('This format already exists. Enter a unique format string.'));
|
||||
if ($format->getPattern() == $pattern && ($format->id() == $this->entity->id())) {
|
||||
drupal_set_message(t('The existing format/name combination has not been altered.'));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
namespace Drupal\system\Form;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Config\PreExistingConfigException;
|
||||
use Drupal\Core\Config\UnmetDependenciesException;
|
||||
|
@ -169,7 +168,7 @@ class ModulesListForm extends FormBase {
|
|||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
require_once DRUPAL_ROOT . '/core/includes/install.inc';
|
||||
$distribution = SafeMarkup::checkPlain(drupal_install_profile_distribution_name());
|
||||
$distribution = drupal_install_profile_distribution_name();
|
||||
|
||||
// Include system.admin.inc so we can use the sort callbacks.
|
||||
$this->moduleHandler->loadInclude('system', 'inc', 'system.admin');
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Drupal\system;
|
|||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Access\AccessManagerInterface;
|
||||
use Drupal\Core\Breadcrumb\Breadcrumb;
|
||||
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Controller\TitleResolverInterface;
|
||||
|
@ -125,6 +126,7 @@ class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(RouteMatchInterface $route_match) {
|
||||
$breadcrumb = new Breadcrumb();
|
||||
$links = array();
|
||||
|
||||
// General path-based breadcrumbs. Use the actual request path, prior to
|
||||
|
@ -139,17 +141,21 @@ class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface {
|
|||
// /user is just a redirect, so skip it.
|
||||
// @todo Find a better way to deal with /user.
|
||||
$exclude['/user'] = TRUE;
|
||||
// Because this breadcrumb builder is entirely path-based, vary by the
|
||||
// 'url.path' cache context.
|
||||
$breadcrumb->setCacheContexts(['url.path']);
|
||||
while (count($path_elements) > 1) {
|
||||
array_pop($path_elements);
|
||||
// Copy the path elements for up-casting.
|
||||
$route_request = $this->getRequestForPath('/' . implode('/', $path_elements), $exclude);
|
||||
if ($route_request) {
|
||||
$route_match = RouteMatch::createFromRequest($route_request);
|
||||
$access = $this->accessManager->check($route_match, $this->currentUser);
|
||||
if ($access) {
|
||||
$access = $this->accessManager->check($route_match, $this->currentUser, NULL, TRUE);
|
||||
// The set of breadcrumb links depends on the access result, so merge
|
||||
// the access result's cacheability metadata.
|
||||
$breadcrumb = $breadcrumb->addCacheableDependency($access);
|
||||
if ($access->isAllowed()) {
|
||||
$title = $this->titleResolver->getTitle($route_request, $route_match->getRouteObject());
|
||||
}
|
||||
if ($access) {
|
||||
if (!isset($title)) {
|
||||
// Fallback to using the raw path component as the title if the
|
||||
// route is missing a _title or _title_callback attribute.
|
||||
|
@ -165,7 +171,8 @@ class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface {
|
|||
// Add the Home link, except for the front page.
|
||||
$links[] = Link::createFromRoute($this->t('Home'), '<front>');
|
||||
}
|
||||
return array_reverse($links);
|
||||
|
||||
return $breadcrumb->setLinks(array_reverse($links));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -77,20 +77,13 @@ class SystemBreadcrumbBlock extends BlockBase implements ContainerFactoryPluginI
|
|||
$breadcrumb = $this->breadcrumbManager->build($this->routeMatch);
|
||||
if (!empty($breadcrumb)) {
|
||||
// $breadcrumb is expected to be an array of rendered breadcrumb links.
|
||||
return array(
|
||||
$build = [
|
||||
'#theme' => 'breadcrumb',
|
||||
'#links' => $breadcrumb,
|
||||
);
|
||||
'#links' => $breadcrumb->getLinks(),
|
||||
];
|
||||
$breadcrumb->applyTo($build);
|
||||
return $build;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @todo Make cacheable in https://www.drupal.org/node/2483183
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -164,9 +164,7 @@ class RequestPath extends ConditionPluginBase implements ContainerFactoryPluginI
|
|||
*/
|
||||
public function getCacheContexts() {
|
||||
$contexts = parent::getCacheContexts();
|
||||
// @todo Add a url.path cache context in
|
||||
// https://www.drupal.org/node/2521978.
|
||||
$contexts[] = 'url';
|
||||
$contexts[] = 'url.path';
|
||||
return $contexts;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Plugin\migrate\destination\EntityDateFormat.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Plugin\migrate\destination;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\migrate\Plugin\migrate\destination\EntityConfigBase;
|
||||
|
||||
/**
|
||||
* @MigrateDestination(
|
||||
* id = "entity:date_format"
|
||||
* )
|
||||
*/
|
||||
class EntityDateFormat extends EntityConfigBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param \Drupal\Core\Datetime\DateFormatInterface $entity
|
||||
* The date entity.
|
||||
*/
|
||||
protected function updateEntityProperty(EntityInterface $entity, array $parents, $value) {
|
||||
if ($parents[0] == 'pattern') {
|
||||
$entity->setPattern($value);
|
||||
}
|
||||
else {
|
||||
parent::updateEntityProperty($entity, $parents, $value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\systeml\Plugin\migrate\process\d6\TimeZone.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Plugin\migrate\process\d6;
|
||||
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Process the D6 Timezone offset into a D8 compatible timezone name.
|
||||
*
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "timezone"
|
||||
* )
|
||||
*/
|
||||
class TimeZone extends ProcessPluginBase {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
$offset = $value;
|
||||
// Convert the integer value of the offset (which can be either
|
||||
// negative or positive) to a timezone name.
|
||||
// Note: Daylight saving time is not to be used.
|
||||
$timezone_name = timezone_name_from_abbr('', intval($offset), 0);
|
||||
if (!$timezone_name) {
|
||||
$timezone_name = 'UTC';
|
||||
}
|
||||
|
||||
return $timezone_name;
|
||||
}
|
||||
|
||||
}
|
48
core/modules/system/src/Plugin/migrate/source/Menu.php
Normal file
48
core/modules/system/src/Plugin/migrate/source/Menu.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Plugin\migrate\source\Menu.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* Menu source from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "menu",
|
||||
* source_provider = "menu"
|
||||
* )
|
||||
*/
|
||||
class Menu extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
return $this->select('menu_custom', 'm')->fields('m');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return array(
|
||||
'menu_name' => $this->t('The menu name. Primary key.'),
|
||||
'title' => $this->t('The human-readable name of the menu.'),
|
||||
'description' => $this->t('A description of the menu'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['menu_name']['type'] = 'string';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
}
|
|
@ -119,7 +119,7 @@ class SystemManager {
|
|||
usort($requirements, function($a, $b) {
|
||||
if (!isset($a['weight'])) {
|
||||
if (!isset($b['weight'])) {
|
||||
return strcmp($a['title'], $b['title']);
|
||||
return strcasecmp($a['title'], $b['title']);
|
||||
}
|
||||
return -$b['weight'];
|
||||
}
|
||||
|
|
|
@ -48,6 +48,9 @@ class FormValuesTest extends AjaxTestBase {
|
|||
// Verify that AJAX elements with invalid callbacks return error code 500.
|
||||
// Ensure the test error log is empty before these tests.
|
||||
$this->assertNoErrorsLogged();
|
||||
// We don't need to check for the X-Drupal-Ajax-Token header with these
|
||||
// invalid requests.
|
||||
$this->assertAjaxHeader = FALSE;
|
||||
foreach (array('null', 'empty', 'nonexistent') as $key) {
|
||||
$element_name = 'select_' . $key . '_callback';
|
||||
$edit = array(
|
||||
|
@ -56,6 +59,8 @@ class FormValuesTest extends AjaxTestBase {
|
|||
$commands = $this->drupalPostAjaxForm('ajax_forms_test_get_form', $edit, $element_name);
|
||||
$this->assertResponse(500);
|
||||
}
|
||||
// Switch this back to the default.
|
||||
$this->assertAjaxHeader = TRUE;
|
||||
// The exceptions are expected. Do not interpret them as a test failure.
|
||||
// Not using File API; a potential error must trigger a PHP warning.
|
||||
unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
|
||||
|
|
|
@ -165,7 +165,7 @@ class FrameworkTest extends AjaxTestBase {
|
|||
// the first AJAX command.
|
||||
$this->assertIdentical($new_settings[$expected['setting_name']], $expected['setting_value'], format_string('Page now has the %setting.', array('%setting' => $expected['setting_name'])));
|
||||
$expected_command = new SettingsCommand(array($expected['setting_name'] => $expected['setting_value']), TRUE);
|
||||
$this->assertCommand(array_slice($commands, 0, 1), $expected_command->render(), format_string('The settings command was first.'));
|
||||
$this->assertCommand(array_slice($commands, 0, 1), $expected_command->render(), 'The settings command was first.');
|
||||
|
||||
// Verify the expected CSS file was added, both to drupalSettings, and as
|
||||
// the second AJAX command for inclusion into the HTML.
|
||||
|
|
|
@ -60,7 +60,7 @@ class MultiFormTest extends AjaxTestBase {
|
|||
$field_name = 'field_ajax_test';
|
||||
|
||||
$form_xpath = '//form[starts-with(@id, "node-page-form")]';
|
||||
$field_xpath = '//div[contains(@class, "field-name-field-ajax-test")]';
|
||||
$field_xpath = '//div[contains(@class, "field--name-field-ajax-test")]';
|
||||
$button_name = $field_name . '_add_more';
|
||||
$button_value = t('Add another item');
|
||||
$button_xpath_suffix = '//input[@name="' . $button_name . '"]';
|
||||
|
|
|
@ -61,4 +61,19 @@ class PageTest extends WebTestBase {
|
|||
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the progress messages are correct.
|
||||
*/
|
||||
function testBatchProgressMessages() {
|
||||
// Go to the initial step only.
|
||||
$this->maximumMetaRefreshCount = 0;
|
||||
$this->drupalGet('batch-test/test-title');
|
||||
$this->assertRaw('<div class="progress__description">Initializing.<br /> </div>', 'Initial progress message appears correctly.');
|
||||
$this->assertNoRaw('&nbsp;', 'Initial progress message is not double escaped.');
|
||||
// Now also go to the next step.
|
||||
$this->maximumMetaRefreshCount = 1;
|
||||
$this->drupalGet('batch-test/test-title');
|
||||
$this->assertRaw('<div class="progress__description">Completed 1 of 1.</div>', 'Progress message for second step appears correctly.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\system\Tests\Batch;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
|
@ -21,7 +22,7 @@ class ProcessingTest extends WebTestBase {
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('batch_test');
|
||||
public static $modules = array('batch_test', 'test_page_test');
|
||||
|
||||
/**
|
||||
* Tests batches triggered outside of form submission.
|
||||
|
@ -34,6 +35,18 @@ class ProcessingTest extends WebTestBase {
|
|||
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests batches that redirect in the batch finished callback.
|
||||
*/
|
||||
function testBatchRedirectFinishedCallback() {
|
||||
// Displaying the page triggers batch 1.
|
||||
$this->drupalGet('batch-test/finish-redirect');
|
||||
$this->assertBatchMessages($this->_resultMessages('batch_1'), 'Batch for step 2 performed successfully.');
|
||||
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), 'Execution order was correct.');
|
||||
$this->assertText('Test page text.', 'Custom redirection after batch execution displays the correct page.');
|
||||
$this->assertUrl(Url::fromRoute('test_page_test.test_page'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests batches defined in a form submit handler.
|
||||
*/
|
||||
|
@ -244,28 +257,28 @@ class ProcessingTest extends WebTestBase {
|
|||
|
||||
switch ($id) {
|
||||
case 'batch_0':
|
||||
$messages[] = 'results for batch 0<br>none';
|
||||
$messages[] = 'results for batch 0<div class="item-list"><ul><li>none</li></ul></div>';
|
||||
break;
|
||||
|
||||
case 'batch_1':
|
||||
$messages[] = 'results for batch 1<br>op 1: processed 10 elements';
|
||||
$messages[] = 'results for batch 1<div class="item-list"><ul><li>op 1: processed 10 elements</li></ul></div>';
|
||||
break;
|
||||
|
||||
case 'batch_2':
|
||||
$messages[] = 'results for batch 2<br>op 2: processed 10 elements';
|
||||
$messages[] = 'results for batch 2<div class="item-list"><ul><li>op 2: processed 10 elements</li></ul></div>';
|
||||
break;
|
||||
|
||||
case 'batch_3':
|
||||
$messages[] = 'results for batch 3<br>op 1: processed 10 elements<br>op 2: processed 10 elements';
|
||||
$messages[] = 'results for batch 3<div class="item-list"><ul><li>op 1: processed 10 elements</li><li>op 2: processed 10 elements</li></ul></div>';
|
||||
break;
|
||||
|
||||
case 'batch_4':
|
||||
$messages[] = 'results for batch 4<br>op 1: processed 10 elements';
|
||||
$messages[] = 'results for batch 4<div class="item-list"><ul><li>op 1: processed 10 elements</li></ul></div>';
|
||||
$messages = array_merge($messages, $this->_resultMessages('batch_2'));
|
||||
break;
|
||||
|
||||
case 'batch_5':
|
||||
$messages[] = 'results for batch 5<br>op 5: processed 10 elements';
|
||||
$messages[] = 'results for batch 5<div class="item-list"><ul><li>op 5: processed 10 elements</li></ul></div>';
|
||||
break;
|
||||
|
||||
case 'chained':
|
||||
|
|
|
@ -24,23 +24,30 @@ class DrupalSetMessageTest extends WebTestBase {
|
|||
public static $modules = array('system_test');
|
||||
|
||||
/**
|
||||
* Tests setting messages and removing one before it is displayed.
|
||||
* Tests drupal_set_message().
|
||||
*/
|
||||
function testSetRemoveMessages() {
|
||||
function testDrupalSetMessage() {
|
||||
// The page at system-test/drupal-set-message sets two messages and then
|
||||
// removes the first before it is displayed.
|
||||
$this->drupalGet('system-test/drupal-set-message');
|
||||
$this->assertNoText('First message (removed).');
|
||||
$this->assertText('Second message (not removed).');
|
||||
}
|
||||
$this->assertRaw(t('Second message with <em>markup!</em> (not removed).'));
|
||||
|
||||
/**
|
||||
* Tests setting duplicated messages.
|
||||
*/
|
||||
function testDuplicatedMessages() {
|
||||
$this->drupalGet('system-test/drupal-set-message');
|
||||
// Ensure duplicate messages are handled as expected.
|
||||
$this->assertUniqueText('Non Duplicated message');
|
||||
$this->assertNoUniqueText('Duplicated message');
|
||||
|
||||
// Ensure SafeString objects are rendered as expected.
|
||||
$this->assertRaw('SafeString with <em>markup!</em>');
|
||||
$this->assertUniqueText('SafeString with markup!');
|
||||
$this->assertRaw('SafeString2 with <em>markup!</em>');
|
||||
|
||||
// Ensure when the same message is of different types it is not duplicated.
|
||||
$this->assertUniqueText('Non duplicate SafeString / string.');
|
||||
$this->assertNoUniqueText('Duplicate SafeString / string.');
|
||||
|
||||
// Ensure that strings that are not marked as safe are escaped.
|
||||
$this->assertEscaped('<em>This<span>markup will be</span> escaped</em>.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -185,9 +185,9 @@ class AttachedAssetsTest extends KernelTestBase {
|
|||
$rendered_footer_js = \Drupal::service('asset.js.collection_renderer')->render($footer_js);
|
||||
$this->assertTrue(
|
||||
count($rendered_footer_js) == 2
|
||||
&& substr($rendered_footer_js[0]['#value'], 0, 20) === 'var drupalSettings ='
|
||||
&& $rendered_footer_js[0]['#attributes']['data-drupal-selector'] === 'drupal-settings-json'
|
||||
&& substr($rendered_footer_js[1]['#attributes']['src'], 0, 7) === 'http://',
|
||||
'There are 2 JavaScript assets in the footer: one with drupalSettings, one with the sole aggregated JavaScript asset.'
|
||||
'There are 2 JavaScript assets in the footer: one with drupal settings, one with the sole aggregated JavaScript asset.'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -206,9 +206,9 @@ class AttachedAssetsTest extends KernelTestBase {
|
|||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
|
||||
// Parse the generated drupalSettings <script> back to a PHP representation.
|
||||
$startToken = 'drupalSettings = ';
|
||||
$startToken = '{';
|
||||
$endToken = '}';
|
||||
$start = strpos($rendered_js, $startToken) + strlen($startToken);
|
||||
$start = strpos($rendered_js, $startToken);
|
||||
$end = strrpos($rendered_js, $endToken);
|
||||
$json = Unicode::substr($rendered_js, $start, $end - $start + 1);
|
||||
$parsed_settings = Json::decode($json);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
namespace Drupal\system\Tests\Common;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
|
@ -46,12 +46,12 @@ class RenderElementTypesTest extends KernelTestBase {
|
|||
$actual_html = (string) \Drupal::service('renderer')->renderRoot($elements);
|
||||
|
||||
$out = '<table><tr>';
|
||||
$out .= '<td valign="top"><pre>' . SafeMarkup::checkPlain($expected_html) . '</pre></td>';
|
||||
$out .= '<td valign="top"><pre>' . SafeMarkup::checkPlain($actual_html) . '</pre></td>';
|
||||
$out .= '<td valign="top"><pre>' . Html::escape($expected_html) . '</pre></td>';
|
||||
$out .= '<td valign="top"><pre>' . Html::escape($actual_html) . '</pre></td>';
|
||||
$out .= '</tr></table>';
|
||||
$this->verbose($out);
|
||||
|
||||
$this->assertIdentical($actual_html, $expected_html, SafeMarkup::checkPlain($message));
|
||||
$this->assertIdentical($actual_html, $expected_html, Html::escape($message));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,8 +91,6 @@ class RenderElementTypesTest extends KernelTestBase {
|
|||
'#type' => 'html_tag',
|
||||
'#tag' => 'meta',
|
||||
'#value' => 'ignored',
|
||||
'#value_prefix' => 'ignored',
|
||||
'#value_suffix' => 'ignored',
|
||||
'#attributes' => array(
|
||||
'name' => 'description',
|
||||
'content' => 'Drupal test',
|
||||
|
@ -104,12 +102,10 @@ class RenderElementTypesTest extends KernelTestBase {
|
|||
'#type' => 'html_tag',
|
||||
'#tag' => 'section',
|
||||
'#value' => 'value',
|
||||
'#value_prefix' => 'value_prefix|',
|
||||
'#value_suffix' => '|value_suffix',
|
||||
'#attributes' => array(
|
||||
'class' => array('unicorns'),
|
||||
),
|
||||
), '<section class="unicorns">value_prefix|value|value_suffix</section>' . "\n", "#type 'html_tag', non-void element renders properly");
|
||||
), '<section class="unicorns">value</section>' . "\n", "#type 'html_tag', non-void element renders properly");
|
||||
|
||||
// Test empty void element tag.
|
||||
$this->assertElements(array(
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace Drupal\system\Tests\Common;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
|
@ -24,6 +26,24 @@ class RenderWebTest extends WebTestBase {
|
|||
*/
|
||||
public static $modules = array('common_test');
|
||||
|
||||
/**
|
||||
* Asserts the cache context for the wrapper format is always present.
|
||||
*/
|
||||
function testWrapperFormatCacheContext() {
|
||||
$this->drupalGet('common-test/type-link-active-class');
|
||||
$this->assertIdentical(0, strpos($this->getRawContent(), "<!DOCTYPE html>\n<html"));
|
||||
$this->assertIdentical('text/html; charset=UTF-8', $this->drupalGetHeader('Content-Type'));
|
||||
$this->assertTitle('Test active link class | Drupal');
|
||||
$this->assertCacheContext('url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT);
|
||||
|
||||
$this->drupalGet('common-test/type-link-active-class', ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'json']]);
|
||||
$this->assertIdentical('application/json', $this->drupalGetHeader('Content-Type'));
|
||||
$json = Json::decode($this->getRawContent());
|
||||
$this->assertEqual(['content', 'title'], array_keys($json));
|
||||
$this->assertIdentical('Test active link class', $json['title']);
|
||||
$this->assertCacheContext('url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rendering form elements without passing through
|
||||
* \Drupal::formBuilder()->doBuildForm().
|
||||
|
|
|
@ -44,7 +44,7 @@ class SimpleTestErrorCollectorTest extends WebTestBase {
|
|||
if (count($this->collectedErrors) == 3) {
|
||||
$this->assertError($this->collectedErrors[0], 'Notice', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Undefined variable: bananas');
|
||||
$this->assertError($this->collectedErrors[1], 'Warning', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Division by zero');
|
||||
$this->assertError($this->collectedErrors[2], 'User warning', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Drupal is awesome');
|
||||
$this->assertError($this->collectedErrors[2], 'User warning', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Drupal & awesome');
|
||||
}
|
||||
else {
|
||||
// Give back the errors to the log report.
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
namespace Drupal\system\Tests\Common;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
|
@ -38,7 +38,7 @@ class TableSortExtenderUnitTest extends KernelTestBase {
|
|||
$request->query->replace(array());
|
||||
\Drupal::getContainer()->get('request_stack')->push($request);
|
||||
$ts = tablesort_init($headers);
|
||||
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => SafeMarkup::checkPlain(var_export($ts, TRUE)))));
|
||||
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
|
||||
$this->assertEqual($ts, $expected_ts, 'Simple table headers sorted correctly.');
|
||||
|
||||
// Test with simple table headers plus $_GET parameters that should _not_
|
||||
|
@ -51,7 +51,7 @@ class TableSortExtenderUnitTest extends KernelTestBase {
|
|||
));
|
||||
\Drupal::getContainer()->get('request_stack')->push($request);
|
||||
$ts = tablesort_init($headers);
|
||||
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => SafeMarkup::checkPlain(var_export($ts, TRUE)))));
|
||||
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
|
||||
$this->assertEqual($ts, $expected_ts, 'Simple table headers plus non-overriding $_GET parameters sorted correctly.');
|
||||
|
||||
// Test with simple table headers plus $_GET parameters that _should_
|
||||
|
@ -67,7 +67,7 @@ class TableSortExtenderUnitTest extends KernelTestBase {
|
|||
$expected_ts['sort'] = 'desc';
|
||||
$expected_ts['query'] = array('alpha' => 'beta');
|
||||
$ts = tablesort_init($headers);
|
||||
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => SafeMarkup::checkPlain(var_export($ts, TRUE)))));
|
||||
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
|
||||
$this->assertEqual($ts, $expected_ts, 'Simple table headers plus $_GET parameters sorted correctly.');
|
||||
|
||||
// Test complex table headers.
|
||||
|
@ -99,7 +99,7 @@ class TableSortExtenderUnitTest extends KernelTestBase {
|
|||
'sort' => 'desc',
|
||||
'query' => array(),
|
||||
);
|
||||
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => SafeMarkup::checkPlain(var_export($ts, TRUE)))));
|
||||
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
|
||||
$this->assertEqual($ts, $expected_ts, 'Complex table headers sorted correctly.');
|
||||
|
||||
// Test complex table headers plus $_GET parameters that should _not_
|
||||
|
@ -118,7 +118,7 @@ class TableSortExtenderUnitTest extends KernelTestBase {
|
|||
'sort' => 'asc',
|
||||
'query' => array(),
|
||||
);
|
||||
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => SafeMarkup::checkPlain(var_export($ts, TRUE)))));
|
||||
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
|
||||
$this->assertEqual($ts, $expected_ts, 'Complex table headers plus non-overriding $_GET parameters sorted correctly.');
|
||||
|
||||
// Test complex table headers plus $_GET parameters that _should_
|
||||
|
@ -139,7 +139,7 @@ class TableSortExtenderUnitTest extends KernelTestBase {
|
|||
'query' => array('alpha' => 'beta'),
|
||||
);
|
||||
$ts = tablesort_init($headers);
|
||||
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => SafeMarkup::checkPlain(var_export($ts, TRUE)))));
|
||||
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
|
||||
$this->assertEqual($ts, $expected_ts, 'Complex table headers plus $_GET parameters sorted correctly.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,14 +33,14 @@ class UrlTest extends WebTestBase {
|
|||
// Test \Drupal::l().
|
||||
$text = $this->randomMachineName();
|
||||
$path = "<SCRIPT>alert('XSS')</SCRIPT>";
|
||||
$encoded_path = "3CSCRIPT%3Ealert%28%27XSS%27%29%3C/SCRIPT%3E";
|
||||
|
||||
$link = \Drupal::l($text, Url::fromUserInput('/' . $path));
|
||||
$sanitized_path = check_url(Url::fromUri('base:' . $path)->toString());
|
||||
$this->assertTrue(strpos($link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered by _l().', array('@path' => $path)));
|
||||
$this->assertTrue(strpos($link, $encoded_path) !== FALSE && strpos($link, $path) === FALSE, format_string('XSS attack @path was filtered by _l().', array('@path' => $path)));
|
||||
|
||||
// Test \Drupal\Core\Url.
|
||||
$link = Url::fromUri('base:' . $path)->toString();
|
||||
$sanitized_path = check_url(Url::fromUri('base:' . $path)->toString());
|
||||
$this->assertTrue(strpos($link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered by #theme', ['@path' => $path]));
|
||||
$this->assertTrue(strpos($link, $encoded_path) !== FALSE && strpos($link, $path) === FALSE, format_string('XSS attack @path was filtered by #theme', ['@path' => $path]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -56,6 +56,8 @@ class XssUnitTest extends KernelTestBase {
|
|||
$expected_plain = 'http://www.example.com/?x=1&y=2';
|
||||
$expected_html = 'http://www.example.com/?x=1&y=2';
|
||||
$this->assertIdentical(check_url($url), $expected_html, 'check_url() filters a URL and encodes it for HTML.');
|
||||
$this->assertIdentical(UrlHelper::stripDangerousProtocols($url), $expected_plain, '\Drupal\Component\Utility\Url::stripDangerousProtocols() filters a URL and returns plain text.');
|
||||
$this->assertIdentical(UrlHelper::filterBadProtocol($url), $expected_html, '\Drupal\Component\Utility\UrlHelper::filterBadProtocol() filters a URL and encodes it for HTML.');
|
||||
$this->assertIdentical(UrlHelper::stripDangerousProtocols($url), $expected_plain, '\Drupal\Component\Utility\UrlHelper::stripDangerousProtocols() filters a URL and returns plain text.');
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\system\Tests\Database;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\SchemaException;
|
||||
use Drupal\Core\Database\SchemaObjectDoesNotExistException;
|
||||
use Drupal\Core\Database\SchemaObjectExistsException;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
@ -99,7 +100,7 @@ class SchemaTest extends KernelTestBase {
|
|||
$index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
|
||||
$this->assertIdentical($index_exists, FALSE, 'Fake index does not exists');
|
||||
// Add index.
|
||||
db_add_index('test_table', 'test_field', array('test_field'));
|
||||
db_add_index('test_table', 'test_field', array('test_field'), $table_specification);
|
||||
// Test for created index and test for the boolean result of indexExists().
|
||||
$index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
|
||||
$this->assertIdentical($index_exists, TRUE, 'Index created.');
|
||||
|
@ -295,6 +296,43 @@ class SchemaTest extends KernelTestBase {
|
|||
);
|
||||
db_create_table('test_table_index_length', $table_specification);
|
||||
|
||||
$schema_object = Database::getConnection()->schema();
|
||||
|
||||
// Ensure expected exception thrown when adding index with missing info.
|
||||
$expected_exception_message = "MySQL needs the 'test_field_text' field specification in order to normalize the 'test_regular' index";
|
||||
$missing_field_spec = $table_specification;
|
||||
unset($missing_field_spec['fields']['test_field_text']);
|
||||
try {
|
||||
$schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $missing_field_spec);
|
||||
$this->fail('SchemaException not thrown when adding index with missing information.');
|
||||
}
|
||||
catch (SchemaException $e) {
|
||||
$this->assertEqual($expected_exception_message, $e->getMessage());
|
||||
}
|
||||
|
||||
// Add a separate index.
|
||||
$schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification);
|
||||
$table_specification_with_new_index = $table_specification;
|
||||
$table_specification_with_new_index['indexes']['test_separate'] = [['test_field_text', 200]];
|
||||
|
||||
// Ensure that the exceptions of addIndex are thrown as expected.
|
||||
|
||||
try {
|
||||
$schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification);
|
||||
$this->fail('\Drupal\Core\Database\SchemaObjectExistsException exception missed.');
|
||||
}
|
||||
catch (SchemaObjectExistsException $e) {
|
||||
$this->pass('\Drupal\Core\Database\SchemaObjectExistsException thrown when index already exists.');
|
||||
}
|
||||
|
||||
try {
|
||||
$schema_object->addIndex('test_table_non_existing', 'test_separate', [['test_field_text', 200]], $table_specification);
|
||||
$this->fail('\Drupal\Core\Database\SchemaObjectDoesNotExistException exception missed.');
|
||||
}
|
||||
catch (SchemaObjectDoesNotExistException $e) {
|
||||
$this->pass('\Drupal\Core\Database\SchemaObjectDoesNotExistException thrown when index already exists.');
|
||||
}
|
||||
|
||||
// Get index information.
|
||||
$results = db_query('SHOW INDEX FROM {test_table_index_length}');
|
||||
$expected_lengths = array(
|
||||
|
@ -316,11 +354,14 @@ class SchemaTest extends KernelTestBase {
|
|||
'test_field_string_ascii_long' => 200,
|
||||
'test_field_string_short' => NULL,
|
||||
),
|
||||
'test_separate' => array(
|
||||
'test_field_text' => 191,
|
||||
),
|
||||
);
|
||||
|
||||
// Count the number of columns defined in the indexes.
|
||||
$column_count = 0;
|
||||
foreach ($table_specification['indexes'] as $index) {
|
||||
foreach ($table_specification_with_new_index['indexes'] as $index) {
|
||||
foreach ($index as $field) {
|
||||
$column_count++;
|
||||
}
|
||||
|
@ -657,4 +698,63 @@ class SchemaTest extends KernelTestBase {
|
|||
// Clean-up.
|
||||
db_drop_table($table_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the findTables() method.
|
||||
*/
|
||||
public function testFindTables() {
|
||||
// We will be testing with three tables, two of them using the default
|
||||
// prefix and the third one with an individually specified prefix.
|
||||
|
||||
// Set up a new connection with different connection info.
|
||||
$connection_info = Database::getConnectionInfo();
|
||||
|
||||
// Add per-table prefix to the second table.
|
||||
$new_connection_info = $connection_info['default'];
|
||||
$new_connection_info['prefix']['test_2_table'] = $new_connection_info['prefix']['default'] . '_shared_';
|
||||
Database::addConnectionInfo('test', 'default', $new_connection_info);
|
||||
|
||||
Database::setActiveConnection('test');
|
||||
|
||||
// Create the tables.
|
||||
$table_specification = [
|
||||
'description' => 'Test table.',
|
||||
'fields' => [
|
||||
'id' => [
|
||||
'type' => 'int',
|
||||
'default' => NULL,
|
||||
],
|
||||
],
|
||||
];
|
||||
Database::getConnection()->schema()->createTable('test_1_table', $table_specification);
|
||||
Database::getConnection()->schema()->createTable('test_2_table', $table_specification);
|
||||
Database::getConnection()->schema()->createTable('the_third_table', $table_specification);
|
||||
|
||||
// Check the "all tables" syntax.
|
||||
$tables = Database::getConnection()->schema()->findTables('%');
|
||||
sort($tables);
|
||||
$expected = [
|
||||
// The 'config' table is added by
|
||||
// \Drupal\simpletest\KernelTestBase::containerBuild().
|
||||
'config',
|
||||
'test_1_table',
|
||||
// This table uses a per-table prefix, yet it is returned as un-prefixed.
|
||||
'test_2_table',
|
||||
'the_third_table',
|
||||
];
|
||||
$this->assertEqual($tables, $expected, 'All tables were found.');
|
||||
|
||||
// Check the restrictive syntax.
|
||||
$tables = Database::getConnection()->schema()->findTables('test_%');
|
||||
sort($tables);
|
||||
$expected = [
|
||||
'test_1_table',
|
||||
'test_2_table',
|
||||
];
|
||||
$this->assertEqual($tables, $expected, 'Two tables were found.');
|
||||
|
||||
// Go back to the initial connection.
|
||||
Database::setActiveConnection('default');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\system\Tests\Database;
|
||||
use Drupal\Core\Database\InvalidQueryException;
|
||||
use Drupal\Core\Database\Database;
|
||||
|
||||
/**
|
||||
* Tests the Select query builder.
|
||||
|
@ -57,10 +58,47 @@ class SelectTest extends DatabaseTestBase {
|
|||
$records = $result->fetchAll();
|
||||
|
||||
$query = (string) $query;
|
||||
$expected = "/* Testing query comments SELECT nid FROM {node}; -- */";
|
||||
$expected = "/* Testing query comments * / SELECT nid FROM {node}; -- */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
|
||||
|
||||
$this->assertEqual(count($records), 4, 'Returned the correct number of rows.');
|
||||
$this->assertNotIdentical(FALSE, strpos($query, $expected), 'The flattened query contains the sanitised comment string.');
|
||||
|
||||
$connection = Database::getConnection();
|
||||
foreach ($this->makeCommentsProvider() as $test_set) {
|
||||
list($expected, $comments) = $test_set;
|
||||
$this->assertEqual($expected, $connection->makeComment($comments));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides expected and input values for testVulnerableComment().
|
||||
*/
|
||||
function makeCommentsProvider() {
|
||||
return [
|
||||
[
|
||||
'/* */ ',
|
||||
[''],
|
||||
],
|
||||
// Try and close the comment early.
|
||||
[
|
||||
'/* Exploit * / DROP TABLE node; -- */ ',
|
||||
['Exploit */ DROP TABLE node; --'],
|
||||
],
|
||||
// Variations on comment closing.
|
||||
[
|
||||
'/* Exploit * / * / DROP TABLE node; -- */ ',
|
||||
['Exploit */*/ DROP TABLE node; --'],
|
||||
],
|
||||
[
|
||||
'/* Exploit * * // DROP TABLE node; -- */ ',
|
||||
['Exploit **// DROP TABLE node; --'],
|
||||
],
|
||||
// Try closing the comment in the second string which is appended.
|
||||
[
|
||||
'/* Exploit * / DROP TABLE node; --; Another try * / DROP TABLE node; -- */ ',
|
||||
['Exploit */ DROP TABLE node; --', 'Another try */ DROP TABLE node; --'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
61
core/modules/system/src/Tests/Database/UpsertTest.php
Normal file
61
core/modules/system/src/Tests/Database/UpsertTest.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Database\UpsertTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Database;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
|
||||
/**
|
||||
* Tests the Upsert query builder.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class UpsertTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Confirms that we can upsert (update-or-insert) records successfully.
|
||||
*/
|
||||
public function testUpsert() {
|
||||
$connection = Database::getConnection();
|
||||
$num_records_before = $connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
|
||||
$upsert = $connection->upsert('test_people')
|
||||
->key('job')
|
||||
->fields(['job', 'age', 'name']);
|
||||
|
||||
// Add a new row.
|
||||
$upsert->values([
|
||||
'job' => 'Presenter',
|
||||
'age' => 31,
|
||||
'name' => 'Tiffany',
|
||||
]);
|
||||
|
||||
// Update an existing row.
|
||||
$upsert->values([
|
||||
'job' => 'Speaker',
|
||||
// The initial age was 30.
|
||||
'age' => 32,
|
||||
'name' => 'Meredith',
|
||||
]);
|
||||
|
||||
$upsert->execute();
|
||||
|
||||
$num_records_after = $connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
$this->assertEqual($num_records_before + 1, $num_records_after, 'Rows were inserted and updated properly.');
|
||||
|
||||
$person = $connection->query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
|
||||
$this->assertEqual($person->job, 'Presenter', 'Job set correctly.');
|
||||
$this->assertEqual($person->age, 31, 'Age set correctly.');
|
||||
$this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
|
||||
|
||||
$person = $connection->query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
|
||||
$this->assertEqual($person->job, 'Speaker', 'Job was not changed.');
|
||||
$this->assertEqual($person->age, 32, 'Age updated correctly.');
|
||||
$this->assertEqual($person->name, 'Meredith', 'Name was not changed.');
|
||||
}
|
||||
|
||||
}
|
|
@ -55,13 +55,11 @@ class DrupalKernelTest extends KernelTestBase {
|
|||
* A request object to use in booting the kernel.
|
||||
* @param array $modules_enabled
|
||||
* A list of modules to enable on the kernel.
|
||||
* @param bool $read_only
|
||||
* Build the kernel in a read only state.
|
||||
*
|
||||
* @return \Drupal\Core\DrupalKernel
|
||||
* New kernel for testing.
|
||||
*/
|
||||
protected function getTestKernel(Request $request, array $modules_enabled = NULL, $read_only = FALSE) {
|
||||
protected function getTestKernel(Request $request, array $modules_enabled = NULL) {
|
||||
// Manually create kernel to avoid replacing settings.
|
||||
$class_loader = require DRUPAL_ROOT . '/autoload.php';
|
||||
$kernel = DrupalKernel::createFromRequest($request, $class_loader, 'testing');
|
||||
|
@ -72,11 +70,6 @@ class DrupalKernelTest extends KernelTestBase {
|
|||
}
|
||||
$kernel->boot();
|
||||
|
||||
if ($read_only) {
|
||||
$php_storage = Settings::get('php_storage');
|
||||
$php_storage['service_container']['class'] = 'Drupal\Component\PhpStorage\FileReadOnlyStorage';
|
||||
$this->settingsSet('php_storage', $php_storage);
|
||||
}
|
||||
return $kernel;
|
||||
}
|
||||
|
||||
|
@ -98,24 +91,19 @@ class DrupalKernelTest extends KernelTestBase {
|
|||
$kernel = $this->getTestKernel($request);
|
||||
$container = $kernel->getContainer();
|
||||
$refClass = new \ReflectionClass($container);
|
||||
$is_compiled_container =
|
||||
$refClass->getParentClass()->getName() == 'Drupal\Core\DependencyInjection\Container' &&
|
||||
!$refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder');
|
||||
$is_compiled_container = !$refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder');
|
||||
$this->assertTrue($is_compiled_container);
|
||||
// Verify that the list of modules is the same for the initial and the
|
||||
// compiled container.
|
||||
$module_list = array_keys($container->get('module_handler')->getModuleList());
|
||||
$this->assertEqual(array_values($modules_enabled), $module_list);
|
||||
|
||||
// Now use the read-only storage implementation, simulating a "production"
|
||||
// environment.
|
||||
$container = $this->getTestKernel($request, NULL, TRUE)
|
||||
// Get the container another time, simulating a "production" environment.
|
||||
$container = $this->getTestKernel($request, NULL)
|
||||
->getContainer();
|
||||
|
||||
$refClass = new \ReflectionClass($container);
|
||||
$is_compiled_container =
|
||||
$refClass->getParentClass()->getName() == 'Drupal\Core\DependencyInjection\Container' &&
|
||||
!$refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder');
|
||||
$is_compiled_container = !$refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder');
|
||||
$this->assertTrue($is_compiled_container);
|
||||
|
||||
// Verify that the list of modules is the same for the initial and the
|
||||
|
@ -137,16 +125,16 @@ class DrupalKernelTest extends KernelTestBase {
|
|||
// Add another module so that we can test that the new module's bundle is
|
||||
// registered to the new container.
|
||||
$modules_enabled['service_provider_test'] = 'service_provider_test';
|
||||
$this->getTestKernel($request, $modules_enabled, TRUE);
|
||||
$this->getTestKernel($request, $modules_enabled);
|
||||
|
||||
// Instantiate it a second time and we should still get a ContainerBuilder
|
||||
// class because we are using the read-only PHP storage.
|
||||
$kernel = $this->getTestKernel($request, $modules_enabled, TRUE);
|
||||
// Instantiate it a second time and we should not get a ContainerBuilder
|
||||
// class because we are loading the container definition from cache.
|
||||
$kernel = $this->getTestKernel($request, $modules_enabled);
|
||||
$container = $kernel->getContainer();
|
||||
|
||||
$refClass = new \ReflectionClass($container);
|
||||
$is_container_builder = $refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder');
|
||||
$this->assertTrue($is_container_builder, 'Container is a builder');
|
||||
$this->assertFalse($is_container_builder, 'Container is not a builder');
|
||||
|
||||
// Assert that the new module's bundle was registered to the new container.
|
||||
$this->assertTrue($container->has('service_provider_test_class'), 'Container has test service');
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace Drupal\system\Tests\Entity;
|
|||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\Tags;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\system\Controller\EntityAutocompleteController;
|
||||
|
@ -65,8 +65,8 @@ class EntityAutocompleteTest extends EntityUnitTestBase {
|
|||
// We should get both entities in a JSON encoded string.
|
||||
$input = '10/';
|
||||
$data = $this->getAutocompleteResult($input);
|
||||
$this->assertIdentical($data[0]['label'], SafeMarkup::checkPlain($entity_1->name->value), 'Autocomplete returned the first matching entity');
|
||||
$this->assertIdentical($data[1]['label'], SafeMarkup::checkPlain($entity_2->name->value), 'Autocomplete returned the second matching entity');
|
||||
$this->assertIdentical($data[0]['label'], Html::escape($entity_1->name->value), 'Autocomplete returned the first matching entity');
|
||||
$this->assertIdentical($data[1]['label'], Html::escape($entity_2->name->value), 'Autocomplete returned the second matching entity');
|
||||
|
||||
// Try to autocomplete a entity label that matches the first entity.
|
||||
// We should only get the first entity in a JSON encoded string.
|
||||
|
@ -74,7 +74,7 @@ class EntityAutocompleteTest extends EntityUnitTestBase {
|
|||
$data = $this->getAutocompleteResult($input);
|
||||
$target = array(
|
||||
'value' => $entity_1->name->value . ' (1)',
|
||||
'label' => SafeMarkup::checkPlain($entity_1->name->value),
|
||||
'label' => Html::escape($entity_1->name->value),
|
||||
);
|
||||
$this->assertIdentical(reset($data), $target, 'Autocomplete returns only the expected matching entity.');
|
||||
|
||||
|
@ -82,7 +82,7 @@ class EntityAutocompleteTest extends EntityUnitTestBase {
|
|||
// the first entity is already typed in the autocomplete (tags) widget.
|
||||
$input = $entity_1->name->value . ' (1), 10/17';
|
||||
$data = $this->getAutocompleteResult($input);
|
||||
$this->assertIdentical($data[0]['label'], SafeMarkup::checkPlain($entity_2->name->value), 'Autocomplete returned the second matching entity');
|
||||
$this->assertIdentical($data[0]['label'], Html::escape($entity_2->name->value), 'Autocomplete returned the second matching entity');
|
||||
|
||||
// Try to autocomplete a entity label with both a comma and a slash.
|
||||
$input = '"label with, and / t';
|
||||
|
@ -92,7 +92,7 @@ class EntityAutocompleteTest extends EntityUnitTestBase {
|
|||
$n = Tags::encode($n);
|
||||
$target = array(
|
||||
'value' => $n,
|
||||
'label' => SafeMarkup::checkPlain($entity_3->name->value),
|
||||
'label' => Html::escape($entity_3->name->value),
|
||||
);
|
||||
$this->assertIdentical(reset($data), $target, 'Autocomplete returns an entity label containing a comma and a slash.');
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Drupal\system\Tests\Entity;
|
|||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
@ -337,6 +338,7 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
|
|||
// The default cache contexts for rendered entities.
|
||||
$default_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'];
|
||||
$entity_cache_contexts = $default_cache_contexts;
|
||||
$page_cache_contexts = Cache::mergeContexts($default_cache_contexts, ['url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT]);
|
||||
|
||||
// Cache tags present on every rendered page.
|
||||
// 'user.permissions' is a required cache context, and responses that vary
|
||||
|
@ -428,7 +430,7 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
|
|||
$this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags);
|
||||
// Verify the entity type's list cache contexts are present.
|
||||
$contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts');
|
||||
$this->assertEqual(Cache::mergeContexts($default_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
|
||||
$this->assertEqual(Cache::mergeContexts($page_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
|
||||
|
||||
|
||||
$this->pass("Test listing containing referenced entity.", 'Debug');
|
||||
|
@ -438,7 +440,7 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
|
|||
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags);
|
||||
// Verify the entity type's list cache contexts are present.
|
||||
$contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts');
|
||||
$this->assertEqual(Cache::mergeContexts($default_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
|
||||
$this->assertEqual(Cache::mergeContexts($page_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
|
||||
|
||||
|
||||
// Verify that after modifying the referenced entity, there is a cache miss
|
||||
|
@ -509,13 +511,12 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
|
|||
}
|
||||
|
||||
|
||||
$bundle_entity_type = $this->entity->getEntityType()->getBundleEntityType();
|
||||
if ($bundle_entity_type !== 'bundle') {
|
||||
if ($bundle_entity_type_id = $this->entity->getEntityType()->getBundleEntityType()) {
|
||||
// Verify that after modifying the corresponding bundle entity, there is a
|
||||
// cache miss for both the referencing entity, and the listing of
|
||||
// referencing entities, but not for any other routes.
|
||||
$this->pass("Test modification of referenced entity's bundle entity.", 'Debug');
|
||||
$bundle_entity = entity_load($bundle_entity_type, $this->entity->bundle());
|
||||
$bundle_entity = entity_load($bundle_entity_type_id, $this->entity->bundle());
|
||||
$bundle_entity->save();
|
||||
$this->verifyPageCache($referencing_entity_url, 'MISS');
|
||||
$this->verifyPageCache($listing_url, 'MISS');
|
||||
|
|
|
@ -125,6 +125,17 @@ trait EntityDefinitionTestTrait {
|
|||
$this->addBaseField('text');
|
||||
}
|
||||
|
||||
/**
|
||||
* Promotes a field to an entity key.
|
||||
*/
|
||||
protected function makeBaseFieldEntityKey() {
|
||||
$entity_type = clone $this->entityManager->getDefinition('entity_test_update');
|
||||
$entity_keys = $entity_type->getKeys();
|
||||
$entity_keys['new_base_field'] = 'new_base_field';
|
||||
$entity_type->set('entity_keys', $entity_keys);
|
||||
$this->state->set('entity_test_update.entity_type', $entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the new base field from the 'entity_test_update' entity type.
|
||||
*/
|
||||
|
|
|
@ -7,16 +7,17 @@
|
|||
|
||||
namespace Drupal\system\Tests\Entity;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\DatabaseExceptionWrapper;
|
||||
use Drupal\Core\Database\IntegrityConstraintViolationException;
|
||||
use Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface;
|
||||
use Drupal\Core\Entity\ContentEntityType;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Entity\EntityTypeEvents;
|
||||
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionEvents;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\entity_test\FieldStorageDefinition;
|
||||
|
||||
/**
|
||||
* Tests EntityDefinitionUpdateManager functionality.
|
||||
|
@ -508,6 +509,18 @@ class EntityDefinitionUpdateTest extends EntityUnitTestBase {
|
|||
// Run the update and ensure the index is deleted.
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index deleted.');
|
||||
|
||||
// Test that composite indexes are handled correctly when dropping and
|
||||
// re-creating one of their columns.
|
||||
$this->addEntityIndex();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$storage_definition = $this->entityDefinitionUpdateManager->getFieldStorageDefinition('name', 'entity_test_update');
|
||||
$this->entityDefinitionUpdateManager->updateFieldStorageDefinition($storage_definition);
|
||||
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created.');
|
||||
$this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($storage_definition);
|
||||
$this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index deleted.');
|
||||
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('name', 'entity_test_update', 'entity_test', $storage_definition);
|
||||
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created again.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -609,21 +622,178 @@ class EntityDefinitionUpdateTest extends EntityUnitTestBase {
|
|||
* Tests ::applyEntityUpdate() and ::applyFieldUpdate().
|
||||
*/
|
||||
public function testSingleActionCalls() {
|
||||
// Ensure that the methods return FALSE when called with bogus information.
|
||||
$this->assertFalse($this->entityDefinitionUpdateManager->applyEntityUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'foo'), 'Calling applyEntityUpdate() with a non-existent entity returns FALSE.');
|
||||
$this->assertFalse($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'foo', 'bar'), 'Calling applyFieldUpdate() with a non-existent entity returns FALSE.');
|
||||
$this->assertFalse($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'entity_test_update', 'bar'), 'Calling applyFieldUpdate() with a non-existent field returns FALSE.');
|
||||
$this->assertFalse($this->entityDefinitionUpdateManager->applyEntityUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'entity_test_update'), 'Calling applyEntityUpdate() with an $op that is not applicable to the entity type returns FALSE.');
|
||||
$this->assertFalse($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_DELETED, 'entity_test_update', 'new_base_field'), 'Calling applyFieldUpdate() with an $op that is not applicable to the field returns FALSE.');
|
||||
$db_schema = $this->database->schema();
|
||||
|
||||
// Ensure that a non-existing entity type cannot be installed.
|
||||
$message = 'A non-existing entity type cannot be installed';
|
||||
try {
|
||||
$this->entityDefinitionUpdateManager->installEntityType(new ContentEntityType(['id' => 'foo']));
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (PluginNotFoundException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Ensure that a field cannot be installed on non-existing entity type.
|
||||
$message = 'A field cannot be installed on a non-existing entity type';
|
||||
try {
|
||||
$storage_definition = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('A new revisionable base field'))
|
||||
->setRevisionable(TRUE);
|
||||
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('bar', 'foo', 'entity_test', $storage_definition);
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (PluginNotFoundException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Ensure that a non-existing field cannot be installed.
|
||||
$storage_definition = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('A new revisionable base field'))
|
||||
->setRevisionable(TRUE);
|
||||
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('bar', 'entity_test_update', 'entity_test', $storage_definition);
|
||||
$this->assertFalse($db_schema->fieldExists('entity_test_update', 'bar'), "A non-existing field cannot be installed.");
|
||||
|
||||
// Ensure that installing an existing entity type is a no-op.
|
||||
$entity_type = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update');
|
||||
$this->entityDefinitionUpdateManager->installEntityType($entity_type);
|
||||
$this->assertTrue($db_schema->tableExists('entity_test_update'), 'Installing an existing entity type is a no-op');
|
||||
|
||||
// Create a new base field.
|
||||
$this->addRevisionableBaseField();
|
||||
$this->assertTrue($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'entity_test_update', 'new_base_field'), 'Calling applyFieldUpdate() correctly returns TRUE.');
|
||||
$this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
|
||||
$storage_definition = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('A new revisionable base field'))
|
||||
->setRevisionable(TRUE);
|
||||
$this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' does not exist before applying the update.");
|
||||
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
|
||||
$this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
|
||||
|
||||
// Ensure that installing an existing entity type is a no-op.
|
||||
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
|
||||
$this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), 'Installing an existing entity type is a no-op');
|
||||
|
||||
// Update an existing field schema.
|
||||
$this->modifyBaseField();
|
||||
$storage_definition = BaseFieldDefinition::create('text')
|
||||
->setName('new_base_field')
|
||||
->setTargetEntityTypeId('entity_test_update')
|
||||
->setLabel(t('A new revisionable base field'))
|
||||
->setRevisionable(TRUE);
|
||||
$this->entityDefinitionUpdateManager->updateFieldStorageDefinition($storage_definition);
|
||||
$this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "Previous schema for 'new_base_field' no longer exists.");
|
||||
$this->assertTrue(
|
||||
$db_schema->fieldExists('entity_test_update', 'new_base_field__value') && $db_schema->fieldExists('entity_test_update', 'new_base_field__format'),
|
||||
"New schema for 'new_base_field' has been created."
|
||||
);
|
||||
|
||||
// Drop an existing field schema.
|
||||
$this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($storage_definition);
|
||||
$this->assertFalse(
|
||||
$db_schema->fieldExists('entity_test_update', 'new_base_field__value') || $db_schema->fieldExists('entity_test_update', 'new_base_field__format'),
|
||||
"The schema for 'new_base_field' has been dropped."
|
||||
);
|
||||
|
||||
// Make the entity type revisionable.
|
||||
$this->updateEntityTypeToRevisionable();
|
||||
$this->assertTrue($this->entityDefinitionUpdateManager->applyEntityUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_UPDATED, 'entity_test_update'), 'Calling applyEntityUpdate() correctly returns TRUE.');
|
||||
$this->assertTrue($this->database->schema()->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' table has been created.");
|
||||
$this->assertFalse($db_schema->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' does not exist before applying the update.");
|
||||
$entity_type = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update');
|
||||
$keys = $entity_type->getKeys();
|
||||
$keys['revision'] = 'revision_id';
|
||||
$entity_type->set('entity_keys', $keys);
|
||||
$this->entityDefinitionUpdateManager->updateEntityType($entity_type);
|
||||
$this->assertTrue($db_schema->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' table has been created.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a new field and index on a shared table are created.
|
||||
*
|
||||
* @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::createSharedTableSchema
|
||||
*/
|
||||
public function testCreateFieldAndIndexOnSharedTable() {
|
||||
$this->addBaseField();
|
||||
$this->addBaseFieldIndex();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
|
||||
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), "New index 'entity_test_update_field__new_base_field' has been created on the 'entity_test_update' table.");
|
||||
// Check index size in for MySQL.
|
||||
if (Database::getConnection()->driver() == 'mysql') {
|
||||
$result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update_field__new_base_field\' and column_name = \'new_base_field\'')->fetchObject();
|
||||
$this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a new entity level index is created when data exists.
|
||||
*
|
||||
* @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::onEntityTypeUpdate
|
||||
*/
|
||||
public function testCreateIndexUsingEntityStorageSchemaWithData() {
|
||||
// Save an entity.
|
||||
$name = $this->randomString();
|
||||
$storage = $this->entityManager->getStorage('entity_test_update');
|
||||
$entity = $storage->create(array('name' => $name));
|
||||
$entity->save();
|
||||
|
||||
// Create an index.
|
||||
$indexes = array(
|
||||
'entity_test_update__type_index' => array('type'),
|
||||
);
|
||||
$this->state->set('entity_test_update.additional_entity_indexes', $indexes);
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__type_index'), "New index 'entity_test_update__type_index' has been created on the 'entity_test_update' table.");
|
||||
// Check index size in for MySQL.
|
||||
if (Database::getConnection()->driver() == 'mysql') {
|
||||
$result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update__type_index\' and column_name = \'type\'')->fetchObject();
|
||||
$this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests updating a base field when it has existing data.
|
||||
*/
|
||||
public function testBaseFieldEntityKeyUpdateWithExistingData() {
|
||||
// Add the base field and run the update.
|
||||
$this->addBaseField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
|
||||
// Save an entity with the base field populated.
|
||||
$this->entityManager->getStorage('entity_test_update')->create(['new_base_field' => $this->randomString()])->save();
|
||||
|
||||
// Save an entity with the base field not populated.
|
||||
/** @var \Drupal\entity_test\Entity\EntityTestUpdate $entity */
|
||||
$entity = $this->entityManager->getStorage('entity_test_update')->create();
|
||||
$entity->save();
|
||||
|
||||
// Promote the base field to an entity key. This will trigger the addition
|
||||
// of a NOT NULL constraint.
|
||||
$this->makeBaseFieldEntityKey();
|
||||
|
||||
// Try to apply the update and verify they fail since we have a NULL value.
|
||||
$message = 'An error occurs when trying to enabling NOT NULL constraints with NULL data.';
|
||||
try {
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Check that the update is correctly applied when no NULL data is left.
|
||||
$entity->set('new_base_field', $this->randomString());
|
||||
$entity->save();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->pass('The update is correctly performed when no NULL data exists.');
|
||||
|
||||
// Check that the update actually applied a NOT NULL constraint.
|
||||
$entity->set('new_base_field', NULL);
|
||||
$message = 'The NOT NULL constraint was correctly applied.';
|
||||
try {
|
||||
$entity->save();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -677,6 +677,7 @@ class EntityFieldTest extends EntityUnitTestBase {
|
|||
$node = entity_create('node', array(
|
||||
'type' => 'page',
|
||||
'uid' => $user->id(),
|
||||
'title' => $this->randomString(),
|
||||
));
|
||||
$reference->setValue($node);
|
||||
$violations = $reference->validate();
|
||||
|
@ -699,6 +700,7 @@ class EntityFieldTest extends EntityUnitTestBase {
|
|||
$node = entity_create('node', array(
|
||||
'type' => 'article',
|
||||
'uid' => $user->id(),
|
||||
'title' => $this->randomString(),
|
||||
));
|
||||
$node->save();
|
||||
$reference->setValue($node);
|
||||
|
|
|
@ -222,7 +222,7 @@ class EntityQueryTest extends EntityUnitTestBase {
|
|||
$query = $this->factory->get('entity_test_mulrev');
|
||||
$group_blue = $query->andConditionGroup()->condition("$figures.color", array('blue'), 'IN');
|
||||
$group_red = $query->andConditionGroup()->condition("$figures.color", array('red'), 'IN');
|
||||
$query
|
||||
$this->queryResults = $query
|
||||
->condition($group_blue)
|
||||
->condition($group_red)
|
||||
->sort('id')
|
||||
|
@ -554,7 +554,7 @@ class EntityQueryTest extends EntityUnitTestBase {
|
|||
}
|
||||
}
|
||||
}
|
||||
$this->assertTrue($ok, format_string("$i is after all entities in bundle2"));
|
||||
$this->assertTrue($ok, "$i is after all entities in bundle2");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
namespace Drupal\system\Tests\Entity\EntityReferenceSelection;
|
||||
|
||||
use Drupal\comment\Tests\CommentTestTrait;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\comment\CommentInterface;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
@ -110,7 +110,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
|
|||
$node = entity_create('node', $values);
|
||||
$node->save();
|
||||
$nodes[$key] = $node;
|
||||
$node_labels[$key] = SafeMarkup::checkPlain($node->label());
|
||||
$node_labels[$key] = Html::escape($node->label());
|
||||
}
|
||||
|
||||
// Test as a non-admin.
|
||||
|
@ -241,7 +241,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
|
|||
$account = $values;
|
||||
}
|
||||
$users[$key] = $account;
|
||||
$user_labels[$key] = SafeMarkup::checkPlain($account->getUsername());
|
||||
$user_labels[$key] = Html::escape($account->getUsername());
|
||||
}
|
||||
|
||||
// Test as a non-admin.
|
||||
|
@ -416,7 +416,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
|
|||
$comment = entity_create('comment', $values);
|
||||
$comment->save();
|
||||
$comments[$key] = $comment;
|
||||
$comment_labels[$key] = SafeMarkup::checkPlain($comment->label());
|
||||
$comment_labels[$key] = Html::escape($comment->label());
|
||||
}
|
||||
|
||||
// Test as a non-admin.
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
namespace Drupal\system\Tests\Entity\EntityReferenceSelection;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\system\Tests\Entity\EntityUnitTestBase;
|
||||
|
@ -93,7 +93,7 @@ class EntityReferenceSelectionSortTest extends EntityUnitTestBase {
|
|||
$node = Node::create($values);
|
||||
$node->save();
|
||||
$nodes[$key] = $node;
|
||||
$node_labels[$key] = SafeMarkup::checkPlain($node->label());
|
||||
$node_labels[$key] = Html::escape($node->label());
|
||||
}
|
||||
|
||||
$selection_options = array(
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Entity\EntityRevisionTranslationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Entity;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTestMulRev;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests proper revision propagation of entities.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityRevisionTranslationTest extends EntityUnitTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['language'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Enable an additional language.
|
||||
ConfigurableLanguage::createFromLangcode('de')->save();
|
||||
|
||||
$this->installEntitySchema('entity_test_mulrev');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the translation object has the right revision id after new revision.
|
||||
*/
|
||||
public function testNewRevisionAfterTranslation() {
|
||||
$user = $this->createUser();
|
||||
|
||||
// Create a test entity.
|
||||
$entity = EntityTestMulRev::create([
|
||||
'name' => $this->randomString(),
|
||||
'user_id' => $user->id(),
|
||||
'language' => 'en',
|
||||
]);
|
||||
$entity->save();
|
||||
$old_rev_id = $entity->getRevisionId();
|
||||
|
||||
$translation = $entity->addTranslation('de');
|
||||
$translation->setNewRevision();
|
||||
$translation->save();
|
||||
|
||||
$this->assertTrue($translation->getRevisionId() > $old_rev_id, 'The saved translation in new revision has a newer revision id.');
|
||||
$this->assertTrue($this->reloadEntity($entity)->getRevisionId() > $old_rev_id, 'The entity from the storage has a newer revision id.');
|
||||
}
|
||||
|
||||
}
|
|
@ -106,6 +106,7 @@ class EntityTranslationFormTest extends WebTestBase {
|
|||
|
||||
// Create a body translation and check the form language.
|
||||
$langcode2 = $this->langcodes[1];
|
||||
$node->getTranslation($langcode2)->title->value = $this->randomString();
|
||||
$node->getTranslation($langcode2)->body->value = $this->randomMachineName(16);
|
||||
$node->getTranslation($langcode2)->setOwnerId($web_user->id());
|
||||
$node->save();
|
||||
|
|
|
@ -39,6 +39,7 @@ class EntityViewControllerTest extends WebTestBase {
|
|||
$this->entities[] = $entity_test;
|
||||
}
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(['view test entity']));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,12 +47,9 @@ class EntityViewControllerTest extends WebTestBase {
|
|||
*/
|
||||
function testEntityViewController() {
|
||||
$get_label_markup = function($label) {
|
||||
return '<h1><div class="field field-entity-test--name field-name-name field-type-string field-label-hidden">
|
||||
<div class="field-items">
|
||||
<div class="field-item">' . $label . '</div>
|
||||
</div>
|
||||
</div>
|
||||
</h1>';
|
||||
return '<h1>
|
||||
<div class="field field--name-name field--type-string field--label-hidden field__item">' . $label . '</div>
|
||||
</h1>';
|
||||
};
|
||||
|
||||
foreach ($this->entities as $entity) {
|
||||
|
|
|
@ -81,12 +81,11 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
|
|||
$this->verifyPageCache($entity_url, 'HIT');
|
||||
|
||||
|
||||
$bundle_entity_type = $this->entity->getEntityType()->getBundleEntityType();
|
||||
if ($bundle_entity_type !== 'bundle') {
|
||||
if ($bundle_entity_type_id = $this->entity->getEntityType()->getBundleEntityType()) {
|
||||
// Verify that after modifying the corresponding bundle entity, there is a
|
||||
// cache miss.
|
||||
$this->pass("Test modification of entity's bundle entity.", 'Debug');
|
||||
$bundle_entity = entity_load($bundle_entity_type, $this->entity->bundle());
|
||||
$bundle_entity = entity_load($bundle_entity_type_id, $this->entity->bundle());
|
||||
$bundle_entity->save();
|
||||
$this->verifyPageCache($entity_url, 'MISS');
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Entity\Update\SqlContentEntityStorageSchemaIndexFilledTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Entity\Update;
|
||||
|
||||
/**
|
||||
* Runs SqlContentEntityStorageSchemaIndexTest with a dump filled with content.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class SqlContentEntityStorageSchemaIndexFilledTest extends SqlContentEntityStorageSchemaIndexTest {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
parent::setDatabaseDumpFiles();
|
||||
$this->databaseDumpFiles[0] = __DIR__ . '/../../../../tests/fixtures/update/drupal-8.filled.standard.php.gz';
|
||||
}
|
||||
|
||||
}
|
|
@ -19,25 +19,16 @@ class SqlContentEntityStorageSchemaIndexTest extends UpdatePathTestBase {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['update_order_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
];
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity and field schema database updates and execution order.
|
||||
*/
|
||||
public function testIndex() {
|
||||
// Enable the hook implementations in the update_order_test module.
|
||||
\Drupal::state()->set('update_order_test', TRUE);
|
||||
|
||||
// The initial Drupal 8 database dump before any updates does not include
|
||||
// the entity ID in the entity field data table indices that were added in
|
||||
// https://www.drupal.org/node/2261669.
|
||||
|
@ -45,38 +36,12 @@ class SqlContentEntityStorageSchemaIndexTest extends UpdatePathTestBase {
|
|||
$this->assertFalse(db_index_exists('node_field_data', 'node__id__default_langcode__langcode'), 'Index node__id__default_langcode__langcode does not exist prior to running updates.');
|
||||
$this->assertFalse(db_index_exists('users_field_data', 'user__id__default_langcode__langcode'), 'Index users__id__default_langcode__langcode does not exist prior to running updates.');
|
||||
|
||||
// Running database updates should automatically update the entity schemata
|
||||
// to add the indices from https://www.drupal.org/node/2261669.
|
||||
// Running database updates should update the entity schemata to add the
|
||||
// indices from https://www.drupal.org/node/2261669.
|
||||
$this->runUpdates();
|
||||
$this->assertFalse(db_index_exists('node_field_data', 'node__default_langcode'), 'Index node__default_langcode properly removed.');
|
||||
$this->assertTrue(db_index_exists('node_field_data', 'node__id__default_langcode__langcode'), 'Index node__id__default_langcode__langcode properly created on the node_field_data table.');
|
||||
$this->assertTrue(db_index_exists('users_field_data', 'user__id__default_langcode__langcode'), 'Index users__id__default_langcode__langcode properly created on the user_field_data table.');
|
||||
|
||||
// Ensure that hook_update_N() implementations were in the expected order
|
||||
// relative to the entity and field updates. The expected order is:
|
||||
// 1. Initial Drupal 8.0.0-beta12 installation with no indices.
|
||||
// 2. update_order_test_update_8001() is invoked.
|
||||
// 3. update_order_test_update_8002() is invoked.
|
||||
// 4. update_order_test_update_8002() explicitly applies the updates for
|
||||
// the update_order_test_field storage. See update_order_test.module.
|
||||
// 5. update_order_test_update_8002() explicitly applies the updates for
|
||||
// the node entity type indices listed above.
|
||||
// 6. The remaining entity schema updates are applied automatically after
|
||||
// all update hook implementations have run, which applies the user
|
||||
// index update.
|
||||
$this->assertTrue(\Drupal::state()->get('update_order_test_update_8001', FALSE), 'Index node__default_langcode still existed during update_order_test_update_8001(), indicating that it ran before the entity type updates.');
|
||||
|
||||
// Node updates were run during update_order_test_update_8002().
|
||||
$this->assertFalse(\Drupal::state()->get('update_order_test_update_8002_node__default_langcode', TRUE), 'The node__default_langcode index was removed during update_order_test_update_8002().');
|
||||
$this->assertTrue(\Drupal::state()->get('update_order_test_update_8002_node__id__default_langcode__langcode', FALSE), 'The node__id__default_langcode__langcode index was created during update_order_test_update_8002().');
|
||||
|
||||
// Ensure that the base field created by update_order_test_update_8002() is
|
||||
// created when we expect.
|
||||
$this->assertFalse(\Drupal::state()->get('update_order_test_update_8002_update_order_test_before', TRUE), 'The update_order_test field was not been created on Node before update_order_test_update_8002().');
|
||||
$this->assertTrue(\Drupal::state()->get('update_order_test_update_8002_update_order_test_after', FALSE), 'The update_order_test field was created on Node by update_order_test_update_8002().');
|
||||
|
||||
// User update were not run during update_order_test_update_8002().
|
||||
$this->assertFalse(\Drupal::state()->get('update_order_test_update_8002_user__id__default_langcode__langcode', TRUE));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Entity\Update\UpdateApiEntityDefinitionUpdateTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Entity\Update;
|
||||
|
||||
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\system\Tests\Update\DbUpdatesTrait;
|
||||
|
||||
/**
|
||||
* Tests performing entity updates through the Update API.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class UpdateApiEntityDefinitionUpdateTest extends WebTestBase {
|
||||
|
||||
use DbUpdatesTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['entity_test'];
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The entity definition update manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
|
||||
*/
|
||||
protected $updatesManager;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->entityManager = $this->container->get('entity.manager');
|
||||
$this->updatesManager = $this->container->get('entity.definition_update_manager');
|
||||
|
||||
$admin = $this->drupalCreateUser([], FALSE, TRUE);
|
||||
$this->drupalLogin($admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that individual updates applied sequentially work as expected.
|
||||
*/
|
||||
public function testSingleUpdates() {
|
||||
// Create a test entity.
|
||||
$user_ids = [mt_rand(), mt_rand()];
|
||||
$entity = EntityTest::create(['name' => $this->randomString(), 'user_id' => $user_ids]);
|
||||
$entity->save();
|
||||
|
||||
// Check that only a single value is stored for 'user_id'.
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertEqual(count($entity->user_id), 1);
|
||||
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
|
||||
|
||||
// Make 'user_id' multiple by running updates.
|
||||
$this->enableUpdates('entity_test', 'entity_definition_updates', 8001);
|
||||
$this->runUpdates();
|
||||
|
||||
// Check that data was correctly migrated.
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertEqual(count($entity->user_id), 1);
|
||||
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
|
||||
|
||||
// Store multiple data and check it is correctly stored.
|
||||
$entity->user_id = $user_ids;
|
||||
$entity->save();
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertEqual(count($entity->user_id), 2);
|
||||
$this->assertEqual($entity->user_id[0]->target_id, $user_ids[0]);
|
||||
$this->assertEqual($entity->user_id[1]->target_id, $user_ids[1]);
|
||||
|
||||
// Make 'user_id' single again by running updates.
|
||||
$this->enableUpdates('entity_test', 'entity_definition_updates', 8002);
|
||||
$this->runUpdates();
|
||||
|
||||
// Check that data was correctly migrated/dropped.
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertEqual(count($entity->user_id), 1);
|
||||
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that multiple updates applied in bulk work as expected.
|
||||
*/
|
||||
public function testMultipleUpdates() {
|
||||
// Create a test entity.
|
||||
$user_ids = [mt_rand(), mt_rand()];
|
||||
$entity = EntityTest::create(['name' => $this->randomString(), 'user_id' => $user_ids]);
|
||||
$entity->save();
|
||||
|
||||
// Check that only a single value is stored for 'user_id'.
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertEqual(count($entity->user_id), 1);
|
||||
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
|
||||
|
||||
// Make 'user_id' multiple and then single again by running updates.
|
||||
$this->enableUpdates('entity_test', 'entity_definition_updates', 8002);
|
||||
$this->runUpdates();
|
||||
|
||||
// Check that data was correctly migrated back and forth.
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertEqual(count($entity->user_id), 1);
|
||||
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
|
||||
|
||||
// Check that only a single value is stored for 'user_id' again.
|
||||
$entity->user_id = $user_ids;
|
||||
$entity->save();
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertEqual(count($entity->user_id), 1);
|
||||
$this->assertEqual($entity->user_id[0]->target_id, $user_ids[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that entity updates are correctly reported in the status report page.
|
||||
*/
|
||||
function testStatusReport() {
|
||||
// Create a test entity.
|
||||
$entity = EntityTest::create(['name' => $this->randomString(), 'user_id' => mt_rand()]);
|
||||
$entity->save();
|
||||
|
||||
// Check that the status report initially displays no error.
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->assertNoRaw('Out of date');
|
||||
$this->assertNoRaw('Mismatch detected');
|
||||
|
||||
// Enable an entity update and check that we have a dedicated status report
|
||||
// item.
|
||||
$this->container->get('state')->set('entity_test.remove_name_field', TRUE);
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->assertNoRaw('Out of date');
|
||||
$this->assertRaw('Mismatch detected');
|
||||
|
||||
// Enable a db update and check that now the entity update status report
|
||||
// item is no longer displayed. We assume an update function will fix the
|
||||
// mismatch.
|
||||
$this->enableUpdates('entity_test', 'status_report', 8001);
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->assertRaw('Out of date');
|
||||
$this->assertNoRaw('Mismatch detected');
|
||||
|
||||
// Run db updates and check that entity updates were not applied.
|
||||
$this->runUpdates();
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->assertNoRaw('Out of date');
|
||||
$this->assertRaw('Mismatch detected');
|
||||
|
||||
// Check that en exception would be triggered when trying to apply them with
|
||||
// existing data.
|
||||
$message = 'Entity updates cannot run if entity data exists.';
|
||||
try {
|
||||
$this->updatesManager->applyUpdates();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (FieldStorageDefinitionUpdateForbiddenException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Check the status report is the same after trying to apply updates.
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->assertNoRaw('Out of date');
|
||||
$this->assertRaw('Mismatch detected');
|
||||
|
||||
// Delete entity data, enable a new update, run updates again and check that
|
||||
// entity updates were not applied even when no data exists.
|
||||
$entity->delete();
|
||||
$this->enableUpdates('entity_test', 'status_report', 8002);
|
||||
$this->runUpdates();
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->assertNoRaw('Out of date');
|
||||
$this->assertRaw('Mismatch detected');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the specified entity.
|
||||
*
|
||||
* @param \Drupal\entity_test\Entity\EntityTest $entity
|
||||
* An entity object.
|
||||
*
|
||||
* @return \Drupal\entity_test\Entity\EntityTest
|
||||
* The reloaded entity object.
|
||||
*/
|
||||
protected function reloadEntity(EntityTest $entity) {
|
||||
$this->entityManager->useCaches(FALSE);
|
||||
$this->entityManager->getStorage('entity_test')->resetCache([$entity->id()]);
|
||||
return EntityTest::load($entity->id());
|
||||
}
|
||||
|
||||
}
|
37
core/modules/system/src/Tests/Extension/UpdaterTest.php
Normal file
37
core/modules/system/src/Tests/Extension/UpdaterTest.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Extension\UpdaterTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Extension;
|
||||
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Drupal\Core\Updater\Updater;
|
||||
|
||||
/**
|
||||
* Tests InfoParser class and exception.
|
||||
*
|
||||
* Files for this test are stored in core/modules/system/tests/fixtures and end
|
||||
* with .info.txt instead of info.yml in order not not be considered as real
|
||||
* extensions.
|
||||
*
|
||||
* @group Extension
|
||||
*/
|
||||
class UpdaterTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Tests project and child project showing correct title.
|
||||
*
|
||||
* @see https://drupal.org/node/2409515
|
||||
*/
|
||||
public function testGetProjectTitleWithChild() {
|
||||
// Get the project title from it's directory. If it can't find the title
|
||||
// it will choose the first project title in the directory.
|
||||
$directory = \Drupal::root() . '/core/modules/system/tests/modules/module_handler_test_multiple';
|
||||
$title = Updater::getProjectTitle($directory);
|
||||
$this->assertEqual('module handler test multiple', $title);
|
||||
}
|
||||
|
||||
}
|
40
core/modules/system/src/Tests/Form/ElementsAccessTest.php
Normal file
40
core/modules/system/src/Tests/Form/ElementsAccessTest.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\system\Tests\Form\ElementsAccessTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Form;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests access control for form elements.
|
||||
*
|
||||
* @group Form
|
||||
*/
|
||||
class ElementsAccessTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('form_test');
|
||||
|
||||
/**
|
||||
* Ensures that child values are still processed when #access = FALSE.
|
||||
*/
|
||||
public function testAccessFalse() {
|
||||
$this->drupalPostForm('form_test/vertical-tabs-access', NULL, t('Submit'));
|
||||
$this->assertNoText(t('This checkbox inside a vertical tab does not have its default value.'));
|
||||
$this->assertNoText(t('This textfield inside a vertical tab does not have its default value.'));
|
||||
$this->assertNoText(t('This checkbox inside a fieldset does not have its default value.'));
|
||||
$this->assertNoText(t('This checkbox inside a container does not have its default value.'));
|
||||
$this->assertNoText(t('This checkbox inside a nested container does not have its default value.'));
|
||||
$this->assertNoText(t('This checkbox inside a vertical tab whose fieldset access is allowed does not have its default value.'));
|
||||
$this->assertText(t('The form submitted correctly.'));
|
||||
}
|
||||
|
||||
}
|
|
@ -53,16 +53,16 @@ class ElementsLabelsTest extends WebTestBase {
|
|||
|
||||
// Exercise various defaults for textboxes and modifications to ensure
|
||||
// appropriate override and correct behavior.
|
||||
$elements = $this->xpath('//label[@for="edit-form-textfield-test-title-and-required" and @class="form-required"]/following-sibling::input[@id="edit-form-textfield-test-title-and-required"]');
|
||||
$elements = $this->xpath('//label[@for="edit-form-textfield-test-title-and-required" and @class="js-form-required form-required"]/following-sibling::input[@id="edit-form-textfield-test-title-and-required"]');
|
||||
$this->assertTrue(isset($elements[0]), 'Label precedes textfield, with required marker inside label.');
|
||||
|
||||
$elements = $this->xpath('//input[@id="edit-form-textfield-test-no-title-required"]/preceding-sibling::label[@for="edit-form-textfield-test-no-title-required" and @class="form-required"]');
|
||||
$elements = $this->xpath('//input[@id="edit-form-textfield-test-no-title-required"]/preceding-sibling::label[@for="edit-form-textfield-test-no-title-required" and @class="js-form-required form-required"]');
|
||||
$this->assertTrue(isset($elements[0]), 'Label tag with required marker precedes required textfield with no title.');
|
||||
|
||||
$elements = $this->xpath('//input[@id="edit-form-textfield-test-title-invisible"]/preceding-sibling::label[@for="edit-form-textfield-test-title-invisible" and @class="visually-hidden"]');
|
||||
$this->assertTrue(isset($elements[0]), 'Label preceding field and label class is visually-hidden.');
|
||||
|
||||
$elements = $this->xpath('//input[@id="edit-form-textfield-test-title"]/preceding-sibling::span[@class="form-required"]');
|
||||
$elements = $this->xpath('//input[@id="edit-form-textfield-test-title"]/preceding-sibling::span[@class="js-form-required form-required"]');
|
||||
$this->assertFalse(isset($elements[0]), 'No required marker on non-required field.');
|
||||
|
||||
$elements = $this->xpath('//input[@id="edit-form-textfield-test-title-after"]/following-sibling::label[@for="edit-form-textfield-test-title-after" and @class="option"]');
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
namespace Drupal\system\Tests\Form;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\form_test\Form\FormTestDisabledElementsForm;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
@ -97,7 +99,7 @@ class FormTest extends WebTestBase {
|
|||
$elements['file']['empty_values'] = $empty_strings;
|
||||
|
||||
// Regular expression to find the expected marker on required elements.
|
||||
$required_marker_preg = '@<.*?class=".*?form-required.*?">@';
|
||||
$required_marker_preg = '@<.*?class=".*?js-form-required.*form-required.*?">@';
|
||||
// Go through all the elements and all the empty values for them.
|
||||
foreach ($elements as $type => $data) {
|
||||
foreach ($data['empty_values'] as $key => $empty) {
|
||||
|
@ -231,6 +233,67 @@ class FormTest extends WebTestBase {
|
|||
$this->assertRaw("The form_test_validate_required_form form was submitted successfully.", 'Validation form submitted successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that input is retained for safe elements even with an invalid token.
|
||||
*
|
||||
* Submits a test form containing several types of form elements.
|
||||
*/
|
||||
public function testInputWithInvalidToken() {
|
||||
// We need to be logged in to have CSRF tokens.
|
||||
$account = $this->createUser();
|
||||
$this->drupalLogin($account);
|
||||
// Submit again with required fields set but an invalid form token and
|
||||
// verify that all the values are retained.
|
||||
$edit = array(
|
||||
'textfield' => $this->randomString(),
|
||||
'checkboxes[bar]' => TRUE,
|
||||
'select' => 'bar',
|
||||
'radios' => 'foo',
|
||||
'form_token' => 'invalid token',
|
||||
);
|
||||
$this->drupalPostForm(Url::fromRoute('form_test.validate_required'), $edit, 'Submit');
|
||||
$this->assertFieldByXpath('//div[contains(@class, "error")]', NULL, 'Error message is displayed with invalid token even when required fields are filled.');
|
||||
$this->assertText('The form has become outdated. Copy any unsaved work in the form below');
|
||||
// Verify that input elements retained the posted values.
|
||||
$this->assertFieldByName('textfield', $edit['textfield']);
|
||||
$this->assertNoFieldChecked('edit-checkboxes-foo');
|
||||
$this->assertFieldChecked('edit-checkboxes-bar');
|
||||
$this->assertOptionSelected('edit-select', 'bar');
|
||||
$this->assertFieldChecked('edit-radios-foo');
|
||||
|
||||
// Check another form that has a textarea input.
|
||||
$edit = array(
|
||||
'textfield' => $this->randomString(),
|
||||
'textarea' => $this->randomString() . "\n",
|
||||
'form_token' => 'invalid token',
|
||||
);
|
||||
$this->drupalPostForm(Url::fromRoute('form_test.required'), $edit, 'Submit');
|
||||
$this->assertFieldByXpath('//div[contains(@class, "error")]', NULL, 'Error message is displayed with invalid token even when required fields are filled.');
|
||||
$this->assertText('The form has become outdated. Copy any unsaved work in the form below');
|
||||
$this->assertFieldByName('textfield', $edit['textfield']);
|
||||
$this->assertFieldByName('textarea', $edit['textarea']);
|
||||
|
||||
// Check another form that has a number input.
|
||||
$edit = array(
|
||||
'integer_step' => mt_rand(1, 100),
|
||||
'form_token' => 'invalid token',
|
||||
);
|
||||
$this->drupalPostForm(Url::fromRoute('form_test.number'), $edit, 'Submit');
|
||||
$this->assertFieldByXpath('//div[contains(@class, "error")]', NULL, 'Error message is displayed with invalid token even when required fields are filled.');
|
||||
$this->assertText('The form has become outdated. Copy any unsaved work in the form below');
|
||||
$this->assertFieldByName('integer_step', $edit['integer_step']);
|
||||
|
||||
// Check a form with a Url field
|
||||
$edit = array(
|
||||
'url' => $this->randomString(),
|
||||
'form_token' => 'invalid token',
|
||||
);
|
||||
$this->drupalPostForm(Url::fromRoute('form_test.url'), $edit, 'Submit');
|
||||
$this->assertFieldByXpath('//div[contains(@class, "error")]', NULL, 'Error message is displayed with invalid token even when required fields are filled.');
|
||||
$this->assertText('The form has become outdated. Copy any unsaved work in the form below');
|
||||
$this->assertFieldByName('url', $edit['url']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests validation for required textfield element without title.
|
||||
*
|
||||
|
@ -533,7 +596,7 @@ class FormTest extends WebTestBase {
|
|||
// All the elements should be marked as disabled, including the ones below
|
||||
// the disabled container.
|
||||
$actual_count = count($disabled_elements);
|
||||
$expected_count = 41;
|
||||
$expected_count = 42;
|
||||
$this->assertEqual($actual_count, $expected_count, SafeMarkup::format('Found @actual elements with disabled property (expected @expected).', array(
|
||||
'@actual' => count($disabled_elements),
|
||||
'@expected' => $expected_count,
|
||||
|
@ -616,7 +679,7 @@ class FormTest extends WebTestBase {
|
|||
$path = strtr($path, array('!type' => $type));
|
||||
// Verify that the element exists.
|
||||
$element = $this->xpath($path, array(
|
||||
':name' => SafeMarkup::checkPlain($name),
|
||||
':name' => Html::escape($name),
|
||||
':div-class' => $class,
|
||||
':value' => isset($item['#value']) ? $item['#value'] : '',
|
||||
));
|
||||
|
|
|
@ -97,7 +97,7 @@ class RebuildTest extends WebTestBase {
|
|||
// field items in the field for which we just added an item.
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->drupalPostAjaxForm(NULL, array(), array('field_ajax_test_add_more' => t('Add another item')), NULL, array(), array(), 'node-page-form');
|
||||
$this->assert(count($this->xpath('//div[contains(@class, "field-name-field-ajax-test")]//input[@type="text"]')) == 2, 'AJAX submission succeeded.');
|
||||
$this->assert(count($this->xpath('//div[contains(@class, "field--name-field-ajax-test")]//input[@type="text"]')) == 2, 'AJAX submission succeeded.');
|
||||
|
||||
// Submit the form with the non-Ajax "Save" button, leaving the title field
|
||||
// blank to trigger a validation error, and ensure that a validation error
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
namespace Drupal\system\Tests\Form;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
@ -292,11 +291,12 @@ class ValidationTest extends WebTestBase {
|
|||
// Gather the element for checking the jump link section.
|
||||
$error_links[] = \Drupal::l($message['title'], Url::fromRoute('<none>', [], ['fragment' => 'edit-' . str_replace('_', '-', $message['key']), 'external' => TRUE]));
|
||||
}
|
||||
$top_message = \Drupal::translation()->formatPlural(count($error_links), '1 error has been found: !errors', '@count errors have been found: !errors', [
|
||||
'!errors' => SafeMarkup::set(implode(', ', $error_links))
|
||||
]);
|
||||
$top_message = \Drupal::translation()->formatPlural(count($error_links), '1 error has been found:', '@count errors have been found:');
|
||||
$this->assertRaw($top_message);
|
||||
$this->assertNoText(t('An illegal choice has been detected. Please contact the site administrator.'));
|
||||
foreach ($error_links as $error_link) {
|
||||
$this->assertRaw($error_link);
|
||||
}
|
||||
$this->assertNoText('An illegal choice has been detected. Please contact the site administrator.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\HttpKernel\HeadersResponseCodeRenderTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\HttpKernel;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests rendering headers and response codes.
|
||||
*
|
||||
* @group Routing
|
||||
*/
|
||||
class HeadersResponseCodeRenderTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('httpkernel_test');
|
||||
|
||||
/**
|
||||
* Tests the rendering of an array-based header and response code.
|
||||
*/
|
||||
public function testHeaderResponseCode() {
|
||||
$this->drupalGet('/httpkernel-test/teapot');
|
||||
$this->assertResponse(418);
|
||||
$this->assertHeader('X-Test-Teapot', 'Teapot Mode Active');
|
||||
$this->assertHeader('X-Test-Teapot-Replace', 'Teapot replaced');
|
||||
$this->assertHeader('X-Test-Teapot-No-Replace', 'This value is not replaced,This one is added');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Installer\InstallerDatabaseErrorMessagesTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Installer;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\simpletest\InstallerTestBase;
|
||||
|
||||
/**
|
||||
* Tests the installer with database errors.
|
||||
*
|
||||
* @group Installer
|
||||
*/
|
||||
class InstallerDatabaseErrorMessagesTest extends InstallerTestBase {
|
||||
|
||||
/**
|
||||
* @{inheritdoc}
|
||||
*/
|
||||
protected function setUpSettings() {
|
||||
// We are creating a table here to force an error in the installer because
|
||||
// it will try and create the drupal_install_test table as this is part of
|
||||
// the standard database tests performed by the installer in
|
||||
// Drupal\Core\Database\Install\Tasks.
|
||||
Database::getConnection('default')->query('CREATE TABLE {drupal_install_test} (id int NULL)');
|
||||
parent::setUpSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* @{inheritdoc}
|
||||
*/
|
||||
protected function setUpSite() {
|
||||
// This step should not appear as we had a failure on the settings screen.
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the error message in the settings step is correct.
|
||||
*/
|
||||
public function testSetUpSettingsErrorMessage() {
|
||||
$this->assertRaw('<ul><li>Failed to <strong>CREATE</strong> a test table');
|
||||
}
|
||||
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\system\Tests\Installer;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\simpletest\InstallerTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
|
@ -45,6 +46,26 @@ class InstallerTranslationTest extends InstallerTestBase {
|
|||
$this->assertEqual($direction, 'ltr');
|
||||
}
|
||||
|
||||
/**
|
||||
* @{inheritdoc}
|
||||
*/
|
||||
protected function setUpSettings() {
|
||||
// We are creating a table here to force an error in the installer because
|
||||
// it will try and create the drupal_install_test table as this is part of
|
||||
// the standard database tests performed by the installer in
|
||||
// Drupal\Core\Database\Install\Tasks.
|
||||
Database::getConnection('default')->query('CREATE TABLE {drupal_install_test} (id int NULL)');
|
||||
parent::setUpSettings();
|
||||
|
||||
// Ensure that the error message translation is working.
|
||||
$this->assertRaw('Beheben Sie alle Probleme unten, um die Installation fortzusetzen. Informationen zur Konfiguration der Datenbankserver finden Sie in der <a href="https://www.drupal.org/getting-started/install">Installationshandbuch</a>, oder kontaktieren Sie Ihren Hosting-Anbieter.');
|
||||
$this->assertRaw('<strong>CREATE</strong> ein Test-Tabelle auf Ihrem Datenbankserver mit dem Befehl <em class="placeholder">CREATE TABLE {drupal_install_test} (id int NULL)</em> fehlgeschlagen.');
|
||||
|
||||
// Now do it successfully.
|
||||
Database::getConnection('default')->query('DROP TABLE {drupal_install_test}');
|
||||
parent::setUpSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the expected behaviors of the installation result.
|
||||
*/
|
||||
|
@ -127,6 +148,12 @@ msgstr "Save and continue $langcode"
|
|||
|
||||
msgid "Anonymous"
|
||||
msgstr "Anonymous $langcode"
|
||||
|
||||
msgid "Resolve all issues below to continue the installation. For help configuring your database server, see the <a href="https://www.drupal.org/getting-started/install">installation handbook</a>, or contact your hosting provider."
|
||||
msgstr "Beheben Sie alle Probleme unten, um die Installation fortzusetzen. Informationen zur Konfiguration der Datenbankserver finden Sie in der <a href="https://www.drupal.org/getting-started/install">Installationshandbuch</a>, oder kontaktieren Sie Ihren Hosting-Anbieter."
|
||||
|
||||
msgid "Failed to <strong>CREATE</strong> a test table on your database server with the command %query. The server reports the following message: %error.<p>Are you sure the configured username has the necessary permissions to create tables in the database?</p>"
|
||||
msgstr "<strong>CREATE</strong> ein Test-Tabelle auf Ihrem Datenbankserver mit dem Befehl %query fehlgeschlagen."
|
||||
ENDPO;
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue