decidim-proposals 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []