trusty-cms 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (388) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +23 -0
  3. data/CHANGELOG.md +754 -0
  4. data/CONTRIBUTORS.md +263 -0
  5. data/Gemfile +40 -0
  6. data/Gemfile.lock +177 -0
  7. data/INSTALL.md +10 -0
  8. data/LICENSE.md +21 -0
  9. data/README.md +85 -0
  10. data/Rakefile +7 -0
  11. data/app/controllers/admin/configuration_controller.rb +50 -0
  12. data/app/controllers/admin/extensions_controller.rb +11 -0
  13. data/app/controllers/admin/layouts_controller.rb +8 -0
  14. data/app/controllers/admin/page_fields_controller.rb +9 -0
  15. data/app/controllers/admin/page_parts_controller.rb +9 -0
  16. data/app/controllers/admin/pages_controller.rb +89 -0
  17. data/app/controllers/admin/preferences_controller.rb +46 -0
  18. data/app/controllers/admin/references_controller.rb +7 -0
  19. data/app/controllers/admin/resource_controller.rb +245 -0
  20. data/app/controllers/admin/users_controller.rb +40 -0
  21. data/app/controllers/admin/welcome_controller.rb +48 -0
  22. data/app/controllers/application_controller.rb +114 -0
  23. data/app/controllers/site_controller.rb +97 -0
  24. data/app/helpers/admin/configuration_helper.rb +80 -0
  25. data/app/helpers/admin/export_helper.rb +2 -0
  26. data/app/helpers/admin/extensions_helper.rb +2 -0
  27. data/app/helpers/admin/layouts_helper.rb +21 -0
  28. data/app/helpers/admin/node_helper.rb +96 -0
  29. data/app/helpers/admin/pages_helper.rb +117 -0
  30. data/app/helpers/admin/preferences_helper.rb +3 -0
  31. data/app/helpers/admin/references_helper.rb +46 -0
  32. data/app/helpers/admin/regions_helper.rb +28 -0
  33. data/app/helpers/admin/resource_helper.rb +2 -0
  34. data/app/helpers/admin/users_helper.rb +8 -0
  35. data/app/helpers/admin/welcome_helper.rb +2 -0
  36. data/app/helpers/application_helper.rb +241 -0
  37. data/app/helpers/site_helper.rb +2 -0
  38. data/app/models/deprecated_tags.rb +42 -0
  39. data/app/models/file_not_found_page.rb +32 -0
  40. data/app/models/layout.rb +15 -0
  41. data/app/models/menu_renderer.rb +140 -0
  42. data/app/models/page.rb +377 -0
  43. data/app/models/page_context.rb +53 -0
  44. data/app/models/page_field.rb +3 -0
  45. data/app/models/page_part.rb +20 -0
  46. data/app/models/standard_tags.rb +1318 -0
  47. data/app/models/status.rb +41 -0
  48. data/app/models/text_filter.rb +36 -0
  49. data/app/models/trusty_cms/config.rb +313 -0
  50. data/app/models/trusty_cms/page_response_cache_director.rb +43 -0
  51. data/app/models/user.rb +96 -0
  52. data/app/models/user_action_observer.rb +25 -0
  53. data/app/views/admin/configuration/edit.html.haml +38 -0
  54. data/app/views/admin/configuration/show.html.haml +58 -0
  55. data/app/views/admin/extensions/index.html.haml +41 -0
  56. data/app/views/admin/layouts/_form.html.haml +39 -0
  57. data/app/views/admin/layouts/edit.html.haml +9 -0
  58. data/app/views/admin/layouts/index.html.haml +35 -0
  59. data/app/views/admin/layouts/new.html.haml +7 -0
  60. data/app/views/admin/layouts/remove.html.haml +16 -0
  61. data/app/views/admin/page_fields/_page_field.html.haml +12 -0
  62. data/app/views/admin/page_parts/_page_part.html.haml +18 -0
  63. data/app/views/admin/pages/_fields.html.haml +71 -0
  64. data/app/views/admin/pages/_meta_row.html.haml +8 -0
  65. data/app/views/admin/pages/_node.html.haml +26 -0
  66. data/app/views/admin/pages/_popups.html.haml +27 -0
  67. data/app/views/admin/pages/children.html.haml +2 -0
  68. data/app/views/admin/pages/edit.html.haml +12 -0
  69. data/app/views/admin/pages/index.html.haml +26 -0
  70. data/app/views/admin/pages/new.html.haml +10 -0
  71. data/app/views/admin/pages/remove.html.haml +18 -0
  72. data/app/views/admin/preferences/edit.html.haml +44 -0
  73. data/app/views/admin/references/_tag_reference.haml +3 -0
  74. data/app/views/admin/references/filters.haml +6 -0
  75. data/app/views/admin/references/tags.haml +30 -0
  76. data/app/views/admin/users/_avatar.html.haml +5 -0
  77. data/app/views/admin/users/_form.html.haml +53 -0
  78. data/app/views/admin/users/_password_fields.html.haml +18 -0
  79. data/app/views/admin/users/edit.html.haml +9 -0
  80. data/app/views/admin/users/index.html.haml +39 -0
  81. data/app/views/admin/users/new.html.haml +6 -0
  82. data/app/views/admin/users/remove.html.haml +16 -0
  83. data/app/views/admin/welcome/login.html.haml +23 -0
  84. data/app/views/layouts/application.html.haml +50 -0
  85. data/app/views/site/not_found.html.haml +3 -0
  86. data/bin/ci/before_script +15 -0
  87. data/bin/trusty_cms +53 -0
  88. data/config/application.rb +124 -0
  89. data/config/boot.rb +9 -0
  90. data/config/compass.config +15 -0
  91. data/config/database.db2.yml +20 -0
  92. data/config/database.mysql.yml +25 -0
  93. data/config/database.postgresql.yml +25 -0
  94. data/config/database.sqlite.yml +16 -0
  95. data/config/database.sqlserver.yml +20 -0
  96. data/config/environment.rb +5 -0
  97. data/config/environments/development.rb +19 -0
  98. data/config/environments/production.rb +27 -0
  99. data/config/environments/test.rb +35 -0
  100. data/config/initializers/active_record_extensions.rb +1 -0
  101. data/config/initializers/configuration_extensions.rb +0 -0
  102. data/config/initializers/haml.rb +2 -0
  103. data/config/initializers/object_extensions.rb +1 -0
  104. data/config/initializers/radiant_config.rb +16 -0
  105. data/config/initializers/rails_patch.rb +2 -0
  106. data/config/initializers/response_cache_timeout.rb +3 -0
  107. data/config/initializers/string_extensions.rb +1 -0
  108. data/config/initializers/symbol_extensions.rb +1 -0
  109. data/config/initializers/tmp.rb +2 -0
  110. data/config/locales/en.yml +230 -0
  111. data/config/locales/en_available_tags.yml +625 -0
  112. data/config/preinitializer.rb +18 -0
  113. data/config/routes.rb +30 -0
  114. data/db/migrate/001_create_radiant_tables.rb +75 -0
  115. data/db/migrate/002_insert_initial_data.rb +12 -0
  116. data/db/migrate/003_rename_behavior_column.rb +9 -0
  117. data/db/migrate/004_rename_filter_column.rb +11 -0
  118. data/db/migrate/005_add_virtual_column_to_page.rb +9 -0
  119. data/db/migrate/006_integer_columns_to_boolean.rb +36 -0
  120. data/db/migrate/007_remove_virtual_column_from_page.rb +9 -0
  121. data/db/migrate/008_add_virtual_column_to_page_again.rb +9 -0
  122. data/db/migrate/009_add_content_type_field_to_layout.rb +9 -0
  123. data/db/migrate/010_merge_behaviors_and_pages.rb +57 -0
  124. data/db/migrate/011_rename_type_column_on_page_to_class_name.rb +9 -0
  125. data/db/migrate/012_create_extension_meta.rb +13 -0
  126. data/db/migrate/013_add_notes_field_to_user.rb +9 -0
  127. data/db/migrate/014_rename_config_default_parts_key.rb +17 -0
  128. data/db/migrate/015_add_optimistic_locking.rb +15 -0
  129. data/db/migrate/016_add_sessions.rb +16 -0
  130. data/db/migrate/017_rename_created_by_updated_by_columns.rb +15 -0
  131. data/db/migrate/018_add_description_and_keywords_to_pages.rb +11 -0
  132. data/db/migrate/019_add_salt_to_users.rb +11 -0
  133. data/db/migrate/020_add_session_info_to_users.rb +11 -0
  134. data/db/migrate/021_remove_session_expire_from_users.rb +9 -0
  135. data/db/migrate/20081203140407_add_indexes.rb +19 -0
  136. data/db/migrate/20090226140109_add_user_language.rb +9 -0
  137. data/db/migrate/20090929164633_rename_developer_role_to_designer.rb +9 -0
  138. data/db/migrate/20091003095744_change_user_language_to_locale.rb +9 -0
  139. data/db/migrate/20100805154952_add_page_fields.rb +15 -0
  140. data/db/migrate/20100805155020_convert_page_metas.rb +24 -0
  141. data/db/migrate/20100810151922_add_field_name_index.rb +11 -0
  142. data/db/migrate/20110902203823_add_allowed_children_cache_to_pages.rb +13 -0
  143. data/db/migrate/20111016150725_extend_page_part_content_limit.rb +15 -0
  144. data/db/migrate/20120209231801_change_pages_allowed_children_cache_to_text.rb +11 -0
  145. data/db/schema.rb +128 -0
  146. data/lib/active_record_extensions/active_record_extensions.rb +27 -0
  147. data/lib/annotatable.rb +68 -0
  148. data/lib/configuration_extensions/configuration_extensions.rb +357 -0
  149. data/lib/generators/extension/USAGE +30 -0
  150. data/lib/generators/extension/extension_generator.rb +97 -0
  151. data/lib/generators/extension/templates/README.md +6 -0
  152. data/lib/generators/extension/templates/RSpecRakefile +109 -0
  153. data/lib/generators/extension/templates/Rakefile +25 -0
  154. data/lib/generators/extension/templates/cucumber.yml +1 -0
  155. data/lib/generators/extension/templates/cucumber_env.rb +11 -0
  156. data/lib/generators/extension/templates/cucumber_paths.rb +22 -0
  157. data/lib/generators/extension/templates/en.yml +3 -0
  158. data/lib/generators/extension/templates/extension.rb +21 -0
  159. data/lib/generators/extension/templates/functional_test.rb +15 -0
  160. data/lib/generators/extension/templates/gemspec.rb +29 -0
  161. data/lib/generators/extension/templates/lib.rb +8 -0
  162. data/lib/generators/extension/templates/migration.rb +9 -0
  163. data/lib/generators/extension/templates/radiant_config.rb +3 -0
  164. data/lib/generators/extension/templates/routes.rb +5 -0
  165. data/lib/generators/extension/templates/spec.opts +6 -0
  166. data/lib/generators/extension/templates/spec_helper.rb +42 -0
  167. data/lib/generators/extension/templates/tasks.rake +47 -0
  168. data/lib/generators/extension/templates/test_helper.rb +26 -0
  169. data/lib/generators/extension_controller/USAGE +36 -0
  170. data/lib/generators/extension_controller/extension_controller_generator.rb +84 -0
  171. data/lib/generators/extension_controller/templates/controller.rb +10 -0
  172. data/lib/generators/extension_controller/templates/controller_spec.rb +23 -0
  173. data/lib/generators/extension_controller/templates/functional_test.rb +11 -0
  174. data/lib/generators/extension_controller/templates/helper.rb +2 -0
  175. data/lib/generators/extension_controller/templates/helper_spec.rb +11 -0
  176. data/lib/generators/extension_controller/templates/helper_test.rb +4 -0
  177. data/lib/generators/extension_controller/templates/view.html.erb +2 -0
  178. data/lib/generators/extension_controller/templates/view_spec.rb +12 -0
  179. data/lib/generators/extension_mailer/USAGE +17 -0
  180. data/lib/generators/extension_mailer/extension_mailer_generator.rb +68 -0
  181. data/lib/generators/extension_mailer/templates/fixture.erb +3 -0
  182. data/lib/generators/extension_mailer/templates/mailer.rb +15 -0
  183. data/lib/generators/extension_mailer/templates/unit_test.rb +21 -0
  184. data/lib/generators/extension_mailer/templates/view.erb +3 -0
  185. data/lib/generators/extension_migration/USAGE +34 -0
  186. data/lib/generators/extension_migration/extension_migration_generator.rb +25 -0
  187. data/lib/generators/extension_migration/templates/migration.rb +11 -0
  188. data/lib/generators/extension_model/USAGE +35 -0
  189. data/lib/generators/extension_model/extension_model_generator.rb +68 -0
  190. data/lib/generators/extension_model/templates/fixtures.yml +19 -0
  191. data/lib/generators/extension_model/templates/migration.rb +16 -0
  192. data/lib/generators/extension_model/templates/model.rb +2 -0
  193. data/lib/generators/extension_model/templates/model_spec.rb +11 -0
  194. data/lib/generators/extension_model/templates/unit_test.rb +8 -0
  195. data/lib/generators/generator_base_extension.rb +16 -0
  196. data/lib/generators/instance/instance_generator.rb +148 -0
  197. data/lib/generators/instance/templates/databases/db2.yml +40 -0
  198. data/lib/generators/instance/templates/databases/mysql.yml +47 -0
  199. data/lib/generators/instance/templates/databases/postgresql.yml +44 -0
  200. data/lib/generators/instance/templates/databases/sqlite3.yml +16 -0
  201. data/lib/generators/instance/templates/databases/sqlserver.yml +21 -0
  202. data/lib/generators/instance/templates/instance_boot.rb +122 -0
  203. data/lib/generators/instance/templates/instance_config.ru +2 -0
  204. data/lib/generators/instance/templates/instance_environment.rb +83 -0
  205. data/lib/generators/instance/templates/instance_gemfile +81 -0
  206. data/lib/generators/instance/templates/instance_generate +6 -0
  207. data/lib/generators/instance/templates/instance_radiant_config.rb +16 -0
  208. data/lib/generators/instance/templates/instance_rakefile +3 -0
  209. data/lib/generators/instance/templates/instance_routes.rb +1 -0
  210. data/lib/generators/language_extension/USAGE +27 -0
  211. data/lib/generators/language_extension/language_extension_generator.rb +71 -0
  212. data/lib/generators/language_extension/templates/README +3 -0
  213. data/lib/generators/language_extension/templates/RSpecRakefile +123 -0
  214. data/lib/generators/language_extension/templates/Rakefile +25 -0
  215. data/lib/generators/language_extension/templates/available_tags.yml +553 -0
  216. data/lib/generators/language_extension/templates/cucumber.yml +1 -0
  217. data/lib/generators/language_extension/templates/cucumber_env.rb +16 -0
  218. data/lib/generators/language_extension/templates/cucumber_paths.rb +14 -0
  219. data/lib/generators/language_extension/templates/extension.rb +12 -0
  220. data/lib/generators/language_extension/templates/functional_test.rb +15 -0
  221. data/lib/generators/language_extension/templates/gemspec.rb +24 -0
  222. data/lib/generators/language_extension/templates/lang.yml +181 -0
  223. data/lib/generators/language_extension/templates/lib.rb +8 -0
  224. data/lib/generators/language_extension/templates/spec.opts +6 -0
  225. data/lib/generators/language_extension/templates/spec_helper.rb +36 -0
  226. data/lib/generators/language_extension/templates/tasks.rake +28 -0
  227. data/lib/generators/language_extension/templates/test_helper.rb +26 -0
  228. data/lib/inheritable_class_attributes.rb +65 -0
  229. data/lib/local_time.rb +6 -0
  230. data/lib/login_system.rb +132 -0
  231. data/lib/method_observer.rb +50 -0
  232. data/lib/object_extensions/object_extensions.rb +5 -0
  233. data/lib/simpleton.rb +21 -0
  234. data/lib/string_extensions/string_extensions.rb +25 -0
  235. data/lib/symbol_extensions/symbol_extensions.rb +5 -0
  236. data/lib/tasks/database.rake +85 -0
  237. data/lib/tasks/environments.rake +11 -0
  238. data/lib/tasks/extensions.rake +85 -0
  239. data/lib/tasks/framework.rake +255 -0
  240. data/lib/tasks/instance.rake +23 -0
  241. data/lib/tasks/prototype.rake +31 -0
  242. data/lib/tasks/radiant_config.rake +18 -0
  243. data/lib/tasks/release.rake +131 -0
  244. data/lib/tasks/translate.rake +59 -0
  245. data/lib/tasks/undefine.rake +28 -0
  246. data/lib/translation_support.rb +54 -0
  247. data/lib/trusty_cms/admin_ui/region_partials.rb +23 -0
  248. data/lib/trusty_cms/admin_ui/region_set.rb +35 -0
  249. data/lib/trusty_cms/admin_ui.rb +244 -0
  250. data/lib/trusty_cms/available_locales.rb +19 -0
  251. data/lib/trusty_cms/cache.rb +100 -0
  252. data/lib/trusty_cms/config/definition.rb +142 -0
  253. data/lib/trusty_cms/engine.rb +10 -0
  254. data/lib/trusty_cms/extension/script.rb +395 -0
  255. data/lib/trusty_cms/extension.rb +146 -0
  256. data/lib/trusty_cms/extension_loader.rb +130 -0
  257. data/lib/trusty_cms/extension_migrator.rb +98 -0
  258. data/lib/trusty_cms/extension_path.rb +200 -0
  259. data/lib/trusty_cms/initializer.rb +162 -0
  260. data/lib/trusty_cms/pagination/controller.rb +41 -0
  261. data/lib/trusty_cms/pagination/link_renderer.rb +34 -0
  262. data/lib/trusty_cms/resource_responses.rb +121 -0
  263. data/lib/trusty_cms/setup.rb +227 -0
  264. data/lib/trusty_cms/taggable.rb +138 -0
  265. data/lib/trusty_cms/task_support.rb +66 -0
  266. data/lib/trusty_cms.rb +19 -0
  267. data/log/.keep +0 -0
  268. data/public/.htaccess +40 -0
  269. data/public/404.html +8 -0
  270. data/public/500.html +8 -0
  271. data/public/dispatch.cgi +10 -0
  272. data/public/dispatch.fcgi +24 -0
  273. data/public/dispatch.rb +10 -0
  274. data/public/favicon.ico +0 -0
  275. data/public/images/admin/add_tab.png +0 -0
  276. data/public/images/admin/avatar_32x32.png +0 -0
  277. data/public/images/admin/avatar_64x64.png +0 -0
  278. data/public/images/admin/avatar_96x96.png +0 -0
  279. data/public/images/admin/brown_bottom_line.gif +0 -0
  280. data/public/images/admin/calendar_down.gif +0 -0
  281. data/public/images/admin/collapse.png +0 -0
  282. data/public/images/admin/draft_page.png +0 -0
  283. data/public/images/admin/expand.png +0 -0
  284. data/public/images/admin/image.png +0 -0
  285. data/public/images/admin/javascript.png +0 -0
  286. data/public/images/admin/layout.png +0 -0
  287. data/public/images/admin/metadata_toggle.png +0 -0
  288. data/public/images/admin/minus.png +0 -0
  289. data/public/images/admin/minus_disabled.png +0 -0
  290. data/public/images/admin/minus_grey.png +0 -0
  291. data/public/images/admin/navigation_secondary_separator.gif +0 -0
  292. data/public/images/admin/page.png +0 -0
  293. data/public/images/admin/plus.png +0 -0
  294. data/public/images/admin/plus_disabled.png +0 -0
  295. data/public/images/admin/plus_grey.png +0 -0
  296. data/public/images/admin/popup_border_background.png +0 -0
  297. data/public/images/admin/popup_border_bottom_left.png +0 -0
  298. data/public/images/admin/popup_border_bottom_right.png +0 -0
  299. data/public/images/admin/popup_border_top_left.png +0 -0
  300. data/public/images/admin/popup_border_top_right.png +0 -0
  301. data/public/images/admin/search.png +0 -0
  302. data/public/images/admin/single_form_shadow.png +0 -0
  303. data/public/images/admin/snippet.png +0 -0
  304. data/public/images/admin/spinner.gif +0 -0
  305. data/public/images/admin/status_background.png +0 -0
  306. data/public/images/admin/status_bottom_left.png +0 -0
  307. data/public/images/admin/status_bottom_right.png +0 -0
  308. data/public/images/admin/status_spinner.gif +0 -0
  309. data/public/images/admin/status_top_left.png +0 -0
  310. data/public/images/admin/status_top_right.png +0 -0
  311. data/public/images/admin/stylesheet.png +0 -0
  312. data/public/images/admin/tab_close.png +0 -0
  313. data/public/images/admin/upload.png +0 -0
  314. data/public/images/admin/virtual_page.png +0 -0
  315. data/public/images/radiant/radiant-badge-color.png +0 -0
  316. data/public/javascripts/admin/application.js +94 -0
  317. data/public/javascripts/admin/codearea.js +165 -0
  318. data/public/javascripts/admin/controls.js +965 -0
  319. data/public/javascripts/admin/cookie.js +80 -0
  320. data/public/javascripts/admin/dateinput.js +402 -0
  321. data/public/javascripts/admin/dragdrop.js +974 -0
  322. data/public/javascripts/admin/dropdown.js +216 -0
  323. data/public/javascripts/admin/effects.js +1122 -0
  324. data/public/javascripts/admin/lowpro.js +340 -0
  325. data/public/javascripts/admin/overrides.js +1 -0
  326. data/public/javascripts/admin/page_preview.js +41 -0
  327. data/public/javascripts/admin/pagefield.js +54 -0
  328. data/public/javascripts/admin/pagestatus.js +17 -0
  329. data/public/javascripts/admin/popup.js +339 -0
  330. data/public/javascripts/admin/prototype.js +4874 -0
  331. data/public/javascripts/admin/ruledtable.js +13 -0
  332. data/public/javascripts/admin/shortcuts.js +33 -0
  333. data/public/javascripts/admin/sitemap.js +149 -0
  334. data/public/javascripts/admin/status.js +233 -0
  335. data/public/javascripts/admin/tabcontrol.js +123 -0
  336. data/public/javascripts/admin/toggle.js +430 -0
  337. data/public/javascripts/admin/utility.js +53 -0
  338. data/public/javascripts/admin/validationerror.js +18 -0
  339. data/public/loading-iframe.html +11 -0
  340. data/public/robots.txt +1 -0
  341. data/public/stylesheets/sass/admin/_base.sass +18 -0
  342. data/public/stylesheets/sass/admin/main.sass +27 -0
  343. data/public/stylesheets/sass/admin/modules/_boxes.sass +6 -0
  344. data/public/stylesheets/sass/admin/modules/_gradients.sass +4 -0
  345. data/public/stylesheets/sass/admin/modules/_links.sass +18 -0
  346. data/public/stylesheets/sass/admin/overrides.sass +2 -0
  347. data/public/stylesheets/sass/admin/partials/_actions.sass +96 -0
  348. data/public/stylesheets/sass/admin/partials/_avatars.sass +10 -0
  349. data/public/stylesheets/sass/admin/partials/_content.sass +58 -0
  350. data/public/stylesheets/sass/admin/partials/_dateinput.sass +62 -0
  351. data/public/stylesheets/sass/admin/partials/_deprecated.sass +55 -0
  352. data/public/stylesheets/sass/admin/partials/_dropdown.sass +28 -0
  353. data/public/stylesheets/sass/admin/partials/_footer.sass +12 -0
  354. data/public/stylesheets/sass/admin/partials/_forms.sass +322 -0
  355. data/public/stylesheets/sass/admin/partials/_header.sass +114 -0
  356. data/public/stylesheets/sass/admin/partials/_index.sass +147 -0
  357. data/public/stylesheets/sass/admin/partials/_layout.sass +10 -0
  358. data/public/stylesheets/sass/admin/partials/_messages.sass +10 -0
  359. data/public/stylesheets/sass/admin/partials/_popup.sass +213 -0
  360. data/public/stylesheets/sass/admin/partials/_tabcontrol.sass +72 -0
  361. data/public/stylesheets/sass/admin/partials/_toolbar.sass +31 -0
  362. data/public/stylesheets/sass/admin/partials/_typography.sass +60 -0
  363. data/public/stylesheets/sass/admin/partials/_validations.sass +19 -0
  364. data/rails/init.rb +1 -0
  365. data/script/about +4 -0
  366. data/script/breakpointer +3 -0
  367. data/script/console +3 -0
  368. data/script/dbconsole +3 -0
  369. data/script/destroy +3 -0
  370. data/script/extension +5 -0
  371. data/script/generate +3 -0
  372. data/script/performance/benchmarker +3 -0
  373. data/script/performance/profiler +3 -0
  374. data/script/performance/request +3 -0
  375. data/script/plugin +3 -0
  376. data/script/process/inspector +3 -0
  377. data/script/process/reaper +3 -0
  378. data/script/process/spawner +3 -0
  379. data/script/process/spinner +3 -0
  380. data/script/rails +6 -0
  381. data/script/runner +3 -0
  382. data/script/server +3 -0
  383. data/script/version +5 -0
  384. data/spec/ci/database.mysql.yml +4 -0
  385. data/spec/ci/database.postgresql.yml +4 -0
  386. data/spec/ci/database.sqlite.yml +3 -0
  387. data/trusty_cms.gemspec +50 -0
  388. metadata +781 -0
@@ -0,0 +1,1318 @@
1
+ require 'trusty_cms/taggable'
2
+ require 'local_time'
3
+ module StandardTags
4
+
5
+ include TrustyCms::Taggable
6
+ include LocalTime
7
+
8
+ require "will_paginate/view_helpers"
9
+ include WillPaginate::ViewHelpers
10
+
11
+ class TagError < StandardError; end
12
+ class RequiredAttributeError < StandardError; end
13
+
14
+ desc %{
15
+ Causes the tags referring to a page's attributes to refer to the current page.
16
+
17
+ *Usage:*
18
+
19
+ <pre><code><r:page>...</r:page></code></pre>
20
+ }
21
+ tag 'page' do |tag|
22
+ tag.locals.page = tag.globals.page
23
+ tag.expand
24
+ end
25
+
26
+ [:breadcrumb, :slug, :title].each do |method|
27
+ desc %{
28
+ Renders the @#{method}@ attribute of the current page.
29
+ }
30
+ tag method.to_s do |tag|
31
+ tag.locals.page.send(method)
32
+ end
33
+ end
34
+
35
+ desc %{
36
+ Renders the @path@ attribute of the current page.
37
+ }
38
+ tag 'path' do |tag|
39
+ relative_url_for(tag.locals.page.path, tag.globals.page.request)
40
+ end
41
+ deprecated_tag 'url', :substitute => 'path', :deadline => '1.2'
42
+
43
+ desc %{
44
+ Gives access to a page's children.
45
+
46
+ *Usage:*
47
+
48
+ <pre><code><r:children>...</r:children></code></pre>
49
+ }
50
+ tag 'children' do |tag|
51
+ tag.locals.children = tag.locals.page.children
52
+ tag.expand
53
+ end
54
+
55
+ desc %{
56
+ Renders the total number of children.
57
+ }
58
+ tag 'children:count' do |tag|
59
+ options = children_find_options(tag)
60
+ options.delete(:order) # Order is irrelevant
61
+ tag.locals.children.count(options)
62
+ end
63
+
64
+ desc %{
65
+ Returns the first child. Inside this tag all page attribute tags are mapped to
66
+ the first child. Takes the same ordering options as @<r:children:each>@.
67
+
68
+ *Usage:*
69
+
70
+ <pre><code><r:children:first>...</r:children:first></code></pre>
71
+ }
72
+ tag 'children:first' do |tag|
73
+ options = children_find_options(tag)
74
+ children = tag.locals.children.where(options)
75
+ if first = children.first
76
+ tag.locals.page = first
77
+ tag.expand
78
+ end
79
+ end
80
+
81
+ desc %{
82
+ Returns the last child. Inside this tag all page attribute tags are mapped to
83
+ the last child. Takes the same ordering options as @<r:children:each>@.
84
+
85
+ *Usage:*
86
+
87
+ <pre><code><r:children:last>...</r:children:last></code></pre>
88
+ }
89
+ tag 'children:last' do |tag|
90
+ options = children_find_options(tag)
91
+ children = tag.locals.children.where(options)
92
+ if last = children.last
93
+ tag.locals.page = last
94
+ tag.expand
95
+ end
96
+ end
97
+
98
+ desc %{
99
+ Cycles through each of the children. Inside this tag all page attribute tags
100
+ are mapped to the current child page.
101
+
102
+ Supply @paginated="true"@ to paginate the displayed list. will_paginate view helper
103
+ options can also be specified, including @per_page@, @previous_label@, @next_label@,
104
+ @class@, @separator@, @inner_window@ and @outer_window@.
105
+
106
+ *Usage:*
107
+
108
+ <pre><code><r:children:each [offset="number"] [limit="number"]
109
+ [by="published_at|updated_at|created_at|slug|title|keywords|description"]
110
+ [order="asc|desc"]
111
+ [status="draft|reviewed|published|hidden|all"]
112
+ [paginated="true"]
113
+ [per_page="number"]
114
+ >
115
+ ...
116
+ </r:children:each>
117
+ </code></pre>
118
+ }
119
+ tag 'children:each' do |tag|
120
+ render_children_with_pagination(tag)
121
+ end
122
+
123
+ desc %{
124
+ The pagination tag is not usually called directly. Supply paginated="true" when you display a list and it will
125
+ be automatically display only the current page of results, with pagination controls at the bottom.
126
+
127
+ *Usage:*
128
+
129
+ <pre><code><r:children:each paginated="true" per_page="50" container="false" previous_label="foo" next_label="bar">
130
+ <r:child>...</r:child>
131
+ </r:children:each>
132
+ </code></pre>
133
+ }
134
+ tag 'pagination' do |tag|
135
+ if tag.locals.paginated_list
136
+ will_paginate(tag.locals.paginated_list, will_paginate_options(tag))
137
+ end
138
+ end
139
+
140
+ desc %{
141
+ Page attribute tags inside of this tag refer to the current child. This is occasionally
142
+ useful if you are inside of another tag (like &lt;r:find&gt;) and need to refer back to the
143
+ current child.
144
+
145
+ *Usage:*
146
+
147
+ <pre><code><r:children:each>
148
+ <r:child>...</r:child>
149
+ </r:children:each>
150
+ </code></pre>
151
+ }
152
+ tag 'children:each:child' do |tag|
153
+ tag.locals.page = tag.locals.child
154
+ tag.expand
155
+ end
156
+
157
+ desc %{
158
+ Renders the tag contents only if the current page is the first child in the context of
159
+ a children:each tag
160
+
161
+ *Usage:*
162
+
163
+ <pre><code><r:children:each>
164
+ <r:if_first >
165
+ ...
166
+ </r:if_first>
167
+ </r:children:each>
168
+ </code></pre>
169
+
170
+ }
171
+ tag 'children:each:if_first' do |tag|
172
+ tag.expand if tag.locals.first_child
173
+ end
174
+
175
+
176
+ desc %{
177
+ Renders the tag contents unless the current page is the first child in the context of
178
+ a children:each tag
179
+
180
+ *Usage:*
181
+
182
+ <pre><code><r:children:each>
183
+ <r:unless_first >
184
+ ...
185
+ </r:unless_first>
186
+ </r:children:each>
187
+ </code></pre>
188
+
189
+ }
190
+ tag 'children:each:unless_first' do |tag|
191
+ tag.expand unless tag.locals.first_child
192
+ end
193
+
194
+ desc %{
195
+ Renders the tag contents only if the current page is the last child in the context of
196
+ a children:each tag
197
+
198
+ *Usage:*
199
+
200
+ <pre><code><r:children:each>
201
+ <r:if_last >
202
+ ...
203
+ </r:if_last>
204
+ </r:children:each>
205
+ </code></pre>
206
+
207
+ }
208
+ tag 'children:each:if_last' do |tag|
209
+ tag.expand if tag.locals.last_child
210
+ end
211
+
212
+
213
+ desc %{
214
+ Renders the tag contents unless the current page is the last child in the context of
215
+ a children:each tag
216
+
217
+ *Usage:*
218
+
219
+ <pre><code><r:children:each>
220
+ <r:unless_last >
221
+ ...
222
+ </r:unless_last>
223
+ </r:children:each>
224
+ </code></pre>
225
+
226
+ }
227
+ tag 'children:each:unless_last' do |tag|
228
+ tag.expand unless tag.locals.last_child
229
+ end
230
+
231
+ desc %{
232
+ Renders the tag contents only if the contents do not match the previous header. This
233
+ is extremely useful for rendering date headers for a list of child pages.
234
+
235
+ If you would like to use several header blocks you may use the @name@ attribute to
236
+ name the header. When a header is named it will not restart until another header of
237
+ the same name is different.
238
+
239
+ Using the @restart@ attribute you can cause other named headers to restart when the
240
+ present header changes. Simply specify the names of the other headers in a semicolon
241
+ separated list.
242
+
243
+ *Usage:*
244
+
245
+ <pre><code><r:children:each>
246
+ <r:header [name="header_name"] [restart="name1[;name2;...]"]>
247
+ ...
248
+ </r:header>
249
+ </r:children:each>
250
+ </code></pre>
251
+ }
252
+ tag 'children:each:header' do |tag|
253
+ previous_headers = tag.locals.previous_headers
254
+ name = tag.attr['name'] || :unnamed
255
+ restart = (tag.attr['restart'] || '').split(';')
256
+ header = tag.expand
257
+ unless header == previous_headers[name]
258
+ previous_headers[name] = header
259
+ unless restart.empty?
260
+ restart.each do |n|
261
+ previous_headers[n] = nil
262
+ end
263
+ end
264
+ header
265
+ end
266
+ end
267
+
268
+ desc %{
269
+ Page attribute tags inside this tag refer to the parent of the current page.
270
+
271
+ *Usage:*
272
+
273
+ <pre><code><r:parent>...</r:parent></code></pre>
274
+ }
275
+ tag "parent" do |tag|
276
+ parent = tag.locals.page.parent
277
+ tag.locals.page = parent
278
+ tag.expand if parent
279
+ end
280
+
281
+ desc %{
282
+ Renders the contained elements only if the current contextual page has a parent, i.e.
283
+ is not the root page.
284
+
285
+ *Usage:*
286
+
287
+ <pre><code><r:if_parent>...</r:if_parent></code></pre>
288
+ }
289
+ tag "if_parent" do |tag|
290
+ parent = tag.locals.page.parent
291
+ tag.expand if parent
292
+ end
293
+
294
+ desc %{
295
+ Renders the contained elements only if the current contextual page has no parent, i.e.
296
+ is the root page.
297
+
298
+ *Usage:*
299
+
300
+ <pre><code><r:unless_parent>...</r:unless_parent></code></pre>
301
+ }
302
+ tag "unless_parent" do |tag|
303
+ parent = tag.locals.page.parent
304
+ tag.expand unless parent
305
+ end
306
+
307
+ desc %{
308
+ Renders the contained elements only if the current contextual page has one or
309
+ more child pages. The @status@ attribute limits the status of found child pages
310
+ to the given status, the default is @"published"@. @status="all"@ includes all
311
+ non-virtual pages regardless of status.
312
+
313
+ *Usage:*
314
+
315
+ <pre><code><r:if_children [status="published"]>...</r:if_children></code></pre>
316
+ }
317
+ tag "if_children" do |tag|
318
+ children = tag.locals.page.children.where(children_find_options(tag)[:conditions]).count
319
+ tag.expand if children > 0
320
+ end
321
+
322
+ desc %{
323
+ Renders the contained elements only if the current contextual page has no children.
324
+ The @status@ attribute limits the status of found child pages to the given status,
325
+ the default is @"published"@. @status="all"@ includes all non-virtual pages
326
+ regardless of status.
327
+
328
+ *Usage:*
329
+
330
+ <pre><code><r:unless_children [status="published"]>...</r:unless_children></code></pre>
331
+ }
332
+ tag "unless_children" do |tag|
333
+ children = tag.locals.page.children.where(children_find_options(tag)[:conditions]).count
334
+ tag.expand unless children > 0
335
+ end
336
+
337
+ desc %{
338
+ Aggregates the children of multiple paths using the @paths@ attribute.
339
+ Useful for combining many different sections/categories into a single
340
+ feed or listing.
341
+
342
+ *Usage*:
343
+
344
+ <pre><code><r:aggregate paths="/section1; /section2; /section3"> ... </r:aggregate></code></pre>
345
+ }
346
+ tag "aggregate" do |tag|
347
+ required_attr(tag, 'paths', 'urls')
348
+ paths = (tag.attr['paths']||tag.attr["urls"]).split(";").map(&:strip).reject(&:blank?).map { |u| clean_path u }
349
+ parent_ids = paths.map {|u| Page.find_by_path(u) }.map(&:id)
350
+ tag.locals.parent_ids = parent_ids
351
+ tag.expand
352
+ end
353
+
354
+ desc %{
355
+ Sets the scope to the individual aggregated page allowing you to
356
+ iterate through each of the listed paths.
357
+
358
+ *Usage*:
359
+
360
+ <pre><code><r:aggregate:each paths="/section1; /section2; /section3"> ... </r:aggregate:each></code></pre>
361
+ }
362
+ tag "aggregate:each" do |tag|
363
+ aggregates = []
364
+ tag.locals.aggregated_pages = tag.locals.parent_ids.map {|p| Page.find(p)}
365
+ tag.locals.aggregated_pages.each do |aggregate_page|
366
+ tag.locals.page = aggregate_page
367
+ aggregates << tag.expand
368
+ end
369
+ aggregates.flatten.join('')
370
+ end
371
+
372
+ tag "aggregate:each:children" do |tag|
373
+ tag.locals.children = tag.locals.page.children
374
+ tag.expand
375
+ end
376
+
377
+ tag "aggregate:each:children:each" do |tag|
378
+ options = children_find_options(tag)
379
+ result = []
380
+ children = tag.locals.children
381
+ tag.locals.previous_headers = {}
382
+ children.where(options).each do |item|
383
+ tag.locals.child = item
384
+ tag.locals.page = item
385
+ result << tag.expand
386
+ end
387
+ result.flatten.join('')
388
+ end
389
+
390
+ tag "aggregate:children" do |tag|
391
+ tag.expand
392
+ end
393
+
394
+ desc %{
395
+ Renders the total count of children of the aggregated pages. Accepts the
396
+ same options as @<r:children:each />@.
397
+
398
+ *Usage*:
399
+
400
+ <pre><code><r:aggregate paths="/section1; /section2; /section3">
401
+ <r:children:count />
402
+ </r:aggregate></code></pre>
403
+ }
404
+ tag "aggregate:children:count" do |tag|
405
+ options = aggregate_children(tag)
406
+ if ActiveRecord::Base.connection.adapter_name.downcase == 'postgresql'
407
+ options[:group] = Page.columns.map {|c| c.name}.join(', ')
408
+ Page.where(options).size
409
+ else
410
+ Page.where(options).count
411
+ end
412
+ end
413
+ desc %{
414
+ Renders the contained block for each child of the aggregated pages. Accepts the
415
+ same options as the plain @<r:children:each />@.
416
+
417
+ *Usage*:
418
+
419
+ <pre><code><r:aggregate paths="/section1; /section2; /section3">
420
+ <r:children:each>
421
+ ...
422
+ </r:children:each>
423
+ </r:aggregate></code></pre>
424
+ }
425
+ tag "aggregate:children:each" do |tag|
426
+ render_children_with_pagination(tag, :aggregate => true)
427
+ end
428
+
429
+ desc %{
430
+ Renders the first child of the aggregated pages. Accepts the
431
+ same options as @<r:children:each />@.
432
+
433
+ *Usage*:
434
+
435
+ <pre><code><r:aggregate paths="/section1; /section2; /section3">
436
+ <r:children:first>
437
+ ...
438
+ </r:children:first>
439
+ </r:aggregate></code></pre>
440
+ }
441
+ tag "aggregate:children:first" do |tag|
442
+ options = aggregate_children(tag)
443
+ children = Page.where(options)
444
+
445
+ # TODO: What is this nonsnese?
446
+ if first = children.first
447
+ tag.locals.page = first
448
+ tag.expand
449
+ end
450
+ end
451
+
452
+ desc %{
453
+ Renders the last child of the aggregated pages. Accepts the
454
+ same options as @<r:children:each />@.
455
+
456
+ *Usage*:
457
+
458
+ <pre><code><r:aggregate paths="/section1; /section2; /section3">
459
+ <r:children:last>
460
+ ...
461
+ </r:children:last>
462
+ </r:aggregate></code></pre>
463
+ }
464
+ tag "aggregate:children:last" do |tag|
465
+ options = aggregate_children(tag)
466
+ children = Page.where(options)
467
+
468
+ # TODO: More nonsense
469
+ if last = children.last
470
+ tag.locals.page = last
471
+ tag.expand
472
+ end
473
+ end
474
+
475
+ desc %{
476
+ Renders a counter value or one of the values given based on a global cycle counter.
477
+
478
+ To get a numeric counter just use the tag, or specify a start value with @start@.
479
+ Use the @reset@ attribute to reset the cycle to the beginning. Using @reset@ on a
480
+ numbered cycle will begin at 0. Use the @name@ attribute to track multiple cycles;
481
+ the default is @cycle@.
482
+
483
+ *Usage:*
484
+
485
+ <pre><code><r:cycle [values="first, second, third"] [reset="true|false"] [name="cycle"] [start="second"] /></code></pre>
486
+ <pre><code><r:cycle start="3" /></code></pre>
487
+ }
488
+ tag 'cycle' do |tag|
489
+ cycle = (tag.globals.cycle ||= {})
490
+ if tag.attr['values']
491
+ values = tag.attr['values'].split(",").collect(&:strip)
492
+ end
493
+ start = tag.attr['start']
494
+ cycle_name = tag.attr['name'] || 'cycle'
495
+ if values
496
+ if start
497
+ current_index = (cycle[cycle_name] ||= values.index(start))
498
+ else
499
+ current_index = (cycle[cycle_name] ||= 0)
500
+ end
501
+ current_index = 0 if tag.attr['reset'] == 'true'
502
+ cycle[cycle_name] = (current_index + 1) % values.size
503
+ values[current_index]
504
+ else
505
+ cycle[cycle_name] ||= (start.presence || 0).to_i
506
+ output = cycle[cycle_name]
507
+ cycle[cycle_name] += 1
508
+ if tag.attr['reset'] == 'true'
509
+ cycle[cycle_name] = 0
510
+ output = cycle[cycle_name]
511
+ end
512
+ output
513
+ end
514
+ end
515
+
516
+ desc %{
517
+ Renders the main content of a page. Use the @part@ attribute to select a specific
518
+ page part. By default the @part@ attribute is set to body. Use the @inherit@
519
+ attribute to specify that if a page does not have a content part by that name that
520
+ the tag should render the parent's content part. By default @inherit@ is set to
521
+ @false@. Use the @contextual@ attribute to force a part inherited from a parent
522
+ part to be evaluated in the context of the child page. By default 'contextual'
523
+ is set to true.
524
+
525
+ *Usage:*
526
+
527
+ <pre><code><r:content [part="part_name"] [inherit="true|false"] [contextual="true|false"] /></code></pre>
528
+ }
529
+ tag 'content' do |tag|
530
+ page = tag.locals.page
531
+ part_name = tag_part_name(tag)
532
+ # Prevent simple and deep recursive rendering of the same page part
533
+ rendering_parts = (tag.locals.rendering_parts ||= Hash.new {|h,k| h[k] = []})
534
+ if rendering_parts[page.id].include?(part_name)
535
+ raise TagError.new(%{Recursion error: already rendering the `#{part_name}' part.})
536
+ else
537
+ rendering_parts[page.id] << part_name
538
+ end
539
+ inherit = boolean_attr_or_error(tag,'inherit',false)
540
+ part_page = page
541
+ if inherit
542
+ while (part_page.part(part_name).nil? and (not part_page.parent.nil?)) do
543
+ part_page = part_page.parent
544
+ end
545
+ end
546
+ contextual = boolean_attr_or_error(tag,'contextual', true)
547
+ part = part_page.part(part_name)
548
+ tag.locals.page = part_page unless contextual
549
+ result = tag.globals.page.render_snippet(part) unless part.nil?
550
+ rendering_parts[page.id].delete(part_name)
551
+ result
552
+ end
553
+
554
+ desc %{
555
+ Renders the containing elements if all of the listed parts exist on a page.
556
+ By default the @part@ attribute is set to @body@, but you may list more than one
557
+ part by separating them with a comma. Setting the optional @inherit@ to true will
558
+ search ancestors independently for each part. By default @inherit@ is set to @false@.
559
+
560
+ When listing more than one part, you may optionally set the @find@ attribute to @any@
561
+ so that it will render the containing elements if any of the listed parts are found.
562
+ By default the @find@ attribute is set to @all@.
563
+
564
+ *Usage:*
565
+
566
+ <pre><code><r:if_content [part="part_name, other_part"] [inherit="true"] [find="any"]>...</r:if_content></code></pre>
567
+ }
568
+ tag 'if_content' do |tag|
569
+ part_name = tag_part_name(tag)
570
+ parts_arr = part_name.split(',')
571
+ inherit = boolean_attr_or_error(tag, 'inherit', 'false')
572
+ find = attr_or_error(tag, :attribute_name => 'find', :default => 'all', :values => 'any, all')
573
+ expandable = true
574
+ one_found = false
575
+ parts_arr.each do |name|
576
+ part_page = tag.locals.page
577
+ name.strip!
578
+ if inherit
579
+ while (part_page.part(name).nil? and (not part_page.parent.nil?)) do
580
+ part_page = part_page.parent
581
+ end
582
+ end
583
+ expandable = false if part_page.part(name).nil?
584
+ one_found ||= true if !part_page.part(name).nil?
585
+ end
586
+ expandable = true if (find == 'any' and one_found)
587
+ tag.expand if expandable
588
+ end
589
+
590
+ desc %{
591
+ The opposite of the @if_content@ tag. It renders the contained elements if all of the
592
+ specified parts do not exist. Setting the optional @inherit@ to true will search
593
+ ancestors independently for each part. By default @inherit@ is set to @false@.
594
+
595
+ When listing more than one part, you may optionally set the @find@ attribute to @any@
596
+ so that it will not render the containing elements if any of the listed parts are found.
597
+ By default the @find@ attribute is set to @all@.
598
+
599
+ *Usage:*
600
+
601
+ <pre><code><r:unless_content [part="part_name, other_part"] [inherit="false"] [find="any"]>...</r:unless_content></code></pre>
602
+ }
603
+ tag 'unless_content' do |tag|
604
+ part_name = tag_part_name(tag)
605
+ parts_arr = part_name.split(',')
606
+ inherit = boolean_attr_or_error(tag, 'inherit', false)
607
+ find = attr_or_error(tag, :attribute_name => 'find', :default => 'all', :values => 'any, all')
608
+ expandable, all_found = true, true
609
+ parts_arr.each do |name|
610
+ part_page = tag.locals.page
611
+ name.strip!
612
+ if inherit
613
+ while (part_page.part(name).nil? and (not part_page.parent.nil?)) do
614
+ part_page = part_page.parent
615
+ end
616
+ end
617
+ expandable = false if !part_page.part(name).nil?
618
+ all_found = false if part_page.part(name).nil?
619
+ end
620
+ if all_found == false and find == 'all'
621
+ expandable = true
622
+ end
623
+ tag.expand if expandable
624
+ end
625
+
626
+ desc %{
627
+ Renders the containing elements only if the page's path matches the regular expression
628
+ given in the @matches@ attribute. If the @ignore_case@ attribute is set to false, the
629
+ match is case sensitive. By default, @ignore_case@ is set to true.
630
+
631
+ *Usage:*
632
+
633
+ <pre><code><r:if_path matches="regexp" [ignore_case="true|false"]>...</r:if_path></code></pre>
634
+ }
635
+ tag 'if_path' do |tag|
636
+ required_attr(tag,'matches')
637
+ regexp = build_regexp_for(tag, 'matches')
638
+ unless tag.locals.page.path.match(regexp).nil?
639
+ tag.expand
640
+ end
641
+ end
642
+ deprecated_tag 'if_url', :substitute => 'if_path', :deadline => '1.2'
643
+
644
+ desc %{
645
+ The opposite of the @if_path@ tag.
646
+
647
+ *Usage:*
648
+
649
+ <pre><code><r:unless_path matches="regexp" [ignore_case="true|false"]>...</r:unless_path></code></pre>
650
+ }
651
+ tag 'unless_path' do |tag|
652
+ required_attr(tag, 'matches')
653
+ regexp = build_regexp_for(tag, 'matches')
654
+ if tag.locals.page.path.match(regexp).nil?
655
+ tag.expand
656
+ end
657
+ end
658
+ deprecated_tag 'unless_url', :substitute => 'unless_path', :deadline => '1.2'
659
+
660
+ desc %{
661
+ Renders the contained elements if the current contextual page is either the actual page or one of its parents.
662
+
663
+ This is typically used inside another tag (like &lt;r:children:each&gt;) to add conditional mark-up if the child element is or descends from the current page.
664
+
665
+ *Usage:*
666
+
667
+ <pre><code><r:if_ancestor_or_self>...</r:if_ancestor_or_self></code></pre>
668
+ }
669
+ tag "if_ancestor_or_self" do |tag|
670
+ tag.expand if (tag.globals.page.ancestors + [tag.globals.page]).include?(tag.locals.page)
671
+ end
672
+
673
+ desc %{
674
+ Renders the contained elements unless the current contextual page is either the actual page or one of its parents.
675
+
676
+ This is typically used inside another tag (like &lt;r:children:each&gt;) to add conditional mark-up unless the child element is or descends from the current page.
677
+
678
+ *Usage:*
679
+
680
+ <pre><code><r:unless_ancestor_or_self>...</r:unless_ancestor_or_self></code></pre>
681
+ }
682
+ tag "unless_ancestor_or_self" do |tag|
683
+ tag.expand unless (tag.globals.page.ancestors + [tag.globals.page]).include?(tag.locals.page)
684
+ end
685
+
686
+ desc %{
687
+ Renders the contained elements if the current contextual page is also the actual page.
688
+
689
+ This is typically used inside another tag (like &lt;r:children:each&gt;) to add conditional mark-up if the child element is the current page.
690
+
691
+ *Usage:*
692
+
693
+ <pre><code><r:if_self>...</r:if_self></code></pre>
694
+ }
695
+ tag "if_self" do |tag|
696
+ tag.expand if tag.locals.page == tag.globals.page
697
+ end
698
+
699
+ desc %{
700
+ Renders the contained elements unless the current contextual page is also the actual page.
701
+
702
+ This is typically used inside another tag (like &lt;r:children:each&gt;) to add conditional mark-up unless the child element is the current page.
703
+
704
+ *Usage:*
705
+
706
+ <pre><code><r:unless_self>...</r:unless_self></code></pre>
707
+ }
708
+ tag "unless_self" do |tag|
709
+ tag.expand unless tag.locals.page == tag.globals.page
710
+ end
711
+
712
+ desc %{
713
+ Renders the name of the author of the current page.
714
+ }
715
+ tag 'author' do |tag|
716
+ page = tag.locals.page
717
+ if author = page.created_by
718
+ author.name
719
+ end
720
+ end
721
+
722
+ desc %{
723
+ Renders the Gravatar of the author of the current page or the named user.
724
+
725
+ *Usage:*
726
+
727
+ <pre><code><r:gravatar /></code></pre>
728
+
729
+ or
730
+
731
+ <pre><code><r:gravatar [name="User Name"]
732
+ [rating="G | PG | R | X"]
733
+ [size="32px"] /></code></pre>
734
+ }
735
+ tag 'gravatar' do |tag|
736
+ page = tag.locals.page
737
+ name = (tag.attr['name'] || page.created_by.name)
738
+ rating = (tag.attr['rating'] || 'G')
739
+ size = (tag.attr['size'] || '32px')
740
+ user = User.find_by_name(name)
741
+ email = user ? user.email : nil
742
+ local_avatar_url = "/images/admin/avatar_#{([size.to_i] * 2).join('x')}.png"
743
+ default_avatar_url = "#{request.protocol}#{request.host_with_port}#{local_avatar_url}"
744
+
745
+ unless email.blank?
746
+ url = '//gravatar.com/avatar/'
747
+ url << "#{Digest::MD5.new.update(email)}?"
748
+ url << "rating=#{rating}"
749
+ url << "&size=#{size.to_i}"
750
+ url << "&default=#{default_avatar_url}" unless request.host_with_port == 'testhost.tld'
751
+ # Test the Gravatar url
752
+ require 'open-uri'
753
+ begin; open "http:#{url}", :proxy => true
754
+ rescue; local_avatar_url
755
+ else; url
756
+ end
757
+ else
758
+ local_avatar_url
759
+ end
760
+ end
761
+
762
+ desc %{
763
+ Renders the date based on the current page (by default when it was published or created).
764
+ The format attribute uses the same formating codes used by the Ruby @strftime@ function.
765
+ By default it's set to @%A, %B %d, %Y@. You may also use the string @rfc1123@ as a shortcut
766
+ for @%a, %d %b %Y %H:%M:%S GMT@ (non-localized). The @for@ attribute selects which date to
767
+ render. Valid options are @published_at@, @created_at@, @updated_at@, and @now@. @now@ will
768
+ render the current date/time, regardless of the page.
769
+
770
+ *Usage:*
771
+
772
+ <pre><code><r:date [format="%A, %B %d, %Y"] [for="published_at"]/></code></pre>
773
+ }
774
+ tag 'date' do |tag|
775
+ page = tag.locals.page
776
+ format = (tag.attr['format'] || '%A, %B %d, %Y')
777
+ time_attr = tag.attr['for']
778
+ date = if time_attr
779
+ case
780
+ when time_attr == 'now'
781
+ Time.zone.now
782
+ when Page.date_column_names.include?(time_attr)
783
+ page[time_attr]
784
+ else
785
+ raise TagError, "Invalid value for 'for' attribute."
786
+ end
787
+ else
788
+ page.published_at || page.created_at
789
+ end
790
+ case format
791
+ when 'rfc1123'
792
+ CGI.rfc1123_date(date.to_time)
793
+ else
794
+ @i18n_date_format_keys ||= (I18n.config.backend.send(:translations)[I18n.locale][:date][:formats].keys rescue [])
795
+ format = @i18n_date_format_keys.include?(format.to_sym) ? format.to_sym : format
796
+ I18n.l date, :format => format
797
+ end
798
+ end
799
+
800
+ desc %{
801
+ Renders a link to the page. When used as a single tag it uses the page's title
802
+ for the link name. When used as a double tag the part in between both tags will
803
+ be used as the link text. The link tag passes all attributes over to the HTML
804
+ @a@ tag. This is very useful for passing attributes like the @class@ attribute
805
+ or @id@ attribute. If the @anchor@ attribute is passed to the tag it will
806
+ append a pound sign (<code>#</code>) followed by the value of the attribute to
807
+ the @href@ attribute of the HTML @a@ tag--effectively making an HTML anchor.
808
+
809
+ *Usage:*
810
+
811
+ <pre><code><r:link [anchor="name"] [other attributes...] /></code></pre>
812
+
813
+ or
814
+
815
+ <pre><code><r:link [anchor="name"] [other attributes...]>link text here</r:link></code></pre>
816
+ }
817
+ tag 'link' do |tag|
818
+ options = tag.attr.dup
819
+ anchor = options['anchor'] ? "##{options.delete('anchor')}" : ''
820
+ attributes = options.inject('') { |s, (k, v)| s << %{#{k.downcase}="#{v}" } }.strip
821
+ attributes = " #{attributes}" unless attributes.empty?
822
+ text = tag.double? ? tag.expand : tag.render('title')
823
+ %{<a href="#{tag.render('path')}#{anchor}"#{attributes}>#{text}</a>}
824
+ end
825
+
826
+ desc %{
827
+ Renders a trail of breadcrumbs to the current page. The separator attribute
828
+ specifies the HTML fragment that is inserted between each of the breadcrumbs. By
829
+ default it is set to @>@. The boolean @nolinks@ attribute can be specified to render
830
+ breadcrumbs in plain text, without any links (useful when generating title tag).
831
+ Set the boolean @noself@ attribute to omit the present page (useful in page headers).
832
+
833
+ *Usage:*
834
+
835
+ <pre><code><r:breadcrumbs [separator="separator_string"] [nolinks="true"] [noself="true"]/></code></pre>
836
+ }
837
+ tag 'breadcrumbs' do |tag|
838
+ page = tag.locals.page
839
+ nolinks = (tag.attr['nolinks'] == 'true')
840
+ noself = (tag.attr['noself'] == 'true')
841
+ breadcrumbs = []
842
+ breadcrumbs.unshift page.breadcrumb unless noself
843
+ page.ancestors.each do |ancestor|
844
+ tag.locals.page = ancestor
845
+ if nolinks
846
+ breadcrumbs.unshift tag.render('breadcrumb')
847
+ else
848
+ breadcrumbs.unshift %{<a href="#{tag.render('path')}">#{tag.render('breadcrumb')}</a>}
849
+ end
850
+ end
851
+ separator = tag.attr['separator'] || ' &gt; '
852
+ breadcrumbs.join(separator)
853
+ end
854
+
855
+ desc %{
856
+ Inside this tag all page related tags refer to the page found at the @path@ attribute.
857
+ @path@s may be relative or absolute paths.
858
+
859
+ *Usage:*
860
+
861
+ <pre><code><r:find path="value_to_find">...</r:find></code></pre>
862
+ }
863
+ tag 'find' do |tag|
864
+ required_attr(tag,'path','url')
865
+ path = tag.attr['path'] || tag.attr['url']
866
+
867
+ found = Page.find_by_path(absolute_path_for(tag.locals.page.path, path))
868
+ if page_found?(found)
869
+ tag.locals.page = found
870
+ tag.expand
871
+ end
872
+ end
873
+
874
+ desc %{
875
+ Randomly renders one of the options specified by the @option@ tags.
876
+
877
+ *Usage:*
878
+
879
+ <pre><code><r:random>
880
+ <r:option>...</r:option>
881
+ <r:option>...</r:option>
882
+ ...
883
+ <r:random>
884
+ </code></pre>
885
+ }
886
+ tag 'random' do |tag|
887
+ tag.locals.random = []
888
+ tag.expand
889
+ options = tag.locals.random
890
+ option = options[rand(options.size)]
891
+ option if option
892
+ end
893
+ tag 'random:option' do |tag|
894
+ items = tag.locals.random
895
+ items << tag.expand
896
+ end
897
+
898
+ desc %{
899
+ Nothing inside a set of hide tags is rendered.
900
+
901
+ *Usage:*
902
+
903
+ <pre><code><r:hide>...</r:hide></code></pre>
904
+ }
905
+ tag 'hide' do |tag|
906
+ end
907
+
908
+ desc %{
909
+ Escapes angle brackets, etc. for rendering in an HTML document.
910
+
911
+ *Usage:*
912
+
913
+ <pre><code><r:escape_html>...</r:escape_html></code></pre>
914
+ }
915
+ tag "escape_html" do |tag|
916
+ CGI.escapeHTML(tag.expand)
917
+ end
918
+
919
+ desc %{
920
+ Renders a list of links specified in the @paths@ attribute according to three
921
+ states:
922
+
923
+ * @normal@ specifies the normal state for the link
924
+ * @here@ specifies the state of the link when the path matches the current
925
+ page's PATH
926
+ * @selected@ specifies the state of the link when the current page matches
927
+ is a child of the specified path
928
+ # @if_last@ renders its contents within a @normal@, @here@ or
929
+ @selected@ tag if the item is the last in the navigation elements
930
+ # @if_first@ renders its contents within a @normal@, @here@ or
931
+ @selected@ tag if the item is the first in the navigation elements
932
+
933
+ The @between@ tag specifies what should be inserted in between each of the links.
934
+
935
+ *Usage:*
936
+
937
+ <pre><code><r:navigation paths="[Title: path | Title: path | ...]">
938
+ <r:normal><a href="<r:path />"><r:title /></a></r:normal>
939
+ <r:here><strong><r:title /></strong></r:here>
940
+ <r:selected><strong><a href="<r:path />"><r:title /></a></strong></r:selected>
941
+ <r:between> | </r:between>
942
+ </r:navigation>
943
+ </code></pre>
944
+ }
945
+ tag 'navigation' do |tag|
946
+ hash = tag.locals.navigation = {}
947
+ tag.expand
948
+ raise TagError.new("`navigation' tag must include a `normal' tag") unless hash.has_key? :normal
949
+ ActiveSupport::Deprecation.warn("The 'urls' attribute of the r:navigation tag has been deprecated in favour of 'paths'. Please update your site.") if tag.attr['urls']
950
+ result = []
951
+ pairs = (tag.attr['paths']||tag.attr['urls']).to_s.split('|').map do |pair|
952
+ parts = pair.split(':')
953
+ value = parts.pop
954
+ key = parts.join(':')
955
+ [key.strip, value.strip]
956
+ end
957
+ pairs.each_with_index do |(title, path), i|
958
+ compare_path = remove_trailing_slash(path)
959
+ page_path = remove_trailing_slash(self.path)
960
+ hash[:title] = title
961
+ hash[:path] = path
962
+ tag.locals.first_child = i == 0
963
+ tag.locals.last_child = i == pairs.length - 1
964
+ case page_path
965
+ when compare_path
966
+ result << (hash[:here] || hash[:selected] || hash[:normal]).call
967
+ when Regexp.compile( '^' + Regexp.quote(path))
968
+ result << (hash[:selected] || hash[:normal]).call
969
+ else
970
+ result << hash[:normal].call
971
+ end
972
+ end
973
+ between = hash.has_key?(:between) ? hash[:between].call : ' '
974
+ result.reject { |i| i.blank? }.join(between)
975
+ end
976
+ [:normal, :here, :selected, :between].each do |symbol|
977
+ tag "navigation:#{symbol}" do |tag|
978
+ hash = tag.locals.navigation
979
+ hash[symbol] = tag.block
980
+ end
981
+ end
982
+ [:title, :path].each do |symbol|
983
+ tag "navigation:#{symbol}" do |tag|
984
+ hash = tag.locals.navigation
985
+ hash[symbol]
986
+ end
987
+ end
988
+ tag "navigation:url" do |tag|
989
+ hash = tag.locals.navigation
990
+ ActiveSupport::Deprecation.warn("The 'r:navigation:url' tag has been deprecated in favour of 'r:navigation:path'. Please update your site.")
991
+ hash[:path]
992
+ end
993
+
994
+ desc %{
995
+ Renders the containing elements if the element is the first
996
+ in the navigation list
997
+
998
+ *Usage:*
999
+
1000
+ <pre><code><r:normal><r:if_first>...</r:if_first></r:normal></code></pre>
1001
+ }
1002
+ tag 'navigation:if_first' do |tag|
1003
+ tag.expand if tag.locals.first_child
1004
+ end
1005
+
1006
+ desc %{
1007
+ Renders the containing elements unless the element is the first
1008
+ in the navigation list
1009
+
1010
+ *Usage:*
1011
+
1012
+ <pre><code><r:normal><r:unless_first>...</r:unless_first></r:normal></code></pre>
1013
+ }
1014
+ tag 'navigation:unless_first' do |tag|
1015
+ tag.expand unless tag.locals.first_child
1016
+ end
1017
+
1018
+ desc %{
1019
+ Renders the containing elements unless the element is the last
1020
+ in the navigation list
1021
+
1022
+ *Usage:*
1023
+
1024
+ <pre><code><r:normal><r:unless_last>...</r:unless_last></r:normal></code></pre>
1025
+ }
1026
+ tag 'navigation:unless_last' do |tag|
1027
+ tag.expand unless tag.locals.last_child
1028
+ end
1029
+
1030
+ desc %{
1031
+ Renders the containing elements if the element is the last
1032
+ in the navigation list
1033
+
1034
+ *Usage:*
1035
+
1036
+ <pre><code><r:normal><r:if_last>...</r:if_last></r:normal></code></pre>
1037
+ }
1038
+ tag 'navigation:if_last' do |tag|
1039
+ tag.expand if tag.locals.last_child
1040
+ end
1041
+
1042
+ desc %{
1043
+ Renders the containing elements only if TrustyCms in is development mode.
1044
+
1045
+ *Usage:*
1046
+
1047
+ <pre><code><r:if_dev>...</r:if_dev></code></pre>
1048
+ }
1049
+ tag 'if_dev' do |tag|
1050
+ tag.expand if dev?(tag.globals.page.request)
1051
+ end
1052
+
1053
+ desc %{
1054
+ The opposite of the @if_dev@ tag.
1055
+
1056
+ *Usage:*
1057
+
1058
+ <pre><code><r:unless_dev>...</r:unless_dev></code></pre>
1059
+ }
1060
+ tag 'unless_dev' do |tag|
1061
+ tag.expand unless dev?(tag.globals.page.request)
1062
+ end
1063
+
1064
+ desc %{
1065
+ Prints the page's status as a string. Optional attribute 'downcase'
1066
+ will cause the status to be all lowercase.
1067
+
1068
+ *Usage:*
1069
+
1070
+ <pre><code><r:status [downcase='true'] /></code></pre>
1071
+ }
1072
+ tag 'status' do |tag|
1073
+ status = tag.globals.page.status.name
1074
+ return status.downcase if tag.attr['downcase']
1075
+ status
1076
+ end
1077
+
1078
+ desc %(
1079
+ Renders the content of the field given in the @name@ attribute.
1080
+
1081
+ *Usage:*
1082
+
1083
+ <pre><code><r:field name="Keywords" /></code></pre>
1084
+ )
1085
+ tag 'field' do |tag|
1086
+ required_attr(tag,'name')
1087
+ tag.locals.page.field(tag.attr['name']).try(:content)
1088
+ end
1089
+
1090
+ desc %(
1091
+ Renders the contained elements if the field given in the @name@ attribute
1092
+ exists. The tag also takes an optional @equals@ or @matches@ attribute;
1093
+ these will expand the tag if the field's content equals or matches the
1094
+ given string or regex.
1095
+
1096
+ *Usage:*
1097
+
1098
+ <pre><code><r:if_field name="author" [equals|matches="John"] [ignore_case="true|false"]>...</r:if_field></code></pre>
1099
+ )
1100
+ tag 'if_field' do |tag|
1101
+ required_attr(tag,'name')
1102
+ field = tag.locals.page.field(tag.attr['name'])
1103
+ return '' if field.nil?
1104
+ tag.expand if case
1105
+ when (tag.attr['equals'] and tag.attr['ignore_case'] == 'false') then field.content == tag.attr['equals']
1106
+ when tag.attr['equals'] then field.content.downcase == tag.attr['equals'].downcase
1107
+ when tag.attr['matches'] then field.content =~ build_regexp_for(tag, 'matches')
1108
+ else field
1109
+ end
1110
+ end
1111
+
1112
+ desc %(
1113
+ The opposite of @if_field@. Renders the contained elements unless the field
1114
+ given in the @name@ attribute exists. The tag also takes an optional
1115
+ @equals@ or @matches@ attribute; these will expand the tag unless the
1116
+ field's content equals or matches the given string or regex.
1117
+
1118
+ *Usage:*
1119
+
1120
+ <pre><code><r:unless_field name="author" [equals|matches="John"] [ignore_case="true|false"]>...</r:unless_field></code></pre>
1121
+ )
1122
+ tag 'unless_field' do |tag|
1123
+ required_attr(tag,'name')
1124
+ field = tag.locals.page.field(tag.attr['name'])
1125
+ tag.expand unless case
1126
+ when (field and (tag.attr['equals'] and tag.attr['ignore_case'] == 'false')) then field.content == tag.attr['equals']
1127
+ when (field and tag.attr['equals']) then field.content.downcase == tag.attr['equals'].downcase
1128
+ when (field and tag.attr['matches']) then field.content =~ build_regexp_for(tag, 'matches')
1129
+ else field
1130
+ end
1131
+ end
1132
+
1133
+ tag 'site' do |tag|
1134
+ tag.expand
1135
+ end
1136
+ desc %{
1137
+ Returns TrustyCms::Config['site.title'] as configured under the Settings tab.
1138
+ }
1139
+ tag "site:title" do |tag|
1140
+ TrustyCms::Config["site.title"]
1141
+ end
1142
+ desc %{
1143
+ Returns TrustyCms::Config['site.host'] as configured under the Settings tab.
1144
+ }
1145
+ tag "site:host" do |tag|
1146
+ TrustyCms::Config["site.host"]
1147
+ end
1148
+ desc %{
1149
+ Returns TrustyCms::Config['dev.host'] as configured under the Settings tab.
1150
+ }
1151
+ tag "site:dev_host" do |tag|
1152
+ TrustyCms::Config["dev.host"]
1153
+ end
1154
+
1155
+ private
1156
+ def render_children_with_pagination(tag, opts={})
1157
+ if opts[:aggregate]
1158
+ findable = Page
1159
+ options = aggregate_children(tag)
1160
+ else
1161
+ findable = tag.locals.children
1162
+ options = children_find_options(tag)
1163
+ end
1164
+ paging = pagination_find_options(tag)
1165
+ result = []
1166
+ tag.locals.previous_headers = {}
1167
+ displayed_children = paging ? findable.paginate(options.merge(paging)) : findable.all(options)
1168
+ displayed_children.each_with_index do |item, i|
1169
+ tag.locals.child = item
1170
+ tag.locals.page = item
1171
+ tag.locals.first_child = i == 0
1172
+ tag.locals.last_child = i == displayed_children.length - 1
1173
+ result << tag.expand
1174
+ end
1175
+ if paging && displayed_children.total_pages > 1
1176
+ tag.locals.paginated_list = displayed_children
1177
+ result << tag.render('pagination', tag.attr.dup)
1178
+ end
1179
+ result.flatten.join('')
1180
+ end
1181
+
1182
+ def children_find_options(tag)
1183
+ attr = tag.attr.symbolize_keys
1184
+
1185
+ options = {}
1186
+
1187
+ [:limit, :offset].each do |symbol|
1188
+ if number = attr[symbol]
1189
+ if number =~ /^\d+$/
1190
+ options[symbol] = number.to_i
1191
+ else
1192
+ raise TagError.new("`#{symbol}' attribute must be a positive number")
1193
+ end
1194
+ end
1195
+ end
1196
+
1197
+ by = (attr[:by] || 'published_at').strip
1198
+ order = (attr[:order] || 'asc').strip
1199
+ order_string = ''
1200
+ if self.attributes.keys.include?(by)
1201
+ order_string << by
1202
+ else
1203
+ raise TagError.new("`by' attribute of `each' tag must be set to a valid field name")
1204
+ end
1205
+ if order =~ /^(asc|desc)$/i
1206
+ order_string << " #{$1.upcase}"
1207
+ else
1208
+ raise TagError.new(%{`order' attribute of `each' tag must be set to either "asc" or "desc"})
1209
+ end
1210
+ options[:order] = order_string
1211
+
1212
+ status = (attr[:status] || ( dev?(tag.globals.page.request) ? 'all' : 'published')).downcase
1213
+ unless status == 'all'
1214
+ stat = Status[status]
1215
+ unless stat.nil?
1216
+ options[:conditions] = ["(virtual = ?) and (status_id = ?)", false, stat.id]
1217
+ else
1218
+ raise TagError.new(%{`status' attribute of `each' tag must be set to a valid status})
1219
+ end
1220
+ else
1221
+ options[:conditions] = ["virtual = ?", false]
1222
+ end
1223
+ options
1224
+ end
1225
+
1226
+ def aggregate_children(tag)
1227
+ options = children_find_options(tag)
1228
+ parent_ids = tag.locals.parent_ids
1229
+
1230
+ conditions = options[:conditions]
1231
+ conditions.first << " AND parent_id IN (?)"
1232
+ conditions << parent_ids
1233
+ options
1234
+ end
1235
+
1236
+ def pagination_find_options(tag)
1237
+ attr = tag.attr.symbolize_keys
1238
+ if attr[:paginated] == 'true'
1239
+ pagination_parameters.merge(attr.slice(:per_page))
1240
+ else
1241
+ false
1242
+ end
1243
+ end
1244
+
1245
+ def will_paginate_options(tag)
1246
+ attr = tag.attr.symbolize_keys
1247
+ if attr[:paginated] == 'true'
1248
+ attr.slice(:class, :previous_label, :next_label, :inner_window, :outer_window, :separator, :per_page).merge({:renderer => TrustyCms::Pagination::LinkRenderer.new(tag.globals.page.path)})
1249
+ else
1250
+ {}
1251
+ end
1252
+ end
1253
+
1254
+ def remove_trailing_slash(string)
1255
+ (string =~ %r{^(.*?)/$}) ? $1 : string
1256
+ end
1257
+
1258
+ def tag_part_name(tag)
1259
+ tag.attr['part'] || 'body'
1260
+ end
1261
+
1262
+ def build_regexp_for(tag, attribute_name)
1263
+ ignore_case = tag.attr.has_key?('ignore_case') && tag.attr['ignore_case']=='false' ? nil : true
1264
+ begin
1265
+ regexp = Regexp.new(tag.attr['matches'], ignore_case)
1266
+ rescue RegexpError => e
1267
+ raise TagError.new("Malformed regular expression in `#{attribute_name}' argument of `#{tag.name}' tag: #{e.message}")
1268
+ end
1269
+ regexp
1270
+ end
1271
+
1272
+ def relative_url_for(url, request)
1273
+ File.join(ActionController::Base.relative_url_root || '', url)
1274
+ end
1275
+
1276
+ def absolute_path_for(base_path, new_path)
1277
+ if new_path.first == '/'
1278
+ new_path
1279
+ else
1280
+ File.expand_path(File.join(base_path, new_path))
1281
+ end
1282
+ end
1283
+
1284
+ def page_found?(page)
1285
+ page && !(FileNotFoundPage === page)
1286
+ end
1287
+
1288
+ def boolean_attr_or_error(tag, attribute_name, default)
1289
+ attribute = attr_or_error(tag, :attribute_name => attribute_name, :default => default.to_s, :values => 'true, false')
1290
+ (attribute.to_s.downcase == 'true') ? true : false
1291
+ end
1292
+
1293
+ def attr_or_error(tag, options = {})
1294
+ attribute_name = options[:attribute_name].to_s
1295
+ default = options[:default]
1296
+ values = options[:values].split(',').map!(&:strip)
1297
+
1298
+ attribute = (tag.attr[attribute_name] || default).to_s
1299
+ raise TagError.new(%{`#{attribute_name}' attribute of `#{tag.name}' tag must be one of: #{values.join(', ')}}) unless values.include?(attribute)
1300
+ return attribute
1301
+ end
1302
+
1303
+ def required_attr(tag, *attribute_names)
1304
+ attr_collection = attribute_names.map{|a| "`#{a}'"}.join(' or ')
1305
+ raise TagError.new("`#{tag.name}' tag must contain a #{attr_collection} attribute.") if (tag.attr.keys & attribute_names).blank?
1306
+ end
1307
+
1308
+ def dev?(request)
1309
+ return false if request.nil?
1310
+ # TODO: More nonsense!
1311
+ if dev_host = TrustyCms::Config['dev.host']
1312
+ dev_host == request.host
1313
+ else
1314
+ request.host =~ /^dev\./
1315
+ end
1316
+ end
1317
+
1318
+ end