zena 0.15.2 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. data/.gitignore +20 -0
  2. data/CREDITS +27 -0
  3. data/Capfile +3 -0
  4. data/DEVELOPERS +46 -0
  5. data/History.txt +15 -0
  6. data/MIT-LICENSE +19 -0
  7. data/Rakefile +44 -0
  8. data/TODO +24 -0
  9. data/TODO_ZENA_1_0 +23 -0
  10. data/app/controllers/application_controller.rb +3 -0
  11. data/app/controllers/documents_controller.rb +22 -56
  12. data/app/controllers/nodes_controller.rb +42 -27
  13. data/app/controllers/pings_controller.rb +19 -0
  14. data/app/controllers/relations_controller.rb +5 -1
  15. data/app/controllers/sites_controller.rb +1 -46
  16. data/app/controllers/user_sessions_controller.rb +47 -0
  17. data/app/controllers/users_controller.rb +1 -0
  18. data/app/controllers/versions_controller.rb +25 -7
  19. data/app/controllers/virtual_classes_controller.rb +1 -1
  20. data/app/helpers/application_helper.rb +1 -1
  21. data/app/models/comment.rb +2 -1
  22. data/app/models/contact_content.rb +2 -2
  23. data/app/models/data_entry.rb +5 -6
  24. data/app/models/document.rb +14 -10
  25. data/app/models/document_content.rb +4 -6
  26. data/app/models/iformat.rb +2 -2
  27. data/app/models/image_content.rb +6 -9
  28. data/app/models/node.rb +106 -164
  29. data/app/models/page.rb +0 -20
  30. data/app/models/site.rb +42 -12
  31. data/app/models/template.rb +3 -8
  32. data/app/models/template_content.rb +2 -0
  33. data/app/models/text_document.rb +13 -8
  34. data/app/models/user.rb +47 -100
  35. data/app/models/user_session.rb +4 -0
  36. data/app/models/version.rb +1 -1
  37. data/app/views/comments/create.rjs +3 -3
  38. data/app/views/comments/edit.rjs +1 -1
  39. data/app/views/comments/update.rjs +1 -1
  40. data/app/views/nodes/_import_results.rhtml +1 -1
  41. data/app/views/nodes/create.rjs +3 -3
  42. data/app/views/templates/document_create_tabs/_file.rhtml +1 -2
  43. data/app/views/templates/document_create_tabs/_import.rhtml +7 -2
  44. data/app/views/templates/edit_tabs/_document.rhtml +1 -3
  45. data/app/views/templates/edit_tabs/_image.rhtml +1 -3
  46. data/app/views/versions/_tr.rhtml +1 -1
  47. data/app/views/versions/edit.rhtml +2 -26
  48. data/bin/zena +6 -1
  49. data/bricks/delayed_job/README +18 -0
  50. data/bricks/delayed_job/migrate/20091104191643_create_delayed_jobs_table.rb +19 -0
  51. data/bricks/delayed_job/misc/init.rb +8 -0
  52. data/bricks/delayed_job/misc/tasks.rb +2 -0
  53. data/bricks/math/patch/application_helper.rb +1 -1
  54. data/bricks/sphinx/MIT-LICENSE +19 -0
  55. data/bricks/sphinx/README +19 -0
  56. data/bricks/sphinx/lib/use_sphinx.rb +78 -0
  57. data/bricks/sphinx/migrate/20091102171258_add_delta_for_sphinx.rb +9 -0
  58. data/bricks/sphinx/misc/deploy.rb +20 -0
  59. data/bricks/sphinx/misc/sphinx.yml +12 -0
  60. data/bricks/sphinx/misc/tasks.rb +21 -0
  61. data/bricks/sphinx/patch/node.rb +8 -0
  62. data/bricks/tags/lib/has_tags.rb +5 -3
  63. data/bricks/tags/test/zafu/tags.yml +13 -1
  64. data/config/bricks.yml +35 -0
  65. data/config/deploy.rb +8 -1
  66. data/config/environment.rb +1 -1
  67. data/config/environments/production.rb +1 -1
  68. data/config/gems.yml +28 -5
  69. data/config/sphinx.yml +12 -0
  70. data/db/init/base/skins/default/Node-+popupLayout.zafu +1 -16
  71. data/db/migrate/20091026161708_add_persistence_token.rb +13 -0
  72. data/db/migrate/20091101184952_add_session_table.rb +16 -0
  73. data/db/migrate/20091123175137_add_single_access_token.rb +9 -0
  74. data/db/migrate/20091124161608_rebuild_fullpath.rb +11 -0
  75. data/db/schema.rb +21 -8
  76. data/doc/README_FOR_APP +24 -0
  77. data/doc/fixtures.graffle +19568 -0
  78. data/doc/fixtures.pdf +0 -0
  79. data/doc/template/LICENSE +184 -0
  80. data/doc/template/README +37 -0
  81. data/doc/template/allison.css +283 -0
  82. data/doc/template/allison.js +307 -0
  83. data/doc/template/allison.rb +260 -0
  84. data/doc/template/cache/BODY +588 -0
  85. data/doc/template/cache/CLASS_INDEX +4 -0
  86. data/doc/template/cache/CLASS_PAGE +1 -0
  87. data/doc/template/cache/FILE_INDEX +4 -0
  88. data/doc/template/cache/FILE_PAGE +1 -0
  89. data/doc/template/cache/FONTS +1 -0
  90. data/doc/template/cache/FR_INDEX_BODY +1 -0
  91. data/doc/template/cache/IMGPATH +1 -0
  92. data/doc/template/cache/INDEX +1 -0
  93. data/doc/template/cache/JAVASCRIPT +307 -0
  94. data/doc/template/cache/METHOD_INDEX +4 -0
  95. data/doc/template/cache/METHOD_LIST +1 -0
  96. data/doc/template/cache/SRC_PAGE +1 -0
  97. data/doc/template/cache/STYLE +283 -0
  98. data/doc/template/cache/URL +1 -0
  99. data/doc/zafu_changes.yml +29 -0
  100. data/lib/base_additions.rb +1 -1
  101. data/lib/bricks.rb +9 -0
  102. data/lib/bricks/loader.rb +86 -0
  103. data/lib/bricks/requirements_validation.rb +71 -0
  104. data/lib/tasks/zena.rake +42 -4
  105. data/lib/zafu/action.rb +285 -0
  106. data/lib/zafu/ajax.rb +93 -0
  107. data/lib/zafu/attributes.rb +117 -0
  108. data/lib/zafu/calendar.rb +159 -0
  109. data/lib/zafu/context.rb +330 -0
  110. data/lib/zafu/core/html.rb +102 -0
  111. data/lib/zafu/core/move_to_parser.rb +167 -0
  112. data/lib/zafu/dates.rb +58 -0
  113. data/lib/zafu/display.rb +502 -0
  114. data/lib/zafu/eval.rb +66 -0
  115. data/lib/zafu/experimental.rb +66 -0
  116. data/lib/zafu/i18n.rb +64 -0
  117. data/lib/zafu/meta.rb +25 -0
  118. data/lib/zafu/refactor.rb +73 -0
  119. data/lib/zafu/support/context.rb +265 -0
  120. data/lib/zafu/support/dom.rb +145 -0
  121. data/lib/zafu/support/erb.rb +62 -0
  122. data/lib/zafu/support/flow.rb +401 -0
  123. data/lib/zafu/support/forms.rb +461 -0
  124. data/lib/zafu/support/links.rb +306 -0
  125. data/lib/zafu_parser.rb +26 -2
  126. data/lib/zena.rb +34 -15
  127. data/lib/zena/acts/multiversion.rb +2 -2
  128. data/lib/zena/acts/secure.rb +41 -30
  129. data/lib/zena/app.rb +7 -10
  130. data/lib/zena/controller/test_case.rb +12 -7
  131. data/lib/zena/crypto_provider/initial.rb +15 -0
  132. data/lib/zena/db.rb +6 -1
  133. data/lib/zena/deploy.rb +34 -6
  134. data/lib/zena/deploy/logrotate_app.rhtml +9 -0
  135. data/lib/zena/deploy/logrotate_host.rhtml +34 -0
  136. data/lib/zena/deploy/template.rb +1 -9
  137. data/lib/zena/foxy_parser.rb +1 -1
  138. data/lib/zena/info.rb +3 -1
  139. data/lib/zena/migrator.rb +1 -1
  140. data/lib/zena/parser.rb +12 -4
  141. data/lib/zena/parser/zazen_rules.rb +6 -6
  142. data/lib/zena/parser/zena_rules.rb +1 -7
  143. data/lib/zena/routes.rb +5 -5
  144. data/lib/zena/test_controller.rb +7 -2
  145. data/lib/zena/unit/test_case.rb +6 -8
  146. data/lib/zena/use/ajax.rb +10 -10
  147. data/lib/zena/use/authlogic.rb +93 -0
  148. data/lib/zena/use/dyn_attributes.rb +5 -0
  149. data/lib/zena/use/html_tags.rb +16 -34
  150. data/lib/zena/use/i18n.rb +4 -1
  151. data/lib/zena/use/node_query_finders.rb +8 -4
  152. data/lib/zena/use/refactor.rb +8 -20
  153. data/lib/zena/use/relations.rb +1 -0
  154. data/lib/zena/use/rendering.rb +4 -2
  155. data/lib/zena/use/search.rb +52 -0
  156. data/lib/zena/use/test_helper.rb +27 -28
  157. data/lib/zena/use/upload.rb +188 -0
  158. data/lib/zena/use/urls.rb +16 -14
  159. data/lib/zena/use/zafu.rb +16 -63
  160. data/lib/zena/use/zazen.rb +8 -8
  161. data/lib/zena/view/test_case.rb +8 -4
  162. data/locale/en/LC_MESSAGES/zena.mo +0 -0
  163. data/locale/en/zena.po +3 -3
  164. data/public/.htaccess +40 -0
  165. data/public/javascripts/upload-progress.js +17 -8
  166. data/public/javascripts/zena.js +8 -2
  167. data/public/stylesheets/popup.css +1 -0
  168. data/script/about +3 -0
  169. data/script/apache_logging +25 -0
  170. data/script/breakpointer +3 -0
  171. data/script/console +3 -0
  172. data/script/dbconsole +3 -0
  173. data/script/destroy +3 -0
  174. data/script/generate +3 -0
  175. data/script/performance/benchmarker +3 -0
  176. data/script/performance/profiler +3 -0
  177. data/script/plugin +3 -0
  178. data/script/process/inspector +3 -0
  179. data/script/process/reaper +3 -0
  180. data/script/process/spawner +3 -0
  181. data/script/runner +3 -0
  182. data/script/server +3 -0
  183. data/script/set_revision +29 -0
  184. data/spec/controllers/versions_controller_spec.rb +11 -0
  185. data/test/fixtures/files/Node-test.zafu +1 -1
  186. data/test/functional/nodes_controller_test.rb +25 -0
  187. data/test/functional/pings_controller_test.rb +8 -0
  188. data/test/functional/user_sessions_controller_test.rb +59 -0
  189. data/test/functional/users_controller_test.rb +81 -19
  190. data/test/helpers/node_query/filters.yml +5 -0
  191. data/test/helpers/node_query_test.rb +3 -3
  192. data/test/integration/multiple_hosts_test.rb +1 -1
  193. data/test/integration/navigation_test.rb +1 -1
  194. data/test/sites/complex/users.yml +1 -1
  195. data/test/sites/ocean/users.yml +3 -3
  196. data/test/sites/zena/users.yml +5 -4
  197. data/test/test_zena.rb +38 -38
  198. data/test/unit/cached_page_test.rb +2 -2
  199. data/test/unit/comment_test.rb +0 -1
  200. data/test/unit/document_test.rb +23 -11
  201. data/test/unit/helpers/ping_helper_test.rb +4 -0
  202. data/test/unit/multiversion_test.rb +24 -16
  203. data/test/unit/node_test.rb +32 -93
  204. data/test/unit/note_test.rb +9 -0
  205. data/test/unit/page_test.rb +2 -2
  206. data/test/unit/secure_test.rb +2 -12
  207. data/test/unit/site_test.rb +43 -24
  208. data/test/unit/template_test.rb +45 -3
  209. data/test/unit/text_document_test.rb +4 -3
  210. data/test/unit/user_test.rb +13 -33
  211. data/test/unit/zena/db_test.rb +8 -0
  212. data/test/unit/zena/parser/zazen.yml +4 -4
  213. data/test/unit/zena/use/dates_view_methods_test.rb +2 -1
  214. data/test/unit/zena/use/html_tags_test.rb +12 -4
  215. data/test/unit/zena/use/refactor_test.rb +4 -3
  216. data/test/unit/zena/use/rendering_test.rb +1 -0
  217. data/test/unit/zena/use/upload_test.rb +76 -0
  218. data/test/unit/zena/use/urls_test.rb +4 -0
  219. data/test/unit/zena/use/zafu_test.rb +8 -0
  220. data/test/unit/zena/workflow/status_version_test.rb +6 -0
  221. data/test/unit/zena/zena_tags/ajax.yml +4 -4
  222. data/test/unit/zena/zena_tags/basic.yml +21 -10
  223. data/test/unit/zena/zena_tags/relations.yml +0 -6
  224. data/test/unit/zena/zena_tags/rubyless.yml +35 -0
  225. data/test/unit/zena/zena_tags/zazen.yml +4 -4
  226. data/test/unit/zena/zena_tags_test.rb +36 -4
  227. data/vendor/TextMate/Zena.tmbundle/Commands/Run all yaml tests.tmCommand +1 -1
  228. data/vendor/TextMate/Zena.tmbundle/Commands/Run focused yaml test.tmCommand +2 -3
  229. data/vendor/TextMate/Zena.tmbundle/Support/RubyMate/catch_exception.rb +39 -0
  230. data/vendor/TextMate/Zena.tmbundle/Support/RubyMate/run_script.rb +102 -58
  231. data/vendor/TextMate/Zena.tmbundle/Support/RubyMate/stdin_dialog.rb +14 -0
  232. data/vendor/TextMate/Zena.tmbundle/info.plist +2 -0
  233. data/zena.gemspec +2085 -0
  234. metadata +265 -90
  235. data/app/controllers/sessions_controller.rb +0 -41
  236. data/app/views/sites/zena_up.html.erb +0 -11
  237. data/config/database.yml +0 -40
  238. data/db/production.sqlite3 +0 -0
  239. data/lib/bricks/patcher.rb +0 -68
  240. data/lib/zena/parser/zena_tags.rb +0 -3562
  241. data/lib/zena/use/authentification.rb +0 -120
  242. data/public/images/ext/contact_pv.png +0 -0
  243. data/public/images/ext/other_pv.png +0 -0
  244. data/public/images/ext/page_pv.png +0 -0
  245. data/public/images/ext/page_tiny.png +0 -0
  246. data/public/images/ext/pdf_pv.png +0 -0
  247. data/public/images/ext/post_pv.png +0 -0
  248. data/public/images/ext/post_tiny.png +0 -0
  249. data/public/images/ext/project_pv.png +0 -0
  250. data/public/images/ext/project_tiny.png +0 -0
  251. data/public/images/ext/tag_pv.png +0 -0
  252. data/public/images/ext/zip_pv.png +0 -0
  253. data/tasks/ann.rake +0 -80
  254. data/tasks/bones.rake +0 -20
  255. data/tasks/gem.rake +0 -201
  256. data/tasks/git.rake +0 -40
  257. data/tasks/notes.rake +0 -27
  258. data/tasks/post_load.rake +0 -34
  259. data/tasks/rdoc.rake +0 -51
  260. data/tasks/rubyforge.rake +0 -55
  261. data/tasks/setup.rb +0 -292
  262. data/tasks/spec.rake +0 -54
  263. data/tasks/svn.rake +0 -47
  264. data/tasks/test.rake +0 -40
  265. data/tasks/zentest.rake +0 -36
  266. data/test/fixtures/comments.yml +0 -126
  267. data/test/fixtures/contact_contents.yml +0 -132
  268. data/test/fixtures/data_entries.yml +0 -65
  269. data/test/fixtures/discussions.yml +0 -48
  270. data/test/fixtures/document_contents.yml +0 -108
  271. data/test/fixtures/dyn_attributes.yml +0 -66
  272. data/test/fixtures/groups.yml +0 -86
  273. data/test/fixtures/groups_users.yml +0 -81
  274. data/test/fixtures/iformats.yml +0 -29
  275. data/test/fixtures/links.yml +0 -313
  276. data/test/fixtures/nodes.yml +0 -2592
  277. data/test/fixtures/relations.yml +0 -126
  278. data/test/fixtures/sites.yml +0 -58
  279. data/test/fixtures/template_contents.yml +0 -172
  280. data/test/fixtures/users.yml +0 -167
  281. data/test/fixtures/versions.yml +0 -1911
  282. data/test/fixtures/virtual_classes.yml +0 -87
  283. data/test/fixtures/zips.yml +0 -15
  284. data/test/functional/sessions_controller_test.rb +0 -73
@@ -1,41 +0,0 @@
1
- =begin rdoc
2
- Create, destroy sessions by letting users login and logout. When the user does not login, he/she is considered to be the anonymous user.
3
- =end
4
- class SessionsController < ApplicationController
5
- skip_before_filter :authorize
6
-
7
- def new
8
- respond_to do |format|
9
- format.html do
10
- @node = visitor.site.root_node
11
- render_and_cache :mode => '+login'
12
- end
13
- end
14
- end
15
-
16
- def create
17
- if user = User.login(params[:login], params[:password], request.host)
18
- successful_login(user)
19
- else
20
- failed_login _("Invalid login or password.")
21
- end
22
- end
23
-
24
- # Clears session information and redirects to home page.
25
- def destroy
26
- reset_session
27
- if request.referer =~ %r{(http://#{visitor.site.host}:\d*/)#{AUTHENTICATED_PREFIX}(.*)}
28
- redirect_to $1 + visitor.lang + $2
29
- else
30
- redirect_to :controller => 'nodes', :action => 'index', :prefix => visitor.lang
31
- end
32
- end
33
-
34
- protected
35
-
36
- def failed_login(message)
37
- session[:user] = nil
38
- flash[:error] = message
39
- redirect_to '/login'
40
- end
41
- end
@@ -1,11 +0,0 @@
1
- <% if @state == :wait -%>
2
- <h2><%= _('Updating...') %></h2>
3
- <p><%= _('Please wait while the code is updated and the site restarts (page reloads every 30s).') %></p>
4
- <% elsif @state == :done -%>
5
- <h2><%= _('Up to date') %></h2>
6
- <p><%= _('Your site is running the latest version of zena.') %> (<%= Zena::VERSION %> r<%= @target_rev %>)</p>
7
- <% else -%>
8
- <h2><%= _('Update') %></h2>
9
- <p><%= sprintf(_('Your site is running rev %i, newest revision is %i.'), @current_rev, @target_rev) %></p>
10
- <p><%= link_to(_('Update'), :action => 'zena_up', :run => 'start', :rev => @target_rev) %></p>
11
- <% end -%>
@@ -1,40 +0,0 @@
1
- # Enter database configuration information here (rename this file "database.yml").
2
- # You do not need to do any of this if you use capistraon (see config/deploy.rg).
3
- # MySQL
4
- login: &dev_login
5
- adapter: mysql
6
- socket: /tmp/mysql.sock
7
- username: root
8
- password:
9
-
10
- development:
11
- database: zena_dev
12
- encoding: utf8
13
- <<: *dev_login
14
-
15
- # Warning: The database defined as "test" will be erased and
16
- # re-generated from your development database when you run "rake".
17
- # Do not set this db to the same as development or production.
18
- test:
19
- database: zena_test
20
- encoding: utf8
21
- <<: *dev_login
22
-
23
- # FIXME: use in memory sqlite3 for testing
24
- #test:
25
- # adapter: sqlite3
26
- # database: db/test.sqlite3
27
- # pool: 5
28
- # timeout: 5000
29
- #
30
- production:
31
- database: zena_dev
32
- encoding: utf8
33
- <<: *dev_login
34
-
35
- #production:
36
- # adapter: sqlite3
37
- # database: db/production.sqlite3
38
- # pool: 5
39
- # timeout: 5000
40
- #
Binary file
@@ -1,68 +0,0 @@
1
- module Bricks
2
- class Patcher
3
- class << self
4
- def bricks_folders
5
- @bricks_folders ||= [File.join(Zena::ROOT, 'bricks'), File.join(RAILS_ROOT, 'bricks')].uniq.reject {|f| !File.exist?(f)}
6
- end
7
-
8
- def models_paths
9
- bricks_folders.map {|f| Dir["#{f}/**/models"] }.flatten
10
- end
11
-
12
- def libs_paths
13
- bricks_folders.map {|f| Dir["#{f}/**/lib"] }.flatten
14
- end
15
-
16
- def foreach_brick(&block)
17
- bricks_folders.each do |bricks_folder|
18
- next unless File.exist?(bricks_folder)
19
- Dir.entries(bricks_folder).sort.each do |brick|
20
- next if brick =~ /\A\./
21
- block.call(File.join(bricks_folder, brick))
22
- end
23
- end
24
- end
25
-
26
- def apply_patches(file_name = nil)
27
- file_name ||= caller[0].split('/').last.split(':').first
28
- foreach_brick do |brick_path|
29
- patch_file = File.join(brick_path, 'patch', file_name)
30
- if File.exist?(patch_file)
31
- load patch_file
32
- end
33
- end
34
- end
35
-
36
- def load_bricks
37
- # load all libraries in bricks
38
- libs_paths.each do |lib_path|
39
- Dir.foreach(lib_path) do |f|
40
- next unless f =~ /\A.+\.rb\Z/
41
- require File.join(lib_path, f)
42
- end
43
- end
44
-
45
- # FIXME: do we really need to load these now, load_path isn't enough ?
46
- models_paths.each do |models_path|
47
- Dir.foreach(models_path) do |f|
48
- next unless f =~ /\A.+\.rb\Z/
49
- require File.join(models_path, f)
50
- end
51
- end
52
- end
53
-
54
- def load_zafu(mod)
55
- foreach_brick do |brick_path|
56
- brick_name = File.basename(brick_path)
57
- zafu_path = File.join(brick_path, 'zafu')
58
- next unless File.exist?(zafu_path)
59
- Dir.foreach(zafu_path) do |rules_name|
60
- next if rules_name =~ /\A\./
61
- load File.join(zafu_path, rules_name)
62
- end
63
- mod.send(:include, eval("Bricks::#{brick_name.capitalize}::Zafu"))
64
- end
65
- end
66
- end
67
- end
68
- end
@@ -1,3562 +0,0 @@
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
- include RubyLess::SafeClass
45
- # Zafu tags used to display / edit nodes and versions
46
- module ZenaTags
47
- PSEUDO_ATTRIBUTES = {
48
- 'now' => 'Time.now',
49
- 'start.id' => '(params[:s] || @node[:zip])',
50
- 'nil' => 'nil',
51
- }
52
-
53
- class << self
54
- def inline_methods(*args)
55
- args.each do |name|
56
- class_eval <<-END
57
- def r_#{name}
58
- "<%= #{name}(:node=>\#{node}\#{params_to_erb(@params)}) %>"
59
- end
60
- END
61
- end
62
- end
63
-
64
- def direct_methods(*args)
65
- args.each do |name|
66
- class_eval <<-END
67
- def r_#{name}
68
- helper.#{name}
69
- end
70
- END
71
- end
72
- end
73
- end
74
- inline_methods :login_link, :visitor_link, :search_box, :show_menu, :show_path, :lang_links
75
- direct_methods :uses_datebox
76
-
77
- def before_render
78
- return unless super
79
-
80
- @var = nil # reset var counter
81
-
82
- if key = @params[:store]
83
- set_stored(Node, key, node)
84
- end
85
-
86
- if key = @params[:store_date]
87
- set_stored(Date, key, current_date)
88
- end
89
- @anchor_param = @params[:anchor]
90
-
91
- true
92
- end
93
-
94
- def do_method(sym)
95
- method = sym
96
- pre, post = '', ''
97
-
98
- # do we need recursion ?
99
- inc = descendant('include')
100
- if inc && inc.params[:part] == @name
101
- @context["#{@name}_method".to_sym] = method_name = template_url[1..-1].gsub(/[\/-]/,'_')
102
- pre << "<% def #{method_name}(depth, node, list); return '' if depth > #{inc.params[:depth] ? [inc.params[:depth].to_i,30].min : 5}; _erbout = '' -%>"
103
- post << "<% _erbout; end -%><%= #{method_name}(0,#{node},#{list || "[#{node}]"}) %>"
104
- @context[:node] = 'node'
105
- @context[:list] = 'list'
106
- end
107
-
108
- if @context[:make_form]
109
- res = case method
110
- when :r_title
111
- make_input(:name => 'v_title')
112
- when :r_link
113
- make_input(:name => (@params[:attr] || 'v_title'))
114
- when :r_show
115
- make_input(:name => (@params[:attr] || @params[:tattr]), :date => @params[:date])
116
- when :r_text
117
- make_textarea(:name => 'v_text')
118
- when :r_summary
119
- make_textarea(:name => 'v_summary')
120
- when :r_zazen
121
- make_textarea(:name => @params[:attr])
122
- else
123
- if node_kind_of?(DataEntry) && @method.to_s =~ /node_/
124
- # select node_id
125
- "<%= select_id('#{base_class.to_s.underscore}', '#{@method}_id') %>"
126
- end
127
- end
128
- res = "<#{@html_tag || 'div'} class='zazen'>#{res}</#{@html_tag || 'div'}>" if [:r_summary, :r_text].include?(sym)
129
- end
130
-
131
-
132
- res ||= super(method)
133
- "#{pre}#{res}#{post}"
134
- end
135
-
136
-
137
- def after_render(text)
138
- if @anchor_param
139
- @params[:anchor] = @anchor_param # set back in case of double rendering so it is computed again
140
- res = r_anchor + super
141
- else
142
- res = super
143
- end
144
- res
145
- end
146
-
147
- # Our special version of r_expand_with tag with "set_" parsing.
148
- def r_expand_with
149
- hash = {}
150
- @params.each do |k,v|
151
- if k.to_s =~ /^set_(.+)$/
152
- # TODO: DRY with render_html_tag
153
- k = $1
154
- value, static = parse_attributes_in_value(v, :erb => false)
155
- hash["exp_#{k}"] = static ? value.inspect : "\"#{value}\""
156
- else
157
- hash["exp_#{k}"] = v.inspect
158
- end
159
- end
160
- @params = {}
161
- expand_with(hash)
162
- end
163
-
164
- def r_show
165
- if attr_or_date = @params[:attr_or_date]
166
- # using [var] shortcut. Can be either a date or an attribute/var
167
- if attr_or_date == 'current_date' || attr_or_date =~ /_at$/
168
- @params[:date] = attr_or_date
169
- else
170
- @params[:attr] = attr_or_date
171
- end
172
- end
173
-
174
- if var_name = @params[:var]
175
- return parser_error("var #{@params[:var].inspect} not set") unless @context[:vars] && @context[:vars].include?(var_name)
176
- attribute_method = "set_#{var_name}"
177
- elsif @params[:eval]
178
- return unless attribute_method = parse_eval_parameter(@params[:eval])
179
- elsif @params[:tattr]
180
- attribute_method = "_(#{node_attribute(@params[:tattr], :else=>@params[:else], :default=>@params[:default])})"
181
- elsif @params[:attr]
182
- attribute_method = node_attribute(@params[:attr], :else=>@params[:else], :default=>@params[:default])
183
- elsif p = @params[:param]
184
- return "<%= params[#{p.to_sym.inspect}] %>"
185
- elsif @params[:date]
186
- # date can be any attribute v_created_at or updated_at etc.
187
- # TODO format with @params[:format] and @params[:tformat] << translated format
188
- # TODO: test
189
- if @params[:tformat]
190
- format = _(@params[:tformat])
191
- elsif @params[:format]
192
- format = @params[:format]
193
- else
194
- format = "%Y-%m-%d"
195
- end
196
-
197
- tz = ''
198
- lang = ''
199
- if tz_name = @params[:time_zone]
200
- 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|
201
- if tz_name =~ /^\[(\w+)\]$/
202
- node_attribute($1)
203
- else
204
- begin
205
- TZInfo::Timezone.get(tz_name)
206
- rescue TZInfo::InvalidTimezoneIdentifier
207
- return parser_error("invalid timezone #{tz_name.inspect}")
208
- end
209
- tz_name.inspect
210
- end
211
- end
212
- tz = ", #{tz_list.join(' || ')}"
213
- end
214
- if lang = @params[:lang]
215
- tz = ', nil' if tz == ''
216
- lang = ", #{lang.inspect}"
217
- end
218
- attribute_method = "format_date(#{node_attribute(@params[:date])}, #{format.inspect}#{tz}#{lang})"
219
- elsif @context[:trans]
220
- # error
221
- return "no attribute for 'show'".inspect
222
- else
223
- return parser_error("missing attribute")
224
- end
225
-
226
- if !@params[:date] && fmt = @params[:format]
227
- begin
228
- # test argument
229
- sprintf(fmt, 123.45)
230
- rescue ArgumentError
231
- return parser_error("incorect format #{fmt.inspect}")
232
- end
233
- if fmt =~ /%[\d\.]*f/
234
- modifier = ".to_f"
235
- elsif fmt =~ /%[\d\.]*i/
236
- modifier = ".to_i"
237
- else
238
- modifier = ''
239
- end
240
-
241
- if @params[:zero] == 'hide'
242
- attribute_method = "sprintf_unless_zero(#{fmt.inspect}, #{attribute_method}#{modifier})"
243
- else
244
- attribute_method = "sprintf(#{fmt.inspect}, #{attribute_method}#{modifier})"
245
- end
246
- end
247
-
248
-
249
- if @context[:trans]
250
- # TODO: what do we do here with gsubs, url ?
251
- return attribute_method
252
- end
253
-
254
- if gsub = @params[:gsub]
255
- if gsub =~ /\A\/(.+)\/(.+)\/\Z/
256
- value = $2
257
- key = $1.gsub(/\#([\{\$\@])/,'# \1') # FIXME: SECURITY.
258
- # Please note that .gsub(/#([\{\$\@])/,'\#\1') won't work, since '\#{blah}' will become '\\#{blah}' and 'blah' will be evaluated.
259
- regexp_ok = begin
260
- output = StringIO.open('','w')
261
- $stderr = output
262
- re = /#{key}/
263
- output.string !~ /warning:/
264
- rescue
265
- false
266
- ensure
267
- $stderr = STDERR
268
- false
269
- end
270
-
271
- if regexp_ok
272
- attribute_method = "#{attribute_method}.to_s.gsub(/#{key}/,#{value.inspect})"
273
- else
274
- # invalid regexp
275
- return parser_error("invalid gsub #{gsub.inspect}")
276
- end
277
- else
278
- # error
279
- return parser_error("invalid gsub #{gsub.inspect}")
280
- end
281
- end
282
-
283
- if @params[:actions]
284
- actions = "<%= node_actions(:node=>#{node}#{params_to_erb(:actions=>@params[:actions], :publish_after_save=>auto_publish_param)}) %>"
285
- else
286
- actions = ''
287
- end
288
-
289
- attribute = @params[:attr] || @params[:tattr] || @params[:date]
290
-
291
- if (@params[:edit_preview] || @params[:ep]) == 'true'
292
- @html_tag_params[:id] = "#{attribute}#{erb_node_id}"
293
- @html_tag ||= 'span'
294
- end
295
-
296
- if @params[:edit] == 'true' && !['url','path'].include?(attribute)
297
- "<% 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 -%>"
298
- else
299
- "#{actions}<%= #{attribute_method} %>"
300
- end
301
- end
302
-
303
- def r_zazen
304
- attribute = @params[:attr] || @params[:tattr]
305
- limit = @params[:limit] ? ", :limit=>#{@params[:limit].to_i}" : ""
306
- if @context[:trans]
307
- # TODO: what do we do here with dates ?
308
- return "#{node_attribute(attribute)}"
309
- elsif @params[:tattr]
310
- return "<%= zazen(_(#{node_attribute(attribute)})#{limit}, :node=>#{node(Node)}) %>"
311
- elsif @params[:attr]
312
- if output_format == 'html'
313
- res = "<%= zazen(#{node_attribute(attribute)}#{limit}, :node=>#{node(Node)}) %>"
314
- else
315
- return "<%= zazen(#{node_attribute(attribute)}#{limit}, :node=>#{node(Node)}, :output=>#{output_format.inspect}) %>"
316
- end
317
- elsif @params[:date]
318
- # date can be any attribute v_created_at or updated_at etc.
319
- # TODO format with @params[:format] and @params[:tformat] << translated format
320
- else
321
- # error
322
- end
323
-
324
- @html_tag ||= 'div'
325
-
326
- add_html_class('zazen')
327
-
328
- if (@params[:edit_preview] || @params[:ep]) == 'true'
329
- @html_tag_params[:id] = "#{attribute}#{erb_node_id}"
330
- end
331
-
332
- if @params[:edit] == 'true' && !['url','path'].include?(attribute)
333
- edit_text = _('edit')
334
- @html_tag_params[:id] = erb_dom_id("_#{attribute}")
335
- 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}"
336
- else
337
- res
338
- end
339
- end
340
-
341
- # TODO: test, rename ?
342
- def r_search_results
343
- do_list("@nodes")
344
- end
345
-
346
-
347
- def r_set
348
- return parser_error("'var' missing") unless var_name = @params[:var]
349
- return parser_error("bad value for 'var' (#{var_name.inspect})") unless var_name =~ /^[a-zA-Z_]+$/
350
- return '' unless @context[:set]
351
- if @params[:value]
352
- out "<% set_#{var_name} = #{@params[:value].inspect} -%>"
353
- # TODO: isn't @context[:vars] = @params[:value].inspect missing here ?
354
- elsif @params[:eval]
355
- return unless eval_string = parse_eval_parameter(@params[:eval])
356
- out "<% set_#{var_name} = #{eval_string} -%>"
357
- else
358
- out "<% set_#{var_name} = capture do %>"
359
- out expand_with(:set => false) # do not propagate
360
- out "<% end -%>"
361
- end
362
- end
363
-
364
-
365
- # TODO: write a test (please)
366
- # 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.
367
- # Define a block of elements to be used by ajax calls (edit/filter)
368
- def r_block
369
- if @context[:block] == self
370
- # called from self (storing template)
371
- @context.reject! do |k,v|
372
- # FIXME: reject all stored elements in a better way then this
373
- k.kind_of?(String) && k =~ /\ANode_\w/
374
- end
375
- @html_tag_done = false
376
- @html_tag_params.merge!(:id=>erb_dom_id)
377
- @context[:scope_node] = node if @context[:scope_node]
378
- out expand_with(:node => node)
379
- if @method == 'drop' && !@context[:make_form]
380
- out drop_javascript
381
- end
382
- else
383
- if parent.method == 'each' && @method == parent.single_child_method
384
- # use parent as block
385
- # FIXME: will not work with block as distant target...
386
- # do nothing
387
- else
388
- @html_tag ||= 'div'
389
- new_dom_scope
390
-
391
- unless @context[:make_form]
392
- # STORE TEMPLATE ========
393
-
394
- context_bak = @context.dup # avoid side effects when rendering the same block
395
- ignore_list = @method == 'block' ? ['form'] : [] # do not show the form in the normal template of a block
396
- template = expand_block(self, :block=>self, :list=>false, :saved_template=>true, :ignore => ignore_list)
397
- @context = context_bak
398
- @result = ''
399
- out helper.save_erb_to_url(template, template_url)
400
-
401
- # STORE FORM ============
402
- if edit = descendant('edit')
403
- publish_after_save = (edit.params[:publish] == 'true')
404
- if form = descendant('form')
405
- # USE BLOCK FORM ========
406
- form_text = expand_block(form, :saved_template=>true, :publish_after_save => publish_after_save)
407
- else
408
- # MAKE A FORM FROM BLOCK ========
409
- form = self.dup
410
- form.method = 'form'
411
- form_text = expand_block(form, :make_form => true, :list => false, :saved_template => true, :publish_after_save => publish_after_save)
412
- end
413
- out helper.save_erb_to_url(form_text, form_url)
414
- end
415
- end
416
-
417
- # RENDER
418
- @html_tag_done = false
419
- @html_tag_params.merge!(:id=>erb_dom_id)
420
- end
421
-
422
- out expand_with
423
- if @method == 'drop' && !@context[:make_form]
424
- out drop_javascript
425
- end
426
- end
427
- end
428
-
429
- # TODO: test
430
- def r_filter
431
- if upd = @params[:update]
432
- return unless block = find_target(upd)
433
- else
434
- return parser_error("missing 'block' in same parent") unless parent && block = parent.descendant('block')
435
- end
436
- return parser_error("cannot use 's' as key (used by start_node)") if @params[:key] == 's'
437
- 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'>"
438
- if @blocks == []
439
- out "<input type='text' name='#{@params[:key] || 'f'}' value='<%= params[#{(@params[:key] || 'f').to_sym.inspect}] %>'/>"
440
- else
441
- out expand_with(:in_filter => true)
442
- end
443
- out "</div></form>"
444
- if @params[:live] || @params[:update]
445
- out "<%= observe_form( \"#{dom_id}_f\" , :method => :get, :frequency => 1, :submit =>\"#{dom_id}_f\", :url => zafu_node_path(#{node_id})) %>"
446
- end
447
- end
448
-
449
- # swap an attribute
450
- # TODO: test
451
- def r_swap
452
- if upd = @params[:update]
453
- if upd == '_page'
454
- block = nil
455
- elsif block = find_target(upd)
456
- # ok
457
- if ancestor('block') || ancestor('each')
458
- upd_both = '&upd_both=true'
459
- else
460
- upd_both = ''
461
- end
462
- else
463
- return
464
- end
465
- elsif ancestor('block') || ancestor('each')
466
- # ancestor: ok
467
- block = self
468
- elsif parent && block = parent.descendant('block')
469
- # sibling: ok
470
- upd_both = ''
471
- else
472
- return parser_error("missing 'block' in same parent")
473
- end
474
-
475
- states = ((@params[:states] || 'todo, done') + ' ').split(',').map(&:strip)
476
-
477
- query_params = "node[#{@params[:attr]}]=\#{#{states.inspect}[ ((#{states.inspect}.index(#{node_attribute(@params[:attr])}.to_s) || 0)+1) % #{states.size}]}#{upd_both}"
478
- out link_to_update(block, :query_params => query_params, :method => :put, :html_params => get_html_params(@params, :link))
479
- end
480
-
481
- def r_load
482
- if dict = @params[:dictionary]
483
- dict_content, absolute_url, doc = self.class.get_template_text(dict, @options[:helper], @options[:current_folder])
484
- return parser_error("dictionary #{dict.inspect} not found") unless doc
485
- @context[:dict] ||= {}
486
- begin
487
- definitions = YAML::load(dict_content)
488
- definitions['translations'].each do |elem|
489
- @context[:dict][elem[0]] = elem[1]
490
- end
491
- rescue
492
- return parser_error("invalid dictionary content #{dict.inspect}")
493
- end
494
- else
495
- return parser_error("missing 'dictionary'")
496
- end
497
- expand_with
498
- end
499
-
500
- def r_trans
501
- static = true
502
- if @params[:text]
503
- text = @params[:text]
504
- elsif @params[:attr]
505
- text = "#{node_attribute(@params[:attr])}"
506
- static = false
507
- else
508
- res = []
509
- text = ""
510
- @blocks.each do |b|
511
- if b.kind_of?(String)
512
- res << b.inspect
513
- text << b
514
- elsif ['show', 'current_date'].include?(b.method)
515
- res << expand_block(b, :trans=>true)
516
- static = false
517
- else
518
- # ignore
519
- end
520
- end
521
- unless static
522
- text = res.join(' + ')
523
- end
524
- end
525
- if static
526
- _(text)
527
- else
528
- "<%= _(#{text}) %>"
529
- end
530
- end
531
-
532
- alias r_t r_trans
533
-
534
- def r_anchor(obj=node)
535
- "<a name='#{anchor_name(@anchor_param, obj)}'></a>"
536
- end
537
-
538
- def anchor_name(p, obj=node)
539
- if p =~ /\[(.+)\]/
540
- "<%= #{node_attribute($1)} %>"
541
- else
542
- "#{base_class.to_s.underscore}#{erb_node_id(obj)}"
543
- end
544
- end
545
-
546
- def r_content_for_layout
547
- "<% if content_for_layout = yield -%><%= content_for_layout %><% else -%>" +
548
- expand_with +
549
- "<% end -%>"
550
- end
551
-
552
- def r_title_for_layout
553
- "<% if @title_for_layout -%><%= @title_for_layout %><% elsif @node && !@node.new_record? -%><%= @node.rootpath %><% elsif @node.parent -%><%= @node.parent.rootpath %><% else -%>" +
554
- expand_with +
555
- "<% end -%>"
556
- end
557
-
558
- def r_check_lang
559
- text = @params[:text] || expand_with
560
- klass = @params[:class] || @html_tag_params[:class]
561
- text = nil if text.blank?
562
- klas = nil if klass.blank?
563
- @html_tag_done = true
564
- "#{@space_before}<%= check_lang(#{node},:text=>#{text.inspect},:class=>#{klass.inspect},:wrap=>#{@html_tag.inspect}) %>"
565
- end
566
-
567
- def r_title
568
- if node_kind_of?(Version)
569
- node = "#{self.node}.node"
570
- elsif node_kind_of?(Node)
571
- node = self.node
572
- else
573
- return parser_error('title','only works with nodes')
574
- end
575
- title_params = {}
576
-
577
- title_params[:check_lang] = @params[:check_lang] if @params.include?(:check_lang)
578
-
579
- if @params[:link]
580
- value, static = parse_attributes_in_value(@params[:link], :erb => false)
581
- link_param = ", :link=>\"#{value}\""
582
- else
583
- link_param = ''
584
- end
585
-
586
- res = "<%= show_title(:node=>#{node}#{link_param}#{params_to_erb(title_params)}"
587
- if @params[:text]
588
- res << ", :text=>#{@params[:text].inspect}"
589
- elsif @params[:attr]
590
- res << ", :text=>#{node_attribute(@params[:attr])}"
591
- end
592
-
593
- if @params.include?(:project)
594
- res << ", :project=>#{@params[:project] == 'true'}"
595
- end
596
- res << ")"
597
- if @params[:actions]
598
- res << " + node_actions(:node=>#{node}#{params_to_erb(:actions=>@params[:actions], :publish_after_save=>auto_publish_param)})"
599
- end
600
- res << "%>"
601
- if @params[:status] == 'true' || (@params[:status].nil? && @params[:actions])
602
- @html_tag ||= 'span'
603
- add_html_class("s<%= #{node}.version.status %>")
604
- end
605
- res
606
- end
607
-
608
- # TODO: test
609
- def r_actions
610
- out expand_with
611
- out "<%= node_actions(:node=>#{node}#{params_to_erb(:actions=>@params[:select], :publish_after_save=>auto_publish_param)}) %>"
612
- end
613
-
614
- # TODO: test
615
- def r_admin_links
616
- "<%= show_link(:admin_links).join('</#{@html_tag}><#{@html_tag}>') %>"
617
- end
618
-
619
- def r_text
620
- text = @params[:text] ? @params[:text].inspect : "#{node_attribute('v_text')}"
621
- limit = @params[:limit] ? ", :limit=>#{@params[:limit].to_i}" : ""
622
-
623
- @html_tag ||= 'div'
624
-
625
- if @html_tag_params[:id]
626
- # add a sub-div
627
- pre = "<div id='v_text#{erb_node_id}'>"
628
- post = "</div>"
629
- else
630
- pre = post = ''
631
- @html_tag_params[:id] = "v_text#{erb_node_id}"
632
- end
633
-
634
- add_html_class('zazen')
635
-
636
- unless @params[:empty] == 'true'
637
- out "#{pre}<% if #{node}.kind_of?(TextDocument); l = #{node}.content_lang -%>"
638
- out "<%= zazen(\"<code\#{l ? \" lang='\#{l}'\" : ''} class=\\'full\\'>\#{#{text}}</code>\") %>"
639
- out "<% else -%>"
640
- out "<%= zazen(#{text}#{limit}, :node=>#{node(Node)}) %>"
641
- out "<% end -%>#{post}"
642
- else
643
- out "#{pre}#{post}"
644
- end
645
- end
646
-
647
- def r_inspect
648
- out ["params: #{@params.inspect}",
649
- "name: #{@context[:name]}",
650
- "node: #{node}",
651
- "list: #{list}"].join("<br/>")
652
- end
653
-
654
- # TODO: replace with a more general 'zazen' or 'show' with id ?
655
- def r_summary
656
- limit = @params[:limit] ? ", :limit=>#{@params[:limit].to_i}" : ""
657
- @html_tag ||= 'div'
658
- if @html_tag_params[:id]
659
- # add a sub-div
660
- pre = "<div id='v_summary#{erb_node_id}'>"
661
- post = "</div>"
662
- else
663
- pre = post = ''
664
- @html_tag_params[:id] = "v_summary#{erb_node_id}"
665
- end
666
-
667
- add_html_class('zazen')
668
-
669
- unless @params[:or]
670
- text = @params[:text] ? @params[:text].inspect : node_attribute('v_summary')
671
- out "#{pre}<%= zazen(#{text}#{limit}, :node=>#{node(Node)}) %>#{post}"
672
- else
673
- limit ||= ', :limit => 2'
674
- first_name = 'v_summary'
675
- first = node_attribute(first_name)
676
-
677
- second_name = @params[:or].gsub(/[^a-z_]/,'') # FIXME: ist this still needed ? (ERB injection)
678
- second = node_attribute(second_name)
679
- out "#{pre}<% if #{first} != '' %>"
680
- out "<%= zazen(#{first}, :node=>#{node(Node)}) %>"
681
- out "<% else %>"
682
- out "<%= zazen(#{second}#{limit}, :node=>#{node(Node)}) %>"
683
- out "<% end %>#{post}"
684
- end
685
- end
686
-
687
- def r_show_author
688
- if @params[:size] == 'large'
689
- out "#{_("posted by")} <b><%= #{node}.author.fullname %></b>"
690
- out "<% if #{node}[:user_id] != #{node}.version[:user_id] -%>"
691
- out "<% if #{node}[:ref_lang] != #{node}.version[:lang] -%>"
692
- out "#{_("traduction by")} <b><%= #{node}.version.author.fullname %></b>"
693
- out "<% else -%>"
694
- out "#{_("modified by")} <b><%= #{node}.version.author.fullname %></b>"
695
- out "<% end"
696
- out " end -%>"
697
- out " #{_("on")} <%= format_date(#{node}.version.updated_at, #{_('short_date').inspect}) %>."
698
- if @params[:traductions] == 'true'
699
- out " #{_("Traductions")} : <span class='traductions'><%= helper.traductions(:node=>#{node}).join(', ') %></span>"
700
- end
701
- else
702
- out "<b><%= #{node}.version.author.initials %></b> - <%= format_date(#{node}.version.updated_at, #{_('short_date').inspect}) %>"
703
- if @params[:traductions] == 'true'
704
- out " <span class='traductions'>(<%= helper.traductions(:node=>#{node}).join(', ') %>)</span>"
705
- end
706
- end
707
- end
708
-
709
- def r_comments_to_publish
710
- open_context("visitor.comments_to_publish", :node_class => [Comment])
711
- end
712
-
713
- def r_to_publish
714
- open_context("visitor.to_publish", :node_class => [Version])
715
- end
716
-
717
- def r_proposed
718
- open_context("visitor.proposed", :node_class => [Version])
719
- end
720
-
721
- def r_redactions
722
- open_context("visitor.redactions", :node_class => [Version])
723
- end
724
-
725
- def r_edit
726
-
727
- if @context[:dom_prefix]
728
- # ajax
729
- if @context[:in_form]
730
- # cancel button
731
- @context[:form_cancel] || ''
732
- else
733
- # edit button
734
-
735
- # TODO: show 'reply' instead of 'edit' in comments if visitor != author
736
- 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)
737
- end
738
- else
739
- # FIXME: we could link to some html page to edit the item.
740
- ""
741
- end
742
- end
743
-
744
- alias r_cancel r_edit
745
-
746
- def r_textarea
747
- out make_textarea(@html_tag_params.merge(@params))
748
- @html_tag_done = true
749
- end
750
-
751
-
752
- # <r:select name='klass' root_class='...'/>
753
- # <r:select name='parent_id' values='projects in site'/>
754
- # TODO: optimization (avoid loading full AR to only use [id, name])
755
- def r_select
756
- html_attributes, attribute = get_input_params()
757
- return parser_error("missing name") unless attribute
758
- if value = @params[:selected]
759
- # FIXME: DRY with html_attributes
760
- value = value.gsub(/\[([^\]]+)\]/) do
761
- node_attr = $1
762
- res = node_attribute(node_attr)
763
- "\#{#{res}}"
764
- end
765
- selected = value.inspect
766
- elsif @context[:in_filter]
767
- selected = "params[#{attribute.to_sym.inspect}].to_s"
768
- else
769
- selected = "#{node_attribute(attribute)}.to_s"
770
- end
771
- html_id = html_attributes[:id] ? " id='#{html_attributes[:id]}'" : ''
772
- if @context[:in_filter]
773
- select_tag = "<select#{html_id} name='#{attribute}'>"
774
- else
775
- select_tag = "<select#{html_id} name='#{base_class.to_s.underscore}[#{attribute}]'>"
776
- end
777
-
778
- if klass = @params[:root_class]
779
- class_opts = {}
780
- class_opts[:without] = @params[:without] if @params[:without]
781
- # do not use 'selected' if the node is not new
782
- "#{select_tag}<%= options_for_select(Node.classes_for_form(:class => #{klass.inspect}#{params_to_erb(class_opts)}), (#{node}.new_record? ? #{selected} : #{node}.klass)) %></select>"
783
- elsif @params[:type] == 'time_zone'
784
- # <r:select name='d_tz' type='time_zone'/>
785
- "#{select_tag}<%= options_for_select(TZInfo::Timezone.all_identifiers, #{selected}) %></select>"
786
- elsif options_list = get_options_for_select
787
- "#{select_tag}<%= options_for_select(#{options_list}, #{selected}) %></select>"
788
- else
789
- parser_error("missing 'nodes', 'root_class' or 'values'")
790
- end
791
- end
792
-
793
-
794
- def r_input
795
- html_attributes, attribute = get_input_params()
796
- case @params[:type]
797
- when 'select' # FIXME: why is this only for classes ?
798
- out parser_error("please use [select] here")
799
- r_select
800
- when 'date_box', 'date'
801
- return parser_error("date_box without name") unless attribute
802
- input_id = @context[:dom_prefix] ? ", :id=>\"#{dom_id}_#{attribute}\"" : ''
803
- "<%= date_box '#{base_class.to_s.underscore}', #{attribute.inspect}, :size=>15#{@context[:in_add] ? ", :value=>''" : ''}#{input_id} %>"
804
- when 'id'
805
- return parser_error("select id without name") unless attribute
806
- name = "#{attribute}_id" unless attribute[-3..-1] == '_id'
807
- input_id = @context[:erb_dom_id] ? ", :input_id =>\"#{erb_dom_id}_#{attribute}\"" : ''
808
- "<%= select_id('#{base_class.to_s.underscore}', #{attribute.inspect}#{input_id}) %>"
809
- when 'time_zone'
810
- out parser_error("please use [select] here")
811
- r_select
812
- when 'submit'
813
- @html_tag = 'input'
814
- @html_tag_params[:type] = @params[:type]
815
- @html_tag_params[:text] = @params[:text] if @params[:text]
816
- @html_tag_params.merge!(html_attributes)
817
- render_html_tag(nil)
818
- else
819
- # 'text', 'hidden', ...
820
- @html_tag = 'input'
821
- @html_tag_params[:type] = @params[:type] || 'text'
822
- if checked = html_attributes.delete(:checked)
823
- @html_tag_params.merge!(html_attributes)
824
- render_html_tag(nil, checked)
825
- else
826
- @html_tag_params.merge!(html_attributes)
827
- render_html_tag(nil)
828
- end
829
- end
830
- end
831
-
832
- def r_form_tag
833
- # replace <form> with constructed form
834
- "#{@context[:form_tag]}#{expand_with(:form_tag => nil)}</form>"
835
- end
836
-
837
- # TODO: add parent_id into the form !
838
- # TODO: add <div style="margin:0;padding:0"><input name="_method" type="hidden" value="put" /></div> if method == put
839
- # FIXME: use <r:form href='self'> or <r:form action='...'>
840
- def r_form
841
- hidden_fields = {}
842
- set_fields = []
843
- id_hash = {:class => @html_tag_params[:class] || @params[:class] || 'form'}
844
- var_name = base_class.to_s.underscore
845
- (descendants('input') + descendants('select')).each do |tag|
846
- set_fields << "#{var_name}[#{tag.params[:name]}]"
847
- end
848
-
849
- if @context[:dom_prefix] || @params[:update]
850
- # ajax
851
- if @context[:in_add]
852
- # inline form used to create new elements: set values to '' and 'parent_id' from context
853
- id_hash[:id] = "#{erb_dom_id}_form"
854
- id_hash[:style] = "display:none;"
855
-
856
- 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"
857
- form = "<%= form_remote_tag(:url => #{base_class.to_s.underscore.pluralize}_path, :html => {:id => \"#{dom_id}_form_t\"}) %>\n"
858
- else
859
- # saved form
860
-
861
- id_hash[:id] = erb_dom_id
862
-
863
- cancel = !@context[:dom_prefix] ? "" : <<-END_TXT
864
- <% if #{node}.new_record? -%>
865
- <p class='btn_x'><a href='#' onclick='[\"<%= params[:dom_id] %>_add\", \"<%= params[:dom_id] %>_form\"].each(Element.toggle);return false;'>#{_('btn_x')}</a></p>
866
- <% else -%>
867
- <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>
868
- <% end -%>
869
- END_TXT
870
- form =<<-END_TXT
871
- <% if #{node}.new_record? -%>
872
- <%= form_remote_tag(:url => #{base_class.to_s.underscore.pluralize}_path, :html => {:id => \"\#{params[:dom_id]}_form_t\"}) %>
873
- <% else -%>
874
- <%= form_remote_tag(:url => #{base_class.to_s.underscore}_path(#{node_id}), :method => :put, :html => {:id => \"#{dom_id}_form_t\"}) %>
875
- <% end -%>
876
- END_TXT
877
- end
878
-
879
- if (descendants('input') || []).select{|elem| elem.params[:type] == 'submit'} != []
880
- # has submit
881
- else
882
- hidden_submit = "<input type='submit'/>" # hidden submit for Firefox compatibility
883
- end
884
-
885
- hidden_fields['link_id'] = "<%= #{node}.link_id %>" if @context[:need_link_id]
886
-
887
- if @params[:update] || (@context[:add] && @context[:add].params[:update])
888
- upd = @params[:update] || @context[:add].params[:update]
889
- if target = find_target(upd)
890
- hidden_fields['u_url'] = target.template_url
891
- hidden_fields['udom_id'] = target.erb_dom_id
892
- hidden_fields['u_id'] = "<%= #{@context[:parent_node]}.zip %>" if @context[:in_add]
893
- hidden_fields['s'] = start_node_s_param(:value)
894
- end
895
- elsif (block = ancestor('block')) && node_kind_of?(DataEntry)
896
- # updates template url
897
- hidden_fields['u_url'] = block.template_url
898
- hidden_fields['udom_id'] = block.erb_dom_id
899
- end
900
-
901
- hidden_fields['t_url'] = template_url
902
- if t_id = @params[:t_id]
903
- hidden_fields['t_id'] = parse_attributes_in_value(t_id)
904
- end
905
-
906
- erb_dom_id = @context[:saved_template] ? '<%= params[:dom_id] %>' : self.erb_dom_id
907
-
908
- hidden_fields['dom_id'] = erb_dom_id
909
-
910
- if node_kind_of?(Node)
911
- hidden_fields['node[parent_id]'] = "<%= #{@context[:in_add] ? "#{@context[:parent_node]}.zip" : "#{node}.parent_zip"} %>"
912
- elsif node_kind_of?(Comment)
913
- # FIXME: the "... || '@node'" is a hack and I don't understand why it's needed...
914
- hidden_fields['node_id'] = "<%= #{@context[:parent_node] || '@node'}.zip %>"
915
- elsif node_kind_of?(DataEntry)
916
- hidden_fields["data_entry[#{@context[:data_root]}_id]"] = "<%= #{@context[:in_add] ? @context[:parent_node] : "#{node}.#{@context[:data_root]}"}.zip %>"
917
- end
918
-
919
- if add_block = @context[:add]
920
- params = add_block.params
921
- [:after, :before, :top, :bottom].each do |sym|
922
- if params[sym]
923
- hidden_fields['position'] = sym.to_s
924
- if params[sym] == 'self'
925
- if sym == :before
926
- hidden_fields['reference'] = "#{erb_dom_id}_add"
927
- else
928
- hidden_fields['reference'] = "#{erb_dom_id}_form"
929
- end
930
- else
931
- hidden_fields['reference'] = params[sym]
932
- end
933
- break
934
- end
935
- end
936
- if params[:done] == 'focus'
937
- if params[:focus]
938
- hidden_fields['done'] = "'$(\"#{erb_dom_id}_#{@params[:focus]}\").focus();'"
939
- else
940
- hidden_fields['done'] = "'$(\"#{erb_dom_id}_form_t\").focusFirstElement();'"
941
- end
942
- elsif params[:done]
943
- hidden_fields['done'] = CGI.escape(params[:done]) # .gsub("NODE_ID", @node.zip).gsub("PARENT_ID", @node.parent_zip)
944
- end
945
- else
946
- # ajax form, not in 'add'
947
- hidden_fields['done'] = CGI.escape(@params[:done]) if @params[:done]
948
- end
949
- else
950
- # no ajax
951
- # FIXME
952
- cancel = "" # link to normal node ?
953
- form = "<form method='post' action='/nodes/#{erb_node_id}'><div style='margin:0;padding:0'><input name='_method' type='hidden' value='put' /></div>"
954
- end
955
-
956
- if node_kind_of?(Node) && (@params[:klass] || @context[:klass])
957
- hidden_fields['node[klass]'] = @params[:klass] || @context[:klass]
958
- end
959
-
960
- if node_kind_of?(Node) && @params[:mode]
961
- hidden_fields['mode'] = @params[:mode]
962
- end
963
-
964
- hidden_fields['node[v_status]'] = Zena::Status[:pub] if @context[:publish_after_save] || auto_publish_param
965
-
966
- form << "<div class='hidden'>"
967
- hidden_fields.each do |k,v|
968
- next if set_fields.include?(k)
969
- v = "'#{v}'" unless v.kind_of?(String) && ['"', "'"].include?(v[0..0])
970
- form << "<input type='hidden' name='#{k}' value=#{v}/>\n"
971
- end
972
- form << hidden_submit << "\n" if hidden_submit
973
- form << "</div>"
974
-
975
- form << "<%= error_messages_for(#{node}) %>"
976
-
977
- if !descendant('cancel') && !descendant('edit')
978
- if !descendant('form_tag')
979
- # add a descendant before blocks.
980
- blocks_bak = @blocks
981
- @blocks = @blocks.dup
982
- @blocks = [make(:void, :method=>'void', :text=>cancel)] + blocks_bak
983
- else
984
- form = cancel + form
985
- cancel = ''
986
- end
987
- end
988
-
989
- if descendant('form_tag')
990
- res = expand_with(:form_tag => form, :in_form => true, :form_cancel => cancel, :erb_dom_id => erb_dom_id, :dom_id => dom_id)
991
- else
992
- res = form + expand_with(:in_form => true, :form_cancel => cancel, :erb_dom_id => erb_dom_id, :dom_id => dom_id) + '</form>'
993
- end
994
-
995
- @blocks = blocks_bak if blocks_bak
996
-
997
- @html_tag_done = false
998
- @html_tag_params.merge!(id_hash)
999
- out render_html_tag(res)
1000
- end
1001
-
1002
- # <r:checkbox role='collaborator_for' values='projects' in='site'/>"
1003
- # TODO: implement checkbox in the same spirit as 'r_select'
1004
- def r_checkbox
1005
- return parser_error("missing 'nodes'") unless values = @params[:values] || @params[:nodes]
1006
- return parser_error("missing 'role'") unless role = (@params[:role] || @params[:name])
1007
- attribute = @params[:attr] || 'name'
1008
- if role =~ /(.*)_ids?\Z/
1009
- role = $1
1010
- end
1011
- meth = role.singularize
1012
-
1013
- if values =~ /^\d+\s*($|,)/
1014
- # ids
1015
- # TODO generate the full query instead of using secure.
1016
- values = values.split(',').map{|v| v.to_i}
1017
- list_finder = "(secure(Node) { Node.find(:all, :conditions => 'zip IN (#{values.join(',')})') })"
1018
- else
1019
- # relation
1020
- list_finder, klass = build_finder_for(:all, values)
1021
- return unless list_finder
1022
- return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1023
- end
1024
- out "<% if (#{list_var} = #{list_finder}) && (#{list_var}_relation = #{node}.relation_proxy(#{role.inspect})) -%>"
1025
- out "<% if #{list_var}_relation.unique? -%>"
1026
-
1027
- out "<% #{list_var}_id = #{list_var}_relation.other_id -%>"
1028
- out "<div class='input_radio'><% #{list_var}.each do |#{var}| -%>"
1029
- 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> "
1030
- out "<% end -%></div>"
1031
- out "<input type='radio' name='node[#{meth}_id]' value=''/> #{_('none')}"
1032
-
1033
- out "<% else -%>"
1034
-
1035
- out "<% #{list_var}_ids = #{list_var}_relation.other_ids -%>"
1036
- out "<div class='input_checkbox'><% #{list_var}.each do |#{var}| -%>"
1037
- 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> "
1038
- out "<% end -%></div>"
1039
- out "<input type='hidden' name='node[#{meth}_ids][]' value=''/>"
1040
-
1041
- out "<% end -%><% end -%>"
1042
- end
1043
-
1044
- alias r_radio r_checkbox
1045
-
1046
- # TODO: test
1047
- def r_add
1048
- return parser_error("should not be called from within 'each'") if parent.method == 'each'
1049
- return '' if @context[:make_form]
1050
-
1051
- # why is node = @node (which we need) but we are supposed to have Comments ?
1052
- # FIXME: during rewrite, replace 'node' by 'node(klass = node_class)' so the ugly lines below would be
1053
- # if node_kind_of?(Comment)
1054
- # out "<% if #{node(Node)}.can_comment? -%>"
1055
- # Refs #198.
1056
- if node_kind_of?(Comment)
1057
- out "<% if #{node}.can_comment? -%>"
1058
- else
1059
- out "<% if #{node}.can_write? -%>"
1060
- end
1061
-
1062
- unless descendant('add_btn')
1063
- # add a descendant between self and blocks.
1064
- blocks = @blocks.dup
1065
- @blocks = []
1066
- add_btn = make(:void, :method=>'add_btn', :params=>@params.dup, :text=>'')
1067
- add_btn.blocks = blocks
1068
- remove_instance_variable(:@all_descendants)
1069
- end
1070
-
1071
- if @context[:form] && @context[:dom_prefix]
1072
- # ajax add
1073
-
1074
- @html_tag_params.merge!(:id => "#{erb_dom_id}_add")
1075
- @html_tag_params[:class] ||= 'btn_add'
1076
- if @params[:focus]
1077
- focus = "$(\"#{erb_dom_id}_#{@params[:focus]}\").focus();"
1078
- else
1079
- focus = "$(\"#{erb_dom_id}_form_t\").focusFirstElement();"
1080
- end
1081
-
1082
- out render_html_tag("#{expand_with(:onclick=>"[\"#{erb_dom_id}_add\", \"#{erb_dom_id}_form\"].each(Element.toggle);#{focus}return false;")}")
1083
-
1084
- if node_kind_of?(Node)
1085
- # FIXME: BUG if we set <r:form klass='Post'/> the user cannot select class with menu...
1086
- klass = @context[:klass] || 'Node'
1087
- # FIXME: inspect '@context[:form]' to see if it contains v_klass ?
1088
- out "<% if #{var}_new = secure(Node) { Node.new_from_class(#{klass.inspect}) } -%>"
1089
- else
1090
- out "<% if #{var}_new = #{node_class}.new -%>"
1091
- end
1092
-
1093
- if @context[:form].method == 'form'
1094
- 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)
1095
- else
1096
- # build form from 'each'
1097
- 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)
1098
- end
1099
- out "<% end -%>"
1100
- else
1101
- # no ajax
1102
- @html_tag_params[:class] ||= 'btn_add' if @html_tag
1103
- out render_html_tag(expand_with)
1104
- end
1105
- out "<% end -%>"
1106
- @html_tag_done = true
1107
- end
1108
-
1109
- def r_add_btn
1110
- if @params[:text]
1111
- text = @params[:text]
1112
- text = "<div>#{text}</div>" unless @html_tag
1113
- elsif @params[:trans]
1114
- text = _(@params[:trans])
1115
- text = "<div>#{text}</div>" unless @html_tag
1116
- elsif @blocks != []
1117
- text = expand_with
1118
- else
1119
- text = node_class == Comment ? _("btn_add_comment") : _("btn_add")
1120
- end
1121
-
1122
- out "<a href='#' onclick='#{@context[:onclick]}'>#{text}</a>"
1123
- end
1124
-
1125
- # Show html to add open a popup window to add a document.
1126
- # TODO: inline ajax for upload ?
1127
- def r_add_document
1128
- return parser_error("only works with nodes (not with #{node_class})") unless node_kind_of?(Node)
1129
- @html_tag_params[:class] ||= 'btn_add'
1130
- 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>"
1131
- "<% if #{node}.can_write? -%>#{render_html_tag(res)}<% end -%>"
1132
- end
1133
-
1134
- #if RAILS_ENV == 'test'
1135
- # def r_test
1136
- # inspect
1137
- # end
1138
- #end
1139
-
1140
- def r_drop
1141
- if parent.method == 'each' && @method == parent.single_child_method
1142
- parent.add_html_class('drop')
1143
- else
1144
- @html_tag_params[:class] ||= 'drop'
1145
- end
1146
- r_block
1147
- end
1148
-
1149
- def drop_javascript
1150
- hover = @params[:hover]
1151
- change = @params[:change]
1152
-
1153
- if role = @params[:set] || @params[:add]
1154
- query_params = ["node[#{role}_id]=[id]"]
1155
- else
1156
- query_params = []
1157
- # set='icon_for=[id], v_status='50', v_title='[v_title]'
1158
- @params.each do |k, v|
1159
- next if [:hover, :change, :done].include?(k)
1160
- value, static = parse_attributes_in_value(v, :erb => false, :skip_node_attributes => true)
1161
- key = change == 'params' ? "params[#{k}]" : "node[#{k}]"
1162
- query_params << "#{key}=#{CGI.escape(value)}"
1163
- end
1164
- return parser_error("missing parameters to set values") if query_params == []
1165
- end
1166
-
1167
- query_params << "change=#{change}" if change == 'receiver'
1168
- query_params << "t_url=#{CGI.escape(template_url)}"
1169
- query_params << "dom_id=#{erb_dom_id}"
1170
- query_params << start_node_s_param(:erb)
1171
- query_params << "done=#{CGI.escape(@params[:done])}" if @params[:done]
1172
-
1173
- "<script type='text/javascript'>
1174
- //<![CDATA[
1175
- 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)})}})
1176
- //]]>
1177
- </script>"
1178
- end
1179
-
1180
- def r_draggable
1181
- new_dom_scope
1182
- @html_tag ||= 'div'
1183
- case @params[:revert]
1184
- when 'move'
1185
- revert_effect = 'Element.move'
1186
- when 'remove'
1187
- revert_effect = 'Element.remove'
1188
- else
1189
- revert_effect = 'Element.move'
1190
- end
1191
-
1192
- res, drag_handle = set_drag_handle_and_id(expand_with, @params, :id => erb_dom_id)
1193
-
1194
- out render_html_tag(res)
1195
-
1196
- if drag_handle
1197
- out "<script type='text/javascript'>\n//<![CDATA[\n
1198
- new Draggable('#{erb_dom_id}', {ghosting:true, revert:true, revertEffect:#{revert_effect}, handle:$('#{erb_dom_id}').select('.#{drag_handle}')[0]});\n//]]>\n</script>"
1199
- else
1200
- out "<script type='text/javascript'>\n//<![CDATA[\nZena.draggable('#{erb_dom_id}',0,true,true,#{revert_effect})\n//]]>\n</script>"
1201
- end
1202
- end
1203
-
1204
- def r_unlink
1205
- return "" if @context[:make_form]
1206
- opts = {}
1207
-
1208
- if upd = @params[:update]
1209
- if upd == '_page'
1210
- target = nil
1211
- elsif target = find_target(upd)
1212
- # ok
1213
- else
1214
- return
1215
- end
1216
- elsif target = ancestor('block')
1217
- # ok
1218
- else
1219
- target = self
1220
- end
1221
-
1222
- if node_kind_of?(Node)
1223
- opts[:cond] = "#{node}.can_write? && #{node}.link_id"
1224
- opts[:url] = "/nodes/\#{#{node_id}}/links/\#{#{node}.link_id}"
1225
- elsif node_kind_of?(Link)
1226
- opts[:url] = "/nodes/\#{#{node}.this_zip}/links/\#{#{node}.zip}"
1227
- end
1228
-
1229
- opts[:method] = :delete
1230
- opts[:default_text] = _('btn_tiny_del')
1231
- opts[:html_params] = get_html_params({:class => 'unlink'}.merge(@params), :link)
1232
-
1233
- out link_to_update(target, opts)
1234
-
1235
- #tag_to_remote
1236
- #"<%= tag_to_remote({:url => node_path(#{node_id}) + \"#{opts[:method] != :put ? '/zafu' : ''}?#{action.join('&')}\", :method => #{opts[:method].inspect}}) %>"
1237
- # 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;\">"
1238
- # if !@blocks.empty?
1239
- # inner = expand_with
1240
- # else
1241
- # inner = _('btn_tiny_del')
1242
- # end
1243
- # out "#{inner}</a><% else -%>#{inner}<% end -%>"
1244
- #elsif node_kind_of?(DataEntry)
1245
- # text = get_text_for_erb
1246
- # if text.blank?
1247
- # text = _('btn_tiny_del')
1248
- # end
1249
- # out "<%= link_to_remote(#{text.inspect}, {:url => \"/data_entries/\#{#{node}[:id]}?dom_id=#{dom_id}#{upd_url}\", :method => :delete}, :class=>#{(@params[:class] || 'unlink').inspect}) %>"
1250
- #end
1251
- end
1252
-
1253
- # Group elements in a list. Use :order to specify order.
1254
- def r_group
1255
- return parser_error("cannot be used outside of a list") unless list_var = @context[:list]
1256
- return parser_error("missing 'by' clause") unless key = @params[:by]
1257
-
1258
- sort_key = @params[:sort] || 'name'
1259
- if node_kind_of?(DataEntry) && DataEntry::NodeLinkSymbols.include?(key.to_sym)
1260
- key = "#{key}_id"
1261
- sort_block = "{|e| (e.#{key} || {})[#{sort_key.to_sym.inspect}]}"
1262
- group_array = "group_array(#{list_var}) {|e| e.#{key}}"
1263
- elsif node_kind_of?(Node)
1264
- if ['project', 'parent', 'section'].include?(key)
1265
- sort_block = "{|e| (e.#{key} || {})[#{sort_key.to_sym.inspect}]}"
1266
- group_array = "group_array(#{list_var}) {|e| e.#{key}_id}"
1267
- end
1268
- end
1269
-
1270
- group_array ||= "group_array(#{list_var}) {|e| #{node_attribute(key, :node => 'e')}}"
1271
-
1272
- if sort_block
1273
- out "<% grp_#{list_var} = sort_array(#{group_array}) #{sort_block} -%>"
1274
- else
1275
- out "<% grp_#{list_var} = #{group_array} -%>"
1276
- end
1277
-
1278
- if descendant('each_group')
1279
- out expand_with(:group => "grp_#{list_var}")
1280
- else
1281
- @context[:group] = "grp_#{list_var}"
1282
- r_each_group
1283
- end
1284
- end
1285
-
1286
-
1287
- # Compute statistics on elements in the current list context.
1288
- def r_stat
1289
- return parser_error("must be used inside a list context") unless list
1290
- find = @params[:find] || @params[:date] || 'count'
1291
- key = @params[:of] || @params[:from] || 'value'
1292
- case find
1293
- when 'sum'
1294
- value = "#{list}.flatten.inject(0) {|#{var}_sum,#{var}| #{var}_sum + #{node_attribute(key, :node => var)}.to_f}"
1295
- when 'min'
1296
- value = "#{node_attribute(key, :node => "min_array(#{list}) {|e| #{node_attribute(key, :node => 'e')}}")}"
1297
- when 'max'
1298
- value = "#{node_attribute(key, :node => "max_array(#{list}) {|e| #{node_attribute(key, :node => 'e')}}")}"
1299
- when 'count'
1300
- return "<%= #{list}.size %>"
1301
- end
1302
- if @params[:date]
1303
- # FIXME: DRY (r_show)
1304
- if @params[:tformat]
1305
- format = _(@params[:tformat])
1306
- elsif @params[:format]
1307
- format = @params[:format]
1308
- else
1309
- format = "%Y-%m-%d"
1310
- end
1311
- "<%= #{list}==[] ? '' : format_date(#{value}, #{format.inspect}) %>"
1312
- elsif format = @params[:format]
1313
-
1314
- if @params[:zero] == 'hide'
1315
- "<%= #{list}==[] ? '' : sprintf_unless_zero(#{@params[:format].inspect}, #{value}) %>"
1316
- else
1317
- "<%= #{list}==[] ? '' : sprintf(#{@params[:format].inspect}, #{value}) %>"
1318
- end
1319
- else
1320
- "<%= #{list}==[] ? '' : #{value} %>"
1321
- end
1322
- end
1323
-
1324
- def r_each_group
1325
- return parser_error("must be used inside a group context") unless group = @context[:group]
1326
- if join = @params[:join]
1327
- join = join.gsub(/&lt;([^%])/, '<\1').gsub(/([^%])&gt;/, '\1>')
1328
- out "<% #{group}.each_index do |#{list_var}_index| -%>"
1329
- out "<%= #{list_var}=#{group}[#{list_var}_index]; #{var} = #{list_var}[0]; #{list_var}_index > 0 ? #{join.inspect} : '' %>"
1330
- else
1331
- out "<% #{group}.each do |#{list_var}|; #{var} = #{list_var}[0]; -%>"
1332
- end
1333
- out render_html_tag(expand_with(:group => nil, :list => list_var, :node => var, :scope_node => var))
1334
- out "<% end -%>"
1335
- end
1336
-
1337
- def r_each
1338
- is_draggable = @params[:draggable] == 'true' || @params[:drag_handle]
1339
-
1340
- if descendant('edit') || descendant('unlink') || descendant('swap') || ['block', 'drop'].include?(single_child_method) || is_draggable
1341
- id_hash = {:id => erb_dom_id}
1342
- else
1343
- id_hash = nil
1344
- end
1345
-
1346
-
1347
- if @context[:make_form]
1348
- # use the elements inside 'each' loop to produce the edit form
1349
- r_form
1350
- elsif @context[:list]
1351
- # normal rendering: not the start of a saved template
1352
- if is_draggable || descendant('unlink')
1353
- out "<% #{var}_dom_ids = [] -%>"
1354
- end
1355
-
1356
- @params[:alt_class] ||= @html_tag_params.delete(:alt_class)
1357
- # FIXME: add alt_reverse='true' to start counting from bottom (if order last on top...)
1358
- if @params[:alt_class] || @params[:join]
1359
- join = @params[:join] || ''
1360
- join = join.gsub(/&lt;([^%])/, '<\1').gsub(/([^%])&gt;/, '\1>')
1361
- out "<% #{var}_max_index = #{list}.size - 1 -%>" if @params[:alt_reverse]
1362
- out "<% #{list}.each_with_index do |#{var},#{var}_index| -%>"
1363
-
1364
- if join_clause = @params[:join_if]
1365
- set_stored(Node, 'prev', "#{var}_prev")
1366
- cond = get_test_condition(var, :test=>join_clause)
1367
- out "<%= #{var}_prev = #{list}[#{var}_index - 1]; (#{var}_index > 0 && #{cond}) ? #{join.inspect} : '' %>"
1368
- else
1369
- out "<%= #{var}_index > 0 ? #{join.inspect} : '' %>"
1370
- end
1371
-
1372
- if alt_class = @params[:alt_class]
1373
- alt_test = @params[:alt_reverse] == 'true' ? "(#{var}_max_index - #{var}_index) % 2 != 0" : "#{var}_index % 2 != 0"
1374
- if html_class = @html_tag_params.delete(:class)
1375
- html_append = " class='#{html_class}<%= #{alt_test} ? #{(' ' + alt_class).inspect} : '' %>'"
1376
- else
1377
- html_append = "<%= #{alt_test} ? ' class=#{alt_class.inspect}' : '' %>"
1378
- end
1379
- else
1380
- html_append = nil
1381
- end
1382
- else
1383
- out "<% #{list}.each do |#{var}| -%>"
1384
- html_append = nil
1385
- end
1386
-
1387
- if is_draggable
1388
- out "<% #{var}_dom_ids << \"#{dom_id}\" -%>"
1389
- end
1390
-
1391
- out r_anchor(var) if @anchor_param # insert anchor inside the each loop
1392
- @params[:anchor] = @anchor_param # set back in case we double render
1393
- @anchor_param = nil
1394
-
1395
- res, drag_handle = set_drag_handle_and_id(expand_with(:node => var, :scope_node => var), @params, id_hash)
1396
-
1397
- out render_html_tag(res, html_append)
1398
-
1399
- out "<% end -%>"
1400
-
1401
- if is_draggable
1402
- if drag_handle
1403
- out "<script type='text/javascript'>\n//<![CDATA[\n<%= #{var}_dom_ids.inspect %>.each(function(dom_id, index) {
1404
- new Draggable(dom_id, {ghosting:true, revert:true, handle:$(dom_id).select('.#{drag_handle}')[0]});
1405
- });\n//]]>\n</script>"
1406
- else
1407
- out "<script type='text/javascript'>\n//<![CDATA[\n<%= #{var}_dom_ids.inspect %>.each(Zena.draggable)\n//]]>\n</script>"
1408
- end
1409
- end
1410
-
1411
- elsif @context[:saved_template]
1412
- # render to start a saved template
1413
- res, drag_handle = set_drag_handle_and_id(expand_with(:scope_node => node), @params, id_hash)
1414
-
1415
- out render_html_tag(res)
1416
-
1417
- if is_draggable
1418
- if drag_handle
1419
- 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>"
1420
- else
1421
- out "<script type='text/javascript'>\n//<![CDATA[\nZena.draggable('#{erb_dom_id}')\n//]]>\n</script>"
1422
- end
1423
- end
1424
- else
1425
- # TODO: make a single list ?
1426
- @context[:list] = "[#{node}]"
1427
- r_each
1428
- end
1429
- end
1430
-
1431
- def r_case
1432
- out "<% if false -%>"
1433
- out expand_with(:in_if=>true, :only=>['when', 'else'], :html_tag => @html_tag, :html_tag_params => @html_tag_params)
1434
- @html_tag_done = true
1435
- out "<% end -%>"
1436
- end
1437
-
1438
- # TODO: test
1439
- def r_if
1440
- cond = get_test_condition
1441
- return parser_error("condition error") unless cond
1442
-
1443
- if cond == 'true'
1444
- return expand_with(:in_if => false)
1445
- elsif cond == 'false'
1446
- if descendant('else') || descendant('elsif')
1447
- return expand_with(:in_if=>true, :only=>['elsif', 'else'])
1448
- else
1449
- @html_tag_done = true
1450
- return ''
1451
- end
1452
- end
1453
-
1454
- out "<% if #{cond} -%>"
1455
- out render_html_tag(expand_with(:in_if=>false))
1456
- out expand_with(:in_if=>true, :only=>['elsif', 'else'], :html_tag => @html_tag, :html_tag_params => @html_tag_params)
1457
- out "<% end -%>"
1458
- end
1459
-
1460
- def r_else
1461
- if @context[:in_if]
1462
- @html_tag = @context[:html_tag]
1463
- @html_tag_params = @context[:html_tag_params] || {}
1464
- out "<% elsif true -%>"
1465
- if @params[:text]
1466
- out render_html_tag(@params[:text])
1467
- else
1468
- out render_html_tag(expand_with(:in_if=>false, :only => nil)) # do not propagate :only from ancestor 'if' clause
1469
- end
1470
- else
1471
- ""
1472
- end
1473
- end
1474
-
1475
- def r_elsif
1476
- return '' unless @context[:in_if]
1477
- @html_tag = @context[:html_tag]
1478
- @html_tag_params = @context[:html_tag_params] || {}
1479
- cond = get_test_condition
1480
- return parser_error("condition error") unless cond
1481
- out "<% elsif #{cond} -%>"
1482
- out render_html_tag(expand_with(:in_if=>false, :only => nil)) # do not propagate :only from ancestor 'if' clause
1483
- end
1484
-
1485
- def r_when
1486
- r_elsif
1487
- end
1488
-
1489
- # be carefull, this gives a list of 'versions', not 'nodes'
1490
- def r_traductions
1491
- if @params[:except]
1492
- case @params[:except]
1493
- when 'current'
1494
- opts = "(:conditions=>\"lang != '#{helper.lang}'\")"
1495
- else
1496
- # list of lang
1497
- # TODO: test
1498
- langs = @params[:except].split(',').map{|l| l.gsub(/[^a-z]/,'').strip }
1499
- opts = "(:conditions=>\"lang NOT IN ('#{langs.join("','")}')\")"
1500
- end
1501
- elsif @params[:only]
1502
- # TODO: test
1503
- case @params[:only]
1504
- when 'current'
1505
- opts = "(:conditions=>\"lang = '#{helper.lang}'\")"
1506
- else
1507
- # list of lang
1508
- # TODO: test
1509
- langs = @params[:only].split(',').map{|l| l.gsub(/[^a-z]/,'').strip }
1510
- opts = "(:conditions=>\"lang IN ('#{langs.join("','")}')\")"
1511
- end
1512
- else
1513
- opts = ""
1514
- end
1515
- out "<% if #{list_var} = #{node}.traductions#{opts} -%>"
1516
- out expand_with(:list=>list_var, :node_class => Version)
1517
- out "<% end -%>"
1518
- end
1519
-
1520
- # TODO: test
1521
- def r_show_traductions
1522
- "<% if #{list_var} = #{node}.traductions -%>"
1523
- "#{_("Traductions:")} <span class='traductions'><%= #{list_var}.join(', ') %></span>"
1524
- "<%= traductions(:node=>#{node}).join(', ') %>"
1525
- end
1526
-
1527
- def r_node
1528
- @method = @params[:select] || 'node' # 'node' is for version.node
1529
- r_unknown
1530
- end
1531
-
1532
- # icon or first image (defined using build_finder_for instead of zafu_known_context for performance reasons).
1533
- def r_icon
1534
- if !@params[:in] && !@params[:where] && !@params[:from] && !@params[:find]
1535
- finder, klass = build_finder_for(:first, 'icon', @params.merge(:or => 'image', :order => 'l_id desc, position asc, name asc', :group => 'id,l_id'))
1536
- return unless finder
1537
- return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1538
- do_var(finder, :node_class => klass)
1539
- else
1540
- r_unknown
1541
- end
1542
- end
1543
-
1544
- def r_date
1545
- select = @params[:select]
1546
- case select
1547
- when 'main'
1548
- expand_with(:date=>'main_date')
1549
- when 'now'
1550
- expand_with(:date=>'Time.now')
1551
- else
1552
- if select =~ /^\d{4}-\d{1,2}-\d{1,2}$/
1553
- begin
1554
- d = Date.parse(select)
1555
- expand_with(:date=>select.inspect)
1556
- rescue
1557
- parser_error("invalid date '#{select}' should be 'YYYY-MM-DD'")
1558
- end
1559
- elsif date = find_stored(Date, select)
1560
- if date[0..0] == '"'
1561
- begin
1562
- d = Date.parse(date[1..-2])
1563
- expand_with(:date=>date)
1564
- rescue
1565
- parser_error("invalid date #{select} (#{date}) should be 'YYYY-MM-DD'")
1566
- end
1567
- else
1568
- expand_with(:date=>select)
1569
- end
1570
- elsif select =~ /\[(.*)\]/
1571
- date, static = parse_attributes_in_value(select, :erb => false)
1572
- expand_with(:date => "\"#{date}\"")
1573
- else
1574
- parser_error("bad parameter '#{select}'")
1575
- end
1576
- end
1577
- end
1578
-
1579
- def r_javascripts
1580
- if @params[:list].nil?
1581
- list = %w{ prototype effects tablekit zena }
1582
- elsif @params[:list] == 'all'
1583
- list = %w{ prototype effects dragdrop tablekit zena }
1584
- else
1585
- list = @params[:list].split(',').map{|e| e.strip}
1586
- end
1587
- helper.javascript_include_tag(*list)
1588
- end
1589
-
1590
- def r_stylesheets
1591
- if @params[:list] == 'all' || @params[:list].nil?
1592
- list = %w{ zena code }
1593
- else
1594
- list = @params[:list].split(',').map{|e| e.strip}
1595
- end
1596
- list << {:media => @params[:media]} if @params[:media]
1597
- helper.stylesheet_link_tag(*list)
1598
- end
1599
-
1600
- def r_flash_messages
1601
- type = @params[:show] || 'both'
1602
- "<div id='messages'>" +
1603
- if (type == 'notice' || type == 'both')
1604
- "<% if flash[:notice] -%><div id='notice' class='flash' onclick='new Effect.Fade(\"error\")'><%= flash[:notice] %></div><% end -%>"
1605
- else
1606
- ''
1607
- end +
1608
- if (type == 'error' || type == 'both')
1609
- "<% if flash[:error] -%><div id='error' class='flash' onclick='new Effect.Fade(\"error\")'><%= flash[:error] %></div><% end -%>"
1610
- else
1611
- ''
1612
- end +
1613
- "</div>"
1614
- end
1615
-
1616
- # Shows a 'made with zena' link or logo. ;-) Thanks for using this !
1617
- # TODO: test and add translation.
1618
- # <r:zena show='logo'/> or <r:zena show='text'/> == <r:zena/>
1619
- def r_zena
1620
- if logo = @params[:logo]
1621
- # FIXME
1622
- case logo
1623
- when 'tiny'
1624
- else
1625
- end
1626
- else
1627
- text = case @params[:type]
1628
- when 'riding'
1629
- _("riding zena")
1630
- when 'peace'
1631
- _("in peace with zena")
1632
- when 'garden'
1633
- _("a zen garden")
1634
- else
1635
- _("made with zena")
1636
- end
1637
- "<a class='zena' href='http://zenadmin.org' title='zena <%= Zena::VERSION %>'>#{text}</a>"
1638
- end
1639
- end
1640
-
1641
- def r_design
1642
- if @params[:by]
1643
- by = "<a href='#{@params[:href]}'>#{@params[:by]}</a>"
1644
- else
1645
- by = expand_with(:trans => true)
1646
- end
1647
- unless skin = @params[:skin]
1648
- skin = helper.instance_variable_get(:@controller).instance_variable_get(:@skin_name)
1649
- end
1650
- skin = "<i>#{skin}</i>" unless skin.blank?
1651
- _("%{skin}, design by %{name}") % {:name => by, :skin => skin}
1652
- end
1653
-
1654
- # creates a link. Options are:
1655
- # :href (node, parent, project, root)
1656
- # :tattr (translated attribute used as text link)
1657
- # :attr (attribute used as text link)
1658
- # <r:link href='node'><r:trans attr='lang'/></r:link>
1659
- # <r:link href='node' tattr='lang'/>
1660
- # <r:link update='dom_id'/>
1661
- # <r:link page='next'/> <r:link page='previous'/> <r:link page='list'/>
1662
- def r_link
1663
- if @params[:page]
1664
- pagination_links
1665
- else
1666
- make_link
1667
- end
1668
- end
1669
-
1670
- def make_link(options = {})
1671
- query_params = options[:query_params] || {}
1672
- default_text = options[:default_text]
1673
- params = @params.dup
1674
-
1675
- opts = {}
1676
- if upd = params.delete(:update)
1677
- return unless remote_target = find_target(upd)
1678
- end
1679
-
1680
- if href = params.delete(:href)
1681
- if lnode = find_stored(Node, href)
1682
- # using stored node
1683
- else
1684
- lnode, klass = build_finder_for(:first, href, {})
1685
- return unless lnode
1686
- return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1687
- end
1688
- else
1689
- # obj
1690
- if node_class == Version
1691
- lnode = "#{node}.node"
1692
- opts[:lang] = "#{node}.lang"
1693
- elsif node_kind_of?(Node)
1694
- lnode = node
1695
- else
1696
- lnode = @context[:previous_node]
1697
- end
1698
- end
1699
-
1700
- if fmt = params.delete(:format)
1701
- if fmt == 'data'
1702
- opts[:format] = "#{node}.c_ext"
1703
- else
1704
- opts[:format] = fmt.inspect
1705
- end
1706
- end
1707
-
1708
- if mode = params.delete(:mode)
1709
- opts[:mode] = mode.inspect
1710
- end
1711
-
1712
- if sharp = params.delete(:sharp)
1713
- opts[:sharp] = sharp.inspect
1714
- end
1715
-
1716
- if sharp_in = params.delete(:in)
1717
- finder, klass = build_finder_for(:first, sharp_in, {})
1718
- return unless finder
1719
- return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1720
- opts[:sharp_in] = finder
1721
- end
1722
-
1723
- if @html_tag && @html_tag != 'a'
1724
- # FIXME: can we remove this ?
1725
- # html attributes do not belong to anchor
1726
- pre_space = ''
1727
- html_params = {}
1728
- else
1729
- html_params = get_html_params(params.merge(@html_tag_params), :link)
1730
- pre_space = @space_before || ''
1731
- @html_tag_done = true
1732
- end
1733
-
1734
- (params.keys - [:style, :class, :id, :rel, :name, :anchor, :attr, :tattr, :trans, :text, :page]).each do |k|
1735
- next if k.to_s =~ /if_|set_/
1736
- query_params[k] = params[k]
1737
- end
1738
-
1739
- # TODO: merge these two query_params cleanup things into something cleaner.
1740
- if remote_target
1741
- # ajax
1742
- query_params_list = []
1743
- query_params.each do |k,v|
1744
- if k == :date
1745
- if v == 'current_date'
1746
- str = "\#{#{current_date}}"
1747
- elsif v =~ /\A\d/
1748
- str = CGI.escape(v.gsub('"',''))
1749
- elsif v =~ /\[/
1750
- attribute, static = parse_attributes_in_value(v.gsub('"',''), :erb => false)
1751
- str = static ? CGI.escape(attribute) : "\#{CGI.escape(\"#{attribute}\")}"
1752
- else
1753
- str = "\#{CGI.escape(#{node_attribute(v)})}"
1754
- end
1755
- else
1756
- attribute, static = parse_attributes_in_value(v.gsub('"',''), :erb => false)
1757
- str = static ? CGI.escape(attribute) : "\#{CGI.escape(\"#{attribute}\")}"
1758
- end
1759
- query_params_list << "#{k.to_s.gsub('"','')}=#{str}"
1760
- end
1761
- pre_space + link_to_update(remote_target, :node_id => "#{lnode}.zip", :query_params => query_params_list, :default_text => default_text, :html_params => html_params)
1762
- else
1763
- # direct link
1764
- query_params.each do |k,v|
1765
- if k == :date
1766
- if v == 'current_date'
1767
- query_params[k] = current_date
1768
- elsif v =~ /\A\d/
1769
- query_params[k] = v.inspect
1770
- elsif v =~ /\[/
1771
- attribute, static = parse_attributes_in_value(v.gsub('"',''), :erb => false)
1772
- query_params[k] = "\"#{attribute}\""
1773
- else
1774
- query_params[k] = node_attribute(v)
1775
- end
1776
- else
1777
- attribute, static = parse_attributes_in_value(v.gsub('"',''), :erb => false)
1778
- query_params[k] = "\"#{attribute}\""
1779
- end
1780
- end
1781
-
1782
- query_params.merge!(opts)
1783
-
1784
- opts_str = ''
1785
- query_params.keys.sort {|a,b| a.to_s <=> b.to_s }.each do |k|
1786
- opts_str << ",:#{k.to_s.gsub(/[^a-z_A-Z_]/,'')}=>#{query_params[k]}"
1787
- end
1788
-
1789
- opts_str += ", :host => #{@context["exp_host"]}" if @context["exp_host"]
1790
-
1791
- pre_space + "<a#{params_to_html(html_params)} href='<%= zen_path(#{lnode}#{opts_str}) %>'>#{text_for_link(default_text)}</a>"
1792
- end
1793
- end
1794
-
1795
- # <r:link page='next'/> <r:link page='previous'/> <r:link page='list'/>
1796
- def pagination_links
1797
- return parser_error("not in pagination scope") unless pagination_key = @context[:paginate]
1798
- case @params[:page]
1799
- when 'previous'
1800
- out "<% if set_#{pagination_key}_previous = (set_#{pagination_key} > 1 ? set_#{pagination_key} - 1 : nil) -%>"
1801
- @context[:vars] ||= []
1802
- @context[:vars] << "#{pagination_key}_previous"
1803
- out make_link(:default_text => "<%= set_#{pagination_key}_previous %>", :query_params => {pagination_key => "[#{pagination_key}_previous]"})
1804
- if descendant('else')
1805
- out expand_with(:in_if => true, :only => ['else', 'elsif'])
1806
- end
1807
- out "<% end -%>"
1808
- when 'next'
1809
- out "<% if set_#{pagination_key}_next = (set_#{pagination_key}_count - set_#{pagination_key} > 0 ? set_#{pagination_key} + 1 : nil) -%>"
1810
- @context[:vars] ||= []
1811
- @context[:vars] << "#{pagination_key}_next"
1812
- out make_link(:default_text => "<%= set_#{pagination_key}_next %>", :query_params => {pagination_key => "[#{pagination_key}_next]"})
1813
- if descendant('else')
1814
- out expand_with(:in_if => true, :only => ['else', 'elsif'])
1815
- end
1816
- out "<% end -%>"
1817
- when 'list'
1818
- @context[:vars] ||= []
1819
- @context[:vars] << "#{pagination_key}_page"
1820
- if @blocks == [] || (@blocks.size == 1 && !@blocks.first.kind_of?(String) && @blocks.first.method == 'else')
1821
- # add a default block
1822
- if tag = @params[:tag]
1823
- open_tag = "<#{tag}>"
1824
- close_tag = "</#{tag}>"
1825
- else
1826
- open_tag = close_tag = ''
1827
- end
1828
- link_params = {}
1829
- @params.each do |k,v|
1830
- next if [:tag, :page, :join, :page_count].include?(k)
1831
- link_params[k] = v
1832
- end
1833
- text = "#{open_tag}<r:link #{params_to_html(link_params)} #{pagination_key}='[#{pagination_key}_page]' do='[#{pagination_key}_page]'/>#{close_tag}"
1834
- @blocks = [make(:void, :method=>'void', :text=>text)]
1835
- remove_instance_variable(:@all_descendants)
1836
- end
1837
-
1838
- if !descendant('else')
1839
- @blocks += [make(:void, :method=>'void', :text=>"<r:else>#{open_tag}<r:show var='#{pagination_key}_page'/>#{close_tag}</r:else>")]
1840
- remove_instance_variable(:@all_descendants)
1841
- end
1842
-
1843
- 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| %>"
1844
- out "<%= #{pagination_key}_page_join %>"
1845
- out "<% if set_#{pagination_key}_page != set_#{pagination_key} -%>"
1846
- out expand_with(:in_if => true)
1847
- out "<% end; end -%>"
1848
- else
1849
- parser_error("unkown 'page' option #{@params[:page].inspect} should be ('previous', 'next' or 'list')")
1850
- end
1851
- end
1852
-
1853
- def r_img
1854
- return unless node_kind_of?(Node)
1855
- if @params[:src]
1856
- finder, klass = build_finder_for(:first, @params[:src])
1857
- return unless finder
1858
- return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1859
- img = finder
1860
- else
1861
- img = node
1862
- end
1863
- mode = @params[:mode] || 'std'
1864
- # FIXME: replace this call by something that integrates better with html_tag_params and such.
1865
- res = "img_tag(#{img}, :mode=>#{mode.inspect}"
1866
- [:class, :alt_src, :id, :border, :style].each do |k|
1867
- res += ", :#{k}=>#{@params[k].inspect}" if @params[k]
1868
- end
1869
- res += ", :host => #{@context["exp_host"]}" if @context["exp_host"]
1870
- res += ")"
1871
- if @params[:link]
1872
- finder, klass = build_finder_for(:first, @params[:link])
1873
- return unless finder
1874
- return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1875
-
1876
- opts_str = @context["exp_host"] ? ", :host => #{@context["exp_host"]}" : ""
1877
-
1878
- "<a href='<%= zen_path(#{finder}#{opts_str}) %>'><%= #{res} %></a>"
1879
- else
1880
- "<%= #{res} %>"
1881
- end
1882
- end
1883
-
1884
- def r_calendar
1885
- if @context[:block] == self
1886
- # called from self (storing template / rendering)
1887
- if role = @params[:assign_as]
1888
- assign_calendar(role)
1889
- else
1890
- display_calendar
1891
- end
1892
- else
1893
- # This is called first to prepare calendar
1894
- if @params[:assign_as]
1895
- fld = 'date'
1896
- table_name = 'links'
1897
- else
1898
- fld = @params[:date] || 'event_at'
1899
- if ['log_at', 'created_at', 'updated_at', 'event_at'].include?(fld) # TODO: use rubyless to learn type
1900
- table_name = 'nodes'
1901
- elsif fld == 'l_date'
1902
- fld = 'date'
1903
- table_name = 'links'
1904
- else
1905
- return parser_error("Invalid 'date' value for calendar (#{fld.inspect}).")
1906
- end
1907
- end
1908
-
1909
- @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')}'"
1910
-
1911
- new_dom_scope
1912
-
1913
- # SAVED TEMPLATE
1914
- template = expand_block(self, :block => self, :saved_template => true)
1915
- out helper.save_erb_to_url(template, template_url)
1916
-
1917
- # INLINE
1918
- out expand_block(self, :block => self, :saved_template => false)
1919
- end
1920
- end
1921
-
1922
- def display_calendar
1923
- size = (params[:size] || 'large').to_sym
1924
- finder = params[:select] || 'notes in project'
1925
-
1926
- if @blocks == []
1927
- # add a default <r:link/> block
1928
- if size == :tiny
1929
- @blocks = [make(:void, :method=>'void', :text=>"<em do='link' date='current_date' do='[current_date]' format='%d'/><r:else do='[current_date]' format='%d'/>")]
1930
- else
1931
- @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'/>")]
1932
- end
1933
- remove_instance_variable(:@all_descendants)
1934
- elsif !descendant('else')
1935
- @blocks += [make(:void, :method=>'void', :text=>"<r:else do='[current_date]' format='%d'/>")]
1936
- remove_instance_variable(:@all_descendants)
1937
- end
1938
-
1939
- @html_tag_done = false
1940
- @html_tag_params[:id] = erb_dom_id
1941
- @html_tag_params[:class] ||= "#{size}cal"
1942
- @html_tag ||= 'div'
1943
-
1944
- finder, klass = build_finder_for(:all, finder, @params, [@date_scope])
1945
- return unless finder
1946
- return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1947
-
1948
- 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 -%>"
1949
-
1950
- render_html_tag(calendar_code(finder, "", cell_code, "", params))
1951
- end
1952
-
1953
- # manage links from @node ---- reference ----> ...
1954
- # <div do='calendar' assign='reference' to='main' split_hours='12' />
1955
- def assign_calendar(as_role)
1956
- size = (params[:size] || 'large').to_sym
1957
- @html_tag_done = false
1958
- @html_tag_params[:id] = erb_dom_id
1959
- @html_tag_params[:class] ||= "#{size}cal"
1960
- @html_tag ||= 'div'
1961
- if rel = RelationProxy.find_by_role(as_role.singularize)
1962
- role = rel.this_role
1963
- else
1964
- return parser_error("Invalid role #{as_role.inspect}.")
1965
- end
1966
- finder, klass = build_finder_for(:all, role, @params, [@date_scope])
1967
- return unless finder
1968
- return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
1969
-
1970
- # SAVED TEMPLATE ========
1971
- template_url = self.template_url + 'cell'
1972
- template = "<%= cal_assign_cell(@node, #{role.inspect}, #{@params[:used].inspect}) %>"
1973
- out helper.save_erb_to_url(template, template_url)
1974
-
1975
- # we call update on node 'B'
1976
- # A (main)
1977
- # ... B (other node)
1978
- # calendar (in B context) ---- role --->
1979
-
1980
- cell_prefix_code = "<span><%= day_#{list_var}.strftime('%d').to_i -%></span><ul>"
1981
- 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}) %>"
1982
- cell_postfix_code = "</ul>"
1983
- render_html_tag(calendar_code(finder, cell_prefix_code, cell_code, cell_postfix_code, params))
1984
- end
1985
-
1986
- def calendar_code(finder, cell_prefix_code, cell_code, cell_postfix_code, params)
1987
- type = params[:type] ? params[:type].to_sym : :month
1988
- size = (params[:size] || 'large').to_sym
1989
- ref_date = params[:assign_as] ? 'l_date' : (params[:date] || 'event_at')
1990
-
1991
- case type
1992
- when :month
1993
- title = "\"\#{_(Date::MONTHNAMES[main_date.mon])} \#{main_date.year}\""
1994
- prev_date = "\#{main_date.advance(:months => -1).strftime(\"%Y-%m-%d\")}"
1995
- next_date = "\#{main_date.advance(:months => 1).strftime(\"%Y-%m-%d\")}"
1996
- when :week
1997
- title = "\"\#{_(Date::MONTHNAMES[main_date.mon])} \#{main_date.year}\""
1998
- prev_date = "\#{main_date.advance(:days => -7).strftime(\"%Y-%m-%d\")}"
1999
- next_date = "\#{main_date.advance(:days => +7).strftime(\"%Y-%m-%d\")}"
2000
- else
2001
- return parser_error("invalid type (should be 'month' or 'week')")
2002
- end
2003
-
2004
- if hours = @params[:split_hours]
2005
- hours = hours.split(',').map{|l| l.to_i}
2006
- hours << 0
2007
- hours = hours.uniq.sort
2008
- # I feel all this would be much better if we could use "each_group" but then how do we access hours ?
2009
- week_code = "<% week.step(week+6,1) do |day_#{list_var}| -%>
2010
- <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>
2011
- <% end -%>"
2012
- (@context[:vars] ||= []) << "hour"
2013
- else
2014
- hours = nil
2015
- week_code = "<% week.step(week+6,1) do |day_#{list_var}| -%>
2016
- <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>
2017
- <% end -%>"
2018
- end
2019
-
2020
- res = <<-END_TXT
2021
- <h3 class='title'>
2022
- <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>
2023
- <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>
2024
- <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>
2025
- </h3>
2026
- <table cellspacing='0' class='#{size}cal#{@params[:assign_as] ? " assign" : ''}'>
2027
- <tr class='head'><%= cal_day_names(#{size.inspect}) %></tr>
2028
- <% start_date, end_date = cal_start_end(#{current_date}, #{type.inspect}) -%>
2029
- <% cal_weeks(#{ref_date.to_sym.inspect}, #{finder}, start_date, end_date, #{hours.inspect}) do |week, nodes_#{list_var}| -%>
2030
- <tr class='body'>
2031
- #{week_code}
2032
- </tr>
2033
- <% end -%>
2034
- </table>
2035
- END_TXT
2036
- end
2037
-
2038
- # part caching
2039
- def r_cache
2040
- kpath = @params[:kpath] || Page.kpath
2041
- out "<% #{cache} = Cache.with(visitor.id, visitor.group_ids, #{kpath.inspect}, #{helper.send(:lang).inspect}, #{template_url.inspect}) do capture do %>"
2042
- out expand_with
2043
- out "<% end; end %><%= #{cache} %>"
2044
- end
2045
-
2046
- # recursion
2047
- def r_include
2048
- return '' if @context[:saved_template]
2049
- return super if @params[:template] || !@params[:part]
2050
- part = @params[:part].gsub(/[^a-zA-Z_]/,'')
2051
- method_name = @context["#{part}_method".to_sym]
2052
- return parser_error("no parent named '#{part}'") unless method_name
2053
- "<%= #{method_name}(depth+1,#{node},#{list}) %>"
2054
- end
2055
-
2056
- # use all other tags as relations
2057
- def r_unknown
2058
- @params[:select] = @method
2059
- r_context
2060
- end
2061
-
2062
- # Enter a new context (<r:context find='all' method='pages'>). This is the same as '<r:pages>...</r:pages>'). It is
2063
- # considered better style to use '<r:pages>...</r:pages>' instead of the more general '<r:context>' because the tags
2064
- # give a clue on the context at start and end. Another way to open a context is the 'do' syntax: "<div do='pages'>...</div>".
2065
- # FIXME: 'else' clause has been removed, find a solution to put it back.
2066
- def r_context
2067
- # DRY ! (build_finder_for, block)
2068
- return parser_error("missing 'method' parameter") unless method = @params[:select]
2069
-
2070
- context = node_class.zafu_known_contexts[method]
2071
- if context && @params.keys == [:select]
2072
- open_context("#{node}.#{method}", context)
2073
- elsif node_kind_of?(Node)
2074
- count = ['first','all','count'].include?(@params[:find]) ? @params[:find].to_sym : nil
2075
- count ||= Node.plural_relation?(method) ? :all : :first
2076
- finder, klass, query = build_finder_for(count, method, @params)
2077
- return unless finder
2078
- if node_kind_of?(Node) && !klass.ancestors.include?(Node)
2079
- # moving out of node: store last Node
2080
- @context[:previous_node] = node
2081
- end
2082
- if count == :all
2083
- # plural
2084
- do_list( finder, query, :node_class => klass)
2085
- # elsif count == :count
2086
- # "<%= #{build_finder_for(count, method, @params)} %>"
2087
- else
2088
- # singular
2089
- do_var( finder, :node_class => klass)
2090
- end
2091
- else
2092
- "unknown relation (#{method}) for #{node_class} class"
2093
- end
2094
- end
2095
-
2096
- def open_context(finder, context)
2097
- klass = context[:node_class]
2098
- # hack to store last 'Node' context until we fix node(Node) stuff:
2099
- previous_node = node_kind_of?(Node) ? node : @context[:previous_node]
2100
- if klass.kind_of?(Array)
2101
- # plural
2102
- do_list( finder, nil, context.merge(:node_class => klass[0], :previous_node => previous_node) )
2103
- else
2104
- # singular
2105
- do_var( finder, context.merge(:previous_node => previous_node) )
2106
- end
2107
- end
2108
-
2109
- # Prepare stylesheet and xml content for xsl-fo post-processor
2110
- def r_fop
2111
- return parser_error("missing 'stylesheet' argument") unless @params[:stylesheet]
2112
- # get stylesheet text
2113
- xsl_content, absolute_url, doc = self.class.get_template_text(@params[:stylesheet], @options[:helper], @options[:current_folder])
2114
- return parser_error("stylesheet #{@params[:stylesheet].inspect} not found") unless doc
2115
-
2116
- template_url = (self.template_url.split('/')[0..-2] + ['_main.xsl']).join('/')
2117
- helper.save_erb_to_url(xsl_content, template_url)
2118
- out "<?xml version='1.0' encoding='utf-8'?>\n"
2119
- out "<!-- xsl_id:#{doc[:id] } -->\n" if doc
2120
- out expand_with
2121
- end
2122
-
2123
- # Prepare content for LateX post-processor
2124
- def r_latex
2125
- out "% latex\n"
2126
- # all content inside this will be informed to render for Latex output
2127
- out expand_with(:output_format => 'latex')
2128
- end
2129
-
2130
- def r_debug
2131
- return '' unless @context[:dev]
2132
- add_html_class('debug')
2133
- out "<p>#{@params[:title]}</p>" if @params[:title]
2134
- (@params[:show] || '').split(',').map(&:strip).each do |what|
2135
- case what
2136
- when 'params'
2137
- out "<pre><%= params.inspect %></pre>"
2138
- else
2139
- parser_error("invalid element to show. Options are ['params'].")
2140
- end
2141
- end
2142
- out expand_with
2143
- end
2144
-
2145
- # ================== HELPER METHODS ================
2146
-
2147
- # Create an sql query to open a new context (passes its arguments to HasRelations#build_find)
2148
- def build_finder_for(count, rel, params=@params, raw_filters = [])
2149
- if (context = node_class.zafu_known_contexts[rel]) && !params[:in] && !params[:where] && !params[:from] && !params[:order] && raw_filters == []
2150
- klass = context[:node_class]
2151
-
2152
- if klass.kind_of?(Array) && count == :all
2153
- return ["#{node}.#{rel}", klass[0]]
2154
- else
2155
- return [(count == :all ? "[#{node}.#{rel}]" : "#{node}.#{rel}"), klass]
2156
- end
2157
- end
2158
-
2159
- rel ||= 'self'
2160
- if (count == :first)
2161
- if rel == 'self'
2162
- return [node, node_class]
2163
- elsif rel == 'main'
2164
- return ["@node", Node]
2165
- elsif rel == 'root'
2166
- return ["(secure(Node) { Node.find(#{current_site[:root_id]})})", Node]
2167
- elsif rel == 'start'
2168
- return ["start_node", Node]
2169
- elsif rel == 'visitor'
2170
- return ["visitor.contact", Node]
2171
- elsif rel =~ /^\d+$/
2172
- return ["(secure(Node) { Node.find_by_zip(#{rel.inspect})})", Node]
2173
- elsif node_name = find_stored(Node, rel)
2174
- return [node_name, Node]
2175
- elsif rel[0..0] == '/'
2176
- rel = rel[1..-1]
2177
- return ["(secure(Node) { Node.find_by_path(#{rel.inspect})})", Node]
2178
- end
2179
- end
2180
-
2181
- pseudo_sql, add_raw_filters = make_pseudo_sql(rel, params)
2182
- raw_filters += add_raw_filters if add_raw_filters
2183
-
2184
- # FIXME: stored should be clarified and managed in a single way through links and contexts.
2185
- # <r:void store='foo'>...
2186
- # <r:link href='foo'/>
2187
- # <r:pages from='foo'/> <-- this is just a matter of changing node parameter
2188
- # <r:pages from='site' project='foo'/>
2189
- # <r:img link='foo'/>
2190
- # ...
2191
-
2192
- if node_kind_of?(Node)
2193
- node_name = @context[:parent_node] || node
2194
- else
2195
- node_name = @context[:previous_node]
2196
- end
2197
-
2198
- # make sure we do not use a new record in a find query:
2199
- query = Node.build_find(count, pseudo_sql, :node_name => node_name, :raw_filters => raw_filters, :ref_date => "\#{#{current_date}}")
2200
-
2201
- unless query.valid?
2202
- out parser_error(query.errors.join(' '), pseudo_sql.join(', '))
2203
- return nil
2204
- end
2205
-
2206
-
2207
- if count == :count
2208
- out "<%= #{query.finder(:count)} %>"
2209
- return nil
2210
- end
2211
-
2212
- klass = query.main_class
2213
-
2214
- if params[:else]
2215
- # FIXME: else not working with zafu_known_contexts
2216
- finder, else_class, else_query = build_finder_for(count, params[:else], {})
2217
- if finder && (else_query.nil? || else_query.valid?) && (else_class == klass || klass.ancestors.include?(else_class) || else_class.ancestors.include?(klass))
2218
- ["(#{query.finder(count)} || #{finder})", klass, query]
2219
- else
2220
- [query.finder(count), query.main_class, query]
2221
- end
2222
- else
2223
- [query.finder(count), query.main_class, query]
2224
- end
2225
- end
2226
-
2227
- # Build pseudo sql from the parameters
2228
- # comments where ... from ... in ... order ... limit
2229
- def make_pseudo_sql(rel, params=@params)
2230
- parts = [rel.dup]
2231
- filters = []
2232
-
2233
- if params[:from]
2234
- parts << params[:from]
2235
-
2236
- key_counter = 1
2237
- while sub_part = params["from#{key_counter}".to_sym]
2238
- key_counter += 1
2239
- parts << sub_part
2240
- end
2241
- end
2242
-
2243
- if params[:where]
2244
- parts[0] << " where #{params[:where]}"
2245
- end
2246
-
2247
- if params[:in]
2248
- parts[-1] << " in #{params[:in]}"
2249
- end
2250
-
2251
- if group = params[:group]
2252
- parts[-1] << " group by #{group}" unless parts[0] =~ /group by/
2253
- end
2254
-
2255
- if order = params[:order]
2256
- parts[-1] << " order by #{order}" unless parts[0] =~ /order by/
2257
- end
2258
-
2259
- if paginate = params[:paginate]
2260
- page_size = params[:limit].to_i
2261
- page_size = 20 if page_size < 1
2262
- parts[-1] << " limit #{page_size} paginate #{paginate.gsub(/[^a-z_A-Z]/,'')}"
2263
- else
2264
- [:limit, :offset].each do |k|
2265
- next unless params[k]
2266
- parts[-1] << " #{k} #{params[k]}" unless parts[0] =~ / #{k} /
2267
- end
2268
- end
2269
-
2270
- finders = [parts.join(' from ')]
2271
- if params[:or]
2272
- finders << params[:or]
2273
-
2274
- key_counter = 1
2275
- while sub_or = params["or#{key_counter}".to_sym]
2276
- key_counter += 1
2277
- finders << sub_or
2278
- end
2279
- else
2280
- or_clause = nil
2281
- end
2282
-
2283
- return [finders, parse_raw_filters(params)]
2284
- end
2285
-
2286
- # Parse special filters
2287
- def parse_raw_filters(params)
2288
- filters = []
2289
-
2290
- if value = params[:author]
2291
- if stored = find_stored(User, value)
2292
- filters << "TABLE_NAME.user_id = '\#{#{stored}.id}'"
2293
- elsif value == 'current'
2294
- filters << "TABLE_NAME.user_id = '\#{#{node}[:user_id]}'"
2295
- elsif value == 'visitor'
2296
- filters << "TABLE_NAME.user_id = '\#{visitor[:id]}'"
2297
- elsif value =~ /\A\d+\Z/
2298
- filters << "TABLE_NAME.user_id = '#{value.to_i}'"
2299
- elsif value =~ /\A[\w\/]+\Z/
2300
- # TODO: path, not implemented yet
2301
- end
2302
- end
2303
-
2304
- if value = params[:project]
2305
- if stored = find_stored(Node, value)
2306
- filters << "TABLE_NAME.project_id = '\#{#{stored}.get_project_id}'"
2307
- elsif value == 'current'
2308
- filters << "TABLE_NAME.project_id = '\#{#{node}.get_project_id}'"
2309
- elsif value =~ /\A\d+\Z/
2310
- filters << "TABLE_NAME.project_id = '#{value.to_i}'"
2311
- elsif value =~ /\A[\w\/]+\Z/
2312
- # TODO: path, not implemented yet
2313
- end
2314
- end
2315
-
2316
- if value = params[:section]
2317
- if stored = find_stored(Node, value)
2318
- filters << "TABLE_NAME.section_id = '\#{#{stored}.get_section_id}'"
2319
- elsif value == 'current'
2320
- filters << "TABLE_NAME.section_id = '\#{#{node}.get_section_id}'"
2321
- elsif value =~ /\A\d+\Z/
2322
- filters << "TABLE_NAME.section_id = '#{value.to_i}'"
2323
- elsif value =~ /\A[\w\/]+\Z/
2324
- # not implemented yet
2325
- end
2326
- end
2327
-
2328
- [:updated, :created, :event, :log].each do |k|
2329
- if value = params[k]
2330
- # current, same are synonym for 'today'
2331
- filters << date_condition(value,"TABLE_NAME.#{k}_at",current_date)
2332
- end
2333
- end
2334
-
2335
- filters == [] ? nil : filters
2336
- end
2337
-
2338
- # helpers
2339
- # get current output format
2340
- def output_format
2341
- @context[:output_format] || 'html'
2342
- end
2343
-
2344
- # find the current node name in the context
2345
- def node(klass = self.node_class)
2346
- if klass == self.node_class
2347
- (@context[:saved_template] && @context[:main_node]) ? "@#{base_class.to_s.underscore}" : (@context[:node] || '@node')
2348
- elsif klass == Node
2349
- @context[:previous_node] || '@node'
2350
- else
2351
- # ?
2352
- out parser_error("could not find node_name for #{klass} (current class is #{node_class})")
2353
- '@node'
2354
- end
2355
- end
2356
-
2357
- def erb_node_id(obj = node)
2358
- if node_kind_of?(Version)
2359
- "<%= #{obj}.node.zip %>.<%= #{obj}.number %>"
2360
- else
2361
- "<%= #{node_id(obj)} %>"
2362
- end
2363
- end
2364
-
2365
- def node_id(obj = node)
2366
- "#{obj}.zip"
2367
- end
2368
-
2369
- def current_date
2370
- @context[:date] || 'main_date'
2371
- end
2372
-
2373
- def var
2374
- return @var if @var
2375
- if node =~ /^var(\d+)$/
2376
- @var = "var#{$1.to_i + 1}"
2377
- else
2378
- @var = "var1"
2379
- end
2380
- end
2381
-
2382
- def cache
2383
- return @cache if @cache
2384
- if @context[:cache] =~ /^cache(\d+)$/
2385
- @cache = "cache#{$1.to_i + 1}"
2386
- else
2387
- @cache = "cache1"
2388
- end
2389
- end
2390
-
2391
- def list_var
2392
- return @list_var if @list_var
2393
- if (list || "") =~ /^list(\d+)$/
2394
- @list_var = "list#{$1.to_i + 1}"
2395
- else
2396
- @list_var = "list1"
2397
- end
2398
- end
2399
-
2400
- # Class of the current 'node' object (can be Version, Comment, Node, DataEntry, etc)
2401
- def node_class
2402
- @context[:node_class] || Node
2403
- end
2404
-
2405
- def base_class
2406
- if node_kind_of?(Node)
2407
- Node
2408
- elsif node_kind_of?(Version)
2409
- Version
2410
- else
2411
- node_class
2412
- end
2413
- end
2414
-
2415
- def node_kind_of?(ancestor)
2416
- node_class.ancestors.include?(ancestor)
2417
- end
2418
-
2419
- def list
2420
- @context[:list]
2421
- end
2422
-
2423
- def helper
2424
- @options[:helper]
2425
- end
2426
-
2427
- def params_to_erb(params, initial_comma = true)
2428
- res = initial_comma ? [""] : []
2429
- params.each do |k,v|
2430
- if v =~ /<%=/ && !(v =~ /"/)
2431
- # replace by #{}
2432
- val = v.gsub('#{', '# {').gsub(/<%=(.*?)%>/,'#{\1}')
2433
- res << "#{k.inspect}=>\"#{val}\""
2434
- else
2435
- res << "#{k.inspect}=>#{v.inspect}"
2436
- end
2437
- end
2438
- res.join(', ')
2439
- end
2440
-
2441
- def do_var(var_finder=nil, opts={})
2442
- clear_dom_scope
2443
- if var_finder == 'nil'
2444
- out "<% if nil -%>"
2445
- elsif var_finder
2446
- out "<% if #{var} = #{var_finder} -%>"
2447
- end
2448
-
2449
- if descendant('unlink')
2450
- @html_tag ||= 'div'
2451
- new_dom_scope
2452
- @html_tag_params[:id] = erb_dom_id
2453
- end
2454
-
2455
- res = expand_with(opts.merge(:node=>var, :in_if => false))
2456
-
2457
- if var_finder
2458
- res += expand_with(opts.merge(:in_if => true, :only => ['else', 'elsif'], :html_tag_params => @html_tag_params, :html_tag => @html_tag))
2459
- end
2460
- out render_html_tag(res)
2461
- out "<% end -%>" if var_finder
2462
- end
2463
-
2464
- def do_list(list_finder, query = nil, opts={})
2465
- clear_dom_scope
2466
-
2467
- @context.merge!(opts) # pass options from 'zafu_known_contexts' to @context
2468
-
2469
- 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))
2470
- new_dom_scope
2471
- # ajax, build template. We could merge the following code with 'r_block'.
2472
- add_block = descendant('add')
2473
- form_block = descendant('form') || each_block
2474
-
2475
- @context[:need_link_id] = form_block.need_link_id
2476
-
2477
- out "<% if (#{list_var} = #{list_finder}) || (#{node}.#{node_kind_of?(Comment) ? "can_comment?" : "can_write?"} && #{list_var}=[]) -%>"
2478
- if query && (pagination_key = query.pagination_key)
2479
- 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 -%>"
2480
- @context[:paginate] = pagination_key
2481
- @context[:vars] ||= []
2482
- @context[:vars] << "#{pagination_key}_nodes"
2483
- @context[:vars] << "#{pagination_key}_count"
2484
- @context[:vars] << "#{pagination_key}"
2485
- end
2486
-
2487
- # should we publish ?
2488
- publish_after_save ||= form_block ? form_block.params[:publish] : nil
2489
- publish_after_save ||= descendant('edit') ? descendant('edit').params[:publish] : nil
2490
-
2491
- # class name for create form
2492
- klass = add_block ? add_block.params[:klass] : nil
2493
- klass ||= form_block ? form_block.params[:klass] : nil
2494
-
2495
- # INLINE ==========
2496
- # 'r_add' needs the form when rendering. Send with :form.
2497
- 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))
2498
- out expand_with(:in_if=>true, :only=>['elsif', 'else'], :html_tag => @html_tag, :html_tag_params => @html_tag_params)
2499
- out "<% end -%>"
2500
-
2501
- # SAVED TEMPLATE ========
2502
- template = expand_block(each_block, :list=>false, :klass => klass, :saved_template => true)
2503
- out helper.save_erb_to_url(template, template_url)
2504
-
2505
- # FORM ============
2506
- if each_block != form_block
2507
- form = expand_block(form_block, :klass => klass, :add=>add_block, :publish_after_save => publish_after_save, :saved_template => true)
2508
- else
2509
- form = expand_block(form_block, :klass => klass, :add=>add_block, :make_form=>true, :publish_after_save => publish_after_save, :saved_template => true)
2510
- end
2511
- out helper.save_erb_to_url(form, form_url)
2512
- else
2513
- # no form, render, edit and add are not ajax
2514
- if descendant('add') || descendant('add_document')
2515
- out "<% if (#{list_var} = #{list_finder}) || (#{node}.#{node_kind_of?(Comment) ? "can_comment?" : "can_write?"} && #{list_var}=[]) -%>"
2516
- elsif list_finder != 'nil'
2517
- out "<% if #{list_var} = #{list_finder} -%>"
2518
- else
2519
- out "<% if nil -%>"
2520
- end
2521
-
2522
- if query && (pagination_key = query.pagination_key)
2523
- 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 -%>"
2524
- @context[:paginate] = pagination_key
2525
- @context[:vars] ||= []
2526
- @context[:vars] << "#{pagination_key}_nodes"
2527
- @context[:vars] << "#{pagination_key}_count"
2528
- @context[:vars] << "#{pagination_key}"
2529
- end
2530
-
2531
- out render_html_tag(expand_with(:list=>list_var, :in_if => false))
2532
- out expand_with(:in_if=>true, :only=>['elsif', 'else'], :html_tag => @html_tag, :html_tag_params => @html_tag_params)
2533
- out "<% end -%>"
2534
- end
2535
- end
2536
-
2537
- def _(text)
2538
- if @context[:dict]
2539
- @context[:dict][text] || helper.send(:_,text)
2540
- else
2541
- helper.send(:_,text)
2542
- end
2543
- end
2544
-
2545
- # Find a block to update on the page
2546
- def find_target(name)
2547
- # find dom_id / template_url
2548
- target = nil
2549
- root.descendants('block').each do |b|
2550
- if b.name == name
2551
- target = b
2552
- break
2553
- end
2554
- end
2555
- out parser_error("could not find a block named '#{name}'") if target.nil?
2556
- target
2557
- end
2558
-
2559
- # DOM id for the current context
2560
- def dom_id(suffix='')
2561
- return "\#{dom_id(#{node})}" if @context && (@context[:saved_template] && @context[:main_node])
2562
- if @context && scope_node = @context[:scope_node]
2563
- res = "#{dom_prefix}_\#{#{scope_node}.zip}"
2564
- else
2565
- res = dom_prefix
2566
- end
2567
- if (method == 'each' || method == 'each_group') && !@context[:make_form]
2568
- "#{res}_\#{#{var}.zip}"
2569
- elsif @context && @context[:in_calendar]
2570
- "#{res}_\#{#{current_date}.to_i}"
2571
- elsif method == 'unlink' || method == 'edit'
2572
- target = nil
2573
- parent = self.parent
2574
- while parent
2575
- if ['block', 'each', 'context', 'icon'].include?(parent.method)
2576
- target = parent
2577
- break
2578
- end
2579
- parent = parent.parent
2580
- end
2581
- target ? target.dom_id(suffix) : (res + suffix)
2582
- else
2583
- res + suffix
2584
- end
2585
- end
2586
-
2587
- def erb_dom_id(suffix='')
2588
- return "<%= dom_id(#{node}) %>" if @context && (@context[:saved_template] && @context[:main_node])
2589
- if @context && scope_node = @context[:scope_node]
2590
- res = "#{dom_prefix}_<%= #{scope_node}.zip %>"
2591
- else
2592
- res = dom_prefix
2593
- end
2594
- if (method == 'each' || method == 'each_group') && !@context[:make_form]
2595
- "#{res}_<%= #{var}.zip %>"
2596
- elsif method == 'draggable'
2597
- "#{res}_<%= #{node}.zip %>"
2598
- elsif @context && @context[:in_calendar]
2599
- "#{res}_<%= #{current_date}.to_i %>"
2600
- elsif method == 'unlink'
2601
- target = nil
2602
- parent = self.parent
2603
- while parent
2604
- if ['block', 'each', 'context', 'icon'].include?(parent.method)
2605
- target = parent
2606
- break
2607
- end
2608
- parent = parent.parent
2609
- end
2610
- target ? target.erb_dom_id(suffix) : (res + suffix)
2611
- else
2612
- res + suffix
2613
- end
2614
- end
2615
-
2616
- # Unique template_url, ending with dom_id
2617
- def template_url
2618
- "#{@options[:root]}/#{dom_prefix}"
2619
- end
2620
-
2621
- def form_url
2622
- template_url + '_form'
2623
- end
2624
-
2625
- # Return parameter value accessor
2626
- def get_param(key)
2627
- "params[:#{key}]"
2628
- end
2629
-
2630
- def context
2631
- return @context if @context
2632
- # not rendered yet, find first parent with context
2633
- @context = parent ? parent.context : {}
2634
- end
2635
-
2636
- # prefix for DOM id
2637
- def dom_prefix
2638
- (@context ? @context[:dom_prefix] : nil) || (@dom_prefix ||= unique_name)
2639
- end
2640
-
2641
- # use our own scope
2642
- def clear_dom_scope
2643
- @context.delete(:make_form) # should not propagate
2644
- @context.delete(:main_node) # should not propagate
2645
- end
2646
-
2647
- # create our own ajax DOM scope
2648
- def new_dom_scope
2649
- clear_dom_scope
2650
- @context.delete(:saved_template) # should not propagate on fresh template
2651
- @context.delete(:dom_prefix) # should not propagate on fresh template
2652
- @context[:main_node] = true # the current context will be rendered with a fresh '@node'
2653
- @context[:dom_prefix] = self.dom_prefix
2654
- end
2655
-
2656
- # Return a different name on each call
2657
- def unique_name(base = context_name)
2658
- root.next_name_index(base, base == @name).gsub(/[^\d\w\/]/,'_')
2659
- end
2660
-
2661
- def context_name
2662
- @name || if @context
2663
- @context[:name] || 'list'
2664
- elsif parent
2665
- parent.context_name
2666
- else
2667
- 'root'
2668
- end
2669
- end
2670
-
2671
- def next_name_index(key, own_id = false)
2672
- @next_name_index ||= {}
2673
- if @next_name_index[key]
2674
- @next_name_index[key] += 1
2675
- key + @next_name_index[key].to_s
2676
- elsif own_id
2677
- @next_name_index[key] = 0
2678
- key
2679
- else
2680
- @next_name_index[key] = 1
2681
- key + '1'
2682
- end
2683
- end
2684
-
2685
- def add_params(text, opts={}, inner = '')
2686
- text.sub(/\A([^<]*)<(\w+)(( .*?)[^%]|)>/) do
2687
- # we must set the first tag id
2688
- before = $1
2689
- tag = $2
2690
- params = parse_params($3)
2691
- opts.each do |k,v|
2692
- next unless v
2693
- params[k] = v
2694
- end
2695
- "#{before}<#{tag}#{params_to_html(params)}>#{inner}"
2696
- end
2697
- end
2698
-
2699
- # TODO: RUBYLESS
2700
- def get_test_condition(node = self.node, params = @params)
2701
- tests = []
2702
- params.each do |k,v|
2703
- if k.to_s =~ /^(or_|)([a-zA-Z_]+)(\d*)$/
2704
- k = $2.to_sym
2705
- end #tagged undocumented
2706
- if [:kind_of, :klass, :status, :lang, :can, :tagged, :node, :in, :visitor, :has].include?(k)
2707
- tests << [k, v]
2708
- elsif k == :test
2709
- if v =~ /\s/
2710
- tests << [:test, v]
2711
- else
2712
- tests << [:attribute, v]
2713
- end
2714
- end
2715
- end
2716
-
2717
-
2718
- tests.map! do |type,value|
2719
- case type
2720
- when :kind_of
2721
- "#{node}.vkind_of?(#{value.inspect})"
2722
- when :klass
2723
- klass = begin Module::const_get(value) rescue "NilClass" end
2724
- "#{node}.klass == #{value.inspect}"
2725
- when :status
2726
- "#{node}.version.status == #{Zena::Status[value.to_sym]}"
2727
- when :tagged
2728
- # TODO: undocumented: remove and use rubyless !
2729
- "#{node}.tagged[#{value.inspect}]"
2730
- when :lang
2731
- "#{node}.version.lang == #{value.inspect}"
2732
- when :can
2733
- # TODO: test
2734
- case value
2735
- when 'write', 'edit'
2736
- "#{node}.can_write?"
2737
- when 'drive', 'publish'
2738
- "#{node}.can_drive?"
2739
- else
2740
- nil
2741
- end
2742
- when :has
2743
- case value
2744
- when 'discussion'
2745
- "#{node}.discussion"
2746
- else
2747
- nil
2748
- end
2749
- when :test
2750
- parse_condition(value, node)
2751
- when :attribute
2752
- '!' + node_attribute(value, :node => node) + '.blank?'
2753
- when :node
2754
- if node_kind_of?(Node)
2755
- value, node_name = get_attribute_and_node(value)
2756
- node_name ||= '@node'
2757
- if value
2758
- case value
2759
- when 'main'
2760
- "#{node}[:id] == #{node_name}[:id]"
2761
- when 'start'
2762
- "#{node}[:zip] == (params[:s] || @node[:zip]).to_i"
2763
- when 'parent'
2764
- "#{node}[:id] == #{node_name}[:parent_id]"
2765
- when 'project'
2766
- "#{node}[:id] == #{node_name}[:project_id]"
2767
- when 'section'
2768
- "#{node}[:id] == #{node_name}[:section_id]"
2769
- when 'ancestor'
2770
- "#{node_name}.fullpath =~ /\\A\#{#{node}.fullpath}/"
2771
- else
2772
- if stored = find_stored(Node, value)
2773
- "#{node}[:id] == #{stored}[:id]"
2774
- else
2775
- nil
2776
- end
2777
- end
2778
- else
2779
- # bad node_name
2780
- nil
2781
- end
2782
- else
2783
- nil
2784
- end
2785
- when :in
2786
- if @context["in_#{value}".to_sym] # FIXME: || ancestors.include?(value) ==> ancestors is a list of zafu tags, not a list of names !
2787
- 'true'
2788
- else
2789
- 'false'
2790
- end
2791
- when :visitor
2792
- if value == 'anon'
2793
- "visitor.is_anon?"
2794
- else
2795
- nil
2796
- end
2797
- else
2798
- nil
2799
- end
2800
- end.compact!
2801
- tests == [] ? nil : tests.join(' || ')
2802
- end
2803
-
2804
- def parse_condition_error(clause, rest, res)
2805
- out parser_error("invalid clause #{clause.inspect} near \"#{res[-2..-1]}#{rest[0..1]}\"")
2806
- end
2807
-
2808
- def parse_condition(clause, node_name)
2809
- rest = clause.strip
2810
- types = [:par_open, :value, :bool_op, :op, :par_close]
2811
- allowed = [:par_open, :value]
2812
- par_count = 0
2813
- uses_bool_op = false
2814
- segment = [] # value op value
2815
- after_value = lambda { segment.size == 3 ? [:bool_op, :par_close] : [:op, :bool_op, :par_close]}
2816
- res = ""
2817
- while rest != ''
2818
- # puts rest.inspect
2819
- if rest =~ /\A\s+/
2820
- rest = rest[$&.size..-1]
2821
- elsif rest[0..0] == '('
2822
- unless allowed.include?(:par_open)
2823
- parse_condition_error(clause, rest, res)
2824
- return nil
2825
- end
2826
- res << '('
2827
- rest = rest[1..-1]
2828
- par_count += 1
2829
- elsif rest[0..0] == ')'
2830
- unless allowed.include?(:par_close)
2831
- parse_condition_error(clause, rest, res)
2832
- return nil
2833
- end
2834
- res << ')'
2835
- rest = rest[1..-1]
2836
- par_count -= 1
2837
- if par_count < 0
2838
- parse_condition_error(clause, rest, res)
2839
- return nil
2840
- end
2841
- allowed = [:bool_op]
2842
- elsif rest =~ /\A(lt|le|eq|ne|ge|gt)\s+/
2843
- unless allowed.include?(:op)
2844
- parse_condition_error(clause, rest, res)
2845
- return nil
2846
- end
2847
- op = $1.strip
2848
- rest = rest[op.size..-1]
2849
- op = {'lt' => '<', 'le' => '<=', 'eq' => '==', 'ne' => '!=', 'ge' => '>=', 'gt' => '>'}[op]
2850
- segment << [op, :op]
2851
- allowed = [:value]
2852
- elsif rest =~ /\A("|')([^\1]*?)\1/
2853
- # string
2854
- unless allowed.include?(:value)
2855
- parse_condition_error(clause, rest, res)
2856
- return nil
2857
- end
2858
- rest = rest[$&.size..-1]
2859
- segment << [$2.inspect, :string]
2860
- allowed = after_value.call
2861
- elsif rest =~ /\A(-?\d+)/
2862
- # number
2863
- unless allowed.include?(:value)
2864
- parse_condition_error(clause, rest, res)
2865
- return nil
2866
- end
2867
- rest = rest[$&.size..-1]
2868
- segment << [$1, :number]
2869
- allowed = after_value.call
2870
- elsif rest =~ /\A(and|or)/
2871
- unless allowed.include?(:bool_op)
2872
- parse_condition_error(clause, rest, res)
2873
- return nil
2874
- end
2875
- uses_bool_op = true
2876
- rest = rest[$&.size..-1]
2877
- res << " #{$1} "
2878
- allowed = [:par_open, :value]
2879
- elsif rest =~ /\A([\w:\.\-]+)/
2880
- # variable
2881
- unless allowed.include?(:value)
2882
- parse_condition_error(clause, rest, res)
2883
- return nil
2884
- end
2885
- rest = rest[$&.size..-1]
2886
- fld = $1
2887
- unless node_attr = node_attribute(fld, :node => node_name)
2888
- parser_error("invalid field #{fld.inspect}")
2889
- return nil
2890
- end
2891
- segment << [node_attr, :var]
2892
- allowed = after_value.call
2893
- else
2894
- parse_condition_error(clause, rest, res)
2895
- return nil
2896
- end
2897
- if segment.size == 3
2898
- toi = (segment[1][0] =~ /(>|<)/ || (segment[0][1] == :number || segment[2][1] == :number))
2899
- segment.map! do |part, type|
2900
- if type == :var
2901
- toi ? "#{part}.to_i" : part
2902
- elsif type == :string
2903
- toi ? part[1..-2].to_i : part
2904
- else
2905
- part
2906
- end
2907
- end
2908
- res << segment.join(" ")
2909
- segment = []
2910
- end
2911
- end
2912
-
2913
- if par_count > 0
2914
- parser_error("invalid clause #{clause.inspect}: missing closing ')'")
2915
- return nil
2916
- elsif allowed.include?(:value)
2917
- parser_error("invalid clause #{clause.inspect}")
2918
- return nil
2919
- else
2920
- return uses_bool_op ? "(#{res})" : res
2921
- end
2922
- end
2923
-
2924
- # Block visibility of descendance with 'do_list'.
2925
- def public_descendants
2926
- all = super
2927
- if ['context', 'each', 'block'].include?(self.method)
2928
- # do not propagate 'form',etc up
2929
- all.reject do |k,v|
2930
- ['form','unlink'].include?(k)
2931
- end
2932
- elsif ['if', 'case'].include?(self.method)
2933
- all.reject do |k,v|
2934
- ['else', 'elsif', 'when'].include?(k)
2935
- end
2936
- else
2937
- all
2938
- end
2939
- end
2940
-
2941
- def single_child_method
2942
- return @single_child_method if defined?(@single_child_method)
2943
- @single_child_method = if @blocks.size == 1
2944
- single_child = @blocks[0]
2945
- return nil if single_child.kind_of?(String)
2946
- single_child.html_tag ? nil : single_child.method
2947
- else
2948
- nil
2949
- end
2950
- end
2951
-
2952
- def get_attribute_and_node(str)
2953
- if str =~ /([^\.]+)\.(.+)/
2954
- node_name = $1
2955
- node_attr = $2
2956
- if att_node = find_stored(Node, node_name)
2957
- return [node_attr, att_node, Node]
2958
- elsif node_name == 'main'
2959
- return [node_attr, '@node', Node]
2960
- elsif node_name == 'visitor'
2961
- return [node_attr, 'visitor.contact', Contact]
2962
- elsif node_name == 'site'
2963
- return [node_attr, 'current_site', Site]
2964
- else
2965
- out parser_error("invalid node name #{node_name.inspect} in attribute #{str.inspect}")
2966
- return [nil]
2967
- end
2968
- else
2969
- return [str]
2970
- end
2971
- end
2972
-
2973
- def parse_attributes_in_value(v, opts = {})
2974
- opts = {:erb => true}.merge(opts)
2975
- static = true
2976
-
2977
- use_node = @var || node
2978
- res = v.gsub(/\[([^\]]+)\]/) do
2979
- static = false
2980
- res = nil
2981
- attribute = $1
2982
-
2983
- if opts[:skip_node_attributes]
2984
- if attribute =~ /^param:(\w+)$/
2985
- attribute = get_param($1)
2986
- elsif attribute == 'current_date'
2987
- attribute = current_date
2988
- else
2989
- res = "[#{attribute}]"
2990
- end
2991
- else
2992
- attribute = node_attribute(attribute, :node => use_node )
2993
- end
2994
-
2995
- res ||= if opts[:erb]
2996
- "<%= #{attribute} %>"
2997
- else
2998
- "\#{#{attribute}}"
2999
- end
3000
- res
3001
- end
3002
- [res, static]
3003
- end
3004
-
3005
- def node_attribute(str, opts={})
3006
-
3007
- if @context[:vars] && @context[:vars].include?(str)
3008
- return "set_#{str}"
3009
- end
3010
-
3011
- res = PSEUDO_ATTRIBUTES[str]
3012
- return res if res
3013
- return current_date if str == 'current_date'
3014
- return get_param($1) if str =~ /^param:(\w+)$/
3015
-
3016
- attribute, att_node, klass = get_attribute_and_node(str)
3017
-
3018
- return 'nil' unless attribute
3019
-
3020
-
3021
- att_node ||= opts[:node] || node
3022
- klass ||= opts[:node_class] || node_class
3023
-
3024
- real_attribute = attribute =~ /\Ad_/ ? attribute : attribute.gsub(/\A(|[\w_]+)id(s?)\Z/, '\1zip\2')
3025
-
3026
- res = if klass.ancestors.include?(Node)
3027
- if ['url','path'].include?(real_attribute)
3028
- # pseudo attribute 'url'
3029
- params = {}
3030
- params[:mode] = @params[:mode] if @params[:mode]
3031
- params[:format] = @params[:format] if @params[:format]
3032
- "zen_#{real_attribute}(#{node}#{params_to_erb(params)})"
3033
- elsif type = safe_method_type([real_attribute])
3034
- type[:method]
3035
- elsif type = klass.safe_method_type([real_attribute])
3036
- "#{att_node}.#{type[:method]}"
3037
- else
3038
- "#{att_node}.safe_read(#{real_attribute.inspect})"
3039
- end
3040
- elsif type = klass.safe_method_type([real_attribute])
3041
- "#{att_node}.#{type[:method]}"
3042
- else
3043
- # unknown class, resolve at runtime
3044
- "#{att_node}.safe_read(#{real_attribute.inspect})"
3045
- end
3046
-
3047
- res = "(#{res} || #{node_attribute(opts[:else])})" if opts[:else]
3048
- res = "(#{res} || #{opts[:default].inspect})" if opts[:default]
3049
- res
3050
- end
3051
-
3052
- # Add a class name to the html_tag
3053
- def add_html_class(class_name)
3054
- if klass = @html_tag_params[:class]
3055
- @html_tag_params[:class] = "#{class_name} #{klass}"
3056
- else
3057
- @html_tag_params[:class] = class_name
3058
- end
3059
- end
3060
-
3061
- def render_html_tag(text,*append)
3062
- append ||= []
3063
- return text if @html_tag_done
3064
- set_params = {}
3065
- if_params = {}
3066
- @params.each do |k,v|
3067
- if k.to_s =~ /^t?set_/
3068
- set_params[k] = v
3069
- end
3070
- end
3071
- tag_class = @html_tag_params[:class] || @params[:class]
3072
- if node_kind_of?(Node)
3073
-
3074
- if @context[:make_form]
3075
- node_name = node
3076
- elsif (@method == 'each' || @method == 'each_group') && @context[:list]
3077
- node_name = var
3078
- elsif @method == 'context'
3079
- node_name = @var || node
3080
- else
3081
- node_name = node
3082
- end
3083
-
3084
- class_cond = nil
3085
- @params.each do |k,v|
3086
- if k.to_s =~ /^(.+)_if$/
3087
- klass = $1
3088
- cond = get_test_condition(node_name, :test => v)
3089
- elsif k.to_s =~ /^(.+)_if_(test|node|kind_of|klass|status|lang|can|in)$/
3090
- klass = $1
3091
- cond = get_test_condition(node_name, $2.to_sym => v)
3092
- end
3093
- if cond
3094
- class_cond = "#{cond} ? \" class='#{klass}'\" : #{class_cond}" # (x = 3) ? "class='foo'" :
3095
- end
3096
- end
3097
-
3098
- if class_cond
3099
- append << "<%= #{class_cond}\"#{tag_class ? " class='#{tag_class}'" : ""}\" %>"
3100
- @html_tag_params.delete(:class)
3101
- end
3102
- end
3103
-
3104
- @html_tag = 'div' if !@html_tag && (set_params != {} || @html_tag_params != {})
3105
-
3106
- bak = @html_tag_params.dup
3107
- @html_tag_params = get_html_params(set_params.merge(@html_tag_params), @html_tag)
3108
- res = super(text,*append)
3109
- @html_tag_params = bak
3110
- res
3111
- end
3112
-
3113
- def link_to_update(target, opts = {})
3114
- method = opts[:method] || :get
3115
-
3116
- html_params = opts[:html_params] || {}
3117
- node_id = opts[:node_id] || self.node_id
3118
-
3119
- url = opts[:url] || "/#{base_class.to_s.pluralize.underscore}/\#{#{node_id}}#{method == :get ? '/zafu' : ''}"
3120
- opts[:cond] ||= "#{node}.can_write?" if method != :get
3121
-
3122
- query_params = [opts[:query_params]].flatten.compact
3123
-
3124
- if method == :get
3125
- if target
3126
- query_params << "t_url=#{CGI.escape(target.template_url)}"
3127
- query_params << "dom_id=#{target.dom_id}"
3128
- else
3129
- query_params << "dom_id=_page"
3130
- end
3131
- else
3132
- query_params << "t_url=#{CGI.escape(template_url)}" if method != :delete
3133
-
3134
- query_params << "dom_id=#{dom_id}"
3135
- if target != self
3136
- if target
3137
- query_params << "u_url=#{CGI.escape(target.template_url)}"
3138
- query_params << "udom_id=#{target.dom_id}"
3139
- else
3140
- query_params << "udom_id=_page"
3141
- end
3142
- end
3143
- end
3144
-
3145
- query_params << "link_id=\#{#{node}.link_id}" if @context[:need_link_id] && node_kind_of?(Node)
3146
- query_params << "node[v_status]=#{Zena::Status[:pub]}" if @params[:publish] # FIXME: this acts like publish = 'force'
3147
- query_params << start_node_s_param(:string)
3148
-
3149
- res = ''
3150
- res += "<% if #{opts[:cond]} -%>" if opts[:cond]
3151
- res += "<%= tag_to_remote({:url => \"#{url}?#{query_params.join('&')}\", :method => #{method.inspect}}#{params_to_erb(html_params)}) %>"
3152
- res += text_for_link(opts[:default_text])
3153
- res += "</a>"
3154
- if opts[:cond]
3155
- if opts[:else] != :void
3156
- res += "<% else -%>"
3157
- res += text_for_link(opts[:default_text])
3158
- end
3159
- res += "<% end -%>"
3160
- end
3161
- res
3162
- end
3163
-
3164
- def text_for_link(default = nil)
3165
- if @blocks.size > 1 || (@blocks.size == 1 && !(@blocks.first.kind_of?(String) || ['else','elsif'].include?(@blocks.first.method)))
3166
- expand_with
3167
- elsif default
3168
- default
3169
- elsif erb_text = get_text_for_erb(@params, false, :string)
3170
- erb_text
3171
- elsif node_kind_of?(Node)
3172
- "<%= #{node}.version.title %>"
3173
- elsif node_kind_of?(Version)
3174
- "<%= #{node}.title %>"
3175
- elsif node_kind_of?(Link)
3176
- "<%= #{node}.name %>"
3177
- else
3178
- _('edit')
3179
- end
3180
- end
3181
-
3182
- def get_text_for_erb(params = @params, use_blocks = true, context = :erb)
3183
- string_context = context == :string
3184
- if params[:attr]
3185
- string_context ? "<%= #{node_attribute(params[:attr])} %>" : node_attribute(params[:attr])
3186
- elsif params[:tattr]
3187
- string_context ? "<%= _(#{node_attribute(params[:tattr])}) %>" : "_(#{node_attribute(params[:tattr])})"
3188
- elsif params[:trans]
3189
- string_context ? _(params[:trans]) : _(params[:trans]).inspect
3190
- elsif params[:text]
3191
- string_context ? params[:text] : params[:text].inspect
3192
- elsif use_blocks && @blocks != []
3193
- res = []
3194
- text = ""
3195
- static = true
3196
- @blocks.each do |b|
3197
- # FIXME: this is a little too hacky
3198
- if b.kind_of?(String)
3199
- res << b.inspect
3200
- text << b
3201
- elsif ['show', 'img'].include?(b.method)
3202
- res << expand_block(b, :trans=>true)
3203
- static = false
3204
- elsif ['rename_asset', 'trans'].include?(b.method)
3205
- # FIXME: if a trans contains non-static: static should become false
3206
- res << expand_block(b).inspect
3207
- text << expand_block(b)
3208
- else
3209
- # ignore
3210
- end
3211
- end
3212
- if static
3213
- # "just plain text"
3214
- string_context ? text : text.inspect
3215
- else
3216
- # function(...) + "blah" + function()
3217
- string_context ? "<%= #{res.join(' + ')} %>" : res.join(' + ')
3218
- end
3219
- else
3220
- nil
3221
- end
3222
- end
3223
-
3224
- def get_input_params(params = @params)
3225
- res = {}
3226
- if res[:name] = (params[:name] || params[:date])
3227
- if res[:name] =~ /\A([\w_]+)\[(.*?)\]/
3228
- attribute, sub_attr = $1, $2
3229
- else
3230
- attribute = res[:name]
3231
- end
3232
-
3233
- unless @context[:in_filter] || attribute == 's'
3234
- if sub_attr
3235
- res[:name] = "#{base_class.to_s.underscore}[#{attribute}][#{sub_attr}]"
3236
- else
3237
- res[:name] = "#{base_class.to_s.underscore}[#{attribute}]"
3238
- end
3239
- end
3240
-
3241
- if sub_attr
3242
- if (nattr = node_attribute(attribute)) != 'nil'
3243
- nattr = "#{nattr}[#{sub_attr.inspect}]"
3244
- end
3245
- else
3246
- nattr = node_attribute(attribute)
3247
- end
3248
-
3249
- if sub_attr && params[:type] == 'checkbox' && !params[:value]
3250
- # Special case when we have a sub_attribute: default value for "tagged[foobar]" is "foobar"
3251
- params[:value] = sub_attr
3252
- end
3253
-
3254
- if @context[:in_add]
3255
- res[:value] = (params[:value] || params[:set_value]) ? ["'#{ helper.fquote(params[:value])}'"] : ["''"]
3256
- elsif @context[:in_filter]
3257
- res[:value] = attribute ? ["'<%= fquote params[#{attribute.to_sym.inspect}] %>'"] : ["''"]
3258
- elsif params[:value]
3259
- res[:value] = ["'#{ helper.fquote(params[:value])}'"]
3260
- else
3261
- if nattr != 'nil'
3262
- res[:value] = ["'<%= fquote #{nattr} %>'"]
3263
- else
3264
- res[:value] = ["''"]
3265
- end
3266
- end
3267
- end
3268
-
3269
- if @context[:dom_prefix]
3270
- res[:id] = "#{erb_dom_id}_#{attribute}"
3271
- else
3272
- res[:id] = params[:id] if params[:id]
3273
- end
3274
-
3275
- if params[:type] == 'checkbox' && nattr
3276
- if value = params[:value]
3277
- res[:checked] = "<%= #{nattr} == #{value.inspect} ? \" checked='checked'\" : '' %>"
3278
- else
3279
- res[:checked] = "<%= #{nattr}.blank? ? '' : \" checked='checked'\" %>"
3280
- end
3281
- end
3282
-
3283
- [:size, :style, :class].each do |k|
3284
- res[k] = params[k] if params[k]
3285
- end
3286
-
3287
- return [res, attribute]
3288
- end
3289
-
3290
- def get_html_params(params, tag_type)
3291
- res = {}
3292
- params.each do |k,v|
3293
- next unless v
3294
- if k.to_s =~ /\A(t?)set_(.+)$/
3295
- key = $2
3296
- trans = $1 == 't'
3297
- value, static = parse_attributes_in_value(v, :erb => !trans)
3298
-
3299
- if trans
3300
- if static
3301
- value = ["'#{_(value)}'"] # array so it is not escaped on render
3302
- else
3303
- value = ["'<%= _(\"#{value}\") %>'"] # FIXME: use dict ! array so it is not escaped on render
3304
- end
3305
- end
3306
- res[key.to_sym] = value
3307
- elsif tag_type == :link && ![:style, :class, :id, :title].include?(k)
3308
- # bad html parameter for links (some keys for link tags are used as query parameters)
3309
- # filter out
3310
- else
3311
- res[k] ||= v
3312
- end
3313
- end
3314
-
3315
- if params[:anchor]
3316
- @anchor_param = nil
3317
- res[:name] = anchor_name(params[:anchor], node)
3318
- end
3319
-
3320
- res
3321
- end
3322
-
3323
- def get_options_for_select
3324
- if nodes = @params[:nodes]
3325
- # TODO: dry with r_checkbox
3326
- if nodes =~ /^\d+\s*($|,)/
3327
- # ids
3328
- # TODO: optimization generate the full query instead of using secure.
3329
- nodes = nodes.split(',').map{|v| v.to_i}
3330
- nodes = "(secure(Node) { Node.find(:all, :conditions => 'zip IN (#{nodes.join(',')})') })"
3331
- else
3332
- # relation
3333
- nodes, klass = build_finder_for(:all, nodes)
3334
- return unless nodes
3335
- return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
3336
- end
3337
- set_attr = @params[:attr] || 'id'
3338
- show_attr = @params[:show] || 'name'
3339
- 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]}"
3340
- elsif values = @params[:values]
3341
- options_list = values.split(',').map(&:strip)
3342
-
3343
- if show = @params[:show]
3344
- show_values = show.split(',').map(&:strip)
3345
- elsif show = @params[:tshow]
3346
- show_values = show.split(',').map do |s|
3347
- _(s.strip)
3348
- end
3349
- end
3350
-
3351
- if show_values
3352
- options_list.each_index do |i|
3353
- options_list[i] = [show_values[i], options_list[i]]
3354
- end
3355
- end
3356
- options_list.inspect
3357
- end
3358
- end
3359
-
3360
- def parse_eval_parameter(str)
3361
- # evaluate an expression. Can only contain vars, '(', ')', '*', '+', '/', '-', '[attr]'
3362
- # FIXME: SECURITY (audit this)
3363
- vars = @context[:vars] || []
3364
- parts = str.split(/\s+/)
3365
- res = []
3366
- test = []
3367
- parts.each do |p|
3368
- if p =~ /\[([\w_]+)\]/
3369
- test << 1
3370
- res << (node_attribute($1) + '.to_f')
3371
- elsif p =~ /^[a-zA-Z_]+$/
3372
- unless vars.include?(p)
3373
- out parser_error("var #{p.inspect} not set in eval")
3374
- return nil
3375
- end
3376
- test << 1
3377
- res << "set_#{p}.to_f"
3378
- elsif ['(', ')', '*', '+', '/', '-'].include?(p)
3379
- res << p
3380
- test << p
3381
- elsif p =~ /^[0-9\.]+$/
3382
- res << p
3383
- test << p
3384
- else
3385
- out parser_error("bad argument #{p.inspect} in eval")
3386
- return nil
3387
- end
3388
- end
3389
- begin
3390
- begin
3391
- eval test.join(' ')
3392
- rescue
3393
- # rescue evaluation error
3394
- out parser_error("error in eval")
3395
- return nil
3396
- end
3397
- "(#{res.join(' ')})"
3398
- rescue SyntaxError => err
3399
- # rescue compilation error
3400
- out parser_error("compilation error in eval")
3401
- return nil
3402
- end
3403
- end
3404
-
3405
- def find_stored(klass, key)
3406
- if "#{klass}_#{key}" == "Node_start_node"
3407
- # main node before ajax stuff (the one in browser url)
3408
- "start_node"
3409
- else
3410
- @context["#{klass}_#{key}"]
3411
- end
3412
- end
3413
-
3414
- def set_stored(klass, key, obj)
3415
- @context["#{klass}_#{key}"] = obj
3416
- end
3417
-
3418
- # transform a 'show' tag into an input field.
3419
- def make_input(params = @params)
3420
- input, attribute = get_input_params(params)
3421
- return parser_error("missing 'name'") unless attribute
3422
- return '' if attribute == 'parent_id' # set with 'r_form'
3423
- return '' if ['url','path'].include?(attribute) # cannot be set with a form
3424
- if params[:date]
3425
- input_id = @context[:dom_prefix] ? ", :id=>\"#{dom_id}_#{attribute}\"" : ''
3426
- return "<%= date_box('#{base_class.to_s.underscore}', #{params[:date].inspect}#{input_id}) %>"
3427
- end
3428
- input_id = @context[:dom_prefix] ? " id='#{erb_dom_id}_#{attribute}'" : ''
3429
- "<input type='#{params[:type] || 'text'}'#{input_id} name='#{input[:name]}' value=#{input[:value]}/>"
3430
- end
3431
-
3432
- # transform a 'zazen' tag into a textarea input field.
3433
- def make_textarea(params)
3434
- return parser_error("missing 'name'") unless name = params[:name]
3435
- if name =~ /\A([\w_]+)\[(.*?)\]/
3436
- attribute = $2
3437
- else
3438
- attribute = name
3439
- name = "#{base_class.to_s.underscore}[#{attribute}]"
3440
- end
3441
- return '' if attribute == 'parent_id' # set with 'r_form'
3442
-
3443
- if @blocks == [] || @blocks == ['']
3444
- if @context[:in_add]
3445
- value = ''
3446
- else
3447
- value = attribute ? "<%= #{node_attribute(attribute)} %>" : ""
3448
- end
3449
- else
3450
- value = expand_with
3451
- end
3452
- html_id = @context[:dom_prefix] ? " id='#{erb_dom_id}_#{attribute}'" : ''
3453
- "<textarea#{html_id} name='#{name}'>#{value}</textarea>"
3454
- end
3455
-
3456
- def default_focus_field
3457
- if (input_fields = descendants('input')) != []
3458
- field = input_fields.first.params[:name]
3459
- elsif (show_fields = descendants('show')) != []
3460
- field = show_fields.first.params[:attr]
3461
- elsif node_kind_of?(Node)
3462
- field = 'v_title'
3463
- else
3464
- field = 'text'
3465
- end
3466
- end
3467
-
3468
- # Returns true if a form/edit needs to keep track of link_id (l_status or l_comment used).
3469
- def need_link_id
3470
- if (input_fields = (descendants('input') + descendants('select'))) != []
3471
- input_fields.each do |f|
3472
- return true if f.params[:name] =~ /\Al_/
3473
- end
3474
- elsif (show_fields = descendants('show')) != []
3475
- show_fields.each do |f|
3476
- return true if f.params[:attr] =~ /\Al_/
3477
- end
3478
- end
3479
- return false
3480
- end
3481
-
3482
- def start_node_s_param(type = :input)
3483
- if type == :input
3484
- "<input type='hidden' name='s' value='<%= params[:s] || @node[:zip] %>'/>"
3485
- elsif type == :erb
3486
- "s=<%= params[:s] || @node[:zip] %>"
3487
- elsif type == :value
3488
- "<%= params[:s] || @node[:zip] %>"
3489
- else
3490
- "s=\#{params[:s] || @node[:zip]}"
3491
- end
3492
- end
3493
-
3494
- def parser_error(message, tag=@method)
3495
- "<span class='parser_error'>[#{tag}] #{message}</span>"
3496
- end
3497
-
3498
- # Used by [each] and [draggable] to insert 'id' and drag handle span
3499
- def set_drag_handle_and_id(text, params, id_hash)
3500
- res, drag_handle = text, nil
3501
- if params[:drag_handle]
3502
- drag_handle = params[:drag_handle] == 'true' ? 'drag_handle' : params[:drag_handle]
3503
- if text =~ /class\s*=\s*['"]#{drag_handle}/
3504
- # nothing to do
3505
- insert = ''
3506
- else
3507
- insert = "<span class='#{drag_handle}'>&nbsp;</span>"
3508
- end
3509
- else
3510
- insert = ''
3511
- end
3512
-
3513
- res = insert + text
3514
-
3515
- if id_hash
3516
- @html_tag ||= 'div'
3517
- @html_tag_params.merge!(id_hash)
3518
- end
3519
-
3520
- [res, drag_handle]
3521
- end
3522
-
3523
- def expand_with(acontext={})
3524
- # set variables
3525
- context = nil
3526
- pre = ''
3527
- @blocks.each do |block|
3528
- next if block.kind_of?(String) || block.method != 'set'
3529
- @context[:vars] ||= []
3530
- context ||= @context.merge(acontext).merge(:set => true)
3531
- pre << expand_block(block, context)
3532
- @context[:vars] << block.params[:var]
3533
- end
3534
-
3535
- pre + super
3536
- end
3537
-
3538
- def auto_publish_param(in_string = false)
3539
- if in_string
3540
- ['true','force'].include?(@params[:publish]) ? "&publish=#{@params[:publish]}" : ''
3541
- else
3542
- @params[:publish]
3543
- end
3544
- end
3545
-
3546
- # This is used by zafu and it's a mess.
3547
- # ref_date can be a string ('2005-05-03') or ruby ('Time.now'). It should not come uncleaned from evil web.
3548
- def date_condition(date_cond, field, ref_date='today')
3549
- if date_cond == 'today' || ref_date == 'today'
3550
- ref_date = Zena::Db::NOW
3551
- elsif ref_date =~ /(\d{4}-\d{1,2}-\d{1,2}( \d{1,2}:\d{1,2}(:\d{1,2})?)?)/
3552
- ref_date = "'#{$1}'"
3553
- elsif ref_date =~ /\A"/
3554
- ref_date = "'\#{format_date(#{ref_date})}'"
3555
- else
3556
- ref_date = "'\#{#{ref_date}.strftime('%Y-%m-%d %H:%M:%S')}'"
3557
- end
3558
- Zena::Db.date_condition(date_cond, field, ref_date)
3559
- end
3560
- end # ZenaTags
3561
- end # Parser
3562
- end # Zena