ver artículos como invitade
This commit is contained in:
parent
847b798a82
commit
a125b388bb
15 changed files with 243 additions and 18 deletions
|
@ -23,6 +23,9 @@ class ApplicationController < ActionController::Base
|
||||||
# lugar de desperdiciar una consulta
|
# lugar de desperdiciar una consulta
|
||||||
current_usuarie.sites.find_by_name(id) ||
|
current_usuarie.sites.find_by_name(id) ||
|
||||||
current_usuarie.sites_as_invitade.find_by_name(id)
|
current_usuarie.sites_as_invitade.find_by_name(id)
|
||||||
|
|
||||||
|
# TODO: reenviar a un 403 si el sitio ya no está permitido para le
|
||||||
|
# usuarie
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_post(site)
|
def find_post(site)
|
||||||
|
|
54
app/controllers/usuaries_controller.rb
Normal file
54
app/controllers/usuaries_controller.rb
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Controlador de Usuaries
|
||||||
|
class UsuariesController < ApplicationController
|
||||||
|
include Pundit
|
||||||
|
before_action :authenticate_usuarie!
|
||||||
|
|
||||||
|
# Mostrar todes les usuaries e invitades de un sitio
|
||||||
|
def index
|
||||||
|
@site = find_site
|
||||||
|
site_usuarie = SiteUsuarie.new(@site, current_usuarie)
|
||||||
|
authorize site_usuarie
|
||||||
|
|
||||||
|
@policy = policy(site_usuarie)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Desasociar une usuarie de un sitio
|
||||||
|
def destroy
|
||||||
|
@site = find_site
|
||||||
|
authorize SiteUsuarie.new(@site, current_usuarie)
|
||||||
|
|
||||||
|
@usuarie = Usuarie.find(params[:id])
|
||||||
|
|
||||||
|
@usuarie.sites.delete(@site)
|
||||||
|
|
||||||
|
redirect_to site_usuaries_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convertir une usuarie en invitade
|
||||||
|
def demote
|
||||||
|
@site = find_site
|
||||||
|
authorize SiteUsuarie.new(@site, current_usuarie)
|
||||||
|
|
||||||
|
@usuarie = Usuarie.find(params[:usuarie_id])
|
||||||
|
|
||||||
|
@usuarie.sites.delete(@site)
|
||||||
|
@site.invitades << @usuarie
|
||||||
|
|
||||||
|
redirect_to site_usuaries_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convertir invitade en usuarie
|
||||||
|
def promote
|
||||||
|
@site = find_site
|
||||||
|
authorize SiteUsuarie.new(@site, current_usuarie)
|
||||||
|
|
||||||
|
@usuarie = Usuarie.find(params[:usuarie_id])
|
||||||
|
|
||||||
|
@usuarie.sites_as_invitade.delete(@site)
|
||||||
|
@site.usuaries << @usuarie
|
||||||
|
|
||||||
|
redirect_to site_usuaries_path
|
||||||
|
end
|
||||||
|
end
|
12
app/models/site_usuarie.rb
Normal file
12
app/models/site_usuarie.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Clase genérica a través de la que podemos obtener la relación entre
|
||||||
|
# sitio y usuarie
|
||||||
|
class SiteUsuarie
|
||||||
|
attr_reader :site, :usuarie
|
||||||
|
|
||||||
|
def initialize(site, usuarie)
|
||||||
|
@site = site
|
||||||
|
@usuarie = usuarie
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,6 +9,7 @@ class Usuarie < ApplicationRecord
|
||||||
validates_uniqueness_of :email
|
validates_uniqueness_of :email
|
||||||
|
|
||||||
has_and_belongs_to_many :sites
|
has_and_belongs_to_many :sites
|
||||||
has_and_belongs_to_many :sites_as_invitade, class_name: 'Site',
|
has_and_belongs_to_many :sites_as_invitade,
|
||||||
|
class_name: 'Site',
|
||||||
join_table: 'invitades_sites'
|
join_table: 'invitades_sites'
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#
|
#
|
||||||
# TODO: Implementar Invitadx
|
# TODO: Implementar Invitadx
|
||||||
class PostPolicy
|
class PostPolicy
|
||||||
attr_reader :post
|
attr_reader :post, :usuarie
|
||||||
|
|
||||||
def initialize(usuarie, post)
|
def initialize(usuarie, post)
|
||||||
@usuarie = usuarie
|
@usuarie = usuarie
|
||||||
|
@ -17,7 +17,7 @@ class PostPolicy
|
||||||
|
|
||||||
# Lxs invitadxs solo pueden ver sus propios posts
|
# Lxs invitadxs solo pueden ver sus propios posts
|
||||||
def show?
|
def show?
|
||||||
true || post.author == usuarix.email
|
post.site.usuarie?(usuarie) || post.author == usuarie.email
|
||||||
end
|
end
|
||||||
|
|
||||||
def new?
|
def new?
|
||||||
|
@ -34,7 +34,7 @@ class PostPolicy
|
||||||
|
|
||||||
# Lxs invitadxs solo pueden modificar sus propios artículos
|
# Lxs invitadxs solo pueden modificar sus propios artículos
|
||||||
def update?
|
def update?
|
||||||
true || post.author == usuarix.email
|
post.site.usuarie?(usuarie) || post.author == usuarie.email
|
||||||
end
|
end
|
||||||
|
|
||||||
# Solo las usuarias pueden eliminar artículos. Lxs invitadxs pueden
|
# Solo las usuarias pueden eliminar artículos. Lxs invitadxs pueden
|
||||||
|
@ -58,8 +58,8 @@ class PostPolicy
|
||||||
#
|
#
|
||||||
# Lxs invitadxs solo pueden ver sus propios posts
|
# Lxs invitadxs solo pueden ver sus propios posts
|
||||||
def resolve
|
def resolve
|
||||||
# TODO: filtrar por invitade
|
return scope if scope.try(:first).try(:site).try(:usuarie?, usuarie)
|
||||||
return scope
|
|
||||||
# Asegurarse que al menos devolvemos []
|
# Asegurarse que al menos devolvemos []
|
||||||
[scope.find do |post|
|
[scope.find do |post|
|
||||||
post.author == usuarie.email
|
post.author == usuarie.email
|
||||||
|
|
35
app/policies/site_usuarie_policy.rb
Normal file
35
app/policies/site_usuarie_policy.rb
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Gestiona la relación entre sitios y usuaries
|
||||||
|
class SiteUsuariePolicy
|
||||||
|
attr_reader :usuarie, :site_usuarie
|
||||||
|
|
||||||
|
def initialize(usuarie, site_usuarie)
|
||||||
|
@usuarie = usuarie
|
||||||
|
@site_usuarie = site_usuarie
|
||||||
|
end
|
||||||
|
|
||||||
|
def index?
|
||||||
|
usuarie?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Les usuaries pueden remover a otres usuaries e invitades del sitio
|
||||||
|
def destroy?
|
||||||
|
usuarie?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Les usuaries pueden convertir a otres usuaries en invitades
|
||||||
|
def demote?
|
||||||
|
usuarie?
|
||||||
|
end
|
||||||
|
|
||||||
|
def promote?
|
||||||
|
usuarie?
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def usuarie?
|
||||||
|
site_usuarie.site.usuarie? usuarie
|
||||||
|
end
|
||||||
|
end
|
|
@ -17,7 +17,7 @@
|
||||||
= link_to t('posts.new_with_template', template: @site.templates.first.id.humanize),
|
= link_to t('posts.new_with_template', template: @site.templates.first.id.humanize),
|
||||||
new_site_post_path(@site, lang: @lang, template: @site.templates.first.id),
|
new_site_post_path(@site, lang: @lang, template: @site.templates.first.id),
|
||||||
class: 'btn btn-success'
|
class: 'btn btn-success'
|
||||||
- if policy(Post).usuaria?
|
- if @site.usuarie? current_usuarie
|
||||||
%button.btn.btn-success.dropdown-toggle.dropdown-toggle-split{data: { toggle: 'split' },
|
%button.btn.btn-success.dropdown-toggle.dropdown-toggle-split{data: { toggle: 'split' },
|
||||||
aria: { haspopup: 'true', expanded: 'false' }}
|
aria: { haspopup: 'true', expanded: 'false' }}
|
||||||
%span.sr-only= t('posts.dropdown')
|
%span.sr-only= t('posts.dropdown')
|
||||||
|
|
|
@ -33,6 +33,12 @@
|
||||||
text: t('i18n.edit'),
|
text: t('i18n.edit'),
|
||||||
type: 'info',
|
type: 'info',
|
||||||
link: site_i18n_edit_path(site)
|
link: site_i18n_edit_path(site)
|
||||||
|
- if policy(SiteUsuarie.new(site, current_usuarie)).index?
|
||||||
|
= render 'layouts/btn_with_tooltip',
|
||||||
|
tooltip: t('usuaries.index.help.self'),
|
||||||
|
text: t('usuaries.index.title'),
|
||||||
|
type: 'info',
|
||||||
|
link: site_usuaries_path(site)
|
||||||
- if policy(site).build?
|
- if policy(site).build?
|
||||||
- if site.enqueued?
|
- if site.enqueued?
|
||||||
= render 'layouts/btn_with_tooltip',
|
= render 'layouts/btn_with_tooltip',
|
||||||
|
|
50
app/views/usuaries/index.haml
Normal file
50
app/views/usuaries/index.haml
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
.row
|
||||||
|
.col
|
||||||
|
= render 'layouts/breadcrumb',
|
||||||
|
crumbs: [ link_to(t('sites.index'), sites_path),
|
||||||
|
@site.name,
|
||||||
|
link_to(t('posts.index'),
|
||||||
|
site_usuaries_path(@site)) ]
|
||||||
|
= render 'layouts/help', help: t('help.breadcrumbs')
|
||||||
|
.row
|
||||||
|
.col
|
||||||
|
%h1= t('.title')
|
||||||
|
|
||||||
|
.row
|
||||||
|
.col
|
||||||
|
-# Una tabla de usuaries y otra de invitades, con acciones
|
||||||
|
- %i[usuaries invitades].each do |u|
|
||||||
|
%h2= t(".#{u.to_s}")
|
||||||
|
%p= t(".help.#{u.to_s}")
|
||||||
|
%table.table.table-striped.table-condensed
|
||||||
|
%tbody
|
||||||
|
- @site.send(u).each do |cuenta|
|
||||||
|
%tr
|
||||||
|
-# TODO: avatares
|
||||||
|
%td= cuenta.email
|
||||||
|
%td
|
||||||
|
.btn-group{role: 'group', 'aria-label': t('.actions')}
|
||||||
|
- if @policy.demote? && @site.usuarie?(cuenta)
|
||||||
|
= link_to t('.demote.text'),
|
||||||
|
site_usuarie_demote_path(@site, cuenta),
|
||||||
|
class: 'btn btn-warning',
|
||||||
|
data: { toggle: 'tooltip',
|
||||||
|
confirm: t('.demote.confirm')},
|
||||||
|
title: t('.help.demote'),
|
||||||
|
method: :patch
|
||||||
|
- if @policy.promote? && @site.invitade?(cuenta)
|
||||||
|
= link_to t('.promote.text'),
|
||||||
|
site_usuarie_promote_path(@site, cuenta),
|
||||||
|
class: 'btn btn-success',
|
||||||
|
data: { toggle: 'tooltip',
|
||||||
|
confirm: t('.promote.confirm')},
|
||||||
|
title: t('.help.promote'),
|
||||||
|
method: :patch
|
||||||
|
- if @policy.destroy?
|
||||||
|
= link_to t('.destroy.text'),
|
||||||
|
site_usuarie_path(@site, cuenta),
|
||||||
|
class: 'btn btn-danger',
|
||||||
|
data: { toggle: 'tooltip',
|
||||||
|
confirm: t('.destroy.confirm')},
|
||||||
|
title: t('.help.destroy'),
|
||||||
|
method: :delete
|
|
@ -258,3 +258,24 @@ en:
|
||||||
blank: Nothing
|
blank: Nothing
|
||||||
destroy: Delete
|
destroy: Delete
|
||||||
confirm_destroy: Are you sure?
|
confirm_destroy: Are you sure?
|
||||||
|
usuaries:
|
||||||
|
index:
|
||||||
|
help:
|
||||||
|
self: Self-manage who has access to this site
|
||||||
|
destroy: Remove access to this site
|
||||||
|
usuaries: 'Users can self-manage every section and option of this site, posts, review and approve posts from guests, publish changes, etc.'
|
||||||
|
invitades: 'Guests can only create new posts and edit those authored by them. Any change needs review and approval by a user.'
|
||||||
|
demote: Removes privileges for this user
|
||||||
|
promote: Gives privileges to this guest
|
||||||
|
title: Users and Guests
|
||||||
|
usuaries: Users
|
||||||
|
invitades: Guests
|
||||||
|
destroy:
|
||||||
|
text: 'Remove access'
|
||||||
|
confirm: "Remove access to this site? The account itself is not deleted, but it won't be able to make changes to this site."
|
||||||
|
demote:
|
||||||
|
text: 'Convert to guest'
|
||||||
|
confirm: 'Convert to guest? They can only edit their own posts and will need approval from other user to publish them.'
|
||||||
|
promote:
|
||||||
|
text: 'Convert to user'
|
||||||
|
confirm: 'Convert to user? They will gain full access to self-manage this site.'
|
||||||
|
|
|
@ -265,3 +265,24 @@ es:
|
||||||
blank: En blanco
|
blank: En blanco
|
||||||
destroy: Borrar
|
destroy: Borrar
|
||||||
confirm_destroy: ¿Estás segurx?
|
confirm_destroy: ¿Estás segurx?
|
||||||
|
usuaries:
|
||||||
|
index:
|
||||||
|
help:
|
||||||
|
self: Gestionar quiénes tienen acceso a este sitio
|
||||||
|
destroy: Impide el acceso a la gestión del sitio
|
||||||
|
usuaries: 'Les usuaries pueden gestionar todas las secciones del sitio, crear artículos, revisar y aprobar artículos de invitades, publicar los cambios, etc.'
|
||||||
|
invitades: 'Les invitades sólo pueden crear artículos nuevos y modificar los que cargaron. Todos los cambios que hagan necesitan la revisión y aprobación de une usuarie.'
|
||||||
|
demote: Quita privilegios a este usuarie
|
||||||
|
promote: Otorga privilegios a este invitade
|
||||||
|
title: Usuaries e invitades
|
||||||
|
usuaries: Usuaries
|
||||||
|
invitades: Invitades
|
||||||
|
destroy:
|
||||||
|
text: 'Quitar acceso'
|
||||||
|
confirm: '¿Quitar acceso a este sitio? La cuenta no será modificada, solo no podrá hacer cambios en este sitio.'
|
||||||
|
demote:
|
||||||
|
text: 'Convertir en invitade'
|
||||||
|
confirm: '¿Convertir en invitade? Solo tendrá acceso a sus propios artículos y necesitará la aprobación de otre usuarie para publicarlos.'
|
||||||
|
promote:
|
||||||
|
text: 'Convertir en usuarie'
|
||||||
|
confirm: '¿Convertir en usuarie? Ganará acceso a la gestión completa del sitio.'
|
||||||
|
|
|
@ -16,16 +16,21 @@ Rails.application.routes.draw do
|
||||||
|
|
||||||
get 'public/:type/:basename', to: 'sites#send_public_file'
|
get 'public/:type/:basename', to: 'sites#send_public_file'
|
||||||
|
|
||||||
resources :posts
|
# Gestionar usuaries
|
||||||
resources :templates
|
resources :usuaries do
|
||||||
resources :invitadxs, only: %i[index new show] do
|
patch 'demote', to: 'usuaries#demote'
|
||||||
get :confirmation, to: 'invitadxs#confirmation'
|
patch 'promote', to: 'usuaries#promote'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Gestionar artículos
|
||||||
|
resources :posts
|
||||||
|
|
||||||
|
# Gestionar traducciones
|
||||||
get 'i18n', to: 'i18n#index'
|
get 'i18n', to: 'i18n#index'
|
||||||
get 'i18n/edit', to: 'i18n#edit'
|
get 'i18n/edit', to: 'i18n#edit'
|
||||||
post 'i18n', to: 'i18n#update'
|
post 'i18n', to: 'i18n#update'
|
||||||
|
|
||||||
|
# Compilar el sitio
|
||||||
post 'enqueue', to: 'sites#enqueue'
|
post 'enqueue', to: 'sites#enqueue'
|
||||||
get 'build_log', to: 'sites#build_log'
|
get 'build_log', to: 'sites#build_log'
|
||||||
post 'reorder_posts', to: 'sites#reorder_posts'
|
post 'reorder_posts', to: 'sites#reorder_posts'
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Agrega índices únicos a la combinación de Usuarie y Site para no tener
|
||||||
|
# accesos duplicados.
|
||||||
|
class AddUniqueToInvitadesAndUsuaries < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
%i[invitades_sites sites_usuaries].each do |t|
|
||||||
|
remove_index t, :site_id
|
||||||
|
remove_index t, :usuarie_id
|
||||||
|
add_index t, %i[site_id usuarie_id], unique: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -12,12 +12,11 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20_190_703_200_455) do
|
ActiveRecord::Schema.define(version: 20_190_705_195_758) do
|
||||||
create_table 'invitades_sites', id: false, force: :cascade do |t|
|
create_table 'invitades_sites', id: false, force: :cascade do |t|
|
||||||
t.integer 'site_id'
|
t.integer 'site_id'
|
||||||
t.integer 'usuarie_id'
|
t.integer 'usuarie_id'
|
||||||
t.index ['site_id'], name: 'index_invitades_sites_on_site_id'
|
t.index %w[site_id usuarie_id], name: 'index_invitades_sites_on_site_id_and_usuarie_id', unique: true
|
||||||
t.index ['usuarie_id'], name: 'index_invitades_sites_on_usuarie_id'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table 'sites', force: :cascade do |t|
|
create_table 'sites', force: :cascade do |t|
|
||||||
|
@ -30,8 +29,7 @@ ActiveRecord::Schema.define(version: 20_190_703_200_455) do
|
||||||
create_table 'sites_usuaries', id: false, force: :cascade do |t|
|
create_table 'sites_usuaries', id: false, force: :cascade do |t|
|
||||||
t.integer 'site_id'
|
t.integer 'site_id'
|
||||||
t.integer 'usuarie_id'
|
t.integer 'usuarie_id'
|
||||||
t.index ['site_id'], name: 'index_sites_usuaries_on_site_id'
|
t.index %w[site_id usuarie_id], name: 'index_sites_usuaries_on_site_id_and_usuarie_id', unique: true
|
||||||
t.index ['usuarie_id'], name: 'index_sites_usuaries_on_usuarie_id'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table 'usuaries', force: :cascade do |t|
|
create_table 'usuaries', force: :cascade do |t|
|
||||||
|
|
|
@ -36,4 +36,10 @@ De lo contrario necesitamos establecer roles y ya entramos en las
|
||||||
dificultades que decíamos más arriba.
|
dificultades que decíamos más arriba.
|
||||||
|
|
||||||
No podemos saber desde cuándo se creo la relación, a menos que tengamos
|
No podemos saber desde cuándo se creo la relación, a menos que tengamos
|
||||||
una tabla de actividades.
|
una tabla de actividades por separado.
|
||||||
|
|
||||||
|
Podemos saber quién es invitade ingresando a un sitio y fijándonos si
|
||||||
|
está en su lista de invitade. Lo mismo para usuaries.
|
||||||
|
|
||||||
|
Les usuaries pueden bloquear invitades y a otres usuaries, y sumar
|
||||||
|
usuaries e invitades a su sitio (via correo de invitación).
|
||||||
|
|
Loading…
Reference in a new issue