adminpanel 1.2.12 → 2.0.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 (127) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +3 -4
  3. data/Gemfile +22 -20
  4. data/README.md +12 -9
  5. data/adminpanel.gemspec +29 -28
  6. data/app/assets/fonts/.keep +0 -0
  7. data/app/assets/javascripts/adminpanel/images_form.js +5 -2
  8. data/app/assets/javascripts/adminpanel/imagesloaded.js +5 -2
  9. data/app/assets/javascripts/adminpanel/tables.js +6 -3
  10. data/app/assets/javascripts/application-admin.js +2 -1
  11. data/app/assets/stylesheets/adminpanel/{_clearfix.css.scss → _clearfix.css} +0 -0
  12. data/app/assets/stylesheets/adminpanel/bootstrap.css +2 -2
  13. data/app/assets/stylesheets/adminpanel/medium-editor.css.scss +1 -1
  14. data/app/assets/stylesheets/application-admin.css +2 -1
  15. data/app/controllers/adminpanel/application_controller.rb +28 -21
  16. data/app/controllers/adminpanel/categories_controller.rb +16 -5
  17. data/app/controllers/adminpanel/galleries_controller.rb +11 -5
  18. data/app/controllers/adminpanel/sections_controller.rb +14 -0
  19. data/app/controllers/adminpanel/users_controller.rb +10 -80
  20. data/app/controllers/concerns/adminpanel/galleryzable_actions.rb +33 -0
  21. data/app/controllers/concerns/adminpanel/rest_actions.rb +109 -0
  22. data/app/helpers/adminpanel/application_helper.rb +0 -1
  23. data/app/helpers/adminpanel/custom_form_builder.rb +12 -13
  24. data/app/helpers/adminpanel/router_helper.rb +11 -2
  25. data/app/helpers/adminpanel/shared_pages_helper.rb +0 -1
  26. data/app/models/adminpanel/analytic.rb +3 -2
  27. data/app/models/adminpanel/gallery.rb +8 -4
  28. data/app/models/adminpanel/group.rb +1 -1
  29. data/app/models/adminpanel/image.rb +1 -2
  30. data/app/models/adminpanel/section.rb +19 -10
  31. data/app/models/adminpanel/user.rb +11 -3
  32. data/app/models/concerns/adminpanel/base.rb +149 -0
  33. data/app/models/concerns/adminpanel/galleryzation.rb +66 -0
  34. data/app/models/contact.rb +16 -9
  35. data/app/views/adminpanel/analytics/index.html.erb +2 -1
  36. data/app/views/adminpanel/categories/_categories_table.html.erb +6 -7
  37. data/app/views/adminpanel/categories/_category_form.html.erb +2 -2
  38. data/app/views/adminpanel/categories/create.js.erb +2 -2
  39. data/app/views/adminpanel/categories/index.html.erb +1 -1
  40. data/app/views/adminpanel/galleries/_galleries_table.html.erb +7 -7
  41. data/app/views/adminpanel/galleries/index.html.erb +6 -5
  42. data/app/views/adminpanel/sections/_sections_table.html.erb +2 -2
  43. data/app/views/adminpanel/sections/edit.html.erb +5 -3
  44. data/app/views/adminpanel/sections/show.html.erb +3 -3
  45. data/app/views/adminpanel/sessions/new.html.erb +1 -1
  46. data/app/views/layouts/_side_menu.html.erb +2 -2
  47. data/app/views/layouts/_top_bar.html.erb +3 -3
  48. data/app/views/layouts/admin-login.html.erb +0 -1
  49. data/app/views/layouts/admin.html.erb +3 -3
  50. data/app/views/shared/_delete_icon_button.html.erb +16 -0
  51. data/app/views/shared/_edit_icon_button.html.erb +14 -0
  52. data/app/views/shared/_error_messages.html.erb +1 -1
  53. data/app/views/shared/_form_fields.html.erb +15 -13
  54. data/app/views/shared/_gallery_entries.html.erb +2 -2
  55. data/app/views/shared/_init_editor.html.erb +5 -2
  56. data/app/views/shared/_new_resource_button.html.erb +18 -0
  57. data/app/views/shared/_remote_form_fields.html.erb +1 -1
  58. data/app/views/shared/_show_icon_button.html.erb +14 -0
  59. data/app/views/shared/edit.html.erb +3 -3
  60. data/app/views/shared/index.html.erb +13 -48
  61. data/app/views/shared/new.html.erb +3 -3
  62. data/app/views/shared/show.html.erb +5 -5
  63. data/config/initializers/pluralization_es.rb +16 -0
  64. data/config/locales/en.yml +89 -0
  65. data/config/locales/es.yml +4 -4
  66. data/config/routes.rb +15 -16
  67. data/lib/adminpanel.rb +8 -8
  68. data/lib/adminpanel/version.rb +1 -1
  69. data/lib/generators/adminpanel/gallery/gallery_generator.rb +12 -8
  70. data/lib/generators/adminpanel/gallery/templates/gallery_template.rb +2 -3
  71. data/lib/generators/adminpanel/initialize/templates/category_template.rb +2 -3
  72. data/lib/generators/adminpanel/resource/resource_generator.rb +24 -18
  73. data/lib/generators/adminpanel/resource/templates/controller.rb +5 -0
  74. data/lib/generators/adminpanel/resource/templates/resource.rb +3 -3
  75. data/lib/tasks/adminpanel/adminpanel.rake +43 -90
  76. data/spec/dummy/app/controllers/adminpanel/mugs_controller.rb +9 -0
  77. data/spec/dummy/app/controllers/adminpanel/products_controller.rb +12 -0
  78. data/spec/dummy/app/models/ability.rb +1 -1
  79. data/spec/dummy/app/models/adminpanel/categorization.rb +1 -2
  80. data/spec/dummy/app/models/adminpanel/category.rb +18 -19
  81. data/spec/dummy/app/models/adminpanel/mug.rb +37 -0
  82. data/spec/dummy/app/models/adminpanel/photo.rb +3 -3
  83. data/spec/dummy/app/models/adminpanel/product.rb +2 -2
  84. data/spec/dummy/config/application.rb +2 -11
  85. data/spec/dummy/config/database.yml +0 -6
  86. data/spec/dummy/config/environments/development.rb +11 -23
  87. data/spec/dummy/config/environments/production.rb +40 -32
  88. data/spec/dummy/config/environments/test.rb +13 -18
  89. data/spec/dummy/config/initializers/adminpanel_setup.rb +2 -1
  90. data/spec/dummy/config/locales/.keep +0 -0
  91. data/spec/dummy/db/schema.rb +68 -2
  92. data/spec/features/authentication_pages_spec.rb +15 -15
  93. data/spec/features/galleries_pages_spec.rb +30 -22
  94. data/spec/features/section_pages_spec.rb +15 -18
  95. data/spec/features/shared_pages_spec.rb +97 -127
  96. data/spec/generators/gallery_generator_spec.rb +2 -3
  97. data/spec/generators/resource_generator_spec.rb +20 -3
  98. data/spec/models/gallery_spec.rb +2 -2
  99. data/spec/models/section_spec.rb +8 -15
  100. data/spec/models/user_spec.rb +23 -39
  101. data/spec/spec_helper.rb +9 -1
  102. data/spec/support/factories.rb +52 -0
  103. data/spec/support/helper_methods.rb +25 -5
  104. data/spec/tasks/adminpanel_rake_spec.rb +11 -10
  105. metadata +361 -353
  106. data/app/assets/fonts/FontAwesome.otf +0 -0
  107. data/app/assets/fonts/fontawesome-webfont.eot +0 -0
  108. data/app/assets/fonts/fontawesome-webfont.svg +0 -255
  109. data/app/assets/fonts/fontawesome-webfont.ttf +0 -0
  110. data/app/assets/fonts/fontawesome-webfont.woff +0 -0
  111. data/app/assets/stylesheets/adminpanel/colorpicker.css +0 -7
  112. data/app/assets/stylesheets/adminpanel/elfinder.min.css +0 -59
  113. data/app/assets/stylesheets/adminpanel/font-awesome.min.css +0 -34
  114. data/app/helpers/adminpanel/pluralizations_helper.rb +0 -25
  115. data/app/helpers/adminpanel/rest_actions_helper.rb +0 -128
  116. data/app/views/adminpanel/galleries/delete.html.erb +0 -2
  117. data/app/views/adminpanel/galleries/edit.html.erb +0 -19
  118. data/app/views/adminpanel/galleries/new.html.erb +0 -18
  119. data/app/views/adminpanel/galleries/show.html.erb +0 -18
  120. data/app/views/adminpanel/galleries/update.html.erb +0 -2
  121. data/lib/adminpanel/active_record/adminpanel_extension.rb +0 -187
  122. data/spec/dummy/config/locales/en.yml +0 -5
  123. data/spec/support/define_factory_models.rb +0 -45
  124. data/spec/support/submit_forms_without_button.rb +0 -17
  125. data/spec/support/test_database.rb +0 -76
  126. data/spec/uploaders/gallery_uploader_spec.rb +0 -37
  127. data/spec/uploaders/image_uploader_spec.rb +0 -31
@@ -2,86 +2,16 @@ module Adminpanel
2
2
  class UsersController < Adminpanel::ApplicationController
3
3
  # authorize_resource :class => false
4
4
  load_and_authorize_resource
5
- # GET /admin/users
6
- # GET /admin/users.json
7
- # def index
8
- # @users = User.all
9
- #
10
- # respond_to do |format|
11
- # format.html # index.html.erb
12
- # format.json { render :json => @users }
13
- # end
14
- # end
15
5
 
16
- # GET /admin/users/1
17
- # GET /admin/users/1.json
18
- # def show
19
- # @user = User.find(params[:id])
20
- #
21
- # respond_to do |format|
22
- # format.html # show.html.erb
23
- # format.json { render :json => @user }
24
- # end
25
- # end
26
- #
27
- # # GET /admin/users/new
28
- # # GET /admin/users/new.json
29
- # def new
30
- # @user = User.new
31
- #
32
- # respond_to do |format|
33
- # format.html # new.html.erb
34
- # format.json { render :json => @user }
35
- # end
36
- # end
37
-
38
- # GET /admin/users/1/edit
39
- # def edit
40
- # @user = User.find(params[:id])
41
- # end
42
- #
43
- # # POST /admin/users
44
- # # POST /admin/users.json
45
- # def create
46
- # @user = User.new(params[:user])
47
- #
48
- # respond_to do |format|
49
- # if @user.save
50
- # format.html { redirect_to @user, :notice => t("user.success") }
51
- # format.json { render :json => @user, :status => :created, :location => @user }
52
- # else
53
- # format.html { render :action => "new" }
54
- # format.json { render :json => @user.errors, :status => :unprocessable_entity }
55
- # end
56
- # end
57
- # end
58
-
59
- # PUT /admin/users/1
60
- # PUT /admin/users/1.json
61
- # def update
62
- # @user = User.find(params[:id])
63
- #
64
- # respond_to do |format|
65
- # if @user.update_attributes(params[:user])
66
- # format.html { redirect_to @user, :notice => 'Se ha actualizado con exito.' }
67
- # format.json { head :no_content }
68
- # else
69
- # format.html { render :action => "edit" }
70
- # format.json { render :json => @user.errors, :status => :unprocessable_entity }
71
- # end
72
- # end
73
- # end
74
-
75
- # DELETE /admin/users/1
76
- # DELETE /admin/users/1.json
77
- # def destroy
78
- # @user = User.find(params[:id])
79
- # @user.destroy
80
- #
81
- # respond_to do |format|
82
- # format.html { redirect_to users_url }
83
- # format.json { head :no_content }
84
- # end
85
- # end
6
+ private
7
+ def user_params
8
+ params.require(:user).permit(
9
+ :email,
10
+ :name,
11
+ :password,
12
+ :password_confirmation,
13
+ :group_id
14
+ )
15
+ end
86
16
  end
87
17
  end
@@ -0,0 +1,33 @@
1
+ module Adminpanel
2
+ module GalleryzableActions
3
+ extend ActiveSupport::Concern
4
+
5
+ def move_better
6
+ respond_to do |format|
7
+ format.js do
8
+ resource = @model.find(params[:id])
9
+ resource.move_to_better_position
10
+ render 'shared/gallery_entries', :locals => {
11
+ :collection => @model.where(
12
+ @model.relation_field.to_sym => resource.send(@model.relation_field)
13
+ )
14
+ }
15
+ end
16
+ end
17
+ end
18
+
19
+ def move_worst
20
+ respond_to do |format|
21
+ format.js do
22
+ resource = @model.find(params[:id])
23
+ resource.move_to_worst_position
24
+ render 'shared/gallery_entries', :locals => {
25
+ :collection => @model.where(
26
+ @model.relation_field.to_sym => resource.send(@model.relation_field)
27
+ )
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,109 @@
1
+ module Adminpanel
2
+ module RestActions
3
+
4
+ def index
5
+ index! do |format|
6
+ format.html { render 'shared/index' }
7
+ end
8
+ end
9
+
10
+ def show
11
+ show! do |format|
12
+ format.html { render 'shared/show' }
13
+ end
14
+ end
15
+
16
+ def new
17
+ set_collections
18
+ new! do |format|
19
+ format.html { render 'shared/new' }
20
+ format.js { render 'shared/new', :locals => { :resource => resource }}
21
+ end
22
+ end
23
+
24
+ def create
25
+ params.merge({:model => params[:model]}) if params[:model].present?
26
+ params.merge({:model_name => params[:model_name]}) if params[:model_name].present?
27
+ params.merge({:belongs_request => params[:belongs_request]}) if params[:belongs_request].present?
28
+ create! do |success, failure|
29
+ success.html do
30
+ flash[:success] = I18n.t("action.save_success")
31
+ redirect_to resource
32
+ end
33
+ failure.html do
34
+ set_collections
35
+ render 'shared/new'
36
+ end
37
+ success.js do
38
+ flash.now[:success] = I18n.t("action.save_success")
39
+ if params[:belongs_request]
40
+ render 'shared/create_belongs_to', :locals => {:resource => resource }
41
+ else
42
+ render 'shared/create_has_many', :locals => {:resource => resource }
43
+ end
44
+ end
45
+ failure.js do
46
+ set_collections
47
+ render 'shared/new', :locals => {:resource => resource }
48
+ end
49
+ end
50
+ end
51
+
52
+
53
+ def edit
54
+ edit! do |format|
55
+ format.html do
56
+ set_collections
57
+ render 'shared/edit'
58
+ end
59
+ end
60
+ end
61
+
62
+ def update
63
+ update! do |success, failure|
64
+ success.html do
65
+ flash[:success] = I18n.t("action.save_success")
66
+ render 'shared/index'
67
+ end
68
+ failure.html do
69
+ set_collections
70
+ render 'shared/edit'
71
+ end
72
+ end
73
+ end
74
+
75
+ def destroy
76
+ destroy! do |format|
77
+ format.html { render 'shared/index' }
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def set_collections
84
+ @collections = {}
85
+ set_belongs_to_collections
86
+ set_has_many_collections
87
+ end
88
+
89
+ def set_belongs_to_collections
90
+ @model.belongs_to_relationships.each do |class_variable|
91
+ set_relationship(class_variable)
92
+ end
93
+ end
94
+
95
+ def set_has_many_collections
96
+ @model.has_many_relationships.each do |class_variable|
97
+ set_relationship(class_variable)
98
+ end
99
+ end
100
+
101
+ def set_relationship(class_variable)
102
+ if class_variable.respond_to?("of_model")
103
+ @collections.merge!({"#{class_variable}" => class_variable.of_model(@model.display_name)})
104
+ else
105
+ @collections.merge!({"#{class_variable}" => class_variable.all})
106
+ end
107
+ end
108
+ end
109
+ end
@@ -3,7 +3,6 @@ module Adminpanel
3
3
  include SessionsHelper
4
4
  include BreadcrumbsHelper
5
5
  include SharedPagesHelper
6
- include PluralizationsHelper
7
6
 
8
7
  def custom_form_for(name, *args, &block)
9
8
  options = args.extract_options!
@@ -153,8 +153,8 @@ module Adminpanel
153
153
  options.reverse_merge! :class => "span7"
154
154
 
155
155
  options.reverse_merge! :include_blank => "(Seleccione por favor)";
156
- label = options[:label]
157
- options.delete(:label)
156
+ label = options['label']
157
+ options.delete('label')
158
158
 
159
159
  @template.content_tag :div, :class => "control-group" do
160
160
  @template.content_tag(:label, label, :class => "control-label") +
@@ -167,8 +167,8 @@ module Adminpanel
167
167
 
168
168
  options.reverse_merge! :class => "span5"
169
169
  options.reverse_merge! :label => name
170
- label = options[:label]
171
- options.delete(:label)
170
+ label = options['label']
171
+ options.delete('label')
172
172
 
173
173
  @template.content_tag :div, :class => "control-group" do
174
174
  @template.content_tag(:label, label, :class => "control-label") +
@@ -180,7 +180,6 @@ module Adminpanel
180
180
  options = args.extract_options!
181
181
 
182
182
  options.reverse_merge! :class => "span7"
183
- # options.reverse_merge! :label => name
184
183
  label = options['label']
185
184
  options.delete('label')
186
185
 
@@ -196,8 +195,8 @@ module Adminpanel
196
195
  options.reverse_merge! :class => "span7"
197
196
  options.reverse_merge! :rows => "10"
198
197
  options.reverse_merge! :label => name
199
- label = options[:label]
200
- options.delete(:label)
198
+ label = options['label']
199
+ options.delete('label')
201
200
 
202
201
  @template.content_tag :div, :class => "control-group" do
203
202
  @template.content_tag(:label, label, :class => "control-label") +
@@ -218,8 +217,8 @@ module Adminpanel
218
217
 
219
218
  options.reverse_merge! :value => Time.now.strftime("%d-%m-%Y")
220
219
  options.reverse_merge! :label => name
221
- label = options[:label]
222
- options.delete(:label)
220
+ label = options['label']
221
+ options.delete('label')
223
222
 
224
223
  @template.content_tag :div, :class => "control-group" do
225
224
  @template.content_tag(:label, label, :class => "control-label") +
@@ -255,8 +254,8 @@ module Adminpanel
255
254
  options = args.extract_options!
256
255
 
257
256
  options.reverse_merge! :label => name
258
- label = options[:label]
259
- options.delete(:label)
257
+ label = options['label']
258
+ options.delete('label')
260
259
 
261
260
  options.reverse_merge! :symbol => "#"
262
261
  symbol = options[:symbol]
@@ -282,8 +281,8 @@ module Adminpanel
282
281
  options = args.extract_options!
283
282
 
284
283
  options.reverse_merge! :label => name
285
- label = options[:label]
286
- options.delete(:label)
284
+ label = options['label']
285
+ options.delete('label')
287
286
 
288
287
  options.reverse_merge! :symbol => "#"
289
288
  symbol = options[:symbol]
@@ -1,7 +1,16 @@
1
1
  module Adminpanel
2
2
  module RouterHelper
3
- def acts_as_a_gallery?(resource)
4
- "adminpanel/#{resource}_controller".classify.constantize.resource_class.act_as_a_gallery?
3
+ def gallery_children(resource)
4
+ resource_class(resource).gallery_children
5
+ end
6
+
7
+ def resources_parameters(resource)
8
+ resource_class(resource).routes_options
9
+ end
10
+
11
+ private
12
+ def resource_class(resource)
13
+ "adminpanel/#{resource.to_s.singularize}".classify.constantize
5
14
  end
6
15
  end
7
16
  end
@@ -5,7 +5,6 @@ module Adminpanel
5
5
  if association.klass.to_s == parent_model
6
6
  if !resource.send(association.name).nil?
7
7
  return resource.send(association.name).name
8
- # return resource.client
9
8
  else
10
9
  return "N/A #{association.klass.to_s}"
11
10
  end
@@ -1,12 +1,13 @@
1
1
  module Adminpanel
2
2
  class Analytic #< ActiveRecord::Base
3
+ include Adminpanel::Base
3
4
 
4
5
  def self.display_name
5
- "Google Analytics"
6
+ 'Google Analtico'
6
7
  end
7
8
 
8
9
  def self.icon
9
- 'icon-dashboard'
10
+ 'dashboard'
10
11
  end
11
12
  end
12
13
  end
@@ -2,12 +2,12 @@ require "carrierwave"
2
2
  require "carrierwave/orm/activerecord"
3
3
  module Adminpanel
4
4
  class Gallery < ActiveRecord::Base
5
- attr_accessible :file
5
+ include Adminpanel::Base
6
6
 
7
7
  mount_uploader :file, Adminpanel::GalleryUploader
8
8
  validates_presence_of :file
9
9
 
10
- act_as_a_gallery
10
+ include Adminpanel::Galleryzation
11
11
 
12
12
  def move_to_better_position
13
13
  if self.position > 1
@@ -47,11 +47,15 @@ module Adminpanel
47
47
  end
48
48
 
49
49
  def self.display_name
50
- I18n.t('Gallery')
50
+ I18n.t('gallery.name')
51
51
  end
52
52
 
53
53
  def self.icon
54
- 'icon-picture'
54
+ 'picture-o'
55
+ end
56
+
57
+ def name
58
+ File.basename(file.path)
55
59
  end
56
60
 
57
61
  private
@@ -1,6 +1,6 @@
1
1
  module Adminpanel
2
2
  class Group < ActiveRecord::Base
3
- attr_accessible :name
3
+ include Adminpanel::Base
4
4
 
5
5
  end
6
6
  end
@@ -1,7 +1,6 @@
1
1
  module Adminpanel
2
2
  class Image < ActiveRecord::Base
3
-
4
- attr_accessible :file
3
+ include Adminpanel::Base
5
4
 
6
5
  validates_presence_of :file
7
6
 
@@ -2,27 +2,28 @@ require 'carrierwave'
2
2
  require 'carrierwave/orm/activerecord'
3
3
  module Adminpanel
4
4
  class Section < ActiveRecord::Base
5
- attr_accessible :description, :has_image, :key, :page, :name, :has_description,
6
- :images_attributes, :file
5
+ include Adminpanel::Base
7
6
 
8
7
  mount_images :images
9
8
 
10
- validates_length_of :description, :minimum => 10, :maximum => 10, :on => :update, :if => lambda{|section| section.key == I18n.t('key.telephone')}
9
+ validates_length_of :description, :minimum => 10, :maximum => 10, :on => :update, :if => lambda{|section| section.key == 'phone'}, :message => I18n.t('activerecord.errors.messages.not_phone')
11
10
  validates_presence_of :description, :minimum => 9, :on => :update, :if => lambda{|section| section.has_description == true}
12
- validates :description, :numericality => { :only_integer => true }, :on => :update, :if => lambda{|section| section.key == I18n.t('key.telephone')}
11
+ validates :description, :numericality => { :only_integer => true }, :on => :update, :if => lambda{|section| section.key == 'phone'}
13
12
  validates_presence_of :key
14
13
  validates_presence_of :name
15
14
  validates_presence_of :page
15
+ VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
16
+ validates_format_of :description, :with => VALID_EMAIL_REGEX, if: lambda{|section| section.key == 'email'}
16
17
 
17
18
  default_scope { order("page ASC")}
18
19
 
19
20
  scope :of_page, lambda{|page| where(:page => page)}
20
21
 
21
- def self.form_methods
22
+ def self.form_attributes
22
23
  [
23
- {'description' => {'name' => 'Descripcion'}},
24
- {'name' => {'name' => 'name'}},
25
- {'key' => {'name' => 'key'}},
24
+ {'description' => {'name' => 'Descripcion', 'description' => 'label', 'label' => 'Seccion'}},
25
+ {'name' => {'name' => 'name', 'label' => 'Seccion'}},
26
+ {'key' => {'name' => 'key', 'label' => 'Llave'}},
26
27
  {'page' => {'name' => 'page'}},
27
28
  # {'key' => {'name' => 'key'}},
28
29
  ]
@@ -30,11 +31,19 @@ module Adminpanel
30
31
  end
31
32
 
32
33
  def self.icon
33
- "icon-tasks"
34
+ "tasks"
34
35
  end
35
36
 
36
37
  def self.display_name
37
- 'Secciones'
38
+ 'Seccion'
39
+ end
40
+
41
+ def description
42
+ if self.has_description
43
+ return self.attributes['description'].html_safe
44
+ else
45
+ return self.attributes['description']
46
+ end
38
47
  end
39
48
  end
40
49
  end