zena 1.0.0.beta3 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (418) hide show
  1. data/History.txt +29 -0
  2. data/Rakefile +2 -0
  3. data/TODO_ZENA_1_0 +13 -23
  4. data/app/controllers/columns_controller.rb +1 -1
  5. data/app/controllers/comments_controller.rb +4 -3
  6. data/app/controllers/documents_controller.rb +8 -11
  7. data/app/controllers/nodes_controller.rb +39 -21
  8. data/app/controllers/users_controller.rb +8 -3
  9. data/app/controllers/versions_controller.rb +2 -2
  10. data/app/controllers/virtual_classes_controller.rb +17 -11
  11. data/app/helpers/documents_helper.rb +0 -3
  12. data/app/helpers/users_helper.rb +17 -0
  13. data/app/models/cache.rb +36 -31
  14. data/app/models/column.rb +48 -5
  15. data/app/models/comment.rb +14 -5
  16. data/app/models/data_entry.rb +2 -2
  17. data/app/models/document.rb +23 -33
  18. data/app/models/idx_nodes_datetime.rb +4 -0
  19. data/app/models/idx_nodes_float.rb +4 -0
  20. data/app/models/idx_project.rb +3 -0
  21. data/app/models/node.rb +372 -308
  22. data/app/models/page.rb +1 -31
  23. data/app/models/relation.rb +4 -4
  24. data/app/models/relation_proxy.rb +128 -17
  25. data/app/models/role.rb +27 -2
  26. data/app/models/site.rb +64 -56
  27. data/app/models/template.rb +11 -12
  28. data/app/models/text_document.rb +6 -7
  29. data/app/models/user.rb +95 -46
  30. data/app/models/version.rb +2 -2
  31. data/app/models/virtual_class.rb +418 -73
  32. data/app/views/columns/_form.html.erb +1 -1
  33. data/app/views/columns/_li.html.erb +1 -1
  34. data/app/views/comments/_form.rhtml +1 -1
  35. data/app/views/comments/_li.rhtml +1 -1
  36. data/app/views/comments/_li_simple.rhtml +1 -1
  37. data/app/views/groups/_form.rhtml +1 -1
  38. data/app/views/links/_li.rhtml +1 -1
  39. data/app/views/nodes/_groups.rhtml +1 -1
  40. data/app/views/nodes/_import_results.rhtml +1 -1
  41. data/app/views/nodes/_parent.rhtml +1 -1
  42. data/app/views/nodes/_results.rhtml +1 -1
  43. data/app/views/nodes/create.rjs +4 -2
  44. data/app/views/relations/_li.erb +2 -2
  45. data/app/views/templates/document_create_tabs/_file.rhtml +1 -1
  46. data/app/views/templates/document_create_tabs/_template.rhtml +2 -2
  47. data/app/views/templates/document_create_tabs/_text_document.rhtml +2 -2
  48. data/app/views/templates/edit_tabs/_help.rhtml +1 -1
  49. data/app/views/templates/edit_tabs/_title.rhtml +0 -3
  50. data/app/views/users/_form.rhtml +2 -6
  51. data/app/views/users/_li.rhtml +1 -3
  52. data/app/views/users/create.rjs +4 -4
  53. data/app/views/users/preferences.html.erb +1 -4
  54. data/app/views/versions/custom_tab.rhtml +5 -0
  55. data/app/views/virtual_classes/_form.erb +20 -10
  56. data/app/views/virtual_classes/_li.erb +21 -8
  57. data/app/views/zafu/default/Node-+search.zafu +1 -1
  58. data/app/views/zafu/default/Node.zafu +3 -3
  59. data/bricks/captcha/lib/bricks/captcha.rb +1 -1
  60. data/bricks/mongrel/zena/deploy.rb +14 -0
  61. data/bricks/{data2pdf → pdf}/.document +0 -0
  62. data/bricks/pdf/README +33 -0
  63. data/bricks/{data2pdf → pdf}/Rakefile +0 -0
  64. data/bricks/pdf/VERSION +1 -0
  65. data/bricks/pdf/lib/bricks/pdf.rb +110 -0
  66. data/bricks/pdf/lib/bricks/pdf/engine/prince.rb +38 -0
  67. data/bricks/pdf/lib/bricks/pdf/engine/xhtml2pdf.rb +9 -0
  68. data/bricks/pdf/lib/bricks/pdf/install.rb +121 -0
  69. data/bricks/pdf/test/engines/test_prince.rb +15 -0
  70. data/bricks/pdf/test/engines/test_xhtml2pdf.rb +15 -0
  71. data/bricks/{data2pdf → pdf}/test/fixtures/application.css +0 -0
  72. data/bricks/{data2pdf → pdf}/test/fixtures/contact.html +0 -0
  73. data/bricks/{data2pdf → pdf}/test/fixtures/pisa-default.css +0 -0
  74. data/bricks/{data2pdf → pdf}/test/fixtures/sheet1.css +0 -0
  75. data/bricks/{data2pdf → pdf}/test/fixtures/sheet2.css +0 -0
  76. data/bricks/{data2pdf → pdf}/test/fixtures/simple-html.html +0 -0
  77. data/bricks/{data2pdf → pdf}/test/fixtures/simple-text.txt +0 -0
  78. data/bricks/{data2pdf → pdf}/test/helper.rb +4 -5
  79. data/bricks/pdf/test/shoulda_macros/shoulda_pdf.rb +72 -0
  80. data/bricks/pdf/zena/init.rb +5 -0
  81. data/bricks/pdf/zena/tasks.rb +17 -0
  82. data/bricks/sphinx/lib/{use_sphinx.rb → bricks/sphinx.rb} +1 -1
  83. data/bricks/tags/zena/init.rb +2 -2
  84. data/bricks/tags/zena/test/zafu/tags.yml +4 -4
  85. data/bricks/zena/zena/migrate/01_base.rb +482 -0
  86. data/config/bricks.yml +22 -6
  87. data/config/gems.yml +8 -6
  88. data/db/20100628074512_zena0x_to1x.rb +6 -1
  89. data/db/fix/024_correct_vclass_kpath.rb +11 -0
  90. data/db/fix/025_move_tag_into_vclass.rb +13 -0
  91. data/db/{migrate → fix}/026_rename_templates.rb +0 -0
  92. data/db/{migrate → fix}/045_avoid_star_in_templates.rb +0 -0
  93. data/db/{migrate → fix}/046_fix_zazen_image_tag.rb +0 -0
  94. data/db/{migrate → fix}/047_change_default_link_id_to_zero.rb +1 -3
  95. data/db/{migrate → fix}/049_fix_publish_from_is_null.rb +0 -0
  96. data/db/{migrate → fix}/20090924141459_zafu_fix_sept09.rb +0 -0
  97. data/db/{migrate → fix}/20091013100351_rename_publish_group_to_drive_group.rb +1 -3
  98. data/db/{migrate → fix}/20091124161608_rebuild_fullpath.rb +0 -1
  99. data/db/{migrate → fix}/20100115134729_rebuild_fullpath_after_fix.rb +0 -0
  100. data/db/{migrate → fix}/20100526090140_renamed_contact_model_to_base_contact.rb +2 -4
  101. data/db/{migrate → fix/old_migrations}/001_create_base.rb +0 -1
  102. data/db/{migrate → fix/old_migrations}/002_add_time_zone_to_users.rb +0 -0
  103. data/db/{migrate → fix/old_migrations}/003_add_custom_base_flag.rb +0 -0
  104. data/db/{migrate → fix/old_migrations}/004_rename_template_skin.rb +0 -0
  105. data/db/{migrate → fix/old_migrations}/005_create_cached_pages.rb +0 -0
  106. data/db/{migrate → fix/old_migrations}/006_create_sites.rb +0 -0
  107. data/db/{migrate → fix/old_migrations}/007_replace_id_by_zip.rb +0 -0
  108. data/db/{migrate → fix/old_migrations}/008_user_status.rb +0 -0
  109. data/db/{migrate → fix/old_migrations}/009_fulltext.rb +0 -0
  110. data/db/fix/old_migrations/010_create_template_content.rb +17 -0
  111. data/db/{migrate → fix/old_migrations}/011_project_to_section.rb +0 -0
  112. data/db/{migrate → fix/old_migrations}/012_add_project_id.rb +0 -0
  113. data/db/{migrate → fix/old_migrations}/013_remove_defaults.rb +0 -0
  114. data/db/{migrate → fix/old_migrations}/014_add_sort_field.rb +0 -0
  115. data/db/{migrate → fix/old_migrations}/015_add_dyn_attributes.rb +0 -0
  116. data/db/{migrate → fix/old_migrations}/016_remove_translations.rb +0 -0
  117. data/db/{migrate → fix/old_migrations}/017_rename_authorize.rb +0 -0
  118. data/db/{migrate → fix/old_migrations}/018_add_auth_option.rb +0 -0
  119. data/db/{migrate → fix/old_migrations}/019_remove_user_status.rb +0 -0
  120. data/db/{migrate → fix/old_migrations}/020_create_participation.rb +0 -0
  121. data/db/{migrate → fix/old_migrations}/021_create_relations.rb +0 -0
  122. data/db/{migrate → fix/old_migrations}/022_create_virtual_classes.rb +0 -0
  123. data/db/{migrate → fix/old_migrations}/023_ip_on_anonymous_comment.rb +0 -0
  124. data/db/{migrate → fix/old_migrations}/027_add_country_to_contacts.rb +0 -0
  125. data/db/{migrate → fix/old_migrations}/028_change_size_of_conten_type_field.rb +0 -0
  126. data/db/{migrate → fix/old_migrations}/029_create_data_entries.rb +0 -0
  127. data/db/{migrate → fix/old_migrations}/030_redit_auto_publish_site_settings.rb +0 -0
  128. data/db/{migrate → fix/old_migrations}/031_create_iformats.rb +0 -0
  129. data/db/{migrate → fix/old_migrations}/032_caches_context_as_hash.rb +0 -0
  130. data/db/{migrate → fix/old_migrations}/033_documents_kpath_change.rb +0 -0
  131. data/db/{migrate → fix/old_migrations}/034_change_file_storage.rb +0 -0
  132. data/db/{migrate → fix/old_migrations}/035_add_status_to_link.rb +0 -0
  133. data/db/{migrate → fix/old_migrations}/036_add_flag_fields_on_nodes.rb +0 -0
  134. data/db/{migrate → fix/old_migrations}/037_add_auto_create_discussion_to_v_class.rb +0 -0
  135. data/db/{migrate → fix/old_migrations}/038_create_site_attributes.rb +0 -0
  136. data/db/{migrate → fix/old_migrations}/039_default_position.rb +0 -0
  137. data/db/{migrate → fix/old_migrations}/040_second_value_for_data_entry.rb +0 -0
  138. data/db/{migrate → fix/old_migrations}/041_add_attributes_to_v_class.rb +0 -0
  139. data/db/{migrate → fix/old_migrations}/042_fix_position_should_be_float.rb +0 -0
  140. data/db/{migrate → fix/old_migrations}/043_move_user_lang_into_participation.rb +0 -0
  141. data/db/{migrate → fix/old_migrations}/044_remove_monolingual_site_option.rb +0 -0
  142. data/db/{migrate → fix/old_migrations}/048_link_source_target_can_be_null.rb +0 -0
  143. data/db/{migrate → fix/old_migrations}/050_date_in_links.rb +0 -0
  144. data/db/{migrate → fix/old_migrations}/051_add_exif_tags_to_images.rb +0 -0
  145. data/db/{migrate → fix/old_migrations}/20090825201159_insert_zero_link.rb +0 -0
  146. data/db/{migrate → fix/old_migrations}/20090825201200_merge_bricks_migrations_with_std_migrations.rb +0 -0
  147. data/db/{migrate → fix/old_migrations}/20090927125912_allow_null_in_text_fields.rb +0 -0
  148. data/db/{migrate → fix/old_migrations}/20090928133440_no_more_private_nodes.rb +0 -0
  149. data/db/{migrate → fix/old_migrations}/20090928143754_version_status_change.rb +0 -0
  150. data/db/{migrate → fix/old_migrations}/20091001084025_change_status_values_for_comments.rb +0 -0
  151. data/db/{migrate → fix/old_migrations}/20091009084057_add_vhash_in_node.rb +0 -0
  152. data/db/{migrate → fix/old_migrations}/20091014130833_fix_template_title.rb +0 -0
  153. data/db/{migrate → fix/old_migrations}/20091014183726_merge_participation_into_users.rb +0 -0
  154. data/db/{migrate → fix/old_migrations}/20091018200734_add_popup_info_to_image_format.rb +0 -0
  155. data/db/{migrate → fix/old_migrations}/20091026161708_add_persistence_token.rb +0 -0
  156. data/db/{migrate → fix/old_migrations}/20091101184952_add_session_table.rb +0 -0
  157. data/db/{migrate → fix/old_migrations}/20091123175137_add_single_access_token.rb +0 -0
  158. data/db/{migrate → fix/old_migrations}/20100125062254_add_dynamo_to_version.rb +0 -0
  159. data/db/{migrate → fix/old_migrations}/20100201133242_remove_default_status_on_version.rb +0 -0
  160. data/db/{migrate → fix/old_migrations}/20100208194210_create_attachments.rb +0 -0
  161. data/db/{migrate → fix/old_migrations}/20100210112319_change_dynamo_to_property.rb +0 -0
  162. data/db/{migrate → fix/old_migrations}/20100320145726_transform_template_contents_into_index.rb +0 -0
  163. data/db/{migrate → fix/old_migrations}/20100328125634_change_skin_name_to_id.rb +0 -0
  164. data/db/{migrate → fix/old_migrations}/20100417061257_add_properties_to_sites.rb +0 -0
  165. data/db/{migrate → fix/old_migrations}/20100419163149_rename_name_to_node_name.rb +0 -0
  166. data/db/{migrate → fix/old_migrations}/20100422091606_change_v_class_table_into_roles.rb +0 -0
  167. data/db/{migrate → fix/old_migrations}/20100422094048_node_habtm_roles.rb +0 -0
  168. data/db/{migrate → fix/old_migrations}/20100422115935_create_columns.rb +0 -0
  169. data/db/{migrate → fix/old_migrations}/20100513181529_add_site_id_to_columns.rb +0 -0
  170. data/db/{migrate → fix/old_migrations}/20100519091711_add_index_definition_to_columns.rb +0 -0
  171. data/db/{migrate → fix/old_migrations}/20100519091940_create_idx_nodes_string.rb +0 -0
  172. data/db/{migrate → fix/old_migrations}/20100519232432_create_idx_nodes_ml_string.rb +0 -0
  173. data/db/{migrate → fix/old_migrations}/20100525113858_add_porperties_to_users.rb +0 -0
  174. data/db/{migrate → fix/old_migrations}/20100527130937_change_column_index_to_string.rb +0 -0
  175. data/db/{migrate → fix/old_migrations}/20100531135128_add_fulltext_builder_fields.rb +0 -0
  176. data/db/{migrate → fix/old_migrations}/20100915062903_add_api_group_id_to_site.rb +0 -0
  177. data/db/fix/old_migrations/20100923154807_remove_base_contact.rb +84 -0
  178. data/db/fix/old_migrations/20100926192223_remove_su_user.rb +8 -0
  179. data/db/fix/old_migrations/20100927141658_add_eval_attributes_to_v_class.rb +12 -0
  180. data/db/fix/old_migrations/20100928185257_add_obvious_idx.rb +52 -0
  181. data/db/fix/old_migrations/20100929143111_remove_node_name.rb +11 -0
  182. data/db/fix/old_migrations/20101006090454_store_properties_in_long_text.rb +9 -0
  183. data/db/fix/old_migrations/20101014185753_remove_user_prototype_id.rb +9 -0
  184. data/db/fix/old_migrations/20101101084318_create_scope_index.rb +35 -0
  185. data/db/fix/old_migrations/20101109074232_create_idx_nodes_tables.rb +65 -0
  186. data/db/fix/old_migrations/20101110184235_add_role_update_to_site.rb +9 -0
  187. data/db/fix/old_migrations/20101116103920_change_scope_index.rb +31 -0
  188. data/db/fix/old_migrations/20101123125822_add_integer_idx.rb +17 -0
  189. data/db/fix/old_migrations/20101130134522_add_index_field.rb +13 -0
  190. data/db/fix/old_migrations/20101213133816_add_group_to_relation.rb +9 -0
  191. data/db/init/base/help.fr.zml +1 -1
  192. data/db/init/base/skins/default.zml +0 -1
  193. data/db/init/base/skins/default/Node-+search.zafu +1 -1
  194. data/db/init/base/skins/default/Node-tree.zafu +3 -3
  195. data/db/init/base/skins/default/Node.zafu +3 -3
  196. data/lib/bricks/loader.rb +4 -1
  197. data/lib/bricks/requirements_validation.rb +11 -6
  198. data/lib/log_recorder/lib/log_recorder.rb +2 -2
  199. data/lib/tasks/zena.rake +25 -15
  200. data/lib/zena.rb +42 -9
  201. data/lib/zena/acts/enrollable.rb +81 -99
  202. data/lib/zena/acts/secure.rb +27 -23
  203. data/lib/zena/acts/secure_node.rb +10 -55
  204. data/lib/zena/acts/serializable.rb +9 -10
  205. data/lib/zena/app.rb +0 -2
  206. data/lib/zena/code_syntax.rb +1 -1
  207. data/lib/zena/controller/test_case.rb +0 -5
  208. data/lib/zena/core_ext/string.rb +48 -20
  209. data/lib/zena/db.rb +10 -442
  210. data/lib/zena/db_helper/abstract_db.rb +184 -0
  211. data/lib/zena/db_helper/mysql.rb +150 -0
  212. data/lib/zena/db_helper/postgresql.rb +79 -0
  213. data/lib/zena/db_helper/sqlite3.rb +135 -0
  214. data/lib/zena/deploy.rb +4 -1
  215. data/lib/zena/deploy/httpd.rhtml +3 -3
  216. data/lib/zena/deploy/vhost.rhtml +1 -1
  217. data/lib/zena/foxy_parser.rb +37 -18
  218. data/lib/zena/info.rb +3 -13
  219. data/lib/zena/migrator.rb +0 -1
  220. data/lib/zena/parser/zafu_rules.rb +9 -4
  221. data/lib/zena/parser/zazen_rules.rb +5 -5
  222. data/lib/zena/parser/zena_rules.rb +1 -1
  223. data/lib/zena/remote/interface.rb +1 -1
  224. data/lib/zena/site_worker.rb +3 -3
  225. data/lib/zena/test_controller.rb +10 -10
  226. data/lib/zena/use/action.rb +66 -6
  227. data/lib/zena/use/ajax.rb +39 -13
  228. data/lib/zena/use/ancestry.rb +210 -0
  229. data/lib/zena/use/authlogic.rb +30 -1
  230. data/lib/zena/use/calendar.rb +158 -0
  231. data/lib/zena/use/conditional.rb +3 -2
  232. data/lib/zena/use/context.rb +42 -12
  233. data/lib/zena/use/dates.rb +15 -14
  234. data/lib/zena/use/display.rb +54 -7
  235. data/lib/zena/use/error_rendering.rb +1 -0
  236. data/lib/zena/use/field_index.rb +20 -0
  237. data/lib/zena/use/fixtures.rb +12 -9
  238. data/lib/zena/use/forms.rb +230 -106
  239. data/lib/zena/use/fulltext.rb +28 -14
  240. data/lib/zena/use/html_tags.rb +1 -24
  241. data/lib/zena/use/i18n.rb +69 -14
  242. data/lib/zena/use/kpath.rb +60 -0
  243. data/lib/zena/use/ml_index.rb +6 -4
  244. data/lib/zena/use/node_context.rb +63 -0
  245. data/lib/zena/use/prop_eval.rb +90 -0
  246. data/lib/zena/use/query_builder.rb +159 -29
  247. data/lib/zena/use/query_comment.rb +1 -1
  248. data/lib/zena/use/query_node.rb +147 -56
  249. data/lib/zena/use/recursion.rb +2 -2
  250. data/lib/zena/use/relations.rb +31 -19
  251. data/lib/zena/use/rendering.rb +111 -121
  252. data/lib/zena/use/scope_index.rb +230 -0
  253. data/lib/zena/use/search.rb +7 -7
  254. data/lib/zena/use/urls.rb +87 -25
  255. data/lib/zena/use/version_hash.rb +113 -113
  256. data/lib/zena/use/workflow.rb +5 -1
  257. data/lib/zena/use/zafu_attributes.rb +11 -14
  258. data/lib/zena/use/zafu_eval.rb +1 -1
  259. data/lib/zena/use/zafu_safe_definitions.rb +91 -9
  260. data/lib/zena/use/zafu_templates.rb +146 -102
  261. data/lib/zena/use/zazen.rb +5 -4
  262. data/lib/zena/zafu_compiler.rb +1 -0
  263. data/locale/en/LC_MESSAGES/zena.mo +0 -0
  264. data/locale/en/zena.po +0 -1
  265. data/locale/fr/LC_MESSAGES/zena.mo +0 -0
  266. data/locale/fr/zena.mo +0 -0
  267. data/locale/fr/zena.po +4 -4
  268. data/misc/zena +35 -0
  269. data/misc/zena_init +41 -0
  270. data/public/images/ext/{basecontact.png → contact.png} +0 -0
  271. data/public/javascripts/zena.js +35 -7
  272. data/public/stylesheets/admin.css +5 -2
  273. data/public/stylesheets/default.css +2 -1
  274. data/public/stylesheets/popup.css +1 -1
  275. data/public/stylesheets/zena.css +2 -2
  276. data/test/custom_queries/complex.host.yml +12 -5
  277. data/test/fixtures/files/Node-test.zafu +3 -3
  278. data/test/fixtures/files/translations_fr.yml +4 -2
  279. data/test/functional/documents_controller_test.rb +31 -0
  280. data/test/functional/nodes_controller_commit_test.rb +1 -5
  281. data/test/functional/nodes_controller_test.rb +92 -12
  282. data/test/functional/user_sessions_controller_test.rb +2 -2
  283. data/test/functional/users_controller_test.rb +31 -29
  284. data/test/functional/versions_controller_test.rb +2 -2
  285. data/test/functional/virtual_classes_controller_test.rb +2 -2
  286. data/test/integration/multiple_hosts_test.rb +19 -8
  287. data/test/integration/navigation_test.rb +91 -12
  288. data/test/integration/query_node/basic.yml +40 -37
  289. data/test/integration/query_node/complex.yml +23 -18
  290. data/test/integration/query_node/dates.yml +3 -3
  291. data/test/integration/query_node/errors.yml +7 -1
  292. data/test/integration/query_node/filters.yml +41 -35
  293. data/test/integration/query_node/idx_fields.yml +11 -0
  294. data/test/integration/query_node/idx_key_value.yml +77 -0
  295. data/test/integration/query_node/idx_scope.yml +33 -0
  296. data/test/integration/query_node/relations.yml +13 -13
  297. data/test/integration/query_node_test.rb +6 -10
  298. data/test/integration/zafu_compiler/action.yml +19 -6
  299. data/test/integration/zafu_compiler/ajax.yml +111 -51
  300. data/test/integration/zafu_compiler/apphelper.yml +1 -1
  301. data/test/integration/zafu_compiler/asset.yml +1 -1
  302. data/test/integration/zafu_compiler/basic.yml +42 -52
  303. data/test/integration/zafu_compiler/calendar.yml +3 -3
  304. data/test/integration/zafu_compiler/complex.yml +16 -16
  305. data/test/integration/zafu_compiler/complex_ok.yml +2 -2
  306. data/test/integration/zafu_compiler/conditional.yml +42 -33
  307. data/test/integration/zafu_compiler/data.yml +3 -3
  308. data/test/integration/zafu_compiler/dates.yml +25 -10
  309. data/test/integration/zafu_compiler/display.yml +49 -12
  310. data/test/integration/zafu_compiler/errors.yml +26 -6
  311. data/test/integration/zafu_compiler/eval.yml +4 -4
  312. data/test/integration/zafu_compiler/forms.yml +89 -15
  313. data/test/integration/zafu_compiler/i18n.yml +23 -18
  314. data/test/integration/zafu_compiler/idx_scope.yml +7 -0
  315. data/test/integration/zafu_compiler/later.yml +10 -16
  316. data/test/integration/zafu_compiler/off/off.yml +2 -2
  317. data/test/integration/zafu_compiler/query.yml +207 -0
  318. data/test/integration/zafu_compiler/recursion.yml +2 -2
  319. data/test/integration/zafu_compiler/relations.yml +144 -168
  320. data/test/integration/zafu_compiler/roles.yml +86 -10
  321. data/test/integration/zafu_compiler/rubyless.yml +49 -6
  322. data/test/integration/zafu_compiler/safe_definitions.yml +35 -6
  323. data/test/integration/zafu_compiler/search.yml +1 -1
  324. data/test/integration/zafu_compiler/security.yml +37 -0
  325. data/test/integration/zafu_compiler/urls.yml +50 -40
  326. data/test/integration/zafu_compiler/user.yml +21 -6
  327. data/test/integration/zafu_compiler/version.yml +6 -6
  328. data/test/integration/zafu_compiler/zafu_attributes.yml +43 -34
  329. data/test/integration/zafu_compiler/zazen.yml +10 -10
  330. data/test/integration/zafu_compiler_test.rb +19 -13
  331. data/test/sites/complex/nodes.yml +0 -2
  332. data/test/sites/complex/roles.yml +9 -1
  333. data/test/sites/complex/sites.yml +0 -1
  334. data/test/sites/complex/users.yml +2 -5
  335. data/test/sites/ocean/nodes.yml +2 -5
  336. data/test/sites/ocean/roles.yml +8 -0
  337. data/test/sites/ocean/sites.yml +0 -1
  338. data/test/sites/ocean/users.yml +0 -13
  339. data/test/sites/zena/columns.yml +27 -5
  340. data/test/sites/zena/idx_projects.yml +5 -0
  341. data/test/sites/zena/nodes.yml +8 -32
  342. data/test/sites/zena/relations.yml +5 -0
  343. data/test/sites/zena/roles.yml +25 -3
  344. data/test/sites/zena/sites.yml +2 -2
  345. data/test/sites/zena/users.yml +1 -21
  346. data/test/sites/zena/versions.yml +35 -12
  347. data/test/test_helper.rb +7 -0
  348. data/test/unit/after_commit_test.rb +7 -7
  349. data/test/unit/cache_test.rb +32 -0
  350. data/test/unit/cached_page_test.rb +1 -1
  351. data/test/unit/column_test.rb +31 -7
  352. data/test/unit/comment_test.rb +2 -2
  353. data/test/unit/core_ext_test.rb +38 -7
  354. data/test/unit/document_test.rb +14 -42
  355. data/test/unit/node_test.rb +311 -324
  356. data/test/unit/note_test.rb +23 -31
  357. data/test/unit/page_test.rb +16 -58
  358. data/test/unit/project_test.rb +2 -2
  359. data/test/unit/relation_proxy_test.rb +148 -21
  360. data/test/unit/relation_test.rb +23 -3
  361. data/test/unit/remote_test.rb +15 -9
  362. data/test/unit/role_test.rb +9 -0
  363. data/test/unit/site_test.rb +49 -47
  364. data/test/unit/skin_test.rb +16 -0
  365. data/test/unit/template_test.rb +60 -69
  366. data/test/unit/text_document_test.rb +15 -14
  367. data/test/unit/user_test.rb +101 -41
  368. data/test/unit/version_test.rb +4 -4
  369. data/test/unit/virtual_class_test.rb +577 -36
  370. data/test/unit/workflow_test.rb +58 -21
  371. data/test/unit/zena/acts/enrollable_test.rb +36 -127
  372. data/test/unit/zena/acts/secure_test.rb +6 -22
  373. data/test/unit/zena/acts/serializable_test.rb +18 -0
  374. data/test/unit/zena/db_test.rb +14 -14
  375. data/test/unit/zena/parser/zafu.yml +5 -3
  376. data/test/unit/zena/use/ancestry_test.rb +198 -0
  377. data/test/unit/zena/use/calendar_test.rb +8 -8
  378. data/test/unit/zena/use/dates_test.rb +2 -0
  379. data/test/unit/zena/use/fulltext_test.rb +9 -1
  380. data/test/unit/zena/use/html_tags_test.rb +2 -16
  381. data/test/unit/zena/use/i18n_test.rb +2 -2
  382. data/test/unit/zena/use/kpath_test.rb +13 -0
  383. data/test/unit/zena/use/ml_index_test.rb +60 -12
  384. data/test/unit/zena/use/prop_eval_test.rb +170 -0
  385. data/test/unit/zena/use/query_node_test.rb +9 -2
  386. data/test/unit/zena/use/rendering_test.rb +98 -1
  387. data/test/unit/zena/use/scope_index_test.rb +464 -0
  388. data/test/unit/zena/use/urls_test.rb +23 -13
  389. data/test/unit/zena/use/version_hash_test.rb +2 -2
  390. data/test/unit/zena/use/zafu_template_test.rb +21 -8
  391. data/test/unit/zena/use/zazen_test.rb +47 -47
  392. data/zena.gemspec +177 -143
  393. metadata +222 -141
  394. data/app/models/base_contact.rb +0 -79
  395. data/app/models/book.rb +0 -242
  396. data/app/models/contact_content.rb +0 -70
  397. data/app/models/contact_version.rb +0 -40
  398. data/app/models/reference.rb +0 -18
  399. data/app/views/templates/edit_tabs/_basecontact.rhtml +0 -8
  400. data/bricks/data2pdf/README +0 -19
  401. data/bricks/data2pdf/VERSION +0 -1
  402. data/bricks/data2pdf/lib/data2pdf.rb +0 -60
  403. data/bricks/data2pdf/lib/engines/prince.rb +0 -39
  404. data/bricks/data2pdf/lib/engines/xhtml2pdf.rb +0 -41
  405. data/bricks/data2pdf/lib/install.rb +0 -111
  406. data/bricks/data2pdf/test/engines/test_prince.rb +0 -14
  407. data/bricks/data2pdf/test/engines/test_xhtml2pdf.rb +0 -14
  408. data/bricks/data2pdf/test/shoulda_macros/shoulda_data2pdf.rb +0 -91
  409. data/bricks/data2pdf/test/unit/test_rendering.rb +0 -37
  410. data/config/routes.rb +0 -3
  411. data/db/migrate/010_create_template_content.rb +0 -17
  412. data/db/migrate/024_correct_vclass_kpath.rb +0 -13
  413. data/db/migrate/025_move_tag_into_vclass.rb +0 -15
  414. data/lib/version_off.rb +0 -323
  415. data/lib/zena/use/node_name.rb +0 -94
  416. data/test/integration/query_node/properties.yml +0 -41
  417. data/test/unit/base_contact_test.rb +0 -242
  418. data/test/unit/node_name_test.rb +0 -137
@@ -1,29 +1,112 @@
1
1
  module Zena
2
2
  module Use
3
3
  module QueryBuilder
4
+ DATE_FIELD_REGEXP = %r{\d+[\.-/]\d+[\.-/]\d+}
4
5
  module ViewMethods
6
+ include RubyLess
7
+ safe_method [:query_parse, String] => {:class => String, :accept_nil => true}
8
+
5
9
  def find_node_by_zip(zip)
6
10
  return nil unless zip
7
11
  secure(Node) { Node.find_by_zip(zip) }
8
12
  end
9
13
 
10
- def query(class_name, node_name, pseudo_sql)
11
- klass = get_class(class_name)
12
- begin
13
- query = klass.build_query(:all, pseudo_sql,
14
- :node_name => node_name,
15
- :main_class => klass,
16
- # We use 'zafu_helper' (which is slower) instead of 'self' because our helper needs to have helper modules
17
- # mixed in and strangely RubyLess cannot access the helpers from 'self'.
18
- :rubyless_helper => zafu_helper.helpers
19
- )
20
- rescue ::QueryBuilder::Error => err
21
- # FIXME: how to return error messages to the user ?
22
- nil
14
+ def query(class_name, node_name, pseudo_sql, opts = {})
15
+ type = opts[:type] || :find
16
+ if klass = VirtualClass[class_name]
17
+ begin
18
+ query = klass.build_query(:all, pseudo_sql,
19
+ :node_name => node_name,
20
+ :main_class => klass,
21
+ # We use 'zafu_helper' (which is slower) instead of 'self' because our helper needs to have helper modules
22
+ # mixed in and strangely RubyLess cannot access the helpers from 'self'.
23
+ :rubyless_helper => zafu_helper.helpers
24
+ )
25
+ if type == :count
26
+ return klass.do_find(:count, eval(query.to_s(:count)))
27
+ else
28
+ return klass.do_find(:all, eval(query.to_s))
29
+ end
30
+ rescue ::QueryBuilder::Error => err
31
+ # FIXME: how to return error messages to the user ?
32
+ end
23
33
  end
34
+ # error
35
+ type == :count ? 0 : nil
36
+ end
24
37
 
25
- klass.do_find(:all, eval(query.to_s))
38
+ # Takes a hash of parameters and builds query arguments for SQLiss
39
+ # the query ends up like ' AND param_name = "foo" AND param_name > 35'...
40
+ def query_parse(params)
41
+ params ||= {}
42
+ res = []
43
+ if params.kind_of?(String)
44
+ return params
45
+ elsif params.kind_of?(Hash)
46
+ params.each do |k,v|
47
+ clause = query_build_clause(k,v)
48
+ res << clause unless clause.blank?
49
+ end
50
+ end
51
+ res.empty? ? '1=1' : res.join(' and ')
26
52
  end
53
+
54
+ private
55
+ # Transforms special syntax into SQLiss. Argument can be
56
+ #
57
+ # '' ==> (empty fields are ignored)
58
+ # '""' ==> key = ""
59
+ # '"null"' ==> key = "null"
60
+ # 'null' ==> key is null
61
+ # 'foobar' ==> key = "foobar"
62
+ # '> 45' ==> key > 45
63
+ # '10..20' ==> key >= 10 and key <= 20
64
+ # '%bar' ==> key like "%bar"
65
+ # '"foo%"' ==> key = "foo%"
66
+ def query_build_clause(key, arg)
67
+ first = arg.first
68
+ return "not (#{query_build_clause(key, arg[1..-1])})" if first == '!'
69
+ if ["'", '"'].include?(first)
70
+ return "#{key} = #{arg}"
71
+ end
72
+
73
+ if arg =~ DATE_FIELD_REGEXP
74
+ arg = query_parse_dates(arg)
75
+ first = arg.first
76
+ end
77
+
78
+ if ['>','<','='].include?(first)
79
+ "#{key} #{arg}"
80
+ elsif arg == 'null'
81
+ "#{key} is null"
82
+ elsif arg == ''
83
+ # ignore
84
+ ''
85
+ elsif arg =~ /\*/
86
+ # like
87
+ "#{key} like #{arg.gsub('*','%').inspect}"
88
+ elsif arg =~ /^(.+)\.\.(.+)$/
89
+ # interval
90
+ "#{key} >= #{$1} and #{key} <= #{$2}"
91
+ elsif arg =~ /^\d+$/
92
+ # number
93
+ "#{key} = #{arg}"
94
+ elsif first == "'"
95
+ "#{key} = #{arg}"
96
+ else
97
+ "#{key} = #{arg.inspect}"
98
+ end
99
+ end
100
+
101
+ def query_parse_dates(str)
102
+ str.gsub(DATE_FIELD_REGEXP) do |date_str|
103
+ if date = DateTime.parse(date_str) rescue nil
104
+ date.strftime("'%Y-%m-%d'")
105
+ else
106
+ date_str
107
+ end
108
+ end
109
+ end
27
110
  end # ViewMethods
28
111
 
29
112
  # 1. try Zafu
@@ -38,46 +121,85 @@ module Zena
38
121
 
39
122
  include RubyLess
40
123
  # The :class argument in this method is only used when the String is not a literal value
41
- safe_method [:find, String] => {:method => 'nil', :pre_processor => :get_finder_type, :class => NilClass}
42
- safe_method [:find, Number] => {:method => :find_node_by_zip, :class => Node, :nil => true, :accept_nil => true}
124
+ safe_method [:find, String] => {:method => 'nil', :pre_processor => :get_type_for_find, :class => NilClass}
125
+ safe_method [:count, String] => {:method => '0', :pre_processor => :get_type_for_count, :class => Number }
126
+ safe_method [:find, Number] => {:method => :find_node_by_zip, :class => Node, :nil => true, :accept_nil => true}
43
127
 
44
128
  def self.included(base)
45
129
  base.process_unknown :querybuilder_eval
46
130
  end
47
131
 
132
+ # Dynamic query mocks the QueryBuilder::Query
133
+ class DynamicQuery
134
+ def initialize(default, node, sql)
135
+ @default, @node, @sql = default, node, sql
136
+ end
137
+
138
+ def to_s(type = :find)
139
+ base = "'#{@node.klass}', #{@node.to_s.inspect}, #{@sql}"
140
+ case type
141
+ when :find
142
+ return "query(#{base})"
143
+ else
144
+ return "query(#{base}, :type => #{type.inspect})"
145
+ end
146
+ end
147
+
148
+ # Pass all other methods to the default query.
149
+ def method_missing(meth, *args)
150
+ @default.send(meth, *args)
151
+ end
152
+ end
153
+
48
154
  # Open a list context with a query comming from the url params. Default param name is
49
155
  # "qb"
50
156
  def r_query
51
157
  return parser_error("Cannot be used in list context (#{node.class_name})") if node.list_context?
52
- return parser_error("Missing 'default' query") unless default = @params[:default]
53
- return parser_error("No query compiler for (#{node.class_name})") if !node.klass.respond_to?(:build_query)
158
+ return parser_error("Missing 'default' query") unless default_psql = @params[:default]
54
159
 
160
+ begin
161
+ default = build_finder(:all, default_psql, {})
162
+ default_query = default[:query]
163
+ rescue ::QueryBuilder::Error => err
164
+ return parser_error(err.message)
165
+ end
55
166
 
56
- default_query = build_query(:all, default)
57
167
  klass = [default_query.main_class]
58
168
 
169
+ can_be_nil = true
59
170
  if sql = @params[:eval]
60
171
  sql = RubyLess.translate(self, sql)
61
172
  unless sql.klass <= String
62
173
  return parser_error("Invalid compilation result for #{sql.inspect} (#{sql.klass})")
63
174
  end
64
- elsif sql = @params[:text]
175
+ can_be_nil = sql.opts[:nil]
176
+ elsif sql = @params[:select]
65
177
  sql = RubyLess.translate_string(self, sql)
178
+ can_be_nil = sql.opts[:nil]
66
179
  else
67
180
  sql = "params[:qb]"
68
181
  end
69
182
 
70
- method = "query('#{node.klass}', #{node.to_s.inspect}, #{sql} || #{default.inspect})"
183
+ if can_be_nil
184
+ sql = "#{sql} || #{default_psql.inspect}"
185
+ end
71
186
 
72
- expand_with_finder(:method => method, :class => klass, :nil => true)
187
+ query = DynamicQuery.new(default_query, node, sql)
188
+ expand_with_finder(:method => query.to_s, :class => [query.main_class], :nil => true, :query => query)
73
189
  end
74
190
 
75
191
  # Pre-processing of the 'find("...")' method.
76
- def get_finder_type(string)
192
+ def get_type_for_find(string)
77
193
  finder = build_finder(:first, string, {})
78
194
  TypedString.new(finder.delete(:method), finder)
79
195
  end
80
196
 
197
+ # Pre-processing of the 'count("...")' method.
198
+ def get_type_for_count(string)
199
+ finder = build_finder(:count, string, {})
200
+ TypedString.new(finder.delete(:method), finder)
201
+ end
202
+
81
203
  # Resolve unknown methods by trying to build a pseudo-sql query with QueryBuilder.
82
204
  def querybuilder_eval(method = @method)
83
205
  return nil if node.klass.kind_of?(Array) # list context
@@ -89,7 +211,11 @@ module Zena
89
211
  finder = build_finder(count, method, @params)
90
212
  end
91
213
 
92
- expand_with_finder(finder)
214
+ if count == :count && @blocks.empty?
215
+ out "<%= #{finder[:method]} %>"
216
+ else
217
+ expand_with_finder(finder)
218
+ end
93
219
  rescue ::QueryBuilder::Error => err
94
220
  parser_continue(err.message)
95
221
  end
@@ -115,10 +241,14 @@ module Zena
115
241
  page_count = get_var_name('paginate', 'count', sub_context)
116
242
  curr_page = get_var_name('paginate', 'current', sub_context)
117
243
 
118
- # Give access to the pagination key.
244
+ # Give access to the page number through the pagination key.
119
245
  set_context_var('set_var', pagination_key, RubyLess::TypedString.new(curr_page, Number))
246
+ # Give access to page_count and count
247
+ # FIXME: DOC
248
+ set_context_var('set_var', 'page_count', RubyLess::TypedString.new(page_count, Number))
249
+ set_context_var('set_var', 'count', RubyLess::TypedString.new(node_count, Number))
120
250
 
121
- out "<% #{node_count} = Node.do_find(:count, #{query.to_s(:count)}); #{page_count} = (#{node_count} / #{query.page_size.to_f}).ceil; #{curr_page} = [1,params[:#{pagination_key}].to_i].max -%>"
251
+ out "<% #{node_count} = Node.do_find(:count, #{query.to_s(:count)}); #{page_count} = (#{node_count} / #{query.page_size.to_f}).ceil; #{curr_page} = [1,params[:#{pagination_key}].to_i].max %>"
122
252
  elsif finder[:method].kind_of?(RubyLess::TypedString)
123
253
  # Hash passed with :zafu => {} is inserted into context
124
254
  sub_context.merge!(finder[:method].opts[:zafu] || {})
@@ -130,7 +260,7 @@ module Zena
130
260
  end
131
261
 
132
262
  private
133
- # Build a Query object from pseudo sql.
263
+ # Build a Query object from SQLiss.
134
264
  def build_query(count, pseudo_sql, raw_filters = [])
135
265
 
136
266
  if !node.klass.respond_to?(:build_query)
@@ -173,7 +303,7 @@ module Zena
173
303
  else_clause = RubyLess.translate(self, else_clause)
174
304
 
175
305
  if else_clause.klass == Array
176
- else_klass = else_clause.opts[:array_content_class]
306
+ else_klass = else_clause.opts[:elem]
177
307
  if count == :all
178
308
  # Get first common ancestor
179
309
  common_klass = (klass.ancestors & else_klass.ancestors).detect {|x| x.kind_of?(Class)}
@@ -232,7 +362,7 @@ module Zena
232
362
  ((params[:paginate] || child['each'] || child['group'] || Node.plural_relation?(method)) ? :all : :first)
233
363
  end
234
364
 
235
- # Build pseudo sql from the parameters
365
+ # Build SQLiss from the parameters
236
366
  # comments where ... from ... in ... order ... limit
237
367
  def get_pseudo_sql(rel, params)
238
368
  parts = [rel.dup]
@@ -45,7 +45,7 @@ module Zena
45
45
  if $3 == 'user_id'
46
46
  value = "visitor.id"
47
47
  else
48
- value = "Node.zafu_attribute(visitor.contact, #{$3.inspect})"
48
+ value = "Node.zafu_attribute(visitor.node, #{$3.inspect})"
49
49
  end
50
50
  when 'node'
51
51
  if $3 == 'user_id'
@@ -14,10 +14,17 @@ module Zena
14
14
 
15
15
  module ModelMethods
16
16
  def self.included(base)
17
- base.send(:include, ::QueryBuilder)
18
- base.extend ClassMethods
19
- base.query_compiler = Zena::Use::QueryNode::Compiler
20
- base.safe_method :db_attr => StringDictionary
17
+ base.class_eval do
18
+ include ::QueryBuilder
19
+ extend ClassMethods
20
+ self.query_compiler = Zena::Use::QueryNode::Compiler
21
+ safe_method :db_attr => StringDictionary
22
+ safe_method [:first, String] => {:class => Node, :nil => true, :method => 'safe_first'}
23
+ end
24
+ end
25
+
26
+ def safe_first(query)
27
+ find(:first, query, :skip_rubyless => true)
21
28
  end
22
29
 
23
30
  # Find related nodes.
@@ -30,8 +37,8 @@ module Zena
30
37
  begin
31
38
  query = self.class.build_query(count, rel,
32
39
  :node_name => 'self',
33
- :main_class => Zena::Acts::Enrollable.make_class(self.vclass),
34
- :rubyless_helper => (opts[:rubyless_helper] || self),
40
+ :main_class => virtual_class,
41
+ :rubyless_helper => (opts[:rubyless_helper] || virtual_class), # should it be || self ?
35
42
  :default => opts[:default]
36
43
  )
37
44
 
@@ -43,7 +50,7 @@ module Zena
43
50
  return opts[:errors] ? err : nil
44
51
  end
45
52
 
46
- type = [:all, :first].include?(count) ? :find : count
53
+ type = [:all, :first].include?(count) ? :find : :count
47
54
 
48
55
  self.class.do_find(count, eval(query.to_s(type)))
49
56
  end
@@ -80,7 +87,8 @@ module Zena
80
87
  res = find_by_sql(query).first
81
88
  secure_result(res)
82
89
  when :count
83
- count_by_sql(query)
90
+ # query can be a number when we use the 'query' helper to count.
91
+ query.kind_of?(Fixnum) ? query : count_by_sql(query)
84
92
  else
85
93
  nil
86
94
  end
@@ -92,7 +100,7 @@ module Zena
92
100
  set_main_table 'nodes'
93
101
  set_main_class 'Node'
94
102
  set_default :scope, 'self'
95
- set_default :order, 'position ASC, node_name ASC'
103
+ set_default :order, 'position ASC, title ASC'
96
104
  set_default :context, 'self'
97
105
  after_process :insert_links_fields
98
106
  after_process :secure_query
@@ -101,6 +109,11 @@ module Zena
101
109
 
102
110
  CORE_CONTEXTS = %w{parent project section}
103
111
 
112
+ # Resolve 'main_class' from a class name.
113
+ def resolve_main_class(class_name)
114
+ VirtualClass[class_name]
115
+ end
116
+
104
117
  class << self
105
118
  attr_accessor :filter_fields
106
119
 
@@ -109,16 +122,15 @@ module Zena
109
122
  end
110
123
  end
111
124
 
112
- # How to treat filters like "where id = 45" or "where parent_id = #{params[:parent_id]}"
125
+ # Enables filters like "where id = 45" or "where parent_id = #{params[:parent_id]}"
113
126
  self.filter_fields = {
114
- 'id' => {:key => 'zip'},
115
- 'parent_id' => {:key => 'zip', :join => ['nodes', 'nodes', 'TABLE2.id = TABLE1.parent_id AND TABLE2.site_id = TABLE1.site_id']},
116
- 'project_id' => {:key => 'zip', :join => ['nodes', 'nodes', 'TABLE2.id = TABLE1.project_id AND TABLE2.site_id = TABLE1.site_id']},
117
- 'section_id' => {:key => 'zip', :join => ['nodes', 'nodes', 'TABLE2.id = TABLE1.section_id AND TABLE2.site_id = TABLE1.site_id']}
127
+ 'id' => {:key => 'zip'}, # alias source target filter
128
+ 'parent_id' => {:key => 'zip', :table => ['jnode', 'nodes', 'nodes', 'TABLE2.id = TABLE1.parent_id AND TABLE2.site_id = TABLE1.site_id']},
129
+ 'project_id' => {:key => 'zip', :table => ['jnode', 'nodes', 'nodes', 'TABLE2.id = TABLE1.project_id AND TABLE2.site_id = TABLE1.site_id']},
130
+ 'section_id' => {:key => 'zip', :table => ['jnode', 'nodes', 'nodes', 'TABLE2.id = TABLE1.section_id AND TABLE2.site_id = TABLE1.site_id']},
131
+ 'now' => Zena::Db::NOW,
118
132
  }
119
133
 
120
- add_filter_field 'now', Zena::Db::NOW
121
-
122
134
  # Scope current context with previous context.
123
135
  # For example:
124
136
  # current previous
@@ -153,20 +165,49 @@ module Zena
153
165
  end
154
166
  end
155
167
 
168
+ def get_scope_index_field(field_name)
169
+ return nil if @query.main_class.real_class.column_names.include?(field_name)
170
+ # scope index
171
+ klass = @query.main_class
172
+ if index_model = klass.kind_of?(VirtualClass) ? klass.idx_class : nil
173
+ index_model = Zena.resolve_const(index_model) rescue NilClass
174
+ if index_model < Zena::Use::ScopeIndex::IndexMethods && index_model.column_names.include?(field_name)
175
+ table_to_use = add_key_value_table('scope_index', index_model.table_name) do |tbl_name|
176
+ # This block is only executed once
177
+ add_filter "#{table('nodes')}.id = #{tbl_name}.node_id"
178
+ end
179
+ "#{table_to_use}.#{field_name}"
180
+ else
181
+ # invalid field_name: ignore
182
+ nil
183
+ end
184
+ else
185
+ # no index model: ignore
186
+ nil
187
+ end
188
+ end
189
+
156
190
  # Overwrite this and take car to check for valid fields.
157
191
  def process_field(field_name)
158
- if processing_filter? && map_def = self.class.filter_fields[field_name]
192
+ if fld = @query.attributes_alias[field_name]
193
+ # use custom query alias value defined in select clause: 'custom_a AS validation'
194
+ return processing_filter? ? "(#{fld})" : fld
195
+ elsif processing_filter? && scope_field = get_scope_index_field(field_name)
196
+ scope_field
197
+ elsif processing_filter? && map_def = self.class.filter_fields[field_name]
198
+ # Special filter fields such as 'role', 'tag' or 'class'
159
199
  if map_def.kind_of?(String)
160
200
  return map_def
161
201
  elsif table_def = map_def[:table]
162
- table_to_use = needs_join_table(*table_def)
163
- elsif join_def = map_def[:join]
164
- # current table
165
- first_table = table(join_def[0])
166
- # filter table
167
- add_table('jnode', join_def[1]) # join node
168
- table_to_use = table('jnode')
169
- add_filter join_def[2].gsub('TABLE1', first_table).gsub('TABLE2', table_to_use)
202
+ use_name, source, target, filter = table_def
203
+ table_to_use = add_key_value_table(use_name, target, map_def[:key]) do |tbl_name|
204
+ # This block is only executed once
205
+ add_filter filter.gsub(
206
+ 'TABLE1', table(source)
207
+ ).gsub(
208
+ 'TABLE2', tbl_name
209
+ )
210
+ end
170
211
  else
171
212
  table_to_use = table
172
213
  end
@@ -196,24 +237,34 @@ module Zena
196
237
  return res
197
238
  end
198
239
 
199
- column = @query.main_class.schema.columns[field_name]
240
+ # property or real column
241
+ column = @query.main_class.columns[field_name]
200
242
  if column && column.indexed?
201
243
  if column.index == true
202
244
  group_name = column.type
245
+ elsif column.index =~ Property::Index::FIELD_INDEX_REGEXP
246
+ # field in nodes
247
+ return "#{table}.#{$1}"
203
248
  else
204
249
  group_name = column.index
205
250
  end
206
251
 
207
- index_table = @query.main_class.index_table_name(group_name)
208
- add_table(index_table)
252
+ index_table = @query.main_class.real_class.index_table_name(group_name)
253
+
254
+ # We use the add_key_value_table rule to avoid inserting the
255
+ # same index access twice.
209
256
 
210
- add_filter "#{table(index_table)}.node_id = #{table}.id"
211
- add_filter "#{table(index_table)}.key = #{quote(field_name)}"
212
- if group_name.to_s =~ /^ml_/
213
- add_filter "#{table(index_table)}.lang = #{quote(visitor.lang)}"
257
+ tbl = add_key_value_table(group_name, index_table, field_name) do |tbl_name|
258
+ # This block is only executed once
259
+ add_filter "#{tbl_name}.node_id = #{table}.id"
260
+ add_filter "#{tbl_name}.key = #{quote(field_name)}"
261
+ if group_name.to_s =~ /^ml_/
262
+ add_filter "#{tbl_name}.lang = #{quote(visitor.lang)}"
263
+ end
264
+ # no need for distinct, the new table makes a 1-1 relation
214
265
  end
215
- distinct!
216
- "#{table(index_table)}.value"
266
+
267
+ "#{tbl}.value"
217
268
  else
218
269
  super # raises an error
219
270
  end
@@ -258,15 +309,56 @@ module Zena
258
309
  end
259
310
  end
260
311
 
312
+ def resolve_scope_idx_fields(arg1, arg2)
313
+ if arg1.first == :function
314
+ # contact.log_at.year
315
+ # arg1 = [:function, [:field, "tag"], [:method, "created_at"]]
316
+ # arg2 = [:method, "year"]
317
+ class_name = arg1[1][1]
318
+ field_name = arg1[2][1]
319
+ function = arg2
320
+ elsif arg1[0] == :field && arg2[0] == :method
321
+ # contact.log_at or log_at.year
322
+ # arg1 = [:field, "contact"]
323
+ # arg2 = [:method, "name"]
324
+ class_or_field = arg1[1]
325
+ field_or_function = arg2[1]
326
+ if @query.main_class.columns[class_or_field]
327
+ # log_at.year
328
+ return [arg1, arg2]
329
+ else
330
+ class_name = class_or_field
331
+ field_name = field_or_function
332
+ function = nil
333
+ end
334
+ else
335
+ return [arg1, arg2]
336
+ end
337
+
338
+ scope_idx_field = "#{class_name}_#{field_name}"
339
+ if get_scope_index_field(scope_idx_field)
340
+ return [[:field, scope_idx_field], function]
341
+ else
342
+ # not a scope index field
343
+ return [arg1, arg2]
344
+ end
345
+ end
346
+
261
347
  def process_function(arg, method)
262
- arg, method = process(arg), process(method)
263
- Zena::Db.sql_function(method, arg)
348
+ # Resolve scope index fields
349
+ arg, method = resolve_scope_idx_fields(arg, method)
350
+ if method
351
+ arg, method = process(arg), process(method)
352
+ Zena::Db.sql_function(method, arg)
353
+ else
354
+ process(arg)
355
+ end
264
356
  end
265
357
 
266
358
  # ******** And maybe overwrite these **********
267
359
  def parse_custom_query_argument(key, value)
268
360
  return nil unless value
269
- super.gsub(/(RELATION_ID|NODE_ATTR)\(([^)]+)\)|(REF_DATE|NODE_ID)/) do
361
+ super.gsub(/(RELATION_ID|NODE_ATTR)\(([^)]+)\)|(REF_DATE|NODE_ID|VISITOR_LANG)/) do
270
362
  type, value = $1, $2
271
363
  type ||= $3
272
364
  case type
@@ -290,6 +382,8 @@ module Zena
290
382
  context[:ref_date] ? insert_bind(context[:ref_date]) : 'now()'
291
383
  when 'NODE_ID'
292
384
  insert_bind("#{node_name}.id")
385
+ when 'VISITOR_LANG'
386
+ insert_bind("visitor.lang")
293
387
  end
294
388
  end
295
389
  end
@@ -303,7 +397,7 @@ module Zena
303
397
  # This is used to avoid finding random indexed objects or links in clauses with and without link filters
304
398
  # like this: "image or icon" ('image' is a filter in 'parent' scope, 'icon' is a
305
399
  # relation found through links).
306
- def resolve_missing_table(query, table_alias, table_name)
400
+ def resolve_missing_table(query, table_name, table_alias)
307
401
  if table_name =~ /^idx_nodes/
308
402
  # index tables
309
403
  query.where.insert 0, "#{table_alias}.node_id = 0"
@@ -345,9 +439,11 @@ module Zena
345
439
  def context_relation(relation)
346
440
  # Not sure we need all these anymore...
347
441
  case relation
348
- #when 'self'
349
- # # Dummy context
350
- # fields = ['id', 'id']
442
+ when 'self'
443
+ # Special pseudo-context
444
+ add_table(main_table)
445
+ add_filter "#{field_or_attr('id')} = #{field_or_attr('id', table(main_table, -1))}"
446
+ return true
351
447
  #when 'parent', 'project', 'section'
352
448
  # # Core contexts
353
449
  # fields = ['id', "#{relation}_id"]
@@ -358,7 +454,7 @@ module Zena
358
454
  when 'root'
359
455
  # Special pseudo-context
360
456
  add_table(main_table)
361
- make_and_set_main_class(Project)
457
+ set_main_class(VirtualClass['Project'])
362
458
  add_filter "#{table}.id = #{current_site.root_id}"
363
459
  return true
364
460
  #when 'author', 'traductions', 'versions'
@@ -367,8 +463,8 @@ module Zena
367
463
  when 'visitor'
368
464
  # Special pseudo-context
369
465
  add_table(main_table)
370
- make_and_set_main_class(BaseContact)
371
- add_filter "#{table}.id = #{insert_bind("visitor.contact_id")}"
466
+ set_main_class(VirtualClass.find_by_kpath(visitor.prototype.kpath))
467
+ add_filter "#{table}.id = #{insert_bind("visitor.node_id")}"
372
468
  return true
373
469
  end
374
470
 
@@ -385,7 +481,7 @@ module Zena
385
481
  if class_name
386
482
  # We have named the relation, set main_class
387
483
  if klass = Node.get_class(class_name)
388
- make_and_set_main_class(klass)
484
+ set_main_class(klass)
389
485
  else
390
486
  raise QueryBuilder::QueryException.new("Unknown class #{klass} in scope '#{class_name}:#{scope}'.")
391
487
  end
@@ -418,15 +514,16 @@ module Zena
418
514
  if klass = @filter_relation_class || Node.get_class(relation)
419
515
  # Relation was found in 'join_relation'
420
516
  @filter_relation_class = nil
421
- res_class = make_and_set_main_class(klass)
517
+ set_main_class(klass)
518
+ res_class = klass
422
519
 
423
520
  add_table(main_table)
424
521
  add_filter "#{table}.kpath LIKE #{quote("#{res_class.kpath}%")}" unless res_class.kpath == 'N'
425
522
  true
426
523
  elsif role = Node.get_role(relation)
427
- klass = Zena::Acts::Enrollable.make_class(@query.main_class)
428
- klass.has_role role
429
- set_main_class(klass)
524
+ if klass = VirtualClass.find_by_kpath(role.kpath)
525
+ set_main_class(klass)
526
+ end
430
527
 
431
528
  add_table(main_table)
432
529
  add_table('nodes_roles')
@@ -459,7 +556,7 @@ module Zena
459
556
  add_table(use_name, main_table)
460
557
  else
461
558
  add_table(main_table)
462
- make_and_set_main_class(rel.other_klass)
559
+ set_main_class(rel.other_vclass)
463
560
  end
464
561
 
465
562
  add_table('links')
@@ -508,12 +605,6 @@ module Zena
508
605
  def node_name
509
606
  @context[:node_name]
510
607
  end
511
-
512
- def make_and_set_main_class(klass)
513
- res_class = Zena::Acts::Enrollable.make_class(klass)
514
- set_main_class(res_class)
515
- res_class
516
- end
517
608
  end # Compiler
518
609
  end # QueryNode
519
610
  end # Use