diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index bcbf0bc2..f673ee83 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -36,25 +36,7 @@ class ApplicationController < ActionController::Base activity['attributedTo'] = @remote_profile end end - - # parámetros de botones: - # text: [texto del botón] - # class: [clases css] - # href: [href del botón] - def botoneras_moderation_queue - # botones de comentarios - @comment_btn_params = [[t('moderation_queue.button_box.text_pause'), 'btn-secondary', ''], - [t('moderation_queue.button_box.text_reject'), 'btn-primary', ''], - [t('moderation_queue.button_box.text_accept'), 'bg-blue white', ''], - [t('moderation_queue.button_box.text_reply'), 'btn-outline-primary', ''], - [t('moderation_queue.button_box.text_report'), 'btn-danger', '']] - - #botones de remote_profile (cuentas) - @profile_btn_params = [[t('moderation_queue.profile_button_box.text_deny'), 'btn-outline-info', ''], - [t('moderation_queue.profile_button_box.text_allow'), 'btn-success', 'https://sutty.nl/'], - [t('moderation_queue.profile_button_box.text_report'), 'btn-outline-danger', 'https://sutty.nl/']] - end - + def notify_unconfirmed_email return unless current_usuarie return if current_usuarie.confirmed? diff --git a/app/controllers/moderation_queue_controller.rb b/app/controllers/moderation_queue_controller.rb index 5141990e..4a65f136 100644 --- a/app/controllers/moderation_queue_controller.rb +++ b/app/controllers/moderation_queue_controller.rb @@ -9,13 +9,11 @@ class ModerationQueueController < ApplicationController @moderation_queue.each do |activity| activity['attributedTo'] = @remote_profile end - botoneras_moderation_queue end # Perfil remoto de usuarie def remote_profile @remote_profile = YAML.safe_load(File.read(Rails.root.join('db', 'seeds', 'remote_profile.yaml'))) - botoneras_moderation_queue end # todon.nl está usando /api/v2/instance diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 36d17339..5be56acc 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -83,7 +83,6 @@ class PostsController < ApplicationController breadcrumb post.title.value, site_post_path(site, post, locale: locale), match: :exact breadcrumb 'posts.edit', '' dummy_data - botoneras_moderation_queue end def update diff --git a/app/helpers/moderation_queue_helper.rb b/app/helpers/moderation_queue_helper.rb new file mode 100644 index 00000000..16be2a51 --- /dev/null +++ b/app/helpers/moderation_queue_helper.rb @@ -0,0 +1,63 @@ +module ModerationQueueHelper + + # parámetros de botones: + # text: [texto del botón] + # class: [clases css] + # href: [href del botón] + def botoneras_moderation_queue + # botones de comentarios + @btn_params = [[t('moderation_queue.button_box.text_pause'), 'btn-secondary', ''], + [t('moderation_queue.button_box.text_reject'), 'btn-primary', ''], + [t('moderation_queue.button_box.text_accept'), 'bg-blue white', ''], + [t('moderation_queue.button_box.text_reply'), 'btn-outline-primary', ''], + [t('moderation_queue.button_box.text_report'), 'btn-danger', '']] + + #botones de remote_profile (cuentas) + @profile_btn_params = [[t('moderation_queue.profile_button_box.text_approve'), 'btn-success', ''], + [t('moderation_queue.profile_button_box.text_check'), 'btn-outline-success', ''], + [t('moderation_queue.profile_button_box.text_deny'), 'bg-blue white', ''], + [t('moderation_queue.profile_button_box.text_report'), 'btn-danger', '']] + + #botones de instances (instancias) + @instances_btn_params = [[t('moderation_queue.instances_button_box.text_check'), 'btn-outline-success', ''], + [t('moderation_queue.instances_button_box.text_allow'), 'btn-success', ''], + [t('moderation_queue.instances_button_box.text_deny'), 'btn-danger', '']] + end + + # parámetros de filtros: + # text: [texto del botón] + # href: [href del botón] + def filtros_moderation_queue + #filtros de comentarios + @comment_filter_params = { + t('moderation_queue.filter_box.text_checked') => + [ + [t('moderation_queue.filter_box.submenu_pause'), '/sutty.nl'], + [t('moderation_queue.filter_box.submenu_reject'), '#reject'], + [t('moderation_queue.filter_box.submenu_accept'),'#accept'] + ], + t('moderation_queue.filter_box.text_show') => + [ + [t('moderation_queue.filter_box.submenu_pause'), '#pause'], + [t('moderation_queue.filter_box.submenu_reject'), '#reject'], + [t('moderation_queue.filter_box.submenu_accept'),'#accept'], + [t('moderation_queue.filter_box.submenu_report'), '#report'] + ] + } + + #filtros de instancias + @instances_filter_params = { + t('moderation_queue.filter_box.text_checked') => + [ + [t('moderation_queue.filter_box.submenu_case'), '#case_by_case'], + [t('moderation_queue.filter_box.submenu_allow'), '#allow'], + [t('moderation_queue.filter_box.submenu_reject'),'#reject'] + ], + t('moderation_queue.filter_box.text_show') => + [ + [t('moderation_queue.filter_box.submenu_allow'), '#allow'], + [t('moderation_queue.filter_box.submenu_reject'), '#reject'] + ] + } + end +end \ No newline at end of file diff --git a/app/javascript/controllers/dropdown_controller.js b/app/javascript/controllers/dropdown_controller.js new file mode 100644 index 00000000..e2b657fd --- /dev/null +++ b/app/javascript/controllers/dropdown_controller.js @@ -0,0 +1,106 @@ +import { Controller } from "stimulus"; + +// https://getbootstrap.com/docs/4.6/components/dropdowns/#single-button +export default class extends Controller { + static targets = ["dropdown", "button", "item"]; + + // Al iniciar el controlador + connect() { + // Llevar la cuenta del item con foco + this.data.set("item", -1); + + // Gestionar las teclas + this.keydownEvent = this.keydown.bind(this); + this.element.addEventListener("keydown", this.keydownEvent); + + // Gestionar el foco + this.focusinEvent = this.focusin.bind(this); + } + + // Al eliminar el controlador (al pasar a otra página) + disconnect() { + // Eliminar la gestión de teclas + this.element.removeEventListener("keydown", this.keydownEvent); + // Eliminar la gestión del foco + document.removeEventListener("focusin", this.focusinEvent); + } + + // Mostrar u ocultar + toggle(event) { + (this.buttonTarget.ariaExpanded === "false") ? this.show() : this.hide(); + } + + // Mostrar + show() { + this.buttonTarget.ariaExpanded = "true"; + this.element.classList.add("show"); + this.dropdownTarget.classList.add("show"); + + // Activar la gestión del foco + document.addEventListener("focusin", this.focusinEvent); + } + + // Ocultar + hide() { + this.buttonTarget.ariaExpanded = "false"; + this.element.classList.remove("show"); + this.dropdownTarget.classList.remove("show"); + // Volver al inicio el foco de items + this.data.set("item", -1); + + // Desactivar la gestión del foco + document.removeEventListener("focusin", this.focusinEvent); + } + + // Gestionar el foco + focusin(event) { + const item = this.itemTargets.find(x => x === event.target); + + // Si el foco se coloca sobre elementos del controlador, no hacer + // nada + if (event.target === this.buttonTarget || item) { + // Si es un item, el comportamiento de las flechas verticales y el + // Tab tiene que ser igual + if (item) this.data.set("item", this.itemTargets.indexOf(item)); + + return; + } + + // De lo contrario, ocultar + this.hide(); + } + + // Gestionar las teclas + keydown(event) { + const initial = parseInt(this.data.get("item")); + let item = initial; + + switch (event.keyCode) { + case 27: + // Esc cierra el menú y devuelve el foco + this.hide(); + this.buttonTarget.focus(); + break; + case 38: + // Moverse hacia arriba con tope en el primer item + if (item > -1) item--; + + break; + case 40: + // Moverse hacia abajo con tope en el último ítem, si el + // dropdown estaba cerrado, abrirlo. + if (item === -1) this.show(); + if (item <= this.itemTargets.length) item++; + + break; + } + + // Si cambió la posición del ítem, darle foco y actualizar el + // contador. + if (initial !== item) { + this.itemTargets[item]?.focus(); + + this.data.set("item", item); + } + } +} diff --git a/app/views/components/_dropdown.haml b/app/views/components/_dropdown.haml new file mode 100644 index 00000000..54ddcffb --- /dev/null +++ b/app/views/components/_dropdown.haml @@ -0,0 +1,34 @@ +-# + @param :text [String] Contenido del botón + @param :button_classes [Array] Clases para el botón + @param :dropdown_classes [Array] Clases para el listado + @yield Un bloque que renderiza components/dropdown_item +- button_classes = local_assigns[:button_classes]&.join(' ') +- dropdown_classes = local_assigns[:dropdown_classes]&.join(' ') + +.btn-group{ + data: { + controller: 'dropdown' + } + } + %button.btn.dropdown-toggle{ + type: 'button', + class: button_classes, + data: { + toggle: 'true', + display: 'static', + action: 'dropdown#toggle', + target: 'dropdown.button' + }, + aria: { + expanded: 'false' + } + } + = text + .dropdown-menu{ + class: dropdown_classes, + data: { + target: 'dropdown.dropdown' + } + } + = yield diff --git a/app/views/components/_dropdown_item.haml b/app/views/components/_dropdown_item.haml new file mode 100644 index 00000000..3f79403d --- /dev/null +++ b/app/views/components/_dropdown_item.haml @@ -0,0 +1,4 @@ +-# + @param :text [String] Contenido del link + @param :path [String] Link += link_to text, path, class: 'dropdown-item', data: { target: 'dropdown.item' } diff --git a/app/views/layouts/_details.haml b/app/views/layouts/_details.haml index 4fe4d2af..1fe8d2ec 100644 --- a/app/views/layouts/_details.haml +++ b/app/views/layouts/_details.haml @@ -1,6 +1,6 @@ -# Detail Cola de Moderación %details.details.py-2 - %summary + %summary %h3.py-2.text-center= @summary - = yield \ No newline at end of file + = yield diff --git a/app/views/moderation_queue/_accounts.haml b/app/views/moderation_queue/_accounts.haml index 5250ae87..9300665b 100644 --- a/app/views/moderation_queue/_accounts.haml +++ b/app/views/moderation_queue/_accounts.haml @@ -1,3 +1,16 @@ = render 'moderation_queue/comment_filter_box' - @moderation_queue.each do |comment| - = render 'account', comment: comment, profile: @remote_profile \ No newline at end of file + = render 'account', comment: comment, profile: @remote_profile +- filtros_moderation_queue +- botoneras_moderation_queue + +.d-flex.py-2.justify-content-center + = render 'components/dropdown', text: t('moderation_queue.filter_box.text_checked') do + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_case'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_allow'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_reject'), path: '/' + = render 'components/dropdown', text: t('moderation_queue.filter_box.text_show') do + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_allow'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_reject'), path: '/' + += render 'moderation_queue/button_box', btn_params: @profile_btn_params diff --git a/app/views/moderation_queue/_block_instances_textarea.haml b/app/views/moderation_queue/_block_instances_textarea.haml new file mode 100644 index 00000000..22a63c39 --- /dev/null +++ b/app/views/moderation_queue/_block_instances_textarea.haml @@ -0,0 +1,5 @@ +.form-group + .d-flex.flex-column.mt-5 + %textarea.red.mb-3{ name: '', id: '', placeholder: t('moderation_queue.instances.custom_block') } + %button.col-4.offset-md-8.rounded{ type: 'submit', class: 'btn btn-primary' }= t('moderation_queue.instances.submit') + diff --git a/app/views/moderation_queue/_block_lists.haml b/app/views/moderation_queue/_block_lists.haml new file mode 100644 index 00000000..669d451a --- /dev/null +++ b/app/views/moderation_queue/_block_lists.haml @@ -0,0 +1,5 @@ +.flex + .card + .card-body + %input{ type: 'checkbox', value: '', class: 'ml-5' } + %h4.d-inline.red.ml-5= t('moderation_queue.instances.block_lists') \ No newline at end of file diff --git a/app/views/moderation_queue/_btn_base.haml b/app/views/moderation_queue/_btn_base.haml index 5f4561ef..0f9d7c08 100644 --- a/app/views/moderation_queue/_btn_base.haml +++ b/app/views/moderation_queue/_btn_base.haml @@ -1,3 +1,3 @@ -# Componente Botón general Moderación -%a.btn.btn-lg.rounded.mx-2{role: "button", href: href, class: @class} #{text} \ No newline at end of file +%a.btn.btn-lg.rounded.mx-2{role: 'button', href: href, class: @class} #{text} diff --git a/app/views/moderation_queue/_button_box.haml b/app/views/moderation_queue/_button_box.haml index a56b2552..8971a780 100644 --- a/app/views/moderation_queue/_button_box.haml +++ b/app/views/moderation_queue/_button_box.haml @@ -1,6 +1,6 @@ -# Componente Botonera de Moderación -.d-flex.py-2.justify-content-center - - comment_btn_params.each do |btn| +.d-flex.py-4.justify-content-center + - btn_params.each do |btn| - @class = btn[1] - = render 'moderation_queue/btn_base', href: btn[2], class: @class, text: btn[0] \ No newline at end of file + = render 'moderation_queue/btn_base', href: btn[2], class: @class, text: btn[0] diff --git a/app/views/moderation_queue/_comment.haml b/app/views/moderation_queue/_comment.haml index d443802d..d3532734 100644 --- a/app/views/moderation_queue/_comment.haml +++ b/app/views/moderation_queue/_comment.haml @@ -2,17 +2,17 @@ .flex.mx-4.my-5 .row.no-gutters .col-1 - %input{type: "checkbox", id: ""} + %input{ type: 'checkbox', id: '' } .col-10 .row.border.border-white .col.col-1.border.border-white.mr-5 %p= comment['published'].to_datetime.strftime('%m/%d/%Y') .col.border.border-white %span.mr-2= t('.source_profile') - %a{:href => comment['attributedTo']}= profile['preferredUsername'] - .row.border.border-white + %a{ href: comment['attributedTo'] }= profile['preferredUsername'] + .row.border.border-white %p.mr-3= t('.reply_to') %span - %a{:href => comment['inReplyTo']}= comment['inReplyTo'] - .row.border.border-white + %a{ href: comment['inReplyTo'] }= comment['inReplyTo'] + .row.border.border-white %p= sanitize comment['content'] diff --git a/app/views/moderation_queue/_comment_filter_box.haml b/app/views/moderation_queue/_comment_filter_box.haml deleted file mode 100644 index e9abe602..00000000 --- a/app/views/moderation_queue/_comment_filter_box.haml +++ /dev/null @@ -1,6 +0,0 @@ --# Componente Caja de Filtros - -.d-flex.py-2.justify-content-center - = render 'moderation_queue/filter_base', text: t('.text_checked') - = render 'moderation_queue/filter_base', text: t('.text_show') - = render 'moderation_queue/filter_base', text: t('.text_order') \ No newline at end of file diff --git a/app/views/moderation_queue/_comments.haml b/app/views/moderation_queue/_comments.haml index 39ac74a3..39c8cfae 100644 --- a/app/views/moderation_queue/_comments.haml +++ b/app/views/moderation_queue/_comments.haml @@ -1,5 +1,16 @@ -= render 'moderation_queue/comment_filter_box' -- @moderation_queue.each do |comment| +- botoneras_moderation_queue +.d-flex.py-2.justify-content-center + = render 'components/dropdown', text: t('moderation_queue.filter_box.text_checked') do + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_pause'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_reject'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_accept'), path: '/' + = render 'components/dropdown', text: t('moderation_queue.filter_box.text_show') do + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_pause'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_reject'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_accept'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_report'), path: '/' + +- moderation_queue.each do |comment| = render 'comment', comment: comment, profile: @remote_profile - = render 'moderation_queue/button_box', comment_btn_params: @comment_btn_params \ No newline at end of file + = render 'moderation_queue/button_box', btn_params: @btn_params \ No newline at end of file diff --git a/app/views/moderation_queue/_filter_base.haml b/app/views/moderation_queue/_filter_base.haml deleted file mode 100644 index 0b63e59b..00000000 --- a/app/views/moderation_queue/_filter_base.haml +++ /dev/null @@ -1,8 +0,0 @@ --# Componente Filtro -.dropdown.mx-4 - %button#dropdownMenuButton.btn.btn-outline-secondary.dropdown-toggle{:type => "button", "data-toggle" => "dropdown", "aria-haspopup" => "true", "aria-expanded" => "false"} - %span #{text} - .dropdown-menu{"aria-labelledby" => "dropdownMenuButton"} - %a.dropdown-item{ href: '#' } Action - %a.dropdown-item{ href: '#' } Another action - %a.dropdown-item{ href: '#' } Something else here diff --git a/app/views/moderation_queue/_instances.haml b/app/views/moderation_queue/_instances.haml index e279f484..1d083d27 100644 --- a/app/views/moderation_queue/_instances.haml +++ b/app/views/moderation_queue/_instances.haml @@ -1 +1,18 @@ = render 'moderation_queue/comment_filter_box' +- botoneras_moderation_queue + +.d-flex.py-2.justify-content-center + = render 'components/dropdown', text: t('moderation_queue.filter_box.text_checked') do + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_case'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_allow'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_reject'), path: '/' + = render 'components/dropdown', text: t('moderation_queue.filter_box.text_show') do + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_allow'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_reject'), path: '/' + += render 'moderation_queue/button_box', btn_params: @instances_btn_params + +%h3.cyan.mt-5= t('moderation_queue.instances.title') +%p.pb-2= t('moderation_queue.instances.description') += render 'moderation_queue/block_lists' += render 'moderation_queue/block_instances_textarea' diff --git a/app/views/moderation_queue/_remote_profile.haml b/app/views/moderation_queue/_remote_profile.haml index a8d60c60..b50d63e6 100644 --- a/app/views/moderation_queue/_remote_profile.haml +++ b/app/views/moderation_queue/_remote_profile.haml @@ -1,5 +1,22 @@ +-# Componente Remote_Profile + .flex.py-2.mx-2.text-center - %h4= t('.profile_name') + ': ' + remote_profile['name'] - %h5= t('.profile_id') + ': ' + remote_profile['id'] + - botoneras_moderation_queue + + .d-flex.py-2.justify-content-center + = render 'components/dropdown', text: t('moderation_queue.filter_box.text_checked') do + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_pause'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_reject'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_accept'), path: '/' + = render 'components/dropdown', text: t('moderation_queue.filter_box.text_show') do + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_pause'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_reject'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_accept'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_report'), path: '/' + + %h4.my-2= t('.profile_name') + ': ' + remote_profile['name'] + %h5= t('.profile_id') + ': ' + remote_profile['id'] %h5= t('.profile_published') + ': ' + remote_profile['published'].to_datetime.strftime('%m/%d/%Y') - = render 'moderation_queue/button_box', comment_btn_params: @profile_btn_params + %h5= t('.profile_summary') + ':' + %p= sanitize remote_profile['summary'] + = render 'moderation_queue/button_box', btn_params: @profile_btn_params diff --git a/app/views/moderation_queue/index.haml b/app/views/moderation_queue/index.haml index d89c30b4..1a9838de 100644 --- a/app/views/moderation_queue/index.haml +++ b/app/views/moderation_queue/index.haml @@ -2,13 +2,10 @@ .col-md-8 - @summary = t('moderation_queue.index.instances') = render 'layouts/details', summary: @summary do - = render 'moderation_queue/instances', site: @site, post: @post, moderation_queue: @moderation_queue + = render 'moderation_queue/instances', site: @site, post: @post, moderation_queue: @moderation_queue - @summary = t('moderation_queue.index.accounts') = render 'layouts/details', summary: @summary do = render 'moderation_queue/accounts', site: @site, post: @post, moderation_queue: @moderation_queue - @summary = t('moderation_queue.index.comments') = render 'layouts/details', summary: @summary do = render 'moderation_queue/comments', site: @site, post: @post, moderation_queue: @moderation_queue - - - diff --git a/app/views/posts/_moderation_queue.haml b/app/views/posts/_moderation_queue.haml index e4eb422c..34e30538 100644 --- a/app/views/posts/_moderation_queue.haml +++ b/app/views/posts/_moderation_queue.haml @@ -1,7 +1,18 @@ .flex %h3.text-center.py-2 Comentarios - = render 'moderation_queue/comment_filter_box' + - botoneras_moderation_queue + .d-flex.py-2.justify-content-center + = render 'components/dropdown', text: t('moderation_queue.filter_box.text_checked') do + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_pause'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_reject'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_accept'), path: '/' + = render 'components/dropdown', text: t('moderation_queue.filter_box.text_show') do + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_pause'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_reject'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_accept'), path: '/' + = render 'components/dropdown_item', text: t('moderation_queue.filter_box.submenu_report'), path: '/' + - moderation_queue.each do |comment| = render 'moderation_queue/comment', comment: comment, profile: @remote_profile .row.d-flex.justify-content-center - = render 'moderation_queue/button_box', comment_btn_params: @comment_btn_params + = render 'moderation_queue/button_box', btn_params: @btn_params diff --git a/config/locales/es.yml b/config/locales/es.yml index ddf8f7f7..96f068f3 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -12,9 +12,11 @@ es: profile_name: Nombre de la Cuenta profile_id: ID profile_published: Publicada + profile_summary: Resumen profile_button_box: - text_deny: Bloquear que te siga - text_allow: Permitir que te siga + text_approve: Aprovar siempre + text_check: Revisar siempre + text_deny: Bloquear text_report: Reportar button_box: text_pause: Pausa @@ -22,10 +24,25 @@ es: text_accept: Aceptar Publicación text_reply: Responder text_report: Reportar - comment_filter_box: - text_order: Ordenar por + filter_box: text_show: Ver text_checked: Con los marcados + submenu_pause: Pausado + submenu_reject: Rechazado + submenu_accept: Aceptado + submenu_report: Reportado + submenu_case: Moderar caso por caso + submenu_allow: Permitir todo + instances_button_box: + text_check: Moderar caso por caso + text_allow: Permitir todo + text_deny: Bloquear instancia + instances: + title: Mis listas de bloqueo + description: Descripción de listas de bloqueo + block_lists: Listas de bloqueo + custom_block: Lista personalizada de bloqueo + submit: Guardar lista de bloqueo dark: Oscuro es: Castellano en: English