decidim-proposals 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +24 -0
  3. data/Rakefile +2 -0
  4. data/app/commands/decidim/proposals/admin/create_proposal.rb +43 -0
  5. data/app/commands/decidim/proposals/create_proposal.rb +42 -0
  6. data/app/controllers/decidim/proposals/admin/application_controller.rb +14 -0
  7. data/app/controllers/decidim/proposals/admin/proposals_controller.rb +37 -0
  8. data/app/controllers/decidim/proposals/application_controller.rb +13 -0
  9. data/app/controllers/decidim/proposals/proposals_controller.rb +41 -0
  10. data/app/forms/decidim/proposals/admin/proposal_form.rb +38 -0
  11. data/app/forms/decidim/proposals/proposal_form.rb +37 -0
  12. data/app/helpers/decidim/proposals/application_helper.rb +10 -0
  13. data/app/models/decidim/proposals/application_record.rb +9 -0
  14. data/app/models/decidim/proposals/proposal.rb +53 -0
  15. data/app/services/decidim/proposals/proposal_search.rb +36 -0
  16. data/app/views/decidim/proposals/admin/proposals/_form.html.erb +19 -0
  17. data/app/views/decidim/proposals/admin/proposals/index.html.erb +34 -0
  18. data/app/views/decidim/proposals/admin/proposals/new.html.erb +9 -0
  19. data/app/views/decidim/proposals/proposals/_proposal.html.erb +34 -0
  20. data/app/views/decidim/proposals/proposals/index.html.erb +23 -0
  21. data/app/views/decidim/proposals/proposals/new.html.erb +40 -0
  22. data/app/views/decidim/proposals/proposals/show.html.erb +37 -0
  23. data/config/i18n-tasks.yml +4 -0
  24. data/config/locales/ca.yml +46 -0
  25. data/config/locales/en.yml +47 -0
  26. data/config/locales/es.yml +46 -0
  27. data/db/migrate/20161212110850_create_decidim_proposals.rb +14 -0
  28. data/lib/decidim/proposals.rb +12 -0
  29. data/lib/decidim/proposals/admin.rb +9 -0
  30. data/lib/decidim/proposals/admin_engine.rb +20 -0
  31. data/lib/decidim/proposals/engine.rb +18 -0
  32. data/lib/decidim/proposals/feature.rb +35 -0
  33. metadata +149 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4be5f073d284da3f8ac70f23505dc51a15b6b888
4
+ data.tar.gz: b6e8be99a9db14b98eec0246f41648d34bbf3d84
5
+ SHA512:
6
+ metadata.gz: e7d3f307976ba987e3eb993452f98e58bedb727ae06a7caa81fd0fd0b7a0afc24f59950f999db00a41c103984b4a1d188703ea8d70dc808976a24b270ecd0867
7
+ data.tar.gz: 82905c51a3d79b6fb87579567dbb717fc3e359ff4088c2992fc1e53ba5341e77411e53f0c1f25f4d5a0b23fdee426cd75d38b001973cbde4cc6d6b96e8d06812
@@ -0,0 +1,24 @@
1
+ # Decidim::Proposals
2
+
3
+ The Proposals module adds one of the main features of Decidim: allows users to contribute to a particiaptory process by creating proposals.
4
+
5
+ ## Usage
6
+ Proposals will be available as a Feature for a Participatory Process.
7
+
8
+ ## Installation
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'decidim-proposals
13
+ ```
14
+
15
+ And then execute:
16
+ ```bash
17
+ $ bundle
18
+ ```
19
+
20
+ ## Contributing
21
+ See [Decidim](https://github.com/AjuntamentdeBarcelona/decidim).
22
+
23
+ ## License
24
+ See [Decidim](https://github.com/AjuntamentdeBarcelona/decidim).
@@ -0,0 +1,2 @@
1
+ # frozen_string_literal: true
2
+ require "decidim/common_rake"
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Proposals
4
+ module Admin
5
+ # A command with all the business logic when a user creates a new proposal.
6
+ class CreateProposal < Rectify::Command
7
+ # Public: Initializes the command.
8
+ #
9
+ # form - A form object with the params.
10
+ def initialize(form)
11
+ @form = form
12
+ end
13
+
14
+ # Executes the command. Broadcasts these events:
15
+ #
16
+ # - :ok when everything is valid, together with the proposal.
17
+ # - :invalid if the form wasn't valid and we couldn't proceed.
18
+ #
19
+ # Returns nothing.
20
+ def call
21
+ return broadcast(:invalid) if form.invalid?
22
+
23
+ create_proposal
24
+ broadcast(:ok)
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :form, :proposal
30
+
31
+ def create_proposal
32
+ @proposal = Proposal.create!(
33
+ title: form.title,
34
+ body: form.body,
35
+ category: form.category,
36
+ scope: form.scope,
37
+ feature: form.feature
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Proposals
4
+ # A command with all the business logic when a user creates a new proposal.
5
+ class CreateProposal < Rectify::Command
6
+ # Public: Initializes the command.
7
+ #
8
+ # form - A form object with the params.
9
+ def initialize(form)
10
+ @form = form
11
+ end
12
+
13
+ # Executes the command. Broadcasts these events:
14
+ #
15
+ # - :ok when everything is valid, together with the proposal.
16
+ # - :invalid if the form wasn't valid and we couldn't proceed.
17
+ #
18
+ # Returns nothing.
19
+ def call
20
+ return broadcast(:invalid) if form.invalid?
21
+
22
+ create_proposal
23
+ broadcast(:ok, proposal)
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :form, :proposal
29
+
30
+ def create_proposal
31
+ @proposal = Proposal.create!(
32
+ title: form.title,
33
+ body: form.body,
34
+ category: form.category,
35
+ scope: form.scope,
36
+ author: form.author,
37
+ feature: form.feature
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Proposals
4
+ module Admin
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::Admin::Features::BaseController`, which
9
+ # override its layout and provide all kinds of useful methods.
10
+ class ApplicationController < Decidim::Admin::Features::BaseController
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Proposals
4
+ module Admin
5
+ # This controller allows admins to manage proposals in a participatory process.
6
+ class ProposalsController < Admin::ApplicationController
7
+ helper_method :proposals
8
+
9
+ def new
10
+ @form = form(ProposalForm).from_params({}, feature: current_feature)
11
+ end
12
+
13
+ def create
14
+ @form = form(ProposalForm).from_params(params, feature: current_feature)
15
+
16
+ CreateProposal.call(@form) do
17
+ on(:ok) do
18
+ flash[:notice] = I18n.t("proposals.create.success", scope: "decidim.proposals.admin")
19
+ redirect_to proposals_path
20
+ end
21
+
22
+ on(:invalid) do
23
+ flash.now[:alert] = I18n.t("proposals.create.invalid", scope: "decidim.proposals.admin")
24
+ render action: "new"
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def proposals
32
+ @proposals ||= Proposal.where(feature: current_feature)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
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,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Proposals
5
+ # Exposes the proposal resource so users can view and create them.
6
+ class ProposalsController < Decidim::Proposals::ApplicationController
7
+ include FormFactory
8
+ before_action :authenticate_user!, only: [:new, :create]
9
+
10
+ def show
11
+ @proposal = Proposal.where(feature: current_feature).find(params[:id])
12
+ end
13
+
14
+ def index
15
+ @search = ProposalSearch.new(current_feature, params[:page], params[:random_seed])
16
+ @proposals = @search.proposals
17
+ @random_seed = @search.random_seed
18
+ end
19
+
20
+ def new
21
+ @form = form(ProposalForm).from_params({}, author: current_user, feature: current_feature)
22
+ end
23
+
24
+ def create
25
+ @form = form(ProposalForm).from_params(params, author: current_user, feature: current_feature)
26
+
27
+ CreateProposal.call(@form) do
28
+ on(:ok) do |proposal|
29
+ flash[:notice] = I18n.t("proposals.create.success", scope: "decidim")
30
+ redirect_to proposal_path(proposal)
31
+ end
32
+
33
+ on(:invalid) do
34
+ flash.now[:alert] = I18n.t("proposals.create.error", scope: "decidim")
35
+ render :new
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Proposals
4
+ module Admin
5
+ # A form object to be used when admin users want to create a proposal.
6
+ class ProposalForm < Decidim::Form
7
+ mimic :proposal
8
+
9
+ attribute :title, String
10
+ attribute :body, String
11
+ attribute :category_id, Integer
12
+ attribute :scope_id, Integer
13
+ attribute :feature, Decidim::Feature
14
+
15
+ validates :title, :body, :feature, presence: true
16
+ validates :category, presence: true, if: ->(form) { form.category_id.present? }
17
+ validates :scope, presence: true, if: ->(form) { form.scope_id.present? }
18
+
19
+ delegate :categories, to: :feature
20
+ delegate :scopes, to: :current_organization
21
+
22
+ # Finds the Category from the category_id.
23
+ #
24
+ # Returns a Decidim::Category
25
+ def category
26
+ @category ||= feature.categories.where(id: category_id).first
27
+ end
28
+
29
+ # Finds the Scope from the scope_id.
30
+ #
31
+ # Returns a Decidim::Scope
32
+ def scope
33
+ @scope ||= feature.scopes.where(id: scope_id).first
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Proposals
4
+ # A form object to be used when public users want to create a proposal.
5
+ class ProposalForm < Decidim::Form
6
+ mimic :proposal
7
+
8
+ attribute :title, String
9
+ attribute :body, String
10
+ attribute :author, Decidim::User
11
+ attribute :category_id, Integer
12
+ attribute :scope_id, Integer
13
+ attribute :feature, Decidim::Feature
14
+
15
+ validates :title, :body, :author, :feature, presence: true
16
+ validates :category, presence: true, if: ->(form) { form.category_id.present? }
17
+ validates :scope, presence: true, if: ->(form) { form.scope_id.present? }
18
+
19
+ delegate :categories, to: :feature
20
+ delegate :scopes, to: :current_organization
21
+
22
+ # Finds the Category from the category_id.
23
+ #
24
+ # Returns a Decidim::Category
25
+ def category
26
+ @category ||= feature.categories.where(id: category_id).first
27
+ end
28
+
29
+ # Finds the Scope from the scope_id.
30
+ #
31
+ # Returns a Decidim::Scope
32
+ def scope
33
+ @scope ||= feature.scopes.where(id: scope_id).first
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Proposals
4
+ # Custom helpers, scoped to the pages engine.
5
+ #
6
+ module ApplicationHelper
7
+ include Decidim::Comments::CommentsHelper
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Proposals
4
+ # Abstract class from which all models in this engine inherit.
5
+ class ApplicationRecord < ActiveRecord::Base
6
+ self.abstract_class = true
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Proposals
4
+ # The data store for a Proposal in the Decidim::Proposals component.
5
+ class Proposal < Proposals::ApplicationRecord
6
+ belongs_to :feature, foreign_key: "decidim_feature_id", class_name: Decidim::Feature
7
+ belongs_to :author, foreign_key: "decidim_author_id", class_name: Decidim::User
8
+ belongs_to :category, foreign_key: "decidim_category_id", class_name: Decidim::Category
9
+ belongs_to :scope, foreign_key: "decidim_scope_id", class_name: Decidim::Scope
10
+ has_one :organization, through: :feature
11
+
12
+ validates :title, :feature, :body, presence: true
13
+ validate :category_belongs_to_feature
14
+ validate :scope_belongs_to_organization
15
+ validate :author_belongs_to_organization
16
+
17
+ def author_name
18
+ author&.name || I18n.t("decidim.proposals.models.proposal.fields.official_proposal")
19
+ end
20
+
21
+ def author_avatar_url
22
+ author&.avatar&.url || ActionController::Base.helpers.asset_path("decidim/default-avatar.svg")
23
+ end
24
+
25
+ # Public: Canpeople comment on this proposal?
26
+ #
27
+ # Until we have a way to store options fore features and its resources we
28
+ # assume all proposals can be commented.
29
+ #
30
+ # Returns Boolean
31
+ def commentable?
32
+ true
33
+ end
34
+
35
+ private
36
+
37
+ def category_belongs_to_feature
38
+ return unless category
39
+ errors.add(:category, :invalid) unless feature.categories.where(id: category.id).exists?
40
+ end
41
+
42
+ def scope_belongs_to_organization
43
+ return unless scope
44
+ errors.add(:scope, :invalid) unless feature.scopes.where(id: scope.id).exists?
45
+ end
46
+
47
+ def author_belongs_to_organization
48
+ return unless author
49
+ errors.add(:author, :invalid) unless Decidim::User.where(decidim_organization_id: feature.organization.id, id: author.id).exists?
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Proposals
4
+ # A service to encapsualte all the logic when searching and filtering
5
+ # proposals in a participatory process.
6
+ class ProposalSearch
7
+ attr_reader :feature, :page, :per_page
8
+
9
+ # Public: Initializes the service.
10
+ # feature - A Decidim::Feature to get the proposals from.
11
+ # page - The page number to paginate the results.
12
+ # random_seed - A random flaot number between -1 and 1 to be used as a random seed at the database.
13
+ # per_page - The number of proposals to return per page.
14
+ def initialize(feature, page = nil, random_seed = nil, per_page = nil)
15
+ @feature = feature
16
+ @page = (page || 1).to_i
17
+ @per_page = (per_page || 12).to_i
18
+ @random_seed = random_seed.to_f
19
+ end
20
+
21
+ # Returns the random proposals for the current page.
22
+ def proposals
23
+ @proposals ||= Proposal.transaction do
24
+ Proposal.connection.execute("SELECT setseed(#{Proposal.connection.quote(random_seed)})")
25
+ Proposal.where(feature: feature).reorder("RANDOM()").page(page).per(per_page).load
26
+ end
27
+ end
28
+
29
+ # Returns the random seed used to randomize the proposals.
30
+ def random_seed
31
+ @random_seed = (rand * 2 - 1) if @random_seed == 0.0 || @random_seed > 1 || @random_seed < -1
32
+ @random_seed
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ <div class="field">
2
+ <%= form.text_field :title %>
3
+ </div>
4
+
5
+ <div class="field">
6
+ <%= form.text_area :body, rows: 10 %>
7
+ </div>
8
+
9
+ <% if @form.categories&.any? %>
10
+ <div class="field">
11
+ <%= form.categories_select :category_id, @form.categories, t(".select_a_category") %>
12
+ </div>
13
+ <% end %>
14
+
15
+ <% if @form.scopes&.any? %>
16
+ <div class="field">
17
+ <%= form.select :scope_id, @form.scopes.map{|s| [translated_attribute(s.name), s.id]}, prompt: t(".select_a_scope") %>
18
+ </div>
19
+ <% end %>
@@ -0,0 +1,34 @@
1
+ <h2><%= t(".title") %></h2>
2
+
3
+ <div class="actions title">
4
+ <%= link_to t("actions.new", scope: "decidim.proposals", name: t("models.proposal.name", scope: "decidim.proposals.admin")), new_proposal_path, class: 'new' if can? :manage, current_feature %>
5
+ </div>
6
+
7
+ <table class="stack">
8
+ <thead>
9
+ <tr>
10
+ <th><%= t("models.proposal.fields.title", scope: "decidim.proposals") %></th>
11
+ <th><%= t("models.proposal.fields.category", scope: "decidim.proposals") %></th>
12
+ <th><%= t("models.proposal.fields.scope", scope: "decidim.proposals") %></th>
13
+ </tr>
14
+ </thead>
15
+ <tbody>
16
+ <% proposals.each do |proposal| %>
17
+ <tr data-id="<%= proposal.id %>">
18
+ <td>
19
+ <%= link_to proposal.title, decidim_proposals.proposal_path(id: proposal, feature_id: current_feature, participatory_process_id: current_participatory_process), target: :blank %><br />
20
+ </td>
21
+ <td>
22
+ <% if proposal.category %>
23
+ <%= translated_attribute proposal.category.name %>
24
+ <% end %>
25
+ </td>
26
+ <td>
27
+ <% if proposal.scope %>
28
+ <%= translated_attribute proposal.scope.name %>
29
+ <% end %>
30
+ </td>
31
+ </tr>
32
+ <% end %>
33
+ </tbody>
34
+ </table>
@@ -0,0 +1,9 @@
1
+ <h3><%= t ".title" %></h3>
2
+
3
+ <%= form_for(@form) do |f| %>
4
+ <%= render partial: 'form', object: f %>
5
+
6
+ <div class="actions">
7
+ <%= f.submit t(".create") %>
8
+ </div>
9
+ <% end %>
@@ -0,0 +1,34 @@
1
+ <div class="column">
2
+ <article class="card card--proposal">
3
+ <div class="card__content">
4
+ <div class="card__header">
5
+ <%= link_to proposal do%>
6
+ <h5 class="card__title"><%= proposal.title %></h5>
7
+ <% end %>
8
+ <div class="card__author author-data author-data--small">
9
+ <div class="author-data__main">
10
+ <div class="author author--inline">
11
+ <span class="author__avatar author__avatar--small">
12
+ <%= image_tag proposal.author_avatar_url %>
13
+ </span>
14
+ <span class="author__name"><%= proposal.author_name %></span>
15
+ <%= l proposal.created_at, format: "%d/%m/%Y" %>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ <p><%= truncate(proposal.body, length: 100) %></p>
21
+ <% if proposal.category %>
22
+ <ul class="tags tags--proposal">
23
+ <li><a href=""><%= translated_attribute(proposal.category.name) %></a></li>
24
+ </ul>
25
+ <% end %>
26
+ </div>
27
+ <div class="card__footer">
28
+ <div class="card__support">
29
+ <div class="card__support__data"></div>
30
+ <%= link_to t(".view_proposal"), proposal, class: "card__button button small secondary" %>
31
+ </div>
32
+ </div>
33
+ </article>
34
+ </div>
@@ -0,0 +1,23 @@
1
+ <div class="row columns">
2
+ <div class="title-action">
3
+ <h2 class="title-action__title section-heading"><%= t(".proposals_count", count: @proposals.total_count) %></h2>
4
+ <%= link_to new_proposal_path, class: "title-action__action button small hollow" do %>
5
+ <%= t(".new_proposal") %>
6
+ <%= icon "plus" %>
7
+ <% end %>
8
+ </div>
9
+ </div>
10
+ <div class="row">
11
+ <div class="columns mediumlarge-12 large-12">
12
+ <div class="row small-up-1 medium-up-3 card-grid">
13
+ <%= render @proposals %>
14
+ </div>
15
+ <%
16
+ # Kaminari uses url_for to generate the url, but this doesn't play nice with our engine system
17
+ # and unless we remove these params they are added again as query string 😩
18
+ params.delete("participatory_process_id")
19
+ params.delete("feature_id")
20
+ %>
21
+ <%= paginate @proposals, theme: "decidim", params: { random_seed: @random_seed } %>
22
+ </div>
23
+ </div>
@@ -0,0 +1,40 @@
1
+ <div class="row columns">
2
+ <%= link_to :back, class: "muted-link" do %>
3
+ <%= icon "chevron-left", class: "icon--small" %>
4
+ <%= t(".back") %>
5
+ <% end %>
6
+ <h2 class="section-heading"><%= t(".title") %></h2>
7
+ </div>
8
+ <div class="row">
9
+ <div class="columns large-6 medium-centered">
10
+ <div class="card">
11
+ <div class="card__content">
12
+ <%= form_for(@form) do |form| %>
13
+ <div class="field">
14
+ <%= form.text_field :title %>
15
+ </div>
16
+
17
+ <div class="field">
18
+ <%= form.text_area :body, rows: 10 %>
19
+ </div>
20
+
21
+ <% if @form.categories&.any? %>
22
+ <div class="field">
23
+ <%= form.categories_select :category_id, @form.categories, prompt: t(".select_a_category") %>
24
+ </div>
25
+ <% end %>
26
+
27
+ <% if @form.scopes&.any? %>
28
+ <div class="field">
29
+ <%= form.select :scope_id, @form.scopes.map{|s| [s.name, s.id]}, prompt: t(".select_a_scope") %>
30
+ </div>
31
+ <% end %>
32
+
33
+ <div class="actions">
34
+ <%= form.submit t(".send"), class: "button expanded" %>
35
+ </div>
36
+ <% end %>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </div>
@@ -0,0 +1,37 @@
1
+ <div class="row column view-header">
2
+ <h2 class="heading2"><%= @proposal.title %></h2>
3
+ <div class="author-data">
4
+ <div class="author-data__main">
5
+ <div class="author author--inline">
6
+ <span class="author__avatar">
7
+ <%= image_tag @proposal.author_avatar_url %>
8
+ </span>
9
+ <%= l @proposal.created_at, format: :long %>
10
+ <span class="author__name">
11
+ <%= @proposal.author_name %>
12
+ </span>
13
+ </div>
14
+ </div>
15
+ </div>
16
+ </div>
17
+ <div class="row">
18
+ <div class="columns section view-side mediumlarge-4 mediumlarge-push-8 large-3 large-push-9">
19
+ </div>
20
+ <div class="columns mediumlarge-8 mediumlarge-pull-4">
21
+ <div class="section">
22
+ <p><%= @proposal.body %></p>
23
+ <% if @proposal.category %>
24
+ <ul class="tags tags--proposal">
25
+ <li><a href=""><%= translated_attribute(@proposal.category.name) %></a></li>
26
+ </ul>
27
+ <% end %>
28
+ </div>
29
+ </div>
30
+ </div>
31
+
32
+
33
+ <%= content_for :expanded do %>
34
+ <% if @proposal.commentable? %>
35
+ <%= comments_for @proposal, arguable: true %>
36
+ <% end %>
37
+ <% end %>
@@ -0,0 +1,4 @@
1
+ base_locale: en
2
+ locales: [en]
3
+ ignore_unused:
4
+ - "decidim.features.proposals.name"
@@ -0,0 +1,46 @@
1
+ ca:
2
+ decidim:
3
+ features:
4
+ proposals:
5
+ name: Propostes
6
+ proposals:
7
+ actions:
8
+ new: Nova proposta
9
+ admin:
10
+ models:
11
+ proposal:
12
+ name: Proposta
13
+ proposals:
14
+ create:
15
+ invalid: Hi ha hagut un problema en crear aquesta proposta
16
+ success: Proposta creada correctament
17
+ form:
18
+ select_a_category: Selecciona una categoria
19
+ select_a_scope: Seleccioneu un àmbit
20
+ index:
21
+ title: Propostes
22
+ new:
23
+ create: Crear proposta
24
+ title: Nova proposta
25
+ create:
26
+ error: Hi ha hagut errors en desar la proposta.
27
+ success: Proposta creada correctament.
28
+ models:
29
+ proposal:
30
+ fields:
31
+ category: Categoria
32
+ official_proposal: Proposta oficial
33
+ scope: Àmbit
34
+ title: Títol
35
+ proposals:
36
+ index:
37
+ new_proposal: Nova proposta
38
+ proposals_count: "%{count} propostes"
39
+ new:
40
+ back: Enrere
41
+ select_a_category: Si us plau, seleccioni una categoria
42
+ select_a_scope: Si us plau, seleccioni un àmbit
43
+ send: Enviar
44
+ title: Nova proposta
45
+ proposal:
46
+ view_proposal: Veure proposta
@@ -0,0 +1,47 @@
1
+ ---
2
+ en:
3
+ decidim:
4
+ features:
5
+ proposals:
6
+ name: Proposals
7
+ proposals:
8
+ actions:
9
+ new: New proposal
10
+ admin:
11
+ models:
12
+ proposal:
13
+ name: Proposal
14
+ proposals:
15
+ create:
16
+ invalid: There's been a problem creating this proposal
17
+ success: Proposal successfully created
18
+ form:
19
+ select_a_category: Select a category
20
+ select_a_scope: Select a scope
21
+ index:
22
+ title: Proposals
23
+ new:
24
+ create: Create proposal
25
+ title: New proposal
26
+ create:
27
+ error: There's been errors when saving the proposal.
28
+ success: Proposal created successfully.
29
+ models:
30
+ proposal:
31
+ fields:
32
+ category: Category
33
+ official_proposal: Official proposal
34
+ scope: Scope
35
+ title: Title
36
+ proposals:
37
+ index:
38
+ new_proposal: New proposal
39
+ proposals_count: "%{count} proposals"
40
+ new:
41
+ back: Back
42
+ select_a_category: Please select a category
43
+ select_a_scope: Please select a scope
44
+ send: Send
45
+ title: New proposal
46
+ proposal:
47
+ view_proposal: View proposal
@@ -0,0 +1,46 @@
1
+ es:
2
+ decidim:
3
+ features:
4
+ proposals:
5
+ name: Propuestas
6
+ proposals:
7
+ actions:
8
+ new: Nueva propuesta
9
+ admin:
10
+ models:
11
+ proposal:
12
+ name: Propuesta
13
+ proposals:
14
+ create:
15
+ invalid: Ha habido un problema al crear esta propuesta
16
+ success: Propuesta creada correctamente
17
+ form:
18
+ select_a_category: Seleccione una categoría
19
+ select_a_scope: Seleccione un ámbito
20
+ index:
21
+ title: Propuestas
22
+ new:
23
+ create: Crear propuesta
24
+ title: Nueva propuesta
25
+ create:
26
+ error: Ha habido errores al guardar la propuesta.
27
+ success: Propuesta creada correctamente.
28
+ models:
29
+ proposal:
30
+ fields:
31
+ category: Categoría
32
+ official_proposal: Propuesta oficial
33
+ scope: Ámbito
34
+ title: Título
35
+ proposals:
36
+ index:
37
+ new_proposal: Nueva propuesta
38
+ proposals_count: "%{count} propuestas"
39
+ new:
40
+ back: Atrás
41
+ select_a_category: Por favor, seleccione una categoría
42
+ select_a_scope: Por favor, seleccione un ámbito
43
+ send: Enviar
44
+ title: Nueva propuesta
45
+ proposal:
46
+ view_proposal: Ver propuesta
@@ -0,0 +1,14 @@
1
+ class CreateDecidimProposals < ActiveRecord::Migration[5.0]
2
+ def change
3
+ create_table :decidim_proposals_proposals do |t|
4
+ t.text :title, null: false
5
+ t.text :body, null: false
6
+ t.references :decidim_feature, index: true, null: false
7
+ t.references :decidim_author, index: true
8
+ t.references :decidim_category, index: true
9
+ t.references :decidim_scope, index: true
10
+
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ require "decidim/proposals/admin"
3
+ require "decidim/proposals/engine"
4
+ require "decidim/proposals/admin_engine"
5
+ require "decidim/proposals/feature"
6
+
7
+ module Decidim
8
+ # This namespace holds the logic of the `Proposals` component. This component
9
+ # allows users to create proposals in a participatory process.
10
+ module Proposals
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Proposals
4
+ # This module contains all the domain logic associated to Decidim's Proposal
5
+ # component admin panel.
6
+ module Admin
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Proposals
4
+ # This is the engine that runs on the public interface of `decidim-proposals`.
5
+ class AdminEngine < ::Rails::Engine
6
+ isolate_namespace Decidim::Proposals::Admin
7
+
8
+ paths["db/migrate"] = nil
9
+
10
+ routes do
11
+ resources :proposals, only: [:index, :new, :create]
12
+ root to: "proposals#index"
13
+ end
14
+
15
+ def load_seed
16
+ nil
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ require "kaminari"
3
+
4
+ module Decidim
5
+ module Proposals
6
+ # This is the engine that runs on the public interface of `decidim-proposals`.
7
+ # It mostly handles rendering the created page associated to a participatory
8
+ # process.
9
+ class Engine < ::Rails::Engine
10
+ isolate_namespace Decidim::Proposals
11
+
12
+ routes do
13
+ resources :proposals, only: [:create, :new, :index, :show]
14
+ root to: "proposals#index"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "decidim/features/namer"
4
+
5
+ Decidim.register_feature(:proposals) do |feature|
6
+ feature.engine = Decidim::Proposals::Engine
7
+ feature.admin_engine = Decidim::Proposals::AdminEngine
8
+
9
+ feature.on(:destroy) do |instance|
10
+ if Decidim::Proposals::Proposal.where(feature: instance).any?
11
+ raise "Can't destroy this feature when there are proposals"
12
+ end
13
+ end
14
+
15
+ feature.seeds do
16
+ Decidim::ParticipatoryProcess.all.each do |process|
17
+ next unless process.steps.any?
18
+
19
+ feature = Decidim::Feature.create!(
20
+ name: Decidim::Features::Namer.new(process.organization.available_locales, :proposals).i18n_name,
21
+ manifest_name: :proposals,
22
+ participatory_process: process
23
+ )
24
+
25
+ 20.times do
26
+ Decidim::Proposals::Proposal.create!(
27
+ feature: feature,
28
+ title: Faker::Lorem.sentence(2),
29
+ body: Faker::Lorem.paragraphs(2).join("\n"),
30
+ author: Decidim::User.where(organization: feature.organization).all.sample
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: decidim-proposals
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Josep Jaume Rey Peroy
8
+ - Marc Riera Casals
9
+ - Oriol Gual Oliva
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2016-12-21 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: decidim-core
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - '='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.0.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - '='
27
+ - !ruby/object:Gem::Version
28
+ version: 0.0.1
29
+ - !ruby/object:Gem::Dependency
30
+ name: decidim-comments
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - '='
34
+ - !ruby/object:Gem::Version
35
+ version: 0.0.1
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - '='
41
+ - !ruby/object:Gem::Version
42
+ version: 0.0.1
43
+ - !ruby/object:Gem::Dependency
44
+ name: rectify
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 0.8.0
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: 0.8.0
57
+ - !ruby/object:Gem::Dependency
58
+ name: kaminari
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: 1.0.0.rc1
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: 1.0.0.rc1
71
+ - !ruby/object:Gem::Dependency
72
+ name: decidim-dev
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - '='
76
+ - !ruby/object:Gem::Version
77
+ version: 0.0.1
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - '='
83
+ - !ruby/object:Gem::Version
84
+ version: 0.0.1
85
+ description: A proposals component for decidim's participatory processes.
86
+ email:
87
+ - josepjaume@gmail.com
88
+ - mrc2407@gmail.com
89
+ - oriolgual@gmail.com
90
+ executables: []
91
+ extensions: []
92
+ extra_rdoc_files: []
93
+ files:
94
+ - README.md
95
+ - Rakefile
96
+ - app/commands/decidim/proposals/admin/create_proposal.rb
97
+ - app/commands/decidim/proposals/create_proposal.rb
98
+ - app/controllers/decidim/proposals/admin/application_controller.rb
99
+ - app/controllers/decidim/proposals/admin/proposals_controller.rb
100
+ - app/controllers/decidim/proposals/application_controller.rb
101
+ - app/controllers/decidim/proposals/proposals_controller.rb
102
+ - app/forms/decidim/proposals/admin/proposal_form.rb
103
+ - app/forms/decidim/proposals/proposal_form.rb
104
+ - app/helpers/decidim/proposals/application_helper.rb
105
+ - app/models/decidim/proposals/application_record.rb
106
+ - app/models/decidim/proposals/proposal.rb
107
+ - app/services/decidim/proposals/proposal_search.rb
108
+ - app/views/decidim/proposals/admin/proposals/_form.html.erb
109
+ - app/views/decidim/proposals/admin/proposals/index.html.erb
110
+ - app/views/decidim/proposals/admin/proposals/new.html.erb
111
+ - app/views/decidim/proposals/proposals/_proposal.html.erb
112
+ - app/views/decidim/proposals/proposals/index.html.erb
113
+ - app/views/decidim/proposals/proposals/new.html.erb
114
+ - app/views/decidim/proposals/proposals/show.html.erb
115
+ - config/i18n-tasks.yml
116
+ - config/locales/ca.yml
117
+ - config/locales/en.yml
118
+ - config/locales/es.yml
119
+ - db/migrate/20161212110850_create_decidim_proposals.rb
120
+ - lib/decidim/proposals.rb
121
+ - lib/decidim/proposals/admin.rb
122
+ - lib/decidim/proposals/admin_engine.rb
123
+ - lib/decidim/proposals/engine.rb
124
+ - lib/decidim/proposals/feature.rb
125
+ homepage: https://github.com/AjuntamentdeBarcelona/decidim
126
+ licenses:
127
+ - AGPLv3
128
+ metadata: {}
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: 2.3.1
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubyforge_project:
145
+ rubygems_version: 2.5.2
146
+ signing_key:
147
+ specification_version: 4
148
+ summary: A proposals component for decidim's participatory processes.
149
+ test_files: []