knitkit 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +22 -0
- data/app/controllers/knitkit/base_controller.rb +15 -2
- data/app/controllers/knitkit/blogs_controller.rb +4 -4
- data/app/controllers/knitkit/erp_app/desktop/app_controller.rb +16 -2
- data/app/controllers/knitkit/erp_app/desktop/articles_controller.rb +198 -57
- data/app/controllers/knitkit/erp_app/desktop/content_controller.rb +25 -11
- data/app/controllers/knitkit/erp_app/desktop/file_assets_controller.rb +123 -44
- data/app/controllers/knitkit/erp_app/desktop/image_assets_controller.rb +83 -9
- data/app/controllers/knitkit/erp_app/desktop/online_document_sections_controller.rb +38 -0
- data/app/controllers/knitkit/erp_app/desktop/position_controller.rb +15 -6
- data/app/controllers/knitkit/erp_app/desktop/theme_controller.rb +185 -98
- data/app/controllers/knitkit/erp_app/desktop/versions_controller.rb +38 -16
- data/app/controllers/knitkit/erp_app/desktop/website_controller.rb +126 -68
- data/app/controllers/knitkit/erp_app/desktop/website_nav_controller.rb +154 -107
- data/app/controllers/knitkit/erp_app/desktop/website_section_controller.rb +113 -54
- data/app/controllers/knitkit/online_document_sections_controller.rb +45 -0
- data/app/controllers/knitkit/unauthorized_controller.rb +5 -0
- data/app/controllers/knitkit/website_sections_controller.rb +8 -9
- data/app/mailers/document_mailer.rb +10 -0
- data/app/models/article.rb +1 -1
- data/app/models/content.rb +35 -3
- data/app/models/document.rb +8 -0
- data/app/models/document_type.rb +3 -0
- data/app/models/documented_content.rb +29 -0
- data/app/models/documented_item.rb +31 -0
- data/app/models/extensions/configuration.rb +5 -0
- data/app/models/extensions/party.rb +13 -0
- data/app/models/online_document_section.rb +40 -0
- data/app/models/theme.rb +22 -26
- data/app/models/valid_document.rb +4 -0
- data/app/models/website.rb +158 -119
- data/app/models/website_inquiry.rb +7 -2
- data/app/models/website_inquiry_mailer.rb +4 -3
- data/app/models/website_party_role.rb +5 -0
- data/app/models/website_section.rb +76 -38
- data/app/views/document_mailer/email_document.html.erb +12 -0
- data/app/views/knitkit/blogs/_add_comment.html.erb +2 -2
- data/app/views/knitkit/online_document_sections/index.html.erb +149 -0
- data/app/views/knitkit/website_sections/index.html.erb +0 -1
- data/app/views/layouts/knitkit/base.html.erb +4 -2
- data/app/views/layouts/knitkit/online_document_sections.html.erb +59 -0
- data/app/views/menus/knitkit/_default_menu.html.erb +1 -1
- data/app/views/menus/knitkit/_default_section_menu.html.erb +1 -1
- data/app/views/menus/knitkit/_default_sub_menu.html.erb +2 -2
- data/app/views/menus/knitkit/_default_sub_section_menu.html.erb +1 -1
- data/app/widgets/contact_us/base.rb +5 -5
- data/app/widgets/contact_us/javascript/contact_us.js +2 -1
- data/app/widgets/google_map/base.rb +0 -4
- data/app/widgets/google_map/javascript/google_map.js +4 -3
- data/app/widgets/login/base.rb +0 -5
- data/app/widgets/login/javascript/login.js +155 -153
- data/app/widgets/login/views/index.html.erb +3 -3
- data/app/widgets/login/views/reset_password.html.erb +2 -2
- data/app/widgets/manage_profile/base.rb +46 -67
- data/app/widgets/manage_profile/javascript/manage_profile.js +2 -1
- data/app/widgets/manage_profile/views/_user_information_form.html.erb +5 -1
- data/app/widgets/reset_password/base.rb +4 -6
- data/app/widgets/reset_password/javascript/reset_password.js +2 -1
- data/app/widgets/reset_password/views/index.html.erb +4 -3
- data/app/widgets/search/base.rb +1 -5
- data/app/widgets/search/javascript/search.js +2 -1
- data/app/widgets/search/views/show.html.erb +2 -2
- data/app/widgets/signup/base.rb +7 -6
- data/app/widgets/signup/javascript/signup.js +2 -1
- data/app/widgets/signup/views/error.html.erb +1 -1
- data/config/routes.rb +6 -2
- data/db/data_migrations/20110509223702_add_publisher_role.rb +10 -0
- data/db/data_migrations/20111118182910_setup_knitkit_capabilities.rb +84 -0
- data/db/data_migrations/20120127144444_create_website_role_types.rb +13 -0
- data/db/data_migrations/20120127150505_create_website_default_configuration.rb +72 -0
- data/db/data_migrations/20120127150506_add_primary_host_to_website_configuration.rb +33 -0
- data/db/data_migrations/20120316150424_add_is_template_to_default_website_config.rb +16 -0
- data/db/data_migrations/upgrade/20120210195616_add_website_configs.rb +19 -0
- data/db/data_migrations/upgrade/20120213205519_populate_website_iids.rb +17 -0
- data/db/migrate/20110211002317_setup_knitkit.rb +22 -5
- data/db/migrate/20111207161928_create_documented_items_table.rb +13 -0
- data/db/migrate/20111208180539_add_document_id_to_documented_item.rb +9 -0
- data/db/migrate/20120315163736_add_document.rb +32 -0
- data/db/migrate/20120503183431_create_valid_documents.rb +16 -0
- data/db/migrate/upgrade/20120116201510_add_render_base_layout_flag.rb +13 -0
- data/db/migrate/upgrade/20120127143745_create_website_party_roles.rb +24 -0
- data/db/migrate/upgrade/20120213184509_add_iid_to_websites.rb +14 -0
- data/lib/knitkit.rb +2 -0
- data/lib/knitkit/config.rb +31 -0
- data/lib/knitkit/engine.rb +12 -3
- data/lib/knitkit/extensions.rb +1 -3
- data/lib/knitkit/extensions/action_controller/theme_support/acts_as_themed_controller.rb +2 -3
- data/lib/knitkit/extensions/active_record/acts_as_document.rb +63 -0
- data/lib/knitkit/extensions/active_record/acts_as_publishable.rb +5 -4
- data/lib/knitkit/extensions/compass_ae/widgets/base.rb +70 -0
- data/lib/knitkit/extensions/railties/action_view.rb +22 -10
- data/lib/knitkit/extensions/railties/theme_support/asset_tag_helper.rb +3 -3
- data/lib/knitkit/extensions/railties/theme_support/theme_file_resolver.rb +8 -3
- data/lib/knitkit/routing_filter/section_router.rb +16 -6
- data/lib/knitkit/version.rb +7 -1
- data/public/images/check.png +0 -0
- data/public/images/credit_card.png +0 -0
- data/public/images/knitkit/tooltip.gif +0 -0
- data/public/javascripts/ajax_pagination.js +33 -0
- data/public/javascripts/datepicker.js +6 -1
- data/public/javascripts/erp_app/desktop/applications/knitkit/articles_grid_panel.js +731 -258
- data/public/javascripts/erp_app/desktop/applications/knitkit/center_region.js +289 -238
- data/public/javascripts/erp_app/desktop/applications/knitkit/comments_grid_panel.js +2 -4
- data/public/javascripts/erp_app/desktop/applications/knitkit/east_region.js +29 -3
- data/public/javascripts/erp_app/desktop/applications/knitkit/file_assets_panel.js +193 -21
- data/public/javascripts/erp_app/desktop/applications/knitkit/image_assets_data_view.js +27 -26
- data/public/javascripts/erp_app/desktop/applications/knitkit/image_assets_panel.js +167 -20
- data/public/javascripts/erp_app/desktop/applications/knitkit/inquiries_grid_panel.js +1 -2
- data/public/javascripts/erp_app/desktop/applications/knitkit/module.js +5 -1
- data/public/javascripts/erp_app/desktop/applications/knitkit/publish_window.js +2 -2
- data/public/javascripts/erp_app/desktop/applications/knitkit/published_grid_panel.js +20 -11
- data/public/javascripts/erp_app/desktop/applications/knitkit/section_articles_grid_panel.js +279 -228
- data/public/javascripts/erp_app/desktop/applications/knitkit/themes_tree_panel.js +40 -40
- data/public/javascripts/erp_app/desktop/applications/knitkit/versions_grid_panel.js +83 -76
- data/public/javascripts/erp_app/desktop/applications/knitkit/west_region.js +1961 -1397
- data/public/javascripts/erp_app/desktop/applications/knitkit/widgets_panel.js +47 -43
- data/public/javascripts/knitkit/helpers.js +26 -0
- data/public/stylesheets/erp_app/desktop/applications/knitkit/knitkit.css +1 -2
- data/public/stylesheets/knitkit/documentation.css +50 -0
- data/public/stylesheets/knitkit/style.css +23 -1
- data/spec/controllers/knitkit/erp_app/desktop/articles_controller_spec.rb +8 -0
- data/spec/controllers/knitkit/erp_app/desktop/website_controller_spec.rb +396 -0
- data/spec/controllers/knitkit/erp_app/desktop/website_nav_controller_spec.rb +260 -0
- data/spec/controllers/knitkit/erp_app/desktop/website_section_controller_spec.rb +222 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +9 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +43 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +8 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/spec.rb +27 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +12 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/config/workflow.yml +1 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/factories/article.rb +4 -0
- data/spec/factories/basic.rb +3 -0
- data/spec/factories/blog.rb +5 -0
- data/spec/factories/documented_content.rb +4 -0
- data/spec/factories/documented_item.rb +4 -0
- data/spec/factories/online_document_section.rb +6 -0
- data/spec/factories/published_website.rb +4 -0
- data/spec/factories/theme.rb +6 -0
- data/spec/factories/website.rb +5 -0
- data/spec/factories/website_host.rb +4 -0
- data/spec/factories/website_nav.rb +5 -0
- data/spec/factories/website_nav_item.rb +4 -0
- data/spec/factories/website_section.rb +5 -0
- data/spec/models/article_spec.rb +35 -0
- data/spec/models/attribute_type_spec.rb +55 -0
- data/spec/models/attribute_value_spec.rb +114 -0
- data/spec/models/blog_spec.rb +16 -0
- data/spec/models/comment_spec.rb +11 -0
- data/spec/models/content_spec.rb +187 -0
- data/spec/models/documented_item_spec.rb +29 -0
- data/spec/models/online_document_section_spec.rb +34 -0
- data/spec/models/published_element_spec.rb +11 -0
- data/spec/models/published_website_spec.rb +11 -0
- data/spec/models/theme_spec.rb +12 -0
- data/spec/models/website_host_spec.rb +11 -0
- data/spec/models/website_inquiry_spec.rb +24 -0
- data/spec/models/website_nav_item_spec.rb +11 -0
- data/spec/models/website_nav_spec.rb +11 -0
- data/spec/models/website_section_content_spec.rb +11 -0
- data/spec/models/website_section_spec.rb +49 -0
- data/spec/models/website_spec.rb +146 -0
- data/spec/spec_helper.rb +61 -0
- metadata +391 -154
- data/app/controllers/knitkit/articles_controller.rb +0 -7
- data/lib/knitkit/extensions/compass/widgets/base.rb +0 -53
@@ -0,0 +1,13 @@
|
|
1
|
+
Party.class_eval do
|
2
|
+
has_many :website_party_roles, :dependent => :destroy
|
3
|
+
has_many :websites, :through =>:website_party_roles do
|
4
|
+
def owned
|
5
|
+
where('role_type_id = ?',RoleType.website_owner).collect(&:website)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_website_with_role(website, role_type)
|
10
|
+
self.website_party_roles << WebsitePartyRole.create(:website => website, :role_type => role_type)
|
11
|
+
self.save
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class OnlineDocumentSection < WebsiteSection
|
2
|
+
has_one :documented_item, :dependent => :destroy
|
3
|
+
delegate :content, :to => :documented_item, :prefix => true
|
4
|
+
delegate :published_content, :to => :documented_item, :prefix => true
|
5
|
+
has_permalink :title, :url_attribute => :permalink, :sync_url => true, :only_when_blank => true, :scope => [:website_id, :parent_id]
|
6
|
+
|
7
|
+
def documented_item_content_html
|
8
|
+
documented_item_content.body_html
|
9
|
+
rescue
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def documented_item_published_content_html(active_publication)
|
14
|
+
documented_item_published_content(active_publication).body_html
|
15
|
+
rescue
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def build_section_hash
|
20
|
+
section_hash = {
|
21
|
+
:name => self.title,
|
22
|
+
:has_layout => false,
|
23
|
+
:type => self.class.to_s,
|
24
|
+
:in_menu => self.in_menu,
|
25
|
+
:roles => self.roles.collect{|role| role.internal_identifier},
|
26
|
+
:path => self.path,
|
27
|
+
:permalink => self.permalink,
|
28
|
+
:internal_identifier => self.internal_identifier,
|
29
|
+
:position => self.position,
|
30
|
+
:online_document_sections => self.children.each.map{|child| child.build_section_hash},
|
31
|
+
:articles => [],
|
32
|
+
:documented_item => {
|
33
|
+
:name => documented_item_content.title,
|
34
|
+
:display_title => documented_item_content.display_title,
|
35
|
+
:internal_identifier => documented_item_content.internal_identifier
|
36
|
+
}
|
37
|
+
}
|
38
|
+
section_hash
|
39
|
+
end
|
40
|
+
end
|
data/app/models/theme.rb
CHANGED
@@ -15,7 +15,7 @@ class Theme < ActiveRecord::Base
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def base_dir(website)
|
18
|
-
"#{root_dir}/sites
|
18
|
+
"#{root_dir}/sites/#{website.iid}/themes"
|
19
19
|
end
|
20
20
|
|
21
21
|
def import(file, website)
|
@@ -29,8 +29,7 @@ class Theme < ActiveRecord::Base
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def make_tmp_dir
|
32
|
-
|
33
|
-
Pathname.new(Rails.root.to_s + "/tmp/themes/tmp_#{random}/").tap do |dir|
|
32
|
+
Pathname.new(Rails.root.to_s + "/tmp/themes/tmp_#{Time.now.to_i.to_s}/").tap do |dir|
|
34
33
|
FileUtils.mkdir_p(dir) unless dir.exist?
|
35
34
|
end
|
36
35
|
end
|
@@ -63,7 +62,7 @@ class Theme < ActiveRecord::Base
|
|
63
62
|
end
|
64
63
|
|
65
64
|
def url
|
66
|
-
"sites
|
65
|
+
"/public/sites/#{website.iid}/themes/#{theme_id}"
|
67
66
|
end
|
68
67
|
|
69
68
|
def activate!
|
@@ -75,26 +74,23 @@ class Theme < ActiveRecord::Base
|
|
75
74
|
end
|
76
75
|
|
77
76
|
def themed_widgets
|
78
|
-
widgets
|
79
|
-
|
80
|
-
|
81
|
-
end
|
82
|
-
widgets
|
77
|
+
Rails.application.config.erp_app.widgets.select do |widget_hash|
|
78
|
+
!(self.files.where("directory like '#{File.join(self.url, 'widgets', widget_hash[:name])}%'").all.empty?)
|
79
|
+
end.collect{|item| item[:name]}
|
83
80
|
end
|
84
81
|
|
85
82
|
def non_themed_widgets
|
86
83
|
already_themed_widgets = self.themed_widgets
|
87
|
-
widgets
|
88
|
-
|
89
|
-
|
90
|
-
end
|
91
|
-
widgets
|
84
|
+
Rails.application.config.erp_app.widgets.select do |widget_hash|
|
85
|
+
!already_themed_widgets.include?(widget_hash[:name])
|
86
|
+
end.collect{|item| item[:name]}
|
92
87
|
end
|
93
88
|
|
94
89
|
def create_layouts_for_widget(widget)
|
95
|
-
|
96
|
-
|
97
|
-
|
90
|
+
widget_hash = Rails.application.config.erp_app.widgets.find{|item| item[:name] == widget}
|
91
|
+
widget_hash[:view_files].each do |view_file|
|
92
|
+
save_theme_file(view_file[:path], :widgets, {:path_to_replace => view_file[:path].split('/views')[0], :widget_name => widget})
|
93
|
+
end
|
98
94
|
end
|
99
95
|
|
100
96
|
def about
|
@@ -105,7 +101,7 @@ class Theme < ActiveRecord::Base
|
|
105
101
|
end
|
106
102
|
|
107
103
|
def import(file)
|
108
|
-
file_support = ErpTechSvcs::FileSupport::Base.new(:storage =>
|
104
|
+
file_support = ErpTechSvcs::FileSupport::Base.new(:storage => Rails.application.config.erp_tech_svcs.file_storage)
|
109
105
|
file = ActionController::UploadedTempfile.new("uploaded-theme").tap do |f|
|
110
106
|
f.puts file.read
|
111
107
|
f.original_filename = file.original_filename
|
@@ -133,12 +129,12 @@ class Theme < ActiveRecord::Base
|
|
133
129
|
data = ''
|
134
130
|
entry.get_input_stream { |io| data = io.read }
|
135
131
|
data = StringIO.new(data) if data.present?
|
136
|
-
theme_file = self.files.where("name = ? and directory = ?", File.basename(name), File.join(
|
132
|
+
theme_file = self.files.where("name = ? and directory = ?", File.basename(name), File.join(self.url,File.dirname(name))).first
|
137
133
|
unless theme_file.nil?
|
138
134
|
theme_file.data = data
|
139
135
|
theme_file.save
|
140
136
|
else
|
141
|
-
self.add_file(data, File.join(file_support.root,self.url,name)) rescue next
|
137
|
+
self.add_file(data, File.join(file_support.root, self.url,name)) rescue next
|
142
138
|
end
|
143
139
|
end
|
144
140
|
end
|
@@ -146,17 +142,17 @@ class Theme < ActiveRecord::Base
|
|
146
142
|
end
|
147
143
|
|
148
144
|
def export
|
149
|
-
file_support = ErpTechSvcs::FileSupport::Base.new(:storage =>
|
145
|
+
file_support = ErpTechSvcs::FileSupport::Base.new(:storage => Rails.application.config.erp_tech_svcs.file_storage)
|
150
146
|
tmp_dir = Theme.make_tmp_dir
|
151
147
|
(tmp_dir + "#{name}[#{theme_id}].zip").tap do |file_name|
|
152
148
|
file_name.unlink if file_name.exist?
|
153
149
|
Zip::ZipFile.open(file_name, Zip::ZipFile::CREATE) do |zip|
|
154
150
|
files.each {|file|
|
155
151
|
contents = file_support.get_contents(File.join(file_support.root,file.directory,file.name))
|
156
|
-
relative_path = file.directory.sub("
|
152
|
+
relative_path = file.directory.sub("#{url}",'')
|
157
153
|
path = FileUtils.mkdir_p(File.join(tmp_dir,relative_path))
|
158
154
|
full_path = File.join(path,file.name)
|
159
|
-
File.open(full_path, 'w
|
155
|
+
File.open(full_path, 'w+:ASCII-8BIT') {|f| f.puts(contents) }
|
160
156
|
zip.add(File.join(relative_path[1..relative_path.length],file.name), full_path) if ::File.exists?(full_path)
|
161
157
|
}
|
162
158
|
::File.open(tmp_dir + 'about.yml', 'w') { |f| f.puts(about.to_yaml) }
|
@@ -202,7 +198,7 @@ class Theme < ActiveRecord::Base
|
|
202
198
|
end
|
203
199
|
|
204
200
|
def delete_theme_files!
|
205
|
-
file_support = ErpTechSvcs::FileSupport::Base.new(:storage => ErpTechSvcs::
|
201
|
+
file_support = ErpTechSvcs::FileSupport::Base.new(:storage => ErpTechSvcs::Config.file_storage)
|
206
202
|
file_support.delete_file(File.join(file_support.root,self.url), :force => true)
|
207
203
|
end
|
208
204
|
|
@@ -228,9 +224,9 @@ class Theme < ActiveRecord::Base
|
|
228
224
|
|
229
225
|
path = case type
|
230
226
|
when :widgets
|
231
|
-
path.gsub(options[:path_to_replace], "
|
227
|
+
path.gsub(options[:path_to_replace], "#{self.url}/widgets/#{options[:widget_name]}")
|
232
228
|
else
|
233
|
-
path.gsub(options[:path_to_replace], "
|
229
|
+
path.gsub(options[:path_to_replace], "#{self.url}/#{type.to_s}")
|
234
230
|
end
|
235
231
|
|
236
232
|
self.add_file(contents, path)
|
data/app/models/website.rb
CHANGED
@@ -1,16 +1,20 @@
|
|
1
1
|
class Website < ActiveRecord::Base
|
2
2
|
after_destroy :remove_sites_directory, :remove_website_role
|
3
3
|
after_create :setup_website
|
4
|
-
|
4
|
+
|
5
5
|
has_file_assets
|
6
|
-
|
6
|
+
has_permalink :name, :internal_identifier, :update => false
|
7
|
+
|
7
8
|
has_many :published_websites, :dependent => :destroy
|
8
9
|
has_many :website_hosts, :dependent => :destroy
|
9
10
|
has_many :website_navs, :dependent => :destroy
|
10
11
|
has_many :website_inquiries, :dependent => :destroy
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
has_many :website_party_roles, :dependent => :destroy
|
13
|
+
has_many :parties, :through => :website_party_roles do
|
14
|
+
def owners
|
15
|
+
where('role_type_id = ?', RoleType.website_owner)
|
16
|
+
end
|
17
|
+
end
|
14
18
|
has_many :website_sections, :dependent => :destroy, :order => :lft do
|
15
19
|
def paths
|
16
20
|
collect{|website_section| website_section.paths}.flatten
|
@@ -20,21 +24,44 @@ class Website < ActiveRecord::Base
|
|
20
24
|
def positioned
|
21
25
|
where('parent_id is null').sort_by!(&:position)
|
22
26
|
end
|
23
|
-
|
27
|
+
|
24
28
|
def update_paths!
|
25
29
|
all.each{|section| section.self_and_descendants.each{|_section| _section.update_path!}}
|
26
30
|
end
|
27
31
|
end
|
32
|
+
|
28
33
|
alias :sections :website_sections
|
29
|
-
|
34
|
+
|
35
|
+
has_many :online_document_sections, :dependent => :destroy, :order => :lft, :class_name => 'OnlineDocumentSection'
|
36
|
+
|
30
37
|
has_many :themes, :dependent => :destroy do
|
31
38
|
def active
|
32
39
|
where('active = 1')
|
33
40
|
end
|
34
41
|
end
|
35
42
|
|
43
|
+
validates_uniqueness_of :internal_identifier
|
44
|
+
|
45
|
+
alias :sections :website_sections
|
46
|
+
alias :hosts :website_hosts
|
47
|
+
|
48
|
+
def to_label
|
49
|
+
self.name
|
50
|
+
end
|
51
|
+
|
52
|
+
# creating method because we only want a getter, not a setter for iid
|
53
|
+
def iid
|
54
|
+
self.internal_identifier
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_party_with_role(party, role_type)
|
58
|
+
self.website_party_roles << WebsitePartyRole.create(:party => party, :role_type => role_type)
|
59
|
+
self.save
|
60
|
+
end
|
61
|
+
|
36
62
|
def all_section_paths
|
37
|
-
|
63
|
+
WebsiteSection.select(:path).where(:website_id => self.id).collect{|row| row['path']}
|
64
|
+
#ActiveRecord::Base.connection.execute("select path from website_sections where website_id = #{self.id}").collect{|row| row['path']}
|
38
65
|
end
|
39
66
|
|
40
67
|
def self.find_by_host(host)
|
@@ -63,7 +90,7 @@ class Website < ActiveRecord::Base
|
|
63
90
|
end
|
64
91
|
|
65
92
|
def active_publication
|
66
|
-
self.published_websites.
|
93
|
+
self.published_websites.where(:active => true).first
|
67
94
|
end
|
68
95
|
|
69
96
|
def role
|
@@ -73,12 +100,18 @@ class Website < ActiveRecord::Base
|
|
73
100
|
def setup_website
|
74
101
|
PublishedWebsite.create(:website => self, :version => 0, :active => true, :comment => 'New Site Created')
|
75
102
|
Role.create(:description => "Website #{self.title}", :internal_identifier => website_role_iid)
|
103
|
+
configuration = ::Configuration.find_template('default_website_configuration').clone(true)
|
104
|
+
configuration.update_configuration_item(ConfigurationItemType.find_by_internal_identifier('contact_us_email_address'), self.email)
|
105
|
+
configuration.update_configuration_item(ConfigurationItemType.find_by_internal_identifier('login_url'), '/login')
|
106
|
+
configuration.update_configuration_item(ConfigurationItemType.find_by_internal_identifier('homepage_url'), '/home')
|
107
|
+
self.configurations << configuration
|
108
|
+
self.save
|
76
109
|
end
|
77
110
|
|
78
111
|
def remove_sites_directory
|
79
|
-
file_support = ErpTechSvcs::FileSupport::Base.new(:storage =>
|
112
|
+
file_support = ErpTechSvcs::FileSupport::Base.new(:storage => Rails.application.config.erp_tech_svcs.file_storage)
|
80
113
|
begin
|
81
|
-
file_support.delete_file(File.join(file_support.root,"sites
|
114
|
+
file_support.delete_file(File.join(file_support.root,"sites/#{self.iid}"), :force => true)
|
82
115
|
rescue
|
83
116
|
#do nothing it may not exist
|
84
117
|
end
|
@@ -87,17 +120,17 @@ class Website < ActiveRecord::Base
|
|
87
120
|
def remove_website_role
|
88
121
|
role.destroy if role
|
89
122
|
end
|
90
|
-
|
123
|
+
|
91
124
|
def setup_default_pages
|
92
125
|
# create default sections for each widget using widget layout
|
93
126
|
widget_classes = [
|
94
|
-
::Widgets::ContactUs::Base,
|
95
|
-
::Widgets::Search::Base,
|
127
|
+
::Widgets::ContactUs::Base,
|
128
|
+
::Widgets::Search::Base,
|
96
129
|
::Widgets::ManageProfile::Base,
|
97
130
|
::Widgets::Login::Base,
|
98
131
|
::Widgets::Signup::Base,
|
99
132
|
::Widgets::ResetPassword::Base
|
100
|
-
]
|
133
|
+
]
|
101
134
|
widget_classes.each do |widget_class|
|
102
135
|
website_section = WebsiteSection.new
|
103
136
|
website_section.title = widget_class.title
|
@@ -118,6 +151,7 @@ class Website < ActiveRecord::Base
|
|
118
151
|
:hosts => hosts.collect(&:host),
|
119
152
|
:title => title,
|
120
153
|
:subtitle => subtitle,
|
154
|
+
:internal_identifier => internal_identifier,
|
121
155
|
:email => email,
|
122
156
|
:auto_activate_publication => auto_activate_publication,
|
123
157
|
:email_inquiries => email_inquiries,
|
@@ -128,7 +162,7 @@ class Website < ActiveRecord::Base
|
|
128
162
|
}
|
129
163
|
|
130
164
|
setup_hash[:sections] = sections.positioned.collect do |website_section|
|
131
|
-
build_section_hash
|
165
|
+
website_section.build_section_hash
|
132
166
|
end
|
133
167
|
|
134
168
|
setup_hash[:website_navs] = website_navs.collect do |website_nav|
|
@@ -138,11 +172,11 @@ class Website < ActiveRecord::Base
|
|
138
172
|
}
|
139
173
|
end
|
140
174
|
|
141
|
-
self.files.where("directory like '%/sites
|
175
|
+
self.files.where("directory like '%/sites/#{self.iid}/images%'").all.each do |image_asset|
|
142
176
|
setup_hash[:images] << {:path => image_asset.directory, :name => image_asset.name}
|
143
177
|
end
|
144
178
|
|
145
|
-
self.files.where("directory like '
|
179
|
+
self.files.where("directory like '%/#{Rails.application.config.erp_tech_svcs.file_assets_location}/sites/#{self.iid}%'").all.each do |file_asset|
|
146
180
|
setup_hash[:files] << {:path => file_asset.directory, :name => file_asset.name}
|
147
181
|
end
|
148
182
|
|
@@ -151,13 +185,16 @@ class Website < ActiveRecord::Base
|
|
151
185
|
|
152
186
|
def export
|
153
187
|
tmp_dir = Website.make_tmp_dir
|
154
|
-
file_support = ErpTechSvcs::FileSupport::Base.new(:storage =>
|
155
|
-
|
188
|
+
file_support = ErpTechSvcs::FileSupport::Base.new(:storage => Rails.application.config.erp_tech_svcs.file_storage)
|
189
|
+
|
156
190
|
sections_path = Pathname.new(File.join(tmp_dir,'sections'))
|
157
191
|
FileUtils.mkdir_p(sections_path) unless sections_path.exist?
|
158
192
|
|
159
193
|
articles_path = Pathname.new(File.join(tmp_dir,'articles'))
|
160
194
|
FileUtils.mkdir_p(articles_path) unless articles_path.exist?
|
195
|
+
|
196
|
+
documented_contents_path = Pathname.new(File.join(tmp_dir, 'documented contents'))
|
197
|
+
FileUtils.mkdir_p(documented_contents_path) unless documented_contents_path.exist?
|
161
198
|
|
162
199
|
excerpts_path = Pathname.new(File.join(tmp_dir,'excerpts'))
|
163
200
|
FileUtils.mkdir_p(excerpts_path) unless excerpts_path.exist?
|
@@ -170,62 +207,49 @@ class Website < ActiveRecord::Base
|
|
170
207
|
|
171
208
|
sections.each do |website_section|
|
172
209
|
unless website_section.layout.blank?
|
173
|
-
File.open(File.join(sections_path,"#{website_section.
|
210
|
+
File.open(File.join(sections_path,"#{website_section.internal_identifier}.rhtml"), 'w+') {|f| f.puts(website_section.layout) }
|
174
211
|
end
|
175
212
|
end
|
176
213
|
|
177
214
|
contents = sections.collect(&:contents).flatten.uniq
|
178
215
|
contents.each do |content|
|
179
|
-
File.open(File.join(articles_path,"#{content.
|
216
|
+
File.open(File.join(articles_path,"#{content.internal_identifier}.html"), 'w+') {|f| f.puts(content.body_html) }
|
180
217
|
unless content.excerpt_html.blank?
|
181
|
-
File.open(File.join(excerpts_path,"#{content.
|
218
|
+
File.open(File.join(excerpts_path,"#{content.internal_identifier}.html"), 'w+') {|f| f.puts(content.excerpt_html) }
|
182
219
|
end
|
183
220
|
end
|
184
221
|
|
185
|
-
|
222
|
+
online_document_sections.each do |online_documented_section|
|
223
|
+
File.open(File.join(documented_contents_path,"#{online_documented_section.internal_identifier}.html"), 'w+') {|f| f.puts(online_documented_section.documented_item_published_content_html(active_publication)) }
|
224
|
+
end
|
225
|
+
|
226
|
+
self.files.where("directory like '%/sites/#{self.iid}/images%'").all.each do |image_asset|
|
186
227
|
contents = file_support.get_contents(File.join(file_support.root,image_asset.directory,image_asset.name))
|
187
|
-
|
228
|
+
FileUtils.mkdir_p(File.join(image_assets_path,image_asset.directory))
|
229
|
+
File.open(File.join(image_assets_path,image_asset.directory,image_asset.name), 'w+:ASCII-8BIT') {|f| f.puts(contents) }
|
188
230
|
end
|
189
231
|
|
190
|
-
self.files.where("directory like '
|
232
|
+
self.files.where("directory like '%/#{Rails.application.config.erp_tech_svcs.file_assets_location}/sites/#{self.iid}%'").all.each do |file_asset|
|
191
233
|
contents = file_support.get_contents(File.join(file_support.root,file_asset.directory,file_asset.name))
|
192
|
-
|
234
|
+
FileUtils.mkdir_p(File.join(file_assets_path,file_asset.directory))
|
235
|
+
File.open(File.join(file_assets_path,file_asset.directory,file_asset.name), 'w+:ASCII-8BIT') {|f| f.puts(contents) }
|
193
236
|
end
|
194
237
|
|
195
238
|
files = []
|
196
|
-
|
197
|
-
Dir.entries(sections_path).each do |entry|
|
239
|
+
Dir.glob("#{tmp_dir.to_s}/**/*").each do |entry|
|
198
240
|
next if entry =~ /^\./
|
199
|
-
|
241
|
+
path = entry
|
242
|
+
entry = entry.gsub(Regexp.new(tmp_dir.to_s), '')
|
243
|
+
entry = entry.gsub(Regexp.new(/^\//), '')
|
244
|
+
files << {:path => path, :name => entry}
|
200
245
|
end
|
201
246
|
|
202
|
-
|
203
|
-
next if entry =~ /^\./
|
204
|
-
files << {:path => File.join(articles_path,entry), :name => File.join('articles/',File.basename(entry))}
|
205
|
-
end
|
206
|
-
|
207
|
-
Dir.entries(excerpts_path).each do |entry|
|
208
|
-
next if entry =~ /^\./
|
209
|
-
files << {:path => File.join(excerpts_path,entry), :name => File.join('excerpts/',File.basename(entry))}
|
210
|
-
end
|
211
|
-
|
212
|
-
Dir.entries(image_assets_path).each do |entry|
|
213
|
-
next if entry =~ /^\./
|
214
|
-
files << {:path => File.join(image_assets_path,entry), :name => File.join('images/',File.basename(entry))}
|
215
|
-
end
|
216
|
-
|
217
|
-
Dir.entries(file_assets_path).each do |entry|
|
218
|
-
next if entry =~ /^\./
|
219
|
-
files << {:path => File.join(file_assets_path,entry), :name => File.join('files/',File.basename(entry))}
|
220
|
-
end
|
221
|
-
|
222
|
-
files.uniq!
|
247
|
+
File.open(tmp_dir + 'setup.yml', 'w') { |f| f.puts(export_setup.to_yaml) }
|
223
248
|
|
224
249
|
(tmp_dir + "#{name}.zip").tap do |file_name|
|
225
250
|
file_name.unlink if file_name.exist?
|
226
251
|
Zip::ZipFile.open(file_name, Zip::ZipFile::CREATE) do |zip|
|
227
252
|
files.each { |file| zip.add(file[:name], file[:path]) if File.exists?(file[:path]) }
|
228
|
-
File.open(tmp_dir + 'setup.yml', 'w') { |f| f.puts(export_setup.to_yaml) }
|
229
253
|
zip.add('setup.yml', tmp_dir + 'setup.yml')
|
230
254
|
end
|
231
255
|
end
|
@@ -233,14 +257,13 @@ class Website < ActiveRecord::Base
|
|
233
257
|
|
234
258
|
class << self
|
235
259
|
def make_tmp_dir
|
236
|
-
|
237
|
-
Pathname.new(Rails.root + "/tmp/website_export/tmp_#{random}/").tap do |dir|
|
260
|
+
Pathname.new(Rails.root + "/tmp/website_export/tmp_#{Time.now.to_i.to_s}/").tap do |dir|
|
238
261
|
FileUtils.mkdir_p(dir) unless dir.exist?
|
239
262
|
end
|
240
263
|
end
|
241
264
|
|
242
265
|
def import(file, current_user)
|
243
|
-
file_support = ErpTechSvcs::FileSupport::Base.new(:storage =>
|
266
|
+
file_support = ErpTechSvcs::FileSupport::Base.new(:storage => Rails.application.config.erp_tech_svcs.file_storage)
|
244
267
|
message = ''
|
245
268
|
success = true
|
246
269
|
|
@@ -249,12 +272,17 @@ class Website < ActiveRecord::Base
|
|
249
272
|
f.original_filename = file.original_filename
|
250
273
|
f.read # no idea why we need this here, otherwise the zip can't be opened
|
251
274
|
end unless file.path
|
252
|
-
|
275
|
+
|
253
276
|
entries = []
|
254
277
|
setup_hash = nil
|
255
278
|
|
279
|
+
tmp_dir = Website.make_tmp_dir
|
256
280
|
Zip::ZipFile.open(file.path) do |zip|
|
257
281
|
zip.each do |entry|
|
282
|
+
f_path = File.join(tmp_dir.to_s, entry.name)
|
283
|
+
FileUtils.mkdir_p(File.dirname(f_path))
|
284
|
+
zip.extract(entry, f_path) unless File.exist?(f_path)
|
285
|
+
|
258
286
|
next if entry.name =~ /__MACOSX\//
|
259
287
|
if entry.name =~ /setup.yml/
|
260
288
|
data = ''
|
@@ -262,46 +290,64 @@ class Website < ActiveRecord::Base
|
|
262
290
|
data = StringIO.new(data) if data.present?
|
263
291
|
setup_hash = YAML.load(data)
|
264
292
|
else
|
265
|
-
type = entry.name.split('/')[
|
293
|
+
type = entry.name.split('/')[0]
|
266
294
|
name = entry.name.split('/').last
|
267
295
|
next if name.nil?
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
296
|
+
|
297
|
+
if File.exist?(f_path) and !File.directory?(f_path)
|
298
|
+
entry_hash = {:type => type, :name => name, :path => entry.name}
|
299
|
+
entries << entry_hash unless name == 'sections' || name == 'articles' || name == 'excerpts' || name == 'documented contents'
|
300
|
+
entry_hash[:data] = File.open(f_path,"rb") {|io| io.read}
|
301
|
+
end
|
274
302
|
end
|
303
|
+
|
275
304
|
end
|
276
305
|
end
|
306
|
+
entries.uniq!
|
307
|
+
FileUtils.rm_rf(tmp_dir.to_s)
|
277
308
|
|
278
|
-
if Website.
|
309
|
+
if Website.find_by_internal_identifier(setup_hash[:internal_identifier]).nil?
|
279
310
|
website = Website.create(
|
280
311
|
:name => setup_hash[:name],
|
281
312
|
:title => setup_hash[:title],
|
282
313
|
:subtitle => setup_hash[:subtitle],
|
314
|
+
:internal_identifier => setup_hash[:internal_identifier],
|
283
315
|
:email => setup_hash[:email],
|
284
316
|
:email_inquiries => setup_hash[:email_inquiries],
|
285
317
|
:auto_activate_publication => setup_hash[:auto_activate_publication]
|
286
318
|
)
|
287
|
-
|
319
|
+
|
288
320
|
#set default publication published by user
|
289
321
|
first_publication = website.published_websites.first
|
290
322
|
first_publication.published_by = current_user
|
291
323
|
first_publication.save
|
292
|
-
|
293
|
-
begin
|
294
324
|
|
325
|
+
begin
|
295
326
|
#handle images
|
327
|
+
# entries.each do |entry|
|
328
|
+
# puts "entry type '#{entry[:type]}'"
|
329
|
+
# puts "entry name '#{entry[:name]}'"
|
330
|
+
# puts "entry path '#{entry[:path]}'"
|
331
|
+
# puts "entry data #{!entry[:data].blank?}"
|
332
|
+
# end
|
333
|
+
# puts "------------------"
|
296
334
|
setup_hash[:images].each do |image_asset|
|
297
|
-
|
298
|
-
|
335
|
+
filename = 'images' + image_asset[:path] + '/' + image_asset[:name]
|
336
|
+
#puts "image_asset '#{filename}'"
|
337
|
+
content = entries.find{|entry| entry[:type] == 'images' and entry[:path] == filename }
|
338
|
+
unless content.nil?
|
339
|
+
website.add_file(content[:data], File.join(file_support.root, image_asset[:path], image_asset[:name]))
|
340
|
+
end
|
299
341
|
end
|
300
342
|
|
301
343
|
#handle files
|
302
344
|
setup_hash[:files].each do |file_asset|
|
303
|
-
|
304
|
-
|
345
|
+
filename = 'files' + file_asset[:path] + '/' + file_asset[:name]
|
346
|
+
#puts "file_asset '#{filename}'"
|
347
|
+
content = entries.find{|entry| entry[:type] == 'files' and entry[:path] == filename }
|
348
|
+
unless content.nil?
|
349
|
+
website.add_file(content[:data], File.join(file_support.root, file_asset[:path], file_asset[:name]))
|
350
|
+
end
|
305
351
|
end
|
306
352
|
|
307
353
|
#handle hosts
|
@@ -312,7 +358,7 @@ class Website < ActiveRecord::Base
|
|
312
358
|
|
313
359
|
#handle sections
|
314
360
|
setup_hash[:sections].each do |section_hash|
|
315
|
-
build_section(section_hash, entries, website)
|
361
|
+
build_section(section_hash, entries, website, current_user)
|
316
362
|
end
|
317
363
|
website.website_sections.update_paths!
|
318
364
|
|
@@ -335,10 +381,10 @@ class Website < ActiveRecord::Base
|
|
335
381
|
website.save
|
336
382
|
success = true
|
337
383
|
else
|
338
|
-
message = 'Website already exists with that
|
384
|
+
message = 'Website already exists with that internal_identifier'
|
339
385
|
success = false
|
340
386
|
end
|
341
|
-
|
387
|
+
|
342
388
|
return success, message
|
343
389
|
end
|
344
390
|
|
@@ -366,22 +412,30 @@ class Website < ActiveRecord::Base
|
|
366
412
|
website_item
|
367
413
|
end
|
368
414
|
|
369
|
-
def build_section(hash, entries, website)
|
415
|
+
def build_section(hash, entries, website, current_user)
|
370
416
|
klass = hash[:type].constantize
|
371
|
-
section = klass.new(:title => hash[:name],
|
417
|
+
section = klass.new(:title => hash[:name],
|
418
|
+
:in_menu => hash[:in_menu],
|
419
|
+
:render_base_layout => hash[:render_base_layout],
|
420
|
+
:position => hash[:position],
|
421
|
+
:render_base_layout => hash[:render_base_layout])
|
422
|
+
section.internal_identifier = hash[:internal_identifier]
|
372
423
|
section.permalink = hash[:permalink]
|
373
424
|
section.path = hash[:path]
|
374
|
-
|
375
|
-
|
425
|
+
content = entries.find{|entry| entry[:type] == 'sections' and entry[:name] == "#{hash[:internal_identifier]}.rhtml"}
|
426
|
+
unless content.nil?
|
427
|
+
section.layout = content[:data]
|
376
428
|
end
|
377
429
|
hash[:articles].each do |article_hash|
|
378
|
-
article = Article.
|
430
|
+
article = Article.find_by_internal_identifier(article_hash[:internal_identifier])
|
379
431
|
if article.nil?
|
380
432
|
article = Article.new(:title => article_hash[:name], :display_title => article_hash[:display_title])
|
433
|
+
article.created_by = current_user
|
381
434
|
article.tag_list = article_hash[:tag_list].split(',').collect{|t| t.strip() } unless article_hash[:tag_list].blank?
|
382
|
-
article.body_html = entries.find{|entry| entry[:type] == 'articles' and entry[:name] == "#{article_hash[:
|
383
|
-
|
384
|
-
|
435
|
+
article.body_html = entries.find{|entry| entry[:type] == 'articles' and entry[:name] == "#{article_hash[:internal_identifier]}.html"}[:data]
|
436
|
+
content = entries.find{|entry| entry[:type] == 'excerpts' and entry[:name] == "#{article_hash[:internal_identifier]}.html"}
|
437
|
+
unless content.nil?
|
438
|
+
article.excerpt_html = content[:data]
|
385
439
|
end
|
386
440
|
end
|
387
441
|
section.contents << article
|
@@ -390,9 +444,26 @@ class Website < ActiveRecord::Base
|
|
390
444
|
end
|
391
445
|
website.website_sections << section
|
392
446
|
section.save
|
393
|
-
hash[:sections]
|
394
|
-
|
395
|
-
|
447
|
+
if hash[:sections]
|
448
|
+
hash[:sections].each do |section_hash|
|
449
|
+
child_section = build_section(section_hash, entries, website, current_user)
|
450
|
+
child_section.move_to_child_of(section)
|
451
|
+
end
|
452
|
+
end
|
453
|
+
if section.is_a? OnlineDocumentSection
|
454
|
+
entry_data = entries.find{|entry| entry[:type] == 'documented contents' and entry[:name] == "#{section.internal_identifier}.html"}[:data]
|
455
|
+
documented_content = DocumentedContent.create(:title => section.title, :body_html => entry_data)
|
456
|
+
DocumentedItem.create(:documented_content_id => documented_content.id, :online_document_section_id => section.id)
|
457
|
+
end
|
458
|
+
if hash[:online_document_sections]
|
459
|
+
hash[:online_document_sections].each do |section_hash|
|
460
|
+
child_section = build_section(section_hash, entries, website, current_user)
|
461
|
+
child_section.move_to_child_of(section)
|
462
|
+
# CREATE THE DOCUMENTED CONTENT HERE
|
463
|
+
entry_data = entries.find{|entry| entry[:type] == 'documented contents' and entry[:name] == "#{child_section.internal_identifier}.html"}[:data]
|
464
|
+
documented_content = DocumentedContent.create(:title => child_section.title, :body_html => entry_data)
|
465
|
+
DocumentedItem.create(:documented_content_id => documented_content.id, :online_document_section_id => child_section.id)
|
466
|
+
end
|
396
467
|
end
|
397
468
|
unless hash[:roles].empty?
|
398
469
|
hash[:roles].each do |role|
|
@@ -401,46 +472,14 @@ class Website < ActiveRecord::Base
|
|
401
472
|
end
|
402
473
|
section
|
403
474
|
end
|
404
|
-
|
475
|
+
|
405
476
|
end
|
406
477
|
|
407
|
-
private
|
408
|
-
|
409
478
|
def website_role_iid
|
410
479
|
"website_#{self.name.underscore.gsub("'","").gsub(",","")}_access"
|
411
480
|
end
|
412
|
-
|
413
|
-
def build_section_hash(section)
|
414
|
-
section_hash = {
|
415
|
-
:name => section.title,
|
416
|
-
:has_layout => !section.layout.blank?,
|
417
|
-
:type => section.class.to_s,
|
418
|
-
:in_menu => section.in_menu,
|
419
|
-
:articles => [],
|
420
|
-
:roles => section.roles.collect{|role| role.internal_identifier},
|
421
|
-
:path => section.path,
|
422
|
-
:permalink => section.permalink,
|
423
|
-
:internal_identifier => section.internal_identifier,
|
424
|
-
:position => section.position,
|
425
|
-
:sections => section.children.each.map{|child| build_section_hash(child)}
|
426
|
-
}
|
427
|
-
|
428
|
-
section.contents.each do |content|
|
429
|
-
content_area = content.content_area_by_website_section(section)
|
430
|
-
position = content.position_by_website_section(section)
|
431
|
-
section_hash[:articles] << {
|
432
|
-
:name => content.title,
|
433
|
-
:tag_list => content.tag_list.join(', '),
|
434
|
-
:content_area => content_area,
|
435
|
-
:position => position,
|
436
|
-
:display_title => content.display_title,
|
437
|
-
:internal_identifier => content.internal_identifier
|
438
|
-
}
|
439
|
-
end
|
440
|
-
|
441
|
-
section_hash
|
442
|
-
end
|
443
481
|
|
482
|
+
private
|
444
483
|
|
445
484
|
def build_menu_item_hash(menu_item)
|
446
485
|
{
|