decidim-plans 0.15.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 +157 -0
- data/Rakefile +48 -0
- data/app/assets/config/admin/decidim_plans_manifest.js +3 -0
- data/app/assets/config/decidim_plans_manifest.css +4 -0
- data/app/assets/config/decidim_plans_manifest.js +2 -0
- data/app/assets/images/decidim/plans/icon.svg +3 -0
- data/app/assets/javascripts/decidim/plans/admin/plans.js.es6 +3 -0
- data/app/assets/javascripts/decidim/plans/admin/sections.js.es6 +78 -0
- data/app/assets/javascripts/decidim/plans/multifield/component.js.es6 +101 -0
- data/app/assets/javascripts/decidim/plans/multifield.js.es6 +5 -0
- data/app/assets/javascripts/decidim/plans/plans_form.js.es6 +9 -0
- data/app/assets/javascripts/decidim/plans/proposal_picker.js.es6 +41 -0
- data/app/assets/javascripts/decidim/plans/remaining_characters.es6 +42 -0
- data/app/assets/javascripts/decidim/plans/social_share.js +2 -0
- data/app/assets/javascripts/decidim/plans/tab_focus.es6 +20 -0
- data/app/assets/stylesheets/decidim/plans/plans_form.scss +26 -0
- data/app/assets/stylesheets/decidim/plans/proposal_picker.scss +3 -0
- data/app/assets/stylesheets/decidim/plans/social_share.css.scss +22 -0
- data/app/cells/decidim/plans/author/flag.erb +5 -0
- data/app/cells/decidim/plans/author/withdraw.erb +6 -0
- data/app/cells/decidim/plans/author_cell.rb +9 -0
- data/app/cells/decidim/plans/coauthorships_cell.rb +23 -0
- data/app/cells/decidim/plans/collapsible_authors/show.erb +17 -0
- data/app/cells/decidim/plans/collapsible_authors_cell.rb +9 -0
- data/app/cells/decidim/plans/plan_activity_cell.rb +19 -0
- data/app/cells/decidim/plans/plan_cell.rb +59 -0
- data/app/cells/decidim/plans/plan_m/footer.erb +8 -0
- data/app/cells/decidim/plans/plan_m_cell.rb +62 -0
- data/app/commands/decidim/plans/accept_access_to_plan.rb +53 -0
- data/app/commands/decidim/plans/admin/answer_plan.rb +82 -0
- data/app/commands/decidim/plans/admin/create_plan.rb +102 -0
- data/app/commands/decidim/plans/admin/update_plan.rb +98 -0
- data/app/commands/decidim/plans/admin/update_sections.rb +54 -0
- data/app/commands/decidim/plans/attachment_methods.rb +78 -0
- data/app/commands/decidim/plans/create_plan.rb +90 -0
- data/app/commands/decidim/plans/destroy_plan.rb +33 -0
- data/app/commands/decidim/plans/nested_updater.rb +26 -0
- data/app/commands/decidim/plans/publish_plan.rb +79 -0
- data/app/commands/decidim/plans/reject_access_to_plan.rb +46 -0
- data/app/commands/decidim/plans/request_access_to_plan.rb +50 -0
- data/app/commands/decidim/plans/respond_to_access_request.rb +76 -0
- data/app/commands/decidim/plans/update_plan.rb +90 -0
- data/app/commands/decidim/plans/withdraw_plan.rb +50 -0
- data/app/controllers/concerns/decidim/plans/orderable.rb +53 -0
- data/app/controllers/decidim/plans/admin/application_controller.rb +16 -0
- data/app/controllers/decidim/plans/admin/plan_answers_controller.rb +40 -0
- data/app/controllers/decidim/plans/admin/plans_controller.rb +80 -0
- data/app/controllers/decidim/plans/admin/sections_controller.rb +75 -0
- data/app/controllers/decidim/plans/application_controller.rb +30 -0
- data/app/controllers/decidim/plans/plan_collaborator_requests_controller.rb +53 -0
- data/app/controllers/decidim/plans/plan_widgets_controller.rb +19 -0
- data/app/controllers/decidim/plans/plans_controller.rb +192 -0
- data/app/controllers/decidim/plans/versions_controller.rb +51 -0
- data/app/events/decidim/plans/accepted_plan_event.rb +9 -0
- data/app/events/decidim/plans/evaluating_plan_event.rb +8 -0
- data/app/events/decidim/plans/plan_access_accepted_event.rb +8 -0
- data/app/events/decidim/plans/plan_access_rejected_event.rb +8 -0
- data/app/events/decidim/plans/plan_access_request_event.rb +27 -0
- data/app/events/decidim/plans/plan_access_requested_event.rb +8 -0
- data/app/events/decidim/plans/plan_access_requester_accepted_event.rb +8 -0
- data/app/events/decidim/plans/plan_access_requester_rejected_event.rb +8 -0
- data/app/events/decidim/plans/publish_plan_event.rb +25 -0
- data/app/events/decidim/plans/rejected_plan_event.rb +9 -0
- data/app/forms/decidim/plans/accept_access_to_plan_form.rb +10 -0
- data/app/forms/decidim/plans/access_to_plan_form.rb +35 -0
- data/app/forms/decidim/plans/admin/plan_answer_form.rb +19 -0
- data/app/forms/decidim/plans/admin/plan_form.rb +88 -0
- data/app/forms/decidim/plans/admin/plan_sections_form.rb +20 -0
- data/app/forms/decidim/plans/admin/section_form.rb +34 -0
- data/app/forms/decidim/plans/attachment_form.rb +20 -0
- data/app/forms/decidim/plans/content_form.rb +58 -0
- data/app/forms/decidim/plans/plan_form.rb +85 -0
- data/app/forms/decidim/plans/reject_access_to_plan_form.rb +9 -0
- data/app/forms/decidim/plans/request_access_to_plan_form.rb +20 -0
- data/app/helpers/decidim/plans/admin/application_helper.rb +19 -0
- data/app/helpers/decidim/plans/application_helper.rb +78 -0
- data/app/helpers/decidim/plans/attached_proposals_helper.rb +44 -0
- data/app/helpers/decidim/plans/attachments_helper.rb +59 -0
- data/app/helpers/decidim/plans/cells_helper.rb +26 -0
- data/app/helpers/decidim/plans/plan_cells_helper.rb +67 -0
- data/app/helpers/decidim/plans/remaining_characters_helper.rb +32 -0
- data/app/helpers/decidim/plans/traceability_helper.rb +79 -0
- data/app/helpers/decidim/plans/user_group_helper.rb +23 -0
- data/app/models/concerns/decidim/plans/traceable.rb +48 -0
- data/app/models/decidim/plans/application_record.rb +10 -0
- data/app/models/decidim/plans/attached_proposal.rb +12 -0
- data/app/models/decidim/plans/content.rb +21 -0
- data/app/models/decidim/plans/paper_trail/version.rb +26 -0
- data/app/models/decidim/plans/paper_trail/version_association.rb +14 -0
- data/app/models/decidim/plans/plan.rb +206 -0
- data/app/models/decidim/plans/plan_collaborator_request.rb +13 -0
- data/app/models/decidim/plans/section.rb +16 -0
- data/app/permissions/decidim/plans/admin/permissions.rb +44 -0
- data/app/permissions/decidim/plans/permissions.rb +67 -0
- data/app/presenters/decidim/plans/admin_log/plan_presenter.rb +49 -0
- data/app/presenters/decidim/plans/admin_log/value_types/plan_state_presenter.rb +16 -0
- data/app/presenters/decidim/plans/content_presenter.rb +25 -0
- data/app/presenters/decidim/plans/log/resource_presenter.rb +18 -0
- data/app/presenters/decidim/plans/official_author_presenter.rb +38 -0
- data/app/presenters/decidim/plans/plan_presenter.rb +48 -0
- data/app/queries/decidim/plans/filtered_plans.rb +37 -0
- data/app/services/decidim/plans/diff_renderer/base.rb +108 -0
- data/app/services/decidim/plans/diff_renderer/categorization.rb +23 -0
- data/app/services/decidim/plans/diff_renderer/content.rb +33 -0
- data/app/services/decidim/plans/diff_renderer/plan.rb +25 -0
- data/app/services/decidim/plans/loggability.rb +19 -0
- data/app/services/decidim/plans/plan_builder.rb +68 -0
- data/app/services/decidim/plans/plan_search.rb +95 -0
- data/app/services/decidim/plans/tracer.rb +29 -0
- data/app/views/decidim/plans/admin/plan_answers/edit.html.erb +22 -0
- data/app/views/decidim/plans/admin/plans/_bulk-actions.html.erb +9 -0
- data/app/views/decidim/plans/admin/plans/_form.html.erb +62 -0
- data/app/views/decidim/plans/admin/plans/_js-callout.html.erb +6 -0
- data/app/views/decidim/plans/admin/plans/_plan-tr.html.erb +53 -0
- data/app/views/decidim/plans/admin/plans/edit.html.erb +7 -0
- data/app/views/decidim/plans/admin/plans/index.html.erb +64 -0
- data/app/views/decidim/plans/admin/plans/new.html.erb +7 -0
- data/app/views/decidim/plans/admin/sections/_bulk-actions.html.erb +7 -0
- data/app/views/decidim/plans/admin/sections/_form.html.erb +27 -0
- data/app/views/decidim/plans/admin/sections/_section.html.erb +66 -0
- data/app/views/decidim/plans/admin/sections/index.html.erb +7 -0
- data/app/views/decidim/plans/admin/shared/_info_plan.html.erb +17 -0
- data/app/views/decidim/plans/attached_proposals/_proposals.html.erb +12 -0
- data/app/views/decidim/plans/plan_widgets/show.html.erb +3 -0
- data/app/views/decidim/plans/plans/_accept_request_access_form.html.erb +8 -0
- data/app/views/decidim/plans/plans/_attached_proposals.html.erb +10 -0
- data/app/views/decidim/plans/plans/_collaborator_requests.html.erb +20 -0
- data/app/views/decidim/plans/plans/_content.html.erb +6 -0
- data/app/views/decidim/plans/plans/_contents.html.erb +8 -0
- data/app/views/decidim/plans/plans/_count.html.erb +1 -0
- data/app/views/decidim/plans/plans/_filters.html.erb +32 -0
- data/app/views/decidim/plans/plans/_filters_small_view.html.erb +18 -0
- data/app/views/decidim/plans/plans/_form.html.erb +57 -0
- data/app/views/decidim/plans/plans/_new_plan_button.html.erb +11 -0
- data/app/views/decidim/plans/plans/_plan.html.erb +1 -0
- data/app/views/decidim/plans/plans/_plan_preview.html.erb +1 -0
- data/app/views/decidim/plans/plans/_plans.html.erb +14 -0
- data/app/views/decidim/plans/plans/_reject_request_access_form.html.erb +10 -0
- data/app/views/decidim/plans/plans/_request_access_form.html.erb +7 -0
- data/app/views/decidim/plans/plans/edit.html.erb +21 -0
- data/app/views/decidim/plans/plans/index.html.erb +28 -0
- data/app/views/decidim/plans/plans/index.js.erb +10 -0
- data/app/views/decidim/plans/plans/new.html.erb +15 -0
- data/app/views/decidim/plans/plans/preview.html.erb +15 -0
- data/app/views/decidim/plans/plans/show.html.erb +133 -0
- data/app/views/decidim/plans/shared/_attachment_fields.html.erb +45 -0
- data/app/views/decidim/plans/shared/_attachments.html.erb +23 -0
- data/app/views/decidim/plans/shared/_remaining_characters_container.html.erb +12 -0
- data/app/views/decidim/plans/shared/_section_content_field.html.erb +39 -0
- data/app/views/decidim/plans/versions/_version.html.erb +20 -0
- data/app/views/decidim/plans/versions/index.html.erb +34 -0
- data/app/views/decidim/plans/versions/show.html.erb +41 -0
- data/config/locales/en.yml +341 -0
- data/config/locales/fi.yml +341 -0
- data/config/locales/sv.yml +341 -0
- data/db/migrate/20181230110225_enable_pg_trgm_extension_for_plans.rb +18 -0
- data/db/migrate/20181230110848_create_decidim_plans.rb +20 -0
- data/db/migrate/20181230111731_create_decidim_plans_sections.rb +13 -0
- data/db/migrate/20181230111931_create_decidim_plans_plan_contents.rb +14 -0
- data/db/migrate/20181230122035_create_decidim_plans_plan_collaborator_requests.rb +12 -0
- data/db/migrate/20190110125855_create_decidim_plans_attached_proposals.rb +12 -0
- data/db/migrate/20190129140441_add_help_to_decidim_plans_sections.rb +7 -0
- data/db/migrate/20190129143607_add_mandatory_to_decidim_plans_sections.rb +7 -0
- data/db/migrate/20190129172040_add_section_type_to_decidim_plans_sections.rb +12 -0
- data/db/migrate/20190129231439_add_answer_length_to_decidim_plans_sections.rb +11 -0
- data/db/migrate/20190202123044_create_version_associations.rb +25 -0
- data/db/migrate/20190202123045_add_transaction_id_column_to_versions.rb +15 -0
- data/db/migrate/20190202200716_add_update_token_to_decidim_plans.rb +7 -0
- data/lib/decidim/plans/admin.rb +10 -0
- data/lib/decidim/plans/admin_engine.rb +29 -0
- data/lib/decidim/plans/component.rb +163 -0
- data/lib/decidim/plans/engine.rb +43 -0
- data/lib/decidim/plans/locale_aware.rb +32 -0
- data/lib/decidim/plans/optionally_translatable_attributes.rb +85 -0
- data/lib/decidim/plans/paper_trail/record_trail.rb +24 -0
- data/lib/decidim/plans/paper_trail.rb +10 -0
- data/lib/decidim/plans/test/factories.rb +130 -0
- data/lib/decidim/plans/version.rb +8 -0
- data/lib/decidim/plans.rb +24 -0
- metadata +323 -0
data/README.md
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
# Decidim::Plans
|
2
|
+
|
3
|
+
A [Decidim](https://github.com/decidim/decidim) module that provides a new
|
4
|
+
component that can be added to any participatory space in Decidim. The component
|
5
|
+
allows users to write plans together that link to specific proposals. Further on
|
6
|
+
these plans can be converted to budgeting projects once the process moves to
|
7
|
+
budgeting.
|
8
|
+
|
9
|
+
The idea of plans is for the people to plan together the proposals further
|
10
|
+
before a budgeting voting is started. During the planning process people should
|
11
|
+
come up with a more specific plan for the project based on which the budget can
|
12
|
+
be evaluated.
|
13
|
+
|
14
|
+
The plans component is based on the Decidim's own proposals component with the
|
15
|
+
following differences:
|
16
|
+
|
17
|
+
- Plans are multilingual so that they can be copied over to budgeting projects.
|
18
|
+
- Plans are collaborative by default, borrowing ideas from collaborative drafts
|
19
|
+
in the proposals component.
|
20
|
+
- Plans can be linked to proposals in the same participatory space.
|
21
|
+
- The format of the plan form can be customized, so the fields that users need
|
22
|
+
to fill can be customized instead of relying on a fixed set of fields. This
|
23
|
+
allows all plans to appear in the same format.
|
24
|
+
- Plans can be later on converted into budgeting processes for the budgets
|
25
|
+
component once they are finished. When converting, the different sections are
|
26
|
+
converted to sub-headings in the budgeting project's description and the
|
27
|
+
budget needs to be specified by the user doing the conversion.
|
28
|
+
|
29
|
+
## Installation
|
30
|
+
|
31
|
+
Add this line to your application's Gemfile:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
gem 'decidim-plans', :git => 'git@github.com:mainio/decidim-module-plans.git'
|
35
|
+
```
|
36
|
+
|
37
|
+
And then execute:
|
38
|
+
|
39
|
+
```bash
|
40
|
+
$ bundle
|
41
|
+
$ bundle exec rails decidim_plans:install:migrations
|
42
|
+
$ bundle exec rails db:migrate
|
43
|
+
```
|
44
|
+
|
45
|
+
To keep the gem up to date, you can use the commands above to also update it.
|
46
|
+
|
47
|
+
## Usage
|
48
|
+
|
49
|
+
You can add the plans component to any participatory space. After adding, users
|
50
|
+
can start writing the plans when plan creation is enabled.
|
51
|
+
|
52
|
+
## Contributing
|
53
|
+
|
54
|
+
For instructions how to setup your development environment for Decidim, see [Decidim](https://github.com/decidim/decidim). Also follow Decidim's general
|
55
|
+
instructions for development for this project as well.
|
56
|
+
|
57
|
+
### Developing
|
58
|
+
|
59
|
+
To start contributing to this project, first:
|
60
|
+
|
61
|
+
- Install the basic dependencies (such as Ruby and PostgreSQL)
|
62
|
+
- Clone this repository
|
63
|
+
|
64
|
+
Decidim's main repository also provides a Docker configuration file if you
|
65
|
+
prefer to use Docker instead of installing the dependencies locally on your
|
66
|
+
machine.
|
67
|
+
|
68
|
+
You can create the development app by running the following commands after
|
69
|
+
cloning this project:
|
70
|
+
|
71
|
+
```bash
|
72
|
+
$ bundle
|
73
|
+
$ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rake development_app
|
74
|
+
$ npm i
|
75
|
+
```
|
76
|
+
|
77
|
+
Note that the database user has to have rights to create and drop a database in
|
78
|
+
order to create the dummy test app database.
|
79
|
+
|
80
|
+
Then to test how the module works in Decidim, start the development server:
|
81
|
+
|
82
|
+
```bash
|
83
|
+
$ cd development_app
|
84
|
+
$ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rails s
|
85
|
+
```
|
86
|
+
|
87
|
+
In case you are using [rbenv](https://github.com/rbenv/rbenv) and have the
|
88
|
+
[rbenv-vars](https://github.com/rbenv/rbenv-vars) plugin installed for it, you
|
89
|
+
can add the environment variables to the root directory of the project in a file
|
90
|
+
named `.rbenv-vars`. If these are defined for the environment, you can omit
|
91
|
+
defining these in the commands shown above.
|
92
|
+
|
93
|
+
#### Code Styling
|
94
|
+
|
95
|
+
Please follow the code styling defined by the different linters that ensure we
|
96
|
+
are all talking with the same language collaborating on the same project. This
|
97
|
+
project is set to follow the same rules that Decidim itself follows.
|
98
|
+
|
99
|
+
The following linters are used:
|
100
|
+
|
101
|
+
- Ruby, [Rubocop](https://rubocop.readthedocs.io/)
|
102
|
+
- JS/ES, [eslint](https://eslint.org/)
|
103
|
+
- CSS/SCSS, [stylelint](https://stylelint.io/)
|
104
|
+
|
105
|
+
You can run the code styling checks by running the following commands from the
|
106
|
+
console:
|
107
|
+
|
108
|
+
```
|
109
|
+
$ bundle exec rubocop
|
110
|
+
$ npm run lint
|
111
|
+
$ npm run stylelint
|
112
|
+
```
|
113
|
+
|
114
|
+
To ease up following the style guide, you should install the plugins to your
|
115
|
+
favorite editor, such as:
|
116
|
+
|
117
|
+
- Atom
|
118
|
+
* [linter-rubocop](https://atom.io/packages/linter-rubocop)
|
119
|
+
* [linter-eslint](https://atom.io/packages/linter-eslint)
|
120
|
+
* [linter-stylelint](https://atom.io/packages/linter-stylelint)
|
121
|
+
- Sublime Text
|
122
|
+
* [Sublime RuboCop](https://github.com/pderichs/sublime_rubocop)
|
123
|
+
* [SublimeLinter-eslint](https://github.com/SublimeLinter/SublimeLinter-eslint)
|
124
|
+
* [SublimeLinter-stylelint](https://github.com/SublimeLinter/SublimeLinter-stylelint)
|
125
|
+
- Visual Studio Code
|
126
|
+
* [Rubocop for Visual Studio Code](https://github.com/misogi/vscode-ruby-rubocop)
|
127
|
+
* [VS Code ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
|
128
|
+
* [vscode-stylelint](https://github.com/shinnn/vscode-stylelint)
|
129
|
+
|
130
|
+
### Testing
|
131
|
+
|
132
|
+
To run the tests run the following in the gem development path:
|
133
|
+
|
134
|
+
```bash
|
135
|
+
$ bundle
|
136
|
+
$ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rake test_app
|
137
|
+
$ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rspec
|
138
|
+
```
|
139
|
+
|
140
|
+
Please read the notes regarding these commands from the development environment
|
141
|
+
setup above.
|
142
|
+
|
143
|
+
#### Test code coverage
|
144
|
+
|
145
|
+
If you want to generate the code coverage report for the tests, you can use
|
146
|
+
the `SIMPLECOV=1` environment variable in the rspec command as follows:
|
147
|
+
|
148
|
+
```bash
|
149
|
+
SIMPLECOV=1 bundle exec rspec
|
150
|
+
```
|
151
|
+
|
152
|
+
This will generate a folder named `coverage` in the project root which contains
|
153
|
+
the code coverage report.
|
154
|
+
|
155
|
+
## License
|
156
|
+
|
157
|
+
See [LICENSE-AGPLv3.txt](LICENSE-AGPLv3.txt).
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "decidim/dev/common_rake"
|
4
|
+
|
5
|
+
def install_module(path)
|
6
|
+
Dir.chdir(path) do
|
7
|
+
system("bundle exec rake decidim_plans:install:migrations")
|
8
|
+
system("bundle exec rake db:migrate")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def seed_db(path)
|
13
|
+
Dir.chdir(path) do
|
14
|
+
system("bundle exec rake db:seed")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Generates a dummy app for testing"
|
19
|
+
task test_app: "decidim:generate_external_test_app" do
|
20
|
+
ENV["RAILS_ENV"] = "test"
|
21
|
+
install_module("spec/decidim_dummy_app")
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "Generates a development app"
|
25
|
+
task :development_app do
|
26
|
+
Bundler.with_original_env do
|
27
|
+
generate_decidim_app(
|
28
|
+
"development_app",
|
29
|
+
"--app_name",
|
30
|
+
"#{base_app_name}_development_app",
|
31
|
+
"--path",
|
32
|
+
"..",
|
33
|
+
"--recreate_db",
|
34
|
+
"--demo"
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
install_module("development_app")
|
39
|
+
seed_db("development_app")
|
40
|
+
end
|
41
|
+
|
42
|
+
# Run all tests, include all
|
43
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
44
|
+
t.verbose = false
|
45
|
+
end
|
46
|
+
|
47
|
+
# Run both by default
|
48
|
+
task default: [:spec]
|
@@ -0,0 +1,3 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 35 35">
|
2
|
+
<path d="M17.5 35A17.5 17.5 0 1 1 35 17.5 17.52 17.52 0 0 1 17.5 35zm0-33.06A15.56 15.56 0 1 0 33.06 17.5 15.57 15.57 0 0 0 17.5 1.94zm9.5 13.7H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zm0 3.68H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zM22.26 23H8a1 1 0 0 1 0-1.94h14.26a1 1 0 0 1 0 1.94z"/>
|
3
|
+
</svg>
|
@@ -0,0 +1,78 @@
|
|
1
|
+
((exports) => {
|
2
|
+
const { AutoLabelByPositionComponent, AutoButtonsByPositionComponent, createDynamicFields, createSortList } = exports.DecidimAdmin;
|
3
|
+
|
4
|
+
const wrapperSelector = ".plan-sections";
|
5
|
+
const fieldSelector = ".plan-section";
|
6
|
+
|
7
|
+
const autoLabelByPosition = new AutoLabelByPositionComponent({
|
8
|
+
listSelector: ".plan-section:not(.hidden)",
|
9
|
+
labelSelector: ".card-title span:first",
|
10
|
+
onPositionComputed: (el, idx) => {
|
11
|
+
$(el).find("input[name$=\\[position\\]]").val(idx);
|
12
|
+
}
|
13
|
+
});
|
14
|
+
|
15
|
+
const autoButtonsByPosition = new AutoButtonsByPositionComponent({
|
16
|
+
listSelector: ".plan-section:not(.hidden)",
|
17
|
+
hideOnFirstSelector: ".move-up-section",
|
18
|
+
hideOnLastSelector: ".move-down-section"
|
19
|
+
});
|
20
|
+
|
21
|
+
const createSortableList = () => {
|
22
|
+
createSortList(".sections-list:not(.published)", {
|
23
|
+
handle: ".section-divider",
|
24
|
+
placeholder: '<div style="border-style: dashed; border-color: #000"></div>',
|
25
|
+
forcePlaceholderSize: true,
|
26
|
+
onSortUpdate: () => { autoLabelByPosition.run() }
|
27
|
+
});
|
28
|
+
};
|
29
|
+
|
30
|
+
const hideDeletedSection = ($target) => {
|
31
|
+
const inputDeleted = $target.find("input[name$=\\[deleted\\]]").val();
|
32
|
+
|
33
|
+
if (inputDeleted === "true") {
|
34
|
+
$target.addClass("hidden");
|
35
|
+
$target.hide();
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
createDynamicFields({
|
40
|
+
placeholderId: "section-id",
|
41
|
+
wrapperSelector: wrapperSelector,
|
42
|
+
containerSelector: ".sections-list",
|
43
|
+
fieldSelector: fieldSelector,
|
44
|
+
addFieldButtonSelector: ".add-section",
|
45
|
+
removeFieldButtonSelector: ".remove-section",
|
46
|
+
moveUpFieldButtonSelector: ".move-up-section",
|
47
|
+
moveDownFieldButtonSelector: ".move-down-section",
|
48
|
+
onAddField: () => {
|
49
|
+
createSortableList();
|
50
|
+
|
51
|
+
autoLabelByPosition.run();
|
52
|
+
autoButtonsByPosition.run();
|
53
|
+
},
|
54
|
+
onRemoveField: () => {
|
55
|
+
autoLabelByPosition.run();
|
56
|
+
autoButtonsByPosition.run();
|
57
|
+
},
|
58
|
+
onMoveUpField: () => {
|
59
|
+
autoLabelByPosition.run();
|
60
|
+
autoButtonsByPosition.run();
|
61
|
+
},
|
62
|
+
onMoveDownField: () => {
|
63
|
+
autoLabelByPosition.run();
|
64
|
+
autoButtonsByPosition.run();
|
65
|
+
}
|
66
|
+
});
|
67
|
+
|
68
|
+
createSortableList();
|
69
|
+
|
70
|
+
$(fieldSelector).each((idx, el) => {
|
71
|
+
const $target = $(el);
|
72
|
+
|
73
|
+
hideDeletedSection($target);
|
74
|
+
});
|
75
|
+
|
76
|
+
autoLabelByPosition.run();
|
77
|
+
autoButtonsByPosition.run();
|
78
|
+
})(window);
|
@@ -0,0 +1,101 @@
|
|
1
|
+
((exports) => {
|
2
|
+
const { AutoLabelByPositionComponent, AutoButtonsByPositionComponent, createDynamicFields, createSortList } = exports.DecidimAdmin;
|
3
|
+
|
4
|
+
const initMultifield = ($wrapper) => {
|
5
|
+
const wrapperSelector = `#${$wrapper.attr("id")}`;
|
6
|
+
const placeholderId = $wrapper.data("placeholder-id");
|
7
|
+
|
8
|
+
const fieldSelector = ".multifield-field";
|
9
|
+
|
10
|
+
const autoLabelByPosition = new AutoLabelByPositionComponent({
|
11
|
+
listSelector: `${wrapperSelector} .multifield-field:not(.hidden)`,
|
12
|
+
labelSelector: ".card-title span:first",
|
13
|
+
onPositionComputed: (el, idx) => {
|
14
|
+
$(el).find("input.position-input").val(idx);
|
15
|
+
}
|
16
|
+
});
|
17
|
+
|
18
|
+
const autoButtonsByPosition = new AutoButtonsByPositionComponent({
|
19
|
+
listSelector: `${wrapperSelector} .multifield-field:not(.hidden)`,
|
20
|
+
hideOnFirstSelector: ".move-up-field",
|
21
|
+
hideOnLastSelector: ".move-down-field"
|
22
|
+
});
|
23
|
+
|
24
|
+
const createSortableList = () => {
|
25
|
+
createSortList(`${wrapperSelector} .fields-list:not(.published)`, {
|
26
|
+
handle: ".multifield-field-divider",
|
27
|
+
placeholder: '<div style="border-style: dashed; border-color: #000"></div>',
|
28
|
+
forcePlaceholderSize: true,
|
29
|
+
onSortUpdate: () => { autoLabelByPosition.run() }
|
30
|
+
});
|
31
|
+
};
|
32
|
+
|
33
|
+
const hideDeletedSection = ($target) => {
|
34
|
+
const inputDeleted = $target.find("input[name$=\\[deleted\\]]").val();
|
35
|
+
|
36
|
+
if (inputDeleted === "true") {
|
37
|
+
$target.addClass("hidden");
|
38
|
+
$target.hide();
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
createDynamicFields({
|
43
|
+
placeholderId: placeholderId,
|
44
|
+
wrapperSelector: wrapperSelector,
|
45
|
+
containerSelector: ".multifield-fields-list",
|
46
|
+
fieldSelector: fieldSelector,
|
47
|
+
addFieldButtonSelector: ".add-field",
|
48
|
+
removeFieldButtonSelector: ".remove-field",
|
49
|
+
moveUpFieldButtonSelector: ".move-up-field",
|
50
|
+
moveDownFieldButtonSelector: ".move-down-field",
|
51
|
+
onAddField: () => {
|
52
|
+
createSortableList();
|
53
|
+
|
54
|
+
autoLabelByPosition.run();
|
55
|
+
autoButtonsByPosition.run();
|
56
|
+
},
|
57
|
+
onRemoveField: () => {
|
58
|
+
autoLabelByPosition.run();
|
59
|
+
autoButtonsByPosition.run();
|
60
|
+
},
|
61
|
+
onMoveUpField: () => {
|
62
|
+
autoLabelByPosition.run();
|
63
|
+
autoButtonsByPosition.run();
|
64
|
+
},
|
65
|
+
onMoveDownField: () => {
|
66
|
+
autoLabelByPosition.run();
|
67
|
+
autoButtonsByPosition.run();
|
68
|
+
}
|
69
|
+
});
|
70
|
+
|
71
|
+
createSortableList();
|
72
|
+
|
73
|
+
$(fieldSelector).each((idx, el) => {
|
74
|
+
const $target = $(el);
|
75
|
+
|
76
|
+
hideDeletedSection($target);
|
77
|
+
});
|
78
|
+
|
79
|
+
autoLabelByPosition.run();
|
80
|
+
autoButtonsByPosition.run();
|
81
|
+
}
|
82
|
+
|
83
|
+
$.fn.multifield = function() {
|
84
|
+
$(this).each(
|
85
|
+
|
86
|
+
/**
|
87
|
+
* @this HTMLElement
|
88
|
+
* @return {void}
|
89
|
+
*/
|
90
|
+
function() {
|
91
|
+
const $elem = $(this);
|
92
|
+
let id = $elem.attr("id");
|
93
|
+
if (!id || id.length < 1) {
|
94
|
+
id = `multifield-${Math.random().toString(16).slice(2)}`;
|
95
|
+
$elem.attr("id", id);
|
96
|
+
}
|
97
|
+
initMultifield($elem);
|
98
|
+
}
|
99
|
+
)
|
100
|
+
}
|
101
|
+
})(window);
|
@@ -0,0 +1,5 @@
|
|
1
|
+
// = require decidim/admin/auto_buttons_by_position.component
|
2
|
+
// = require decidim/admin/auto_label_by_position.component
|
3
|
+
// = require decidim/admin/dynamic_fields.component
|
4
|
+
// = require decidim/admin/sort_list.component
|
5
|
+
// = require decidim/plans/multifield/component
|
@@ -0,0 +1,41 @@
|
|
1
|
+
// = require decidim/data_picker
|
2
|
+
// = require jquery.auto-complete
|
3
|
+
|
4
|
+
// = require_self
|
5
|
+
|
6
|
+
$(function() {
|
7
|
+
$(document).on("open.zf.reveal", "#data_picker-modal", function () {
|
8
|
+
let xhr = null;
|
9
|
+
|
10
|
+
$("#data_picker-autocomplete").autoComplete({
|
11
|
+
minChars: 2,
|
12
|
+
source: function(term, response) {
|
13
|
+
try {
|
14
|
+
xhr.abort();
|
15
|
+
} catch (exception) { xhr = null; }
|
16
|
+
|
17
|
+
let url = $("#proposal-picker-choose").attr("href");
|
18
|
+
xhr = $.getJSON(
|
19
|
+
url,
|
20
|
+
{ term: term },
|
21
|
+
function(data) { response(data); }
|
22
|
+
);
|
23
|
+
},
|
24
|
+
renderItem: function (item, search) {
|
25
|
+
let sanitizedSearch = search.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
26
|
+
let re = new RegExp(`(${sanitizedSearch.split(" ").join("|")})`, "gi");
|
27
|
+
let title = item[0];
|
28
|
+
let modelId = item[1];
|
29
|
+
return `<div class="autocomplete-suggestion" data-model-id="${modelId}" data-val ="${title}">${title.replace(re, "<b>$1</b>")}</div>`;
|
30
|
+
},
|
31
|
+
onSelect: function(event, term, item) {
|
32
|
+
let choose = $("#proposal-picker-choose");
|
33
|
+
let modelId = item.data("modelId");
|
34
|
+
let val = item.data("val");
|
35
|
+
choose.data("picker-value", modelId);
|
36
|
+
choose.data("picker-text", val);
|
37
|
+
choose.data("picker-choose", "");
|
38
|
+
}
|
39
|
+
});
|
40
|
+
});
|
41
|
+
});
|
@@ -0,0 +1,42 @@
|
|
1
|
+
$(function() {
|
2
|
+
const COUNT_KEY = "%count%";
|
3
|
+
|
4
|
+
$.fn.remainingCharacters = function() {
|
5
|
+
return $(this).each(
|
6
|
+
|
7
|
+
/**
|
8
|
+
* @this HTMLElement
|
9
|
+
* @returns {void}
|
10
|
+
*/
|
11
|
+
function() {
|
12
|
+
const $input = $(this);
|
13
|
+
const $target = $($input.data("remaining-characters"));
|
14
|
+
const maxCharacters = parseInt($(this).attr("maxlength"), 10);
|
15
|
+
|
16
|
+
if ($target.length > 0 && maxCharacters > 0) {
|
17
|
+
const messagesJson = $target.data("remaining-characters-messages");
|
18
|
+
const messages = $.extend({
|
19
|
+
one: `${COUNT_KEY} character left`,
|
20
|
+
many: `${COUNT_KEY} characters left`
|
21
|
+
}, messagesJson);
|
22
|
+
|
23
|
+
const updateStatus = function() {
|
24
|
+
const numCharacters = $input.val().length;
|
25
|
+
const remaining = maxCharacters - numCharacters;
|
26
|
+
let message = messages.many;
|
27
|
+
if (remaining === 1) {
|
28
|
+
message = messages.one;
|
29
|
+
}
|
30
|
+
|
31
|
+
$target.text(message.replace(COUNT_KEY, remaining));
|
32
|
+
};
|
33
|
+
|
34
|
+
$input.on("keyup focus", function() {
|
35
|
+
updateStatus();
|
36
|
+
});
|
37
|
+
updateStatus();
|
38
|
+
}
|
39
|
+
}
|
40
|
+
);
|
41
|
+
};
|
42
|
+
});
|
@@ -0,0 +1,20 @@
|
|
1
|
+
// = require_self
|
2
|
+
|
3
|
+
/**
|
4
|
+
* When switching tabs in i18n fields, autofocus on the input to save clicks
|
5
|
+
*
|
6
|
+
* Similar to:
|
7
|
+
* decidim-admin/app/assets/javascripts/decidim/admin/tab_focus.js.es6
|
8
|
+
*/
|
9
|
+
$(() => {
|
10
|
+
// Event launched by foundation
|
11
|
+
$("[data-tabs]").on("change.zf.tabs", (event) => {
|
12
|
+
const $container = $(event.target).parent().next(".tabs-content");
|
13
|
+
const $panel = $(".tabs-panel.is-active", $container);
|
14
|
+
|
15
|
+
const $content = $("input, textarea", $panel).first();
|
16
|
+
if ($content.length > 0) {
|
17
|
+
$content.focus();
|
18
|
+
}
|
19
|
+
});
|
20
|
+
});
|
@@ -0,0 +1,26 @@
|
|
1
|
+
.multifield-fields{
|
2
|
+
.multifield-field-divider{
|
3
|
+
padding-top: .8rem;
|
4
|
+
padding-bottom: .8rem;
|
5
|
+
}
|
6
|
+
|
7
|
+
.card-title{
|
8
|
+
padding-top: 0;
|
9
|
+
padding-bottom: 0;
|
10
|
+
|
11
|
+
.card-title-text{
|
12
|
+
display: inline-block;
|
13
|
+
padding-top: .2rem;
|
14
|
+
padding-bottom: .2em;
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
.button--title{
|
19
|
+
float: right;
|
20
|
+
margin-left: 1rem;
|
21
|
+
|
22
|
+
&:first-child{
|
23
|
+
margin-left: 0;
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
/*
|
2
|
+
*= require social-share-button
|
3
|
+
*/
|
4
|
+
|
5
|
+
$size: 45px;
|
6
|
+
|
7
|
+
.share-link:hover{
|
8
|
+
text-decoration: underline;
|
9
|
+
cursor: pointer;
|
10
|
+
}
|
11
|
+
|
12
|
+
.social-share-button{
|
13
|
+
display: inline-block;
|
14
|
+
vertical-align: top;
|
15
|
+
|
16
|
+
.ssb-icon{
|
17
|
+
margin-right: 5px;
|
18
|
+
background-size: $size $size;
|
19
|
+
height: $size;
|
20
|
+
width: $size;
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<% if flagable? %>
|
2
|
+
<button type="button" data-open="<%= current_user.present? ? "flagModal" : "loginModal" %>" title="<%= t("report", scope: "decidim.plans.plans.show") %>" aria-controls="<%= current_user.present? ? "flagModal" : "loginModal" %>" aria-haspopup="true" tabindex="0">
|
3
|
+
<%= icon "flag", aria_label: t("report", scope: "decidim.plans.plans.show"), class: "icon--small" %>
|
4
|
+
</button>
|
5
|
+
<% end %>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<% if withdrawable? %>
|
2
|
+
<%= action_authorized_link_to :withdraw, withdraw_path, method: :put, class: "title-action__action button small hollow", title: t("withdraw_btn_hint", scope: "decidim.plans.plans.show"), data: { confirm: t("withdraw_confirmation", scope: "decidim.plans.plans.show") } do %>
|
3
|
+
<%= t("withdraw_btn", scope: "decidim.plans.plans.show") %>
|
4
|
+
<%= icon "x" %>
|
5
|
+
<% end %>
|
6
|
+
<% end %>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Plans
|
5
|
+
class CoauthorshipsCell < Decidim::CoauthorshipsCell
|
6
|
+
def show
|
7
|
+
if authorable?
|
8
|
+
cell "decidim/plans/author", presenter_for_author(model), extra_classes.merge(has_actions: has_actions?, from: model)
|
9
|
+
else
|
10
|
+
cell(
|
11
|
+
"decidim/plans/collapsible_authors",
|
12
|
+
presenters_for_identities(model),
|
13
|
+
cell_name: "decidim/plans/author",
|
14
|
+
cell_options: extra_classes,
|
15
|
+
size: size,
|
16
|
+
from: model,
|
17
|
+
has_actions: has_actions?
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<div class="author--inline">
|
2
|
+
<%= cell(
|
3
|
+
"decidim/collapsible_list",
|
4
|
+
list,
|
5
|
+
cell_name: cell_name,
|
6
|
+
cell_options: options.merge(has_actions: false),
|
7
|
+
size: size
|
8
|
+
) %>
|
9
|
+
</div>
|
10
|
+
<% if actionable? %>
|
11
|
+
<div class="author-data__extra">
|
12
|
+
<% author_cell= cell("decidim/plans/author", model, from: from_context) %>
|
13
|
+
<%= author_cell.(:date) %>
|
14
|
+
<%= author_cell.(:flag) %>
|
15
|
+
<%= author_cell.(:withdraw) %>
|
16
|
+
</div>
|
17
|
+
<% end %>
|