zena 0.15.2 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
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