zena 1.0.0.rc2 → 1.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (274) hide show
  1. data/History.txt +23 -0
  2. data/README.rdoc +1 -1
  3. data/app/controllers/columns_controller.rb +3 -31
  4. data/app/controllers/comments_controller.rb +8 -3
  5. data/app/controllers/data_entries_controller.rb +1 -1
  6. data/app/controllers/documents_controller.rb +2 -2
  7. data/app/controllers/nodes_controller.rb +29 -12
  8. data/app/controllers/relations_controller.rb +2 -2
  9. data/app/controllers/sites_controller.rb +1 -1
  10. data/app/controllers/user_sessions_controller.rb +6 -3
  11. data/app/controllers/users_controller.rb +18 -16
  12. data/app/controllers/versions_controller.rb +20 -18
  13. data/app/controllers/virtual_classes_controller.rb +103 -17
  14. data/app/helpers/users_helper.rb +1 -1
  15. data/app/models/column.rb +19 -50
  16. data/app/models/comment.rb +2 -1
  17. data/app/models/node.rb +45 -22
  18. data/app/models/relation.rb +13 -0
  19. data/app/models/relation_proxy.rb +3 -2
  20. data/app/models/role.rb +213 -4
  21. data/app/models/site.rb +18 -11
  22. data/app/models/template.rb +37 -35
  23. data/app/models/version.rb +1 -1
  24. data/app/models/virtual_class.rb +154 -86
  25. data/app/views/columns/_li.html.erb +1 -1
  26. data/app/views/columns/index.html.erb +1 -9
  27. data/app/views/comments/index.rhtml +10 -8
  28. data/app/views/documents/_crop.rhtml +5 -6
  29. data/app/views/documents/crop_form.rjs +3 -2
  30. data/app/views/groups/index.rhtml +1 -1
  31. data/app/views/iformats/index.rhtml +1 -1
  32. data/app/views/nodes/_import_results.rhtml +1 -1
  33. data/app/views/nodes/_parent.rhtml +1 -2
  34. data/app/views/nodes/update.rjs +3 -4
  35. data/app/views/relations/index.erb +1 -1
  36. data/app/views/sites/index.erb +1 -1
  37. data/app/views/templates/drive_tabs/_drive.rhtml +0 -2
  38. data/app/views/templates/edit_tabs/_image.rhtml +1 -1
  39. data/app/views/templates/edit_tabs/_title.rhtml +0 -6
  40. data/app/views/users/index.rhtml +1 -1
  41. data/app/views/users/preferences.html.erb +2 -2
  42. data/app/views/versions/backup.rjs +1 -1
  43. data/app/views/versions/custom_tab.rhtml +9 -4
  44. data/app/views/versions/destroy.rjs +2 -2
  45. data/app/views/versions/update.rjs +2 -9
  46. data/app/views/virtual_classes/_form.erb +3 -2
  47. data/app/views/virtual_classes/import_prepare.html.erb +13 -0
  48. data/app/views/virtual_classes/index.erb +28 -8
  49. data/app/views/zafu/default/Node-+adminLayout.zafu +1 -13
  50. data/app/views/zafu/default/Node-+login.zafu +1 -0
  51. data/app/views/zafu/default/Node-+notFound.zafu +1 -1
  52. data/app/views/zafu/default/Node-+popupLayout.zafu +1 -2
  53. data/app/views/zafu/default/Node-+search.zafu +1 -1
  54. data/app/views/zafu/default/Node-admin.zafu +205 -0
  55. data/app/views/zafu/default/Node.zafu +11 -11
  56. data/bricks/captcha/lib/bricks/captcha.rb +3 -2
  57. data/bricks/mongrel/zena/init.rb +2 -1
  58. data/bricks/pdf/README +5 -5
  59. data/bricks/pdf/lib/bricks/pdf/engine/prince.rb +2 -2
  60. data/bricks/pdf/lib/bricks/pdf/engine/xhtml2pdf.rb +2 -2
  61. data/bricks/pdf/lib/bricks/pdf/install.rb +5 -5
  62. data/bricks/pdf/lib/bricks/pdf.rb +11 -11
  63. data/bricks/pdf/test/engines/test_prince.rb +4 -4
  64. data/bricks/pdf/test/engines/test_xhtml2pdf.rb +4 -4
  65. data/bricks/pdf/test/shoulda_macros/shoulda_pdf.rb +2 -2
  66. data/bricks/pdf/zena/init.rb +2 -2
  67. data/bricks/pdf/zena/tasks.rb +2 -2
  68. data/bricks/sphinx/lib/bricks/sphinx.rb +6 -2
  69. data/bricks/sphinx/zena/{sphinx.yml → sphinx.yml.erb} +2 -2
  70. data/bricks/sphinx/zena/tasks.rb +28 -2
  71. data/bricks/tags/lib/bricks/tags.rb +16 -1
  72. data/bricks/tags/zena/test/unit/tags_test.rb +15 -0
  73. data/bricks/tags/zena/test/zafu/tags.yml +5 -1
  74. data/bricks/worker/lib/bricks/worker.rb +39 -0
  75. data/bricks/worker/zena/deploy.rb +0 -2
  76. data/bricks/worker/zena/init.rb +1 -0
  77. data/bricks/worker/zena/test/sites/zena/delayed_jobs.yml +16 -0
  78. data/bricks/worker/zena/test/zafu/worker.yml +8 -0
  79. data/bricks/zena/zena/migrate/01_base.rb +36 -60
  80. data/bricks/zena/zena/migrate/02_zerox1_schema.rb +388 -0
  81. data/bricks/zena/zena/migrate/03_zerox1_data.rb +380 -0
  82. data/bricks/zena/zena/migrate/20110315161158_add_reverse_scope_to_roles.rb +9 -0
  83. data/config/database_example.yml +1 -1
  84. data/config/environment.rb +1 -1
  85. data/config/gems.yml +17 -14
  86. data/db/init/base/skins/default/Node-+index.zafu +8 -1
  87. data/db/init/base/skins/default/Node-+login.zafu +1 -0
  88. data/db/init/base/skins/default/Node-+popupLayout.zafu +1 -2
  89. data/db/init/base/skins/default/Node-+search.zafu +2 -2
  90. data/db/init/base/skins/default/Node.zafu +9 -9
  91. data/db/init/base/skins/default/{favicon.png → img/favicon.png} +0 -0
  92. data/db/init/base/skins/default/{style.css → img/style.css} +0 -0
  93. data/db/init/base/skins/default/img/translations.yml +11 -0
  94. data/db/init/base/skins/default/notes.zafu +7 -9
  95. data/doc/zafu_changes.yml +12 -0
  96. data/lib/bricks/loader.rb +38 -15
  97. data/lib/tasks/zena.rake +74 -24
  98. data/lib/zena/acts/enrollable.rb +4 -1
  99. data/lib/zena/acts/secure.rb +2 -48
  100. data/lib/zena/acts/serializable.rb +13 -1
  101. data/lib/zena/app.rb +9 -0
  102. data/lib/zena/code_syntax.rb +154 -151
  103. data/lib/zena/console.rb +141 -0
  104. data/lib/zena/controller/test_case.rb +1 -1
  105. data/lib/zena/db_helper/abstract_db.rb +17 -5
  106. data/lib/zena/db_helper/mysql.rb +14 -12
  107. data/lib/zena/db_helper/postgresql.rb +1 -2
  108. data/lib/zena/db_helper/sqlite3.rb +6 -6
  109. data/lib/zena/deploy/awstats.conf.rhtml +1 -1
  110. data/lib/zena/deploy/httpd.rhtml +6 -1
  111. data/lib/zena/deploy/vhost.rhtml +9 -1
  112. data/lib/zena/deploy.rb +12 -7
  113. data/lib/zena/foxy_parser.rb +3 -1
  114. data/lib/zena/info.rb +1 -1
  115. data/lib/zena/parser/zafu_tags.rb +1 -0
  116. data/lib/zena/parser/zazen_rules.rb +1 -1
  117. data/lib/zena/remote/node.rb +15 -3
  118. data/lib/zena/remote/serializable_array.rb +19 -0
  119. data/lib/zena/remote.rb +1 -0
  120. data/lib/zena/routes.rb +7 -2
  121. data/lib/zena/site_worker.rb +11 -1
  122. data/lib/zena/unit/test_case.rb +68 -0
  123. data/lib/zena/use/action.rb +6 -2
  124. data/lib/zena/use/ajax.rb +127 -53
  125. data/lib/zena/use/ancestry.rb +11 -8
  126. data/lib/zena/use/calendar.rb +265 -129
  127. data/lib/zena/use/conditional.rb +1 -1
  128. data/lib/zena/use/context.rb +5 -5
  129. data/lib/zena/use/dates.rb +172 -60
  130. data/lib/zena/use/display.rb +70 -39
  131. data/lib/zena/use/error_rendering.rb +1 -3
  132. data/lib/zena/use/field_index.rb +4 -1
  133. data/lib/zena/use/forms.rb +94 -72
  134. data/lib/zena/use/fulltext.rb +16 -24
  135. data/lib/zena/use/html_tags.rb +20 -12
  136. data/lib/zena/use/i18n.rb +37 -37
  137. data/lib/zena/use/image_builder.rb +8 -1
  138. data/lib/zena/use/ml_index.rb +16 -16
  139. data/lib/zena/use/prop_eval.rb +10 -5
  140. data/lib/zena/use/query_builder.rb +55 -23
  141. data/lib/zena/use/query_node.rb +51 -25
  142. data/lib/zena/use/refactor.rb +2 -28
  143. data/lib/zena/use/relations.rb +1 -1
  144. data/lib/zena/use/rendering.rb +29 -0
  145. data/lib/zena/use/scope_index.rb +75 -14
  146. data/lib/zena/use/search.rb +5 -10
  147. data/lib/zena/use/test_helper.rb +2 -2
  148. data/lib/zena/use/urls.rb +125 -104
  149. data/lib/zena/use/workflow.rb +2 -1
  150. data/lib/zena/use/zafu_attributes.rb +2 -2
  151. data/lib/zena/use/zafu_safe_definitions.rb +20 -0
  152. data/lib/zena/use/zafu_templates.rb +20 -6
  153. data/lib/zena/use/zazen.rb +31 -20
  154. data/lib/zena/view/test_case.rb +5 -0
  155. data/lib/zena/zafu_compiler.rb +24 -2
  156. data/lib/zena.rb +12 -6
  157. data/locale/de/LC_MESSAGES/zena.mo +0 -0
  158. data/locale/de/zena.po +1345 -1164
  159. data/locale/en/LC_MESSAGES/zena.mo +0 -0
  160. data/locale/en/zena.po +1275 -1129
  161. data/locale/fr/LC_MESSAGES/zena.mo +0 -0
  162. data/locale/fr/zena.mo +0 -0
  163. data/locale/fr/zena.po +1617 -1441
  164. data/locale/log.txt +9 -0
  165. data/locale/zena.pot +957 -748
  166. data/public/javascripts/prototype.js +1 -1
  167. data/public/javascripts/zena.js +99 -44
  168. data/public/stylesheets/admin.css +6 -4
  169. data/public/stylesheets/backend.css +71 -0
  170. data/public/stylesheets/calendar.css +24 -25
  171. data/public/stylesheets/code.css +11 -6
  172. data/public/stylesheets/comment.css +2 -1
  173. data/public/stylesheets/popup.css +7 -8
  174. data/test/custom_queries/complex.host.yml +15 -1
  175. data/test/fixtures/files/Node-test.zafu +29 -28
  176. data/test/fixtures/files/translations_de.yml +12 -1
  177. data/test/fixtures/files/translations_fr.yml +12 -1
  178. data/test/functional/comments_controller_test.rb +9 -0
  179. data/test/functional/iformats_controller_test.rb +1 -1
  180. data/test/functional/nodes_controller_test.rb +124 -35
  181. data/test/functional/users_controller_test.rb +132 -3
  182. data/test/functional/virtual_classes_controller_test.rb +75 -4
  183. data/test/integration/navigation_test.rb +51 -9
  184. data/test/integration/query_node/basic.yml +19 -7
  185. data/test/integration/query_node/complex.yml +1 -1
  186. data/test/integration/query_node/dates.yml +27 -1
  187. data/test/integration/query_node/filters.yml +1 -1
  188. data/test/integration/query_node/relations.yml +13 -4
  189. data/test/integration/query_node_test.rb +4 -0
  190. data/test/integration/xml_api_test.rb +6 -1
  191. data/test/integration/zafu_compiler/action.yml +3 -3
  192. data/test/integration/zafu_compiler/ajax.yml +103 -22
  193. data/test/integration/zafu_compiler/basic.yml +0 -52
  194. data/test/integration/zafu_compiler/calendar.yml +44 -20
  195. data/test/integration/zafu_compiler/comments.yml +53 -0
  196. data/test/integration/zafu_compiler/complex.yml +11 -11
  197. data/test/integration/zafu_compiler/complex_ok.yml +16 -3
  198. data/test/integration/zafu_compiler/conditional.yml +15 -5
  199. data/test/integration/zafu_compiler/context.yml +9 -0
  200. data/test/integration/zafu_compiler/dates.yml +43 -15
  201. data/test/integration/zafu_compiler/display.yml +60 -6
  202. data/test/integration/zafu_compiler/errors.yml +6 -2
  203. data/test/integration/zafu_compiler/forms.yml +45 -6
  204. data/test/integration/zafu_compiler/i18n.yml +8 -1
  205. data/test/integration/zafu_compiler/meta.yml +38 -0
  206. data/test/integration/zafu_compiler/query.yml +43 -4
  207. data/test/integration/zafu_compiler/relations.yml +26 -33
  208. data/test/integration/zafu_compiler/rubyless.yml +10 -0
  209. data/test/integration/zafu_compiler/safe_definitions.yml +21 -1
  210. data/test/integration/zafu_compiler/urls.yml +75 -5
  211. data/test/integration/zafu_compiler/version.yml +2 -2
  212. data/test/integration/zafu_compiler/zafu_attributes.yml +5 -1
  213. data/test/integration/zafu_compiler/zazen.yml +14 -6
  214. data/test/integration/zafu_compiler_test.rb +5 -1
  215. data/test/sites/complex/columns.yml +5 -0
  216. data/test/sites/complex/roles.yml +4 -0
  217. data/test/sites/zena/nodes.yml +13 -2
  218. data/test/sites/zena/roles.yml +13 -5
  219. data/test/sites/zena/versions.yml +27 -9
  220. data/test/unit/column_test.rb +51 -5
  221. data/test/unit/iformat_test.rb +2 -2
  222. data/test/unit/node_test.rb +29 -17
  223. data/test/unit/note_test.rb +1 -1
  224. data/test/unit/relation_proxy_test.rb +4 -5
  225. data/test/unit/relation_test.rb +16 -0
  226. data/test/unit/remote_test.rb +2 -2
  227. data/test/unit/role_test.rb +292 -4
  228. data/test/unit/site_test.rb +12 -0
  229. data/test/unit/template_test.rb +1 -1
  230. data/test/unit/text_document_test.rb +1 -1
  231. data/test/unit/virtual_class_test.rb +200 -83
  232. data/test/unit/zena/acts/enrollable_test.rb +26 -31
  233. data/test/unit/zena/use/calendar_test.rb +90 -37
  234. data/test/unit/zena/use/field_index_test.rb +28 -0
  235. data/test/unit/zena/use/html_tags_test.rb +7 -3
  236. data/test/unit/zena/use/ml_index_test.rb +2 -16
  237. data/test/unit/zena/use/nested_attributes_alias_view_test.rb +2 -2
  238. data/test/unit/zena/use/prop_eval_test.rb +50 -8
  239. data/test/unit/zena/use/query_node_test.rb +11 -0
  240. data/test/unit/zena/use/rendering_test.rb +72 -0
  241. data/test/unit/zena/use/scope_index_test.rb +37 -2
  242. data/test/unit/zena/use/urls_test.rb +10 -0
  243. data/test/unit/zena/use/zazen_test.rb +3 -3
  244. data/vendor/plugins/gettext_i18n_rails/Gemfile +11 -0
  245. data/vendor/plugins/gettext_i18n_rails/Gemfile.lock +92 -0
  246. data/vendor/plugins/gettext_i18n_rails/Rakefile +12 -17
  247. data/vendor/plugins/gettext_i18n_rails/Readme.md +215 -0
  248. data/vendor/plugins/gettext_i18n_rails/VERSION +1 -1
  249. data/vendor/plugins/gettext_i18n_rails/gettext_i18n_rails.gemspec +38 -34
  250. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/active_record.rb +1 -1
  251. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/backend.rb +30 -14
  252. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/haml_parser.rb +1 -1
  253. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/html_safe_translations.rb +29 -0
  254. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/i18n_hacks.rb +29 -1
  255. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/model_attributes_finder.rb +7 -1
  256. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/railtie.rb +10 -0
  257. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/ruby_gettext_extractor.rb +6 -2
  258. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/string_interpolate_fix.rb +20 -0
  259. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/tasks.rb +120 -0
  260. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails.rb +10 -3
  261. data/vendor/plugins/gettext_i18n_rails/lib/tasks/gettext_rails_i18n.rake +1 -74
  262. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails/active_record_spec.rb +51 -20
  263. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails/backend_spec.rb +12 -7
  264. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails/string_interpolate_fix_spec.rb +32 -0
  265. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails_spec.rb +38 -1
  266. data/vendor/plugins/gettext_i18n_rails/spec/rails2/Gemfile +11 -0
  267. data/vendor/plugins/gettext_i18n_rails/spec/spec_helper.rb +1 -8
  268. data/zena.gemspec +2241 -2217
  269. metadata +123 -83
  270. data/.gitignore +0 -36
  271. data/app/views/nodes/_dates.rhtml +0 -13
  272. data/db/init/base/skins/default/Node-+adminLayout.zafu +0 -46
  273. data/db/init/base/skins/default/Node-tree.zafu +0 -19
  274. data/vendor/plugins/gettext_i18n_rails/README.markdown +0 -143
@@ -7,8 +7,10 @@ module Zena
7
7
  %Q{INNER JOIN idx_nodes_ml_strings AS id1 ON id1.node_id = nodes.id AND id1.key = 'title' AND id1.lang = '#{visitor.lang}'}
8
8
  end
9
9
 
10
- # (slow). Find a node by it's path. This is used during node importation when stored as zml files.
11
- def find_by_path(path, parent_id = current_site.root_id)
10
+ TITLE_ML_JOIN = %Q{INNER JOIN idx_nodes_ml_strings AS id1 ON id1.node_id = nodes.id AND id1.key = 'title'}
11
+
12
+ # (slow). Find a node by it's path. This is used during node importation when stored as zml files or to resolve custom_base url until we have an "alias" table.
13
+ def find_by_path(path, parent_id = current_site.root_id, multilingual = false)
12
14
  res = nil
13
15
  path = path.split('/') unless path.kind_of?(Array)
14
16
  last = path.size - 1
@@ -16,7 +18,7 @@ module Zena
16
18
  klass = i == last ? self : Node
17
19
  unless p = klass.find(:first,
18
20
  :select => i == last ? 'nodes.*' : 'nodes.id',
19
- :joins => title_join,
21
+ :joins => multilingual ? title_join : TITLE_ML_JOIN,
20
22
  :conditions => ["parent_id = ? AND id1.value = ? AND #{secure_scope('nodes')}", parent_id, title]
21
23
  )
22
24
  # Block as soon as we cannot find an element
@@ -62,7 +64,7 @@ module Zena
62
64
  module ModelMethods
63
65
  include RubyLess
64
66
  safe_context :ancestors => {:class => ['Node'], :method => 'z_ancestors'}
65
- safe_method :fullpath => String, :short_path => String
67
+ safe_method :fullpath => String, :short_path => [String]
66
68
 
67
69
  def self.included(base)
68
70
  base.class_eval do
@@ -81,7 +83,7 @@ module Zena
81
83
  end
82
84
 
83
85
  # Return true if the current node is an ancestor for the given child
84
- def ancestor?(child)
86
+ def is_ancestor?(child)
85
87
  child.fullpath =~ %r{\A#{fullpath}}
86
88
  end
87
89
 
@@ -107,7 +109,7 @@ module Zena
107
109
 
108
110
  # (slow). Return an array with the node title and the last two parents' titles.
109
111
  def short_path
110
- path = fullpath.split('/')
112
+ path = (fullpath || '').split('/')
111
113
  if path.size > 2
112
114
  ['..'] + fullpath_as_title(path[-2..-1])
113
115
  else
@@ -129,9 +131,10 @@ module Zena
129
131
  # (slow). Transform a list of zips into a fullpath.
130
132
  def fullpath_as_title(path = fullpath)
131
133
  if path == self.fullpath
132
- @fullpath_as_title ||= secure(Node) { Node.fullpath_map(path, :title) }
134
+ # secure returns nil instead of [] so we fix this.
135
+ @fullpath_as_title ||= secure(Node) { Node.fullpath_map(path, :title) } || []
133
136
  else
134
- secure(Node) { Node.fullpath_map(path, :title) }
137
+ secure(Node) { Node.fullpath_map(path, :title) } || []
135
138
  end
136
139
  end
137
140
 
@@ -1,15 +1,16 @@
1
1
  module Zena
2
2
  module Use
3
3
  module Calendar
4
+ DAY_FORMAT = '%Y-%m-%d'
4
5
 
5
6
  module ViewMethods
6
- def cal_day_names(size)
7
+ def cal_day_names(size, week_start_day)
7
8
  if size == :tiny
8
9
  day_names = Date::ABBR_DAYNAMES
9
10
  else
10
11
  day_names = Date::DAYNAMES
11
12
  end
12
- week_start_day = _('week_start_day').to_i
13
+
13
14
  res = ""
14
15
  0.upto(6) do |i|
15
16
  j = (i+week_start_day) % 7
@@ -18,14 +19,15 @@ module Zena
18
19
  elsif j == 6
19
20
  html_class = " class='sat'"
20
21
  end
21
- res << "<td#{html_class}>#{_(day_names[j])}</td>"
22
+ res << "<th#{html_class}>#{_(day_names[j])}</th>"
22
23
  end
23
24
  res
24
25
  end
25
26
 
26
27
  # find start and end dates for a calendar showing a specified date
27
- def cal_start_end(date, type=:month)
28
- week_start_day = _('week_start_day').to_i
28
+ def cal_start_end(utc_date, type, tz, week_start_day)
29
+ # We need to compute start/end in local tz
30
+ date = tz.utc_to_local(utc_date)
29
31
 
30
32
  case type
31
33
  when :week
@@ -34,16 +36,24 @@ module Zena
34
36
  end_date = date
35
37
  else
36
38
  # month
37
- start_date = Date.civil(date.year, date.mon, 1)
38
- end_date = Date.civil(date.year, date.mon, -1)
39
+ # From 2000-10-01 00:00
40
+ start_date = Time.utc(date.year, date.mon, 1)
41
+ # To 2000-11-01 00:00
42
+ end_date = start_date.advance(:months => 1)
39
43
  end
40
- start_date -= (start_date.wday + 7 - week_start_day) % 7
41
- end_date += (6 + week_start_day - end_date.wday) % 7
42
- [start_date, end_date]
44
+
45
+ start_date = start_date.advance(:days => -((start_date.wday + 7 - week_start_day) % 7))
46
+ # end_date.wday - 1 because at 00:00 this is considered to be the next day but we do not
47
+ # show this day.
48
+ end_date = end_date.advance(:days => (6 + week_start_day - (end_date.wday - 1)) % 7)
49
+ # convert back to UTC
50
+ [tz.local_to_utc(start_date.to_time), tz.local_to_utc(end_date)]
43
51
  end
44
52
 
45
- def cal_class(date, ref)
46
- @today ||= Date.today
53
+ # Get day class. The first parameter is an UTC Date. The second is a local Time.
54
+ def cal_class(utc_date, local_ref, tz)
55
+ date = tz.utc_to_local(utc_date.to_time)
56
+ @cal_today ||= tz.utc_to_local(Time.now).strftime(DAY_FORMAT)
47
57
  case date.wday
48
58
  when 6
49
59
  s = "sat"
@@ -52,15 +62,15 @@ module Zena
52
62
  else
53
63
  s = ""
54
64
  end
55
- s += 'other' if date.mon != ref.mon
65
+ s += 'other' if date.mon != local_ref.mon
56
66
  s = s == '' ? [] : [s]
57
- s << 'today' if date == @today
58
- s << 'ref' if date == ref
67
+ s << 'today' if date.strftime(DAY_FORMAT) == @today
68
+ s << 'ref' if date.strftime(DAY_FORMAT) == local_ref.strftime(DAY_FORMAT)
59
69
  s == [] ? '' : " class='#{s.join(' ')}'"
60
70
  end
61
71
 
62
72
  # Yield block for every week between 'start_date' and 'end_date' with a hash of days => events.
63
- def cal_weeks(date_attr, list, start_date, end_date, hours = nil)
73
+ def cal_weeks(date_attr, list, start_date, end_date, tz, hours = nil)
64
74
  # build event hash
65
75
  cal_hash = {}
66
76
  if hours
@@ -69,13 +79,19 @@ module Zena
69
79
  # 12 => dates from 12:00 to 23:59
70
80
 
71
81
  (list || []).each do |n|
72
- d = n.send(date_attr)
73
- next unless d
82
+ # d is an UTC date
83
+ utc_d = n.send(date_attr) rescue nil
84
+ next unless utc_d && utc_d.kind_of?(Time)
85
+ d = tz.utc_to_local(utc_d)
74
86
  hours.reverse_each do |h|
75
87
  if d.hour >= h
76
- d = d - (d.hour - h) * 3600 # too bad Time does not have an hour= method, we could have written d.hour = h
77
- n.send("#{date_attr}=", d) # we need this to properly display hour class in ajax return
78
- h_list = cal_hash[d.strftime("%Y-%m-%d %H")] ||= []
88
+ # too bad Time does not have an hour= method, we could have written d.hour = h
89
+ # d = d - (d.hour - h) * 3600
90
+ # # we need this to properly display hour class in ajax return ?
91
+ # n.send("#{date_attr}=", d)
92
+
93
+ # d = local date
94
+ h_list = cal_hash[d.strftime('%Y-%m-%d %H')] ||= []
79
95
  h_list << n
80
96
  break
81
97
  end
@@ -84,15 +100,17 @@ module Zena
84
100
 
85
101
  else
86
102
  (list || []).each do |n|
87
- d = n.send(date_attr)
88
- next unless d
89
- cal_hash[d.strftime("%Y-%m-%d 00")] ||= []
90
- cal_hash[d.strftime("%Y-%m-%d 00")] << n
103
+ utc_d = n.send(date_attr)
104
+ next unless utc_d
105
+ d = tz.utc_to_local(utc_d)
106
+ h_list = cal_hash[d.strftime('%Y-%m-%d 00')] ||= []
107
+ h_list << n
91
108
  end
92
109
  end
93
110
 
94
- start_date.step(end_date,7) do |week|
95
- # each week
111
+ # Date#step includes the last date [first, last] but we need [first, last[
112
+ start_date.to_datetime.step(end_date.to_datetime.advance(:seconds => -1),7) do |week|
113
+ # each week (UTC Date)
96
114
  yield(week, cal_hash)
97
115
  end
98
116
  end
@@ -114,7 +132,7 @@ module Zena
114
132
  if state == 'used' && remove_used.nil?
115
133
  res << title
116
134
  else
117
- date_format = "%Y-%m-%d+%H"
135
+ date_format = "%Y-%m-%dT%H"
118
136
  opts = {:url => "/nodes/#{node.zip}?node[rel][#{role}][date]=#{date.strftime(date_format)}&node[rel][#{role}][other_id]=#{state == 'free' ? target_zip : ''}&s=#{target_zip}&dom_id=#{full_dom_id}&t_url=#{CGI.escape(template_url)}&date=#{date.strftime(date_format)}", :method => :put}
119
137
  if state == 'used' && remove_used == 'warn'
120
138
  opts[:confirm] = _("Delete relation '%{role}' between '%{source}' and '%{target}' ?") % {:role => role, :source => node.title, :target => node.linked_node.title}
@@ -127,77 +145,245 @@ module Zena
127
145
  end # ViewMethods
128
146
 
129
147
  module ZafuMethods
130
- # Calendar methods need a rewrite...
131
- =begin
148
+
149
+ # Display calendar content
132
150
  def r_calendar
133
- if @context[:block] == self
134
- # called from self (storing template / rendering)
135
- if role = @params[:assign_as]
136
- assign_calendar(role)
151
+ # Should work like r_block (storage/rendering)
152
+ display_calendar
153
+ #if @context[:block] == self
154
+ # # called from self (storing template / rendering)
155
+ # if role = @params[:assign_as]
156
+ # assign_calendar(role)
157
+ # else
158
+ # display_calendar
159
+ # end
160
+ #else
161
+ # # This is called first to prepare calendar
162
+ # if @params[:assign_as]
163
+ # fld = 'date'
164
+ # table_name = 'links'
165
+ # else
166
+ # fld = @params[:date] || 'event_at'
167
+ # if ['log_at', 'created_at', 'updated_at', 'event_at'].include?(fld) # TODO: use rubyless to learn type
168
+ # table_name = 'nodes'
169
+ # elsif fld == 'l_date'
170
+ # fld = 'date'
171
+ # table_name = 'links'
172
+ # else
173
+ # return parser_error("Invalid 'date' value for calendar (#{fld.inspect}).")
174
+ # end
175
+ # end
176
+ #
177
+ # @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')}'"
178
+ #
179
+ # new_dom_scope
180
+ #
181
+ # # SAVED TEMPLATE
182
+ # template = expand_block(self, :block => self, :saved_template => true)
183
+ # out helper.save_erb_to_url(template, template_url)
184
+ #
185
+ # # INLINE
186
+ # out expand_block(self, :block => self, :saved_template => false)
187
+ #end
188
+ end
189
+
190
+ private
191
+ def display_calendar
192
+ opts = {
193
+ :size => (@params[:size] || 'large').to_sym
194
+ }
195
+ return parser_error("Missing 'select' parameter.") unless opts[:select] = @params[:select]
196
+
197
+ if header_block = descendant('header')
198
+ elsif @params[:type] == 'week'
199
+ add_block(%q{<h3 do='header'>
200
+ <r:link date='#{date.advance(:days =&gt; -1).strftime("%Y-%m-%d", tz)}' t='img_prev_page'/>
201
+ <r:date format='%B'/>
202
+ <r:link date='#{date.advance(:days =&gt; 1).strftime("%Y-%m-%d", tz)}' t='img_next_page'/>
203
+ </h3>}, true)
204
+ header_block = descendant('header')
137
205
  else
138
- display_calendar
206
+ add_block(%q{<h3 do='header'>
207
+ <r:link date='#{date.advance(:months =&gt; -1).strftime("%Y-%m-%d", tz)}' t='img_prev_page'/>
208
+ <r:date format='%B'/>
209
+ <r:link date='#{date.advance(:months =&gt; 1).strftime("%Y-%m-%d", tz)}' t='img_next_page'/>
210
+ </h3>}, true)
211
+ header_block = descendant('header')
139
212
  end
140
- else
141
- # This is called first to prepare calendar
142
- if @params[:assign_as]
143
- fld = 'date'
144
- table_name = 'links'
213
+
214
+ if cell_block = descendant('cell')
145
215
  else
146
- fld = @params[:date] || 'event_at'
147
- if ['log_at', 'created_at', 'updated_at', 'event_at'].include?(fld) # TODO: use rubyless to learn type
148
- table_name = 'nodes'
149
- elsif fld == 'l_date'
150
- fld = 'date'
151
- table_name = 'links'
216
+ # add a default <r:link/> block
217
+ if opts[:size] == :tiny
218
+ add_block %Q{<r:cell><em do='link' date='#{format_date(date, "%Y-%m-%d")}' do='date' format='%e'/></r:cell>}
152
219
  else
153
- return parser_error("Invalid 'date' value for calendar (#{fld.inspect}).")
220
+ add_block "<r:cell><p do='date' format='%e'/><ol><li do='each' do='link' eval='title.limit(10)'/></ol></r:cell>"
154
221
  end
222
+ cell_block = descendant('cell')
155
223
  end
156
224
 
157
- @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')}'"
225
+ if !cell_block.descendant('else')
226
+ cell_block.add_block %q{<r:else do='date' format='%e'/>}
227
+ end
158
228
 
159
- new_dom_scope
229
+ opts[:cell] = cell_block
230
+ opts[:header] = header_block
231
+ opts[:current_date] = get_var_name('calendar', 'c_date')
232
+ make_calendar(opts)
233
+ end
160
234
 
161
- # SAVED TEMPLATE
162
- template = expand_block(self, :block => self, :saved_template => true)
163
- out helper.save_erb_to_url(template, template_url)
235
+ def make_calendar(opts)
236
+ current_date = opts[:current_date]
237
+ type = params[:type] ? params[:type].to_sym : :month
164
238
 
165
- # INLINE
166
- out expand_block(self, :block => self, :saved_template => false)
167
- end
168
- end
239
+ size = opts[:size]
169
240
 
170
- private
171
- def display_calendar
172
- size = (params[:size] || 'large').to_sym
173
- finder = params[:select] || 'notes in project'
241
+ if params[:assign_as]
242
+ date_attr = 'l_date'
243
+ else
244
+ return parser_error("Missing 'attr' attribute.") unless date_attr = params[:attr]
245
+ end
174
246
 
175
- if @blocks == []
176
- # add a default <r:link/> block
177
- if size == :tiny
178
- @blocks = [make(:void, :method=>'void', :text=>"<em do='link' date='current_date' do='[current_date]' format='%d'/><r:else do='[current_date]' format='%d'/>")]
247
+ day_var = get_var_name('calendar', 'day')
248
+
249
+ week_var = get_var_name('calendar', 'week')
250
+
251
+ if tz_name = @params[:tz]
252
+ tz_result, tz_var = set_tz_var(tz_name)
253
+ return tz_result unless tz_var
254
+ set_tz = tz_result
255
+ else
256
+ tz_var = get_var_name('calendar', 'tz')
257
+ set_tz = "<% #{tz_var} = visitor.tz %>"
258
+ end
259
+
260
+ cell_date = get_var_name('calendar', 'date')
261
+ cal_start = get_var_name('calendar', 'cal_start')
262
+ cal_end = get_var_name('calendar', 'cal_end')
263
+
264
+ # To avoid wrapping in each cell
265
+ markup = @markup
266
+ @markup = Zafu::Markup.new(nil) # dummy to avoid cell wrapping
267
+
268
+ # Declare calendar var 'cal_start' and 'cal_end' (can be used in header)
269
+ set_context_var('set_var', 'cal_start', RubyLess::TypedString.new(
270
+ cal_start,
271
+ :class => Time
272
+ ))
273
+
274
+ set_context_var('set_var', 'cal_end', RubyLess::TypedString.new(
275
+ cal_end,
276
+ :class => Time
277
+ ))
278
+
279
+ set_context_var('set_var', 'tz', RubyLess::TypedString.new(
280
+ tz_var,
281
+ :class => TZInfo::Timezone
282
+ ))
283
+
284
+
285
+ if date_code = @params[:date]
286
+ c_date_code = RubyLess.translate(self, date_code)
287
+ if !c_date_code.klass <= Time
288
+ return parser_error("Invalid 'date' parameter. Should be a Time (found #{current_date.klass})")
289
+ end
179
290
  else
180
- @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'/>")]
291
+ # Get current date from url for the current time_zone
292
+ c_date_code = RubyLess.translate(self, 'date(tz)')
181
293
  end
182
- remove_instance_variable(:@all_descendants)
183
- elsif !descendant('else')
184
- @blocks += [make(:void, :method=>'void', :text=>"<r:else do='[current_date]' format='%d'/>")]
185
- remove_instance_variable(:@all_descendants)
186
- end
294
+ set_current_date = "#{current_date} = #{c_date_code}"
187
295
 
188
- @html_tag_done = false
189
- @html_tag_params[:id] = erb_dom_id
190
- @html_tag_params[:class] ||= "#{size}cal"
191
- @html_tag ||= 'div'
296
+ # 'current date' for the whole calendar
297
+ set_context_var('set_var', 'date', RubyLess::TypedString.new(
298
+ current_date,
299
+ :class => Time
300
+ ))
301
+
302
+ # BUILD FINDER
303
+ return unless finder = build_finder(:all, opts[:select])
304
+
305
+ klass = finder[:query].main_class
306
+ return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
307
+
308
+ if type = klass.safe_method_type([date_attr])
309
+ if type[:class] <= Time
310
+ # OK
311
+ else
312
+ return parser_error("Invalid attribute '#{date_attr}': type is '#{type[:class]}' should be Time")
313
+ end
314
+ else
315
+ return parser_error("Invalid attribute '#{date_attr}' for #{klass}")
316
+ end
317
+ # HACK to overwrite 'header' method...
318
+ h = opts[:header]
319
+ h.method = 'void'
320
+ header_code = expand_block(h)
321
+ h.method = 'header'
322
+
323
+ # Date for the cell
324
+ set_context_var('set_var', 'date', RubyLess::TypedString.new(
325
+ cell_date,
326
+ :class => Time
327
+ ))
192
328
 
193
- finder, klass = build_finder_for(:all, finder, @params, [@date_scope])
194
- return unless finder
195
- return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node)
196
329
 
197
- 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 %>"
330
+ # HACK to render sub-elements...
331
+ bak = @blocks
332
+ @blocks = opts[:cell].blocks
333
+ cell_code = expand_if(var, node.move_to(var, [klass]))
334
+ @blocks = bak
335
+ @markup = markup
198
336
 
199
- render_html_tag(calendar_code(finder, "", cell_code, "", params))
337
+ # Reference date (the one in the url) as seen by the calendar's timezone.
338
+ local_ref = get_var_name('calendar', 'local_ref')
339
+ set_local_ref = "<% #{local_ref} = #{tz_var}.utc_to_local(#{current_date}) %>"
340
+
341
+ week_start_day = param(:start_day) || _('week_start_day').to_i
342
+
343
+ # List of events for each day/hour (hour is 00 when not used)
344
+ # The time is encoded in the visitor's timezone.
345
+ # '%Y-%m-%d %H' => [...]
346
+ events_hash = get_var_name('calendar', 'nodes')
347
+
348
+ base_class = node.real_class <= Node ? Node : node.real_class
349
+
350
+ if hours = @params[:split_hours]
351
+ hours = hours.split(',').map{|l| l.to_i}
352
+ hours << 0
353
+ hours = hours.uniq.sort
354
+ # I feel all this would be much better if we could use "each_group" but then how do we access hours ?
355
+
356
+ hour_var = get_var_name('calendar', 'hour')
357
+
358
+ week_code = "<% #{week_var}.step(#{week_var}+6,1) do |#{day_var}| %>
359
+ <td<%= cal_class(#{day_var},#{local_ref}, #{tz_var}) %>>#{opts[:cell_prefix_code]}<% #{hours.inspect}.each do |#{hour_var}|; #{cell_date} = #{day_var}.to_time.advance(:hours => #{hour_var}); #{var} = #{events_hash}[#{cell_date}).strftime_tz('%Y-%m-%d %H',#{tz_var})] %>#{cell_code}<% end %>#{opts[:cell_postfix_code]}</td>
360
+ <% end %>"
361
+ (@context[:vars] ||= []) << "hour"
362
+ else
363
+ hours = nil
364
+ week_code = "<% #{week_var}.step(#{week_var}+6,1) do |#{day_var}| %>
365
+ <td<%= cal_class(#{day_var},#{local_ref}, #{tz_var}) %>><% #{cell_date} = #{day_var}.to_time; #{var} = #{events_hash}[#{cell_date}.strftime_tz('%Y-%m-%d 00',#{tz_var})] %>#{opts[:cell_prefix_code]}#{cell_code}#{opts[:cell_postfix_code]}</td>
366
+ <% end %>"
367
+ end
368
+
369
+
370
+ out "#{set_tz}<% if #{set_current_date} %>"
371
+ out wrap %Q{#{set_local_ref}#{header_code}
372
+ <table cellspacing='0' class='#{size}cal#{@params[:assign_as] ? " assign" : ''}'>
373
+ <tr><%= cal_day_names(#{size.inspect}, #{week_start_day}) %></tr>
374
+ <% #{cal_start}, #{cal_end} = cal_start_end(#{current_date}, #{type.inspect}, #{tz_var}, #{week_start_day}) %>
375
+ <% cal_weeks(#{date_attr.inspect}, #{finder[:method]}, #{cal_start}, #{cal_end}, #{tz_var}, #{hours.inspect}) do |#{week_var}, #{events_hash}| %>
376
+ <tr class='body'>
377
+ #{week_code}
378
+ </tr>
379
+ <% end %>
380
+ </table>}
381
+ out "<% end %>"
382
+ rescue ::QueryBuilder::Error => err
383
+ out parser_error(err.message)
200
384
  end
385
+ # Calendar methods need a rewrite...
386
+ =begin
201
387
 
202
388
  # manage links from @node ---- reference ----> ...
203
389
  # <div do='calendar' assign='reference' to='main' split_hours='12' />
@@ -232,56 +418,6 @@ module Zena
232
418
  render_html_tag(calendar_code(finder, cell_prefix_code, cell_code, cell_postfix_code, params))
233
419
  end
234
420
 
235
- def calendar_code(finder, cell_prefix_code, cell_code, cell_postfix_code, params)
236
- type = params[:type] ? params[:type].to_sym : :month
237
- size = (params[:size] || 'large').to_sym
238
- ref_date = params[:assign_as] ? 'l_date' : (params[:date] || 'event_at')
239
-
240
- case type
241
- when :month
242
- title = "\"\#{_(Date::MONTHNAMES[main_date.mon])} \#{main_date.year}\""
243
- prev_date = "\#{main_date.advance(:months => -1).strftime(\"%Y-%m-%d\")}"
244
- next_date = "\#{main_date.advance(:months => 1).strftime(\"%Y-%m-%d\")}"
245
- when :week
246
- title = "\"\#{_(Date::MONTHNAMES[main_date.mon])} \#{main_date.year}\""
247
- prev_date = "\#{main_date.advance(:days => -7).strftime(\"%Y-%m-%d\")}"
248
- next_date = "\#{main_date.advance(:days => +7).strftime(\"%Y-%m-%d\")}"
249
- else
250
- return parser_error("invalid type (should be 'month' or 'week')")
251
- end
252
-
253
- if hours = @params[:split_hours]
254
- hours = hours.split(',').map{|l| l.to_i}
255
- hours << 0
256
- hours = hours.uniq.sort
257
- # I feel all this would be much better if we could use "each_group" but then how do we access hours ?
258
- week_code = "<% week.step(week+6,1) do |day_#{list_var}| %>
259
- <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>
260
- <% end %>"
261
- (@context[:vars] ||= []) << "hour"
262
- else
263
- hours = nil
264
- week_code = "<% week.step(week+6,1) do |day_#{list_var}| %>
265
- <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>
266
- <% end %>"
267
- end
268
-
269
- res = %Q{
270
- <h3 class='title'>
271
- <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>
272
- <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>
273
- <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>
274
- </h3>
275
- <table cellspacing='0' class='#{size}cal#{@params[:assign_as] ? " assign" : ''}'>
276
- <tr class='head'><%= cal_day_names(#{size.inspect}) %></tr>
277
- <% start_date, end_date = cal_start_end(#{current_date}, #{type.inspect}) %>
278
- <% cal_weeks(#{ref_date.to_sym.inspect}, #{finder}, start_date, end_date, #{hours.inspect}) do |week, nodes_#{list_var}| %>
279
- <tr class='body'>
280
- #{week_code}
281
- </tr>
282
- <% end %>
283
- </table>}
284
- end
285
421
  =end
286
422
  end # ZafuMethods
287
423
  end # Calendar
@@ -4,7 +4,7 @@ module Zena::Use::Conditional
4
4
  def rubyless_class_scope(class_name)
5
5
  return parser_error("Cannot scope class in list (use each before filtering).") if node.list_context?
6
6
  # capital letter ==> class conditional
7
- if klass = get_class(class_name)
7
+ if klass = VirtualClass[class_name]
8
8
  if klass.kpath =~ %r{^#{node.klass.kpath}} || @context[:saved_template]
9
9
  # Saved templates can be rendered with anything...
10
10
  # FIXME: Make sure saved templates from 'block' start with the proper node type ?
@@ -143,12 +143,12 @@ module Zena
143
143
  end # ViewMethods
144
144
 
145
145
  module ZafuMethods
146
+ # Uses Enrollable::ZafuMethods::get_class
146
147
 
147
- # Resolve class for @post ==> Post, etc.
148
- def get_class(class_name)
149
- VirtualClass[class_name] || super
150
- rescue
151
- nil
148
+ # Return the node context for a given class (looks up into the hierarchy) or the
149
+ # current node context if klass is nil.
150
+ def node(klass = nil)
151
+ super(klass && klass.kind_of?(VirtualClass) ? klass.real_class : klass)
152
152
  end
153
153
 
154
154
  # Enter a new context (<r:context find='all' select='pages'>). This is the same as '<r:pages>...</r:pages>'). It is