zena 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (1826) hide show
  1. data/History.txt +18 -0
  2. data/README.txt +133 -0
  3. data/app/controllers/application_controller.rb +3 -0
  4. data/app/controllers/comments_controller.rb +141 -0
  5. data/app/controllers/data_entries_controller.rb +81 -0
  6. data/app/controllers/discussions_controller.rb +48 -0
  7. data/app/controllers/documents_controller.rb +136 -0
  8. data/app/controllers/groups_controller.rb +99 -0
  9. data/app/controllers/iformats_controller.rb +78 -0
  10. data/app/controllers/links_controller.rb +98 -0
  11. data/app/controllers/nodes_controller.rb +497 -0
  12. data/app/controllers/preferences_controller.rb +52 -0
  13. data/app/controllers/relations_controller.rb +95 -0
  14. data/app/controllers/sessions_controller.rb +41 -0
  15. data/app/controllers/sites_controller.rb +112 -0
  16. data/app/controllers/users_controller.rb +139 -0
  17. data/app/controllers/versions_controller.rb +268 -0
  18. data/app/controllers/virtual_classes_controller.rb +95 -0
  19. data/app/helpers/application_helper.rb +4 -0
  20. data/app/helpers/documents_helper.rb +13 -0
  21. data/app/helpers/nodes_helper.rb +20 -0
  22. data/app/helpers/versions_helper.rb +18 -0
  23. data/app/models/book.rb +242 -0
  24. data/app/models/cache.rb +52 -0
  25. data/app/models/cached_page.rb +134 -0
  26. data/app/models/comment.rb +125 -0
  27. data/app/models/contact.rb +32 -0
  28. data/app/models/contact_content.rb +70 -0
  29. data/app/models/contact_version.rb +40 -0
  30. data/app/models/data_entry.rb +162 -0
  31. data/app/models/discussion.rb +65 -0
  32. data/app/models/document.rb +146 -0
  33. data/app/models/document_content.rb +186 -0
  34. data/app/models/document_version.rb +13 -0
  35. data/app/models/dyn_attribute.rb +4 -0
  36. data/app/models/group.rb +142 -0
  37. data/app/models/iformat.rb +146 -0
  38. data/app/models/image.rb +96 -0
  39. data/app/models/image_content.rb +189 -0
  40. data/app/models/image_version.rb +21 -0
  41. data/app/models/link.rb +121 -0
  42. data/app/models/node.rb +1752 -0
  43. data/app/models/note.rb +58 -0
  44. data/app/models/page.rb +53 -0
  45. data/app/models/project.rb +10 -0
  46. data/app/models/reference.rb +18 -0
  47. data/app/models/relation.rb +18 -0
  48. data/app/models/relation_proxy.rb +379 -0
  49. data/app/models/section.rb +5 -0
  50. data/app/models/site.rb +355 -0
  51. data/app/models/skin.rb +20 -0
  52. data/app/models/template.rb +134 -0
  53. data/app/models/template_content.rb +112 -0
  54. data/app/models/template_version.rb +37 -0
  55. data/app/models/text_document.rb +162 -0
  56. data/app/models/text_document_content.rb +46 -0
  57. data/app/models/text_document_version.rb +21 -0
  58. data/app/models/user.rb +412 -0
  59. data/app/models/version.rb +328 -0
  60. data/app/models/virtual_class.rb +134 -0
  61. data/app/models/zip.rb +6 -0
  62. data/app/views/comments/_add.rhtml +6 -0
  63. data/app/views/comments/_bin.rhtml +5 -0
  64. data/app/views/comments/_form.rhtml +26 -0
  65. data/app/views/comments/_li.rhtml +13 -0
  66. data/app/views/comments/_li_simple.rhtml +3 -0
  67. data/app/views/comments/_list.rhtml +13 -0
  68. data/app/views/comments/create.rjs +32 -0
  69. data/app/views/comments/edit.rjs +2 -0
  70. data/app/views/comments/empty_bin.rjs +1 -0
  71. data/app/views/comments/index.rhtml +12 -0
  72. data/app/views/comments/publish.rjs +7 -0
  73. data/app/views/comments/remove.rjs +9 -0
  74. data/app/views/comments/reply_to.rjs +2 -0
  75. data/app/views/comments/update.rjs +1 -0
  76. data/app/views/data_entries/_li.html.erb +9 -0
  77. data/app/views/data_entries/create.rjs +1 -0
  78. data/app/views/data_entries/destroy.rjs +1 -0
  79. data/app/views/data_entries/edit.rjs +1 -0
  80. data/app/views/data_entries/index.html.erb +5 -0
  81. data/app/views/data_entries/show.html.erb +5 -0
  82. data/app/views/data_entries/show.rjs +1 -0
  83. data/app/views/data_entries/update.rjs +1 -0
  84. data/app/views/discussions/_add.rhtml +6 -0
  85. data/app/views/discussions/_form.rhtml +21 -0
  86. data/app/views/discussions/_li.rhtml +9 -0
  87. data/app/views/discussions/_list.rhtml +4 -0
  88. data/app/views/discussions/create.rjs +10 -0
  89. data/app/views/discussions/remove.rjs +7 -0
  90. data/app/views/discussions/show.rjs +1 -0
  91. data/app/views/discussions/update.rjs +2 -0
  92. data/app/views/documents/_crop.rhtml +10 -0
  93. data/app/views/documents/crop_form.rjs +4 -0
  94. data/app/views/documents/new.rhtml +18 -0
  95. data/app/views/documents/show.rhtml +17 -0
  96. data/app/views/groups/_add.rhtml +6 -0
  97. data/app/views/groups/_form.rhtml +44 -0
  98. data/app/views/groups/_li.rhtml +13 -0
  99. data/app/views/groups/create.rjs +8 -0
  100. data/app/views/groups/destroy.rjs +2 -0
  101. data/app/views/groups/edit.html.erb +5 -0
  102. data/app/views/groups/index.rhtml +6 -0
  103. data/app/views/groups/show.html.erb +5 -0
  104. data/app/views/groups/show.rjs +1 -0
  105. data/app/views/iformats/_add.rhtml +6 -0
  106. data/app/views/iformats/_form.rhtml +29 -0
  107. data/app/views/iformats/_li.rhtml +15 -0
  108. data/app/views/iformats/create.rjs +8 -0
  109. data/app/views/iformats/destroy.rjs +11 -0
  110. data/app/views/iformats/edit.html.erb +5 -0
  111. data/app/views/iformats/index.rhtml +7 -0
  112. data/app/views/iformats/show.html.erb +5 -0
  113. data/app/views/iformats/show.rjs +1 -0
  114. data/app/views/iformats/update.rjs +5 -0
  115. data/app/views/image/_title.rhtml +8 -0
  116. data/app/views/links/_add.rhtml +6 -0
  117. data/app/views/links/_form.rhtml +34 -0
  118. data/app/views/links/_li.rhtml +7 -0
  119. data/app/views/links/_list.rhtml +9 -0
  120. data/app/views/links/create.rjs +5 -0
  121. data/app/views/links/destroy.rjs +11 -0
  122. data/app/views/links/show.rjs +5 -0
  123. data/app/views/nodes/404.html +15 -0
  124. data/app/views/nodes/500.html +15 -0
  125. data/app/views/nodes/_dates.rhtml +13 -0
  126. data/app/views/nodes/_edit_attribute.rhtml +32 -0
  127. data/app/views/nodes/_gallery.rhtml +16 -0
  128. data/app/views/nodes/_groups.rhtml +25 -0
  129. data/app/views/nodes/_import_results.rhtml +20 -0
  130. data/app/views/nodes/_list_nodes.rhtml +9 -0
  131. data/app/views/nodes/_parent.rhtml +15 -0
  132. data/app/views/nodes/_position.rhtml +22 -0
  133. data/app/views/nodes/_results.rhtml +29 -0
  134. data/app/views/nodes/_show_attr.rhtml +6 -0
  135. data/app/views/nodes/_table.rhtml +14 -0
  136. data/app/views/nodes/clear_order.rjs +7 -0
  137. data/app/views/nodes/create.rjs +17 -0
  138. data/app/views/nodes/drive.rjs +2 -0
  139. data/app/views/nodes/drop.rjs +1 -0
  140. data/app/views/nodes/edit.html.erb +17 -0
  141. data/app/views/nodes/edit.rjs +7 -0
  142. data/app/views/nodes/import.rhtml +10 -0
  143. data/app/views/nodes/save_text.rjs +13 -0
  144. data/app/views/nodes/search.rjs +3 -0
  145. data/app/views/nodes/show.rjs +1 -0
  146. data/app/views/nodes/table_update.rjs +8 -0
  147. data/app/views/nodes/update.rjs +29 -0
  148. data/app/views/nodes/zafu.rjs +1 -0
  149. data/app/views/relations/_add.erb +6 -0
  150. data/app/views/relations/_form.erb +43 -0
  151. data/app/views/relations/_li.erb +16 -0
  152. data/app/views/relations/create.rjs +8 -0
  153. data/app/views/relations/destroy.rjs +6 -0
  154. data/app/views/relations/edit.erb +2 -0
  155. data/app/views/relations/index.erb +6 -0
  156. data/app/views/relations/new.erb +2 -0
  157. data/app/views/relations/show.rjs +1 -0
  158. data/app/views/relations/update.rjs +5 -0
  159. data/app/views/search/_form.rhtml +13 -0
  160. data/app/views/sites/_form.erb +37 -0
  161. data/app/views/sites/_li.erb +13 -0
  162. data/app/views/sites/clear_cache.rjs +2 -0
  163. data/app/views/sites/edit.erb +2 -0
  164. data/app/views/sites/index.erb +6 -0
  165. data/app/views/sites/show.rjs +1 -0
  166. data/app/views/sites/update.rjs +5 -0
  167. data/app/views/sites/zena_up.html.erb +11 -0
  168. data/app/views/templates/defaults/+adminLayout.zafu +69 -0
  169. data/app/views/templates/defaults/+login.zafu +32 -0
  170. data/app/views/templates/document_create_tabs/_file.rhtml +13 -0
  171. data/app/views/templates/document_create_tabs/_import.rhtml +9 -0
  172. data/app/views/templates/document_create_tabs/_template.rhtml +22 -0
  173. data/app/views/templates/document_create_tabs/_text_doc.rhtml +14 -0
  174. data/app/views/templates/drive_tabs/_drive.rhtml +11 -0
  175. data/app/views/templates/drive_tabs/_help.rhtml +0 -0
  176. data/app/views/templates/drive_tabs/_links.rhtml +8 -0
  177. data/app/views/templates/edit_tabs/_contact.rhtml +8 -0
  178. data/app/views/templates/edit_tabs/_custom.rhtml +6 -0
  179. data/app/views/templates/edit_tabs/_document.rhtml +8 -0
  180. data/app/views/templates/edit_tabs/_help.rhtml +7 -0
  181. data/app/views/templates/edit_tabs/_image.rhtml +19 -0
  182. data/app/views/templates/edit_tabs/_template.rhtml +8 -0
  183. data/app/views/templates/edit_tabs/_text.rhtml +1 -0
  184. data/app/views/templates/edit_tabs/_textdocument.rhtml +16 -0
  185. data/app/views/templates/edit_tabs/_title.rhtml +27 -0
  186. data/app/views/users/_add.rhtml +6 -0
  187. data/app/views/users/_form.rhtml +49 -0
  188. data/app/views/users/_li.rhtml +18 -0
  189. data/app/views/users/change_info.rjs +7 -0
  190. data/app/views/users/change_password.rjs +7 -0
  191. data/app/views/users/create.rjs +8 -0
  192. data/app/views/users/index.rhtml +7 -0
  193. data/app/views/users/preferences.html.erb +40 -0
  194. data/app/views/users/show.rjs +1 -0
  195. data/app/views/users/update.rjs +16 -0
  196. data/app/views/versions/_li.rhtml +3 -0
  197. data/app/views/versions/_list.rhtml +5 -0
  198. data/app/views/versions/_tr.rhtml +10 -0
  199. data/app/views/versions/backup.rjs +16 -0
  200. data/app/views/versions/css_preview.rjs +2 -0
  201. data/app/views/versions/destroy.rjs +6 -0
  202. data/app/views/versions/diff.rjs +3 -0
  203. data/app/views/versions/edit.rhtml +55 -0
  204. data/app/views/versions/preview.rjs +9 -0
  205. data/app/views/versions/show.rjs +5 -0
  206. data/app/views/versions/update.rjs +9 -0
  207. data/app/views/virtual_classes/_add.erb +6 -0
  208. data/app/views/virtual_classes/_form.erb +30 -0
  209. data/app/views/virtual_classes/_li.erb +12 -0
  210. data/app/views/virtual_classes/create.rjs +8 -0
  211. data/app/views/virtual_classes/destroy.rjs +6 -0
  212. data/app/views/virtual_classes/index.erb +7 -0
  213. data/app/views/virtual_classes/new.erb +2 -0
  214. data/app/views/virtual_classes/show.rjs +1 -0
  215. data/app/views/virtual_classes/update.rjs +5 -0
  216. data/bin/zena +5 -0
  217. data/bricks/captcha/MIT-LICENSE +19 -0
  218. data/bricks/captcha/README +11 -0
  219. data/bricks/captcha/patch/application_controller.rb +10 -0
  220. data/bricks/captcha/patch/application_helper.rb +32 -0
  221. data/bricks/captcha/patch/site.rb +2 -0
  222. data/bricks/captcha/zafu/captcha.rb +29 -0
  223. data/bricks/math/patch/application_helper.rb +95 -0
  224. data/bricks/tags/README +31 -0
  225. data/bricks/tags/lib/has_tags.rb +164 -0
  226. data/bricks/tags/patch/node.rb +15 -0
  227. data/bricks/tags/test/sites/zena/links.yml +11 -0
  228. data/bricks/tags/test/unit/tags_test.rb +103 -0
  229. data/bricks/tags/test/zafu/tags.yml +28 -0
  230. data/bricks/toto.zip +0 -0
  231. data/config/awstats.conf.rhtml +1497 -0
  232. data/config/boot.rb +109 -0
  233. data/config/database.rhtml +25 -0
  234. data/config/database.yml +40 -0
  235. data/config/database_example.yml +27 -0
  236. data/config/deploy.rb +382 -0
  237. data/config/deploy_config_example.rb +7 -0
  238. data/config/environment.rb +13 -0
  239. data/config/environments/development.rb +21 -0
  240. data/config/environments/production.rb +27 -0
  241. data/config/environments/test.rb +22 -0
  242. data/config/gems.yml +46 -0
  243. data/config/httpd.rhtml +18 -0
  244. data/config/initializers/mime_types.rb +10 -0
  245. data/config/initializers/zena.rb +1 -0
  246. data/config/locales/de.yml +120 -0
  247. data/config/locales/fr-CH.yml +123 -0
  248. data/config/locales/fr.yml +123 -0
  249. data/config/mongrel_upload_progress.conf +7 -0
  250. data/config/routes.rb +3 -0
  251. data/config/start.html +44 -0
  252. data/config/stats.vhost.rhtml +40 -0
  253. data/config/vhost.rhtml +84 -0
  254. data/config/vhost_www.rhtml +10 -0
  255. data/config/zena.rb +39 -0
  256. data/db/init/base/help.en.zml +94 -0
  257. data/db/init/base/help.fr.zml +88 -0
  258. data/db/init/base/skins.zml +3 -0
  259. data/db/init/base/skins/default.zml +10 -0
  260. data/db/init/base/skins/default/Node-+adminLayout.zafu +45 -0
  261. data/db/init/base/skins/default/Node-+index.zafu +1 -0
  262. data/db/init/base/skins/default/Node-+login.zafu +25 -0
  263. data/db/init/base/skins/default/Node-+notFound.zafu +14 -0
  264. data/db/init/base/skins/default/Node-+popupLayout.zafu +39 -0
  265. data/db/init/base/skins/default/Node-+search.zafu +20 -0
  266. data/db/init/base/skins/default/Node-tree.zafu +19 -0
  267. data/db/init/base/skins/default/Node.zafu +95 -0
  268. data/db/init/base/skins/default/Project.zafu +8 -0
  269. data/db/init/base/skins/default/favicon.png +0 -0
  270. data/db/init/base/skins/default/notes.zafu +22 -0
  271. data/db/init/base/skins/default/style.css +80 -0
  272. data/db/migrate/001_create_base.rb +156 -0
  273. data/db/migrate/002_add_time_zone_to_users.rb +9 -0
  274. data/db/migrate/003_add_custom_base_flag.rb +11 -0
  275. data/db/migrate/004_rename_template_skin.rb +9 -0
  276. data/db/migrate/005_create_cached_pages.rb +20 -0
  277. data/db/migrate/006_create_sites.rb +60 -0
  278. data/db/migrate/007_replace_id_by_zip.rb +17 -0
  279. data/db/migrate/008_user_status.rb +10 -0
  280. data/db/migrate/009_fulltext.rb +13 -0
  281. data/db/migrate/010_create_template_content.rb +17 -0
  282. data/db/migrate/011_project_to_section.rb +9 -0
  283. data/db/migrate/012_add_project_id.rb +12 -0
  284. data/db/migrate/013_remove_defaults.rb +29 -0
  285. data/db/migrate/014_add_sort_field.rb +10 -0
  286. data/db/migrate/015_add_dyn_attributes.rb +18 -0
  287. data/db/migrate/016_remove_translations.rb +20 -0
  288. data/db/migrate/017_rename_authorize.rb +9 -0
  289. data/db/migrate/018_add_auth_option.rb +9 -0
  290. data/db/migrate/019_remove_user_status.rb +25 -0
  291. data/db/migrate/020_create_participation.rb +30 -0
  292. data/db/migrate/021_create_relations.rb +23 -0
  293. data/db/migrate/022_create_virtual_classes.rb +20 -0
  294. data/db/migrate/023_ip_on_anonymous_comment.rb +9 -0
  295. data/db/migrate/024_correct_vclass_kpath.rb +11 -0
  296. data/db/migrate/025_move_tag_into_vclass.rb +13 -0
  297. data/db/migrate/026_rename_templates.rb +47 -0
  298. data/db/migrate/027_add_country_to_contacts.rb +9 -0
  299. data/db/migrate/028_change_size_of_conten_type_field.rb +9 -0
  300. data/db/migrate/029_create_data_entries.rb +21 -0
  301. data/db/migrate/030_redit_auto_publish_site_settings.rb +13 -0
  302. data/db/migrate/031_create_iformats.rb +21 -0
  303. data/db/migrate/032_caches_context_as_hash.rb +10 -0
  304. data/db/migrate/033_documents_kpath_change.rb +17 -0
  305. data/db/migrate/034_change_file_storage.rb +38 -0
  306. data/db/migrate/035_add_status_to_link.rb +13 -0
  307. data/db/migrate/036_add_flag_fields_on_nodes.rb +11 -0
  308. data/db/migrate/037_add_auto_create_discussion_to_v_class.rb +11 -0
  309. data/db/migrate/038_create_site_attributes.rb +15 -0
  310. data/db/migrate/039_default_position.rb +10 -0
  311. data/db/migrate/040_second_value_for_data_entry.rb +11 -0
  312. data/db/migrate/041_add_attributes_to_v_class.rb +9 -0
  313. data/db/migrate/042_fix_position_should_be_float.rb +8 -0
  314. data/db/migrate/043_move_user_lang_into_participation.rb +15 -0
  315. data/db/migrate/044_remove_monolingual_site_option.rb +9 -0
  316. data/db/migrate/045_avoid_star_in_templates.rb +13 -0
  317. data/db/migrate/046_fix_zazen_image_tag.rb +47 -0
  318. data/db/migrate/047_change_default_link_id_to_zero.rb +12 -0
  319. data/db/migrate/048_link_source_target_can_be_null.rb +11 -0
  320. data/db/migrate/049_fix_publish_from_is_null.rb +9 -0
  321. data/db/migrate/050_date_in_links.rb +9 -0
  322. data/db/migrate/051_add_exif_tags_to_images.rb +9 -0
  323. data/db/migrate/20090825201159_insert_zero_link.rb +8 -0
  324. data/db/migrate/20090825201200_merge_bricks_migrations_with_std_migrations.rb +19 -0
  325. data/db/migrate/20090924141459_zafu_fix_sept09.rb +19 -0
  326. data/db/migrate/20090927125912_allow_null_in_text_fields.rb +17 -0
  327. data/db/migrate/20090928133440_no_more_private_nodes.rb +25 -0
  328. data/db/migrate/20090928143754_version_status_change.rb +24 -0
  329. data/db/migrate/20091001084025_change_status_values_for_comments.rb +22 -0
  330. data/db/migrate/20091009084057_add_vhash_in_node.rb +13 -0
  331. data/db/migrate/20091013100351_rename_publish_group_to_drive_group.rb +12 -0
  332. data/db/migrate/20091014130833_fix_template_title.rb +16 -0
  333. data/db/migrate/20091014183726_merge_participation_into_users.rb +21 -0
  334. data/db/production.sqlite3 +0 -0
  335. data/db/schema.rb +281 -0
  336. data/lib/base_additions.rb +7 -0
  337. data/lib/bricks/patcher.rb +68 -0
  338. data/lib/comment_query.rb +92 -0
  339. data/lib/core_ext/date_time.rb +5 -0
  340. data/lib/core_ext/dir.rb +12 -0
  341. data/lib/core_ext/fixnum.rb +20 -0
  342. data/lib/core_ext/string.rb +74 -0
  343. data/lib/exif_data.rb +54 -0
  344. data/lib/fix_rails_layouts.rb +9 -0
  345. data/lib/gettext_strings.rb +160 -0
  346. data/lib/image_builder.rb +325 -0
  347. data/lib/log_recorder/lib/log_recorder.rb +138 -0
  348. data/lib/log_recorder/test/log_recorder_test.rb +0 -0
  349. data/lib/parser.rb +1 -0
  350. data/lib/tasks/capistrano.rake +99 -0
  351. data/lib/tasks/zena.rake +358 -0
  352. data/lib/tasks/zena.rb +2 -0
  353. data/lib/upload_progress_server.rb +24 -0
  354. data/lib/webdav_adapter.rb +172 -0
  355. data/lib/zafu_parser.rb +3 -0
  356. data/lib/zazen_parser.rb +1 -0
  357. data/lib/zena.rb +398 -0
  358. data/lib/zena/acts/multiversion.rb +755 -0
  359. data/lib/zena/acts/secure.rb +774 -0
  360. data/lib/zena/app.rb +39 -0
  361. data/lib/zena/code_syntax.rb +275 -0
  362. data/lib/zena/controller/test_case.rb +32 -0
  363. data/lib/zena/db.rb +252 -0
  364. data/lib/zena/fix/mysql_connection.rb +17 -0
  365. data/lib/zena/foxy_parser.rb +845 -0
  366. data/lib/zena/migrator.rb +109 -0
  367. data/lib/zena/parser.rb +601 -0
  368. data/lib/zena/parser/zafu_rules.rb +232 -0
  369. data/lib/zena/parser/zafu_tags.rb +197 -0
  370. data/lib/zena/parser/zazen_rules.rb +427 -0
  371. data/lib/zena/parser/zazen_tags.rb +44 -0
  372. data/lib/zena/parser/zena_rules.rb +29 -0
  373. data/lib/zena/parser/zena_tags.rb +3559 -0
  374. data/lib/zena/root.rb +3 -0
  375. data/lib/zena/routes.rb +73 -0
  376. data/lib/zena/test_controller.rb +74 -0
  377. data/lib/zena/unit/test_case.rb +24 -0
  378. data/lib/zena/use/ajax.rb +119 -0
  379. data/lib/zena/use/authentification.rb +120 -0
  380. data/lib/zena/use/calendar.rb +130 -0
  381. data/lib/zena/use/custom_queries/complex.host.yml +173 -0
  382. data/lib/zena/use/dates.rb +230 -0
  383. data/lib/zena/use/dyn_attributes.rb +256 -0
  384. data/lib/zena/use/error_rendering.rb +52 -0
  385. data/lib/zena/use/fixtures.rb +133 -0
  386. data/lib/zena/use/grid.rb +148 -0
  387. data/lib/zena/use/html_tags.rb +603 -0
  388. data/lib/zena/use/i18n.rb +247 -0
  389. data/lib/zena/use/nested_attributes_alias.rb +201 -0
  390. data/lib/zena/use/node_query_finders.rb +491 -0
  391. data/lib/zena/use/refactor.rb +231 -0
  392. data/lib/zena/use/relations.rb +360 -0
  393. data/lib/zena/use/rendering.rb +248 -0
  394. data/lib/zena/use/test_helper.rb +151 -0
  395. data/lib/zena/use/urls.rb +180 -0
  396. data/lib/zena/use/zafu.rb +549 -0
  397. data/lib/zena/use/zazen.rb +230 -0
  398. data/lib/zena/view/test_case.rb +23 -0
  399. data/locale/en/LC_MESSAGES/zena.mo +0 -0
  400. data/locale/en/zena.po +2116 -0
  401. data/locale/fr/LC_MESSAGES/zena.mo +0 -0
  402. data/locale/fr/zena.po +2172 -0
  403. data/locale/zena.pot +1915 -0
  404. data/public/calendar/calendar-brown.css +225 -0
  405. data/public/calendar/calendar-setup.js +153 -0
  406. data/public/calendar/calendar.js +1817 -0
  407. data/public/calendar/iconCalendar.gif +0 -0
  408. data/public/calendar/lang/calendar-en-utf8.js +128 -0
  409. data/public/calendar/lang/calendar-fr-utf8.js +120 -0
  410. data/public/calendar/lang/lang.zip +0 -0
  411. data/public/dispatch.cgi +10 -0
  412. data/public/dispatch.fcgi +24 -0
  413. data/public/dispatch.rb +10 -0
  414. data/public/favicon.ico +0 -0
  415. data/public/images/accept.png +0 -0
  416. data/public/images/action.png +0 -0
  417. data/public/images/add.png +0 -0
  418. data/public/images/ajax-loader.gif +0 -0
  419. data/public/images/anchor.png +0 -0
  420. data/public/images/application.png +0 -0
  421. data/public/images/application_add.png +0 -0
  422. data/public/images/application_cascade.png +0 -0
  423. data/public/images/application_delete.png +0 -0
  424. data/public/images/application_double.png +0 -0
  425. data/public/images/application_edit.png +0 -0
  426. data/public/images/application_error.png +0 -0
  427. data/public/images/application_form.png +0 -0
  428. data/public/images/application_form_add.png +0 -0
  429. data/public/images/application_form_delete.png +0 -0
  430. data/public/images/application_form_edit.png +0 -0
  431. data/public/images/application_form_magnify.png +0 -0
  432. data/public/images/application_get.png +0 -0
  433. data/public/images/application_go.png +0 -0
  434. data/public/images/application_home.png +0 -0
  435. data/public/images/application_key.png +0 -0
  436. data/public/images/application_lightning.png +0 -0
  437. data/public/images/application_link.png +0 -0
  438. data/public/images/application_osx.png +0 -0
  439. data/public/images/application_osx_terminal.png +0 -0
  440. data/public/images/application_put.png +0 -0
  441. data/public/images/application_side_boxes.png +0 -0
  442. data/public/images/application_side_contract.png +0 -0
  443. data/public/images/application_side_expand.png +0 -0
  444. data/public/images/application_side_list.png +0 -0
  445. data/public/images/application_side_tree.png +0 -0
  446. data/public/images/application_split.png +0 -0
  447. data/public/images/application_tile_horizontal.png +0 -0
  448. data/public/images/application_tile_vertical.png +0 -0
  449. data/public/images/application_view_columns.png +0 -0
  450. data/public/images/application_view_detail.png +0 -0
  451. data/public/images/application_view_gallery.png +0 -0
  452. data/public/images/application_view_icons.png +0 -0
  453. data/public/images/application_view_list.png +0 -0
  454. data/public/images/application_view_tile.png +0 -0
  455. data/public/images/application_xp.png +0 -0
  456. data/public/images/application_xp_terminal.png +0 -0
  457. data/public/images/arrow_branch.png +0 -0
  458. data/public/images/arrow_divide.png +0 -0
  459. data/public/images/arrow_down.png +0 -0
  460. data/public/images/arrow_in.png +0 -0
  461. data/public/images/arrow_inout.png +0 -0
  462. data/public/images/arrow_join.png +0 -0
  463. data/public/images/arrow_left.png +0 -0
  464. data/public/images/arrow_merge.png +0 -0
  465. data/public/images/arrow_out.png +0 -0
  466. data/public/images/arrow_redo.png +0 -0
  467. data/public/images/arrow_refresh.png +0 -0
  468. data/public/images/arrow_refresh_small.png +0 -0
  469. data/public/images/arrow_right.png +0 -0
  470. data/public/images/arrow_rotate_anticlockwise.png +0 -0
  471. data/public/images/arrow_rotate_clockwise.png +0 -0
  472. data/public/images/arrow_switch.png +0 -0
  473. data/public/images/arrow_turn_left.png +0 -0
  474. data/public/images/arrow_turn_right.png +0 -0
  475. data/public/images/arrow_undo.png +0 -0
  476. data/public/images/arrow_up.png +0 -0
  477. data/public/images/asterisk_orange.png +0 -0
  478. data/public/images/asterisk_yellow.png +0 -0
  479. data/public/images/attach.png +0 -0
  480. data/public/images/award_star_add.png +0 -0
  481. data/public/images/award_star_bronze_1.png +0 -0
  482. data/public/images/award_star_bronze_2.png +0 -0
  483. data/public/images/award_star_bronze_3.png +0 -0
  484. data/public/images/award_star_delete.png +0 -0
  485. data/public/images/award_star_gold_1.png +0 -0
  486. data/public/images/award_star_gold_2.png +0 -0
  487. data/public/images/award_star_gold_3.png +0 -0
  488. data/public/images/award_star_silver_1.png +0 -0
  489. data/public/images/award_star_silver_2.png +0 -0
  490. data/public/images/award_star_silver_3.png +0 -0
  491. data/public/images/basket.png +0 -0
  492. data/public/images/basket_add.png +0 -0
  493. data/public/images/basket_delete.png +0 -0
  494. data/public/images/basket_edit.png +0 -0
  495. data/public/images/basket_error.png +0 -0
  496. data/public/images/basket_go.png +0 -0
  497. data/public/images/basket_put.png +0 -0
  498. data/public/images/basket_remove.png +0 -0
  499. data/public/images/bell.png +0 -0
  500. data/public/images/bell_add.png +0 -0
  501. data/public/images/bell_delete.png +0 -0
  502. data/public/images/bell_error.png +0 -0
  503. data/public/images/bell_go.png +0 -0
  504. data/public/images/bell_link.png +0 -0
  505. data/public/images/bin.png +0 -0
  506. data/public/images/bin_closed.png +0 -0
  507. data/public/images/bin_empty.gif +0 -0
  508. data/public/images/bin_empty.png +0 -0
  509. data/public/images/bin_full.gif +0 -0
  510. data/public/images/bomb.png +0 -0
  511. data/public/images/book.png +0 -0
  512. data/public/images/book_add.png +0 -0
  513. data/public/images/book_addresses.png +0 -0
  514. data/public/images/book_delete.png +0 -0
  515. data/public/images/book_edit.png +0 -0
  516. data/public/images/book_error.png +0 -0
  517. data/public/images/book_go.png +0 -0
  518. data/public/images/book_key.png +0 -0
  519. data/public/images/book_link.png +0 -0
  520. data/public/images/book_next.png +0 -0
  521. data/public/images/book_open.png +0 -0
  522. data/public/images/book_previous.png +0 -0
  523. data/public/images/box.png +0 -0
  524. data/public/images/brick.png +0 -0
  525. data/public/images/brick_add.png +0 -0
  526. data/public/images/brick_delete.png +0 -0
  527. data/public/images/brick_edit.png +0 -0
  528. data/public/images/brick_error.png +0 -0
  529. data/public/images/brick_go.png +0 -0
  530. data/public/images/brick_link.png +0 -0
  531. data/public/images/bricks.png +0 -0
  532. data/public/images/briefcase.png +0 -0
  533. data/public/images/bug.png +0 -0
  534. data/public/images/bug_add.png +0 -0
  535. data/public/images/bug_delete.png +0 -0
  536. data/public/images/bug_edit.png +0 -0
  537. data/public/images/bug_error.png +0 -0
  538. data/public/images/bug_go.png +0 -0
  539. data/public/images/bug_link.png +0 -0
  540. data/public/images/building.png +0 -0
  541. data/public/images/building_add.png +0 -0
  542. data/public/images/building_delete.png +0 -0
  543. data/public/images/building_edit.png +0 -0
  544. data/public/images/building_error.png +0 -0
  545. data/public/images/building_go.png +0 -0
  546. data/public/images/building_key.png +0 -0
  547. data/public/images/building_link.png +0 -0
  548. data/public/images/bullet_add.png +0 -0
  549. data/public/images/bullet_arrow_bottom.png +0 -0
  550. data/public/images/bullet_arrow_down.png +0 -0
  551. data/public/images/bullet_arrow_top.png +0 -0
  552. data/public/images/bullet_arrow_up.png +0 -0
  553. data/public/images/bullet_black.png +0 -0
  554. data/public/images/bullet_blue.png +0 -0
  555. data/public/images/bullet_delete.png +0 -0
  556. data/public/images/bullet_disk.png +0 -0
  557. data/public/images/bullet_error.png +0 -0
  558. data/public/images/bullet_feed.png +0 -0
  559. data/public/images/bullet_go.png +0 -0
  560. data/public/images/bullet_green.png +0 -0
  561. data/public/images/bullet_key.png +0 -0
  562. data/public/images/bullet_orange.png +0 -0
  563. data/public/images/bullet_picture.png +0 -0
  564. data/public/images/bullet_pink.png +0 -0
  565. data/public/images/bullet_purple.png +0 -0
  566. data/public/images/bullet_red.png +0 -0
  567. data/public/images/bullet_star.png +0 -0
  568. data/public/images/bullet_toggle_minus.png +0 -0
  569. data/public/images/bullet_toggle_plus.png +0 -0
  570. data/public/images/bullet_white.png +0 -0
  571. data/public/images/bullet_wrench.png +0 -0
  572. data/public/images/bullet_yellow.png +0 -0
  573. data/public/images/cake.png +0 -0
  574. data/public/images/calculator.png +0 -0
  575. data/public/images/calculator_add.png +0 -0
  576. data/public/images/calculator_delete.png +0 -0
  577. data/public/images/calculator_edit.png +0 -0
  578. data/public/images/calculator_error.png +0 -0
  579. data/public/images/calculator_link.png +0 -0
  580. data/public/images/calendar.png +0 -0
  581. data/public/images/calendar_add.png +0 -0
  582. data/public/images/calendar_delete.png +0 -0
  583. data/public/images/calendar_edit.png +0 -0
  584. data/public/images/calendar_link.png +0 -0
  585. data/public/images/calendar_view_day.png +0 -0
  586. data/public/images/calendar_view_month.png +0 -0
  587. data/public/images/calendar_view_week.png +0 -0
  588. data/public/images/camera.png +0 -0
  589. data/public/images/camera_add.png +0 -0
  590. data/public/images/camera_delete.png +0 -0
  591. data/public/images/camera_edit.png +0 -0
  592. data/public/images/camera_error.png +0 -0
  593. data/public/images/camera_go.png +0 -0
  594. data/public/images/camera_link.png +0 -0
  595. data/public/images/camera_small.png +0 -0
  596. data/public/images/cancel.png +0 -0
  597. data/public/images/car.png +0 -0
  598. data/public/images/car_add.png +0 -0
  599. data/public/images/car_delete.png +0 -0
  600. data/public/images/cart.png +0 -0
  601. data/public/images/cart_add.png +0 -0
  602. data/public/images/cart_delete.png +0 -0
  603. data/public/images/cart_edit.png +0 -0
  604. data/public/images/cart_error.png +0 -0
  605. data/public/images/cart_go.png +0 -0
  606. data/public/images/cart_put.png +0 -0
  607. data/public/images/cart_remove.png +0 -0
  608. data/public/images/cd.png +0 -0
  609. data/public/images/cd_add.png +0 -0
  610. data/public/images/cd_burn.png +0 -0
  611. data/public/images/cd_delete.png +0 -0
  612. data/public/images/cd_edit.png +0 -0
  613. data/public/images/cd_eject.png +0 -0
  614. data/public/images/cd_go.png +0 -0
  615. data/public/images/chart_bar.png +0 -0
  616. data/public/images/chart_bar_add.png +0 -0
  617. data/public/images/chart_bar_delete.png +0 -0
  618. data/public/images/chart_bar_edit.png +0 -0
  619. data/public/images/chart_bar_error.png +0 -0
  620. data/public/images/chart_bar_link.png +0 -0
  621. data/public/images/chart_curve.png +0 -0
  622. data/public/images/chart_curve_add.png +0 -0
  623. data/public/images/chart_curve_delete.png +0 -0
  624. data/public/images/chart_curve_edit.png +0 -0
  625. data/public/images/chart_curve_error.png +0 -0
  626. data/public/images/chart_curve_go.png +0 -0
  627. data/public/images/chart_curve_link.png +0 -0
  628. data/public/images/chart_line.png +0 -0
  629. data/public/images/chart_line_add.png +0 -0
  630. data/public/images/chart_line_delete.png +0 -0
  631. data/public/images/chart_line_edit.png +0 -0
  632. data/public/images/chart_line_error.png +0 -0
  633. data/public/images/chart_line_link.png +0 -0
  634. data/public/images/chart_organisation.png +0 -0
  635. data/public/images/chart_organisation_add.png +0 -0
  636. data/public/images/chart_organisation_delete.png +0 -0
  637. data/public/images/chart_pie.png +0 -0
  638. data/public/images/chart_pie_add.png +0 -0
  639. data/public/images/chart_pie_delete.png +0 -0
  640. data/public/images/chart_pie_edit.png +0 -0
  641. data/public/images/chart_pie_error.png +0 -0
  642. data/public/images/chart_pie_link.png +0 -0
  643. data/public/images/clock.png +0 -0
  644. data/public/images/clock_add.png +0 -0
  645. data/public/images/clock_delete.png +0 -0
  646. data/public/images/clock_edit.png +0 -0
  647. data/public/images/clock_error.png +0 -0
  648. data/public/images/clock_go.png +0 -0
  649. data/public/images/clock_link.png +0 -0
  650. data/public/images/clock_pause.png +0 -0
  651. data/public/images/clock_play.png +0 -0
  652. data/public/images/clock_red.png +0 -0
  653. data/public/images/clock_stop.png +0 -0
  654. data/public/images/cog.png +0 -0
  655. data/public/images/cog_add.png +0 -0
  656. data/public/images/cog_delete.png +0 -0
  657. data/public/images/cog_edit.png +0 -0
  658. data/public/images/cog_error.png +0 -0
  659. data/public/images/cog_go.png +0 -0
  660. data/public/images/coins.png +0 -0
  661. data/public/images/coins_add.png +0 -0
  662. data/public/images/coins_delete.png +0 -0
  663. data/public/images/collection.png +0 -0
  664. data/public/images/color_swatch.png +0 -0
  665. data/public/images/color_wheel.png +0 -0
  666. data/public/images/column_add.png +0 -0
  667. data/public/images/column_delete.png +0 -0
  668. data/public/images/comment.png +0 -0
  669. data/public/images/comment_add.png +0 -0
  670. data/public/images/comment_delete.png +0 -0
  671. data/public/images/comment_edit.png +0 -0
  672. data/public/images/comments.png +0 -0
  673. data/public/images/comments_add.png +0 -0
  674. data/public/images/comments_delete.png +0 -0
  675. data/public/images/comments_red.png +0 -0
  676. data/public/images/compress.png +0 -0
  677. data/public/images/computer.png +0 -0
  678. data/public/images/computer_add.png +0 -0
  679. data/public/images/computer_delete.png +0 -0
  680. data/public/images/computer_edit.png +0 -0
  681. data/public/images/computer_error.png +0 -0
  682. data/public/images/computer_go.png +0 -0
  683. data/public/images/computer_key.png +0 -0
  684. data/public/images/computer_link.png +0 -0
  685. data/public/images/connect.png +0 -0
  686. data/public/images/contrast.png +0 -0
  687. data/public/images/contrast_decrease.png +0 -0
  688. data/public/images/contrast_high.png +0 -0
  689. data/public/images/contrast_increase.png +0 -0
  690. data/public/images/contrast_low.png +0 -0
  691. data/public/images/control_eject.png +0 -0
  692. data/public/images/control_eject_blue.png +0 -0
  693. data/public/images/control_end.png +0 -0
  694. data/public/images/control_end_blue.png +0 -0
  695. data/public/images/control_equalizer.png +0 -0
  696. data/public/images/control_equalizer_blue.png +0 -0
  697. data/public/images/control_fastforward.png +0 -0
  698. data/public/images/control_fastforward_blue.png +0 -0
  699. data/public/images/control_pause.png +0 -0
  700. data/public/images/control_pause_blue.png +0 -0
  701. data/public/images/control_play.png +0 -0
  702. data/public/images/control_play_blue.png +0 -0
  703. data/public/images/control_repeat.png +0 -0
  704. data/public/images/control_repeat_blue.png +0 -0
  705. data/public/images/control_rewind.png +0 -0
  706. data/public/images/control_rewind_blue.png +0 -0
  707. data/public/images/control_start.png +0 -0
  708. data/public/images/control_start_blue.png +0 -0
  709. data/public/images/control_stop.png +0 -0
  710. data/public/images/control_stop_blue.png +0 -0
  711. data/public/images/controller.png +0 -0
  712. data/public/images/controller_add.png +0 -0
  713. data/public/images/controller_delete.png +0 -0
  714. data/public/images/controller_error.png +0 -0
  715. data/public/images/creditcards.png +0 -0
  716. data/public/images/cross.png +0 -0
  717. data/public/images/css.png +0 -0
  718. data/public/images/css_add.png +0 -0
  719. data/public/images/css_delete.png +0 -0
  720. data/public/images/css_go.png +0 -0
  721. data/public/images/css_valid.png +0 -0
  722. data/public/images/cup.png +0 -0
  723. data/public/images/cup_add.png +0 -0
  724. data/public/images/cup_delete.png +0 -0
  725. data/public/images/cup_edit.png +0 -0
  726. data/public/images/cup_error.png +0 -0
  727. data/public/images/cup_go.png +0 -0
  728. data/public/images/cup_key.png +0 -0
  729. data/public/images/cup_link.png +0 -0
  730. data/public/images/cursor.png +0 -0
  731. data/public/images/cut.png +0 -0
  732. data/public/images/cut_red.png +0 -0
  733. data/public/images/database.png +0 -0
  734. data/public/images/database_add.png +0 -0
  735. data/public/images/database_connect.png +0 -0
  736. data/public/images/database_delete.png +0 -0
  737. data/public/images/database_edit.png +0 -0
  738. data/public/images/database_error.png +0 -0
  739. data/public/images/database_gear.png +0 -0
  740. data/public/images/database_go.png +0 -0
  741. data/public/images/database_key.png +0 -0
  742. data/public/images/database_lightning.png +0 -0
  743. data/public/images/database_link.png +0 -0
  744. data/public/images/database_refresh.png +0 -0
  745. data/public/images/database_save.png +0 -0
  746. data/public/images/database_table.png +0 -0
  747. data/public/images/date.png +0 -0
  748. data/public/images/date_add.png +0 -0
  749. data/public/images/date_delete.png +0 -0
  750. data/public/images/date_edit.png +0 -0
  751. data/public/images/date_error.png +0 -0
  752. data/public/images/date_go.png +0 -0
  753. data/public/images/date_link.png +0 -0
  754. data/public/images/date_magnify.png +0 -0
  755. data/public/images/date_next.png +0 -0
  756. data/public/images/date_previous.png +0 -0
  757. data/public/images/delete.png +0 -0
  758. data/public/images/disconnect.png +0 -0
  759. data/public/images/disk.png +0 -0
  760. data/public/images/disk_multiple.png +0 -0
  761. data/public/images/door.png +0 -0
  762. data/public/images/door_in.png +0 -0
  763. data/public/images/door_open.png +0 -0
  764. data/public/images/door_out.png +0 -0
  765. data/public/images/drink.png +0 -0
  766. data/public/images/drink_empty.png +0 -0
  767. data/public/images/drive.png +0 -0
  768. data/public/images/drive_add.png +0 -0
  769. data/public/images/drive_burn.png +0 -0
  770. data/public/images/drive_cd.png +0 -0
  771. data/public/images/drive_cd_empty.png +0 -0
  772. data/public/images/drive_delete.png +0 -0
  773. data/public/images/drive_disk.png +0 -0
  774. data/public/images/drive_edit.png +0 -0
  775. data/public/images/drive_error.png +0 -0
  776. data/public/images/drive_go.png +0 -0
  777. data/public/images/drive_key.png +0 -0
  778. data/public/images/drive_link.png +0 -0
  779. data/public/images/drive_magnify.png +0 -0
  780. data/public/images/drive_network.png +0 -0
  781. data/public/images/drive_rename.png +0 -0
  782. data/public/images/drive_user.png +0 -0
  783. data/public/images/drive_web.png +0 -0
  784. data/public/images/dvd.png +0 -0
  785. data/public/images/dvd_add.png +0 -0
  786. data/public/images/dvd_delete.png +0 -0
  787. data/public/images/dvd_edit.png +0 -0
  788. data/public/images/dvd_error.png +0 -0
  789. data/public/images/dvd_go.png +0 -0
  790. data/public/images/dvd_key.png +0 -0
  791. data/public/images/dvd_link.png +0 -0
  792. data/public/images/email.png +0 -0
  793. data/public/images/email_add.png +0 -0
  794. data/public/images/email_attach.png +0 -0
  795. data/public/images/email_delete.png +0 -0
  796. data/public/images/email_edit.png +0 -0
  797. data/public/images/email_error.png +0 -0
  798. data/public/images/email_go.png +0 -0
  799. data/public/images/email_link.png +0 -0
  800. data/public/images/email_open.png +0 -0
  801. data/public/images/email_open_image.png +0 -0
  802. data/public/images/emoticon_evilgrin.png +0 -0
  803. data/public/images/emoticon_grin.png +0 -0
  804. data/public/images/emoticon_happy.png +0 -0
  805. data/public/images/emoticon_smile.png +0 -0
  806. data/public/images/emoticon_surprised.png +0 -0
  807. data/public/images/emoticon_tongue.png +0 -0
  808. data/public/images/emoticon_unhappy.png +0 -0
  809. data/public/images/emoticon_waii.png +0 -0
  810. data/public/images/emoticon_wink.png +0 -0
  811. data/public/images/error.png +0 -0
  812. data/public/images/error_add.png +0 -0
  813. data/public/images/error_delete.png +0 -0
  814. data/public/images/error_go.png +0 -0
  815. data/public/images/exclamation.png +0 -0
  816. data/public/images/ext/contact.png +0 -0
  817. data/public/images/ext/contact_pv.png +0 -0
  818. data/public/images/ext/doc.png +0 -0
  819. data/public/images/ext/doc_tiny.png +0 -0
  820. data/public/images/ext/jpg.png +0 -0
  821. data/public/images/ext/jpg_tiny.png +0 -0
  822. data/public/images/ext/other.png +0 -0
  823. data/public/images/ext/other_pv.png +0 -0
  824. data/public/images/ext/other_tiny.png +0 -0
  825. data/public/images/ext/page.png +0 -0
  826. data/public/images/ext/page_pv.png +0 -0
  827. data/public/images/ext/page_tiny.png +0 -0
  828. data/public/images/ext/pdf.png +0 -0
  829. data/public/images/ext/pdf_pv.png +0 -0
  830. data/public/images/ext/pdf_tiny.png +0 -0
  831. data/public/images/ext/png.png +0 -0
  832. data/public/images/ext/png_tiny.png +0 -0
  833. data/public/images/ext/post.png +0 -0
  834. data/public/images/ext/post_pv.png +0 -0
  835. data/public/images/ext/post_tiny.png +0 -0
  836. data/public/images/ext/project.png +0 -0
  837. data/public/images/ext/project_pv.png +0 -0
  838. data/public/images/ext/project_tiny.png +0 -0
  839. data/public/images/ext/rtf.png +0 -0
  840. data/public/images/ext/rtf_tiny.png +0 -0
  841. data/public/images/ext/tag.png +0 -0
  842. data/public/images/ext/tag_pv.png +0 -0
  843. data/public/images/ext/xls.png +0 -0
  844. data/public/images/ext/xls_tiny.png +0 -0
  845. data/public/images/ext/zip.png +0 -0
  846. data/public/images/ext/zip_pv.png +0 -0
  847. data/public/images/ext/zip_tiny.png +0 -0
  848. data/public/images/eye.png +0 -0
  849. data/public/images/feed.png +0 -0
  850. data/public/images/feed_add.png +0 -0
  851. data/public/images/feed_delete.png +0 -0
  852. data/public/images/feed_disk.png +0 -0
  853. data/public/images/feed_edit.png +0 -0
  854. data/public/images/feed_error.png +0 -0
  855. data/public/images/feed_go.png +0 -0
  856. data/public/images/feed_key.png +0 -0
  857. data/public/images/feed_link.png +0 -0
  858. data/public/images/feed_magnify.png +0 -0
  859. data/public/images/female.png +0 -0
  860. data/public/images/film.png +0 -0
  861. data/public/images/film_add.png +0 -0
  862. data/public/images/film_delete.png +0 -0
  863. data/public/images/film_edit.png +0 -0
  864. data/public/images/film_error.png +0 -0
  865. data/public/images/film_go.png +0 -0
  866. data/public/images/film_key.png +0 -0
  867. data/public/images/film_link.png +0 -0
  868. data/public/images/film_save.png +0 -0
  869. data/public/images/find.png +0 -0
  870. data/public/images/flag_blue.png +0 -0
  871. data/public/images/flag_green.png +0 -0
  872. data/public/images/flag_orange.png +0 -0
  873. data/public/images/flag_pink.png +0 -0
  874. data/public/images/flag_purple.png +0 -0
  875. data/public/images/flag_red.png +0 -0
  876. data/public/images/flag_yellow.png +0 -0
  877. data/public/images/folder.png +0 -0
  878. data/public/images/folder_add.png +0 -0
  879. data/public/images/folder_bell.png +0 -0
  880. data/public/images/folder_brick.png +0 -0
  881. data/public/images/folder_bug.png +0 -0
  882. data/public/images/folder_camera.png +0 -0
  883. data/public/images/folder_database.png +0 -0
  884. data/public/images/folder_delete.png +0 -0
  885. data/public/images/folder_edit.png +0 -0
  886. data/public/images/folder_error.png +0 -0
  887. data/public/images/folder_explore.png +0 -0
  888. data/public/images/folder_feed.png +0 -0
  889. data/public/images/folder_find.png +0 -0
  890. data/public/images/folder_go.png +0 -0
  891. data/public/images/folder_heart.png +0 -0
  892. data/public/images/folder_image.png +0 -0
  893. data/public/images/folder_key.png +0 -0
  894. data/public/images/folder_lightbulb.png +0 -0
  895. data/public/images/folder_link.png +0 -0
  896. data/public/images/folder_magnify.png +0 -0
  897. data/public/images/folder_page.png +0 -0
  898. data/public/images/folder_page_white.png +0 -0
  899. data/public/images/folder_palette.png +0 -0
  900. data/public/images/folder_picture.png +0 -0
  901. data/public/images/folder_star.png +0 -0
  902. data/public/images/folder_table.png +0 -0
  903. data/public/images/folder_user.png +0 -0
  904. data/public/images/folder_wrench.png +0 -0
  905. data/public/images/font.png +0 -0
  906. data/public/images/font_add.png +0 -0
  907. data/public/images/font_delete.png +0 -0
  908. data/public/images/font_go.png +0 -0
  909. data/public/images/group.png +0 -0
  910. data/public/images/group_add.png +0 -0
  911. data/public/images/group_admin.png +0 -0
  912. data/public/images/group_delete.png +0 -0
  913. data/public/images/group_edit.png +0 -0
  914. data/public/images/group_error.png +0 -0
  915. data/public/images/group_gear.png +0 -0
  916. data/public/images/group_go.png +0 -0
  917. data/public/images/group_key.png +0 -0
  918. data/public/images/group_link.png +0 -0
  919. data/public/images/group_pub.png +0 -0
  920. data/public/images/group_site.png +0 -0
  921. data/public/images/heart.png +0 -0
  922. data/public/images/heart_add.png +0 -0
  923. data/public/images/heart_delete.png +0 -0
  924. data/public/images/help.png +0 -0
  925. data/public/images/home.png +0 -0
  926. data/public/images/hourglass.png +0 -0
  927. data/public/images/hourglass_add.png +0 -0
  928. data/public/images/hourglass_delete.png +0 -0
  929. data/public/images/hourglass_go.png +0 -0
  930. data/public/images/hourglass_link.png +0 -0
  931. data/public/images/house.png +0 -0
  932. data/public/images/house_go.png +0 -0
  933. data/public/images/house_link.png +0 -0
  934. data/public/images/html.png +0 -0
  935. data/public/images/html_add.png +0 -0
  936. data/public/images/html_delete.png +0 -0
  937. data/public/images/html_go.png +0 -0
  938. data/public/images/html_valid.png +0 -0
  939. data/public/images/image.png +0 -0
  940. data/public/images/image_add.png +0 -0
  941. data/public/images/image_delete.png +0 -0
  942. data/public/images/image_edit.png +0 -0
  943. data/public/images/image_link.png +0 -0
  944. data/public/images/images.png +0 -0
  945. data/public/images/img_not_found.png +0 -0
  946. data/public/images/information.png +0 -0
  947. data/public/images/ipod.png +0 -0
  948. data/public/images/ipod_cast.png +0 -0
  949. data/public/images/ipod_cast_add.png +0 -0
  950. data/public/images/ipod_cast_delete.png +0 -0
  951. data/public/images/ipod_sound.png +0 -0
  952. data/public/images/joystick.png +0 -0
  953. data/public/images/joystick_add.png +0 -0
  954. data/public/images/joystick_delete.png +0 -0
  955. data/public/images/joystick_error.png +0 -0
  956. data/public/images/key.png +0 -0
  957. data/public/images/key_add.png +0 -0
  958. data/public/images/key_delete.png +0 -0
  959. data/public/images/key_go.png +0 -0
  960. data/public/images/keyboard.png +0 -0
  961. data/public/images/keyboard_add.png +0 -0
  962. data/public/images/keyboard_delete.png +0 -0
  963. data/public/images/keyboard_magnify.png +0 -0
  964. data/public/images/latex_error.png +0 -0
  965. data/public/images/layers.png +0 -0
  966. data/public/images/layout.png +0 -0
  967. data/public/images/layout_add.png +0 -0
  968. data/public/images/layout_content.png +0 -0
  969. data/public/images/layout_delete.png +0 -0
  970. data/public/images/layout_edit.png +0 -0
  971. data/public/images/layout_error.png +0 -0
  972. data/public/images/layout_header.png +0 -0
  973. data/public/images/layout_link.png +0 -0
  974. data/public/images/layout_sidebar.png +0 -0
  975. data/public/images/lightbulb.png +0 -0
  976. data/public/images/lightbulb_add.png +0 -0
  977. data/public/images/lightbulb_delete.png +0 -0
  978. data/public/images/lightbulb_off.png +0 -0
  979. data/public/images/lightning.png +0 -0
  980. data/public/images/lightning_add.png +0 -0
  981. data/public/images/lightning_delete.png +0 -0
  982. data/public/images/lightning_go.png +0 -0
  983. data/public/images/link.png +0 -0
  984. data/public/images/link_add.png +0 -0
  985. data/public/images/link_break.png +0 -0
  986. data/public/images/link_delete.png +0 -0
  987. data/public/images/link_edit.png +0 -0
  988. data/public/images/link_error.png +0 -0
  989. data/public/images/link_go.png +0 -0
  990. data/public/images/lock.png +0 -0
  991. data/public/images/lock_add.png +0 -0
  992. data/public/images/lock_break.png +0 -0
  993. data/public/images/lock_delete.png +0 -0
  994. data/public/images/lock_edit.png +0 -0
  995. data/public/images/lock_go.png +0 -0
  996. data/public/images/lock_open.png +0 -0
  997. data/public/images/lorry.png +0 -0
  998. data/public/images/lorry_add.png +0 -0
  999. data/public/images/lorry_delete.png +0 -0
  1000. data/public/images/lorry_error.png +0 -0
  1001. data/public/images/lorry_flatbed.png +0 -0
  1002. data/public/images/lorry_go.png +0 -0
  1003. data/public/images/lorry_link.png +0 -0
  1004. data/public/images/magifier_zoom_out.png +0 -0
  1005. data/public/images/magnifier.png +0 -0
  1006. data/public/images/magnifier_zoom_in.png +0 -0
  1007. data/public/images/male.png +0 -0
  1008. data/public/images/map.png +0 -0
  1009. data/public/images/map_add.png +0 -0
  1010. data/public/images/map_delete.png +0 -0
  1011. data/public/images/map_edit.png +0 -0
  1012. data/public/images/map_go.png +0 -0
  1013. data/public/images/map_magnify.png +0 -0
  1014. data/public/images/medal_bronze_1.png +0 -0
  1015. data/public/images/medal_bronze_2.png +0 -0
  1016. data/public/images/medal_bronze_3.png +0 -0
  1017. data/public/images/medal_bronze_add.png +0 -0
  1018. data/public/images/medal_bronze_delete.png +0 -0
  1019. data/public/images/medal_gold_1.png +0 -0
  1020. data/public/images/medal_gold_2.png +0 -0
  1021. data/public/images/medal_gold_3.png +0 -0
  1022. data/public/images/medal_gold_add.png +0 -0
  1023. data/public/images/medal_gold_delete.png +0 -0
  1024. data/public/images/medal_silver_1.png +0 -0
  1025. data/public/images/medal_silver_2.png +0 -0
  1026. data/public/images/medal_silver_3.png +0 -0
  1027. data/public/images/medal_silver_add.png +0 -0
  1028. data/public/images/medal_silver_delete.png +0 -0
  1029. data/public/images/money.png +0 -0
  1030. data/public/images/money_add.png +0 -0
  1031. data/public/images/money_delete.png +0 -0
  1032. data/public/images/money_dollar.png +0 -0
  1033. data/public/images/money_euro.png +0 -0
  1034. data/public/images/money_pound.png +0 -0
  1035. data/public/images/money_yen.png +0 -0
  1036. data/public/images/monitor.png +0 -0
  1037. data/public/images/monitor_add.png +0 -0
  1038. data/public/images/monitor_delete.png +0 -0
  1039. data/public/images/monitor_edit.png +0 -0
  1040. data/public/images/monitor_error.png +0 -0
  1041. data/public/images/monitor_go.png +0 -0
  1042. data/public/images/monitor_lightning.png +0 -0
  1043. data/public/images/monitor_link.png +0 -0
  1044. data/public/images/mouse.png +0 -0
  1045. data/public/images/mouse_add.png +0 -0
  1046. data/public/images/mouse_delete.png +0 -0
  1047. data/public/images/mouse_error.png +0 -0
  1048. data/public/images/music.png +0 -0
  1049. data/public/images/new.png +0 -0
  1050. data/public/images/newspaper.png +0 -0
  1051. data/public/images/newspaper_add.png +0 -0
  1052. data/public/images/newspaper_delete.png +0 -0
  1053. data/public/images/newspaper_go.png +0 -0
  1054. data/public/images/newspaper_link.png +0 -0
  1055. data/public/images/note.png +0 -0
  1056. data/public/images/note_add.png +0 -0
  1057. data/public/images/note_delete.png +0 -0
  1058. data/public/images/note_edit.png +0 -0
  1059. data/public/images/note_error.png +0 -0
  1060. data/public/images/note_go.png +0 -0
  1061. data/public/images/overlays.png +0 -0
  1062. data/public/images/package.png +0 -0
  1063. data/public/images/package_add.png +0 -0
  1064. data/public/images/package_delete.png +0 -0
  1065. data/public/images/package_go.png +0 -0
  1066. data/public/images/package_green.png +0 -0
  1067. data/public/images/package_link.png +0 -0
  1068. data/public/images/page.png +0 -0
  1069. data/public/images/page_add.png +0 -0
  1070. data/public/images/page_attach.png +0 -0
  1071. data/public/images/page_code.png +0 -0
  1072. data/public/images/page_copy.png +0 -0
  1073. data/public/images/page_delete.png +0 -0
  1074. data/public/images/page_edit.png +0 -0
  1075. data/public/images/page_error.png +0 -0
  1076. data/public/images/page_excel.png +0 -0
  1077. data/public/images/page_find.png +0 -0
  1078. data/public/images/page_gear.png +0 -0
  1079. data/public/images/page_go.png +0 -0
  1080. data/public/images/page_green.png +0 -0
  1081. data/public/images/page_key.png +0 -0
  1082. data/public/images/page_lightning.png +0 -0
  1083. data/public/images/page_link.png +0 -0
  1084. data/public/images/page_paintbrush.png +0 -0
  1085. data/public/images/page_paste.png +0 -0
  1086. data/public/images/page_red.png +0 -0
  1087. data/public/images/page_refresh.png +0 -0
  1088. data/public/images/page_save.png +0 -0
  1089. data/public/images/page_white.png +0 -0
  1090. data/public/images/page_white_acrobat.png +0 -0
  1091. data/public/images/page_white_actionscript.png +0 -0
  1092. data/public/images/page_white_add.png +0 -0
  1093. data/public/images/page_white_c.png +0 -0
  1094. data/public/images/page_white_camera.png +0 -0
  1095. data/public/images/page_white_cd.png +0 -0
  1096. data/public/images/page_white_code.png +0 -0
  1097. data/public/images/page_white_code_red.png +0 -0
  1098. data/public/images/page_white_coldfusion.png +0 -0
  1099. data/public/images/page_white_compressed.png +0 -0
  1100. data/public/images/page_white_copy.png +0 -0
  1101. data/public/images/page_white_cplusplus.png +0 -0
  1102. data/public/images/page_white_csharp.png +0 -0
  1103. data/public/images/page_white_cup.png +0 -0
  1104. data/public/images/page_white_database.png +0 -0
  1105. data/public/images/page_white_delete.png +0 -0
  1106. data/public/images/page_white_dvd.png +0 -0
  1107. data/public/images/page_white_edit.png +0 -0
  1108. data/public/images/page_white_error.png +0 -0
  1109. data/public/images/page_white_excel.png +0 -0
  1110. data/public/images/page_white_find.png +0 -0
  1111. data/public/images/page_white_flash.png +0 -0
  1112. data/public/images/page_white_freehand.png +0 -0
  1113. data/public/images/page_white_gear.png +0 -0
  1114. data/public/images/page_white_get.png +0 -0
  1115. data/public/images/page_white_go.png +0 -0
  1116. data/public/images/page_white_h.png +0 -0
  1117. data/public/images/page_white_horizontal.png +0 -0
  1118. data/public/images/page_white_key.png +0 -0
  1119. data/public/images/page_white_lightning.png +0 -0
  1120. data/public/images/page_white_link.png +0 -0
  1121. data/public/images/page_white_magnify.png +0 -0
  1122. data/public/images/page_white_medal.png +0 -0
  1123. data/public/images/page_white_office.png +0 -0
  1124. data/public/images/page_white_paint.png +0 -0
  1125. data/public/images/page_white_paintbrush.png +0 -0
  1126. data/public/images/page_white_paste.png +0 -0
  1127. data/public/images/page_white_php.png +0 -0
  1128. data/public/images/page_white_picture.png +0 -0
  1129. data/public/images/page_white_powerpoint.png +0 -0
  1130. data/public/images/page_white_put.png +0 -0
  1131. data/public/images/page_white_ruby.png +0 -0
  1132. data/public/images/page_white_stack.png +0 -0
  1133. data/public/images/page_white_star.png +0 -0
  1134. data/public/images/page_white_swoosh.png +0 -0
  1135. data/public/images/page_white_text.png +0 -0
  1136. data/public/images/page_white_text_width.png +0 -0
  1137. data/public/images/page_white_tux.png +0 -0
  1138. data/public/images/page_white_vector.png +0 -0
  1139. data/public/images/page_white_visualstudio.png +0 -0
  1140. data/public/images/page_white_width.png +0 -0
  1141. data/public/images/page_white_word.png +0 -0
  1142. data/public/images/page_white_world.png +0 -0
  1143. data/public/images/page_white_wrench.png +0 -0
  1144. data/public/images/page_white_zip.png +0 -0
  1145. data/public/images/page_word.png +0 -0
  1146. data/public/images/page_world.png +0 -0
  1147. data/public/images/paintbrush.png +0 -0
  1148. data/public/images/paintcan.png +0 -0
  1149. data/public/images/palette.png +0 -0
  1150. data/public/images/paste_plain.png +0 -0
  1151. data/public/images/paste_word.png +0 -0
  1152. data/public/images/pencil.png +0 -0
  1153. data/public/images/pencil_add.png +0 -0
  1154. data/public/images/pencil_delete.png +0 -0
  1155. data/public/images/pencil_go.png +0 -0
  1156. data/public/images/phone.png +0 -0
  1157. data/public/images/phone_add.png +0 -0
  1158. data/public/images/phone_delete.png +0 -0
  1159. data/public/images/phone_sound.png +0 -0
  1160. data/public/images/photo.png +0 -0
  1161. data/public/images/photo_add.png +0 -0
  1162. data/public/images/photo_blue.png +0 -0
  1163. data/public/images/photo_delete.png +0 -0
  1164. data/public/images/photo_grey.png +0 -0
  1165. data/public/images/photo_link.png +0 -0
  1166. data/public/images/photos.png +0 -0
  1167. data/public/images/picture.png +0 -0
  1168. data/public/images/picture_add.png +0 -0
  1169. data/public/images/picture_delete.png +0 -0
  1170. data/public/images/picture_edit.png +0 -0
  1171. data/public/images/picture_empty.png +0 -0
  1172. data/public/images/picture_error.png +0 -0
  1173. data/public/images/picture_go.png +0 -0
  1174. data/public/images/picture_key.png +0 -0
  1175. data/public/images/picture_link.png +0 -0
  1176. data/public/images/picture_save.png +0 -0
  1177. data/public/images/pictures.png +0 -0
  1178. data/public/images/pilcrow.png +0 -0
  1179. data/public/images/pill.png +0 -0
  1180. data/public/images/pill_add.png +0 -0
  1181. data/public/images/pill_delete.png +0 -0
  1182. data/public/images/pill_go.png +0 -0
  1183. data/public/images/plugin.png +0 -0
  1184. data/public/images/plugin_add.png +0 -0
  1185. data/public/images/plugin_delete.png +0 -0
  1186. data/public/images/plugin_disabled.png +0 -0
  1187. data/public/images/plugin_edit.png +0 -0
  1188. data/public/images/plugin_error.png +0 -0
  1189. data/public/images/plugin_go.png +0 -0
  1190. data/public/images/plugin_link.png +0 -0
  1191. data/public/images/printer.png +0 -0
  1192. data/public/images/printer_add.png +0 -0
  1193. data/public/images/printer_delete.png +0 -0
  1194. data/public/images/printer_empty.png +0 -0
  1195. data/public/images/printer_error.png +0 -0
  1196. data/public/images/project.png +0 -0
  1197. data/public/images/rails.png +0 -0
  1198. data/public/images/rainbow.png +0 -0
  1199. data/public/images/report.png +0 -0
  1200. data/public/images/report_add.png +0 -0
  1201. data/public/images/report_delete.png +0 -0
  1202. data/public/images/report_disk.png +0 -0
  1203. data/public/images/report_edit.png +0 -0
  1204. data/public/images/report_go.png +0 -0
  1205. data/public/images/report_key.png +0 -0
  1206. data/public/images/report_link.png +0 -0
  1207. data/public/images/report_magnify.png +0 -0
  1208. data/public/images/report_picture.png +0 -0
  1209. data/public/images/report_user.png +0 -0
  1210. data/public/images/report_word.png +0 -0
  1211. data/public/images/resultset_first.png +0 -0
  1212. data/public/images/resultset_last.png +0 -0
  1213. data/public/images/resultset_next.png +0 -0
  1214. data/public/images/resultset_previous.png +0 -0
  1215. data/public/images/rosette.png +0 -0
  1216. data/public/images/row_add.png +0 -0
  1217. data/public/images/row_delete.png +0 -0
  1218. data/public/images/rss.png +0 -0
  1219. data/public/images/rss_add.png +0 -0
  1220. data/public/images/rss_delete.png +0 -0
  1221. data/public/images/rss_go.png +0 -0
  1222. data/public/images/rss_valid.png +0 -0
  1223. data/public/images/ruby.png +0 -0
  1224. data/public/images/ruby_add.png +0 -0
  1225. data/public/images/ruby_delete.png +0 -0
  1226. data/public/images/ruby_gear.png +0 -0
  1227. data/public/images/ruby_get.png +0 -0
  1228. data/public/images/ruby_go.png +0 -0
  1229. data/public/images/ruby_key.png +0 -0
  1230. data/public/images/ruby_link.png +0 -0
  1231. data/public/images/ruby_put.png +0 -0
  1232. data/public/images/script.png +0 -0
  1233. data/public/images/script_add.png +0 -0
  1234. data/public/images/script_code.png +0 -0
  1235. data/public/images/script_code_red.png +0 -0
  1236. data/public/images/script_delete.png +0 -0
  1237. data/public/images/script_edit.png +0 -0
  1238. data/public/images/script_error.png +0 -0
  1239. data/public/images/script_gear.png +0 -0
  1240. data/public/images/script_go.png +0 -0
  1241. data/public/images/script_key.png +0 -0
  1242. data/public/images/script_lightning.png +0 -0
  1243. data/public/images/script_link.png +0 -0
  1244. data/public/images/script_palette.png +0 -0
  1245. data/public/images/script_save.png +0 -0
  1246. data/public/images/server.png +0 -0
  1247. data/public/images/server_add.png +0 -0
  1248. data/public/images/server_chart.png +0 -0
  1249. data/public/images/server_compressed.png +0 -0
  1250. data/public/images/server_connect.png +0 -0
  1251. data/public/images/server_database.png +0 -0
  1252. data/public/images/server_delete.png +0 -0
  1253. data/public/images/server_edit.png +0 -0
  1254. data/public/images/server_error.png +0 -0
  1255. data/public/images/server_go.png +0 -0
  1256. data/public/images/server_key.png +0 -0
  1257. data/public/images/server_lightning.png +0 -0
  1258. data/public/images/server_link.png +0 -0
  1259. data/public/images/server_uncompressed.png +0 -0
  1260. data/public/images/shading.png +0 -0
  1261. data/public/images/shape_align_bottom.png +0 -0
  1262. data/public/images/shape_align_center.png +0 -0
  1263. data/public/images/shape_align_left.png +0 -0
  1264. data/public/images/shape_align_middle.png +0 -0
  1265. data/public/images/shape_align_right.png +0 -0
  1266. data/public/images/shape_align_top.png +0 -0
  1267. data/public/images/shape_flip_horizontal.png +0 -0
  1268. data/public/images/shape_flip_vertical.png +0 -0
  1269. data/public/images/shape_group.png +0 -0
  1270. data/public/images/shape_handles.png +0 -0
  1271. data/public/images/shape_move_back.png +0 -0
  1272. data/public/images/shape_move_backwards.png +0 -0
  1273. data/public/images/shape_move_forwards.png +0 -0
  1274. data/public/images/shape_move_front.png +0 -0
  1275. data/public/images/shape_rotate_anticlockwise.png +0 -0
  1276. data/public/images/shape_rotate_clockwise.png +0 -0
  1277. data/public/images/shape_square.png +0 -0
  1278. data/public/images/shape_square_add.png +0 -0
  1279. data/public/images/shape_square_delete.png +0 -0
  1280. data/public/images/shape_square_edit.png +0 -0
  1281. data/public/images/shape_square_error.png +0 -0
  1282. data/public/images/shape_square_go.png +0 -0
  1283. data/public/images/shape_square_key.png +0 -0
  1284. data/public/images/shape_square_link.png +0 -0
  1285. data/public/images/shape_ungroup.png +0 -0
  1286. data/public/images/shield.png +0 -0
  1287. data/public/images/shield_add.png +0 -0
  1288. data/public/images/shield_delete.png +0 -0
  1289. data/public/images/shield_go.png +0 -0
  1290. data/public/images/silk.html +1495 -0
  1291. data/public/images/sitemap.png +0 -0
  1292. data/public/images/sitemap_color.png +0 -0
  1293. data/public/images/sound.png +0 -0
  1294. data/public/images/sound_add.png +0 -0
  1295. data/public/images/sound_delete.png +0 -0
  1296. data/public/images/sound_low.png +0 -0
  1297. data/public/images/sound_mute.png +0 -0
  1298. data/public/images/sound_none.png +0 -0
  1299. data/public/images/spellcheck.png +0 -0
  1300. data/public/images/sport_8ball.png +0 -0
  1301. data/public/images/sport_basketball.png +0 -0
  1302. data/public/images/sport_football.png +0 -0
  1303. data/public/images/sport_golf.png +0 -0
  1304. data/public/images/sport_raquet.png +0 -0
  1305. data/public/images/sport_shuttlecock.png +0 -0
  1306. data/public/images/sport_soccer.png +0 -0
  1307. data/public/images/sport_tennis.png +0 -0
  1308. data/public/images/star.png +0 -0
  1309. data/public/images/status_away.png +0 -0
  1310. data/public/images/status_busy.png +0 -0
  1311. data/public/images/status_offline.png +0 -0
  1312. data/public/images/status_online.png +0 -0
  1313. data/public/images/stop.png +0 -0
  1314. data/public/images/style.png +0 -0
  1315. data/public/images/style_add.png +0 -0
  1316. data/public/images/style_delete.png +0 -0
  1317. data/public/images/style_edit.png +0 -0
  1318. data/public/images/style_go.png +0 -0
  1319. data/public/images/sum.png +0 -0
  1320. data/public/images/swf/xspf/Main.as +55 -0
  1321. data/public/images/swf/xspf/com/zuardi/musicplayer/MusicButton.as +557 -0
  1322. data/public/images/swf/xspf/com/zuardi/musicplayer/MusicPlayer.as +118 -0
  1323. data/public/images/swf/xspf/com/zuardi/musicplayer/Playlist.as +127 -0
  1324. data/public/images/swf/xspf/com/zuardi/musicplayer/ProgressiveSlider.as +66 -0
  1325. data/public/images/swf/xspf/license.txt +10 -0
  1326. data/public/images/swf/xspf/musicplayer.swf +0 -0
  1327. data/public/images/swf/xspf/musicplayer_f6.swf +0 -0
  1328. data/public/images/swf/xspf/musicplayerbtn.fla +0 -0
  1329. data/public/images/swf/xspf/parameters.as +39 -0
  1330. data/public/images/tab.png +0 -0
  1331. data/public/images/tab_add.png +0 -0
  1332. data/public/images/tab_delete.png +0 -0
  1333. data/public/images/tab_edit.png +0 -0
  1334. data/public/images/tab_go.png +0 -0
  1335. data/public/images/table.png +0 -0
  1336. data/public/images/table_add.png +0 -0
  1337. data/public/images/table_delete.png +0 -0
  1338. data/public/images/table_edit.png +0 -0
  1339. data/public/images/table_error.png +0 -0
  1340. data/public/images/table_gear.png +0 -0
  1341. data/public/images/table_go.png +0 -0
  1342. data/public/images/table_key.png +0 -0
  1343. data/public/images/table_lightning.png +0 -0
  1344. data/public/images/table_link.png +0 -0
  1345. data/public/images/table_multiple.png +0 -0
  1346. data/public/images/table_refresh.png +0 -0
  1347. data/public/images/table_relationship.png +0 -0
  1348. data/public/images/table_row_delete.png +0 -0
  1349. data/public/images/table_row_insert.png +0 -0
  1350. data/public/images/table_save.png +0 -0
  1351. data/public/images/table_sort.png +0 -0
  1352. data/public/images/tag.png +0 -0
  1353. data/public/images/tag_blue.png +0 -0
  1354. data/public/images/tag_blue_add.png +0 -0
  1355. data/public/images/tag_blue_delete.png +0 -0
  1356. data/public/images/tag_blue_edit.png +0 -0
  1357. data/public/images/tag_green.png +0 -0
  1358. data/public/images/tag_orange.png +0 -0
  1359. data/public/images/tag_pink.png +0 -0
  1360. data/public/images/tag_purple.png +0 -0
  1361. data/public/images/tag_red.png +0 -0
  1362. data/public/images/tag_yellow.png +0 -0
  1363. data/public/images/target.png +0 -0
  1364. data/public/images/telephone.png +0 -0
  1365. data/public/images/telephone_add.png +0 -0
  1366. data/public/images/telephone_delete.png +0 -0
  1367. data/public/images/telephone_edit.png +0 -0
  1368. data/public/images/telephone_error.png +0 -0
  1369. data/public/images/telephone_go.png +0 -0
  1370. data/public/images/telephone_key.png +0 -0
  1371. data/public/images/telephone_link.png +0 -0
  1372. data/public/images/television.png +0 -0
  1373. data/public/images/television_add.png +0 -0
  1374. data/public/images/television_delete.png +0 -0
  1375. data/public/images/text_align_center.png +0 -0
  1376. data/public/images/text_align_justify.png +0 -0
  1377. data/public/images/text_align_left.png +0 -0
  1378. data/public/images/text_align_right.png +0 -0
  1379. data/public/images/text_allcaps.png +0 -0
  1380. data/public/images/text_bold.png +0 -0
  1381. data/public/images/text_columns.png +0 -0
  1382. data/public/images/text_dropcaps.png +0 -0
  1383. data/public/images/text_heading_1.png +0 -0
  1384. data/public/images/text_heading_2.png +0 -0
  1385. data/public/images/text_heading_3.png +0 -0
  1386. data/public/images/text_heading_4.png +0 -0
  1387. data/public/images/text_heading_5.png +0 -0
  1388. data/public/images/text_heading_6.png +0 -0
  1389. data/public/images/text_horizontalrule.png +0 -0
  1390. data/public/images/text_indent.png +0 -0
  1391. data/public/images/text_indent_remove.png +0 -0
  1392. data/public/images/text_italic.png +0 -0
  1393. data/public/images/text_kerning.png +0 -0
  1394. data/public/images/text_letter_omega.png +0 -0
  1395. data/public/images/text_letterspacing.png +0 -0
  1396. data/public/images/text_linespacing.png +0 -0
  1397. data/public/images/text_list_bullets.png +0 -0
  1398. data/public/images/text_list_numbers.png +0 -0
  1399. data/public/images/text_lowercase.png +0 -0
  1400. data/public/images/text_padding_bottom.png +0 -0
  1401. data/public/images/text_padding_left.png +0 -0
  1402. data/public/images/text_padding_right.png +0 -0
  1403. data/public/images/text_padding_top.png +0 -0
  1404. data/public/images/text_replace.png +0 -0
  1405. data/public/images/text_signature.png +0 -0
  1406. data/public/images/text_smallcaps.png +0 -0
  1407. data/public/images/text_strikethrough.png +0 -0
  1408. data/public/images/text_subscript.png +0 -0
  1409. data/public/images/text_superscript.png +0 -0
  1410. data/public/images/text_underline.png +0 -0
  1411. data/public/images/text_uppercase.png +0 -0
  1412. data/public/images/textfield.png +0 -0
  1413. data/public/images/textfield_add.png +0 -0
  1414. data/public/images/textfield_delete.png +0 -0
  1415. data/public/images/textfield_key.png +0 -0
  1416. data/public/images/textfield_rename.png +0 -0
  1417. data/public/images/thumb_down.png +0 -0
  1418. data/public/images/thumb_up.png +0 -0
  1419. data/public/images/tick.png +0 -0
  1420. data/public/images/time.png +0 -0
  1421. data/public/images/time_add.png +0 -0
  1422. data/public/images/time_delete.png +0 -0
  1423. data/public/images/time_go.png +0 -0
  1424. data/public/images/timeline_marker.png +0 -0
  1425. data/public/images/transmit.png +0 -0
  1426. data/public/images/transmit_add.png +0 -0
  1427. data/public/images/transmit_blue.png +0 -0
  1428. data/public/images/transmit_delete.png +0 -0
  1429. data/public/images/transmit_edit.png +0 -0
  1430. data/public/images/transmit_error.png +0 -0
  1431. data/public/images/transmit_go.png +0 -0
  1432. data/public/images/tux.png +0 -0
  1433. data/public/images/user.png +0 -0
  1434. data/public/images/user_add.png +0 -0
  1435. data/public/images/user_admin.png +0 -0
  1436. data/public/images/user_comment.png +0 -0
  1437. data/public/images/user_delete.png +0 -0
  1438. data/public/images/user_edit.png +0 -0
  1439. data/public/images/user_female.png +0 -0
  1440. data/public/images/user_go.png +0 -0
  1441. data/public/images/user_gray.png +0 -0
  1442. data/public/images/user_green.png +0 -0
  1443. data/public/images/user_orange.png +0 -0
  1444. data/public/images/user_pub.png +0 -0
  1445. data/public/images/user_red.png +0 -0
  1446. data/public/images/user_su.png +0 -0
  1447. data/public/images/user_suit.png +0 -0
  1448. data/public/images/validate.png +0 -0
  1449. data/public/images/valider.png +0 -0
  1450. data/public/images/vcard.png +0 -0
  1451. data/public/images/vcard_add.png +0 -0
  1452. data/public/images/vcard_delete.png +0 -0
  1453. data/public/images/vcard_edit.png +0 -0
  1454. data/public/images/vector.png +0 -0
  1455. data/public/images/vector_add.png +0 -0
  1456. data/public/images/vector_delete.png +0 -0
  1457. data/public/images/wand.png +0 -0
  1458. data/public/images/weather_clouds.png +0 -0
  1459. data/public/images/weather_cloudy.png +0 -0
  1460. data/public/images/weather_lightning.png +0 -0
  1461. data/public/images/weather_rain.png +0 -0
  1462. data/public/images/weather_snow.png +0 -0
  1463. data/public/images/weather_sun.png +0 -0
  1464. data/public/images/webcam.png +0 -0
  1465. data/public/images/webcam_add.png +0 -0
  1466. data/public/images/webcam_delete.png +0 -0
  1467. data/public/images/webcam_error.png +0 -0
  1468. data/public/images/world.png +0 -0
  1469. data/public/images/world_add.png +0 -0
  1470. data/public/images/world_delete.png +0 -0
  1471. data/public/images/world_edit.png +0 -0
  1472. data/public/images/world_go.png +0 -0
  1473. data/public/images/world_link.png +0 -0
  1474. data/public/images/wrench.png +0 -0
  1475. data/public/images/wrench_orange.png +0 -0
  1476. data/public/images/xhtml.png +0 -0
  1477. data/public/images/xhtml_add.png +0 -0
  1478. data/public/images/xhtml_delete.png +0 -0
  1479. data/public/images/xhtml_go.png +0 -0
  1480. data/public/images/xhtml_valid.png +0 -0
  1481. data/public/images/zoom.png +0 -0
  1482. data/public/images/zoom_in.png +0 -0
  1483. data/public/images/zoom_out.png +0 -0
  1484. data/public/img/logo.png +0 -0
  1485. data/public/javascripts/application.js +2 -0
  1486. data/public/javascripts/builder.js +136 -0
  1487. data/public/javascripts/controls.js +965 -0
  1488. data/public/javascripts/dragdrop.js +975 -0
  1489. data/public/javascripts/effects.js +1130 -0
  1490. data/public/javascripts/prototype.js +4320 -0
  1491. data/public/javascripts/scriptaculous.js +60 -0
  1492. data/public/javascripts/slider.js +275 -0
  1493. data/public/javascripts/sound.js +55 -0
  1494. data/public/javascripts/tablekit.js +965 -0
  1495. data/public/javascripts/unittest.js +568 -0
  1496. data/public/javascripts/upload-progress.js +133 -0
  1497. data/public/javascripts/zena.js +501 -0
  1498. data/public/robots.txt +1 -0
  1499. data/public/stylesheets/admin.css +77 -0
  1500. data/public/stylesheets/calendar.css +55 -0
  1501. data/public/stylesheets/code.css +67 -0
  1502. data/public/stylesheets/comment.css +43 -0
  1503. data/public/stylesheets/csshover2.htc +121 -0
  1504. data/public/stylesheets/default.css +85 -0
  1505. data/public/stylesheets/popup.css +200 -0
  1506. data/public/stylesheets/reset.css +33 -0
  1507. data/public/stylesheets/search.css +19 -0
  1508. data/public/stylesheets/upload-progress.css +4 -0
  1509. data/public/stylesheets/wiki.css +100 -0
  1510. data/public/stylesheets/zen.css +230 -0
  1511. data/public/stylesheets/zena.css +78 -0
  1512. data/rails/init.rb +2 -0
  1513. data/tasks/ann.rake +80 -0
  1514. data/tasks/bones.rake +20 -0
  1515. data/tasks/gem.rake +201 -0
  1516. data/tasks/git.rake +40 -0
  1517. data/tasks/notes.rake +27 -0
  1518. data/tasks/post_load.rake +34 -0
  1519. data/tasks/rdoc.rake +51 -0
  1520. data/tasks/rubyforge.rake +55 -0
  1521. data/tasks/setup.rb +292 -0
  1522. data/tasks/spec.rake +54 -0
  1523. data/tasks/svn.rake +47 -0
  1524. data/tasks/test.rake +40 -0
  1525. data/tasks/zentest.rake +36 -0
  1526. data/test/fixtures/comments.yml +126 -0
  1527. data/test/fixtures/contact_contents.yml +132 -0
  1528. data/test/fixtures/data_entries.yml +65 -0
  1529. data/test/fixtures/discussions.yml +48 -0
  1530. data/test/fixtures/document_contents.yml +108 -0
  1531. data/test/fixtures/dyn_attributes.yml +66 -0
  1532. data/test/fixtures/files/Node-test.zafu +307 -0
  1533. data/test/fixtures/files/bird.jpg +0 -0
  1534. data/test/fixtures/files/bomb.png +0 -0
  1535. data/test/fixtures/files/exif_sample.jpg +0 -0
  1536. data/test/fixtures/files/flower.jpg +0 -0
  1537. data/test/fixtures/files/forest.pdf +0 -0
  1538. data/test/fixtures/files/import.tgz +0 -0
  1539. data/test/fixtures/files/jet_30.zip +0 -0
  1540. data/test/fixtures/files/lake.jpg +0 -0
  1541. data/test/fixtures/files/letter.zip +0 -0
  1542. data/test/fixtures/files/other.txt +1 -0
  1543. data/test/fixtures/files/simple.zml.gz +0 -0
  1544. data/test/fixtures/files/some.txt +1 -0
  1545. data/test/fixtures/files/translations_de.yml +4 -0
  1546. data/test/fixtures/files/translations_fr.yml +4 -0
  1547. data/test/fixtures/files/tree.jpg +0 -0
  1548. data/test/fixtures/files/water.pdf +0 -0
  1549. data/test/fixtures/groups.yml +86 -0
  1550. data/test/fixtures/groups_users.yml +81 -0
  1551. data/test/fixtures/iformats.yml +28 -0
  1552. data/test/fixtures/import/photos.en.zml +11 -0
  1553. data/test/fixtures/import/photos/bird.jpg +0 -0
  1554. data/test/fixtures/import/photos/bird.jpg.en.zml +1 -0
  1555. data/test/fixtures/import/photos/bird.jpg.fr.zml +1 -0
  1556. data/test/fixtures/import/photos/document.yml.en +4 -0
  1557. data/test/fixtures/import/photos/document.yml.fr +4 -0
  1558. data/test/fixtures/import/simple.zml +8 -0
  1559. data/test/fixtures/links.yml +313 -0
  1560. data/test/fixtures/nodes.yml +2592 -0
  1561. data/test/fixtures/relations.yml +126 -0
  1562. data/test/fixtures/sites.yml +58 -0
  1563. data/test/fixtures/template_contents.yml +172 -0
  1564. data/test/fixtures/users.yml +167 -0
  1565. data/test/fixtures/versions.yml +1909 -0
  1566. data/test/fixtures/virtual_classes.yml +87 -0
  1567. data/test/fixtures/zips.yml +15 -0
  1568. data/test/functional/application_controller_test.rb +136 -0
  1569. data/test/functional/calendar_controller_test.rb +25 -0
  1570. data/test/functional/comments_controller_test.rb +59 -0
  1571. data/test/functional/data_entries_controller_test.rb +22 -0
  1572. data/test/functional/discussion_controller_test.rb +20 -0
  1573. data/test/functional/documents_controller_test.rb +155 -0
  1574. data/test/functional/groups_controller_test.rb +109 -0
  1575. data/test/functional/iformats_controller_test.rb +115 -0
  1576. data/test/functional/links_controller_test.rb +26 -0
  1577. data/test/functional/nodes_controller_test.rb +448 -0
  1578. data/test/functional/note_controller_test.rb +53 -0
  1579. data/test/functional/preferences_controller_test.rb +11 -0
  1580. data/test/functional/relations_controller_test.rb +55 -0
  1581. data/test/functional/search_controller_test.rb +41 -0
  1582. data/test/functional/sessions_controller_test.rb +73 -0
  1583. data/test/functional/sites_controller_test.rb +102 -0
  1584. data/test/functional/users_controller_test.rb +28 -0
  1585. data/test/functional/versions_controller_test.rb +99 -0
  1586. data/test/functional/virtual_classes_controller_test.rb +62 -0
  1587. data/test/helpers/node_query/basic.yml +149 -0
  1588. data/test/helpers/node_query/comments.yml +18 -0
  1589. data/test/helpers/node_query/complex.yml +98 -0
  1590. data/test/helpers/node_query/filters.yml +105 -0
  1591. data/test/helpers/node_query/relations.yml +43 -0
  1592. data/test/helpers/node_query_test.rb +117 -0
  1593. data/test/integration/multiple_hosts_test.rb +98 -0
  1594. data/test/integration/multiversion_test.rb +164 -0
  1595. data/test/integration/navigation_test.rb +283 -0
  1596. data/test/sites/README +2 -0
  1597. data/test/sites/complex/complex.png +0 -0
  1598. data/test/sites/complex/groups.yml +9 -0
  1599. data/test/sites/complex/links.yml +75 -0
  1600. data/test/sites/complex/nodes.yml +253 -0
  1601. data/test/sites/complex/relations.yml +11 -0
  1602. data/test/sites/complex/sites.yml +13 -0
  1603. data/test/sites/complex/users.yml +20 -0
  1604. data/test/sites/complex/versions.yml +1 -0
  1605. data/test/sites/complex/virtual_classes.yml +19 -0
  1606. data/test/sites/ocean/contact_contents.yml +19 -0
  1607. data/test/sites/ocean/groups.yml +9 -0
  1608. data/test/sites/ocean/iformats.yml +7 -0
  1609. data/test/sites/ocean/nodes.yml +36 -0
  1610. data/test/sites/ocean/sites.yml +14 -0
  1611. data/test/sites/ocean/template_contents.yml +8 -0
  1612. data/test/sites/ocean/users.yml +33 -0
  1613. data/test/sites/ocean/versions.yml +48 -0
  1614. data/test/sites/zena/comments.yml +82 -0
  1615. data/test/sites/zena/contact_contents.yml +55 -0
  1616. data/test/sites/zena/data_entries.yml +42 -0
  1617. data/test/sites/zena/discussions.yml +30 -0
  1618. data/test/sites/zena/document_contents.yml +76 -0
  1619. data/test/sites/zena/dyn_attributes.yml +43 -0
  1620. data/test/sites/zena/groups.yml +11 -0
  1621. data/test/sites/zena/iformats.yml +6 -0
  1622. data/test/sites/zena/links.yml +49 -0
  1623. data/test/sites/zena/nodes.yml +338 -0
  1624. data/test/sites/zena/relations.yml +66 -0
  1625. data/test/sites/zena/sites.yml +13 -0
  1626. data/test/sites/zena/template_contents.yml +103 -0
  1627. data/test/sites/zena/users.yml +48 -0
  1628. data/test/sites/zena/versions.yml +558 -0
  1629. data/test/sites/zena/virtual_classes.yml +23 -0
  1630. data/test/test_helper.rb +62 -0
  1631. data/test/test_zena.rb +138 -0
  1632. data/test/unit/cache_test.rb +62 -0
  1633. data/test/unit/cached_page_test.rb +165 -0
  1634. data/test/unit/comment_test.rb +185 -0
  1635. data/test/unit/contact_content_test.rb +33 -0
  1636. data/test/unit/contact_test.rb +39 -0
  1637. data/test/unit/contact_version_test.rb +48 -0
  1638. data/test/unit/core_ext_test.rb +48 -0
  1639. data/test/unit/data_entry_test.rb +92 -0
  1640. data/test/unit/discussion_test.rb +59 -0
  1641. data/test/unit/document_content_test.rb +110 -0
  1642. data/test/unit/document_test.rb +234 -0
  1643. data/test/unit/document_version_test.rb +11 -0
  1644. data/test/unit/dyn_attributes_test.rb +229 -0
  1645. data/test/unit/exif_data_test.rb +66 -0
  1646. data/test/unit/group_test.rb +142 -0
  1647. data/test/unit/iformat_test.rb +190 -0
  1648. data/test/unit/image_builder_test.rb +214 -0
  1649. data/test/unit/image_content_test.rb +154 -0
  1650. data/test/unit/image_test.rb +311 -0
  1651. data/test/unit/image_version_test.rb +18 -0
  1652. data/test/unit/letter_test.rb +9 -0
  1653. data/test/unit/link_test.rb +32 -0
  1654. data/test/unit/multiversion_test.rb +1008 -0
  1655. data/test/unit/node_test.rb +1578 -0
  1656. data/test/unit/note_test.rb +85 -0
  1657. data/test/unit/page_test.rb +79 -0
  1658. data/test/unit/project_test.rb +32 -0
  1659. data/test/unit/reference_test.rb +9 -0
  1660. data/test/unit/relation_proxy_test.rb +409 -0
  1661. data/test/unit/relation_test.rb +48 -0
  1662. data/test/unit/section_test.rb +32 -0
  1663. data/test/unit/secure_test.rb +939 -0
  1664. data/test/unit/site_test.rb +236 -0
  1665. data/test/unit/skin_test.rb +18 -0
  1666. data/test/unit/template_content_test.rb +9 -0
  1667. data/test/unit/template_test.rb +357 -0
  1668. data/test/unit/template_version_test.rb +7 -0
  1669. data/test/unit/text_document_content_test.rb +8 -0
  1670. data/test/unit/text_document_test.rb +139 -0
  1671. data/test/unit/text_document_version_test.rb +37 -0
  1672. data/test/unit/user_test.rb +295 -0
  1673. data/test/unit/version_test.rb +312 -0
  1674. data/test/unit/virtual_class_test.rb +242 -0
  1675. data/test/unit/zena/db_test.rb +94 -0
  1676. data/test/unit/zena/parser/latex.yml +14 -0
  1677. data/test/unit/zena/parser/zafu.yml +391 -0
  1678. data/test/unit/zena/parser/zafu_asset.yml +43 -0
  1679. data/test/unit/zena/parser/zafu_insight.yml +3 -0
  1680. data/test/unit/zena/parser/zazen.yml +211 -0
  1681. data/test/unit/zena/parser_test.rb +184 -0
  1682. data/test/unit/zena/unit/test_case_test.rb +12 -0
  1683. data/test/unit/zena/use/calendar_test.rb +49 -0
  1684. data/test/unit/zena/use/dates_model_methods_test.rb +41 -0
  1685. data/test/unit/zena/use/dates_string_methods_test.rb +94 -0
  1686. data/test/unit/zena/use/dates_view_methods_test.rb +158 -0
  1687. data/test/unit/zena/use/html_tags_test.rb +244 -0
  1688. data/test/unit/zena/use/i18n_test.rb +34 -0
  1689. data/test/unit/zena/use/nested_attributes_alias_model_test.rb +104 -0
  1690. data/test/unit/zena/use/nested_attributes_alias_view_test.rb +99 -0
  1691. data/test/unit/zena/use/refactor_test.rb +52 -0
  1692. data/test/unit/zena/use/rendering_test.rb +23 -0
  1693. data/test/unit/zena/use/urls_test.rb +119 -0
  1694. data/test/unit/zena/use/zafu_test.rb +21 -0
  1695. data/test/unit/zena/use/zazen_test.rb +230 -0
  1696. data/test/unit/zena/zena_tags/ajax.yml +233 -0
  1697. data/test/unit/zena/zena_tags/apphelper.yml +85 -0
  1698. data/test/unit/zena/zena_tags/basic.yml +1295 -0
  1699. data/test/unit/zena/zena_tags/complex.yml +249 -0
  1700. data/test/unit/zena/zena_tags/data.yml +73 -0
  1701. data/test/unit/zena/zena_tags/errors.yml +71 -0
  1702. data/test/unit/zena/zena_tags/eval.yml +51 -0
  1703. data/test/unit/zena/zena_tags/relations.yml +411 -0
  1704. data/test/unit/zena/zena_tags/zazen.yml +84 -0
  1705. data/test/unit/zena/zena_tags_test.rb +235 -0
  1706. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/Run 2.tmCommand +24 -0
  1707. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/Run Context.tmCommand +58 -0
  1708. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/Run Focused Should.tmCommand +88 -0
  1709. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/Run.tmCommand +27 -0
  1710. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/YAML to Shoulda.tmCommand +23 -0
  1711. data/vendor/TextMate/Ruby Shoulda.tmbundle/Preferences/Symbol List: Context.tmPreferences +19 -0
  1712. data/vendor/TextMate/Ruby Shoulda.tmbundle/Preferences/Symbol List: Should.tmPreferences +19 -0
  1713. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory attributes for.tmSnippet +16 -0
  1714. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory build.tmSnippet +16 -0
  1715. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory.tmSnippet +16 -0
  1716. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory_define with class.tmSnippet +18 -0
  1717. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory_define.tmSnippet +18 -0
  1718. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory_next.tmSnippet +16 -0
  1719. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory_sequence.tmSnippet +18 -0
  1720. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_bad_value.tmSnippet +16 -0
  1721. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_contains.tmSnippet +16 -0
  1722. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_does_not_contain.tmSnippet +16 -0
  1723. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_good_value.tmSnippet +16 -0
  1724. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_same_elements.tmSnippet +16 -0
  1725. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_save.tmSnippet +16 -0
  1726. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_sent_email.tmSnippet +18 -0
  1727. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_valid.tmSnippet +16 -0
  1728. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/association.tmSnippet +17 -0
  1729. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/attribute.tmSnippet +17 -0
  1730. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/before_should block.tmSnippet +18 -0
  1731. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/context block get.tmSnippet +22 -0
  1732. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/context block post.tmSnippet +23 -0
  1733. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/context block with setup.tmSnippet +25 -0
  1734. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/setup.tmSnippet +18 -0
  1735. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should block with before proc.tmSnippet +18 -0
  1736. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should block.tmSnippet +18 -0
  1737. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_allow_values_for.tmSnippet +16 -0
  1738. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_assign_to.tmSnippet +16 -0
  1739. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_be_restful denied.tmSnippet +20 -0
  1740. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_be_restful.tmSnippet +20 -0
  1741. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_belong_to.tmSnippet +16 -0
  1742. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_change by.tmSnippet +16 -0
  1743. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_change from to.tmSnippet +16 -0
  1744. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_ensure_length_at_least.tmSnippet +16 -0
  1745. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_ensure_length_in_range.tmSnippet +16 -0
  1746. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_ensure_length_is.tmSnippet +16 -0
  1747. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_ensure_value_in_range.tmSnippet +16 -0
  1748. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_eventually.tmSnippet +18 -0
  1749. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_filter_params.tmSnippet +16 -0
  1750. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_and_belong_to_many.tmSnippet +16 -0
  1751. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_class_methods.tmSnippet +16 -0
  1752. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_db_column.tmSnippet +16 -0
  1753. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_db_columns.tmSnippet +16 -0
  1754. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_index.tmSnippet +16 -0
  1755. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_indices.tmSnippet +16 -0
  1756. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_instance_methods.tmSnippet +16 -0
  1757. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_many.tmSnippet +16 -0
  1758. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_named_scope.tmSnippet +16 -0
  1759. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_one.tmSnippet +16 -0
  1760. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_readonly_attributes.tmSnippet +16 -0
  1761. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_allow_mass_assignment_of.tmSnippet +16 -0
  1762. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_allow_values_for.tmSnippet +16 -0
  1763. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_assign_to.tmSnippet +16 -0
  1764. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_change.tmSnippet +16 -0
  1765. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_set_the_flash.tmSnippet +16 -0
  1766. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_redirect_to.tmSnippet +16 -0
  1767. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_render_a_form.tmSnippet +16 -0
  1768. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_render_template.tmSnippet +16 -0
  1769. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_render_with_layout.tmSnippet +16 -0
  1770. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_render_without_layout.tmSnippet +16 -0
  1771. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_respond_with.tmSnippet +16 -0
  1772. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_respond_with_content_type.tmSnippet +16 -0
  1773. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_return_from_session.tmSnippet +16 -0
  1774. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_route.tmSnippet +16 -0
  1775. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_set_the_flash_to.tmSnippet +16 -0
  1776. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_acceptance_of.tmSnippet +16 -0
  1777. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_numericality_of.tmSnippet +16 -0
  1778. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_presence_of.tmSnippet +16 -0
  1779. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_uniqueness_of with scope.tmSnippet +16 -0
  1780. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_uniqueness_of.tmSnippet +16 -0
  1781. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/catch_exception.rb +39 -0
  1782. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/run_script.rb +104 -0
  1783. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/stdin_dialog.rb +14 -0
  1784. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/test.rb +17 -0
  1785. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/todo.txt +13 -0
  1786. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/bin/yaml_to_shoulda.rb +25 -0
  1787. data/vendor/TextMate/Ruby Shoulda.tmbundle/Syntaxes/Ruby on Rails (Shoulda).tmLanguage +166 -0
  1788. data/vendor/TextMate/Ruby Shoulda.tmbundle/info.plist +304 -0
  1789. data/vendor/TextMate/Zena.tmbundle/Commands/Run all yaml tests.tmCommand +37 -0
  1790. data/vendor/TextMate/Zena.tmbundle/Commands/Run focused yaml test.tmCommand +53 -0
  1791. data/vendor/TextMate/Zena.tmbundle/Support/RubyMate/run_script.rb +74 -0
  1792. data/vendor/TextMate/Zena.tmbundle/info.plist +15 -0
  1793. data/vendor/apache_upload/mod_upload_progress.c +813 -0
  1794. data/vendor/bricks/20070122-172926.txt +282 -0
  1795. data/vendor/plugins/ar_mysql_full_text/README +15 -0
  1796. data/vendor/plugins/ar_mysql_full_text/init.rb +1 -0
  1797. data/vendor/plugins/ar_mysql_full_text/lib/ar_mysql_full_text.rb +69 -0
  1798. data/vendor/plugins/gettext_i18n_rails/README.markdown +139 -0
  1799. data/vendor/plugins/gettext_i18n_rails/Rakefile +6 -0
  1800. data/vendor/plugins/gettext_i18n_rails/init.rb +10 -0
  1801. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails.rb +21 -0
  1802. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/action_controller.rb +6 -0
  1803. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/active_record.rb +17 -0
  1804. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/backend.rb +47 -0
  1805. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/haml_parser.rb +43 -0
  1806. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/i18n_hacks.rb +10 -0
  1807. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/model_attributes_finder.rb +46 -0
  1808. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/ruby_gettext_extractor.rb +136 -0
  1809. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails/action_controller_spec.rb +36 -0
  1810. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails/active_record_spec.rb +50 -0
  1811. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails/backend_spec.rb +41 -0
  1812. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails_spec.rb +43 -0
  1813. data/vendor/plugins/gettext_i18n_rails/spec/spec_helper.rb +15 -0
  1814. data/vendor/plugins/gettext_i18n_rails/tasks/gettext_rails_i18n.rake +73 -0
  1815. data/vendor/plugins/responds_to_parent/MIT-LICENSE +20 -0
  1816. data/vendor/plugins/responds_to_parent/README +42 -0
  1817. data/vendor/plugins/responds_to_parent/Rakefile +22 -0
  1818. data/vendor/plugins/responds_to_parent/init.rb +1 -0
  1819. data/vendor/plugins/responds_to_parent/lib/responds_to_parent.rb +46 -0
  1820. data/vendor/plugins/responds_to_parent/test/responds_to_parent_test.rb +115 -0
  1821. data/vendor/plugins/rjs-assertions/MIT-LICENSE +20 -0
  1822. data/vendor/plugins/rjs-assertions/README +64 -0
  1823. data/vendor/plugins/rjs-assertions/init.rb +1 -0
  1824. data/vendor/plugins/rjs-assertions/lib/rjs-assertions.rb +99 -0
  1825. data/vendor/rails_patches/support_for_inverse_relationships_on_active_record_objects.diff +739 -0
  1826. metadata +2075 -0
@@ -0,0 +1,44 @@
1
+ require 'redcloth'
2
+
3
+ module Zena
4
+ module Parser
5
+ module ZazenTags
6
+ # This is not exactly how compile/render is meant to work with Parser, but there is no real need for a two step
7
+ # rendering, so we compile here (enter(:void)) instead of doing this whith 'start'. This also lets us have the
8
+ # context during compilation which makes it easier to manage the callbacks to the helper.
9
+ def r_void
10
+ @context = {:images => true, :pretty_code=>true, :output => 'html'}.merge(@context)
11
+ @translate_ids = @context[:translate_ids]
12
+ @text = @text.gsub("\r\n","\n") # this also creates our own 'working' copy of the text
13
+ @blocks = "" # same reason as why we rewrite 'store'
14
+
15
+ extract_code(@text)
16
+
17
+ # set whether the first paragraphe is spaced preserved.
18
+ @in_space_pre = (@text[0..0] == ' ')
19
+
20
+ enter(:void) # <== parse here
21
+
22
+ unless @translate_ids
23
+ store '</pre>' if @in_space_pre
24
+
25
+ case @context[:output]
26
+ when 'html'
27
+ # TODO: we should write our own parser for textile with rendering formats...
28
+ @text = RedCloth.new(@blocks).to_html
29
+ when 'latex'
30
+ # replace RedCloth markup by latex equivalent
31
+ @text = RedCloth.new(@blocks).to_latex
32
+ end
33
+
34
+ # Replace placeholders by their real values
35
+ @helper.replace_placeholders(@text) if @helper.respond_to?('replace_placeholders')
36
+ @blocks = ""
37
+ enter(:wiki)
38
+ end
39
+ render_code(@blocks)
40
+ @blocks
41
+ end
42
+ end # ZazenTags
43
+ end # Parser
44
+ end # Zena
@@ -0,0 +1,29 @@
1
+ module Zena
2
+ module Parser
3
+ module ZenaRules
4
+ def start(mode)
5
+ super
6
+ if @method =~ /^\[(.*)\]$/
7
+ # do='[text]
8
+ @method = 'show'
9
+ @params[:attr_or_date] = $1
10
+ elsif @method =~ /^\{(.*)\}$/
11
+ # do='{v_text}'
12
+ @method = 'zazen'
13
+ @params[:attr] = $1
14
+ elsif @method =~ /\A(\w+)\s+(\w+)\s+(.+)$/
15
+ # 'pages where name ...'
16
+ @params[:select] = @method
17
+ @method = 'context'
18
+ end
19
+
20
+ if @method == 'with' || self.respond_to?("r_#{@method}")
21
+ # ok
22
+ else
23
+ @params[:select] = @method
24
+ @method = 'context'
25
+ end
26
+ end
27
+ end # ZenaRules
28
+ end # Parser
29
+ end # Zena
@@ -0,0 +1,3559 @@
1
+ =begin
2
+ Thoughts for ajax related stuff cleanup (dom_id, erb_dom_id, ...)
3
+
4
+ When we open a list context:
5
+ - we change the context_dom_id => unique name => "page"
6
+
7
+ When we open a single context (do_var, each, block):
8
+ - we change the context_dom_id => unique name with id => "page_{parent_id}"
9
+
10
+ Any 'id' is set with:
11
+ - "{context_dom_id}_{id}" => 'page_34' (if not in list) or 'page_12_34' if in list.
12
+
13
+
14
+ List in list (initial node zip = 12):
15
+ context_dom_id dom_id (context_erb_dom_id, erb_dom_id)
16
+ <ul> page_12 page_12 [pages in='site'] new unique name
17
+ <li id='page_12_13'> page_12 page_12_13 [each] expand_with(:context_dom_id => 'page_13')
18
+ <ul> page_13
19
+ <li id='page_13_23'/> page_13 page_13_23 [each]
20
+ <li id='page_13_24'/> page_13 page_13_24 [each]
21
+ <li id='page_13_25'/> page_13 page_13_25 [each]
22
+ </ul>
23
+ </li>
24
+ <li id='page_12_14'> page_12 page_12_14 [each] expand_with(:context_dom_id => 'page_14')
25
+ <ul>
26
+ <li id='page_14_23'/> page_14 page_14_23 [each] expand_with(:context_dom_id => 'page_23')
27
+ <li id='page_14_27'/> page_14 page_14_27 [each] ...
28
+ </ul>
29
+ </li>
30
+ </ul>
31
+
32
+ <div id='page1_12'> page1_12 page1_12 [block] new unique name
33
+ <ul>
34
+ <li id='page1_12_24'/> page1_12 page1_12_24 [each]
35
+ <li id='page1_12_32'/> page1_12 page1_12_32 [each]
36
+ </ul>
37
+ </div>
38
+ =end
39
+
40
+ require 'yaml'
41
+
42
+ module Zena
43
+ module Parser
44
+ # Zafu tags used to display / edit nodes and versions
45
+ module ZenaTags
46
+ PSEUDO_ATTRIBUTES = {
47
+ 'now' => 'Time.now',
48
+ 'start.id' => '(params[:s] || @node[:zip])',
49
+ 'nil' => 'nil',
50
+ }
51
+
52
+ class << self
53
+ def inline_methods(*args)
54
+ args.each do |name|
55
+ class_eval <<-END
56
+ def r_#{name}
57
+ "<%= #{name}(:node=>\#{node}\#{params_to_erb(@params)}) %>"
58
+ end
59
+ END
60
+ end
61
+ end
62
+
63
+ def direct_methods(*args)
64
+ args.each do |name|
65
+ class_eval <<-END
66
+ def r_#{name}
67
+ helper.#{name}
68
+ end
69
+ END
70
+ end
71
+ end
72
+ end
73
+ inline_methods :login_link, :visitor_link, :search_box, :show_menu, :show_path, :lang_links
74
+ direct_methods :uses_datebox
75
+
76
+ def before_render
77
+ return unless super
78
+
79
+ @var = nil # reset var counter
80
+
81
+ if key = @params[:store]
82
+ set_stored(Node, key, node)
83
+ end
84
+
85
+ if key = @params[:store_date]
86
+ set_stored(Date, key, current_date)
87
+ end
88
+ @anchor_param = @params[:anchor]
89
+
90
+ true
91
+ end
92
+
93
+ def do_method(sym)
94
+ method = sym
95
+ pre, post = '', ''
96
+
97
+ # do we need recursion ?
98
+ inc = descendant('include')
99
+ if inc && inc.params[:part] == @name
100
+ @context["#{@name}_method".to_sym] = method_name = template_url[1..-1].gsub(/[\/-]/,'_')
101
+ pre << "<% def #{method_name}(depth, node, list); return '' if depth > #{inc.params[:depth] ? [inc.params[:depth].to_i,30].min : 5}; _erbout = '' -%>"
102
+ post << "<% _erbout; end -%><%= #{method_name}(0,#{node},#{list || "[#{node}]"}) %>"
103
+ @context[:node] = 'node'
104
+ @context[:list] = 'list'
105
+ end
106
+
107
+ if @context[:make_form]
108
+ res = case method
109
+ when :r_title
110
+ make_input(:name => 'v_title')
111
+ when :r_link
112
+ make_input(:name => (@params[:attr] || 'v_title'))
113
+ when :r_show
114
+ make_input(:name => (@params[:attr] || @params[:tattr]), :date => @params[:date])
115
+ when :r_text
116
+ make_textarea(:name => 'v_text')
117
+ when :r_summary
118
+ make_textarea(:name => 'v_summary')
119
+ when :r_zazen
120
+ make_textarea(:name => @params[:attr])
121
+ else
122
+ if node_kind_of?(DataEntry) && @method.to_s =~ /node_/
123
+ # select node_id
124
+ "<%= select_id('#{base_class.to_s.underscore}', '#{@method}_id') %>"
125
+ end
126
+ end
127
+ res = "<#{@html_tag || 'div'} class='zazen'>#{res}</#{@html_tag || 'div'}>" if [:r_summary, :r_text].include?(sym)
128
+ end
129
+
130
+
131
+ res ||= super(method)
132
+ "#{pre}#{res}#{post}"
133
+ end
134
+
135
+
136
+ def after_render(text)
137
+ if @anchor_param
138
+ @params[:anchor] = @anchor_param # set back in case of double rendering so it is computed again
139
+ res = r_anchor + super
140
+ else
141
+ res = super
142
+ end
143
+ res
144
+ end
145
+
146
+ # Our special version of r_expand_with tag with "set_" parsing.
147
+ def r_expand_with
148
+ hash = {}
149
+ @params.each do |k,v|
150
+ if k.to_s =~ /^set_(.+)$/
151
+ # TODO: DRY with render_html_tag
152
+ k = $1
153
+ value, static = parse_attributes_in_value(v, :erb => false)
154
+ hash["exp_#{k}"] = static ? value.inspect : "\"#{value}\""
155
+ else
156
+ hash["exp_#{k}"] = v.inspect
157
+ end
158
+ end
159
+ @params = {}
160
+ expand_with(hash)
161
+ end
162
+
163
+ def r_show
164
+ if attr_or_date = @params[:attr_or_date]
165
+ # using [var] shortcut. Can be either a date or an attribute/var
166
+ if attr_or_date == 'current_date' || attr_or_date =~ /_at$/
167
+ @params[:date] = attr_or_date
168
+ else
169
+ @params[:attr] = attr_or_date
170
+ end
171
+ end
172
+
173
+ if var_name = @params[:var]
174
+ return parser_error("var #{@params[:var].inspect} not set") unless @context[:vars] && @context[:vars].include?(var_name)
175
+ attribute_method = "set_#{var_name}"
176
+ elsif @params[:eval]
177
+ return unless attribute_method = parse_eval_parameter(@params[:eval])
178
+ elsif @params[:tattr]
179
+ attribute_method = "_(#{node_attribute(@params[:tattr], :else=>@params[:else], :default=>@params[:default])})"
180
+ elsif @params[:attr]
181
+ attribute_method = node_attribute(@params[:attr], :else=>@params[:else], :default=>@params[:default])
182
+ elsif p = @params[:param]
183
+ return "<%= params[#{p.to_sym.inspect}] %>"
184
+ elsif @params[:date]
185
+ # date can be any attribute v_created_at or updated_at etc.
186
+ # TODO format with @params[:format] and @params[:tformat] << translated format
187
+ # TODO: test
188
+ if @params[:tformat]
189
+ format = _(@params[:tformat])
190
+ elsif @params[:format]
191
+ format = @params[:format]
192
+ else
193
+ format = "%Y-%m-%d"
194
+ end
195
+
196
+ tz = ''
197
+ lang = ''
198
+ if tz_name = @params[:time_zone]
199
+ tz_list = @params.reject {|k,v| !(k.to_s =~ /^time_zone\d*$/)}.to_a.sort {|a,b| a[0].to_s <=> b[0].to_s }.map do |k,tz_name|
200
+ if tz_name =~ /^\[(\w+)\]$/
201
+ node_attribute($1)
202
+ else
203
+ begin
204
+ TZInfo::Timezone.get(tz_name)
205
+ rescue TZInfo::InvalidTimezoneIdentifier
206
+ return parser_error("invalid timezone #{tz_name.inspect}")
207
+ end
208
+ tz_name.inspect
209
+ end
210
+ end
211
+ tz = ", #{tz_list.join(' || ')}"
212
+ end
213
+ if lang = @params[:lang]
214
+ tz = ', nil' if tz == ''
215
+ lang = ", #{lang.inspect}"
216
+ end
217
+ attribute_method = "format_date(#{node_attribute(@params[:date])}, #{format.inspect}#{tz}#{lang})"
218
+ elsif @context[:trans]
219
+ # error
220
+ return "no attribute for 'show'".inspect
221
+ else
222
+ return parser_error("missing attribute")
223
+ end
224
+
225
+ if !@params[:date] && fmt = @params[:format]
226
+ begin
227
+ # test argument
228
+ sprintf(fmt, 123.45)
229
+ rescue ArgumentError
230
+ return parser_error("incorect format #{fmt.inspect}")
231
+ end
232
+ if fmt =~ /%[\d\.]*f/
233
+ modifier = ".to_f"
234
+ elsif fmt =~ /%[\d\.]*i/
235
+ modifier = ".to_i"
236
+ else
237
+ modifier = ''
238
+ end
239
+
240
+ if @params[:zero] == 'hide'
241
+ attribute_method = "sprintf_unless_zero(#{fmt.inspect}, #{attribute_method}#{modifier})"
242
+ else
243
+ attribute_method = "sprintf(#{fmt.inspect}, #{attribute_method}#{modifier})"
244
+ end
245
+ end
246
+
247
+
248
+ if @context[:trans]
249
+ # TODO: what do we do here with gsubs, url ?
250
+ return attribute_method
251
+ end
252
+
253
+ if gsub = @params[:gsub]
254
+ if gsub =~ /\A\/(.+)\/(.+)\/\Z/
255
+ value = $2
256
+ key = $1.gsub(/\#([\{\$\@])/,'# \1') # FIXME: SECURITY.
257
+ # Please note that .gsub(/#([\{\$\@])/,'\#\1') won't work, since '\#{blah}' will become '\\#{blah}' and 'blah' will be evaluated.
258
+ regexp_ok = begin
259
+ output = StringIO.open('','w')
260
+ $stderr = output
261
+ re = /#{key}/
262
+ output.string !~ /warning:/
263
+ rescue
264
+ false
265
+ ensure
266
+ $stderr = STDERR
267
+ false
268
+ end
269
+
270
+ if regexp_ok
271
+ attribute_method = "#{attribute_method}.to_s.gsub(/#{key}/,#{value.inspect})"
272
+ else
273
+ # invalid regexp
274
+ return parser_error("invalid gsub #{gsub.inspect}")
275
+ end
276
+ else
277
+ # error
278
+ return parser_error("invalid gsub #{gsub.inspect}")
279
+ end
280
+ end
281
+
282
+ if @params[:actions]
283
+ actions = "<%= node_actions(:node=>#{node}#{params_to_erb(:actions=>@params[:actions], :publish_after_save=>auto_publish_param)}) %>"
284
+ else
285
+ actions = ''
286
+ end
287
+
288
+ attribute = @params[:attr] || @params[:tattr] || @params[:date]
289
+
290
+ if (@params[:edit_preview] || @params[:ep]) == 'true'
291
+ @html_tag_params[:id] = "#{attribute}#{erb_node_id}"
292
+ @html_tag ||= 'span'
293
+ end
294
+
295
+ if @params[:edit] == 'true' && !['url','path'].include?(attribute)
296
+ "<% if #{node}.can_write? -%><span class='show_edit' id='#{erb_dom_id("_#{attribute}")}'>#{actions}<%= link_to_remote(#{attribute_method}, :url => edit_node_path(#{node_id}) + \"?attribute=#{attribute}&dom_id=#{dom_id("_#{attribute}")}#{auto_publish_param(true)}\", :method => :get) %></span><% else -%>#{actions}<%= #{attribute_method} %><% end -%>"
297
+ else
298
+ "#{actions}<%= #{attribute_method} %>"
299
+ end
300
+ end
301
+
302
+ def r_zazen
303
+ attribute = @params[:attr] || @params[:tattr]
304
+ limit = @params[:limit] ? ", :limit=>#{@params[:limit].to_i}" : ""
305
+ if @context[:trans]
306
+ # TODO: what do we do here with dates ?
307
+ return "#{node_attribute(attribute)}"
308
+ elsif @params[:tattr]
309
+ return "<%= zazen(_(#{node_attribute(attribute)})#{limit}, :node=>#{node(Node)}) %>"
310
+ elsif @params[:attr]
311
+ if output_format == 'html'
312
+ res = "<%= zazen(#{node_attribute(attribute)}#{limit}, :node=>#{node(Node)}) %>"
313
+ else
314
+ return "<%= zazen(#{node_attribute(attribute)}#{limit}, :node=>#{node(Node)}, :output=>#{output_format.inspect}) %>"
315
+ end
316
+ elsif @params[:date]
317
+ # date can be any attribute v_created_at or updated_at etc.
318
+ # TODO format with @params[:format] and @params[:tformat] << translated format
319
+ else
320
+ # error
321
+ end
322
+
323
+ @html_tag ||= 'div'
324
+
325
+ add_html_class('zazen')
326
+
327
+ if (@params[:edit_preview] || @params[:ep]) == 'true'
328
+ @html_tag_params[:id] = "#{attribute}#{erb_node_id}"
329
+ end
330
+
331
+ if @params[:edit] == 'true' && !['url','path'].include?(attribute)
332
+ edit_text = _('edit')
333
+ @html_tag_params[:id] = erb_dom_id("_#{attribute}")
334
+ res = "<% if #{node}.can_write? -%><span class='zazen_edit'><%= link_to_remote(#{edit_text.inspect}, :url => edit_node_path(#{node_id}) + \"?attribute=#{attribute}&dom_id=#{dom_id("_#{attribute}")}#{auto_publish_param(true)}&zazen=true\", :method => :get) %></span><% end -%>#{res}"
335
+ else
336
+ res
337
+ end
338
+ end
339
+
340
+ # TODO: test, rename ?
341
+ def r_search_results
342
+ do_list("@nodes")
343
+ end
344
+
345
+
346
+ def r_set
347
+ return parser_error("'var' missing") unless var_name = @params[:var]
348
+ return parser_error("bad value for 'var' (#{var_name.inspect})") unless var_name =~ /^[a-zA-Z_]+$/
349
+ return '' unless @context[:set]
350
+ if @params[:value]
351
+ out "<% set_#{var_name} = #{@params[:value].inspect} -%>"
352
+ # TODO: isn't @context[:vars] = @params[:value].inspect missing here ?
353
+ elsif @params[:eval]
354
+ return unless eval_string = parse_eval_parameter(@params[:eval])
355
+ out "<% set_#{var_name} = #{eval_string} -%>"
356
+ else
357
+ out "<% set_#{var_name} = capture do %>"
358
+ out expand_with(:set => false) # do not propagate
359
+ out "<% end -%>"
360
+ end
361
+ end
362
+
363
+
364
+ # TODO: write a test (please)
365
+ # FIXME: we should use a single way to change a whole context into a template (applies to 'each', 'form', 'block'). Then 'swap' could use the 'each' block.
366
+ # Define a block of elements to be used by ajax calls (edit/filter)
367
+ def r_block
368
+ if @context[:block] == self
369
+ # called from self (storing template)
370
+ @context.reject! do |k,v|
371
+ # FIXME: reject all stored elements in a better way then this
372
+ k.kind_of?(String) && k =~ /\ANode_\w/
373
+ end
374
+ @html_tag_done = false
375
+ @html_tag_params.merge!(:id=>erb_dom_id)
376
+ @context[:scope_node] = node if @context[:scope_node]
377
+ out expand_with(:node => node)
378
+ if @method == 'drop' && !@context[:make_form]
379
+ out drop_javascript
380
+ end
381
+ else
382
+ if parent.method == 'each' && @method == parent.single_child_method
383
+ # use parent as block
384
+ # FIXME: will not work with block as distant target...
385
+ # do nothing
386
+ else
387
+ @html_tag ||= 'div'
388
+ new_dom_scope
389
+
390
+ unless @context[:make_form]
391
+ # STORE TEMPLATE ========
392
+
393
+ context_bak = @context.dup # avoid side effects when rendering the same block
394
+ ignore_list = @method == 'block' ? ['form'] : [] # do not show the form in the normal template of a block
395
+ template = expand_block(self, :block=>self, :list=>false, :saved_template=>true, :ignore => ignore_list)
396
+ @context = context_bak
397
+ @result = ''
398
+ out helper.save_erb_to_url(template, template_url)
399
+
400
+ # STORE FORM ============
401
+ if edit = descendant('edit')
402
+ publish_after_save = (edit.params[:publish] == 'true')
403
+ if form = descendant('form')
404
+ # USE BLOCK FORM ========
405
+ form_text = expand_block(form, :saved_template=>true, :publish_after_save => publish_after_save)
406
+ else
407
+ # MAKE A FORM FROM BLOCK ========
408
+ form = self.dup
409
+ form.method = 'form'
410
+ form_text = expand_block(form, :make_form => true, :list => false, :saved_template => true, :publish_after_save => publish_after_save)
411
+ end
412
+ out helper.save_erb_to_url(form_text, form_url)
413
+ end
414
+ end
415
+
416
+ # RENDER
417
+ @html_tag_done = false
418
+ @html_tag_params.merge!(:id=>erb_dom_id)
419
+ end
420
+
421
+ out expand_with
422
+ if @method == 'drop' && !@context[:make_form]
423
+ out drop_javascript
424
+ end
425
+ end
426
+ end
427
+
428
+ # TODO: test
429
+ def r_filter
430
+ if upd = @params[:update]
431
+ return unless block = find_target(upd)
432
+ else
433
+ return parser_error("missing 'block' in same parent") unless parent && block = parent.descendant('block')
434
+ end
435
+ return parser_error("cannot use 's' as key (used by start_node)") if @params[:key] == 's'
436
+ out "<%= form_remote_tag(:url => zafu_node_path(#{node_id}), :method => :get, :html => {:id => \"#{dom_id}_f\"}) %><div class='hidden'><input type='hidden' name='t_url' value='#{block.template_url}'/><input type='hidden' name='dom_id' value='#{block.erb_dom_id}'/>#{start_node_s_param(:input)}</div><div class='wrapper'>"
437
+ if @blocks == []
438
+ out "<input type='text' name='#{@params[:key] || 'f'}' value='<%= params[#{(@params[:key] || 'f').to_sym.inspect}] %>'/>"
439
+ else
440
+ out expand_with(:in_filter => true)
441
+ end
442
+ out "</div></form>"
443
+ if @params[:live] || @params[:update]
444
+ out "<%= observe_form( \"#{dom_id}_f\" , :method => :get, :frequency => 1, :submit =>\"#{dom_id}_f\", :url => zafu_node_path(#{node_id})) %>"
445
+ end
446
+ end
447
+
448
+ # swap an attribute
449
+ # TODO: test
450
+ def r_swap
451
+ if upd = @params[:update]
452
+ if upd == '_page'
453
+ block = nil
454
+ elsif block = find_target(upd)
455
+ # ok
456
+ if ancestor('block') || ancestor('each')
457
+ upd_both = '&upd_both=true'
458
+ else
459
+ upd_both = ''
460
+ end
461
+ else
462
+ return
463
+ end
464
+ elsif ancestor('block') || ancestor('each')
465
+ # ancestor: ok
466
+ block = self
467
+ elsif parent && block = parent.descendant('block')
468
+ # sibling: ok
469
+ upd_both = ''
470
+ else
471
+ return parser_error("missing 'block' in same parent")
472
+ end
473
+
474
+ states = ((@params[:states] || 'todo, done') + ' ').split(',').map(&:strip)
475
+
476
+ query_params = "node[#{@params[:attr]}]=\#{#{states.inspect}[ ((#{states.inspect}.index(#{node_attribute(@params[:attr])}.to_s) || 0)+1) % #{states.size}]}#{upd_both}"
477
+ out link_to_update(block, :query_params => query_params, :method => :put, :html_params => get_html_params(@params, :link))
478
+ end
479
+
480
+ def r_load
481
+ if dict = @params[:dictionary]
482
+ dict_content, absolute_url, doc = self.class.get_template_text(dict, @options[:helper], @options[:current_folder])
483
+ return parser_error("dictionary #{dict.inspect} not found") unless doc
484
+ @context[:dict] ||= {}
485
+ begin
486
+ definitions = YAML::load(dict_content)
487
+ definitions['translations'].each do |elem|
488
+ @context[:dict][elem[0]] = elem[1]
489
+ end
490
+ rescue
491
+ return parser_error("invalid dictionary content #{dict.inspect}")
492
+ end
493
+ else
494
+ return parser_error("missing 'dictionary'")
495
+ end
496
+ expand_with
497
+ end
498
+
499
+ def r_trans
500
+ static = true
501
+ if @params[:text]
502
+ text = @params[:text]
503
+ elsif @params[:attr]
504
+ text = "#{node_attribute(@params[:attr])}"
505
+ static = false
506
+ else
507
+ res = []
508
+ text = ""
509
+ @blocks.each do |b|
510
+ if b.kind_of?(String)
511
+ res << b.inspect
512
+ text << b
513
+ elsif ['show', 'current_date'].include?(b.method)
514
+ res << expand_block(b, :trans=>true)
515
+ static = false
516
+ else
517
+ # ignore
518
+ end
519
+ end
520
+ unless static
521
+ text = res.join(' + ')
522
+ end
523
+ end
524
+ if static
525
+ _(text)
526
+ else
527
+ "<%= _(#{text}) %>"
528
+ end
529
+ end
530
+
531
+ alias r_t r_trans
532
+
533
+ def r_anchor(obj=node)
534
+ "<a name='#{anchor_name(@anchor_param, obj)}'></a>"
535
+ end
536
+
537
+ def anchor_name(p, obj=node)
538
+ if p =~ /\[(.+)\]/
539
+ "<%= #{node_attribute($1)} %>"
540
+ else
541
+ "#{base_class.to_s.underscore}#{erb_node_id(obj)}"
542
+ end
543
+ end
544
+
545
+ def r_content_for_layout
546
+ "<% if content_for_layout = yield -%><%= content_for_layout %><% else -%>" +
547
+ expand_with +
548
+ "<% end -%>"
549
+ end
550
+
551
+ def r_title_for_layout
552
+ "<% if @title_for_layout -%><%= @title_for_layout %><% elsif @node && !@node.new_record? -%><%= @node.rootpath %><% elsif @node.parent -%><%= @node.parent.rootpath %><% else -%>" +
553
+ expand_with +
554
+ "<% end -%>"
555
+ end
556
+
557
+ def r_check_lang
558
+ text = @params[:text] || expand_with
559
+ klass = @params[:class] || @html_tag_params[:class]
560
+ text = nil if text.blank?
561
+ klas = nil if klass.blank?
562
+ @html_tag_done = true
563
+ "#{@space_before}<%= check_lang(#{node},:text=>#{text.inspect},:class=>#{klass.inspect},:wrap=>#{@html_tag.inspect}) %>"
564
+ end
565
+
566
+ def r_title
567
+ if node_kind_of?(Version)
568
+ node = "#{self.node}.node"
569
+ elsif node_kind_of?(Node)
570
+ node = self.node
571
+ else
572
+ return parser_error('title','only works with nodes')
573
+ end
574
+ title_params = {}
575
+
576
+ title_params[:check_lang] = @params[:check_lang] if @params.include?(:check_lang)
577
+
578
+ if @params[:link]
579
+ value, static = parse_attributes_in_value(@params[:link], :erb => false)
580
+ link_param = ", :link=>\"#{value}\""
581
+ else
582
+ link_param = ''
583
+ end
584
+
585
+ res = "<%= show_title(:node=>#{node}#{link_param}#{params_to_erb(title_params)}"
586
+ if @params[:text]
587
+ res << ", :text=>#{@params[:text].inspect}"
588
+ elsif @params[:attr]
589
+ res << ", :text=>#{node_attribute(@params[:attr])}"
590
+ end
591
+
592
+ if @params.include?(:project)
593
+ res << ", :project=>#{@params[:project] == 'true'}"
594
+ end
595
+ res << ")"
596
+ if @params[:actions]
597
+ res << " + node_actions(:node=>#{node}#{params_to_erb(:actions=>@params[:actions], :publish_after_save=>auto_publish_param)})"
598
+ end
599
+ res << "%>"
600
+ if @params[:status] == 'true' || (@params[:status].nil? && @params[:actions])
601
+ @html_tag ||= 'span'
602
+ add_html_class("s<%= #{node}.version.status %>")
603
+ end
604
+ res
605
+ end
606
+
607
+ # TODO: test
608
+ def r_actions
609
+ out expand_with
610
+ out "<%= node_actions(:node=>#{node}#{params_to_erb(:actions=>@params[:select], :publish_after_save=>auto_publish_param)}) %>"
611
+ end
612
+
613
+ # TODO: test
614
+ def r_admin_links
615
+ "<%= show_link(:admin_links).join('</#{@html_tag}><#{@html_tag}>') %>"
616
+ end
617
+
618
+ def r_text
619
+ text = @params[:text] ? @params[:text].inspect : "#{node_attribute('v_text')}"
620
+ limit = @params[:limit] ? ", :limit=>#{@params[:limit].to_i}" : ""
621
+
622
+ @html_tag ||= 'div'
623
+
624
+ if @html_tag_params[:id]
625
+ # add a sub-div
626
+ pre = "<div id='v_text#{erb_node_id}'>"
627
+ post = "</div>"
628
+ else
629
+ pre = post = ''
630
+ @html_tag_params[:id] = "v_text#{erb_node_id}"
631
+ end
632
+
633
+ add_html_class('zazen')
634
+
635
+ unless @params[:empty] == 'true'
636
+ out "#{pre}<% if #{node}.kind_of?(TextDocument); l = #{node}.content_lang -%>"
637
+ out "<%= zazen(\"<code\#{l ? \" lang='\#{l}'\" : ''} class=\\'full\\'>\#{#{text}}</code>\") %>"
638
+ out "<% else -%>"
639
+ out "<%= zazen(#{text}#{limit}, :node=>#{node(Node)}) %>"
640
+ out "<% end -%>#{post}"
641
+ else
642
+ out "#{pre}#{post}"
643
+ end
644
+ end
645
+
646
+ def r_inspect
647
+ out ["params: #{@params.inspect}",
648
+ "name: #{@context[:name]}",
649
+ "node: #{node}",
650
+ "list: #{list}"].join("<br/>")
651
+ end
652
+
653
+ # TODO: replace with a more general 'zazen' or 'show' with id ?
654
+ def r_summary
655
+ limit = @params[:limit] ? ", :limit=>#{@params[:limit].to_i}" : ""
656
+ @html_tag ||= 'div'
657
+ if @html_tag_params[:id]
658
+ # add a sub-div
659
+ pre = "<div id='v_summary#{erb_node_id}'>"
660
+ post = "</div>"
661
+ else
662
+ pre = post = ''
663
+ @html_tag_params[:id] = "v_summary#{erb_node_id}"
664
+ end
665
+
666
+ add_html_class('zazen')
667
+
668
+ unless @params[:or]
669
+ text = @params[:text] ? @params[:text].inspect : node_attribute('v_summary')
670
+ out "#{pre}<%= zazen(#{text}#{limit}, :node=>#{node(Node)}) %>#{post}"
671
+ else
672
+ limit ||= ', :limit => 2'
673
+ first_name = 'v_summary'
674
+ first = node_attribute(first_name)
675
+
676
+ second_name = @params[:or].gsub(/[^a-z_]/,'') # FIXME: ist this still needed ? (ERB injection)
677
+ second = node_attribute(second_name)
678
+ out "#{pre}<% if #{first} != '' %>"
679
+ out "<%= zazen(#{first}, :node=>#{node(Node)}) %>"
680
+ out "<% else %>"
681
+ out "<%= zazen(#{second}#{limit}, :node=>#{node(Node)}) %>"
682
+ out "<% end %>#{post}"
683
+ end
684
+ end
685
+
686
+ def r_show_author
687
+ if @params[:size] == 'large'
688
+ out "#{_("posted by")} <b><%= #{node}.author.fullname %></b>"
689
+ out "<% if #{node}[:user_id] != #{node}.version[:user_id] -%>"
690
+ out "<% if #{node}[:ref_lang] != #{node}.version[:lang] -%>"
691
+ out "#{_("traduction by")} <b><%= #{node}.version.author.fullname %></b>"
692
+ out "<% else -%>"
693
+ out "#{_("modified by")} <b><%= #{node}.version.author.fullname %></b>"
694
+ out "<% end"
695
+ out " end -%>"
696
+ out " #{_("on")} <%= format_date(#{node}.version.updated_at, #{_('short_date').inspect}) %>."
697
+ if @params[:traductions] == 'true'
698
+ out " #{_("Traductions")} : <span class='traductions'><%= helper.traductions(:node=>#{node}).join(', ') %></span>"
699
+ end
700
+ else
701
+ out "<b><%= #{node}.version.author.initials %></b> - <%= format_date(#{node}.version.updated_at, #{_('short_date').inspect}) %>"
702
+ if @params[:traductions] == 'true'
703
+ out " <span class='traductions'>(<%= helper.traductions(:node=>#{node}).join(', ') %>)</span>"
704
+ end
705
+ end
706
+ end
707
+
708
+ def r_comments_to_publish
709
+ open_context("visitor.comments_to_publish", :node_class => [Comment])
710
+ end
711
+
712
+ def r_to_publish
713
+ open_context("visitor.to_publish", :node_class => [Version])
714
+ end
715
+
716
+ def r_proposed
717
+ open_context("visitor.proposed", :node_class => [Version])
718
+ end
719
+
720
+ def r_redactions
721
+ open_context("visitor.redactions", :node_class => [Version])
722
+ end
723
+
724
+ def r_edit
725
+
726
+ if @context[:dom_prefix]
727
+ # ajax
728
+ if @context[:in_form]
729
+ # cancel button
730
+ @context[:form_cancel] || ''
731
+ else
732
+ # edit button
733
+
734
+ # TODO: show 'reply' instead of 'edit' in comments if visitor != author
735
+ out link_to_update(self, :default_text => _('edit'), :url => "\#{edit_#{base_class.to_s.underscore}_path(#{node_id})}", :html_params => get_html_params(@params, :link), :method => :get, :cond => "#{node}.can_write?", :else => :void)
736
+ end
737
+ else
738
+ # FIXME: we could link to some html page to edit the item.
739
+ ""
740
+ end
741
+ end
742
+
743
+ alias r_cancel r_edit
744
+
745
+ def r_textarea
746
+ out make_textarea(@html_tag_params.merge(@params))
747
+ @html_tag_done = true
748
+ end
749
+
750
+
751
+ # <r:select name='klass' root_class='...'/>
752
+ # <r:select name='parent_id' values='projects in site'/>
753
+ # TODO: optimization (avoid loading full AR to only use [id, name])
754
+ def r_select
755
+ html_attributes, attribute = get_input_params()
756
+ return parser_error("missing name") unless attribute
757
+ if value = @params[:selected]
758
+ # FIXME: DRY with html_attributes
759
+ value = value.gsub(/\[([^\]]+)\]/) do
760
+ node_attr = $1
761
+ res = node_attribute(node_attr)
762
+ "\#{#{res}}"
763
+ end
764
+ selected = value.inspect
765
+ elsif @context[:in_filter]
766
+ selected = "params[#{attribute.to_sym.inspect}].to_s"
767
+ else
768
+ selected = "#{node_attribute(attribute)}.to_s"
769
+ end
770
+ html_id = html_attributes[:id] ? " id='#{html_attributes[:id]}'" : ''
771
+ if @context[:in_filter]
772
+ select_tag = "<select#{html_id} name='#{attribute}'>"
773
+ else
774
+ select_tag = "<select#{html_id} name='#{base_class.to_s.underscore}[#{attribute}]'>"
775
+ end
776
+
777
+ if klass = @params[:root_class]
778
+ class_opts = {}
779
+ class_opts[:without] = @params[:without] if @params[:without]
780
+ # do not use 'selected' if the node is not new
781
+ "#{select_tag}<%= options_for_select(Node.classes_for_form(:class => #{klass.inspect}#{params_to_erb(class_opts)}), (#{node}.new_record? ? #{selected} : #{node}.klass)) %></select>"
782
+ elsif @params[:type] == 'time_zone'
783
+ # <r:select name='d_tz' type='time_zone'/>
784
+ "#{select_tag}<%= options_for_select(TZInfo::Timezone.all_identifiers, #{selected}) %></select>"
785
+ elsif options_list = get_options_for_select
786
+ "#{select_tag}<%= options_for_select(#{options_list}, #{selected}) %></select>"
787
+ else
788
+ parser_error("missing 'nodes', 'root_class' or 'values'")
789
+ end
790
+ end
791
+
792
+
793
+ def r_input
794
+ html_attributes, attribute = get_input_params()
795
+ case @params[:type]
796
+ when 'select' # FIXME: why is this only for classes ?
797
+ out parser_error("please use [select] here")
798
+ r_select
799
+ when 'date_box', 'date'
800
+ return parser_error("date_box without name") unless attribute
801
+ input_id = @context[:dom_prefix] ? ", :id=>\"#{dom_id}_#{attribute}\"" : ''
802
+ "<%= date_box '#{base_class.to_s.underscore}', #{attribute.inspect}, :size=>15#{@context[:in_add] ? ", :value=>''" : ''}#{input_id} %>"
803
+ when 'id'
804
+ return parser_error("select id without name") unless attribute
805
+ name = "#{attribute}_id" unless attribute[-3..-1] == '_id'
806
+ input_id = @context[:erb_dom_id] ? ", :input_id =>\"#{erb_dom_id}_#{attribute}\"" : ''
807
+ "<%= select_id('#{base_class.to_s.underscore}', #{attribute.inspect}#{input_id}) %>"
808
+ when 'time_zone'
809
+ out parser_error("please use [select] here")
810
+ r_select
811
+ when 'submit'
812
+ @html_tag = 'input'
813
+ @html_tag_params[:type] = @params[:type]
814
+ @html_tag_params[:text] = @params[:text] if @params[:text]
815
+ @html_tag_params.merge!(html_attributes)
816
+ render_html_tag(nil)
817
+ else
818
+ # 'text', 'hidden', ...
819
+ @html_tag = 'input'
820
+ @html_tag_params[:type] = @params[:type] || 'text'
821
+ if checked = html_attributes.delete(:checked)
822
+ @html_tag_params.merge!(html_attributes)
823
+ render_html_tag(nil, checked)
824
+ else
825
+ @html_tag_params.merge!(html_attributes)
826
+ render_html_tag(nil)
827
+ end
828
+ end
829
+ end
830
+
831
+ def r_form_tag
832
+ # replace <form> with constructed form
833
+ "#{@context[:form_tag]}#{expand_with(:form_tag => nil)}</form>"
834
+ end
835
+
836
+ # TODO: add parent_id into the form !
837
+ # TODO: add <div style="margin:0;padding:0"><input name="_method" type="hidden" value="put" /></div> if method == put
838
+ # FIXME: use <r:form href='self'> or <r:form action='...'>
839
+ def r_form
840
+ hidden_fields = {}
841
+ set_fields = []
842
+ id_hash = {:class => @html_tag_params[:class] || @params[:class] || 'form'}
843
+ var_name = base_class.to_s.underscore
844
+ (descendants('input') + descendants('select')).each do |tag|
845
+ set_fields << "#{var_name}[#{tag.params[:name]}]"
846
+ end
847
+
848
+ if @context[:dom_prefix] || @params[:update]
849
+ # ajax
850
+ if @context[:in_add]
851
+ # inline form used to create new elements: set values to '' and 'parent_id' from context
852
+ id_hash[:id] = "#{erb_dom_id}_form"
853
+ id_hash[:style] = "display:none;"
854
+
855
+ cancel = "<p class='btn_x'><a href='#' onclick='[\"#{erb_dom_id}_add\", \"#{erb_dom_id}_form\"].each(Element.toggle);return false;'>#{_('btn_x')}</a></p>\n"
856
+ form = "<%= form_remote_tag(:url => #{base_class.to_s.underscore.pluralize}_path, :html => {:id => \"#{dom_id}_form_t\"}) %>\n"
857
+ else
858
+ # saved form
859
+
860
+ id_hash[:id] = erb_dom_id
861
+
862
+ cancel = !@context[:dom_prefix] ? "" : <<-END_TXT
863
+ <% if #{node}.new_record? -%>
864
+ <p class='btn_x'><a href='#' onclick='[\"<%= params[:dom_id] %>_add\", \"<%= params[:dom_id] %>_form\"].each(Element.toggle);return false;'>#{_('btn_x')}</a></p>
865
+ <% else -%>
866
+ <p class='btn_x'><%= link_to_remote(#{_('btn_x').inspect}, :url => #{base_class.to_s.underscore}_path(#{node_id}) + \"/zafu?t_url=#{CGI.escape(template_url)}&dom_id=\#{params[:dom_id]}#{@context[:need_link_id] ? "&link_id=\#{#{node}.link_id}" : ''}\", :method => :get) %></p>
867
+ <% end -%>
868
+ END_TXT
869
+ form =<<-END_TXT
870
+ <% if #{node}.new_record? -%>
871
+ <%= form_remote_tag(:url => #{base_class.to_s.underscore.pluralize}_path, :html => {:id => \"\#{params[:dom_id]}_form_t\"}) %>
872
+ <% else -%>
873
+ <%= form_remote_tag(:url => #{base_class.to_s.underscore}_path(#{node_id}), :method => :put, :html => {:id => \"#{dom_id}_form_t\"}) %>
874
+ <% end -%>
875
+ END_TXT
876
+ end
877
+
878
+ if (descendants('input') || []).select{|elem| elem.params[:type] == 'submit'} != []
879
+ # has submit
880
+ else
881
+ hidden_submit = "<input type='submit'/>" # hidden submit for Firefox compatibility
882
+ end
883
+
884
+ hidden_fields['link_id'] = "<%= #{node}.link_id %>" if @context[:need_link_id]
885
+
886
+ if @params[:update] || (@context[:add] && @context[:add].params[:update])
887
+ upd = @params[:update] || @context[:add].params[:update]
888
+ if target = find_target(upd)
889
+ hidden_fields['u_url'] = target.template_url
890
+ hidden_fields['udom_id'] = target.erb_dom_id
891
+ hidden_fields['u_id'] = "<%= #{@context[:parent_node]}.zip %>" if @context[:in_add]
892
+ hidden_fields['s'] = start_node_s_param(:value)
893
+ end
894
+ elsif (block = ancestor('block')) && node_kind_of?(DataEntry)
895
+ # updates template url
896
+ hidden_fields['u_url'] = block.template_url
897
+ hidden_fields['udom_id'] = block.erb_dom_id
898
+ end
899
+
900
+ hidden_fields['t_url'] = template_url
901
+ if t_id = @params[:t_id]
902
+ hidden_fields['t_id'] = parse_attributes_in_value(t_id)
903
+ end
904
+
905
+ erb_dom_id = @context[:saved_template] ? '<%= params[:dom_id] %>' : self.erb_dom_id
906
+
907
+ hidden_fields['dom_id'] = erb_dom_id
908
+
909
+ if node_kind_of?(Node)
910
+ hidden_fields['node[parent_id]'] = "<%= #{@context[:in_add] ? "#{@context[:parent_node]}.zip" : "#{node}.parent_zip"} %>"
911
+ elsif node_kind_of?(Comment)
912
+ # FIXME: the "... || '@node'" is a hack and I don't understand why it's needed...
913
+ hidden_fields['node_id'] = "<%= #{@context[:parent_node] || '@node'}.zip %>"
914
+ elsif node_kind_of?(DataEntry)
915
+ hidden_fields["data_entry[#{@context[:data_root]}_id]"] = "<%= #{@context[:in_add] ? @context[:parent_node] : "#{node}.#{@context[:data_root]}"}.zip %>"
916
+ end
917
+
918
+ if add_block = @context[:add]
919
+ params = add_block.params
920
+ [:after, :before, :top, :bottom].each do |sym|
921
+ if params[sym]
922
+ hidden_fields['position'] = sym.to_s
923
+ if params[sym] == 'self'
924
+ if sym == :before
925
+ hidden_fields['reference'] = "#{erb_dom_id}_add"
926
+ else
927
+ hidden_fields['reference'] = "#{erb_dom_id}_form"
928
+ end
929
+ else
930
+ hidden_fields['reference'] = params[sym]
931
+ end
932
+ break
933
+ end
934
+ end
935
+ if params[:done] == 'focus'
936
+ if params[:focus]
937
+ hidden_fields['done'] = "'$(\"#{erb_dom_id}_#{@params[:focus]}\").focus();'"
938
+ else
939
+ hidden_fields['done'] = "'$(\"#{erb_dom_id}_form_t\").focusFirstElement();'"
940
+ end
941
+ elsif params[:done]
942
+ hidden_fields['done'] = CGI.escape(params[:done]) # .gsub("NODE_ID", @node.zip).gsub("PARENT_ID", @node.parent_zip)
943
+ end
944
+ else
945
+ # ajax form, not in 'add'
946
+ hidden_fields['done'] = CGI.escape(@params[:done]) if @params[:done]
947
+ end
948
+ else
949
+ # no ajax
950
+ # FIXME
951
+ cancel = "" # link to normal node ?
952
+ form = "<form method='post' action='/nodes/#{erb_node_id}'><div style='margin:0;padding:0'><input name='_method' type='hidden' value='put' /></div>"
953
+ end
954
+
955
+ if node_kind_of?(Node) && (@params[:klass] || @context[:klass])
956
+ hidden_fields['node[klass]'] = @params[:klass] || @context[:klass]
957
+ end
958
+
959
+ if node_kind_of?(Node) && @params[:mode]
960
+ hidden_fields['mode'] = @params[:mode]
961
+ end
962
+
963
+ hidden_fields['node[v_status]'] = Zena::Status[:pub] if @context[:publish_after_save] || auto_publish_param
964
+
965
+ form << "<div class='hidden'>"
966
+ hidden_fields.each do |k,v|
967
+ next if set_fields.include?(k)
968
+ v = "'#{v}'" unless v.kind_of?(String) && ['"', "'"].include?(v[0..0])
969
+ form << "<input type='hidden' name='#{k}' value=#{v}/>\n"
970
+ end
971
+ form << hidden_submit << "\n" if hidden_submit
972
+ form << "</div>"
973
+
974
+ form << "<%= error_messages_for(#{node}) %>"
975
+
976
+ if !descendant('cancel') && !descendant('edit')
977
+ if !descendant('form_tag')
978
+ # add a descendant before blocks.
979
+ blocks_bak = @blocks
980
+ @blocks = @blocks.dup
981
+ @blocks = [make(:void, :method=>'void', :text=>cancel)] + blocks_bak
982
+ else
983
+ form = cancel + form
984
+ cancel = ''
985
+ end
986
+ end
987
+
988
+ if descendant('form_tag')
989
+ res = expand_with(:form_tag => form, :in_form => true, :form_cancel => cancel, :erb_dom_id => erb_dom_id, :dom_id => dom_id)
990
+ else
991
+ res = form + expand_with(:in_form => true, :form_cancel => cancel, :erb_dom_id => erb_dom_id, :dom_id => dom_id) + '</form>'
992
+ end
993
+
994
+ @blocks = blocks_bak if blocks_bak
995
+
996
+ @html_tag_done = false
997
+ @html_tag_params.merge!(id_hash)
998
+ out render_html_tag(res)
999
+ end
1000
+
1001
+ # <r:checkbox role='collaborator_for' values='projects' in='site'/>"
1002
+ # TODO: implement checkbox in the same spirit as 'r_select'
1003
+ def r_checkbox
1004
+ return parser_error("missing 'nodes'") unless values = @params[:values] || @params[:nodes]
1005
+ return parser_error("missing 'role'") unless role = (@params[:role] || @params[:name])
1006
+ attribute = @params[:attr] || 'name'
1007
+ if role =~ /(.*)_ids?\Z/
1008
+ role = $1
1009
+ end
1010
+ meth = role.singularize
1011
+
1012
+ if values =~ /^\d+\s*($|,)/
1013
+ # ids
1014
+ # TODO generate the full query instead of using secure.
1015
+ values = values.split(',').map{|v| v.to_i}
1016
+ list_finder = "(secure(Node) { Node.find(:all, :conditions => 'zip IN (#{values.join(',')})') })"
1017
+ else
1018
+ # relation
1019
+ list_finder, klass = build_finder_for(:all, values)
1020
+ return unless list_finder
1021
+ return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1022
+ end
1023
+ out "<% if (#{list_var} = #{list_finder}) && (#{list_var}_relation = #{node}.relation_proxy(#{role.inspect})) -%>"
1024
+ out "<% if #{list_var}_relation.unique? -%>"
1025
+
1026
+ out "<% #{list_var}_id = #{list_var}_relation.other_id -%>"
1027
+ out "<div class='input_radio'><% #{list_var}.each do |#{var}| -%>"
1028
+ out "<span><input type='radio' name='node[#{meth}_id]' value='#{erb_node_id(var)}'<%= #{list_var}_id == #{var}[:id] ? \" checked='checked'\" : '' %>/> <%= #{node_attribute(attribute, :node=>var)} %></span> "
1029
+ out "<% end -%></div>"
1030
+ out "<input type='radio' name='node[#{meth}_id]' value=''/> #{_('none')}"
1031
+
1032
+ out "<% else -%>"
1033
+
1034
+ out "<% #{list_var}_ids = #{list_var}_relation.other_ids -%>"
1035
+ out "<div class='input_checkbox'><% #{list_var}.each do |#{var}| -%>"
1036
+ out "<span><input type='checkbox' name='node[#{meth}_ids][]' value='#{erb_node_id(var)}'<%= #{list_var}_ids.include?(#{var}[:id]) ? \" checked='checked'\" : '' %>/> <%= #{node_attribute(attribute, :node=>var)} %></span> "
1037
+ out "<% end -%></div>"
1038
+ out "<input type='hidden' name='node[#{meth}_ids][]' value=''/>"
1039
+
1040
+ out "<% end -%><% end -%>"
1041
+ end
1042
+
1043
+ alias r_radio r_checkbox
1044
+
1045
+ # TODO: test
1046
+ def r_add
1047
+ return parser_error("should not be called from within 'each'") if parent.method == 'each'
1048
+ return '' if @context[:make_form]
1049
+
1050
+ # why is node = @node (which we need) but we are supposed to have Comments ?
1051
+ # FIXME: during rewrite, replace 'node' by 'node(klass = node_class)' so the ugly lines below would be
1052
+ # if node_kind_of?(Comment)
1053
+ # out "<% if #{node(Node)}.can_comment? -%>"
1054
+ # Refs #198.
1055
+ if node_kind_of?(Comment)
1056
+ out "<% if #{node}.can_comment? -%>"
1057
+ else
1058
+ out "<% if #{node}.can_write? -%>"
1059
+ end
1060
+
1061
+ unless descendant('add_btn')
1062
+ # add a descendant between self and blocks.
1063
+ blocks = @blocks.dup
1064
+ @blocks = []
1065
+ add_btn = make(:void, :method=>'add_btn', :params=>@params.dup, :text=>'')
1066
+ add_btn.blocks = blocks
1067
+ remove_instance_variable(:@all_descendants)
1068
+ end
1069
+
1070
+ if @context[:form] && @context[:dom_prefix]
1071
+ # ajax add
1072
+
1073
+ @html_tag_params.merge!(:id => "#{erb_dom_id}_add")
1074
+ @html_tag_params[:class] ||= 'btn_add'
1075
+ if @params[:focus]
1076
+ focus = "$(\"#{erb_dom_id}_#{@params[:focus]}\").focus();"
1077
+ else
1078
+ focus = "$(\"#{erb_dom_id}_form_t\").focusFirstElement();"
1079
+ end
1080
+
1081
+ out render_html_tag("#{expand_with(:onclick=>"[\"#{erb_dom_id}_add\", \"#{erb_dom_id}_form\"].each(Element.toggle);#{focus}return false;")}")
1082
+
1083
+ if node_kind_of?(Node)
1084
+ # FIXME: BUG if we set <r:form klass='Post'/> the user cannot select class with menu...
1085
+ klass = @context[:klass] || 'Node'
1086
+ # FIXME: inspect '@context[:form]' to see if it contains v_klass ?
1087
+ out "<% if #{var}_new = secure(Node) { Node.new_from_class(#{klass.inspect}) } -%>"
1088
+ else
1089
+ out "<% if #{var}_new = #{node_class}.new -%>"
1090
+ end
1091
+
1092
+ if @context[:form].method == 'form'
1093
+ out expand_block(@context[:form], :in_add => true, :no_ignore => ['form'], :add=>self, :node => "#{var}_new", :parent_node => node, :klass => klass, :publish_after_save => auto_publish_param)
1094
+ else
1095
+ # build form from 'each'
1096
+ out expand_block(@context[:form], :in_add => true, :no_ignore => ['form'], :add=>self, :make_form => true, :node => "#{var}_new", :parent_node => node, :klass => klass, :publish_after_save => auto_publish_param)
1097
+ end
1098
+ out "<% end -%>"
1099
+ else
1100
+ # no ajax
1101
+ @html_tag_params[:class] ||= 'btn_add' if @html_tag
1102
+ out render_html_tag(expand_with)
1103
+ end
1104
+ out "<% end -%>"
1105
+ @html_tag_done = true
1106
+ end
1107
+
1108
+ def r_add_btn
1109
+ if @params[:text]
1110
+ text = @params[:text]
1111
+ text = "<div>#{text}</div>" unless @html_tag
1112
+ elsif @params[:trans]
1113
+ text = _(@params[:trans])
1114
+ text = "<div>#{text}</div>" unless @html_tag
1115
+ elsif @blocks != []
1116
+ text = expand_with
1117
+ else
1118
+ text = node_class == Comment ? _("btn_add_comment") : _("btn_add")
1119
+ end
1120
+
1121
+ out "<a href='#' onclick='#{@context[:onclick]}'>#{text}</a>"
1122
+ end
1123
+
1124
+ # Show html to add open a popup window to add a document.
1125
+ # TODO: inline ajax for upload ?
1126
+ def r_add_document
1127
+ return parser_error("only works with nodes (not with #{node_class})") unless node_kind_of?(Node)
1128
+ @html_tag_params[:class] ||= 'btn_add'
1129
+ res = "<a href='/documents/new?parent_id=#{erb_node_id}' onclick='uploader=window.open(\"/documents/new?parent_id=#{erb_node_id}\", \"upload\", \"width=400,height=300\");return false;'>#{_('btn_add_doc')}</a>"
1130
+ "<% if #{node}.can_write? -%>#{render_html_tag(res)}<% end -%>"
1131
+ end
1132
+
1133
+ #if RAILS_ENV == 'test'
1134
+ # def r_test
1135
+ # inspect
1136
+ # end
1137
+ #end
1138
+
1139
+ def r_drop
1140
+ if parent.method == 'each' && @method == parent.single_child_method
1141
+ parent.add_html_class('drop')
1142
+ else
1143
+ @html_tag_params[:class] ||= 'drop'
1144
+ end
1145
+ r_block
1146
+ end
1147
+
1148
+ def drop_javascript
1149
+ hover = @params[:hover]
1150
+ change = @params[:change]
1151
+
1152
+ if role = @params[:set] || @params[:add]
1153
+ query_params = ["node[#{role}_id]=[id]"]
1154
+ else
1155
+ query_params = []
1156
+ # set='icon_for=[id], v_status='50', v_title='[v_title]'
1157
+ @params.each do |k, v|
1158
+ next if [:hover, :change, :done].include?(k)
1159
+ value, static = parse_attributes_in_value(v, :erb => false, :skip_node_attributes => true)
1160
+ key = change == 'params' ? "params[#{k}]" : "node[#{k}]"
1161
+ query_params << "#{key}=#{CGI.escape(value)}"
1162
+ end
1163
+ return parser_error("missing parameters to set values") if query_params == []
1164
+ end
1165
+
1166
+ query_params << "change=#{change}" if change == 'receiver'
1167
+ query_params << "t_url=#{CGI.escape(template_url)}"
1168
+ query_params << "dom_id=#{erb_dom_id}"
1169
+ query_params << start_node_s_param(:erb)
1170
+ query_params << "done=#{CGI.escape(@params[:done])}" if @params[:done]
1171
+
1172
+ "<script type='text/javascript'>
1173
+ //<![CDATA[
1174
+ Droppables.add('#{erb_dom_id}', {hoverclass:'#{hover || 'drop_hover'}', onDrop:function(element){new Ajax.Request('/nodes/#{erb_node_id}/drop?#{query_params.join('&')}', {asynchronous:true, evalScripts:true, method:'put', parameters:'drop=' + encodeURIComponent(element.id)})}})
1175
+ //]]>
1176
+ </script>"
1177
+ end
1178
+
1179
+ def r_draggable
1180
+ new_dom_scope
1181
+ @html_tag ||= 'div'
1182
+ case @params[:revert]
1183
+ when 'move'
1184
+ revert_effect = 'Element.move'
1185
+ when 'remove'
1186
+ revert_effect = 'Element.remove'
1187
+ else
1188
+ revert_effect = 'Element.move'
1189
+ end
1190
+
1191
+ res, drag_handle = set_drag_handle_and_id(expand_with, @params, :id => erb_dom_id)
1192
+
1193
+ out render_html_tag(res)
1194
+
1195
+ if drag_handle
1196
+ out "<script type='text/javascript'>\n//<![CDATA[\n
1197
+ new Draggable('#{erb_dom_id}', {ghosting:true, revert:true, revertEffect:#{revert_effect}, handle:$('#{erb_dom_id}').select('.#{drag_handle}')[0]});\n//]]>\n</script>"
1198
+ else
1199
+ out "<script type='text/javascript'>\n//<![CDATA[\nZena.draggable('#{erb_dom_id}',0,true,true,#{revert_effect})\n//]]>\n</script>"
1200
+ end
1201
+ end
1202
+
1203
+ def r_unlink
1204
+ return "" if @context[:make_form]
1205
+ opts = {}
1206
+
1207
+ if upd = @params[:update]
1208
+ if upd == '_page'
1209
+ target = nil
1210
+ elsif target = find_target(upd)
1211
+ # ok
1212
+ else
1213
+ return
1214
+ end
1215
+ elsif target = ancestor('block')
1216
+ # ok
1217
+ else
1218
+ target = self
1219
+ end
1220
+
1221
+ if node_kind_of?(Node)
1222
+ opts[:cond] = "#{node}.can_write? && #{node}.link_id"
1223
+ opts[:url] = "/nodes/\#{#{node_id}}/links/\#{#{node}.link_id}"
1224
+ elsif node_kind_of?(Link)
1225
+ opts[:url] = "/nodes/\#{#{node}.this_zip}/links/\#{#{node}.zip}"
1226
+ end
1227
+
1228
+ opts[:method] = :delete
1229
+ opts[:default_text] = _('btn_tiny_del')
1230
+ opts[:html_params] = get_html_params({:class => 'unlink'}.merge(@params), :link)
1231
+
1232
+ out link_to_update(target, opts)
1233
+
1234
+ #tag_to_remote
1235
+ #"<%= tag_to_remote({:url => node_path(#{node_id}) + \"#{opts[:method] != :put ? '/zafu' : ''}?#{action.join('&')}\", :method => #{opts[:method].inspect}}) %>"
1236
+ # out "<a class='#{@params[:class] || 'unlink'}' href='/nodes/#{erb_node_id}/links/<%= #{node}.link_id %>?#{action}' onclick=\"new Ajax.Request('/nodes/#{erb_node_id}/links/<%= #{node}.link_id %>?#{action}', {asynchronous:true, evalScripts:true, method:'delete'}); return false;\">"
1237
+ # if !@blocks.empty?
1238
+ # inner = expand_with
1239
+ # else
1240
+ # inner = _('btn_tiny_del')
1241
+ # end
1242
+ # out "#{inner}</a><% else -%>#{inner}<% end -%>"
1243
+ #elsif node_kind_of?(DataEntry)
1244
+ # text = get_text_for_erb
1245
+ # if text.blank?
1246
+ # text = _('btn_tiny_del')
1247
+ # end
1248
+ # out "<%= link_to_remote(#{text.inspect}, {:url => \"/data_entries/\#{#{node}[:id]}?dom_id=#{dom_id}#{upd_url}\", :method => :delete}, :class=>#{(@params[:class] || 'unlink').inspect}) %>"
1249
+ #end
1250
+ end
1251
+
1252
+ # Group elements in a list. Use :order to specify order.
1253
+ def r_group
1254
+ return parser_error("cannot be used outside of a list") unless list_var = @context[:list]
1255
+ return parser_error("missing 'by' clause") unless key = @params[:by]
1256
+
1257
+ sort_key = @params[:sort] || 'name'
1258
+ if node_kind_of?(DataEntry) && DataEntry::NodeLinkSymbols.include?(key.to_sym)
1259
+ key = "#{key}_id"
1260
+ sort_block = "{|e| (e.#{key} || {})[#{sort_key.to_sym.inspect}]}"
1261
+ group_array = "group_array(#{list_var}) {|e| e.#{key}}"
1262
+ elsif node_kind_of?(Node)
1263
+ if ['project', 'parent', 'section'].include?(key)
1264
+ sort_block = "{|e| (e.#{key} || {})[#{sort_key.to_sym.inspect}]}"
1265
+ group_array = "group_array(#{list_var}) {|e| e.#{key}_id}"
1266
+ end
1267
+ end
1268
+
1269
+ group_array ||= "group_array(#{list_var}) {|e| #{node_attribute(key, :node => 'e')}}"
1270
+
1271
+ if sort_block
1272
+ out "<% grp_#{list_var} = sort_array(#{group_array}) #{sort_block} -%>"
1273
+ else
1274
+ out "<% grp_#{list_var} = #{group_array} -%>"
1275
+ end
1276
+
1277
+ if descendant('each_group')
1278
+ out expand_with(:group => "grp_#{list_var}")
1279
+ else
1280
+ @context[:group] = "grp_#{list_var}"
1281
+ r_each_group
1282
+ end
1283
+ end
1284
+
1285
+
1286
+ # Compute statistics on elements in the current list context.
1287
+ def r_stat
1288
+ return parser_error("must be used inside a list context") unless list
1289
+ find = @params[:find] || @params[:date] || 'count'
1290
+ key = @params[:of] || @params[:from] || 'value'
1291
+ case find
1292
+ when 'sum'
1293
+ value = "#{list}.flatten.inject(0) {|#{var}_sum,#{var}| #{var}_sum + #{node_attribute(key, :node => var)}.to_f}"
1294
+ when 'min'
1295
+ value = "#{node_attribute(key, :node => "min_array(#{list}) {|e| #{node_attribute(key, :node => 'e')}}")}"
1296
+ when 'max'
1297
+ value = "#{node_attribute(key, :node => "max_array(#{list}) {|e| #{node_attribute(key, :node => 'e')}}")}"
1298
+ when 'count'
1299
+ return "<%= #{list}.size %>"
1300
+ end
1301
+ if @params[:date]
1302
+ # FIXME: DRY (r_show)
1303
+ if @params[:tformat]
1304
+ format = _(@params[:tformat])
1305
+ elsif @params[:format]
1306
+ format = @params[:format]
1307
+ else
1308
+ format = "%Y-%m-%d"
1309
+ end
1310
+ "<%= #{list}==[] ? '' : format_date(#{value}, #{format.inspect}) %>"
1311
+ elsif format = @params[:format]
1312
+
1313
+ if @params[:zero] == 'hide'
1314
+ "<%= #{list}==[] ? '' : sprintf_unless_zero(#{@params[:format].inspect}, #{value}) %>"
1315
+ else
1316
+ "<%= #{list}==[] ? '' : sprintf(#{@params[:format].inspect}, #{value}) %>"
1317
+ end
1318
+ else
1319
+ "<%= #{list}==[] ? '' : #{value} %>"
1320
+ end
1321
+ end
1322
+
1323
+ def r_each_group
1324
+ return parser_error("must be used inside a group context") unless group = @context[:group]
1325
+ if join = @params[:join]
1326
+ join = join.gsub(/&lt;([^%])/, '<\1').gsub(/([^%])&gt;/, '\1>')
1327
+ out "<% #{group}.each_index do |#{list_var}_index| -%>"
1328
+ out "<%= #{list_var}=#{group}[#{list_var}_index]; #{var} = #{list_var}[0]; #{list_var}_index > 0 ? #{join.inspect} : '' %>"
1329
+ else
1330
+ out "<% #{group}.each do |#{list_var}|; #{var} = #{list_var}[0]; -%>"
1331
+ end
1332
+ out render_html_tag(expand_with(:group => nil, :list => list_var, :node => var, :scope_node => var))
1333
+ out "<% end -%>"
1334
+ end
1335
+
1336
+ def r_each
1337
+ is_draggable = @params[:draggable] == 'true' || @params[:drag_handle]
1338
+
1339
+ if descendant('edit') || descendant('unlink') || descendant('swap') || ['block', 'drop'].include?(single_child_method) || is_draggable
1340
+ id_hash = {:id => erb_dom_id}
1341
+ else
1342
+ id_hash = nil
1343
+ end
1344
+
1345
+
1346
+ if @context[:make_form]
1347
+ # use the elements inside 'each' loop to produce the edit form
1348
+ r_form
1349
+ elsif @context[:list]
1350
+ # normal rendering: not the start of a saved template
1351
+ if is_draggable || descendant('unlink')
1352
+ out "<% #{var}_dom_ids = [] -%>"
1353
+ end
1354
+
1355
+ @params[:alt_class] ||= @html_tag_params.delete(:alt_class)
1356
+ # FIXME: add alt_reverse='true' to start counting from bottom (if order last on top...)
1357
+ if @params[:alt_class] || @params[:join]
1358
+ join = @params[:join] || ''
1359
+ join = join.gsub(/&lt;([^%])/, '<\1').gsub(/([^%])&gt;/, '\1>')
1360
+ out "<% #{var}_max_index = #{list}.size - 1 -%>" if @params[:alt_reverse]
1361
+ out "<% #{list}.each_with_index do |#{var},#{var}_index| -%>"
1362
+
1363
+ if join_clause = @params[:join_if]
1364
+ set_stored(Node, 'prev', "#{var}_prev")
1365
+ cond = get_test_condition(var, :test=>join_clause)
1366
+ out "<%= #{var}_prev = #{list}[#{var}_index - 1]; (#{var}_index > 0 && #{cond}) ? #{join.inspect} : '' %>"
1367
+ else
1368
+ out "<%= #{var}_index > 0 ? #{join.inspect} : '' %>"
1369
+ end
1370
+
1371
+ if alt_class = @params[:alt_class]
1372
+ alt_test = @params[:alt_reverse] == 'true' ? "(#{var}_max_index - #{var}_index) % 2 != 0" : "#{var}_index % 2 != 0"
1373
+ if html_class = @html_tag_params.delete(:class)
1374
+ html_append = " class='#{html_class}<%= #{alt_test} ? #{(' ' + alt_class).inspect} : '' %>'"
1375
+ else
1376
+ html_append = "<%= #{alt_test} ? ' class=#{alt_class.inspect}' : '' %>"
1377
+ end
1378
+ else
1379
+ html_append = nil
1380
+ end
1381
+ else
1382
+ out "<% #{list}.each do |#{var}| -%>"
1383
+ html_append = nil
1384
+ end
1385
+
1386
+ if is_draggable
1387
+ out "<% #{var}_dom_ids << \"#{dom_id}\" -%>"
1388
+ end
1389
+
1390
+ out r_anchor(var) if @anchor_param # insert anchor inside the each loop
1391
+ @params[:anchor] = @anchor_param # set back in case we double render
1392
+ @anchor_param = nil
1393
+
1394
+ res, drag_handle = set_drag_handle_and_id(expand_with(:node => var, :scope_node => var), @params, id_hash)
1395
+
1396
+ out render_html_tag(res, html_append)
1397
+
1398
+ out "<% end -%>"
1399
+
1400
+ if is_draggable
1401
+ if drag_handle
1402
+ out "<script type='text/javascript'>\n//<![CDATA[\n<%= #{var}_dom_ids.inspect %>.each(function(dom_id, index) {
1403
+ new Draggable(dom_id, {ghosting:true, revert:true, handle:$(dom_id).select('.#{drag_handle}')[0]});
1404
+ });\n//]]>\n</script>"
1405
+ else
1406
+ out "<script type='text/javascript'>\n//<![CDATA[\n<%= #{var}_dom_ids.inspect %>.each(Zena.draggable)\n//]]>\n</script>"
1407
+ end
1408
+ end
1409
+
1410
+ elsif @context[:saved_template]
1411
+ # render to start a saved template
1412
+ res, drag_handle = set_drag_handle_and_id(expand_with(:scope_node => node), @params, id_hash)
1413
+
1414
+ out render_html_tag(res)
1415
+
1416
+ if is_draggable
1417
+ if drag_handle
1418
+ out "<script type='text/javascript'>\n//<![CDATA[\nnew Draggable('#{erb_dom_id}', {ghosting:true, revert:true, handle:$('#{erb_dom_id}').select('.#{drag_handle}')[0]});\n//]]>\n</script>"
1419
+ else
1420
+ out "<script type='text/javascript'>\n//<![CDATA[\nZena.draggable('#{erb_dom_id}')\n//]]>\n</script>"
1421
+ end
1422
+ end
1423
+ else
1424
+ # TODO: make a single list ?
1425
+ @context[:list] = "[#{node}]"
1426
+ r_each
1427
+ end
1428
+ end
1429
+
1430
+ def r_case
1431
+ out "<% if false -%>"
1432
+ out expand_with(:in_if=>true, :only=>['when', 'else'], :html_tag => @html_tag, :html_tag_params => @html_tag_params)
1433
+ @html_tag_done = true
1434
+ out "<% end -%>"
1435
+ end
1436
+
1437
+ # TODO: test
1438
+ def r_if
1439
+ cond = get_test_condition
1440
+ return parser_error("condition error") unless cond
1441
+
1442
+ if cond == 'true'
1443
+ return expand_with(:in_if => false)
1444
+ elsif cond == 'false'
1445
+ if descendant('else') || descendant('elsif')
1446
+ return expand_with(:in_if=>true, :only=>['elsif', 'else'])
1447
+ else
1448
+ @html_tag_done = true
1449
+ return ''
1450
+ end
1451
+ end
1452
+
1453
+ out "<% if #{cond} -%>"
1454
+ out render_html_tag(expand_with(:in_if=>false))
1455
+ out expand_with(:in_if=>true, :only=>['elsif', 'else'], :html_tag => @html_tag, :html_tag_params => @html_tag_params)
1456
+ out "<% end -%>"
1457
+ end
1458
+
1459
+ def r_else
1460
+ if @context[:in_if]
1461
+ @html_tag = @context[:html_tag]
1462
+ @html_tag_params = @context[:html_tag_params] || {}
1463
+ out "<% elsif true -%>"
1464
+ if @params[:text]
1465
+ out render_html_tag(@params[:text])
1466
+ else
1467
+ out render_html_tag(expand_with(:in_if=>false, :only => nil)) # do not propagate :only from ancestor 'if' clause
1468
+ end
1469
+ else
1470
+ ""
1471
+ end
1472
+ end
1473
+
1474
+ def r_elsif
1475
+ return '' unless @context[:in_if]
1476
+ @html_tag = @context[:html_tag]
1477
+ @html_tag_params = @context[:html_tag_params] || {}
1478
+ cond = get_test_condition
1479
+ return parser_error("condition error") unless cond
1480
+ out "<% elsif #{cond} -%>"
1481
+ out render_html_tag(expand_with(:in_if=>false, :only => nil)) # do not propagate :only from ancestor 'if' clause
1482
+ end
1483
+
1484
+ def r_when
1485
+ r_elsif
1486
+ end
1487
+
1488
+ # be carefull, this gives a list of 'versions', not 'nodes'
1489
+ def r_traductions
1490
+ if @params[:except]
1491
+ case @params[:except]
1492
+ when 'current'
1493
+ opts = "(:conditions=>\"lang != '#{helper.lang}'\")"
1494
+ else
1495
+ # list of lang
1496
+ # TODO: test
1497
+ langs = @params[:except].split(',').map{|l| l.gsub(/[^a-z]/,'').strip }
1498
+ opts = "(:conditions=>\"lang NOT IN ('#{langs.join("','")}')\")"
1499
+ end
1500
+ elsif @params[:only]
1501
+ # TODO: test
1502
+ case @params[:only]
1503
+ when 'current'
1504
+ opts = "(:conditions=>\"lang = '#{helper.lang}'\")"
1505
+ else
1506
+ # list of lang
1507
+ # TODO: test
1508
+ langs = @params[:only].split(',').map{|l| l.gsub(/[^a-z]/,'').strip }
1509
+ opts = "(:conditions=>\"lang IN ('#{langs.join("','")}')\")"
1510
+ end
1511
+ else
1512
+ opts = ""
1513
+ end
1514
+ out "<% if #{list_var} = #{node}.traductions#{opts} -%>"
1515
+ out expand_with(:list=>list_var, :node_class => Version)
1516
+ out "<% end -%>"
1517
+ end
1518
+
1519
+ # TODO: test
1520
+ def r_show_traductions
1521
+ "<% if #{list_var} = #{node}.traductions -%>"
1522
+ "#{_("Traductions:")} <span class='traductions'><%= #{list_var}.join(', ') %></span>"
1523
+ "<%= traductions(:node=>#{node}).join(', ') %>"
1524
+ end
1525
+
1526
+ def r_node
1527
+ @method = @params[:select] || 'node' # 'node' is for version.node
1528
+ r_unknown
1529
+ end
1530
+
1531
+ # icon or first image (defined using build_finder_for instead of zafu_known_context for performance reasons).
1532
+ def r_icon
1533
+ if !@params[:in] && !@params[:where] && !@params[:from] && !@params[:find]
1534
+ finder, klass = build_finder_for(:first, 'icon', @params.merge(:or => 'image', :order => 'l_id desc, position asc, name asc', :group => 'id,l_id'))
1535
+ return unless finder
1536
+ return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1537
+ do_var(finder, :node_class => klass)
1538
+ else
1539
+ r_unknown
1540
+ end
1541
+ end
1542
+
1543
+ def r_date
1544
+ select = @params[:select]
1545
+ case select
1546
+ when 'main'
1547
+ expand_with(:date=>'main_date')
1548
+ when 'now'
1549
+ expand_with(:date=>'Time.now')
1550
+ else
1551
+ if select =~ /^\d{4}-\d{1,2}-\d{1,2}$/
1552
+ begin
1553
+ d = Date.parse(select)
1554
+ expand_with(:date=>select.inspect)
1555
+ rescue
1556
+ parser_error("invalid date '#{select}' should be 'YYYY-MM-DD'")
1557
+ end
1558
+ elsif date = find_stored(Date, select)
1559
+ if date[0..0] == '"'
1560
+ begin
1561
+ d = Date.parse(date[1..-2])
1562
+ expand_with(:date=>date)
1563
+ rescue
1564
+ parser_error("invalid date #{select} (#{date}) should be 'YYYY-MM-DD'")
1565
+ end
1566
+ else
1567
+ expand_with(:date=>select)
1568
+ end
1569
+ elsif select =~ /\[(.*)\]/
1570
+ date, static = parse_attributes_in_value(select, :erb => false)
1571
+ expand_with(:date => "\"#{date}\"")
1572
+ else
1573
+ parser_error("bad parameter '#{select}'")
1574
+ end
1575
+ end
1576
+ end
1577
+
1578
+ def r_javascripts
1579
+ if @params[:list].nil?
1580
+ list = %w{ prototype effects tablekit zena }
1581
+ elsif @params[:list] == 'all'
1582
+ list = %w{ prototype effects dragdrop tablekit zena }
1583
+ else
1584
+ list = @params[:list].split(',').map{|e| e.strip}
1585
+ end
1586
+ helper.javascript_include_tag(*list)
1587
+ end
1588
+
1589
+ def r_stylesheets
1590
+ if @params[:list] == 'all' || @params[:list].nil?
1591
+ list = %w{ zena code }
1592
+ else
1593
+ list = @params[:list].split(',').map{|e| e.strip}
1594
+ end
1595
+ list << {:media => @params[:media]} if @params[:media]
1596
+ helper.stylesheet_link_tag(*list)
1597
+ end
1598
+
1599
+ def r_flash_messages
1600
+ type = @params[:show] || 'both'
1601
+ "<div id='messages'>" +
1602
+ if (type == 'notice' || type == 'both')
1603
+ "<% if flash[:notice] -%><div id='notice' class='flash' onclick='new Effect.Fade(\"error\")'><%= flash[:notice] %></div><% end -%>"
1604
+ else
1605
+ ''
1606
+ end +
1607
+ if (type == 'error' || type == 'both')
1608
+ "<% if flash[:error] -%><div id='error' class='flash' onclick='new Effect.Fade(\"error\")'><%= flash[:error] %></div><% end -%>"
1609
+ else
1610
+ ''
1611
+ end +
1612
+ "</div>"
1613
+ end
1614
+
1615
+ # Shows a 'made with zena' link or logo. ;-) Thanks for using this !
1616
+ # TODO: test and add translation.
1617
+ # <r:zena show='logo'/> or <r:zena show='text'/> == <r:zena/>
1618
+ def r_zena
1619
+ if logo = @params[:logo]
1620
+ # FIXME
1621
+ case logo
1622
+ when 'tiny'
1623
+ else
1624
+ end
1625
+ else
1626
+ text = case @params[:type]
1627
+ when 'riding'
1628
+ _("riding zena")
1629
+ when 'peace'
1630
+ _("in peace with zena")
1631
+ when 'garden'
1632
+ _("a zen garden")
1633
+ else
1634
+ _("made with zena")
1635
+ end
1636
+ "<a class='zena' href='http://zenadmin.org' title='zena <%= Zena::VERSION %>'>#{text}</a>"
1637
+ end
1638
+ end
1639
+
1640
+ def r_design
1641
+ if @params[:by]
1642
+ by = "<a href='#{@params[:href]}'>#{@params[:by]}</a>"
1643
+ else
1644
+ by = expand_with(:trans => true)
1645
+ end
1646
+ unless skin = @params[:skin]
1647
+ skin = helper.instance_variable_get(:@controller).instance_variable_get(:@skin_name)
1648
+ end
1649
+ skin = "<i>#{skin}</i>" unless skin.blank?
1650
+ _("%{skin}, design by %{name}") % {:name => by, :skin => skin}
1651
+ end
1652
+
1653
+ # creates a link. Options are:
1654
+ # :href (node, parent, project, root)
1655
+ # :tattr (translated attribute used as text link)
1656
+ # :attr (attribute used as text link)
1657
+ # <r:link href='node'><r:trans attr='lang'/></r:link>
1658
+ # <r:link href='node' tattr='lang'/>
1659
+ # <r:link update='dom_id'/>
1660
+ # <r:link page='next'/> <r:link page='previous'/> <r:link page='list'/>
1661
+ def r_link
1662
+ if @params[:page]
1663
+ pagination_links
1664
+ else
1665
+ make_link
1666
+ end
1667
+ end
1668
+
1669
+ def make_link(options = {})
1670
+ query_params = options[:query_params] || {}
1671
+ default_text = options[:default_text]
1672
+ params = @params.dup
1673
+
1674
+ opts = {}
1675
+ if upd = params.delete(:update)
1676
+ return unless remote_target = find_target(upd)
1677
+ end
1678
+
1679
+ if href = params.delete(:href)
1680
+ if lnode = find_stored(Node, href)
1681
+ # using stored node
1682
+ else
1683
+ lnode, klass = build_finder_for(:first, href, {})
1684
+ return unless lnode
1685
+ return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1686
+ end
1687
+ else
1688
+ # obj
1689
+ if node_class == Version
1690
+ lnode = "#{node}.node"
1691
+ opts[:lang] = "#{node}.lang"
1692
+ elsif node_kind_of?(Node)
1693
+ lnode = node
1694
+ else
1695
+ lnode = @context[:previous_node]
1696
+ end
1697
+ end
1698
+
1699
+ if fmt = params.delete(:format)
1700
+ if fmt == 'data'
1701
+ opts[:format] = "#{node}.c_ext"
1702
+ else
1703
+ opts[:format] = fmt.inspect
1704
+ end
1705
+ end
1706
+
1707
+ if mode = params.delete(:mode)
1708
+ opts[:mode] = mode.inspect
1709
+ end
1710
+
1711
+ if sharp = params.delete(:sharp)
1712
+ opts[:sharp] = sharp.inspect
1713
+ end
1714
+
1715
+ if sharp_in = params.delete(:in)
1716
+ finder, klass = build_finder_for(:first, sharp_in, {})
1717
+ return unless finder
1718
+ return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1719
+ opts[:sharp_in] = finder
1720
+ end
1721
+
1722
+ if @html_tag && @html_tag != 'a'
1723
+ # FIXME: can we remove this ?
1724
+ # html attributes do not belong to anchor
1725
+ pre_space = ''
1726
+ html_params = {}
1727
+ else
1728
+ html_params = get_html_params(params.merge(@html_tag_params), :link)
1729
+ pre_space = @space_before || ''
1730
+ @html_tag_done = true
1731
+ end
1732
+
1733
+ (params.keys - [:style, :class, :id, :rel, :name, :anchor, :attr, :tattr, :trans, :text, :page]).each do |k|
1734
+ next if k.to_s =~ /if_|set_/
1735
+ query_params[k] = params[k]
1736
+ end
1737
+
1738
+ # TODO: merge these two query_params cleanup things into something cleaner.
1739
+ if remote_target
1740
+ # ajax
1741
+ query_params_list = []
1742
+ query_params.each do |k,v|
1743
+ if k == :date
1744
+ if v == 'current_date'
1745
+ str = "\#{#{current_date}}"
1746
+ elsif v =~ /\A\d/
1747
+ str = CGI.escape(v.gsub('"',''))
1748
+ elsif v =~ /\[/
1749
+ attribute, static = parse_attributes_in_value(v.gsub('"',''), :erb => false)
1750
+ str = static ? CGI.escape(attribute) : "\#{CGI.escape(\"#{attribute}\")}"
1751
+ else
1752
+ str = "\#{CGI.escape(#{node_attribute(v)})}"
1753
+ end
1754
+ else
1755
+ attribute, static = parse_attributes_in_value(v.gsub('"',''), :erb => false)
1756
+ str = static ? CGI.escape(attribute) : "\#{CGI.escape(\"#{attribute}\")}"
1757
+ end
1758
+ query_params_list << "#{k.to_s.gsub('"','')}=#{str}"
1759
+ end
1760
+ pre_space + link_to_update(remote_target, :node_id => "#{lnode}.zip", :query_params => query_params_list, :default_text => default_text, :html_params => html_params)
1761
+ else
1762
+ # direct link
1763
+ query_params.each do |k,v|
1764
+ if k == :date
1765
+ if v == 'current_date'
1766
+ query_params[k] = current_date
1767
+ elsif v =~ /\A\d/
1768
+ query_params[k] = v.inspect
1769
+ elsif v =~ /\[/
1770
+ attribute, static = parse_attributes_in_value(v.gsub('"',''), :erb => false)
1771
+ query_params[k] = "\"#{attribute}\""
1772
+ else
1773
+ query_params[k] = node_attribute(v)
1774
+ end
1775
+ else
1776
+ attribute, static = parse_attributes_in_value(v.gsub('"',''), :erb => false)
1777
+ query_params[k] = "\"#{attribute}\""
1778
+ end
1779
+ end
1780
+
1781
+ query_params.merge!(opts)
1782
+
1783
+ opts_str = ''
1784
+ query_params.keys.sort {|a,b| a.to_s <=> b.to_s }.each do |k|
1785
+ opts_str << ",:#{k.to_s.gsub(/[^a-z_A-Z_]/,'')}=>#{query_params[k]}"
1786
+ end
1787
+
1788
+ opts_str += ", :host => #{@context["exp_host"]}" if @context["exp_host"]
1789
+
1790
+ pre_space + "<a#{params_to_html(html_params)} href='<%= zen_path(#{lnode}#{opts_str}) %>'>#{text_for_link(default_text)}</a>"
1791
+ end
1792
+ end
1793
+
1794
+ # <r:link page='next'/> <r:link page='previous'/> <r:link page='list'/>
1795
+ def pagination_links
1796
+ return parser_error("not in pagination scope") unless pagination_key = @context[:paginate]
1797
+ case @params[:page]
1798
+ when 'previous'
1799
+ out "<% if set_#{pagination_key}_previous = (set_#{pagination_key} > 1 ? set_#{pagination_key} - 1 : nil) -%>"
1800
+ @context[:vars] ||= []
1801
+ @context[:vars] << "#{pagination_key}_previous"
1802
+ out make_link(:default_text => "<%= set_#{pagination_key}_previous %>", :query_params => {pagination_key => "[#{pagination_key}_previous]"})
1803
+ if descendant('else')
1804
+ out expand_with(:in_if => true, :only => ['else', 'elsif'])
1805
+ end
1806
+ out "<% end -%>"
1807
+ when 'next'
1808
+ out "<% if set_#{pagination_key}_next = (set_#{pagination_key}_count - set_#{pagination_key} > 0 ? set_#{pagination_key} + 1 : nil) -%>"
1809
+ @context[:vars] ||= []
1810
+ @context[:vars] << "#{pagination_key}_next"
1811
+ out make_link(:default_text => "<%= set_#{pagination_key}_next %>", :query_params => {pagination_key => "[#{pagination_key}_next]"})
1812
+ if descendant('else')
1813
+ out expand_with(:in_if => true, :only => ['else', 'elsif'])
1814
+ end
1815
+ out "<% end -%>"
1816
+ when 'list'
1817
+ @context[:vars] ||= []
1818
+ @context[:vars] << "#{pagination_key}_page"
1819
+ if @blocks == [] || (@blocks.size == 1 && !@blocks.first.kind_of?(String) && @blocks.first.method == 'else')
1820
+ # add a default block
1821
+ if tag = @params[:tag]
1822
+ open_tag = "<#{tag}>"
1823
+ close_tag = "</#{tag}>"
1824
+ else
1825
+ open_tag = close_tag = ''
1826
+ end
1827
+ link_params = {}
1828
+ @params.each do |k,v|
1829
+ next if [:tag, :page, :join, :page_count].include?(k)
1830
+ link_params[k] = v
1831
+ end
1832
+ text = "#{open_tag}<r:link #{params_to_html(link_params)} #{pagination_key}='[#{pagination_key}_page]' do='[#{pagination_key}_page]'/>#{close_tag}"
1833
+ @blocks = [make(:void, :method=>'void', :text=>text)]
1834
+ remove_instance_variable(:@all_descendants)
1835
+ end
1836
+
1837
+ if !descendant('else')
1838
+ @blocks += [make(:void, :method=>'void', :text=>"<r:else>#{open_tag}<r:show var='#{pagination_key}_page'/>#{close_tag}</r:else>")]
1839
+ remove_instance_variable(:@all_descendants)
1840
+ end
1841
+
1842
+ out "<% page_numbers(set_#{pagination_key}, set_#{pagination_key}_count, #{(@params[:join] || ' ').inspect}, #{@params[:page_count] ? @params[:page_count].to_i : 'nil'}) do |set_#{pagination_key}_page, #{pagination_key}_page_join| %>"
1843
+ out "<%= #{pagination_key}_page_join %>"
1844
+ out "<% if set_#{pagination_key}_page != set_#{pagination_key} -%>"
1845
+ out expand_with(:in_if => true)
1846
+ out "<% end; end -%>"
1847
+ else
1848
+ parser_error("unkown 'page' option #{@params[:page].inspect} should be ('previous', 'next' or 'list')")
1849
+ end
1850
+ end
1851
+
1852
+ def r_img
1853
+ return unless node_kind_of?(Node)
1854
+ if @params[:src]
1855
+ finder, klass = build_finder_for(:first, @params[:src])
1856
+ return unless finder
1857
+ return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1858
+ img = finder
1859
+ else
1860
+ img = node
1861
+ end
1862
+ mode = @params[:mode] || 'std'
1863
+ # FIXME: replace this call by something that integrates better with html_tag_params and such.
1864
+ res = "img_tag(#{img}, :mode=>#{mode.inspect}"
1865
+ [:class, :alt_src, :id, :border, :style].each do |k|
1866
+ res += ", :#{k}=>#{@params[k].inspect}" if @params[k]
1867
+ end
1868
+ res += ", :host => #{@context["exp_host"]}" if @context["exp_host"]
1869
+ res += ")"
1870
+ if @params[:link]
1871
+ finder, klass = build_finder_for(:first, @params[:link])
1872
+ return unless finder
1873
+ return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1874
+
1875
+ opts_str = @context["exp_host"] ? ", :host => #{@context["exp_host"]}" : ""
1876
+
1877
+ "<a href='<%= zen_path(#{finder}#{opts_str}) %>'><%= #{res} %></a>"
1878
+ else
1879
+ "<%= #{res} %>"
1880
+ end
1881
+ end
1882
+
1883
+ def r_calendar
1884
+ if @context[:block] == self
1885
+ # called from self (storing template / rendering)
1886
+ if role = @params[:assign_as]
1887
+ assign_calendar(role)
1888
+ else
1889
+ display_calendar
1890
+ end
1891
+ else
1892
+ # This is called first to prepare calendar
1893
+ if @params[:assign_as]
1894
+ fld = 'date'
1895
+ table_name = 'links'
1896
+ else
1897
+ fld = @params[:date] || 'event_at'
1898
+ if ['log_at', 'created_at', 'updated_at', 'event_at'].include?(fld) # TODO: use rubyless to learn type
1899
+ table_name = 'nodes'
1900
+ elsif fld == 'l_date'
1901
+ fld = 'date'
1902
+ table_name = 'links'
1903
+ else
1904
+ return parser_error("Invalid 'date' value for calendar (#{fld.inspect}).")
1905
+ end
1906
+ end
1907
+
1908
+ @date_scope = "TABLE_NAME[#{table_name}].#{fld} >= '\#{start_date.strftime('%Y-%m-%d')}' AND TABLE_NAME[#{table_name}].#{fld} <= '\#{end_date.strftime('%Y-%m-%d')}'"
1909
+
1910
+ new_dom_scope
1911
+
1912
+ # SAVED TEMPLATE
1913
+ template = expand_block(self, :block => self, :saved_template => true)
1914
+ out helper.save_erb_to_url(template, template_url)
1915
+
1916
+ # INLINE
1917
+ out expand_block(self, :block => self, :saved_template => false)
1918
+ end
1919
+ end
1920
+
1921
+ def display_calendar
1922
+ size = (params[:size] || 'large').to_sym
1923
+ finder = params[:select] || 'notes in project'
1924
+
1925
+ if @blocks == []
1926
+ # add a default <r:link/> block
1927
+ if size == :tiny
1928
+ @blocks = [make(:void, :method=>'void', :text=>"<em do='link' date='current_date' do='[current_date]' format='%d'/><r:else do='[current_date]' format='%d'/>")]
1929
+ else
1930
+ @blocks = [make(:void, :method=>'void', :text=>"<span do='show' date='current_date' format='%d'/><ul><li do='each' do='link' attr='name'/></ul><r:else do='[current_date]' format='%d'/>")]
1931
+ end
1932
+ remove_instance_variable(:@all_descendants)
1933
+ elsif !descendant('else')
1934
+ @blocks += [make(:void, :method=>'void', :text=>"<r:else do='[current_date]' format='%d'/>")]
1935
+ remove_instance_variable(:@all_descendants)
1936
+ end
1937
+
1938
+ @html_tag_done = false
1939
+ @html_tag_params[:id] = erb_dom_id
1940
+ @html_tag_params[:class] ||= "#{size}cal"
1941
+ @html_tag ||= 'div'
1942
+
1943
+ finder, klass = build_finder_for(:all, finder, @params, [@date_scope])
1944
+ return unless finder
1945
+ return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1946
+
1947
+ cell_code = "<% if #{list_var} = nodes_#{list_var}[cal_#{list_var}.strftime('%Y-%m-%d %H')] -%>#{expand_with(:in_if => true, :list => list_var, :date => "cal_#{list_var}", :saved_template => nil, :dom_prefix => nil, :in_calendar => true)}<% end -%>"
1948
+
1949
+ render_html_tag(calendar_code(finder, "", cell_code, "", params))
1950
+ end
1951
+
1952
+ # manage links from @node ---- reference ----> ...
1953
+ # <div do='calendar' assign='reference' to='main' split_hours='12' />
1954
+ def assign_calendar(as_role)
1955
+ size = (params[:size] || 'large').to_sym
1956
+ @html_tag_done = false
1957
+ @html_tag_params[:id] = erb_dom_id
1958
+ @html_tag_params[:class] ||= "#{size}cal"
1959
+ @html_tag ||= 'div'
1960
+ if rel = RelationProxy.find_by_role(as_role.singularize)
1961
+ role = rel.this_role
1962
+ else
1963
+ return parser_error("Invalid role #{as_role.inspect}.")
1964
+ end
1965
+ finder, klass = build_finder_for(:all, role, @params, [@date_scope])
1966
+ return unless finder
1967
+ return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1968
+
1969
+ # SAVED TEMPLATE ========
1970
+ template_url = self.template_url + 'cell'
1971
+ template = "<%= cal_assign_cell(@node, #{role.inspect}, #{@params[:used].inspect}) %>"
1972
+ out helper.save_erb_to_url(template, template_url)
1973
+
1974
+ # we call update on node 'B'
1975
+ # A (main)
1976
+ # ... B (other node)
1977
+ # calendar (in B context) ---- role --->
1978
+
1979
+ cell_prefix_code = "<span><%= day_#{list_var}.strftime('%d').to_i -%></span><ul>"
1980
+ cell_code = "<%= #{list_var} = nodes_#{list_var}[cal_#{list_var}.strftime('%Y-%m-%d %H')]; #{node}.linked_node = #{list_var} ? #{list_var}.first : nil; cal_assign_cell(#{node}, #{role.inspect}, #{@params[:used].inspect}, params[:s] || @node.zip, cal_#{list_var}, #{template_url.inspect}) %>"
1981
+ cell_postfix_code = "</ul>"
1982
+ render_html_tag(calendar_code(finder, cell_prefix_code, cell_code, cell_postfix_code, params))
1983
+ end
1984
+
1985
+ def calendar_code(finder, cell_prefix_code, cell_code, cell_postfix_code, params)
1986
+ type = params[:type] ? params[:type].to_sym : :month
1987
+ size = (params[:size] || 'large').to_sym
1988
+ ref_date = params[:assign_as] ? 'l_date' : (params[:date] || 'event_at')
1989
+
1990
+ case type
1991
+ when :month
1992
+ title = "\"\#{_(Date::MONTHNAMES[main_date.mon])} \#{main_date.year}\""
1993
+ prev_date = "\#{main_date.advance(:months => -1).strftime(\"%Y-%m-%d\")}"
1994
+ next_date = "\#{main_date.advance(:months => 1).strftime(\"%Y-%m-%d\")}"
1995
+ when :week
1996
+ title = "\"\#{_(Date::MONTHNAMES[main_date.mon])} \#{main_date.year}\""
1997
+ prev_date = "\#{main_date.advance(:days => -7).strftime(\"%Y-%m-%d\")}"
1998
+ next_date = "\#{main_date.advance(:days => +7).strftime(\"%Y-%m-%d\")}"
1999
+ else
2000
+ return parser_error("invalid type (should be 'month' or 'week')")
2001
+ end
2002
+
2003
+ if hours = @params[:split_hours]
2004
+ hours = hours.split(',').map{|l| l.to_i}
2005
+ hours << 0
2006
+ hours = hours.uniq.sort
2007
+ # I feel all this would be much better if we could use "each_group" but then how do we access hours ?
2008
+ week_code = "<% week.step(week+6,1) do |day_#{list_var}| -%>
2009
+ <td<%= cal_class(day_#{list_var},#{current_date}) %>>#{cell_prefix_code}<% #{hours.inspect}.each do |set_hour|; cal_#{list_var} = Time.utc(day_#{list_var}.year,day_#{list_var}.month,day_#{list_var}.day,set_hour) -%>#{cell_code}<% end -%>#{cell_postfix_code}</td>
2010
+ <% end -%>"
2011
+ (@context[:vars] ||= []) << "hour"
2012
+ else
2013
+ hours = nil
2014
+ week_code = "<% week.step(week+6,1) do |day_#{list_var}| -%>
2015
+ <td<%= cal_class(day_#{list_var},#{current_date}) %>><% cal_#{list_var} = Time.utc(day_#{list_var}.year,day_#{list_var}.month,day_#{list_var}.day) -%>#{cell_prefix_code}#{cell_code}#{cell_postfix_code}</td>
2016
+ <% end -%>"
2017
+ end
2018
+
2019
+ res = <<-END_TXT
2020
+ <h3 class='title'>
2021
+ <span><%= link_to_remote(#{_('img_prev_page').inspect}, :url => #{base_class.to_s.underscore}_path(#{node_id}) + \"/zafu?t_url=#{CGI.escape(template_url)}&dom_id=#{dom_id}&date=#{prev_date}&#{start_node_s_param(:string)}\", :method => :get) %></span>
2022
+ <span class='date'><%= link_to_remote(#{title}, :url => #{base_class.to_s.underscore}_path(#{node_id}) + \"/zafu?t_url=#{CGI.escape(template_url)}&dom_id=#{dom_id}&#{start_node_s_param(:string)}\", :method => :get) %></span>
2023
+ <span><%= link_to_remote(#{_('img_next_page').inspect}, :url => #{base_class.to_s.underscore}_path(#{node_id}) + \"/zafu?t_url=#{CGI.escape(template_url)}&dom_id=#{dom_id}&date=#{next_date}&#{start_node_s_param(:string)}\", :method => :get) %></span>
2024
+ </h3>
2025
+ <table cellspacing='0' class='#{size}cal#{@params[:assign_as] ? " assign" : ''}'>
2026
+ <tr class='head'><%= cal_day_names(#{size.inspect}) %></tr>
2027
+ <% start_date, end_date = cal_start_end(#{current_date}, #{type.inspect}) -%>
2028
+ <% cal_weeks(#{ref_date.to_sym.inspect}, #{finder}, start_date, end_date, #{hours.inspect}) do |week, nodes_#{list_var}| -%>
2029
+ <tr class='body'>
2030
+ #{week_code}
2031
+ </tr>
2032
+ <% end -%>
2033
+ </table>
2034
+ END_TXT
2035
+ end
2036
+
2037
+ # part caching
2038
+ def r_cache
2039
+ kpath = @params[:kpath] || Page.kpath
2040
+ out "<% #{cache} = Cache.with(visitor.id, visitor.group_ids, #{kpath.inspect}, #{helper.send(:lang).inspect}, #{template_url.inspect}) do capture do %>"
2041
+ out expand_with
2042
+ out "<% end; end %><%= #{cache} %>"
2043
+ end
2044
+
2045
+ # recursion
2046
+ def r_include
2047
+ return '' if @context[:saved_template]
2048
+ return super if @params[:template] || !@params[:part]
2049
+ part = @params[:part].gsub(/[^a-zA-Z_]/,'')
2050
+ method_name = @context["#{part}_method".to_sym]
2051
+ return parser_error("no parent named '#{part}'") unless method_name
2052
+ "<%= #{method_name}(depth+1,#{node},#{list}) %>"
2053
+ end
2054
+
2055
+ # use all other tags as relations
2056
+ def r_unknown
2057
+ @params[:select] = @method
2058
+ r_context
2059
+ end
2060
+
2061
+ # Enter a new context (<r:context find='all' method='pages'>). This is the same as '<r:pages>...</r:pages>'). It is
2062
+ # considered better style to use '<r:pages>...</r:pages>' instead of the more general '<r:context>' because the tags
2063
+ # give a clue on the context at start and end. Another way to open a context is the 'do' syntax: "<div do='pages'>...</div>".
2064
+ # FIXME: 'else' clause has been removed, find a solution to put it back.
2065
+ def r_context
2066
+ # DRY ! (build_finder_for, block)
2067
+ return parser_error("missing 'method' parameter") unless method = @params[:select]
2068
+
2069
+ context = node_class.zafu_known_contexts[method]
2070
+ if context && @params.keys == [:select]
2071
+ open_context("#{node}.#{method}", context)
2072
+ elsif node_kind_of?(Node)
2073
+ count = ['first','all','count'].include?(@params[:find]) ? @params[:find].to_sym : nil
2074
+ count ||= Node.plural_relation?(method) ? :all : :first
2075
+ finder, klass, query = build_finder_for(count, method, @params)
2076
+ return unless finder
2077
+ if node_kind_of?(Node) && !klass.ancestors.include?(Node)
2078
+ # moving out of node: store last Node
2079
+ @context[:previous_node] = node
2080
+ end
2081
+ if count == :all
2082
+ # plural
2083
+ do_list( finder, query, :node_class => klass)
2084
+ # elsif count == :count
2085
+ # "<%= #{build_finder_for(count, method, @params)} %>"
2086
+ else
2087
+ # singular
2088
+ do_var( finder, :node_class => klass)
2089
+ end
2090
+ else
2091
+ "unknown relation (#{method}) for #{node_class} class"
2092
+ end
2093
+ end
2094
+
2095
+ def open_context(finder, context)
2096
+ klass = context[:node_class]
2097
+ # hack to store last 'Node' context until we fix node(Node) stuff:
2098
+ previous_node = node_kind_of?(Node) ? node : @context[:previous_node]
2099
+ if klass.kind_of?(Array)
2100
+ # plural
2101
+ do_list( finder, nil, context.merge(:node_class => klass[0], :previous_node => previous_node) )
2102
+ else
2103
+ # singular
2104
+ do_var( finder, context.merge(:previous_node => previous_node) )
2105
+ end
2106
+ end
2107
+
2108
+ # Prepare stylesheet and xml content for xsl-fo post-processor
2109
+ def r_fop
2110
+ return parser_error("missing 'stylesheet' argument") unless @params[:stylesheet]
2111
+ # get stylesheet text
2112
+ xsl_content, absolute_url, doc = self.class.get_template_text(@params[:stylesheet], @options[:helper], @options[:current_folder])
2113
+ return parser_error("stylesheet #{@params[:stylesheet].inspect} not found") unless doc
2114
+
2115
+ template_url = (self.template_url.split('/')[0..-2] + ['_main.xsl']).join('/')
2116
+ helper.save_erb_to_url(xsl_content, template_url)
2117
+ out "<?xml version='1.0' encoding='utf-8'?>\n"
2118
+ out "<!-- xsl_id:#{doc[:id] } -->\n" if doc
2119
+ out expand_with
2120
+ end
2121
+
2122
+ # Prepare content for LateX post-processor
2123
+ def r_latex
2124
+ out "% latex\n"
2125
+ # all content inside this will be informed to render for Latex output
2126
+ out expand_with(:output_format => 'latex')
2127
+ end
2128
+
2129
+ def r_debug
2130
+ return '' unless @context[:dev]
2131
+ add_html_class('debug')
2132
+ out "<p>#{@params[:title]}</p>" if @params[:title]
2133
+ (@params[:show] || '').split(',').map(&:strip).each do |what|
2134
+ case what
2135
+ when 'params'
2136
+ out "<pre><%= params.inspect %></pre>"
2137
+ else
2138
+ parser_error("invalid element to show. Options are ['params'].")
2139
+ end
2140
+ end
2141
+ out expand_with
2142
+ end
2143
+
2144
+ # ================== HELPER METHODS ================
2145
+
2146
+ # Create an sql query to open a new context (passes its arguments to HasRelations#build_find)
2147
+ def build_finder_for(count, rel, params=@params, raw_filters = [])
2148
+ if (context = node_class.zafu_known_contexts[rel]) && !params[:in] && !params[:where] && !params[:from] && !params[:order] && raw_filters == []
2149
+ klass = context[:node_class]
2150
+
2151
+ if klass.kind_of?(Array) && count == :all
2152
+ return ["#{node}.#{rel}", klass[0]]
2153
+ else
2154
+ return [(count == :all ? "[#{node}.#{rel}]" : "#{node}.#{rel}"), klass]
2155
+ end
2156
+ end
2157
+
2158
+ rel ||= 'self'
2159
+ if (count == :first)
2160
+ if rel == 'self'
2161
+ return [node, node_class]
2162
+ elsif rel == 'main'
2163
+ return ["@node", Node]
2164
+ elsif rel == 'root'
2165
+ return ["(secure(Node) { Node.find(#{current_site[:root_id]})})", Node]
2166
+ elsif rel == 'start'
2167
+ return ["start_node", Node]
2168
+ elsif rel == 'visitor'
2169
+ return ["visitor.contact", Node]
2170
+ elsif rel =~ /^\d+$/
2171
+ return ["(secure(Node) { Node.find_by_zip(#{rel.inspect})})", Node]
2172
+ elsif node_name = find_stored(Node, rel)
2173
+ return [node_name, Node]
2174
+ elsif rel[0..0] == '/'
2175
+ rel = rel[1..-1]
2176
+ return ["(secure(Node) { Node.find_by_path(#{rel.inspect})})", Node]
2177
+ end
2178
+ end
2179
+
2180
+ pseudo_sql, add_raw_filters = make_pseudo_sql(rel, params)
2181
+ raw_filters += add_raw_filters if add_raw_filters
2182
+
2183
+ # FIXME: stored should be clarified and managed in a single way through links and contexts.
2184
+ # <r:void store='foo'>...
2185
+ # <r:link href='foo'/>
2186
+ # <r:pages from='foo'/> <-- this is just a matter of changing node parameter
2187
+ # <r:pages from='site' project='foo'/>
2188
+ # <r:img link='foo'/>
2189
+ # ...
2190
+
2191
+ if node_kind_of?(Node)
2192
+ node_name = @context[:parent_node] || node
2193
+ else
2194
+ node_name = @context[:previous_node]
2195
+ end
2196
+
2197
+ # make sure we do not use a new record in a find query:
2198
+ query = Node.build_find(count, pseudo_sql, :node_name => node_name, :raw_filters => raw_filters, :ref_date => "\#{#{current_date}}")
2199
+
2200
+ unless query.valid?
2201
+ out parser_error(query.errors.join(' '), pseudo_sql.join(', '))
2202
+ return nil
2203
+ end
2204
+
2205
+
2206
+ if count == :count
2207
+ out "<%= #{query.finder(:count)} %>"
2208
+ return nil
2209
+ end
2210
+
2211
+ klass = query.main_class
2212
+
2213
+ if params[:else]
2214
+ # FIXME: else not working with zafu_known_contexts
2215
+ finder, else_class, else_query = build_finder_for(count, params[:else], {})
2216
+ if finder && (else_query.nil? || else_query.valid?) && (else_class == klass || klass.ancestors.include?(else_class) || else_class.ancestors.include?(klass))
2217
+ ["(#{query.finder(count)} || #{finder})", klass, query]
2218
+ else
2219
+ [query.finder(count), query.main_class, query]
2220
+ end
2221
+ else
2222
+ [query.finder(count), query.main_class, query]
2223
+ end
2224
+ end
2225
+
2226
+ # Build pseudo sql from the parameters
2227
+ # comments where ... from ... in ... order ... limit
2228
+ def make_pseudo_sql(rel, params=@params)
2229
+ parts = [rel.dup]
2230
+ filters = []
2231
+
2232
+ if params[:from]
2233
+ parts << params[:from]
2234
+
2235
+ key_counter = 1
2236
+ while sub_part = params["from#{key_counter}".to_sym]
2237
+ key_counter += 1
2238
+ parts << sub_part
2239
+ end
2240
+ end
2241
+
2242
+ if params[:where]
2243
+ parts[0] << " where #{params[:where]}"
2244
+ end
2245
+
2246
+ if params[:in]
2247
+ parts[-1] << " in #{params[:in]}"
2248
+ end
2249
+
2250
+ if group = params[:group]
2251
+ parts[-1] << " group by #{group}" unless parts[0] =~ /group by/
2252
+ end
2253
+
2254
+ if order = params[:order]
2255
+ parts[-1] << " order by #{order}" unless parts[0] =~ /order by/
2256
+ end
2257
+
2258
+ if paginate = params[:paginate]
2259
+ page_size = params[:limit].to_i
2260
+ page_size = 20 if page_size < 1
2261
+ parts[-1] << " limit #{page_size} paginate #{paginate.gsub(/[^a-z_A-Z]/,'')}"
2262
+ else
2263
+ [:limit, :offset].each do |k|
2264
+ next unless params[k]
2265
+ parts[-1] << " #{k} #{params[k]}" unless parts[0] =~ / #{k} /
2266
+ end
2267
+ end
2268
+
2269
+ finders = [parts.join(' from ')]
2270
+ if params[:or]
2271
+ finders << params[:or]
2272
+
2273
+ key_counter = 1
2274
+ while sub_or = params["or#{key_counter}".to_sym]
2275
+ key_counter += 1
2276
+ finders << sub_or
2277
+ end
2278
+ else
2279
+ or_clause = nil
2280
+ end
2281
+
2282
+ return [finders, parse_raw_filters(params)]
2283
+ end
2284
+
2285
+ # Parse special filters
2286
+ def parse_raw_filters(params)
2287
+ filters = []
2288
+
2289
+ if value = params[:author]
2290
+ if stored = find_stored(User, value)
2291
+ filters << "TABLE_NAME.user_id = '\#{#{stored}.id}'"
2292
+ elsif value == 'current'
2293
+ filters << "TABLE_NAME.user_id = '\#{#{node}[:user_id]}'"
2294
+ elsif value == 'visitor'
2295
+ filters << "TABLE_NAME.user_id = '\#{visitor[:id]}'"
2296
+ elsif value =~ /\A\d+\Z/
2297
+ filters << "TABLE_NAME.user_id = '#{value.to_i}'"
2298
+ elsif value =~ /\A[\w\/]+\Z/
2299
+ # TODO: path, not implemented yet
2300
+ end
2301
+ end
2302
+
2303
+ if value = params[:project]
2304
+ if stored = find_stored(Node, value)
2305
+ filters << "TABLE_NAME.project_id = '\#{#{stored}.get_project_id}'"
2306
+ elsif value == 'current'
2307
+ filters << "TABLE_NAME.project_id = '\#{#{node}.get_project_id}'"
2308
+ elsif value =~ /\A\d+\Z/
2309
+ filters << "TABLE_NAME.project_id = '#{value.to_i}'"
2310
+ elsif value =~ /\A[\w\/]+\Z/
2311
+ # TODO: path, not implemented yet
2312
+ end
2313
+ end
2314
+
2315
+ if value = params[:section]
2316
+ if stored = find_stored(Node, value)
2317
+ filters << "TABLE_NAME.section_id = '\#{#{stored}.get_section_id}'"
2318
+ elsif value == 'current'
2319
+ filters << "TABLE_NAME.section_id = '\#{#{node}.get_section_id}'"
2320
+ elsif value =~ /\A\d+\Z/
2321
+ filters << "TABLE_NAME.section_id = '#{value.to_i}'"
2322
+ elsif value =~ /\A[\w\/]+\Z/
2323
+ # not implemented yet
2324
+ end
2325
+ end
2326
+
2327
+ [:updated, :created, :event, :log].each do |k|
2328
+ if value = params[k]
2329
+ # current, same are synonym for 'today'
2330
+ filters << date_condition(value,"TABLE_NAME.#{k}_at",current_date)
2331
+ end
2332
+ end
2333
+
2334
+ filters == [] ? nil : filters
2335
+ end
2336
+
2337
+ # helpers
2338
+ # get current output format
2339
+ def output_format
2340
+ @context[:output_format] || 'html'
2341
+ end
2342
+
2343
+ # find the current node name in the context
2344
+ def node(klass = self.node_class)
2345
+ if klass == self.node_class
2346
+ (@context[:saved_template] && @context[:main_node]) ? "@#{base_class.to_s.underscore}" : (@context[:node] || '@node')
2347
+ elsif klass == Node
2348
+ @context[:previous_node] || '@node'
2349
+ else
2350
+ # ?
2351
+ out parser_error("could not find node_name for #{klass} (current class is #{node_class})")
2352
+ '@node'
2353
+ end
2354
+ end
2355
+
2356
+ def erb_node_id(obj = node)
2357
+ if node_kind_of?(Version)
2358
+ "<%= #{obj}.node.zip %>.<%= #{obj}.number %>"
2359
+ else
2360
+ "<%= #{node_id(obj)} %>"
2361
+ end
2362
+ end
2363
+
2364
+ def node_id(obj = node)
2365
+ "#{obj}.zip"
2366
+ end
2367
+
2368
+ def current_date
2369
+ @context[:date] || 'main_date'
2370
+ end
2371
+
2372
+ def var
2373
+ return @var if @var
2374
+ if node =~ /^var(\d+)$/
2375
+ @var = "var#{$1.to_i + 1}"
2376
+ else
2377
+ @var = "var1"
2378
+ end
2379
+ end
2380
+
2381
+ def cache
2382
+ return @cache if @cache
2383
+ if @context[:cache] =~ /^cache(\d+)$/
2384
+ @cache = "cache#{$1.to_i + 1}"
2385
+ else
2386
+ @cache = "cache1"
2387
+ end
2388
+ end
2389
+
2390
+ def list_var
2391
+ return @list_var if @list_var
2392
+ if (list || "") =~ /^list(\d+)$/
2393
+ @list_var = "list#{$1.to_i + 1}"
2394
+ else
2395
+ @list_var = "list1"
2396
+ end
2397
+ end
2398
+
2399
+ # Class of the current 'node' object (can be Version, Comment, Node, DataEntry, etc)
2400
+ def node_class
2401
+ @context[:node_class] || Node
2402
+ end
2403
+
2404
+ def base_class
2405
+ if node_kind_of?(Node)
2406
+ Node
2407
+ elsif node_kind_of?(Version)
2408
+ Version
2409
+ else
2410
+ node_class
2411
+ end
2412
+ end
2413
+
2414
+ def node_kind_of?(ancestor)
2415
+ node_class.ancestors.include?(ancestor)
2416
+ end
2417
+
2418
+ def list
2419
+ @context[:list]
2420
+ end
2421
+
2422
+ def helper
2423
+ @options[:helper]
2424
+ end
2425
+
2426
+ def params_to_erb(params, initial_comma = true)
2427
+ res = initial_comma ? [""] : []
2428
+ params.each do |k,v|
2429
+ if v =~ /<%=/ && !(v =~ /"/)
2430
+ # replace by #{}
2431
+ val = v.gsub('#{', '# {').gsub(/<%=(.*?)%>/,'#{\1}')
2432
+ res << "#{k.inspect}=>\"#{val}\""
2433
+ else
2434
+ res << "#{k.inspect}=>#{v.inspect}"
2435
+ end
2436
+ end
2437
+ res.join(', ')
2438
+ end
2439
+
2440
+ def do_var(var_finder=nil, opts={})
2441
+ clear_dom_scope
2442
+ if var_finder == 'nil'
2443
+ out "<% if nil -%>"
2444
+ elsif var_finder
2445
+ out "<% if #{var} = #{var_finder} -%>"
2446
+ end
2447
+
2448
+ if descendant('unlink')
2449
+ @html_tag ||= 'div'
2450
+ new_dom_scope
2451
+ @html_tag_params[:id] = erb_dom_id
2452
+ end
2453
+
2454
+ res = expand_with(opts.merge(:node=>var, :in_if => false))
2455
+
2456
+ if var_finder
2457
+ res += expand_with(opts.merge(:in_if => true, :only => ['else', 'elsif'], :html_tag_params => @html_tag_params, :html_tag => @html_tag))
2458
+ end
2459
+ out render_html_tag(res)
2460
+ out "<% end -%>" if var_finder
2461
+ end
2462
+
2463
+ def do_list(list_finder, query = nil, opts={})
2464
+ clear_dom_scope
2465
+
2466
+ @context.merge!(opts) # pass options from 'zafu_known_contexts' to @context
2467
+
2468
+ if (each_block = descendant('each')) && (each_block.descendant('edit') || descendant('add') || descendant('add_document') || (descendant('swap') && descendant('swap').parent.method != 'block') || ['block', 'drop'].include?(each_block.single_child_method))
2469
+ new_dom_scope
2470
+ # ajax, build template. We could merge the following code with 'r_block'.
2471
+ add_block = descendant('add')
2472
+ form_block = descendant('form') || each_block
2473
+
2474
+ @context[:need_link_id] = form_block.need_link_id
2475
+
2476
+ out "<% if (#{list_var} = #{list_finder}) || (#{node}.#{node_kind_of?(Comment) ? "can_comment?" : "can_write?"} && #{list_var}=[]) -%>"
2477
+ if query && (pagination_key = query.pagination_key)
2478
+ out "<% set_#{pagination_key}_nodes = #{query.finder(:count)}; set_#{pagination_key}_count = (set_#{pagination_key}_nodes / #{query.page_size.to_f}).ceil; set_#{pagination_key} = [1,params[:#{pagination_key}].to_i].max -%>"
2479
+ @context[:paginate] = pagination_key
2480
+ @context[:vars] ||= []
2481
+ @context[:vars] << "#{pagination_key}_nodes"
2482
+ @context[:vars] << "#{pagination_key}_count"
2483
+ @context[:vars] << "#{pagination_key}"
2484
+ end
2485
+
2486
+ # should we publish ?
2487
+ publish_after_save ||= form_block ? form_block.params[:publish] : nil
2488
+ publish_after_save ||= descendant('edit') ? descendant('edit').params[:publish] : nil
2489
+
2490
+ # class name for create form
2491
+ klass = add_block ? add_block.params[:klass] : nil
2492
+ klass ||= form_block ? form_block.params[:klass] : nil
2493
+
2494
+ # INLINE ==========
2495
+ # 'r_add' needs the form when rendering. Send with :form.
2496
+ out render_html_tag(expand_with(:list=>list_var, :in_if => false, :form=>form_block, :publish_after_save => publish_after_save, :ignore => ['form'], :klass => klass))
2497
+ out expand_with(:in_if=>true, :only=>['elsif', 'else'], :html_tag => @html_tag, :html_tag_params => @html_tag_params)
2498
+ out "<% end -%>"
2499
+
2500
+ # SAVED TEMPLATE ========
2501
+ template = expand_block(each_block, :list=>false, :klass => klass, :saved_template => true)
2502
+ out helper.save_erb_to_url(template, template_url)
2503
+
2504
+ # FORM ============
2505
+ if each_block != form_block
2506
+ form = expand_block(form_block, :klass => klass, :add=>add_block, :publish_after_save => publish_after_save, :saved_template => true)
2507
+ else
2508
+ form = expand_block(form_block, :klass => klass, :add=>add_block, :make_form=>true, :publish_after_save => publish_after_save, :saved_template => true)
2509
+ end
2510
+ out helper.save_erb_to_url(form, form_url)
2511
+ else
2512
+ # no form, render, edit and add are not ajax
2513
+ if descendant('add') || descendant('add_document')
2514
+ out "<% if (#{list_var} = #{list_finder}) || (#{node}.#{node_kind_of?(Comment) ? "can_comment?" : "can_write?"} && #{list_var}=[]) -%>"
2515
+ elsif list_finder != 'nil'
2516
+ out "<% if #{list_var} = #{list_finder} -%>"
2517
+ else
2518
+ out "<% if nil -%>"
2519
+ end
2520
+
2521
+ if query && (pagination_key = query.pagination_key)
2522
+ out "<% set_#{pagination_key}_nodes = #{query.finder(:count)}; set_#{pagination_key}_count = (set_#{pagination_key}_nodes / #{query.page_size.to_f}).ceil; set_#{pagination_key} = [1,params[:#{pagination_key}].to_i].max -%>"
2523
+ @context[:paginate] = pagination_key
2524
+ @context[:vars] ||= []
2525
+ @context[:vars] << "#{pagination_key}_nodes"
2526
+ @context[:vars] << "#{pagination_key}_count"
2527
+ @context[:vars] << "#{pagination_key}"
2528
+ end
2529
+
2530
+ out render_html_tag(expand_with(:list=>list_var, :in_if => false))
2531
+ out expand_with(:in_if=>true, :only=>['elsif', 'else'], :html_tag => @html_tag, :html_tag_params => @html_tag_params)
2532
+ out "<% end -%>"
2533
+ end
2534
+ end
2535
+
2536
+ def _(text)
2537
+ if @context[:dict]
2538
+ @context[:dict][text] || helper.send(:_,text)
2539
+ else
2540
+ helper.send(:_,text)
2541
+ end
2542
+ end
2543
+
2544
+ # Find a block to update on the page
2545
+ def find_target(name)
2546
+ # find dom_id / template_url
2547
+ target = nil
2548
+ root.descendants('block').each do |b|
2549
+ if b.name == name
2550
+ target = b
2551
+ break
2552
+ end
2553
+ end
2554
+ out parser_error("could not find a block named '#{name}'") if target.nil?
2555
+ target
2556
+ end
2557
+
2558
+ # DOM id for the current context
2559
+ def dom_id(suffix='')
2560
+ return "\#{dom_id(#{node})}" if @context && (@context[:saved_template] && @context[:main_node])
2561
+ if @context && scope_node = @context[:scope_node]
2562
+ res = "#{dom_prefix}_\#{#{scope_node}.zip}"
2563
+ else
2564
+ res = dom_prefix
2565
+ end
2566
+ if (method == 'each' || method == 'each_group') && !@context[:make_form]
2567
+ "#{res}_\#{#{var}.zip}"
2568
+ elsif @context && @context[:in_calendar]
2569
+ "#{res}_\#{#{current_date}.to_i}"
2570
+ elsif method == 'unlink' || method == 'edit'
2571
+ target = nil
2572
+ parent = self.parent
2573
+ while parent
2574
+ if ['block', 'each', 'context', 'icon'].include?(parent.method)
2575
+ target = parent
2576
+ break
2577
+ end
2578
+ parent = parent.parent
2579
+ end
2580
+ target ? target.dom_id(suffix) : (res + suffix)
2581
+ else
2582
+ res + suffix
2583
+ end
2584
+ end
2585
+
2586
+ def erb_dom_id(suffix='')
2587
+ return "<%= dom_id(#{node}) %>" if @context && (@context[:saved_template] && @context[:main_node])
2588
+ if @context && scope_node = @context[:scope_node]
2589
+ res = "#{dom_prefix}_<%= #{scope_node}.zip %>"
2590
+ else
2591
+ res = dom_prefix
2592
+ end
2593
+ if (method == 'each' || method == 'each_group') && !@context[:make_form]
2594
+ "#{res}_<%= #{var}.zip %>"
2595
+ elsif method == 'draggable'
2596
+ "#{res}_<%= #{node}.zip %>"
2597
+ elsif @context && @context[:in_calendar]
2598
+ "#{res}_<%= #{current_date}.to_i %>"
2599
+ elsif method == 'unlink'
2600
+ target = nil
2601
+ parent = self.parent
2602
+ while parent
2603
+ if ['block', 'each', 'context', 'icon'].include?(parent.method)
2604
+ target = parent
2605
+ break
2606
+ end
2607
+ parent = parent.parent
2608
+ end
2609
+ target ? target.erb_dom_id(suffix) : (res + suffix)
2610
+ else
2611
+ res + suffix
2612
+ end
2613
+ end
2614
+
2615
+ # Unique template_url, ending with dom_id
2616
+ def template_url
2617
+ "#{@options[:root]}/#{dom_prefix}"
2618
+ end
2619
+
2620
+ def form_url
2621
+ template_url + '_form'
2622
+ end
2623
+
2624
+ # Return parameter value accessor
2625
+ def get_param(key)
2626
+ "params[:#{key}]"
2627
+ end
2628
+
2629
+ def context
2630
+ return @context if @context
2631
+ # not rendered yet, find first parent with context
2632
+ @context = parent ? parent.context : {}
2633
+ end
2634
+
2635
+ # prefix for DOM id
2636
+ def dom_prefix
2637
+ (@context ? @context[:dom_prefix] : nil) || (@dom_prefix ||= unique_name)
2638
+ end
2639
+
2640
+ # use our own scope
2641
+ def clear_dom_scope
2642
+ @context.delete(:make_form) # should not propagate
2643
+ @context.delete(:main_node) # should not propagate
2644
+ end
2645
+
2646
+ # create our own ajax DOM scope
2647
+ def new_dom_scope
2648
+ clear_dom_scope
2649
+ @context.delete(:saved_template) # should not propagate on fresh template
2650
+ @context.delete(:dom_prefix) # should not propagate on fresh template
2651
+ @context[:main_node] = true # the current context will be rendered with a fresh '@node'
2652
+ @context[:dom_prefix] = self.dom_prefix
2653
+ end
2654
+
2655
+ # Return a different name on each call
2656
+ def unique_name(base = context_name)
2657
+ root.next_name_index(base, base == @name).gsub(/[^\d\w\/]/,'_')
2658
+ end
2659
+
2660
+ def context_name
2661
+ @name || if @context
2662
+ @context[:name] || 'list'
2663
+ elsif parent
2664
+ parent.context_name
2665
+ else
2666
+ 'root'
2667
+ end
2668
+ end
2669
+
2670
+ def next_name_index(key, own_id = false)
2671
+ @next_name_index ||= {}
2672
+ if @next_name_index[key]
2673
+ @next_name_index[key] += 1
2674
+ key + @next_name_index[key].to_s
2675
+ elsif own_id
2676
+ @next_name_index[key] = 0
2677
+ key
2678
+ else
2679
+ @next_name_index[key] = 1
2680
+ key + '1'
2681
+ end
2682
+ end
2683
+
2684
+ def add_params(text, opts={}, inner = '')
2685
+ text.sub(/\A([^<]*)<(\w+)(( .*?)[^%]|)>/) do
2686
+ # we must set the first tag id
2687
+ before = $1
2688
+ tag = $2
2689
+ params = parse_params($3)
2690
+ opts.each do |k,v|
2691
+ next unless v
2692
+ params[k] = v
2693
+ end
2694
+ "#{before}<#{tag}#{params_to_html(params)}>#{inner}"
2695
+ end
2696
+ end
2697
+
2698
+ # TODO: RUBYLESS
2699
+ def get_test_condition(node = self.node, params = @params)
2700
+ tests = []
2701
+ params.each do |k,v|
2702
+ if k.to_s =~ /^(or_|)([a-zA-Z_]+)(\d*)$/
2703
+ k = $2.to_sym
2704
+ end #tagged undocumented
2705
+ if [:kind_of, :klass, :status, :lang, :can, :tagged, :node, :in, :visitor, :has].include?(k)
2706
+ tests << [k, v]
2707
+ elsif k == :test
2708
+ if v =~ /\s/
2709
+ tests << [:test, v]
2710
+ else
2711
+ tests << [:attribute, v]
2712
+ end
2713
+ end
2714
+ end
2715
+
2716
+
2717
+ tests.map! do |type,value|
2718
+ case type
2719
+ when :kind_of
2720
+ "#{node}.vkind_of?(#{value.inspect})"
2721
+ when :klass
2722
+ klass = begin Module::const_get(value) rescue "NilClass" end
2723
+ "#{node}.klass == #{value.inspect}"
2724
+ when :status
2725
+ "#{node}.version.status == #{Zena::Status[value.to_sym]}"
2726
+ when :tagged
2727
+ # TODO: undocumented: remove and use rubyless !
2728
+ "#{node}.tagged[#{value.inspect}]"
2729
+ when :lang
2730
+ "#{node}.version.lang == #{value.inspect}"
2731
+ when :can
2732
+ # TODO: test
2733
+ case value
2734
+ when 'write', 'edit'
2735
+ "#{node}.can_write?"
2736
+ when 'drive', 'publish'
2737
+ "#{node}.can_drive?"
2738
+ else
2739
+ nil
2740
+ end
2741
+ when :has
2742
+ case value
2743
+ when 'discussion'
2744
+ "#{node}.discussion"
2745
+ else
2746
+ nil
2747
+ end
2748
+ when :test
2749
+ parse_condition(value, node)
2750
+ when :attribute
2751
+ '!' + node_attribute(value, :node => node) + '.blank?'
2752
+ when :node
2753
+ if node_kind_of?(Node)
2754
+ value, node_name = get_attribute_and_node(value)
2755
+ node_name ||= '@node'
2756
+ if value
2757
+ case value
2758
+ when 'main'
2759
+ "#{node}[:id] == #{node_name}[:id]"
2760
+ when 'start'
2761
+ "#{node}[:zip] == (params[:s] || @node[:zip]).to_i"
2762
+ when 'parent'
2763
+ "#{node}[:id] == #{node_name}[:parent_id]"
2764
+ when 'project'
2765
+ "#{node}[:id] == #{node_name}[:project_id]"
2766
+ when 'section'
2767
+ "#{node}[:id] == #{node_name}[:section_id]"
2768
+ when 'ancestor'
2769
+ "#{node_name}.fullpath =~ /\\A\#{#{node}.fullpath}/"
2770
+ else
2771
+ if stored = find_stored(Node, value)
2772
+ "#{node}[:id] == #{stored}[:id]"
2773
+ else
2774
+ nil
2775
+ end
2776
+ end
2777
+ else
2778
+ # bad node_name
2779
+ nil
2780
+ end
2781
+ else
2782
+ nil
2783
+ end
2784
+ when :in
2785
+ if @context["in_#{value}".to_sym] # FIXME: || ancestors.include?(value) ==> ancestors is a list of zafu tags, not a list of names !
2786
+ 'true'
2787
+ else
2788
+ 'false'
2789
+ end
2790
+ when :visitor
2791
+ if value == 'anon'
2792
+ "visitor.is_anon?"
2793
+ else
2794
+ nil
2795
+ end
2796
+ else
2797
+ nil
2798
+ end
2799
+ end.compact!
2800
+ tests == [] ? nil : tests.join(' || ')
2801
+ end
2802
+
2803
+ def parse_condition_error(clause, rest, res)
2804
+ out parser_error("invalid clause #{clause.inspect} near \"#{res[-2..-1]}#{rest[0..1]}\"")
2805
+ end
2806
+
2807
+ def parse_condition(clause, node_name)
2808
+ rest = clause.strip
2809
+ types = [:par_open, :value, :bool_op, :op, :par_close]
2810
+ allowed = [:par_open, :value]
2811
+ par_count = 0
2812
+ uses_bool_op = false
2813
+ segment = [] # value op value
2814
+ after_value = lambda { segment.size == 3 ? [:bool_op, :par_close] : [:op, :bool_op, :par_close]}
2815
+ res = ""
2816
+ while rest != ''
2817
+ # puts rest.inspect
2818
+ if rest =~ /\A\s+/
2819
+ rest = rest[$&.size..-1]
2820
+ elsif rest[0..0] == '('
2821
+ unless allowed.include?(:par_open)
2822
+ parse_condition_error(clause, rest, res)
2823
+ return nil
2824
+ end
2825
+ res << '('
2826
+ rest = rest[1..-1]
2827
+ par_count += 1
2828
+ elsif rest[0..0] == ')'
2829
+ unless allowed.include?(:par_close)
2830
+ parse_condition_error(clause, rest, res)
2831
+ return nil
2832
+ end
2833
+ res << ')'
2834
+ rest = rest[1..-1]
2835
+ par_count -= 1
2836
+ if par_count < 0
2837
+ parse_condition_error(clause, rest, res)
2838
+ return nil
2839
+ end
2840
+ allowed = [:bool_op]
2841
+ elsif rest =~ /\A(lt|le|eq|ne|ge|gt)\s+/
2842
+ unless allowed.include?(:op)
2843
+ parse_condition_error(clause, rest, res)
2844
+ return nil
2845
+ end
2846
+ op = $1.strip
2847
+ rest = rest[op.size..-1]
2848
+ op = {'lt' => '<', 'le' => '<=', 'eq' => '==', 'ne' => '!=', 'ge' => '>=', 'gt' => '>'}[op]
2849
+ segment << [op, :op]
2850
+ allowed = [:value]
2851
+ elsif rest =~ /\A("|')([^\1]*?)\1/
2852
+ # string
2853
+ unless allowed.include?(:value)
2854
+ parse_condition_error(clause, rest, res)
2855
+ return nil
2856
+ end
2857
+ rest = rest[$&.size..-1]
2858
+ segment << [$2.inspect, :string]
2859
+ allowed = after_value.call
2860
+ elsif rest =~ /\A(-?\d+)/
2861
+ # number
2862
+ unless allowed.include?(:value)
2863
+ parse_condition_error(clause, rest, res)
2864
+ return nil
2865
+ end
2866
+ rest = rest[$&.size..-1]
2867
+ segment << [$1, :number]
2868
+ allowed = after_value.call
2869
+ elsif rest =~ /\A(and|or)/
2870
+ unless allowed.include?(:bool_op)
2871
+ parse_condition_error(clause, rest, res)
2872
+ return nil
2873
+ end
2874
+ uses_bool_op = true
2875
+ rest = rest[$&.size..-1]
2876
+ res << " #{$1} "
2877
+ allowed = [:par_open, :value]
2878
+ elsif rest =~ /\A([\w:\.\-]+)/
2879
+ # variable
2880
+ unless allowed.include?(:value)
2881
+ parse_condition_error(clause, rest, res)
2882
+ return nil
2883
+ end
2884
+ rest = rest[$&.size..-1]
2885
+ fld = $1
2886
+ unless node_attr = node_attribute(fld, :node => node_name)
2887
+ parser_error("invalid field #{fld.inspect}")
2888
+ return nil
2889
+ end
2890
+ segment << [node_attr, :var]
2891
+ allowed = after_value.call
2892
+ else
2893
+ parse_condition_error(clause, rest, res)
2894
+ return nil
2895
+ end
2896
+ if segment.size == 3
2897
+ toi = (segment[1][0] =~ /(>|<)/ || (segment[0][1] == :number || segment[2][1] == :number))
2898
+ segment.map! do |part, type|
2899
+ if type == :var
2900
+ toi ? "#{part}.to_i" : part
2901
+ elsif type == :string
2902
+ toi ? part[1..-2].to_i : part
2903
+ else
2904
+ part
2905
+ end
2906
+ end
2907
+ res << segment.join(" ")
2908
+ segment = []
2909
+ end
2910
+ end
2911
+
2912
+ if par_count > 0
2913
+ parser_error("invalid clause #{clause.inspect}: missing closing ')'")
2914
+ return nil
2915
+ elsif allowed.include?(:value)
2916
+ parser_error("invalid clause #{clause.inspect}")
2917
+ return nil
2918
+ else
2919
+ return uses_bool_op ? "(#{res})" : res
2920
+ end
2921
+ end
2922
+
2923
+ # Block visibility of descendance with 'do_list'.
2924
+ def public_descendants
2925
+ all = super
2926
+ if ['context', 'each', 'block'].include?(self.method)
2927
+ # do not propagate 'form',etc up
2928
+ all.reject do |k,v|
2929
+ ['form','unlink'].include?(k)
2930
+ end
2931
+ elsif ['if', 'case'].include?(self.method)
2932
+ all.reject do |k,v|
2933
+ ['else', 'elsif', 'when'].include?(k)
2934
+ end
2935
+ else
2936
+ all
2937
+ end
2938
+ end
2939
+
2940
+ def single_child_method
2941
+ return @single_child_method if defined?(@single_child_method)
2942
+ @single_child_method = if @blocks.size == 1
2943
+ single_child = @blocks[0]
2944
+ return nil if single_child.kind_of?(String)
2945
+ single_child.html_tag ? nil : single_child.method
2946
+ else
2947
+ nil
2948
+ end
2949
+ end
2950
+
2951
+ def get_attribute_and_node(str)
2952
+ if str =~ /([^\.]+)\.(.+)/
2953
+ node_name = $1
2954
+ node_attr = $2
2955
+ if att_node = find_stored(Node, node_name)
2956
+ return [node_attr, att_node, Node]
2957
+ elsif node_name == 'main'
2958
+ return [node_attr, '@node', Node]
2959
+ elsif node_name == 'visitor'
2960
+ return [node_attr, 'visitor.contact', Contact]
2961
+ elsif node_name == 'site'
2962
+ return [node_attr, 'current_site', Site]
2963
+ else
2964
+ out parser_error("invalid node name #{node_name.inspect} in attribute #{str.inspect}")
2965
+ return [nil]
2966
+ end
2967
+ else
2968
+ return [str]
2969
+ end
2970
+ end
2971
+
2972
+ def parse_attributes_in_value(v, opts = {})
2973
+ opts = {:erb => true}.merge(opts)
2974
+ static = true
2975
+
2976
+ use_node = @var || node
2977
+ res = v.gsub(/\[([^\]]+)\]/) do
2978
+ static = false
2979
+ res = nil
2980
+ attribute = $1
2981
+
2982
+ if opts[:skip_node_attributes]
2983
+ if attribute =~ /^param:(\w+)$/
2984
+ attribute = get_param($1)
2985
+ elsif attribute == 'current_date'
2986
+ attribute = current_date
2987
+ else
2988
+ res = "[#{attribute}]"
2989
+ end
2990
+ else
2991
+ attribute = node_attribute(attribute, :node => use_node )
2992
+ end
2993
+
2994
+ res ||= if opts[:erb]
2995
+ "<%= #{attribute} %>"
2996
+ else
2997
+ "\#{#{attribute}}"
2998
+ end
2999
+ res
3000
+ end
3001
+ [res, static]
3002
+ end
3003
+
3004
+ def node_attribute(str, opts={})
3005
+
3006
+ if @context[:vars] && @context[:vars].include?(str)
3007
+ return "set_#{str}"
3008
+ end
3009
+
3010
+ res = PSEUDO_ATTRIBUTES[str]
3011
+ return res if res
3012
+ return current_date if str == 'current_date'
3013
+ return get_param($1) if str =~ /^param:(\w+)$/
3014
+
3015
+ attribute, att_node, klass = get_attribute_and_node(str)
3016
+
3017
+ return 'nil' unless attribute
3018
+
3019
+
3020
+ att_node ||= opts[:node] || node
3021
+ klass ||= opts[:node_class] || node_class
3022
+
3023
+ real_attribute = attribute =~ /\Ad_/ ? attribute : attribute.gsub(/\A(|[\w_]+)id(s?)\Z/, '\1zip\2')
3024
+
3025
+ res = if klass.ancestors.include?(Node)
3026
+ if ['url','path'].include?(real_attribute)
3027
+ # pseudo attribute 'url'
3028
+ params = {}
3029
+ params[:mode] = @params[:mode] if @params[:mode]
3030
+ params[:format] = @params[:format] if @params[:format]
3031
+ "zen_#{real_attribute}(#{node}#{params_to_erb(params)})"
3032
+ elsif type = klass.safe_method_type([real_attribute])
3033
+ "#{att_node}.#{type[:method]}"
3034
+ else
3035
+ "#{att_node}.safe_read(#{real_attribute.inspect})"
3036
+ end
3037
+ elsif type = klass.safe_method_type([real_attribute])
3038
+ "#{att_node}.#{type[:method]}"
3039
+ else
3040
+ # unknown class, resolve at runtime
3041
+ "#{att_node}.safe_read(#{real_attribute.inspect})"
3042
+ end
3043
+
3044
+ res = "(#{res} || #{node_attribute(opts[:else])})" if opts[:else]
3045
+ res = "(#{res} || #{opts[:default].inspect})" if opts[:default]
3046
+ res
3047
+ end
3048
+
3049
+ # Add a class name to the html_tag
3050
+ def add_html_class(class_name)
3051
+ if klass = @html_tag_params[:class]
3052
+ @html_tag_params[:class] = "#{class_name} #{klass}"
3053
+ else
3054
+ @html_tag_params[:class] = class_name
3055
+ end
3056
+ end
3057
+
3058
+ def render_html_tag(text,*append)
3059
+ append ||= []
3060
+ return text if @html_tag_done
3061
+ set_params = {}
3062
+ if_params = {}
3063
+ @params.each do |k,v|
3064
+ if k.to_s =~ /^t?set_/
3065
+ set_params[k] = v
3066
+ end
3067
+ end
3068
+ tag_class = @html_tag_params[:class] || @params[:class]
3069
+ if node_kind_of?(Node)
3070
+
3071
+ if @context[:make_form]
3072
+ node_name = node
3073
+ elsif (@method == 'each' || @method == 'each_group') && @context[:list]
3074
+ node_name = var
3075
+ elsif @method == 'context'
3076
+ node_name = @var || node
3077
+ else
3078
+ node_name = node
3079
+ end
3080
+
3081
+ class_cond = nil
3082
+ @params.each do |k,v|
3083
+ if k.to_s =~ /^(.+)_if$/
3084
+ klass = $1
3085
+ cond = get_test_condition(node_name, :test => v)
3086
+ elsif k.to_s =~ /^(.+)_if_(test|node|kind_of|klass|status|lang|can|in)$/
3087
+ klass = $1
3088
+ cond = get_test_condition(node_name, $2.to_sym => v)
3089
+ end
3090
+ if cond
3091
+ class_cond = "#{cond} ? \" class='#{klass}'\" : #{class_cond}" # (x = 3) ? "class='foo'" :
3092
+ end
3093
+ end
3094
+
3095
+ if class_cond
3096
+ append << "<%= #{class_cond}\"#{tag_class ? " class='#{tag_class}'" : ""}\" %>"
3097
+ @html_tag_params.delete(:class)
3098
+ end
3099
+ end
3100
+
3101
+ @html_tag = 'div' if !@html_tag && (set_params != {} || @html_tag_params != {})
3102
+
3103
+ bak = @html_tag_params.dup
3104
+ @html_tag_params = get_html_params(set_params.merge(@html_tag_params), @html_tag)
3105
+ res = super(text,*append)
3106
+ @html_tag_params = bak
3107
+ res
3108
+ end
3109
+
3110
+ def link_to_update(target, opts = {})
3111
+ method = opts[:method] || :get
3112
+
3113
+ html_params = opts[:html_params] || {}
3114
+ node_id = opts[:node_id] || self.node_id
3115
+
3116
+ url = opts[:url] || "/#{base_class.to_s.pluralize.underscore}/\#{#{node_id}}#{method == :get ? '/zafu' : ''}"
3117
+ opts[:cond] ||= "#{node}.can_write?" if method != :get
3118
+
3119
+ query_params = [opts[:query_params]].flatten.compact
3120
+
3121
+ if method == :get
3122
+ if target
3123
+ query_params << "t_url=#{CGI.escape(target.template_url)}"
3124
+ query_params << "dom_id=#{target.dom_id}"
3125
+ else
3126
+ query_params << "dom_id=_page"
3127
+ end
3128
+ else
3129
+ query_params << "t_url=#{CGI.escape(template_url)}" if method != :delete
3130
+
3131
+ query_params << "dom_id=#{dom_id}"
3132
+ if target != self
3133
+ if target
3134
+ query_params << "u_url=#{CGI.escape(target.template_url)}"
3135
+ query_params << "udom_id=#{target.dom_id}"
3136
+ else
3137
+ query_params << "udom_id=_page"
3138
+ end
3139
+ end
3140
+ end
3141
+
3142
+ query_params << "link_id=\#{#{node}.link_id}" if @context[:need_link_id] && node_kind_of?(Node)
3143
+ query_params << "node[v_status]=#{Zena::Status[:pub]}" if @params[:publish] # FIXME: this acts like publish = 'force'
3144
+ query_params << start_node_s_param(:string)
3145
+
3146
+ res = ''
3147
+ res += "<% if #{opts[:cond]} -%>" if opts[:cond]
3148
+ res += "<%= tag_to_remote({:url => \"#{url}?#{query_params.join('&')}\", :method => #{method.inspect}}#{params_to_erb(html_params)}) %>"
3149
+ res += text_for_link(opts[:default_text])
3150
+ res += "</a>"
3151
+ if opts[:cond]
3152
+ if opts[:else] != :void
3153
+ res += "<% else -%>"
3154
+ res += text_for_link(opts[:default_text])
3155
+ end
3156
+ res += "<% end -%>"
3157
+ end
3158
+ res
3159
+ end
3160
+
3161
+ def text_for_link(default = nil)
3162
+ if @blocks.size > 1 || (@blocks.size == 1 && !(@blocks.first.kind_of?(String) || ['else','elsif'].include?(@blocks.first.method)))
3163
+ expand_with
3164
+ elsif default
3165
+ default
3166
+ elsif erb_text = get_text_for_erb(@params, false, :string)
3167
+ erb_text
3168
+ elsif node_kind_of?(Node)
3169
+ "<%= #{node}.version.title %>"
3170
+ elsif node_kind_of?(Version)
3171
+ "<%= #{node}.title %>"
3172
+ elsif node_kind_of?(Link)
3173
+ "<%= #{node}.name %>"
3174
+ else
3175
+ _('edit')
3176
+ end
3177
+ end
3178
+
3179
+ def get_text_for_erb(params = @params, use_blocks = true, context = :erb)
3180
+ string_context = context == :string
3181
+ if params[:attr]
3182
+ string_context ? "<%= #{node_attribute(params[:attr])} %>" : node_attribute(params[:attr])
3183
+ elsif params[:tattr]
3184
+ string_context ? "<%= _(#{node_attribute(params[:tattr])}) %>" : "_(#{node_attribute(params[:tattr])})"
3185
+ elsif params[:trans]
3186
+ string_context ? _(params[:trans]) : _(params[:trans]).inspect
3187
+ elsif params[:text]
3188
+ string_context ? params[:text] : params[:text].inspect
3189
+ elsif use_blocks && @blocks != []
3190
+ res = []
3191
+ text = ""
3192
+ static = true
3193
+ @blocks.each do |b|
3194
+ # FIXME: this is a little too hacky
3195
+ if b.kind_of?(String)
3196
+ res << b.inspect
3197
+ text << b
3198
+ elsif ['show', 'img'].include?(b.method)
3199
+ res << expand_block(b, :trans=>true)
3200
+ static = false
3201
+ elsif ['rename_asset', 'trans'].include?(b.method)
3202
+ # FIXME: if a trans contains non-static: static should become false
3203
+ res << expand_block(b).inspect
3204
+ text << expand_block(b)
3205
+ else
3206
+ # ignore
3207
+ end
3208
+ end
3209
+ if static
3210
+ # "just plain text"
3211
+ string_context ? text : text.inspect
3212
+ else
3213
+ # function(...) + "blah" + function()
3214
+ string_context ? "<%= #{res.join(' + ')} %>" : res.join(' + ')
3215
+ end
3216
+ else
3217
+ nil
3218
+ end
3219
+ end
3220
+
3221
+ def get_input_params(params = @params)
3222
+ res = {}
3223
+ if res[:name] = (params[:name] || params[:date])
3224
+ if res[:name] =~ /\A([\w_]+)\[(.*?)\]/
3225
+ attribute, sub_attr = $1, $2
3226
+ else
3227
+ attribute = res[:name]
3228
+ end
3229
+
3230
+ unless @context[:in_filter] || attribute == 's'
3231
+ if sub_attr
3232
+ res[:name] = "#{base_class.to_s.underscore}[#{attribute}][#{sub_attr}]"
3233
+ else
3234
+ res[:name] = "#{base_class.to_s.underscore}[#{attribute}]"
3235
+ end
3236
+ end
3237
+
3238
+ if sub_attr
3239
+ if (nattr = node_attribute(attribute)) != 'nil'
3240
+ nattr = "#{nattr}[#{sub_attr.inspect}]"
3241
+ end
3242
+ else
3243
+ nattr = node_attribute(attribute)
3244
+ end
3245
+
3246
+ if sub_attr && params[:type] == 'checkbox' && !params[:value]
3247
+ # Special case when we have a sub_attribute: default value for "tagged[foobar]" is "foobar"
3248
+ params[:value] = sub_attr
3249
+ end
3250
+
3251
+ if @context[:in_add]
3252
+ res[:value] = (params[:value] || params[:set_value]) ? ["'#{ helper.fquote(params[:value])}'"] : ["''"]
3253
+ elsif @context[:in_filter]
3254
+ res[:value] = attribute ? ["'<%= fquote params[#{attribute.to_sym.inspect}] %>'"] : ["''"]
3255
+ elsif params[:value]
3256
+ res[:value] = ["'#{ helper.fquote(params[:value])}'"]
3257
+ else
3258
+ if nattr != 'nil'
3259
+ res[:value] = ["'<%= fquote #{nattr} %>'"]
3260
+ else
3261
+ res[:value] = ["''"]
3262
+ end
3263
+ end
3264
+ end
3265
+
3266
+ if @context[:dom_prefix]
3267
+ res[:id] = "#{erb_dom_id}_#{attribute}"
3268
+ else
3269
+ res[:id] = params[:id] if params[:id]
3270
+ end
3271
+
3272
+ if params[:type] == 'checkbox' && nattr
3273
+ if value = params[:value]
3274
+ res[:checked] = "<%= #{nattr} == #{value.inspect} ? \" checked='checked'\" : '' %>"
3275
+ else
3276
+ res[:checked] = "<%= #{nattr}.blank? ? '' : \" checked='checked'\" %>"
3277
+ end
3278
+ end
3279
+
3280
+ [:size, :style, :class].each do |k|
3281
+ res[k] = params[k] if params[k]
3282
+ end
3283
+
3284
+ return [res, attribute]
3285
+ end
3286
+
3287
+ def get_html_params(params, tag_type)
3288
+ res = {}
3289
+ params.each do |k,v|
3290
+ next unless v
3291
+ if k.to_s =~ /\A(t?)set_(.+)$/
3292
+ key = $2
3293
+ trans = $1 == 't'
3294
+ value, static = parse_attributes_in_value(v, :erb => !trans)
3295
+
3296
+ if trans
3297
+ if static
3298
+ value = ["'#{_(value)}'"] # array so it is not escaped on render
3299
+ else
3300
+ value = ["'<%= _(\"#{value}\") %>'"] # FIXME: use dict ! array so it is not escaped on render
3301
+ end
3302
+ end
3303
+ res[key.to_sym] = value
3304
+ elsif tag_type == :link && ![:style, :class, :id, :title].include?(k)
3305
+ # bad html parameter for links (some keys for link tags are used as query parameters)
3306
+ # filter out
3307
+ else
3308
+ res[k] ||= v
3309
+ end
3310
+ end
3311
+
3312
+ if params[:anchor]
3313
+ @anchor_param = nil
3314
+ res[:name] = anchor_name(params[:anchor], node)
3315
+ end
3316
+
3317
+ res
3318
+ end
3319
+
3320
+ def get_options_for_select
3321
+ if nodes = @params[:nodes]
3322
+ # TODO: dry with r_checkbox
3323
+ if nodes =~ /^\d+\s*($|,)/
3324
+ # ids
3325
+ # TODO: optimization generate the full query instead of using secure.
3326
+ nodes = nodes.split(',').map{|v| v.to_i}
3327
+ nodes = "(secure(Node) { Node.find(:all, :conditions => 'zip IN (#{nodes.join(',')})') })"
3328
+ else
3329
+ # relation
3330
+ nodes, klass = build_finder_for(:all, nodes)
3331
+ return unless nodes
3332
+ return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
3333
+ end
3334
+ set_attr = @params[:attr] || 'id'
3335
+ show_attr = @params[:show] || 'name'
3336
+ options_list = "[['','']] + (#{nodes} || []).map{|r| [#{node_attribute(show_attr, :node => 'r', :node_class => Node)}, #{node_attribute(set_attr, :node => 'r', :node_class => Node)}.to_s]}"
3337
+ elsif values = @params[:values]
3338
+ options_list = values.split(',').map(&:strip)
3339
+
3340
+ if show = @params[:show]
3341
+ show_values = show.split(',').map(&:strip)
3342
+ elsif show = @params[:tshow]
3343
+ show_values = show.split(',').map do |s|
3344
+ _(s.strip)
3345
+ end
3346
+ end
3347
+
3348
+ if show_values
3349
+ options_list.each_index do |i|
3350
+ options_list[i] = [show_values[i], options_list[i]]
3351
+ end
3352
+ end
3353
+ options_list.inspect
3354
+ end
3355
+ end
3356
+
3357
+ def parse_eval_parameter(str)
3358
+ # evaluate an expression. Can only contain vars, '(', ')', '*', '+', '/', '-', '[attr]'
3359
+ # FIXME: SECURITY (audit this)
3360
+ vars = @context[:vars] || []
3361
+ parts = str.split(/\s+/)
3362
+ res = []
3363
+ test = []
3364
+ parts.each do |p|
3365
+ if p =~ /\[([\w_]+)\]/
3366
+ test << 1
3367
+ res << (node_attribute($1) + '.to_f')
3368
+ elsif p =~ /^[a-zA-Z_]+$/
3369
+ unless vars.include?(p)
3370
+ out parser_error("var #{p.inspect} not set in eval")
3371
+ return nil
3372
+ end
3373
+ test << 1
3374
+ res << "set_#{p}.to_f"
3375
+ elsif ['(', ')', '*', '+', '/', '-'].include?(p)
3376
+ res << p
3377
+ test << p
3378
+ elsif p =~ /^[0-9\.]+$/
3379
+ res << p
3380
+ test << p
3381
+ else
3382
+ out parser_error("bad argument #{p.inspect} in eval")
3383
+ return nil
3384
+ end
3385
+ end
3386
+ begin
3387
+ begin
3388
+ eval test.join(' ')
3389
+ rescue
3390
+ # rescue evaluation error
3391
+ out parser_error("error in eval")
3392
+ return nil
3393
+ end
3394
+ "(#{res.join(' ')})"
3395
+ rescue SyntaxError => err
3396
+ # rescue compilation error
3397
+ out parser_error("compilation error in eval")
3398
+ return nil
3399
+ end
3400
+ end
3401
+
3402
+ def find_stored(klass, key)
3403
+ if "#{klass}_#{key}" == "Node_start_node"
3404
+ # main node before ajax stuff (the one in browser url)
3405
+ "start_node"
3406
+ else
3407
+ @context["#{klass}_#{key}"]
3408
+ end
3409
+ end
3410
+
3411
+ def set_stored(klass, key, obj)
3412
+ @context["#{klass}_#{key}"] = obj
3413
+ end
3414
+
3415
+ # transform a 'show' tag into an input field.
3416
+ def make_input(params = @params)
3417
+ input, attribute = get_input_params(params)
3418
+ return parser_error("missing 'name'") unless attribute
3419
+ return '' if attribute == 'parent_id' # set with 'r_form'
3420
+ return '' if ['url','path'].include?(attribute) # cannot be set with a form
3421
+ if params[:date]
3422
+ input_id = @context[:dom_prefix] ? ", :id=>\"#{dom_id}_#{attribute}\"" : ''
3423
+ return "<%= date_box('#{base_class.to_s.underscore}', #{params[:date].inspect}#{input_id}) %>"
3424
+ end
3425
+ input_id = @context[:dom_prefix] ? " id='#{erb_dom_id}_#{attribute}'" : ''
3426
+ "<input type='#{params[:type] || 'text'}'#{input_id} name='#{input[:name]}' value=#{input[:value]}/>"
3427
+ end
3428
+
3429
+ # transform a 'zazen' tag into a textarea input field.
3430
+ def make_textarea(params)
3431
+ return parser_error("missing 'name'") unless name = params[:name]
3432
+ if name =~ /\A([\w_]+)\[(.*?)\]/
3433
+ attribute = $2
3434
+ else
3435
+ attribute = name
3436
+ name = "#{base_class.to_s.underscore}[#{attribute}]"
3437
+ end
3438
+ return '' if attribute == 'parent_id' # set with 'r_form'
3439
+
3440
+ if @blocks == [] || @blocks == ['']
3441
+ if @context[:in_add]
3442
+ value = ''
3443
+ else
3444
+ value = attribute ? "<%= #{node_attribute(attribute)} %>" : ""
3445
+ end
3446
+ else
3447
+ value = expand_with
3448
+ end
3449
+ html_id = @context[:dom_prefix] ? " id='#{erb_dom_id}_#{attribute}'" : ''
3450
+ "<textarea#{html_id} name='#{name}'>#{value}</textarea>"
3451
+ end
3452
+
3453
+ def default_focus_field
3454
+ if (input_fields = descendants('input')) != []
3455
+ field = input_fields.first.params[:name]
3456
+ elsif (show_fields = descendants('show')) != []
3457
+ field = show_fields.first.params[:attr]
3458
+ elsif node_kind_of?(Node)
3459
+ field = 'v_title'
3460
+ else
3461
+ field = 'text'
3462
+ end
3463
+ end
3464
+
3465
+ # Returns true if a form/edit needs to keep track of link_id (l_status or l_comment used).
3466
+ def need_link_id
3467
+ if (input_fields = (descendants('input') + descendants('select'))) != []
3468
+ input_fields.each do |f|
3469
+ return true if f.params[:name] =~ /\Al_/
3470
+ end
3471
+ elsif (show_fields = descendants('show')) != []
3472
+ show_fields.each do |f|
3473
+ return true if f.params[:attr] =~ /\Al_/
3474
+ end
3475
+ end
3476
+ return false
3477
+ end
3478
+
3479
+ def start_node_s_param(type = :input)
3480
+ if type == :input
3481
+ "<input type='hidden' name='s' value='<%= params[:s] || @node[:zip] %>'/>"
3482
+ elsif type == :erb
3483
+ "s=<%= params[:s] || @node[:zip] %>"
3484
+ elsif type == :value
3485
+ "<%= params[:s] || @node[:zip] %>"
3486
+ else
3487
+ "s=\#{params[:s] || @node[:zip]}"
3488
+ end
3489
+ end
3490
+
3491
+ def parser_error(message, tag=@method)
3492
+ "<span class='parser_error'>[#{tag}] #{message}</span>"
3493
+ end
3494
+
3495
+ # Used by [each] and [draggable] to insert 'id' and drag handle span
3496
+ def set_drag_handle_and_id(text, params, id_hash)
3497
+ res, drag_handle = text, nil
3498
+ if params[:drag_handle]
3499
+ drag_handle = params[:drag_handle] == 'true' ? 'drag_handle' : params[:drag_handle]
3500
+ if text =~ /class\s*=\s*['"]#{drag_handle}/
3501
+ # nothing to do
3502
+ insert = ''
3503
+ else
3504
+ insert = "<span class='#{drag_handle}'>&nbsp;</span>"
3505
+ end
3506
+ else
3507
+ insert = ''
3508
+ end
3509
+
3510
+ res = insert + text
3511
+
3512
+ if id_hash
3513
+ @html_tag ||= 'div'
3514
+ @html_tag_params.merge!(id_hash)
3515
+ end
3516
+
3517
+ [res, drag_handle]
3518
+ end
3519
+
3520
+ def expand_with(acontext={})
3521
+ # set variables
3522
+ context = nil
3523
+ pre = ''
3524
+ @blocks.each do |block|
3525
+ next if block.kind_of?(String) || block.method != 'set'
3526
+ @context[:vars] ||= []
3527
+ context ||= @context.merge(acontext).merge(:set => true)
3528
+ pre << expand_block(block, context)
3529
+ @context[:vars] << block.params[:var]
3530
+ end
3531
+
3532
+ pre + super
3533
+ end
3534
+
3535
+ def auto_publish_param(in_string = false)
3536
+ if in_string
3537
+ ['true','force'].include?(@params[:publish]) ? "&publish=#{@params[:publish]}" : ''
3538
+ else
3539
+ @params[:publish]
3540
+ end
3541
+ end
3542
+
3543
+ # This is used by zafu and it's a mess.
3544
+ # ref_date can be a string ('2005-05-03') or ruby ('Time.now'). It should not come uncleaned from evil web.
3545
+ def date_condition(date_cond, field, ref_date='today')
3546
+ if date_cond == 'today' || ref_date == 'today'
3547
+ ref_date = Zena::Db::NOW
3548
+ elsif ref_date =~ /(\d{4}-\d{1,2}-\d{1,2}( \d{1,2}:\d{1,2}(:\d{1,2})?)?)/
3549
+ ref_date = "'#{$1}'"
3550
+ elsif ref_date =~ /\A"/
3551
+ ref_date = "'\#{format_date(#{ref_date})}'"
3552
+ else
3553
+ ref_date = "'\#{#{ref_date}.strftime('%Y-%m-%d %H:%M:%S')}'"
3554
+ end
3555
+ Zena::Db.date_condition(date_cond, field, ref_date)
3556
+ end
3557
+ end # ZenaTags
3558
+ end # Parser
3559
+ end # Zena