decidim-core 0.7.4 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +9 -2
- data/app/assets/javascripts/decidim.js.es6 +1 -0
- data/app/assets/javascripts/decidim/editor.js.es6 +8 -2
- data/app/assets/javascripts/decidim/form_filter.component.js.es6 +2 -2
- data/app/assets/javascripts/decidim/foundation.js.es6 +18 -8
- data/app/assets/javascripts/decidim/select2.js.es6 +3 -0
- data/app/assets/stylesheets/decidim/_decidim.scss +1 -1
- data/app/assets/stylesheets/decidim/modules/_buttons.scss +0 -4
- data/app/assets/stylesheets/decidim/modules/_cards.scss +41 -1
- data/app/assets/stylesheets/decidim/modules/_messages.scss +35 -0
- data/app/assets/stylesheets/decidim/modules/_modules.scss +1 -0
- data/app/assets/stylesheets/decidim/modules/_navbar.scss +2 -1
- data/app/assets/stylesheets/decidim/utils/_settings.scss +363 -69
- data/app/commands/decidim/messaging/reply_to_conversation.rb +55 -0
- data/app/commands/decidim/messaging/start_conversation.rb +55 -0
- data/app/controllers/concerns/decidim/action_authorization.rb +6 -22
- data/app/controllers/concerns/decidim/user_profile.rb +5 -3
- data/app/controllers/decidim/application_controller.rb +1 -0
- data/app/controllers/decidim/devise/omniauth_registrations_controller.rb +2 -2
- data/app/controllers/decidim/devise/sessions_controller.rb +5 -3
- data/app/controllers/decidim/features/base_controller.rb +1 -0
- data/app/controllers/decidim/messaging/conversations_controller.rb +81 -0
- data/app/controllers/decidim/pages_controller.rb +2 -11
- data/app/forms/decidim/messaging/conversation_form.rb +19 -0
- data/app/forms/decidim/messaging/message_form.rb +14 -0
- data/app/forms/translatable_presence_validator.rb +7 -10
- data/app/helpers/decidim/authorization_form_helper.rb +1 -1
- data/app/helpers/decidim/cta_button_helper.rb +1 -1
- data/app/helpers/decidim/datetime_helper.rb +23 -0
- data/app/helpers/decidim/feature_path_helper.rb +4 -2
- data/app/helpers/decidim/layout_helper.rb +0 -2
- data/app/helpers/decidim/menu_helper.rb +10 -0
- data/app/helpers/decidim/messaging/conversation_helper.rb +36 -0
- data/app/helpers/decidim/sanitize_helper.rb +19 -0
- data/app/helpers/decidim/traceability_helper.rb +89 -0
- data/app/helpers/decidim/translations_helper.rb +4 -2
- data/app/helpers/decidim/view_hooks_helper.rb +15 -0
- data/app/jobs/decidim/email_notification_generator_job.rb +1 -1
- data/app/jobs/decidim/notification_generator_for_recipient_job.rb +1 -1
- data/app/jobs/decidim/notification_generator_job.rb +1 -1
- data/app/mailers/decidim/messaging/conversation_mailer.rb +47 -0
- data/app/mailers/decidim/newsletter_mailer.rb +1 -0
- data/app/models/decidim/abilities/base_ability.rb +16 -2
- data/app/models/decidim/authorization.rb +20 -2
- data/app/models/decidim/messaging/conversation.rb +129 -0
- data/app/models/decidim/messaging/message.rb +49 -0
- data/app/models/decidim/messaging/participation.rb +23 -0
- data/app/models/decidim/messaging/receipt.rb +27 -0
- data/app/models/decidim/moderation.rb +1 -1
- data/app/models/decidim/organization.rb +2 -2
- data/app/models/decidim/scope.rb +2 -1
- data/app/models/decidim/scope_type.rb +1 -1
- data/app/models/decidim/user.rb +11 -5
- data/app/models/decidim/user_group.rb +1 -1
- data/app/presenters/decidim/inline_menu_presenter.rb +10 -0
- data/app/presenters/decidim/menu_presenter.rb +1 -1
- data/app/queries/decidim/messaging/user_conversations.rb +29 -0
- data/app/scrubbers/decidim/user_input_scrubber.rb +31 -0
- data/app/services/decidim/action_authorizer.rb +48 -12
- data/app/services/decidim/traceability.rb +91 -0
- data/app/views/decidim/messaging/conversation_mailer/new_conversation.html.erb +9 -0
- data/app/views/decidim/messaging/conversation_mailer/new_message.html.erb +9 -0
- data/app/views/decidim/messaging/conversations/_message.html.erb +20 -0
- data/app/views/decidim/messaging/conversations/_reply.html.erb +11 -0
- data/app/views/decidim/messaging/conversations/_show.html.erb +21 -0
- data/app/views/decidim/messaging/conversations/_start.html.erb +12 -0
- data/app/views/decidim/messaging/conversations/create.js.erb +2 -0
- data/app/views/decidim/messaging/conversations/index.html.erb +51 -0
- data/app/views/decidim/messaging/conversations/new.html.erb +5 -0
- data/app/views/decidim/messaging/conversations/show.html.erb +9 -0
- data/app/views/decidim/messaging/conversations/update.js.erb +1 -0
- data/app/views/decidim/newsletter_mailer/newsletter.html.erb +1 -1
- data/app/views/decidim/notifications/_notification.html.erb +1 -1
- data/app/views/decidim/shared/_action_authorization_modal.html.erb +11 -1
- data/app/views/decidim/shared/_announcement.html.erb +1 -1
- data/app/views/decidim/shared/_version_author.html.erb +18 -0
- data/app/views/layouts/decidim/_wrapper.html.erb +3 -0
- data/app/views/layouts/decidim/user_profile.html.erb +1 -9
- data/app/views/pages/decidim_page.html.erb +1 -1
- data/app/views/pages/home.html.erb +0 -2
- data/app/views/pages/home/_footer_sub_hero.html.erb +5 -3
- data/app/views/pages/home/_hero.html.erb +1 -1
- data/app/views/pages/home/_highlighted_processes.html.erb +7 -37
- data/app/views/pages/home/_sub_hero.html.erb +6 -4
- data/config/locales/ca.yml +49 -21
- data/config/locales/en.yml +47 -19
- data/config/locales/es.yml +48 -20
- data/config/locales/eu.yml +51 -23
- data/config/locales/fi.yml +50 -22
- data/config/locales/fr.yml +50 -22
- data/config/locales/it.yml +89 -61
- data/config/locales/nl.yml +72 -44
- data/config/locales/pl.yml +49 -21
- data/config/locales/pt.yml +431 -0
- data/config/locales/ru.yml +4 -27
- data/config/locales/uk.yml +10 -23
- data/config/routes.rb +3 -5
- data/db/migrate/20170313095436_add_available_authorizations_to_organization.rb +2 -2
- data/db/migrate/20170713131308_migrate_user_roles_to_participatory_process_roles.rb +7 -3
- data/db/migrate/20170914092117_add_status_to_authorizations.rb +9 -0
- data/db/migrate/20171011194251_add_verification_metadata_to_authorizations.rb +11 -0
- data/db/migrate/20171013124505_add_verification_attachment_to_authorizations.rb +9 -0
- data/db/migrate/20171023123330_create_decidim_messaging.rb +23 -0
- data/db/migrate/20171107103253_create_versions.rb +18 -0
- data/db/migrate/20171107103254_add_object_changes_to_versions.rb +14 -0
- data/db/migrate/20171117100533_create_decidim_receipts.rb +13 -0
- data/db/seeds.rb +13 -3
- data/lib/decidim/core.rb +13 -6
- data/lib/decidim/core/engine.rb +37 -0
- data/lib/decidim/core/test/factories.rb +33 -21
- data/lib/decidim/core/test/shared_examples/follows_examples.rb +1 -1
- data/lib/decidim/core/test/shared_examples/manage_moderations_examples.rb +6 -11
- data/lib/decidim/core/test/shared_examples/process_announcements_examples.rb +2 -4
- data/lib/decidim/core/test/shared_examples/reportable.rb +33 -31
- data/lib/decidim/core/test/shared_examples/scope_helper_examples.rb +29 -31
- data/lib/decidim/core/version.rb +1 -1
- data/lib/decidim/engine_router.rb +4 -2
- data/lib/decidim/has_reference.rb +10 -2
- data/lib/decidim/messaging.rb +9 -0
- data/lib/decidim/participable.rb +1 -1
- data/lib/decidim/traceable.rb +31 -0
- data/lib/decidim/view_hooks.rb +108 -0
- data/vendor/assets/javascripts/datepicker-locales/foundation-datepicker.es.js +4 -2
- data/vendor/assets/javascripts/datepicker-locales/foundation-datepicker.pt.js +14 -0
- metadata +179 -113
- data/app/commands/decidim/authorize_user.rb +0 -59
- data/app/controllers/decidim/authorizations_controller.rb +0 -80
- data/app/services/decidim/authorization_handler.rb +0 -95
- data/app/views/decidim/authorizations/first_login.html.erb +0 -22
- data/app/views/decidim/authorizations/index.html.erb +0 -49
- data/app/views/decidim/authorizations/new.html.erb +0 -33
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Messaging
|
5
|
+
#
|
6
|
+
# Holds a single message in a conversation. A message has a body, and sender
|
7
|
+
# and a set of receipts, which correspond to each user that will receive the
|
8
|
+
# message, namely, the interlocutors of the sender in the conversation.
|
9
|
+
#
|
10
|
+
class Message < ApplicationRecord
|
11
|
+
belongs_to :sender,
|
12
|
+
foreign_key: :decidim_sender_id,
|
13
|
+
class_name: "Decidim::User"
|
14
|
+
|
15
|
+
belongs_to :conversation,
|
16
|
+
foreign_key: :decidim_conversation_id,
|
17
|
+
class_name: "Decidim::Messaging::Conversation"
|
18
|
+
|
19
|
+
has_many :receipts,
|
20
|
+
dependent: :destroy,
|
21
|
+
foreign_key: :decidim_message_id,
|
22
|
+
inverse_of: :message
|
23
|
+
|
24
|
+
validates :sender, :body, presence: true
|
25
|
+
validates :body, length: { maximum: 1_000 }
|
26
|
+
|
27
|
+
validate :sender_is_participant
|
28
|
+
|
29
|
+
#
|
30
|
+
# Associates receipts for this message for each of the given users,
|
31
|
+
# including also a receipt for the remitent (sender) of the message.
|
32
|
+
# Receipts are unread by default, except for the sender's receipt.
|
33
|
+
#
|
34
|
+
# @param recipients [Array<Decidim::User>]
|
35
|
+
#
|
36
|
+
def envelope_for(recipients)
|
37
|
+
receipts.build(recipient: sender, read_at: Time.zone.now)
|
38
|
+
|
39
|
+
recipients.each { |recipient| receipts.build(recipient: recipient) }
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def sender_is_participant
|
45
|
+
errors.add(:sender, :invalid) unless conversation.participants.include?(sender)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Messaging
|
5
|
+
#
|
6
|
+
# Holds a many-to-many relationship between conversations and their participants
|
7
|
+
#
|
8
|
+
class Participation < ApplicationRecord
|
9
|
+
belongs_to :conversation,
|
10
|
+
foreign_key: :decidim_conversation_id,
|
11
|
+
class_name: "Decidim::Messaging::Conversation",
|
12
|
+
inverse_of: :participations
|
13
|
+
|
14
|
+
belongs_to :participant,
|
15
|
+
foreign_key: :decidim_participant_id,
|
16
|
+
class_name: "Decidim::User"
|
17
|
+
|
18
|
+
validates :conversation, :participant, presence: true
|
19
|
+
|
20
|
+
validates :decidim_conversation_id, uniqueness: { scope: :decidim_participant_id }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Messaging
|
5
|
+
#
|
6
|
+
# Represents the reception of a message by a user. This model is supposed to
|
7
|
+
# hold any information about a message that is specific to each user, for
|
8
|
+
# example, the read/unread status, the deleted/undeleted status, and so on.
|
9
|
+
#
|
10
|
+
class Receipt < ApplicationRecord
|
11
|
+
belongs_to :recipient, foreign_key: "decidim_recipient_id", class_name: "Decidim::User"
|
12
|
+
belongs_to :message, foreign_key: "decidim_message_id", class_name: "Decidim::Messaging::Message"
|
13
|
+
|
14
|
+
validates :recipient, :message, presence: true
|
15
|
+
|
16
|
+
scope :recipient, ->(recipient) { where(recipient: recipient) }
|
17
|
+
scope :unread_by, ->(user) { recipient(user).unread }
|
18
|
+
scope :unread, -> { where(read_at: nil) }
|
19
|
+
|
20
|
+
# rubocop:disable Rails/SkipsModelValidations
|
21
|
+
def self.mark_as_read(user)
|
22
|
+
recipient(user).update_all(read_at: Time.zone.now)
|
23
|
+
end
|
24
|
+
# rubocop:enable Rails/SkipsModelValidations
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -5,7 +5,7 @@ module Decidim
|
|
5
5
|
class Moderation < ApplicationRecord
|
6
6
|
belongs_to :reportable, foreign_key: "decidim_reportable_id", foreign_type: "decidim_reportable_type", polymorphic: true
|
7
7
|
belongs_to :participatory_space, foreign_key: "decidim_participatory_space_id", foreign_type: "decidim_participatory_space_type", polymorphic: true
|
8
|
-
has_many :reports, foreign_key: "decidim_moderation_id", class_name: "Decidim::Report"
|
8
|
+
has_many :reports, foreign_key: "decidim_moderation_id", class_name: "Decidim::Report", dependent: :destroy
|
9
9
|
|
10
10
|
delegate :feature, :organization, to: :reportable
|
11
11
|
end
|
@@ -7,12 +7,12 @@ module Decidim
|
|
7
7
|
class Organization < ApplicationRecord
|
8
8
|
SOCIAL_HANDLERS = [:twitter, :facebook, :instagram, :youtube, :github].freeze
|
9
9
|
|
10
|
-
has_many :static_pages, foreign_key: "decidim_organization_id", class_name: "Decidim::StaticPage", inverse_of: :organization
|
10
|
+
has_many :static_pages, foreign_key: "decidim_organization_id", class_name: "Decidim::StaticPage", inverse_of: :organization, dependent: :destroy
|
11
11
|
has_many :scopes, -> { order(name: :asc) }, foreign_key: "decidim_organization_id", class_name: "Decidim::Scope", inverse_of: :organization
|
12
12
|
has_many :scope_types, -> { order(name: :asc) }, foreign_key: "decidim_organization_id", class_name: "Decidim::ScopeType", inverse_of: :organization
|
13
13
|
has_many :admins, -> { where(admin: true) }, foreign_key: "decidim_organization_id", class_name: "Decidim::User"
|
14
14
|
has_many :users_with_any_role, -> { where.not(roles: []) }, foreign_key: "decidim_organization_id", class_name: "Decidim::User"
|
15
|
-
has_many :users, foreign_key: "decidim_organization_id", class_name: "Decidim::User"
|
15
|
+
has_many :users, foreign_key: "decidim_organization_id", class_name: "Decidim::User", dependent: :destroy
|
16
16
|
|
17
17
|
validates :name, :host, uniqueness: true
|
18
18
|
validates :reference_prefix, presence: true
|
data/app/models/decidim/scope.rb
CHANGED
@@ -9,7 +9,7 @@ module Decidim
|
|
9
9
|
class_name: "Decidim::Organization",
|
10
10
|
inverse_of: :scope_types
|
11
11
|
|
12
|
-
has_many :scopes, foreign_key: "
|
12
|
+
has_many :scopes, foreign_key: "scope_type_id", class_name: "Decidim::Scope", inverse_of: :scope_type, dependent: :nullify
|
13
13
|
|
14
14
|
validates :name, presence: true
|
15
15
|
end
|
data/app/models/decidim/user.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_dependency "devise/models/decidim_validatable"
|
4
|
+
require "valid_email2"
|
4
5
|
|
5
6
|
module Decidim
|
6
7
|
# A User is a citizen that wants to join the platform to participate.
|
@@ -14,9 +15,8 @@ module Decidim
|
|
14
15
|
request_keys: [:env], reset_password_keys: [:decidim_organization_id, :email]
|
15
16
|
|
16
17
|
belongs_to :organization, foreign_key: "decidim_organization_id", class_name: "Decidim::Organization"
|
17
|
-
has_many :
|
18
|
-
has_many :
|
19
|
-
has_many :memberships, class_name: "Decidim::UserGroupMembership", foreign_key: :decidim_user_id
|
18
|
+
has_many :identities, foreign_key: "decidim_user_id", class_name: "Decidim::Identity", dependent: :destroy
|
19
|
+
has_many :memberships, class_name: "Decidim::UserGroupMembership", foreign_key: :decidim_user_id, dependent: :destroy
|
20
20
|
has_many :user_groups, through: :memberships, class_name: "Decidim::UserGroup", foreign_key: :decidim_user_group_id
|
21
21
|
has_many :follows, foreign_key: "decidim_user_id", class_name: "Decidim::Follow", dependent: :destroy
|
22
22
|
has_many :notifications, foreign_key: "decidim_user_id", class_name: "Decidim::Notification", dependent: :destroy
|
@@ -26,6 +26,8 @@ module Decidim
|
|
26
26
|
validates :tos_agreement, acceptance: true, allow_nil: false, on: :create
|
27
27
|
validates :avatar, file_size: { less_than_or_equal_to: ->(_record) { Decidim.maximum_avatar_size } }
|
28
28
|
validates :email, uniqueness: { scope: :organization }, unless: -> { deleted? || managed? }
|
29
|
+
validates :email, 'valid_email_2/email': { disposable: true }
|
30
|
+
|
29
31
|
validate :all_roles_are_valid
|
30
32
|
|
31
33
|
mount_uploader :avatar, Decidim::AvatarUploader
|
@@ -68,6 +70,10 @@ module Decidim
|
|
68
70
|
Decidim::Follow.where(user: self, followable: followable).any?
|
69
71
|
end
|
70
72
|
|
73
|
+
def unread_conversations
|
74
|
+
Decidim::Messaging::Conversation.unread_by(self)
|
75
|
+
end
|
76
|
+
|
71
77
|
# Check if the user exists with the given email and the current organization
|
72
78
|
#
|
73
79
|
# warden_conditions - A hash with the authentication conditions
|
@@ -76,10 +82,10 @@ module Decidim
|
|
76
82
|
# Returns a User.
|
77
83
|
def self.find_for_authentication(warden_conditions)
|
78
84
|
organization = warden_conditions.dig(:env, "decidim.current_organization")
|
79
|
-
|
85
|
+
find_by(
|
80
86
|
email: warden_conditions[:email],
|
81
87
|
decidim_organization_id: organization.id
|
82
|
-
)
|
88
|
+
)
|
83
89
|
end
|
84
90
|
|
85
91
|
protected
|
@@ -5,7 +5,7 @@ module Decidim
|
|
5
5
|
class UserGroup < ApplicationRecord
|
6
6
|
belongs_to :organization, foreign_key: "decidim_organization_id", class_name: "Decidim::Organization"
|
7
7
|
|
8
|
-
has_many :memberships, class_name: "Decidim::UserGroupMembership", foreign_key: :decidim_user_group_id
|
8
|
+
has_many :memberships, class_name: "Decidim::UserGroupMembership", foreign_key: :decidim_user_group_id, dependent: :destroy
|
9
9
|
has_many :users, through: :memberships, class_name: "Decidim::User", foreign_key: :decidim_user_id
|
10
10
|
|
11
11
|
validates :name, presence: true, uniqueness: { scope: :decidim_organization_id }
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Messaging
|
5
|
+
# A class used to find the conversations a user is participating in.
|
6
|
+
class UserConversations < Rectify::Query
|
7
|
+
# Syntactic sugar to initialize the class and return the queried objects.
|
8
|
+
#
|
9
|
+
# user - a User that needs to find which processes can manage
|
10
|
+
def self.for(user)
|
11
|
+
new(user).query
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(user)
|
15
|
+
@user = user
|
16
|
+
end
|
17
|
+
|
18
|
+
def query
|
19
|
+
Conversation
|
20
|
+
.joins(:participations)
|
21
|
+
.where(decidim_messaging_participations: { decidim_participant_id: user.id })
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :user
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
# Use this class as a scrubber to sanitize user input. The default
|
5
|
+
# scrubbed provided by Rails does not allow `iframe`s, and we're using
|
6
|
+
# them to embed videos, so we need to provide a whole new scrubber.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# sanitize(@page.body, scrubber: Decidim::UserInputScrubber.new)
|
11
|
+
#
|
12
|
+
# Lists of default tags and attributes are extracted from
|
13
|
+
# https://stackoverflow.com/a/35073814/2110884.
|
14
|
+
class UserInputScrubber < Rails::Html::PermitScrubber
|
15
|
+
def initialize
|
16
|
+
super
|
17
|
+
self.tags = custom_allowed_tags
|
18
|
+
self.attributes = custom_allowed_attributes
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def custom_allowed_attributes
|
24
|
+
Loofah::HTML5::WhiteList::ALLOWED_ATTRIBUTES + %w(frameborder allowfullscreen)
|
25
|
+
end
|
26
|
+
|
27
|
+
def custom_allowed_tags
|
28
|
+
Loofah::HTML5::WhiteList::ALLOWED_ELEMENTS_WITH_LIBXML2 + %w(iframe)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -27,19 +27,31 @@ module Decidim
|
|
27
27
|
#
|
28
28
|
# Returns nil.
|
29
29
|
def authorize
|
30
|
-
|
31
|
-
|
32
|
-
return status(:ok) unless authorization_handler_name
|
33
|
-
|
34
|
-
return status(:missing) unless authorization
|
35
|
-
return status(:invalid, fields: unmatched_fields) if unmatched_fields.any?
|
36
|
-
return status(:incomplete, fields: missing_fields) if missing_fields.any?
|
30
|
+
status_code, fields = *status_data
|
37
31
|
|
38
|
-
status(
|
32
|
+
status(status_code, fields || {})
|
39
33
|
end
|
40
34
|
|
41
35
|
private
|
42
36
|
|
37
|
+
def status_data
|
38
|
+
raise AuthorizationError, "Missing data" unless feature && action
|
39
|
+
|
40
|
+
if !authorization_handler_name
|
41
|
+
:ok
|
42
|
+
elsif !authorization
|
43
|
+
:missing
|
44
|
+
elsif !authorization.granted?
|
45
|
+
:pending
|
46
|
+
elsif unmatched_fields.any?
|
47
|
+
[:invalid, fields: unmatched_fields]
|
48
|
+
elsif missing_fields.any?
|
49
|
+
[:incomplete, fields: missing_fields]
|
50
|
+
else
|
51
|
+
:ok
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
43
55
|
def status(status_code, data = {})
|
44
56
|
AuthorizationStatus.new(status_code, authorization_handler_name, data)
|
45
57
|
end
|
@@ -52,9 +64,7 @@ module Decidim
|
|
52
64
|
handler = permission["authorization_handler_name"]
|
53
65
|
return nil unless handler
|
54
66
|
|
55
|
-
@authorization ||= user
|
56
|
-
authorization.name == handler
|
57
|
-
end
|
67
|
+
@authorization ||= Verifications::Authorizations.new(user: user, name: handler).first
|
58
68
|
end
|
59
69
|
|
60
70
|
def unmatched_fields
|
@@ -87,7 +97,7 @@ module Decidim
|
|
87
97
|
end
|
88
98
|
|
89
99
|
class AuthorizationStatus
|
90
|
-
attr_reader :code, :
|
100
|
+
attr_reader :code, :data
|
91
101
|
|
92
102
|
def initialize(code, handler_name, data)
|
93
103
|
@code = code.to_sym
|
@@ -95,9 +105,35 @@ module Decidim
|
|
95
105
|
@data = data.symbolize_keys
|
96
106
|
end
|
97
107
|
|
108
|
+
def auth_method
|
109
|
+
return unless @handler_name
|
110
|
+
|
111
|
+
@auth_method ||= Verifications::Adapter.from_element(@handler_name)
|
112
|
+
end
|
113
|
+
|
114
|
+
def current_path(redirect_url: nil)
|
115
|
+
return unless auth_method
|
116
|
+
|
117
|
+
if pending?
|
118
|
+
auth_method.resume_authorization_path(redirect_url: redirect_url)
|
119
|
+
else
|
120
|
+
auth_method.root_path(redirect_url: redirect_url)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def handler_name
|
125
|
+
return unless auth_method
|
126
|
+
|
127
|
+
auth_method.key
|
128
|
+
end
|
129
|
+
|
98
130
|
def ok?
|
99
131
|
@code == :ok
|
100
132
|
end
|
133
|
+
|
134
|
+
def pending?
|
135
|
+
@code == :pending
|
136
|
+
end
|
101
137
|
end
|
102
138
|
|
103
139
|
class AuthorizationError < StandardError; end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
# This class wraps the logic to trace resource changes and their authorship.
|
5
|
+
# It is expected to be used with classes implementing the `Decidim::Traceable`
|
6
|
+
# concern. Version authors can be retrieved using the methods in
|
7
|
+
# `Decidim::TraceabilityHelper`.
|
8
|
+
#
|
9
|
+
# Examples:
|
10
|
+
#
|
11
|
+
# # consider MyResource implements Decidim::Traceable
|
12
|
+
# resource = Decidim::Traceability.new.create!(MyResource, author, params)
|
13
|
+
# resource.versions.count # => 1
|
14
|
+
# resource.versions.last.whodunnit # => author.to_gid.to_s
|
15
|
+
# resource.versions.last.event # => "create"
|
16
|
+
# resource = Decidim::Traceability.new.update!(resource, author, params)
|
17
|
+
# resource.versions.count # => 2
|
18
|
+
# resource.versions.last.event # => "update"
|
19
|
+
#
|
20
|
+
# This class uses the `paper_trail` gem internally, so refer to its documentation
|
21
|
+
# for further info on how to interact with versions.
|
22
|
+
class Traceability
|
23
|
+
# Calls the `create` method to the given class and sets the author of the version.
|
24
|
+
#
|
25
|
+
# klass - An ActiveRecord class that implements `Decidim::Traceable`
|
26
|
+
# author - An object that implements `to_gid` or a String
|
27
|
+
# params - a Hash
|
28
|
+
#
|
29
|
+
# Returns an instance of `klass`.
|
30
|
+
def create(klass, author, params)
|
31
|
+
PaperTrail.whodunnit(gid(author)) do
|
32
|
+
klass.create(params)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Calls the `create!` method to the given class and sets the author of the version.
|
37
|
+
#
|
38
|
+
# klass - An ActiveRecord class that implements `Decidim::Traceable`
|
39
|
+
# author - An object that implements `to_gid` or a String
|
40
|
+
# params - a Hash
|
41
|
+
#
|
42
|
+
# Returns an instance of `klass`.
|
43
|
+
def create!(klass, author, params)
|
44
|
+
PaperTrail.whodunnit(gid(author)) do
|
45
|
+
klass.create!(params)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Updates the `resource` with `update_attributes!` and sets the author of the version.
|
50
|
+
#
|
51
|
+
# resource - An ActiveRecord instance that implements `Decidim::Traceable`
|
52
|
+
# author - An object that implements `to_gid` or a String
|
53
|
+
# params - a Hash
|
54
|
+
#
|
55
|
+
# Returns the updated `resource`.
|
56
|
+
def update!(resource, author, params)
|
57
|
+
PaperTrail.whodunnit(gid(author)) do
|
58
|
+
resource.update_attributes!(params)
|
59
|
+
resource
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Finds the author of the last version of the resource.
|
64
|
+
#
|
65
|
+
# resource - an object implementing `Decidim::Traceable`
|
66
|
+
#
|
67
|
+
# Returns an object identifiable via GlobalID or a String.
|
68
|
+
def last_editor(resource)
|
69
|
+
version_editor(resource.versions.last)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Finds the author of the given version.
|
73
|
+
#
|
74
|
+
# version - an object that responds to `whodunnit` and returns a String.
|
75
|
+
#
|
76
|
+
# Returns an object identifiable via GlobalID or a String.
|
77
|
+
def version_editor(version)
|
78
|
+
::GlobalID::Locator.locate(version.whodunnit) || version.whodunnit
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# Calculates the GlobalID of the version author. If the object does not respond to
|
84
|
+
# `to_gid`, then it returns the object itself.
|
85
|
+
def gid(author)
|
86
|
+
return if author.blank?
|
87
|
+
return author.to_gid if author.respond_to?(:to_gid)
|
88
|
+
author
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|