Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
|
@ -0,0 +1,8 @@
|
|||
id: node.full
|
||||
label: 'Full content'
|
||||
status: false
|
||||
cache: true
|
||||
targetEntityType: node
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
|
@ -0,0 +1,8 @@
|
|||
id: node.rss
|
||||
label: RSS
|
||||
status: false
|
||||
cache: true
|
||||
targetEntityType: node
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
|
@ -0,0 +1,8 @@
|
|||
id: node.search_index
|
||||
label: 'Search index'
|
||||
status: false
|
||||
cache: true
|
||||
targetEntityType: node
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
|
@ -0,0 +1,8 @@
|
|||
id: node.search_result
|
||||
label: 'Search result highlighting input'
|
||||
status: false
|
||||
cache: true
|
||||
targetEntityType: node
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
|
@ -0,0 +1,8 @@
|
|||
id: node.teaser
|
||||
label: Teaser
|
||||
status: true
|
||||
cache: true
|
||||
targetEntityType: node
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
17
core/modules/node/config/install/field.storage.node.body.yml
Normal file
17
core/modules/node/config/install/field.storage.node.body.yml
Normal file
|
@ -0,0 +1,17 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- text
|
||||
id: node.body
|
||||
field_name: body
|
||||
entity_type: node
|
||||
type: text_with_summary
|
||||
settings: { }
|
||||
module: text
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: true
|
1
core/modules/node/config/install/node.settings.yml
Normal file
1
core/modules/node/config/install/node.settings.yml
Normal file
|
@ -0,0 +1 @@
|
|||
use_admin_theme: false
|
|
@ -0,0 +1,9 @@
|
|||
id: node_delete_action
|
||||
label: 'Delete content'
|
||||
status: true
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_delete_action
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
|
@ -0,0 +1,9 @@
|
|||
id: node_make_sticky_action
|
||||
label: 'Make content sticky'
|
||||
status: true
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_make_sticky_action
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
|
@ -0,0 +1,9 @@
|
|||
id: node_make_unsticky_action
|
||||
label: 'Make content unsticky'
|
||||
status: true
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_make_unsticky_action
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
|
@ -0,0 +1,9 @@
|
|||
id: node_promote_action
|
||||
label: 'Promote content to front page'
|
||||
status: true
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_promote_action
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
|
@ -0,0 +1,9 @@
|
|||
id: node_publish_action
|
||||
label: 'Publish content'
|
||||
status: true
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_publish_action
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
|
@ -0,0 +1,9 @@
|
|||
id: node_save_action
|
||||
label: 'Save content'
|
||||
status: true
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_save_action
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
|
@ -0,0 +1,9 @@
|
|||
id: node_unpromote_action
|
||||
label: 'Remove content from front page'
|
||||
status: true
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_unpromote_action
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
|
@ -0,0 +1,9 @@
|
|||
id: node_unpublish_action
|
||||
label: 'Unpublish content'
|
||||
status: true
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: node_unpublish_action
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
|
@ -0,0 +1,12 @@
|
|||
id: node_search
|
||||
label: Content
|
||||
status: true
|
||||
langcode: en
|
||||
path: node
|
||||
weight: -10
|
||||
plugin: node_search
|
||||
configuration:
|
||||
rankings: { }
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
207
core/modules/node/config/optional/views.view.archive.yml
Normal file
207
core/modules/node/config/optional/views.view.archive.yml
Normal file
|
@ -0,0 +1,207 @@
|
|||
langcode: en
|
||||
status: false
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
id: archive
|
||||
label: Archive
|
||||
module: node
|
||||
description: 'All content, by month.'
|
||||
tag: default
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
core: '8'
|
||||
display:
|
||||
default:
|
||||
id: default
|
||||
display_title: Master
|
||||
display_plugin: default
|
||||
position: 0
|
||||
display_options:
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
query_comment: ''
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_tags: { }
|
||||
title: 'Monthly archive'
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access content'
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
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: 0
|
||||
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: ››
|
||||
sorts:
|
||||
created:
|
||||
id: created
|
||||
table: node_field_data
|
||||
field: created
|
||||
order: DESC
|
||||
plugin_id: date
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
granularity: second
|
||||
entity_type: node
|
||||
entity_field: created
|
||||
arguments:
|
||||
created_year_month:
|
||||
id: created_year_month
|
||||
table: node_field_data
|
||||
field: created_year_month
|
||||
default_action: summary
|
||||
exception:
|
||||
title_enable: true
|
||||
title_enable: true
|
||||
title: '%1'
|
||||
default_argument_type: fixed
|
||||
summary:
|
||||
sort_order: desc
|
||||
format: default_summary
|
||||
summary_options:
|
||||
override: true
|
||||
items_per_page: 30
|
||||
specify_validation: true
|
||||
plugin_id: date_year_month
|
||||
entity_type: node
|
||||
filters:
|
||||
status:
|
||||
id: status
|
||||
table: node_field_data
|
||||
field: status
|
||||
value: true
|
||||
group: 0
|
||||
expose:
|
||||
operator: '0'
|
||||
plugin_id: boolean
|
||||
entity_type: node
|
||||
entity_field: status
|
||||
langcode:
|
||||
id: langcode
|
||||
table: node_field_data
|
||||
field: langcode
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: in
|
||||
value:
|
||||
'***LANGUAGE_language_content***': '***LANGUAGE_language_content***'
|
||||
group: 1
|
||||
exposed: false
|
||||
expose:
|
||||
operator_id: ''
|
||||
label: ''
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: ''
|
||||
identifier: ''
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
reduce: false
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
plugin_id: language
|
||||
entity_type: node
|
||||
entity_field: langcode
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
row:
|
||||
type: 'entity:node'
|
||||
options:
|
||||
view_mode: teaser
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
fields: { }
|
||||
block_1:
|
||||
id: block_1
|
||||
display_title: Block
|
||||
display_plugin: block
|
||||
position: 1
|
||||
display_options:
|
||||
query:
|
||||
type: views_query
|
||||
options: { }
|
||||
defaults:
|
||||
arguments: false
|
||||
arguments:
|
||||
created_year_month:
|
||||
id: created_year_month
|
||||
table: node_field_data
|
||||
field: created_year_month
|
||||
default_action: summary
|
||||
exception:
|
||||
title_enable: true
|
||||
title_enable: true
|
||||
title: '%1'
|
||||
default_argument_type: fixed
|
||||
summary:
|
||||
format: default_summary
|
||||
summary_options:
|
||||
items_per_page: 30
|
||||
specify_validation: true
|
||||
plugin_id: date_year_month
|
||||
entity_type: node
|
||||
page_1:
|
||||
id: page_1
|
||||
display_title: Page
|
||||
display_plugin: page
|
||||
position: 2
|
||||
display_options:
|
||||
query:
|
||||
type: views_query
|
||||
options: { }
|
||||
path: archive
|
594
core/modules/node/config/optional/views.view.content.yml
Normal file
594
core/modules/node/config/optional/views.view.content.yml
Normal file
|
@ -0,0 +1,594 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- user
|
||||
id: content
|
||||
label: Content
|
||||
module: node
|
||||
description: 'Find and manage content.'
|
||||
tag: default
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access content overview'
|
||||
cache:
|
||||
type: tag
|
||||
query:
|
||||
type: views_query
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Filter
|
||||
reset_button: true
|
||||
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: 50
|
||||
tags:
|
||||
previous: '‹ previous'
|
||||
next: 'next ›'
|
||||
first: '« first'
|
||||
last: 'last »'
|
||||
style:
|
||||
type: table
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
override: true
|
||||
sticky: true
|
||||
caption: ''
|
||||
summary: ''
|
||||
description: ''
|
||||
columns:
|
||||
node_bulk_form: node_bulk_form
|
||||
title: title
|
||||
type: type
|
||||
name: name
|
||||
status: status
|
||||
changed: changed
|
||||
edit_node: edit_node
|
||||
delete_node: delete_node
|
||||
dropbutton: dropbutton
|
||||
timestamp: title
|
||||
info:
|
||||
node_bulk_form:
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
title:
|
||||
sortable: true
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
type:
|
||||
sortable: true
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
name:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: priority-low
|
||||
status:
|
||||
sortable: true
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
changed:
|
||||
sortable: true
|
||||
default_sort_order: desc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: priority-low
|
||||
edit_node:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
delete_node:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
dropbutton:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
timestamp:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
default: changed
|
||||
empty_table: true
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
node_bulk_form:
|
||||
id: node_bulk_form
|
||||
table: node
|
||||
field: node_bulk_form
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
element_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
plugin_id: node_bulk_form
|
||||
entity_type: node
|
||||
title:
|
||||
id: title
|
||||
table: node_field_data
|
||||
field: title
|
||||
label: Title
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
element_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
type: string
|
||||
settings:
|
||||
link_to_entity: true
|
||||
plugin_id: field
|
||||
type:
|
||||
id: type
|
||||
table: node_field_data
|
||||
field: type
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: 'Content Type'
|
||||
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: 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: node
|
||||
entity_field: type
|
||||
plugin_id: field
|
||||
name:
|
||||
id: name
|
||||
table: users_field_data
|
||||
field: name
|
||||
relationship: uid
|
||||
label: Author
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
element_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
plugin_id: field
|
||||
type: user_name
|
||||
entity_type: user
|
||||
entity_field: name
|
||||
status:
|
||||
id: status
|
||||
table: node_field_data
|
||||
field: status
|
||||
label: Status
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
element_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
type: boolean
|
||||
settings:
|
||||
format: custom
|
||||
format_custom_true: Published
|
||||
format_custom_false: Unpublished
|
||||
plugin_id: field
|
||||
entity_type: node
|
||||
entity_field: status
|
||||
changed:
|
||||
id: changed
|
||||
table: node_field_data
|
||||
field: changed
|
||||
label: Updated
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
element_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
date_format: short
|
||||
custom_date_format: ''
|
||||
timezone: ''
|
||||
plugin_id: date
|
||||
entity_type: node
|
||||
entity_field: changed
|
||||
operations:
|
||||
id: operations
|
||||
table: node
|
||||
field: operations
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Operations
|
||||
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
|
||||
destination: true
|
||||
plugin_id: entity_operations
|
||||
filters:
|
||||
status_extra:
|
||||
id: status_extra
|
||||
table: node_field_data
|
||||
field: status_extra
|
||||
operator: '='
|
||||
value: false
|
||||
plugin_id: node_status
|
||||
group: 1
|
||||
entity_type: node
|
||||
status:
|
||||
id: status
|
||||
table: node_field_data
|
||||
field: status
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: '='
|
||||
value: true
|
||||
group: 1
|
||||
exposed: true
|
||||
expose:
|
||||
operator_id: ''
|
||||
label: Status
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: status_op
|
||||
identifier: status
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
is_grouped: true
|
||||
group_info:
|
||||
label: 'Published status'
|
||||
description: ''
|
||||
identifier: status
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items:
|
||||
1:
|
||||
title: Published
|
||||
operator: '='
|
||||
value: '1'
|
||||
2:
|
||||
title: Unpublished
|
||||
operator: '='
|
||||
value: '0'
|
||||
plugin_id: boolean
|
||||
entity_type: node
|
||||
entity_field: status
|
||||
type:
|
||||
id: type
|
||||
table: node_field_data
|
||||
field: type
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: in
|
||||
value: { }
|
||||
group: 1
|
||||
exposed: true
|
||||
expose:
|
||||
operator_id: type_op
|
||||
label: Type
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: type_op
|
||||
identifier: type
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
anonymous: '0'
|
||||
administrator: '0'
|
||||
reduce: false
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
plugin_id: bundle
|
||||
entity_type: node
|
||||
entity_field: type
|
||||
title:
|
||||
id: title
|
||||
table: node_field_data
|
||||
field: title
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: contains
|
||||
value: ''
|
||||
group: 1
|
||||
exposed: true
|
||||
expose:
|
||||
operator_id: title_op
|
||||
label: Title
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: title_op
|
||||
identifier: title
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
anonymous: '0'
|
||||
administrator: '0'
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
plugin_id: string
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
langcode:
|
||||
id: langcode
|
||||
table: node_field_data
|
||||
field: langcode
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: in
|
||||
value: { }
|
||||
group: 1
|
||||
exposed: true
|
||||
expose:
|
||||
operator_id: langcode_op
|
||||
label: Language
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: langcode_op
|
||||
identifier: langcode
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
anonymous: '0'
|
||||
administrator: '0'
|
||||
reduce: false
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
plugin_id: language
|
||||
entity_type: node
|
||||
entity_field: langcode
|
||||
sorts: { }
|
||||
title: Content
|
||||
empty:
|
||||
area_text_custom:
|
||||
id: area_text_custom
|
||||
table: views
|
||||
field: area_text_custom
|
||||
empty: true
|
||||
content: 'No content available.'
|
||||
plugin_id: text_custom
|
||||
arguments: { }
|
||||
relationships:
|
||||
uid:
|
||||
id: uid
|
||||
table: node_field_data
|
||||
field: uid
|
||||
admin_label: author
|
||||
required: true
|
||||
plugin_id: standard
|
||||
show_admin_links: false
|
||||
filter_groups:
|
||||
operator: AND
|
||||
groups:
|
||||
1: AND
|
||||
display_extenders: { }
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
cache_metadata:
|
||||
contexts:
|
||||
- languages
|
||||
- url
|
||||
- user
|
||||
- 'user.node_grants:view'
|
||||
cacheable: false
|
||||
page_1:
|
||||
display_options:
|
||||
path: admin/content/node
|
||||
menu:
|
||||
type: 'default tab'
|
||||
title: Content
|
||||
description: ''
|
||||
menu_name: admin
|
||||
weight: -10
|
||||
context: ''
|
||||
tab_options:
|
||||
type: normal
|
||||
title: Content
|
||||
description: 'Find and manage content'
|
||||
menu_name: admin
|
||||
weight: -10
|
||||
display_extenders: { }
|
||||
display_plugin: page
|
||||
display_title: Page
|
||||
id: page_1
|
||||
position: 1
|
||||
cache_metadata:
|
||||
contexts:
|
||||
- languages
|
||||
- url
|
||||
- user
|
||||
- 'user.node_grants:view'
|
||||
cacheable: false
|
428
core/modules/node/config/optional/views.view.content_recent.yml
Normal file
428
core/modules/node/config/optional/views.view.content_recent.yml
Normal file
|
@ -0,0 +1,428 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- user
|
||||
id: content_recent
|
||||
label: 'Recent content'
|
||||
module: node
|
||||
description: 'Recent content.'
|
||||
tag: default
|
||||
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: some
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
style:
|
||||
type: table
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
override: true
|
||||
sticky: false
|
||||
caption: ''
|
||||
summary: ''
|
||||
description: ''
|
||||
columns:
|
||||
title: title
|
||||
name: title
|
||||
edit_node: edit_node
|
||||
delete_node: delete_node
|
||||
info:
|
||||
title:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
name:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
edit_node:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: true
|
||||
responsive: ''
|
||||
delete_node:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: true
|
||||
responsive: ''
|
||||
default: '-1'
|
||||
empty_table: false
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
title:
|
||||
id: title
|
||||
table: node_field_data
|
||||
field: title
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
label: ''
|
||||
exclude: false
|
||||
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
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
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_alter_empty: true
|
||||
type: string
|
||||
settings:
|
||||
link_to_entity: true
|
||||
plugin_id: field
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
name:
|
||||
id: name
|
||||
table: users_field_data
|
||||
field: name
|
||||
relationship: uid
|
||||
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: div
|
||||
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
|
||||
entity_type: user
|
||||
entity_field: name
|
||||
plugin_id: field
|
||||
type: user_name
|
||||
edit_node:
|
||||
id: edit_node
|
||||
table: node
|
||||
field: edit_node
|
||||
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
|
||||
text: Edit
|
||||
entity_type: node
|
||||
plugin_id: entity_link_edit
|
||||
delete_node:
|
||||
id: delete_node
|
||||
table: node
|
||||
field: delete_node
|
||||
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
|
||||
text: Delete
|
||||
entity_type: node
|
||||
plugin_id: entity_link_delete
|
||||
filters:
|
||||
status_extra:
|
||||
id: status_extra
|
||||
table: node_field_data
|
||||
field: status_extra
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: '='
|
||||
value: false
|
||||
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: node_status
|
||||
langcode:
|
||||
id: langcode
|
||||
table: node_field_data
|
||||
field: langcode
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: in
|
||||
value:
|
||||
'***LANGUAGE_language_content***': '***LANGUAGE_language_content***'
|
||||
group: 1
|
||||
exposed: false
|
||||
expose:
|
||||
operator_id: ''
|
||||
label: ''
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: ''
|
||||
identifier: ''
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
reduce: false
|
||||
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
|
||||
entity_field: langcode
|
||||
plugin_id: language
|
||||
sorts:
|
||||
changed:
|
||||
id: changed
|
||||
table: node_field_data
|
||||
field: changed
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: DESC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
granularity: second
|
||||
entity_type: node
|
||||
entity_field: changed
|
||||
plugin_id: date
|
||||
title: 'Recent content'
|
||||
header: { }
|
||||
footer: { }
|
||||
empty:
|
||||
area_text_custom:
|
||||
id: area_text_custom
|
||||
table: views
|
||||
field: area_text_custom
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
empty: true
|
||||
tokenize: false
|
||||
content: 'No content available.'
|
||||
plugin_id: text_custom
|
||||
relationships:
|
||||
uid:
|
||||
id: uid
|
||||
table: node_field_data
|
||||
field: uid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: author
|
||||
required: true
|
||||
entity_type: node
|
||||
entity_field: uid
|
||||
plugin_id: standard
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
use_more: true
|
||||
use_more_always: true
|
||||
use_more_text: More
|
||||
link_url: admin/content
|
||||
link_display: custom_url
|
||||
block_1:
|
||||
display_plugin: block
|
||||
id: block_1
|
||||
display_title: Block
|
||||
position: 1
|
||||
display_options:
|
||||
display_extenders: { }
|
273
core/modules/node/config/optional/views.view.frontpage.yml
Normal file
273
core/modules/node/config/optional/views.view.frontpage.yml
Normal file
|
@ -0,0 +1,273 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- user
|
||||
id: frontpage
|
||||
label: Frontpage
|
||||
module: node
|
||||
description: 'All content promoted to the front page.'
|
||||
tag: default
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access content'
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
empty:
|
||||
area_text_custom:
|
||||
admin_label: ''
|
||||
content: 'No front page content has been created yet.'
|
||||
empty: true
|
||||
field: area_text_custom
|
||||
group_type: group
|
||||
id: area_text_custom
|
||||
label: ''
|
||||
relationship: none
|
||||
table: views
|
||||
tokenize: false
|
||||
plugin_id: text_custom
|
||||
node_listing_empty:
|
||||
admin_label: ''
|
||||
empty: true
|
||||
field: node_listing_empty
|
||||
group_type: group
|
||||
id: node_listing_empty
|
||||
label: ''
|
||||
relationship: none
|
||||
table: node
|
||||
plugin_id: node_listing_empty
|
||||
entity_type: node
|
||||
title:
|
||||
id: title
|
||||
table: views
|
||||
field: title
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
empty: true
|
||||
title: 'Welcome to [site:name]'
|
||||
plugin_id: title
|
||||
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
|
||||
filters:
|
||||
promote:
|
||||
admin_label: ''
|
||||
expose:
|
||||
description: ''
|
||||
identifier: ''
|
||||
label: ''
|
||||
multiple: false
|
||||
operator: ''
|
||||
operator_id: ''
|
||||
remember: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
required: false
|
||||
use_operator: false
|
||||
exposed: false
|
||||
field: promote
|
||||
group: 1
|
||||
group_info:
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
description: ''
|
||||
group_items: { }
|
||||
identifier: ''
|
||||
label: ''
|
||||
multiple: false
|
||||
optional: true
|
||||
remember: false
|
||||
widget: select
|
||||
group_type: group
|
||||
id: promote
|
||||
is_grouped: false
|
||||
operator: '='
|
||||
relationship: none
|
||||
table: node_field_data
|
||||
value: true
|
||||
plugin_id: boolean
|
||||
entity_type: node
|
||||
entity_field: promote
|
||||
status:
|
||||
expose:
|
||||
operator: ''
|
||||
field: status
|
||||
group: 1
|
||||
id: status
|
||||
table: node_field_data
|
||||
value: true
|
||||
plugin_id: boolean
|
||||
entity_type: node
|
||||
entity_field: status
|
||||
langcode:
|
||||
id: langcode
|
||||
table: node_field_data
|
||||
field: langcode
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: in
|
||||
value:
|
||||
'***LANGUAGE_language_content***': '***LANGUAGE_language_content***'
|
||||
group: 1
|
||||
exposed: false
|
||||
expose:
|
||||
operator_id: ''
|
||||
label: ''
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: ''
|
||||
identifier: ''
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
reduce: false
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
plugin_id: language
|
||||
entity_type: node
|
||||
entity_field: langcode
|
||||
pager:
|
||||
type: full
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: 0
|
||||
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
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
row:
|
||||
type: 'entity:node'
|
||||
options:
|
||||
view_mode: teaser
|
||||
sorts:
|
||||
sticky:
|
||||
admin_label: ''
|
||||
expose:
|
||||
label: ''
|
||||
exposed: false
|
||||
field: sticky
|
||||
group_type: group
|
||||
id: sticky
|
||||
order: DESC
|
||||
relationship: none
|
||||
table: node_field_data
|
||||
plugin_id: boolean
|
||||
entity_type: node
|
||||
entity_field: sticky
|
||||
created:
|
||||
field: created
|
||||
id: created
|
||||
order: DESC
|
||||
table: node_field_data
|
||||
plugin_id: date
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
granularity: second
|
||||
entity_type: node
|
||||
entity_field: created
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
title: ''
|
||||
header: { }
|
||||
footer: { }
|
||||
relationships: { }
|
||||
fields: { }
|
||||
arguments: { }
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
feed_1:
|
||||
display_plugin: feed
|
||||
id: feed_1
|
||||
display_title: Feed
|
||||
position: 2
|
||||
display_options:
|
||||
sitename_title: true
|
||||
path: rss.xml
|
||||
displays:
|
||||
page_1: page_1
|
||||
default: ''
|
||||
pager:
|
||||
type: some
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
style:
|
||||
type: rss
|
||||
options:
|
||||
description: ''
|
||||
grouping: { }
|
||||
uses_fields: false
|
||||
row:
|
||||
type: node_rss
|
||||
options:
|
||||
relationship: none
|
||||
view_mode: rss
|
||||
page_1:
|
||||
display_options:
|
||||
path: node
|
||||
display_plugin: page
|
||||
display_title: Page
|
||||
id: page_1
|
||||
position: 1
|
419
core/modules/node/config/optional/views.view.glossary.yml
Normal file
419
core/modules/node/config/optional/views.view.glossary.yml
Normal file
|
@ -0,0 +1,419 @@
|
|||
langcode: en
|
||||
status: false
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- user
|
||||
id: glossary
|
||||
label: Glossary
|
||||
module: node
|
||||
description: 'All content, by letter.'
|
||||
tag: default
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
core: '8'
|
||||
display:
|
||||
default:
|
||||
id: default
|
||||
display_title: Master
|
||||
display_plugin: default
|
||||
position: 0
|
||||
display_options:
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
query_comment: ''
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_tags: { }
|
||||
use_ajax: true
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access content'
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
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: 36
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: 0
|
||||
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: ››
|
||||
fields:
|
||||
title:
|
||||
id: title
|
||||
table: node_field_data
|
||||
field: title
|
||||
plugin_id: field
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Title
|
||||
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
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
name:
|
||||
id: name
|
||||
table: users_field_data
|
||||
field: name
|
||||
label: Author
|
||||
relationship: uid
|
||||
plugin_id: field
|
||||
type: user_name
|
||||
group_type: group
|
||||
admin_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: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
entity_type: user
|
||||
entity_field: name
|
||||
changed:
|
||||
id: changed
|
||||
table: node_field_data
|
||||
field: changed
|
||||
label: 'Last update'
|
||||
date_format: long
|
||||
plugin_id: date
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_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: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
custom_date_format: ''
|
||||
timezone: ''
|
||||
entity_type: node
|
||||
entity_field: changed
|
||||
arguments:
|
||||
title:
|
||||
id: title
|
||||
table: node_field_data
|
||||
field: title
|
||||
default_action: default
|
||||
exception:
|
||||
title_enable: true
|
||||
default_argument_type: fixed
|
||||
default_argument_options:
|
||||
argument: a
|
||||
summary:
|
||||
format: default_summary
|
||||
specify_validation: true
|
||||
glossary: true
|
||||
limit: 1
|
||||
case: upper
|
||||
path_case: lower
|
||||
transform_dash: false
|
||||
plugin_id: string
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
title_enable: false
|
||||
title: ''
|
||||
default_argument_skip_url: false
|
||||
summary_options: { }
|
||||
validate:
|
||||
type: none
|
||||
fail: 'not found'
|
||||
validate_options: { }
|
||||
break_phrase: false
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
relationships:
|
||||
uid:
|
||||
id: uid
|
||||
table: node_field_data
|
||||
field: uid
|
||||
plugin_id: standard
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: author
|
||||
required: false
|
||||
style:
|
||||
type: table
|
||||
options:
|
||||
columns:
|
||||
title: title
|
||||
name: name
|
||||
changed: changed
|
||||
default: title
|
||||
info:
|
||||
title:
|
||||
sortable: true
|
||||
separator: ''
|
||||
name:
|
||||
sortable: true
|
||||
separator: ''
|
||||
changed:
|
||||
sortable: true
|
||||
separator: ''
|
||||
override: true
|
||||
sticky: false
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
order: asc
|
||||
summary: ''
|
||||
empty_table: false
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
default_field_elements: true
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
sorts: { }
|
||||
filters:
|
||||
langcode:
|
||||
id: langcode
|
||||
table: node_field_data
|
||||
field: langcode
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: in
|
||||
value:
|
||||
'***LANGUAGE_language_content***': '***LANGUAGE_language_content***'
|
||||
group: 1
|
||||
exposed: false
|
||||
expose:
|
||||
operator_id: ''
|
||||
label: ''
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: ''
|
||||
identifier: ''
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
reduce: false
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
plugin_id: language
|
||||
entity_type: node
|
||||
entity_field: langcode
|
||||
attachment_1:
|
||||
id: attachment_1
|
||||
display_title: Attachment
|
||||
display_plugin: attachment
|
||||
position: 2
|
||||
display_options:
|
||||
query:
|
||||
type: views_query
|
||||
options: { }
|
||||
pager:
|
||||
type: none
|
||||
options:
|
||||
offset: 0
|
||||
items_per_page: 0
|
||||
defaults:
|
||||
arguments: false
|
||||
arguments:
|
||||
title:
|
||||
id: title
|
||||
table: node_field_data
|
||||
field: title
|
||||
default_action: summary
|
||||
exception:
|
||||
title_enable: true
|
||||
default_argument_type: fixed
|
||||
default_argument_options:
|
||||
argument: a
|
||||
summary:
|
||||
format: unformatted_summary
|
||||
summary_options:
|
||||
items_per_page: 25
|
||||
inline: true
|
||||
separator: ' | '
|
||||
specify_validation: true
|
||||
glossary: true
|
||||
limit: 1
|
||||
case: upper
|
||||
path_case: lower
|
||||
transform_dash: false
|
||||
plugin_id: string
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
title_enable: false
|
||||
title: ''
|
||||
default_argument_skip_url: false
|
||||
validate:
|
||||
type: none
|
||||
fail: 'not found'
|
||||
validate_options: { }
|
||||
break_phrase: false
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
displays:
|
||||
default: default
|
||||
page_1: page_1
|
||||
inherit_arguments: false
|
||||
page_1:
|
||||
id: page_1
|
||||
display_title: Page
|
||||
display_plugin: page
|
||||
position: 1
|
||||
display_options:
|
||||
query:
|
||||
type: views_query
|
||||
options: { }
|
||||
path: glossary
|
||||
menu:
|
||||
type: normal
|
||||
title: Glossary
|
||||
weight: 0
|
||||
menu_name: main
|
||||
parent: ''
|
114
core/modules/node/config/schema/node.schema.yml
Normal file
114
core/modules/node/config/schema/node.schema.yml
Normal file
|
@ -0,0 +1,114 @@
|
|||
# Schema for the configuration files of the node module.
|
||||
|
||||
node.settings:
|
||||
type: config_object
|
||||
label: 'Node settings'
|
||||
mapping:
|
||||
use_admin_theme:
|
||||
type: boolean
|
||||
label: 'Use admin theme when editing or creating content'
|
||||
|
||||
node.type.*:
|
||||
type: config_entity
|
||||
label: 'Content type'
|
||||
mapping:
|
||||
name:
|
||||
type: label
|
||||
label: 'Name'
|
||||
type:
|
||||
type: string
|
||||
label: 'Machine-readable name'
|
||||
description:
|
||||
type: text
|
||||
label: 'Description'
|
||||
help:
|
||||
type: text
|
||||
label: 'Explanation or submission guidelines'
|
||||
new_revision:
|
||||
type: boolean
|
||||
label: 'Whether a new revision should be created by default'
|
||||
preview_mode:
|
||||
type: integer
|
||||
label: 'Preview before submitting'
|
||||
display_submitted:
|
||||
type: boolean
|
||||
label: 'Display setting for author and date Submitted by post information'
|
||||
|
||||
# Plugin \Drupal\node\Plugin\Search\NodeSearch
|
||||
search.plugin.node_search:
|
||||
type: mapping
|
||||
label: 'Content search'
|
||||
mapping:
|
||||
rankings:
|
||||
type: sequence
|
||||
label: 'Content ranking'
|
||||
sequence:
|
||||
type: integer
|
||||
label: 'Influence'
|
||||
|
||||
action.configuration.node_assign_owner_action:
|
||||
type: mapping
|
||||
label: 'Change the author of content configuration'
|
||||
mapping:
|
||||
owner_uid:
|
||||
type: text
|
||||
label: 'Username'
|
||||
|
||||
action.configuration.node_unpromote_action:
|
||||
type: action_configuration_default
|
||||
label: 'Demote selected content from front page configuration'
|
||||
|
||||
action.configuration.node_promote_action:
|
||||
type: action_configuration_default
|
||||
label: 'Promote selected content from front page configuration'
|
||||
|
||||
action.configuration.node_publish_action:
|
||||
type: action_configuration_default
|
||||
label: 'Publish selected content configuration'
|
||||
|
||||
action.configuration.node_unpublish_action:
|
||||
type: action_configuration_default
|
||||
label: 'Unpublish selected content configuration'
|
||||
|
||||
action.configuration.node_save_action:
|
||||
type: action_configuration_default
|
||||
label: 'Save content configuration'
|
||||
|
||||
action.configuration.node_delete_action:
|
||||
type: action_configuration_default
|
||||
label: 'Delete content configuration'
|
||||
|
||||
action.configuration.node_make_sticky_action:
|
||||
type: action_configuration_default
|
||||
label: 'Make selected content sticky configuration'
|
||||
|
||||
action.configuration.node_make_unsticky_action:
|
||||
type: action_configuration_default
|
||||
label: 'Make selected content unsticky configuration'
|
||||
|
||||
action.configuration.node_unpublish_by_keyword_action:
|
||||
type: mapping
|
||||
label: 'Unpublish content containing keyword(s) configuration'
|
||||
mapping:
|
||||
keyword:
|
||||
type: sequence
|
||||
label: 'Keywords'
|
||||
sequence:
|
||||
type: string
|
||||
label: 'Keyword'
|
||||
|
||||
block.settings.node_syndicate_block:
|
||||
type: block_settings
|
||||
label: 'Syndicate block'
|
||||
mapping:
|
||||
block_count:
|
||||
type: integer
|
||||
label: 'Block count'
|
||||
|
||||
condition.plugin.node_type:
|
||||
type: condition.plugin
|
||||
mapping:
|
||||
bundles:
|
||||
type: sequence
|
||||
sequence:
|
||||
type: string
|
183
core/modules/node/config/schema/node.views.schema.yml
Normal file
183
core/modules/node/config/schema/node.views.schema.yml
Normal file
|
@ -0,0 +1,183 @@
|
|||
# Schema for the views plugins of the Node module.
|
||||
|
||||
views.area.node_listing_empty:
|
||||
type: views_area
|
||||
label: 'Node link'
|
||||
|
||||
views.argument.node_nid:
|
||||
type: views_argument
|
||||
label: 'Node ID'
|
||||
mapping:
|
||||
break_phrase:
|
||||
type: boolean
|
||||
label: 'Allow multiple values'
|
||||
not:
|
||||
type: boolean
|
||||
label: 'Exclude'
|
||||
|
||||
views.argument.node_type:
|
||||
type: views_argument
|
||||
label: 'Node type'
|
||||
mapping:
|
||||
glossary:
|
||||
type: boolean
|
||||
label: 'Glossary mode'
|
||||
limit:
|
||||
type: integer
|
||||
label: 'Character limit'
|
||||
case:
|
||||
type: string
|
||||
label: 'Case'
|
||||
path_case:
|
||||
type: string
|
||||
label: 'Case in path'
|
||||
transform_dash:
|
||||
type: boolean
|
||||
label: 'Transform spaces to dashes in URL'
|
||||
break_phrase:
|
||||
type: boolean
|
||||
label: 'Allow multiple values'
|
||||
add_table:
|
||||
type: boolean
|
||||
label: 'Allow multiple filter values to work together'
|
||||
require_value:
|
||||
type: boolean
|
||||
label: 'Do not display items with no value in summary'
|
||||
|
||||
views.argument.node_uid_revision:
|
||||
type: views_argument
|
||||
label: 'Node user ID'
|
||||
mapping:
|
||||
break_phrase:
|
||||
type: boolean
|
||||
label: 'Allow multiple values'
|
||||
not:
|
||||
type: boolean
|
||||
label: 'Exclude'
|
||||
|
||||
views.argument.node_vid:
|
||||
type: views_argument
|
||||
label: 'Node revision ID'
|
||||
mapping:
|
||||
break_phrase:
|
||||
type: boolean
|
||||
label: 'Allow multiple values'
|
||||
not:
|
||||
type: boolean
|
||||
label: 'Exclude'
|
||||
|
||||
views.argument_default.node:
|
||||
type: sequence
|
||||
label: 'Content ID from URL'
|
||||
sequence:
|
||||
type: string
|
||||
label: 'Nid'
|
||||
|
||||
views.argument_validator.node:
|
||||
type: mapping
|
||||
label: 'Content'
|
||||
mapping:
|
||||
types:
|
||||
type: sequence
|
||||
label: 'Content types'
|
||||
sequence:
|
||||
type: string
|
||||
label: 'Type'
|
||||
access:
|
||||
type: boolean
|
||||
label: 'Validate user has access to the content'
|
||||
access_op:
|
||||
type: boolean
|
||||
label: 'Access operation to check'
|
||||
nid_type:
|
||||
type: string
|
||||
label: 'Filter value format'
|
||||
|
||||
views.field.node:
|
||||
type: views_field
|
||||
label: 'Node'
|
||||
mapping:
|
||||
link_to_node:
|
||||
type: boolean
|
||||
label: 'Link this field to the original piece of content'
|
||||
|
||||
views.field.node_bulk_form:
|
||||
type: views_field_bulk_form
|
||||
label: 'Node bulk form'
|
||||
|
||||
views.field.node_path:
|
||||
type: views_field
|
||||
label: 'Node path'
|
||||
mapping:
|
||||
absolute:
|
||||
type: boolean
|
||||
label: 'Use absolute link (begins with "http://")'
|
||||
|
||||
views.field.node_revision_link:
|
||||
type: views_field
|
||||
label: 'Link to a node revision'
|
||||
mapping:
|
||||
text:
|
||||
type: label
|
||||
label: 'Text to display'
|
||||
|
||||
views.field.node_revision_link_delete:
|
||||
type: views_field
|
||||
label: 'Link to delete a node revision'
|
||||
mapping:
|
||||
text:
|
||||
type: label
|
||||
label: 'Text to display'
|
||||
|
||||
views.field.node_revision_link_revert:
|
||||
type: views_field
|
||||
label: 'Link to revert a node to a revision'
|
||||
mapping:
|
||||
text:
|
||||
type: label
|
||||
label: 'Text to display'
|
||||
|
||||
views.filter.node_access:
|
||||
type: views_filter
|
||||
label: 'Node access'
|
||||
|
||||
views.filter.node_status:
|
||||
type: views_filter
|
||||
label: 'Node status'
|
||||
|
||||
views.filter.node_uid_revision:
|
||||
type: views_filter
|
||||
label: 'Node revisions of an user'
|
||||
mapping:
|
||||
operator:
|
||||
type: string
|
||||
label: 'Operator'
|
||||
value:
|
||||
type: sequence
|
||||
label: 'Values'
|
||||
sequence:
|
||||
type: string
|
||||
label: 'Value'
|
||||
expose:
|
||||
type: mapping
|
||||
label: 'Expose'
|
||||
mapping:
|
||||
reduce:
|
||||
type: boolean
|
||||
label: 'Reduce'
|
||||
|
||||
views.filter_value.node_access:
|
||||
type: string
|
||||
label: 'Access'
|
||||
|
||||
views.filter_value.node_status:
|
||||
type: boolean
|
||||
label: 'Status'
|
||||
|
||||
views.row.node_rss:
|
||||
type: views_row
|
||||
label: 'Content options'
|
||||
mapping:
|
||||
view_mode:
|
||||
type: string
|
||||
label: 'Display type'
|
58
core/modules/node/content_types.js
Normal file
58
core/modules/node/content_types.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* @file
|
||||
* Javascript for the node content editing form.
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.contentTypes = {
|
||||
attach: function (context) {
|
||||
var $context = $(context);
|
||||
// Provide the vertical tab summaries.
|
||||
$context.find('#edit-submission').drupalSetSummary(function (context) {
|
||||
var vals = [];
|
||||
vals.push(Drupal.checkPlain($(context).find('#edit-title-label').val()) || Drupal.t('Requires a title'));
|
||||
return vals.join(', ');
|
||||
});
|
||||
$context.find('#edit-workflow').drupalSetSummary(function (context) {
|
||||
var vals = [];
|
||||
$(context).find("input[name^='options']:checked").parent().each(function () {
|
||||
vals.push(Drupal.checkPlain($(this).text()));
|
||||
});
|
||||
if (!$(context).find('#edit-options-status').is(':checked')) {
|
||||
vals.unshift(Drupal.t('Not published'));
|
||||
}
|
||||
return vals.join(', ');
|
||||
});
|
||||
$('#edit-language', context).drupalSetSummary(function (context) {
|
||||
var vals = [];
|
||||
|
||||
vals.push($(".form-item-language-configuration-langcode select option:selected", context).text());
|
||||
|
||||
$('input:checked', context).next('label').each(function () {
|
||||
vals.push(Drupal.checkPlain($(this).text()));
|
||||
});
|
||||
|
||||
return vals.join(', ');
|
||||
});
|
||||
$context.find('#edit-display').drupalSetSummary(function (context) {
|
||||
var vals = [];
|
||||
var $editContext = $(context);
|
||||
$editContext.find('input:checked').next('label').each(function () {
|
||||
vals.push(Drupal.checkPlain($(this).text()));
|
||||
});
|
||||
if (!$editContext.find('#edit-display-submitted').is(':checked')) {
|
||||
vals.unshift(Drupal.t("Don't display post information"));
|
||||
}
|
||||
return vals.join(', ');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
11
core/modules/node/css/node.admin.css
Normal file
11
core/modules/node/css/node.admin.css
Normal file
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* @file
|
||||
* Styles for administration pages.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Revisions overview screen.
|
||||
*/
|
||||
.revision-current {
|
||||
background: #ffc;
|
||||
}
|
76
core/modules/node/css/node.module.css
Normal file
76
core/modules/node/css/node.module.css
Normal file
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* @file
|
||||
* Styles for administration pages.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Node add/edit form layout
|
||||
*/
|
||||
|
||||
/* Narrow screens */
|
||||
.layout-region {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Wide screens */
|
||||
@media
|
||||
screen and (min-width: 780px),
|
||||
(orientation: landscape) and (min-device-height: 780px) {
|
||||
|
||||
.layout-region-node-main,
|
||||
.layout-region-node-footer {
|
||||
float: left; /* LTR */
|
||||
width: 65%;
|
||||
padding-right: 2em; /* LTR */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
[dir="rtl"] .layout-region-node-main,
|
||||
[dir="rtl"] .layout-region-node-footer {
|
||||
float: right;
|
||||
padding-left: 2em;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.layout-region-node-secondary {
|
||||
float: right; /* LTR */
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
[dir="rtl"] .layout-region-node-secondary {
|
||||
float: left;
|
||||
}
|
||||
|
||||
/* @todo File an issue to add a standard class to all text-like inputs */
|
||||
.layout-region-node-secondary .form-autocomplete,
|
||||
.layout-region-node-secondary .form-text,
|
||||
.layout-region-node-secondary .form-tel,
|
||||
.layout-region-node-secondary .form-email,
|
||||
.layout-region-node-secondary .form-url,
|
||||
.layout-region-node-secondary .form-search,
|
||||
.layout-region-node-secondary .form-number,
|
||||
.layout-region-node-secondary .form-color,
|
||||
.layout-region-node-secondary textarea {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The vertical toolbar mode gets triggered for narrow screens, which throws off
|
||||
* the intent of media queries written for the viewport width. When the vertical
|
||||
* toolbar is on, we need to suppress layout for the original media width + the
|
||||
* toolbar width (240px). In this case, 240px + 780px.
|
||||
*/
|
||||
@media
|
||||
screen and (max-width: 1020px) {
|
||||
|
||||
.toolbar-vertical.toolbar-tray-open .layout-region-node-main,
|
||||
.toolbar-vertical.toolbar-tray-open .layout-region-node-footer,
|
||||
.toolbar-vertical.toolbar-tray-open .layout-region-node-secondary {
|
||||
float: none;
|
||||
width: auto;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
22
core/modules/node/css/node.preview.css
Normal file
22
core/modules/node/css/node.preview.css
Normal file
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* @file
|
||||
* Styles for node preview page.
|
||||
*/
|
||||
|
||||
.node-preview-container {
|
||||
position: fixed;
|
||||
z-index: 499;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 36em) {
|
||||
.node-preview-container .form-type-select {
|
||||
margin-left: 25%; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .node-preview-container .form-type-select {
|
||||
margin-right: 25%;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
176
core/modules/node/node.admin.inc
Normal file
176
core/modules/node/node.admin.inc
Normal file
|
@ -0,0 +1,176 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Content administration and module settings user interface.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Updates all nodes in the passed-in array with the passed-in field values.
|
||||
*
|
||||
* IMPORTANT NOTE: This function is intended to work when called from a form
|
||||
* submission handler. Calling it outside of the form submission process may not
|
||||
* work correctly.
|
||||
*
|
||||
* @param array $nodes
|
||||
* Array of node nids or nodes to update.
|
||||
* @param array $updates
|
||||
* Array of key/value pairs with node field names and the value to update that
|
||||
* field to.
|
||||
* @param string $langcode
|
||||
* (optional) The language updates should be applied to. If none is specified
|
||||
* all available languages are processed.
|
||||
* @param bool $load
|
||||
* (optional) TRUE if $nodes contains an array of node IDs to be loaded, FALSE
|
||||
* if it contains fully loaded nodes. Defaults to FALSE.
|
||||
* @param bool $revisions
|
||||
* (optional) TRUE if $nodes contains an array of revision IDs instead of
|
||||
* node IDs. Defaults to FALSE; will be ignored if $load is FALSE.
|
||||
*/
|
||||
function node_mass_update(array $nodes, array $updates, $langcode = NULL, $load = FALSE, $revisions = FALSE) {
|
||||
// We use batch processing to prevent timeout when updating a large number
|
||||
// of nodes.
|
||||
if (count($nodes) > 10) {
|
||||
$batch = array(
|
||||
'operations' => array(
|
||||
array('_node_mass_update_batch_process', array($nodes, $updates, $langcode, $load, $revisions))
|
||||
),
|
||||
'finished' => '_node_mass_update_batch_finished',
|
||||
'title' => t('Processing'),
|
||||
// We use a single multi-pass operation, so the default
|
||||
// 'Remaining x of y operations' message will be confusing here.
|
||||
'progress_message' => '',
|
||||
'error_message' => t('The update has encountered an error.'),
|
||||
// The operations do not live in the .module file, so we need to
|
||||
// tell the batch engine which file to load before calling them.
|
||||
'file' => drupal_get_path('module', 'node') . '/node.admin.inc',
|
||||
);
|
||||
batch_set($batch);
|
||||
}
|
||||
else {
|
||||
if ($load && !$revisions) {
|
||||
$nodes = Node::loadMultiple($nodes);
|
||||
}
|
||||
foreach ($nodes as $node) {
|
||||
if ($load && $revisions) {
|
||||
$node = entity_revision_load('node', $node);
|
||||
}
|
||||
_node_mass_update_helper($node, $updates, $langcode);
|
||||
}
|
||||
drupal_set_message(t('The update has been performed.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates individual nodes when fewer than 10 are queued.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* A node to update.
|
||||
* @param array $updates
|
||||
* Associative array of updates.
|
||||
* @param string $langcode
|
||||
* (optional) The language updates should be applied to. If none is specified
|
||||
* all available languages are processed.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* An updated node object.
|
||||
*
|
||||
* @see node_mass_update()
|
||||
*/
|
||||
function _node_mass_update_helper(NodeInterface $node, array $updates, $langcode = NULL) {
|
||||
$langcodes = isset($langcode) ? array($langcode) : array_keys($node->getTranslationLanguages());
|
||||
// For efficiency manually save the original node before applying any changes.
|
||||
$node->original = clone $node;
|
||||
foreach ($langcodes as $langcode) {
|
||||
foreach ($updates as $name => $value) {
|
||||
$node->getTranslation($langcode)->$name = $value;
|
||||
}
|
||||
}
|
||||
$node->save();
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements callback_batch_operation().
|
||||
*
|
||||
* Executes a batch operation for node_mass_update().
|
||||
*
|
||||
* @param array $nodes
|
||||
* An array of node IDs.
|
||||
* @param array $updates
|
||||
* Associative array of updates.
|
||||
* @param bool $load
|
||||
* TRUE if $nodes contains an array of node IDs to be loaded, FALSE if it
|
||||
* contains fully loaded nodes.
|
||||
* @param bool $revisions
|
||||
* (optional) TRUE if $nodes contains an array of revision IDs instead of
|
||||
* node IDs. Defaults to FALSE; will be ignored if $load is FALSE.
|
||||
* @param array $context
|
||||
* An array of contextual key/values.
|
||||
*/
|
||||
function _node_mass_update_batch_process(array $nodes, array $updates, $load, $revisions, array &$context) {
|
||||
if (!isset($context['sandbox']['progress'])) {
|
||||
$context['sandbox']['progress'] = 0;
|
||||
$context['sandbox']['max'] = count($nodes);
|
||||
$context['sandbox']['nodes'] = $nodes;
|
||||
}
|
||||
|
||||
// Process nodes by groups of 5.
|
||||
$count = min(5, count($context['sandbox']['nodes']));
|
||||
for ($i = 1; $i <= $count; $i++) {
|
||||
// For each nid, load the node, reset the values, and save it.
|
||||
$node = array_shift($context['sandbox']['nodes']);
|
||||
if ($load) {
|
||||
$node = $revisions ?
|
||||
entity_revision_load('node', $node) : Node::load($node);
|
||||
}
|
||||
$node = _node_mass_update_helper($node, $updates);
|
||||
|
||||
// Store result for post-processing in the finished callback.
|
||||
$context['results'][] = \Drupal::l($node->label(), $node->urlInfo());
|
||||
|
||||
// Update our progress information.
|
||||
$context['sandbox']['progress']++;
|
||||
}
|
||||
|
||||
// Inform the batch engine that we are not finished,
|
||||
// and provide an estimation of the completion level we reached.
|
||||
if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
|
||||
$context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements callback_batch_finished().
|
||||
*
|
||||
* Reports the 'finished' status of batch operation for node_mass_update().
|
||||
*
|
||||
* @param bool $success
|
||||
* A boolean indicating whether the batch mass update operation successfully
|
||||
* concluded.
|
||||
* @param string[] $results
|
||||
* An array of rendered links to nodes updated via the batch mode process.
|
||||
* @param array $operations
|
||||
* An array of function calls (not used in this function).
|
||||
*
|
||||
* @see _node_mass_update_batch_process()
|
||||
*/
|
||||
function _node_mass_update_batch_finished($success, $results, $operations) {
|
||||
if ($success) {
|
||||
drupal_set_message(t('The update has been performed.'));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('An error occurred and processing did not complete.'), 'error');
|
||||
$message = \Drupal::translation()->formatPlural(count($results), '1 item successfully processed:', '@count items successfully processed:');
|
||||
$item_list = array(
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $results,
|
||||
);
|
||||
$message .= drupal_render($item_list);
|
||||
drupal_set_message($message);
|
||||
}
|
||||
}
|
511
core/modules/node/node.api.php
Normal file
511
core/modules/node/node.api.php
Normal file
|
@ -0,0 +1,511 @@
|
|||
<?php
|
||||
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks specific to the Node module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Inform the node access system what permissions the user has.
|
||||
*
|
||||
* This hook is for implementation by node access modules. In this hook,
|
||||
* the module grants a user different "grant IDs" within one or more
|
||||
* "realms". In hook_node_access_records(), the realms and grant IDs are
|
||||
* associated with permission to view, edit, and delete individual nodes.
|
||||
*
|
||||
* The realms and grant IDs can be arbitrarily defined by your node access
|
||||
* module; it is common to use role IDs as grant IDs, but that is not required.
|
||||
* Your module could instead maintain its own list of users, where each list has
|
||||
* an ID. In that case, the return value of this hook would be an array of the
|
||||
* list IDs that this user is a member of.
|
||||
*
|
||||
* A node access module may implement as many realms as necessary to properly
|
||||
* define the access privileges for the nodes. Note that the system makes no
|
||||
* distinction between published and unpublished nodes. It is the module's
|
||||
* responsibility to provide appropriate realms to limit access to unpublished
|
||||
* content.
|
||||
*
|
||||
* Node access records are stored in the {node_access} table and define which
|
||||
* grants are required to access a node. There is a special case for the view
|
||||
* operation -- a record with node ID 0 corresponds to a "view all" grant for
|
||||
* the realm and grant ID of that record. If there are no node access modules
|
||||
* enabled, the core node module adds a node ID 0 record for realm 'all'. Node
|
||||
* access modules can also grant "view all" permission on their custom realms;
|
||||
* for example, a module could create a record in {node_access} with:
|
||||
* @code
|
||||
* $record = array(
|
||||
* 'nid' => 0,
|
||||
* 'gid' => 888,
|
||||
* 'realm' => 'example_realm',
|
||||
* 'grant_view' => 1,
|
||||
* 'grant_update' => 0,
|
||||
* 'grant_delete' => 0,
|
||||
* );
|
||||
* db_insert('node_access')->fields($record)->execute();
|
||||
* @endcode
|
||||
* And then in its hook_node_grants() implementation, it would need to return:
|
||||
* @code
|
||||
* if ($op == 'view') {
|
||||
* $grants['example_realm'] = array(888);
|
||||
* }
|
||||
* @endcode
|
||||
* If you decide to do this, be aware that the node_access_rebuild() function
|
||||
* will erase any node ID 0 entry when it is called, so you will need to make
|
||||
* sure to restore your {node_access} record after node_access_rebuild() is
|
||||
* called.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The account object whose grants are requested.
|
||||
* @param string $op
|
||||
* The node operation to be performed, such as 'view', 'update', or 'delete'.
|
||||
*
|
||||
* @return array
|
||||
* An array whose keys are "realms" of grants, and whose values are arrays of
|
||||
* the grant IDs within this realm that this user is being granted.
|
||||
*
|
||||
* For a detailed example, see node_access_example.module.
|
||||
*
|
||||
* @see node_access_view_all_nodes()
|
||||
* @see node_access_rebuild()
|
||||
* @ingroup node_access
|
||||
*/
|
||||
function hook_node_grants(\Drupal\Core\Session\AccountInterface $account, $op) {
|
||||
if ($account->hasPermission('access private content')) {
|
||||
$grants['example'] = array(1);
|
||||
}
|
||||
if ($account->id()) {
|
||||
$grants['example_author'] = array($account->id());
|
||||
}
|
||||
return $grants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set permissions for a node to be written to the database.
|
||||
*
|
||||
* When a node is saved, a module implementing hook_node_access_records() will
|
||||
* be asked if it is interested in the access permissions for a node. If it is
|
||||
* interested, it must respond with an array of permissions arrays for that
|
||||
* node.
|
||||
*
|
||||
* Node access grants apply regardless of the published or unpublished status
|
||||
* of the node. Implementations must make sure not to grant access to
|
||||
* unpublished nodes if they don't want to change the standard access control
|
||||
* behavior. Your module may need to create a separate access realm to handle
|
||||
* access to unpublished nodes.
|
||||
*
|
||||
* Note that the grant values in the return value from your hook must be
|
||||
* integers and not boolean TRUE and FALSE.
|
||||
*
|
||||
* Each permissions item in the array is an array with the following elements:
|
||||
* - 'realm': The name of a realm that the module has defined in
|
||||
* hook_node_grants().
|
||||
* - 'gid': A 'grant ID' from hook_node_grants().
|
||||
* - 'grant_view': If set to 1 a user that has been identified as a member
|
||||
* of this gid within this realm can view this node. This should usually be
|
||||
* set to $node->isPublished(). Failure to do so may expose unpublished content
|
||||
* to some users.
|
||||
* - 'grant_update': If set to 1 a user that has been identified as a member
|
||||
* of this gid within this realm can edit this node.
|
||||
* - 'grant_delete': If set to 1 a user that has been identified as a member
|
||||
* of this gid within this realm can delete this node.
|
||||
* - langcode: (optional) The language code of a specific translation of the
|
||||
* node, if any. Modules may add this key to grant different access to
|
||||
* different translations of a node, such that (e.g.) a particular group is
|
||||
* granted access to edit the Catalan version of the node, but not the
|
||||
* Hungarian version. If no value is provided, the langcode is set
|
||||
* automatically from the $node parameter and the node's original language (if
|
||||
* specified) is used as a fallback. Only specify multiple grant records with
|
||||
* different languages for a node if the site has those languages configured.
|
||||
*
|
||||
* A "deny all" grant may be used to deny all access to a particular node or
|
||||
* node translation:
|
||||
* @code
|
||||
* $grants[] = array(
|
||||
* 'realm' => 'all',
|
||||
* 'gid' => 0,
|
||||
* 'grant_view' => 0,
|
||||
* 'grant_update' => 0,
|
||||
* 'grant_delete' => 0,
|
||||
* 'langcode' => 'ca',
|
||||
* );
|
||||
* @endcode
|
||||
* Note that another module node access module could override this by granting
|
||||
* access to one or more nodes, since grants are additive. To enforce that
|
||||
* access is denied in a particular case, use hook_node_access_records_alter().
|
||||
* Also note that a deny all is not written to the database; denies are
|
||||
* implicit.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node that has just been saved.
|
||||
*
|
||||
* @return
|
||||
* An array of grants as defined above.
|
||||
*
|
||||
* @see node_access_write_grants()
|
||||
* @see hook_node_access_records_alter()
|
||||
* @ingroup node_access
|
||||
*/
|
||||
function hook_node_access_records(\Drupal\node\NodeInterface $node) {
|
||||
// We only care about the node if it has been marked private. If not, it is
|
||||
// treated just like any other node and we completely ignore it.
|
||||
if ($node->private->value) {
|
||||
$grants = array();
|
||||
// Only published Catalan translations of private nodes should be viewable
|
||||
// to all users. If we fail to check $node->isPublished(), all users would be able
|
||||
// to view an unpublished node.
|
||||
if ($node->isPublished()) {
|
||||
$grants[] = array(
|
||||
'realm' => 'example',
|
||||
'gid' => 1,
|
||||
'grant_view' => 1,
|
||||
'grant_update' => 0,
|
||||
'grant_delete' => 0,
|
||||
'langcode' => 'ca'
|
||||
);
|
||||
}
|
||||
// For the example_author array, the GID is equivalent to a UID, which
|
||||
// means there are many groups of just 1 user.
|
||||
// Note that an author can always view his or her nodes, even if they
|
||||
// have status unpublished.
|
||||
if ($node->getOwnerId()) {
|
||||
$grants[] = array(
|
||||
'realm' => 'example_author',
|
||||
'gid' => $node->getOwnerId(),
|
||||
'grant_view' => 1,
|
||||
'grant_update' => 1,
|
||||
'grant_delete' => 1,
|
||||
'langcode' => 'ca'
|
||||
);
|
||||
}
|
||||
|
||||
return $grants;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter permissions for a node before it is written to the database.
|
||||
*
|
||||
* Node access modules establish rules for user access to content. Node access
|
||||
* records are stored in the {node_access} table and define which permissions
|
||||
* are required to access a node. This hook is invoked after node access modules
|
||||
* returned their requirements via hook_node_access_records(); doing so allows
|
||||
* modules to modify the $grants array by reference before it is stored, so
|
||||
* custom or advanced business logic can be applied.
|
||||
*
|
||||
* Upon viewing, editing or deleting a node, hook_node_grants() builds a
|
||||
* permissions array that is compared against the stored access records. The
|
||||
* user must have one or more matching permissions in order to complete the
|
||||
* requested operation.
|
||||
*
|
||||
* A module may deny all access to a node by setting $grants to an empty array.
|
||||
*
|
||||
* @param array $grants
|
||||
* The $grants array returned by hook_node_access_records().
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node for which the grants were acquired.
|
||||
*
|
||||
* The preferred use of this hook is in a module that bridges multiple node
|
||||
* access modules with a configurable behavior, as shown in the example with the
|
||||
* 'is_preview' field.
|
||||
*
|
||||
* @see hook_node_access_records()
|
||||
* @see hook_node_grants()
|
||||
* @see hook_node_grants_alter()
|
||||
* @ingroup node_access
|
||||
*/
|
||||
function hook_node_access_records_alter(&$grants, Drupal\node\NodeInterface $node) {
|
||||
// Our module allows editors to mark specific articles with the 'is_preview'
|
||||
// field. If the node being saved has a TRUE value for that field, then only
|
||||
// our grants are retained, and other grants are removed. Doing so ensures
|
||||
// that our rules are enforced no matter what priority other grants are given.
|
||||
if ($node->is_preview) {
|
||||
// Our module grants are set in $grants['example'].
|
||||
$temp = $grants['example'];
|
||||
// Now remove all module grants but our own.
|
||||
$grants = array('example' => $temp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter user access rules when trying to view, edit or delete a node.
|
||||
*
|
||||
* Node access modules establish rules for user access to content.
|
||||
* hook_node_grants() defines permissions for a user to view, edit or delete
|
||||
* nodes by building a $grants array that indicates the permissions assigned to
|
||||
* the user by each node access module. This hook is called to allow modules to
|
||||
* modify the $grants array by reference, so the interaction of multiple node
|
||||
* access modules can be altered or advanced business logic can be applied.
|
||||
*
|
||||
* The resulting grants are then checked against the records stored in the
|
||||
* {node_access} table to determine if the operation may be completed.
|
||||
*
|
||||
* A module may deny all access to a user by setting $grants to an empty array.
|
||||
*
|
||||
* Developers may use this hook to either add additional grants to a user or to
|
||||
* remove existing grants. These rules are typically based on either the
|
||||
* permissions assigned to a user role, or specific attributes of a user
|
||||
* account.
|
||||
*
|
||||
* @param array $grants
|
||||
* The $grants array returned by hook_node_grants().
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The account requesting access to content.
|
||||
* @param string $op
|
||||
* The operation being performed, 'view', 'update' or 'delete'.
|
||||
*
|
||||
* @see hook_node_grants()
|
||||
* @see hook_node_access_records()
|
||||
* @see hook_node_access_records_alter()
|
||||
* @ingroup node_access
|
||||
*/
|
||||
function hook_node_grants_alter(&$grants, \Drupal\Core\Session\AccountInterface $account, $op) {
|
||||
// Our sample module never allows certain roles to edit or delete
|
||||
// content. Since some other node access modules might allow this
|
||||
// permission, we expressly remove it by returning an empty $grants
|
||||
// array for roles specified in our variable setting.
|
||||
|
||||
// Get our list of banned roles.
|
||||
$restricted = \Drupal::config('example.settings')->get('restricted_roles');
|
||||
|
||||
if ($op != 'view' && !empty($restricted)) {
|
||||
// Now check the roles for this account against the restrictions.
|
||||
foreach ($account->getRoles() as $rid) {
|
||||
if (in_array($rid, $restricted)) {
|
||||
$grants = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls access to a node.
|
||||
*
|
||||
* Modules may implement this hook if they want to have a say in whether or not
|
||||
* a given user has access to perform a given operation on a node.
|
||||
*
|
||||
* The administrative account (user ID #1) always passes any access check, so
|
||||
* this hook is not called in that case. Users with the "bypass node access"
|
||||
* permission may always view and edit content through the administrative
|
||||
* interface.
|
||||
*
|
||||
* Note that not all modules will want to influence access on all node types. If
|
||||
* your module does not want to explicitly allow or forbid access, return an
|
||||
* AccessResultInterface object with neither isAllowed() nor isForbidden()
|
||||
* equaling TRUE. Blindly returning an object with isForbidden() equaling TRUE
|
||||
* will break other node access modules.
|
||||
*
|
||||
* Also note that this function isn't called for node listings (e.g., RSS feeds,
|
||||
* the default home page at path 'node', a recent content block, etc.) See
|
||||
* @link node_access Node access rights @endlink for a full explanation.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface|string $node
|
||||
* Either a node entity or the machine name of the content type on which to
|
||||
* perform the access check.
|
||||
* @param string $op
|
||||
* The operation to be performed. Possible values:
|
||||
* - "create"
|
||||
* - "delete"
|
||||
* - "update"
|
||||
* - "view"
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user object to perform the access check operation on.
|
||||
* @param string $langcode
|
||||
* The language code to perform the access check operation on.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*
|
||||
* @ingroup node_access
|
||||
*/
|
||||
function hook_node_access(\Drupal\node\NodeInterface $node, $op, \Drupal\Core\Session\AccountInterface $account, $langcode) {
|
||||
$type = $node->bundle();
|
||||
|
||||
switch ($op) {
|
||||
case 'create':
|
||||
return AccessResult::allowedIfHasPermission($account, 'create ' . $type . ' content');
|
||||
|
||||
case 'update':
|
||||
if ($account->hasPermission('edit any ' . $type . ' content', $account)) {
|
||||
return AccessResult::allowed()->cachePerPermissions();
|
||||
}
|
||||
else {
|
||||
return AccessResult::allowedIf($account->hasPermission('edit own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($node);
|
||||
}
|
||||
|
||||
case 'delete':
|
||||
if ($account->hasPermission('delete any ' . $type . ' content', $account)) {
|
||||
return AccessResult::allowed()->cachePerPermissions();
|
||||
}
|
||||
else {
|
||||
return AccessResult::allowedIf($account->hasPermission('delete own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($node);
|
||||
}
|
||||
|
||||
default:
|
||||
// No opinion.
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Act on a node being displayed as a search result.
|
||||
*
|
||||
* This hook is invoked from the node search plugin during search execution,
|
||||
* after loading and rendering the node.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node being displayed in a search result.
|
||||
* @param string $langcode
|
||||
* Language code of result being displayed.
|
||||
*
|
||||
* @return array
|
||||
* Extra information to be displayed with search result. This information
|
||||
* should be presented as an associative array. It will be concatenated with
|
||||
* the post information (last updated, author) in the default search result
|
||||
* theming.
|
||||
*
|
||||
* @see template_preprocess_search_result()
|
||||
* @see search-result.html.twig
|
||||
*
|
||||
* @ingroup entity_crud
|
||||
*/
|
||||
function hook_node_search_result(\Drupal\node\NodeInterface $node, $langcode) {
|
||||
$rating = db_query('SELECT SUM(points) FROM {my_rating} WHERE nid = :nid', array('nid' => $node->id()))->fetchField();
|
||||
return array('rating' => \Drupal::translation()->formatPlural($rating, '1 point', '@count points'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Act on a node being indexed for searching.
|
||||
*
|
||||
* This hook is invoked during search indexing, after loading, and after the
|
||||
* result of rendering is added as $node->rendered to the node object.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node being indexed.
|
||||
* @param string $langcode
|
||||
* Language code of the variant of the node being indexed.
|
||||
*
|
||||
* @return string
|
||||
* Additional node information to be indexed.
|
||||
*
|
||||
* @ingroup entity_crud
|
||||
*/
|
||||
function hook_node_update_index(\Drupal\node\NodeInterface $node, $langcode) {
|
||||
$text = '';
|
||||
$ratings = db_query('SELECT title, description FROM {my_ratings} WHERE nid = :nid', array(':nid' => $node->id()));
|
||||
foreach ($ratings as $rating) {
|
||||
$text .= '<h2>' . SafeMarkup::checkPlain($rating->title) . '</h2>' . Xss::filter($rating->description);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide additional methods of scoring for core search results for nodes.
|
||||
*
|
||||
* A node's search score is used to rank it among other nodes matched by the
|
||||
* search, with the highest-ranked nodes appearing first in the search listing.
|
||||
*
|
||||
* For example, a module allowing users to vote on content could expose an
|
||||
* option to allow search results' rankings to be influenced by the average
|
||||
* voting score of a node.
|
||||
*
|
||||
* All scoring mechanisms are provided as options to site administrators, and
|
||||
* may be tweaked based on individual sites or disabled altogether if they do
|
||||
* not make sense. Individual scoring mechanisms, if enabled, are assigned a
|
||||
* weight from 1 to 10. The weight represents the factor of magnification of
|
||||
* the ranking mechanism, with higher-weighted ranking mechanisms having more
|
||||
* influence. In order for the weight system to work, each scoring mechanism
|
||||
* must return a value between 0 and 1 for every node. That value is then
|
||||
* multiplied by the administrator-assigned weight for the ranking mechanism,
|
||||
* and then the weighted scores from all ranking mechanisms are added, which
|
||||
* brings about the same result as a weighted average.
|
||||
*
|
||||
* @return array
|
||||
* An associative array of ranking data. The keys should be strings,
|
||||
* corresponding to the internal name of the ranking mechanism, such as
|
||||
* 'recent', or 'comments'. The values should be arrays themselves, with the
|
||||
* following keys available:
|
||||
* - title: (required) The human readable name of the ranking mechanism.
|
||||
* - join: (optional) An array with information to join any additional
|
||||
* necessary table. This is not necessary if the table required is already
|
||||
* joined to by the base query, such as for the {node} table. Other tables
|
||||
* should use the full table name as an alias to avoid naming collisions.
|
||||
* - score: (required) The part of a query string to calculate the score for
|
||||
* the ranking mechanism based on values in the database. This does not need
|
||||
* to be wrapped in parentheses, as it will be done automatically; it also
|
||||
* does not need to take the weighted system into account, as it will be
|
||||
* done automatically. It does, however, need to calculate a decimal between
|
||||
* 0 and 1; be careful not to cast the entire score to an integer by
|
||||
* inadvertently introducing a variable argument.
|
||||
* - arguments: (optional) If any arguments are required for the score, they
|
||||
* can be specified in an array here.
|
||||
*
|
||||
* @ingroup entity_crud
|
||||
*/
|
||||
function hook_ranking() {
|
||||
// If voting is disabled, we can avoid returning the array, no hard feelings.
|
||||
if (\Drupal::config('vote.settings')->get('node_enabled')) {
|
||||
return array(
|
||||
'vote_average' => array(
|
||||
'title' => t('Average vote'),
|
||||
// Note that we use i.sid, the search index's search item id, rather than
|
||||
// n.nid.
|
||||
'join' => array(
|
||||
'type' => 'LEFT',
|
||||
'table' => 'vote_node_data',
|
||||
'alias' => 'vote_node_data',
|
||||
'on' => 'vote_node_data.nid = i.sid',
|
||||
),
|
||||
// The highest possible score should be 1, and the lowest possible score,
|
||||
// always 0, should be 0.
|
||||
'score' => 'vote_node_data.average / CAST(%f AS DECIMAL)',
|
||||
// Pass in the highest possible voting score as a decimal argument.
|
||||
'arguments' => array(\Drupal::config('vote.settings')->get('score_max')),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the links of a node.
|
||||
*
|
||||
* @param array &$links
|
||||
* A renderable array representing the node links.
|
||||
* @param \Drupal\node\NodeInterface $entity
|
||||
* The node being rendered.
|
||||
* @param array &$context
|
||||
* Various aspects of the context in which the node links are going to be
|
||||
* displayed, with the following keys:
|
||||
* - 'view_mode': the view mode in which the node is being viewed
|
||||
* - 'langcode': the language in which the node is being viewed
|
||||
*
|
||||
* @see \Drupal\node\NodeViewBuilder::renderLinks()
|
||||
* @see \Drupal\node\NodeViewBuilder::buildLinks()
|
||||
* @see entity_crud
|
||||
*/
|
||||
function hook_node_links_alter(array &$links, NodeInterface $entity, array &$context) {
|
||||
$links['mymodule'] = array(
|
||||
'#theme' => 'links__node__mymodule',
|
||||
'#attributes' => array('class' => array('links', 'inline')),
|
||||
'#links' => array(
|
||||
'node-report' => array(
|
||||
'title' => t('Report'),
|
||||
'href' => "node/{$entity->id()}/report",
|
||||
'query' => array('token' => \Drupal::getContainer()->get('csrf_token')->get("node/{$entity->id()}/report")),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
10
core/modules/node/node.info.yml
Normal file
10
core/modules/node/node.info.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
name: Node
|
||||
type: module
|
||||
description: 'Allows content to be submitted to the site and displayed on pages.'
|
||||
package: Core
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
configure: entity.node_type.collection
|
||||
dependencies:
|
||||
- text
|
||||
- entity_reference
|
155
core/modules/node/node.install
Normal file
155
core/modules/node/node.install
Normal file
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the node module.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Uuid\Uuid;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_requirements().
|
||||
*/
|
||||
function node_requirements($phase) {
|
||||
$requirements = array();
|
||||
if ($phase === 'runtime') {
|
||||
// Only show rebuild button if there are either 0, or 2 or more, rows
|
||||
// in the {node_access} table, or if there are modules that
|
||||
// implement hook_node_grants().
|
||||
$grant_count = \Drupal::entityManager()->getAccessControlHandler('node')->countGrants();
|
||||
if ($grant_count != 1 || count(\Drupal::moduleHandler()->getImplementations('node_grants')) > 0) {
|
||||
$value = \Drupal::translation()->formatPlural($grant_count, 'One permission in use', '@count permissions in use', array('@count' => $grant_count));
|
||||
}
|
||||
else {
|
||||
$value = t('Disabled');
|
||||
}
|
||||
|
||||
$requirements['node_access'] = array(
|
||||
'title' => t('Node Access Permissions'),
|
||||
'value' => $value,
|
||||
'description' => t('If the site is experiencing problems with permissions to content, you may have to rebuild the permissions cache. Rebuilding will remove all privileges to content and replace them with permissions based on the current modules and settings. Rebuilding may take some time if there is a lot of content or complex permission settings. After rebuilding has completed, content will automatically use the new permissions. <a href="@rebuild">Rebuild permissions</a>', array(
|
||||
'@rebuild' => \Drupal::url('node.configure_rebuild_confirm'),
|
||||
)),
|
||||
);
|
||||
}
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function node_schema() {
|
||||
$schema['node_access'] = array(
|
||||
'description' => 'Identifies which realm/grant pairs a user must possess in order to view, update, or delete specific nodes.',
|
||||
'fields' => array(
|
||||
'nid' => array(
|
||||
'description' => 'The {node}.nid this record affects.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'langcode' => array(
|
||||
'description' => 'The {language}.langcode of this node.',
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 12,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'fallback' => array(
|
||||
'description' => 'Boolean indicating whether this record should be used as a fallback if a language condition is not provided.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
),
|
||||
'gid' => array(
|
||||
'description' => "The grant ID a user must possess in the specified realm to gain this row's privileges on the node.",
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'realm' => array(
|
||||
'description' => 'The realm in which the user must possess the grant ID. Each node access node can define one or more realms.',
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'grant_view' => array(
|
||||
'description' => 'Boolean indicating whether a user with the realm/grant pair can view this node.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'tiny',
|
||||
),
|
||||
'grant_update' => array(
|
||||
'description' => 'Boolean indicating whether a user with the realm/grant pair can edit this node.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'tiny',
|
||||
),
|
||||
'grant_delete' => array(
|
||||
'description' => 'Boolean indicating whether a user with the realm/grant pair can delete this node.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'size' => 'tiny',
|
||||
),
|
||||
),
|
||||
'primary key' => array('nid', 'gid', 'realm', 'langcode'),
|
||||
'foreign keys' => array(
|
||||
'affected_node' => array(
|
||||
'table' => 'node',
|
||||
'columns' => array('nid' => 'nid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function node_install() {
|
||||
// Enable default permissions for system roles.
|
||||
// IMPORTANT: Modules SHOULD NOT automatically grant any user role access
|
||||
// permissions in hook_install().
|
||||
// However, the 'access content' permission is a very special case, since
|
||||
// there is hardly a point in installing the Node module without granting
|
||||
// these permissions. Doing so also allows tests to continue to operate as
|
||||
// expected without first having to manually grant these default permissions.
|
||||
if (\Drupal::moduleHandler()->moduleExists('user')) {
|
||||
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, array('access content'));
|
||||
user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, array('access content'));
|
||||
}
|
||||
|
||||
// Populate the node access table.
|
||||
db_insert('node_access')
|
||||
->fields(array(
|
||||
'nid' => 0,
|
||||
'gid' => 0,
|
||||
'realm' => 'all',
|
||||
'grant_view' => 1,
|
||||
'grant_update' => 0,
|
||||
'grant_delete' => 0,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function node_uninstall() {
|
||||
// Delete remaining general module variables.
|
||||
\Drupal::state()->delete('node.node_access_needs_rebuild');
|
||||
}
|
74
core/modules/node/node.js
Normal file
74
core/modules/node/node.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* @file
|
||||
* Defines Javascript behaviors for the node module.
|
||||
*/
|
||||
|
||||
(function ($, Drupal, drupalSettings) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.nodeDetailsSummaries = {
|
||||
attach: function (context) {
|
||||
var $context = $(context);
|
||||
$context.find('.node-form-revision-information').drupalSetSummary(function (context) {
|
||||
var $revisionContext = $(context);
|
||||
var revisionCheckbox = $revisionContext.find('.form-item-revision input');
|
||||
|
||||
// Return 'New revision' if the 'Create new revision' checkbox is checked,
|
||||
// or if the checkbox doesn't exist, but the revision log does. For users
|
||||
// without the "Administer content" permission the checkbox won't appear,
|
||||
// but the revision log will if the content type is set to auto-revision.
|
||||
if (revisionCheckbox.is(':checked') || (!revisionCheckbox.length && $revisionContext.find('.form-item-revision-log textarea').length)) {
|
||||
return Drupal.t('New revision');
|
||||
}
|
||||
|
||||
return Drupal.t('No revision');
|
||||
});
|
||||
|
||||
$context.find('.node-form-author').drupalSetSummary(function (context) {
|
||||
var $authorContext = $(context);
|
||||
var name = $authorContext.find('.field-name-uid input').val();
|
||||
var date = $authorContext.find('.field-name-created input').val();
|
||||
return date ?
|
||||
Drupal.t('By @name on @date', {'@name': name, '@date': date}) :
|
||||
Drupal.t('By @name', {'@name': name});
|
||||
});
|
||||
|
||||
$context.find('.node-form-options').drupalSetSummary(function (context) {
|
||||
var $optionsContext = $(context);
|
||||
var vals = [];
|
||||
|
||||
if ($optionsContext.find('input').is(':checked')) {
|
||||
$optionsContext.find('input:checked').next('label').each(function () {
|
||||
vals.push(Drupal.checkPlain($.trim($(this).text())));
|
||||
});
|
||||
return vals.join(', ');
|
||||
}
|
||||
else {
|
||||
return Drupal.t('Not promoted');
|
||||
}
|
||||
});
|
||||
|
||||
$context.find('fieldset.node-translation-options').drupalSetSummary(function (context) {
|
||||
var $translationContext = $(context);
|
||||
var translate;
|
||||
var $checkbox = $translationContext.find('.form-item-translation-translate input');
|
||||
|
||||
if ($checkbox.size()) {
|
||||
translate = $checkbox.is(':checked') ? Drupal.t('Needs to be updated') : Drupal.t('Does not need to be updated');
|
||||
}
|
||||
else {
|
||||
$checkbox = $translationContext.find('.form-item-translation-retranslate input');
|
||||
translate = $checkbox.is(':checked') ? Drupal.t('Flag other translations as outdated') : Drupal.t('Do not flag other translations as outdated');
|
||||
}
|
||||
|
||||
return translate;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, Drupal, drupalSettings);
|
46
core/modules/node/node.libraries.yml
Normal file
46
core/modules/node/node.libraries.yml
Normal file
|
@ -0,0 +1,46 @@
|
|||
drupal.node:
|
||||
version: VERSION
|
||||
css:
|
||||
layout:
|
||||
css/node.module.css: {}
|
||||
js:
|
||||
node.js: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
- core/drupalSettings
|
||||
- core/drupal.form
|
||||
|
||||
drupal.node.preview:
|
||||
version: VERSION
|
||||
css:
|
||||
theme:
|
||||
css/node.preview.css: {}
|
||||
js:
|
||||
node.preview.js: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/jquery.once
|
||||
- core/drupal
|
||||
- core/drupal.form
|
||||
|
||||
drupal.content_types:
|
||||
version: VERSION
|
||||
js:
|
||||
content_types.js: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
- core/drupal.form
|
||||
|
||||
form:
|
||||
version: VERSION
|
||||
css:
|
||||
layout:
|
||||
css/node.module.css: {}
|
||||
|
||||
drupal.node.admin:
|
||||
version: VERSION
|
||||
css:
|
||||
theme:
|
||||
css/node.admin.css: {}
|
10
core/modules/node/node.links.action.yml
Normal file
10
core/modules/node/node.links.action.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
node.type_add:
|
||||
route_name: node.type_add
|
||||
title: 'Add content type'
|
||||
appears_on:
|
||||
- entity.node_type.collection
|
||||
node.add_page:
|
||||
route_name: node.add_page
|
||||
title: 'Add content'
|
||||
appears_on:
|
||||
- system.admin_content
|
10
core/modules/node/node.links.contextual.yml
Normal file
10
core/modules/node/node.links.contextual.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
entity.node.edit_form:
|
||||
route_name: entity.node.edit_form
|
||||
group: node
|
||||
title: Edit
|
||||
|
||||
entity.node.delete_form:
|
||||
route_name: entity.node.delete_form
|
||||
group: node
|
||||
title: Delete
|
||||
weight: 10
|
8
core/modules/node/node.links.menu.yml
Normal file
8
core/modules/node/node.links.menu.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
entity.node_type.collection:
|
||||
title: 'Content types'
|
||||
parent: system.admin_structure
|
||||
description: 'Manage content types, including default status, front page promotion, comment settings, etc.'
|
||||
route_name: entity.node_type.collection
|
||||
node.add_page:
|
||||
title: 'Add content'
|
||||
route_name: node.add_page
|
26
core/modules/node/node.links.task.yml
Normal file
26
core/modules/node/node.links.task.yml
Normal file
|
@ -0,0 +1,26 @@
|
|||
entity.node.canonical:
|
||||
route_name: entity.node.canonical
|
||||
base_route: entity.node.canonical
|
||||
title: 'View'
|
||||
entity.node.edit_form:
|
||||
route_name: entity.node.edit_form
|
||||
base_route: entity.node.canonical
|
||||
title: Edit
|
||||
entity.node.delete_form:
|
||||
route_name: entity.node.delete_form
|
||||
base_route: entity.node.canonical
|
||||
title: Delete
|
||||
weight: 10
|
||||
entity.node.version_history:
|
||||
route_name: entity.node.version_history
|
||||
base_route: entity.node.canonical
|
||||
title: 'Revisions'
|
||||
weight: 20
|
||||
entity.node_type.edit_form:
|
||||
title: 'Edit'
|
||||
route_name: entity.node_type.edit_form
|
||||
base_route: entity.node_type.edit_form
|
||||
entity.node_type.collection:
|
||||
title: List
|
||||
route_name: entity.node_type.collection
|
||||
base_route: entity.node_type.collection
|
1355
core/modules/node/node.module
Normal file
1355
core/modules/node/node.module
Normal file
File diff suppressed because it is too large
Load diff
29
core/modules/node/node.permissions.yml
Normal file
29
core/modules/node/node.permissions.yml
Normal file
|
@ -0,0 +1,29 @@
|
|||
bypass node access:
|
||||
title: 'Bypass content access control'
|
||||
description: 'View, edit and delete all content regardless of permission restrictions.'
|
||||
restrict access: true
|
||||
administer content types:
|
||||
title: 'Administer content types'
|
||||
description: 'Promote, change ownership, edit revisions, and perform other tasks across all content types.'
|
||||
restrict access: true
|
||||
administer nodes:
|
||||
title: 'Administer content'
|
||||
restrict access: true
|
||||
access content overview:
|
||||
title: 'Access the Content overview page'
|
||||
description: 'Get an overview of all content.'
|
||||
access content:
|
||||
title: 'View published content'
|
||||
view own unpublished content:
|
||||
title: 'View own unpublished content'
|
||||
view all revisions:
|
||||
title: 'View all revisions'
|
||||
revert all revisions:
|
||||
title: 'Revert all revisions'
|
||||
description: 'Role requires permission <em>view revisions</em> and <em>edit rights</em> for nodes in question or <em>administer nodes</em>.'
|
||||
delete all revisions:
|
||||
title: 'Delete all revisions'
|
||||
description: 'Role requires permission to <em>view revisions</em> and <em>delete rights</em> for nodes in question or <em>administer nodes</em>.'
|
||||
|
||||
permission_callbacks:
|
||||
- \Drupal\node\NodePermissions::nodeTypePermissions
|
87
core/modules/node/node.preview.js
Normal file
87
core/modules/node/node.preview.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* @file
|
||||
* Preview behaviors.
|
||||
*/
|
||||
|
||||
(function ($, Drupal) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Disabling all links (except local fragment identifiers such as href="#frag")
|
||||
* in node previews to prevent users from leaving the page.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.nodePreviewDestroyLinks = {
|
||||
attach: function (context) {
|
||||
|
||||
function clickPreviewModal(event) {
|
||||
// Only confirm leaving previews when left-clicking and user is not
|
||||
// pressing the ALT, CTRL, META (Command key on the Macintosh keyboard)
|
||||
// or SHIFT key.
|
||||
if (event.button === 0 && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
var $previewDialog = $('<div>' + Drupal.theme('nodePreviewModal') + '</div>').appendTo('body');
|
||||
Drupal.dialog($previewDialog, {
|
||||
title: Drupal.t('Leave preview?'),
|
||||
buttons: [
|
||||
{
|
||||
text: Drupal.t('Cancel'),
|
||||
click: function () {
|
||||
$(this).dialog('close');
|
||||
}
|
||||
}, {
|
||||
text: Drupal.t('Leave preview'),
|
||||
click: function () {
|
||||
window.top.location.href = event.target.href;
|
||||
}
|
||||
}
|
||||
]
|
||||
}).showModal();
|
||||
}
|
||||
}
|
||||
|
||||
var $preview = $(context).find('.content').once('node-preview');
|
||||
if ($(context).find('.node-preview-container').length) {
|
||||
$preview.on('click.preview', 'a:not([href^=#], #edit-backlink, #toolbar-administration a)', clickPreviewModal);
|
||||
}
|
||||
},
|
||||
detach: function (context, settings, trigger) {
|
||||
if (trigger === 'unload') {
|
||||
var $preview = $(context).find('.content').removeOnce('node-preview');
|
||||
if ($preview.length) {
|
||||
$preview.off('click.preview');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Switch view mode.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.nodePreviewSwitchViewMode = {
|
||||
attach: function (context) {
|
||||
var $autosubmit = $(context).find('[data-drupal-autosubmit]').once('autosubmit');
|
||||
if ($autosubmit.length) {
|
||||
$autosubmit.on('formUpdated.preview', function () {
|
||||
$(this.form).trigger('submit');
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
Drupal.theme.nodePreviewModal = function () {
|
||||
return '<p>' +
|
||||
Drupal.t('Leaving the preview will cause unsaved changes to be lost. Are you sure you want to leave the preview?') +
|
||||
'</p><small class="description">' +
|
||||
Drupal.t('CTRL+Left click will prevent this dialog from showing and proceed to the clicked link.') + '</small>';
|
||||
};
|
||||
|
||||
})(jQuery, Drupal);
|
118
core/modules/node/node.routing.yml
Normal file
118
core/modules/node/node.routing.yml
Normal file
|
@ -0,0 +1,118 @@
|
|||
node.multiple_delete_confirm:
|
||||
path: '/admin/content/node/delete'
|
||||
defaults:
|
||||
_form: '\Drupal\node\Form\DeleteMultiple'
|
||||
requirements:
|
||||
_permission: 'administer nodes'
|
||||
|
||||
node.add_page:
|
||||
path: '/node/add'
|
||||
defaults:
|
||||
_title: 'Add content'
|
||||
_controller: '\Drupal\node\Controller\NodeController::addPage'
|
||||
options:
|
||||
_node_operation_route: TRUE
|
||||
requirements:
|
||||
_node_add_access: 'node'
|
||||
|
||||
node.add:
|
||||
path: '/node/add/{node_type}'
|
||||
defaults:
|
||||
_controller: '\Drupal\node\Controller\NodeController::add'
|
||||
_title_callback: '\Drupal\node\Controller\NodeController::addPageTitle'
|
||||
requirements:
|
||||
_node_add_access: 'node:{node_type}'
|
||||
options:
|
||||
_node_operation_route: TRUE
|
||||
parameters:
|
||||
node_type:
|
||||
with_config_overrides: TRUE
|
||||
|
||||
entity.node.preview:
|
||||
path: '/node/preview/{node_preview}/{view_mode_id}'
|
||||
defaults:
|
||||
_controller: '\Drupal\node\Controller\NodePreviewController::view'
|
||||
_title_callback: '\Drupal\node\Controller\NodePreviewController::title'
|
||||
requirements:
|
||||
_node_preview_access: '{node_preview}'
|
||||
options:
|
||||
parameters:
|
||||
node_preview:
|
||||
type: 'node_preview'
|
||||
|
||||
entity.node.version_history:
|
||||
path: '/node/{node}/revisions'
|
||||
defaults:
|
||||
_title: 'Revisions'
|
||||
_controller: '\Drupal\node\Controller\NodeController::revisionOverview'
|
||||
requirements:
|
||||
_access_node_revision: 'view'
|
||||
options:
|
||||
_node_operation_route: TRUE
|
||||
|
||||
entity.node.revision:
|
||||
path: '/node/{node}/revisions/{node_revision}/view'
|
||||
defaults:
|
||||
_controller: '\Drupal\node\Controller\NodeController::revisionShow'
|
||||
_title_callback: '\Drupal\node\Controller\NodeController::revisionPageTitle'
|
||||
requirements:
|
||||
_access_node_revision: 'view'
|
||||
|
||||
node.revision_revert_confirm:
|
||||
path: '/node/{node}/revisions/{node_revision}/revert'
|
||||
defaults:
|
||||
_form: '\Drupal\node\Form\NodeRevisionRevertForm'
|
||||
_title: 'Revert to earlier revision'
|
||||
requirements:
|
||||
_access_node_revision: 'update'
|
||||
options:
|
||||
_node_operation_route: TRUE
|
||||
|
||||
node.revision_delete_confirm:
|
||||
path: '/node/{node}/revisions/{node_revision}/delete'
|
||||
defaults:
|
||||
_form: '\Drupal\node\Form\NodeRevisionDeleteForm'
|
||||
_title: 'Delete earlier revision'
|
||||
requirements:
|
||||
_access_node_revision: 'delete'
|
||||
options:
|
||||
_node_operation_route: TRUE
|
||||
|
||||
entity.node_type.collection:
|
||||
path: '/admin/structure/types'
|
||||
defaults:
|
||||
_controller: '\Drupal\Core\Entity\Controller\EntityListController::listing'
|
||||
entity_type: 'node_type'
|
||||
_title: 'Content types'
|
||||
requirements:
|
||||
_permission: 'administer content types'
|
||||
|
||||
node.type_add:
|
||||
path: '/admin/structure/types/add'
|
||||
defaults:
|
||||
_entity_form: 'node_type.add'
|
||||
_title: 'Add content type'
|
||||
requirements:
|
||||
_permission: 'administer content types'
|
||||
|
||||
entity.node_type.edit_form:
|
||||
path: '/admin/structure/types/manage/{node_type}'
|
||||
defaults:
|
||||
_entity_form: 'node_type.edit'
|
||||
requirements:
|
||||
_permission: 'administer content types'
|
||||
|
||||
entity.node_type.delete_form:
|
||||
path: '/admin/structure/types/manage/{node_type}/delete'
|
||||
defaults:
|
||||
_entity_form: 'node_type.delete'
|
||||
_title: 'Delete'
|
||||
requirements:
|
||||
_entity_access: 'node_type.delete'
|
||||
|
||||
node.configure_rebuild_confirm:
|
||||
path: '/admin/reports/status/rebuild'
|
||||
defaults:
|
||||
_form: 'Drupal\node\Form\RebuildPermissionsForm'
|
||||
requirements:
|
||||
_permission: 'access administration pages'
|
47
core/modules/node/node.services.yml
Normal file
47
core/modules/node/node.services.yml
Normal file
|
@ -0,0 +1,47 @@
|
|||
services:
|
||||
node.route_subscriber:
|
||||
class: Drupal\node\Routing\RouteSubscriber
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
node.grant_storage:
|
||||
class: Drupal\node\NodeGrantDatabaseStorage
|
||||
arguments: ['@database', '@module_handler', '@language_manager']
|
||||
tags:
|
||||
- { name: backend_overridable }
|
||||
access_check.node.revision:
|
||||
class: Drupal\node\Access\NodeRevisionAccessCheck
|
||||
arguments: ['@entity.manager']
|
||||
tags:
|
||||
- { name: access_check, applies_to: _access_node_revision }
|
||||
access_check.node.add:
|
||||
class: Drupal\node\Access\NodeAddAccessCheck
|
||||
arguments: ['@entity.manager']
|
||||
tags:
|
||||
- { name: access_check, applies_to: _node_add_access }
|
||||
access_check.node.preview:
|
||||
class: Drupal\node\Access\NodePreviewAccessCheck
|
||||
arguments: ['@entity.manager']
|
||||
tags:
|
||||
- { name: access_check, applies_to: _node_preview_access }
|
||||
node.admin_path.route_subscriber:
|
||||
class: Drupal\node\EventSubscriber\NodeAdminRouteSubscriber
|
||||
arguments: ['@config.factory']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
node_preview:
|
||||
class: Drupal\node\ParamConverter\NodePreviewConverter
|
||||
arguments: ['@user.private_tempstore']
|
||||
tags:
|
||||
- { name: paramconverter }
|
||||
lazy: true
|
||||
node.page_cache_response_policy.deny_node_preview:
|
||||
class: Drupal\node\PageCache\DenyNodePreview
|
||||
arguments: ['@current_route_match']
|
||||
public: false
|
||||
tags:
|
||||
- { name: page_cache_response_policy }
|
||||
cache_context.user.node_grants:
|
||||
class: Drupal\node\Cache\NodeAccessGrantsCacheContext
|
||||
arguments: ['@current_user']
|
||||
tags:
|
||||
- { name: cache.context }
|
206
core/modules/node/node.tokens.inc
Normal file
206
core/modules/node/node.tokens.inc
Normal file
|
@ -0,0 +1,206 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Builds placeholder replacement tokens for node-related data.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Implements hook_token_info().
|
||||
*/
|
||||
function node_token_info() {
|
||||
$type = array(
|
||||
'name' => t('Nodes'),
|
||||
'description' => t('Tokens related to individual content items, or "nodes".'),
|
||||
'needs-data' => 'node',
|
||||
);
|
||||
|
||||
// Core tokens for nodes.
|
||||
$node['nid'] = array(
|
||||
'name' => t("Content ID"),
|
||||
'description' => t('The unique ID of the content item, or "node".'),
|
||||
);
|
||||
$node['vid'] = array(
|
||||
'name' => t("Revision ID"),
|
||||
'description' => t("The unique ID of the node's latest revision."),
|
||||
);
|
||||
$node['type'] = array(
|
||||
'name' => t("Content type"),
|
||||
);
|
||||
$node['type-name'] = array(
|
||||
'name' => t("Content type name"),
|
||||
'description' => t("The human-readable name of the node type."),
|
||||
);
|
||||
$node['title'] = array(
|
||||
'name' => t("Title"),
|
||||
);
|
||||
$node['body'] = array(
|
||||
'name' => t("Body"),
|
||||
'description' => t("The main body text of the node."),
|
||||
);
|
||||
$node['summary'] = array(
|
||||
'name' => t("Summary"),
|
||||
'description' => t("The summary of the node's main body text."),
|
||||
);
|
||||
$node['langcode'] = array(
|
||||
'name' => t('Language code'),
|
||||
'description' => t('The language code of the language the node is written in.'),
|
||||
);
|
||||
$node['url'] = array(
|
||||
'name' => t("URL"),
|
||||
'description' => t("The URL of the node."),
|
||||
);
|
||||
$node['edit-url'] = array(
|
||||
'name' => t("Edit URL"),
|
||||
'description' => t("The URL of the node's edit page."),
|
||||
);
|
||||
|
||||
// Chained tokens for nodes.
|
||||
$node['created'] = array(
|
||||
'name' => t("Date created"),
|
||||
'type' => 'date',
|
||||
);
|
||||
$node['changed'] = array(
|
||||
'name' => t("Date changed"),
|
||||
'description' => t("The date the node was most recently updated."),
|
||||
'type' => 'date',
|
||||
);
|
||||
$node['author'] = array(
|
||||
'name' => t("Author"),
|
||||
'type' => 'user',
|
||||
);
|
||||
|
||||
return array(
|
||||
'types' => array('node' => $type),
|
||||
'tokens' => array('node' => $node),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function node_tokens($type, $tokens, array $data = array(), array $options = array()) {
|
||||
$token_service = \Drupal::token();
|
||||
|
||||
$url_options = array('absolute' => TRUE);
|
||||
if (isset($options['langcode'])) {
|
||||
$url_options['language'] = \Drupal::languageManager()->getLanguage($options['langcode']);
|
||||
$langcode = $options['langcode'];
|
||||
}
|
||||
else {
|
||||
$langcode = LanguageInterface::LANGCODE_DEFAULT;
|
||||
}
|
||||
$sanitize = !empty($options['sanitize']);
|
||||
|
||||
$replacements = array();
|
||||
|
||||
if ($type == 'node' && !empty($data['node'])) {
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $data['node'];
|
||||
|
||||
foreach ($tokens as $name => $original) {
|
||||
switch ($name) {
|
||||
// Simple key values on the node.
|
||||
case 'nid':
|
||||
$replacements[$original] = $node->id();
|
||||
break;
|
||||
|
||||
case 'vid':
|
||||
$replacements[$original] = $node->getRevisionId();
|
||||
break;
|
||||
|
||||
case 'type':
|
||||
$replacements[$original] = $sanitize ? SafeMarkup::checkPlain($node->getType()) : $node->getType();
|
||||
break;
|
||||
|
||||
case 'type-name':
|
||||
$type_name = node_get_type_label($node);
|
||||
$replacements[$original] = $sanitize ? SafeMarkup::checkPlain($type_name) : $type_name;
|
||||
break;
|
||||
|
||||
case 'title':
|
||||
$replacements[$original] = $sanitize ? SafeMarkup::checkPlain($node->getTitle()) : $node->getTitle();
|
||||
break;
|
||||
|
||||
case 'body':
|
||||
case 'summary':
|
||||
$translation = \Drupal::entityManager()->getTranslationFromContext($node, $langcode, array('operation' => 'node_tokens'));
|
||||
if ($translation->hasField('body') && ($items = $translation->get('body')) && !$items->isEmpty()) {
|
||||
$item = $items[0];
|
||||
$field_definition = \Drupal::entityManager()->getFieldDefinitions('node', $node->bundle())['body'];
|
||||
// If the summary was requested and is not empty, use it.
|
||||
if ($name == 'summary' && !empty($item->summary)) {
|
||||
$output = $sanitize ? $item->summary_processed : $item->summary;
|
||||
}
|
||||
// Attempt to provide a suitable version of the 'body' field.
|
||||
else {
|
||||
$output = $sanitize ? $item->processed : $item->value;
|
||||
// A summary was requested.
|
||||
if ($name == 'summary') {
|
||||
// Generate an optionally trimmed summary of the body field.
|
||||
|
||||
// Get the 'trim_length' size used for the 'teaser' mode, if
|
||||
// present, or use the default trim_length size.
|
||||
$display_options = entity_get_display('node', $node->getType(), 'teaser')->getComponent('body');
|
||||
if (isset($display_options['settings']['trim_length'])) {
|
||||
$length = $display_options['settings']['trim_length'];
|
||||
}
|
||||
else {
|
||||
$settings = \Drupal::service('plugin.manager.field.formatter')->getDefaultSettings('text_summary_or_trimmed');
|
||||
$length = $settings['trim_length'];
|
||||
}
|
||||
|
||||
$output = text_summary($output, $item->format, $length);
|
||||
}
|
||||
}
|
||||
$replacements[$original] = $output;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'langcode':
|
||||
$replacements[$original] = $sanitize ? SafeMarkup::checkPlain($node->language()->getId()) : $node->language()->getId();
|
||||
break;
|
||||
|
||||
case 'url':
|
||||
$replacements[$original] = $node->url('canonical', $url_options);
|
||||
break;
|
||||
|
||||
case 'edit-url':
|
||||
$replacements[$original] = $node->url('edit-form', $url_options);
|
||||
break;
|
||||
|
||||
// Default values for the chained tokens handled below.
|
||||
case 'author':
|
||||
$account = $node->getOwner() ? $node->getOwner() : User::load(0);
|
||||
$replacements[$original] = $sanitize ? SafeMarkup::checkPlain($account->label()) : $account->label();
|
||||
break;
|
||||
|
||||
case 'created':
|
||||
$replacements[$original] = format_date($node->getCreatedTime(), 'medium', '', NULL, $langcode);
|
||||
break;
|
||||
|
||||
case 'changed':
|
||||
$replacements[$original] = format_date($node->getChangedTime(), 'medium', '', NULL, $langcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($author_tokens = $token_service->findWithPrefix($tokens, 'author')) {
|
||||
$replacements += $token_service->generate('user', $author_tokens, array('user' => $node->getOwner()), $options);
|
||||
}
|
||||
|
||||
if ($created_tokens = $token_service->findWithPrefix($tokens, 'created')) {
|
||||
$replacements += $token_service->generate('date', $created_tokens, array('date' => $node->getCreatedTime()), $options);
|
||||
}
|
||||
|
||||
if ($changed_tokens = $token_service->findWithPrefix($tokens, 'changed')) {
|
||||
$replacements += $token_service->generate('date', $changed_tokens, array('date' => $node->getChangedTime()), $options);
|
||||
}
|
||||
}
|
||||
|
||||
return $replacements;
|
||||
}
|
20
core/modules/node/node.views.inc
Normal file
20
core/modules/node/node.views.inc
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide views data for node.module.
|
||||
*/
|
||||
|
||||
use Drupal\views\Analyzer;
|
||||
use Drupal\views\ViewExecutable;
|
||||
|
||||
/**
|
||||
* Implements hook_views_wizard().
|
||||
*/
|
||||
function node_views_wizard() {
|
||||
// @todo: figure this piece out.
|
||||
if (\Drupal::moduleHandler()->moduleExists('statistics')) {
|
||||
$plugins['node']['available_sorts']['node_counter-totalcount:DESC'] = t('Number of hits');
|
||||
}
|
||||
|
||||
}
|
63
core/modules/node/node.views_execution.inc
Normal file
63
core/modules/node/node.views_execution.inc
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide views runtime hooks for node.module.
|
||||
*/
|
||||
|
||||
use Drupal\user\RoleInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Implements hook_views_query_substitutions().
|
||||
*/
|
||||
function node_views_query_substitutions(ViewExecutable $view) {
|
||||
$account = \Drupal::currentUser();
|
||||
return array(
|
||||
'***ADMINISTER_NODES***' => intval($account->hasPermission('administer nodes')),
|
||||
'***VIEW_OWN_UNPUBLISHED_NODES***' => intval($account->hasPermission('view own unpublished content')),
|
||||
'***BYPASS_NODE_ACCESS***' => intval($account->hasPermission('bypass node access')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_analyze().
|
||||
*/
|
||||
function node_views_analyze(ViewExecutable $view) {
|
||||
$ret = array();
|
||||
// Check for something other than the default display:
|
||||
if ($view->storage->get('base_table') == 'node') {
|
||||
foreach ($view->displayHandlers as $display) {
|
||||
if (!$display->isDefaulted('access') || !$display->isDefaulted('filters')) {
|
||||
// check for no access control
|
||||
$access = $display->getOption('access');
|
||||
if (empty($access['type']) || $access['type'] == 'none') {
|
||||
$anonymous_role = Role::load(RoleInterface::ANONYMOUS_ID);
|
||||
$anonymous_has_access = $anonymous_role && $anonymous_role->hasPermission('access content');
|
||||
$authenticated_role = Role::load(RoleInterface::AUTHENTICATED_ID);
|
||||
$authenticated_has_access = $authenticated_role && $authenticated_role->hasPermission('access content');
|
||||
if (!$anonymous_has_access || !$authenticated_has_access) {
|
||||
$ret[] = Analyzer::formatMessage(t('Some roles lack permission to access content, but display %display has no access control.', array('%display' => $display->display['display_title'])), 'warning');
|
||||
}
|
||||
$filters = $display->getOption('filters');
|
||||
foreach ($filters as $filter) {
|
||||
if ($filter['table'] == 'node' && ($filter['field'] == 'status' || $filter['field'] == 'status_extra')) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
$ret[] = Analyzer::formatMessage(t('Display %display has no access control but does not contain a filter for published nodes.', array('%display' => $display->display['display_title'])), 'warning');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($view->displayHandlers as $display) {
|
||||
if ($display->getPluginId() == 'page') {
|
||||
if ($display->getOption('path') == 'node/%') {
|
||||
$ret[] = Analyzer::formatMessage(t('Display %display has set node/% as path. This will not produce what you want. If you want to have multiple versions of the node view, use panels.', array('%display' => $display->display['display_title'])), 'warning');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
72
core/modules/node/src/Access/NodeAddAccessCheck.php
Normal file
72
core/modules/node/src/Access/NodeAddAccessCheck.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Access\NodeAddAccessCheck.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\node\NodeTypeInterface;
|
||||
|
||||
/**
|
||||
* Determines access to for node add pages.
|
||||
*
|
||||
* @ingroup node_access
|
||||
*/
|
||||
class NodeAddAccessCheck implements AccessInterface {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Constructs a EntityCreateAccessCheck object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager) {
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks access to the node add page for the node type.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The currently logged in account.
|
||||
* @param \Drupal\node\NodeTypeInterface $node_type
|
||||
* (optional) The node type. If not specified, access is allowed if there
|
||||
* exists at least one node type for which the user may create a node.
|
||||
*
|
||||
* @return string
|
||||
* A \Drupal\Core\Access\AccessInterface constant value.
|
||||
*/
|
||||
public function access(AccountInterface $account, NodeTypeInterface $node_type = NULL) {
|
||||
$access_control_handler = $this->entityManager->getAccessControlHandler('node');
|
||||
// If checking whether a node of a particular type may be created.
|
||||
if ($account->hasPermission('administer content types')) {
|
||||
return AccessResult::allowed()->cachePerPermissions();
|
||||
}
|
||||
if ($node_type) {
|
||||
return $access_control_handler->createAccess($node_type->id(), $account, [], TRUE);
|
||||
}
|
||||
// If checking whether a node of any type may be created.
|
||||
foreach ($this->entityManager->getStorage('node_type')->loadMultiple() as $node_type) {
|
||||
if (($access = $access_control_handler->createAccess($node_type->id(), $account, [], TRUE)) && $access->isAllowed()) {
|
||||
return $access;
|
||||
}
|
||||
}
|
||||
|
||||
// No opinion.
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
|
||||
}
|
60
core/modules/node/src/Access/NodePreviewAccessCheck.php
Normal file
60
core/modules/node/src/Access/NodePreviewAccessCheck.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Access\NodePreviewAccessCheck.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Access;
|
||||
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
/**
|
||||
* Determines access to node previews.
|
||||
*
|
||||
* @ingroup node_access
|
||||
*/
|
||||
class NodePreviewAccessCheck implements AccessInterface {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Constructs a EntityCreateAccessCheck object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager) {
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks access to the node preview page.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The currently logged in account.
|
||||
* @param \Drupal\node\NodeInterface $node_preview
|
||||
* The node that is being previewed.
|
||||
*
|
||||
* @return string
|
||||
* A \Drupal\Core\Access\AccessInterface constant value.
|
||||
*/
|
||||
public function access(AccountInterface $account, NodeInterface $node_preview) {
|
||||
if ($node_preview->isNew()) {
|
||||
$access_controller = $this->entityManager->getAccessControlHandler('node');
|
||||
return $access_controller->createAccess($node_preview->bundle(), $account, [], TRUE);
|
||||
}
|
||||
else {
|
||||
return $node_preview->access('update', $account, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
159
core/modules/node/src/Access/NodeRevisionAccessCheck.php
Normal file
159
core/modules/node/src/Access/NodeRevisionAccessCheck.php
Normal file
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Access\NodeRevisionAccessCheck.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Provides an access checker for node revisions.
|
||||
*
|
||||
* @ingroup node_access
|
||||
*/
|
||||
class NodeRevisionAccessCheck implements AccessInterface {
|
||||
|
||||
/**
|
||||
* The node storage.
|
||||
*
|
||||
* @var \Drupal\node\NodeStorageInterface
|
||||
*/
|
||||
protected $nodeStorage;
|
||||
|
||||
/**
|
||||
* The node access control handler.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityAccessControlHandlerInterface
|
||||
*/
|
||||
protected $nodeAccess;
|
||||
|
||||
/**
|
||||
* A static cache of access checks.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $access = array();
|
||||
|
||||
/**
|
||||
* Constructs a new NodeRevisionAccessCheck.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager) {
|
||||
$this->nodeStorage = $entity_manager->getStorage('node');
|
||||
$this->nodeAccess = $entity_manager->getAccessControlHandler('node');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks routing access for the node revision.
|
||||
*
|
||||
* @param \Symfony\Component\Routing\Route $route
|
||||
* The route to check against.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The currently logged in account.
|
||||
* @param int $node_revision
|
||||
* (optional) The node revision ID. If not specified, but $node is, access
|
||||
* is checked for that object's revision.
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* (optional) A node object. Used for checking access to a node's default
|
||||
* revision when $node_revision is unspecified. Ignored when $node_revision
|
||||
* is specified. If neither $node_revision nor $node are specified, then
|
||||
* access is denied.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public function access(Route $route, AccountInterface $account, $node_revision = NULL, NodeInterface $node = NULL) {
|
||||
if ($node_revision) {
|
||||
$node = $this->nodeStorage->loadRevision($node_revision);
|
||||
}
|
||||
$operation = $route->getRequirement('_access_node_revision');
|
||||
return AccessResult::allowedIf($node && $this->checkAccess($node, $account, $operation))->cachePerPermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks node revision access.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node to check.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* A user object representing the user for whom the operation is to be
|
||||
* performed.
|
||||
* @param string $op
|
||||
* (optional) The specific operation being checked. Defaults to 'view.'
|
||||
* @param string|null $langcode
|
||||
* (optional) Language code for the variant of the node. Different language
|
||||
* variants might have different permissions associated. If NULL, the
|
||||
* original langcode of the node is used. Defaults to NULL.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the operation may be performed, FALSE otherwise.
|
||||
*/
|
||||
public function checkAccess(NodeInterface $node, AccountInterface $account, $op = 'view', $langcode = NULL) {
|
||||
$map = array(
|
||||
'view' => 'view all revisions',
|
||||
'update' => 'revert all revisions',
|
||||
'delete' => 'delete all revisions',
|
||||
);
|
||||
$bundle = $node->bundle();
|
||||
$type_map = array(
|
||||
'view' => "view $bundle revisions",
|
||||
'update' => "revert $bundle revisions",
|
||||
'delete' => "delete $bundle revisions",
|
||||
);
|
||||
|
||||
if (!$node || !isset($map[$op]) || !isset($type_map[$op])) {
|
||||
// If there was no node to check against, or the $op was not one of the
|
||||
// supported ones, we return access denied.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// If no language code was provided, default to the node revision's langcode.
|
||||
if (empty($langcode)) {
|
||||
$langcode = $node->language()->getId();
|
||||
}
|
||||
|
||||
// Statically cache access by revision ID, language code, user account ID,
|
||||
// and operation.
|
||||
$cid = $node->getRevisionId() . ':' . $langcode . ':' . $account->id() . ':' . $op;
|
||||
|
||||
if (!isset($this->access[$cid])) {
|
||||
// Perform basic permission checks first.
|
||||
if (!$account->hasPermission($map[$op]) && !$account->hasPermission($type_map[$op]) && !$account->hasPermission('administer nodes')) {
|
||||
$this->access[$cid] = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// There should be at least two revisions. If the vid of the given node
|
||||
// and the vid of the default revision differ, then we already have two
|
||||
// different revisions so there is no need for a separate database check.
|
||||
// Also, if you try to revert to or delete the default revision, that's
|
||||
// not good.
|
||||
if ($node->isDefaultRevision() && ($this->nodeStorage->countDefaultLanguageRevisions($node) == 1 || $op == 'update' || $op == 'delete')) {
|
||||
$this->access[$cid] = FALSE;
|
||||
}
|
||||
elseif ($account->hasPermission('administer nodes')) {
|
||||
$this->access[$cid] = TRUE;
|
||||
}
|
||||
else {
|
||||
// First check the access to the default revision and finally, if the
|
||||
// node passed in is not the default revision then access to that, too.
|
||||
$this->access[$cid] = $this->nodeAccess->access($this->nodeStorage->load($node->id()), $op, $langcode, $account) && ($node->isDefaultRevision() || $this->nodeAccess->access($node, $op, $langcode, $account));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->access[$cid];
|
||||
}
|
||||
|
||||
}
|
81
core/modules/node/src/Cache/NodeAccessGrantsCacheContext.php
Normal file
81
core/modules/node/src/Cache/NodeAccessGrantsCacheContext.php
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Cache\NodeAccessGrantsCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Cache;
|
||||
|
||||
use Drupal\Core\Cache\Context\CalculatedCacheContextInterface;
|
||||
use Drupal\Core\Cache\Context\UserCacheContext;
|
||||
|
||||
/**
|
||||
* Defines the node access view cache context service.
|
||||
*
|
||||
* This allows for node access grants-sensitive caching when listing nodes.
|
||||
*
|
||||
* @see node_query_node_access_alter()
|
||||
* @ingroup node_access
|
||||
*/
|
||||
class NodeAccessGrantsCacheContext extends UserCacheContext implements CalculatedCacheContextInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t("Content access view grants");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext($operation = NULL) {
|
||||
// If the current user either can bypass node access then we don't need to
|
||||
// determine the exact node grants for the current user.
|
||||
if ($this->user->hasPermission('bypass node access')) {
|
||||
return 'all';
|
||||
}
|
||||
|
||||
// When no specific operation is specified, check the grants for all three
|
||||
// possible operations.
|
||||
if ($operation === NULL) {
|
||||
$result = [];
|
||||
foreach (['view', 'update', 'delete'] as $op) {
|
||||
$result[] = $this->checkNodeGrants($op);
|
||||
}
|
||||
return implode('-', $result);
|
||||
}
|
||||
else {
|
||||
return $this->checkNodeGrants($operation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the node grants for the given operation.
|
||||
*
|
||||
* @param string $operation
|
||||
* The operation to check the node grants for.
|
||||
*
|
||||
* @return string
|
||||
* The string representation of the cache context.
|
||||
*/
|
||||
protected function checkNodeGrants($operation) {
|
||||
// When checking the grants for the 'view' operation and the current user
|
||||
// has a global view grant (i.e. a view grant for node ID 0) — note that
|
||||
// this is automatically the case if no node access modules exist (no
|
||||
// hook_node_grants() implementations) then we don't need to determine the
|
||||
// exact node view grants for the current user.
|
||||
if ($operation === 'view' && node_access_view_all_nodes($this->user)) {
|
||||
return 'view.all';
|
||||
}
|
||||
|
||||
$grants = node_access_grants($operation, $this->user);
|
||||
$grants_context_parts = [];
|
||||
foreach ($grants as $realm => $gids) {
|
||||
$grants_context_parts[] = $realm . ':' . implode(',', $gids);
|
||||
}
|
||||
return $operation . '.' . implode(';', $grants_context_parts);
|
||||
}
|
||||
|
||||
}
|
249
core/modules/node/src/Controller/NodeController.php
Normal file
249
core/modules/node/src/Controller/NodeController.php
Normal file
|
@ -0,0 +1,249 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Controller\NodeController.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Controller;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Datetime\DateFormatter;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\node\NodeTypeInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Returns responses for Node routes.
|
||||
*/
|
||||
class NodeController extends ControllerBase implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The date formatter service.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\DateFormatter
|
||||
*/
|
||||
protected $dateFormatter;
|
||||
|
||||
/**
|
||||
* The renderer service.
|
||||
*
|
||||
* @var \Drupal\Core\Render\RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* Constructs a NodeController object.
|
||||
*
|
||||
* @param \Drupal\Core\Datetime\DateFormatter $date_formatter
|
||||
* The date formatter service.
|
||||
* @param \Drupal\Core\Render\RendererInterface $renderer
|
||||
* The renderer service.
|
||||
*/
|
||||
public function __construct(DateFormatter $date_formatter, RendererInterface $renderer) {
|
||||
$this->dateFormatter = $date_formatter;
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('date.formatter'),
|
||||
$container->get('renderer')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Displays add content links for available content types.
|
||||
*
|
||||
* Redirects to node/add/[type] if only one content type is available.
|
||||
*
|
||||
* @return array
|
||||
* A render array for a list of the node types that can be added; however,
|
||||
* if there is only one node type defined for the site, the function
|
||||
* redirects to the node add page for that one node type and does not return
|
||||
* at all.
|
||||
*
|
||||
* @see node_menu()
|
||||
*/
|
||||
public function addPage() {
|
||||
$content = array();
|
||||
|
||||
// Only use node types the user has access to.
|
||||
foreach ($this->entityManager()->getStorage('node_type')->loadMultiple() as $type) {
|
||||
if ($this->entityManager()->getAccessControlHandler('node')->createAccess($type->id())) {
|
||||
$content[$type->id()] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
// Bypass the node/add listing if only one content type is available.
|
||||
if (count($content) == 1) {
|
||||
$type = array_shift($content);
|
||||
return $this->redirect('node.add', array('node_type' => $type->id()));
|
||||
}
|
||||
|
||||
return array(
|
||||
'#theme' => 'node_add_list',
|
||||
'#content' => $content,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the node submission form.
|
||||
*
|
||||
* @param \Drupal\node\NodeTypeInterface $node_type
|
||||
* The node type entity for the node.
|
||||
*
|
||||
* @return array
|
||||
* A node submission form.
|
||||
*/
|
||||
public function add(NodeTypeInterface $node_type) {
|
||||
$node = $this->entityManager()->getStorage('node')->create(array(
|
||||
'type' => $node_type->id(),
|
||||
));
|
||||
|
||||
$form = $this->entityFormBuilder()->getForm($node);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a node revision.
|
||||
*
|
||||
* @param int $node_revision
|
||||
* The node revision ID.
|
||||
*
|
||||
* @return array
|
||||
* An array suitable for drupal_render().
|
||||
*/
|
||||
public function revisionShow($node_revision) {
|
||||
$node = $this->entityManager()->getStorage('node')->loadRevision($node_revision);
|
||||
$node_view_controller = new NodeViewController($this->entityManager, $this->renderer);
|
||||
$page = $node_view_controller->view($node);
|
||||
unset($page['nodes'][$node->id()]['#cache']);
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Page title callback for a node revision.
|
||||
*
|
||||
* @param int $node_revision
|
||||
* The node revision ID.
|
||||
*
|
||||
* @return string
|
||||
* The page title.
|
||||
*/
|
||||
public function revisionPageTitle($node_revision) {
|
||||
$node = $this->entityManager()->getStorage('node')->loadRevision($node_revision);
|
||||
return $this->t('Revision of %title from %date', array('%title' => $node->label(), '%date' => format_date($node->getRevisionCreationTime())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an overview table of older revisions of a node.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* A node object.
|
||||
*
|
||||
* @return array
|
||||
* An array as expected by drupal_render().
|
||||
*/
|
||||
public function revisionOverview(NodeInterface $node) {
|
||||
$account = $this->currentUser();
|
||||
$node_storage = $this->entityManager()->getStorage('node');
|
||||
$type = $node->getType();
|
||||
|
||||
$build = array();
|
||||
$build['#title'] = $this->t('Revisions for %title', array('%title' => $node->label()));
|
||||
$header = array($this->t('Revision'), $this->t('Operations'));
|
||||
|
||||
$revert_permission = (($account->hasPermission("revert $type revisions") || $account->hasPermission('revert all revisions') || $account->hasPermission('administer nodes')) && $node->access('update'));
|
||||
$delete_permission = (($account->hasPermission("delete $type revisions") || $account->hasPermission('delete all revisions') || $account->hasPermission('administer nodes')) && $node->access('delete'));
|
||||
|
||||
$rows = array();
|
||||
|
||||
$vids = $node_storage->revisionIds($node);
|
||||
|
||||
foreach (array_reverse($vids) as $vid) {
|
||||
if ($revision = $node_storage->loadRevision($vid)) {
|
||||
$row = array();
|
||||
|
||||
$revision_author = $revision->uid->entity;
|
||||
|
||||
if ($vid == $node->getRevisionId()) {
|
||||
$username = array(
|
||||
'#theme' => 'username',
|
||||
'#account' => $revision_author,
|
||||
);
|
||||
$row[] = array('data' => $this->t('!date by !username', array('!date' => $node->link($this->dateFormatter->format($revision->revision_timestamp->value, 'short')), '!username' => drupal_render($username)))
|
||||
. (($revision->revision_log->value != '') ? '<p class="revision-log">' . Xss::filter($revision->revision_log->value) . '</p>' : ''),
|
||||
'class' => array('revision-current'));
|
||||
$row[] = array('data' => SafeMarkup::placeholder($this->t('current revision')), 'class' => array('revision-current'));
|
||||
}
|
||||
else {
|
||||
$username = array(
|
||||
'#theme' => 'username',
|
||||
'#account' => $revision_author,
|
||||
);
|
||||
$row[] = $this->t('!date by !username', array('!date' => $this->l($this->dateFormatter->format($revision->revision_timestamp->value, 'short'), new Url('entity.node.revision', array('node' => $node->id(), 'node_revision' => $vid))), '!username' => drupal_render($username)))
|
||||
. (($revision->revision_log->value != '') ? '<p class="revision-log">' . Xss::filter($revision->revision_log->value) . '</p>' : '');
|
||||
|
||||
if ($revert_permission) {
|
||||
$links['revert'] = array(
|
||||
'title' => $this->t('Revert'),
|
||||
'url' => Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $vid]),
|
||||
);
|
||||
}
|
||||
|
||||
if ($delete_permission) {
|
||||
$links['delete'] = array(
|
||||
'title' => $this->t('Delete'),
|
||||
'url' => Url::fromRoute('node.revision_delete_confirm', ['node' => $node->id(), 'node_revision' => $vid]),
|
||||
);
|
||||
}
|
||||
|
||||
$row[] = array(
|
||||
'data' => array(
|
||||
'#type' => 'operations',
|
||||
'#links' => $links,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
$build['node_revisions_table'] = array(
|
||||
'#theme' => 'table',
|
||||
'#rows' => $rows,
|
||||
'#header' => $header,
|
||||
'#attached' => array(
|
||||
'library' => array('node/drupal.node.admin'),
|
||||
),
|
||||
);
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* The _title_callback for the node.add route.
|
||||
*
|
||||
* @param \Drupal\node\NodeTypeInterface $node_type
|
||||
* The current node.
|
||||
*
|
||||
* @return string
|
||||
* The page title.
|
||||
*/
|
||||
public function addPageTitle(NodeTypeInterface $node_type) {
|
||||
return $this->t('Create @name', array('@name' => $node_type->label()));
|
||||
}
|
||||
|
||||
}
|
67
core/modules/node/src/Controller/NodePreviewController.php
Normal file
67
core/modules/node/src/Controller/NodePreviewController.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Controller\NodePreviewController.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Controller;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\Controller\EntityViewController;
|
||||
|
||||
/**
|
||||
* Defines a controller to render a single node in preview.
|
||||
*/
|
||||
class NodePreviewController extends EntityViewController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function view(EntityInterface $node_preview, $view_mode_id = 'full', $langcode = NULL) {
|
||||
$node_preview->preview_view_mode = $view_mode_id;
|
||||
$build = parent::view($node_preview, $view_mode_id);
|
||||
|
||||
$build['#attached']['library'][] = 'node/drupal.node.preview';
|
||||
|
||||
// Don't render cache previews.
|
||||
unset($build['#cache']);
|
||||
|
||||
foreach ($node_preview->uriRelationships() as $rel) {
|
||||
// Set the node path as the canonical URL to prevent duplicate content.
|
||||
$build['#attached']['html_head_link'][] = array(
|
||||
array(
|
||||
'rel' => $rel,
|
||||
'href' => $node_preview->url($rel),
|
||||
)
|
||||
, TRUE);
|
||||
|
||||
if ($rel == 'canonical') {
|
||||
// Set the non-aliased canonical path as a default shortlink.
|
||||
$build['#attached']['html_head_link'][] = array(
|
||||
array(
|
||||
'rel' => 'shortlink',
|
||||
'href' => $node_preview->url($rel, array('alias' => TRUE)),
|
||||
)
|
||||
, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* The _title_callback for the page that renders a single node in preview.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node_preview
|
||||
* The current node.
|
||||
*
|
||||
* @return string
|
||||
* The page title.
|
||||
*/
|
||||
public function title(EntityInterface $node_preview) {
|
||||
return SafeMarkup::checkPlain($this->entityManager->getTranslationFromContext($node_preview)->label());
|
||||
}
|
||||
|
||||
}
|
63
core/modules/node/src/Controller/NodeViewController.php
Normal file
63
core/modules/node/src/Controller/NodeViewController.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Controller\NodeViewController.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Controller;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\Controller\EntityViewController;
|
||||
|
||||
/**
|
||||
* Defines a controller to render a single node.
|
||||
*/
|
||||
class NodeViewController extends EntityViewController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function view(EntityInterface $node, $view_mode = 'full', $langcode = NULL) {
|
||||
$build = parent::view($node);
|
||||
|
||||
foreach ($node->uriRelationships() as $rel) {
|
||||
// Set the node path as the canonical URL to prevent duplicate content.
|
||||
$build['#attached']['html_head_link'][] = array(
|
||||
array(
|
||||
'rel' => $rel,
|
||||
'href' => $node->url($rel),
|
||||
),
|
||||
TRUE,
|
||||
);
|
||||
|
||||
if ($rel == 'canonical') {
|
||||
// Set the non-aliased canonical path as a default shortlink.
|
||||
$build['#attached']['html_head_link'][] = array(
|
||||
array(
|
||||
'rel' => 'shortlink',
|
||||
'href' => $node->url($rel, array('alias' => TRUE)),
|
||||
),
|
||||
TRUE,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* The _title_callback for the page that renders a single node.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* The current node.
|
||||
*
|
||||
* @return string
|
||||
* The page title.
|
||||
*/
|
||||
public function title(EntityInterface $node) {
|
||||
return SafeMarkup::checkPlain($this->entityManager->getTranslationFromContext($node)->label());
|
||||
}
|
||||
|
||||
}
|
519
core/modules/node/src/Entity/Node.php
Normal file
519
core/modules/node/src/Entity/Node.php
Normal file
|
@ -0,0 +1,519 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Entity\Node.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Entity;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityBase;
|
||||
use Drupal\Core\Entity\EntityChangedTrait;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
* Defines the node entity class.
|
||||
*
|
||||
* @ContentEntityType(
|
||||
* id = "node",
|
||||
* label = @Translation("Content"),
|
||||
* bundle_label = @Translation("Content type"),
|
||||
* handlers = {
|
||||
* "storage" = "Drupal\node\NodeStorage",
|
||||
* "storage_schema" = "Drupal\node\NodeStorageSchema",
|
||||
* "view_builder" = "Drupal\node\NodeViewBuilder",
|
||||
* "access" = "Drupal\node\NodeAccessControlHandler",
|
||||
* "views_data" = "Drupal\node\NodeViewsData",
|
||||
* "form" = {
|
||||
* "default" = "Drupal\node\NodeForm",
|
||||
* "delete" = "Drupal\node\Form\NodeDeleteForm",
|
||||
* "edit" = "Drupal\node\NodeForm"
|
||||
* },
|
||||
* "route_provider" = {
|
||||
* "html" = "Drupal\node\Entity\NodeRouteProvider",
|
||||
* },
|
||||
* "list_builder" = "Drupal\node\NodeListBuilder",
|
||||
* "translation" = "Drupal\node\NodeTranslationHandler"
|
||||
* },
|
||||
* base_table = "node",
|
||||
* data_table = "node_field_data",
|
||||
* revision_table = "node_revision",
|
||||
* revision_data_table = "node_field_revision",
|
||||
* translatable = TRUE,
|
||||
* list_cache_contexts = { "user.node_grants:view" },
|
||||
* entity_keys = {
|
||||
* "id" = "nid",
|
||||
* "revision" = "vid",
|
||||
* "bundle" = "type",
|
||||
* "label" = "title",
|
||||
* "langcode" = "langcode",
|
||||
* "uuid" = "uuid"
|
||||
* },
|
||||
* bundle_entity_type = "node_type",
|
||||
* field_ui_base_route = "entity.node_type.edit_form",
|
||||
* common_reference_target = TRUE,
|
||||
* permission_granularity = "bundle",
|
||||
* links = {
|
||||
* "canonical" = "/node/{node}",
|
||||
* "delete-form" = "/node/{node}/delete",
|
||||
* "edit-form" = "/node/{node}/edit",
|
||||
* "version-history" = "/node/{node}/revisions",
|
||||
* "revision" = "/node/{node}/revisions/{node_revision}/view",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class Node extends ContentEntityBase implements NodeInterface {
|
||||
|
||||
use EntityChangedTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage) {
|
||||
parent::preSave($storage);
|
||||
|
||||
// If no owner has been set explicitly, make the current user the owner.
|
||||
if (!$this->getOwner()) {
|
||||
$this->setOwnerId(\Drupal::currentUser()->id());
|
||||
}
|
||||
// If no revision author has been set explicitly, make the node owner the
|
||||
// revision author.
|
||||
if (!$this->getRevisionAuthor()) {
|
||||
$this->setRevisionAuthorId($this->getOwnerId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSaveRevision(EntityStorageInterface $storage, \stdClass $record) {
|
||||
parent::preSaveRevision($storage, $record);
|
||||
|
||||
if (!$this->isNewRevision() && isset($this->original) && (!isset($record->revision_log) || $record->revision_log === '')) {
|
||||
// If we are updating an existing node without adding a new revision, we
|
||||
// need to make sure $entity->revision_log is reset whenever it is empty.
|
||||
// Therefore, this code allows us to avoid clobbering an existing log
|
||||
// entry with an empty one.
|
||||
$record->revision_log = $this->original->revision_log->value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
|
||||
parent::postSave($storage, $update);
|
||||
|
||||
// Update the node access table for this node, but only if it is the
|
||||
// default revision. There's no need to delete existing records if the node
|
||||
// is new.
|
||||
if ($this->isDefaultRevision()) {
|
||||
\Drupal::entityManager()->getAccessControlHandler('node')->writeGrants($this, $update);
|
||||
}
|
||||
|
||||
// Reindex the node when it is updated. The node is automatically indexed
|
||||
// when it is added, simply by being added to the node table.
|
||||
if ($update) {
|
||||
node_reindex_node_search($this->id());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageInterface $storage, array $entities) {
|
||||
parent::preDelete($storage, $entities);
|
||||
|
||||
// Ensure that all nodes deleted are removed from the search index.
|
||||
if (\Drupal::moduleHandler()->moduleExists('search')) {
|
||||
foreach ($entities as $entity) {
|
||||
search_index_clear('node_search', $entity->nid->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageInterface $storage, array $nodes) {
|
||||
parent::postDelete($storage, $nodes);
|
||||
\Drupal::service('node.grant_storage')->deleteNodeRecords(array_keys($nodes));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType() {
|
||||
return $this->bundle();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($operation = 'view', AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
if ($operation == 'create') {
|
||||
return parent::access($operation, $account, $return_as_object);
|
||||
}
|
||||
|
||||
return \Drupal::entityManager()
|
||||
->getAccessControlHandler($this->entityTypeId)
|
||||
->access($this, $operation, $this->prepareLangcode(), $account, $return_as_object);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareLangcode() {
|
||||
$langcode = $this->language()->getId();
|
||||
// If the Language module is enabled, try to use the language from content
|
||||
// negotiation.
|
||||
if (\Drupal::moduleHandler()->moduleExists('language')) {
|
||||
// Load languages the node exists in.
|
||||
$node_translations = $this->getTranslationLanguages();
|
||||
// Load the language from content negotiation.
|
||||
$content_negotiation_langcode = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
|
||||
// If there is a translation available, use it.
|
||||
if (isset($node_translations[$content_negotiation_langcode])) {
|
||||
$langcode = $content_negotiation_langcode;
|
||||
}
|
||||
}
|
||||
return $langcode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->get('title')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setTitle($title) {
|
||||
$this->set('title', $title);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCreatedTime() {
|
||||
return $this->get('created')->value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCreatedTime($timestamp) {
|
||||
$this->set('created', $timestamp);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getChangedTime() {
|
||||
return $this->get('changed')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isPromoted() {
|
||||
return (bool) $this->get('promote')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setPromoted($promoted) {
|
||||
$this->set('promote', $promoted ? NODE_PROMOTED : NODE_NOT_PROMOTED);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isSticky() {
|
||||
return (bool) $this->get('sticky')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSticky($sticky) {
|
||||
$this->set('sticky', $sticky ? NODE_STICKY : NODE_NOT_STICKY);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isPublished() {
|
||||
return (bool) $this->get('status')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setPublished($published) {
|
||||
$this->set('status', $published ? NODE_PUBLISHED : NODE_NOT_PUBLISHED);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOwner() {
|
||||
return $this->get('uid')->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOwnerId() {
|
||||
return $this->get('uid')->target_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setOwnerId($uid) {
|
||||
$this->set('uid', $uid);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setOwner(UserInterface $account) {
|
||||
$this->set('uid', $account->id());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRevisionCreationTime() {
|
||||
return $this->get('revision_timestamp')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRevisionCreationTime($timestamp) {
|
||||
$this->set('revision_timestamp', $timestamp);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRevisionAuthor() {
|
||||
return $this->get('revision_uid')->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRevisionAuthorId($uid) {
|
||||
$this->set('revision_uid', $uid);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
$fields['nid'] = BaseFieldDefinition::create('integer')
|
||||
->setLabel(t('Node ID'))
|
||||
->setDescription(t('The node ID.'))
|
||||
->setReadOnly(TRUE)
|
||||
->setSetting('unsigned', TRUE);
|
||||
|
||||
$fields['uuid'] = BaseFieldDefinition::create('uuid')
|
||||
->setLabel(t('UUID'))
|
||||
->setDescription(t('The node UUID.'))
|
||||
->setReadOnly(TRUE);
|
||||
|
||||
$fields['vid'] = BaseFieldDefinition::create('integer')
|
||||
->setLabel(t('Revision ID'))
|
||||
->setDescription(t('The node revision ID.'))
|
||||
->setReadOnly(TRUE)
|
||||
->setSetting('unsigned', TRUE);
|
||||
|
||||
$fields['type'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Type'))
|
||||
->setDescription(t('The node type.'))
|
||||
->setSetting('target_type', 'node_type')
|
||||
->setReadOnly(TRUE);
|
||||
|
||||
$fields['langcode'] = BaseFieldDefinition::create('language')
|
||||
->setLabel(t('Language'))
|
||||
->setDescription(t('The node language code.'))
|
||||
->setTranslatable(TRUE)
|
||||
->setRevisionable(TRUE)
|
||||
->setDisplayOptions('view', array(
|
||||
'type' => 'hidden',
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'language_select',
|
||||
'weight' => 2,
|
||||
));
|
||||
|
||||
$fields['title'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('Title'))
|
||||
->setRequired(TRUE)
|
||||
->setTranslatable(TRUE)
|
||||
->setRevisionable(TRUE)
|
||||
->setDefaultValue('')
|
||||
->setSetting('max_length', 255)
|
||||
->setDisplayOptions('view', array(
|
||||
'label' => 'hidden',
|
||||
'type' => 'string',
|
||||
'weight' => -5,
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'string_textfield',
|
||||
'weight' => -5,
|
||||
))
|
||||
->setDisplayConfigurable('form', TRUE);
|
||||
|
||||
$fields['uid'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Authored by'))
|
||||
->setDescription(t('The username of the content author.'))
|
||||
->setRevisionable(TRUE)
|
||||
->setSetting('target_type', 'user')
|
||||
->setSetting('handler', 'default')
|
||||
->setDefaultValueCallback('Drupal\node\Entity\Node::getCurrentUserId')
|
||||
->setTranslatable(TRUE)
|
||||
->setDisplayOptions('view', array(
|
||||
'label' => 'hidden',
|
||||
'type' => 'author',
|
||||
'weight' => 0,
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'entity_reference_autocomplete',
|
||||
'weight' => 5,
|
||||
'settings' => array(
|
||||
'match_operator' => 'CONTAINS',
|
||||
'size' => '60',
|
||||
'placeholder' => '',
|
||||
),
|
||||
))
|
||||
->setDisplayConfigurable('form', TRUE);
|
||||
|
||||
$fields['status'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('Publishing status'))
|
||||
->setDescription(t('A boolean indicating whether the node is published.'))
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE)
|
||||
->setDefaultValue(TRUE);
|
||||
|
||||
$fields['created'] = BaseFieldDefinition::create('created')
|
||||
->setLabel(t('Authored on'))
|
||||
->setDescription(t('The time that the node was created.'))
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE)
|
||||
->setDisplayOptions('view', array(
|
||||
'label' => 'hidden',
|
||||
'type' => 'timestamp',
|
||||
'weight' => 0,
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'datetime_timestamp',
|
||||
'weight' => 10,
|
||||
))
|
||||
->setDisplayConfigurable('form', TRUE);
|
||||
|
||||
$fields['changed'] = BaseFieldDefinition::create('changed')
|
||||
->setLabel(t('Changed'))
|
||||
->setDescription(t('The time that the node was last edited.'))
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE);
|
||||
|
||||
$fields['promote'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('Promoted to front page'))
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE)
|
||||
->setDefaultValue(TRUE)
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'boolean_checkbox',
|
||||
'settings' => array(
|
||||
'display_label' => TRUE,
|
||||
),
|
||||
'weight' => 15,
|
||||
))
|
||||
->setDisplayConfigurable('form', TRUE);
|
||||
|
||||
$fields['sticky'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('Sticky at top of lists'))
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE)
|
||||
->setDefaultValue(FALSE)
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'boolean_checkbox',
|
||||
'settings' => array(
|
||||
'display_label' => TRUE,
|
||||
),
|
||||
'weight' => 16,
|
||||
))
|
||||
->setDisplayConfigurable('form', TRUE);
|
||||
|
||||
$fields['revision_timestamp'] = BaseFieldDefinition::create('created')
|
||||
->setLabel(t('Revision timestamp'))
|
||||
->setDescription(t('The time that the current revision was created.'))
|
||||
->setQueryable(FALSE)
|
||||
->setRevisionable(TRUE);
|
||||
|
||||
$fields['revision_uid'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Revision user ID'))
|
||||
->setDescription(t('The user ID of the author of the current revision.'))
|
||||
->setSetting('target_type', 'user')
|
||||
->setQueryable(FALSE)
|
||||
->setRevisionable(TRUE);
|
||||
|
||||
$fields['revision_log'] = BaseFieldDefinition::create('string_long')
|
||||
->setLabel(t('Revision log message'))
|
||||
->setDescription(t('Briefly describe the changes you have made.'))
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE)
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'string_textarea',
|
||||
'weight' => 25,
|
||||
'settings' => array(
|
||||
'rows' => 4,
|
||||
),
|
||||
));
|
||||
|
||||
$fields['revision_translation_affected'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('Revision translation affected'))
|
||||
->setDescription(t('Indicates if the last edit of a translation belongs to current revision.'))
|
||||
->setReadOnly(TRUE)
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default value callback for 'uid' base field definition.
|
||||
*
|
||||
* @see ::baseFieldDefinitions()
|
||||
*
|
||||
* @return array
|
||||
* An array of default values.
|
||||
*/
|
||||
public static function getCurrentUserId() {
|
||||
return array(\Drupal::currentUser()->id());
|
||||
}
|
||||
|
||||
}
|
51
core/modules/node/src/Entity/NodeRouteProvider.php
Normal file
51
core/modules/node/src/Entity/NodeRouteProvider.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Entity\NodeRouteProvider.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\Routing\EntityRouteProviderInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Provides routes for nodes.
|
||||
*/
|
||||
class NodeRouteProvider implements EntityRouteProviderInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRoutes( EntityTypeInterface $entity_type) {
|
||||
$route_collection = new RouteCollection();
|
||||
$route = (new Route('/node/{node}'))
|
||||
->addDefaults([
|
||||
'_controller' => '\Drupal\node\Controller\NodeViewController::view',
|
||||
'_title_callback' => '\Drupal\node\Controller\NodeViewController::title',
|
||||
])
|
||||
->setRequirement('_entity_access', 'node.view');
|
||||
$route_collection->add('entity.node.canonical', $route);
|
||||
|
||||
$route = (new Route('/node/{node}/delete'))
|
||||
->addDefaults([
|
||||
'_entity_form' => 'node.delete',
|
||||
'_title' => 'Delete',
|
||||
])
|
||||
->setRequirement('_entity_access', 'node.delete')
|
||||
->setOption('_node_operation_route', TRUE);
|
||||
$route_collection->add('entity.node.delete_form', $route);
|
||||
|
||||
$route = (new Route('/node/{node}/edit'))
|
||||
->setDefault('_entity_form', 'node.edit')
|
||||
->setRequirement('_entity_access', 'node.update')
|
||||
->setOption('_node_operation_route', TRUE);
|
||||
$route_collection->add('entity.node.edit_form', $route);
|
||||
|
||||
return $route_collection;
|
||||
}
|
||||
|
||||
}
|
213
core/modules/node/src/Entity/NodeType.php
Normal file
213
core/modules/node/src/Entity/NodeType.php
Normal file
|
@ -0,0 +1,213 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Entity\NodeType.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\node\NodeTypeInterface;
|
||||
|
||||
/**
|
||||
* Defines the Node type configuration entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "node_type",
|
||||
* label = @Translation("Content type"),
|
||||
* handlers = {
|
||||
* "access" = "Drupal\node\NodeTypeAccessControlHandler",
|
||||
* "form" = {
|
||||
* "add" = "Drupal\node\NodeTypeForm",
|
||||
* "edit" = "Drupal\node\NodeTypeForm",
|
||||
* "delete" = "Drupal\node\Form\NodeTypeDeleteConfirm"
|
||||
* },
|
||||
* "list_builder" = "Drupal\node\NodeTypeListBuilder",
|
||||
* },
|
||||
* admin_permission = "administer content types",
|
||||
* config_prefix = "type",
|
||||
* bundle_of = "node",
|
||||
* entity_keys = {
|
||||
* "id" = "type",
|
||||
* "label" = "name"
|
||||
* },
|
||||
* links = {
|
||||
* "edit-form" = "/admin/structure/types/manage/{node_type}",
|
||||
* "delete-form" = "/admin/structure/types/manage/{node_type}/delete",
|
||||
* "collection" = "/admin/structure/types",
|
||||
* },
|
||||
* config_export = {
|
||||
* "name",
|
||||
* "type",
|
||||
* "description",
|
||||
* "help",
|
||||
* "new_revision",
|
||||
* "preview_mode",
|
||||
* "display_submitted",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class NodeType extends ConfigEntityBundleBase implements NodeTypeInterface {
|
||||
|
||||
/**
|
||||
* The machine name of this node type.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @todo Rename to $id.
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* The human-readable name of the node type.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @todo Rename to $label.
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* A brief description of this node type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* Help information shown to the user when creating a Node of this type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $help;
|
||||
|
||||
/**
|
||||
* Default value of the 'Create new revision' checkbox of this node type.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $new_revision = FALSE;
|
||||
|
||||
/**
|
||||
* The preview mode.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $preview_mode = DRUPAL_OPTIONAL;
|
||||
|
||||
/**
|
||||
* Display setting for author and date Submitted by post information.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $display_submitted = TRUE;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function id() {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isLocked() {
|
||||
$locked = \Drupal::state()->get('node.type.locked');
|
||||
return isset($locked[$this->id()]) ? $locked[$this->id()] : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isNewRevision() {
|
||||
return $this->new_revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setNewRevision($new_revision) {
|
||||
$this->new_revision = $new_revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function displaySubmitted() {
|
||||
return $this->display_submitted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDisplaySubmitted($display_submitted) {
|
||||
$this->display_submitted = $display_submitted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPreviewMode() {
|
||||
return $this->preview_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setPreviewMode($preview_mode) {
|
||||
$this->preview_mode = $preview_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHelp() {
|
||||
return $this->help;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
|
||||
parent::postSave($storage, $update);
|
||||
|
||||
if ($update && $this->getOriginalId() != $this->id()) {
|
||||
$update_count = node_type_update_nodes($this->getOriginalId(), $this->id());
|
||||
if ($update_count) {
|
||||
drupal_set_message(\Drupal::translation()->formatPlural($update_count,
|
||||
'Changed the content type of 1 post from %old-type to %type.',
|
||||
'Changed the content type of @count posts from %old-type to %type.',
|
||||
array(
|
||||
'%old-type' => $this->getOriginalId(),
|
||||
'%type' => $this->id(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
if ($update) {
|
||||
// Clear the cached field definitions as some settings affect the field
|
||||
// definitions.
|
||||
$this->entityManager()->clearCachedFieldDefinitions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageInterface $storage, array $entities) {
|
||||
parent::postDelete($storage, $entities);
|
||||
|
||||
// Clear the node type cache to reflect the removal.
|
||||
$storage->resetCache(array_keys($entities));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\EventSubscriber\NodeAdminRouteSubscriber.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Routing\RouteSubscriberBase;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Sets the _admin_route for specific node-related routes.
|
||||
*/
|
||||
class NodeAdminRouteSubscriber extends RouteSubscriberBase {
|
||||
|
||||
/**
|
||||
* The config factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* Constructs a new NodeAdminRouteSubscriber.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory) {
|
||||
$this->configFactory = $config_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function alterRoutes(RouteCollection $collection) {
|
||||
if ($this->configFactory->get('node.settings')->get('use_admin_theme')) {
|
||||
foreach ($collection->all() as $route) {
|
||||
if ($route->hasOption('_node_operation_route')) {
|
||||
$route->setOption('_admin_route', TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
204
core/modules/node/src/Form/DeleteMultiple.php
Normal file
204
core/modules/node/src/Form/DeleteMultiple.php
Normal file
|
@ -0,0 +1,204 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Form\DeleteMultiple.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\user\PrivateTempStoreFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
/**
|
||||
* Provides a node deletion confirmation form.
|
||||
*/
|
||||
class DeleteMultiple extends ConfirmFormBase {
|
||||
|
||||
/**
|
||||
* The array of nodes to delete.
|
||||
*
|
||||
* @var string[][]
|
||||
*/
|
||||
protected $nodeInfo = array();
|
||||
|
||||
/**
|
||||
* The tempstore factory.
|
||||
*
|
||||
* @var \Drupal\user\PrivateTempStoreFactory
|
||||
*/
|
||||
protected $tempStoreFactory;
|
||||
|
||||
/**
|
||||
* The node storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* Constructs a DeleteMultiple form object.
|
||||
*
|
||||
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
|
||||
* The tempstore factory.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityManagerInterface $manager) {
|
||||
$this->tempStoreFactory = $temp_store_factory;
|
||||
$this->storage = $manager->getStorage('node');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('user.private_tempstore'),
|
||||
$container->get('entity.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'node_multiple_delete_confirm';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->formatPlural(count($this->nodeInfo), 'Are you sure you want to delete this item?', 'Are you sure you want to delete these items?');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('system.admin_content');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return t('Delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$this->nodeInfo = $this->tempStoreFactory->get('node_multiple_delete_confirm')->get(\Drupal::currentUser()->id());
|
||||
if (empty($this->nodeInfo)) {
|
||||
return new RedirectResponse($this->getCancelUrl()->setAbsolute()->toString());
|
||||
}
|
||||
/** @var \Drupal\node\NodeInterface[] $nodes */
|
||||
$nodes = $this->storage->loadMultiple(array_keys($this->nodeInfo));
|
||||
|
||||
$items = [];
|
||||
foreach ($this->nodeInfo as $id => $langcodes) {
|
||||
foreach ($langcodes as $langcode) {
|
||||
$node = $nodes[$id]->getTranslation($langcode);
|
||||
$key = $id . ':' . $langcode;
|
||||
$default_key = $id . ':' . $node->getUntranslated()->language()->getId();
|
||||
|
||||
// If we have a translated entity we build a nested list of translations
|
||||
// that will be deleted.
|
||||
$languages = $node->getTranslationLanguages();
|
||||
if (count($languages) > 1 && $node->isDefaultTranslation()) {
|
||||
$names = [];
|
||||
foreach ($languages as $translation_langcode => $language) {
|
||||
$names[] = $language->getName();
|
||||
unset($items[$id . ':' . $translation_langcode]);
|
||||
}
|
||||
$items[$default_key] = [
|
||||
'label' => [
|
||||
'#markup' => $this->t('@label (Original translation) - <em>The following content translations will be deleted:</em>', ['@label' => $node->label()]),
|
||||
],
|
||||
'deleted_translations' => [
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $names,
|
||||
],
|
||||
];
|
||||
}
|
||||
elseif (!isset($items[$default_key])) {
|
||||
$items[$key] = $node->label();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$form['nodes'] = array(
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $items,
|
||||
);
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
if ($form_state->getValue('confirm') && !empty($this->nodeInfo)) {
|
||||
$total_count = 0;
|
||||
$delete_nodes = [];
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface[][] $delete_translations */
|
||||
$delete_translations = [];
|
||||
/** @var \Drupal\node\NodeInterface[] $nodes */
|
||||
$nodes = $this->storage->loadMultiple(array_keys($this->nodeInfo));
|
||||
|
||||
foreach ($this->nodeInfo as $id => $langcodes) {
|
||||
foreach ($langcodes as $langcode) {
|
||||
$node = $nodes[$id]->getTranslation($langcode);
|
||||
if ($node->isDefaultTranslation()) {
|
||||
$delete_nodes[$id] = $node;
|
||||
unset($delete_translations[$id]);
|
||||
$total_count += count($node->getTranslationLanguages());
|
||||
}
|
||||
elseif (!isset($delete_nodes[$id])) {
|
||||
$delete_translations[$id][] = $node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($delete_nodes) {
|
||||
$this->storage->delete($delete_nodes);
|
||||
$this->logger('content')->notice('Deleted @count posts.', array('@count' => count($delete_nodes)));
|
||||
}
|
||||
|
||||
if ($delete_translations) {
|
||||
$count = 0;
|
||||
foreach ($delete_translations as $id => $translations) {
|
||||
$node = $nodes[$id]->getUntranslated();
|
||||
foreach ($translations as $translation) {
|
||||
$node->removeTranslation($translation->language()->getId());
|
||||
}
|
||||
$node->save();
|
||||
$count += count($translations);
|
||||
}
|
||||
if ($count) {
|
||||
$total_count += $count;
|
||||
$this->logger('content')->notice('Deleted @count content translations.', array('@count' => $count));
|
||||
}
|
||||
}
|
||||
|
||||
if ($total_count) {
|
||||
drupal_set_message($this->formatPlural($total_count, 'Deleted 1 post.', 'Deleted @count posts.'));
|
||||
}
|
||||
|
||||
$this->tempStoreFactory->get('node_multiple_delete_confirm')->delete(\Drupal::currentUser()->id());
|
||||
}
|
||||
|
||||
$form_state->setRedirect('system.admin_content');
|
||||
}
|
||||
|
||||
}
|
50
core/modules/node/src/Form/NodeDeleteForm.php
Normal file
50
core/modules/node/src/Form/NodeDeleteForm.php
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Form\NodeDeleteForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Form;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityDeleteForm;
|
||||
|
||||
/**
|
||||
* Provides a form for deleting a node.
|
||||
*/
|
||||
class NodeDeleteForm extends ContentEntityDeleteForm {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDeletionMessage() {
|
||||
/** @var \Drupal\node\NodeInterface $entity */
|
||||
$entity = $this->getEntity();
|
||||
|
||||
$node_type_storage = $this->entityManager->getStorage('node_type');
|
||||
$node_type = $node_type_storage->load($entity->bundle())->label();
|
||||
|
||||
if (!$entity->isDefaultTranslation()) {
|
||||
return $this->t('@language translation of the @type %label has been deleted.', [
|
||||
'@language' => $entity->language()->getName(),
|
||||
'@type' => $node_type,
|
||||
'%label' => $entity->label(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->t('The @type %title has been deleted.', array(
|
||||
'@type' => $node_type,
|
||||
'%title' => $this->getEntity()->label(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function logDeletionMessage() {
|
||||
/** @var \Drupal\node\NodeInterface $entity */
|
||||
$entity = $this->getEntity();
|
||||
$this->logger('content')->notice('@type: deleted %title.', ['@type' => $entity->getType(), '%title' => $entity->label()]);
|
||||
}
|
||||
|
||||
}
|
169
core/modules/node/src/Form/NodePreviewForm.php
Normal file
169
core/modules/node/src/Form/NodePreviewForm.php
Normal file
|
@ -0,0 +1,169 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Form\NodePreviewForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Form;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Contains a form for switching the view mode of a node during preview.
|
||||
*/
|
||||
class NodePreviewForm extends FormBase implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The entity manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The config factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static($container->get('entity.manager'), $container->get('config.factory'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new NodePreviewForm.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager service.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The configuration factory.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, ConfigFactoryInterface $config_factory) {
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->configFactory = $config_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'node_preview_form_select';
|
||||
}
|
||||
|
||||
/**
|
||||
* Form constructor.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* The node being previews
|
||||
*
|
||||
* @return array
|
||||
* The form structure.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, EntityInterface $node = NULL) {
|
||||
$view_mode = $node->preview_view_mode;
|
||||
|
||||
$query_options = $node->isNew() ? array('query' => array('uuid' => $node->uuid())) : array();
|
||||
$form['backlink'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Back to content editing'),
|
||||
'#url' => $node->isNew() ? Url::fromRoute('node.add', ['node_type' => $node->bundle()]) : $node->urlInfo('edit-form'),
|
||||
'#options' => array('attributes' => array('class' => array('node-preview-backlink'))) + $query_options,
|
||||
);
|
||||
|
||||
$view_mode_options = $this->getViewModeOptions($node);
|
||||
|
||||
$form['uuid'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $node->uuid(),
|
||||
);
|
||||
|
||||
$form['view_mode'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('View mode'),
|
||||
'#options' => $view_mode_options,
|
||||
'#default_value' => $view_mode,
|
||||
'#attributes' => array(
|
||||
'data-drupal-autosubmit' => TRUE,
|
||||
)
|
||||
);
|
||||
|
||||
$form['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Switch'),
|
||||
'#attributes' => array(
|
||||
'class' => array('js-hide'),
|
||||
),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$form_state->setRedirect('entity.node.preview', array(
|
||||
'node_preview' => $form_state->getValue('uuid'),
|
||||
'view_mode_id' => $form_state->getValue('view_mode'),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of available view modes for the current node.
|
||||
*
|
||||
* @param EntityInterface $node
|
||||
* The node being previewed.
|
||||
*
|
||||
* @return array
|
||||
* List of available view modes for the current node.
|
||||
*/
|
||||
protected function getViewModeOptions(EntityInterface $node) {
|
||||
$load_ids = array();
|
||||
$view_mode_options = array();
|
||||
|
||||
// Load all the node's view modes.
|
||||
$view_modes = $this->entityManager->getViewModes('node');
|
||||
|
||||
// Get the list of available view modes for the current node's bundle.
|
||||
$ids = $this->configFactory->listAll('core.entity_view_display.node.' . $node->bundle());
|
||||
foreach ($ids as $id) {
|
||||
$config_id = str_replace('core.entity_view_display' . '.', '', $id);
|
||||
$load_ids[] = $config_id;
|
||||
}
|
||||
$displays = entity_load_multiple('entity_view_display', $load_ids);
|
||||
|
||||
// Generate the display options array.
|
||||
foreach ($displays as $display) {
|
||||
|
||||
$view_mode_name = $display->get('mode');
|
||||
|
||||
// Skip view modes that are not used in the front end.
|
||||
if (in_array($view_mode_name, array('rss', 'search_index'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($display->status()) {
|
||||
$view_mode_options[$view_mode_name] = ($view_mode_name == 'default') ? t('Default') : $view_modes[$view_mode_name]['label'];
|
||||
}
|
||||
}
|
||||
|
||||
return $view_mode_options;
|
||||
}
|
||||
|
||||
}
|
138
core/modules/node/src/Form/NodeRevisionDeleteForm.php
Normal file
138
core/modules/node/src/Form/NodeRevisionDeleteForm.php
Normal file
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Form\NodeRevisionDeleteForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Form;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a form for reverting a node revision.
|
||||
*/
|
||||
class NodeRevisionDeleteForm extends ConfirmFormBase {
|
||||
|
||||
/**
|
||||
* The node revision.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface
|
||||
*/
|
||||
protected $revision;
|
||||
|
||||
/**
|
||||
* The node storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $nodeStorage;
|
||||
|
||||
/**
|
||||
* The node type storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $nodeTypeStorage;
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Constructs a new NodeRevisionDeleteForm.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $node_storage
|
||||
* The node storage.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $node_type_storage
|
||||
* The node type storage.
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection.
|
||||
*/
|
||||
public function __construct(EntityStorageInterface $node_storage, EntityStorageInterface $node_type_storage, Connection $connection) {
|
||||
$this->nodeStorage = $node_storage;
|
||||
$this->nodeTypeStorage = $node_type_storage;
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
$entity_manager = $container->get('entity.manager');
|
||||
return new static(
|
||||
$entity_manager->getStorage('node'),
|
||||
$entity_manager->getStorage('node_type'),
|
||||
$container->get('database')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'node_revision_delete_confirm';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return t('Are you sure you want to delete the revision from %revision-date?', array('%revision-date' => format_date($this->revision->getRevisionCreationTime())));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('entity.node.version_history', array('node' => $this->revision->id()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return t('Delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $node_revision = NULL) {
|
||||
$this->revision = $this->nodeStorage->loadRevision($node_revision);
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->nodeStorage->deleteRevision($this->revision->getRevisionId());
|
||||
|
||||
$this->logger('content')->notice('@type: deleted %title revision %revision.', array('@type' => $this->revision->bundle(), '%title' => $this->revision->label(), '%revision' => $this->revision->getRevisionId()));
|
||||
$node_type = $this->nodeTypeStorage->load($this->revision->bundle())->label();
|
||||
drupal_set_message(t('Revision from %revision-date of @type %title has been deleted.', array('%revision-date' => format_date($this->revision->getRevisionCreationTime()), '@type' => $node_type, '%title' => $this->revision->label())));
|
||||
$form_state->setRedirect(
|
||||
'entity.node.canonical',
|
||||
array('node' => $this->revision->id())
|
||||
);
|
||||
if ($this->connection->query('SELECT COUNT(DISTINCT vid) FROM {node_field_revision} WHERE nid = :nid', array(':nid' => $this->revision->id()))->fetchField() > 1) {
|
||||
$form_state->setRedirect(
|
||||
'entity.node.version_history',
|
||||
array('node' => $this->revision->id())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
160
core/modules/node/src/Form/NodeRevisionRevertForm.php
Normal file
160
core/modules/node/src/Form/NodeRevisionRevertForm.php
Normal file
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Form\NodeRevisionRevertForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a form for reverting a node revision.
|
||||
*/
|
||||
class NodeRevisionRevertForm extends ConfirmFormBase {
|
||||
|
||||
/**
|
||||
* The node revision.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface
|
||||
*/
|
||||
protected $revision;
|
||||
|
||||
/**
|
||||
* The node storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $nodeStorage;
|
||||
|
||||
/**
|
||||
* Constructs a new NodeRevisionRevertForm.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $node_storage
|
||||
* The node storage.
|
||||
*/
|
||||
public function __construct(EntityStorageInterface $node_storage) {
|
||||
$this->nodeStorage = $node_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager')->getStorage('node')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'node_revision_revert_confirm';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return t('Are you sure you want to revert to the revision from %revision-date?', array('%revision-date' => format_date($this->revision->getRevisionCreationTime())));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('entity.node.version_history', array('node' => $this->revision->id()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return t('Revert');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $node_revision = NULL) {
|
||||
$this->revision = $this->nodeStorage->loadRevision($node_revision);
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$revision = $this->prepareRevertedRevision($this->revision);
|
||||
|
||||
// The revision timestamp will be updated when the revision is saved. Keep the
|
||||
// original one for the confirmation message.
|
||||
$original_revision_timestamp = $revision->getRevisionCreationTime();
|
||||
$revision->revision_log = t('Copy of the revision from %date.', array('%date' => format_date($original_revision_timestamp)));
|
||||
|
||||
$revision->save();
|
||||
|
||||
$this->logger('content')->notice('@type: reverted %title revision %revision.', array('@type' => $this->revision->bundle(), '%title' => $this->revision->label(), '%revision' => $this->revision->getRevisionId()));
|
||||
drupal_set_message(t('@type %title has been reverted to the revision from %revision-date.', array('@type' => node_get_type_label($this->revision), '%title' => $this->revision->label(), '%revision-date' => format_date($original_revision_timestamp))));
|
||||
$form_state->setRedirect(
|
||||
'entity.node.version_history',
|
||||
array('node' => $this->revision->id())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a revision to be reverted.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $revision
|
||||
* The revision to be reverted.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The prepared revision ready to be stored.
|
||||
*/
|
||||
protected function prepareRevertedRevision(NodeInterface $revision) {
|
||||
/** @var \Drupal\node\NodeInterface $default_revision */
|
||||
$default_revision = $this->nodeStorage->load($revision->id());
|
||||
|
||||
// If the entity is translated, make sure only translations affected by the
|
||||
// specified revision are reverted.
|
||||
$languages = $default_revision->getTranslationLanguages();
|
||||
if (count($languages) > 1) {
|
||||
// @todo Instead of processing all the available translations, we should
|
||||
// let the user decide which translations should be reverted. See
|
||||
// https://www.drupal.org/node/2465907.
|
||||
foreach ($languages as $langcode => $language) {
|
||||
if ($revision->hasTranslation($langcode) && !$revision->getTranslation($langcode)->isRevisionTranslationAffected()) {
|
||||
$revision_translation = $revision->getTranslation($langcode);
|
||||
$default_translation = $default_revision->getTranslation($langcode);
|
||||
foreach ($default_revision->getFieldDefinitions() as $field_name => $definition) {
|
||||
if ($definition->isTranslatable()) {
|
||||
$revision_translation->set($field_name, $default_translation->get($field_name)->getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$revision->setNewRevision();
|
||||
$revision->isDefaultRevision(TRUE);
|
||||
|
||||
return $revision;
|
||||
}
|
||||
|
||||
}
|
64
core/modules/node/src/Form/NodeTypeDeleteConfirm.php
Normal file
64
core/modules/node/src/Form/NodeTypeDeleteConfirm.php
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Form\NodeTypeDeleteConfirm.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Form;
|
||||
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\Core\Entity\EntityDeleteForm;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a form for content type deletion.
|
||||
*/
|
||||
class NodeTypeDeleteConfirm extends EntityDeleteForm {
|
||||
|
||||
/**
|
||||
* The query factory to create entity queries.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
protected $queryFactory;
|
||||
|
||||
/**
|
||||
* Constructs a new NodeTypeDeleteConfirm object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\Query\QueryFactory $query_factory
|
||||
* The entity query object.
|
||||
*/
|
||||
public function __construct(QueryFactory $query_factory) {
|
||||
$this->queryFactory = $query_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.query')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$num_nodes = $this->queryFactory->get('node')
|
||||
->condition('type', $this->entity->id())
|
||||
->count()
|
||||
->execute();
|
||||
if ($num_nodes) {
|
||||
$caption = '<p>' . $this->formatPlural($num_nodes, '%type is used by 1 piece of content on your site. You can not remove this content type until you have removed all of the %type content.', '%type is used by @count pieces of content on your site. You may not remove %type until you have removed all of the %type content.', array('%type' => $this->entity->label())) . '</p>';
|
||||
$form['#title'] = $this->getQuestion();
|
||||
$form['description'] = array('#markup' => $caption);
|
||||
return $form;
|
||||
}
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
59
core/modules/node/src/Form/RebuildPermissionsForm.php
Normal file
59
core/modules/node/src/Form/RebuildPermissionsForm.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Form\RebuildPermissionsForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Form;
|
||||
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
class RebuildPermissionsForm extends ConfirmFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'node_configure_rebuild_confirm';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return t('Are you sure you want to rebuild the permissions on site content?');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('system.status');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return t('Rebuild permissions');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return t('This action rebuilds all permissions on site content, and may be a lengthy process. This action cannot be undone.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
node_access_rebuild(TRUE);
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
}
|
202
core/modules/node/src/NodeAccessControlHandler.php
Normal file
202
core/modules/node/src/NodeAccessControlHandler.php
Normal file
|
@ -0,0 +1,202 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeAccessControlHandler.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines the access control handler for the node entity type.
|
||||
*
|
||||
* @see \Drupal\node\Entity\Node
|
||||
* @ingroup node_access
|
||||
*/
|
||||
class NodeAccessControlHandler extends EntityAccessControlHandler implements NodeAccessControlHandlerInterface, EntityHandlerInterface {
|
||||
|
||||
/**
|
||||
* The node grant storage.
|
||||
*
|
||||
* @var \Drupal\node\NodeGrantDatabaseStorageInterface
|
||||
*/
|
||||
protected $grantStorage;
|
||||
|
||||
/**
|
||||
* Constructs a NodeAccessControlHandler object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\node\NodeGrantDatabaseStorageInterface $grant_storage
|
||||
* The node grant storage.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, NodeGrantDatabaseStorageInterface $grant_storage) {
|
||||
parent::__construct($entity_type);
|
||||
$this->grantStorage = $grant_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('node.grant_storage')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access(EntityInterface $entity, $operation, $langcode = LanguageInterface::LANGCODE_DEFAULT, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
$account = $this->prepareUser($account);
|
||||
|
||||
if ($account->hasPermission('bypass node access')) {
|
||||
$result = AccessResult::allowed()->cachePerPermissions();
|
||||
return $return_as_object ? $result : $result->isAllowed();
|
||||
}
|
||||
if (!$account->hasPermission('access content')) {
|
||||
$result = AccessResult::forbidden()->cachePerPermissions();
|
||||
return $return_as_object ? $result : $result->isAllowed();
|
||||
}
|
||||
$result = parent::access($entity, $operation, $langcode, $account, TRUE)->cachePerPermissions();
|
||||
return $return_as_object ? $result : $result->isAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createAccess($entity_bundle = NULL, AccountInterface $account = NULL, array $context = array(), $return_as_object = FALSE) {
|
||||
$account = $this->prepareUser($account);
|
||||
|
||||
if ($account->hasPermission('bypass node access')) {
|
||||
$result = AccessResult::allowed()->cachePerPermissions();
|
||||
return $return_as_object ? $result : $result->isAllowed();
|
||||
}
|
||||
if (!$account->hasPermission('access content')) {
|
||||
$result = AccessResult::forbidden()->cachePerPermissions();
|
||||
return $return_as_object ? $result : $result->isAllowed();
|
||||
}
|
||||
|
||||
$result = parent::createAccess($entity_bundle, $account, $context, TRUE)->cachePerPermissions();
|
||||
return $return_as_object ? $result : $result->isAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $node, $operation, $langcode, AccountInterface $account) {
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
/** @var \Drupal\node\NodeInterface $translation */
|
||||
$translation = $node->getTranslation($langcode);
|
||||
// Fetch information from the node object if possible.
|
||||
$status = $translation->isPublished();
|
||||
$uid = $translation->getOwnerId();
|
||||
|
||||
// Check if authors can view their own unpublished nodes.
|
||||
if ($operation === 'view' && !$status && $account->hasPermission('view own unpublished content') && $account->isAuthenticated() && $account->id() == $uid) {
|
||||
return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($node);
|
||||
}
|
||||
|
||||
// Evaluate node grants.
|
||||
return $this->grantStorage->access($node, $operation, $langcode, $account);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
|
||||
return AccessResult::allowedIf($account->hasPermission('create ' . $entity_bundle . ' content'))->cachePerPermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkFieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
|
||||
// Only users with the administer nodes permission can edit administrative
|
||||
// fields.
|
||||
$administrative_fields = array('uid', 'status', 'created', 'promote', 'sticky');
|
||||
if ($operation == 'edit' && in_array($field_definition->getName(), $administrative_fields, TRUE)) {
|
||||
return AccessResult::allowedIfHasPermission($account, 'administer nodes');
|
||||
}
|
||||
|
||||
// No user can change read only fields.
|
||||
$read_only_fields = array('revision_timestamp', 'revision_uid');
|
||||
if ($operation == 'edit' && in_array($field_definition->getName(), $read_only_fields, TRUE)) {
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
// Users have access to the revision_log field either if they have
|
||||
// administrative permissions or if the new revision option is enabled.
|
||||
if ($operation == 'edit' && $field_definition->getName() == 'revision_log') {
|
||||
if ($account->hasPermission('administer nodes')) {
|
||||
return AccessResult::allowed()->cachePerPermissions();
|
||||
}
|
||||
return AccessResult::allowedIf($items->getEntity()->type->entity->isNewRevision())->cachePerPermissions();
|
||||
}
|
||||
return parent::checkFieldAccess($operation, $field_definition, $account, $items);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function acquireGrants(NodeInterface $node) {
|
||||
$grants = $this->moduleHandler->invokeAll('node_access_records', array($node));
|
||||
// Let modules alter the grants.
|
||||
$this->moduleHandler->alter('node_access_records', $grants, $node);
|
||||
// If no grants are set and the node is published, then use the default grant.
|
||||
if (empty($grants) && $node->isPublished()) {
|
||||
$grants[] = array('realm' => 'all', 'gid' => 0, 'grant_view' => 1, 'grant_update' => 0, 'grant_delete' => 0);
|
||||
}
|
||||
return $grants;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function writeGrants(NodeInterface $node, $delete = TRUE) {
|
||||
$grants = $this->acquireGrants($node);
|
||||
$this->grantStorage->write($node, $grants, NULL, $delete);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function writeDefaultGrant() {
|
||||
$this->grantStorage->writeDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteGrants() {
|
||||
$this->grantStorage->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function countGrants() {
|
||||
return $this->grantStorage->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkAllGrants(AccountInterface $account) {
|
||||
return $this->grantStorage->checkAll($account);
|
||||
}
|
||||
|
||||
}
|
92
core/modules/node/src/NodeAccessControlHandlerInterface.php
Normal file
92
core/modules/node/src/NodeAccessControlHandlerInterface.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeAccessControlHandlerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Node specific entity access control methods.
|
||||
*
|
||||
* @ingroup node_access
|
||||
*/
|
||||
interface NodeAccessControlHandlerInterface {
|
||||
|
||||
/**
|
||||
* Gets the list of node access grants.
|
||||
*
|
||||
* This function is called to check the access grants for a node. It collects
|
||||
* all node access grants for the node from hook_node_access_records()
|
||||
* implementations, allows these grants to be altered via
|
||||
* hook_node_access_records_alter() implementations, and returns the grants to
|
||||
* the caller.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The $node to acquire grants for.
|
||||
*
|
||||
* @return array $grants
|
||||
* The access rules for the node.
|
||||
*/
|
||||
public function acquireGrants(NodeInterface $node);
|
||||
|
||||
|
||||
/**
|
||||
* Writes a list of grants to the database, deleting any previously saved ones.
|
||||
*
|
||||
* If a realm is provided, it will only delete grants from that realm, but it
|
||||
* will always delete a grant from the 'all' realm. Modules that use node
|
||||
* access can use this function when doing mass updates due to widespread
|
||||
* permission changes.
|
||||
*
|
||||
* Note: Don't call this function directly from a contributed module. Call
|
||||
* node_access_acquire_grants() instead.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node whose grants are being written.
|
||||
* @param $grants
|
||||
* A list of grants to write. See hook_node_access_records() for the
|
||||
* expected structure of the grants array.
|
||||
* @param $realm
|
||||
* (optional) If provided, read/write grants for that realm only. Defaults to
|
||||
* NULL.
|
||||
* @param $delete
|
||||
* (optional) If false, does not delete records. This is only for optimization
|
||||
* purposes, and assumes the caller has already performed a mass delete of
|
||||
* some form. Defaults to TRUE.
|
||||
*/
|
||||
public function writeGrants(NodeInterface $node, $delete = TRUE);
|
||||
|
||||
/**
|
||||
* Creates the default node access grant entry on the grant storage.
|
||||
*/
|
||||
public function writeDefaultGrant();
|
||||
|
||||
/**
|
||||
* Deletes all node access entries.
|
||||
*/
|
||||
public function deleteGrants();
|
||||
|
||||
/**
|
||||
* Counts available node grants.
|
||||
*
|
||||
* @return int
|
||||
* Returns the amount of node grants.
|
||||
*/
|
||||
public function countGrants();
|
||||
|
||||
/**
|
||||
* Checks all grants for a given account.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* A user object representing the user for whom the operation is to be
|
||||
* performed.
|
||||
*
|
||||
* @return int.
|
||||
* Status of the access check.
|
||||
*/
|
||||
public function checkAllGrants(AccountInterface $account);
|
||||
|
||||
}
|
421
core/modules/node/src/NodeForm.php
Normal file
421
core/modules/node/src/NodeForm.php
Normal file
|
@ -0,0 +1,421 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Entity\ContentEntityForm;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\user\PrivateTempStoreFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Form controller for the node edit forms.
|
||||
*/
|
||||
class NodeForm extends ContentEntityForm {
|
||||
|
||||
/**
|
||||
* The tempstore factory.
|
||||
*
|
||||
* @var \Drupal\user\PrivateTempStoreFactory
|
||||
*/
|
||||
protected $tempStoreFactory;
|
||||
|
||||
/**
|
||||
* Whether this node has been previewed or not.
|
||||
*/
|
||||
protected $hasBeenPreviewed = FALSE;
|
||||
|
||||
/**
|
||||
* Constructs a ContentEntityForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
|
||||
* The factory for the temp store object.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, PrivateTempStoreFactory $temp_store_factory) {
|
||||
parent::__construct($entity_manager);
|
||||
$this->tempStoreFactory = $temp_store_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager'),
|
||||
$container->get('user.private_tempstore')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareEntity() {
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $this->entity;
|
||||
|
||||
if (!$node->isNew()) {
|
||||
// Remove the revision log message from the original node entity.
|
||||
$node->revision_log = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
// Try to restore from temp store, this must be done before calling
|
||||
// parent::form().
|
||||
$uuid = $this->entity->uuid();
|
||||
$store = $this->tempStoreFactory->get('node_preview');
|
||||
|
||||
// If the user is creating a new node, the UUID is passed in the request.
|
||||
if ($request_uuid = \Drupal::request()->query->get('uuid')) {
|
||||
$uuid = $request_uuid;
|
||||
}
|
||||
|
||||
if ($preview = $store->get($uuid)) {
|
||||
/** @var $preview \Drupal\Core\Form\FormStateInterface */
|
||||
$form_state = $preview;
|
||||
|
||||
// Rebuild the form.
|
||||
$form_state->setRebuild();
|
||||
$this->entity = $preview->getFormObject()->getEntity();
|
||||
unset($this->entity->in_preview);
|
||||
|
||||
// Remove the stale temp store entry for existing nodes.
|
||||
if (!$this->entity->isNew()) {
|
||||
$store->delete($uuid);
|
||||
}
|
||||
|
||||
$this->hasBeenPreviewed = TRUE;
|
||||
}
|
||||
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $this->entity;
|
||||
|
||||
if ($this->operation == 'edit') {
|
||||
$form['#title'] = $this->t('<em>Edit @type</em> @title', array('@type' => node_get_type_label($node), '@title' => $node->label()));
|
||||
}
|
||||
|
||||
$current_user = $this->currentUser();
|
||||
|
||||
// Changed must be sent to the client, for later overwrite error checking.
|
||||
$form['changed'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#default_value' => $node->getChangedTime(),
|
||||
);
|
||||
|
||||
$form['advanced'] = array(
|
||||
'#type' => 'vertical_tabs',
|
||||
'#attributes' => array('class' => array('entity-meta')),
|
||||
'#weight' => 99,
|
||||
);
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
// Add a revision_log field if the "Create new revision" option is checked,
|
||||
// or if the current user has the ability to check that option.
|
||||
$form['revision_information'] = array(
|
||||
'#type' => 'details',
|
||||
'#group' => 'advanced',
|
||||
'#title' => t('Revision information'),
|
||||
// Open by default when "Create new revision" is checked.
|
||||
'#open' => $node->isNewRevision(),
|
||||
'#attributes' => array(
|
||||
'class' => array('node-form-revision-information'),
|
||||
),
|
||||
'#attached' => array(
|
||||
'library' => array('node/drupal.node'),
|
||||
),
|
||||
'#weight' => 20,
|
||||
'#optional' => TRUE,
|
||||
);
|
||||
|
||||
$form['revision'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Create new revision'),
|
||||
'#default_value' => $node->type->entity->isNewRevision(),
|
||||
'#access' => $current_user->hasPermission('administer nodes'),
|
||||
'#group' => 'revision_information',
|
||||
);
|
||||
|
||||
$form['revision_log'] += array(
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
':input[name="revision"]' => array('checked' => TRUE),
|
||||
),
|
||||
),
|
||||
'#group' => 'revision_information',
|
||||
);
|
||||
|
||||
// Node author information for administrators.
|
||||
$form['author'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Authoring information'),
|
||||
'#group' => 'advanced',
|
||||
'#attributes' => array(
|
||||
'class' => array('node-form-author'),
|
||||
),
|
||||
'#attached' => array(
|
||||
'library' => array('node/drupal.node'),
|
||||
),
|
||||
'#weight' => 90,
|
||||
'#optional' => TRUE,
|
||||
);
|
||||
|
||||
if (isset($form['uid'])) {
|
||||
$form['uid']['#group'] = 'author';
|
||||
}
|
||||
|
||||
if (isset($form['created'])) {
|
||||
$form['created']['#group'] = 'author';
|
||||
}
|
||||
|
||||
// Node options for administrators.
|
||||
$form['options'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Promotion options'),
|
||||
'#group' => 'advanced',
|
||||
'#attributes' => array(
|
||||
'class' => array('node-form-options'),
|
||||
),
|
||||
'#attached' => array(
|
||||
'library' => array('node/drupal.node'),
|
||||
),
|
||||
'#weight' => 95,
|
||||
'#optional' => TRUE,
|
||||
);
|
||||
|
||||
if (isset($form['promote'])) {
|
||||
$form['promote']['#group'] = 'options';
|
||||
}
|
||||
|
||||
if (isset($form['sticky'])) {
|
||||
$form['sticky']['#group'] = 'options';
|
||||
}
|
||||
|
||||
$form['#attached']['library'][] = 'node/form';
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$element = parent::actions($form, $form_state);
|
||||
$node = $this->entity;
|
||||
$preview_mode = $node->type->entity->getPreviewMode();
|
||||
|
||||
$element['submit']['#access'] = $preview_mode != DRUPAL_REQUIRED || $this->hasBeenPreviewed;
|
||||
|
||||
// If saving is an option, privileged users get dedicated form submit
|
||||
// buttons to adjust the publishing status while saving in one go.
|
||||
// @todo This adjustment makes it close to impossible for contributed
|
||||
// modules to integrate with "the Save operation" of this form. Modules
|
||||
// need a way to plug themselves into 1) the ::submit() step, and
|
||||
// 2) the ::save() step, both decoupled from the pressed form button.
|
||||
if ($element['submit']['#access'] && \Drupal::currentUser()->hasPermission('administer nodes')) {
|
||||
// isNew | prev status » default & publish label & unpublish label
|
||||
// 1 | 1 » publish & Save and publish & Save as unpublished
|
||||
// 1 | 0 » unpublish & Save and publish & Save as unpublished
|
||||
// 0 | 1 » publish & Save and keep published & Save and unpublish
|
||||
// 0 | 0 » unpublish & Save and keep unpublished & Save and publish
|
||||
|
||||
// Add a "Publish" button.
|
||||
$element['publish'] = $element['submit'];
|
||||
$element['publish']['#dropbutton'] = 'save';
|
||||
if ($node->isNew()) {
|
||||
$element['publish']['#value'] = t('Save and publish');
|
||||
}
|
||||
else {
|
||||
$element['publish']['#value'] = $node->isPublished() ? t('Save and keep published') : t('Save and publish');
|
||||
}
|
||||
$element['publish']['#weight'] = 0;
|
||||
array_unshift($element['publish']['#submit'], '::publish');
|
||||
|
||||
// Add a "Unpublish" button.
|
||||
$element['unpublish'] = $element['submit'];
|
||||
$element['unpublish']['#dropbutton'] = 'save';
|
||||
if ($node->isNew()) {
|
||||
$element['unpublish']['#value'] = t('Save as unpublished');
|
||||
}
|
||||
else {
|
||||
$element['unpublish']['#value'] = !$node->isPublished() ? t('Save and keep unpublished') : t('Save and unpublish');
|
||||
}
|
||||
$element['unpublish']['#weight'] = 10;
|
||||
array_unshift($element['unpublish']['#submit'], '::unpublish');
|
||||
|
||||
// If already published, the 'publish' button is primary.
|
||||
if ($node->isPublished()) {
|
||||
unset($element['unpublish']['#button_type']);
|
||||
}
|
||||
// Otherwise, the 'unpublish' button is primary and should come first.
|
||||
else {
|
||||
unset($element['publish']['#button_type']);
|
||||
$element['unpublish']['#weight'] = -10;
|
||||
}
|
||||
|
||||
// Remove the "Save" button.
|
||||
$element['submit']['#access'] = FALSE;
|
||||
}
|
||||
|
||||
$element['preview'] = array(
|
||||
'#type' => 'submit',
|
||||
'#access' => $preview_mode != DRUPAL_DISABLED && ($node->access('create') || $node->access('update')),
|
||||
'#value' => t('Preview'),
|
||||
'#weight' => 20,
|
||||
'#validate' => array('::validate'),
|
||||
'#submit' => array('::submitForm', '::preview'),
|
||||
);
|
||||
|
||||
$element['delete']['#access'] = $node->access('delete');
|
||||
$element['delete']['#weight'] = 100;
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Updates the node object by processing the submitted values.
|
||||
*
|
||||
* This function can be called by a "Next" button of a wizard to update the
|
||||
* form state's entity with the current step's values before proceeding to the
|
||||
* next step.
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// Build the node object from the submitted values.
|
||||
parent::submitForm($form, $form_state);
|
||||
$node = $this->entity;
|
||||
|
||||
// Save as a new revision if requested to do so.
|
||||
if (!$form_state->isValueEmpty('revision') && $form_state->getValue('revision') != FALSE) {
|
||||
$node->setNewRevision();
|
||||
// If a new revision is created, save the current user as revision author.
|
||||
$node->setRevisionCreationTime(REQUEST_TIME);
|
||||
$node->setRevisionAuthorId(\Drupal::currentUser()->id());
|
||||
}
|
||||
else {
|
||||
$node->setNewRevision(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for the 'preview' action.
|
||||
*
|
||||
* @param $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
public function preview(array $form, FormStateInterface $form_state) {
|
||||
$store = $this->tempStoreFactory->get('node_preview');
|
||||
$this->entity->in_preview = TRUE;
|
||||
$store->set($this->entity->uuid(), $form_state);
|
||||
$form_state->setRedirect('entity.node.preview', array(
|
||||
'node_preview' => $this->entity->uuid(),
|
||||
'view_mode_id' => 'default',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for the 'publish' action.
|
||||
*
|
||||
* @param $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
public function publish(array $form, FormStateInterface $form_state) {
|
||||
$node = $this->entity;
|
||||
$node->setPublished(TRUE);
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for the 'unpublish' action.
|
||||
*
|
||||
* @param $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
public function unpublish(array $form, FormStateInterface $form_state) {
|
||||
$node = $this->entity;
|
||||
$node->setPublished(FALSE);
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildEntity(array $form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\node\NodeInterface $entity */
|
||||
$entity = parent::buildEntity($form, $form_state);
|
||||
// A user might assign the node author by entering a user name in the node
|
||||
// form, which we then need to translate to a user ID.
|
||||
// @todo: Remove it when https://www.drupal.org/node/2322525 is pushed.
|
||||
if (!empty($form_state->getValue('uid')[0]['target_id']) && $account = User::load($form_state->getValue('uid')[0]['target_id'])) {
|
||||
$entity->setOwnerId($account->id());
|
||||
}
|
||||
else {
|
||||
$entity->setOwnerId(0);
|
||||
}
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$node = $this->entity;
|
||||
$insert = $node->isNew();
|
||||
$node->save();
|
||||
$node_link = $node->link($this->t('View'));
|
||||
$context = array('@type' => $node->getType(), '%title' => $node->label(), 'link' => $node_link);
|
||||
$t_args = array('@type' => node_get_type_label($node), '%title' => $node->label());
|
||||
|
||||
if ($insert) {
|
||||
$this->logger('content')->notice('@type: added %title.', $context);
|
||||
drupal_set_message(t('@type %title has been created.', $t_args));
|
||||
}
|
||||
else {
|
||||
$this->logger('content')->notice('@type: updated %title.', $context);
|
||||
drupal_set_message(t('@type %title has been updated.', $t_args));
|
||||
}
|
||||
|
||||
if ($node->id()) {
|
||||
$form_state->setValue('nid', $node->id());
|
||||
$form_state->set('nid', $node->id());
|
||||
if ($node->access('view')) {
|
||||
$form_state->setRedirect(
|
||||
'entity.node.canonical',
|
||||
array('node' => $node->id())
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form_state->setRedirect('<front>');
|
||||
}
|
||||
|
||||
// Remove the preview entry from the temp store, if any.
|
||||
$store = $this->tempStoreFactory->get('node_preview');
|
||||
$store->delete($node->uuid());
|
||||
}
|
||||
else {
|
||||
// In the unlikely case something went wrong on save, the node will be
|
||||
// rebuilt and node form redisplayed the same way as in preview.
|
||||
drupal_set_message(t('The post could not be saved.'), 'error');
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
311
core/modules/node/src/NodeGrantDatabaseStorage.php
Normal file
311
core/modules/node/src/NodeGrantDatabaseStorage.php
Normal file
|
@ -0,0 +1,311 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeGrantDatabaseStorage.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Entity\ContentEntityBase;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\user\Entity\User;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a controller class that handles the node grants system.
|
||||
*
|
||||
* This is used to build node query access.
|
||||
*
|
||||
* @ingroup node_access
|
||||
*/
|
||||
class NodeGrantDatabaseStorage implements NodeGrantDatabaseStorageInterface {
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* Constructs a NodeGrantDatabaseStorage object.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* The database connection.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
*/
|
||||
public function __construct(Connection $database, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager) {
|
||||
$this->database = $database;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access(NodeInterface $node, $operation, $langcode, AccountInterface $account) {
|
||||
// If no module implements the hook or the node does not have an id there is
|
||||
// no point in querying the database for access grants.
|
||||
if (!$this->moduleHandler->getImplementations('node_grants') || !$node->id()) {
|
||||
// Return the equivalent of the default grant, defined by
|
||||
// self::writeDefault().
|
||||
if ($operation === 'view') {
|
||||
return AccessResult::allowedIf($node->getTranslation($langcode)->isPublished())->cacheUntilEntityChanges($node);
|
||||
}
|
||||
else {
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
}
|
||||
|
||||
// Check the database for potential access grants.
|
||||
$query = $this->database->select('node_access');
|
||||
$query->addExpression('1');
|
||||
// Only interested for granting in the current operation.
|
||||
$query->condition('grant_' . $operation, 1, '>=');
|
||||
// Check for grants for this node and the correct langcode.
|
||||
$nids = $query->andConditionGroup()
|
||||
->condition('nid', $node->id())
|
||||
->condition('langcode', $langcode);
|
||||
// If the node is published, also take the default grant into account. The
|
||||
// default is saved with a node ID of 0.
|
||||
$status = $node->isPublished();
|
||||
if ($status) {
|
||||
$nids = $query->orConditionGroup()
|
||||
->condition($nids)
|
||||
->condition('nid', 0);
|
||||
}
|
||||
$query->condition($nids);
|
||||
$query->range(0, 1);
|
||||
|
||||
$grants = static::buildGrantsQueryCondition(node_access_grants($operation, $account));
|
||||
|
||||
if (count($grants) > 0) {
|
||||
$query->condition($grants);
|
||||
}
|
||||
|
||||
// Only the 'view' node grant can currently be cached; the others currently
|
||||
// don't have any cacheability metadata. Hopefully, we can add that in the
|
||||
// future, which would allow this access check result to be cacheable in all
|
||||
// cases. For now, this must remain marked as uncacheable, even when it is
|
||||
// theoretically cacheable, because we don't have the necessary metadata to
|
||||
// know it for a fact.
|
||||
$set_cacheability = function (AccessResult $access_result) use ($operation) {
|
||||
$access_result->addCacheContexts(['user.node_grants:' . $operation]);
|
||||
if ($operation !== 'view') {
|
||||
$access_result->setCacheMaxAge(0);
|
||||
}
|
||||
return $access_result;
|
||||
};
|
||||
|
||||
if ($query->execute()->fetchField()) {
|
||||
return $set_cacheability(AccessResult::allowed());
|
||||
}
|
||||
else {
|
||||
return $set_cacheability(AccessResult::neutral());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkAll(AccountInterface $account) {
|
||||
$query = $this->database->select('node_access');
|
||||
$query->addExpression('COUNT(*)');
|
||||
$query
|
||||
->condition('nid', 0)
|
||||
->condition('grant_view', 1, '>=');
|
||||
|
||||
$grants = static::buildGrantsQueryCondition(node_access_grants('view', $account));
|
||||
|
||||
if (count($grants) > 0 ) {
|
||||
$query->condition($grants);
|
||||
}
|
||||
return $query->execute()->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alterQuery($query, array $tables, $op, AccountInterface $account, $base_table) {
|
||||
if (!$langcode = $query->getMetaData('langcode')) {
|
||||
$langcode = FALSE;
|
||||
}
|
||||
|
||||
// Find all instances of the base table being joined -- could appear
|
||||
// more than once in the query, and could be aliased. Join each one to
|
||||
// the node_access table.
|
||||
$grants = node_access_grants($op, $account);
|
||||
foreach ($tables as $nalias => $tableinfo) {
|
||||
$table = $tableinfo['table'];
|
||||
if (!($table instanceof SelectInterface) && $table == $base_table) {
|
||||
// Set the subquery.
|
||||
$subquery = $this->database->select('node_access', 'na')
|
||||
->fields('na', array('nid'));
|
||||
|
||||
// If any grant exists for the specified user, then user has access to the
|
||||
// node for the specified operation.
|
||||
$grant_conditions = static::buildGrantsQueryCondition($grants);
|
||||
|
||||
// Attach conditions to the subquery for nodes.
|
||||
if (count($grant_conditions->conditions())) {
|
||||
$subquery->condition($grant_conditions);
|
||||
}
|
||||
$subquery->condition('na.grant_' . $op, 1, '>=');
|
||||
|
||||
// Add langcode-based filtering if this is a multilingual site.
|
||||
if (\Drupal::languageManager()->isMultilingual()) {
|
||||
// If no specific langcode to check for is given, use the grant entry
|
||||
// which is set as a fallback.
|
||||
// If a specific langcode is given, use the grant entry for it.
|
||||
if ($langcode === FALSE) {
|
||||
$subquery->condition('na.fallback', 1, '=');
|
||||
}
|
||||
else {
|
||||
$subquery->condition('na.langcode', $langcode, '=');
|
||||
}
|
||||
}
|
||||
|
||||
$field = 'nid';
|
||||
// Now handle entities.
|
||||
$subquery->where("$nalias.$field = na.nid");
|
||||
|
||||
$query->exists($subquery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write(NodeInterface $node, array $grants, $realm = NULL, $delete = TRUE) {
|
||||
if ($delete) {
|
||||
$query = $this->database->delete('node_access')->condition('nid', $node->id());
|
||||
if ($realm) {
|
||||
$query->condition('realm', array($realm, 'all'), 'IN');
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
// Only perform work when node_access modules are active.
|
||||
if (!empty($grants) && count($this->moduleHandler->getImplementations('node_grants'))) {
|
||||
$query = $this->database->insert('node_access')->fields(array('nid', 'langcode', 'fallback', 'realm', 'gid', 'grant_view', 'grant_update', 'grant_delete'));
|
||||
// If we have defined a granted langcode, use it. But if not, add a grant
|
||||
// for every language this node is translated to.
|
||||
foreach ($grants as $grant) {
|
||||
if ($realm && $realm != $grant['realm']) {
|
||||
continue;
|
||||
}
|
||||
if (isset($grant['langcode'])) {
|
||||
$grant_languages = array($grant['langcode'] => $this->languageManager->getLanguage($grant['langcode']));
|
||||
}
|
||||
else {
|
||||
$grant_languages = $node->getTranslationLanguages(TRUE);
|
||||
}
|
||||
foreach ($grant_languages as $grant_langcode => $grant_language) {
|
||||
// Only write grants; denies are implicit.
|
||||
if ($grant['grant_view'] || $grant['grant_update'] || $grant['grant_delete']) {
|
||||
$grant['nid'] = $node->id();
|
||||
$grant['langcode'] = $grant_langcode;
|
||||
// The record with the original langcode is used as the fallback.
|
||||
if ($grant['langcode'] == $node->language()->getId()) {
|
||||
$grant['fallback'] = 1;
|
||||
}
|
||||
else {
|
||||
$grant['fallback'] = 0;
|
||||
}
|
||||
$query->values($grant);
|
||||
}
|
||||
}
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete() {
|
||||
$this->database->truncate('node_access')->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function writeDefault() {
|
||||
$this->database->insert('node_access')
|
||||
->fields(array(
|
||||
'nid' => 0,
|
||||
'realm' => 'all',
|
||||
'gid' => 0,
|
||||
'grant_view' => 1,
|
||||
'grant_update' => 0,
|
||||
'grant_delete' => 0,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count() {
|
||||
return $this->database->query('SELECT COUNT(*) FROM {node_access}')->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteNodeRecords(array $nids) {
|
||||
$this->database->delete('node_access')
|
||||
->condition('nid', $nids, 'IN')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query condition from an array of node access grants.
|
||||
*
|
||||
* @param array $node_access_grants
|
||||
* An array of grants, as returned by node_access_grants().
|
||||
* @return \Drupal\Core\Database\Query\Condition
|
||||
* A condition object to be passed to $query->condition().
|
||||
*
|
||||
* @see node_access_grants()
|
||||
*/
|
||||
protected static function buildGrantsQueryCondition(array $node_access_grants) {
|
||||
$grants = new Condition("OR");
|
||||
foreach ($node_access_grants as $realm => $gids) {
|
||||
if (!empty($gids)) {
|
||||
$and = new Condition('AND');
|
||||
$grants->condition($and
|
||||
->condition('gid', $gids, 'IN')
|
||||
->condition('realm', $realm)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $grants;
|
||||
}
|
||||
|
||||
}
|
137
core/modules/node/src/NodeGrantDatabaseStorageInterface.php
Normal file
137
core/modules/node/src/NodeGrantDatabaseStorageInterface.php
Normal file
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeGrantDatabaseStorageInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for node access grant storage.
|
||||
*
|
||||
* @ingroup node_access
|
||||
*/
|
||||
interface NodeGrantDatabaseStorageInterface {
|
||||
|
||||
/**
|
||||
* Checks all grants for a given account.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* A user object representing the user for whom the operation is to be
|
||||
* performed.
|
||||
*
|
||||
* @return int.
|
||||
* Status of the access check.
|
||||
*/
|
||||
public function checkAll(AccountInterface $account);
|
||||
|
||||
/**
|
||||
* Alters a query when node access is required.
|
||||
*
|
||||
* @param mixed $query
|
||||
* Query that is being altered.
|
||||
* @param array $tables
|
||||
* A list of tables that need to be part of the alter.
|
||||
* @param string $op
|
||||
* The operation to be performed on the node. Possible values are:
|
||||
* - "view"
|
||||
* - "update"
|
||||
* - "delete"
|
||||
* - "create"
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* A user object representing the user for whom the operation is to be
|
||||
* performed.
|
||||
* @param string $base_table
|
||||
* The base table of the query.
|
||||
*
|
||||
* @return int
|
||||
* Status of the access check.
|
||||
*/
|
||||
public function alterQuery($query, array $tables, $op, AccountInterface $account, $base_table);
|
||||
|
||||
/**
|
||||
* Writes a list of grants to the database, deleting previously saved ones.
|
||||
*
|
||||
* If a realm is provided, it will only delete grants from that realm, but
|
||||
* it will always delete a grant from the 'all' realm. Modules that use
|
||||
* node access can use this method when doing mass updates due to widespread
|
||||
* permission changes.
|
||||
*
|
||||
* Note: Don't call this method directly from a contributed module. Call
|
||||
* node_access_write_grants() instead.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node whose grants are being written.
|
||||
* @param array $grants
|
||||
* A list of grants to write. Each grant is an array that must contain the
|
||||
* following keys: realm, gid, grant_view, grant_update, grant_delete.
|
||||
* The realm is specified by a particular module; the gid is as well, and
|
||||
* is a module-defined id to define grant privileges. each grant_* field
|
||||
* is a boolean value.
|
||||
* @param string $realm
|
||||
* (optional) If provided, read/write grants for that realm only. Defaults to
|
||||
* NULL.
|
||||
* @param bool $delete
|
||||
* (optional) If false, does not delete records. This is only for optimization
|
||||
* purposes, and assumes the caller has already performed a mass delete of
|
||||
* some form. Defaults to TRUE.
|
||||
*
|
||||
* @see node_access_write_grants()
|
||||
* @see node_access_acquire_grants()
|
||||
*/
|
||||
public function write(NodeInterface $node, array $grants, $realm = NULL, $delete = TRUE);
|
||||
|
||||
/**
|
||||
* Deletes all node access entries.
|
||||
*/
|
||||
public function delete();
|
||||
|
||||
/**
|
||||
* Creates the default node access grant entry.
|
||||
*/
|
||||
public function writeDefault();
|
||||
|
||||
/**
|
||||
* Determines access to nodes based on node grants.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The entity for which to check 'create' access.
|
||||
* @param string $operation
|
||||
* The entity operation. Usually one of 'view', 'edit', 'create' or
|
||||
* 'delete'.
|
||||
* @param string $langcode
|
||||
* The language code for which to check access.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user for which to check access.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result, either allowed or neutral. If there are no node
|
||||
* grants, the default grant defined by writeDefault() is applied.
|
||||
*
|
||||
* @see hook_node_grants()
|
||||
* @see hook_node_access_records()
|
||||
* @see \Drupal\node\NodeGrantDatabaseStorageInterface::writeDefault()
|
||||
*/
|
||||
public function access(NodeInterface $node, $operation, $langcode, AccountInterface $account);
|
||||
|
||||
/**
|
||||
* Counts available node grants.
|
||||
*
|
||||
* @return int
|
||||
* Returns the amount of node grants.
|
||||
*/
|
||||
public function count();
|
||||
|
||||
/**
|
||||
* Remove the access records belonging to certain nodes.
|
||||
*
|
||||
* @param array $nids
|
||||
* A list of node IDs. The grant records belonging to these nodes will be
|
||||
* deleted.
|
||||
*/
|
||||
public function deleteNodeRecords(array $nids);
|
||||
|
||||
}
|
171
core/modules/node/src/NodeInterface.php
Normal file
171
core/modules/node/src/NodeInterface.php
Normal file
|
@ -0,0 +1,171 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
use Drupal\Core\Entity\EntityChangedInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a node entity.
|
||||
*/
|
||||
interface NodeInterface extends ContentEntityInterface, EntityChangedInterface, EntityOwnerInterface {
|
||||
|
||||
/**
|
||||
* Gets the node type.
|
||||
*
|
||||
* @return string
|
||||
* The node type.
|
||||
*/
|
||||
public function getType();
|
||||
|
||||
/**
|
||||
* Gets the node title.
|
||||
*
|
||||
* @return string
|
||||
* Title of the node.
|
||||
*/
|
||||
public function getTitle();
|
||||
|
||||
/**
|
||||
* Sets the node title.
|
||||
*
|
||||
* @param string $title
|
||||
* The node title.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The called node entity.
|
||||
*/
|
||||
public function setTitle($title);
|
||||
|
||||
/**
|
||||
* Gets the node creation timestamp.
|
||||
*
|
||||
* @return int
|
||||
* Creation timestamp of the node.
|
||||
*/
|
||||
public function getCreatedTime();
|
||||
|
||||
/**
|
||||
* Sets the node creation timestamp.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* The node creation timestamp.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The called node entity.
|
||||
*/
|
||||
public function setCreatedTime($timestamp);
|
||||
|
||||
/**
|
||||
* Returns the node promotion status.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the node is promoted.
|
||||
*/
|
||||
public function isPromoted();
|
||||
|
||||
/**
|
||||
* Sets the node promoted status.
|
||||
*
|
||||
* @param bool $promoted
|
||||
* TRUE to set this node to promoted, FALSE to set it to not promoted.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The called node entity.
|
||||
*/
|
||||
public function setPromoted($promoted);
|
||||
|
||||
/**
|
||||
* Returns the node sticky status.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the node is sticky.
|
||||
*/
|
||||
public function isSticky();
|
||||
|
||||
/**
|
||||
* Sets the node sticky status.
|
||||
*
|
||||
* @param bool $sticky
|
||||
* TRUE to set this node to sticky, FALSE to set it to not sticky.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The called node entity.
|
||||
*/
|
||||
public function setSticky($sticky);
|
||||
|
||||
/**
|
||||
* Returns the node published status indicator.
|
||||
*
|
||||
* Unpublished nodes are only visible to their authors and to administrators.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the node is published.
|
||||
*/
|
||||
public function isPublished();
|
||||
|
||||
/**
|
||||
* Sets the published status of a node..
|
||||
*
|
||||
* @param bool $published
|
||||
* TRUE to set this node to published, FALSE to set it to unpublished.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The called node entity.
|
||||
*/
|
||||
public function setPublished($published);
|
||||
|
||||
/**
|
||||
* Gets the node revision creation timestamp.
|
||||
*
|
||||
* @return int
|
||||
* The UNIX timestamp of when this revision was created.
|
||||
*/
|
||||
public function getRevisionCreationTime();
|
||||
|
||||
/**
|
||||
* Sets the node revision creation timestamp.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* The UNIX timestamp of when this revision was created.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The called node entity.
|
||||
*/
|
||||
public function setRevisionCreationTime($timestamp);
|
||||
|
||||
/**
|
||||
* Gets the node revision author.
|
||||
*
|
||||
* @return \Drupal\user\UserInterface
|
||||
* The user entity for the revision author.
|
||||
*/
|
||||
public function getRevisionAuthor();
|
||||
|
||||
/**
|
||||
* Sets the node revision author.
|
||||
*
|
||||
* @param int $uid
|
||||
* The user ID of the revision author.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The called node entity.
|
||||
*/
|
||||
public function setRevisionAuthorId($uid);
|
||||
|
||||
/**
|
||||
* Prepares the langcode for a node.
|
||||
*
|
||||
* @return string
|
||||
* The langcode for this node.
|
||||
*/
|
||||
public function prepareLangcode();
|
||||
|
||||
}
|
150
core/modules/node/src/NodeListBuilder.php
Normal file
150
core/modules/node/src/NodeListBuilder.php
Normal file
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeListBuilder.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Datetime\DateFormatter;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityListBuilder;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Routing\RedirectDestinationInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a class to build a listing of node entities.
|
||||
*
|
||||
* @see \Drupal\node\Entity\Node
|
||||
*/
|
||||
class NodeListBuilder extends EntityListBuilder {
|
||||
|
||||
/**
|
||||
* The date formatter service.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\DateFormatter
|
||||
*/
|
||||
protected $dateFormatter;
|
||||
|
||||
/**
|
||||
* The redirect destination service.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RedirectDestinationInterface
|
||||
*/
|
||||
protected $redirectDestination;
|
||||
|
||||
/**
|
||||
* Constructs a new NodeListBuilder object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage class.
|
||||
* @param \Drupal\Core\Datetime\DateFormatter $date_formatter
|
||||
* The date formatter service.
|
||||
* @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
|
||||
* The redirect destination service.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, DateFormatter $date_formatter, RedirectDestinationInterface $redirect_destination) {
|
||||
parent::__construct($entity_type, $storage);
|
||||
|
||||
$this->dateFormatter = $date_formatter;
|
||||
$this->redirectDestination = $redirect_destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('entity.manager')->getStorage($entity_type->id()),
|
||||
$container->get('date.formatter'),
|
||||
$container->get('redirect.destination')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
// Enable language column and filter if multiple languages are added.
|
||||
$header = array(
|
||||
'title' => $this->t('Title'),
|
||||
'type' => array(
|
||||
'data' => $this->t('Content type'),
|
||||
'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
|
||||
),
|
||||
'author' => array(
|
||||
'data' => $this->t('Author'),
|
||||
'class' => array(RESPONSIVE_PRIORITY_LOW),
|
||||
),
|
||||
'status' => $this->t('Status'),
|
||||
'changed' => array(
|
||||
'data' => $this->t('Updated'),
|
||||
'class' => array(RESPONSIVE_PRIORITY_LOW),
|
||||
),
|
||||
);
|
||||
if (\Drupal::languageManager()->isMultilingual()) {
|
||||
$header['language_name'] = array(
|
||||
'data' => $this->t('Language'),
|
||||
'class' => array(RESPONSIVE_PRIORITY_LOW),
|
||||
);
|
||||
}
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
/** @var \Drupal\node\NodeInterface $entity */
|
||||
$mark = array(
|
||||
'#theme' => 'mark',
|
||||
'#mark_type' => node_mark($entity->id(), $entity->getChangedTime()),
|
||||
);
|
||||
$langcode = $entity->language()->getId();
|
||||
$uri = $entity->urlInfo();
|
||||
$options = $uri->getOptions();
|
||||
$options += ($langcode != LanguageInterface::LANGCODE_NOT_SPECIFIED && isset($languages[$langcode]) ? array('language' => $languages[$langcode]) : array());
|
||||
$uri->setOptions($options);
|
||||
$row['title']['data'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => $entity->label(),
|
||||
'#suffix' => ' ' . drupal_render($mark),
|
||||
'#url' => $uri,
|
||||
);
|
||||
$row['type'] = SafeMarkup::checkPlain(node_get_type_label($entity));
|
||||
$row['author']['data'] = array(
|
||||
'#theme' => 'username',
|
||||
'#account' => $entity->getOwner(),
|
||||
);
|
||||
$row['status'] = $entity->isPublished() ? $this->t('published') : $this->t('not published');
|
||||
$row['changed'] = $this->dateFormatter->format($entity->getChangedTime(), 'short');
|
||||
$language_manager = \Drupal::languageManager();
|
||||
if ($language_manager->isMultilingual()) {
|
||||
$row['language_name'] = $language_manager->getLanguageName($langcode);
|
||||
}
|
||||
$row['operations']['data'] = $this->buildOperations($entity);
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultOperations(EntityInterface $entity) {
|
||||
$operations = parent::getDefaultOperations($entity);
|
||||
|
||||
$destination = $this->redirectDestination->getAsArray();
|
||||
foreach ($operations as $key => $operation) {
|
||||
$operations[$key]['query'] = $destination;
|
||||
}
|
||||
return $operations;
|
||||
}
|
||||
|
||||
}
|
82
core/modules/node/src/NodePermissions.php
Normal file
82
core/modules/node/src/NodePermissions.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodePermissions.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Routing\UrlGeneratorTrait;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Defines a class containing permission callbacks.
|
||||
*/
|
||||
class NodePermissions {
|
||||
|
||||
use StringTranslationTrait;
|
||||
use UrlGeneratorTrait;
|
||||
|
||||
/**
|
||||
* Gets an array of node type permissions.
|
||||
*
|
||||
* @return array
|
||||
* The node type permissions.
|
||||
* @see \Drupal\user\PermissionHandlerInterface::getPermissions()
|
||||
*/
|
||||
public function nodeTypePermissions() {
|
||||
$perms = array();
|
||||
// Generate node permissions for all node types.
|
||||
foreach (NodeType::loadMultiple() as $type) {
|
||||
$perms += $this->buildPermissions($type);
|
||||
}
|
||||
|
||||
return $perms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a standard list of node permissions for a given type.
|
||||
*
|
||||
* @param \Drupal\node\Entity\NodeType $type
|
||||
* The machine name of the node type.
|
||||
*
|
||||
* @return array
|
||||
* An array of permission names and descriptions.
|
||||
*/
|
||||
protected function buildPermissions(NodeType $type) {
|
||||
$type_id = $type->id();
|
||||
$type_params = array('%type_name' => $type->label());
|
||||
|
||||
return array(
|
||||
"create $type_id content" => array(
|
||||
'title' => $this->t('%type_name: Create new content', $type_params),
|
||||
),
|
||||
"edit own $type_id content" => array(
|
||||
'title' => $this->t('%type_name: Edit own content', $type_params),
|
||||
),
|
||||
"edit any $type_id content" => array(
|
||||
'title' => $this->t('%type_name: Edit any content', $type_params),
|
||||
),
|
||||
"delete own $type_id content" => array(
|
||||
'title' => $this->t('%type_name: Delete own content', $type_params),
|
||||
),
|
||||
"delete any $type_id content" => array(
|
||||
'title' => $this->t('%type_name: Delete any content', $type_params),
|
||||
),
|
||||
"view $type_id revisions" => array(
|
||||
'title' => $this->t('%type_name: View revisions', $type_params),
|
||||
),
|
||||
"revert $type_id revisions" => array(
|
||||
'title' => $this->t('%type_name: Revert revisions', $type_params),
|
||||
'description' => t('Role requires permission <em>view revisions</em> and <em>edit rights</em> for nodes in question, or <em>administer nodes</em>.'),
|
||||
),
|
||||
"delete $type_id revisions" => array(
|
||||
'title' => $this->t('%type_name: Delete revisions', $type_params),
|
||||
'description' => $this->t('Role requires permission to <em>view revisions</em> and <em>delete rights</em> for nodes in question, or <em>administer nodes</em>.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
69
core/modules/node/src/NodeStorage.php
Normal file
69
core/modules/node/src/NodeStorage.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeStorage.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
* Defines the controller class for nodes.
|
||||
*
|
||||
* This extends the base storage class, adding required special handling for
|
||||
* node entities.
|
||||
*/
|
||||
class NodeStorage extends SqlContentEntityStorage implements NodeStorageInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function revisionIds(NodeInterface $node) {
|
||||
return $this->database->query(
|
||||
'SELECT vid FROM {node_revision} WHERE nid=:nid ORDER BY vid',
|
||||
array(':nid' => $node->id())
|
||||
)->fetchCol();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function userRevisionIds(AccountInterface $account) {
|
||||
return $this->database->query(
|
||||
'SELECT vid FROM {node_field_revision} WHERE uid = :uid ORDER BY vid',
|
||||
array(':uid' => $account->id())
|
||||
)->fetchCol();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function countDefaultLanguageRevisions(NodeInterface $node) {
|
||||
return $this->database->query('SELECT COUNT(*) FROM {node_field_revision} WHERE nid = :nid AND default_langcode = 1', array(':nid' => $node->id()))->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateType($old_type, $new_type) {
|
||||
return $this->database->update('node')
|
||||
->fields(array('type' => $new_type))
|
||||
->condition('type', $old_type)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clearRevisionsLanguage(LanguageInterface $language) {
|
||||
return $this->database->update('node_revision')
|
||||
->fields(array('langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED))
|
||||
->condition('langcode', $language->getId())
|
||||
->execute();
|
||||
}
|
||||
|
||||
}
|
72
core/modules/node/src/NodeStorageInterface.php
Normal file
72
core/modules/node/src/NodeStorageInterface.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeStorageInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Defines an interface for node entity storage classes.
|
||||
*/
|
||||
interface NodeStorageInterface extends EntityStorageInterface {
|
||||
|
||||
/**
|
||||
* Gets a list of node revision IDs for a specific node.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface
|
||||
* The node entity.
|
||||
*
|
||||
* @return int[]
|
||||
* Node revision IDs (in ascending order).
|
||||
*/
|
||||
public function revisionIds(NodeInterface $node);
|
||||
|
||||
/**
|
||||
* Gets a list of revision IDs having a given user as node author.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user entity.
|
||||
*
|
||||
* @return int[]
|
||||
* Node revision IDs (in ascending order).
|
||||
*/
|
||||
public function userRevisionIds(AccountInterface $account);
|
||||
|
||||
/**
|
||||
* Counts the number of revisions in the default language.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface
|
||||
* The node entity.
|
||||
*
|
||||
* @return int
|
||||
* The number of revisions in the default language.
|
||||
*/
|
||||
public function countDefaultLanguageRevisions(NodeInterface $node);
|
||||
|
||||
/**
|
||||
* Updates all nodes of one type to be of another type.
|
||||
*
|
||||
* @param string $old_type
|
||||
* The current node type of the nodes.
|
||||
* @param string $new_type
|
||||
* The new node type of the nodes.
|
||||
*
|
||||
* @return int
|
||||
* The number of nodes whose node type field was modified.
|
||||
*/
|
||||
public function updateType($old_type, $new_type);
|
||||
|
||||
/**
|
||||
* Unsets the language for all nodes with the given language.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageInterface $language
|
||||
* The language object.
|
||||
*/
|
||||
public function clearRevisionsLanguage(LanguageInterface $language);
|
||||
}
|
95
core/modules/node/src/NodeStorageSchema.php
Normal file
95
core/modules/node/src/NodeStorageSchema.php
Normal file
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeStorageSchema.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Defines the node schema handler.
|
||||
*/
|
||||
class NodeStorageSchema extends SqlContentEntityStorageSchema {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $reset = FALSE) {
|
||||
$schema = parent::getEntitySchema($entity_type, $reset);
|
||||
|
||||
// Marking the respective fields as NOT NULL makes the indexes more
|
||||
// performant.
|
||||
$schema['node_field_data']['fields']['default_langcode']['not null'] = TRUE;
|
||||
$schema['node_field_revision']['fields']['default_langcode']['not null'] = TRUE;
|
||||
|
||||
$schema['node_field_data']['indexes'] += array(
|
||||
'node__default_langcode' => array('default_langcode'),
|
||||
'node__frontpage' => array('promote', 'status', 'sticky', 'created'),
|
||||
'node__status_type' => array('status', 'type', 'nid'),
|
||||
'node__title_type' => array('title', array('type', 4)),
|
||||
);
|
||||
|
||||
$schema['node_field_revision']['indexes'] += array(
|
||||
'node__default_langcode' => array('default_langcode'),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $storage_definition, $table_name, array $column_mapping) {
|
||||
$schema = parent::getSharedTableFieldSchema($storage_definition, $table_name, $column_mapping);
|
||||
$field_name = $storage_definition->getName();
|
||||
|
||||
if ($table_name == 'node_revision') {
|
||||
switch ($field_name) {
|
||||
case 'langcode':
|
||||
$this->addSharedTableFieldIndex($storage_definition, $schema, TRUE);
|
||||
break;
|
||||
|
||||
case 'revision_uid':
|
||||
$this->addSharedTableFieldForeignKey($storage_definition, $schema, 'users', 'uid');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($table_name == 'node_field_data') {
|
||||
switch ($field_name) {
|
||||
case 'promote':
|
||||
case 'status':
|
||||
case 'sticky':
|
||||
case 'title':
|
||||
// Improves the performance of the indexes defined
|
||||
// in getEntitySchema().
|
||||
$schema['fields'][$field_name]['not null'] = TRUE;
|
||||
break;
|
||||
|
||||
case 'changed':
|
||||
case 'created':
|
||||
case 'langcode':
|
||||
// @todo Revisit index definitions:
|
||||
// https://www.drupal.org/node/2015277.
|
||||
$this->addSharedTableFieldIndex($storage_definition, $schema, TRUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($table_name == 'node_field_revision') {
|
||||
switch ($field_name) {
|
||||
case 'langcode':
|
||||
$this->addSharedTableFieldIndex($storage_definition, $schema, TRUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
}
|
91
core/modules/node/src/NodeTranslationHandler.php
Normal file
91
core/modules/node/src/NodeTranslationHandler.php
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeTranslationHandler.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\content_translation\ContentTranslationHandler;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Defines the translation handler for nodes.
|
||||
*/
|
||||
class NodeTranslationHandler extends ContentTranslationHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityFormAlter(array &$form, FormStateInterface $form_state, EntityInterface $entity) {
|
||||
parent::entityFormAlter($form, $form_state, $entity);
|
||||
|
||||
// Move the translation fieldset to a vertical tab.
|
||||
if (isset($form['content_translation'])) {
|
||||
$form['content_translation'] += array(
|
||||
'#group' => 'advanced',
|
||||
'#attributes' => array(
|
||||
'class' => array('node-translation-options'),
|
||||
),
|
||||
);
|
||||
|
||||
$form['content_translation']['#weight'] = 100;
|
||||
|
||||
// We do not need to show these values on node forms: they inherit the
|
||||
// basic node property values.
|
||||
$form['content_translation']['status']['#access'] = FALSE;
|
||||
$form['content_translation']['name']['#access'] = FALSE;
|
||||
$form['content_translation']['created']['#access'] = FALSE;
|
||||
}
|
||||
|
||||
$form_object = $form_state->getFormObject();
|
||||
$form_langcode = $form_object->getFormLangcode($form_state);
|
||||
$translations = $entity->getTranslationLanguages();
|
||||
$status_translatable = NULL;
|
||||
// Change the submit button labels if there was a status field they affect
|
||||
// in which case their publishing / unpublishing may or may not apply
|
||||
// to all translations.
|
||||
if (!$entity->isNew() && (!isset($translations[$form_langcode]) || count($translations) > 1)) {
|
||||
foreach ($entity->getFieldDefinitions() as $property_name => $definition) {
|
||||
if ($property_name == 'status') {
|
||||
$status_translatable = $definition->isTranslatable();
|
||||
}
|
||||
}
|
||||
if (isset($status_translatable)) {
|
||||
foreach (array('publish', 'unpublish', 'submit') as $button) {
|
||||
if (isset($form['actions'][$button])) {
|
||||
$form['actions'][$button]['#value'] .= ' ' . ($status_translatable ? t('(this translation)') : t('(all translations)'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function entityFormTitle(EntityInterface $entity) {
|
||||
$type_name = node_get_type_label($entity);
|
||||
return t('<em>Edit @type</em> @title', array('@type' => $type_name, '@title' => $entity->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityFormEntityBuild($entity_type, EntityInterface $entity, array $form, FormStateInterface $form_state) {
|
||||
if ($form_state->hasValue('content_translation')) {
|
||||
$form_object = $form_state->getFormObject();
|
||||
$translation = &$form_state->getValue('content_translation');
|
||||
$translation['status'] = $form_object->getEntity()->isPublished();
|
||||
// $form['content_translation']['name'] is the equivalent field
|
||||
// for translation author uid.
|
||||
$account = $entity->uid->entity;
|
||||
$translation['name'] = $account ? $account->getUsername() : '';
|
||||
$translation['created'] = format_date($entity->created->value, 'custom', 'Y-m-d H:i:s O');
|
||||
}
|
||||
parent::entityFormEntityBuild($entity_type, $entity, $form, $form_state);
|
||||
}
|
||||
|
||||
}
|
37
core/modules/node/src/NodeTypeAccessControlHandler.php
Normal file
37
core/modules/node/src/NodeTypeAccessControlHandler.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeTypeAccessControlHandler.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Defines the access control handler for the node type entity type.
|
||||
*
|
||||
* @see \Drupal\node\Entity\NodeType
|
||||
*/
|
||||
class NodeTypeAccessControlHandler extends EntityAccessControlHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
|
||||
if ($operation == 'delete') {
|
||||
if ($entity->isLocked()) {
|
||||
return AccessResult::forbidden()->cacheUntilEntityChanges($entity);
|
||||
}
|
||||
else {
|
||||
return parent::checkAccess($entity, $operation, $langcode, $account)->cacheUntilEntityChanges($entity);
|
||||
}
|
||||
}
|
||||
return parent::checkAccess($entity, $operation, $langcode, $account);
|
||||
}
|
||||
|
||||
}
|
262
core/modules/node/src/NodeTypeForm.php
Normal file
262
core/modules/node/src/NodeTypeForm.php
Normal file
|
@ -0,0 +1,262 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeTypeForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\language\Entity\ContentLanguageSettings;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Form controller for node type forms.
|
||||
*/
|
||||
class NodeTypeForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Constructs the NodeTypeForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager) {
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
$type = $this->entity;
|
||||
if ($this->operation == 'add') {
|
||||
$form['#title'] = SafeMarkup::checkPlain($this->t('Add content type'));
|
||||
$fields = $this->entityManager->getBaseFieldDefinitions('node');
|
||||
// Create a node with a fake bundle using the type's UUID so that we can
|
||||
// get the default values for workflow settings.
|
||||
// @todo Make it possible to get default values without an entity.
|
||||
// https://www.drupal.org/node/2318187
|
||||
$node = $this->entityManager->getStorage('node')->create(array('type' => $type->uuid()));
|
||||
}
|
||||
else {
|
||||
$form['#title'] = $this->t('Edit %label content type', array('%label' => $type->label()));
|
||||
$fields = $this->entityManager->getFieldDefinitions('node', $type->id());
|
||||
// Create a node to get the current values for workflow settings fields.
|
||||
$node = $this->entityManager->getStorage('node')->create(array('type' => $type->id()));
|
||||
}
|
||||
|
||||
$form['name'] = array(
|
||||
'#title' => t('Name'),
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => $type->label(),
|
||||
'#description' => t('The human-readable name of this content type. This text will be displayed as part of the list on the <em>Add content</em> page. This name must be unique.'),
|
||||
'#required' => TRUE,
|
||||
'#size' => 30,
|
||||
);
|
||||
|
||||
$form['type'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#default_value' => $type->id(),
|
||||
'#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
|
||||
'#disabled' => $type->isLocked(),
|
||||
'#machine_name' => array(
|
||||
'exists' => ['Drupal\node\Entity\NodeType', 'load'],
|
||||
'source' => array('name'),
|
||||
),
|
||||
'#description' => t('A unique machine-readable name for this content type. It must only contain lowercase letters, numbers, and underscores. This name will be used for constructing the URL of the %node-add page, in which underscores will be converted into hyphens.', array(
|
||||
'%node-add' => t('Add content'),
|
||||
)),
|
||||
);
|
||||
|
||||
$form['description'] = array(
|
||||
'#title' => t('Description'),
|
||||
'#type' => 'textarea',
|
||||
'#default_value' => $type->getDescription(),
|
||||
'#description' => t('Describe this content type. The text will be displayed on the <em>Add content</em> page.'),
|
||||
);
|
||||
|
||||
$form['additional_settings'] = array(
|
||||
'#type' => 'vertical_tabs',
|
||||
'#attached' => array(
|
||||
'library' => array('node/drupal.content_types'),
|
||||
),
|
||||
);
|
||||
|
||||
$form['submission'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Submission form settings'),
|
||||
'#group' => 'additional_settings',
|
||||
'#open' => TRUE,
|
||||
);
|
||||
$form['submission']['title_label'] = array(
|
||||
'#title' => t('Title field label'),
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => $fields['title']->getLabel(),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['submission']['preview_mode'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Preview before submitting'),
|
||||
'#default_value' => $type->getPreviewMode(),
|
||||
'#options' => array(
|
||||
DRUPAL_DISABLED => t('Disabled'),
|
||||
DRUPAL_OPTIONAL => t('Optional'),
|
||||
DRUPAL_REQUIRED => t('Required'),
|
||||
),
|
||||
);
|
||||
$form['submission']['help'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Explanation or submission guidelines'),
|
||||
'#default_value' => $type->getHelp(),
|
||||
'#description' => t('This text will be displayed at the top of the page when creating or editing content of this type.'),
|
||||
);
|
||||
$form['workflow'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Publishing options'),
|
||||
'#group' => 'additional_settings',
|
||||
);
|
||||
$workflow_options = array(
|
||||
'status' => $node->status->value,
|
||||
'promote' => $node->promote->value,
|
||||
'sticky' => $node->sticky->value,
|
||||
'revision' => $type->isNewRevision(),
|
||||
);
|
||||
// Prepare workflow options to be used for 'checkboxes' form element.
|
||||
$keys = array_keys(array_filter($workflow_options));
|
||||
$workflow_options = array_combine($keys, $keys);
|
||||
$form['workflow']['options'] = array('#type' => 'checkboxes',
|
||||
'#title' => t('Default options'),
|
||||
'#default_value' => $workflow_options,
|
||||
'#options' => array(
|
||||
'status' => t('Published'),
|
||||
'promote' => t('Promoted to front page'),
|
||||
'sticky' => t('Sticky at top of lists'),
|
||||
'revision' => t('Create new revision'),
|
||||
),
|
||||
'#description' => t('Users with the <em>Administer content</em> permission will be able to override these options.'),
|
||||
);
|
||||
if ($this->moduleHandler->moduleExists('language')) {
|
||||
$form['language'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Language settings'),
|
||||
'#group' => 'additional_settings',
|
||||
);
|
||||
|
||||
$language_configuration = ContentLanguageSettings::loadByEntityTypeBundle('node', $type->id());
|
||||
$form['language']['language_configuration'] = array(
|
||||
'#type' => 'language_configuration',
|
||||
'#entity_information' => array(
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type->id(),
|
||||
),
|
||||
'#default_value' => $language_configuration,
|
||||
);
|
||||
}
|
||||
$form['display'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Display settings'),
|
||||
'#group' => 'additional_settings',
|
||||
);
|
||||
$form['display']['display_submitted'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Display author and date information'),
|
||||
'#default_value' => $type->displaySubmitted(),
|
||||
'#description' => t('Author username and publish date will be displayed.'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = t('Save content type');
|
||||
$actions['delete']['#value'] = t('Delete content type');
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate(array $form, FormStateInterface $form_state) {
|
||||
parent::validate($form, $form_state);
|
||||
|
||||
$id = trim($form_state->getValue('type'));
|
||||
// '0' is invalid, since elsewhere we check it using empty().
|
||||
if ($id == '0') {
|
||||
$form_state->setErrorByName('type', $this->t("Invalid machine-readable name. Enter a name other than %invalid.", array('%invalid' => $id)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$type = $this->entity;
|
||||
$type->setNewRevision($form_state->getValue(array('options', 'revision')));
|
||||
$type->set('type', trim($type->id()));
|
||||
$type->set('name', trim($type->label()));
|
||||
|
||||
$status = $type->save();
|
||||
|
||||
$t_args = array('%name' => $type->label());
|
||||
|
||||
if ($status == SAVED_UPDATED) {
|
||||
drupal_set_message(t('The content type %name has been updated.', $t_args));
|
||||
}
|
||||
elseif ($status == SAVED_NEW) {
|
||||
node_add_body_field($type);
|
||||
drupal_set_message(t('The content type %name has been added.', $t_args));
|
||||
$context = array_merge($t_args, array('link' => $type->link($this->t('View'), 'collection')));
|
||||
$this->logger('node')->notice('Added content type %name.', $context);
|
||||
}
|
||||
|
||||
$fields = $this->entityManager->getFieldDefinitions('node', $type->id());
|
||||
// Update title field definition.
|
||||
$title_field = $fields['title'];
|
||||
$title_label = $form_state->getValue('title_label');
|
||||
if ($title_field->getLabel() != $title_label) {
|
||||
$title_field->getConfig($type->id())->setLabel($title_label)->save();
|
||||
}
|
||||
// Update workflow options.
|
||||
// @todo Make it possible to get default values without an entity.
|
||||
// https://www.drupal.org/node/2318187
|
||||
$node = $this->entityManager->getStorage('node')->create(array('type' => $type->id()));
|
||||
foreach (array('status', 'promote', 'sticky') as $field_name) {
|
||||
$value = (bool) $form_state->getValue(['options', $field_name]);
|
||||
if ($node->$field_name->value != $value) {
|
||||
$fields[$field_name]->getConfig($type->id())->setDefaultValue($value)->save();
|
||||
}
|
||||
}
|
||||
|
||||
$this->entityManager->clearCachedFieldDefinitions();
|
||||
$form_state->setRedirectUrl($type->urlInfo('collection'));
|
||||
}
|
||||
|
||||
}
|
88
core/modules/node/src/NodeTypeInterface.php
Normal file
88
core/modules/node/src/NodeTypeInterface.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeTypeInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a node type entity.
|
||||
*/
|
||||
interface NodeTypeInterface extends ConfigEntityInterface {
|
||||
|
||||
/**
|
||||
* Determines whether the node type is locked.
|
||||
*
|
||||
* @return string|false
|
||||
* The module name that locks the type or FALSE.
|
||||
*/
|
||||
public function isLocked();
|
||||
|
||||
/**
|
||||
* Gets whether a new revision should be created by default.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if a new revision should be created by default.
|
||||
*/
|
||||
public function isNewRevision();
|
||||
|
||||
/**
|
||||
* Sets whether a new revision should be created by default.
|
||||
*
|
||||
* @param bool $new_revision_
|
||||
* TRUE if a new revision should be created by default.
|
||||
*/
|
||||
public function setNewRevision($new_revision);
|
||||
|
||||
/**
|
||||
* Gets whether 'Submitted by' information should be shown.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the submitted by information should be shown.
|
||||
*/
|
||||
public function displaySubmitted();
|
||||
|
||||
/**
|
||||
* Sets whether 'Submitted by' information should be shown.
|
||||
*
|
||||
* @param bool $display_submitted
|
||||
* TRUE if the submitted by information should be shown.
|
||||
*/
|
||||
public function setDisplaySubmitted($display_submitted);
|
||||
|
||||
/**
|
||||
* Gets the preview mode.
|
||||
*
|
||||
* @return int
|
||||
* DRUPAL_DISABLED, DRUPAL_OPTIONAL or DRUPAL_REQUIRED.
|
||||
*/
|
||||
public function getPreviewMode();
|
||||
|
||||
/**
|
||||
* Sets the preview mode.
|
||||
*
|
||||
* @param int $preview_mode
|
||||
* DRUPAL_DISABLED, DRUPAL_OPTIONAL or DRUPAL_REQUIRED.
|
||||
*/
|
||||
public function setPreviewMode($preview_mode);
|
||||
|
||||
/**
|
||||
* Gets the help information.
|
||||
*
|
||||
* @return string
|
||||
* The help information of this node type.
|
||||
*/
|
||||
public function getHelp();
|
||||
|
||||
/**
|
||||
* Gets the description.
|
||||
*
|
||||
* @return string
|
||||
* The description of this node type.
|
||||
*/
|
||||
public function getDescription();
|
||||
}
|
70
core/modules/node/src/NodeTypeListBuilder.php
Normal file
70
core/modules/node/src/NodeTypeListBuilder.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeTypeListBuilder.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
|
||||
/**
|
||||
* Defines a class to build a listing of node type entities.
|
||||
*
|
||||
* @see \Drupal\node\Entity\NodeType
|
||||
*/
|
||||
class NodeTypeListBuilder extends ConfigEntityListBuilder {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['title'] = t('Name');
|
||||
$header['description'] = array(
|
||||
'data' => t('Description'),
|
||||
'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
|
||||
);
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
$row['title'] = array(
|
||||
'data' => $this->getLabel($entity),
|
||||
'class' => array('menu-label'),
|
||||
);
|
||||
$row['description'] = Xss::filterAdmin($entity->getDescription());
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultOperations(EntityInterface $entity) {
|
||||
$operations = parent::getDefaultOperations($entity);
|
||||
// Place the edit operation after the operations added by field_ui.module
|
||||
// which have the weights 15, 20, 25.
|
||||
if (isset($operations['edit'])) {
|
||||
$operations['edit']['weight'] = 30;
|
||||
}
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render() {
|
||||
$build = parent::render();
|
||||
$build['table']['#empty'] = $this->t('No content types available. <a href="@link">Add content type</a>.', [
|
||||
'@link' => Url::fromRoute('node.type_add')->toString()
|
||||
]);
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
163
core/modules/node/src/NodeViewBuilder.php
Normal file
163
core/modules/node/src/NodeViewBuilder.php
Normal file
|
@ -0,0 +1,163 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeViewBuilder.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityViewBuilder;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Render controller for nodes.
|
||||
*/
|
||||
class NodeViewBuilder extends EntityViewBuilder {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildComponents(array &$build, array $entities, array $displays, $view_mode, $langcode = NULL) {
|
||||
/** @var \Drupal\node\NodeInterface[] $entities */
|
||||
if (empty($entities)) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::buildComponents($build, $entities, $displays, $view_mode, $langcode);
|
||||
|
||||
foreach ($entities as $id => $entity) {
|
||||
$bundle = $entity->bundle();
|
||||
$display = $displays[$bundle];
|
||||
|
||||
if ($display->getComponent('links')) {
|
||||
$build[$id]['links'] = array(
|
||||
'#lazy_builder' => [get_called_class() . '::renderLinks', [
|
||||
$entity->id(),
|
||||
$view_mode,
|
||||
$langcode,
|
||||
!empty($entity->in_preview),
|
||||
]],
|
||||
'#create_placeholder' => TRUE,
|
||||
);
|
||||
}
|
||||
|
||||
// Add Language field text element to node render array.
|
||||
if ($display->getComponent('langcode')) {
|
||||
$build[$id]['langcode'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Language'),
|
||||
'#markup' => $entity->language()->getName(),
|
||||
'#prefix' => '<div id="field-language-display">',
|
||||
'#suffix' => '</div>'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) {
|
||||
$defaults = parent::getBuildDefaults($entity, $view_mode, $langcode);
|
||||
|
||||
// Don't cache nodes that are in 'preview' mode.
|
||||
if (isset($defaults['#cache']) && isset($entity->in_preview)) {
|
||||
unset($defaults['#cache']);
|
||||
}
|
||||
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* #lazy_builder callback; builds a node's links.
|
||||
*
|
||||
* @param string $node_entity_id
|
||||
* The node entity ID.
|
||||
* @param string $view_mode
|
||||
* The view mode in which the node entity is being viewed.
|
||||
* @param string $langcode
|
||||
* The language in which the node entity is being viewed.
|
||||
* @param bool $is_in_preview
|
||||
* Whether the node is currently being previewed.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array representing the node links.
|
||||
*/
|
||||
public static function renderLinks($node_entity_id, $view_mode, $langcode, $is_in_preview) {
|
||||
$links = array(
|
||||
'#theme' => 'links__node',
|
||||
'#pre_render' => array('drupal_pre_render_links'),
|
||||
'#attributes' => array('class' => array('links', 'inline')),
|
||||
);
|
||||
|
||||
if (!$is_in_preview) {
|
||||
$entity = Node::load($node_entity_id)->getTranslation($langcode);
|
||||
$links['node'] = static::buildLinks($entity, $view_mode);
|
||||
|
||||
// Allow other modules to alter the node links.
|
||||
$hook_context = array(
|
||||
'view_mode' => $view_mode,
|
||||
'langcode' => $langcode,
|
||||
);
|
||||
\Drupal::moduleHandler()->alter('node_links', $links, $entity, $hook_context);
|
||||
}
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the default links (Read more) for a node.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $entity
|
||||
* The node object.
|
||||
* @param string $view_mode
|
||||
* A view mode identifier.
|
||||
*
|
||||
* @return array
|
||||
* An array that can be processed by drupal_pre_render_links().
|
||||
*/
|
||||
protected static function buildLinks(NodeInterface $entity, $view_mode) {
|
||||
$links = array();
|
||||
|
||||
// Always display a read more link on teasers because we have no way
|
||||
// to know when a teaser view is different than a full view.
|
||||
if ($view_mode == 'teaser') {
|
||||
$node_title_stripped = strip_tags($entity->label());
|
||||
$links['node-readmore'] = array(
|
||||
'title' => t('Read more<span class="visually-hidden"> about @title</span>', array(
|
||||
'@title' => $node_title_stripped,
|
||||
)),
|
||||
'url' => $entity->urlInfo(),
|
||||
'language' => $entity->language(),
|
||||
'attributes' => array(
|
||||
'rel' => 'tag',
|
||||
'title' => $node_title_stripped,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'#theme' => 'links__node__node',
|
||||
'#links' => $links,
|
||||
'#attributes' => array('class' => array('links', 'inline')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function alterBuild(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode = NULL) {
|
||||
/** @var \Drupal\node\NodeInterface $entity */
|
||||
parent::alterBuild($build, $entity, $display, $view_mode, $langcode);
|
||||
if ($entity->id()) {
|
||||
$build['#contextual_links']['node'] = array(
|
||||
'route_parameters' =>array('node' => $entity->id()),
|
||||
'metadata' => array('changed' => $entity->getChangedTime()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
388
core/modules/node/src/NodeViewsData.php
Normal file
388
core/modules/node/src/NodeViewsData.php
Normal file
|
@ -0,0 +1,388 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\NodeViewsData.
|
||||
*/
|
||||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\views\EntityViewsData;
|
||||
|
||||
/**
|
||||
* Provides the views data for the node entity type.
|
||||
*/
|
||||
class NodeViewsData extends EntityViewsData {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getViewsData() {
|
||||
$data = parent::getViewsData();
|
||||
|
||||
$data['node_field_data']['table']['base']['weight'] = -10;
|
||||
$data['node_field_data']['table']['base']['access query tag'] = 'node_access';
|
||||
$data['node_field_data']['table']['wizard_id'] = 'node';
|
||||
|
||||
$data['node_field_data']['nid']['field']['argument'] = [
|
||||
'id' => 'node_nid',
|
||||
'name field' => 'title',
|
||||
'numeric' => TRUE,
|
||||
'validate type' => 'nid',
|
||||
];
|
||||
|
||||
$data['node_field_data']['title']['field']['default_formatter_settings'] = ['link_to_entity' => TRUE];
|
||||
|
||||
$data['node_field_data']['title']['field']['link_to_node default'] = TRUE;
|
||||
|
||||
$data['node_field_data']['type']['argument']['id'] = 'node_type';
|
||||
|
||||
$data['node_field_data']['langcode']['help'] = t('The language of the content or translation.');
|
||||
|
||||
$data['node_field_data']['status']['filter']['label'] = t('Published status');
|
||||
$data['node_field_data']['status']['filter']['type'] = 'yes-no';
|
||||
// Use status = 1 instead of status <> 0 in WHERE statement.
|
||||
$data['node_field_data']['status']['filter']['use_equal'] = TRUE;
|
||||
|
||||
$data['node_field_data']['status_extra'] = array(
|
||||
'title' => t('Published status or admin user'),
|
||||
'help' => t('Filters out unpublished content if the current user cannot view it.'),
|
||||
'filter' => array(
|
||||
'field' => 'status',
|
||||
'id' => 'node_status',
|
||||
'label' => t('Published status or admin user'),
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['promote']['filter']['label'] = t('Promoted to front page status');
|
||||
$data['node_field_data']['promote']['filter']['type'] = 'yes-no';
|
||||
|
||||
$data['node_field_data']['sticky']['filter']['label'] = t('Sticky status');
|
||||
$data['node_field_data']['sticky']['filter']['type'] = 'yes-no';
|
||||
$data['node_field_data']['sticky']['sort']['help'] = t('Whether or not the content is sticky. To list sticky content first, set this to descending.');
|
||||
|
||||
$data['node']['path'] = array(
|
||||
'field' => array(
|
||||
'title' => t('Path'),
|
||||
'help' => t('The aliased path to this content.'),
|
||||
'id' => 'node_path',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node']['node_bulk_form'] = array(
|
||||
'title' => t('Node operations bulk form'),
|
||||
'help' => t('Add a form element that lets you run operations on multiple nodes.'),
|
||||
'field' => array(
|
||||
'id' => 'node_bulk_form',
|
||||
),
|
||||
);
|
||||
|
||||
// Bogus fields for aliasing purposes.
|
||||
|
||||
// @todo Add similar support to any date field
|
||||
// @see https://www.drupal.org/node/2337507
|
||||
$data['node_field_data']['created_fulldate'] = array(
|
||||
'title' => t('Created date'),
|
||||
'help' => t('Date in the form of CCYYMMDD.'),
|
||||
'argument' => array(
|
||||
'field' => 'created',
|
||||
'id' => 'date_fulldate',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['created_year_month'] = array(
|
||||
'title' => t('Created year + month'),
|
||||
'help' => t('Date in the form of YYYYMM.'),
|
||||
'argument' => array(
|
||||
'field' => 'created',
|
||||
'id' => 'date_year_month',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['created_year'] = array(
|
||||
'title' => t('Created year'),
|
||||
'help' => t('Date in the form of YYYY.'),
|
||||
'argument' => array(
|
||||
'field' => 'created',
|
||||
'id' => 'date_year',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['created_month'] = array(
|
||||
'title' => t('Created month'),
|
||||
'help' => t('Date in the form of MM (01 - 12).'),
|
||||
'argument' => array(
|
||||
'field' => 'created',
|
||||
'id' => 'date_month',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['created_day'] = array(
|
||||
'title' => t('Created day'),
|
||||
'help' => t('Date in the form of DD (01 - 31).'),
|
||||
'argument' => array(
|
||||
'field' => 'created',
|
||||
'id' => 'date_day',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['created_week'] = array(
|
||||
'title' => t('Created week'),
|
||||
'help' => t('Date in the form of WW (01 - 53).'),
|
||||
'argument' => array(
|
||||
'field' => 'created',
|
||||
'id' => 'date_week',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['changed_fulldate'] = array(
|
||||
'title' => t('Updated date'),
|
||||
'help' => t('Date in the form of CCYYMMDD.'),
|
||||
'argument' => array(
|
||||
'field' => 'changed',
|
||||
'id' => 'date_fulldate',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['changed_year_month'] = array(
|
||||
'title' => t('Updated year + month'),
|
||||
'help' => t('Date in the form of YYYYMM.'),
|
||||
'argument' => array(
|
||||
'field' => 'changed',
|
||||
'id' => 'date_year_month',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['changed_year'] = array(
|
||||
'title' => t('Updated year'),
|
||||
'help' => t('Date in the form of YYYY.'),
|
||||
'argument' => array(
|
||||
'field' => 'changed',
|
||||
'id' => 'date_year',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['changed_month'] = array(
|
||||
'title' => t('Updated month'),
|
||||
'help' => t('Date in the form of MM (01 - 12).'),
|
||||
'argument' => array(
|
||||
'field' => 'changed',
|
||||
'id' => 'date_month',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['changed_day'] = array(
|
||||
'title' => t('Updated day'),
|
||||
'help' => t('Date in the form of DD (01 - 31).'),
|
||||
'argument' => array(
|
||||
'field' => 'changed',
|
||||
'id' => 'date_day',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['changed_week'] = array(
|
||||
'title' => t('Updated week'),
|
||||
'help' => t('Date in the form of WW (01 - 53).'),
|
||||
'argument' => array(
|
||||
'field' => 'changed',
|
||||
'id' => 'date_week',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['uid']['help'] = t('The user authoring the content. If you need more fields than the uid add the content: author relationship');
|
||||
$data['node_field_data']['uid']['filter']['id'] = 'user_name';
|
||||
$data['node_field_data']['uid']['relationship']['title'] = t('Content author');
|
||||
$data['node_field_data']['uid']['relationship']['help'] = t('Relate content to the user who created it.');
|
||||
$data['node_field_data']['uid']['relationship']['label'] = t('author');
|
||||
|
||||
$data['node']['node_listing_empty'] = array(
|
||||
'title' => t('Empty Node Frontpage behavior'),
|
||||
'help' => t('Provides a link to the node add overview page.'),
|
||||
'area' => array(
|
||||
'id' => 'node_listing_empty',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['uid_revision']['title'] = t('User has a revision');
|
||||
$data['node_field_data']['uid_revision']['help'] = t('All nodes where a certain user has a revision');
|
||||
$data['node_field_data']['uid_revision']['real field'] = 'nid';
|
||||
$data['node_field_data']['uid_revision']['filter']['id'] = 'node_uid_revision';
|
||||
$data['node_field_data']['uid_revision']['argument']['id'] = 'node_uid_revision';
|
||||
|
||||
$data['node_field_revision']['table']['wizard_id'] = 'node_revision';
|
||||
|
||||
// Advertise this table as a possible base table.
|
||||
$data['node_field_revision']['table']['base']['help'] = t('Content revision is a history of changes to content.');
|
||||
$data['node_field_revision']['table']['base']['defaults']['title'] = 'title';
|
||||
|
||||
$data['node_field_revision']['nid']['argument'] = [
|
||||
'id' => 'node_nid',
|
||||
'numeric' => TRUE,
|
||||
];
|
||||
// @todo the NID field needs different behaviour on revision/non-revision
|
||||
// tables. It would be neat if this could be encoded in the base field
|
||||
// definition.
|
||||
$data['node_field_revision']['nid']['relationship']['id'] = 'standard';
|
||||
$data['node_field_revision']['nid']['relationship']['base'] = 'node_field_data';
|
||||
$data['node_field_revision']['nid']['relationship']['base field'] = 'nid';
|
||||
$data['node_field_revision']['nid']['relationship']['title'] = t('Content');
|
||||
$data['node_field_revision']['nid']['relationship']['label'] = t('Get the actual content from a content revision.');
|
||||
|
||||
$data['node_field_revision']['vid'] = array(
|
||||
'argument' => array(
|
||||
'id' => 'node_vid',
|
||||
'numeric' => TRUE,
|
||||
),
|
||||
'relationship' => array(
|
||||
'id' => 'standard',
|
||||
'base' => 'node_field_data',
|
||||
'base field' => 'vid',
|
||||
'title' => t('Content'),
|
||||
'label' => t('Get the actual content from a content revision.'),
|
||||
),
|
||||
) + $data['node_revision']['vid'];
|
||||
|
||||
$data['node_field_revision']['langcode']['help'] = t('The language the original content is in.');
|
||||
|
||||
$data['node_revision']['revision_uid']['help'] = t('Relate a content revision to the user who created the revision.');
|
||||
$data['node_revision']['revision_uid']['relationship']['label'] = t('revision user');
|
||||
|
||||
$data['node_field_revision']['table']['wizard_id'] = 'node_field_revision';
|
||||
|
||||
$data['node_field_revision']['table']['join']['node_field_data']['left_field'] = 'vid';
|
||||
$data['node_field_revision']['table']['join']['node_field_data']['field'] = 'vid';
|
||||
|
||||
$data['node_field_revision']['status']['filter']['label'] = t('Published');
|
||||
$data['node_field_revision']['status']['filter']['type'] = 'yes-no';
|
||||
$data['node_field_revision']['status']['filter']['use_equal'] = TRUE;
|
||||
|
||||
$data['node_field_revision']['langcode']['help'] = t('The language of the content or translation.');
|
||||
|
||||
$data['node_field_revision']['link_to_revision'] = array(
|
||||
'field' => array(
|
||||
'title' => t('Link to revision'),
|
||||
'help' => t('Provide a simple link to the revision.'),
|
||||
'id' => 'node_revision_link',
|
||||
'click sortable' => FALSE,
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_revision']['revert_revision'] = array(
|
||||
'field' => array(
|
||||
'title' => t('Link to revert revision'),
|
||||
'help' => t('Provide a simple link to revert to the revision.'),
|
||||
'id' => 'node_revision_link_revert',
|
||||
'click sortable' => FALSE,
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_revision']['delete_revision'] = array(
|
||||
'field' => array(
|
||||
'title' => t('Link to delete revision'),
|
||||
'help' => t('Provide a simple link to delete the content revision.'),
|
||||
'id' => 'node_revision_link_delete',
|
||||
'click sortable' => FALSE,
|
||||
),
|
||||
);
|
||||
|
||||
// Define the base group of this table. Fields that don't have a group defined
|
||||
// will go into this field by default.
|
||||
$data['node_access']['table']['group'] = t('Content access');
|
||||
|
||||
// For other base tables, explain how we join.
|
||||
$data['node_access']['table']['join'] = array(
|
||||
'node_field_data' => array(
|
||||
'left_field' => 'nid',
|
||||
'field' => 'nid',
|
||||
),
|
||||
);
|
||||
$data['node_access']['nid'] = array(
|
||||
'title' => t('Access'),
|
||||
'help' => t('Filter by access.'),
|
||||
'filter' => array(
|
||||
'id' => 'node_access',
|
||||
'help' => t('Filter for content by view access. <strong>Not necessary if you are using node as your base table.</strong>'),
|
||||
),
|
||||
);
|
||||
|
||||
// Add search table, fields, filters, etc., but only if a page using the
|
||||
// node_search plugin is enabled.
|
||||
if (\Drupal::moduleHandler()->moduleExists('search')) {
|
||||
$enabled = FALSE;
|
||||
$search_page_repository = \Drupal::service('search.search_page_repository');
|
||||
foreach ($search_page_repository->getActiveSearchpages() as $page) {
|
||||
if ($page->getPlugin()->getPluginId() == 'node_search') {
|
||||
$enabled = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($enabled) {
|
||||
$data['node_search_index']['table']['group'] = t('Search');
|
||||
|
||||
// Automatically join to the node table (or actually, node_field_data).
|
||||
// Use a Views table alias to allow other modules to use this table too,
|
||||
// if they use the search index.
|
||||
$data['node_search_index']['table']['join'] = array(
|
||||
'node_field_data' => array(
|
||||
'left_field' => 'nid',
|
||||
'field' => 'sid',
|
||||
'table' => 'search_index',
|
||||
'extra' => "node_search_index.type = 'node_search' AND node_search_index.langcode = node_field_data.langcode",
|
||||
)
|
||||
);
|
||||
|
||||
$data['node_search_total']['table']['join'] = array(
|
||||
'node_search_index' => array(
|
||||
'left_field' => 'word',
|
||||
'field' => 'word',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_search_dataset']['table']['join'] = array(
|
||||
'node_field_data' => array(
|
||||
'left_field' => 'sid',
|
||||
'left_table' => 'node_search_index',
|
||||
'field' => 'sid',
|
||||
'table' => 'search_dataset',
|
||||
'extra' => 'node_search_index.type = node_search_dataset.type AND node_search_index.langcode = node_search_dataset.langcode',
|
||||
'type' => 'INNER',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_search_index']['score'] = array(
|
||||
'title' => t('Score'),
|
||||
'help' => t('The score of the search item. This will not be used if the search filter is not also present.'),
|
||||
'field' => array(
|
||||
'id' => 'search_score',
|
||||
'float' => TRUE,
|
||||
'no group by' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'id' => 'search_score',
|
||||
'no group by' => TRUE,
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_search_index']['keys'] = array(
|
||||
'title' => t('Search Keywords'),
|
||||
'help' => t('The keywords to search for.'),
|
||||
'filter' => array(
|
||||
'id' => 'search_keywords',
|
||||
'no group by' => TRUE,
|
||||
'search_type' => 'node_search',
|
||||
),
|
||||
'argument' => array(
|
||||
'id' => 'search',
|
||||
'no group by' => TRUE,
|
||||
'search_type' => 'node_search',
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
49
core/modules/node/src/PageCache/DenyNodePreview.php
Normal file
49
core/modules/node/src/PageCache/DenyNodePreview.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\PageCache\DenyNodePreview.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\PageCache;
|
||||
|
||||
use Drupal\Core\PageCache\ResponsePolicyInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Cache policy for node preview page.
|
||||
*
|
||||
* This policy rule denies caching of responses generated by the
|
||||
* entity.node.preview route.
|
||||
*/
|
||||
class DenyNodePreview implements ResponsePolicyInterface {
|
||||
|
||||
/**
|
||||
* The current route match.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RouteMatchInterface
|
||||
*/
|
||||
protected $routeMatch;
|
||||
|
||||
/**
|
||||
* Constructs a deny node preview page cache policy.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The current route match.
|
||||
*/
|
||||
public function __construct(RouteMatchInterface $route_match) {
|
||||
$this->routeMatch = $route_match;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function check(Response $response, Request $request) {
|
||||
if ($this->routeMatch->getRouteName() === 'entity.node.preview') {
|
||||
return static::DENY;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\ParamConverter\NodePreviewConverter.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\ParamConverter;
|
||||
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\user\PrivateTempStoreFactory;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Drupal\Core\ParamConverter\ParamConverterInterface;
|
||||
|
||||
/**
|
||||
* Provides upcasting for a node entity in preview.
|
||||
*/
|
||||
class NodePreviewConverter implements ParamConverterInterface {
|
||||
|
||||
/**
|
||||
* Stores the tempstore factory.
|
||||
*
|
||||
* @var \Drupal\user\PrivateTempStoreFactory
|
||||
*/
|
||||
protected $tempStoreFactory;
|
||||
|
||||
/**
|
||||
* Constructs a new NodePreviewConverter.
|
||||
*
|
||||
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
|
||||
* The factory for the temp store object.
|
||||
*/
|
||||
public function __construct(PrivateTempStoreFactory $temp_store_factory) {
|
||||
$this->tempStoreFactory = $temp_store_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function convert($value, $definition, $name, array $defaults) {
|
||||
$store = $this->tempStoreFactory->get('node_preview');
|
||||
if ($form_state = $store->get($value)) {
|
||||
return $form_state->getFormObject()->getEntity();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies($definition, $name, Route $route) {
|
||||
if (!empty($definition['type']) && $definition['type'] == 'node_preview') {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
149
core/modules/node/src/Plugin/Action/AssignOwnerNode.php
Normal file
149
core/modules/node/src/Plugin/Action/AssignOwnerNode.php
Normal file
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\AssignOwnerNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Action\ConfigurableActionBase;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\user\Entity\User;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Assigns ownership of a node to a user.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_assign_owner_action",
|
||||
* label = @Translation("Change the author of content"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class AssignOwnerNode extends ConfigurableActionBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Constructs a new AssignOwnerNode action.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $connection) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static($configuration, $plugin_id, $plugin_definition,
|
||||
$container->get('database')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
/** @var \Drupal\node\NodeInterface $entity */
|
||||
$entity->setOwnerId($this->configuration['owner_uid'])->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return array(
|
||||
'owner_uid' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$description = t('The username of the user to which you would like to assign ownership.');
|
||||
$count = $this->connection->query("SELECT COUNT(*) FROM {users}")->fetchField();
|
||||
|
||||
// Use dropdown for fewer than 200 users; textbox for more than that.
|
||||
if (intval($count) < 200) {
|
||||
$options = array();
|
||||
$result = $this->connection->query("SELECT uid, name FROM {users_field_data} WHERE uid > 0 AND default_langcode = 1 ORDER BY name");
|
||||
foreach ($result as $data) {
|
||||
$options[$data->uid] = $data->name;
|
||||
}
|
||||
$form['owner_uid'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Username'),
|
||||
'#default_value' => $this->configuration['owner_uid'],
|
||||
'#options' => $options,
|
||||
'#description' => $description,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form['owner_uid'] = array(
|
||||
'#type' => 'entity_autocomplete',
|
||||
'#title' => t('Username'),
|
||||
'#target_type' => 'user',
|
||||
'#selection_setttings' => array(
|
||||
'include_anonymous' => FALSE,
|
||||
),
|
||||
'#default_value' => User::load($this->configuration['owner_uid']),
|
||||
// Validation is done in static::validateConfigurationForm().
|
||||
'#validate_reference' => FALSE,
|
||||
'#size' => '6',
|
||||
'#maxlength' => '60',
|
||||
'#description' => $description,
|
||||
);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
$exists = (bool) $this->connection->queryRange('SELECT 1 FROM {users_field_data} WHERE uid = :uid AND default_langcode = 1', 0, 1, array(':name' => $form_state->getValue('owner_uid')))->fetchField();
|
||||
if (!$exists) {
|
||||
$form_state->setErrorByName('owner_uid', t('Enter a valid username.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->configuration['owner_uid'] = $form_state->getValue('owner_uid');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
/** @var \Drupal\node\NodeInterface $object */
|
||||
$result = $object->access('update', $account, TRUE)
|
||||
->andIf($object->getOwner()->access('edit', $account, TRUE));
|
||||
|
||||
return $return_as_object ? $result : $result->isAllowed();
|
||||
}
|
||||
|
||||
}
|
104
core/modules/node/src/Plugin/Action/DeleteNode.php
Normal file
104
core/modules/node/src/Plugin/Action/DeleteNode.php
Normal file
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\DeleteNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\user\PrivateTempStoreFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Redirects to a node deletion form.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_delete_action",
|
||||
* label = @Translation("Delete content"),
|
||||
* type = "node",
|
||||
* confirm_form_route_name = "node.multiple_delete_confirm"
|
||||
* )
|
||||
*/
|
||||
class DeleteNode extends ActionBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The tempstore object.
|
||||
*
|
||||
* @var \Drupal\user\SharedTempStore
|
||||
*/
|
||||
protected $tempStore;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* Constructs a new DeleteNode object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
|
||||
* The tempstore factory.
|
||||
* @param AccountInterface $current_user
|
||||
* Current user.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
|
||||
$this->currentUser = $current_user;
|
||||
$this->tempStore = $temp_store_factory->get('node_multiple_delete_confirm');
|
||||
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('user.private_tempstore'),
|
||||
$container->get('current_user')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function executeMultiple(array $entities) {
|
||||
$info = [];
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
foreach ($entities as $node) {
|
||||
$langcode = $node->language()->getId();
|
||||
$info[$node->id()][$langcode] = $langcode;
|
||||
}
|
||||
$this->tempStore->set($this->currentUser->id(), $info);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($object = NULL) {
|
||||
$this->executeMultiple(array($object));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
/** @var \Drupal\node\NodeInterface $object */
|
||||
return $object->access('delete', $account, $return_as_object);
|
||||
}
|
||||
|
||||
}
|
43
core/modules/node/src/Plugin/Action/DemoteNode.php
Normal file
43
core/modules/node/src/Plugin/Action/DemoteNode.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\DemoteNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Demotes a node.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_unpromote_action",
|
||||
* label = @Translation("Demote selected content from front page"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class DemoteNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->setPromoted(FALSE);
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
/** @var \Drupal\node\NodeInterface $object */
|
||||
$result = $object->access('update', $account, TRUE)
|
||||
->andIf($object->promote->access('edit', $account, TRUE));
|
||||
|
||||
return $return_as_object ? $result : $result->isAllowed();
|
||||
}
|
||||
|
||||
}
|
42
core/modules/node/src/Plugin/Action/PromoteNode.php
Normal file
42
core/modules/node/src/Plugin/Action/PromoteNode.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\PromoteNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Promotes a node.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_promote_action",
|
||||
* label = @Translation("Promote selected content to front page"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class PromoteNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->setPromoted(TRUE);
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
/** @var \Drupal\node\NodeInterface $object */
|
||||
$access = $object->access('update', $account, TRUE)
|
||||
->andif($object->promote->access('edit', $account, TRUE));
|
||||
return $return_as_object ? $access : $access->isAllowed();
|
||||
}
|
||||
|
||||
}
|
43
core/modules/node/src/Plugin/Action/PublishNode.php
Normal file
43
core/modules/node/src/Plugin/Action/PublishNode.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\PublishNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Publishes a node.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_publish_action",
|
||||
* label = @Translation("Publish selected content"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class PublishNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->status = NODE_PUBLISHED;
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
/** @var \Drupal\node\NodeInterface $object */
|
||||
$result = $object->access('update', $account, TRUE)
|
||||
->andIf($object->status->access('edit', $account, TRUE));
|
||||
|
||||
return $return_as_object ? $result : $result->isAllowed();
|
||||
}
|
||||
|
||||
}
|
42
core/modules/node/src/Plugin/Action/SaveNode.php
Normal file
42
core/modules/node/src/Plugin/Action/SaveNode.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\SaveNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Provides an action that can save any entity.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_save_action",
|
||||
* label = @Translation("Save content"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class SaveNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
// We need to change at least one value, otherwise the changed timestamp
|
||||
// will not be updated.
|
||||
$entity->changed = 0;
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
/** @var \Drupal\node\NodeInterface $object */
|
||||
return $object->access('update', $account, $return_as_object);
|
||||
}
|
||||
|
||||
}
|
42
core/modules/node/src/Plugin/Action/StickyNode.php
Normal file
42
core/modules/node/src/Plugin/Action/StickyNode.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\StickyNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Makes a node sticky.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_make_sticky_action",
|
||||
* label = @Translation("Make selected content sticky"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class StickyNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->sticky = NODE_STICKY;
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
/** @var \Drupal\node\NodeInterface $object */
|
||||
$access = $object->access('update', $account, TRUE)
|
||||
->andif($object->sticky->access('edit', $account, TRUE));
|
||||
return $return_as_object ? $access : $access->isAllowed();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\UnpublishByKeywordNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Component\Utility\Tags;
|
||||
use Drupal\Core\Action\ConfigurableActionBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Unpublishes a node containing certain keywords.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_unpublish_by_keyword_action",
|
||||
* label = @Translation("Unpublish content containing keyword(s)"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class UnpublishByKeywordNode extends ConfigurableActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($node = NULL) {
|
||||
foreach ($this->configuration['keywords'] as $keyword) {
|
||||
$elements = node_view(clone $node);
|
||||
if (strpos(drupal_render($elements), $keyword) !== FALSE || strpos($node->label(), $keyword) !== FALSE) {
|
||||
$node->setPublished(FALSE);
|
||||
$node->save();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return array(
|
||||
'keywords' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form['keywords'] = array(
|
||||
'#title' => t('Keywords'),
|
||||
'#type' => 'textarea',
|
||||
'#description' => t('The content will be unpublished if it contains any of the phrases above. Use a case-sensitive, comma-separated list of phrases. Example: funny, bungee jumping, "Company, Inc."'),
|
||||
'#default_value' => Tags::implode($this->configuration['keywords']),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->configuration['keywords'] = Tags::explode($form_state->getValue('keywords'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
/** @var \Drupal\node\NodeInterface $object */
|
||||
$access = $object->access('update', $account, TRUE)
|
||||
->andIf($object->status->access('edit', $account, TRUE));
|
||||
|
||||
return $return_as_object ? $access : $access->isAllowed();
|
||||
}
|
||||
|
||||
}
|
43
core/modules/node/src/Plugin/Action/UnpublishNode.php
Normal file
43
core/modules/node/src/Plugin/Action/UnpublishNode.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\UnpublishNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Unpublishes a node.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_unpublish_action",
|
||||
* label = @Translation("Unpublish selected content"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class UnpublishNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->status = NODE_NOT_PUBLISHED;
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
/** @var \Drupal\node\NodeInterface $object */
|
||||
$access = $object->access('update', $account, TRUE)
|
||||
->andIf($object->status->access('edit', $account, TRUE));
|
||||
|
||||
return $return_as_object ? $access : $access->isAllowed();
|
||||
}
|
||||
|
||||
}
|
43
core/modules/node/src/Plugin/Action/UnstickyNode.php
Normal file
43
core/modules/node/src/Plugin/Action/UnstickyNode.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Action\UnstickyNode.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Makes a node not sticky.
|
||||
*
|
||||
* @Action(
|
||||
* id = "node_make_unsticky_action",
|
||||
* label = @Translation("Make selected content not sticky"),
|
||||
* type = "node"
|
||||
* )
|
||||
*/
|
||||
class UnstickyNode extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->sticky = NODE_NOT_STICKY;
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
/** @var \Drupal\node\NodeInterface $object */
|
||||
$access = $object->access('update', $account, TRUE)
|
||||
->andIf($object->sticky->access('edit', $account, TRUE));
|
||||
|
||||
return $return_as_object ? $access : $access->isAllowed();
|
||||
}
|
||||
|
||||
}
|
76
core/modules/node/src/Plugin/Block/SyndicateBlock.php
Normal file
76
core/modules/node/src/Plugin/Block/SyndicateBlock.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Block\SyndicateBlock.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Provides a 'Syndicate' block that links to the site's RSS feed.
|
||||
*
|
||||
* @Block(
|
||||
* id = "node_syndicate_block",
|
||||
* admin_label = @Translation("Syndicate"),
|
||||
* category = @Translation("System")
|
||||
* )
|
||||
*/
|
||||
class SyndicateBlock extends BlockBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return array(
|
||||
'block_count' => 10,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function blockAccess(AccountInterface $account) {
|
||||
return AccessResult::allowedIfHasPermission($account, 'access content');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
return array(
|
||||
'#theme' => 'feed_icon',
|
||||
'#url' => 'rss.xml',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildConfigurationForm($form, $form_state);
|
||||
|
||||
// @see ::getCacheMaxAge()
|
||||
$form['cache']['#disabled'] = TRUE;
|
||||
$form['cache']['max_age']['#value'] = Cache::PERMANENT;
|
||||
$form['cache']['#description'] = $this->t('This block is always cached forever, it is not configurable.');
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
// The 'Syndicate' block is permanently cacheable, because its
|
||||
// contents can never change.
|
||||
return Cache::PERMANENT;
|
||||
}
|
||||
|
||||
}
|
127
core/modules/node/src/Plugin/Condition/NodeType.php
Normal file
127
core/modules/node/src/Plugin/Condition/NodeType.php
Normal file
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Condition\NodeType.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Condition;
|
||||
|
||||
use Drupal\Core\Condition\ConditionPluginBase;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a 'Node Type' condition.
|
||||
*
|
||||
* @Condition(
|
||||
* id = "node_type",
|
||||
* label = @Translation("Node Bundle"),
|
||||
* context = {
|
||||
* "node" = @ContextDefinition("entity:node", label = @Translation("Node"))
|
||||
* }
|
||||
* )
|
||||
*
|
||||
*/
|
||||
class NodeType extends ConditionPluginBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The entity storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $entityStorage;
|
||||
|
||||
/**
|
||||
* Creates a new NodeType instance.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $entity_storage
|
||||
* The entity storage.
|
||||
* @param array $configuration
|
||||
* The plugin configuration, i.e. an array with configuration values keyed
|
||||
* by configuration option name. The special key 'context' may be used to
|
||||
* initialize the defined contexts by setting it to an array of context
|
||||
* values keyed by context names.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
*/
|
||||
public function __construct(EntityStorageInterface $entity_storage, array $configuration, $plugin_id, $plugin_definition) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->entityStorage = $entity_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$container->get('entity.manager')->getStorage('node_type'),
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$options = array();
|
||||
$node_types = $this->entityStorage->loadMultiple();
|
||||
foreach ($node_types as $type) {
|
||||
$options[$type->id()] = $type->label();
|
||||
}
|
||||
$form['bundles'] = array(
|
||||
'#title' => $this->t('Node types'),
|
||||
'#type' => 'checkboxes',
|
||||
'#options' => $options,
|
||||
'#default_value' => $this->configuration['bundles'],
|
||||
);
|
||||
return parent::buildConfigurationForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->configuration['bundles'] = array_filter($form_state->getValue('bundles'));
|
||||
parent::submitConfigurationForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function summary() {
|
||||
if (count($this->configuration['bundles']) > 1) {
|
||||
$bundles = $this->configuration['bundles'];
|
||||
$last = array_pop($bundles);
|
||||
$bundles = implode(', ', $bundles);
|
||||
return $this->t('The node bundle is @bundles or @last', array('@bundles' => $bundles, '@last' => $last));
|
||||
}
|
||||
$bundle = reset($this->configuration['bundles']);
|
||||
return $this->t('The node bundle is @bundle', array('@bundle' => $bundle));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function evaluate() {
|
||||
if (empty($this->configuration['bundles']) && !$this->isNegated()) {
|
||||
return TRUE;
|
||||
}
|
||||
$node = $this->getContextValue('node');
|
||||
return !empty($this->configuration['bundles'][$node->getType()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return array('bundles' => array()) + parent::defaultConfiguration();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\EntityReferenceSelection\NodeSelection.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\EntityReferenceSelection;
|
||||
|
||||
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides specific access control for the node entity type.
|
||||
*
|
||||
* @EntityReferenceSelection(
|
||||
* id = "default:node",
|
||||
* label = @Translation("Node selection"),
|
||||
* entity_types = {"node"},
|
||||
* group = "default",
|
||||
* weight = 1
|
||||
* )
|
||||
*/
|
||||
class NodeSelection extends SelectionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildConfigurationForm($form, $form_state);
|
||||
$form['target_bundles']['#title'] = $this->t('Content types');
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
|
||||
$query = parent::buildEntityQuery($match, $match_operator);
|
||||
// Adding the 'node_access' tag is sadly insufficient for nodes: core
|
||||
// requires us to also know about the concept of 'published' and
|
||||
// 'unpublished'. We need to do that as long as there are no access control
|
||||
// modules in use on the site. As long as one access control module is there,
|
||||
// it is supposed to handle this check.
|
||||
if (!$this->currentUser->hasPermission('bypass node access') && !count($this->moduleHandler->getImplementations('node_grants'))) {
|
||||
$query->condition('status', NODE_PUBLISHED);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
}
|
702
core/modules/node/src/Plugin/Search/NodeSearch.php
Normal file
702
core/modules/node/src/Plugin/Search/NodeSearch.php
Normal file
|
@ -0,0 +1,702 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\Search\NodeSearch.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\Search;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\SelectExtender;
|
||||
use Drupal\Core\Database\StatementInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Access\AccessibleInterface;
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\search\Plugin\ConfigurableSearchPluginBase;
|
||||
use Drupal\search\Plugin\SearchIndexingInterface;
|
||||
use Drupal\Search\SearchQuery;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Handles searching for node entities using the Search module index.
|
||||
*
|
||||
* @SearchPlugin(
|
||||
* id = "node_search",
|
||||
* title = @Translation("Content")
|
||||
* )
|
||||
*/
|
||||
class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInterface, SearchIndexingInterface {
|
||||
|
||||
/**
|
||||
* A database connection object.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* An entity manager object.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* A module manager object.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* A config object for 'search.settings'.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Config
|
||||
*/
|
||||
protected $searchSettings;
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The Drupal account to use for checking for access to advanced search.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* The Renderer service to format the username and node.
|
||||
*
|
||||
* @var \Drupal\Core\Render\RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* An array of additional rankings from hook_ranking().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $rankings;
|
||||
|
||||
/**
|
||||
* The list of options and info for advanced search filters.
|
||||
*
|
||||
* Each entry in the array has the option as the key and and for its value, an
|
||||
* array that determines how the value is matched in the database query. The
|
||||
* possible keys in that array are:
|
||||
* - column: (required) Name of the database column to match against.
|
||||
* - join: (optional) Information on a table to join. By default the data is
|
||||
* matched against the {node_field_data} table.
|
||||
* - operator: (optional) OR or AND, defaults to OR.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $advanced = array(
|
||||
'type' => array('column' => 'n.type'),
|
||||
'language' => array('column' => 'i.langcode'),
|
||||
'author' => array('column' => 'n.uid'),
|
||||
'term' => array('column' => 'ti.tid', 'join' => array('table' => 'taxonomy_index', 'alias' => 'ti', 'condition' => 'n.nid = ti.nid')),
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static public function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('database'),
|
||||
$container->get('entity.manager'),
|
||||
$container->get('module_handler'),
|
||||
$container->get('config.factory')->get('search.settings'),
|
||||
$container->get('language_manager'),
|
||||
$container->get('renderer'),
|
||||
$container->get('current_user')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a \Drupal\node\Plugin\Search\NodeSearch object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* A database connection object.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* An entity manager object.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* A module manager object.
|
||||
* @param \Drupal\Core\Config\Config $search_settings
|
||||
* A config object for 'search.settings'.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The $account object to use for checking for access to advanced search.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, Config $search_settings, LanguageManagerInterface $language_manager, RendererInterface $renderer, AccountInterface $account = NULL) {
|
||||
$this->database = $database;
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->searchSettings = $search_settings;
|
||||
$this->languageManager = $language_manager;
|
||||
$this->renderer = $renderer;
|
||||
$this->account = $account;
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($operation = 'view', AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
$result = AccessResult::allowedIfHasPermission($account, 'access content');
|
||||
return $return_as_object ? $result : $result->isAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isSearchExecutable() {
|
||||
// Node search is executable if we have keywords or an advanced parameter.
|
||||
// At least, we should parse out the parameters and see if there are any
|
||||
// keyword matches in that case, rather than just printing out the
|
||||
// "Please enter keywords" message.
|
||||
return !empty($this->keywords) || (isset($this->searchParameters['f']) && count($this->searchParameters['f']));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType() {
|
||||
return $this->getPluginId();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute() {
|
||||
if ($this->isSearchExecutable()) {
|
||||
$results = $this->findResults();
|
||||
|
||||
if ($results) {
|
||||
return $this->prepareResults($results);
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries to find search results, and sets status messages.
|
||||
*
|
||||
* This method can assume that $this->isSearchExecutable() has already been
|
||||
* checked and returned TRUE.
|
||||
*
|
||||
* @return \Drupal\Core\Database\StatementInterface|null
|
||||
* Results from search query execute() method, or NULL if the search
|
||||
* failed.
|
||||
*/
|
||||
protected function findResults() {
|
||||
$keys = $this->keywords;
|
||||
|
||||
// Build matching conditions.
|
||||
$query = $this->database
|
||||
->select('search_index', 'i', array('target' => 'replica'))
|
||||
->extend('Drupal\search\SearchQuery')
|
||||
->extend('Drupal\Core\Database\Query\PagerSelectExtender');
|
||||
$query->join('node_field_data', 'n', 'n.nid = i.sid');
|
||||
$query->condition('n.status', 1)
|
||||
->addTag('node_access')
|
||||
->searchExpression($keys, $this->getPluginId());
|
||||
|
||||
// Handle advanced search filters in the f query string.
|
||||
// \Drupal::request()->query->get('f') is an array that looks like this in
|
||||
// the URL: ?f[]=type:page&f[]=term:27&f[]=term:13&f[]=langcode:en
|
||||
// So $parameters['f'] looks like:
|
||||
// array('type:page', 'term:27', 'term:13', 'langcode:en');
|
||||
// We need to parse this out into query conditions, some of which go into
|
||||
// the keywords string, and some of which are separate conditions.
|
||||
$parameters = $this->getParameters();
|
||||
if (!empty($parameters['f']) && is_array($parameters['f'])) {
|
||||
$filters = array();
|
||||
// Match any query value that is an expected option and a value
|
||||
// separated by ':' like 'term:27'.
|
||||
$pattern = '/^(' . implode('|', array_keys($this->advanced)) . '):([^ ]*)/i';
|
||||
foreach ($parameters['f'] as $item) {
|
||||
if (preg_match($pattern, $item, $m)) {
|
||||
// Use the matched value as the array key to eliminate duplicates.
|
||||
$filters[$m[1]][$m[2]] = $m[2];
|
||||
}
|
||||
}
|
||||
|
||||
// Now turn these into query conditions. This assumes that everything in
|
||||
// $filters is a known type of advanced search.
|
||||
foreach ($filters as $option => $matched) {
|
||||
$info = $this->advanced[$option];
|
||||
// Insert additional conditions. By default, all use the OR operator.
|
||||
$operator = empty($info['operator']) ? 'OR' : $info['operator'];
|
||||
$where = new Condition($operator);
|
||||
foreach ($matched as $value) {
|
||||
$where->condition($info['column'], $value);
|
||||
}
|
||||
$query->condition($where);
|
||||
if (!empty($info['join'])) {
|
||||
$query->join($info['join']['table'], $info['join']['alias'], $info['join']['condition']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the ranking expressions.
|
||||
$this->addNodeRankings($query);
|
||||
|
||||
// Run the query.
|
||||
$find = $query
|
||||
// Add the language code of the indexed item to the result of the query,
|
||||
// since the node will be rendered using the respective language.
|
||||
->fields('i', array('langcode'))
|
||||
// And since SearchQuery makes these into GROUP BY queries, if we add
|
||||
// a field, for PostgreSQL we also need to make it an aggregate or a
|
||||
// GROUP BY. In this case, we want GROUP BY.
|
||||
->groupBy('i.langcode')
|
||||
->limit(10)
|
||||
->execute();
|
||||
|
||||
// Check query status and set messages if needed.
|
||||
$status = $query->getStatus();
|
||||
|
||||
if ($status & SearchQuery::EXPRESSIONS_IGNORED) {
|
||||
drupal_set_message($this->t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', array('@count' => $this->searchSettings->get('and_or_limit'))), 'warning');
|
||||
}
|
||||
|
||||
if ($status & SearchQuery::LOWER_CASE_OR) {
|
||||
drupal_set_message($this->t('Search for either of the two terms with uppercase <strong>OR</strong>. For example, <strong>cats OR dogs</strong>.'), 'warning');
|
||||
}
|
||||
|
||||
if ($status & SearchQuery::NO_POSITIVE_KEYWORDS) {
|
||||
drupal_set_message($this->formatPlural($this->searchSettings->get('index.minimum_word_size'), 'You must include at least one positive keyword with 1 character or more.', 'You must include at least one positive keyword with @count characters or more.'), 'warning');
|
||||
}
|
||||
|
||||
return $find;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares search results for rendering.
|
||||
*
|
||||
* @param \Drupal\Core\Database\StatementInterface $found
|
||||
* Results found from a successful search query execute() method.
|
||||
*
|
||||
* @return array
|
||||
* Array of search result item render arrays (empty array if no results).
|
||||
*/
|
||||
protected function prepareResults(StatementInterface $found) {
|
||||
$results = array();
|
||||
|
||||
$node_storage = $this->entityManager->getStorage('node');
|
||||
$node_render = $this->entityManager->getViewBuilder('node');
|
||||
$keys = $this->keywords;
|
||||
|
||||
foreach ($found as $item) {
|
||||
// Render the node.
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $node_storage->load($item->sid)->getTranslation($item->langcode);
|
||||
$build = $node_render->view($node, 'search_result', $item->langcode);
|
||||
|
||||
/** @var \Drupal\node\NodeTypeInterface $type*/
|
||||
$type = $this->entityManager->getStorage('node_type')->load($node->bundle());
|
||||
|
||||
unset($build['#theme']);
|
||||
$build['#pre_render'][] = array($this, 'removeSubmittedInfo');
|
||||
|
||||
// Fetch comment count for snippet.
|
||||
$rendered = SafeMarkup::set(
|
||||
$this->renderer->renderPlain($build) . ' ' .
|
||||
SafeMarkup::escape($this->moduleHandler->invoke('comment', 'node_update_index', array($node, $item->langcode)))
|
||||
);
|
||||
|
||||
$extra = $this->moduleHandler->invokeAll('node_search_result', array($node, $item->langcode));
|
||||
|
||||
$language = $this->languageManager->getLanguage($item->langcode);
|
||||
$username = array(
|
||||
'#theme' => 'username',
|
||||
'#account' => $node->getOwner(),
|
||||
);
|
||||
|
||||
$result = array(
|
||||
'link' => $node->url('canonical', array('absolute' => TRUE, 'language' => $language)),
|
||||
'type' => SafeMarkup::checkPlain($type->label()),
|
||||
'title' => $node->label(),
|
||||
'node' => $node,
|
||||
'extra' => $extra,
|
||||
'score' => $item->calculated_score,
|
||||
'snippet' => search_excerpt($keys, $rendered, $item->langcode),
|
||||
'langcode' => $node->language()->getId(),
|
||||
);
|
||||
|
||||
if ($type->displaySubmitted()) {
|
||||
$result += array(
|
||||
'user' => $this->renderer->renderPlain($username),
|
||||
'date' => $node->getChangedTime(),
|
||||
);
|
||||
}
|
||||
|
||||
$results[] = $result;
|
||||
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the submitted by information from the build array.
|
||||
*
|
||||
* This information is being removed from the rendered node that is used to
|
||||
* build the search result snippet. It just doesn't make sense to have it
|
||||
* displayed in the snippet.
|
||||
*
|
||||
* @param array $build
|
||||
* The build array.
|
||||
*
|
||||
* @return array
|
||||
* The modified build array.
|
||||
*/
|
||||
public function removeSubmittedInfo(array $build) {
|
||||
unset($build['created']);
|
||||
unset($build['uid']);
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the configured rankings to the search query.
|
||||
*
|
||||
* @param $query
|
||||
* A query object that has been extended with the Search DB Extender.
|
||||
*/
|
||||
protected function addNodeRankings(SelectExtender $query) {
|
||||
if ($ranking = $this->getRankings()) {
|
||||
$tables = &$query->getTables();
|
||||
foreach ($ranking as $rank => $values) {
|
||||
if (isset($this->configuration['rankings'][$rank]) && !empty($this->configuration['rankings'][$rank])) {
|
||||
$node_rank = $this->configuration['rankings'][$rank];
|
||||
// If the table defined in the ranking isn't already joined, then add it.
|
||||
if (isset($values['join']) && !isset($tables[$values['join']['alias']])) {
|
||||
$query->addJoin($values['join']['type'], $values['join']['table'], $values['join']['alias'], $values['join']['on']);
|
||||
}
|
||||
$arguments = isset($values['arguments']) ? $values['arguments'] : array();
|
||||
$query->addScore($values['score'], $arguments, $node_rank);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateIndex() {
|
||||
// Interpret the cron limit setting as the maximum number of nodes to index
|
||||
// per cron run.
|
||||
$limit = (int) $this->searchSettings->get('index.cron_limit');
|
||||
|
||||
$result = $this->database->queryRange("SELECT n.nid, MAX(sd.reindex) FROM {node} n LEFT JOIN {search_dataset} sd ON sd.sid = n.nid AND sd.type = :type WHERE sd.sid IS NULL OR sd.reindex <> 0 GROUP BY n.nid ORDER BY MAX(sd.reindex) is null DESC, MAX(sd.reindex) ASC, n.nid ASC", 0, $limit, array(':type' => $this->getPluginId()), array('target' => 'replica'));
|
||||
$nids = $result->fetchCol();
|
||||
if (!$nids) {
|
||||
return;
|
||||
}
|
||||
|
||||
$node_storage = $this->entityManager->getStorage('node');
|
||||
foreach ($node_storage->loadMultiple($nids) as $node) {
|
||||
$this->indexNode($node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indexes a single node.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node to index.
|
||||
*/
|
||||
protected function indexNode(NodeInterface $node) {
|
||||
$languages = $node->getTranslationLanguages();
|
||||
$node_render = $this->entityManager->getViewBuilder('node');
|
||||
|
||||
foreach ($languages as $language) {
|
||||
$node = $node->getTranslation($language->getId());
|
||||
// Render the node.
|
||||
$build = $node_render->view($node, 'search_index', $language->getId());
|
||||
|
||||
unset($build['#theme']);
|
||||
$rendered = $this->renderer->renderPlain($build);
|
||||
|
||||
$text = '<h1>' . SafeMarkup::checkPlain($node->label($language->getId())) . '</h1>' . $rendered;
|
||||
|
||||
// Fetch extra data normally not visible.
|
||||
$extra = $this->moduleHandler->invokeAll('node_update_index', array($node, $language->getId()));
|
||||
foreach ($extra as $t) {
|
||||
$text .= $t;
|
||||
}
|
||||
|
||||
// Update index, using search index "type" equal to the plugin ID.
|
||||
search_index($this->getPluginId(), $node->id(), $language->getId(), $text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function indexClear() {
|
||||
// All NodeSearch pages share a common search index "type" equal to
|
||||
// the plugin ID.
|
||||
search_index_clear($this->getPluginId());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function markForReindex() {
|
||||
// All NodeSearch pages share a common search index "type" equal to
|
||||
// the plugin ID.
|
||||
search_mark_for_reindex($this->getPluginId());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function indexStatus() {
|
||||
$total = $this->database->query('SELECT COUNT(*) FROM {node}')->fetchField();
|
||||
$remaining = $this->database->query("SELECT COUNT(DISTINCT n.nid) FROM {node} n LEFT JOIN {search_dataset} sd ON sd.sid = n.nid AND sd.type = :type WHERE sd.sid IS NULL OR sd.reindex <> 0", array(':type' => $this->getPluginId()))->fetchField();
|
||||
|
||||
return array('remaining' => $remaining, 'total' => $total);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function searchFormAlter(array &$form, FormStateInterface $form_state) {
|
||||
// Add advanced search keyword-related boxes.
|
||||
$form['advanced'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Advanced search'),
|
||||
'#attributes' => array('class' => array('search-advanced')),
|
||||
'#access' => $this->account && $this->account->hasPermission('use advanced search'),
|
||||
);
|
||||
$form['advanced']['keywords-fieldset'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Keywords'),
|
||||
);
|
||||
$form['advanced']['keywords'] = array(
|
||||
'#prefix' => '<div class="criterion">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
$form['advanced']['keywords-fieldset']['keywords']['or'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Containing any of the words'),
|
||||
'#size' => 30,
|
||||
'#maxlength' => 255,
|
||||
);
|
||||
$form['advanced']['keywords-fieldset']['keywords']['phrase'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Containing the phrase'),
|
||||
'#size' => 30,
|
||||
'#maxlength' => 255,
|
||||
);
|
||||
$form['advanced']['keywords-fieldset']['keywords']['negative'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Containing none of the words'),
|
||||
'#size' => 30,
|
||||
'#maxlength' => 255,
|
||||
);
|
||||
|
||||
// Add node types.
|
||||
$types = array_map(array('\Drupal\Component\Utility\SafeMarkup', 'checkPlain'), node_type_get_names());
|
||||
$form['advanced']['types-fieldset'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Types'),
|
||||
);
|
||||
$form['advanced']['types-fieldset']['type'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Only of the type(s)'),
|
||||
'#prefix' => '<div class="criterion">',
|
||||
'#suffix' => '</div>',
|
||||
'#options' => $types,
|
||||
);
|
||||
$form['advanced']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Advanced search'),
|
||||
'#prefix' => '<div class="action">',
|
||||
'#suffix' => '</div>',
|
||||
'#weight' => 100,
|
||||
);
|
||||
|
||||
// Add languages.
|
||||
$language_options = array();
|
||||
$language_list = $this->languageManager->getLanguages(LanguageInterface::STATE_ALL);
|
||||
foreach ($language_list as $langcode => $language) {
|
||||
// Make locked languages appear special in the list.
|
||||
$language_options[$langcode] = $language->isLocked() ? t('- @name -', array('@name' => $language->getName())) : $language->getName();
|
||||
}
|
||||
if (count($language_options) > 1) {
|
||||
$form['advanced']['lang-fieldset'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Languages'),
|
||||
);
|
||||
$form['advanced']['lang-fieldset']['language'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Languages'),
|
||||
'#prefix' => '<div class="criterion">',
|
||||
'#suffix' => '</div>',
|
||||
'#options' => $language_options,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildSearchUrlQuery(FormStateInterface $form_state) {
|
||||
// Read keyword and advanced search information from the form values,
|
||||
// and put these into the GET parameters.
|
||||
$keys = trim($form_state->getValue('keys'));
|
||||
|
||||
// Collect extra filters.
|
||||
$filters = array();
|
||||
if ($form_state->hasValue('type') && is_array($form_state->getValue('type'))) {
|
||||
// Retrieve selected types - Form API sets the value of unselected
|
||||
// checkboxes to 0.
|
||||
foreach ($form_state->getValue('type') as $type) {
|
||||
if ($type) {
|
||||
$filters[] = 'type:' . $type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($form_state->hasValue('term') && is_array($form_state->getValue('term'))) {
|
||||
foreach ($form_state->getValue('term') as $term) {
|
||||
$filters[] = 'term:' . $term;
|
||||
}
|
||||
}
|
||||
if ($form_state->hasValue('language') && is_array($form_state->getValue('language'))) {
|
||||
foreach ($form_state->getValue('language') as $language) {
|
||||
if ($language) {
|
||||
$filters[] = 'language:' . $language;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($form_state->getValue('or') != '') {
|
||||
if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' ' . $form_state->getValue('or'), $matches)) {
|
||||
$keys .= ' ' . implode(' OR ', $matches[1]);
|
||||
}
|
||||
}
|
||||
if ($form_state->getValue('negative') != '') {
|
||||
if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' ' . $form_state->getValue('negative'), $matches)) {
|
||||
$keys .= ' -' . implode(' -', $matches[1]);
|
||||
}
|
||||
}
|
||||
if ($form_state->getValue('phrase') != '') {
|
||||
$keys .= ' "' . str_replace('"', ' ', $form_state->getValue('phrase')) . '"';
|
||||
}
|
||||
$keys = trim($keys);
|
||||
|
||||
// Put the keywords and advanced parameters into GET parameters. Make sure
|
||||
// to put keywords into the query even if it is empty, because the page
|
||||
// controller uses that to decide it's time to check for search results.
|
||||
$query = array('keys' => $keys);
|
||||
if ($filters) {
|
||||
$query['f'] = $filters;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers ranking definitions from hook_ranking().
|
||||
*
|
||||
* @return array
|
||||
* An array of ranking definitions.
|
||||
*/
|
||||
protected function getRankings() {
|
||||
if (!$this->rankings) {
|
||||
$this->rankings = $this->moduleHandler->invokeAll('ranking');
|
||||
}
|
||||
return $this->rankings;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
$configuration = array(
|
||||
'rankings' => array(),
|
||||
);
|
||||
return $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
// Output form for defining rank factor weights.
|
||||
$form['content_ranking'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Content ranking'),
|
||||
'#open' => TRUE,
|
||||
);
|
||||
$form['content_ranking']['info'] = array(
|
||||
'#markup' => '<p><em>' . $this->t('Influence is a numeric multiplier used in ordering search results. A higher number means the corresponding factor has more influence on search results; zero means the factor is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . '</em></p>'
|
||||
);
|
||||
// Prepare table.
|
||||
$header = [$this->t('Factor'), $this->t('Influence')];
|
||||
$form['content_ranking']['rankings'] = array(
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
);
|
||||
|
||||
// Note: reversed to reflect that higher number = higher ranking.
|
||||
$range = range(0, 10);
|
||||
$options = array_combine($range, $range);
|
||||
foreach ($this->getRankings() as $var => $values) {
|
||||
$form['content_ranking']['rankings'][$var]['name'] = array(
|
||||
'#markup' => $values['title'],
|
||||
);
|
||||
$form['content_ranking']['rankings'][$var]['value'] = array(
|
||||
'#type' => 'select',
|
||||
'#options' => $options,
|
||||
'#attributes' => ['aria-label' => $this->t("Influence of '@title'", ['@title' => $values['title']])],
|
||||
'#default_value' => isset($this->configuration['rankings'][$var]) ? $this->configuration['rankings'][$var] : 0,
|
||||
);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
foreach ($this->getRankings() as $var => $values) {
|
||||
if (!$form_state->isValueEmpty(['rankings', $var, 'value'])) {
|
||||
$this->configuration['rankings'][$var] = $form_state->getValue(['rankings', $var, 'value']);
|
||||
}
|
||||
else {
|
||||
unset($this->configuration['rankings'][$var]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
82
core/modules/node/src/Plugin/views/area/ListingEmpty.php
Normal file
82
core/modules/node/src/Plugin/views/area/ListingEmpty.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\views\area\ListingEmpty.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\views\area;
|
||||
|
||||
use Drupal\Core\Access\AccessManagerInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\views\Plugin\views\area\AreaPluginBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines an area plugin to display a node/add link.
|
||||
*
|
||||
* @ingroup views_area_handlers
|
||||
*
|
||||
* @ViewsArea("node_listing_empty")
|
||||
*/
|
||||
class ListingEmpty extends AreaPluginBase {
|
||||
|
||||
/**
|
||||
* The access manager.
|
||||
*
|
||||
* @var \Drupal\Core\Access\AccessManagerInterface
|
||||
*/
|
||||
protected $accessManager;
|
||||
|
||||
/**
|
||||
* Constructs a new ListingEmpty.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Access\AccessManagerInterface $access_manager
|
||||
* The access manager.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, AccessManagerInterface $access_manager) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->accessManager = $access_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('access_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render($empty = FALSE) {
|
||||
$account = \Drupal::currentUser();
|
||||
if (!$empty || !empty($this->options['empty'])) {
|
||||
$element = array(
|
||||
'#theme' => 'links',
|
||||
'#links' => array(
|
||||
array(
|
||||
'url' => Url::fromRoute('node.add_page'),
|
||||
'title' => $this->t('Add content'),
|
||||
),
|
||||
),
|
||||
'#access' => $this->accessManager->checkNamedRoute('node.add_page', array(), $account),
|
||||
);
|
||||
return $element;
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
}
|
70
core/modules/node/src/Plugin/views/argument/Nid.php
Normal file
70
core/modules/node/src/Plugin/views/argument/Nid.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\views\argument\Nid.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\views\argument;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\node\NodeStorageInterface;
|
||||
use Drupal\views\Plugin\views\argument\NumericArgument;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Argument handler to accept a node id.
|
||||
*
|
||||
* @ViewsArgument("node_nid")
|
||||
*/
|
||||
class Nid extends NumericArgument {
|
||||
|
||||
/**
|
||||
* The node storage.
|
||||
*
|
||||
* @var \Drupal\node\NodeStorageInterface
|
||||
*/
|
||||
protected $nodeStorage;
|
||||
|
||||
/**
|
||||
* Constructs the Nid object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param NodeStorageInterface $node_storage
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, NodeStorageInterface $node_storage) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->nodeStorage = $node_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity.manager')->getStorage('node')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the behavior of title(). Get the title of the node.
|
||||
*/
|
||||
public function titleQuery() {
|
||||
$titles = array();
|
||||
|
||||
$nodes = $this->nodeStorage->loadMultiple($this->value);
|
||||
foreach ($nodes as $node) {
|
||||
$titles[] = SafeMarkup::checkPlain($node->label());
|
||||
}
|
||||
return $titles;
|
||||
}
|
||||
|
||||
}
|
82
core/modules/node/src/Plugin/views/argument/Type.php
Normal file
82
core/modules/node/src/Plugin/views/argument/Type.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node\Plugin\views\argument\Type.
|
||||
*/
|
||||
|
||||
namespace Drupal\node\Plugin\views\argument;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\views\Plugin\views\argument\StringArgument;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Argument handler to accept a node type.
|
||||
*
|
||||
* @ViewsArgument("node_type")
|
||||
*/
|
||||
class Type extends StringArgument {
|
||||
|
||||
/**
|
||||
* NodeType storage controller.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $nodeTypeStorage;
|
||||
|
||||
/**
|
||||
* Constructs a new Node Type object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage class.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $node_type_storage) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->nodeTypeStorage = $node_type_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
$entity_manager = $container->get('entity.manager');
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$entity_manager->getStorage('node_type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the behavior of summaryName(). Get the user friendly version
|
||||
* of the node type.
|
||||
*/
|
||||
public function summaryName($data) {
|
||||
return $this->node_type($data->{$this->name_alias});
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the behavior of title(). Get the user friendly version of the
|
||||
* node type.
|
||||
*/
|
||||
function title() {
|
||||
return $this->node_type($this->argument);
|
||||
}
|
||||
|
||||
function node_type($type_name) {
|
||||
$type = $this->nodeTypeStorage->load($type_name);
|
||||
$output = $type ? $type->label() : $this->t('Unknown content type');
|
||||
return SafeMarkup::checkPlain($output);
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue