knitkit 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. data/GPL-3-LICENSE +674 -0
  2. data/README.md +6 -0
  3. data/Rakefile +30 -0
  4. data/app/assets/javascripts/knitkit/application.js +9 -0
  5. data/app/assets/stylesheets/knitkit/application.css +7 -0
  6. data/app/controllers/knitkit/articles_controller.rb +7 -0
  7. data/app/controllers/knitkit/base_controller.rb +45 -0
  8. data/app/controllers/knitkit/blogs_controller.rb +27 -0
  9. data/app/controllers/knitkit/comments_controller.rb +18 -0
  10. data/app/controllers/knitkit/erp_app/desktop/app_controller.rb +135 -0
  11. data/app/controllers/knitkit/erp_app/desktop/articles_controller.rb +144 -0
  12. data/app/controllers/knitkit/erp_app/desktop/comments_controller.rb +43 -0
  13. data/app/controllers/knitkit/erp_app/desktop/content_controller.rb +27 -0
  14. data/app/controllers/knitkit/erp_app/desktop/file_assets_controller.rb +145 -0
  15. data/app/controllers/knitkit/erp_app/desktop/image_assets_controller.rb +28 -0
  16. data/app/controllers/knitkit/erp_app/desktop/inquiries_controller.rb +68 -0
  17. data/app/controllers/knitkit/erp_app/desktop/position_controller.rb +20 -0
  18. data/app/controllers/knitkit/erp_app/desktop/theme_controller.rb +272 -0
  19. data/app/controllers/knitkit/erp_app/desktop/versions_controller.rb +135 -0
  20. data/app/controllers/knitkit/erp_app/desktop/website_controller.rb +177 -0
  21. data/app/controllers/knitkit/erp_app/desktop/website_nav_controller.rb +143 -0
  22. data/app/controllers/knitkit/erp_app/desktop/website_section_controller.rb +116 -0
  23. data/app/controllers/knitkit/unauthorized_controller.rb +8 -0
  24. data/app/controllers/knitkit/website_sections_controller.rb +17 -0
  25. data/app/helpers/application_helper.rb +132 -0
  26. data/app/models/article.rb +14 -0
  27. data/app/models/blog.rb +21 -0
  28. data/app/models/comment.rb +24 -0
  29. data/app/models/content.rb +158 -0
  30. data/app/models/extensions/user.rb +5 -0
  31. data/app/models/published_element.rb +9 -0
  32. data/app/models/published_website.rb +118 -0
  33. data/app/models/theme.rb +239 -0
  34. data/app/models/website.rb +457 -0
  35. data/app/models/website_host.rb +3 -0
  36. data/app/models/website_inquiry.rb +11 -0
  37. data/app/models/website_inquiry_mailer.rb +12 -0
  38. data/app/models/website_nav.rb +17 -0
  39. data/app/models/website_nav_item.rb +21 -0
  40. data/app/models/website_section.rb +152 -0
  41. data/app/models/website_section_content.rb +4 -0
  42. data/app/views/knitkit/articles/index.html.erb +3 -0
  43. data/app/views/knitkit/articles/show.html.erb +4 -0
  44. data/app/views/knitkit/blogs/_add_comment.html.erb +28 -0
  45. data/app/views/knitkit/blogs/_comment.html.erb +8 -0
  46. data/app/views/knitkit/blogs/index.html.erb +51 -0
  47. data/app/views/knitkit/blogs/index.rss.builder +23 -0
  48. data/app/views/knitkit/blogs/show.html.erb +22 -0
  49. data/app/views/knitkit/unauthorized/index.html.erb +4 -0
  50. data/app/views/knitkit/website_sections/index.html.erb +6 -0
  51. data/app/views/layouts/knitkit/base.html.erb +59 -0
  52. data/app/views/menus/knitkit/_default_menu.html.erb +23 -0
  53. data/app/views/menus/knitkit/_default_section_menu.html.erb +27 -0
  54. data/app/views/menus/knitkit/_default_sub_menu.html.erb +45 -0
  55. data/app/views/menus/knitkit/_default_sub_section_menu.html.erb +49 -0
  56. data/app/views/shared/knitkit/_bread_crumb.html.erb +7 -0
  57. data/app/views/shared/knitkit/_footer.html.erb +3 -0
  58. data/app/views/website_inquiry_mailer/inquiry.erb +3 -0
  59. data/app/widgets/contact_us/base.rb +86 -0
  60. data/app/widgets/contact_us/javascript/contact_us.js +13 -0
  61. data/app/widgets/contact_us/views/_contact_form.html.erb +36 -0
  62. data/app/widgets/contact_us/views/error.html.erb +10 -0
  63. data/app/widgets/contact_us/views/index.html.erb +1 -0
  64. data/app/widgets/contact_us/views/layouts/base.html.erb +7 -0
  65. data/app/widgets/contact_us/views/success.html.erb +4 -0
  66. data/app/widgets/google_map/base.rb +48 -0
  67. data/app/widgets/google_map/javascript/google_map.js +174 -0
  68. data/app/widgets/google_map/views/index.html.erb +41 -0
  69. data/app/widgets/login/base.rb +61 -0
  70. data/app/widgets/login/javascript/login.js +162 -0
  71. data/app/widgets/login/views/index.html.erb +37 -0
  72. data/app/widgets/login/views/layouts/base.html.erb +14 -0
  73. data/app/widgets/login/views/login_header.html.erb +9 -0
  74. data/app/widgets/login/views/reset_password.html.erb +26 -0
  75. data/app/widgets/manage_profile/base.rb +327 -0
  76. data/app/widgets/manage_profile/javascript/manage_profile.js +11 -0
  77. data/app/widgets/manage_profile/views/_contact_information_form.html.erb +180 -0
  78. data/app/widgets/manage_profile/views/_password_form.html.erb +20 -0
  79. data/app/widgets/manage_profile/views/_user_information_form.html.erb +30 -0
  80. data/app/widgets/manage_profile/views/contact_type_in_use.html.erb +4 -0
  81. data/app/widgets/manage_profile/views/default_type_error.html.erb +5 -0
  82. data/app/widgets/manage_profile/views/error.html.erb +3 -0
  83. data/app/widgets/manage_profile/views/index.html.erb +46 -0
  84. data/app/widgets/manage_profile/views/layouts/base.html.erb +1 -0
  85. data/app/widgets/manage_profile/views/password_blank.html.erb +4 -0
  86. data/app/widgets/manage_profile/views/password_invalid.html.erb +4 -0
  87. data/app/widgets/manage_profile/views/password_success.html.erb +3 -0
  88. data/app/widgets/manage_profile/views/success.html.erb +3 -0
  89. data/app/widgets/reset_password/base.rb +42 -0
  90. data/app/widgets/reset_password/javascript/reset_password.js +13 -0
  91. data/app/widgets/reset_password/views/index.html.erb +31 -0
  92. data/app/widgets/reset_password/views/layouts/base.html.erb +1 -0
  93. data/app/widgets/search/base.rb +80 -0
  94. data/app/widgets/search/javascript/search.js +31 -0
  95. data/app/widgets/search/views/_search.html.erb +39 -0
  96. data/app/widgets/search/views/index.html.erb +3 -0
  97. data/app/widgets/search/views/layouts/base.html.erb +24 -0
  98. data/app/widgets/search/views/show.html.erb +48 -0
  99. data/app/widgets/signup/base.rb +72 -0
  100. data/app/widgets/signup/javascript/signup.js +13 -0
  101. data/app/widgets/signup/views/_signup_form.html.erb +36 -0
  102. data/app/widgets/signup/views/error.html.erb +11 -0
  103. data/app/widgets/signup/views/index.html.erb +1 -0
  104. data/app/widgets/signup/views/layouts/base.html.erb +7 -0
  105. data/app/widgets/signup/views/success.html.erb +4 -0
  106. data/config/environment.rb +0 -0
  107. data/config/routes.rb +41 -0
  108. data/db/data_migrations/20110509223702_add_publisher_role.rb +11 -0
  109. data/db/data_migrations/20110816153456_add_knitkit_application.rb +31 -0
  110. data/db/data_migrations/upgrade/20111011203718_create_paths_for_sections.rb +13 -0
  111. data/db/data_migrations/upgrade/20111216192114_add_secured_models_to_menu_items.rb +18 -0
  112. data/db/data_migrations/upgrade/20111216202819_set_contents_iid_to_permalink_where_null.rb +12 -0
  113. data/db/migrate/20110211002317_setup_knitkit.rb +259 -0
  114. data/db/migrate/upgrade/20111014190442_update_contents.rb +25 -0
  115. data/db/migrate/upgrade/20111014201502_add_published_by_to_published_elements.rb +22 -0
  116. data/db/migrate/upgrade/20111017133851_add_iid_to_section.rb +21 -0
  117. data/db/migrate/upgrade/20111027145006_add_in_menu_to_section.rb +19 -0
  118. data/lib/knitkit.rb +12 -0
  119. data/lib/knitkit/engine.rb +26 -0
  120. data/lib/knitkit/extensions.rb +18 -0
  121. data/lib/knitkit/extensions/action_controller/theme_support/acts_as_themed_controller.rb +88 -0
  122. data/lib/knitkit/extensions/active_record/acts_as_commentable.rb +28 -0
  123. data/lib/knitkit/extensions/active_record/acts_as_publishable.rb +38 -0
  124. data/lib/knitkit/extensions/active_record/theme_support/has_many_themes.rb +34 -0
  125. data/lib/knitkit/extensions/compass/widgets/base.rb +53 -0
  126. data/lib/knitkit/extensions/core/array.rb +5 -0
  127. data/lib/knitkit/extensions/railties/action_view.rb +187 -0
  128. data/lib/knitkit/extensions/railties/theme_support/asset_tag_helper.rb +198 -0
  129. data/lib/knitkit/extensions/railties/theme_support/theme_file_resolver.rb +44 -0
  130. data/lib/knitkit/routing_filter/section_router.rb +55 -0
  131. data/lib/knitkit/syntax_validator.rb +32 -0
  132. data/lib/knitkit/version.rb +3 -0
  133. data/lib/tasks/knitkit_tasks.rake +4 -0
  134. data/public/images/knitkit/bullet.png +0 -0
  135. data/public/images/knitkit/content.png +0 -0
  136. data/public/images/knitkit/footer.png +0 -0
  137. data/public/images/knitkit/graphic.png +0 -0
  138. data/public/images/knitkit/greyFadeDown.png +0 -0
  139. data/public/images/knitkit/link.png +0 -0
  140. data/public/images/knitkit/logo.png +0 -0
  141. data/public/images/knitkit/menu.png +0 -0
  142. data/public/images/knitkit/menu_select.png +0 -0
  143. data/public/images/knitkit/search.png +0 -0
  144. data/public/javascripts/datepicker.js +440 -0
  145. data/public/javascripts/erp_app/desktop/applications/knitkit/articles_grid_panel.js +473 -0
  146. data/public/javascripts/erp_app/desktop/applications/knitkit/center_region.js +576 -0
  147. data/public/javascripts/erp_app/desktop/applications/knitkit/comments_grid_panel.js +217 -0
  148. data/public/javascripts/erp_app/desktop/applications/knitkit/east_region.js +26 -0
  149. data/public/javascripts/erp_app/desktop/applications/knitkit/file_assets_panel.js +109 -0
  150. data/public/javascripts/erp_app/desktop/applications/knitkit/image_assets_data_view.js +30 -0
  151. data/public/javascripts/erp_app/desktop/applications/knitkit/image_assets_panel.js +160 -0
  152. data/public/javascripts/erp_app/desktop/applications/knitkit/inquiries_grid_panel.js +46 -0
  153. data/public/javascripts/erp_app/desktop/applications/knitkit/module.js +46 -0
  154. data/public/javascripts/erp_app/desktop/applications/knitkit/publish_window.js +92 -0
  155. data/public/javascripts/erp_app/desktop/applications/knitkit/published_grid_panel.js +220 -0
  156. data/public/javascripts/erp_app/desktop/applications/knitkit/section_articles_grid_panel.js +613 -0
  157. data/public/javascripts/erp_app/desktop/applications/knitkit/themes_tree_panel.js +573 -0
  158. data/public/javascripts/erp_app/desktop/applications/knitkit/versions_grid_panel.js +664 -0
  159. data/public/javascripts/erp_app/desktop/applications/knitkit/west_region.js +2161 -0
  160. data/public/javascripts/erp_app/desktop/applications/knitkit/widgets_panel.js +52 -0
  161. data/public/stylesheets/datepicker.css +121 -0
  162. data/public/stylesheets/erp_app/desktop/applications/knitkit/knitkit.css +114 -0
  163. data/public/stylesheets/extjs/resources/css/knitkit_extjs_4.css +219 -0
  164. data/public/stylesheets/knitkit/style.css +394 -0
  165. metadata +289 -0
@@ -0,0 +1,5 @@
1
+ User.class_eval do
2
+ has_many :comments, :dependent => :nullify
3
+
4
+ alias :approved_comments :comments
5
+ end
@@ -0,0 +1,9 @@
1
+ class PublishedElement < ActiveRecord::Base
2
+ belongs_to :published_website
3
+ belongs_to :published_element_record, :polymorphic => true
4
+ belongs_to :published_by, :class_name => "User"
5
+
6
+ def published_by_username
7
+ self.published_by.username rescue ''
8
+ end
9
+ end
@@ -0,0 +1,118 @@
1
+ class PublishedWebsite < ActiveRecord::Base
2
+ belongs_to :website
3
+ belongs_to :published_by, :class_name => "User"
4
+ has_many :published_elements, :dependent => :destroy
5
+
6
+ def published_by_username
7
+ self.published_by.username rescue ''
8
+ end
9
+
10
+ def self.activate(website, version, current_user)
11
+ published_websites = self.where('website_id = ?', website.id).all
12
+ published_websites.each do |published_website|
13
+ if published_website.version == version
14
+ published_website.active = true
15
+ published_website.published_by = current_user
16
+ else
17
+ published_website.active = false
18
+ end
19
+ published_website.save
20
+ end
21
+ end
22
+
23
+ def publish(comment, current_user)
24
+ new_publication = clone_publication(1, comment, current_user)
25
+ elements = []
26
+
27
+ #get a publish sections
28
+ website_sections = new_publication.website.website_sections
29
+ website_sections = website_sections | website_sections.collect{|section| section.descendants}.flatten
30
+ website_sections.each do |website_section|
31
+ if new_publication.published_elements.where('published_element_record_id = ? and (published_element_record_type = ? or published_element_record_type = ?)', website_section.id, website_section.class.to_s, website_section.class.superclass.to_s).first.nil?
32
+ published_element = PublishedElement.new
33
+ published_element.published_website = new_publication
34
+ published_element.published_element_record = website_section
35
+ published_element.version = website_section.version
36
+ published_element.published_by = current_user
37
+ published_element.save
38
+ end
39
+
40
+ elements = elements | website_section.contents
41
+ end
42
+
43
+ #make sure all elements have published_element objects
44
+ elements.each do |element|
45
+ if new_publication.published_elements.where('published_element_record_id = ? and (published_element_record_type = ? or published_element_record_type = ?)', element.id, element.class.to_s, element.class.superclass.to_s).first.nil?
46
+ published_element = PublishedElement.new
47
+ published_element.published_website = new_publication
48
+ published_element.published_element_record = element
49
+ published_element.version = element.version
50
+ published_element.published_by = current_user
51
+ published_element.save
52
+ end
53
+ end
54
+
55
+ #get latest version for all elements
56
+ new_publication.published_elements.each do |published_element|
57
+ published_element.version = published_element.published_element_record.version
58
+ published_element.save
59
+ end
60
+
61
+ #check if we want to auto active this publication
62
+ if new_publication.website.auto_activate_publication?
63
+ PublishedWebsite.activate(new_publication.website, new_publication.version, current_user)
64
+ end
65
+ end
66
+
67
+ def publish_element(comment, element, version, current_user)
68
+ new_publication = clone_publication(0.1, comment, current_user)
69
+
70
+ published_element = new_publication.published_elements.where('published_element_record_id = ? and (published_element_record_type = ? or published_element_record_type = ?)', element.id, element.class.to_s, element.class.superclass.to_s).first
71
+
72
+ unless published_element.nil?
73
+ published_element.version = version
74
+ published_element.published_by = current_user
75
+ published_element.save
76
+ else
77
+ new_published_element = PublishedElement.new
78
+ new_published_element.published_website = new_publication
79
+ new_published_element.published_element_record = element
80
+ new_published_element.version = version
81
+ new_published_element.published_by = current_user
82
+ new_published_element.save
83
+ end
84
+
85
+ #check if we want to auto active this publication
86
+ if new_publication.website.auto_activate_publication?
87
+ PublishedWebsite.activate(new_publication.website, new_publication.version, current_user)
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def clone_publication(version_increment, comment, current_user)
94
+ #create new PublishedWebsite with comment
95
+ published_website = PublishedWebsite.new
96
+ published_website.website = self.website
97
+ if version_increment == 1
98
+ published_website.version = (self.version.to_i + version_increment)
99
+ else
100
+ published_website.version = (self.version + version_increment).round(6)
101
+ end
102
+ published_website.published_by = current_user
103
+ published_website.comment = comment
104
+ published_website.save
105
+
106
+ #create new PublishedWebsiteElements
107
+ published_elements.each do |published_element|
108
+ new_published_element = PublishedElement.new
109
+ new_published_element.published_website = published_website
110
+ new_published_element.published_element_record = published_element.published_element_record
111
+ new_published_element.version = published_element.version
112
+ new_published_element.published_by = current_user
113
+ new_published_element.save
114
+ end
115
+
116
+ published_website
117
+ end
118
+ end
@@ -0,0 +1,239 @@
1
+ require 'yaml'
2
+ require 'fileutils'
3
+
4
+ class Theme < ActiveRecord::Base
5
+ THEME_STRUCTURE = ['stylesheets', 'javascripts', 'images', 'templates']
6
+ BASE_LAYOUTS_VIEWS_PATH = "#{Knitkit::Engine.root.to_s}/app/views"
7
+ KNITKIT_WEBSITE_STYLESHEETS_PATH = "#{Knitkit::Engine.root.to_s}/public/stylesheets/knitkit"
8
+ KNITKIT_WEBSITE_IMAGES_PATH = "#{Knitkit::Engine.root.to_s}/public/images/knitkit"
9
+
10
+ has_file_assets
11
+
12
+ class << self
13
+ def root_dir
14
+ @@root_dir ||= "#{Rails.root}/public"
15
+ end
16
+
17
+ def base_dir(website)
18
+ "#{root_dir}/sites/site-#{website.id}/themes"
19
+ end
20
+
21
+ def import(file, website)
22
+ name_and_id = file.original_filename.to_s.gsub(/(^.*(\\|\/))|(\.zip$)/, '')
23
+ theme_name = name_and_id.split('[').first
24
+ theme_id = name_and_id.split('[').last.gsub(']','')
25
+ return false unless valid_theme?(file)
26
+ Theme.create(:name => theme_name, :theme_id => theme_id, :website => website).tap do |theme|
27
+ theme.import(file)
28
+ end
29
+ end
30
+
31
+ def make_tmp_dir
32
+ random = Time.now.to_i.to_s.split('').sort_by { rand }
33
+ Pathname.new(Rails.root.to_s + "/tmp/themes/tmp_#{random}/").tap do |dir|
34
+ FileUtils.mkdir_p(dir) unless dir.exist?
35
+ end
36
+ end
37
+
38
+ def valid_theme?(file)
39
+ valid = false
40
+ Zip::ZipFile.open(file.path) do |zip|
41
+ zip.sort.each do |entry|
42
+ entry.name.split('/').each do |file|
43
+ valid = true if THEME_STRUCTURE.include?(file)
44
+ end
45
+ end
46
+ end
47
+ valid
48
+ end
49
+ end
50
+
51
+ belongs_to :website
52
+
53
+ has_permalink :name, :theme_id, :scope => :website_id,
54
+ :only_when_blank => false, :sync_url => true
55
+
56
+ validates :name, :presence => {:message => 'Name cannot be blank'}
57
+ validates_uniqueness_of :theme_id, :scope => :website_id
58
+
59
+ before_destroy :delete_theme_files!
60
+
61
+ def path
62
+ "#{self.class.base_dir(website)}/#{theme_id}"
63
+ end
64
+
65
+ def url
66
+ "sites/site-#{website.id}/themes/#{theme_id}"
67
+ end
68
+
69
+ def activate!
70
+ update_attributes! :active => true
71
+ end
72
+
73
+ def deactivate!
74
+ update_attributes! :active => false
75
+ end
76
+
77
+ def themed_widgets
78
+ widgets = []
79
+ ErpApp::Widgets::Base.installed_widgets.each do |widget_name|
80
+ widgets << widget_name unless self.files.where("directory like '/#{File.join(self.url, 'widgets', widget_name)}%'").empty?
81
+ end
82
+ widgets
83
+ end
84
+
85
+ def non_themed_widgets
86
+ already_themed_widgets = self.themed_widgets
87
+ widgets = []
88
+ ErpApp::Widgets::Base.installed_widgets.each do |widget_name|
89
+ widgets << widget_name unless already_themed_widgets.include?(widget_name)
90
+ end
91
+ widgets
92
+ end
93
+
94
+ def create_layouts_for_widget(widget)
95
+ file_support = ErpTechSvcs::FileSupport::Base.new
96
+ views_location = "::Widgets::#{widget.camelize}::Base".constantize.views_location
97
+ create_theme_files_for_directory_node(file_support.build_tree(views_location, :preload => true), :widgets, :path_to_replace => views_location, :widget_name => widget)
98
+ end
99
+
100
+ def about
101
+ %w(name author version homepage summary).inject({}) do |result, key|
102
+ result[key] = send(key)
103
+ result
104
+ end
105
+ end
106
+
107
+ def import(file)
108
+ file_support = ErpTechSvcs::FileSupport::Base.new(:storage => ErpTechSvcs::FileSupport.options[:storage])
109
+ file = ActionController::UploadedTempfile.new("uploaded-theme").tap do |f|
110
+ f.puts file.read
111
+ f.original_filename = file.original_filename
112
+ f.read # no idea why we need this here, otherwise the zip can't be opened
113
+ end unless file.path
114
+
115
+ theme_root = Theme.find_theme_root(file)
116
+
117
+ Zip::ZipFile.open(file.path) do |zip|
118
+ zip.each do |entry|
119
+ if entry.name == 'about.yml'
120
+ #TODO
121
+ #FIXME this does not work for some reason
122
+ data = ''
123
+ entry.get_input_stream { |io| data = io.read }
124
+ data = StringIO.new(data) if data.present?
125
+ about = YAML.load(data)
126
+ self.author = about['author'] if about['author']
127
+ self.version = about['version'] if about['version']
128
+ self.homepage = about['homepage'] if about['homepage']
129
+ self.summary = about['summary'] if about['summary']
130
+ else
131
+ name = entry.name.sub(/__MACOSX\//, '')
132
+ name = Theme.strip_path(entry.name, theme_root)
133
+ data = ''
134
+ entry.get_input_stream { |io| data = io.read }
135
+ data = StringIO.new(data) if data.present?
136
+ theme_file = self.files.where("name = ? and directory = ?", File.basename(name), File.join("",self.url,File.dirname(name))).first
137
+ unless theme_file.nil?
138
+ theme_file.data = data
139
+ theme_file.save
140
+ else
141
+ self.add_file(data, File.join(file_support.root,self.url,name)) rescue next
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ def export
149
+ file_support = ErpTechSvcs::FileSupport::Base.new(:storage => ErpTechSvcs::FileSupport.options[:storage])
150
+ tmp_dir = Theme.make_tmp_dir
151
+ (tmp_dir + "#{name}[#{theme_id}].zip").tap do |file_name|
152
+ file_name.unlink if file_name.exist?
153
+ Zip::ZipFile.open(file_name, Zip::ZipFile::CREATE) do |zip|
154
+ files.each {|file|
155
+ contents = file_support.get_contents(File.join(file_support.root,file.directory,file.name))
156
+ relative_path = file.directory.sub("/#{url}",'')
157
+ path = FileUtils.mkdir_p(File.join(tmp_dir,relative_path))
158
+ full_path = File.join(path,file.name)
159
+ File.open(full_path, 'w+') {|f| f.puts(contents) }
160
+ zip.add(File.join(relative_path[1..relative_path.length],file.name), full_path) if ::File.exists?(full_path)
161
+ }
162
+ ::File.open(tmp_dir + 'about.yml', 'w') { |f| f.puts(about.to_yaml) }
163
+ zip.add('about.yml', tmp_dir + 'about.yml')
164
+ end
165
+ end
166
+ end
167
+
168
+ def has_template?(directory, name)
169
+ self.templates.find{|item| item.directory == File.join(path,directory).gsub(Rails.root.to_s, '') and item.name == name}
170
+ end
171
+
172
+ class << self
173
+ def find_theme_root(file)
174
+ theme_root = ''
175
+ Zip::ZipFile.open(file.path) do |zip|
176
+ zip.each do |entry|
177
+ entry.name.sub!(/__MACOSX\//, '')
178
+ if theme_root = root_in_path(entry.name)
179
+ break
180
+ end
181
+ end
182
+ end
183
+ theme_root
184
+ end
185
+
186
+ def root_in_path(path)
187
+ root_found = false
188
+ theme_root = ''
189
+ path.split('/').each do |piece|
190
+ if piece == 'about.yml' || THEME_STRUCTURE.include?(piece)
191
+ root_found = true
192
+ else
193
+ theme_root += piece + '/' if !piece.match('\.') && !root_found
194
+ end
195
+ end
196
+ root_found ? theme_root : false
197
+ end
198
+
199
+ def strip_path(file_name, path)
200
+ file_name.sub(path, '')
201
+ end
202
+ end
203
+
204
+ def delete_theme_files!
205
+ file_support = ErpTechSvcs::FileSupport::Base.new(:storage => ErpTechSvcs::FileSupport.options[:storage])
206
+ file_support.delete_file(File.join(file_support.root,self.url), :force => true)
207
+ end
208
+
209
+ def create_theme_files!
210
+ file_support = ErpTechSvcs::FileSupport::Base.new
211
+ create_theme_files_for_directory_node(file_support.build_tree(BASE_LAYOUTS_VIEWS_PATH, :preload => true), :templates, :path_to_replace => BASE_LAYOUTS_VIEWS_PATH)
212
+ create_theme_files_for_directory_node(file_support.build_tree(KNITKIT_WEBSITE_STYLESHEETS_PATH, :preload => true), :stylesheets, :path_to_replace => KNITKIT_WEBSITE_STYLESHEETS_PATH)
213
+ create_theme_files_for_directory_node(file_support.build_tree(KNITKIT_WEBSITE_IMAGES_PATH, :preload => true), :images, :path_to_replace => KNITKIT_WEBSITE_IMAGES_PATH)
214
+ end
215
+
216
+ private
217
+
218
+ def create_theme_files_for_directory_node(node, type, options={})
219
+ node[:children].each do |child_node|
220
+ child_node[:leaf] ? save_theme_file(child_node[:id], type, options) : create_theme_files_for_directory_node(child_node, type, options)
221
+ end
222
+ end
223
+
224
+ def save_theme_file(path, type, options)
225
+ contents = IO.read(path)
226
+ contents.gsub!("../../images/knitkit","../images") unless path.scan('style.css').empty?
227
+ contents.gsub!("<%= static_stylesheet_link_tag('knitkit/style.css') %>","<%= theme_stylesheet_link_tag('#{self.theme_id}','style.css') %>") unless path.scan('base.html.erb').empty?
228
+
229
+ path = case type
230
+ when :widgets
231
+ path.gsub(options[:path_to_replace], "/#{self.url}/widgets/#{options[:widget_name]}")
232
+ else
233
+ path.gsub(options[:path_to_replace], "/#{self.url}/#{type.to_s}")
234
+ end
235
+
236
+ self.add_file(contents, path)
237
+ end
238
+
239
+ end
@@ -0,0 +1,457 @@
1
+ class Website < ActiveRecord::Base
2
+ after_destroy :remove_sites_directory, :remove_website_role
3
+ after_create :setup_website
4
+
5
+ has_file_assets
6
+
7
+ has_many :published_websites, :dependent => :destroy
8
+ has_many :website_hosts, :dependent => :destroy
9
+ has_many :website_navs, :dependent => :destroy
10
+ has_many :website_inquiries, :dependent => :destroy
11
+
12
+ alias :hosts :website_hosts
13
+
14
+ has_many :website_sections, :dependent => :destroy, :order => :lft do
15
+ def paths
16
+ collect{|website_section| website_section.paths}.flatten
17
+ end
18
+
19
+ #have to do a sort by to override what awesome nested set does for the order by (lft)
20
+ def positioned
21
+ where('parent_id is null').sort_by!(&:position)
22
+ end
23
+
24
+ def update_paths!
25
+ all.each{|section| section.self_and_descendants.each{|_section| _section.update_path!}}
26
+ end
27
+ end
28
+ alias :sections :website_sections
29
+
30
+ has_many :themes, :dependent => :destroy do
31
+ def active
32
+ where('active = 1')
33
+ end
34
+ end
35
+
36
+ def all_section_paths
37
+ ActiveRecord::Base.connection.execute("select path from website_sections where website_id = #{self.id}").collect{|row| row['path']}
38
+ end
39
+
40
+ def self.find_by_host(host)
41
+ website = nil
42
+ website_host = WebsiteHost.find_by_host(host)
43
+ website = website_host.website unless website_host.nil?
44
+ website
45
+ end
46
+
47
+ def deactivate_themes!
48
+ themes.each do |theme|
49
+ theme.deactivate!
50
+ end
51
+ end
52
+
53
+ def publish_element(comment, element, version, current_user)
54
+ self.published_websites.last.publish_element(comment, element, version, current_user)
55
+ end
56
+
57
+ def publish(comment, current_user)
58
+ self.published_websites.last.publish(comment, current_user)
59
+ end
60
+
61
+ def set_publication_version(version, current_user)
62
+ PublishedWebsite.activate(self, version, current_user)
63
+ end
64
+
65
+ def active_publication
66
+ self.published_websites.all.find{|item| item.active}
67
+ end
68
+
69
+ def role
70
+ Role.iid(website_role_iid)
71
+ end
72
+
73
+ def setup_website
74
+ PublishedWebsite.create(:website => self, :version => 0, :active => true, :comment => 'New Site Created')
75
+ Role.create(:description => "Website #{self.title}", :internal_identifier => website_role_iid)
76
+ end
77
+
78
+ def remove_sites_directory
79
+ file_support = ErpTechSvcs::FileSupport::Base.new(:storage => ErpTechSvcs::FileSupport.options[:storage])
80
+ begin
81
+ file_support.delete_file(File.join(file_support.root,"sites/site-#{self.id}"), :force => true)
82
+ rescue
83
+ #do nothing it may not exist
84
+ end
85
+ end
86
+
87
+ def remove_website_role
88
+ role.destroy if role
89
+ end
90
+
91
+ def setup_default_pages
92
+ # create default sections for each widget using widget layout
93
+ widget_classes = [
94
+ ::Widgets::ContactUs::Base,
95
+ ::Widgets::Search::Base,
96
+ ::Widgets::ManageProfile::Base,
97
+ ::Widgets::Login::Base,
98
+ ::Widgets::Signup::Base,
99
+ ::Widgets::ResetPassword::Base
100
+ ]
101
+ widget_classes.each do |widget_class|
102
+ website_section = WebsiteSection.new
103
+ website_section.title = widget_class.title
104
+ website_section.in_menu = true unless ["Login", "Sign Up", "Reset Password"].include?(widget_class.title)
105
+ website_section.layout = widget_class.base_layout
106
+ website_section.save
107
+ #make manage profile secured
108
+ website_section.add_role(self.role) if widget_class.title == 'Manage Profile'
109
+ self.website_sections << website_section
110
+ end
111
+ self.save
112
+ self.website_sections.update_paths!
113
+ end
114
+
115
+ def export_setup
116
+ setup_hash = {
117
+ :name => name,
118
+ :hosts => hosts.collect(&:host),
119
+ :title => title,
120
+ :subtitle => subtitle,
121
+ :email => email,
122
+ :auto_activate_publication => auto_activate_publication,
123
+ :email_inquiries => email_inquiries,
124
+ :sections => [],
125
+ :images => [],
126
+ :files => [],
127
+ :website_navs => []
128
+ }
129
+
130
+ setup_hash[:sections] = sections.positioned.collect do |website_section|
131
+ build_section_hash(website_section)
132
+ end
133
+
134
+ setup_hash[:website_navs] = website_navs.collect do |website_nav|
135
+ {
136
+ :name => website_nav.name,
137
+ :items => website_nav.items.positioned.map{|website_nav_item| build_menu_item_hash(website_nav_item)}
138
+ }
139
+ end
140
+
141
+ self.files.where("directory like '%/sites/site-#{self.id}/images%'").all.each do |image_asset|
142
+ setup_hash[:images] << {:path => image_asset.directory, :name => image_asset.name}
143
+ end
144
+
145
+ self.files.where("directory like '%/sites/site-#{self.id}/file_assets%'").all.each do |file_asset|
146
+ setup_hash[:files] << {:path => file_asset.directory, :name => file_asset.name}
147
+ end
148
+
149
+ setup_hash
150
+ end
151
+
152
+ def export
153
+ tmp_dir = Website.make_tmp_dir
154
+ file_support = ErpTechSvcs::FileSupport::Base.new(:storage => ErpTechSvcs::FileSupport.options[:storage])
155
+
156
+ sections_path = Pathname.new(File.join(tmp_dir,'sections'))
157
+ FileUtils.mkdir_p(sections_path) unless sections_path.exist?
158
+
159
+ articles_path = Pathname.new(File.join(tmp_dir,'articles'))
160
+ FileUtils.mkdir_p(articles_path) unless articles_path.exist?
161
+
162
+ excerpts_path = Pathname.new(File.join(tmp_dir,'excerpts'))
163
+ FileUtils.mkdir_p(excerpts_path) unless excerpts_path.exist?
164
+
165
+ image_assets_path = Pathname.new(File.join(tmp_dir,'images'))
166
+ FileUtils.mkdir_p(image_assets_path) unless image_assets_path.exist?
167
+
168
+ file_assets_path = Pathname.new(File.join(tmp_dir,'files'))
169
+ FileUtils.mkdir_p(file_assets_path) unless file_assets_path.exist?
170
+
171
+ sections.each do |website_section|
172
+ unless website_section.layout.blank?
173
+ File.open(File.join(sections_path,"#{website_section.title}.rhtml"), 'w+') {|f| f.puts(website_section.layout) }
174
+ end
175
+ end
176
+
177
+ contents = sections.collect(&:contents).flatten.uniq
178
+ contents.each do |content|
179
+ File.open(File.join(articles_path,"#{content.title}.html"), 'w+') {|f| f.puts(content.body_html) }
180
+ unless content.excerpt_html.blank?
181
+ File.open(File.join(excerpts_path,"#{content.title}.html"), 'w+') {|f| f.puts(content.excerpt_html) }
182
+ end
183
+ end
184
+
185
+ self.files.where("directory like '%/sites/site-#{self.id}/images%'").all.each do |image_asset|
186
+ contents = file_support.get_contents(File.join(file_support.root,image_asset.directory,image_asset.name))
187
+ File.open(File.join(image_assets_path,image_asset.name), 'w+') {|f| f.puts(contents) }
188
+ end
189
+
190
+ self.files.where("directory like '%/sites/site-#{self.id}/file_assets%'").all.each do |file_asset|
191
+ contents = file_support.get_contents(File.join(file_support.root,file_asset.directory,file_asset.name))
192
+ File.open(File.join(file_assets_path,file_asset.name), 'w+') {|f| f.puts(contents) }
193
+ end
194
+
195
+ files = []
196
+
197
+ Dir.entries(sections_path).each do |entry|
198
+ next if entry =~ /^\./
199
+ files << {:path => File.join(sections_path,entry), :name => File.join('sections/',File.basename(entry))}
200
+ end
201
+
202
+ Dir.entries(articles_path).each do |entry|
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!
223
+
224
+ (tmp_dir + "#{name}.zip").tap do |file_name|
225
+ file_name.unlink if file_name.exist?
226
+ Zip::ZipFile.open(file_name, Zip::ZipFile::CREATE) do |zip|
227
+ 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
+ zip.add('setup.yml', tmp_dir + 'setup.yml')
230
+ end
231
+ end
232
+ end
233
+
234
+ class << self
235
+ def make_tmp_dir
236
+ random = Time.now.to_i.to_s.split('').sort_by { rand }
237
+ Pathname.new(Rails.root + "/tmp/website_export/tmp_#{random}/").tap do |dir|
238
+ FileUtils.mkdir_p(dir) unless dir.exist?
239
+ end
240
+ end
241
+
242
+ def import(file, current_user)
243
+ file_support = ErpTechSvcs::FileSupport::Base.new(:storage => ErpTechSvcs::FileSupport.options[:storage])
244
+ message = ''
245
+ success = true
246
+
247
+ file = ActionController::UploadedTempfile.new("uploaded-theme").tap do |f|
248
+ f.puts file.read
249
+ f.original_filename = file.original_filename
250
+ f.read # no idea why we need this here, otherwise the zip can't be opened
251
+ end unless file.path
252
+
253
+ entries = []
254
+ setup_hash = nil
255
+
256
+ Zip::ZipFile.open(file.path) do |zip|
257
+ zip.each do |entry|
258
+ next if entry.name =~ /__MACOSX\//
259
+ if entry.name =~ /setup.yml/
260
+ data = ''
261
+ entry.get_input_stream { |io| data = io.read }
262
+ data = StringIO.new(data) if data.present?
263
+ setup_hash = YAML.load(data)
264
+ else
265
+ type = entry.name.split('/')[(entry.name.split('/').count - 2)]
266
+ name = entry.name.split('/').last
267
+ next if name.nil?
268
+ data = ''
269
+ entry_hash = {:type => type, :name => name}
270
+ entries << entry_hash unless name == 'sections' || name == 'articles' || name == 'excerpts'
271
+ entries.uniq!
272
+ data = entry.get_input_stream.read
273
+ entry_hash[:data] = data
274
+ end
275
+ end
276
+ end
277
+
278
+ if Website.find_by_name(setup_hash[:name]).nil?
279
+ website = Website.create(
280
+ :name => setup_hash[:name],
281
+ :title => setup_hash[:title],
282
+ :subtitle => setup_hash[:subtitle],
283
+ :email => setup_hash[:email],
284
+ :email_inquiries => setup_hash[:email_inquiries],
285
+ :auto_activate_publication => setup_hash[:auto_activate_publication]
286
+ )
287
+
288
+ #set default publication published by user
289
+ first_publication = website.published_websites.first
290
+ first_publication.published_by = current_user
291
+ first_publication.save
292
+
293
+ begin
294
+
295
+ #handle images
296
+ setup_hash[:images].each do |image_asset|
297
+ content = entries.find{|entry| entry[:type] == 'images' and entry[:name] == image_asset[:name]}[:data]
298
+ website.add_file(content, File.join(file_support.root, image_asset[:path].sub(/site-[0-9][0-9]*/, "site-#{website.id}"), image_asset[:name]))
299
+ end
300
+
301
+ #handle files
302
+ setup_hash[:files].each do |file_asset|
303
+ content = entries.find{|entry| entry[:type] == 'files' and entry[:name] == file_asset[:name]}[:data]
304
+ website.add_file(content, File.join(file_support.root, file_asset[:path].sub(/site-[0-9][0-9]*/, "site-#{website.id}"), file_asset[:name]))
305
+ end
306
+
307
+ #handle hosts
308
+ setup_hash[:hosts].each do |host|
309
+ website.hosts << WebsiteHost.create(:host => host)
310
+ website.save
311
+ end
312
+
313
+ #handle sections
314
+ setup_hash[:sections].each do |section_hash|
315
+ build_section(section_hash, entries, website)
316
+ end
317
+ website.website_sections.update_paths!
318
+
319
+ #handle website_navs
320
+ setup_hash[:website_navs].each do |website_nav_hash|
321
+ website_nav = WebsiteNav.new(:name => website_nav_hash[:name])
322
+ website_nav_hash[:items].each do |item|
323
+ website_nav.website_nav_items << build_menu_item(item)
324
+ end
325
+ website.website_navs << website_nav
326
+ end
327
+
328
+ website.publish("Website Imported", current_user)
329
+
330
+ rescue Exception=>ex
331
+ website.destroy unless website.nil?
332
+ raise ex
333
+ end
334
+
335
+ website.save
336
+ success = true
337
+ else
338
+ message = 'Website already exists with that name'
339
+ success = false
340
+ end
341
+
342
+ return success, message
343
+ end
344
+
345
+ protected
346
+
347
+ def build_menu_item(hash)
348
+ website_item = WebsiteNavItem.new(
349
+ :title => hash[:title],
350
+ :url => hash[:url],
351
+ :position => hash[:position]
352
+ )
353
+ unless hash[:linked_to_item_type].blank?
354
+ website_item.linked_to_item = WebsiteSection.find_by_path(hash[:linked_to_item_path])
355
+ end
356
+ website_item.save
357
+ hash[:items].each do |item|
358
+ child_website_item = build_menu_item(item)
359
+ child_website_item.move_to_child_of(website_item)
360
+ end
361
+ unless hash[:roles].empty?
362
+ hash[:roles].each do |role|
363
+ website_item.add_role(Role.find_by_internal_identifier(role)) rescue nil #do nothing if the role does not exist
364
+ end
365
+ end
366
+ website_item
367
+ end
368
+
369
+ def build_section(hash, entries, website)
370
+ klass = hash[:type].constantize
371
+ section = klass.new(:title => hash[:name], :in_menu => hash[:in_menu], :position => hash[:position])
372
+ section.permalink = hash[:permalink]
373
+ section.path = hash[:path]
374
+ unless entries.find{|entry| entry[:type] == 'sections' and entry[:name] == "#{hash[:name]}.rhtml"}.nil?
375
+ section.layout = entries.find{|entry| entry[:type] == 'sections' and entry[:name] == "#{hash[:name]}.rhtml"}[:data]
376
+ end
377
+ hash[:articles].each do |article_hash|
378
+ article = Article.find_by_title(article_hash[:name])
379
+ if article.nil?
380
+ article = Article.new(:title => article_hash[:name], :display_title => article_hash[:display_title])
381
+ 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[:name]}.html"}[:data]
383
+ unless entries.find{|entry| entry[:type] == 'excerpts' and entry[:name] == "#{article_hash[:name]}.html"}.nil?
384
+ article.excerpt_html = entries.find{|entry| entry[:type] == 'excerpts' and entry[:name] == "#{article_hash[:name]}.html"}[:data]
385
+ end
386
+ end
387
+ section.contents << article
388
+ section.save
389
+ article.update_content_area_and_position_by_section(section, article_hash[:content_area], article_hash[:position])
390
+ end
391
+ website.website_sections << section
392
+ section.save
393
+ hash[:sections].each do |section_hash|
394
+ child_section = build_section(section_hash, entries, website)
395
+ child_section.move_to_child_of(section)
396
+ end
397
+ unless hash[:roles].empty?
398
+ hash[:roles].each do |role|
399
+ section.add_role(Role.find_by_internal_identifier(role)) rescue nil #do nothing if the role does not exist
400
+ end
401
+ end
402
+ section
403
+ end
404
+
405
+ end
406
+
407
+ private
408
+
409
+ def website_role_iid
410
+ "website_#{self.name.underscore.gsub("'","").gsub(",","")}_access"
411
+ 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
+
444
+
445
+ def build_menu_item_hash(menu_item)
446
+ {
447
+ :title => menu_item.title,
448
+ :url => menu_item.url,
449
+ :roles => menu_item.roles.collect{|role| role.internal_identifier},
450
+ :linked_to_item_type => menu_item.linked_to_item_type,
451
+ :linked_to_item_path => menu_item.linked_to_item.nil? ? nil : menu_item.linked_to_item.path,
452
+ :position => menu_item.position,
453
+ :items => menu_item.children.collect{|child| build_menu_item_hash(child)}
454
+ }
455
+ end
456
+
457
+ end