Move into nested docroot
This commit is contained in:
parent
83a0d3a149
commit
c8b70abde9
13405 changed files with 0 additions and 0 deletions
|
@ -0,0 +1,409 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- system.menu.main
|
||||
module:
|
||||
- content_moderation
|
||||
- user
|
||||
id: latest
|
||||
label: Latest
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: node_field_revision
|
||||
base_field: vid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'view all revisions'
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: full
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: '‹ Previous'
|
||||
next: 'Next ›'
|
||||
first: '« First'
|
||||
last: 'Last »'
|
||||
quantity: 9
|
||||
style:
|
||||
type: table
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
nid:
|
||||
id: nid
|
||||
table: node_field_revision
|
||||
field: nid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: 'Node ID'
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: number_integer
|
||||
settings:
|
||||
thousand_separator: ''
|
||||
prefix_suffix: true
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: node
|
||||
entity_field: nid
|
||||
plugin_id: field
|
||||
vid:
|
||||
id: vid
|
||||
table: node_field_revision
|
||||
field: vid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: 'Revision ID'
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: number_integer
|
||||
settings:
|
||||
thousand_separator: ''
|
||||
prefix_suffix: true
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: node
|
||||
entity_field: vid
|
||||
plugin_id: field
|
||||
title:
|
||||
id: title
|
||||
table: node_field_revision
|
||||
field: title
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
alter:
|
||||
alter_text: false
|
||||
make_link: false
|
||||
absolute: false
|
||||
trim: false
|
||||
word_boundary: false
|
||||
ellipsis: false
|
||||
strip_tags: false
|
||||
html: false
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
settings:
|
||||
link_to_entity: false
|
||||
plugin_id: field
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Title
|
||||
exclude: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: string
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
moderation_state:
|
||||
id: moderation_state
|
||||
table: content_moderation_state_field_revision
|
||||
field: moderation_state
|
||||
relationship: moderation_state
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: 'Moderation state'
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_label
|
||||
settings:
|
||||
link: true
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: content_moderation_state
|
||||
entity_field: moderation_state
|
||||
plugin_id: field
|
||||
filters:
|
||||
latest_revision:
|
||||
id: latest_revision
|
||||
table: node_revision
|
||||
field: latest_revision
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: '='
|
||||
value: ''
|
||||
group: 1
|
||||
exposed: false
|
||||
expose:
|
||||
operator_id: ''
|
||||
label: ''
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: ''
|
||||
identifier: ''
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
entity_type: node
|
||||
plugin_id: latest_revision
|
||||
sorts: { }
|
||||
title: Latest
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships:
|
||||
moderation_state:
|
||||
id: moderation_state
|
||||
table: node_field_revision
|
||||
field: moderation_state
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: 'Content moderation state'
|
||||
required: false
|
||||
entity_type: node
|
||||
plugin_id: standard
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: 0
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
tags: { }
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: Page
|
||||
position: 1
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
path: latest
|
||||
menu:
|
||||
type: normal
|
||||
title: Drafts
|
||||
description: ''
|
||||
expanded: false
|
||||
parent: ''
|
||||
weight: 0
|
||||
context: '0'
|
||||
menu_name: main
|
||||
cache_metadata:
|
||||
max-age: 0
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
tags: { }
|
|
@ -0,0 +1,406 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- content_moderation
|
||||
- node
|
||||
- user
|
||||
id: test_content_moderation_base_table_test
|
||||
label: test_content_moderation_base_table_test
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access content'
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: mini
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: ‹‹
|
||||
next: ››
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
default_field_elements: true
|
||||
fields:
|
||||
nid:
|
||||
id: nid
|
||||
table: node_field_data
|
||||
field: nid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: number_integer
|
||||
settings:
|
||||
thousand_separator: ''
|
||||
prefix_suffix: true
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: node
|
||||
entity_field: nid
|
||||
plugin_id: field
|
||||
moderation_state:
|
||||
id: moderation_state
|
||||
table: content_moderation_state_field_data
|
||||
field: moderation_state
|
||||
relationship: moderation_state
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_label
|
||||
settings:
|
||||
link: false
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: content_moderation_state
|
||||
entity_field: moderation_state
|
||||
plugin_id: field
|
||||
moderation_state_1:
|
||||
id: moderation_state_1
|
||||
table: content_moderation_state_field_revision
|
||||
field: moderation_state
|
||||
relationship: moderation_state
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_label
|
||||
settings:
|
||||
link: false
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: content_moderation_state
|
||||
entity_field: moderation_state
|
||||
plugin_id: field
|
||||
moderation_state_2:
|
||||
id: moderation_state_2
|
||||
table: content_moderation_state_field_revision
|
||||
field: moderation_state
|
||||
relationship: moderation_state_1
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_entity_id
|
||||
settings: { }
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: content_moderation_state
|
||||
entity_field: moderation_state
|
||||
plugin_id: field
|
||||
filters: { }
|
||||
sorts:
|
||||
created:
|
||||
id: created
|
||||
table: node_field_data
|
||||
field: created
|
||||
order: DESC
|
||||
entity_type: node
|
||||
entity_field: created
|
||||
plugin_id: date
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
granularity: second
|
||||
vid:
|
||||
id: vid
|
||||
table: node_field_data
|
||||
field: vid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: ASC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
entity_type: node
|
||||
entity_field: vid
|
||||
plugin_id: standard
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships:
|
||||
moderation_state:
|
||||
id: moderation_state
|
||||
table: node_field_data
|
||||
field: moderation_state
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: 'Content moderation state'
|
||||
required: false
|
||||
entity_type: node
|
||||
plugin_id: standard
|
||||
moderation_state_1:
|
||||
id: moderation_state_1
|
||||
table: node_field_revision
|
||||
field: moderation_state
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: 'Content moderation state (revision)'
|
||||
required: false
|
||||
entity_type: node
|
||||
plugin_id: standard
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
tags: { }
|
|
@ -0,0 +1,447 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- user
|
||||
id: test_content_moderation_latest_revision
|
||||
label: test_content_moderation_latest_revision
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access content'
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: mini
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: ‹‹
|
||||
next: ››
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
default_field_elements: true
|
||||
fields:
|
||||
nid:
|
||||
id: nid
|
||||
table: node_field_data
|
||||
field: nid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: number_integer
|
||||
settings:
|
||||
thousand_separator: ''
|
||||
prefix_suffix: true
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: node
|
||||
entity_field: nid
|
||||
plugin_id: field
|
||||
revision_id:
|
||||
id: revision_id
|
||||
table: content_revision_tracker
|
||||
field: revision_id
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
plugin_id: standard
|
||||
title:
|
||||
id: title
|
||||
table: node_field_revision
|
||||
field: title
|
||||
relationship: latest_revision__node
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: string
|
||||
settings:
|
||||
link_to_entity: false
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
plugin_id: field
|
||||
moderation_state:
|
||||
id: moderation_state
|
||||
table: content_moderation_state_field_revision
|
||||
field: moderation_state
|
||||
relationship: moderation_state
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_entity_id
|
||||
settings: { }
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: content_moderation_state
|
||||
entity_field: moderation_state
|
||||
plugin_id: field
|
||||
moderation_state_1:
|
||||
id: moderation_state_1
|
||||
table: content_moderation_state_field_revision
|
||||
field: moderation_state
|
||||
relationship: moderation_state_1
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_entity_id
|
||||
settings: { }
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: content_moderation_state
|
||||
entity_field: moderation_state
|
||||
plugin_id: field
|
||||
filters: { }
|
||||
sorts:
|
||||
nid:
|
||||
id: nid
|
||||
table: node_field_data
|
||||
field: nid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: ASC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
entity_type: node
|
||||
entity_field: nid
|
||||
plugin_id: standard
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships:
|
||||
latest_revision__node:
|
||||
id: latest_revision__node
|
||||
table: content_revision_tracker
|
||||
field: latest_revision__node
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: 'Content latest revision'
|
||||
required: false
|
||||
plugin_id: standard
|
||||
moderation_state_1:
|
||||
id: moderation_state_1
|
||||
table: node_field_revision
|
||||
field: moderation_state
|
||||
relationship: latest_revision__node
|
||||
group_type: group
|
||||
admin_label: 'Content moderation state (latest revision)'
|
||||
required: false
|
||||
entity_type: node
|
||||
plugin_id: standard
|
||||
moderation_state:
|
||||
id: moderation_state
|
||||
table: node_field_revision
|
||||
field: moderation_state
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: 'Content moderation state'
|
||||
required: false
|
||||
entity_type: node
|
||||
plugin_id: standard
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
rendering_language: '***LANGUAGE_entity_default***'
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
tags: { }
|
|
@ -0,0 +1,315 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_content_moderation_revision_test
|
||||
label: test_content_moderation_revision_test
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: node_field_revision
|
||||
base_field: vid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'view all revisions'
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: mini
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: ‹‹
|
||||
next: ››
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
default_field_elements: true
|
||||
fields:
|
||||
vid:
|
||||
id: vid
|
||||
table: node_field_revision
|
||||
field: vid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: number_integer
|
||||
settings:
|
||||
thousand_separator: ''
|
||||
prefix_suffix: true
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: node
|
||||
entity_field: vid
|
||||
plugin_id: field
|
||||
moderation_state:
|
||||
id: moderation_state
|
||||
table: content_moderation_state_field_revision
|
||||
field: moderation_state
|
||||
relationship: moderation_state
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_entity_id
|
||||
settings: { }
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: content_moderation_state
|
||||
entity_field: moderation_state
|
||||
plugin_id: field
|
||||
revision_id:
|
||||
id: revision_id
|
||||
table: content_moderation_state_field_revision
|
||||
field: revision_id
|
||||
relationship: moderation_state
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: number_integer
|
||||
settings:
|
||||
thousand_separator: ''
|
||||
prefix_suffix: true
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: content_moderation_state
|
||||
entity_field: revision_id
|
||||
plugin_id: field
|
||||
filters: { }
|
||||
sorts:
|
||||
vid:
|
||||
id: vid
|
||||
table: node_field_revision
|
||||
field: vid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: ASC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
entity_type: node
|
||||
entity_field: vid
|
||||
plugin_id: standard
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships:
|
||||
moderation_state:
|
||||
id: moderation_state
|
||||
table: node_field_revision
|
||||
field: moderation_state
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: 'Content moderation state'
|
||||
required: false
|
||||
entity_type: node
|
||||
plugin_id: standard
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
tags: { }
|
|
@ -0,0 +1,10 @@
|
|||
name: 'Content moderation test views'
|
||||
type: module
|
||||
description: 'Provides default views for views Content moderation tests.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- content_moderation
|
||||
- node
|
||||
- views
|
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the "Latest Revision" views filter.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class LatestRevisionViewsFilterTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation_test_views',
|
||||
'content_moderation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests view shows the correct node IDs.
|
||||
*/
|
||||
public function testViewShowsCorrectNids() {
|
||||
$node_type = $this->createNodeType('Test', 'test');
|
||||
|
||||
$permissions = [
|
||||
'access content',
|
||||
'view all revisions',
|
||||
];
|
||||
$editor1 = $this->drupalCreateUser($permissions);
|
||||
|
||||
$this->drupalLogin($editor1);
|
||||
|
||||
// Make a pre-moderation node.
|
||||
/** @var Node $node_0 */
|
||||
$node_0 = Node::create([
|
||||
'type' => 'test',
|
||||
'title' => 'Node 0 - Rev 1',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_0->save();
|
||||
|
||||
// Now enable moderation for subsequent nodes.
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->save();
|
||||
|
||||
// Make a node that is only ever in Draft.
|
||||
/** @var Node $node_1 */
|
||||
$node_1 = Node::create([
|
||||
'type' => 'test',
|
||||
'title' => 'Node 1 - Rev 1',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_1->moderation_state->target_id = 'draft';
|
||||
$node_1->save();
|
||||
|
||||
// Make a node that is in Draft, then Published.
|
||||
/** @var Node $node_2 */
|
||||
$node_2 = Node::create([
|
||||
'type' => 'test',
|
||||
'title' => 'Node 2 - Rev 1',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_2->moderation_state->target_id = 'draft';
|
||||
$node_2->save();
|
||||
|
||||
$node_2->setTitle('Node 2 - Rev 2');
|
||||
$node_2->moderation_state->target_id = 'published';
|
||||
$node_2->save();
|
||||
|
||||
// Make a node that is in Draft, then Published, then Draft.
|
||||
/** @var Node $node_3 */
|
||||
$node_3 = Node::create([
|
||||
'type' => 'test',
|
||||
'title' => 'Node 3 - Rev 1',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_3->moderation_state->target_id = 'draft';
|
||||
$node_3->save();
|
||||
|
||||
$node_3->setTitle('Node 3 - Rev 2');
|
||||
$node_3->moderation_state->target_id = 'published';
|
||||
$node_3->save();
|
||||
|
||||
$node_3->setTitle('Node 3 - Rev 3');
|
||||
$node_3->moderation_state->target_id = 'draft';
|
||||
$node_3->save();
|
||||
|
||||
// Now show the View, and confirm that only the correct titles are showing.
|
||||
$this->drupalGet('/latest');
|
||||
$page = $this->getSession()->getPage();
|
||||
$this->assertEquals(200, $this->getSession()->getStatusCode());
|
||||
$this->assertTrue($page->hasContent('Node 1 - Rev 1'));
|
||||
$this->assertTrue($page->hasContent('Node 2 - Rev 2'));
|
||||
$this->assertTrue($page->hasContent('Node 3 - Rev 3'));
|
||||
$this->assertFalse($page->hasContent('Node 2 - Rev 1'));
|
||||
$this->assertFalse($page->hasContent('Node 3 - Rev 1'));
|
||||
$this->assertFalse($page->hasContent('Node 3 - Rev 2'));
|
||||
$this->assertFalse($page->hasContent('Node 0 - Rev 1'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new node type.
|
||||
*
|
||||
* @param string $label
|
||||
* The human-readable label of the type to create.
|
||||
* @param string $machine_name
|
||||
* The machine name of the type to create.
|
||||
*
|
||||
* @return NodeType
|
||||
* The node type just created.
|
||||
*/
|
||||
protected function createNodeType($label, $machine_name) {
|
||||
/** @var NodeType $node_type */
|
||||
$node_type = NodeType::create([
|
||||
'type' => $machine_name,
|
||||
'label' => $label,
|
||||
]);
|
||||
$node_type->save();
|
||||
|
||||
return $node_type;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the view access control handler for moderation state entities.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ModerationStateAccessTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation_test_views',
|
||||
'content_moderation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Test the view operation access handler with the view permission.
|
||||
*/
|
||||
public function testViewShowsCorrectStates() {
|
||||
$node_type_id = 'test';
|
||||
$this->createNodeType('Test', $node_type_id);
|
||||
|
||||
$permissions = [
|
||||
'access content',
|
||||
'view all revisions',
|
||||
'view moderation states',
|
||||
];
|
||||
$editor1 = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($editor1);
|
||||
|
||||
$node_1 = Node::create([
|
||||
'type' => $node_type_id,
|
||||
'title' => 'Draft node',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_1->moderation_state->target_id = 'draft';
|
||||
$node_1->save();
|
||||
|
||||
$node_2 = Node::create([
|
||||
'type' => $node_type_id,
|
||||
'title' => 'Published node',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_2->moderation_state->target_id = 'published';
|
||||
$node_2->save();
|
||||
|
||||
// Resave the node with a new state.
|
||||
$node_2->setTitle('Archived node');
|
||||
$node_2->moderation_state->target_id = 'archived';
|
||||
$node_2->save();
|
||||
|
||||
// Now show the View, and confirm that the state labels are showing.
|
||||
$this->drupalGet('/latest');
|
||||
$page = $this->getSession()->getPage();
|
||||
$this->assertTrue($page->hasLink('Draft'));
|
||||
$this->assertTrue($page->hasLink('Archived'));
|
||||
$this->assertFalse($page->hasLink('Published'));
|
||||
|
||||
// Now log in as an admin and test the same thing.
|
||||
$permissions = [
|
||||
'access content',
|
||||
'view all revisions',
|
||||
'administer moderation states',
|
||||
];
|
||||
$admin1 = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($admin1);
|
||||
|
||||
$this->drupalGet('/latest');
|
||||
$page = $this->getSession()->getPage();
|
||||
$this->assertEquals(200, $this->getSession()->getStatusCode());
|
||||
$this->assertTrue($page->hasLink('Draft'));
|
||||
$this->assertTrue($page->hasLink('Archived'));
|
||||
$this->assertFalse($page->hasLink('Published'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new node type.
|
||||
*
|
||||
* @param string $label
|
||||
* The human-readable label of the type to create.
|
||||
* @param string $machine_name
|
||||
* The machine name of the type to create.
|
||||
*
|
||||
* @return NodeType
|
||||
* The node type just created.
|
||||
*/
|
||||
protected function createNodeType($label, $machine_name) {
|
||||
/** @var NodeType $node_type */
|
||||
$node_type = NodeType::create([
|
||||
'type' => $machine_name,
|
||||
'label' => $label,
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->save();
|
||||
|
||||
return $node_type;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\block_content\Entity\BlockContentType;
|
||||
use Drupal\config\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\content_moderation\Entity\ModerationState;
|
||||
use Drupal\content_moderation\Entity\ModerationStateTransition;
|
||||
|
||||
/**
|
||||
* Ensures that content moderation schema is correct.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ContentModerationSchemaTest extends KernelTestBase {
|
||||
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation',
|
||||
'node',
|
||||
'user',
|
||||
'block_content',
|
||||
'system',
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests content moderation default schema.
|
||||
*/
|
||||
public function testContentModerationDefaultConfig() {
|
||||
$this->installConfig(['content_moderation']);
|
||||
$typed_config = \Drupal::service('config.typed');
|
||||
$moderation_states = ModerationState::loadMultiple();
|
||||
foreach ($moderation_states as $moderation_state) {
|
||||
$this->assertConfigSchema($typed_config, $moderation_state->getEntityType()->getConfigPrefix() . '.' . $moderation_state->id(), $moderation_state->toArray());
|
||||
}
|
||||
$moderation_state_transitions = ModerationStateTransition::loadMultiple();
|
||||
foreach ($moderation_state_transitions as $moderation_state_transition) {
|
||||
$this->assertConfigSchema($typed_config, $moderation_state_transition->getEntityType()->getConfigPrefix() . '.' . $moderation_state_transition->id(), $moderation_state_transition->toArray());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests content moderation third party schema for node types.
|
||||
*/
|
||||
public function testContentModerationNodeTypeConfig() {
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installConfig(['content_moderation']);
|
||||
$typed_config = \Drupal::service('config.typed');
|
||||
$moderation_states = ModerationState::loadMultiple();
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', array_keys($moderation_states));
|
||||
$node_type->setThirdPartySetting('content_moderation', 'default_moderation_state', '');
|
||||
$node_type->save();
|
||||
$this->assertConfigSchema($typed_config, $node_type->getEntityType()->getConfigPrefix() . '.' . $node_type->id(), $node_type->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests content moderation third party schema for block content types.
|
||||
*/
|
||||
public function testContentModerationBlockContentTypeConfig() {
|
||||
$this->installEntitySchema('block_content');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installConfig(['content_moderation']);
|
||||
$typed_config = \Drupal::service('config.typed');
|
||||
$moderation_states = ModerationState::loadMultiple();
|
||||
$block_content_type = BlockContentType::create([
|
||||
'id' => 'basic',
|
||||
'label' => 'basic',
|
||||
'revision' => TRUE,
|
||||
]);
|
||||
$block_content_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$block_content_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', array_keys($moderation_states));
|
||||
$block_content_type->setThirdPartySetting('content_moderation', 'default_moderation_state', '');
|
||||
$block_content_type->save();
|
||||
$this->assertConfigSchema($typed_config, $block_content_type->getEntityType()->getConfigPrefix() . '.' . $block_content_type->id(), $block_content_type->toArray());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,324 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\content_moderation\Entity\ContentModerationState;
|
||||
use Drupal\content_moderation\Entity\ModerationState;
|
||||
use Drupal\entity_test\Entity\EntityTestBundle;
|
||||
use Drupal\entity_test\Entity\EntityTestWithBundle;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
/**
|
||||
* Tests links between a content entity and a content_moderation_state entity.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ContentModerationStateTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'entity_test',
|
||||
'node',
|
||||
'content_moderation',
|
||||
'user',
|
||||
'system',
|
||||
'language',
|
||||
'content_translation',
|
||||
'text',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('node', 'node_access');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('entity_test_with_bundle');
|
||||
$this->installEntitySchema('content_moderation_state');
|
||||
$this->installConfig('content_moderation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic monolingual content moderation through the API.
|
||||
*/
|
||||
public function testBasicModeration() {
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', ['draft', 'published']);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
|
||||
$node_type->save();
|
||||
$node = Node::create([
|
||||
'type' => 'example',
|
||||
'title' => 'Test title',
|
||||
]);
|
||||
$node->save();
|
||||
$node = $this->reloadNode($node);
|
||||
$this->assertEquals('draft', $node->moderation_state->entity->id());
|
||||
|
||||
$published = ModerationState::load('published');
|
||||
$node->moderation_state->entity = $published;
|
||||
$node->save();
|
||||
|
||||
$node = $this->reloadNode($node);
|
||||
$this->assertEquals('published', $node->moderation_state->entity->id());
|
||||
|
||||
// Change the state without saving the node.
|
||||
$content_moderation_state = ContentModerationState::load(1);
|
||||
$content_moderation_state->set('moderation_state', 'draft');
|
||||
$content_moderation_state->setNewRevision(TRUE);
|
||||
$content_moderation_state->save();
|
||||
|
||||
$node = $this->reloadNode($node, 3);
|
||||
$this->assertEquals('draft', $node->moderation_state->entity->id());
|
||||
$this->assertFalse($node->isPublished());
|
||||
|
||||
// Get the default revision.
|
||||
$node = $this->reloadNode($node);
|
||||
$this->assertTrue($node->isPublished());
|
||||
$this->assertEquals(2, $node->getRevisionId());
|
||||
|
||||
$node->moderation_state->target_id = 'published';
|
||||
$node->save();
|
||||
|
||||
$node = $this->reloadNode($node, 4);
|
||||
$this->assertEquals('published', $node->moderation_state->entity->id());
|
||||
|
||||
// Get the default revision.
|
||||
$node = $this->reloadNode($node);
|
||||
$this->assertTrue($node->isPublished());
|
||||
$this->assertEquals(4, $node->getRevisionId());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic multilingual content moderation through the API.
|
||||
*/
|
||||
public function testMultilingualModeration() {
|
||||
// Enable French.
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', ['draft', 'published']);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
|
||||
$node_type->save();
|
||||
$english_node = Node::create([
|
||||
'type' => 'example',
|
||||
'title' => 'Test title',
|
||||
]);
|
||||
// Revision 1 (en).
|
||||
$english_node
|
||||
->setPublished(FALSE)
|
||||
->save();
|
||||
$this->assertEquals('draft', $english_node->moderation_state->entity->id());
|
||||
$this->assertFalse($english_node->isPublished());
|
||||
|
||||
// Create a French translation.
|
||||
$french_node = $english_node->addTranslation('fr', ['title' => 'French title']);
|
||||
$french_node->setPublished(FALSE);
|
||||
// Revision 1 (fr).
|
||||
$french_node->save();
|
||||
$french_node = $this->reloadNode($english_node)->getTranslation('fr');
|
||||
$this->assertEquals('draft', $french_node->moderation_state->entity->id());
|
||||
$this->assertFalse($french_node->isPublished());
|
||||
|
||||
// Move English node to create another draft.
|
||||
$english_node = $this->reloadNode($english_node);
|
||||
$english_node->moderation_state->target_id = 'draft';
|
||||
// Revision 2 (en, fr).
|
||||
$english_node->save();
|
||||
$english_node = $this->reloadNode($english_node);
|
||||
$this->assertEquals('draft', $english_node->moderation_state->entity->id());
|
||||
|
||||
// French node should still be in draft.
|
||||
$french_node = $this->reloadNode($english_node)->getTranslation('fr');
|
||||
$this->assertEquals('draft', $french_node->moderation_state->entity->id());
|
||||
|
||||
// Publish the French node.
|
||||
$french_node->moderation_state->target_id = 'published';
|
||||
// Revision 3 (en, fr).
|
||||
$french_node->save();
|
||||
$french_node = $this->reloadNode($french_node)->getTranslation('fr');
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
$this->assertEquals('published', $french_node->moderation_state->entity->id());
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
$english_node = $french_node->getTranslation('en');
|
||||
$this->assertEquals('draft', $english_node->moderation_state->entity->id());
|
||||
|
||||
// Publish the English node.
|
||||
$english_node->moderation_state->target_id = 'published';
|
||||
// Revision 4 (en, fr).
|
||||
$english_node->save();
|
||||
$english_node = $this->reloadNode($english_node);
|
||||
$this->assertTrue($english_node->isPublished());
|
||||
|
||||
// Move the French node back to draft.
|
||||
$french_node = $this->reloadNode($english_node)->getTranslation('fr');
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
$french_node->moderation_state->target_id = 'draft';
|
||||
// Revision 5 (en, fr).
|
||||
$french_node->save();
|
||||
$french_node = $this->reloadNode($english_node, 5)->getTranslation('fr');
|
||||
$this->assertFalse($french_node->isPublished());
|
||||
$this->assertTrue($french_node->getTranslation('en')->isPublished());
|
||||
|
||||
// Republish the French node.
|
||||
$french_node->moderation_state->target_id = 'published';
|
||||
// Revision 6 (en, fr).
|
||||
$french_node->save();
|
||||
$french_node = $this->reloadNode($english_node)->getTranslation('fr');
|
||||
$this->assertTrue($french_node->isPublished());
|
||||
|
||||
// Change the EN state without saving the node.
|
||||
$content_moderation_state = ContentModerationState::load(1);
|
||||
$content_moderation_state->set('moderation_state', 'draft');
|
||||
$content_moderation_state->setNewRevision(TRUE);
|
||||
// Revision 7 (en, fr).
|
||||
$content_moderation_state->save();
|
||||
$english_node = $this->reloadNode($french_node, $french_node->getRevisionId() + 1);
|
||||
|
||||
$this->assertEquals('draft', $english_node->moderation_state->entity->id());
|
||||
$french_node = $this->reloadNode($english_node)->getTranslation('fr');
|
||||
$this->assertEquals('published', $french_node->moderation_state->entity->id());
|
||||
|
||||
// This should unpublish the French node.
|
||||
$content_moderation_state = ContentModerationState::load(1);
|
||||
$content_moderation_state = $content_moderation_state->getTranslation('fr');
|
||||
$content_moderation_state->set('moderation_state', 'draft');
|
||||
$content_moderation_state->setNewRevision(TRUE);
|
||||
// Revision 8 (en, fr).
|
||||
$content_moderation_state->save();
|
||||
|
||||
$english_node = $this->reloadNode($english_node, $english_node->getRevisionId());
|
||||
$this->assertEquals('draft', $english_node->moderation_state->entity->id());
|
||||
$french_node = $this->reloadNode($english_node, '8')->getTranslation('fr');
|
||||
$this->assertEquals('draft', $french_node->moderation_state->entity->id());
|
||||
// Switching the moderation state to an unpublished state should update the
|
||||
// entity.
|
||||
$this->assertFalse($french_node->isPublished());
|
||||
|
||||
// Get the default english node.
|
||||
$english_node = $this->reloadNode($english_node);
|
||||
$this->assertTrue($english_node->isPublished());
|
||||
$this->assertEquals(6, $english_node->getRevisionId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a non-translatable entity type with a langcode can be moderated.
|
||||
*/
|
||||
public function testNonTranslatableEntityTypeModeration() {
|
||||
// Make the 'entity_test_with_bundle' entity type revisionable.
|
||||
$entity_type = clone \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle');
|
||||
$keys = $entity_type->getKeys();
|
||||
$keys['revision'] = 'revision_id';
|
||||
$entity_type->set('entity_keys', $keys);
|
||||
\Drupal::state()->set('entity_test_with_bundle.entity_type', $entity_type);
|
||||
\Drupal::entityDefinitionUpdateManager()->applyUpdates();
|
||||
|
||||
// Create a test bundle.
|
||||
$entity_test_bundle = EntityTestBundle::create([
|
||||
'id' => 'example',
|
||||
]);
|
||||
$entity_test_bundle->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$entity_test_bundle->setThirdPartySetting('content_moderation', 'allowed_moderation_states', [
|
||||
'draft',
|
||||
'published'
|
||||
]);
|
||||
$entity_test_bundle->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
|
||||
$entity_test_bundle->save();
|
||||
|
||||
// Check that the tested entity type is not translatable.
|
||||
$entity_type = \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle');
|
||||
$this->assertFalse($entity_type->isTranslatable(), 'The test entity type is not translatable.');
|
||||
|
||||
// Create a test entity.
|
||||
$entity_test_with_bundle = EntityTestWithBundle::create([
|
||||
'type' => 'example'
|
||||
]);
|
||||
$entity_test_with_bundle->save();
|
||||
$this->assertEquals('draft', $entity_test_with_bundle->moderation_state->entity->id());
|
||||
|
||||
$entity_test_with_bundle->moderation_state->target_id = 'published';
|
||||
$entity_test_with_bundle->save();
|
||||
|
||||
$this->assertEquals('published', EntityTestWithBundle::load($entity_test_with_bundle->id())->moderation_state->entity->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a non-translatable entity type without a langcode can be
|
||||
* moderated.
|
||||
*/
|
||||
public function testNonLangcodeEntityTypeModeration() {
|
||||
// Make the 'entity_test_with_bundle' entity type revisionable and unset
|
||||
// the langcode entity key.
|
||||
$entity_type = clone \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle');
|
||||
$keys = $entity_type->getKeys();
|
||||
$keys['revision'] = 'revision_id';
|
||||
unset($keys['langcode']);
|
||||
$entity_type->set('entity_keys', $keys);
|
||||
\Drupal::state()->set('entity_test_with_bundle.entity_type', $entity_type);
|
||||
\Drupal::entityDefinitionUpdateManager()->applyUpdates();
|
||||
|
||||
// Create a test bundle.
|
||||
$entity_test_bundle = EntityTestBundle::create([
|
||||
'id' => 'example',
|
||||
]);
|
||||
$entity_test_bundle->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$entity_test_bundle->setThirdPartySetting('content_moderation', 'allowed_moderation_states', [
|
||||
'draft',
|
||||
'published'
|
||||
]);
|
||||
$entity_test_bundle->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
|
||||
$entity_test_bundle->save();
|
||||
|
||||
// Check that the tested entity type is not translatable.
|
||||
$entity_type = \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle');
|
||||
$this->assertFalse($entity_type->isTranslatable(), 'The test entity type is not translatable.');
|
||||
|
||||
// Create a test entity.
|
||||
$entity_test_with_bundle = EntityTestWithBundle::create([
|
||||
'type' => 'example'
|
||||
]);
|
||||
$entity_test_with_bundle->save();
|
||||
$this->assertEquals('draft', $entity_test_with_bundle->moderation_state->entity->id());
|
||||
|
||||
$entity_test_with_bundle->moderation_state->target_id = 'published';
|
||||
$entity_test_with_bundle->save();
|
||||
|
||||
$this->assertEquals('published', EntityTestWithBundle::load($entity_test_with_bundle->id())->moderation_state->entity->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the node after clearing the static cache.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node to reload.
|
||||
* @param int|false $revision_id
|
||||
* The specific revision ID to load. Defaults FALSE and just loads the
|
||||
* default revision.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The reloaded node.
|
||||
*/
|
||||
protected function reloadNode(NodeInterface $node, $revision_id = FALSE) {
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
$storage->resetCache([$node->id()]);
|
||||
if ($revision_id) {
|
||||
return $storage->loadRevision($revision_id);
|
||||
}
|
||||
return $storage->load($node->id());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\content_moderation\Entity\ModerationState;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\EntityOperations
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class EntityOperationsTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation',
|
||||
'node',
|
||||
'user',
|
||||
'system',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('node');
|
||||
$this->installSchema('node', 'node_access');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('content_moderation_state');
|
||||
$this->installConfig('content_moderation');
|
||||
|
||||
$this->createNodeType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a page node type to test with, ensuring that it's moderated.
|
||||
*/
|
||||
protected function createNodeType() {
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'page',
|
||||
'label' => 'Page',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the process of saving forward-revisions works as expected.
|
||||
*/
|
||||
public function testForwardRevisions() {
|
||||
// Create a new node in draft.
|
||||
$page = Node::create([
|
||||
'type' => 'page',
|
||||
'title' => 'A',
|
||||
]);
|
||||
$page->moderation_state->target_id = 'draft';
|
||||
$page->save();
|
||||
|
||||
$id = $page->id();
|
||||
|
||||
// Verify the entity saved correctly, and that the presence of forward
|
||||
// revisions doesn't affect the default node load.
|
||||
/** @var Node $page */
|
||||
$page = Node::load($id);
|
||||
$this->assertEquals('A', $page->getTitle());
|
||||
$this->assertTrue($page->isDefaultRevision());
|
||||
$this->assertFalse($page->isPublished());
|
||||
|
||||
// Moderate the entity to published.
|
||||
$page->setTitle('B');
|
||||
$page->moderation_state->target_id = 'published';
|
||||
$page->save();
|
||||
|
||||
// Verify the entity is now published and public.
|
||||
$page = Node::load($id);
|
||||
$this->assertEquals('B', $page->getTitle());
|
||||
$this->assertTrue($page->isDefaultRevision());
|
||||
$this->assertTrue($page->isPublished());
|
||||
|
||||
// Make a new forward-revision in Draft.
|
||||
$page->setTitle('C');
|
||||
$page->moderation_state->target_id = 'draft';
|
||||
$page->save();
|
||||
|
||||
// Verify normal loads return the still-default previous version.
|
||||
$page = Node::load($id);
|
||||
$this->assertEquals('B', $page->getTitle());
|
||||
|
||||
// Verify we can load the forward revision, even if the mechanism is kind
|
||||
// of gross. Note: revisionIds() is only available on NodeStorageInterface,
|
||||
// so this won't work for non-nodes. We'd need to use entity queries. This
|
||||
// is a core bug that should get fixed.
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
$revision_ids = $storage->revisionIds($page);
|
||||
sort($revision_ids);
|
||||
$latest = end($revision_ids);
|
||||
$page = $storage->loadRevision($latest);
|
||||
$this->assertEquals('C', $page->getTitle());
|
||||
|
||||
$page->setTitle('D');
|
||||
$page->moderation_state->target_id = 'published';
|
||||
$page->save();
|
||||
|
||||
// Verify normal loads return the still-default previous version.
|
||||
$page = Node::load($id);
|
||||
$this->assertEquals('D', $page->getTitle());
|
||||
$this->assertTrue($page->isDefaultRevision());
|
||||
$this->assertTrue($page->isPublished());
|
||||
|
||||
// Now check that we can immediately add a new published revision over it.
|
||||
$page->setTitle('E');
|
||||
$page->moderation_state->target_id = 'published';
|
||||
$page->save();
|
||||
|
||||
$page = Node::load($id);
|
||||
$this->assertEquals('E', $page->getTitle());
|
||||
$this->assertTrue($page->isDefaultRevision());
|
||||
$this->assertTrue($page->isPublished());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a newly-created node can go straight to published.
|
||||
*/
|
||||
public function testPublishedCreation() {
|
||||
// Create a new node in draft.
|
||||
$page = Node::create([
|
||||
'type' => 'page',
|
||||
'title' => 'A',
|
||||
]);
|
||||
$page->moderation_state->target_id = 'published';
|
||||
$page->save();
|
||||
|
||||
$id = $page->id();
|
||||
|
||||
// Verify the entity saved correctly.
|
||||
/** @var Node $page */
|
||||
$page = Node::load($id);
|
||||
$this->assertEquals('A', $page->getTitle());
|
||||
$this->assertTrue($page->isDefaultRevision());
|
||||
$this->assertTrue($page->isPublished());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that an unpublished state may be made the default revision.
|
||||
*/
|
||||
public function testArchive() {
|
||||
$published_id = $this->randomMachineName();
|
||||
$published_state = ModerationState::create([
|
||||
'id' => $published_id,
|
||||
'label' => $this->randomString(),
|
||||
'published' => TRUE,
|
||||
'default_revision' => TRUE,
|
||||
]);
|
||||
$published_state->save();
|
||||
|
||||
$archived_id = $this->randomMachineName();
|
||||
$archived_state = ModerationState::create([
|
||||
'id' => $archived_id,
|
||||
'label' => $this->randomString(),
|
||||
'published' => FALSE,
|
||||
'default_revision' => TRUE,
|
||||
]);
|
||||
$archived_state->save();
|
||||
|
||||
$page = Node::create([
|
||||
'type' => 'page',
|
||||
'title' => $this->randomString(),
|
||||
]);
|
||||
$page->moderation_state->target_id = $published_id;
|
||||
$page->save();
|
||||
|
||||
$id = $page->id();
|
||||
|
||||
// The newly-created page should already be published.
|
||||
$page = Node::load($id);
|
||||
$this->assertTrue($page->isPublished());
|
||||
|
||||
// When the page is moderated to the archived state, then the latest
|
||||
// revision should be the default revision, and it should be unpublished.
|
||||
$page->moderation_state->target_id = $archived_id;
|
||||
$page->save();
|
||||
$new_revision_id = $page->getRevisionId();
|
||||
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
$new_revision = $storage->loadRevision($new_revision_id);
|
||||
$this->assertFalse($new_revision->isPublished());
|
||||
$this->assertTrue($new_revision->isDefaultRevision());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\ParamConverter\EntityRevisionConverter
|
||||
* @group content_moderation
|
||||
*/
|
||||
class EntityRevisionConverterTest extends KernelTestBase {
|
||||
|
||||
public static $modules = [
|
||||
'user',
|
||||
'entity_test',
|
||||
'system',
|
||||
'content_moderation',
|
||||
'node',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('content_moderation_state');
|
||||
$this->installSchema('system', 'router');
|
||||
$this->installSchema('system', 'sequences');
|
||||
$this->installSchema('node', 'node_access');
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::convert
|
||||
*/
|
||||
public function testConvertNonRevisionableEntityType() {
|
||||
$entity_test = EntityTest::create([
|
||||
'name' => 'test',
|
||||
]);
|
||||
|
||||
$entity_test->save();
|
||||
|
||||
/** @var \Symfony\Component\Routing\RouterInterface $router */
|
||||
$router = \Drupal::service('router.no_access_checks');
|
||||
$result = $router->match('/entity_test/' . $entity_test->id());
|
||||
|
||||
$this->assertInstanceOf(EntityTest::class, $result['entity_test']);
|
||||
$this->assertEquals($entity_test->getRevisionId(), $result['entity_test']->getRevisionId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::convert
|
||||
*/
|
||||
public function testConvertWithRevisionableEntityType() {
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'article',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->save();
|
||||
|
||||
$revision_ids = [];
|
||||
$node = Node::create([
|
||||
'title' => 'test',
|
||||
'type' => 'article',
|
||||
]);
|
||||
$node->save();
|
||||
|
||||
$revision_ids[] = $node->getRevisionId();
|
||||
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
$revision_ids[] = $node->getRevisionId();
|
||||
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->isDefaultRevision(FALSE);
|
||||
$node->save();
|
||||
$revision_ids[] = $node->getRevisionId();
|
||||
|
||||
/** @var \Symfony\Component\Routing\RouterInterface $router */
|
||||
$router = \Drupal::service('router.no_access_checks');
|
||||
$result = $router->match('/node/' . $node->id() . '/edit');
|
||||
|
||||
$this->assertInstanceOf(Node::class, $result['node']);
|
||||
$this->assertEquals($revision_ids[2], $result['node']->getRevisionId());
|
||||
$this->assertFalse($result['node']->isDefaultRevision());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\Plugin\Validation\Constraint\ModerationStateConstraintValidator
|
||||
* @group content_moderation
|
||||
*/
|
||||
class EntityStateChangeValidationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'node',
|
||||
'content_moderation',
|
||||
'user',
|
||||
'system',
|
||||
'language',
|
||||
'content_translation',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('node', 'node_access');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('content_moderation_state');
|
||||
$this->installConfig('content_moderation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test valid transitions.
|
||||
*
|
||||
* @covers ::validate
|
||||
*/
|
||||
public function testValidTransition() {
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->save();
|
||||
$node = Node::create([
|
||||
'type' => 'example',
|
||||
'title' => 'Test title',
|
||||
]);
|
||||
$node->moderation_state->target_id = 'draft';
|
||||
$node->save();
|
||||
|
||||
$node->moderation_state->target_id = 'published';
|
||||
$this->assertCount(0, $node->validate());
|
||||
$node->save();
|
||||
|
||||
$this->assertEquals('published', $node->moderation_state->entity->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invalid transitions.
|
||||
*
|
||||
* @covers ::validate
|
||||
*/
|
||||
public function testInvalidTransition() {
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->save();
|
||||
$node = Node::create([
|
||||
'type' => 'example',
|
||||
'title' => 'Test title',
|
||||
]);
|
||||
$node->moderation_state->target_id = 'draft';
|
||||
$node->save();
|
||||
|
||||
$node->moderation_state->target_id = 'archived';
|
||||
$violations = $node->validate();
|
||||
$this->assertCount(1, $violations);
|
||||
|
||||
$this->assertEquals('Invalid state transition from <em class="placeholder">Draft</em> to <em class="placeholder">Archived</em>', $violations->get(0)->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that content without prior moderation information can be moderated.
|
||||
*/
|
||||
public function testLegacyContent() {
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->save();
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = Node::create([
|
||||
'type' => 'example',
|
||||
'title' => 'Test title',
|
||||
]);
|
||||
$node->save();
|
||||
|
||||
$nid = $node->id();
|
||||
|
||||
// Enable moderation for our node type.
|
||||
/** @var NodeType $node_type */
|
||||
$node_type = NodeType::load('example');
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', ['draft', 'published']);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
|
||||
$node_type->save();
|
||||
|
||||
$node = Node::load($nid);
|
||||
|
||||
// Having no previous state should not break validation.
|
||||
$violations = $node->validate();
|
||||
|
||||
$this->assertCount(0, $violations);
|
||||
|
||||
// Having no previous state should not break saving the node.
|
||||
$node->setTitle('New');
|
||||
$node->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that content without prior moderation information can be translated.
|
||||
*/
|
||||
public function testLegacyMultilingualContent() {
|
||||
// Enable French.
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->save();
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = Node::create([
|
||||
'type' => 'example',
|
||||
'title' => 'Test title',
|
||||
'langcode' => 'en',
|
||||
]);
|
||||
$node->save();
|
||||
|
||||
$nid = $node->id();
|
||||
|
||||
$node = Node::load($nid);
|
||||
|
||||
// Creating a translation shouldn't break, even though there's no previous
|
||||
// moderated revision for the new language.
|
||||
$node_fr = $node->addTranslation('fr');
|
||||
$node_fr->setTitle('Francais');
|
||||
$node_fr->save();
|
||||
|
||||
// Enable moderation for our node type.
|
||||
/** @var NodeType $node_type */
|
||||
$node_type = NodeType::load('example');
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', ['draft', 'published']);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
|
||||
$node_type->save();
|
||||
|
||||
// Reload the French version of the node.
|
||||
$node = Node::load($nid);
|
||||
$node_fr = $node->getTranslation('fr');
|
||||
|
||||
/** @var \Drupal\node\NodeInterface $node_fr */
|
||||
$node_fr->setTitle('Nouveau');
|
||||
$node_fr->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\content_moderation\Entity\Handler\ModerationHandler;
|
||||
use Drupal\content_moderation\EntityTypeInfo;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\EntityTypeInfo
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class EntityTypeInfoTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation',
|
||||
'entity_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The entity type info class.
|
||||
*
|
||||
* @var \Drupal\content_moderation\EntityTypeInfo
|
||||
*/
|
||||
protected $entityTypeInfo;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->entityTypeInfo = $this->container->get('class_resolver')->getInstanceFromDefinition(EntityTypeInfo::class);
|
||||
$this->entityTypeManager = $this->container->get('entity_type.manager');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::entityBaseFieldInfo
|
||||
*/
|
||||
public function testEntityBaseFieldInfo() {
|
||||
$definition = $this->entityTypeManager->getDefinition('entity_test');
|
||||
$definition->setHandlerClass('moderation', ModerationHandler::class);
|
||||
|
||||
$base_fields = $this->entityTypeInfo->entityBaseFieldInfo($definition);
|
||||
|
||||
$this->assertFalse($base_fields['moderation_state']->isReadOnly());
|
||||
$this->assertTrue($base_fields['moderation_state']->isComputed());
|
||||
$this->assertTrue($base_fields['moderation_state']->isTranslatable());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\content_moderation\Entity\ModerationState;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\Entity\ModerationState
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ModerationStateEntityTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['content_moderation'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('moderation_state');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify moderation state methods based on entity properties.
|
||||
*
|
||||
* @covers ::isPublishedState
|
||||
* @covers ::isDefaultRevisionState
|
||||
*
|
||||
* @dataProvider moderationStateProvider
|
||||
*/
|
||||
public function testModerationStateProperties($published, $default_revision, $is_published, $is_default) {
|
||||
$moderation_state_id = $this->randomMachineName();
|
||||
$moderation_state = ModerationState::create([
|
||||
'id' => $moderation_state_id,
|
||||
'label' => $this->randomString(),
|
||||
'published' => $published,
|
||||
'default_revision' => $default_revision,
|
||||
]);
|
||||
$moderation_state->save();
|
||||
|
||||
$moderation_state = ModerationState::load($moderation_state_id);
|
||||
$this->assertEquals($is_published, $moderation_state->isPublishedState());
|
||||
$this->assertEquals($is_default, $moderation_state->isDefaultRevisionState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for ::testModerationStateProperties.
|
||||
*/
|
||||
public function moderationStateProvider() {
|
||||
return [
|
||||
// Draft, Needs review; should not touch the default revision.
|
||||
[FALSE, FALSE, FALSE, FALSE],
|
||||
// Published; this state should update and publish the default revision.
|
||||
[TRUE, TRUE, TRUE, TRUE],
|
||||
// Archive; this state should update but not publish the default revision.
|
||||
[FALSE, TRUE, FALSE, TRUE],
|
||||
// We try to prevent creating this state via the UI, but when a moderation
|
||||
// state is a published state, it should also become the default revision.
|
||||
[TRUE, FALSE, TRUE, TRUE],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\Plugin\Field\ModerationStateFieldItemList
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ModerationStateFieldItemListTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'node',
|
||||
'content_moderation',
|
||||
'user',
|
||||
'system',
|
||||
'language',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \Drupal\node\NodeInterface
|
||||
*/
|
||||
protected $testNode;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('node', 'node_access');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('content_moderation_state');
|
||||
$this->installConfig('content_moderation');
|
||||
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'example',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'allowed_moderation_states', ['draft']);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
|
||||
$node_type->save();
|
||||
$this->testNode = Node::create([
|
||||
'type' => 'example',
|
||||
'title' => 'Test title',
|
||||
]);
|
||||
$this->testNode->save();
|
||||
\Drupal::entityTypeManager()->getStorage('node')->resetCache();
|
||||
$this->testNode = Node::load($this->testNode->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the field item list when accessing an index.
|
||||
*/
|
||||
public function testArrayIndex() {
|
||||
$this->assertEquals('draft', $this->testNode->moderation_state[0]->entity->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the field item list when iterating.
|
||||
*/
|
||||
public function testArrayIteration() {
|
||||
$states = [];
|
||||
foreach ($this->testNode->moderation_state as $item) {
|
||||
$states[] = $item->entity->id();
|
||||
}
|
||||
$this->assertEquals(['draft'], $states);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the views integration of content_moderation.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ViewsDataIntegrationTest extends ViewsKernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation_test_views',
|
||||
'node',
|
||||
'content_moderation',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('content_moderation_state');
|
||||
$this->installSchema('node', 'node_access');
|
||||
$this->installConfig('content_moderation_test_views');
|
||||
$this->installConfig('content_moderation');
|
||||
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'page',
|
||||
]);
|
||||
$node_type->setThirdPartySetting('content_moderation', 'enabled', TRUE);
|
||||
$node_type->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests content_moderation_views_data().
|
||||
*
|
||||
* @see content_moderation_views_data()
|
||||
*/
|
||||
public function testViewsData() {
|
||||
$node = Node::create([
|
||||
'type' => 'page',
|
||||
'title' => 'Test title first revision',
|
||||
]);
|
||||
$node->moderation_state->target_id = 'published';
|
||||
$node->save();
|
||||
|
||||
$revision = clone $node;
|
||||
$revision->setNewRevision(TRUE);
|
||||
$revision->isDefaultRevision(FALSE);
|
||||
$revision->title->value = 'Test title second revision';
|
||||
$revision->moderation_state->target_id = 'draft';
|
||||
$revision->save();
|
||||
|
||||
$view = Views::getView('test_content_moderation_latest_revision');
|
||||
$view->execute();
|
||||
|
||||
// Ensure that the content_revision_tracker contains the right latest
|
||||
// revision ID.
|
||||
// Also ensure that the relationship back to the revision table contains the
|
||||
// right latest revision.
|
||||
$expected_result = [
|
||||
[
|
||||
'nid' => $node->id(),
|
||||
'revision_id' => $revision->getRevisionId(),
|
||||
'title' => $revision->label(),
|
||||
'moderation_state_1' => 'draft',
|
||||
'moderation_state' => 'published',
|
||||
],
|
||||
];
|
||||
$this->assertIdenticalResultset($view, $expected_result, ['nid' => 'nid', 'content_revision_tracker_revision_id' => 'revision_id', 'moderation_state' => 'moderation_state', 'moderation_state_1' => 'moderation_state_1']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the join from the revision data table to the moderation state table.
|
||||
*/
|
||||
public function testContentModerationStateRevisionJoin() {
|
||||
$node = Node::create([
|
||||
'type' => 'page',
|
||||
'title' => 'Test title first revision',
|
||||
]);
|
||||
$node->moderation_state->target_id = 'published';
|
||||
$node->save();
|
||||
|
||||
$revision = clone $node;
|
||||
$revision->setNewRevision(TRUE);
|
||||
$revision->isDefaultRevision(FALSE);
|
||||
$revision->title->value = 'Test title second revision';
|
||||
$revision->moderation_state->target_id = 'draft';
|
||||
$revision->save();
|
||||
|
||||
$view = Views::getView('test_content_moderation_revision_test');
|
||||
$view->execute();
|
||||
|
||||
$expected_result = [
|
||||
[
|
||||
'revision_id' => $node->getRevisionId(),
|
||||
'moderation_state' => 'published',
|
||||
],
|
||||
[
|
||||
'revision_id' => $revision->getRevisionId(),
|
||||
'moderation_state' => 'draft',
|
||||
],
|
||||
];
|
||||
$this->assertIdenticalResultset($view, $expected_result, ['revision_id' => 'revision_id', 'moderation_state' => 'moderation_state']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the join from the data table to the moderation state table.
|
||||
*/
|
||||
public function testContentModerationStateBaseJoin() {
|
||||
$node = Node::create([
|
||||
'type' => 'page',
|
||||
'title' => 'Test title first revision',
|
||||
]);
|
||||
$node->moderation_state->target_id = 'published';
|
||||
$node->save();
|
||||
|
||||
$revision = clone $node;
|
||||
$revision->setNewRevision(TRUE);
|
||||
$revision->isDefaultRevision(FALSE);
|
||||
$revision->title->value = 'Test title second revision';
|
||||
$revision->moderation_state->target_id = 'draft';
|
||||
$revision->save();
|
||||
|
||||
$view = Views::getView('test_content_moderation_base_table_test');
|
||||
$view->execute();
|
||||
|
||||
$expected_result = [
|
||||
[
|
||||
'nid' => $node->id(),
|
||||
// @todo I would have expected that the content_moderation_state default
|
||||
// revision is the same one as in the node, but it isn't.
|
||||
// Joins from the base table to the default revision of the
|
||||
// content_moderation.
|
||||
'moderation_state' => 'draft',
|
||||
// Joins from the revision table to the default revision of the
|
||||
// content_moderation.
|
||||
'moderation_state_1' => 'draft',
|
||||
// Joins from the revision table to the revision of the
|
||||
// content_moderation.
|
||||
'moderation_state_2' => 'published',
|
||||
],
|
||||
];
|
||||
$this->assertIdenticalResultset($view, $expected_result, ['nid' => 'nid', 'moderation_state' => 'moderation_state', 'moderation_state_1' => 'moderation_state_1', 'moderation_state_2' => 'moderation_state_2']);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Unit;
|
||||
|
||||
use Drupal\content_moderation\ContentPreprocess;
|
||||
use Drupal\Core\Routing\CurrentRouteMatch;
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\ContentPreprocess
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ContentPreprocessTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* @covers ::isLatestVersionPage
|
||||
* @dataProvider routeNodeProvider
|
||||
*/
|
||||
public function testIsLatestVersionPage($route_name, $route_nid, $check_nid, $result, $message) {
|
||||
$content_preprocess = new ContentPreprocess($this->setupCurrentRouteMatch($route_name, $route_nid));
|
||||
$node = $this->setupNode($check_nid);
|
||||
$this->assertEquals($result, $content_preprocess->isLatestVersionPage($node), $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for self::testIsLatestVersionPage().
|
||||
*/
|
||||
public function routeNodeProvider() {
|
||||
return [
|
||||
['entity.node.canonical', 1, 1, FALSE, 'Not on the latest version tab route.'],
|
||||
['entity.node.latest_version', 1, 1, TRUE, 'On the latest version tab route, with the route node.'],
|
||||
['entity.node.latest_version', 1, 2, FALSE, 'On the latest version tab route, with a different node.'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock the current route matching object.
|
||||
*
|
||||
* @param string $route_name
|
||||
* The route to mock.
|
||||
* @param int $nid
|
||||
* The node ID for mocking.
|
||||
*
|
||||
* @return \Drupal\Core\Routing\CurrentRouteMatch
|
||||
* The mocked current route match object.
|
||||
*/
|
||||
protected function setupCurrentRouteMatch($route_name, $nid) {
|
||||
$route_match = $this->prophesize(CurrentRouteMatch::class);
|
||||
$route_match->getRouteName()->willReturn($route_name);
|
||||
$route_match->getParameter('node')->willReturn($this->setupNode($nid));
|
||||
|
||||
return $route_match->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock a node object.
|
||||
*
|
||||
* @param int $nid
|
||||
* The node ID to mock.
|
||||
*
|
||||
* @return \Drupal\node\Entity\Node
|
||||
* The mocked node.
|
||||
*/
|
||||
protected function setupNode($nid) {
|
||||
$node = $this->prophesize(Node::class);
|
||||
$node->id()->willReturn($nid);
|
||||
|
||||
return $node->reveal();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Unit;
|
||||
|
||||
use Drupal\block_content\Entity\BlockContent;
|
||||
use Drupal\Core\Access\AccessResultAllowed;
|
||||
use Drupal\Core\Access\AccessResultForbidden;
|
||||
use Drupal\Core\Routing\RouteMatch;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\content_moderation\Access\LatestRevisionCheck;
|
||||
use Drupal\content_moderation\ModerationInformation;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\Access\LatestRevisionCheck
|
||||
* @group content_moderation
|
||||
*/
|
||||
class LatestRevisionCheckTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* Test the access check of the LatestRevisionCheck service.
|
||||
*
|
||||
* @param string $entity_class
|
||||
* The class of the entity to mock.
|
||||
* @param string $entity_type
|
||||
* The machine name of the entity to mock.
|
||||
* @param bool $has_forward
|
||||
* Whether this entity should have a forward revision in the system.
|
||||
* @param string $result_class
|
||||
* The AccessResult class that should result. One of AccessResultAllowed,
|
||||
* AccessResultForbidden, AccessResultNeutral.
|
||||
*
|
||||
* @dataProvider accessSituationProvider
|
||||
*/
|
||||
public function testLatestAccessPermissions($entity_class, $entity_type, $has_forward, $result_class) {
|
||||
|
||||
/** @var \Drupal\Core\Entity\EntityInterface $entity */
|
||||
$entity = $this->prophesize($entity_class);
|
||||
$entity->getCacheContexts()->willReturn([]);
|
||||
$entity->getCacheTags()->willReturn([]);
|
||||
$entity->getCacheMaxAge()->willReturn(0);
|
||||
|
||||
/** @var \Drupal\content_moderation\ModerationInformation $mod_info */
|
||||
$mod_info = $this->prophesize(ModerationInformation::class);
|
||||
$mod_info->hasForwardRevision($entity->reveal())->willReturn($has_forward);
|
||||
|
||||
$route = $this->prophesize(Route::class);
|
||||
|
||||
$route->getOption('_content_moderation_entity_type')->willReturn($entity_type);
|
||||
|
||||
$route_match = $this->prophesize(RouteMatch::class);
|
||||
$route_match->getParameter($entity_type)->willReturn($entity->reveal());
|
||||
|
||||
$lrc = new LatestRevisionCheck($mod_info->reveal());
|
||||
|
||||
/** @var \Drupal\Core\Access\AccessResult $result */
|
||||
$result = $lrc->access($route->reveal(), $route_match->reveal());
|
||||
|
||||
$this->assertInstanceOf($result_class, $result);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testLastAccessPermissions().
|
||||
*/
|
||||
public function accessSituationProvider() {
|
||||
return [
|
||||
[Node::class, 'node', TRUE, AccessResultAllowed::class],
|
||||
[Node::class, 'node', FALSE, AccessResultForbidden::class],
|
||||
[BlockContent::class, 'block_content', TRUE, AccessResultAllowed::class],
|
||||
[BlockContent::class, 'block_content', FALSE, AccessResultForbidden::class],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Unit;
|
||||
|
||||
use Drupal\content_moderation\Entity\Handler\ModerationHandler;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\ContentEntityType;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\content_moderation\ModerationInformation;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\ModerationInformation
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ModerationInformationTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* Builds a mock user.
|
||||
*
|
||||
* @return AccountInterface
|
||||
* The mocked user.
|
||||
*/
|
||||
protected function getUser() {
|
||||
return $this->prophesize(AccountInterface::class)->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mock Entity Type Manager.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $entity_bundle_storage
|
||||
* Entity bundle storage.
|
||||
*
|
||||
* @return EntityTypeManagerInterface
|
||||
* The mocked entity type manager.
|
||||
*/
|
||||
protected function getEntityTypeManager(EntityStorageInterface $entity_bundle_storage) {
|
||||
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
|
||||
$entity_type_manager->getStorage('entity_test_bundle')->willReturn($entity_bundle_storage);
|
||||
return $entity_type_manager->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up content moderation and entity manager mocking.
|
||||
*
|
||||
* @param bool $status
|
||||
* TRUE if content_moderation should be enabled, FALSE if not.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
* The mocked entity type manager.
|
||||
*/
|
||||
public function setupModerationEntityManager($status) {
|
||||
$bundle = $this->prophesize(ConfigEntityInterface::class);
|
||||
$bundle->getThirdPartySetting('content_moderation', 'enabled', FALSE)->willReturn($status);
|
||||
|
||||
$entity_storage = $this->prophesize(EntityStorageInterface::class);
|
||||
$entity_storage->load('test_bundle')->willReturn($bundle->reveal());
|
||||
|
||||
return $this->getEntityTypeManager($entity_storage->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerBoolean
|
||||
* @covers ::isModeratedEntity
|
||||
*/
|
||||
public function testIsModeratedEntity($status) {
|
||||
$moderation_information = new ModerationInformation($this->setupModerationEntityManager($status), $this->getUser());
|
||||
|
||||
$entity_type = new ContentEntityType([
|
||||
'id' => 'test_entity_type',
|
||||
'bundle_entity_type' => 'entity_test_bundle',
|
||||
'handlers' => ['moderation' => ModerationHandler::class],
|
||||
]);
|
||||
$entity = $this->prophesize(ContentEntityInterface::class);
|
||||
$entity->getEntityType()->willReturn($entity_type);
|
||||
$entity->bundle()->willReturn('test_bundle');
|
||||
|
||||
$this->assertEquals($status, $moderation_information->isModeratedEntity($entity->reveal()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::isModeratedEntity
|
||||
*/
|
||||
public function testIsModeratedEntityForNonBundleEntityType() {
|
||||
$entity_type = new ContentEntityType([
|
||||
'id' => 'test_entity_type',
|
||||
]);
|
||||
$entity = $this->prophesize(ContentEntityInterface::class);
|
||||
$entity->getEntityType()->willReturn($entity_type);
|
||||
$entity->bundle()->willReturn('test_entity_type');
|
||||
|
||||
$entity_storage = $this->prophesize(EntityStorageInterface::class);
|
||||
$entity_type_manager = $this->getEntityTypeManager($entity_storage->reveal());
|
||||
$moderation_information = new ModerationInformation($entity_type_manager, $this->getUser());
|
||||
|
||||
$this->assertEquals(FALSE, $moderation_information->isModeratedEntity($entity->reveal()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerBoolean
|
||||
* @covers ::shouldModerateEntitiesOfBundle
|
||||
*/
|
||||
public function testShouldModerateEntities($status) {
|
||||
$entity_type = new ContentEntityType([
|
||||
'id' => 'test_entity_type',
|
||||
'bundle_entity_type' => 'entity_test_bundle',
|
||||
'handlers' => ['moderation' => ModerationHandler::class],
|
||||
]);
|
||||
|
||||
$moderation_information = new ModerationInformation($this->setupModerationEntityManager($status), $this->getUser());
|
||||
|
||||
$this->assertEquals($status, $moderation_information->shouldModerateEntitiesOfBundle($entity_type, 'test_bundle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for several tests.
|
||||
*/
|
||||
public function providerBoolean() {
|
||||
return [
|
||||
[FALSE],
|
||||
[TRUE],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Unit;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\content_moderation\ModerationStateInterface;
|
||||
use Drupal\content_moderation\ModerationStateTransitionInterface;
|
||||
use Drupal\content_moderation\StateTransitionValidation;
|
||||
use Prophecy\Argument;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\StateTransitionValidation
|
||||
* @group content_moderation
|
||||
*/
|
||||
class StateTransitionValidationTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* Builds a mock storage object for Transitions.
|
||||
*
|
||||
* @return EntityStorageInterface
|
||||
* The mocked storage object for Transitions.
|
||||
*/
|
||||
protected function setupTransitionStorage() {
|
||||
$entity_storage = $this->prophesize(EntityStorageInterface::class);
|
||||
|
||||
$list = $this->setupTransitionEntityList();
|
||||
$entity_storage->loadMultiple()->willReturn($list);
|
||||
$entity_storage->loadMultiple(Argument::type('array'))->will(function ($args) use ($list) {
|
||||
$keys = $args[0];
|
||||
if (empty($keys)) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
$return = array_map(function($key) use ($list) {
|
||||
return $list[$key];
|
||||
}, $keys);
|
||||
|
||||
return $return;
|
||||
});
|
||||
return $entity_storage->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an array of mocked Transition objects.
|
||||
*
|
||||
* @return ModerationStateTransitionInterface[]
|
||||
* An array of mocked Transition objects.
|
||||
*/
|
||||
protected function setupTransitionEntityList() {
|
||||
$transition = $this->prophesize(ModerationStateTransitionInterface::class);
|
||||
$transition->id()->willReturn('draft__needs_review');
|
||||
$transition->getFromState()->willReturn('draft');
|
||||
$transition->getToState()->willReturn('needs_review');
|
||||
$list[$transition->reveal()->id()] = $transition->reveal();
|
||||
|
||||
$transition = $this->prophesize(ModerationStateTransitionInterface::class);
|
||||
$transition->id()->willReturn('needs_review__staging');
|
||||
$transition->getFromState()->willReturn('needs_review');
|
||||
$transition->getToState()->willReturn('staging');
|
||||
$list[$transition->reveal()->id()] = $transition->reveal();
|
||||
|
||||
$transition = $this->prophesize(ModerationStateTransitionInterface::class);
|
||||
$transition->id()->willReturn('staging__published');
|
||||
$transition->getFromState()->willReturn('staging');
|
||||
$transition->getToState()->willReturn('published');
|
||||
$list[$transition->reveal()->id()] = $transition->reveal();
|
||||
|
||||
$transition = $this->prophesize(ModerationStateTransitionInterface::class);
|
||||
$transition->id()->willReturn('needs_review__draft');
|
||||
$transition->getFromState()->willReturn('needs_review');
|
||||
$transition->getToState()->willReturn('draft');
|
||||
$list[$transition->reveal()->id()] = $transition->reveal();
|
||||
|
||||
$transition = $this->prophesize(ModerationStateTransitionInterface::class);
|
||||
$transition->id()->willReturn('draft__draft');
|
||||
$transition->getFromState()->willReturn('draft');
|
||||
$transition->getToState()->willReturn('draft');
|
||||
$list[$transition->reveal()->id()] = $transition->reveal();
|
||||
|
||||
$transition = $this->prophesize(ModerationStateTransitionInterface::class);
|
||||
$transition->id()->willReturn('needs_review__needs_review');
|
||||
$transition->getFromState()->willReturn('needs_review');
|
||||
$transition->getToState()->willReturn('needs_review');
|
||||
$list[$transition->reveal()->id()] = $transition->reveal();
|
||||
|
||||
$transition = $this->prophesize(ModerationStateTransitionInterface::class);
|
||||
$transition->id()->willReturn('published__published');
|
||||
$transition->getFromState()->willReturn('published');
|
||||
$transition->getToState()->willReturn('published');
|
||||
$list[$transition->reveal()->id()] = $transition->reveal();
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a mock storage object for States.
|
||||
*
|
||||
* @return EntityStorageInterface
|
||||
* The mocked storage object for States.
|
||||
*/
|
||||
protected function setupStateStorage() {
|
||||
$entity_storage = $this->prophesize(EntityStorageInterface::class);
|
||||
|
||||
$state = $this->prophesize(ModerationStateInterface::class);
|
||||
$state->id()->willReturn('draft');
|
||||
$state->label()->willReturn('Draft');
|
||||
$state->isPublishedState()->willReturn(FALSE);
|
||||
$state->isDefaultRevisionState()->willReturn(FALSE);
|
||||
$states['draft'] = $state->reveal();
|
||||
|
||||
$state = $this->prophesize(ModerationStateInterface::class);
|
||||
$state->id()->willReturn('needs_review');
|
||||
$state->label()->willReturn('Needs Review');
|
||||
$state->isPublishedState()->willReturn(FALSE);
|
||||
$state->isDefaultRevisionState()->willReturn(FALSE);
|
||||
$states['needs_review'] = $state->reveal();
|
||||
|
||||
$state = $this->prophesize(ModerationStateInterface::class);
|
||||
$state->id()->willReturn('staging');
|
||||
$state->label()->willReturn('Staging');
|
||||
$state->isPublishedState()->willReturn(FALSE);
|
||||
$state->isDefaultRevisionState()->willReturn(FALSE);
|
||||
$states['staging'] = $state->reveal();
|
||||
|
||||
$state = $this->prophesize(ModerationStateInterface::class);
|
||||
$state->id()->willReturn('published');
|
||||
$state->label()->willReturn('Published');
|
||||
$state->isPublishedState()->willReturn(TRUE);
|
||||
$state->isDefaultRevisionState()->willReturn(TRUE);
|
||||
$states['published'] = $state->reveal();
|
||||
|
||||
$state = $this->prophesize(ModerationStateInterface::class);
|
||||
$state->id()->willReturn('archived');
|
||||
$state->label()->willReturn('Archived');
|
||||
$state->isPublishedState()->willReturn(TRUE);
|
||||
$state->isDefaultRevisionState()->willReturn(TRUE);
|
||||
$states['archived'] = $state->reveal();
|
||||
|
||||
$entity_storage->loadMultiple()->willReturn($states);
|
||||
|
||||
foreach ($states as $id => $state) {
|
||||
$entity_storage->load($id)->willReturn($state);
|
||||
}
|
||||
|
||||
return $entity_storage->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a mocked Entity Type Manager.
|
||||
*
|
||||
* @return EntityTypeManagerInterface
|
||||
* The mocked Entity Type Manager.
|
||||
*/
|
||||
protected function setupEntityTypeManager(EntityStorageInterface $storage) {
|
||||
$entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class);
|
||||
$entityTypeManager->getStorage('moderation_state')->willReturn($storage);
|
||||
$entityTypeManager->getStorage('moderation_state_transition')->willReturn($this->setupTransitionStorage());
|
||||
|
||||
return $entityTypeManager->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a mocked query factory that does nothing.
|
||||
*
|
||||
* @return QueryFactory
|
||||
* The mocked query factory that does nothing.
|
||||
*/
|
||||
protected function setupQueryFactory() {
|
||||
$factory = $this->prophesize(QueryFactory::class);
|
||||
|
||||
return $factory->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::isTransitionAllowed
|
||||
* @covers ::calculatePossibleTransitions
|
||||
*
|
||||
* @dataProvider providerIsTransitionAllowedWithValidTransition
|
||||
*/
|
||||
public function testIsTransitionAllowedWithValidTransition($from_id, $to_id) {
|
||||
$storage = $this->setupStateStorage();
|
||||
$state_transition_validation = new StateTransitionValidation($this->setupEntityTypeManager($storage), $this->setupQueryFactory());
|
||||
$this->assertTrue($state_transition_validation->isTransitionAllowed($storage->load($from_id), $storage->load($to_id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for self::testIsTransitionAllowedWithValidTransition().
|
||||
*/
|
||||
public function providerIsTransitionAllowedWithValidTransition() {
|
||||
return [
|
||||
['draft', 'draft'],
|
||||
['draft', 'needs_review'],
|
||||
['needs_review', 'needs_review'],
|
||||
['needs_review', 'staging'],
|
||||
['staging', 'published'],
|
||||
['needs_review', 'draft'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::isTransitionAllowed
|
||||
* @covers ::calculatePossibleTransitions
|
||||
*
|
||||
* @dataProvider providerIsTransitionAllowedWithInValidTransition
|
||||
*/
|
||||
public function testIsTransitionAllowedWithInValidTransition($from_id, $to_id) {
|
||||
$storage = $this->setupStateStorage();
|
||||
$state_transition_validation = new StateTransitionValidation($this->setupEntityTypeManager($storage), $this->setupQueryFactory());
|
||||
$this->assertFalse($state_transition_validation->isTransitionAllowed($storage->load($from_id), $storage->load($to_id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for self::testIsTransitionAllowedWithInValidTransition().
|
||||
*/
|
||||
public function providerIsTransitionAllowedWithInValidTransition() {
|
||||
return [
|
||||
['published', 'needs_review'],
|
||||
['published', 'staging'],
|
||||
['staging', 'needs_review'],
|
||||
['staging', 'staging'],
|
||||
['needs_review', 'published'],
|
||||
['published', 'archived'],
|
||||
['archived', 'published'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies user-aware transition validation.
|
||||
*
|
||||
* @param string $from_id
|
||||
* The state to transition from.
|
||||
* @param string $to_id
|
||||
* The state to transition to.
|
||||
* @param string $permission
|
||||
* The permission to give the user, or not.
|
||||
* @param bool $allowed
|
||||
* Whether or not to grant a user this permission.
|
||||
* @param bool $result
|
||||
* Whether userMayTransition() is expected to return TRUE or FALSE.
|
||||
*
|
||||
* @dataProvider userTransitionsProvider
|
||||
*/
|
||||
public function testUserSensitiveValidTransitions($from_id, $to_id, $permission, $allowed, $result) {
|
||||
$user = $this->prophesize(AccountInterface::class);
|
||||
// The one listed permission will be returned as instructed; Any others are
|
||||
// always denied.
|
||||
$user->hasPermission($permission)->willReturn($allowed);
|
||||
$user->hasPermission(Argument::type('string'))->willReturn(FALSE);
|
||||
|
||||
$storage = $this->setupStateStorage();
|
||||
$validator = new Validator($this->setupEntityTypeManager($storage), $this->setupQueryFactory());
|
||||
|
||||
$this->assertEquals($result, $validator->userMayTransition($storage->load($from_id), $storage->load($to_id), $user->reveal()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the user transition test.
|
||||
*/
|
||||
public function userTransitionsProvider() {
|
||||
// The user has the right permission, so let it through.
|
||||
$ret[] = ['draft', 'draft', 'use draft__draft transition', TRUE, TRUE];
|
||||
|
||||
// The user doesn't have the right permission, block it.
|
||||
$ret[] = ['draft', 'draft', 'use draft__draft transition', FALSE, FALSE];
|
||||
|
||||
// The user has some other permission that doesn't matter.
|
||||
$ret[] = ['draft', 'draft', 'use draft__needs_review transition', TRUE, FALSE];
|
||||
|
||||
// The user has permission, but the transition isn't allowed anyway.
|
||||
$ret[] = ['published', 'needs_review', 'use published__needs_review transition', TRUE, FALSE];
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Testable subclass for selected tests.
|
||||
*
|
||||
* EntityQuery is beyond untestable, so we have to subclass and override the
|
||||
* method that uses it.
|
||||
*/
|
||||
class Validator extends StateTransitionValidation {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTransitionFromStates(ModerationStateInterface $from, ModerationStateInterface $to) {
|
||||
if ($from->id() === 'draft' && $to->id() === 'draft') {
|
||||
return $this->transitionStorage()->loadMultiple(['draft__draft'])[0];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue