zena 1.2.2 → 1.2.3

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 (143) hide show
  1. data/History.txt +25 -0
  2. data/app/controllers/documents_controller.rb +3 -25
  3. data/app/controllers/nodes_controller.rb +34 -24
  4. data/app/controllers/user_sessions_controller.rb +5 -4
  5. data/app/controllers/versions_controller.rb +44 -17
  6. data/app/models/acl.rb +2 -7
  7. data/app/models/group.rb +6 -2
  8. data/app/models/link.rb +14 -0
  9. data/app/models/node.rb +2 -2
  10. data/app/models/site.rb +13 -4
  11. data/app/models/text_document.rb +1 -1
  12. data/app/models/user.rb +11 -2
  13. data/app/models/virtual_class.rb +1 -1
  14. data/app/views/groups/_form.rhtml +6 -6
  15. data/app/views/nodes/render_error.rhtml +15 -0
  16. data/app/views/templates/document_create_tabs/_file.rhtml +1 -1
  17. data/app/views/templates/document_create_tabs/_import.rhtml +1 -1
  18. data/app/views/templates/document_create_tabs/_template.rhtml +1 -1
  19. data/app/views/templates/document_create_tabs/_text_document.rhtml +1 -1
  20. data/app/views/templates/edit_tabs/_title.rhtml +1 -1
  21. data/app/views/zafu/default/Node-admin.zafu +1 -1
  22. data/bricks/acls/zena/test/integration/acl_integration_test.rb +2 -2
  23. data/bricks/acls/zena/test/unit/acl_test.rb +2 -1
  24. data/bricks/fs_skin/zena/migrate/20110702010330_add_fs_skin_to_idx_templates.rb +1 -0
  25. data/bricks/fs_skin/zena/skins/blog/img/style.css +4 -4
  26. data/bricks/grid/lib/bricks/grid.rb +9 -3
  27. data/bricks/passenger/zena/deploy.rb +2 -1
  28. data/bricks/pdf/lib/bricks/pdf.rb +1 -1
  29. data/bricks/tags/zena/test/zafu/tags.yml +5 -1
  30. data/bricks/zena/zena/migrate/20120904071601_change_link_status_to_float.rb +13 -0
  31. data/config/bricks.yml +10 -10
  32. data/config/deploy.rb +1 -5
  33. data/config/gems.yml +2 -2
  34. data/db/init/base/skins/default/Node.zafu +7 -3
  35. data/db/init/base/skins/default/notes.zafu +3 -1
  36. data/lib/zafu/all.rb +0 -9
  37. data/lib/zafu/compiler.rb +0 -4
  38. data/lib/zafu/controller_methods.rb +0 -2
  39. data/lib/zafu/handler.rb +0 -5
  40. data/lib/zafu/markup.rb +4 -6
  41. data/lib/zafu/ordered_hash.rb +3 -2
  42. data/lib/zafu/parsing_rules.rb +1 -3
  43. data/lib/zafu/process/ajax.rb +4 -2
  44. data/lib/zafu/process/context.rb +34 -4
  45. data/lib/zafu/process/forms.rb +2 -2
  46. data/lib/zafu/process/ruby_less_processing.rb +5 -10
  47. data/lib/zafu/template.rb +0 -2
  48. data/lib/zafu/test_helper.rb +0 -2
  49. data/lib/zafu/view_methods.rb +0 -1
  50. data/lib/zafu.rb +1 -1
  51. data/lib/zena/acts/secure_node.rb +5 -4
  52. data/lib/zena/console.rb +19 -17
  53. data/lib/zena/core_ext/string.rb +3 -2
  54. data/lib/zena/deploy/app_init.rhtml +6 -1
  55. data/lib/zena/deploy/httpd.rhtml +16 -13
  56. data/lib/zena/deploy/stats.vhost.rhtml +1 -1
  57. data/lib/zena/deploy/vhost.rhtml +31 -11
  58. data/lib/zena/deploy/vhost_ssl_redir.rhtml +12 -0
  59. data/lib/zena/deploy/vhost_www.rhtml +1 -1
  60. data/lib/zena/deploy.rb +55 -11
  61. data/lib/zena/info.rb +1 -1
  62. data/lib/zena/parser/zazen_rules.rb +18 -9
  63. data/lib/zena/routes.rb +1 -3
  64. data/lib/zena/site_worker.rb +8 -1
  65. data/lib/zena/use/ajax.rb +29 -3
  66. data/lib/zena/use/ancestry.rb +2 -1
  67. data/lib/zena/use/authlogic.rb +12 -18
  68. data/lib/zena/use/context.rb +1 -1
  69. data/lib/zena/use/dates.rb +28 -18
  70. data/lib/zena/use/display.rb +49 -7
  71. data/lib/zena/use/forms.rb +51 -18
  72. data/lib/zena/use/html_tags.rb +6 -6
  73. data/lib/zena/use/i18n.rb +13 -4
  74. data/lib/zena/use/image_builder.rb +2 -0
  75. data/lib/zena/use/query_builder.rb +39 -14
  76. data/lib/zena/use/query_link.rb +57 -0
  77. data/lib/zena/use/query_node.rb +68 -32
  78. data/lib/zena/use/relations.rb +25 -15
  79. data/lib/zena/use/rendering.rb +66 -15
  80. data/lib/zena/use/upload.rb +34 -5
  81. data/lib/zena/use/urls.rb +28 -25
  82. data/lib/zena/use/version_hash.rb +14 -2
  83. data/lib/zena/use/zafu_safe_definitions.rb +72 -3
  84. data/lib/zena/use/zazen.rb +16 -4
  85. data/lib/zena.rb +1 -0
  86. data/public/javascripts/grid.js +213 -64
  87. data/public/javascripts/raphael.js +10 -0
  88. data/public/javascripts/zena.js +146 -22
  89. data/public/stylesheets/reset.css +12 -12
  90. data/public/stylesheets/zena.css +1 -1
  91. data/test/custom_queries/complex.host.yml +19 -0
  92. data/test/fixtures/files/TestNode.zafu +40 -4
  93. data/test/functional/nodes_controller_test.rb +84 -39
  94. data/test/functional/versions_controller_test.rb +2 -2
  95. data/test/integration/navigation_test.rb +61 -35
  96. data/test/integration/query_node/basic.yml +7 -7
  97. data/test/integration/query_node/comments.yml +1 -1
  98. data/test/integration/query_node/complex.yml +3 -3
  99. data/test/integration/query_node/filters.yml +32 -8
  100. data/test/integration/query_node/idx_key_value.yml +10 -10
  101. data/test/integration/query_node/idx_scope.yml +7 -7
  102. data/test/integration/query_node/relations.yml +4 -4
  103. data/test/integration/zafu_compiler/ajax.yml +19 -11
  104. data/test/integration/zafu_compiler/apphelper.yml +1 -1
  105. data/test/integration/zafu_compiler/asset.yml +2 -2
  106. data/test/integration/zafu_compiler/comments.yml +1 -1
  107. data/test/integration/zafu_compiler/dates.yml +1 -1
  108. data/test/integration/zafu_compiler/display.yml +49 -21
  109. data/test/integration/zafu_compiler/eval.yml +4 -4
  110. data/test/integration/zafu_compiler/forms.yml +25 -11
  111. data/test/integration/zafu_compiler/i18n.yml +5 -0
  112. data/test/integration/zafu_compiler/meta.yml +3 -3
  113. data/test/integration/zafu_compiler/query.yml +27 -9
  114. data/test/integration/zafu_compiler/relations.yml +9 -9
  115. data/test/integration/zafu_compiler/roles.yml +6 -6
  116. data/test/integration/zafu_compiler/rubyless.yml +7 -2
  117. data/test/integration/zafu_compiler/safe_definitions.yml +33 -4
  118. data/test/integration/zafu_compiler/security.yml +46 -1
  119. data/test/integration/zafu_compiler/urls.yml +28 -13
  120. data/test/integration/zafu_compiler/user.yml +12 -7
  121. data/test/integration/zafu_compiler/zafu_attributes.yml +1 -1
  122. data/test/integration/zafu_compiler/zazen.yml +5 -5
  123. data/test/integration/zafu_compiler_test.rb +18 -0
  124. data/test/selenium/Filter/filter3.rsel +20 -0
  125. data/test/selenium/Filter/filter4.rsel +20 -0
  126. data/test/sites/zena/versions.yml +2 -0
  127. data/test/unit/exif_data_test.rb +6 -1
  128. data/test/unit/group_test.rb +18 -3
  129. data/test/unit/node_test.rb +0 -7
  130. data/test/unit/project_test.rb +4 -0
  131. data/test/unit/relation_proxy_test.rb +2 -2
  132. data/test/unit/remote_test.rb +0 -9
  133. data/test/unit/role_test.rb +1 -1
  134. data/test/unit/string_hash_test.rb +1 -1
  135. data/test/unit/text_document_test.rb +13 -13
  136. data/test/unit/zena/use/html_tags_test.rb +6 -6
  137. data/test/unit/zena/use/rendering_test.rb +20 -10
  138. data/test/unit/zena/use/urls_test.rb +21 -18
  139. data/test/unit/zena/use/zafu_template_test.rb +0 -5
  140. data/test/unit/zena/use/zazen_test.rb +25 -25
  141. data/zena.gemspec +63 -57
  142. metadata +136 -130
  143. data/test/functional/nodes_controller_commit_test.rb +0 -67
@@ -177,14 +177,14 @@ module Zena
177
177
 
178
178
  def get_scope_index_field(field_name)
179
179
  return nil if @query.main_class.real_class.column_names.include?(field_name)
180
- # scope index
180
+ # 1. Try scope index
181
181
  klass = @query.main_class
182
182
  if index_model = klass.kind_of?(VirtualClass) ? klass.idx_class : nil
183
183
  index_model = Zena.resolve_const(index_model) rescue NilClass
184
184
  if index_model < Zena::Use::ScopeIndex::IndexMethods && index_model.column_names.include?(field_name)
185
185
  table_to_use = add_key_value_table('scope_index', index_model.table_name) do |tbl_name|
186
- # This block is only executed once
187
- add_filter "#{table('nodes')}.id = #{tbl_name}.node_id"
186
+ # This block is only executed once (ON clause)
187
+ "#{table('nodes')}.id = #{tbl_name}.node_id"
188
188
  end
189
189
  "#{table_to_use}.#{field_name}"
190
190
  else
@@ -192,7 +192,6 @@ module Zena
192
192
  nil
193
193
  end
194
194
  else
195
- # no index model: ignore
196
195
  nil
197
196
  end
198
197
  end
@@ -220,7 +219,7 @@ module Zena
220
219
  use_name, source, target, filter = table_def
221
220
  table_to_use = add_key_value_table(use_name, target, map_def[:key]) do |tbl_name|
222
221
  # This block is only executed once
223
- add_filter filter.gsub(
222
+ filter.gsub(
224
223
  'TABLE1', table(source)
225
224
  ).gsub(
226
225
  'TABLE2', tbl_name
@@ -241,20 +240,6 @@ module Zena
241
240
  elsif field_name == 'random'
242
241
  Zena::Db.sql_function(field_name, nil)
243
242
  else
244
- if processing_filter? && field_name =~ /^(.*)_ids?$/
245
- # tag_id = 33 ===> join links as lk, nodes as tt .......
246
- rel = $1
247
-
248
- # Fake field_or_attr so it does not use 'zip' on nodes
249
- context[:processing] = :relation
250
- if join_relation($1, 'jnode')
251
- res = "#{table('jnode')}.zip"
252
- end
253
- context[:processing] = :filter
254
-
255
- return res
256
- end
257
-
258
243
  # property or real column
259
244
 
260
245
  # FIXME !!!! Why does this happen ?
@@ -279,12 +264,14 @@ module Zena
279
264
 
280
265
  tbl = add_key_value_table(group_name, index_table, field_name) do |tbl_name|
281
266
  # This block is only executed once
282
- add_filter "#{tbl_name}.node_id = #{table}.id"
283
- add_filter "#{tbl_name}.key = #{quote(field_name)}"
267
+ on_clause = "#{tbl_name}.node_id = #{table}.id AND #{tbl_name}.key = #{quote(field_name)}"
284
268
  if group_name.to_s =~ /^ml_/
285
- add_filter "#{tbl_name}.lang = #{quote(visitor.lang)}"
269
+ on_clause << " AND #{tbl_name}.lang = #{quote(visitor.lang)}"
286
270
  end
287
271
  # no need for distinct, the new table makes a 1-1 relation
272
+ # ON CLAUSE
273
+ on_clause
274
+
288
275
  end
289
276
 
290
277
  "#{tbl}.value"
@@ -364,8 +351,44 @@ module Zena
364
351
  if fld = get_scope_index_field(scope_idx_field)
365
352
  return [[:idx_field, fld], function]
366
353
  else
367
- # not a scope index field
368
- return [arg1, arg2]
354
+ # 2. Try to use relation as scope filter 'hot.title = "xxx"', 'hot.title = "xxx"'
355
+ # Multiple links with hot.id = 334 and hot2.id = 323...
356
+ rel = class_name.sub(/\d$/,'')
357
+
358
+ source_kpath = @query.main_class.kpath
359
+
360
+ if rel = RelationProxy.find_by_role(rel.singularize, source_kpath)
361
+ table_to_use = add_key_value_table('jnode', 'nodes', class_name) do |tbl_name|
362
+ # This block is only executed once per relation name (once for 'hot', once for 'hot2')
363
+ # TODO: Can we remove this ?
364
+ distinct!
365
+ lnk = add_key_value_table('links', 'links', class_name) do |lt|
366
+ "#{lt}.#{rel.link_side} = #{table(main_table)}.id AND #{lt}.relation_id = #{rel.id}"
367
+ end
368
+
369
+ %Q{#{tbl_name}.id = #{lnk}.#{rel.other_side}}
370
+ end
371
+
372
+ # Temporarily move to the remote class
373
+ tbl_alias_bak = context[:table_alias]
374
+ context[:table_alias] = table_to_use
375
+ main_class_bak = @query.main_class
376
+
377
+ @query.main_class = rel.other_vclass
378
+ fld = process_field(field_name)
379
+
380
+ # Move back
381
+ @query.main_class = main_class_bak
382
+ context[:table_alias] = tbl_alias_bak
383
+ if fld
384
+ [[:idx_field, fld], function]
385
+ else
386
+ nil
387
+ end
388
+ else
389
+ # not a scope index field
390
+ return [arg1, arg2]
391
+ end
369
392
  end
370
393
  end
371
394
 
@@ -465,6 +488,24 @@ module Zena
465
488
  add_filter "#{table('comments')}.discussion_id = #{table('discussions')}.id"
466
489
  # after_parse
467
490
  end
491
+ when 'tag_cloud'
492
+ # tag cloud
493
+ if last?
494
+ change_processor Link.query_compiler, :rubyless_helper => @rubyless_helper
495
+ # no need to load discussions, versions and all the mess
496
+ add_table('links')
497
+ lnk = table('links')
498
+ add_filter "(#{lnk}.source_id = #{process_attr('id')} OR #{lnk}.target_id = #{process_attr('id')}) AND #{lnk}.relation_id IS NULL"
499
+ else
500
+ after_process # Make sure we secure the current part
501
+ change_processor Link.query_compiler, :rubyless_helper => @rubyless_helper
502
+
503
+ add_table('links')
504
+ lnk = table('links')
505
+ add_filter "(#{lnk}.source_id = #{table('nodes')}.id OR #{lnk}.target_id = #{table('nodes')}.id) AND #{lnk}.relation_id IS NULL"
506
+ end
507
+ @query.group = " GROUP BY #{lnk}.comment"
508
+ add_select("COUNT(#{table('nodes')}.id)", 'link_count')
468
509
  else
469
510
  return nil
470
511
  end
@@ -603,14 +644,9 @@ module Zena
603
644
  end
604
645
 
605
646
  if rel = RelationProxy.find_by_role(relation.singularize, source_kpath)
606
- if use_name
607
- # Doing a jnode.. (node for filtering), we need to reverse relation
608
- rel.side = rel.side == :source ? :target : :source
609
- add_table(use_name, main_table)
610
- else
611
- add_table(main_table)
612
- set_main_class(rel.other_vclass)
613
- end
647
+
648
+ add_table(main_table)
649
+ set_main_class(rel.other_vclass)
614
650
 
615
651
  add_table('links')
616
652
 
@@ -130,7 +130,7 @@ module Zena
130
130
  def l_status
131
131
  return @l_status if defined? @l_status
132
132
  val = @link ? @link[:status] : self['l_status']
133
- val ? val.to_i : nil
133
+ val ? val.to_f : nil
134
134
  end
135
135
 
136
136
  # TODO: could we use LINK_ATTRIBUTES and 'define_method' here ?
@@ -151,19 +151,6 @@ module Zena
151
151
  @link ? @link[:id] : (self[:link_id] == -1 ? nil : self[:link_id]) # -1 == dummy link
152
152
  end
153
153
 
154
- def link_id=(v)
155
- if @link && @link[:id].to_i != v.to_i
156
- @link = nil
157
- end
158
- self[:link_id] = v.to_i
159
- if @link_attributes_to_update
160
- if rel = relation_proxy_from_link
161
- @link_attributes_to_update.each do |k,v|
162
- rel.send("other_#{k}=",v)
163
- end
164
- end
165
- end
166
- end
167
154
 
168
155
  # FIXME: this method does an 'update' not only 'add'
169
156
  def add_link(role, hash)
@@ -228,13 +215,19 @@ module Zena
228
215
  @l_comment = v.blank? ? nil : v
229
216
  if rel = relation_proxy_from_link
230
217
  rel.other_comment = @l_comment
218
+ else
219
+ l = @link_attributes_to_update ||= {}
220
+ l[:comment] = @l_comment
231
221
  end
232
222
  end
233
223
 
234
224
  def l_status=(v)
235
- @l_status = v.blank? ? nil : v.to_i
225
+ @l_status = v.blank? ? nil : v
236
226
  if rel = relation_proxy_from_link
237
227
  rel.other_status = @l_status
228
+ else
229
+ l = @link_attributes_to_update ||= {}
230
+ l[:status] = @l_status
238
231
  end
239
232
  end
240
233
 
@@ -242,6 +235,23 @@ module Zena
242
235
  @l_date = v.blank? ? nil : v
243
236
  if rel = relation_proxy_from_link
244
237
  rel.other_date = @l_date
238
+ else
239
+ l = @link_attributes_to_update ||= {}
240
+ l[:date] = @l_date
241
+ end
242
+ end
243
+
244
+ def link_id=(v)
245
+ if @link && @link[:id].to_i != v.to_i
246
+ @link = nil
247
+ end
248
+ self[:link_id] = v.to_i
249
+ if @link_attributes_to_update
250
+ if rel = relation_proxy_from_link
251
+ @link_attributes_to_update.each do |k,v|
252
+ rel.send("other_#{k}=",v)
253
+ end
254
+ end
245
255
  end
246
256
  end
247
257
 
@@ -35,6 +35,7 @@ module Zena
35
35
  module ControllerMethods
36
36
  def self.included(base)
37
37
  base.send(:helper_attr, :js_data, :zafu_headers)
38
+ base.send(:helper_method, :set_caching)
38
39
  base.send(:layout, false)
39
40
  end
40
41
 
@@ -45,6 +46,12 @@ module Zena
45
46
  def zafu_headers
46
47
  @zafu_headers ||= {}
47
48
  end
49
+
50
+ def set_caching(opts)
51
+ if request.query_string =~ opts[:allow_query]
52
+ @cache_query = request.query_string
53
+ end
54
+ end
48
55
 
49
56
  # TODO: test
50
57
  # Our own handling of exceptions
@@ -147,10 +154,18 @@ module Zena
147
154
  }
148
155
  end
149
156
 
150
- if result[:type] == 'text/html'
157
+ if error = result[:error]
151
158
  # error reporting from rendering engine
152
159
  opts[:cache] = false
153
- render :text => result[:data]
160
+ @render_error = error
161
+ render :file => 'nodes/render_error'
162
+
163
+ elsif result[:type] == 'text/html'
164
+ # debugging mode from rendering engine
165
+ opts[:cache] = false
166
+
167
+ render :inline => result[:data]
168
+
154
169
  else
155
170
  if zafu_headers
156
171
  if disposition = zafu_headers.delete('Content-Disposition')
@@ -182,9 +197,13 @@ module Zena
182
197
  cache_page(:content_data => result[:data], :content_path => result[:file]) if opts[:cache]
183
198
  else
184
199
  # html
185
- render :file => template_url(opts), :layout=>false, :status => opts[:status]
186
- headers.merge!(zafu_headers)
187
- cache_page(:url => opts[:cache_url]) if opts[:cache]
200
+ if opts.delete(:as_string)
201
+ render_to_string :file => template_url(opts), :layout => false
202
+ else
203
+ render :file => template_url(opts), :layout => false, :status => opts[:status]
204
+ headers.merge!(zafu_headers)
205
+ cache_page(:url => opts[:cache_url]) if opts[:cache]
206
+ end
188
207
  end
189
208
  # This does not work, Rendering::Redirect is wrapped in TemplateError
190
209
  # rescue Zena::Use::Rendering::Redirect
@@ -204,14 +223,15 @@ module Zena
204
223
  def cache_page(opts={})
205
224
  if cachestamp_format?(params['format'])
206
225
  headers['Expires'] = (Time.now + 365*24*3600).strftime("%a, %d %b %Y %H:%M:%S GMT")
207
- headers['Cache-Control'] = (!current_site.authentication? && @node.public?) ? 'public' : 'private'
226
+ headers['Cache-Control'] = (!current_site.authentication? && @node.v_public?) ? 'public' : 'private'
208
227
  end
209
228
 
210
229
  if perform_caching && caching_allowed(:authenticated => opts.delete(:authenticated))
211
230
 
212
231
  url = page_cache_file(opts.delete(:url))
232
+
213
233
  opts = {:expire_after => nil,
214
- :path => (current_site.public_path + url),
234
+ :path => (current_site.cache_path + url),
215
235
  :content_data => response.body,
216
236
  :node_id => @node[:id]
217
237
  }.merge(opts)
@@ -221,7 +241,7 @@ module Zena
221
241
 
222
242
  # Return true if we can cache the current page
223
243
  def caching_allowed(opts = {})
224
- return false if current_site.authentication? || query_params != {}
244
+ return false if current_site.authentication? || (query_params != {} && !@cache_query)
225
245
  # Cache even if authenticated (public content).
226
246
  # Content viewed by anonymous user should be cached anyway.
227
247
  opts[:authenticated] || visitor.is_anon?
@@ -229,16 +249,23 @@ module Zena
229
249
 
230
250
  # Cache file path that reflects the called url
231
251
  def page_cache_file(url = nil)
232
- path = url || url_for(:only_path => true, :skip_relative_url_root => true, :cachestamp => nil)
252
+ path = url || url_for(:only_path => true, :skip_relative_url_root => true)
233
253
  path = ((path.empty? || path == "/") ? "/index" : URI.unescape(path))
234
254
  ext = params[:format].blank? ? 'html' : params[:format]
235
- path << ".#{ext}" unless path =~ /\.#{ext}(\?\d+|)$/
236
- #
255
+
237
256
  # FULL QUERY_STRING in cached page ?
257
+ if @cache_query
258
+ # This builds blog29.htmlp=2.html and it is OK (helps make rewrite rules simple)
259
+ path << @cache_query << ".#{ext}"
260
+ else
261
+ path << ".#{ext}" unless path =~ /\.#{ext}(\?\d+|)$/
262
+ end
263
+
238
264
  if cachestamp_format?(params['format'])
239
- # We have to use a '.' because apache cannot serve static files with '?'.
240
- path << "." << make_cachestamp(@node, params['mode'])
265
+ # Set expire
266
+ response.headers['Expires'] = 1.year.from_now.httpdate
241
267
  end
268
+
242
269
  path
243
270
  end
244
271
 
@@ -272,14 +299,15 @@ module Zena
272
299
  # urls to localhost (the rendering engine is external to Zena and will need to
273
300
  # make calls to get assets).
274
301
  def baseurl
275
- if Zena::ASSET_PORT
302
+ if Zena::ASSET_PORT > 0
276
303
  if Zena::ASSET_PORT == request.port
277
304
  raise Exception.new("Custom rendering not allowed on this process (port == asset_port).")
278
305
  else
279
306
  "http://127.0.0.1:#{Zena::ASSET_PORT}"
280
307
  end
281
308
  else
282
- raise Exception.new("Using custom rendering without an asset host ('asset_port' setting in bricks.yml).")
309
+ # This can break if using mongrel and round-robin (deadlock)
310
+ "#{ssl_request? ? 'https' : 'http'}://#{current_site.host}"
283
311
  end
284
312
  end
285
313
 
@@ -304,6 +332,29 @@ module Zena
304
332
  out "<% set_headers(#{headers.join(', ')}) %>"
305
333
  end
306
334
  end
335
+
336
+ # <r:cache rebuild='true' allow_query='p=1?[0-9]'/>
337
+ # <r:cache rebuild='true' allow_query='p=1?[0-9]'/>
338
+ def r_cache
339
+ params_list = []
340
+ if re = @params[:allow_query]
341
+ reg_test = %r{^#{re}$}
342
+ params_list << ":allow_query => #{reg_test.inspect}"
343
+ end
344
+
345
+ if rebuild = @params[:rebuild]
346
+ # TODO: implement "replace if changed (diff)" instead of cache removal if there is the rebuild
347
+ # setting so that the cached timestamp does not change.
348
+ params_list << ":rebuild => #{@params[:rebuild].inspect}"
349
+ end
350
+
351
+ if at = @params[:expire_at]
352
+ params_list << ":rebuild => #{@params[:rebuild].inspect}"
353
+ end
354
+ out "<% set_caching(:allow_query => #{reg_test.inspect}, :rebuild => #{@params[:rebuild].inspect}) %>"
355
+ rescue => err
356
+ parser_error("Invalid regular expression (#{err.message})")
357
+ end
307
358
 
308
359
  def r_style
309
360
  @markup.tag = 'style'
@@ -5,6 +5,8 @@ require 'uuidtools'
5
5
  module Zena
6
6
  module Use
7
7
  module Upload
8
+ UPLOAD_KEY = defined?(Mongrel) ? 'upload_id' : "X-Progress-ID"
9
+
8
10
  def self.has_network?
9
11
  response = nil
10
12
  Net::HTTP.new('example.com', '80').start do |http|
@@ -41,6 +43,7 @@ module Zena
41
43
  yield(att, error) if block_given?
42
44
  [att, error]
43
45
  end
46
+
44
47
 
45
48
  private
46
49
  def fetch_uri(uri_str, max_file_size = 10)
@@ -141,13 +144,37 @@ module Zena
141
144
  end
142
145
  end
143
146
  end
147
+
148
+ def render_upload
149
+ responds_to_parent do # execute the redirect in the iframe's parent window
150
+ render :update do |page|
151
+ if @node.new_record?
152
+ page.replace_html 'form_errors', error_messages_for(:node, :object => @node)
153
+ page.call 'UploadProgress.setAsError'
154
+ else
155
+ page.call 'UploadProgress.setAsFinished'
156
+ page.delay(1) do # allow the progress bar fade to complete
157
+ if js = params[:js]
158
+ page << js.gsub('NODE_ID', @node.zip.to_s)
159
+ end
160
+ if params[:reload]
161
+ page << "Zena.t().Zena.reload(#{params[:reload].inspect})"
162
+ end
163
+ if params[:redir]
164
+ page << "Zena.reload_and_close(#{params[:redir].inspect})"
165
+ else
166
+ page.redirect_to document_url(@node[:zip], :reload => params[:reload], :js => params[:js])
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
144
173
  end # ControllerMethods
145
174
 
146
175
  module ViewMethods
147
176
  include RubyLess
148
- safe_method [:upload_field, {:type => String}] => String
149
177
 
150
- UPLOAD_KEY = defined?(Mongrel) ? 'upload_id' : "X-Progress-ID"
151
178
  def upload_form_tag(url_opts, html_opts = {})
152
179
  @uuid = UUIDTools::UUID.random_create.to_s.gsub('-','')
153
180
  html_opts.reverse_merge!(:multipart => true, :id => "UploadForm#{@uuid}")
@@ -165,6 +192,8 @@ module Zena
165
192
  end
166
193
 
167
194
  def upload_field(opts = {})
195
+ uuid = opts[:uuid] || @uuid
196
+ dom = opts[:dom] || 'node'
168
197
  case opts[:type].to_s
169
198
  when 'onclick'
170
199
  link = link_to_remote(_("change"), :update=>'upload_field', :url => get_uf_documents_path(:uuid => @uuid), :method => :get, :complete=>"['file', 'upload_field'].each(Element.toggle);")
@@ -174,11 +203,11 @@ module Zena
174
203
  <div id="upload_field" class='toggle_div' style='display:none;'></div>
175
204
  TXT
176
205
  else
177
- attach_file_id, attach_url_id = "af#{@uuid}", "au#{@uuid}"
178
- onchange = %Q{onchange="Zena.get_filename(this,'node_title'); $('node_title').focus(); $('node_title').select();"}
206
+ attach_file_id, attach_url_id = "af#{uuid}", "au#{uuid}"
207
+ onchange = %Q{onchange="Zena.get_filename(this,'#{dom}_title'); $('#{dom}_title').focus(); $('#{dom}_title').select();"}
179
208
  <<-TXT
180
209
  <div id='#{attach_file_id}' class='attach'><label for='attachment' onclick=\"['#{attach_file_id}', '#{attach_url_id}'].each(Element.toggle);\">#{_('file')} / <span class='off'>#{_('url')}</span></label>
181
- <input style='line-height:1.5em;' id="attachment#{@uuid}" name="attachment" #{onchange} class='file' type="file" /></div>
210
+ <input style='line-height:1.5em;' id="attachment#{uuid}" name="attachment" #{onchange} class='file' type='file'/></div>
182
211
 
183
212
  <div id='#{attach_url_id}' class='attach' style='display:none;'><label for='url' onclick=\"['#{attach_file_id}', '#{attach_url_id}'].each(Element.toggle);\"><span class='off'>#{_('file')}</span> / #{_('url')}</label>
184
213
  <input style='line-height:1.5em;' size='30' id='attachment_url' type='text' #{onchange} name='attachment_url'/><br/></div>
data/lib/zena/use/urls.rb CHANGED
@@ -18,10 +18,12 @@ module Zena
18
18
  }
19
19
 
20
20
 
21
- ALLOWED_REGEXP = /\A(([a-zA-Z]+)([0-9]+)|([#{String::ALLOWED_CHARS_IN_URL}\-%]+))(_[a-zA-Z]+|)(\..+|)\Z/
21
+ ALLOWED_REGEXP = /\A(([a-zA-Z]+)([0-9]+)|([#{String::ALLOWED_CHARS_IN_URL}\-%]+))(_[a-zA-Z]+|)(=[a-z0-9]+|)(\..+|)\Z/
22
22
 
23
23
  module Common
24
- CACHESTAMP_FORMATS = ['jpg', 'png', 'gif', 'css', 'js']
24
+ # This is directly related to the FileMatch clause in httpd.rhtml (mod_expires for apaches)
25
+ CACHESTAMP_FORMATS = %w{ico flv jpg jpeg png gif js css swf}
26
+
25
27
  def prefix
26
28
  if visitor.is_anon?
27
29
  visitor.lang
@@ -99,7 +101,7 @@ module Zena
99
101
  end
100
102
 
101
103
  if node.kind_of?(Document) && format == node.ext
102
- if node.public? && !visitor.site.authentication?
104
+ if node.v_public? && !visitor.site.authentication?
103
105
  # force the use of a cacheable path for the data, even when navigating in '/oo'
104
106
  pre = node.version.lang
105
107
  end
@@ -108,12 +110,9 @@ module Zena
108
110
  if asset = opts.delete(:asset)
109
111
  mode = nil
110
112
  end
111
-
112
-
113
+
113
114
  if should_cachestamp?(node, format, asset)
114
- opts[:cachestamp] = make_cachestamp(node, mode)
115
- else
116
- opts.delete(:cachestamp) # cachestamp
115
+ stamp = make_cachestamp(node, mode)
117
116
  end
118
117
 
119
118
  path = if !asset && node[:id] == visitor.site[:root_id] && mode.nil? && format == 'html'
@@ -122,7 +121,8 @@ module Zena
122
121
  "#{abs_url_prefix}/#{pre}/" +
123
122
  basepath_as_url(node.basepath) +
124
123
  (mode ? "_#{mode}" : '') +
125
- (asset ? ".#{asset}" : '') +
124
+ (asset ? "=#{asset}" : '') +
125
+ (stamp ? ".#{stamp}" : '') +
126
126
  (format == 'html' ? '' : ".#{format}")
127
127
  else
128
128
  "#{abs_url_prefix}/#{pre}/" +
@@ -130,7 +130,8 @@ module Zena
130
130
  (node.klass.downcase ) +
131
131
  (node[:zip].to_s ) +
132
132
  (mode ? "_#{mode}" : '') +
133
- (asset ? ".#{asset}" : '') +
133
+ (asset ? "=#{asset}" : '') +
134
+ (stamp ? ".#{stamp}" : '') +
134
135
  ".#{format}"
135
136
  end
136
137
  append_query_params(path, opts)
@@ -150,7 +151,6 @@ module Zena
150
151
  if opts == {}
151
152
  path
152
153
  else
153
- cachestamp = opts.delete(:cachestamp)
154
154
  tz = opts.delete(:tz)
155
155
  list = opts.keys.map do |k|
156
156
  # FIXME: DOC
@@ -181,14 +181,10 @@ module Zena
181
181
  nil
182
182
  end
183
183
  end.flatten.compact
184
- if cachestamp
185
- result = path + "?#{cachestamp}" + (list.empty? ? '' : "&#{list.sort.join('&')}")
186
- result
187
- else
188
- # TODO: replace '&' by '&amp;' ? Or escape later ? Use h before zen_path in templates ? What about css/xls/other stuff ?
189
- # Best solution: use 'h' in template when set in default
190
- path + (list.empty? ? '' : "?#{list.sort.join('&')}")
191
- end
184
+
185
+ # TODO: replace '&' by '&amp;' ? Or escape later ? Use h before zen_path in templates ? What about css/xls/other stuff ?
186
+ # Best solution: use 'h' in template when set in default
187
+ path + (list.empty? ? '' : "?#{list.sort.join('&')}")
192
188
  end
193
189
  end
194
190
 
@@ -218,7 +214,7 @@ module Zena
218
214
  end
219
215
 
220
216
  def make_cachestamp(node, mode)
221
- if mode
217
+ str = if mode
222
218
  if node.kind_of?(Image)
223
219
  if iformat = Iformat[mode]
224
220
  "#{node.updated_at.to_i + iformat[:hash_id]}"
@@ -234,6 +230,8 @@ module Zena
234
230
  else
235
231
  node.updated_at.to_i.to_s
236
232
  end
233
+
234
+ Digest::SHA1.hexdigest(str)[0..4]
237
235
  end
238
236
 
239
237
  # Url parameters (without format/mode/prefix...)
@@ -695,7 +693,7 @@ module Zena
695
693
 
696
694
  def insert_ajax_args(target, hash_params, action)
697
695
  hash_params << ":s => start_id"
698
- hash_params << ":link_id => this.link_id" if @context[:has_link_id] && node.will_be?(Node)
696
+ hash_params << ":link_id => this.link_id" if @context[:has_link_id] && node.will_be?(Node) && !node.list_context?
699
697
 
700
698
  # FIXME: when we have proper markup.dyn_params[:id] support,
701
699
  # we should not need this crap anymore.
@@ -816,6 +814,7 @@ module Zena
816
814
  end
817
815
 
818
816
  def text_for_link(default = nil)
817
+
819
818
  if dynamic_blocks?
820
819
  expand_with
821
820
  else
@@ -826,15 +825,19 @@ module Zena
826
825
  end
827
826
 
828
827
  if method
829
- method.literal ? erb_escape(method.literal) : "<%= #{method} %>"
828
+ if method.opts[:html_safe]
829
+ method.literal || "<%= #{method} %>"
830
+ else
831
+ method.literal ? ::ERB::Util.html_escape(method.literal) : "<%=h #{method} %>"
832
+ end
830
833
  elsif default
831
834
  default
832
835
  elsif node.will_be?(Node)
833
- "<%= #{node(Node)}.prop['title'] %>"
836
+ "<%=h #{node(Node)}.prop['title'] %>"
834
837
  elsif node.will_be?(Version)
835
- "<%= #{node(Version)}.node.prop['title'] %>"
838
+ "<%=h #{node(Version)}.node.prop['title'] %>"
836
839
  elsif node.will_be?(Link)
837
- "<%= #{node(Link)}.name %>"
840
+ "<%=h #{node(Link)}.name %>"
838
841
  else
839
842
  _('edit')
840
843
  end
@@ -59,11 +59,23 @@ module Zena
59
59
  end
60
60
  end
61
61
 
62
- def version_id(lang = nil)
62
+ def version_id(lang = nil, ugps = visitor.group_ids)
63
63
  lang ||= visitor.lang
64
- access = can_see_redactions? ? vhash['w'] : vhash['r']
64
+ access = can_see_redactions?(ugps) ? vhash['w'] : vhash['r']
65
65
  access[lang] || access[self[:ref_lang]] || access.values.first
66
66
  end
67
+
68
+ # Return true if the current version can be seen by the public.
69
+ def v_public?
70
+ visitor.is_anon? ||
71
+ begin
72
+ anon = visitor.site.anon
73
+ # visible by anonymous
74
+ can_read?(anon, anon.group_ids) &&
75
+ # anonymous would see this exact version
76
+ version.id == version_id(visitor.lang, visitor.site.anon.group_ids)
77
+ end
78
+ end
67
79
 
68
80
  # Return the list of versions that are stored in the vhash and could be loaded depending
69
81
  # on the visitor.