decidim-sortitions 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE-AGPLv3.txt +661 -0
- data/README.md +31 -0
- data/Rakefile +3 -0
- data/app/assets/config/decidim_sortitions_manifest.css +1 -0
- data/app/assets/config/decidim_sortitions_manifest.js +1 -0
- data/app/assets/images/decidim/sortitions/icon.svg +4 -0
- data/app/assets/javascripts/decidim/sortitions/admin/sortitions.js +2 -0
- data/app/assets/javascripts/decidim/sortitions/social_share.js +2 -0
- data/app/assets/stylesheets/decidim/sortitions/_sortitions.scss +45 -0
- data/app/assets/stylesheets/decidim/sortitions/social_share.css.scss +18 -0
- data/app/commands/decidim/sortitions/admin/create_sortition.rb +81 -0
- data/app/commands/decidim/sortitions/admin/destroy_sortition.rb +45 -0
- data/app/commands/decidim/sortitions/admin/update_sortition.rb +45 -0
- data/app/controllers/concerns/decidim/sortitions/orderable.rb +50 -0
- data/app/controllers/decidim/sortitions/admin/application_controller.rb +28 -0
- data/app/controllers/decidim/sortitions/admin/sortitions_controller.rb +104 -0
- data/app/controllers/decidim/sortitions/application_controller.rb +13 -0
- data/app/controllers/decidim/sortitions/sortition_widgets_controller.rb +20 -0
- data/app/controllers/decidim/sortitions/sortitions_controller.rb +45 -0
- data/app/events/decidim/sortitions/create_sortition_event.rb +8 -0
- data/app/forms/decidim/sortitions/admin/destroy_sortition_form.rb +20 -0
- data/app/forms/decidim/sortitions/admin/edit_sortition_form.rb +22 -0
- data/app/forms/decidim/sortitions/admin/sortition_form.rb +44 -0
- data/app/helpers/decidim/sortitions/admin/sortitions_helper.rb +24 -0
- data/app/helpers/decidim/sortitions/sortitions_helper.rb +38 -0
- data/app/models/decidim/sortitions/abilities/admin/admin_ability.rb +23 -0
- data/app/models/decidim/sortitions/abilities/admin/process_admin_ability.rb +46 -0
- data/app/models/decidim/sortitions/application_record.rb +9 -0
- data/app/models/decidim/sortitions/sortition.rb +81 -0
- data/app/queries/decidim/sortitions/admin/participatory_space_proposal_features.rb +29 -0
- data/app/queries/decidim/sortitions/admin/participatory_space_proposals.rb +49 -0
- data/app/queries/decidim/sortitions/filtered_sortitions.rb +37 -0
- data/app/services/decidim/sortitions/admin/draw.rb +31 -0
- data/app/services/decidim/sortitions/sortition_search.rb +47 -0
- data/app/views/decidim/sortitions/admin/sortitions/_form.html.erb +41 -0
- data/app/views/decidim/sortitions/admin/sortitions/confirm_destroy.html.erb +21 -0
- data/app/views/decidim/sortitions/admin/sortitions/edit.html.erb +27 -0
- data/app/views/decidim/sortitions/admin/sortitions/index.html.erb +57 -0
- data/app/views/decidim/sortitions/admin/sortitions/new.html.erb +11 -0
- data/app/views/decidim/sortitions/admin/sortitions/show.html.erb +55 -0
- data/app/views/decidim/sortitions/sortition_widgets/show.html.erb +4 -0
- data/app/views/decidim/sortitions/sortitions/_count.html.erb +1 -0
- data/app/views/decidim/sortitions/sortitions/_filters.html.erb +31 -0
- data/app/views/decidim/sortitions/sortitions/_filters_small_view.html.erb +18 -0
- data/app/views/decidim/sortitions/sortitions/_filters_small_view0.html.erb +18 -0
- data/app/views/decidim/sortitions/sortitions/_linked_sortitions.html.erb +29 -0
- data/app/views/decidim/sortitions/sortitions/_proposal.html.erb +24 -0
- data/app/views/decidim/sortitions/sortitions/_proposal_badge.html.erb +3 -0
- data/app/views/decidim/sortitions/sortitions/_results_count.html.erb +8 -0
- data/app/views/decidim/sortitions/sortitions/_sortition.html.erb +25 -0
- data/app/views/decidim/sortitions/sortitions/_sortition_author.html.erb +17 -0
- data/app/views/decidim/sortitions/sortitions/_sortition_cancel_author.html.erb +17 -0
- data/app/views/decidim/sortitions/sortitions/_sortitions.html.erb +14 -0
- data/app/views/decidim/sortitions/sortitions/_sortitions_count.html.erb +1 -0
- data/app/views/decidim/sortitions/sortitions/_tags.html.erb +16 -0
- data/app/views/decidim/sortitions/sortitions/index.html.erb +21 -0
- data/app/views/decidim/sortitions/sortitions/index.js.erb +10 -0
- data/app/views/decidim/sortitions/sortitions/show.html.erb +134 -0
- data/config/initializers/social_share_button.rb +5 -0
- data/config/locales/ca.yml +155 -0
- data/config/locales/en.yml +154 -0
- data/config/locales/es.yml +155 -0
- data/db/migrate/20171215161358_create_decidim_module_sortitions_sortitions.rb +23 -0
- data/db/migrate/20171220164658_add_witnesses_to_sortitions.rb +7 -0
- data/db/migrate/20171220164744_add_additional_info_to_sortitions.rb +7 -0
- data/db/migrate/20180102100101_add_author_to_sortitions.rb +7 -0
- data/db/migrate/20180102101128_add_reference_to_sortitions.rb +8 -0
- data/db/migrate/20180103082645_add_title_to_sortition.rb +7 -0
- data/db/migrate/20180103123055_drop_decidim_category_id_from_sortitions.rb +13 -0
- data/db/migrate/20180103160301_add_cancel_data_to_sortition.rb +9 -0
- data/db/migrate/20180104143054_make_sortition_reference_nullable.rb +7 -0
- data/db/migrate/20180104145344_add_candidate_proposals_to_sortitions.rb +7 -0
- data/db/migrate/20180108132729_rename_sortitions_table.rb +7 -0
- data/lib/decidim/sortitions.rb +24 -0
- data/lib/decidim/sortitions/admin.rb +10 -0
- data/lib/decidim/sortitions/admin_engine.rb +35 -0
- data/lib/decidim/sortitions/engine.rb +26 -0
- data/lib/decidim/sortitions/feature.rb +43 -0
- data/lib/decidim/sortitions/test/factories.rb +37 -0
- data/lib/decidim/sortitions/version.rb +7 -0
- metadata +208 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Sortitions
|
5
|
+
# This controller is the abstract class from which all other controllers of
|
6
|
+
# this engine inherit.
|
7
|
+
#
|
8
|
+
# Note that it inherits from `Decidim::Features::BaseController`, which
|
9
|
+
# override its layout and provide all kinds of useful methods.
|
10
|
+
class ApplicationController < Decidim::Features::BaseController
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Sortitions
|
5
|
+
class SortitionWidgetsController < Decidim::WidgetsController
|
6
|
+
helper Decidim::SanitizeHelper
|
7
|
+
helper Sortitions::SortitionsHelper
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def model
|
12
|
+
@model ||= Sortition.where(feature: params[:feature_id]).find(params[:sortition_id])
|
13
|
+
end
|
14
|
+
|
15
|
+
def iframe_url
|
16
|
+
@iframe_url ||= sortition_sortition_widget_url(model)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Sortitions
|
5
|
+
# Exposes the sortition resource so users can view them
|
6
|
+
class SortitionsController < Decidim::Sortitions::ApplicationController
|
7
|
+
helper Decidim::WidgetUrlsHelper
|
8
|
+
include FilterResource
|
9
|
+
include Orderable
|
10
|
+
include Paginable
|
11
|
+
|
12
|
+
helper_method :sortition
|
13
|
+
|
14
|
+
helper Decidim::Proposals::ApplicationHelper
|
15
|
+
|
16
|
+
def index
|
17
|
+
@sortitions = search
|
18
|
+
.results
|
19
|
+
.includes(:author)
|
20
|
+
.includes(:category)
|
21
|
+
|
22
|
+
@sortitions = paginate(@sortitions)
|
23
|
+
@sortitions = reorder(@sortitions)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def sortition
|
29
|
+
Sortition.find(params[:id])
|
30
|
+
end
|
31
|
+
|
32
|
+
def search_klass
|
33
|
+
SortitionSearch
|
34
|
+
end
|
35
|
+
|
36
|
+
def default_filter_params
|
37
|
+
{
|
38
|
+
search_text: "",
|
39
|
+
category_id: "",
|
40
|
+
state: "active"
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Sortitions
|
5
|
+
module Admin
|
6
|
+
class DestroySortitionForm < Form
|
7
|
+
include TranslatableAttributes
|
8
|
+
|
9
|
+
mimic :sortition
|
10
|
+
|
11
|
+
translatable_attribute :cancel_reason, String
|
12
|
+
|
13
|
+
validates :cancel_reason, translatable_presence: true
|
14
|
+
|
15
|
+
delegate :current_participatory_space, to: :context
|
16
|
+
delegate :current_feature, to: :context
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Sortitions
|
5
|
+
module Admin
|
6
|
+
class EditSortitionForm < Form
|
7
|
+
include TranslatableAttributes
|
8
|
+
|
9
|
+
mimic :sortition
|
10
|
+
|
11
|
+
translatable_attribute :title, String
|
12
|
+
translatable_attribute :additional_info, String
|
13
|
+
|
14
|
+
validates :title, translatable_presence: true
|
15
|
+
validates :additional_info, translatable_presence: true
|
16
|
+
|
17
|
+
delegate :current_participatory_space, to: :context
|
18
|
+
delegate :current_feature, to: :context
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Sortitions
|
5
|
+
module Admin
|
6
|
+
class SortitionForm < Form
|
7
|
+
include TranslatableAttributes
|
8
|
+
|
9
|
+
mimic :sortition
|
10
|
+
|
11
|
+
translatable_attribute :title, String
|
12
|
+
attribute :decidim_proposals_feature_id, Integer
|
13
|
+
attribute :decidim_category_id, Integer
|
14
|
+
attribute :dice, Integer
|
15
|
+
attribute :target_items, Integer
|
16
|
+
translatable_attribute :witnesses, String
|
17
|
+
translatable_attribute :additional_info, String
|
18
|
+
|
19
|
+
validates :title, translatable_presence: true
|
20
|
+
validates :decidim_proposals_feature_id, presence: true
|
21
|
+
validates :witnesses, translatable_presence: true
|
22
|
+
validates :additional_info, translatable_presence: true
|
23
|
+
validates :dice,
|
24
|
+
presence: true,
|
25
|
+
numericality: {
|
26
|
+
only_integer: true,
|
27
|
+
greater_than_or_equal_to: 1,
|
28
|
+
less_than_or_equal_to: 6
|
29
|
+
}
|
30
|
+
|
31
|
+
validates :target_items,
|
32
|
+
presence: true,
|
33
|
+
numericality: {
|
34
|
+
only_integer: true,
|
35
|
+
greater_than_or_equal_to: 1
|
36
|
+
}
|
37
|
+
|
38
|
+
delegate :categories, to: :current_participatory_space
|
39
|
+
delegate :current_participatory_space, to: :context
|
40
|
+
delegate :current_feature, to: :context
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Sortitions
|
5
|
+
module Admin
|
6
|
+
module SortitionsHelper
|
7
|
+
include Decidim::TranslationsHelper
|
8
|
+
|
9
|
+
# Converst a list of features into a list of selectable options
|
10
|
+
def features_options(features)
|
11
|
+
features.map do |f|
|
12
|
+
[translated_attribute(f.name), f.id]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def sortition_category(sortition)
|
17
|
+
return translated_attribute sortition.category&.name if sortition.category
|
18
|
+
|
19
|
+
I18n.t("sortitions.form.all_categories", scope: "decidim.sortitions.admin")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Sortitions
|
5
|
+
module SortitionsHelper
|
6
|
+
include Decidim::TranslationsHelper
|
7
|
+
|
8
|
+
def proposal_path(proposal)
|
9
|
+
EngineRouter.main_proxy(proposal.feature).proposal_path(proposal)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Generates the sortition category label
|
13
|
+
def sortition_category_label(sortition)
|
14
|
+
if sortition.category.present?
|
15
|
+
return I18n.t("show.category",
|
16
|
+
scope: "decidim.sortitions.sortitions",
|
17
|
+
category: translated_attribute(sortition.category.name))
|
18
|
+
end
|
19
|
+
|
20
|
+
I18n.t("show.any_category", scope: "decidim.sortitions.sortitions")
|
21
|
+
end
|
22
|
+
|
23
|
+
# Show list of candidate proposals for a sortition. Selected sortition ids will appear with bold font.
|
24
|
+
def sortition_proposal_candidate_ids(sortition)
|
25
|
+
result = []
|
26
|
+
sortition.candidate_proposals.each do |proposal_id|
|
27
|
+
result << if sortition.selected_proposals.include? proposal_id
|
28
|
+
"<b>#{proposal_id}</b>"
|
29
|
+
else
|
30
|
+
proposal_id.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
result.join(", ").html_safe
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Sortitions
|
5
|
+
module Abilities
|
6
|
+
module Admin
|
7
|
+
# Defines the abilities for a user in the admin section. Intended to be
|
8
|
+
# used with `cancancan`.
|
9
|
+
class AdminAbility < Decidim::Abilities::AdminAbility
|
10
|
+
def define_abilities
|
11
|
+
super
|
12
|
+
|
13
|
+
can :manage, Sortition
|
14
|
+
cannot :destroy, Sortition
|
15
|
+
can :destroy, Sortition do |sortition|
|
16
|
+
!sortition.cancelled?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Sortitions
|
5
|
+
module Abilities
|
6
|
+
module Admin
|
7
|
+
class ProcessAdminAbility
|
8
|
+
include CanCan::Ability
|
9
|
+
|
10
|
+
attr_reader :user, :context
|
11
|
+
|
12
|
+
def initialize(user, context)
|
13
|
+
return unless user
|
14
|
+
return if user.admin?
|
15
|
+
|
16
|
+
@user = user
|
17
|
+
@context = context
|
18
|
+
|
19
|
+
return unless process_administrator?
|
20
|
+
|
21
|
+
can :manage, Sortition
|
22
|
+
cannot :destroy, Sortition
|
23
|
+
can :destroy, Sortition do |sortition|
|
24
|
+
!sortition.cancelled?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def process_administrator?
|
31
|
+
return false unless current_participatory_space.is_a? Decidim::ParticipatoryProcess
|
32
|
+
|
33
|
+
Decidim::ParticipatoryProcesses::Admin::AdminUsers
|
34
|
+
.for(current_participatory_space)
|
35
|
+
.where(id: user.id)
|
36
|
+
.any?
|
37
|
+
end
|
38
|
+
|
39
|
+
def current_participatory_space
|
40
|
+
@current_participatory_space ||= @context[:current_participatory_space]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Sortitions
|
5
|
+
# Model that encapsulates the parameters of a sortion
|
6
|
+
class Sortition < ApplicationRecord
|
7
|
+
include Decidim::Resourceable
|
8
|
+
include Decidim::HasCategory
|
9
|
+
include Decidim::Authorable
|
10
|
+
include Decidim::HasFeature
|
11
|
+
include Decidim::HasReference
|
12
|
+
include Decidim::Comments::Commentable
|
13
|
+
|
14
|
+
feature_manifest_name "sortitions"
|
15
|
+
|
16
|
+
belongs_to :decidim_proposals_feature,
|
17
|
+
foreign_key: "decidim_proposals_feature_id",
|
18
|
+
class_name: "Decidim::Feature"
|
19
|
+
|
20
|
+
belongs_to :cancelled_by_user,
|
21
|
+
foreign_key: "cancelled_by_user_id",
|
22
|
+
class_name: "Decidim::User",
|
23
|
+
optional: true
|
24
|
+
|
25
|
+
scope :categorized_as, lambda { |category_id|
|
26
|
+
includes(:categorization)
|
27
|
+
.where("decidim_categorizations.decidim_category_id" => category_id)
|
28
|
+
}
|
29
|
+
|
30
|
+
scope :active, -> { where(cancelled_on: nil) }
|
31
|
+
scope :cancelled, -> { where.not(cancelled_on: nil) }
|
32
|
+
|
33
|
+
def proposals
|
34
|
+
Decidim::Proposals::Proposal.where(id: selected_proposals)
|
35
|
+
end
|
36
|
+
|
37
|
+
def similar_count
|
38
|
+
Sortition.where(feature: feature)
|
39
|
+
.where(decidim_proposals_feature: decidim_proposals_feature)
|
40
|
+
.categorized_as(category&.id)
|
41
|
+
.where(target_items: target_items)
|
42
|
+
.count
|
43
|
+
end
|
44
|
+
|
45
|
+
def seed
|
46
|
+
request_timestamp.to_i * dice
|
47
|
+
end
|
48
|
+
|
49
|
+
def cancelled?
|
50
|
+
cancelled_on.present?
|
51
|
+
end
|
52
|
+
|
53
|
+
# Public: Overrides the `commentable?` Commentable concern method.
|
54
|
+
def commentable?
|
55
|
+
feature.settings.comments_enabled?
|
56
|
+
end
|
57
|
+
|
58
|
+
# Public: Overrides the `accepts_new_comments?` Commentable concern method.
|
59
|
+
def accepts_new_comments?
|
60
|
+
commentable?
|
61
|
+
end
|
62
|
+
|
63
|
+
# Public: Overrides the `comments_have_alignment?` Commentable concern method.
|
64
|
+
def comments_have_alignment?
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
# Public: Overrides the `comments_have_votes?` Commentable concern method.
|
69
|
+
def comments_have_votes?
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.order_randomly(seed)
|
74
|
+
transaction do
|
75
|
+
connection.execute("SELECT setseed(#{connection.quote(seed)})")
|
76
|
+
order("RANDOM()").load
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Sortitions
|
5
|
+
module Admin
|
6
|
+
# Query that retrieves a list of proposal features
|
7
|
+
class ParticipatorySpaceProposalFeatures < Rectify::Query
|
8
|
+
attr_reader :participatory_space
|
9
|
+
|
10
|
+
# Sugar syntax. Allow retrieving all proposal features for the
|
11
|
+
# given participatory space.
|
12
|
+
def self.for(participatory_space)
|
13
|
+
new(participatory_space).query
|
14
|
+
end
|
15
|
+
|
16
|
+
# Initializes the query
|
17
|
+
def initialize(participatory_space)
|
18
|
+
@participatory_space = participatory_space
|
19
|
+
end
|
20
|
+
|
21
|
+
def query
|
22
|
+
Feature
|
23
|
+
.where(participatory_space: participatory_space, manifest_name: "proposals")
|
24
|
+
.published
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|