comfypress 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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