mirror of
https://gitlab.unicamp.br/infimecc_drupal11_modules/site_users.git
synced 2026-05-04 01:30:40 -03:00
Adiciona sub-módulo site_users_blog e melhora negociador de tema
Sub-módulo site_users_blog:
- Tipo de conteúdo blog_post (título, corpo, imagem, assuntos)
- Vocabulário blog_tags para categorias
- Listagem em /user/{uid}/blog via Views com filtro contextual por autor
- Padrão Pathauto: user/[node:author:uid]/blog/[node:title]
- hook_node_presave: preenche field_site_section com o autor
- hook_node_access: restringe criação às roles configuradas
- hook_preprocess_structural_pages_menu: injeta item "Blog" quando
usuário tem posts publicados
- Plugin BlogUserHandler: resolve usuário ancestral para rotas de blog
(post individual e listagem Views)
- Link "Post de blog" no menu "Adicionar" da conta
- Página de configuração de roles permitidas
- Update 10001: adiciona field_site_section a posts existentes
MicrositeThemeNegotiator:
- Injeta path.current para cobrir rotas sem parâmetro 'user' (ex.: Views)
- Qualquer path /user/{uid}/... recebe o tema do microsite
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
|||||||
|
langcode: pt-br
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
config:
|
||||||
|
- field.field.node.blog_post.field_blog_image
|
||||||
|
- field.field.node.blog_post.field_blog_tags
|
||||||
|
- node.type.blog_post
|
||||||
|
module:
|
||||||
|
- image
|
||||||
|
- taxonomy
|
||||||
|
targetEntityType: node
|
||||||
|
bundle: blog_post
|
||||||
|
mode: default
|
||||||
|
content:
|
||||||
|
title:
|
||||||
|
type: string_textfield
|
||||||
|
weight: -5
|
||||||
|
region: content
|
||||||
|
settings:
|
||||||
|
size: 60
|
||||||
|
placeholder: ''
|
||||||
|
third_party_settings: {}
|
||||||
|
field_blog_image:
|
||||||
|
type: image_image
|
||||||
|
weight: 1
|
||||||
|
region: content
|
||||||
|
settings:
|
||||||
|
progress_indicator: throbber
|
||||||
|
preview_image_style: thumbnail
|
||||||
|
third_party_settings: {}
|
||||||
|
body:
|
||||||
|
type: text_textarea_with_summary
|
||||||
|
weight: 2
|
||||||
|
region: content
|
||||||
|
settings:
|
||||||
|
rows: 20
|
||||||
|
summary_rows: 5
|
||||||
|
placeholder: ''
|
||||||
|
show_summary: false
|
||||||
|
third_party_settings: {}
|
||||||
|
field_blog_tags:
|
||||||
|
type: entity_reference_autocomplete_tags
|
||||||
|
weight: 3
|
||||||
|
region: content
|
||||||
|
settings:
|
||||||
|
match_operator: CONTAINS
|
||||||
|
match_limit: 10
|
||||||
|
size: 60
|
||||||
|
placeholder: ''
|
||||||
|
third_party_settings: {}
|
||||||
|
status:
|
||||||
|
type: boolean_checkbox
|
||||||
|
weight: 10
|
||||||
|
region: content
|
||||||
|
settings:
|
||||||
|
display_label: true
|
||||||
|
third_party_settings: {}
|
||||||
|
hidden:
|
||||||
|
created: true
|
||||||
|
field_site_section: true
|
||||||
|
promote: true
|
||||||
|
sticky: true
|
||||||
|
uid: true
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
langcode: pt-br
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
config:
|
||||||
|
- field.field.node.blog_post.field_blog_image
|
||||||
|
- field.field.node.blog_post.field_blog_tags
|
||||||
|
- node.type.blog_post
|
||||||
|
module:
|
||||||
|
- image
|
||||||
|
- taxonomy
|
||||||
|
- user
|
||||||
|
targetEntityType: node
|
||||||
|
bundle: blog_post
|
||||||
|
mode: default
|
||||||
|
content:
|
||||||
|
field_blog_image:
|
||||||
|
type: image
|
||||||
|
label: hidden
|
||||||
|
weight: 0
|
||||||
|
region: content
|
||||||
|
settings:
|
||||||
|
image_style: large
|
||||||
|
image_link: ''
|
||||||
|
image_loading:
|
||||||
|
attribute: lazy
|
||||||
|
third_party_settings: {}
|
||||||
|
body:
|
||||||
|
type: text_default
|
||||||
|
label: hidden
|
||||||
|
weight: 1
|
||||||
|
region: content
|
||||||
|
settings: {}
|
||||||
|
third_party_settings: {}
|
||||||
|
field_blog_tags:
|
||||||
|
type: entity_reference_label
|
||||||
|
label: above
|
||||||
|
weight: 2
|
||||||
|
region: content
|
||||||
|
settings:
|
||||||
|
link: true
|
||||||
|
third_party_settings: {}
|
||||||
|
links:
|
||||||
|
weight: 100
|
||||||
|
region: content
|
||||||
|
settings: {}
|
||||||
|
third_party_settings: {}
|
||||||
|
hidden:
|
||||||
|
field_site_section: true
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
langcode: pt-br
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
config:
|
||||||
|
- field.field.node.blog_post.field_blog_image
|
||||||
|
- field.field.node.blog_post.field_blog_tags
|
||||||
|
- node.type.blog_post
|
||||||
|
module:
|
||||||
|
- image
|
||||||
|
- taxonomy
|
||||||
|
- user
|
||||||
|
targetEntityType: node
|
||||||
|
bundle: blog_post
|
||||||
|
mode: teaser
|
||||||
|
content:
|
||||||
|
field_blog_image:
|
||||||
|
type: image
|
||||||
|
label: hidden
|
||||||
|
weight: 0
|
||||||
|
region: content
|
||||||
|
settings:
|
||||||
|
image_style: medium
|
||||||
|
image_link: content
|
||||||
|
image_loading:
|
||||||
|
attribute: lazy
|
||||||
|
third_party_settings: {}
|
||||||
|
body:
|
||||||
|
type: text_summary_or_trimmed
|
||||||
|
label: hidden
|
||||||
|
weight: 1
|
||||||
|
region: content
|
||||||
|
settings:
|
||||||
|
trim_length: 300
|
||||||
|
third_party_settings: {}
|
||||||
|
field_blog_tags:
|
||||||
|
type: entity_reference_label
|
||||||
|
label: hidden
|
||||||
|
weight: 2
|
||||||
|
region: content
|
||||||
|
settings:
|
||||||
|
link: true
|
||||||
|
third_party_settings: {}
|
||||||
|
links:
|
||||||
|
weight: 100
|
||||||
|
region: content
|
||||||
|
settings: {}
|
||||||
|
third_party_settings: {}
|
||||||
|
hidden:
|
||||||
|
field_site_section: true
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
langcode: pt-br
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
config:
|
||||||
|
- field.storage.node.field_blog_image
|
||||||
|
- node.type.blog_post
|
||||||
|
entity_type: node
|
||||||
|
field_name: field_blog_image
|
||||||
|
bundle: blog_post
|
||||||
|
label: 'Imagem de destaque'
|
||||||
|
description: ''
|
||||||
|
required: false
|
||||||
|
translatable: true
|
||||||
|
default_value: {}
|
||||||
|
default_value_callback: ''
|
||||||
|
settings:
|
||||||
|
file_directory: 'blog/[date:custom:Y-m]'
|
||||||
|
file_extensions: 'png gif jpg jpeg webp'
|
||||||
|
max_filesize: ''
|
||||||
|
max_resolution: ''
|
||||||
|
min_resolution: ''
|
||||||
|
alt_field: true
|
||||||
|
alt_field_required: false
|
||||||
|
title_field: false
|
||||||
|
title_field_required: false
|
||||||
|
default_image:
|
||||||
|
uuid: null
|
||||||
|
alt: ''
|
||||||
|
title: ''
|
||||||
|
width: null
|
||||||
|
height: null
|
||||||
|
handler: default:file
|
||||||
|
handler_settings: {}
|
||||||
|
field_type: image
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
langcode: pt-br
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
config:
|
||||||
|
- field.storage.node.field_blog_tags
|
||||||
|
- node.type.blog_post
|
||||||
|
- taxonomy.vocabulary.blog_tags
|
||||||
|
entity_type: node
|
||||||
|
field_name: field_blog_tags
|
||||||
|
bundle: blog_post
|
||||||
|
label: Assuntos
|
||||||
|
description: ''
|
||||||
|
required: false
|
||||||
|
translatable: false
|
||||||
|
default_value: {}
|
||||||
|
default_value_callback: ''
|
||||||
|
settings:
|
||||||
|
handler: default:taxonomy_term
|
||||||
|
handler_settings:
|
||||||
|
target_bundles:
|
||||||
|
blog_tags: blog_tags
|
||||||
|
sort:
|
||||||
|
field: name
|
||||||
|
direction: asc
|
||||||
|
auto_create: true
|
||||||
|
auto_create_bundle: blog_tags
|
||||||
|
field_type: entity_reference
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
langcode: en
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
config:
|
||||||
|
- field.storage.node.field_site_section
|
||||||
|
- node.type.blog_post
|
||||||
|
module:
|
||||||
|
- dynamic_entity_reference
|
||||||
|
entity_type: node
|
||||||
|
field_name: field_site_section
|
||||||
|
bundle: blog_post
|
||||||
|
label: 'Seção do site'
|
||||||
|
description: ''
|
||||||
|
required: false
|
||||||
|
translatable: false
|
||||||
|
default_value: {}
|
||||||
|
default_value_callback: ''
|
||||||
|
settings:
|
||||||
|
handler: default
|
||||||
|
handler_settings: {}
|
||||||
|
field_type: dynamic_entity_reference
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
langcode: en
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
module:
|
||||||
|
- image
|
||||||
|
entity_type: node
|
||||||
|
field_name: field_blog_image
|
||||||
|
type: image
|
||||||
|
settings:
|
||||||
|
uri_scheme: public
|
||||||
|
default_image:
|
||||||
|
uuid: null
|
||||||
|
alt: ''
|
||||||
|
title: ''
|
||||||
|
width: null
|
||||||
|
height: null
|
||||||
|
column_groups:
|
||||||
|
file:
|
||||||
|
label: File
|
||||||
|
columns:
|
||||||
|
- target_id
|
||||||
|
- width
|
||||||
|
- height
|
||||||
|
translatable: false
|
||||||
|
alt:
|
||||||
|
label: Alt
|
||||||
|
translatable: true
|
||||||
|
title:
|
||||||
|
label: Title
|
||||||
|
translatable: true
|
||||||
|
module: image
|
||||||
|
locked: false
|
||||||
|
cardinality: 1
|
||||||
|
translatable: true
|
||||||
|
indexes: {}
|
||||||
|
persist_with_no_fields: false
|
||||||
|
custom_storage: false
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
langcode: en
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
module:
|
||||||
|
- taxonomy
|
||||||
|
entity_type: node
|
||||||
|
field_name: field_blog_tags
|
||||||
|
type: entity_reference
|
||||||
|
settings:
|
||||||
|
target_type: taxonomy_term
|
||||||
|
module: core
|
||||||
|
locked: false
|
||||||
|
cardinality: -1
|
||||||
|
translatable: true
|
||||||
|
indexes: {}
|
||||||
|
persist_with_no_fields: false
|
||||||
|
custom_storage: false
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
langcode: pt-br
|
||||||
|
status: true
|
||||||
|
dependencies: {}
|
||||||
|
name: 'Post de blog'
|
||||||
|
type: blog_post
|
||||||
|
description: 'Publicação em um blog de usuário.'
|
||||||
|
help: ''
|
||||||
|
new_revision: true
|
||||||
|
preview_mode: 1
|
||||||
|
display_submitted: true
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
allowed_roles: {}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
langcode: pt-br
|
||||||
|
status: true
|
||||||
|
dependencies: {}
|
||||||
|
name: 'Assuntos (blog)'
|
||||||
|
vid: blog_tags
|
||||||
|
description: 'Categorias e assuntos para posts de blog.'
|
||||||
|
hierarchy: 0
|
||||||
|
weight: 0
|
||||||
235
modules/site_users_blog/config/install/views.view.user_blog.yml
Normal file
235
modules/site_users_blog/config/install/views.view.user_blog.yml
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
langcode: pt-br
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
config:
|
||||||
|
- node.type.blog_post
|
||||||
|
module:
|
||||||
|
- node
|
||||||
|
- user
|
||||||
|
id: user_blog
|
||||||
|
label: 'Blog do usuário'
|
||||||
|
module: views
|
||||||
|
description: 'Lista posts do blog de um usuário, acessível em /user/{uid}/blog.'
|
||||||
|
tag: ''
|
||||||
|
base_table: node_field_data
|
||||||
|
base_field: nid
|
||||||
|
display:
|
||||||
|
default:
|
||||||
|
id: default
|
||||||
|
display_title: Default
|
||||||
|
display_plugin: default
|
||||||
|
position: 0
|
||||||
|
display_options:
|
||||||
|
title: Blog
|
||||||
|
pager:
|
||||||
|
type: full
|
||||||
|
options:
|
||||||
|
items_per_page: 10
|
||||||
|
offset: 0
|
||||||
|
id: 0
|
||||||
|
total_pages: null
|
||||||
|
tags:
|
||||||
|
previous: '‹ Anterior'
|
||||||
|
next: 'Próximo ›'
|
||||||
|
first: '« Primeira'
|
||||||
|
last: 'Última »'
|
||||||
|
expose:
|
||||||
|
items_per_page: false
|
||||||
|
items_per_page_label: 'Itens por página'
|
||||||
|
items_per_page_options: '5, 10, 25, 50'
|
||||||
|
items_per_page_options_all: false
|
||||||
|
items_per_page_options_all_label: '- Todos -'
|
||||||
|
offset: false
|
||||||
|
offset_label: Deslocamento
|
||||||
|
quantity: 9
|
||||||
|
style:
|
||||||
|
type: default
|
||||||
|
options:
|
||||||
|
grouping: []
|
||||||
|
row_class: ''
|
||||||
|
default_row_class: true
|
||||||
|
row:
|
||||||
|
type: 'entity:node'
|
||||||
|
options:
|
||||||
|
relationship: none
|
||||||
|
view_mode: teaser
|
||||||
|
fields: {}
|
||||||
|
filters:
|
||||||
|
status:
|
||||||
|
id: status
|
||||||
|
table: node_field_data
|
||||||
|
field: status
|
||||||
|
relationship: none
|
||||||
|
group_type: group
|
||||||
|
admin_label: ''
|
||||||
|
entity_type: node
|
||||||
|
entity_field: status
|
||||||
|
plugin_id: boolean
|
||||||
|
operator: '='
|
||||||
|
value: '1'
|
||||||
|
group: 1
|
||||||
|
exposed: false
|
||||||
|
expose:
|
||||||
|
operator: ''
|
||||||
|
is_grouped: false
|
||||||
|
type:
|
||||||
|
id: type
|
||||||
|
table: node_field_data
|
||||||
|
field: type
|
||||||
|
relationship: none
|
||||||
|
group_type: group
|
||||||
|
admin_label: ''
|
||||||
|
entity_type: node
|
||||||
|
entity_field: type
|
||||||
|
plugin_id: bundle
|
||||||
|
operator: in
|
||||||
|
value:
|
||||||
|
blog_post: blog_post
|
||||||
|
group: 1
|
||||||
|
exposed: false
|
||||||
|
expose:
|
||||||
|
operator_id: ''
|
||||||
|
label: ''
|
||||||
|
description: ''
|
||||||
|
use_operator: false
|
||||||
|
operator: ''
|
||||||
|
operator_limit_selection: false
|
||||||
|
operator_list: {}
|
||||||
|
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: {}
|
||||||
|
sorts:
|
||||||
|
created:
|
||||||
|
id: created
|
||||||
|
table: node_field_data
|
||||||
|
field: created
|
||||||
|
relationship: none
|
||||||
|
group_type: group
|
||||||
|
admin_label: ''
|
||||||
|
entity_type: node
|
||||||
|
entity_field: created
|
||||||
|
plugin_id: date
|
||||||
|
order: DESC
|
||||||
|
expose:
|
||||||
|
label: ''
|
||||||
|
granularity: second
|
||||||
|
arguments:
|
||||||
|
uid:
|
||||||
|
id: uid
|
||||||
|
table: node_field_data
|
||||||
|
field: uid
|
||||||
|
relationship: none
|
||||||
|
group_type: group
|
||||||
|
admin_label: Usuário
|
||||||
|
entity_type: node
|
||||||
|
entity_field: uid
|
||||||
|
plugin_id: node_uid
|
||||||
|
default_action: 'not found'
|
||||||
|
exception:
|
||||||
|
value: all
|
||||||
|
title_enable: false
|
||||||
|
title: All
|
||||||
|
title_enable: false
|
||||||
|
title: ''
|
||||||
|
default_argument_type: raw
|
||||||
|
default_argument_options:
|
||||||
|
index: 1
|
||||||
|
use_alias: false
|
||||||
|
summary_options:
|
||||||
|
base_path: ''
|
||||||
|
count: true
|
||||||
|
items_per_page: 25
|
||||||
|
override: false
|
||||||
|
summary:
|
||||||
|
sort_order: asc
|
||||||
|
number_of_records: 0
|
||||||
|
format: default_summary
|
||||||
|
specify_validation: true
|
||||||
|
validate:
|
||||||
|
type: 'entity:user'
|
||||||
|
fail: 'not found'
|
||||||
|
validate_options:
|
||||||
|
access: false
|
||||||
|
operation: view
|
||||||
|
multiple: '0'
|
||||||
|
roles: {}
|
||||||
|
break_phrase: false
|
||||||
|
not: false
|
||||||
|
access:
|
||||||
|
type: perm
|
||||||
|
options:
|
||||||
|
perm: 'access content'
|
||||||
|
cache:
|
||||||
|
type: tag
|
||||||
|
options: {}
|
||||||
|
empty:
|
||||||
|
area_text_custom:
|
||||||
|
id: area_text_custom
|
||||||
|
table: views
|
||||||
|
field: area_text_custom
|
||||||
|
relationship: none
|
||||||
|
group_type: group
|
||||||
|
admin_label: ''
|
||||||
|
plugin_id: text_custom
|
||||||
|
content: 'Nenhum post publicado ainda.'
|
||||||
|
tokenize: false
|
||||||
|
empty: true
|
||||||
|
header: {}
|
||||||
|
footer: {}
|
||||||
|
relationships: {}
|
||||||
|
use_ajax: false
|
||||||
|
query:
|
||||||
|
type: views_query
|
||||||
|
options:
|
||||||
|
query_comment: ''
|
||||||
|
disable_sql_rewrite: false
|
||||||
|
distinct: false
|
||||||
|
replica: false
|
||||||
|
query_tags: []
|
||||||
|
exposed_form:
|
||||||
|
type: basic
|
||||||
|
options:
|
||||||
|
submit_button: Aplicar
|
||||||
|
reset_button: false
|
||||||
|
reset_button_label: Resetar
|
||||||
|
exposed_sorts_label: 'Ordenar por'
|
||||||
|
expose_sort_order: true
|
||||||
|
sort_asc_label: Crescente
|
||||||
|
sort_desc_label: Decrescente
|
||||||
|
use_more: false
|
||||||
|
use_more_always: true
|
||||||
|
use_more_text: mais
|
||||||
|
display_extenders: {}
|
||||||
|
rendering_language: '***LANGUAGE_language_interface***'
|
||||||
|
page_user_blog:
|
||||||
|
id: page_user_blog
|
||||||
|
display_title: 'Página do blog'
|
||||||
|
display_plugin: page
|
||||||
|
position: 1
|
||||||
|
display_options:
|
||||||
|
display_extenders: {}
|
||||||
|
path: 'user/%/blog'
|
||||||
|
menu:
|
||||||
|
type: none
|
||||||
|
title: ''
|
||||||
|
description: ''
|
||||||
|
expanded: false
|
||||||
|
parent: ''
|
||||||
|
weight: 0
|
||||||
|
context: '0'
|
||||||
|
menu_name: main
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
langcode: pt-br
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
module:
|
||||||
|
- node
|
||||||
|
- pathauto
|
||||||
|
- user
|
||||||
|
id: blog_post
|
||||||
|
label: 'Blog post'
|
||||||
|
type: 'canonical_entities:node'
|
||||||
|
pattern: 'user/[node:author:uid]/blog/[node:title]'
|
||||||
|
selection_criteria:
|
||||||
|
bundle:
|
||||||
|
id: 'entity_bundle:node'
|
||||||
|
negate: false
|
||||||
|
bundles:
|
||||||
|
blog_post: blog_post
|
||||||
|
selection_logic: and
|
||||||
|
weight: 0
|
||||||
|
relationships: {}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
site_users_blog.settings:
|
||||||
|
type: config_object
|
||||||
|
label: 'Configurações do blog de usuário'
|
||||||
|
mapping:
|
||||||
|
allowed_roles:
|
||||||
|
type: sequence
|
||||||
|
label: 'Roles com permissão para criar posts'
|
||||||
|
sequence:
|
||||||
|
type: boolean
|
||||||
12
modules/site_users_blog/site_users_blog.info.yml
Normal file
12
modules/site_users_blog/site_users_blog.info.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
name: 'Site Users — Blog'
|
||||||
|
type: module
|
||||||
|
description: 'Blog por usuário integrado ao microsite pessoal.'
|
||||||
|
package: 'Site Users'
|
||||||
|
core_version_requirement: ^11
|
||||||
|
dependencies:
|
||||||
|
- drupal:node
|
||||||
|
- drupal:taxonomy
|
||||||
|
- drupal:views
|
||||||
|
- drupal:user
|
||||||
|
- site_users:site_users
|
||||||
|
- structural_pages:structural_pages
|
||||||
79
modules/site_users_blog/site_users_blog.install
Normal file
79
modules/site_users_blog/site_users_blog.install
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Instalação do módulo site_users_blog.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_install().
|
||||||
|
*/
|
||||||
|
function site_users_blog_install(): void {
|
||||||
|
// Adiciona o campo body ao tipo blog_post.
|
||||||
|
$type = \Drupal::entityTypeManager()
|
||||||
|
->getStorage('node_type')
|
||||||
|
->load('blog_post');
|
||||||
|
|
||||||
|
if ($type) {
|
||||||
|
node_add_body_field($type, 'Conteúdo');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adiciona field_site_section ao blog_post e preenche posts existentes.
|
||||||
|
*/
|
||||||
|
function site_users_blog_update_10001(): string {
|
||||||
|
$entity_type_manager = \Drupal::entityTypeManager();
|
||||||
|
|
||||||
|
// Cria a instância do campo se ainda não existir.
|
||||||
|
if (!\Drupal\field\Entity\FieldConfig::loadByName('node', 'blog_post', 'field_site_section')) {
|
||||||
|
\Drupal\field\Entity\FieldConfig::create([
|
||||||
|
'field_name' => 'field_site_section',
|
||||||
|
'entity_type' => 'node',
|
||||||
|
'bundle' => 'blog_post',
|
||||||
|
'label' => 'Seção do site',
|
||||||
|
'required' => FALSE,
|
||||||
|
'translatable' => FALSE,
|
||||||
|
])->save();
|
||||||
|
|
||||||
|
// Oculta o campo no formulário padrão.
|
||||||
|
$form_display = $entity_type_manager
|
||||||
|
->getStorage('entity_form_display')
|
||||||
|
->load('node.blog_post.default');
|
||||||
|
if ($form_display) {
|
||||||
|
$form_display->removeComponent('field_site_section')->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Oculta o campo nas exibições padrão e teaser.
|
||||||
|
foreach (['default', 'teaser'] as $mode) {
|
||||||
|
$view_display = $entity_type_manager
|
||||||
|
->getStorage('entity_view_display')
|
||||||
|
->load('node.blog_post.' . $mode);
|
||||||
|
if ($view_display) {
|
||||||
|
$view_display->removeComponent('field_site_section')->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preenche posts existentes que ainda não têm field_site_section.
|
||||||
|
$nids = $entity_type_manager->getStorage('node')->getQuery()
|
||||||
|
->condition('type', 'blog_post')
|
||||||
|
->notExists('field_site_section')
|
||||||
|
->accessCheck(FALSE)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
foreach ($nids as $nid) {
|
||||||
|
$node = $entity_type_manager->getStorage('node')->load($nid);
|
||||||
|
if ($node) {
|
||||||
|
$node->set('field_site_section', [
|
||||||
|
'target_type' => 'user',
|
||||||
|
'target_id' => $node->getOwnerId(),
|
||||||
|
]);
|
||||||
|
$node->save();
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "field_site_section adicionado ao blog_post; $count posts existentes atualizados.";
|
||||||
|
}
|
||||||
15
modules/site_users_blog/site_users_blog.links.menu.yml
Normal file
15
modules/site_users_blog/site_users_blog.links.menu.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
site_users_blog.add_blog_post:
|
||||||
|
title: 'Post de blog'
|
||||||
|
route_name: node.add
|
||||||
|
route_parameters:
|
||||||
|
node_type: blog_post
|
||||||
|
menu_name: account
|
||||||
|
parent: site_users.add_content
|
||||||
|
weight: 10
|
||||||
|
|
||||||
|
site_users_blog.settings:
|
||||||
|
title: 'Blog de usuário'
|
||||||
|
description: 'Configura roles com permissão para publicar posts de blog.'
|
||||||
|
route_name: site_users_blog.settings
|
||||||
|
parent: site_users_microsite.settings
|
||||||
|
weight: 10
|
||||||
102
modules/site_users_blog/site_users_blog.module
Normal file
102
modules/site_users_blog/site_users_blog.module
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Blog pessoal por usuário.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Drupal\Core\Access\AccessResult;
|
||||||
|
use Drupal\Core\Access\AccessResultInterface;
|
||||||
|
use Drupal\Core\Session\AccountInterface;
|
||||||
|
use Drupal\node\NodeInterface;
|
||||||
|
use Drupal\user\UserInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_node_presave().
|
||||||
|
*
|
||||||
|
* Preenche field_site_section com o autor do post para que o bloco de
|
||||||
|
* navegação do structural_pages encontre o usuário ancestral corretamente.
|
||||||
|
*/
|
||||||
|
function site_users_blog_node_presave(NodeInterface $node): void {
|
||||||
|
if ($node->bundle() !== 'blog_post') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$node->hasField('field_site_section') || !$node->get('field_site_section')->isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$node->set('field_site_section', [
|
||||||
|
'target_type' => 'user',
|
||||||
|
'target_id' => $node->getOwnerId(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_node_access().
|
||||||
|
*
|
||||||
|
* Restringe a criação de blog_post às roles configuradas pelo administrador.
|
||||||
|
* Se nenhuma role estiver configurada, qualquer usuário autenticado pode criar.
|
||||||
|
*/
|
||||||
|
function site_users_blog_node_access(NodeInterface $node, string $op, AccountInterface $account): AccessResultInterface {
|
||||||
|
if ($node->bundle() !== 'blog_post' || $op !== 'create') {
|
||||||
|
return AccessResult::neutral();
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowed = \Drupal::config('site_users_blog.settings')->get('allowed_roles') ?? [];
|
||||||
|
$allowed = array_filter($allowed);
|
||||||
|
|
||||||
|
$result = AccessResult::neutral()
|
||||||
|
->addCacheTags(['config:site_users_blog.settings'])
|
||||||
|
->cachePerUser();
|
||||||
|
|
||||||
|
if (empty($allowed)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (array_keys($allowed) as $role) {
|
||||||
|
if ($account->hasRole($role)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AccessResult::forbidden()
|
||||||
|
->addCacheTags(['config:site_users_blog.settings'])
|
||||||
|
->cachePerUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_preprocess_structural_pages_menu().
|
||||||
|
*
|
||||||
|
* Adiciona o item "Blog" ao menu do microsite quando o usuário tem pelo menos
|
||||||
|
* um post publicado.
|
||||||
|
*/
|
||||||
|
function site_users_blog_preprocess_structural_pages_menu(array &$variables): void {
|
||||||
|
$user = site_users_get_microsite_user();
|
||||||
|
if ($user === NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nids = \Drupal::entityTypeManager()->getStorage('node')
|
||||||
|
->getQuery()
|
||||||
|
->condition('uid', $user->id())
|
||||||
|
->condition('type', 'blog_post')
|
||||||
|
->condition('status', 1)
|
||||||
|
->accessCheck(FALSE)
|
||||||
|
->range(0, 1)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
if (empty($nids)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = \Drupal\Core\Url::fromRoute('view.user_blog.page_user_blog', [
|
||||||
|
'arg_0' => $user->id(),
|
||||||
|
])->toString();
|
||||||
|
|
||||||
|
$variables['tree'][] = [
|
||||||
|
'id' => 0,
|
||||||
|
'title' => t('Blog'),
|
||||||
|
'url' => $url,
|
||||||
|
'is_redirect' => FALSE,
|
||||||
|
'children' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
3
modules/site_users_blog/site_users_blog.permissions.yml
Normal file
3
modules/site_users_blog/site_users_blog.permissions.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
administer site_users_blog settings:
|
||||||
|
title: 'Administrar configurações do blog de usuário'
|
||||||
|
restrict access: true
|
||||||
7
modules/site_users_blog/site_users_blog.routing.yml
Normal file
7
modules/site_users_blog/site_users_blog.routing.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
site_users_blog.settings:
|
||||||
|
path: '/admin/config/local-modules/site-users/blog'
|
||||||
|
defaults:
|
||||||
|
_form: '\Drupal\site_users_blog\Form\BlogSettingsForm'
|
||||||
|
_title: 'Blog de usuário'
|
||||||
|
requirements:
|
||||||
|
_permission: 'administer site_users_blog settings'
|
||||||
63
modules/site_users_blog/src/Form/BlogSettingsForm.php
Normal file
63
modules/site_users_blog/src/Form/BlogSettingsForm.php
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\site_users_blog\Form;
|
||||||
|
|
||||||
|
use Drupal\Core\Form\ConfigFormBase;
|
||||||
|
use Drupal\Core\Form\FormStateInterface;
|
||||||
|
use Drupal\user\RoleInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formulário de configuração do blog de usuário.
|
||||||
|
*/
|
||||||
|
class BlogSettingsForm extends ConfigFormBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getEditableConfigNames(): array {
|
||||||
|
return ['site_users_blog.settings'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getFormId(): string {
|
||||||
|
return 'site_users_blog_settings_form';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function buildForm(array $form, FormStateInterface $form_state): array {
|
||||||
|
$config = $this->config('site_users_blog.settings');
|
||||||
|
$allowed = $config->get('allowed_roles') ?? [];
|
||||||
|
|
||||||
|
$roles = user_roles(TRUE);
|
||||||
|
unset($roles[RoleInterface::AUTHENTICATED_ID]);
|
||||||
|
|
||||||
|
$options = array_map(fn($role) => $role->label(), $roles);
|
||||||
|
|
||||||
|
$form['allowed_roles'] = [
|
||||||
|
'#type' => 'checkboxes',
|
||||||
|
'#title' => $this->t('Roles que podem criar posts de blog'),
|
||||||
|
'#description' => $this->t('Nenhuma seleção significa que qualquer usuário autenticado pode criar posts.'),
|
||||||
|
'#options' => $options,
|
||||||
|
'#default_value' => array_keys(array_filter($allowed)),
|
||||||
|
];
|
||||||
|
|
||||||
|
return parent::buildForm($form, $form_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function submitForm(array &$form, FormStateInterface $form_state): void {
|
||||||
|
$selected = array_filter($form_state->getValue('allowed_roles'));
|
||||||
|
$this->config('site_users_blog.settings')
|
||||||
|
->set('allowed_roles', $selected)
|
||||||
|
->save();
|
||||||
|
|
||||||
|
parent::submitForm($form, $form_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\site_users_blog\Plugin\ParentEntityHandler;
|
||||||
|
|
||||||
|
use Drupal\Core\Entity\EntityInterface;
|
||||||
|
use Drupal\Core\Routing\RouteMatchInterface;
|
||||||
|
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||||
|
use Drupal\node\NodeInterface;
|
||||||
|
use Drupal\structural_pages\Attribute\ParentEntityHandler;
|
||||||
|
use Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerBase;
|
||||||
|
use Drupal\user\UserInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determina o usuário pai a partir de rotas de blog (post ou listagem).
|
||||||
|
*
|
||||||
|
* Cobre:
|
||||||
|
* - entity.node.canonical de blog_post → retorna o autor
|
||||||
|
* - view.user_blog.page_user_blog → retorna o usuário cujo UID é arg_0
|
||||||
|
*/
|
||||||
|
#[ParentEntityHandler(
|
||||||
|
id: 'blog_user',
|
||||||
|
label: new TranslatableMarkup('Blog do usuário'),
|
||||||
|
entity_type_id: 'user',
|
||||||
|
clears_site_section: FALSE,
|
||||||
|
sort_field: 'name',
|
||||||
|
weight: 20,
|
||||||
|
)]
|
||||||
|
class BlogUserHandler extends ParentEntityHandlerBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getEntityFromRoute(RouteMatchInterface $route_match): ?EntityInterface {
|
||||||
|
$route_name = $route_match->getRouteName() ?? '';
|
||||||
|
|
||||||
|
if ($route_name === 'entity.node.canonical') {
|
||||||
|
$node = $route_match->getParameter('node');
|
||||||
|
if ($node instanceof NodeInterface && $node->bundle() === 'blog_post') {
|
||||||
|
return $node->getOwner();
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($route_name === 'view.user_blog.page_user_blog') {
|
||||||
|
$uid = $route_match->getParameter('arg_0');
|
||||||
|
if (is_numeric($uid)) {
|
||||||
|
return $this->entityTypeManager->getStorage('user')->load($uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function handlesEntity(EntityInterface $entity): bool {
|
||||||
|
return $entity instanceof UserInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ services:
|
|||||||
|
|
||||||
site_users_microsite.theme_negotiator:
|
site_users_microsite.theme_negotiator:
|
||||||
class: Drupal\site_users_microsite\Theme\MicrositeThemeNegotiator
|
class: Drupal\site_users_microsite\Theme\MicrositeThemeNegotiator
|
||||||
arguments: ['@path_alias.manager']
|
arguments: ['@path_alias.manager', '@path.current']
|
||||||
tags:
|
tags:
|
||||||
- { name: theme_negotiator, priority: 100 }
|
- { name: theme_negotiator, priority: 100 }
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Drupal\site_users_microsite\Theme;
|
namespace Drupal\site_users_microsite\Theme;
|
||||||
|
|
||||||
|
use Drupal\Core\Path\CurrentPathStack;
|
||||||
use Drupal\Core\Routing\RouteMatchInterface;
|
use Drupal\Core\Routing\RouteMatchInterface;
|
||||||
use Drupal\Core\Theme\ThemeNegotiatorInterface;
|
use Drupal\Core\Theme\ThemeNegotiatorInterface;
|
||||||
use Drupal\path_alias\AliasManagerInterface;
|
use Drupal\path_alias\AliasManagerInterface;
|
||||||
@@ -14,6 +15,7 @@ class MicrositeThemeNegotiator implements ThemeNegotiatorInterface {
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private AliasManagerInterface $aliasManager,
|
private AliasManagerInterface $aliasManager,
|
||||||
|
private CurrentPathStack $currentPath,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,7 +57,7 @@ class MicrositeThemeNegotiator implements ThemeNegotiatorInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nós cujo alias começa com /user/{uid}/ (ex.: structural_pages).
|
// Nós cujo alias começa com /user/{uid}/ (ex.: structural_pages, blog).
|
||||||
if ($route_name === 'entity.node.canonical') {
|
if ($route_name === 'entity.node.canonical') {
|
||||||
$node = $route_match->getParameter('node');
|
$node = $route_match->getParameter('node');
|
||||||
if ($node) {
|
if ($node) {
|
||||||
@@ -67,6 +69,12 @@ class MicrositeThemeNegotiator implements ThemeNegotiatorInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Qualquer rota cujo path atual (já processado) seja /user/{uid}/...
|
||||||
|
// Cobre Views e outras rotas que não expõem parâmetro 'user' na rota.
|
||||||
|
if (preg_match('#^/user/\d+/#', $this->currentPath->getPath())) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user