decidim-admin 0.8.4 → 0.9.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 (94) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +1 -1
  3. data/app/assets/javascripts/decidim/admin/application.js.es6 +5 -2
  4. data/app/assets/stylesheets/decidim/admin/_decidim.scss +2 -5
  5. data/app/assets/stylesheets/decidim/admin/extra/_action-icon.scss +4 -4
  6. data/app/assets/stylesheets/decidim/admin/extra/_cards.scss +3 -2
  7. data/app/assets/stylesheets/decidim/admin/extra/_categories.scss +1 -1
  8. data/app/assets/stylesheets/decidim/admin/extra/_dropdown_inverted.scss +8 -1
  9. data/app/assets/stylesheets/decidim/admin/extra/_label-required.scss +1 -1
  10. data/app/assets/stylesheets/decidim/admin/extra/_login.scss +1 -0
  11. data/app/assets/stylesheets/decidim/admin/extra/_quill.scss +2 -2
  12. data/app/assets/stylesheets/decidim/admin/extra/_select_multiple.scss +1 -1
  13. data/app/assets/stylesheets/decidim/admin/extra/_sort.scss +5 -4
  14. data/app/assets/stylesheets/decidim/admin/extra/_title_bar.scss +17 -7
  15. data/app/assets/stylesheets/decidim/admin/modules/_action-icon.scss +2 -2
  16. data/app/assets/stylesheets/decidim/admin/modules/_buttons.scss +6 -2
  17. data/app/assets/stylesheets/decidim/admin/modules/_callouts.scss +3 -0
  18. data/app/assets/stylesheets/decidim/admin/modules/_cards.scss +12 -0
  19. data/app/assets/stylesheets/decidim/admin/modules/_char-counter.scss +2 -0
  20. data/app/assets/stylesheets/decidim/admin/modules/_forms.scss +5 -1
  21. data/app/assets/stylesheets/decidim/admin/modules/_icons.scss +1 -0
  22. data/app/assets/stylesheets/decidim/admin/modules/_layout.scss +9 -1
  23. data/app/assets/stylesheets/decidim/admin/modules/_main-nav.scss +5 -0
  24. data/app/assets/stylesheets/decidim/admin/modules/_process-header.scss +1 -0
  25. data/app/assets/stylesheets/decidim/admin/modules/_secondary-nav.scss +10 -5
  26. data/app/assets/stylesheets/decidim/admin/modules/_table-list.scss +7 -1
  27. data/app/assets/stylesheets/decidim/admin/modules/_tabs.scss +7 -0
  28. data/app/assets/stylesheets/decidim/admin/modules/_title-bar.scss +3 -0
  29. data/app/assets/stylesheets/decidim/admin/modules/_user-login.scss +2 -0
  30. data/app/assets/stylesheets/decidim/admin/plugins/_foundation-datepicker.scss +36 -36
  31. data/app/assets/stylesheets/decidim/admin/utils/_fontface.scss +22 -20
  32. data/app/assets/stylesheets/decidim/admin/utils/_helpers.scss +6 -6
  33. data/app/assets/stylesheets/decidim/admin/utils/_keyframes.scss +6 -6
  34. data/app/assets/stylesheets/decidim/admin/utils/_mixins.scss +2 -3
  35. data/app/assets/stylesheets/decidim/admin/utils/_settings.scss +406 -129
  36. data/app/assets/stylesheets/decidim/admin/utils/_toggle-expand.scss +1 -0
  37. data/app/commands/decidim/admin/create_attachment.rb +13 -3
  38. data/app/commands/decidim/admin/create_feature.rb +1 -0
  39. data/app/commands/decidim/admin/create_managed_user.rb +20 -10
  40. data/app/commands/decidim/admin/impersonate_managed_user.rb +7 -9
  41. data/app/commands/decidim/admin/officialize_user.rb +47 -0
  42. data/app/commands/decidim/admin/unofficialize_user.rb +35 -0
  43. data/app/commands/decidim/admin/update_organization_appearance.rb +32 -5
  44. data/app/controllers/decidim/admin/categories_controller.rb +1 -1
  45. data/app/controllers/decidim/admin/features/base_controller.rb +1 -1
  46. data/app/controllers/decidim/admin/features_controller.rb +7 -0
  47. data/app/controllers/decidim/admin/managed_users/impersonations_controller.rb +1 -1
  48. data/app/controllers/decidim/admin/managed_users_controller.rb +23 -9
  49. data/app/controllers/decidim/admin/newsletters_controller.rb +2 -2
  50. data/app/controllers/decidim/admin/officializations_controller.rb +64 -0
  51. data/app/events/decidim/attachment_created_event.rb +25 -0
  52. data/app/events/decidim/feature_published_event.rb +27 -0
  53. data/app/events/decidim/participatory_process_step_activated_event.rb +31 -0
  54. data/app/forms/decidim/admin/managed_user_form.rb +0 -11
  55. data/app/forms/decidim/admin/officialization_form.rb +30 -0
  56. data/app/forms/decidim/admin/organization_appearance_form.rb +46 -1
  57. data/app/helpers/decidim/admin/application_helper.rb +1 -3
  58. data/app/models/decidim/admin/abilities/admin_ability.rb +1 -0
  59. data/app/models/decidim/admin/abilities/participatory_process_moderator_ability.rb +1 -1
  60. data/app/queries/decidim/admin/user_groups_evaluation.rb +9 -8
  61. data/app/queries/decidim/admin/users_officialization.rb +53 -0
  62. data/app/views/decidim/admin/managed_users/new.html.erb +5 -5
  63. data/app/views/decidim/admin/officializations/index.html.erb +82 -0
  64. data/app/views/decidim/admin/officializations/new.html.erb +18 -0
  65. data/app/views/decidim/admin/organization_appearance/_form.html.erb +60 -0
  66. data/app/views/layouts/decidim/admin/_title_bar.html.erb +1 -1
  67. data/app/views/layouts/decidim/admin/users.html.erb +5 -0
  68. data/config/locales/ca.yml +44 -2
  69. data/config/locales/en.yml +44 -1
  70. data/config/locales/es.yml +47 -5
  71. data/config/locales/eu.yml +46 -3
  72. data/config/locales/fi.yml +55 -12
  73. data/config/locales/fr.yml +45 -2
  74. data/config/locales/gl.yml +514 -0
  75. data/config/locales/it.yml +47 -4
  76. data/config/locales/nl.yml +47 -4
  77. data/config/locales/pl.yml +44 -1
  78. data/config/locales/pt-BR.yml +514 -0
  79. data/config/locales/pt.yml +106 -63
  80. data/config/locales/ru.yml +36 -6
  81. data/config/locales/sv.yml +514 -0
  82. data/config/locales/uk.yml +36 -8
  83. data/config/routes.rb +2 -0
  84. data/db/migrate/20171219154507_add_officialization_to_users.rb +10 -0
  85. data/lib/decidim/admin/engine.rb +1 -2
  86. data/lib/decidim/admin/test/manage_feature_permissions_examples.rb +0 -1
  87. data/lib/decidim/admin/test/manage_moderations_examples.rb +64 -0
  88. data/lib/decidim/admin/test.rb +5 -0
  89. data/lib/decidim/admin/version.rb +1 -1
  90. data/vendor/assets/javascripts/moment.min.js +1 -7
  91. metadata +37 -38
  92. data/app/assets/javascripts/decidim/admin/scopes.js.es6 +0 -20
  93. data/app/assets/javascripts/decidim/admin/select2.js.es6 +0 -8
  94. data/app/assets/stylesheets/decidim/admin/plugins/_select2.scss +0 -27
@@ -0,0 +1,25 @@
1
+ # frozen-string_literal: true
2
+
3
+ module Decidim
4
+ class AttachmentCreatedEvent < Decidim::Events::SimpleEvent
5
+ i18n_attributes :attached_to_url
6
+
7
+ def resource_path
8
+ @resource.url
9
+ end
10
+
11
+ def resource_url
12
+ @resource.url
13
+ end
14
+
15
+ private
16
+
17
+ def attached_to_url
18
+ resource_locator.url
19
+ end
20
+
21
+ def resource
22
+ @resource.attached_to
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ # frozen-string_literal: true
2
+
3
+ module Decidim
4
+ class FeaturePublishedEvent < Decidim::Events::SimpleEvent
5
+ include Decidim::FeaturePathHelper
6
+
7
+ i18n_attributes :participatory_space_title
8
+
9
+ private
10
+
11
+ def resource_path
12
+ @resource_path ||= main_feature_path(resource)
13
+ end
14
+
15
+ def resource_url
16
+ @resource_url ||= main_feature_url(resource)
17
+ end
18
+
19
+ def participatory_space_title
20
+ resource.participatory_space.title[I18n.locale.to_s]
21
+ end
22
+
23
+ def resource_title
24
+ @resource_title ||= resource.name[I18n.locale.to_s]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen-string_literal: true
2
+
3
+ module Decidim
4
+ class ParticipatoryProcessStepActivatedEvent < Decidim::Events::SimpleEvent
5
+ include Rails.application.routes.mounted_helpers
6
+
7
+ i18n_attributes :participatory_space_title
8
+
9
+ private
10
+
11
+ def participatory_space
12
+ resource.participatory_process
13
+ end
14
+
15
+ def resource_path
16
+ @resource_path ||= decidim_participatory_processes.participatory_process_participatory_process_steps_path(participatory_space)
17
+ end
18
+
19
+ def resource_url
20
+ @resource_url ||= decidim_participatory_processes
21
+ .participatory_process_participatory_process_steps_url(
22
+ resource.participatory_process,
23
+ host: resource.participatory_process.organization.host
24
+ )
25
+ end
26
+
27
+ def participatory_space_title
28
+ participatory_space.title[I18n.locale.to_s]
29
+ end
30
+ end
31
+ end
@@ -10,7 +10,6 @@ module Decidim
10
10
  attribute :name, String
11
11
 
12
12
  validates :name, presence: true
13
- validate :authorization_uniqueness
14
13
 
15
14
  def initialize(attributes)
16
15
  extend(Virtus.model)
@@ -20,16 +19,6 @@ module Decidim
20
19
 
21
20
  super
22
21
  end
23
-
24
- private
25
-
26
- def authorization_uniqueness
27
- errors.add :authorization, :invalid if Authorization.where(
28
- user: current_organization.users,
29
- name: authorization.handler_name,
30
- unique_id: authorization.unique_id
31
- ).exists?
32
- end
33
22
  end
34
23
  end
35
24
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ # A form object used to officialize users from the admin dashboard.
6
+ class OfficializationForm < Form
7
+ include TranslatableAttributes
8
+
9
+ translatable_attribute :officialized_as, String
10
+
11
+ attribute :user_id, Integer
12
+
13
+ validates :officialized_as, length: { maximum: 300 }
14
+
15
+ validates :user, presence: true, if: ->(form) { form.user_id.present? }
16
+
17
+ def map_model(user)
18
+ self.officialized_as = user.officialized_as
19
+ self.user_id = user.id
20
+ end
21
+
22
+ def user
23
+ @user ||= Decidim::User.find_by(
24
+ id: user_id,
25
+ organization: current_organization
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
@@ -24,18 +24,63 @@ module Decidim
24
24
  attribute :show_statistics, Boolean
25
25
  attribute :header_snippets, String
26
26
  attribute :cta_button_path, String
27
+ attribute :highlighted_content_banner_enabled, Boolean, default: false
28
+ attribute :highlighted_content_banner_action_url, String
29
+ attribute :highlighted_content_banner_image
30
+ attribute :remove_highlighted_content_banner_image
31
+ attribute :enable_omnipresent_banner, Boolean, default: false
32
+ attribute :omnipresent_banner_url, String
27
33
 
28
34
  translatable_attribute :cta_button_text, String
29
35
  translatable_attribute :description, String
30
36
  translatable_attribute :welcome_text, String
37
+ translatable_attribute :highlighted_content_banner_title, String
38
+ translatable_attribute :highlighted_content_banner_short_description, String
39
+ translatable_attribute :highlighted_content_banner_action_title, String
40
+ translatable_attribute :highlighted_content_banner_action_subtitle, String
41
+ translatable_attribute :omnipresent_banner_title, String
42
+ translatable_attribute :omnipresent_banner_short_description, String
31
43
 
32
- validates :cta_button_path, format: { with: %r{\A[a-zA-Z]+[a-zA-Z0-9\-/]+\z} }, allow_blank: true
44
+ validates :cta_button_path, format: { with: %r{\A[a-zA-Z]+[a-zA-Z0-9\-\_/]+\z} }, allow_blank: true
33
45
  validates :official_img_header,
34
46
  :official_img_footer,
35
47
  :homepage_image,
36
48
  :logo,
37
49
  file_size: { less_than_or_equal_to: ->(_record) { Decidim.maximum_attachment_size } },
38
50
  file_content_type: { allow: ["image/jpeg", "image/png"] }
51
+
52
+ validates :highlighted_content_banner_action_url, presence: true, if: :highlighted_content_banner_enabled?
53
+ validates :highlighted_content_banner_image,
54
+ presence: true,
55
+ file_size: { less_than_or_equal_to: ->(_record) { Decidim.maximum_attachment_size } },
56
+ file_content_type: { allow: ["image/jpeg", "image/png"] },
57
+ if: :highlighted_content_banner_enabled?
58
+
59
+ validates :highlighted_content_banner_title,
60
+ translatable_presence: true,
61
+ if: :highlighted_content_banner_enabled?
62
+
63
+ validates :highlighted_content_banner_short_description,
64
+ translatable_presence: true,
65
+ if: :highlighted_content_banner_enabled?
66
+
67
+ validates :highlighted_content_banner_action_title,
68
+ translatable_presence: true,
69
+ if: :highlighted_content_banner_enabled?
70
+
71
+ validates :omnipresent_banner_url, presence: true, if: :enable_omnipresent_banner?
72
+ validates :omnipresent_banner_title, translatable_presence: true, if: :enable_omnipresent_banner?
73
+ validates :omnipresent_banner_short_description, translatable_presence: true, if: :enable_omnipresent_banner?
74
+
75
+ private
76
+
77
+ def highlighted_content_banner_enabled?
78
+ highlighted_content_banner_enabled
79
+ end
80
+
81
+ def enable_omnipresent_banner?
82
+ enable_omnipresent_banner
83
+ end
39
84
  end
40
85
  end
41
86
  end
@@ -17,9 +17,7 @@ module Decidim
17
17
  end
18
18
 
19
19
  def foundation_datepicker_locale_tag
20
- if I18n.locale != :en
21
- javascript_include_tag "datepicker-locales/foundation-datepicker.#{I18n.locale}.js"
22
- end
20
+ javascript_include_tag "datepicker-locales/foundation-datepicker.#{I18n.locale}.js" if I18n.locale != :en
23
21
  end
24
22
  end
25
23
  end
@@ -45,6 +45,7 @@ module Decidim
45
45
  end
46
46
 
47
47
  can [:index, :verify, :reject], UserGroup
48
+ can [:index, :new, :create, :destroy], :officializations
48
49
 
49
50
  can :index, :authorization_workflows
50
51
  can [:index, :update], Authorization
@@ -14,7 +14,7 @@ module Decidim
14
14
  end
15
15
 
16
16
  can :manage, Moderation do |moderation|
17
- can_manage_process?(moderation.participatory_process)
17
+ can_manage_process?(moderation.participatory_space)
18
18
  end
19
19
  end
20
20
  end
@@ -2,26 +2,29 @@
2
2
 
3
3
  module Decidim
4
4
  module Admin
5
- # A class used to find the ParticipatoryProcesses that the given user can
6
- # manage.
5
+ # A class used to find the UserGroup's by their evaluation state.
7
6
  class UserGroupsEvaluation < Rectify::Query
8
7
  # Syntactic sugar to initialize the class and return the queried objects.
9
8
  #
10
- # user - a User Group that needs to be listed.
9
+ # user_groups - the initial User Group relation that needs to be filtered.
10
+ # q - query to filter user group names
11
+ # state - evaluation state to be used as a filter
11
12
  def self.for(user_groups, q = nil, state = nil)
12
13
  new(user_groups, q, state).query
13
14
  end
14
15
 
15
16
  # Initializes the class.
16
17
  #
17
- # user_group - a User groups that need to be lsited
18
+ # user_groups - the User groups that need to be filtered
19
+ # q - query to filter user group names
20
+ # state - evaluation state to be used as a filter
18
21
  def initialize(user_groups, q = nil, state = nil)
19
22
  @user_groups = user_groups
20
23
  @q = q
21
24
  @state = state
22
25
  end
23
26
 
24
- # List the User groups by the diferents filters.
27
+ # List the User groups by the diferent filters.
25
28
  def query
26
29
  @user_groups = filter_by_search(@user_groups)
27
30
  @user_groups = filter_by_state(@user_groups)
@@ -30,11 +33,9 @@ module Decidim
30
33
 
31
34
  private
32
35
 
33
- attr_reader :user_group
34
-
35
36
  def filter_by_search(user_groups)
36
37
  return user_groups if @q.blank?
37
- user_groups.where("LOWER(name) LIKE LOWER('%#{@q}%')")
38
+ user_groups.where("LOWER(name) LIKE LOWER(?)", "%#{@q}%")
38
39
  end
39
40
 
40
41
  def filter_by_state(user_groups)
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ # A class used to filter User's by their officialization state.
6
+ class UsersOfficialization < Rectify::Query
7
+ # Syntactic sugar to initialize the class and return the queried objects.
8
+ #
9
+ # q - query to filter user group names
10
+ # state - evaluation state to be used as a filter
11
+ def self.for(q = nil, state = nil)
12
+ new(q, state).query
13
+ end
14
+
15
+ # Initializes the class.
16
+ #
17
+ # q - query to filter user group names
18
+ # state - officialization state to be used as a filter
19
+ def initialize(q = nil, state = nil)
20
+ @q = q
21
+ @state = state
22
+ end
23
+
24
+ # List the User groups by the diferents filters.
25
+ def query
26
+ users = Decidim::User.all
27
+ users = filter_by_search(users)
28
+ users = filter_by_state(users)
29
+ users
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :q, :state
35
+
36
+ def filter_by_search(users)
37
+ return users if q.blank?
38
+ users.where("LOWER(name) LIKE LOWER(?)", "%#{q}%")
39
+ end
40
+
41
+ def filter_by_state(users)
42
+ case state
43
+ when "officialized"
44
+ users.where.not(officialized_at: nil)
45
+ when "not_officialized"
46
+ users.where(officialized_at: nil)
47
+ else
48
+ users
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,17 +1,17 @@
1
1
  <h2 class="process-title-summary">
2
2
  <%= t('.new_managed_user') %>
3
- <% if more_than_one_authorization? %>
3
+ <% if more_than_one_authorization_handler? %>
4
4
  <span class="text-muted float-right"><%= t('.step', current: params[:handler_name].present? ? 2 : 1, total: 2) %></span>
5
5
  <% end %>
6
6
  </h2>
7
7
 
8
- <% if more_than_one_authorization? && params[:handler_name].nil? %>
8
+ <% if select_authorization_handler_step? %>
9
9
  <div class="card card--list">
10
10
  <div class="card-divider">
11
11
  <h2 class="card-title"><%= t ".select_authorization_method" %></h2>
12
12
  </div>
13
13
  <div class="card-section">
14
- <% available_authorizations.each do |handler_name| %>
14
+ <% available_authorization_handlers.each do |handler| %>
15
15
  <div class="card--list__item">
16
16
  <div class="card--list__text">
17
17
  <a href="#">
@@ -19,12 +19,12 @@
19
19
  </a>
20
20
  <div>
21
21
  <h5 class="card--list__heading">
22
- <%= link_to t("#{handler_name}.name", scope: "decidim.authorization_handlers"), new_managed_user_path(handler_name: handler_name) %>
22
+ <%= link_to handler.fullname, new_managed_user_path(handler_name: handler.name) %>
23
23
  </h5>
24
24
  </div>
25
25
  </div>
26
26
  <div class="card--list__data">
27
- <%= link_to new_managed_user_path(handler_name: handler_name), class: "card--list__data__icon" do %>
27
+ <%= link_to new_managed_user_path(handler_name: handler.name), class: "card--list__data__icon" do %>
28
28
  <%= icon "chevron-right" %>
29
29
  <% end %>
30
30
  </div>
@@ -0,0 +1,82 @@
1
+ <div class="filters row">
2
+ <div class="column medium-3">
3
+ <span class="dropdown-menu-inverted_label"><%= t(".filter_by") %> :</span>
4
+ <ul class="dropdown menu dropdown-inverted" data-dropdown-menu data-close-on-click-inside="false">
5
+ <li class="is-dropdown-submenu-parent">
6
+ <a href="#">
7
+ <% if @state.present? %>
8
+ <%= t(".filter.#{@state}") %>
9
+ <% else %>
10
+ <%= t(".filter.all") %>
11
+ <% end %>
12
+ </a>
13
+ <ul class="menu is-dropdown-submenu">
14
+ <li><%= link_to t(".filter.officialized"), url_for(state: "officialized", q: @query) %></li>
15
+ <li><%= link_to t(".filter.not_officialized"), url_for(state: "not_officialized", q: @query) %></li>
16
+ <li><%= link_to t(".filter.all"), url_for(q: @query) %></li>
17
+ </ul>
18
+ </li>
19
+ </ul>
20
+ </div>
21
+ <div class="column medium-4">
22
+ <%= form_tag "", method: :get do %>
23
+ <div class="filters__search">
24
+ <div class="input-group">
25
+ <%= search_field_tag :q, @query,label: false, class: "input-group-field", placeholder: t('.search') %>
26
+ <%= hidden_field_tag :state, @state %>
27
+ <div class="input-group-button">
28
+ <button type="submit" class="button button--muted">
29
+ <%= icon "magnifying-glass", aria_label: t('.search') %>
30
+ </button>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ <% end %>
35
+ </div>
36
+ </div>
37
+
38
+ <div class="card" id='user-groups'>
39
+ <div class="card-divider">
40
+ <h2 class="card-title"><%= t "decidim.admin.titles.officializations" %></h2>
41
+ </div>
42
+ <div class="card-section">
43
+ <div class="table-scroll">
44
+ <table class="table-list">
45
+ <thead>
46
+ <tr>
47
+ <th><%= t(".name") %></th>
48
+ <th><%= t(".nickname") %></th>
49
+ <th><%= t(".created_at") %></th>
50
+ <th><%= t(".status") %></th>
51
+ <th><%= t(".badge") %></th>
52
+ <th><%= t(".actions") %></th>
53
+ </tr>
54
+ </thead>
55
+ <tbody>
56
+ <% @users.each do |user| %>
57
+ <tr data-user-id="<%= user.id %>">
58
+ <td><%= user.name %></td>
59
+ <td><%= user.nickname %></td>
60
+ <td><%= l user.created_at, format: :short %></td>
61
+ <td><%= user.officialized? ? t(".officialized") : t(".not_officialized") %></td>
62
+ <td><%= translated_attribute(user.officialized_as) %></td>
63
+
64
+ <td class="table-list__actions">
65
+ <% if user.officialized? %>
66
+ <%= icon "circle-check", class: "action-icon action-icon--disabled" %>
67
+ <%= icon_link_to "pencil", new_officialization_path(user_id: user.id), t(".reofficialize"), class: "action-icon--new" %>
68
+ <%= icon_link_to "circle-x", officialization_path(user.id), t(".unofficialize"), method: :delete, class: "action-icon--reject" %>
69
+ <% else %>
70
+ <%= icon_link_to "circle-check", new_officialization_path(user_id: user.id), t(".officialize"), class: "action-icon--verify" %>
71
+ <%= icon "pencil", class: "action-icon action-icon--disabled" %>
72
+ <%= icon "circle-x", class: "action-icon action-icon--disabled" %>
73
+ <% end %>
74
+ </td>
75
+ </tr>
76
+ <% end %>
77
+ </tbody>
78
+ </table>
79
+ <%= paginate @users, theme: "decidim" %>
80
+ </div>
81
+ </div>
82
+ </div>