decidim-admin 0.20.1 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of decidim-admin might be problematic. Click here for more details.

Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -0
  3. data/app/assets/javascripts/decidim/admin/application.js.es6 +1 -0
  4. data/app/assets/javascripts/decidim/admin/bundle.js +5 -5
  5. data/app/assets/javascripts/decidim/admin/bundle.js.map +1 -1
  6. data/app/assets/javascripts/decidim/admin/newsletters.js.es6 +8 -0
  7. data/app/assets/stylesheets/decidim/admin/_variables.scss +1 -1
  8. data/app/assets/stylesheets/decidim/admin/extra/_action-icon.scss +6 -0
  9. data/app/assets/stylesheets/decidim/admin/extra/_cards.scss +11 -0
  10. data/app/assets/stylesheets/decidim/admin/modules/_buttons.scss +5 -0
  11. data/app/assets/stylesheets/decidim/admin/modules/_cards.scss +11 -0
  12. data/app/assets/stylesheets/decidim/admin/modules/_filters.scss +78 -1
  13. data/app/assets/stylesheets/decidim/admin/modules/_layout.scss +1 -1
  14. data/app/assets/stylesheets/decidim/admin/modules/_secondary-nav.scss +5 -1
  15. data/app/assets/stylesheets/decidim/admin/modules/_table-list.scss +24 -3
  16. data/app/cells/decidim/admin/results_per_page/show.erb +16 -0
  17. data/app/cells/decidim/admin/results_per_page_cell.rb +14 -0
  18. data/app/commands/decidim/admin/deliver_newsletter.rb +1 -1
  19. data/app/commands/decidim/admin/update_organization.rb +4 -1
  20. data/app/controllers/concerns/decidim/admin/filterable.rb +152 -0
  21. data/app/controllers/concerns/decidim/admin/officializations/filterable.rb +31 -0
  22. data/app/controllers/concerns/decidim/admin/paginable.rb +20 -0
  23. data/app/controllers/decidim/admin/admin_terms_controller.rb +20 -0
  24. data/app/controllers/decidim/admin/application_controller.rb +2 -0
  25. data/app/controllers/decidim/admin/components/base_controller.rb +5 -1
  26. data/app/controllers/decidim/admin/components_controller.rb +16 -20
  27. data/app/controllers/decidim/admin/concerns/has_private_users.rb +4 -0
  28. data/app/controllers/decidim/admin/newsletters_controller.rb +12 -1
  29. data/app/controllers/decidim/admin/officializations_controller.rb +7 -6
  30. data/app/forms/decidim/admin/organization_form.rb +7 -0
  31. data/app/helpers/decidim/admin/admin_terms_helper.rb +47 -0
  32. data/app/helpers/decidim/admin/application_helper.rb +1 -0
  33. data/app/helpers/decidim/admin/dashboard_helper.rb +25 -0
  34. data/app/helpers/decidim/admin/filterable_helper.rb +121 -0
  35. data/app/helpers/decidim/admin/newsletters_helper.rb +18 -0
  36. data/app/helpers/decidim/admin/paginable/per_page_helper.rb +22 -0
  37. data/app/helpers/decidim/admin/scopes_helper.rb +6 -0
  38. data/app/helpers/decidim/admin/settings_helper.rb +18 -2
  39. data/app/helpers/decidim/admin/user_roles_helper.rb +19 -0
  40. data/app/permissions/decidim/admin/permissions.rb +23 -6
  41. data/app/queries/decidim/admin/newsletter_recipients.rb +11 -4
  42. data/app/views/decidim/admin/admin_terms/show.html.erb +26 -0
  43. data/app/views/decidim/admin/components/_component.html.erb +35 -33
  44. data/app/views/decidim/admin/components/index.html.erb +10 -8
  45. data/app/views/decidim/admin/dashboard/show.html.erb +15 -0
  46. data/app/views/decidim/admin/newsletters/index.html.erb +9 -3
  47. data/app/views/decidim/admin/newsletters/select_recipients_to_deliver.html.erb +4 -4
  48. data/app/views/decidim/admin/officializations/index.html.erb +2 -38
  49. data/app/views/decidim/admin/organization/_form.html.erb +16 -0
  50. data/app/views/decidim/admin/shared/_filters.html.erb +40 -0
  51. data/app/views/layouts/decidim/admin/_application.html.erb +1 -1
  52. data/config/locales/ar.yml +40 -6
  53. data/config/locales/ca.yml +45 -6
  54. data/config/locales/cs.yml +45 -6
  55. data/config/locales/de.yml +9 -6
  56. data/config/locales/el.yml +1 -0
  57. data/config/locales/en.yml +45 -6
  58. data/config/locales/es-MX.yml +45 -6
  59. data/config/locales/es-PY.yml +45 -6
  60. data/config/locales/es.yml +45 -6
  61. data/config/locales/eu.yml +9 -6
  62. data/config/locales/fi-plain.yml +45 -6
  63. data/config/locales/fi.yml +45 -6
  64. data/config/locales/fr.yml +9 -6
  65. data/config/locales/gl.yml +9 -6
  66. data/config/locales/hu.yml +45 -6
  67. data/config/locales/id-ID.yml +9 -6
  68. data/config/locales/is-IS.yml +9 -6
  69. data/config/locales/it.yml +45 -6
  70. data/config/locales/nl.yml +28 -6
  71. data/config/locales/no.yml +45 -6
  72. data/config/locales/pl.yml +9 -6
  73. data/config/locales/pt-BR.yml +9 -6
  74. data/config/locales/pt.yml +9 -6
  75. data/config/locales/ru.yml +9 -6
  76. data/config/locales/sv.yml +9 -6
  77. data/config/locales/tr-TR.yml +9 -6
  78. data/config/locales/uk.yml +9 -6
  79. data/config/routes.rb +6 -0
  80. data/db/migrate/20191118112040_add_accepted_admin_terms_at_field_to_users.rb +7 -0
  81. data/lib/decidim/admin.rb +17 -0
  82. data/lib/decidim/admin/form_builder.rb +5 -0
  83. data/lib/decidim/admin/test.rb +2 -0
  84. data/lib/decidim/admin/test/filterable_examples.rb +129 -0
  85. data/lib/decidim/admin/test/manage_paginated_collection_examples.rb +22 -0
  86. data/lib/decidim/admin/version.rb +1 -1
  87. data/vendor/assets/javascripts/jquery.serializejson.js +344 -0
  88. metadata +26 -8
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module Admin
7
+ module Officializations
8
+ module Filterable
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ include Decidim::Admin::Filterable
13
+
14
+ private
15
+
16
+ def base_query
17
+ collection
18
+ end
19
+
20
+ def search_field_predicate
21
+ :name_or_nickname_or_email_cont
22
+ end
23
+
24
+ def filters
25
+ [:officialized_at_null]
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module Admin
7
+ module Paginable
8
+ # Common logic to paginate admin resources
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ include Decidim::Paginable
13
+
14
+ def per_page
15
+ params[:per_page].present? ? params[:per_page].to_i : Decidim::Admin.per_page_range.first
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ # The controller to handle the Admin
6
+ # Terms of use agreement.
7
+ class AdminTermsController < Decidim::Admin::ApplicationController
8
+ def accept
9
+ current_user.admin_terms_accepted_at = Time.current
10
+ if current_user.save!
11
+ flash[:notice] = t("accept.success", scope: "decidim.admin.admin_terms_of_use")
12
+ redirect_to decidim_admin.root_path
13
+ else
14
+ flash[:alert] = t("accept.error", scope: "decidim.admin.admin_terms_of_use")
15
+ redirect_to decidim_admin.admin_terms_show_path
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -8,6 +8,7 @@ module Decidim
8
8
  include NeedsPermission
9
9
  include FormFactory
10
10
  include LocaleSwitcher
11
+ include UseOrganizationTimeZone
11
12
  include PayloadInfo
12
13
  include HttpCachingDisabler
13
14
 
@@ -17,6 +18,7 @@ module Decidim
17
18
  helper Decidim::Admin::IconLinkHelper
18
19
  helper Decidim::Admin::MenuHelper
19
20
  helper Decidim::Admin::ScopesHelper
21
+ helper Decidim::Admin::Paginable::PerPageHelper
20
22
  helper Decidim::DecidimFormHelper
21
23
  helper Decidim::ReplaceButtonsHelper
22
24
  helper Decidim::ScopesHelper
@@ -22,7 +22,7 @@ module Decidim
22
22
  :parent_path
23
23
 
24
24
  before_action except: [:index, :show] do
25
- enforce_permission_to :manage, :component, component: current_component
25
+ enforce_permission_to :manage, :component, component: current_component unless skip_manage_component_permission
26
26
  end
27
27
 
28
28
  before_action on: [:index, :show] do
@@ -59,6 +59,10 @@ module Decidim
59
59
  def parent_path
60
60
  @parent_path ||= ::Decidim::EngineRouter.admin_proxy(current_participatory_space).components_path
61
61
  end
62
+
63
+ def skip_manage_component_permission
64
+ false
65
+ end
62
66
  end
63
67
  end
64
68
  end
@@ -27,7 +27,7 @@ module Decidim
27
27
  end
28
28
 
29
29
  def create
30
- @form = form(ComponentForm).from_params(form_params)
30
+ @form = form(ComponentForm).from_params(component_params)
31
31
  enforce_permission_to :create, :component
32
32
 
33
33
  CreateComponent.call(@form) do
@@ -52,7 +52,7 @@ module Decidim
52
52
 
53
53
  def update
54
54
  @component = query_scope.find(params[:id])
55
- @form = form(ComponentForm).from_params(form_params)
55
+ @form = form(ComponentForm).from_params(component_params)
56
56
  enforce_permission_to :update, :component, component: @component
57
57
 
58
58
  UpdateComponent.call(@form, @component) do
@@ -113,28 +113,24 @@ module Decidim
113
113
 
114
114
  private
115
115
 
116
- # Returns a Class with the attributes sanitized, coerced and filtered
117
- # to the right type. See Decidim::SettingsManifest#schema.
118
- def new_settings_schema(name, data)
119
- manifest.settings(name).schema.new(data, current_organization.default_locale)
120
- end
121
-
122
116
  # Processes the component params so Decidim::Admin::ComponentForm
123
117
  # can assign and validate the attributes when using #from_params.
124
- def form_params
125
- form_params = params[:component].permit!
126
- form_params[:id] = params[:id]
127
- form_params[:manifest] = manifest
128
- form_params[:participatory_space] = current_participatory_space
129
- form_params[:settings] = new_settings_schema(:global, form_params[:settings])
130
- if form_params[:default_step_settings]
131
- form_params[:default_step_settings] = new_settings_schema(:step, form_params[:default_step_settings])
132
- else
133
- form_params[:step_settings].each do |key, value|
134
- form_params[:step_settings][key] = new_settings_schema(:step, value)
118
+ def component_params
119
+ new_settings = proc { |name, data| Component.build_settings(manifest, name, data, current_organization) }
120
+
121
+ params[:component].permit!.tap do |hsh|
122
+ hsh[:id] = params[:id]
123
+ hsh[:manifest] = manifest
124
+ hsh[:participatory_space] = current_participatory_space
125
+ hsh[:settings] = new_settings.call(:global, hsh[:settings])
126
+ if hsh[:default_step_settings]
127
+ hsh[:default_step_settings] = new_settings.call(:step, hsh[:default_step_settings])
128
+ else
129
+ hsh[:step_settings].each do |key, value|
130
+ hsh[:step_settings][key] = new_settings.call(:step, value)
131
+ end
135
132
  end
136
133
  end
137
- form_params
138
134
  end
139
135
 
140
136
  def query_scope
@@ -93,8 +93,12 @@ module Decidim
93
93
  end
94
94
 
95
95
  def collection
96
+ # there's an unidentified corner case where Decidim::User
97
+ # may have been destroyed, but the related ParticipatorySpacePrivateUser
98
+ # remains in the database. That's why filtering by not null users
96
99
  @collection ||= privatable_to
97
100
  .participatory_space_private_users
101
+ .includes(:user).where.not("decidim_users.id" => nil)
98
102
  .page(params[:page])
99
103
  .per(20)
100
104
  end
@@ -7,7 +7,7 @@ module Decidim
7
7
  include Decidim::NewslettersHelper
8
8
  include Decidim::Admin::NewslettersHelper
9
9
  include Paginable
10
- helper_method :newsletter
10
+ helper_method :newsletter, :recipients_count_query
11
11
 
12
12
  def index
13
13
  enforce_permission_to :index, :newsletter
@@ -96,6 +96,12 @@ module Decidim
96
96
  @form.send_to_all_users = current_user.admin?
97
97
  end
98
98
 
99
+ def recipients_count
100
+ data = params.permit(data: {}).to_h[:data]
101
+ @form = form(SelectiveNewsletterForm).from_params(data)
102
+ render plain: recipients_count_query
103
+ end
104
+
99
105
  def deliver
100
106
  enforce_permission_to :update, :newsletter, newsletter: newsletter
101
107
  @form = form(SelectiveNewsletterForm).from_params(params)
@@ -127,6 +133,11 @@ module Decidim
127
133
  def newsletter
128
134
  @newsletter ||= collection.find_by(id: params[:id])
129
135
  end
136
+
137
+ def recipients_count_query
138
+ @form ||= form(SelectiveNewsletterForm).instance
139
+ NewsletterRecipients.for(@form).size
140
+ end
130
141
  end
131
142
  end
132
143
  end
@@ -5,6 +5,8 @@ module Decidim
5
5
  # Controller that allows managing user officializations at the admin panel.
6
6
  #
7
7
  class OfficializationsController < Decidim::Admin::ApplicationController
8
+ include Decidim::Admin::Officializations::Filterable
9
+
8
10
  layout "decidim/admin/users"
9
11
 
10
12
  helper_method :user
@@ -12,12 +14,7 @@ module Decidim
12
14
 
13
15
  def index
14
16
  enforce_permission_to :read, :officialization
15
- @query = params[:q]
16
- @state = params[:state]
17
-
18
- @users = Decidim::Admin::UserFilter.for(current_organization.users.not_deleted, @query, @state)
19
- .page(params[:page])
20
- .per(15)
17
+ @users = filtered_collection
21
18
  end
22
19
 
23
20
  def new
@@ -54,6 +51,10 @@ module Decidim
54
51
 
55
52
  private
56
53
 
54
+ def collection
55
+ @collection ||= current_organization.users.not_deleted
56
+ end
57
+
57
58
  def user
58
59
  @user ||= Decidim::User.find_by(
59
60
  id: params[:user_id],
@@ -12,6 +12,7 @@ module Decidim
12
12
 
13
13
  attribute :name, String
14
14
  attribute :reference_prefix, String
15
+ attribute :time_zone, String
15
16
  attribute :twitter_handler, String
16
17
  attribute :facebook_handler, String
17
18
  attribute :instagram_handler, String
@@ -20,6 +21,7 @@ module Decidim
20
21
  attribute :default_locale, String
21
22
  attribute :badges_enabled, Boolean
22
23
  attribute :user_groups_enabled, Boolean
24
+ attribute :rich_text_editor_in_public_views, Boolean
23
25
 
24
26
  attribute :send_welcome_notification, Boolean
25
27
  attribute :customize_welcome_notification, Boolean
@@ -27,11 +29,16 @@ module Decidim
27
29
  translatable_attribute :welcome_notification_subject, String
28
30
  translatable_attribute :welcome_notification_body, String
29
31
 
32
+ translatable_attribute :admin_terms_of_use_body, String
33
+
30
34
  validates :welcome_notification_subject, :welcome_notification_body, translatable_presence: true, if: proc { |form| form.customize_welcome_notification }
31
35
 
32
36
  validates :name, presence: true
37
+ validates :time_zone, presence: true
38
+ validates :time_zone, time_zone: true
33
39
  validates :default_locale, :reference_prefix, presence: true
34
40
  validates :default_locale, inclusion: { in: :available_locales }
41
+ validates :admin_terms_of_use_body, translatable_presence: true
35
42
 
36
43
  private
37
44
 
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ # This module includes helpers to show Admin Terms of Use
6
+ module AdminTermsHelper
7
+ def admin_terms_of_use_body
8
+ current_organization.admin_terms_of_use_body.symbolize_keys[I18n.locale].html_safe
9
+ end
10
+
11
+ def admin_terms_announcement_args
12
+ {
13
+ callout_class: current_user.admin_terms_accepted? ? "success" : "warning",
14
+ announcement: announcement_body
15
+ }
16
+ end
17
+
18
+ def announcement_body
19
+ if current_user.admin_terms_accepted?
20
+ t("accept.success", scope: "decidim.admin.admin_terms_of_use")
21
+ else
22
+ t("required_review.callout", scope: "decidim.admin.admin_terms_of_use")
23
+ end
24
+ end
25
+
26
+ def button_to_accept_admin_terms
27
+ button_to(
28
+ t("decidim.admin.admin_terms_of_use.actions.accept"),
29
+ admin_terms_accept_path,
30
+ class: "button success",
31
+ method: :put
32
+ )
33
+ end
34
+
35
+ def button_to_refuse_admin_terms
36
+ link_to(
37
+ t("decidim.admin.admin_terms_of_use.actions.refuse"),
38
+ decidim.root_path,
39
+ class: "button clear",
40
+ data: {
41
+ confirm: t("actions.are_you_sure", scope: "decidim.admin.admin_terms_of_use")
42
+ }
43
+ )
44
+ end
45
+ end
46
+ end
47
+ end
@@ -12,6 +12,7 @@ module Decidim
12
12
  include Decidim::MetaTagsHelper
13
13
  include Decidim::MapHelper
14
14
  include Decidim::Admin::LogRenderHelper
15
+ include Decidim::Admin::UserRolesHelper
15
16
 
16
17
  def title
17
18
  current_organization.name
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ # This module includes helpers to be used in the admin dashboard, including helper methods to show the Admin Terms of Use.
6
+ module DashboardHelper
7
+ def admin_terms_announcement_args
8
+ {
9
+ callout_class: "warning",
10
+ announcement: announcement_body
11
+ }
12
+ end
13
+
14
+ def announcement_body
15
+ body = t("required_review.callout", scope: "decidim.admin.admin_terms_of_use")
16
+ body += " "
17
+ body += link_to(
18
+ t("required_review.cta", scope: "decidim.admin.admin_terms_of_use"),
19
+ admin_terms_show_path
20
+ )
21
+ body
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ # Helper that provides methods related to Decidim::Admin::Filterable concern.
6
+ module FilterableHelper
7
+ # Renders the filters selector with tags in the admin panel.
8
+ def admin_filter_selector
9
+ render partial: "decidim/admin/shared/filters"
10
+ end
11
+
12
+ # Builds a tree of links from Decidim::Admin::Filterable::filters_with_values
13
+ def submenu_options_tree
14
+ filters_with_values.each_with_object({}) do |(filter, values), hash|
15
+ link = filter_link_label(filter)
16
+ hash[link] = if values.is_a?(Array)
17
+ build_submenu_options_tree_from_array(filter, values)
18
+ elsif values.is_a?(Hash)
19
+ build_submenu_options_tree_from_hash(filter, values)
20
+ end
21
+ end
22
+ end
23
+
24
+ # Builds a tree of links from an array. The tree will have only one level.
25
+ def build_submenu_options_tree_from_array(filter, values)
26
+ links = []
27
+ links += extra_dropdown_submenu_options_items(filter)
28
+ links += values.map { |value| filter_link_value(filter, value) }
29
+ links.each_with_object({}) { |link, hash| hash[link] = nil }
30
+ end
31
+
32
+ # To be overriden. Useful for adding links that do not match with the filter.
33
+ # Must return an Array.
34
+ def extra_dropdown_submenu_options_items(_filter)
35
+ []
36
+ end
37
+
38
+ # Builds a tree of links from an Hash. The tree can have many levels.
39
+ def build_submenu_options_tree_from_hash(filter, values)
40
+ values.each_with_object({}) do |(key, value), hash|
41
+ link = filter_link_value(filter, key)
42
+ hash[link] = if value.nil?
43
+ nil
44
+ elsif value.is_a?(Hash)
45
+ build_submenu_options_tree_from_hash(filter, value)
46
+ end
47
+ end
48
+ end
49
+
50
+ # Produces the html for the dropdown submenu from the options tree.
51
+ # Returns a ActiveSupport::SafeBuffer.
52
+ def dropdown_submenu(options)
53
+ content_tag(:ul, class: "vertical menu") do
54
+ options.map do |key, value|
55
+ if value.nil?
56
+ content_tag(:li, key)
57
+ elsif value.is_a?(Hash)
58
+ content_tag(:li, class: "is-dropdown-submenu-parent") do
59
+ key + dropdown_submenu(value)
60
+ end
61
+ end
62
+ end.join.html_safe
63
+ end
64
+ end
65
+
66
+ def filter_link_label(filter)
67
+ link_to(i18n_filter_label(filter), href: "#")
68
+ end
69
+
70
+ def filter_link_value(filter, value)
71
+ link_to(i18n_filter_value(filter, value), query_params_with(filter => value))
72
+ end
73
+
74
+ def i18n_filter_label(filter)
75
+ t("decidim.admin.filters.#{filter}.label")
76
+ end
77
+
78
+ def i18n_filter_value(filter, value)
79
+ if I18n.exists?("decidim.admin.filters.#{filter}.values.#{value}")
80
+ t(value, scope: "decidim.admin.filters.#{filter}.values")
81
+ else
82
+ find_dynamic_translation(filter, value)
83
+ end
84
+ end
85
+
86
+ def applied_filters_hidden_field_tags
87
+ html = []
88
+ html += ransack_params.slice(*filters, *extra_filters).map do |filter, value|
89
+ hidden_field_tag("q[#{filter}]", value)
90
+ end
91
+ html += query_params.slice(*extra_allowed_params).map do |filter, value|
92
+ hidden_field_tag(filter, value)
93
+ end
94
+ html.join.html_safe
95
+ end
96
+
97
+ def applied_filters_tags
98
+ ransack_params.slice(*filters).map do |filter, value|
99
+ applied_filter_tag(filter, value)
100
+ end.join.html_safe
101
+ end
102
+
103
+ def applied_filter_tag(filter, value)
104
+ content_tag(:span, class: "label secondary") do
105
+ concat "#{i18n_filter_label(filter)}: "
106
+ concat i18n_filter_value(filter, value)
107
+ concat remove_filter_icon_link(filter)
108
+ end
109
+ end
110
+
111
+ def remove_filter_icon_link(filter)
112
+ icon_link_to(
113
+ "circle-x",
114
+ url_for(query_params_without(filter)),
115
+ t("decidim.admin.actions.cancel"),
116
+ class: "action-icon--remove"
117
+ )
118
+ end
119
+ end
120
+ end
121
+ end