comfypress 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (289) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.travis.yml +22 -0
  4. data/Gemfile +22 -0
  5. data/Gemfile.lock +175 -0
  6. data/LICENSE +20 -0
  7. data/README.md +79 -0
  8. data/Rakefile +5 -0
  9. data/app/assets/images/comfypress/arrow_bottom.gif +0 -0
  10. data/app/assets/images/comfypress/arrow_right.gif +0 -0
  11. data/app/assets/images/comfypress/bootstrap/glyphicons-halflings-white.png +0 -0
  12. data/app/assets/images/comfypress/bootstrap/glyphicons-halflings.png +0 -0
  13. data/app/assets/images/comfypress/checkerboard.gif +0 -0
  14. data/app/assets/images/comfypress/icon_draft.gif +0 -0
  15. data/app/assets/images/comfypress/icon_file.gif +0 -0
  16. data/app/assets/images/comfypress/icon_layout.gif +0 -0
  17. data/app/assets/images/comfypress/icon_move.gif +0 -0
  18. data/app/assets/images/comfypress/icon_page.gif +0 -0
  19. data/app/assets/images/comfypress/icon_site.gif +0 -0
  20. data/app/assets/images/comfypress/icon_snippet.gif +0 -0
  21. data/app/assets/images/comfypress/nav_arrow.png +0 -0
  22. data/app/assets/javascripts/comfypress/admin/application.js +1 -0
  23. data/app/assets/javascripts/comfypress/application.js.coffee +175 -0
  24. data/app/assets/javascripts/comfypress/lib/bootstrap-datetimepicker.js +21 -0
  25. data/app/assets/javascripts/comfypress/lib/bootstrap-wysihtml5.js +492 -0
  26. data/app/assets/javascripts/comfypress/lib/bootstrap.js +6 -0
  27. data/app/assets/javascripts/comfypress/lib/codemirror.js +10 -0
  28. data/app/assets/javascripts/comfypress/lib/wysihtml5.js +261 -0
  29. data/app/assets/stylesheets/comfypress/admin/application.css +1 -0
  30. data/app/assets/stylesheets/comfypress/application.css.sass +8 -0
  31. data/app/assets/stylesheets/comfypress/base.css.sass +333 -0
  32. data/app/assets/stylesheets/comfypress/bootstrap_overrides.css.sass +45 -0
  33. data/app/assets/stylesheets/comfypress/codemirror_overrides.css.sass +27 -0
  34. data/app/assets/stylesheets/comfypress/lib/bootstrap-datetimepicker.css +9 -0
  35. data/app/assets/stylesheets/comfypress/lib/bootstrap-wysihtml5.css +102 -0
  36. data/app/assets/stylesheets/comfypress/lib/bootstrap.css +9 -0
  37. data/app/assets/stylesheets/comfypress/lib/codemirror.css +240 -0
  38. data/app/assets/stylesheets/comfypress/wysihtml5_overrides.css.sass +5 -0
  39. data/app/controllers/application_controller.rb +5 -0
  40. data/app/controllers/cms_admin/base_controller.rb +51 -0
  41. data/app/controllers/cms_admin/categories_controller.rb +35 -0
  42. data/app/controllers/cms_admin/files_controller.rb +108 -0
  43. data/app/controllers/cms_admin/layouts_controller.rb +67 -0
  44. data/app/controllers/cms_admin/menu_items_controller.rb +65 -0
  45. data/app/controllers/cms_admin/menus_controller.rb +60 -0
  46. data/app/controllers/cms_admin/pages_controller.rb +112 -0
  47. data/app/controllers/cms_admin/revisions_controller.rb +57 -0
  48. data/app/controllers/cms_admin/sites_controller.rb +64 -0
  49. data/app/controllers/cms_admin/snippets_controller.rb +64 -0
  50. data/app/controllers/cms_content_controller.rb +79 -0
  51. data/app/helpers/application_helper.rb +16 -0
  52. data/app/models/cms/block.rb +48 -0
  53. data/app/models/cms/categorization.rb +21 -0
  54. data/app/models/cms/category.rb +30 -0
  55. data/app/models/cms/file.rb +75 -0
  56. data/app/models/cms/layout.rb +99 -0
  57. data/app/models/cms/menu.rb +26 -0
  58. data/app/models/cms/menu_item.rb +14 -0
  59. data/app/models/cms/page.rb +202 -0
  60. data/app/models/cms/revision.rb +17 -0
  61. data/app/models/cms/site.rb +113 -0
  62. data/app/models/cms/snippet.rb +58 -0
  63. data/app/models/cms.rb +5 -0
  64. data/app/views/cms_admin/categories/_categories.html.haml +3 -0
  65. data/app/views/cms_admin/categories/_edit.html.haml +6 -0
  66. data/app/views/cms_admin/categories/_form.html.haml +8 -0
  67. data/app/views/cms_admin/categories/_index.html.haml +24 -0
  68. data/app/views/cms_admin/categories/_show.html.haml +13 -0
  69. data/app/views/cms_admin/categories/create.js.erb +7 -0
  70. data/app/views/cms_admin/categories/destroy.js.erb +3 -0
  71. data/app/views/cms_admin/categories/edit.js.erb +3 -0
  72. data/app/views/cms_admin/categories/update.js.erb +6 -0
  73. data/app/views/cms_admin/files/_file.html.haml +13 -0
  74. data/app/views/cms_admin/files/_form.html.haml +14 -0
  75. data/app/views/cms_admin/files/_index.html.haml +14 -0
  76. data/app/views/cms_admin/files/_page_form.html.haml +9 -0
  77. data/app/views/cms_admin/files/destroy.js.coffee +2 -0
  78. data/app/views/cms_admin/files/edit.html.haml +5 -0
  79. data/app/views/cms_admin/files/index.html.haml +28 -0
  80. data/app/views/cms_admin/files/new.html.haml +5 -0
  81. data/app/views/cms_admin/layouts/_form.html.haml +17 -0
  82. data/app/views/cms_admin/layouts/_index_branch.html.haml +23 -0
  83. data/app/views/cms_admin/layouts/edit.html.haml +9 -0
  84. data/app/views/cms_admin/layouts/index.html.haml +9 -0
  85. data/app/views/cms_admin/layouts/new.html.haml +5 -0
  86. data/app/views/cms_admin/menu_items/_form.html.haml +22 -0
  87. data/app/views/cms_admin/menu_items/edit.html.haml +8 -0
  88. data/app/views/cms_admin/menu_items/index.html.haml +24 -0
  89. data/app/views/cms_admin/menu_items/new.html.haml +5 -0
  90. data/app/views/cms_admin/menus/_form.html.haml +8 -0
  91. data/app/views/cms_admin/menus/edit.html.haml +8 -0
  92. data/app/views/cms_admin/menus/index.html.haml +24 -0
  93. data/app/views/cms_admin/menus/new.html.haml +5 -0
  94. data/app/views/cms_admin/pages/_form.html.haml +38 -0
  95. data/app/views/cms_admin/pages/_form_blocks.html.haml +28 -0
  96. data/app/views/cms_admin/pages/_index_branch.html.haml +35 -0
  97. data/app/views/cms_admin/pages/edit.html.haml +9 -0
  98. data/app/views/cms_admin/pages/form_blocks.js.erb +2 -0
  99. data/app/views/cms_admin/pages/index.html.haml +11 -0
  100. data/app/views/cms_admin/pages/new.html.haml +5 -0
  101. data/app/views/cms_admin/pages/toggle_branch.js.erb +11 -0
  102. data/app/views/cms_admin/revisions/show.html.haml +28 -0
  103. data/app/views/cms_admin/sites/_form.html.haml +14 -0
  104. data/app/views/cms_admin/sites/_mirrors.html.haml +17 -0
  105. data/app/views/cms_admin/sites/edit.html.haml +5 -0
  106. data/app/views/cms_admin/sites/index.html.haml +24 -0
  107. data/app/views/cms_admin/sites/new.html.haml +5 -0
  108. data/app/views/cms_admin/snippets/_form.html.haml +11 -0
  109. data/app/views/cms_admin/snippets/edit.html.haml +9 -0
  110. data/app/views/cms_admin/snippets/index.html.haml +28 -0
  111. data/app/views/cms_admin/snippets/new.html.haml +5 -0
  112. data/app/views/cms_content/_menu.html.haml +5 -0
  113. data/app/views/cms_content/render_sitemap.xml.builder +15 -0
  114. data/app/views/layouts/cms_admin/_body.html.haml +16 -0
  115. data/app/views/layouts/cms_admin/_center.html.haml +7 -0
  116. data/app/views/layouts/cms_admin/_footer.html.haml +3 -0
  117. data/app/views/layouts/cms_admin/_footer_js.html.haml +7 -0
  118. data/app/views/layouts/cms_admin/_head.html.haml +12 -0
  119. data/app/views/layouts/cms_admin/_left.html.haml +11 -0
  120. data/app/views/layouts/cms_admin/_right.html.haml +1 -0
  121. data/app/views/layouts/cms_admin.html.haml +4 -0
  122. data/comfypress.gemspec +28 -0
  123. data/config/application.rb +69 -0
  124. data/config/boot.rb +6 -0
  125. data/config/database.sqlite.yml +47 -0
  126. data/config/database.yml +47 -0
  127. data/config/environment.rb +5 -0
  128. data/config/environments/development.rb +39 -0
  129. data/config/environments/production.rb +67 -0
  130. data/config/environments/test.rb +39 -0
  131. data/config/initializers/comfypress.rb +100 -0
  132. data/config/initializers/paperclip.rb +3 -0
  133. data/config/locales/de.yml +236 -0
  134. data/config/locales/en.yml +285 -0
  135. data/config/locales/es.yml +236 -0
  136. data/config/locales/fr.yml +236 -0
  137. data/config/locales/ja.yml +237 -0
  138. data/config/locales/pl.yml +236 -0
  139. data/config/locales/pt-BR.yml +236 -0
  140. data/config/locales/ru.yml +236 -0
  141. data/config/locales/sv.yml +236 -0
  142. data/config/locales/zh-CN.yml +236 -0
  143. data/config/routes.rb +10 -0
  144. data/config.ru +4 -0
  145. data/db/cms_fixtures/sample-site/layouts/default/_default.yml +1 -0
  146. data/db/cms_fixtures/sample-site/layouts/default/content.html +5 -0
  147. data/db/cms_fixtures/sample-site/layouts/default/css.css +1 -0
  148. data/db/cms_fixtures/sample-site/layouts/default/js.js +1 -0
  149. data/db/cms_fixtures/sample-site/layouts/default/nested/_nested.yml +2 -0
  150. data/db/cms_fixtures/sample-site/layouts/default/nested/content.html +2 -0
  151. data/db/cms_fixtures/sample-site/layouts/default/nested/css.css +1 -0
  152. data/db/cms_fixtures/sample-site/layouts/default/nested/js.js +1 -0
  153. data/db/cms_fixtures/sample-site/pages/index/_index.yml +2 -0
  154. data/db/cms_fixtures/sample-site/pages/index/child/_child.yml +3 -0
  155. data/db/cms_fixtures/sample-site/pages/index/child/left.html +1 -0
  156. data/db/cms_fixtures/sample-site/pages/index/child/right.html +1 -0
  157. data/db/cms_fixtures/sample-site/pages/index/content.html +2 -0
  158. data/db/cms_fixtures/sample-site/snippets/default/_default.yml +1 -0
  159. data/db/cms_fixtures/sample-site/snippets/default/content.html +1 -0
  160. data/db/migrate/01_create_cms.rb +163 -0
  161. data/db/seeds.rb +9 -0
  162. data/db/upgrade_migrations/02_upgrade_to_1_1_0.rb +19 -0
  163. data/db/upgrade_migrations/03_upgrade_to_1_2_0.rb +15 -0
  164. data/db/upgrade_migrations/04_upgrade_to_1_3_0.rb +23 -0
  165. data/db/upgrade_migrations/05_upgrade_to_1_4_0.rb +33 -0
  166. data/db/upgrade_migrations/06_upgrade_to_1_5_0.rb +21 -0
  167. data/db/upgrade_migrations/07_upgrade_to_1_6_0.rb +25 -0
  168. data/doc/preview.png +0 -0
  169. data/doc/sofa.png +0 -0
  170. data/lib/comfypress/authentication/dummy_auth.rb +8 -0
  171. data/lib/comfypress/authentication/http_auth.rb +18 -0
  172. data/lib/comfypress/configuration.rb +99 -0
  173. data/lib/comfypress/engine.rb +18 -0
  174. data/lib/comfypress/error.rb +24 -0
  175. data/lib/comfypress/extensions/acts_as_tree.rb +102 -0
  176. data/lib/comfypress/extensions/has_revisions.rb +64 -0
  177. data/lib/comfypress/extensions/is_categorized.rb +49 -0
  178. data/lib/comfypress/extensions/is_mirrored.rb +85 -0
  179. data/lib/comfypress/extensions/rails.rb +24 -0
  180. data/lib/comfypress/fixtures.rb +289 -0
  181. data/lib/comfypress/form_builder.rb +97 -0
  182. data/lib/comfypress/render_methods.rb +85 -0
  183. data/lib/comfypress/routing.rb +62 -0
  184. data/lib/comfypress/sitemap.rb +27 -0
  185. data/lib/comfypress/tag.rb +154 -0
  186. data/lib/comfypress/tags/asset.rb +25 -0
  187. data/lib/comfypress/tags/collection.rb +64 -0
  188. data/lib/comfypress/tags/field_datetime.rb +17 -0
  189. data/lib/comfypress/tags/field_integer.rb +17 -0
  190. data/lib/comfypress/tags/field_rich_text.rb +17 -0
  191. data/lib/comfypress/tags/field_string.rb +17 -0
  192. data/lib/comfypress/tags/field_text.rb +17 -0
  193. data/lib/comfypress/tags/file.rb +29 -0
  194. data/lib/comfypress/tags/helper.rb +24 -0
  195. data/lib/comfypress/tags/menu.rb +27 -0
  196. data/lib/comfypress/tags/page_datetime.rb +13 -0
  197. data/lib/comfypress/tags/page_file.rb +51 -0
  198. data/lib/comfypress/tags/page_files.rb +52 -0
  199. data/lib/comfypress/tags/page_integer.rb +13 -0
  200. data/lib/comfypress/tags/page_markdown.rb +22 -0
  201. data/lib/comfypress/tags/page_rich_text.rb +13 -0
  202. data/lib/comfypress/tags/page_string.rb +13 -0
  203. data/lib/comfypress/tags/page_text.rb +13 -0
  204. data/lib/comfypress/tags/partial.rb +23 -0
  205. data/lib/comfypress/tags/snippet.rb +19 -0
  206. data/lib/comfypress/version.rb +3 -0
  207. data/lib/comfypress/view_hooks.rb +32 -0
  208. data/lib/comfypress/view_methods.rb +52 -0
  209. data/lib/comfypress.rb +66 -0
  210. data/lib/generators/comfy/cms/README +10 -0
  211. data/lib/generators/comfy/cms/cms_generator.rb +57 -0
  212. data/lib/tasks/comfypress.rake +45 -0
  213. data/script/rails +6 -0
  214. data/test/fixtures/cms/blocks.yml +12 -0
  215. data/test/fixtures/cms/categories.yml +4 -0
  216. data/test/fixtures/cms/categorizations.yml +3 -0
  217. data/test/fixtures/cms/files.yml +9 -0
  218. data/test/fixtures/cms/layouts.yml +40 -0
  219. data/test/fixtures/cms/menu_items.yml +15 -0
  220. data/test/fixtures/cms/menus.yml +9 -0
  221. data/test/fixtures/cms/pages.yml +39 -0
  222. data/test/fixtures/cms/revisions.yml +21 -0
  223. data/test/fixtures/cms/sites.yml +6 -0
  224. data/test/fixtures/cms/snippets.yml +6 -0
  225. data/test/fixtures/files/data.zip +0 -0
  226. data/test/fixtures/files/document.pdf +0 -0
  227. data/test/fixtures/files/image.gif +0 -0
  228. data/test/fixtures/files/image.jpg +0 -0
  229. data/test/fixtures/views/_nav_hook.html.erb +1 -0
  230. data/test/fixtures/views/_nav_hook_2.html.erb +1 -0
  231. data/test/fixtures/views/render_test/_test.html.erb +1 -0
  232. data/test/fixtures/views/render_test/new.html.erb +1 -0
  233. data/test/fixtures/views/render_test/render_layout.html.erb +1 -0
  234. data/test/functional/cms_admin/base_controller_test.rb +18 -0
  235. data/test/functional/cms_admin/categories_controller_test.rb +70 -0
  236. data/test/functional/cms_admin/files_controller_test.rb +216 -0
  237. data/test/functional/cms_admin/layouts_controller_test.rb +123 -0
  238. data/test/functional/cms_admin/pages_controller_test.rb +450 -0
  239. data/test/functional/cms_admin/revisions_controller_test.rb +138 -0
  240. data/test/functional/cms_admin/sites_controller_test.rb +105 -0
  241. data/test/functional/cms_admin/snippets_controller_test.rb +140 -0
  242. data/test/functional/cms_content_controller_test.rb +205 -0
  243. data/test/gemfiles/Gemfile.rails.3.1 +16 -0
  244. data/test/gemfiles/Gemfile.rails.3.2 +16 -0
  245. data/test/gemfiles/Gemfile.rails.4.0 +16 -0
  246. data/test/integration/authentication_test.rb +48 -0
  247. data/test/integration/fixtures_test.rb +61 -0
  248. data/test/integration/mirrors_test.rb +72 -0
  249. data/test/integration/render_cms_test.rb +233 -0
  250. data/test/integration/sites_test.rb +120 -0
  251. data/test/integration/view_hooks_test.rb +43 -0
  252. data/test/test_helper.rb +124 -0
  253. data/test/unit/cms/menu_item_test.rb +7 -0
  254. data/test/unit/cms/menu_test.rb +7 -0
  255. data/test/unit/configuration_test.rb +42 -0
  256. data/test/unit/fixtures_test.rb +357 -0
  257. data/test/unit/mirrors_test.rb +242 -0
  258. data/test/unit/models/block_test.rb +199 -0
  259. data/test/unit/models/categorization_test.rb +76 -0
  260. data/test/unit/models/category_test.rb +39 -0
  261. data/test/unit/models/file_test.rb +89 -0
  262. data/test/unit/models/layout_test.rb +125 -0
  263. data/test/unit/models/page_test.rb +255 -0
  264. data/test/unit/models/site_test.rb +152 -0
  265. data/test/unit/models/snippet_test.rb +49 -0
  266. data/test/unit/revisions_test.rb +185 -0
  267. data/test/unit/sitemap_test.rb +20 -0
  268. data/test/unit/tag_test.rb +308 -0
  269. data/test/unit/tags/asset_test.rb +56 -0
  270. data/test/unit/tags/collection_test.rb +116 -0
  271. data/test/unit/tags/field_datetime_test.rb +50 -0
  272. data/test/unit/tags/field_integer_test.rb +49 -0
  273. data/test/unit/tags/field_rich_text_test.rb +49 -0
  274. data/test/unit/tags/field_string_test.rb +52 -0
  275. data/test/unit/tags/field_text_test.rb +48 -0
  276. data/test/unit/tags/file_test.rb +67 -0
  277. data/test/unit/tags/helper_test.rb +86 -0
  278. data/test/unit/tags/page_datetime_test.rb +50 -0
  279. data/test/unit/tags/page_file_test.rb +119 -0
  280. data/test/unit/tags/page_files_test.rb +113 -0
  281. data/test/unit/tags/page_integer_test.rb +49 -0
  282. data/test/unit/tags/page_markdown_test.rb +48 -0
  283. data/test/unit/tags/page_rich_text_test.rb +49 -0
  284. data/test/unit/tags/page_string_test.rb +49 -0
  285. data/test/unit/tags/page_text_test.rb +52 -0
  286. data/test/unit/tags/partial_test.rb +76 -0
  287. data/test/unit/tags/snippet_test.rb +45 -0
  288. data/test/unit/view_methods_test.rb +80 -0
  289. metadata +457 -0
@@ -0,0 +1,48 @@
1
+ class Cms::Block < ActiveRecord::Base
2
+
3
+ ComfyPress.establish_connection(self)
4
+
5
+ self.table_name = 'cms_blocks'
6
+
7
+ attr_accessible :identifier,
8
+ :content
9
+
10
+ # -- Relationships --------------------------------------------------------
11
+ belongs_to :page
12
+ has_many :files,
13
+ :autosave => true,
14
+ :dependent => :destroy
15
+
16
+ # -- Callbacks ------------------------------------------------------------
17
+ before_save :prepare_files
18
+
19
+ # -- Validations ----------------------------------------------------------
20
+ validates :identifier,
21
+ :presence => true,
22
+ :uniqueness => { :scope => :page_id }
23
+
24
+ # -- Instance Methods -----------------------------------------------------
25
+ # Tag object that is using this block
26
+ def tag
27
+ @tag ||= page.tags(true).detect{|t| t.is_cms_block? && t.identifier == identifier}
28
+ end
29
+
30
+ protected
31
+
32
+ def prepare_files
33
+ temp_files = [self.content].flatten.select do |f|
34
+ %w(ActionDispatch::Http::UploadedFile Rack::Test::UploadedFile).member?(f.class.name)
35
+ end
36
+
37
+ # only accepting one file if it's PageFile. PageFiles will take all
38
+ single_file = self.tag.is_a?(ComfyPress::Tag::PageFile)
39
+ temp_files = [temp_files.first].compact if single_file
40
+
41
+ temp_files.each do |file|
42
+ self.files.collect{|f| f.mark_for_destruction } if single_file
43
+ self.files.build(:site => self.page.site, :dimensions => self.tag.try(:dimensions), :file => file)
44
+ end
45
+
46
+ self.content = nil unless self.content.is_a?(String)
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ class Cms::Categorization < ActiveRecord::Base
2
+
3
+ ComfyPress.establish_connection(self)
4
+
5
+ self.table_name = 'cms_categorizations'
6
+
7
+ attr_accessible :categorized
8
+
9
+ # -- Relationships --------------------------------------------------------
10
+ belongs_to :category
11
+ belongs_to :categorized,
12
+ :polymorphic => true
13
+
14
+ # -- Validations ----------------------------------------------------------
15
+ validates :categorized_type, :categorized_id,
16
+ :presence => true
17
+ validates :category_id,
18
+ :presence => true,
19
+ :uniqueness => { :scope => [:categorized_type, :categorized_id] }
20
+
21
+ end
@@ -0,0 +1,30 @@
1
+ class Cms::Category < ActiveRecord::Base
2
+
3
+ ComfyPress.establish_connection(self)
4
+
5
+ self.table_name = 'cms_categories'
6
+
7
+ attr_accessible :label,
8
+ :categorized_type
9
+
10
+ # -- Relationships --------------------------------------------------------
11
+ belongs_to :site
12
+ has_many :categorizations,
13
+ :dependent => :destroy
14
+
15
+ # -- Validations ----------------------------------------------------------
16
+ validates :site_id,
17
+ :presence => true
18
+ validates :label,
19
+ :presence => true,
20
+ :uniqueness => { :scope => [:categorized_type, :site_id] }
21
+ validates :categorized_type,
22
+ :presence => true
23
+
24
+ # -- Scopes ---------------------------------------------------------------
25
+ default_scope order(:label)
26
+ scope :of_type, lambda { |type|
27
+ where(:categorized_type => type)
28
+ }
29
+
30
+ end
@@ -0,0 +1,75 @@
1
+ class Cms::File < ActiveRecord::Base
2
+
3
+ IMAGE_MIMETYPES = %w(gif jpeg pjpeg png svg+xml tiff).collect{|subtype| "image/#{subtype}"}
4
+
5
+ ComfyPress.establish_connection(self)
6
+
7
+ self.table_name = 'cms_files'
8
+
9
+ cms_is_categorized
10
+
11
+ attr_accessor :dimensions
12
+
13
+ attr_accessible :site, :site_id,
14
+ :file,
15
+ :dimensions,
16
+ :label,
17
+ :description,
18
+ :category_ids,
19
+ :position
20
+
21
+ # -- AR Extensions --------------------------------------------------------
22
+ has_attached_file :file, ComfyPress.config.upload_file_options.merge(
23
+ # dimensions accessor needs to be set before file assignment for this to work
24
+ :styles => lambda { |f|
25
+ if f.respond_to?(:instance) && f.instance.respond_to?(:dimensions)
26
+ (f.instance.dimensions.blank?? { } : { :original => f.instance.dimensions }).merge(
27
+ :cms_thumb => '80x60#'
28
+ )
29
+ end
30
+ }
31
+ )
32
+ before_post_process :is_image?
33
+
34
+ # -- Relationships --------------------------------------------------------
35
+ belongs_to :site
36
+ belongs_to :block
37
+
38
+ # -- Validations ----------------------------------------------------------
39
+ validates :site_id, :presence => true
40
+ validates_attachment_presence :file
41
+
42
+ # -- Callbacks ------------------------------------------------------------
43
+ before_save :assign_label
44
+ before_create :assign_position
45
+ after_save :reload_page_cache
46
+ after_destroy :reload_page_cache
47
+
48
+ # -- Scopes ---------------------------------------------------------------
49
+ scope :images, where(:file_content_type => IMAGE_MIMETYPES)
50
+ scope :not_images, where('file_content_type NOT IN (?)', IMAGE_MIMETYPES)
51
+
52
+ # -- Instance Methods -----------------------------------------------------
53
+ def is_image?
54
+ IMAGE_MIMETYPES.include?(file_content_type)
55
+ end
56
+
57
+ protected
58
+
59
+ def assign_label
60
+ self.label = self.label.blank?? self.file_file_name.gsub(/\.[^\.]*?$/, '').titleize : self.label
61
+ end
62
+
63
+ def assign_position
64
+ max = Cms::File.maximum(:position)
65
+ self.position = max ? max + 1 : 0
66
+ end
67
+
68
+ # FIX: Terrible, but no way of creating cached page content overwise
69
+ def reload_page_cache
70
+ return unless self.block
71
+ p = self.block.page
72
+ Cms::Page.where(:id => p.id).update_all(:content => p.content(true))
73
+ end
74
+
75
+ end
@@ -0,0 +1,99 @@
1
+ class Cms::Layout < ActiveRecord::Base
2
+
3
+ ComfyPress.establish_connection(self)
4
+
5
+ self.table_name = 'cms_layouts'
6
+
7
+ cms_acts_as_tree
8
+ cms_is_mirrored
9
+ cms_has_revisions_for :content, :css, :js
10
+
11
+ attr_accessible :label,
12
+ :identifier,
13
+ :content,
14
+ :css,
15
+ :js,
16
+ :parent, :parent_id,
17
+ :app_layout
18
+
19
+ # -- Relationships --------------------------------------------------------
20
+ belongs_to :site
21
+ has_many :pages, :dependent => :nullify
22
+
23
+ # -- Callbacks ------------------------------------------------------------
24
+ before_validation :assign_label
25
+ before_create :assign_position
26
+ after_save :clear_cached_page_content
27
+ after_destroy :clear_cached_page_content
28
+
29
+ # -- Validations ----------------------------------------------------------
30
+ validates :site_id,
31
+ :presence => true
32
+ validates :label,
33
+ :presence => true
34
+ validates :identifier,
35
+ :presence => true,
36
+ :uniqueness => { :scope => :site_id },
37
+ :format => { :with => /^\w[a-z0-9_-]*$/i }
38
+
39
+ # -- Scopes ---------------------------------------------------------------
40
+ default_scope order('cms_layouts.position')
41
+
42
+ # -- Class Methods --------------------------------------------------------
43
+ # Tree-like structure for layouts
44
+ def self.options_for_select(site, layout = nil, current_layout = nil, depth = 0, spacer = '. . ')
45
+ out = []
46
+ [current_layout || site.layouts.roots].flatten.each do |l|
47
+ next if layout == l
48
+ out << [ "#{spacer*depth}#{l.label}", l.id ]
49
+ l.children.each do |child|
50
+ out += options_for_select(site, layout, child, depth + 1, spacer)
51
+ end
52
+ end
53
+ return out.compact
54
+ end
55
+
56
+ # List of available application layouts
57
+ def self.app_layouts_for_select
58
+ Dir.glob(File.expand_path('app/views/layouts/**/*.html.*', Rails.root)).collect do |filename|
59
+ filename.gsub!("#{File.expand_path('app/views/layouts', Rails.root)}/", '')
60
+ filename.split('/').last[0...1] == '_' ? nil : filename.split('.').first
61
+ end.compact.sort
62
+ end
63
+
64
+ # -- Instance Methods -----------------------------------------------------
65
+ # magical merging tag is {cms:page:content} If parent layout has this tag
66
+ # defined its content will be merged. If no such tag found, parent content
67
+ # is ignored.
68
+ def merged_content
69
+ if parent
70
+ regex = /\{\{\s*cms:page:content:?(?:(?::text)|(?::rich_text))?\s*\}\}/
71
+ if parent.merged_content.match(regex)
72
+ parent.merged_content.gsub(regex, content.to_s)
73
+ else
74
+ content.to_s
75
+ end
76
+ else
77
+ content.to_s
78
+ end
79
+ end
80
+
81
+ protected
82
+
83
+ def assign_label
84
+ self.label = self.label.blank?? self.identifier.try(:titleize) : self.label
85
+ end
86
+
87
+ def assign_position
88
+ return if self.position.to_i > 0
89
+ max = self.site.layouts.where(:parent_id => self.parent_id).maximum(:position)
90
+ self.position = max ? max + 1 : 0
91
+ end
92
+
93
+ # Forcing page content reload
94
+ def clear_cached_page_content
95
+ self.pages.each{ |page| page.save! }
96
+ self.children.each{ |child_layout| child_layout.clear_cached_page_content }
97
+ end
98
+
99
+ end
@@ -0,0 +1,26 @@
1
+ class Cms::Menu < ActiveRecord::Base
2
+
3
+ attr_accessible :label, :site_id, :identifier
4
+
5
+ # -- Relationships --------------------------------------------------------
6
+ belongs_to :site
7
+ has_many :menu_items,
8
+ :autosave => true,
9
+ :dependent => :destroy
10
+
11
+ # -- Validations ----------------------------------------------------------
12
+ validates :site_id,
13
+ :presence => true
14
+ validates :label,
15
+ :presence => true
16
+
17
+ # -- Class Methods --------------------------------------------------------
18
+ def self.options_for_select(site)
19
+ out = []
20
+ site.menus.each do |amenu|
21
+ out << [ "#{amenu.label}", amenu.id ]
22
+ end
23
+ return out.compact
24
+ end
25
+
26
+ end
@@ -0,0 +1,14 @@
1
+ class Cms::MenuItem < ActiveRecord::Base
2
+ attr_accessible :label, :link, :menu_id, :page_id, :menu_item_type
3
+ # -- Relationships --------------------------------------------------------
4
+ belongs_to :menu
5
+
6
+ # -- Validations ----------------------------------------------------------
7
+ validates :menu_id,
8
+ :presence => true
9
+ validates :label,
10
+ :presence => true
11
+ # validates :menu_item_type,
12
+ # :presence => true
13
+
14
+ end
@@ -0,0 +1,202 @@
1
+ # encoding: utf-8
2
+
3
+ class Cms::Page < ActiveRecord::Base
4
+
5
+ ComfyPress.establish_connection(self)
6
+
7
+ self.table_name = 'cms_pages'
8
+
9
+ attr_accessible :layout, :layout_id,
10
+ :label,
11
+ :slug,
12
+ :parent, :parent_id,
13
+ :blocks_attributes,
14
+ :is_published,
15
+ :target_page_id, :menu_id,
16
+ :category_ids
17
+
18
+ cms_acts_as_tree :counter_cache => :children_count
19
+ cms_is_categorized
20
+ cms_is_mirrored
21
+ cms_has_revisions_for :blocks_attributes
22
+
23
+ attr_accessor :tags,
24
+ :blocks_attributes_changed
25
+
26
+ # -- Relationships --------------------------------------------------------
27
+ belongs_to :site
28
+ belongs_to :layout
29
+ belongs_to :target_page,
30
+ :class_name => 'Cms::Page'
31
+ has_many :blocks,
32
+ :autosave => true,
33
+ :dependent => :destroy
34
+ has_one :menu
35
+
36
+ # -- Callbacks ------------------------------------------------------------
37
+ before_validation :assigns_label,
38
+ :assign_parent,
39
+ :escape_slug,
40
+ :assign_full_path
41
+ before_create :assign_position
42
+ before_save :set_cached_content
43
+ after_save :sync_child_pages
44
+ after_find :unescape_slug_and_path
45
+
46
+ # -- Validations ----------------------------------------------------------
47
+ validates :site_id,
48
+ :presence => true
49
+ validates :label,
50
+ :presence => true
51
+ validates :slug,
52
+ :presence => true,
53
+ :uniqueness => { :scope => :parent_id },
54
+ :unless => lambda{ |p| p.site && (p.site.pages.count == 0 || p.site.pages.root == self) }
55
+ validates :layout,
56
+ :presence => true
57
+ validate :validate_target_page
58
+ validate :validate_format_of_unescaped_slug
59
+
60
+ # -- Scopes ---------------------------------------------------------------
61
+ default_scope order('cms_pages.position')
62
+ scope :published, where(:is_published => true)
63
+
64
+ # -- Class Methods --------------------------------------------------------
65
+ # Tree-like structure for pages
66
+ def self.options_for_select(site, page = nil, current_page = nil, depth = 0, exclude_self = true, spacer = '. . ')
67
+ return [] if (current_page ||= site.pages.root) == page && exclude_self || !current_page
68
+ out = []
69
+ out << [ "#{spacer*depth}#{current_page.label}", current_page.id ] unless current_page == page
70
+ current_page.children.each do |child|
71
+ out += options_for_select(site, page, child, depth + 1, exclude_self, spacer)
72
+ end
73
+ return out.compact
74
+ end
75
+
76
+ # -- Instance Methods -----------------------------------------------------
77
+ # For previewing purposes sometimes we need to have full_path set. This
78
+ # full path take care of the pages and its childs but not of the site path
79
+ def full_path
80
+ self.read_attribute(:full_path) || self.assign_full_path
81
+ end
82
+
83
+ # Transforms existing cms_block information into a hash that can be used
84
+ # during form processing. That's the only way to modify cms_blocks.
85
+ def blocks_attributes(was = false)
86
+ self.blocks.collect do |block|
87
+ block_attr = {}
88
+ block_attr[:identifier] = block.identifier
89
+ block_attr[:content] = was ? block.content_was : block.content
90
+ block_attr
91
+ end
92
+ end
93
+
94
+ # Array of block hashes in the following format:
95
+ # [
96
+ # { :identifier => 'block_1', :content => 'block content' },
97
+ # { :identifier => 'block_2', :content => 'block content' }
98
+ # ]
99
+ def blocks_attributes=(block_hashes = [])
100
+ block_hashes = block_hashes.values if block_hashes.is_a?(Hash)
101
+ block_hashes.each do |block_hash|
102
+ block_hash.symbolize_keys! unless block_hash.is_a?(HashWithIndifferentAccess)
103
+ block =
104
+ self.blocks.detect{|b| b.identifier == block_hash[:identifier]} ||
105
+ self.blocks.build(:identifier => block_hash[:identifier])
106
+ block.content = block_hash[:content]
107
+ self.blocks_attributes_changed = self.blocks_attributes_changed || block.content_changed?
108
+ end
109
+ end
110
+
111
+ # Processing content will return rendered content and will populate
112
+ # self.cms_tags with instances of CmsTag
113
+ def content(force_reload = false)
114
+ @content = force_reload ? nil : read_attribute(:content)
115
+ @content ||= begin
116
+ self.tags = [] # resetting
117
+ if layout
118
+ ComfyPress::Tag.process_content(
119
+ self,
120
+ ComfyPress::Tag.sanitize_irb(layout.merged_content)
121
+ )
122
+ else
123
+ ''
124
+ end
125
+ end
126
+ end
127
+
128
+ # Array of cms_tags for a page. Content generation is called if forced.
129
+ # These also include initialized cms_blocks if present
130
+ def tags(force_reload = false)
131
+ self.content(true) if force_reload
132
+ @tags ||= []
133
+ end
134
+
135
+ # Full url for a page
136
+ def url
137
+ "http://" + "#{self.site.hostname}/#{self.site.path}/#{self.full_path}".squeeze("/")
138
+ end
139
+
140
+ # Method to collect prevous state of blocks for revisions
141
+ def blocks_attributes_was
142
+ blocks_attributes(true)
143
+ end
144
+
145
+ protected
146
+
147
+ def assigns_label
148
+ self.label = self.label.blank?? self.slug.try(:titleize) : self.label
149
+ end
150
+
151
+ def assign_parent
152
+ return unless site
153
+ self.parent ||= site.pages.root unless self == site.pages.root || site.pages.count == 0
154
+ end
155
+
156
+ def assign_full_path
157
+ self.full_path = self.parent ? "#{CGI::escape(self.parent.full_path).gsub('%2F', '/')}/#{self.slug}".squeeze('/') : '/'
158
+ end
159
+
160
+ def assign_position
161
+ return unless self.parent
162
+ return if self.position.to_i > 0
163
+ max = self.parent.children.maximum(:position)
164
+ self.position = max ? max + 1 : 0
165
+ end
166
+
167
+ def validate_target_page
168
+ return unless self.target_page
169
+ p = self
170
+ while p.target_page do
171
+ return self.errors.add(:target_page_id, 'Invalid Redirect') if (p = p.target_page) == self
172
+ end
173
+ end
174
+
175
+ def validate_format_of_unescaped_slug
176
+ return unless slug.present?
177
+ unescaped_slug = CGI::unescape(self.slug)
178
+ errors.add(:slug, :invalid) unless unescaped_slug =~ /^\p{Alnum}[\.\p{Alnum}\p{Mark}_-]*$/i
179
+ end
180
+
181
+ # NOTE: This can create 'phantom' page blocks as they are defined in the layout. This is normal.
182
+ def set_cached_content
183
+ write_attribute(:content, self.content(true))
184
+ end
185
+
186
+ # Forcing re-saves for child pages so they can update full_paths
187
+ def sync_child_pages
188
+ children.each{ |p| p.save! } if full_path_changed?
189
+ end
190
+
191
+ # Escape slug unless it's nonexistent (root)
192
+ def escape_slug
193
+ self.slug = CGI::escape(self.slug) unless self.slug.nil?
194
+ end
195
+
196
+ # Unescape the slug and full path back into their original forms unless they're nonexistent
197
+ def unescape_slug_and_path
198
+ self.slug = CGI::unescape(self.slug) unless self.slug.nil?
199
+ self.full_path = CGI::unescape(self.full_path) unless self.full_path.nil?
200
+ end
201
+
202
+ end
@@ -0,0 +1,17 @@
1
+ class Cms::Revision < ActiveRecord::Base
2
+
3
+ ComfyPress.establish_connection(self)
4
+
5
+ self.table_name = 'cms_revisions'
6
+
7
+ serialize :data
8
+
9
+ attr_accessible :data
10
+
11
+ # -- Relationships --------------------------------------------------------
12
+ belongs_to :record, :polymorphic => true
13
+
14
+ # -- Scopes ---------------------------------------------------------------
15
+ default_scope order('created_at DESC')
16
+
17
+ end
@@ -0,0 +1,113 @@
1
+ class Cms::Site < ActiveRecord::Base
2
+
3
+ ComfyPress.establish_connection(self)
4
+
5
+ self.table_name = 'cms_sites'
6
+
7
+ attr_accessible :identifier,
8
+ :label,
9
+ :hostname,
10
+ :path,
11
+ :locale,
12
+ :is_mirrored,
13
+ :menu_id
14
+
15
+ # -- Relationships --------------------------------------------------------
16
+ with_options :dependent => :destroy do |site|
17
+ site.has_many :layouts
18
+ site.has_many :pages
19
+ site.has_many :snippets
20
+ site.has_many :files
21
+ site.has_many :categories
22
+ site.has_many :menus
23
+ site.has_many :menu_items, :through => :menus
24
+ end
25
+
26
+ # -- Callbacks ------------------------------------------------------------
27
+ before_validation :assign_identifier,
28
+ :assign_hostname,
29
+ :assign_label
30
+ before_save :clean_path
31
+ after_save :sync_mirrors
32
+
33
+ # -- Validations ----------------------------------------------------------
34
+ validates :identifier,
35
+ :presence => true,
36
+ :uniqueness => true,
37
+ :format => { :with => /^\w[a-z0-9_-]*$/i }
38
+ validates :label,
39
+ :presence => true
40
+ validates :hostname,
41
+ :presence => true,
42
+ :uniqueness => { :scope => :path },
43
+ :format => { :with => /^[\w\.\-]+(?:\:\d+)?$/ }
44
+
45
+ # -- Scopes ---------------------------------------------------------------
46
+ scope :mirrored, where(:is_mirrored => true)
47
+
48
+ # -- Class Methods --------------------------------------------------------
49
+ # returning the Cms::Site instance based on host and path
50
+ def self.find_site(host, path = nil)
51
+ return Cms::Site.first if Cms::Site.count == 1
52
+ cms_site = nil
53
+ Cms::Site.find_all_by_hostname(real_host_from_aliases(host)).each do |site|
54
+ if site.path.blank?
55
+ cms_site = site
56
+ elsif "#{path.to_s.split('?')[0]}/".match /^\/#{Regexp.escape(site.path.to_s)}\//
57
+ cms_site = site
58
+ break
59
+ end
60
+ end
61
+ return cms_site
62
+ end
63
+
64
+ # -- Instance Methods -----------------------------------------------------
65
+ # When removing entire site, let's not destroy content from other sites
66
+ # Since before_destroy doesn't really work, this does the trick
67
+ def destroy
68
+ self.class.update_all({:is_mirrored => false}, :id => self.id) if self.is_mirrored?
69
+ super
70
+ end
71
+
72
+ protected
73
+
74
+ def self.real_host_from_aliases(host)
75
+ if aliases = ComfyPress.config.hostname_aliases
76
+ aliases.each do |alias_host, aliases|
77
+ return alias_host if aliases.include?(host)
78
+ end
79
+ end
80
+ host
81
+ end
82
+
83
+ def assign_identifier
84
+ self.identifier = self.identifier.blank?? self.hostname.try(:slugify) : self.identifier
85
+ end
86
+
87
+ def assign_hostname
88
+ self.hostname ||= self.identifier
89
+ end
90
+
91
+ def assign_label
92
+ self.label = self.label.blank?? self.identifier.try(:titleize) : self.label
93
+ end
94
+
95
+ def clean_path
96
+ self.path ||= ''
97
+ self.path.squeeze!('/')
98
+ self.path.gsub!(/\/$/, '')
99
+ end
100
+
101
+ # When site is marked as a mirror we need to sync its structure
102
+ # with other mirrors.
103
+ def sync_mirrors
104
+ return unless is_mirrored_changed? && is_mirrored?
105
+
106
+ [self, Cms::Site.mirrored.where("id != #{id}").first].compact.each do |site|
107
+ (site.layouts(:reload).roots + site.layouts.roots.map(&:descendants)).flatten.map(&:sync_mirror)
108
+ (site.pages(:reload).roots + site.pages.roots.map(&:descendants)).flatten.map(&:sync_mirror)
109
+ site.snippets(:reload).map(&:sync_mirror)
110
+ end
111
+ end
112
+
113
+ end