decidim-admin 0.1.0 → 0.2.0

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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -0
  3. data/app/assets/stylesheets/decidim/admin/application.scss.erb +6 -0
  4. data/app/assets/stylesheets/decidim/admin/extra/_action-icon.scss +1 -0
  5. data/app/assets/stylesheets/decidim/admin/extra/_dropdown_inverted.scss +38 -0
  6. data/app/commands/decidim/admin/activate_participatory_process_step.rb +1 -0
  7. data/app/commands/decidim/admin/copy_participatory_process.rb +113 -0
  8. data/app/commands/decidim/admin/create_attachment.rb +1 -0
  9. data/app/commands/decidim/admin/create_category.rb +1 -0
  10. data/app/commands/decidim/admin/create_feature.rb +1 -0
  11. data/app/commands/decidim/admin/create_newsletter.rb +1 -0
  12. data/app/commands/decidim/admin/create_participatory_process.rb +1 -0
  13. data/app/commands/decidim/admin/create_participatory_process_admin.rb +1 -0
  14. data/app/commands/decidim/admin/create_participatory_process_group.rb +1 -0
  15. data/app/commands/decidim/admin/create_participatory_process_step.rb +1 -0
  16. data/app/commands/decidim/admin/create_scope.rb +1 -0
  17. data/app/commands/decidim/admin/create_static_page.rb +1 -0
  18. data/app/commands/decidim/admin/deactivate_participatory_process_step.rb +1 -0
  19. data/app/commands/decidim/admin/deliver_newsletter.rb +1 -0
  20. data/app/commands/decidim/admin/destroy_category.rb +1 -0
  21. data/app/commands/decidim/admin/destroy_feature.rb +1 -0
  22. data/app/commands/decidim/admin/destroy_participatory_process_step.rb +1 -0
  23. data/app/commands/decidim/admin/hide_resource.rb +1 -0
  24. data/app/commands/decidim/admin/publish_participatory_process.rb +1 -0
  25. data/app/commands/decidim/admin/reject_user_group.rb +33 -0
  26. data/app/commands/decidim/admin/reorder_participatory_process_steps.rb +1 -0
  27. data/app/commands/decidim/admin/unpublish_participatory_process.rb +1 -0
  28. data/app/commands/decidim/admin/unreport_resource.rb +1 -0
  29. data/app/commands/decidim/admin/update_attachment.rb +1 -0
  30. data/app/commands/decidim/admin/update_category.rb +1 -0
  31. data/app/commands/decidim/admin/update_feature.rb +1 -0
  32. data/app/commands/decidim/admin/update_feature_permissions.rb +1 -0
  33. data/app/commands/decidim/admin/update_newsletter.rb +1 -0
  34. data/app/commands/decidim/admin/update_organization.rb +1 -0
  35. data/app/commands/decidim/admin/update_participatory_process.rb +1 -0
  36. data/app/commands/decidim/admin/update_participatory_process_admin.rb +1 -0
  37. data/app/commands/decidim/admin/update_participatory_process_group.rb +1 -0
  38. data/app/commands/decidim/admin/update_participatory_process_step.rb +1 -0
  39. data/app/commands/decidim/admin/update_scope.rb +1 -0
  40. data/app/commands/decidim/admin/update_static_page.rb +1 -0
  41. data/app/commands/decidim/admin/update_user_groups.rb +44 -0
  42. data/app/commands/decidim/admin/verify_user_group.rb +32 -0
  43. data/app/constraints/decidim/admin/organization_dashboard_constraint.rb +1 -0
  44. data/app/controllers/decidim/admin/application_controller.rb +2 -2
  45. data/app/controllers/decidim/admin/categories_controller.rb +1 -0
  46. data/app/controllers/decidim/admin/concerns/has_attachments.rb +1 -0
  47. data/app/controllers/decidim/admin/concerns/participatory_process_admin.rb +1 -0
  48. data/app/controllers/decidim/admin/dashboard_controller.rb +1 -0
  49. data/app/controllers/decidim/admin/exports_controller.rb +39 -0
  50. data/app/controllers/decidim/admin/feature_permissions_controller.rb +1 -0
  51. data/app/controllers/decidim/admin/features/base_controller.rb +3 -0
  52. data/app/controllers/decidim/admin/features_controller.rb +1 -0
  53. data/app/controllers/decidim/admin/moderations_controller.rb +1 -0
  54. data/app/controllers/decidim/admin/organization_controller.rb +1 -0
  55. data/app/controllers/decidim/admin/participatory_process_attachments_controller.rb +1 -0
  56. data/app/controllers/decidim/admin/participatory_process_copies_controller.rb +47 -0
  57. data/app/controllers/decidim/admin/participatory_process_groups_controller.rb +1 -0
  58. data/app/controllers/decidim/admin/participatory_process_publications_controller.rb +1 -0
  59. data/app/controllers/decidim/admin/participatory_process_step_activations_controller.rb +1 -0
  60. data/app/controllers/decidim/admin/participatory_process_step_ordering_controller.rb +1 -0
  61. data/app/controllers/decidim/admin/participatory_process_steps_controller.rb +1 -0
  62. data/app/controllers/decidim/admin/participatory_process_user_roles_controller.rb +1 -0
  63. data/app/controllers/decidim/admin/participatory_processes_controller.rb +5 -0
  64. data/app/controllers/decidim/admin/scopes_controller.rb +1 -0
  65. data/app/controllers/decidim/admin/static_pages_controller.rb +1 -0
  66. data/app/controllers/decidim/admin/user_groups_controller.rb +32 -4
  67. data/app/controllers/decidim/admin/users_controller.rb +1 -0
  68. data/app/forms/decidim/admin/attachment_form.rb +1 -0
  69. data/app/forms/decidim/admin/category_form.rb +1 -0
  70. data/app/forms/decidim/admin/feature_form.rb +1 -0
  71. data/app/forms/decidim/admin/newsletter_form.rb +1 -0
  72. data/app/forms/decidim/admin/organization_form.rb +1 -0
  73. data/app/forms/decidim/admin/participatory_process_copy_form.rb +34 -0
  74. data/app/forms/decidim/admin/participatory_process_form.rb +1 -0
  75. data/app/forms/decidim/admin/participatory_process_group_form.rb +1 -0
  76. data/app/forms/decidim/admin/participatory_process_step_form.rb +1 -0
  77. data/app/forms/decidim/admin/participatory_process_user_role_form.rb +1 -0
  78. data/app/forms/decidim/admin/permission_form.rb +1 -0
  79. data/app/forms/decidim/admin/permissions_form.rb +1 -0
  80. data/app/forms/decidim/admin/scope_form.rb +1 -0
  81. data/app/forms/decidim/admin/static_page_form.rb +1 -0
  82. data/app/helpers/decidim/admin/application_helper.rb +1 -8
  83. data/app/helpers/decidim/admin/attributes_display_helper.rb +1 -0
  84. data/app/helpers/decidim/admin/exports_helper.rb +18 -0
  85. data/app/helpers/decidim/admin/feature_settings_helper.rb +1 -0
  86. data/app/helpers/decidim/admin/icon_link_helper.rb +1 -0
  87. data/app/helpers/decidim/admin/menu_helper.rb +18 -0
  88. data/app/helpers/decidim/admin/process_groups_for_select_helper.rb +1 -0
  89. data/app/helpers/decidim/admin/processes_for_select_helper.rb +1 -0
  90. data/app/jobs/decidim/admin/application_job.rb +1 -0
  91. data/app/jobs/decidim/admin/newsletter_delivery_job.rb +1 -0
  92. data/app/jobs/decidim/admin/newsletter_job.rb +1 -0
  93. data/app/mailers/decidim/admin/application_mailer.rb +1 -0
  94. data/app/models/decidim/admin/abilities/admin_user.rb +2 -1
  95. data/app/models/decidim/admin/abilities/base.rb +1 -0
  96. data/app/models/decidim/admin/abilities/collaborator_user.rb +1 -0
  97. data/app/models/decidim/admin/abilities/participatory_process_admin.rb +1 -0
  98. data/app/models/decidim/admin/application_record.rb +1 -0
  99. data/app/models/decidim/admin/participatory_process_user_role.rb +3 -2
  100. data/app/queries/decidim/admin/manageable_participatory_processes_for_user.rb +1 -0
  101. data/app/queries/decidim/admin/process_admin_roles_for_process.rb +1 -0
  102. data/app/queries/decidim/admin/process_admins.rb +1 -0
  103. data/app/queries/decidim/admin/user_groups_evaluation.rb +54 -0
  104. data/app/views/decidim/admin/exports/_dropdown.html.erb +10 -0
  105. data/app/views/decidim/admin/participatory_process_copies/_form.html.erb +38 -0
  106. data/app/views/decidim/admin/participatory_process_copies/new.html.erb +7 -0
  107. data/app/views/decidim/admin/participatory_processes/index.html.erb +2 -0
  108. data/app/views/decidim/admin/scopes/new.html.erb +0 -1
  109. data/app/views/decidim/admin/user_groups/index.html.erb +67 -5
  110. data/app/views/layouts/decidim/admin/_application.html.erb +1 -1
  111. data/app/views/layouts/decidim/admin/_title_bar.html.erb +1 -1
  112. data/config/i18n-tasks.yml +6 -6
  113. data/config/locales/ca.yml +36 -2
  114. data/config/locales/en.yml +36 -2
  115. data/config/locales/es.yml +37 -3
  116. data/config/locales/eu.yml +41 -3
  117. data/config/locales/fi.yml +0 -3
  118. data/config/locales/fr.yml +0 -3
  119. data/config/routes.rb +4 -0
  120. data/db/seeds.rb +1 -0
  121. data/lib/decidim/admin.rb +1 -0
  122. data/lib/decidim/admin/engine.rb +51 -0
  123. data/lib/decidim/admin/features.rb +1 -0
  124. data/lib/decidim/admin/test/factories.rb +1 -0
  125. data/lib/decidim/admin/test/manage_attachments_examples.rb +1 -0
  126. metadata +21 -24
  127. data/app/assets/stylesheets/decidim/admin/application.scss +0 -2
  128. data/app/assets/stylesheets/decidim/admin/extra/_sidebar.scss +0 -102
  129. data/app/views/layouts/decidim/admin/_language_chooser.html.erb +0 -14
  130. data/app/views/layouts/decidim/admin/_main_nav.html.erb +0 -11
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ # A command with all the business logic when updating a usergroup.
6
+ class UpdateUserGroups < Rectify::Command
7
+ # Public: Initializes the command.
8
+ #
9
+ # scope - The Scope to update
10
+ # form - A form object with the params.
11
+ def initialize(user_group)
12
+ @user_group = user_group
13
+ @form = form
14
+ end
15
+
16
+ # Executes the command. Broadcasts these events:
17
+ #
18
+ # - :ok when everything is valid.
19
+ # - :invalid if the form wasn't valid and we couldn't proceed.
20
+ #
21
+ # Returns nothing.
22
+ def call
23
+ return broadcast(:invalid) if form.invalid?
24
+
25
+ update_scope
26
+ broadcast(:ok)
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :form
32
+
33
+ def update_scope
34
+ @scope.update_attributes!(attributes)
35
+ end
36
+
37
+ def attributes
38
+ {
39
+ name: form.name
40
+ }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ # A command with all the business logic when verifying a user_group.
6
+ class VerifyUserGroup < Rectify::Command
7
+ # Public: Initializes the command.
8
+ #
9
+ # user_group - The user_group to verify
10
+ def initialize(user_group)
11
+ @user_group = user_group
12
+ end
13
+
14
+ # Executes the command. Broadcasts these events:
15
+ #
16
+ # - :ok when everything is valid.
17
+ #
18
+ # Returns nothing.
19
+ def call
20
+ return broadcast(:invalid) unless @user_group.valid?
21
+ verify_user_group
22
+ broadcast(:ok)
23
+ end
24
+
25
+ private
26
+
27
+ def verify_user_group
28
+ @user_group.update_attributes(verified_at: Time.current, rejected_at: nil)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Decidim
3
4
  module Admin
4
5
  # A Rails routes constraint to only allow access to an Organization admin to
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Decidim
3
4
  module Admin
4
5
  # The main application controller that inherits from Rails.
@@ -14,13 +15,12 @@ module Decidim
14
15
  helper Decidim::Admin::ProcessGroupsForSelectHelper
15
16
  helper Decidim::Admin::ProcessesForSelectHelper
16
17
  helper Decidim::Admin::IconLinkHelper
18
+ helper Decidim::Admin::MenuHelper
17
19
  helper Decidim::DecidimFormHelper
18
20
  helper Decidim::ReplaceButtonsHelper
19
21
  helper Decidim::OrganizationScopesHelper
20
22
  helper Decidim::TranslationsHelper
21
23
 
22
- helper Decidim::LanguageChooserHelper
23
-
24
24
  protect_from_forgery with: :exception, prepend: true
25
25
 
26
26
  def user_not_authorized_path
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Decidim
3
4
  module Admin
4
5
  module Concerns
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Decidim
3
4
  module Admin
4
5
  module Concerns
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ # This controller allows admins to manage proposals in a participatory process.
6
+ class ExportsController < ApplicationController
7
+ include Concerns::ParticipatoryProcessAdmin
8
+
9
+ def create
10
+ authorize! :manage, feature
11
+ name = params[:id]
12
+
13
+ ExportJob.perform_later(current_user, feature, name, params[:format] || default_format)
14
+
15
+ flash[:notice] = t("decidim.admin.exports.notice")
16
+
17
+ redirect_back(fallback_location: fallback_location)
18
+ end
19
+
20
+ private
21
+
22
+ def fallback_location
23
+ send(
24
+ "decidim_admin_#{feature.manifest.name}_path",
25
+ feature_id: feature.id,
26
+ participatory_process_id: participatory_process.id
27
+ )
28
+ end
29
+
30
+ def default_format
31
+ "json"
32
+ end
33
+
34
+ def feature
35
+ @feature ||= participatory_process.features.find(params[:feature_id])
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Decidim
3
4
  module Admin
4
5
  module Features
@@ -10,6 +11,8 @@ module Decidim
10
11
  include NeedsParticipatoryProcess
11
12
  include FeatureSettings
12
13
 
14
+ helper Decidim::Admin::ExportsHelper
15
+
13
16
  helper_method :current_feature,
14
17
  :current_participatory_process,
15
18
  :parent_path
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Decidim
3
4
  module Admin
4
5
  # This controller allows admins to manage moderations in a participatory process.
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "decidim/admin/application_controller"
4
+
5
+ module Decidim
6
+ module Admin
7
+ # Controller that allows managing participatory processes.
8
+ #
9
+ class ParticipatoryProcessCopiesController < ApplicationController
10
+ include Concerns::ParticipatoryProcessAdmin
11
+
12
+ helper_method :participatory_process
13
+
14
+ def new
15
+ authorize! :new, Decidim::ParticipatoryProcess
16
+ @form = form(ParticipatoryProcessCopyForm).from_model(participatory_process)
17
+ end
18
+
19
+ def create
20
+ authorize! :create, Decidim::ParticipatoryProcess
21
+ @form = form(ParticipatoryProcessCopyForm).from_params(params)
22
+
23
+ CopyParticipatoryProcess.call(@form, participatory_process) do
24
+ on(:ok) do
25
+ flash[:notice] = I18n.t("participatory_processes_copies.create.success", scope: "decidim.admin")
26
+ redirect_to participatory_processes_path
27
+ end
28
+
29
+ on(:invalid) do
30
+ flash.now[:alert] = I18n.t("participatory_processes_copies.create.error", scope: "decidim.admin")
31
+ render :new
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def collection
39
+ @collection ||= ManageableParticipatoryProcessesForUser.for(current_user)
40
+ end
41
+
42
+ def participatory_process
43
+ @participatory_process ||= collection.find(params[:participatory_process_id])
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -76,6 +77,10 @@ module Decidim
76
77
  redirect_to participatory_processes_path
77
78
  end
78
79
 
80
+ def copy
81
+ @participatory_process ||= collection.find(params[:id])
82
+ end
83
+
79
84
  private
80
85
 
81
86
  attr_reader :participatory_process
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -9,17 +9,45 @@ module Decidim
9
9
 
10
10
  def index
11
11
  authorize! :index, UserGroup
12
- @user_groups = collection.page(params[:page]).per(15)
12
+ @query = params[:q]
13
+ @state = params[:state]
14
+
15
+ @user_groups = Decidim::Admin::UserGroupsEvaluation.for(collection, @query, @state)
16
+ .page(params[:page]).per(15)
13
17
  end
14
18
 
15
19
  def verify
16
20
  @user_group = collection.find(params[:id])
17
21
  authorize! :verify, @user_group
18
22
 
19
- @user_group.verify!
23
+ VerifyUserGroup.call(@user_group) do
24
+ on(:ok) do
25
+ flash[:notice] = I18n.t("user_group.verify.success", scope: "decidim.admin")
26
+ redirect_back(fallback_location: decidim_admin.user_groups_path)
27
+ end
28
+
29
+ on(:invalid) do
30
+ flash[:alert] = I18n.t("user_group.verify.invalid", scope: "decidim.admin")
31
+ redirect_back(fallback_location: decidim_admin.user_groups_path)
32
+ end
33
+ end
34
+ end
35
+
36
+ def reject
37
+ @user_group = collection.find(params[:id])
38
+ authorize! :reject, @user_group
39
+
40
+ RejectUserGroup.call(@user_group) do
41
+ on(:ok) do
42
+ flash[:notice] = I18n.t("user_group.reject.success", scope: "decidim.admin")
43
+ redirect_back(fallback_location: decidim_admin.user_groups_path)
44
+ end
20
45
 
21
- flash[:notice] = I18n.t("user_groups.verify.success", scope: "decidim.admin")
22
- redirect_to decidim_admin.user_groups_path
46
+ on(:invalid) do
47
+ flash[:alert] = I18n.t("user_group.reject.invalid", scope: "decidim.admin")
48
+ redirect_back(fallback_location: decidim_admin.user_groups_path)
49
+ end
50
+ end
23
51
  end
24
52
 
25
53
  private
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_dependency "decidim/admin/application_controller"
3
4
 
4
5
  module Decidim
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Decidim
3
4
  module Admin
4
5
  # A form object used to create attachments in a participatory process.
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Decidim
3
4
  module Admin
4
5
  # A form object used to create categories from the admin dashboard.
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Decidim
3
4
  module Admin
4
5
  # A form object used to attach a feature to a participatory process from the
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Decidim
3
4
  module Admin
4
5
  # The form that validates the data to construct a valid Newsletter.
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Decidim
3
4
  module Admin
4
5
  # A form object used to update the current organization from the admin
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ # A form object used to copy a participatory processes from the admin
6
+ # dashboard.
7
+ #
8
+ class ParticipatoryProcessCopyForm < Form
9
+ include TranslatableAttributes
10
+
11
+ translatable_attribute :title, String
12
+
13
+ attribute :slug, String
14
+ attribute :copy_steps, Boolean
15
+ attribute :copy_categories, Boolean
16
+ attribute :copy_process_users, Boolean
17
+ attribute :copy_moderations, Boolean
18
+ attribute :copy_pages, Boolean
19
+ attribute :copy_features, Boolean
20
+
21
+ validates :slug, presence: true
22
+ validates :title, translatable_presence: true
23
+ validate :slug, :slug_uniqueness
24
+
25
+ private
26
+
27
+ def slug_uniqueness
28
+ return unless current_organization.participatory_processes.where(slug: slug).where.not(id: id).any?
29
+
30
+ errors.add(:slug, :taken)
31
+ end
32
+ end
33
+ end
34
+ end