zena 0.15.2 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. data/.gitignore +20 -0
  2. data/CREDITS +27 -0
  3. data/Capfile +3 -0
  4. data/DEVELOPERS +46 -0
  5. data/History.txt +15 -0
  6. data/MIT-LICENSE +19 -0
  7. data/Rakefile +44 -0
  8. data/TODO +24 -0
  9. data/TODO_ZENA_1_0 +23 -0
  10. data/app/controllers/application_controller.rb +3 -0
  11. data/app/controllers/documents_controller.rb +22 -56
  12. data/app/controllers/nodes_controller.rb +42 -27
  13. data/app/controllers/pings_controller.rb +19 -0
  14. data/app/controllers/relations_controller.rb +5 -1
  15. data/app/controllers/sites_controller.rb +1 -46
  16. data/app/controllers/user_sessions_controller.rb +47 -0
  17. data/app/controllers/users_controller.rb +1 -0
  18. data/app/controllers/versions_controller.rb +25 -7
  19. data/app/controllers/virtual_classes_controller.rb +1 -1
  20. data/app/helpers/application_helper.rb +1 -1
  21. data/app/models/comment.rb +2 -1
  22. data/app/models/contact_content.rb +2 -2
  23. data/app/models/data_entry.rb +5 -6
  24. data/app/models/document.rb +14 -10
  25. data/app/models/document_content.rb +4 -6
  26. data/app/models/iformat.rb +2 -2
  27. data/app/models/image_content.rb +6 -9
  28. data/app/models/node.rb +106 -164
  29. data/app/models/page.rb +0 -20
  30. data/app/models/site.rb +42 -12
  31. data/app/models/template.rb +3 -8
  32. data/app/models/template_content.rb +2 -0
  33. data/app/models/text_document.rb +13 -8
  34. data/app/models/user.rb +47 -100
  35. data/app/models/user_session.rb +4 -0
  36. data/app/models/version.rb +1 -1
  37. data/app/views/comments/create.rjs +3 -3
  38. data/app/views/comments/edit.rjs +1 -1
  39. data/app/views/comments/update.rjs +1 -1
  40. data/app/views/nodes/_import_results.rhtml +1 -1
  41. data/app/views/nodes/create.rjs +3 -3
  42. data/app/views/templates/document_create_tabs/_file.rhtml +1 -2
  43. data/app/views/templates/document_create_tabs/_import.rhtml +7 -2
  44. data/app/views/templates/edit_tabs/_document.rhtml +1 -3
  45. data/app/views/templates/edit_tabs/_image.rhtml +1 -3
  46. data/app/views/versions/_tr.rhtml +1 -1
  47. data/app/views/versions/edit.rhtml +2 -26
  48. data/bin/zena +6 -1
  49. data/bricks/delayed_job/README +18 -0
  50. data/bricks/delayed_job/migrate/20091104191643_create_delayed_jobs_table.rb +19 -0
  51. data/bricks/delayed_job/misc/init.rb +8 -0
  52. data/bricks/delayed_job/misc/tasks.rb +2 -0
  53. data/bricks/math/patch/application_helper.rb +1 -1
  54. data/bricks/sphinx/MIT-LICENSE +19 -0
  55. data/bricks/sphinx/README +19 -0
  56. data/bricks/sphinx/lib/use_sphinx.rb +78 -0
  57. data/bricks/sphinx/migrate/20091102171258_add_delta_for_sphinx.rb +9 -0
  58. data/bricks/sphinx/misc/deploy.rb +20 -0
  59. data/bricks/sphinx/misc/sphinx.yml +12 -0
  60. data/bricks/sphinx/misc/tasks.rb +21 -0
  61. data/bricks/sphinx/patch/node.rb +8 -0
  62. data/bricks/tags/lib/has_tags.rb +5 -3
  63. data/bricks/tags/test/zafu/tags.yml +13 -1
  64. data/config/bricks.yml +35 -0
  65. data/config/deploy.rb +8 -1
  66. data/config/environment.rb +1 -1
  67. data/config/environments/production.rb +1 -1
  68. data/config/gems.yml +28 -5
  69. data/config/sphinx.yml +12 -0
  70. data/db/init/base/skins/default/Node-+popupLayout.zafu +1 -16
  71. data/db/migrate/20091026161708_add_persistence_token.rb +13 -0
  72. data/db/migrate/20091101184952_add_session_table.rb +16 -0
  73. data/db/migrate/20091123175137_add_single_access_token.rb +9 -0
  74. data/db/migrate/20091124161608_rebuild_fullpath.rb +11 -0
  75. data/db/schema.rb +21 -8
  76. data/doc/README_FOR_APP +24 -0
  77. data/doc/fixtures.graffle +19568 -0
  78. data/doc/fixtures.pdf +0 -0
  79. data/doc/template/LICENSE +184 -0
  80. data/doc/template/README +37 -0
  81. data/doc/template/allison.css +283 -0
  82. data/doc/template/allison.js +307 -0
  83. data/doc/template/allison.rb +260 -0
  84. data/doc/template/cache/BODY +588 -0
  85. data/doc/template/cache/CLASS_INDEX +4 -0
  86. data/doc/template/cache/CLASS_PAGE +1 -0
  87. data/doc/template/cache/FILE_INDEX +4 -0
  88. data/doc/template/cache/FILE_PAGE +1 -0
  89. data/doc/template/cache/FONTS +1 -0
  90. data/doc/template/cache/FR_INDEX_BODY +1 -0
  91. data/doc/template/cache/IMGPATH +1 -0
  92. data/doc/template/cache/INDEX +1 -0
  93. data/doc/template/cache/JAVASCRIPT +307 -0
  94. data/doc/template/cache/METHOD_INDEX +4 -0
  95. data/doc/template/cache/METHOD_LIST +1 -0
  96. data/doc/template/cache/SRC_PAGE +1 -0
  97. data/doc/template/cache/STYLE +283 -0
  98. data/doc/template/cache/URL +1 -0
  99. data/doc/zafu_changes.yml +29 -0
  100. data/lib/base_additions.rb +1 -1
  101. data/lib/bricks.rb +9 -0
  102. data/lib/bricks/loader.rb +86 -0
  103. data/lib/bricks/requirements_validation.rb +71 -0
  104. data/lib/tasks/zena.rake +42 -4
  105. data/lib/zafu/action.rb +285 -0
  106. data/lib/zafu/ajax.rb +93 -0
  107. data/lib/zafu/attributes.rb +117 -0
  108. data/lib/zafu/calendar.rb +159 -0
  109. data/lib/zafu/context.rb +330 -0
  110. data/lib/zafu/core/html.rb +102 -0
  111. data/lib/zafu/core/move_to_parser.rb +167 -0
  112. data/lib/zafu/dates.rb +58 -0
  113. data/lib/zafu/display.rb +502 -0
  114. data/lib/zafu/eval.rb +66 -0
  115. data/lib/zafu/experimental.rb +66 -0
  116. data/lib/zafu/i18n.rb +64 -0
  117. data/lib/zafu/meta.rb +25 -0
  118. data/lib/zafu/refactor.rb +73 -0
  119. data/lib/zafu/support/context.rb +265 -0
  120. data/lib/zafu/support/dom.rb +145 -0
  121. data/lib/zafu/support/erb.rb +62 -0
  122. data/lib/zafu/support/flow.rb +401 -0
  123. data/lib/zafu/support/forms.rb +461 -0
  124. data/lib/zafu/support/links.rb +306 -0
  125. data/lib/zafu_parser.rb +26 -2
  126. data/lib/zena.rb +34 -15
  127. data/lib/zena/acts/multiversion.rb +2 -2
  128. data/lib/zena/acts/secure.rb +41 -30
  129. data/lib/zena/app.rb +7 -10
  130. data/lib/zena/controller/test_case.rb +12 -7
  131. data/lib/zena/crypto_provider/initial.rb +15 -0
  132. data/lib/zena/db.rb +6 -1
  133. data/lib/zena/deploy.rb +34 -6
  134. data/lib/zena/deploy/logrotate_app.rhtml +9 -0
  135. data/lib/zena/deploy/logrotate_host.rhtml +34 -0
  136. data/lib/zena/deploy/template.rb +1 -9
  137. data/lib/zena/foxy_parser.rb +1 -1
  138. data/lib/zena/info.rb +3 -1
  139. data/lib/zena/migrator.rb +1 -1
  140. data/lib/zena/parser.rb +12 -4
  141. data/lib/zena/parser/zazen_rules.rb +6 -6
  142. data/lib/zena/parser/zena_rules.rb +1 -7
  143. data/lib/zena/routes.rb +5 -5
  144. data/lib/zena/test_controller.rb +7 -2
  145. data/lib/zena/unit/test_case.rb +6 -8
  146. data/lib/zena/use/ajax.rb +10 -10
  147. data/lib/zena/use/authlogic.rb +93 -0
  148. data/lib/zena/use/dyn_attributes.rb +5 -0
  149. data/lib/zena/use/html_tags.rb +16 -34
  150. data/lib/zena/use/i18n.rb +4 -1
  151. data/lib/zena/use/node_query_finders.rb +8 -4
  152. data/lib/zena/use/refactor.rb +8 -20
  153. data/lib/zena/use/relations.rb +1 -0
  154. data/lib/zena/use/rendering.rb +4 -2
  155. data/lib/zena/use/search.rb +52 -0
  156. data/lib/zena/use/test_helper.rb +27 -28
  157. data/lib/zena/use/upload.rb +188 -0
  158. data/lib/zena/use/urls.rb +16 -14
  159. data/lib/zena/use/zafu.rb +16 -63
  160. data/lib/zena/use/zazen.rb +8 -8
  161. data/lib/zena/view/test_case.rb +8 -4
  162. data/locale/en/LC_MESSAGES/zena.mo +0 -0
  163. data/locale/en/zena.po +3 -3
  164. data/public/.htaccess +40 -0
  165. data/public/javascripts/upload-progress.js +17 -8
  166. data/public/javascripts/zena.js +8 -2
  167. data/public/stylesheets/popup.css +1 -0
  168. data/script/about +3 -0
  169. data/script/apache_logging +25 -0
  170. data/script/breakpointer +3 -0
  171. data/script/console +3 -0
  172. data/script/dbconsole +3 -0
  173. data/script/destroy +3 -0
  174. data/script/generate +3 -0
  175. data/script/performance/benchmarker +3 -0
  176. data/script/performance/profiler +3 -0
  177. data/script/plugin +3 -0
  178. data/script/process/inspector +3 -0
  179. data/script/process/reaper +3 -0
  180. data/script/process/spawner +3 -0
  181. data/script/runner +3 -0
  182. data/script/server +3 -0
  183. data/script/set_revision +29 -0
  184. data/spec/controllers/versions_controller_spec.rb +11 -0
  185. data/test/fixtures/files/Node-test.zafu +1 -1
  186. data/test/functional/nodes_controller_test.rb +25 -0
  187. data/test/functional/pings_controller_test.rb +8 -0
  188. data/test/functional/user_sessions_controller_test.rb +59 -0
  189. data/test/functional/users_controller_test.rb +81 -19
  190. data/test/helpers/node_query/filters.yml +5 -0
  191. data/test/helpers/node_query_test.rb +3 -3
  192. data/test/integration/multiple_hosts_test.rb +1 -1
  193. data/test/integration/navigation_test.rb +1 -1
  194. data/test/sites/complex/users.yml +1 -1
  195. data/test/sites/ocean/users.yml +3 -3
  196. data/test/sites/zena/users.yml +5 -4
  197. data/test/test_zena.rb +38 -38
  198. data/test/unit/cached_page_test.rb +2 -2
  199. data/test/unit/comment_test.rb +0 -1
  200. data/test/unit/document_test.rb +23 -11
  201. data/test/unit/helpers/ping_helper_test.rb +4 -0
  202. data/test/unit/multiversion_test.rb +24 -16
  203. data/test/unit/node_test.rb +32 -93
  204. data/test/unit/note_test.rb +9 -0
  205. data/test/unit/page_test.rb +2 -2
  206. data/test/unit/secure_test.rb +2 -12
  207. data/test/unit/site_test.rb +43 -24
  208. data/test/unit/template_test.rb +45 -3
  209. data/test/unit/text_document_test.rb +4 -3
  210. data/test/unit/user_test.rb +13 -33
  211. data/test/unit/zena/db_test.rb +8 -0
  212. data/test/unit/zena/parser/zazen.yml +4 -4
  213. data/test/unit/zena/use/dates_view_methods_test.rb +2 -1
  214. data/test/unit/zena/use/html_tags_test.rb +12 -4
  215. data/test/unit/zena/use/refactor_test.rb +4 -3
  216. data/test/unit/zena/use/rendering_test.rb +1 -0
  217. data/test/unit/zena/use/upload_test.rb +76 -0
  218. data/test/unit/zena/use/urls_test.rb +4 -0
  219. data/test/unit/zena/use/zafu_test.rb +8 -0
  220. data/test/unit/zena/workflow/status_version_test.rb +6 -0
  221. data/test/unit/zena/zena_tags/ajax.yml +4 -4
  222. data/test/unit/zena/zena_tags/basic.yml +21 -10
  223. data/test/unit/zena/zena_tags/relations.yml +0 -6
  224. data/test/unit/zena/zena_tags/rubyless.yml +35 -0
  225. data/test/unit/zena/zena_tags/zazen.yml +4 -4
  226. data/test/unit/zena/zena_tags_test.rb +36 -4
  227. data/vendor/TextMate/Zena.tmbundle/Commands/Run all yaml tests.tmCommand +1 -1
  228. data/vendor/TextMate/Zena.tmbundle/Commands/Run focused yaml test.tmCommand +2 -3
  229. data/vendor/TextMate/Zena.tmbundle/Support/RubyMate/catch_exception.rb +39 -0
  230. data/vendor/TextMate/Zena.tmbundle/Support/RubyMate/run_script.rb +102 -58
  231. data/vendor/TextMate/Zena.tmbundle/Support/RubyMate/stdin_dialog.rb +14 -0
  232. data/vendor/TextMate/Zena.tmbundle/info.plist +2 -0
  233. data/zena.gemspec +2085 -0
  234. metadata +265 -90
  235. data/app/controllers/sessions_controller.rb +0 -41
  236. data/app/views/sites/zena_up.html.erb +0 -11
  237. data/config/database.yml +0 -40
  238. data/db/production.sqlite3 +0 -0
  239. data/lib/bricks/patcher.rb +0 -68
  240. data/lib/zena/parser/zena_tags.rb +0 -3562
  241. data/lib/zena/use/authentification.rb +0 -120
  242. data/public/images/ext/contact_pv.png +0 -0
  243. data/public/images/ext/other_pv.png +0 -0
  244. data/public/images/ext/page_pv.png +0 -0
  245. data/public/images/ext/page_tiny.png +0 -0
  246. data/public/images/ext/pdf_pv.png +0 -0
  247. data/public/images/ext/post_pv.png +0 -0
  248. data/public/images/ext/post_tiny.png +0 -0
  249. data/public/images/ext/project_pv.png +0 -0
  250. data/public/images/ext/project_tiny.png +0 -0
  251. data/public/images/ext/tag_pv.png +0 -0
  252. data/public/images/ext/zip_pv.png +0 -0
  253. data/tasks/ann.rake +0 -80
  254. data/tasks/bones.rake +0 -20
  255. data/tasks/gem.rake +0 -201
  256. data/tasks/git.rake +0 -40
  257. data/tasks/notes.rake +0 -27
  258. data/tasks/post_load.rake +0 -34
  259. data/tasks/rdoc.rake +0 -51
  260. data/tasks/rubyforge.rake +0 -55
  261. data/tasks/setup.rb +0 -292
  262. data/tasks/spec.rake +0 -54
  263. data/tasks/svn.rake +0 -47
  264. data/tasks/test.rake +0 -40
  265. data/tasks/zentest.rake +0 -36
  266. data/test/fixtures/comments.yml +0 -126
  267. data/test/fixtures/contact_contents.yml +0 -132
  268. data/test/fixtures/data_entries.yml +0 -65
  269. data/test/fixtures/discussions.yml +0 -48
  270. data/test/fixtures/document_contents.yml +0 -108
  271. data/test/fixtures/dyn_attributes.yml +0 -66
  272. data/test/fixtures/groups.yml +0 -86
  273. data/test/fixtures/groups_users.yml +0 -81
  274. data/test/fixtures/iformats.yml +0 -29
  275. data/test/fixtures/links.yml +0 -313
  276. data/test/fixtures/nodes.yml +0 -2592
  277. data/test/fixtures/relations.yml +0 -126
  278. data/test/fixtures/sites.yml +0 -58
  279. data/test/fixtures/template_contents.yml +0 -172
  280. data/test/fixtures/users.yml +0 -167
  281. data/test/fixtures/versions.yml +0 -1911
  282. data/test/fixtures/virtual_classes.yml +0 -87
  283. data/test/fixtures/zips.yml +0 -15
  284. data/test/functional/sessions_controller_test.rb +0 -73
@@ -0,0 +1,93 @@
1
+ module Zafu
2
+ module Ajax
3
+ # TODO: write a test (please)
4
+ # 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.
5
+ # Define a block of elements to be used by ajax calls (edit/filter)
6
+ def r_block
7
+ if @context[:block] == self
8
+ # called from self (storing template)
9
+ @context.reject! do |k,v|
10
+ # FIXME: reject all stored elements in a better way then this
11
+ k.kind_of?(String) && k =~ /\ANode_\w/
12
+ end
13
+ @html_tag_done = false
14
+ @html_tag_params.merge!(:id=>erb_dom_id)
15
+ @context[:scope_node] = node if @context[:scope_node]
16
+ out expand_with(:node => node)
17
+ if @method == 'drop' && !@context[:make_form]
18
+ out drop_javascript
19
+ end
20
+ else
21
+ if parent.method == 'each' && @method == parent.single_child_method
22
+ # use parent as block
23
+ # FIXME: will not work with block as distant target...
24
+ # do nothing
25
+ else
26
+ @html_tag ||= 'div'
27
+ new_dom_scope
28
+
29
+ unless @context[:make_form]
30
+ # STORE TEMPLATE ========
31
+
32
+ context_bak = @context.dup # avoid side effects when rendering the same block
33
+ ignore_list = @method == 'block' ? ['form'] : [] # do not show the form in the normal template of a block
34
+ template = expand_block(self, :block=>self, :list=>false, :saved_template=>true, :ignore => ignore_list)
35
+ @context = context_bak
36
+ @result = ''
37
+ out helper.save_erb_to_url(template, template_url)
38
+
39
+ # STORE FORM ============
40
+ if edit = descendant('edit')
41
+ publish_after_save = (edit.params[:publish] == 'true')
42
+ if form = descendant('form')
43
+ # USE BLOCK FORM ========
44
+ form_text = expand_block(form, :saved_template=>true, :publish_after_save => publish_after_save)
45
+ else
46
+ # MAKE A FORM FROM BLOCK ========
47
+ form = self.dup
48
+ form.method = 'form'
49
+ form_text = expand_block(form, :make_form => true, :list => false, :saved_template => true, :publish_after_save => publish_after_save)
50
+ end
51
+ out helper.save_erb_to_url(form_text, form_url)
52
+ end
53
+ end
54
+
55
+ # RENDER
56
+ @html_tag_done = false
57
+ @html_tag_params.merge!(:id=>erb_dom_id)
58
+ end
59
+
60
+ out expand_with
61
+ if @method == 'drop' && !@context[:make_form]
62
+ out drop_javascript
63
+ end
64
+ end
65
+ end
66
+
67
+ protected
68
+ # Used by [each] and [draggable] to insert 'id' and drag handle span
69
+ def set_drag_handle_and_id(text, params, id_hash)
70
+ res, drag_handle = text, nil
71
+ if params[:drag_handle]
72
+ drag_handle = params[:drag_handle] == 'true' ? 'drag_handle' : params[:drag_handle]
73
+ if text =~ /class\s*=\s*['"]#{drag_handle}/
74
+ # nothing to do
75
+ insert = ''
76
+ else
77
+ insert = "<span class='#{drag_handle}'>&nbsp;</span>"
78
+ end
79
+ else
80
+ insert = ''
81
+ end
82
+
83
+ res = insert + text
84
+
85
+ if id_hash
86
+ @html_tag ||= 'div'
87
+ @html_tag_params.merge!(id_hash)
88
+ end
89
+
90
+ [res, drag_handle]
91
+ end
92
+ end # Ajax
93
+ end # Zafu
@@ -0,0 +1,117 @@
1
+ module Zafu
2
+ # All this should be replaced by RubyLess
3
+ module Attributes
4
+ include RubyLess::SafeClass
5
+
6
+ PSEUDO_ATTRIBUTES = {
7
+ 'now' => 'Time.now',
8
+ 'start.id' => '(params[:s] || @node[:zip])',
9
+ 'nil' => 'nil',
10
+ }
11
+
12
+
13
+ def node_attribute(str, opts={})
14
+
15
+ if @context[:vars] && @context[:vars].include?(str)
16
+ return "set_#{str}"
17
+ end
18
+
19
+ res = PSEUDO_ATTRIBUTES[str]
20
+ return res if res
21
+ return current_date if str == 'current_date'
22
+ return get_param($1) if str =~ /^param:(\w+)$/
23
+
24
+ attribute, att_node, klass = get_attribute_and_node(str)
25
+
26
+ return 'nil' unless attribute
27
+
28
+
29
+ att_node ||= opts[:node] || node
30
+ klass ||= opts[:node_class] || node_class
31
+
32
+ real_attribute = attribute =~ /\Ad_/ ? attribute : attribute.gsub(/\A(|[\w_]+)id(s?)\Z/, '\1zip\2')
33
+
34
+ if klass.ancestors.include?(Node)
35
+ if ['url','path'].include?(real_attribute)
36
+ # pseudo attribute 'url'
37
+ params = {}
38
+ params[:mode] = @params[:mode] if @params[:mode]
39
+ params[:format] = @params[:format] if @params[:format]
40
+ res = "zen_#{real_attribute}(#{node}#{params_to_erb(params)})"
41
+ elsif type = safe_method_type([real_attribute])
42
+ res = type[:method]
43
+ elsif type = klass.safe_method_type([real_attribute])
44
+ res = "#{att_node}.#{type[:method]}"
45
+ else
46
+ res = "#{att_node}.safe_read(#{real_attribute.inspect})"
47
+ end
48
+ elsif type = RubyLess::SafeClass.safe_method_type_for(klass, [real_attribute])
49
+ res = "#{att_node}.#{type[:method]}"
50
+ elsif klass.instance_methods.include?('safe_read')
51
+ # Unknown method but safe class: can resolve at runtime
52
+ res = "#{att_node}.safe_read(#{real_attribute.inspect})"
53
+ else
54
+ out parser_error("#{klass} does not respond to #{real_attribute.inspect}")
55
+ return 'nil'
56
+ end
57
+
58
+ res = "(#{res} || #{node_attribute(opts[:else])})" if opts[:else]
59
+ res = "(#{res} || #{opts[:default].inspect})" if opts[:default]
60
+ res
61
+ end
62
+
63
+ def parse_attributes_in_value(v, opts = {})
64
+ opts = {:erb => true}.merge(opts)
65
+ static = true
66
+
67
+ use_node = @var || node
68
+ res = v.gsub(/\[([^\]]+)\]/) do
69
+ static = false
70
+ res = nil
71
+ attribute = $1
72
+
73
+ if opts[:skip_node_attributes]
74
+ if attribute =~ /^param:(\w+)$/
75
+ attribute = get_param($1)
76
+ elsif attribute == 'current_date'
77
+ attribute = current_date
78
+ else
79
+ res = "[#{attribute}]"
80
+ end
81
+ else
82
+ attribute = node_attribute(attribute, :node => use_node )
83
+ end
84
+
85
+ res ||= if opts[:erb]
86
+ "<%= #{attribute} %>"
87
+ else
88
+ "\#{#{attribute}}"
89
+ end
90
+ res
91
+ end
92
+ [res, static]
93
+ end
94
+
95
+ def get_attribute_and_node(str)
96
+ if str =~ /([^\.]+)\.(.+)/
97
+ node_name = $1
98
+ node_attr = $2
99
+ if att_node = find_stored(Node, node_name)
100
+ return [node_attr, att_node, Node]
101
+ elsif node_name == 'main'
102
+ return [node_attr, '@node', Node]
103
+ elsif node_name == 'visitor'
104
+ return [node_attr, 'visitor.contact', Contact]
105
+ elsif node_name == 'site'
106
+ return [node_attr, 'current_site', Site]
107
+ else
108
+ out parser_error("invalid node name #{node_name.inspect} in attribute #{str.inspect}")
109
+ return [nil]
110
+ end
111
+ else
112
+ return [str]
113
+ end
114
+ end
115
+
116
+ end # Attributes
117
+ end # Zafu
@@ -0,0 +1,159 @@
1
+ module Zafu
2
+ module Calendar
3
+
4
+ def r_calendar
5
+ if @context[:block] == self
6
+ # called from self (storing template / rendering)
7
+ if role = @params[:assign_as]
8
+ assign_calendar(role)
9
+ else
10
+ display_calendar
11
+ end
12
+ else
13
+ # This is called first to prepare calendar
14
+ if @params[:assign_as]
15
+ fld = 'date'
16
+ table_name = 'links'
17
+ else
18
+ fld = @params[:date] || 'event_at'
19
+ if ['log_at', 'created_at', 'updated_at', 'event_at'].include?(fld) # TODO: use rubyless to learn type
20
+ table_name = 'nodes'
21
+ elsif fld == 'l_date'
22
+ fld = 'date'
23
+ table_name = 'links'
24
+ else
25
+ return parser_error("Invalid 'date' value for calendar (#{fld.inspect}).")
26
+ end
27
+ end
28
+
29
+ @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')}'"
30
+
31
+ new_dom_scope
32
+
33
+ # SAVED TEMPLATE
34
+ template = expand_block(self, :block => self, :saved_template => true)
35
+ out helper.save_erb_to_url(template, template_url)
36
+
37
+ # INLINE
38
+ out expand_block(self, :block => self, :saved_template => false)
39
+ end
40
+ end
41
+
42
+ private
43
+ def display_calendar
44
+ size = (params[:size] || 'large').to_sym
45
+ finder = params[:select] || 'notes in project'
46
+
47
+ if @blocks == []
48
+ # add a default <r:link/> block
49
+ if size == :tiny
50
+ @blocks = [make(:void, :method=>'void', :text=>"<em do='link' date='current_date' do='[current_date]' format='%d'/><r:else do='[current_date]' format='%d'/>")]
51
+ else
52
+ @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'/>")]
53
+ end
54
+ remove_instance_variable(:@all_descendants)
55
+ elsif !descendant('else')
56
+ @blocks += [make(:void, :method=>'void', :text=>"<r:else do='[current_date]' format='%d'/>")]
57
+ remove_instance_variable(:@all_descendants)
58
+ end
59
+
60
+ @html_tag_done = false
61
+ @html_tag_params[:id] = erb_dom_id
62
+ @html_tag_params[:class] ||= "#{size}cal"
63
+ @html_tag ||= 'div'
64
+
65
+ finder, klass = build_finder_for(:all, finder, @params, [@date_scope])
66
+ return unless finder
67
+ return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
68
+
69
+ 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 -%>"
70
+
71
+ render_html_tag(calendar_code(finder, "", cell_code, "", params))
72
+ end
73
+
74
+ # manage links from @node ---- reference ----> ...
75
+ # <div do='calendar' assign='reference' to='main' split_hours='12' />
76
+ def assign_calendar(as_role)
77
+ size = (params[:size] || 'large').to_sym
78
+ @html_tag_done = false
79
+ @html_tag_params[:id] = erb_dom_id
80
+ @html_tag_params[:class] ||= "#{size}cal"
81
+ @html_tag ||= 'div'
82
+ if rel = RelationProxy.find_by_role(as_role.singularize)
83
+ role = rel.this_role
84
+ else
85
+ return parser_error("Invalid role #{as_role.inspect}.")
86
+ end
87
+ finder, klass = build_finder_for(:all, role, @params, [@date_scope])
88
+ return unless finder
89
+ return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
90
+
91
+ # SAVED TEMPLATE ========
92
+ template_url = self.template_url + 'cell'
93
+ template = "<%= cal_assign_cell(@node, #{role.inspect}, #{@params[:used].inspect}) %>"
94
+ out helper.save_erb_to_url(template, template_url)
95
+
96
+ # we call update on node 'B'
97
+ # A (main)
98
+ # ... B (other node)
99
+ # calendar (in B context) ---- role --->
100
+
101
+ cell_prefix_code = "<span><%= day_#{list_var}.strftime('%d').to_i -%></span><ul>"
102
+ 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}) %>"
103
+ cell_postfix_code = "</ul>"
104
+ render_html_tag(calendar_code(finder, cell_prefix_code, cell_code, cell_postfix_code, params))
105
+ end
106
+
107
+ def calendar_code(finder, cell_prefix_code, cell_code, cell_postfix_code, params)
108
+ type = params[:type] ? params[:type].to_sym : :month
109
+ size = (params[:size] || 'large').to_sym
110
+ ref_date = params[:assign_as] ? 'l_date' : (params[:date] || 'event_at')
111
+
112
+ case type
113
+ when :month
114
+ title = "\"\#{_(Date::MONTHNAMES[main_date.mon])} \#{main_date.year}\""
115
+ prev_date = "\#{main_date.advance(:months => -1).strftime(\"%Y-%m-%d\")}"
116
+ next_date = "\#{main_date.advance(:months => 1).strftime(\"%Y-%m-%d\")}"
117
+ when :week
118
+ title = "\"\#{_(Date::MONTHNAMES[main_date.mon])} \#{main_date.year}\""
119
+ prev_date = "\#{main_date.advance(:days => -7).strftime(\"%Y-%m-%d\")}"
120
+ next_date = "\#{main_date.advance(:days => +7).strftime(\"%Y-%m-%d\")}"
121
+ else
122
+ return parser_error("invalid type (should be 'month' or 'week')")
123
+ end
124
+
125
+ if hours = @params[:split_hours]
126
+ hours = hours.split(',').map{|l| l.to_i}
127
+ hours << 0
128
+ hours = hours.uniq.sort
129
+ # I feel all this would be much better if we could use "each_group" but then how do we access hours ?
130
+ week_code = "<% week.step(week+6,1) do |day_#{list_var}| -%>
131
+ <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>
132
+ <% end -%>"
133
+ (@context[:vars] ||= []) << "hour"
134
+ else
135
+ hours = nil
136
+ week_code = "<% week.step(week+6,1) do |day_#{list_var}| -%>
137
+ <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>
138
+ <% end -%>"
139
+ end
140
+
141
+ res = <<-END_TXT
142
+ <h3 class='title'>
143
+ <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>
144
+ <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>
145
+ <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>
146
+ </h3>
147
+ <table cellspacing='0' class='#{size}cal#{@params[:assign_as] ? " assign" : ''}'>
148
+ <tr class='head'><%= cal_day_names(#{size.inspect}) %></tr>
149
+ <% start_date, end_date = cal_start_end(#{current_date}, #{type.inspect}) -%>
150
+ <% cal_weeks(#{ref_date.to_sym.inspect}, #{finder}, start_date, end_date, #{hours.inspect}) do |week, nodes_#{list_var}| -%>
151
+ <tr class='body'>
152
+ #{week_code}
153
+ </tr>
154
+ <% end -%>
155
+ </table>
156
+ END_TXT
157
+ end
158
+ end # Calendar
159
+ end # Zafu
@@ -0,0 +1,330 @@
1
+ module Zafu
2
+ module Context
3
+ # TODO: test, rename ?
4
+ def r_search_results
5
+ pagination_key = 'page'
6
+ out "<% set_#{pagination_key}_nodes = @search_count; set_#{pagination_key}_count = (set_#{pagination_key}_nodes / @search_per_page).ceil; set_#{pagination_key} = [1,params[:page].to_i].max -%>"
7
+ @context[:vars] ||= []
8
+ @context[:vars] << "#{pagination_key}_nodes"
9
+ @context[:vars] << "#{pagination_key}_count"
10
+ @context[:vars] << pagination_key
11
+ @context[:paginate] = pagination_key
12
+ do_list('@nodes')
13
+ end
14
+
15
+ # FIXME: replace by rubyless declarations
16
+ def r_comments_to_publish
17
+ open_context(:method => 'visitor.comments_to_publish', :class => [Comment])
18
+ end
19
+
20
+ def r_to_publish
21
+ open_context(:method => 'visitor.to_publish', :class => [Version])
22
+ end
23
+
24
+ def r_proposed
25
+ open_context(:method => 'visitor.proposed', :class => [Version])
26
+ end
27
+
28
+ def r_redactions
29
+ open_context(:method => 'visitor.redactions', :class => [Version])
30
+ end
31
+
32
+ protected
33
+
34
+ # FIXME: we should replace this with @context.find_context(finder) and move it into Zafu core.
35
+ def change_context(rel, opts = {})
36
+ # FIXME: replace with RubyLess.translate(rel)
37
+ raw_filters = opts[:raw_filters] || []
38
+
39
+ signature = [rel]
40
+ unless params.empty?
41
+ signature += [Hash[*params.map{|k,v| [k,String]}.flatten]]
42
+ end
43
+
44
+ if !opts[:skip_rubyless] && context = RubyLess::SafeClass.safe_method_type_for(node_class, signature)
45
+ if params.empty?
46
+ return context.merge(:method => "#{node}.#{context[:method]}")
47
+ else
48
+ return context.merge(:method => "#{node}.#{context[:method]}(#{params.inspect})")
49
+ end
50
+ end
51
+
52
+ rel ||= 'self'
53
+
54
+ # TODO: simplify !
55
+ count = opts[:find] || (['first','all','count'].include?(@params[:find]) ? @params[:find].to_sym : nil)
56
+
57
+ # count ||= Node.plural_relation?(method) ? :all : :first
58
+ unless count
59
+ if params[:paginate] || child['each'] || child['group'] || Node.plural_relation?(rel)
60
+ count = :all
61
+ else
62
+ count = :first
63
+ end
64
+ end
65
+
66
+
67
+ if (count == :first)
68
+ if rel == 'self'
69
+ return {:method => node, :class => node_class}
70
+ elsif rel == 'main'
71
+ return {:method => '@node', :class => Node}
72
+ elsif rel == 'root'
73
+ return {:method => "(secure(Node) { Node.find(#{current_site[:root_id]})})", :class => Node}
74
+ elsif rel == 'start'
75
+ return {:method => 'start_node', :class => Node}
76
+ elsif rel == 'visitor'
77
+ return {:method => 'visitor.contact', :class => Contact}
78
+ elsif rel =~ /^\d+$/
79
+ return {:method => "(secure(Node) { Node.find_by_zip(#{rel.inspect})})", :class => Node}
80
+ elsif node_name = find_stored(Node, rel)
81
+ return {:method => node_name, :class => Node}
82
+ elsif rel[0..0] == '/'
83
+ rel = rel[1..-1]
84
+ return {:method => "(secure(Node) { Node.find_by_path(#{rel.inspect})})", :class => Node}
85
+ end
86
+ end
87
+
88
+ pseudo_sql, add_raw_filters = make_pseudo_sql(rel, params)
89
+ raw_filters += add_raw_filters if add_raw_filters
90
+
91
+ # FIXME: stored should be clarified and managed in a single way through links and contexts.
92
+ # <r:void store='foo'>...
93
+ # <r:link href='foo'/>
94
+ # <r:pages from='foo'/> <-- this is just a matter of changing node parameter
95
+ # <r:pages from='site' project='foo'/>
96
+ # <r:img link='foo'/>
97
+ # ...
98
+
99
+ if node_kind_of?(Node)
100
+ node_name = @context[:parent_node] || node
101
+ else
102
+ node_name = @context[:previous_node]
103
+ end
104
+
105
+ # make sure we do not use a new record in a find query:
106
+ query = Node.build_find(count, pseudo_sql, :node_name => node_name, :raw_filters => raw_filters, :ref_date => "\#{#{current_date}}")
107
+
108
+ unless query.valid?
109
+ out parser_error(query.errors.join(' '), pseudo_sql.join(', '))
110
+ return nil
111
+ end
112
+
113
+
114
+ if count == :count
115
+ out "<%= #{query.finder(:count)} %>"
116
+ return nil
117
+ end
118
+
119
+ klass = query.main_class
120
+
121
+ if params[:else]
122
+ # FIXME: else not working with safe_method_type ?
123
+ finder, else_class, else_query = build_finder_for(count, params[:else], {})
124
+ if finder && (else_query.nil? || else_query.valid?) && (else_class == klass || klass.ancestors.include?(else_class) || else_class.ancestors.include?(klass))
125
+ klass = [klass] if count == :all
126
+ {:method => "(#{query.finder(count)} || #{finder})", :class => klass, :query => query}
127
+ else
128
+ klass = count == :all ? [query.main_class] : query.main_class
129
+ {:method => query.finder(count), :class => klass, :query => query}
130
+ end
131
+ else
132
+ # FIXME: query_builder should respond to safe_type ===> {:method => ..., :class => ...}
133
+ klass = count == :all ? [query.main_class] : query.main_class
134
+ {:method => query.finder(count), :class => klass, :query => query}
135
+ end
136
+ end
137
+
138
+ # Create an sql query to open a new context (passes its arguments to HasRelations#build_find)
139
+ def build_finder_for(count, rel, params=@params, raw_filters = [])
140
+ if (context = RubyLess::SafeClass.safe_method_type_for(node_class, [rel])) && !params[:in] && !params[:where] && !params[:from] && !params[:order] && raw_filters == []
141
+ klass = context[:class]
142
+
143
+ if klass.kind_of?(Array) && count == :all
144
+ return ["#{node}.#{rel}", klass[0]]
145
+ else
146
+ return [(count == :all ? "[#{node}.#{rel}]" : "#{node}.#{rel}"), klass]
147
+ end
148
+ end
149
+
150
+ rel ||= 'self'
151
+ if (count == :first)
152
+ if rel == 'self'
153
+ return [node, node_class]
154
+ elsif rel == 'main'
155
+ return ["@node", Node]
156
+ elsif rel == 'root'
157
+ return ["(secure(Node) { Node.find(#{current_site[:root_id]})})", Node]
158
+ elsif rel == 'start'
159
+ return ["start_node", Node]
160
+ elsif rel == 'visitor'
161
+ return ["visitor.contact", Node]
162
+ elsif rel =~ /^\d+$/
163
+ return ["(secure(Node) { Node.find_by_zip(#{rel.inspect})})", Node]
164
+ elsif node_name = find_stored(Node, rel)
165
+ return [node_name, Node]
166
+ elsif rel[0..0] == '/'
167
+ rel = rel[1..-1]
168
+ return ["(secure(Node) { Node.find_by_path(#{rel.inspect})})", Node]
169
+ end
170
+ end
171
+
172
+ pseudo_sql, add_raw_filters = make_pseudo_sql(rel, params)
173
+ raw_filters += add_raw_filters if add_raw_filters
174
+
175
+ # FIXME: stored should be clarified and managed in a single way through links and contexts.
176
+ # <r:void store='foo'>...
177
+ # <r:link href='foo'/>
178
+ # <r:pages from='foo'/> <-- this is just a matter of changing node parameter
179
+ # <r:pages from='site' project='foo'/>
180
+ # <r:img link='foo'/>
181
+ # ...
182
+
183
+ if node_kind_of?(Node)
184
+ node_name = @context[:parent_node] || node
185
+ else
186
+ node_name = @context[:previous_node]
187
+ end
188
+
189
+ # make sure we do not use a new record in a find query:
190
+ query = Node.build_find(count, pseudo_sql, :node_name => node_name, :raw_filters => raw_filters, :ref_date => "\#{#{current_date}}")
191
+
192
+ unless query.valid?
193
+ out parser_error(query.errors.join(' '), pseudo_sql.join(', '))
194
+ return nil
195
+ end
196
+
197
+
198
+ if count == :count
199
+ out "<%= #{query.finder(:count)} %>"
200
+ return nil
201
+ end
202
+
203
+ klass = query.main_class
204
+
205
+ if params[:else]
206
+ # FIXME: else not working with safe_method_type ?
207
+ finder, else_class, else_query = build_finder_for(count, params[:else], {})
208
+ if finder && (else_query.nil? || else_query.valid?) && (else_class == klass || klass.ancestors.include?(else_class) || else_class.ancestors.include?(klass))
209
+ ["(#{query.finder(count)} || #{finder})", klass, query]
210
+ else
211
+ [query.finder(count), query.main_class, query]
212
+ end
213
+ else
214
+ [query.finder(count), query.main_class, query]
215
+ end
216
+ end
217
+
218
+ # Build pseudo sql from the parameters
219
+ # comments where ... from ... in ... order ... limit
220
+ def make_pseudo_sql(rel, params=@params)
221
+ parts = [rel.dup]
222
+ filters = []
223
+
224
+ if params[:from]
225
+ parts << params[:from]
226
+
227
+ key_counter = 1
228
+ while sub_part = params["from#{key_counter}".to_sym]
229
+ key_counter += 1
230
+ parts << sub_part
231
+ end
232
+ end
233
+
234
+ if params[:where]
235
+ parts[0] << " where #{params[:where]}"
236
+ end
237
+
238
+ if params[:in]
239
+ parts[-1] << " in #{params[:in]}"
240
+ end
241
+
242
+ if group = params[:group]
243
+ parts[-1] << " group by #{group}" unless parts[0] =~ /group by/
244
+ end
245
+
246
+ if order = params[:order]
247
+ parts[-1] << " order by #{order}" unless parts[0] =~ /order by/
248
+ end
249
+
250
+ if paginate = params[:paginate]
251
+ page_size = params[:limit].to_i
252
+ page_size = 20 if page_size < 1
253
+ parts[-1] << " limit #{page_size} paginate #{paginate.gsub(/[^a-z_A-Z]/,'')}"
254
+ else
255
+ [:limit, :offset].each do |k|
256
+ next unless params[k]
257
+ parts[-1] << " #{k} #{params[k]}" unless parts[0] =~ / #{k} /
258
+ end
259
+ end
260
+
261
+ finders = [parts.join(' from ')]
262
+ if params[:or]
263
+ finders << params[:or]
264
+
265
+ key_counter = 1
266
+ while sub_or = params["or#{key_counter}".to_sym]
267
+ key_counter += 1
268
+ finders << sub_or
269
+ end
270
+ else
271
+ or_clause = nil
272
+ end
273
+
274
+ return [finders, parse_raw_filters(params)]
275
+ end
276
+
277
+ # Parse special filters
278
+ def parse_raw_filters(params)
279
+ filters = []
280
+
281
+ if value = params[:author]
282
+ if stored = find_stored(User, value)
283
+ filters << "TABLE_NAME.user_id = '\#{#{stored}.id}'"
284
+ elsif value == 'current'
285
+ filters << "TABLE_NAME.user_id = '\#{#{node}[:user_id]}'"
286
+ elsif value == 'visitor'
287
+ filters << "TABLE_NAME.user_id = '\#{visitor[:id]}'"
288
+ elsif value =~ /\A\d+\Z/
289
+ filters << "TABLE_NAME.user_id = '#{value.to_i}'"
290
+ elsif value =~ /\A[\w\/]+\Z/
291
+ # TODO: path, not implemented yet
292
+ end
293
+ end
294
+
295
+ if value = params[:project]
296
+ if stored = find_stored(Node, value)
297
+ filters << "TABLE_NAME.project_id = '\#{#{stored}.get_project_id}'"
298
+ elsif value == 'current'
299
+ filters << "TABLE_NAME.project_id = '\#{#{node}.get_project_id}'"
300
+ elsif value =~ /\A\d+\Z/
301
+ filters << "TABLE_NAME.project_id = '#{value.to_i}'"
302
+ elsif value =~ /\A[\w\/]+\Z/
303
+ # TODO: path, not implemented yet
304
+ end
305
+ end
306
+
307
+ if value = params[:section]
308
+ if stored = find_stored(Node, value)
309
+ filters << "TABLE_NAME.section_id = '\#{#{stored}.get_section_id}'"
310
+ elsif value == 'current'
311
+ filters << "TABLE_NAME.section_id = '\#{#{node}.get_section_id}'"
312
+ elsif value =~ /\A\d+\Z/
313
+ filters << "TABLE_NAME.section_id = '#{value.to_i}'"
314
+ elsif value =~ /\A[\w\/]+\Z/
315
+ # not implemented yet
316
+ end
317
+ end
318
+
319
+ [:updated, :created, :event, :log].each do |k|
320
+ if value = params[k]
321
+ # current, same are synonym for 'today'
322
+ filters << date_condition(value,"TABLE_NAME.#{k}_at",current_date)
323
+ end
324
+ end
325
+
326
+ filters == [] ? nil : filters
327
+ end
328
+
329
+ end # Context
330
+ end # Zafu