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
@@ -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