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
@@ -9,7 +9,8 @@ module Decidim
|
|
9
9
|
#
|
10
10
|
# Returns a url.
|
11
11
|
def main_feature_path(feature)
|
12
|
-
|
12
|
+
current_params = try(:params) || {}
|
13
|
+
EngineRouter.main_proxy(feature).root_path(locale: current_params[:locale])
|
13
14
|
end
|
14
15
|
|
15
16
|
# Returns the defined admin root path for a given feature.
|
@@ -18,7 +19,8 @@ module Decidim
|
|
18
19
|
#
|
19
20
|
# Returns a url.
|
20
21
|
def manage_feature_path(feature)
|
21
|
-
|
22
|
+
current_params = try(:params) || {}
|
23
|
+
EngineRouter.admin_proxy(feature).root_path(locale: current_params[:locale])
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
@@ -51,9 +51,7 @@ module Decidim
|
|
51
51
|
#
|
52
52
|
# Returns an <img /> tag with the SVG icon.
|
53
53
|
def external_icon(path, options = {})
|
54
|
-
# Ugly hack to prevent PhantomJS from freaking out with SVGs.
|
55
54
|
classes = _icon_classes(options) + ["external-icon"]
|
56
|
-
return content_tag(:span, "?", class: classes.join(" "), "data-src" => path) if Rails.env.test?
|
57
55
|
|
58
56
|
if path.split(".").last == "svg"
|
59
57
|
asset = Rails.application.assets_manifest.find_sources(path).first
|
@@ -12,5 +12,15 @@ module Decidim
|
|
12
12
|
active_class: "main-nav__link--active"
|
13
13
|
)
|
14
14
|
end
|
15
|
+
|
16
|
+
# Public: Returns the user menu presenter object
|
17
|
+
def user_menu
|
18
|
+
@user_menu ||= ::Decidim::InlineMenuPresenter.new(
|
19
|
+
:user_menu,
|
20
|
+
self,
|
21
|
+
element_class: "tabs-title",
|
22
|
+
active_class: "is-active"
|
23
|
+
)
|
24
|
+
end
|
15
25
|
end
|
16
26
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Messaging
|
5
|
+
module ConversationHelper
|
6
|
+
#
|
7
|
+
# Builds a link to the conversation between the current user and another
|
8
|
+
# user.
|
9
|
+
#
|
10
|
+
# * If there's no current user, it links to the login form.
|
11
|
+
#
|
12
|
+
# * If there's no prior existing conversation between the users, it links
|
13
|
+
# to the new conversation form.
|
14
|
+
#
|
15
|
+
# * Otherwise, it links to the existing conversation.
|
16
|
+
#
|
17
|
+
# @param user [Decidim::User] The user to link to a conversation with
|
18
|
+
#
|
19
|
+
# @return [String] The resulting route
|
20
|
+
#
|
21
|
+
def link_to_current_or_new_conversation_with(user)
|
22
|
+
return decidim.new_user_session_path unless user_signed_in?
|
23
|
+
|
24
|
+
conversation_between = UserConversations.for(current_user).find do |conversation|
|
25
|
+
conversation.participants.to_set == [current_user, user].to_set
|
26
|
+
end
|
27
|
+
|
28
|
+
if conversation_between
|
29
|
+
decidim.conversation_path(conversation_between)
|
30
|
+
else
|
31
|
+
decidim.new_conversation_path(recipient_id: user.id)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
# Helper that provides methods to render order selector and links
|
5
|
+
module SanitizeHelper
|
6
|
+
include ActionView::Helpers::SanitizeHelper
|
7
|
+
|
8
|
+
# Public: It sanitizes a user-inputted string with the
|
9
|
+
# `Decidim::UserInputScrubber` scrubber, so that video embeds work
|
10
|
+
# as expected. Uses Rails' `sanitize` internally.
|
11
|
+
#
|
12
|
+
# html - A string representing user-inputted HTML.
|
13
|
+
#
|
14
|
+
# Returns an HTML-safe String.
|
15
|
+
def decidim_sanitize(html)
|
16
|
+
sanitize(html, scrubber: Decidim::UserInputScrubber.new)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
# A Helper to find and render the author of a version.
|
5
|
+
module TraceabilityHelper
|
6
|
+
include Decidim::SanitizeHelper
|
7
|
+
# Renders the avatar and author name of the author of the last version of the given
|
8
|
+
# resource.
|
9
|
+
#
|
10
|
+
# resource - an object implementing `Decidim::Traceable`
|
11
|
+
#
|
12
|
+
# Returns an HTML-safe String representing the HTML to render the author.
|
13
|
+
def render_resource_last_editor(resource)
|
14
|
+
render partial: "decidim/shared/version_author",
|
15
|
+
locals: {
|
16
|
+
author: Decidim.traceability.last_editor(resource)
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
# Renders the avatar and author name of the author of the given version.
|
21
|
+
#
|
22
|
+
# version - an object that responds to `whodunnit` and returns a String.
|
23
|
+
#
|
24
|
+
# Returns an HTML-safe String representing the HTML to render the author.
|
25
|
+
def render_resource_editor(version)
|
26
|
+
render partial: "decidim/shared/version_author",
|
27
|
+
locals: {
|
28
|
+
author: Decidim.traceability.version_editor(version)
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
# Caches a DiffRenderer instance for the `current_version`.
|
33
|
+
def diff_renderer
|
34
|
+
@diff_renderer ||= Decidim::Accountability::DiffRenderer.new(current_version)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Renders the diff between `:old_data` and `:new_data` keys in the `data` param.
|
38
|
+
#
|
39
|
+
# data - A Hash with `old_data`, `:new_data` and `:type` keys.
|
40
|
+
#
|
41
|
+
# Returns an HTML-safe string.
|
42
|
+
def render_diff_data(data)
|
43
|
+
content_tag(:div, class: "card card--list diff diff-#{data[:type]}") do
|
44
|
+
if [:i18n, :i18n_html].include?(data[:type])
|
45
|
+
render_diff_value(
|
46
|
+
" ",
|
47
|
+
data[:type],
|
48
|
+
nil,
|
49
|
+
data: {
|
50
|
+
old_value: data[:old_value].to_s.gsub("</p>", "</p>\n"),
|
51
|
+
new_value: data[:new_value].to_s.gsub("</p>", "</p>\n")
|
52
|
+
}
|
53
|
+
)
|
54
|
+
else
|
55
|
+
render_diff_value(data[:old_value], data[:type], :removal) +
|
56
|
+
render_diff_value(data[:new_value], data[:type], :addition)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Renders the given value in a user-friendly way based on the value class.
|
64
|
+
#
|
65
|
+
# value - an object to be rendered
|
66
|
+
#
|
67
|
+
# Returns an HTML-ready String.
|
68
|
+
def render_diff_value(value, type, action, options = {})
|
69
|
+
return "".html_safe if value.blank?
|
70
|
+
|
71
|
+
value_to_render = case type
|
72
|
+
when :date
|
73
|
+
l value, format: :long
|
74
|
+
when :percentage
|
75
|
+
number_to_percentage value, precision: 2
|
76
|
+
else
|
77
|
+
value
|
78
|
+
end
|
79
|
+
|
80
|
+
content_tag(:div, class: "card--list__item #{action}") do
|
81
|
+
content_tag(:div, class: "card--list__text") do
|
82
|
+
content_tag(:div, { class: "diff__value" }.merge(options)) do
|
83
|
+
value_to_render
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -4,14 +4,16 @@ module Decidim
|
|
4
4
|
# Helper that provides convenient methods to deal with translated attributes.
|
5
5
|
module TranslationsHelper
|
6
6
|
# Public: Returns the translation of an attribute using the current locale,
|
7
|
-
# if available.
|
7
|
+
# if available. Checks for the organization default locale as fallback.
|
8
8
|
#
|
9
9
|
# attribute - A Hash where keys (strings) are locales, and their values are
|
10
10
|
# the translation for each locale.
|
11
11
|
#
|
12
12
|
# Returns a String with the translation.
|
13
13
|
def translated_attribute(attribute)
|
14
|
-
attribute.try(:[], I18n.locale.to_s) ||
|
14
|
+
attribute.try(:[], I18n.locale.to_s).presence ||
|
15
|
+
attribute.try(:[], current_organization.default_locale).presence ||
|
16
|
+
""
|
15
17
|
end
|
16
18
|
|
17
19
|
# Public: Creates a translation for each available language in the list
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
# This module includes helpers to manage view hooks in layout
|
5
|
+
module ViewHooksHelper
|
6
|
+
# Public: Renders all hooks registered as `hook_name`.
|
7
|
+
#
|
8
|
+
# hook_name - a Symbol representing the name of the hook.
|
9
|
+
#
|
10
|
+
# Returns an HTML safe String.
|
11
|
+
def render_hook(hook_name)
|
12
|
+
Decidim.view_hooks.render(hook_name, self)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Messaging
|
5
|
+
# A custom mailer for sending notifications to users when they receive
|
6
|
+
# private messages
|
7
|
+
class ConversationMailer < Decidim::ApplicationMailer
|
8
|
+
def new_conversation(originator, user, conversation)
|
9
|
+
notification_mail(
|
10
|
+
from: originator,
|
11
|
+
to: user,
|
12
|
+
conversation: conversation,
|
13
|
+
action: "new_conversation"
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def new_message(sender, user, conversation)
|
18
|
+
notification_mail(
|
19
|
+
from: sender,
|
20
|
+
to: user,
|
21
|
+
conversation: conversation,
|
22
|
+
action: "new_conversation"
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def notification_mail(from:, to:, conversation:, action:)
|
29
|
+
with_user(to) do
|
30
|
+
@organization = to.organization
|
31
|
+
@conversation = conversation
|
32
|
+
@sender = from.name
|
33
|
+
@recipient = to.name
|
34
|
+
@host = @organization.host
|
35
|
+
|
36
|
+
subject = I18n.t(
|
37
|
+
"conversation_mailer.#{action}.subject",
|
38
|
+
scope: "decidim.messaging",
|
39
|
+
sender: @sender
|
40
|
+
)
|
41
|
+
|
42
|
+
mail(to: to.email, subject: subject)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -25,8 +25,12 @@ module Decidim
|
|
25
25
|
merge ability.constantize.new(user, context)
|
26
26
|
end
|
27
27
|
|
28
|
-
can :
|
29
|
-
authorization.user == user
|
28
|
+
can :create, Authorization do |authorization|
|
29
|
+
authorization.user == user && not_already_active?(user, authorization)
|
30
|
+
end
|
31
|
+
|
32
|
+
can :update, Authorization do |authorization|
|
33
|
+
authorization.user == user && !authorization.granted?
|
30
34
|
end
|
31
35
|
|
32
36
|
can :manage, Follow do |follow|
|
@@ -36,6 +40,16 @@ module Decidim
|
|
36
40
|
can :manage, Notification do |notification|
|
37
41
|
notification.user == user
|
38
42
|
end
|
43
|
+
|
44
|
+
can :manage, Messaging::Conversation do |conversation|
|
45
|
+
conversation.participants.include?(user)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def not_already_active?(user, authorization)
|
52
|
+
Verifications::Authorizations.new(user: user, name: authorization.name).none?
|
39
53
|
end
|
40
54
|
end
|
41
55
|
end
|
@@ -11,16 +11,34 @@ module Decidim
|
|
11
11
|
# depending on the response it allows the creation of the authorization or
|
12
12
|
# not.
|
13
13
|
class Authorization < ApplicationRecord
|
14
|
-
|
14
|
+
mount_uploader :verification_attachment, Decidim::Verifications::AttachmentUploader
|
15
|
+
|
16
|
+
belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User"
|
15
17
|
|
16
18
|
validates :name, uniqueness: { scope: :decidim_user_id }
|
19
|
+
validates :verification_metadata, absence: true, if: :granted?
|
20
|
+
validates :verification_attachment, absence: true, if: :granted?
|
17
21
|
|
18
22
|
validate :active_handler?
|
19
23
|
|
24
|
+
def grant!
|
25
|
+
remove_verification_attachment!
|
26
|
+
|
27
|
+
update!(granted_at: Time.zone.now, verification_metadata: {})
|
28
|
+
end
|
29
|
+
|
30
|
+
def granted?
|
31
|
+
!granted_at.nil?
|
32
|
+
end
|
33
|
+
|
20
34
|
private
|
21
35
|
|
22
36
|
def active_handler?
|
23
|
-
|
37
|
+
if Decidim::Verifications.find_workflow_manifest(name)
|
38
|
+
true
|
39
|
+
else
|
40
|
+
false
|
41
|
+
end
|
24
42
|
end
|
25
43
|
end
|
26
44
|
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Messaging
|
5
|
+
#
|
6
|
+
# Holds a conversation between a number of participants. Each conversation
|
7
|
+
# would be equivalent to an entry in your Telegram conversation list, be it
|
8
|
+
# a group or a one-to-one conversation.
|
9
|
+
#
|
10
|
+
class Conversation < ApplicationRecord
|
11
|
+
has_many :participations, foreign_key: :decidim_conversation_id,
|
12
|
+
class_name: "Decidim::Messaging::Participation",
|
13
|
+
dependent: :destroy,
|
14
|
+
inverse_of: :conversation
|
15
|
+
|
16
|
+
has_many :participants, through: :participations
|
17
|
+
|
18
|
+
has_many :messages, foreign_key: :decidim_conversation_id,
|
19
|
+
class_name: "Decidim::Messaging::Message",
|
20
|
+
dependent: :destroy,
|
21
|
+
inverse_of: :conversation
|
22
|
+
|
23
|
+
has_many :receipts, through: :messages
|
24
|
+
|
25
|
+
scope :unread_by, lambda { |user|
|
26
|
+
joins(:receipts).merge(Receipt.unread_by(user)).distinct
|
27
|
+
}
|
28
|
+
|
29
|
+
delegate :mark_as_read, to: :receipts
|
30
|
+
|
31
|
+
#
|
32
|
+
# Initiates a conversation between a user and a set of interlocutors with
|
33
|
+
# an initial message. Works just like .start, but saves all the dependent
|
34
|
+
# objects into DB.
|
35
|
+
#
|
36
|
+
# @param (see .start)
|
37
|
+
#
|
38
|
+
# @return (see .start)
|
39
|
+
#
|
40
|
+
def self.start!(originator:, interlocutors:, body:)
|
41
|
+
conversation = start(
|
42
|
+
originator: originator,
|
43
|
+
interlocutors: interlocutors,
|
44
|
+
body: body
|
45
|
+
)
|
46
|
+
|
47
|
+
conversation.save!
|
48
|
+
|
49
|
+
conversation
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# Initiates a conversation between a user and a set of interlocutors with
|
54
|
+
# an initial message.
|
55
|
+
#
|
56
|
+
# @param originator [Decidim::User] The user starting the conversation
|
57
|
+
# @param interlocutors [Array<Decidim::User>] The set of interlocutors in
|
58
|
+
# the conversation (not including the originator).
|
59
|
+
# @param body [String] The content of the initial message
|
60
|
+
#
|
61
|
+
# @return [Decidim::Messaging::Conversation] The newly created conversation
|
62
|
+
#
|
63
|
+
def self.start(originator:, interlocutors:, body:)
|
64
|
+
conversation = new(participants: [originator] + interlocutors)
|
65
|
+
|
66
|
+
conversation.add_message(sender: originator, body: body)
|
67
|
+
|
68
|
+
conversation
|
69
|
+
end
|
70
|
+
|
71
|
+
# Appends a message to this conversation and saves everything to DB.
|
72
|
+
#
|
73
|
+
# @param (see #add_message)
|
74
|
+
#
|
75
|
+
# @return (see #add_message)
|
76
|
+
#
|
77
|
+
def add_message!(sender:, body:)
|
78
|
+
add_message(sender: sender, body: body)
|
79
|
+
|
80
|
+
save!
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# Appends a message to this conversation
|
85
|
+
#
|
86
|
+
# @param sender [Decidim::User] The sender of the message
|
87
|
+
# @param body [String] The content of the message
|
88
|
+
#
|
89
|
+
# @return [Decidim::Messaging::Message] The newly created message
|
90
|
+
#
|
91
|
+
def add_message(sender:, body:)
|
92
|
+
message = messages.build(sender: sender, body: body)
|
93
|
+
|
94
|
+
message.envelope_for(interlocutors(sender))
|
95
|
+
|
96
|
+
message
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Given a user, returns her interlocutors in this conversation
|
101
|
+
#
|
102
|
+
# @param user [Decidim::User] The user to find interlocutors for
|
103
|
+
#
|
104
|
+
# @return [Array<Decidim::User>]
|
105
|
+
#
|
106
|
+
def interlocutors(user)
|
107
|
+
participants.reject { |participant| participant.id == user.id }
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# The most recent message in this conversation
|
112
|
+
#
|
113
|
+
# @return [Decidim::Messaging::Message]
|
114
|
+
#
|
115
|
+
def last_message
|
116
|
+
messages.last
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# The number of messages in this conversation a user has not yet read
|
121
|
+
#
|
122
|
+
# @return [Integer]
|
123
|
+
#
|
124
|
+
def unread_count(user)
|
125
|
+
receipts.unread_by(user).count
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|