zena 1.0.0.rc2 → 1.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (274) hide show
  1. data/History.txt +23 -0
  2. data/README.rdoc +1 -1
  3. data/app/controllers/columns_controller.rb +3 -31
  4. data/app/controllers/comments_controller.rb +8 -3
  5. data/app/controllers/data_entries_controller.rb +1 -1
  6. data/app/controllers/documents_controller.rb +2 -2
  7. data/app/controllers/nodes_controller.rb +29 -12
  8. data/app/controllers/relations_controller.rb +2 -2
  9. data/app/controllers/sites_controller.rb +1 -1
  10. data/app/controllers/user_sessions_controller.rb +6 -3
  11. data/app/controllers/users_controller.rb +18 -16
  12. data/app/controllers/versions_controller.rb +20 -18
  13. data/app/controllers/virtual_classes_controller.rb +103 -17
  14. data/app/helpers/users_helper.rb +1 -1
  15. data/app/models/column.rb +19 -50
  16. data/app/models/comment.rb +2 -1
  17. data/app/models/node.rb +45 -22
  18. data/app/models/relation.rb +13 -0
  19. data/app/models/relation_proxy.rb +3 -2
  20. data/app/models/role.rb +213 -4
  21. data/app/models/site.rb +18 -11
  22. data/app/models/template.rb +37 -35
  23. data/app/models/version.rb +1 -1
  24. data/app/models/virtual_class.rb +154 -86
  25. data/app/views/columns/_li.html.erb +1 -1
  26. data/app/views/columns/index.html.erb +1 -9
  27. data/app/views/comments/index.rhtml +10 -8
  28. data/app/views/documents/_crop.rhtml +5 -6
  29. data/app/views/documents/crop_form.rjs +3 -2
  30. data/app/views/groups/index.rhtml +1 -1
  31. data/app/views/iformats/index.rhtml +1 -1
  32. data/app/views/nodes/_import_results.rhtml +1 -1
  33. data/app/views/nodes/_parent.rhtml +1 -2
  34. data/app/views/nodes/update.rjs +3 -4
  35. data/app/views/relations/index.erb +1 -1
  36. data/app/views/sites/index.erb +1 -1
  37. data/app/views/templates/drive_tabs/_drive.rhtml +0 -2
  38. data/app/views/templates/edit_tabs/_image.rhtml +1 -1
  39. data/app/views/templates/edit_tabs/_title.rhtml +0 -6
  40. data/app/views/users/index.rhtml +1 -1
  41. data/app/views/users/preferences.html.erb +2 -2
  42. data/app/views/versions/backup.rjs +1 -1
  43. data/app/views/versions/custom_tab.rhtml +9 -4
  44. data/app/views/versions/destroy.rjs +2 -2
  45. data/app/views/versions/update.rjs +2 -9
  46. data/app/views/virtual_classes/_form.erb +3 -2
  47. data/app/views/virtual_classes/import_prepare.html.erb +13 -0
  48. data/app/views/virtual_classes/index.erb +28 -8
  49. data/app/views/zafu/default/Node-+adminLayout.zafu +1 -13
  50. data/app/views/zafu/default/Node-+login.zafu +1 -0
  51. data/app/views/zafu/default/Node-+notFound.zafu +1 -1
  52. data/app/views/zafu/default/Node-+popupLayout.zafu +1 -2
  53. data/app/views/zafu/default/Node-+search.zafu +1 -1
  54. data/app/views/zafu/default/Node-admin.zafu +205 -0
  55. data/app/views/zafu/default/Node.zafu +11 -11
  56. data/bricks/captcha/lib/bricks/captcha.rb +3 -2
  57. data/bricks/mongrel/zena/init.rb +2 -1
  58. data/bricks/pdf/README +5 -5
  59. data/bricks/pdf/lib/bricks/pdf/engine/prince.rb +2 -2
  60. data/bricks/pdf/lib/bricks/pdf/engine/xhtml2pdf.rb +2 -2
  61. data/bricks/pdf/lib/bricks/pdf/install.rb +5 -5
  62. data/bricks/pdf/lib/bricks/pdf.rb +11 -11
  63. data/bricks/pdf/test/engines/test_prince.rb +4 -4
  64. data/bricks/pdf/test/engines/test_xhtml2pdf.rb +4 -4
  65. data/bricks/pdf/test/shoulda_macros/shoulda_pdf.rb +2 -2
  66. data/bricks/pdf/zena/init.rb +2 -2
  67. data/bricks/pdf/zena/tasks.rb +2 -2
  68. data/bricks/sphinx/lib/bricks/sphinx.rb +6 -2
  69. data/bricks/sphinx/zena/{sphinx.yml → sphinx.yml.erb} +2 -2
  70. data/bricks/sphinx/zena/tasks.rb +28 -2
  71. data/bricks/tags/lib/bricks/tags.rb +16 -1
  72. data/bricks/tags/zena/test/unit/tags_test.rb +15 -0
  73. data/bricks/tags/zena/test/zafu/tags.yml +5 -1
  74. data/bricks/worker/lib/bricks/worker.rb +39 -0
  75. data/bricks/worker/zena/deploy.rb +0 -2
  76. data/bricks/worker/zena/init.rb +1 -0
  77. data/bricks/worker/zena/test/sites/zena/delayed_jobs.yml +16 -0
  78. data/bricks/worker/zena/test/zafu/worker.yml +8 -0
  79. data/bricks/zena/zena/migrate/01_base.rb +36 -60
  80. data/bricks/zena/zena/migrate/02_zerox1_schema.rb +388 -0
  81. data/bricks/zena/zena/migrate/03_zerox1_data.rb +380 -0
  82. data/bricks/zena/zena/migrate/20110315161158_add_reverse_scope_to_roles.rb +9 -0
  83. data/config/database_example.yml +1 -1
  84. data/config/environment.rb +1 -1
  85. data/config/gems.yml +17 -14
  86. data/db/init/base/skins/default/Node-+index.zafu +8 -1
  87. data/db/init/base/skins/default/Node-+login.zafu +1 -0
  88. data/db/init/base/skins/default/Node-+popupLayout.zafu +1 -2
  89. data/db/init/base/skins/default/Node-+search.zafu +2 -2
  90. data/db/init/base/skins/default/Node.zafu +9 -9
  91. data/db/init/base/skins/default/{favicon.png → img/favicon.png} +0 -0
  92. data/db/init/base/skins/default/{style.css → img/style.css} +0 -0
  93. data/db/init/base/skins/default/img/translations.yml +11 -0
  94. data/db/init/base/skins/default/notes.zafu +7 -9
  95. data/doc/zafu_changes.yml +12 -0
  96. data/lib/bricks/loader.rb +38 -15
  97. data/lib/tasks/zena.rake +74 -24
  98. data/lib/zena/acts/enrollable.rb +4 -1
  99. data/lib/zena/acts/secure.rb +2 -48
  100. data/lib/zena/acts/serializable.rb +13 -1
  101. data/lib/zena/app.rb +9 -0
  102. data/lib/zena/code_syntax.rb +154 -151
  103. data/lib/zena/console.rb +141 -0
  104. data/lib/zena/controller/test_case.rb +1 -1
  105. data/lib/zena/db_helper/abstract_db.rb +17 -5
  106. data/lib/zena/db_helper/mysql.rb +14 -12
  107. data/lib/zena/db_helper/postgresql.rb +1 -2
  108. data/lib/zena/db_helper/sqlite3.rb +6 -6
  109. data/lib/zena/deploy/awstats.conf.rhtml +1 -1
  110. data/lib/zena/deploy/httpd.rhtml +6 -1
  111. data/lib/zena/deploy/vhost.rhtml +9 -1
  112. data/lib/zena/deploy.rb +12 -7
  113. data/lib/zena/foxy_parser.rb +3 -1
  114. data/lib/zena/info.rb +1 -1
  115. data/lib/zena/parser/zafu_tags.rb +1 -0
  116. data/lib/zena/parser/zazen_rules.rb +1 -1
  117. data/lib/zena/remote/node.rb +15 -3
  118. data/lib/zena/remote/serializable_array.rb +19 -0
  119. data/lib/zena/remote.rb +1 -0
  120. data/lib/zena/routes.rb +7 -2
  121. data/lib/zena/site_worker.rb +11 -1
  122. data/lib/zena/unit/test_case.rb +68 -0
  123. data/lib/zena/use/action.rb +6 -2
  124. data/lib/zena/use/ajax.rb +127 -53
  125. data/lib/zena/use/ancestry.rb +11 -8
  126. data/lib/zena/use/calendar.rb +265 -129
  127. data/lib/zena/use/conditional.rb +1 -1
  128. data/lib/zena/use/context.rb +5 -5
  129. data/lib/zena/use/dates.rb +172 -60
  130. data/lib/zena/use/display.rb +70 -39
  131. data/lib/zena/use/error_rendering.rb +1 -3
  132. data/lib/zena/use/field_index.rb +4 -1
  133. data/lib/zena/use/forms.rb +94 -72
  134. data/lib/zena/use/fulltext.rb +16 -24
  135. data/lib/zena/use/html_tags.rb +20 -12
  136. data/lib/zena/use/i18n.rb +37 -37
  137. data/lib/zena/use/image_builder.rb +8 -1
  138. data/lib/zena/use/ml_index.rb +16 -16
  139. data/lib/zena/use/prop_eval.rb +10 -5
  140. data/lib/zena/use/query_builder.rb +55 -23
  141. data/lib/zena/use/query_node.rb +51 -25
  142. data/lib/zena/use/refactor.rb +2 -28
  143. data/lib/zena/use/relations.rb +1 -1
  144. data/lib/zena/use/rendering.rb +29 -0
  145. data/lib/zena/use/scope_index.rb +75 -14
  146. data/lib/zena/use/search.rb +5 -10
  147. data/lib/zena/use/test_helper.rb +2 -2
  148. data/lib/zena/use/urls.rb +125 -104
  149. data/lib/zena/use/workflow.rb +2 -1
  150. data/lib/zena/use/zafu_attributes.rb +2 -2
  151. data/lib/zena/use/zafu_safe_definitions.rb +20 -0
  152. data/lib/zena/use/zafu_templates.rb +20 -6
  153. data/lib/zena/use/zazen.rb +31 -20
  154. data/lib/zena/view/test_case.rb +5 -0
  155. data/lib/zena/zafu_compiler.rb +24 -2
  156. data/lib/zena.rb +12 -6
  157. data/locale/de/LC_MESSAGES/zena.mo +0 -0
  158. data/locale/de/zena.po +1345 -1164
  159. data/locale/en/LC_MESSAGES/zena.mo +0 -0
  160. data/locale/en/zena.po +1275 -1129
  161. data/locale/fr/LC_MESSAGES/zena.mo +0 -0
  162. data/locale/fr/zena.mo +0 -0
  163. data/locale/fr/zena.po +1617 -1441
  164. data/locale/log.txt +9 -0
  165. data/locale/zena.pot +957 -748
  166. data/public/javascripts/prototype.js +1 -1
  167. data/public/javascripts/zena.js +99 -44
  168. data/public/stylesheets/admin.css +6 -4
  169. data/public/stylesheets/backend.css +71 -0
  170. data/public/stylesheets/calendar.css +24 -25
  171. data/public/stylesheets/code.css +11 -6
  172. data/public/stylesheets/comment.css +2 -1
  173. data/public/stylesheets/popup.css +7 -8
  174. data/test/custom_queries/complex.host.yml +15 -1
  175. data/test/fixtures/files/Node-test.zafu +29 -28
  176. data/test/fixtures/files/translations_de.yml +12 -1
  177. data/test/fixtures/files/translations_fr.yml +12 -1
  178. data/test/functional/comments_controller_test.rb +9 -0
  179. data/test/functional/iformats_controller_test.rb +1 -1
  180. data/test/functional/nodes_controller_test.rb +124 -35
  181. data/test/functional/users_controller_test.rb +132 -3
  182. data/test/functional/virtual_classes_controller_test.rb +75 -4
  183. data/test/integration/navigation_test.rb +51 -9
  184. data/test/integration/query_node/basic.yml +19 -7
  185. data/test/integration/query_node/complex.yml +1 -1
  186. data/test/integration/query_node/dates.yml +27 -1
  187. data/test/integration/query_node/filters.yml +1 -1
  188. data/test/integration/query_node/relations.yml +13 -4
  189. data/test/integration/query_node_test.rb +4 -0
  190. data/test/integration/xml_api_test.rb +6 -1
  191. data/test/integration/zafu_compiler/action.yml +3 -3
  192. data/test/integration/zafu_compiler/ajax.yml +103 -22
  193. data/test/integration/zafu_compiler/basic.yml +0 -52
  194. data/test/integration/zafu_compiler/calendar.yml +44 -20
  195. data/test/integration/zafu_compiler/comments.yml +53 -0
  196. data/test/integration/zafu_compiler/complex.yml +11 -11
  197. data/test/integration/zafu_compiler/complex_ok.yml +16 -3
  198. data/test/integration/zafu_compiler/conditional.yml +15 -5
  199. data/test/integration/zafu_compiler/context.yml +9 -0
  200. data/test/integration/zafu_compiler/dates.yml +43 -15
  201. data/test/integration/zafu_compiler/display.yml +60 -6
  202. data/test/integration/zafu_compiler/errors.yml +6 -2
  203. data/test/integration/zafu_compiler/forms.yml +45 -6
  204. data/test/integration/zafu_compiler/i18n.yml +8 -1
  205. data/test/integration/zafu_compiler/meta.yml +38 -0
  206. data/test/integration/zafu_compiler/query.yml +43 -4
  207. data/test/integration/zafu_compiler/relations.yml +26 -33
  208. data/test/integration/zafu_compiler/rubyless.yml +10 -0
  209. data/test/integration/zafu_compiler/safe_definitions.yml +21 -1
  210. data/test/integration/zafu_compiler/urls.yml +75 -5
  211. data/test/integration/zafu_compiler/version.yml +2 -2
  212. data/test/integration/zafu_compiler/zafu_attributes.yml +5 -1
  213. data/test/integration/zafu_compiler/zazen.yml +14 -6
  214. data/test/integration/zafu_compiler_test.rb +5 -1
  215. data/test/sites/complex/columns.yml +5 -0
  216. data/test/sites/complex/roles.yml +4 -0
  217. data/test/sites/zena/nodes.yml +13 -2
  218. data/test/sites/zena/roles.yml +13 -5
  219. data/test/sites/zena/versions.yml +27 -9
  220. data/test/unit/column_test.rb +51 -5
  221. data/test/unit/iformat_test.rb +2 -2
  222. data/test/unit/node_test.rb +29 -17
  223. data/test/unit/note_test.rb +1 -1
  224. data/test/unit/relation_proxy_test.rb +4 -5
  225. data/test/unit/relation_test.rb +16 -0
  226. data/test/unit/remote_test.rb +2 -2
  227. data/test/unit/role_test.rb +292 -4
  228. data/test/unit/site_test.rb +12 -0
  229. data/test/unit/template_test.rb +1 -1
  230. data/test/unit/text_document_test.rb +1 -1
  231. data/test/unit/virtual_class_test.rb +200 -83
  232. data/test/unit/zena/acts/enrollable_test.rb +26 -31
  233. data/test/unit/zena/use/calendar_test.rb +90 -37
  234. data/test/unit/zena/use/field_index_test.rb +28 -0
  235. data/test/unit/zena/use/html_tags_test.rb +7 -3
  236. data/test/unit/zena/use/ml_index_test.rb +2 -16
  237. data/test/unit/zena/use/nested_attributes_alias_view_test.rb +2 -2
  238. data/test/unit/zena/use/prop_eval_test.rb +50 -8
  239. data/test/unit/zena/use/query_node_test.rb +11 -0
  240. data/test/unit/zena/use/rendering_test.rb +72 -0
  241. data/test/unit/zena/use/scope_index_test.rb +37 -2
  242. data/test/unit/zena/use/urls_test.rb +10 -0
  243. data/test/unit/zena/use/zazen_test.rb +3 -3
  244. data/vendor/plugins/gettext_i18n_rails/Gemfile +11 -0
  245. data/vendor/plugins/gettext_i18n_rails/Gemfile.lock +92 -0
  246. data/vendor/plugins/gettext_i18n_rails/Rakefile +12 -17
  247. data/vendor/plugins/gettext_i18n_rails/Readme.md +215 -0
  248. data/vendor/plugins/gettext_i18n_rails/VERSION +1 -1
  249. data/vendor/plugins/gettext_i18n_rails/gettext_i18n_rails.gemspec +38 -34
  250. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/active_record.rb +1 -1
  251. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/backend.rb +30 -14
  252. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/haml_parser.rb +1 -1
  253. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/html_safe_translations.rb +29 -0
  254. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/i18n_hacks.rb +29 -1
  255. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/model_attributes_finder.rb +7 -1
  256. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/railtie.rb +10 -0
  257. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/ruby_gettext_extractor.rb +6 -2
  258. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/string_interpolate_fix.rb +20 -0
  259. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/tasks.rb +120 -0
  260. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails.rb +10 -3
  261. data/vendor/plugins/gettext_i18n_rails/lib/tasks/gettext_rails_i18n.rake +1 -74
  262. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails/active_record_spec.rb +51 -20
  263. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails/backend_spec.rb +12 -7
  264. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails/string_interpolate_fix_spec.rb +32 -0
  265. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails_spec.rb +38 -1
  266. data/vendor/plugins/gettext_i18n_rails/spec/rails2/Gemfile +11 -0
  267. data/vendor/plugins/gettext_i18n_rails/spec/spec_helper.rb +1 -8
  268. data/zena.gemspec +2241 -2217
  269. metadata +123 -83
  270. data/.gitignore +0 -36
  271. data/app/views/nodes/_dates.rhtml +0 -13
  272. data/db/init/base/skins/default/Node-+adminLayout.zafu +0 -46
  273. data/db/init/base/skins/default/Node-tree.zafu +0 -19
  274. data/vendor/plugins/gettext_i18n_rails/README.markdown +0 -143
@@ -5,14 +5,20 @@ module Zena
5
5
  module ViewMethods
6
6
  include RubyLess
7
7
  safe_method [:query_parse, String] => {:class => String, :accept_nil => true}
8
+ safe_method :query_errors => {:class => String, :nil => true}
8
9
 
9
10
  def find_node_by_zip(zip)
10
11
  return nil unless zip
11
12
  secure(Node) { Node.find_by_zip(zip) }
12
13
  end
13
14
 
15
+ def query_errors
16
+ @query_errors
17
+ end
18
+
14
19
  def query(class_name, node_name, pseudo_sql, opts = {})
15
20
  type = opts[:type] || :find
21
+ @query_errors = nil
16
22
  if klass = VirtualClass[class_name]
17
23
  begin
18
24
  query = klass.build_query(:all, pseudo_sql,
@@ -28,7 +34,7 @@ module Zena
28
34
  return klass.do_find(:all, eval(query.to_s))
29
35
  end
30
36
  rescue ::QueryBuilder::Error => err
31
- # FIXME: how to return error messages to the user ?
37
+ @query_errors = "<span class='query'>#{pseudo_sql}</span> <span class='error'>#{err}</span>"
32
38
  end
33
39
  end
34
40
  # error
@@ -71,6 +77,7 @@ module Zena
71
77
  end
72
78
 
73
79
  if arg =~ DATE_FIELD_REGEXP
80
+ # FIXME: Use to_utc and date function in Zena::Db
74
81
  arg = query_parse_dates(arg)
75
82
  first = arg.first
76
83
  end
@@ -202,7 +209,7 @@ module Zena
202
209
 
203
210
  # Resolve unknown methods by trying to build a pseudo-sql query with QueryBuilder.
204
211
  def querybuilder_eval(method = @method)
205
- return nil if node.klass.kind_of?(Array) # list context
212
+ node = single_node
206
213
 
207
214
  if method =~ /^\d+$/
208
215
  finder = {:method => "find_node_by_zip(#{method})", :class => Node, :nil => true}
@@ -234,34 +241,54 @@ module Zena
234
241
  def node_context_vars(finder)
235
242
  sub_context = super
236
243
  query = finder[:query]
237
- if query && (pagination_key = query.pagination_key)
238
- set_context_var('paginate', 'key', pagination_key, sub_context)
239
-
244
+ if query && ((pagination_key = query.pagination_key) || child['count'])
240
245
  node_count = get_var_name('paginate', 'nodes', sub_context)
241
- page_count = get_var_name('paginate', 'count', sub_context)
242
- curr_page = get_var_name('paginate', 'current', sub_context)
243
-
244
- # Give access to the page number through the pagination key.
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
246
  set_context_var('set_var', 'count', RubyLess::TypedString.new(node_count, Number))
250
247
 
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 %>"
248
+ res = "#{node_count} = Node.do_find(:count, #{query.to_s(:count)})"
249
+ if pagination_key
250
+ # Full pagination needed
251
+ set_context_var('paginate', 'key', pagination_key, sub_context)
252
+
253
+ page_count = get_var_name('paginate', 'count', sub_context)
254
+ curr_page = get_var_name('paginate', 'current', sub_context)
255
+
256
+ # Give access to the page number through the pagination key.
257
+ set_context_var('set_var', pagination_key, RubyLess::TypedString.new(curr_page, Number))
258
+ # Give access to page_count and count
259
+ # FIXME: DOC
260
+ set_context_var('set_var', 'page_count', RubyLess::TypedString.new(page_count, Number))
261
+
262
+ res << "; #{page_count} = (#{node_count} / #{query.page_size.to_f}).ceil; #{curr_page} = [1,params[:#{pagination_key}].to_i].max"
263
+ end
264
+
265
+ out "<% #{res} %>"
266
+
252
267
  elsif finder[:method].kind_of?(RubyLess::TypedString)
253
268
  # Hash passed with :zafu => {} is inserted into context
254
269
  sub_context.merge!(finder[:method].opts[:zafu] || {})
255
270
  end
256
271
 
257
- sub_context[:has_link_id] = query && query.select.to_s =~ / AS link_id/
272
+ sub_context[:has_link_id] = query && query.select_keys.include?('link_id')
258
273
 
259
274
  sub_context
260
275
  end
261
276
 
262
277
  private
278
+ def single_node
279
+ node = self.node
280
+ while node.list_context?
281
+ node = node.up
282
+ if !node
283
+ raise ::QueryBuilder::Error.new("Could not access node context query builder from list #{self.node.class_name}")
284
+ end
285
+ end
286
+ node
287
+ end
288
+
263
289
  # Build a Query object from SQLiss.
264
290
  def build_query(count, pseudo_sql, raw_filters = [])
291
+ node = single_node
265
292
 
266
293
  if !node.klass.respond_to?(:build_query)
267
294
  raise ::QueryBuilder::Error.new("No query builder for class #{node.klass}")
@@ -299,8 +326,12 @@ module Zena
299
326
 
300
327
  finder = get_finder(query, count)
301
328
 
302
- if count != :count && else_clause = @params[:else]
303
- else_clause = RubyLess.translate(self, else_clause)
329
+ if count != :count && params[:else]
330
+ begin
331
+ else_clause = RubyLess.translate(self, params[:else])
332
+ rescue RubyLess::Error
333
+ else_clause = RubyLess.translate(self, "find(#{params[:else].inspect})")
334
+ end
304
335
 
305
336
  if else_clause.klass == Array
306
337
  else_klass = else_clause.opts[:elem]
@@ -367,6 +398,7 @@ module Zena
367
398
  def get_pseudo_sql(rel, params)
368
399
  parts = [rel.dup]
369
400
  filters = []
401
+ group_order_limit = ''
370
402
 
371
403
  if params[:from]
372
404
  parts << params[:from]
@@ -389,21 +421,21 @@ module Zena
389
421
  # [group by GROUP_CLAUSE] [order by ORDER_CLAUSE] [limit num(,num)] [offset num] [paginate key]
390
422
 
391
423
  if group = params[:group]
392
- parts[-1] << " group by #{group}" unless parts[0] =~ /group by/
424
+ group_order_limit << " group by #{group}" unless parts[0] =~ /group by/
393
425
  end
394
426
 
395
427
  if order = params[:order]
396
- parts[-1] << " order by #{order}" unless parts[0] =~ /order by/
428
+ group_order_limit << " order by #{order}" unless parts[0] =~ /order by/
397
429
  end
398
430
 
399
431
  if paginate = params[:paginate]
400
432
  page_size = params[:limit].to_i
401
433
  page_size = 20 if page_size < 1
402
- parts[-1] << " limit #{page_size} paginate #{paginate.gsub(/[^a-z_A-Z]/,'')}"
434
+ group_order_limit << " limit #{page_size} paginate #{paginate.gsub(/[^a-z_A-Z]/,'')}"
403
435
  else
404
436
  [:limit, :offset].each do |k|
405
437
  next unless params[k]
406
- parts[-1] << " #{k} #{params[k]}" unless parts[0] =~ / #{k} /
438
+ group_order_limit << " #{k} #{params[k]}" unless parts[0] =~ / #{k} /
407
439
  end
408
440
  end
409
441
 
@@ -421,9 +453,9 @@ module Zena
421
453
  end
422
454
 
423
455
  if finders.size > 1
424
- finders = "(#{finders.join(') or (')})"
456
+ finders = "(#{finders.join(') or (')})#{group_order_limit}"
425
457
  else
426
- finders = finders.first
458
+ finders = finders.first + group_order_limit
427
459
  end
428
460
 
429
461
  return [finders, parse_raw_filters(params)]
@@ -29,19 +29,18 @@ module Zena
29
29
 
30
30
  # Find related nodes.
31
31
  # See Node#build_find for details on the options available.
32
- # TODO: do we need rubyless translate here ?
33
- def find(count, rel, opts = {})
34
- if !opts[:skip_rubyless] && rel.size == 1 && type = RubyLess::SafeClass.safe_method_type_for(self.class, [rel.first])
32
+ # TODO: do we need rubyless translate here ? It should be removed.
33
+ def find(count, pseudo_sql, opts = {})
34
+ if !opts[:skip_rubyless] && type = RubyLess::SafeClass.safe_method_type_for(self.class, [pseudo_sql])
35
35
  self.send(type[:method])
36
36
  else
37
37
  begin
38
- query = self.class.build_query(count, rel,
38
+ query = self.class.build_query(count, pseudo_sql,
39
39
  :node_name => 'self',
40
40
  :main_class => virtual_class,
41
41
  :rubyless_helper => (opts[:rubyless_helper] || virtual_class), # should it be || self ?
42
42
  :default => opts[:default]
43
43
  )
44
-
45
44
  if limit = opts[:limit]
46
45
  query.limit = " LIMIT #{limit.to_i}"
47
46
  query.offset = " OFFSET #{opts[:offset].to_i}"
@@ -52,7 +51,7 @@ module Zena
52
51
 
53
52
  type = [:all, :first].include?(count) ? :find : :count
54
53
 
55
- self.class.do_find(count, eval(query.to_s(type)))
54
+ Node.do_find(count, eval(query.to_s(type)))
56
55
  end
57
56
  end
58
57
 
@@ -105,7 +104,7 @@ module Zena
105
104
  after_process :insert_links_fields
106
105
  after_process :secure_query
107
106
 
108
- load_custom_queries Bricks.paths_for('queries')
107
+ load_custom_queries Bricks.paths_for('zena/queries')
109
108
 
110
109
  CORE_CONTEXTS = %w{parent project section}
111
110
 
@@ -187,7 +186,7 @@ module Zena
187
186
  end
188
187
  end
189
188
 
190
- # Overwrite this and take car to check for valid fields.
189
+ # Overwrite this and take care to check for valid fields.
191
190
  def process_field(field_name)
192
191
  if fld = @query.attributes_alias[field_name]
193
192
  # use custom query alias value defined in select clause: 'custom_a AS validation'
@@ -238,6 +237,11 @@ module Zena
238
237
  end
239
238
 
240
239
  # property or real column
240
+
241
+ # FIXME !!!! Why does this happen ?
242
+ return nil if @query.main_class.columns.kind_of?(Array)
243
+
244
+
241
245
  column = @query.main_class.columns[field_name]
242
246
  if column && column.indexed?
243
247
  if column.index == true
@@ -291,6 +295,15 @@ module Zena
291
295
  add_table('nodes_roles')
292
296
  "(#{table('nodes_roles')}.node_id = #{table('nodes')}.id AND #{table('nodes_roles')}.role_id = #{role.id})"
293
297
  end
298
+ elsif left.first == :function && left.last.last == 'date'
299
+ # transform "foo.date = baz"
300
+ # [:function, [:field, "foo"], [:method, "date"]]
301
+ # [:field, baz]
302
+ # ==> into
303
+ # "baz >= foo and foo < baz + 1 day"
304
+ a = left[1]
305
+ b = right
306
+ process([:and, [:<=, b, a], [:<, a, [:+, b, [:interval, [:integer, '1'], 'day']]]])
294
307
  else
295
308
  super
296
309
  end
@@ -320,17 +333,10 @@ module Zena
320
333
  elsif arg1[0] == :field && arg2[0] == :method
321
334
  # contact.log_at or log_at.year
322
335
  # arg1 = [:field, "contact"]
336
+ class_name = arg1[1]
323
337
  # 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
338
+ field_name = arg2[1]
339
+ function = nil
334
340
  else
335
341
  return [arg1, arg2]
336
342
  end
@@ -358,7 +364,7 @@ module Zena
358
364
  # ******** And maybe overwrite these **********
359
365
  def parse_custom_query_argument(key, value)
360
366
  return nil unless value
361
- super.gsub(/(RELATION_ID|NODE_ATTR)\(([^)]+)\)|(REF_DATE|NODE_ID|VISITOR_LANG)/) do
367
+ super.gsub(/(RELATION_ID|NODE_ATTR|SECURE_TABLE)\(([^)]+)\)|(REF_DATE|NODE_ID|VISITOR_LANG)/) do
362
368
  type, value = $1, $2
363
369
  type ||= $3
364
370
  case type
@@ -367,16 +373,18 @@ module Zena
367
373
  if rel = RelationProxy.find_by_role(role.singularize)
368
374
  rel[:id]
369
375
  else
370
- @errors << "could not find Relation '#{role}' in custom query"
371
- '-1'
376
+ raise ::QueryBuilder::Error.new("Custom query: could not find Relation '#{role}'")
372
377
  end
378
+ when 'SECURE_TABLE'
379
+ table_name = value
380
+ add_filter "\#{secure_scope('#{table_name}')}"
373
381
  when 'NODE_ATTR'
374
382
  attribute = value
375
383
  if Node.safe_method_type([attribute])
376
384
  insert_bind("#{node_name}.#{attribute}")
377
385
  else
378
- @errors << "cannot read attribute '#{attribute}' in custom query"
379
- '-1'
386
+ # not found: consider it's a property
387
+ insert_bind("#{node_name}.prop[#{attribute.inspect}]")
380
388
  end
381
389
  when 'REF_DATE'
382
390
  context[:ref_date] ? insert_bind(context[:ref_date]) : 'now()'
@@ -480,25 +488,43 @@ module Zena
480
488
 
481
489
  if class_name
482
490
  # We have named the relation, set main_class
491
+ # We should also insert class filter...
483
492
  if klass = Node.get_class(class_name)
484
493
  set_main_class(klass)
494
+ kpath_filter = ".kpath LIKE #{quote("#{klass.kpath}%")}" unless klass.kpath == 'N'
485
495
  else
486
496
  raise QueryBuilder::QueryException.new("Unknown class #{klass} in scope '#{class_name}:#{scope}'.")
487
497
  end
498
+ else
499
+ klass = nil
488
500
  end
489
501
 
490
502
  if relation == 'start'
491
503
  add_table(main_table)
492
504
  add_filter "#{field_or_attr('zip')} = #{insert_bind('start_node_zip')}"
493
505
  add_filter "#{table}.site_id = #{insert_bind('current_site.id')}"
506
+ if kpath_filter
507
+ add_filter "#{table}#{kpath_filter}"
508
+ end
494
509
  return true
495
510
  end
496
511
 
497
512
  if CORE_CONTEXTS.include?(relation)
513
+ unless class_name
514
+ if %w{project section}.include?(relation)
515
+ set_main_class(VirtualClass[relation.capitalize])
516
+ else
517
+ set_main_class(VirtualClass['Node'])
518
+ end
519
+ end
498
520
 
499
521
  # PREVIOUS_GROUP.id = NEW_GROUP.project_id
500
522
  add_table(main_table)
501
523
  add_filter "#{field_or_attr('id')} = #{field_or_attr("#{relation}_id", table(main_table, -1))}"
524
+ if kpath_filter
525
+ add_filter "#{table}#{kpath_filter}"
526
+ end
527
+ true
502
528
  else
503
529
  nil
504
530
  end
@@ -595,9 +621,9 @@ module Zena
595
621
  unless @query.select
596
622
  @query.select = ["#{@query.main_table}.*"]
597
623
  end
598
- add_select "#{link_table}.id AS link_id"
624
+ add_select("#{link_table}.id", 'link_id')
599
625
  Zena::Use::Relations::LINK_ATTRIBUTES.each do |l|
600
- add_select "#{link_table}.#{l} AS l_#{l}"
626
+ add_select("#{link_table}.#{l}", "l_#{l}")
601
627
  end
602
628
  end
603
629
  end
@@ -13,9 +13,9 @@ module Zena
13
13
  include Common
14
14
 
15
15
  # Read the parameters and add errors to the object if it is considered spam. Save it otherwize.
16
- def save_if_not_spam(obj, params)
16
+ def should_save(obj, params)
17
17
  # do nothing (overwritten by plugins like zena_captcha)
18
- obj.save
18
+ true
19
19
  end
20
20
 
21
21
  end # ControllerMethods
@@ -161,32 +161,6 @@ ENDTXT
161
161
  end
162
162
  trad_list
163
163
  end
164
-
165
- # This lets helpers render partials
166
- # TODO: make sure this is the best way to handle this problem.
167
- # def render_to_string(*args)
168
- # controller = self.controller || begin
169
- # # ==> this means render_to_string uses a view with everything ApplicationController has...
170
- # ApplicationController.new.instance_eval do
171
- # class << self
172
- # public :render, :render_to_string
173
- # attr_accessor :request, :response, :params
174
- # end
175
- #
176
- # @request = ::ActionController::TestRequest.new
177
- # @response = ::ActionController::TestResponse.new
178
- #
179
- # @params = {}
180
- # send(:initialize_current_url)
181
- # @template = @response.template = ::ActionView::Base.new(self.class.view_paths, {}, self)
182
- # @template.helpers.send :include, self.class.master_helper_module
183
- # self
184
- # end
185
- # end
186
- #
187
- # controller.render(*args)
188
- # end
189
-
190
164
  end # ViewMethods
191
165
  end # Refactor
192
166
  end # Use
@@ -231,7 +231,7 @@ module Zena
231
231
  end
232
232
 
233
233
  def l_status=(v)
234
- @l_status = v.blank? ? nil : v
234
+ @l_status = v.blank? ? nil : v.to_i
235
235
  if rel = relation_proxy_from_link
236
236
  rel.other_status = @l_status
237
237
  end
@@ -3,6 +3,14 @@ require 'tempfile'
3
3
  module Zena
4
4
  module Use
5
5
  module Rendering
6
+ class Redirect < Exception
7
+ attr_reader :url
8
+
9
+ def initialize(url)
10
+ @url = url
11
+ end
12
+ end # Redirect class
13
+
6
14
  module ViewMethods
7
15
  # Append javascript to the end of the page.
8
16
  def render_js(in_html = true)
@@ -174,6 +182,16 @@ module Zena
174
182
  headers.merge!(zafu_headers)
175
183
  cache_page(:url => opts[:cache_url]) if opts[:cache]
176
184
  end
185
+ # This does not work, Rendering::Redirect is wrapped in TemplateError
186
+ # rescue Zena::Use::Rendering::Redirect
187
+ # This does not work either: infinity loop, CPU hog on errors.
188
+ # rescue ActionView::TemplateError => err
189
+ # orig = err.original_exception
190
+ # if orig.kind_of?(Zena::Use::Rendering::Redirect)
191
+ # redirect_to orig.url
192
+ # else
193
+ # raise err
194
+ # end
177
195
  end
178
196
 
179
197
  # Cache page content into a static file in the current sites directory : SITES_ROOT/test.host/public
@@ -277,6 +295,17 @@ module Zena
277
295
  out "<% set_headers(#{headers.join(', ')}) %>"
278
296
  end
279
297
  end
298
+
299
+ def r_not_found
300
+ out "<% raise ActiveRecord::RecordNotFound %>"
301
+ end
302
+
303
+ # Does not work properly. FIXME.
304
+ # def r_redirect
305
+ # out parser_error('Missing "url" parameter.') unless url = @params[:url]
306
+ # code = ::RubyLess.translate_string(self, url)
307
+ # out "<% raise Zena::Use::Rendering::Redirect.new(#{code}) %>"
308
+ # end
280
309
  end # ZafuMethods
281
310
  end # Rendering
282
311
  end # Use
@@ -9,16 +9,23 @@ module Zena
9
9
 
10
10
  module VirtualClassMethods
11
11
  def self.included(base)
12
- base.validate :validate_idx_class, :validate_idx_scope
13
- base.attr_accessible :idx_class
14
- base.attr_accessible :idx_scope
12
+ base.class_eval do
13
+ validate :validate_idx_class, :validate_idx_scope
14
+ attr_accessible :idx_class, :idx_scope, :idx_reverse_scope
15
+ property do |p|
16
+ p.string 'idx_class'
17
+ p.string 'idx_scope'
18
+ p.string 'idx_reverse_scope'
19
+ end
20
+ self.export_attributes += %w{idx_class idx_scope idx_reverse_scope}
21
+ end
15
22
  end
16
23
 
17
24
  protected
18
25
  def validate_idx_class
19
- self[:idx_class] = nil if self[:idx_class].blank?
26
+ self.idx_class = nil if self.idx_class.blank?
20
27
 
21
- if model_name = self[:idx_class]
28
+ if model_name = self.idx_class
22
29
  if model_name =~ /\A[A-Z][a-zA-Z]+\Z/
23
30
  if klass = Zena.const_get(model_name) rescue NilClass
24
31
  if klass < Zena::Use::ScopeIndex::IndexMethods
@@ -36,11 +43,11 @@ module Zena
36
43
  end
37
44
 
38
45
  def validate_idx_scope
39
- self[:idx_scope] = nil if self[:idx_scope].blank?
40
- if scopes = self[:idx_scope]
46
+ self.idx_scope = nil if self.idx_scope.blank?
47
+ if scopes = self.idx_scope
41
48
  # Try to compile query in instance of class self
42
49
  begin
43
- scopes = new_instance.safe_eval self[:idx_scope]
50
+ scopes = new_instance.safe_eval scopes
44
51
  if scopes.kind_of?(Hash)
45
52
  scopes.each do |keys, query|
46
53
  unless keys.kind_of?(String) &&
@@ -122,11 +129,34 @@ module Zena
122
129
  end
123
130
  end
124
131
 
132
+ def update_after_destroy(deleted_node, keys)
133
+ groups = self.class.groups
134
+ attrs = {}
135
+ keys.each do |group_key|
136
+ next unless list = groups[group_key]
137
+ next unless should_clear_group?(group_key, deleted_node)
138
+ list.each do |key|
139
+ attrs["#{group_key}_#{key}"] = nil
140
+ end
141
+ end
142
+
143
+ if !attrs.empty?
144
+ self.attributes = attrs
145
+ save
146
+ end
147
+ end
148
+
125
149
  # Return true if the indices from the given group should be altered by the node.
126
150
  def should_update_group?(group_key, node)
127
151
  node.id >= self["#{group_key}_id"].to_i
128
152
  end
129
153
 
154
+ # Return true if the indices from the given group should be cleared when
155
+ # the given node is deleted.
156
+ def should_clear_group?(group_key, deleted_node)
157
+ deleted_node.id == self["#{group_key}_id"].to_i
158
+ end
159
+
130
160
  def set_site_id
131
161
  self[:site_id] = current_site.id
132
162
  end
@@ -134,7 +164,8 @@ module Zena
134
164
 
135
165
  module ModelMethods
136
166
  def self.included(base)
137
- base.after_save :update_scope_indices
167
+ base.after_save :update_scope_indices
168
+ base.after_destroy :update_scope_indices_on_destroy
138
169
  base.safe_context :scope_index => scope_index_proc
139
170
  base.alias_method_chain :rebuild_index!, :scope_index
140
171
  end
@@ -149,7 +180,7 @@ module Zena
149
180
  end
150
181
  end
151
182
 
152
- # FIXME: test !
183
+ # Rebuild 'remote' indexes based on changes in this node.
153
184
  def rebuild_index_with_scope_index!
154
185
  rebuild_index_without_scope_index!
155
186
  update_scope_indices
@@ -171,6 +202,21 @@ module Zena
171
202
  end
172
203
  end
173
204
 
205
+ # Trigger 'rebuild_index!' in all elements that could affect this model's
206
+ # scope index.
207
+ def rebuild_scope_index!
208
+ if vclass && query = vclass.idx_reverse_scope
209
+ if nodes = find(:all, query)
210
+ nodes.each do |node|
211
+ node.rebuild_index!
212
+ end
213
+ end
214
+ else
215
+ nil
216
+ end
217
+
218
+ end
219
+
174
220
  protected
175
221
  # Update scope indices (project/section).
176
222
  def update_scope_indices
@@ -182,7 +228,13 @@ module Zena
182
228
  # FIXME: raise when we have transactional save.
183
229
  end
184
230
 
185
- def update_scope_indices_on_prop_change
231
+ def update_scope_indices_on_destroy
232
+ update_scope_indices_on_prop_change(true)
233
+ # How can we handle this ?
234
+ # update_scope_indices_on_link_change
235
+ end
236
+
237
+ def update_scope_indices_on_prop_change(deleted=false)
186
238
  if virtual_class && scopes = virtual_class.idx_scope
187
239
  scopes = safe_eval(scopes)
188
240
  return unless scopes.kind_of?(Hash)
@@ -198,15 +250,24 @@ module Zena
198
250
  mapped_scopes.each do |keys, query|
199
251
  next unless query.kind_of?(String) && keys.kind_of?(Array) && keys.inject(true) {|s,k| s && k.kind_of?(String) }
200
252
  if query.strip == 'self'
253
+ next if deleted
201
254
  models_to_update = [self]
202
255
  else
203
- models_to_update = find(:all, query) || []
256
+ models_to_update = find(:all, query, :skip_rubyless => true) || []
204
257
  end
205
258
 
206
259
  models_to_update.each do |m|
260
+ next if m.destroyed? # on destroy, self can be in this list
207
261
  if idx_model = m.scope_index
208
- # force creation of index record
209
- idx_model.update_with(self, keys, true)
262
+ if deleted
263
+ # Clear obsolete content
264
+ idx_model.update_after_destroy(self, keys)
265
+ # Rebuild index
266
+ m.rebuild_scope_index!
267
+ else
268
+ # force creation of index record
269
+ idx_model.update_with(self, keys, true)
270
+ end
210
271
  end
211
272
  end
212
273
  end
@@ -10,17 +10,12 @@ module Zena
10
10
  :conditions => ["parent_id = ?",node[:id]],
11
11
  :order => 'zip ASC' )
12
12
  elsif !query.blank?
13
- if Zena::Db.adapter == 'mysql' && RAILS_ENV != 'test'
14
- match = sanitize_sql(["MATCH (vs.idx_text_high,vs.idx_text_medium,vs.idx_text_low) AGAINST (?)", query, "#{options[:name_query] || query.url_name}%"])
15
- select = sanitize_sql(["nodes.*, MATCH (vs.idx_text_high,vs.idx_text_medium,vs.idx_text_low) AGAINST (?) AS score", query, "#{query}%"])
16
- else
17
- match = sanitize_sql(["nodes._id LIKE ?", "#{query}%"])
18
- select = "nodes.*, #{match} AS score"
19
- end
13
+ match = sanitize_sql(["vs.idx_text_high LIKE ?", "%#{query.gsub('*','%')}%"])
14
+ select = "nodes.*"
20
15
 
21
16
  case Zena::Db.adapter
22
17
  when 'postgresql'
23
- select = "DISTINCT ON (nodes.zip, score) #{select}"
18
+ select = "DISTINCT ON (nodes.zip) #{select}"
24
19
  group = nil
25
20
  else
26
21
  group = 'nodes.id'
@@ -31,7 +26,7 @@ module Zena
31
26
  :joins => "INNER JOIN versions AS vs ON vs.node_id = nodes.id AND vs.status >= #{Zena::Status[:pub]}",
32
27
  :conditions => match,
33
28
  :group => group,
34
- :order => "score DESC, zip ASC")
29
+ :order => "zip DESC") # new items first
35
30
  else
36
31
  # error
37
32
  return options.merge(:conditions => '0')
@@ -69,7 +64,7 @@ module Zena
69
64
 
70
65
  query = "nodes where #{query_args.join(' and ')} in site"
71
66
  end
72
-
67
+
73
68
  res = node.find(count, query, options.merge(:errors => true, :rubyless_helper => self, :default => default))
74
69
 
75
70
  if res.kind_of?(::QueryBuilder::Error)
@@ -102,7 +102,7 @@ module Zena
102
102
  uploaded_fixture(fname, 'image/jpeg', filename)
103
103
  end
104
104
 
105
- # PDF helper
105
+ # Pdf helper
106
106
  def uploaded_pdf(fname, filename=nil)
107
107
  uploaded_fixture(fname, 'application/pdf', filename)
108
108
  end
@@ -141,7 +141,7 @@ module Zena
141
141
  digest = Digest::SHA1.hexdigest(content_id.to_s)
142
142
  "#{SITES_ROOT}/test.host/data/#{mode}/#{digest[0..0]}/#{digest[1..1]}/#{digest[2..2]}/#{fname}"
143
143
  end
144
-
144
+
145
145
  def execute_after_commit!
146
146
  Node.connection.instance_eval do
147
147
  # Simulate commit