trusty-cms 1.0.0

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 (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