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
@@ -13,6 +13,8 @@ module Zena
13
13
 
14
14
  module ImageTags
15
15
  include Common
16
+ IMG_TAG_EXTRA_RE = %r{UUID|(PATH)_([a-z]+)\.([a-z]+)}
17
+ IMG_TAG_EXTRA_JS = %r{\[JS\](.*?)\[/JS\]}
16
18
 
17
19
  # This is used by _crop.rhtml
18
20
  def crop_formats(obj)
@@ -48,7 +50,7 @@ module Zena
48
50
  return img_tag(icon, opts.merge(:alt_src => nil))
49
51
  end
50
52
  end
51
-
53
+
52
54
  # 3. generic icon
53
55
  res ||= generic_img_tag(obj, opts)
54
56
 
@@ -67,6 +69,30 @@ module Zena
67
69
  # <img> tag definition to show an Image / mp3 document
68
70
  # FIXME: this should live inside zafu
69
71
  def asset_img_tag(obj, opts)
72
+ if img_tag = obj.prop["img_tag_#{opts[:mode] || 'std'}"]
73
+ # Hand made image tag
74
+ if img_tag.kind_of?(String)
75
+ # We use code to make it raw.
76
+ uuid = "img#{UUIDTools::UUID.random_create.to_s.gsub('-','')[0..6]}"
77
+ tag = img_tag.gsub(IMG_TAG_EXTRA_RE) do
78
+ if $& == 'UUID'
79
+ uuid
80
+ elsif $1 == 'PATH'
81
+ zen_path(obj, :mode => $2, :format => $3)
82
+ end
83
+ end
84
+ tag.gsub!(IMG_TAG_EXTRA_JS) do
85
+ self.js_data << $1
86
+ ''
87
+ end
88
+ return raw_content(tag)
89
+ end
90
+ elsif img_tag = obj.prop["img_tag"]
91
+ if img_tag.kind_of?(Hash) && img_tag = img_tag[opts[:mode] || 'std']
92
+ return raw_content(img_tag.gsub('UUID', "img#{UUIDTools::UUID.random_create.to_s.gsub('-','')[0..6]}"))
93
+ end
94
+ end
95
+
70
96
  if obj.kind_of?(Image)
71
97
  res = {}
72
98
  format = Iformat[opts[:mode]] || Iformat['std']
@@ -208,7 +234,7 @@ module Zena
208
234
  include ImageTags
209
235
  include RubyLess
210
236
  safe_method [:sprintf, String, Number] => {:class => String, :method => 'sprintf'}
211
- safe_method [:search_box, {:ajax => String, :type => String}] => String
237
+ safe_method [:search_box, {:ajax => String, :type => String}] => {:class => String, :method => 'search_box', :html_safe => true}
212
238
  safe_context [:admin_links, {:list => String}] => [String]
213
239
 
214
240
  # Return sprintf formated entry. Return '' for values eq to zero.
@@ -432,6 +458,7 @@ module Zena
432
458
  :class => String,
433
459
  :method => 'zazen',
434
460
  :accept_nil => true,
461
+ :html_safe => true,
435
462
  :append_hash => {:node => ::RubyLess::TypedString.new(node.to_s, :class => node.klass)}
436
463
  }
437
464
  else
@@ -488,6 +515,7 @@ module Zena
488
515
  end
489
516
 
490
517
  def extract_label(res, attribute)
518
+ return res if @params[:type] == 'hidden'
491
519
  attribute ||= @params[:param]
492
520
  if (label = param(:label) || param(:tlabel)) && attribute
493
521
  case label
@@ -517,13 +545,13 @@ module Zena
517
545
 
518
546
  # ??? <r:h do='foasfa'/> ?
519
547
  # def r_h
520
- # out "<%= h ??? %>"
548
+ # out "<%=h ??? %>"
521
549
  # end
522
550
 
523
551
  # Insert javascript asset tags
524
552
  def r_javascripts
525
553
  if @params[:list] == 'all' || @params[:list].nil?
526
- list = %w{ prototype effects dragdrop window zena grid }
554
+ list = %w{ prototype effects dragdrop window zena grid upload-progress }
527
555
  else
528
556
  list = @params[:list].split(',').map{|e| e.strip}
529
557
  end
@@ -570,7 +598,7 @@ module Zena
570
598
  # Insert stylesheet asset tags
571
599
  def r_stylesheets
572
600
  if @params[:list] == 'all' || @params[:list].nil?
573
- list = %w{ reset window zena code grid }
601
+ list = %w{ reset window zena code grid upload-progress }
574
602
  else
575
603
  list = @params[:list].split(',').map{|e| e.strip}
576
604
  end
@@ -698,10 +726,24 @@ module Zena
698
726
 
699
727
  # Also used by rubyless_expand
700
728
  def show_string(method)
701
- if param(:h) == 'false'
729
+ if method.kind_of?(RubyLess::TypedString)
730
+ if lit = method.literal
731
+ if method.opts[:html_safe]
732
+ lit
733
+ elsif param(:h) == 'false'
734
+ erb_escape(lit)
735
+ else
736
+ ::ERB::Util.html_escape(lit)
737
+ end
738
+ elsif param(:h) == 'false' || method.opts[:html_safe]
739
+ "<%= #{method} %>"
740
+ else
741
+ "<%=h #{method} %>"
742
+ end
743
+ elsif param(:h) == 'false'
702
744
  "<%= #{method} %>"
703
745
  else
704
- "<%= h #{method} %>"
746
+ "<%=h #{method} %>"
705
747
  end
706
748
  end
707
749
 
@@ -238,8 +238,16 @@ module Zena
238
238
  error_messages = r_errors + "\n"
239
239
  end
240
240
 
241
- opts[:form_tag] = %Q{
242
- <% form_for(:#{node.form_name}, #{node}, :url => #{node}.new_record? ? #{node.form_name.pluralize}_path : #{node.form_name}_path(#{node}.zip), :html => {:method => #{node}.new_record? ? :post : :put, :id => \"\#{ndom_id(#{node})}_form_t\"}) do |f| %>
241
+ html_id = "#{node.dom_prefix}_form_t".inspect
242
+ if descendant('upload_field')
243
+ uuid = UUIDTools::UUID.random_create.to_s.gsub('-','')
244
+ set_context_var('upload', 'uuid', uuid)
245
+ upload_html = ", :multipart => true, :onsubmit => %Q{submitUploadForm(#{html_id}, '#{uuid}');}"
246
+ upload_url = "'#{Zena::Use::Upload::UPLOAD_KEY}' => '#{uuid}'"
247
+ end
248
+
249
+ opts[:form_tag] = %Q{
250
+ <% form_for(:#{node.form_name}, #{node}, :url => #{node}.new_record? ? #{node.form_name.pluralize}_path(#{upload_url}) : #{node.form_name}_path(#{node}.zip#{upload_url ? ", #{upload_url}" : ""}), :html => {:method => #{node}.new_record? ? :post : :put, :id => #{html_id}#{upload_html}}) do |f| %>
243
251
  #{error_messages}}
244
252
  end
245
253
 
@@ -247,6 +255,8 @@ module Zena
247
255
  end
248
256
 
249
257
  def form_hidden_fields(opts)
258
+ dom_name = node.dom_prefix || dom_name
259
+
250
260
  hidden_fields = super
251
261
  add_params = @context[:add] ? @context[:add].params : {}
252
262
  set_fields = []
@@ -317,15 +327,17 @@ module Zena
317
327
  break
318
328
  end
319
329
  end
320
- if params[:done] == 'focus'
321
- if params[:focus]
322
- hidden_fields['done'] = "'$(\"#{erb_dom_id}_#{params[:focus]}\").focus();'"
323
- else
330
+
331
+ if params[:done]
332
+ if params[:done] == 'focus'
324
333
  hidden_fields['done'] = "'$(\"#{erb_dom_id}_form_t\").focusFirstElement();'"
334
+ else
335
+ done = RubyLess.translate_string(self, params[:done])
325
336
  end
326
- elsif params[:done]
327
- done = RubyLess.translate_string(self, params[:done])
337
+ elsif params[:focus]
338
+ hidden_fields['done'] = "'$(\"#{erb_dom_id}_#{params[:focus]}\").focus();'"
328
339
  end
340
+
329
341
  else
330
342
  # ajax form, not in 'add'
331
343
  done = RubyLess.translate_string(self, @params[:done])
@@ -338,6 +350,13 @@ module Zena
338
350
  end
339
351
  hidden_fields['done'] = done
340
352
  end
353
+ elsif upd = @params.delete(:preview)
354
+ hidden_fields['t_url'] = template_url(upd)
355
+ hidden_fields['dom_id'] = upd
356
+ hidden_fields['s'] = "<%= start_node_zip %>"
357
+ loading = @params[:loading]
358
+ loading = 'Zena.loading' if loading == 'true'
359
+ out "<% filter_form(#{node}, \"#{dom_name}_form_t\", #{loading.inspect}, '#{upd}') %>"
341
360
  else
342
361
  # no ajax
343
362
  cancel = "" # link to normal node ?
@@ -398,6 +417,10 @@ module Zena
398
417
 
399
418
  hidden_fields
400
419
  end
420
+
421
+ def r_upload_field
422
+ "<%= upload_field(:uuid => #{get_context_var('upload', 'uuid').inspect}, :dom => #{node.dom_prefix.inspect}) %>"
423
+ end
401
424
 
402
425
  def r_textarea
403
426
  html_attributes, attribute = get_input_params()
@@ -436,9 +459,15 @@ module Zena
436
459
  attribute = param
437
460
  else
438
461
  return parser_error("missing name") unless attribute
439
-
440
- if value = @params[:selected]
441
- selected = ::RubyLess.translate_string(self, value)
462
+ # {:value=>"<%= fquote((@node.prop['settings'] ? @node.prop['settings'][\"foo\"] : nil)) %>", :name=>"node[settings][foo]"}
463
+ # "settings_foo"
464
+
465
+
466
+ if selected = @params[:value] || @params[:selected]
467
+ selected = ::RubyLess.translate(self, selected)
468
+ unless selected.klass <= String
469
+ selected = "#{selected}.to_s"
470
+ end
442
471
  elsif @context[:in_filter]
443
472
  selected = "param_value(#{attribute.inspect}).to_s"
444
473
  elsif %w{parent_id}.include?(attribute)
@@ -448,18 +477,21 @@ module Zena
448
477
  elsif attribute =~ /^(.*)_id$/
449
478
  # relation
450
479
  selected = "#{node}.rel[#{$1.inspect}].other_zip.to_s"
451
- elsif type = node.klass.safe_method_type([attribute])
452
- selected = "#{node}.#{type[:method]}.to_s"
480
+ elsif selected = html_attributes[:value]
481
+ if selected =~ /\A<%= .*?\((.+)\)\s*%>/
482
+ # remove <%= %>
483
+ selected = "#{$1}.to_s"
484
+ else
485
+ selected = selected.inspect
486
+ end
453
487
  else
454
- # ???
455
- selected = "#{node}.prop[#{attribute.inspect}].to_s"
488
+ # FIXME: This would not work for a [asdf][asdf] hash property type...
489
+ selected = ::RubyLess.translate(self, "this.#{attribute}")
456
490
  end
457
491
  end
458
492
 
459
493
  if @context[:in_filter] || @params[:param]
460
494
  html_attributes[:name] = attribute
461
- else
462
- html_attributes[:name] = "#{node.form_name}[#{attribute}]"
463
495
  end
464
496
  html_attributes.delete(:value)
465
497
  select_tag = Zafu::Markup.new('select', html_attributes)
@@ -480,7 +512,7 @@ module Zena
480
512
  end
481
513
  end
482
514
  end
483
- select_tag.wrap "<%= options_for_select(#{options_list.inspect}, (#{node}.new_record? ? #{selected} : #{node}.klass)) %>"
515
+ select_tag.wrap "<%= options_for_select(#{options_list.inspect}, #{selected}) %>"
484
516
  elsif @params[:type] == 'time_zone'
485
517
  # <r:select name='d_tz' type='time_zone'/>
486
518
  select_tag.wrap "<%= options_for_select(TZInfo::Timezone.all_identifiers, #{selected}) %>"
@@ -802,6 +834,7 @@ module Zena
802
834
  show_values = translate_list(show)
803
835
  else
804
836
  tprefix = @params[:tprefix] || @params[:name] || @params[:param]
837
+ tprefix = tprefix.gsub('[','_').gsub(']','')
805
838
  if tprefix == 'false'
806
839
  tprefix = ''
807
840
  else
@@ -6,9 +6,9 @@ module Zena
6
6
 
7
7
  module FormTags
8
8
  #TODO: test
9
- # Return the list of groups from the visitor for forms
10
- def form_groups
11
- @form_groups ||= Group.find(:all, :select=>'id, name', :conditions=>"id IN (#{visitor.group_ids.join(',')})", :order=>"name ASC").collect {|p| [p.name, p.id]}
9
+ # Return the list of groups from the visitor for forms
10
+ def form_groups
11
+ @form_groups ||= Group.find(:all, :select=>'id, name', :conditions=>"id IN (#{visitor.group_ids.join(',')})", :order=>"name ASC").collect {|p| [p.name, p.id]}
12
12
  end
13
13
 
14
14
  #TODO: test
@@ -163,7 +163,7 @@ module Zena
163
163
  include LinkTags
164
164
  include RubyLess
165
165
 
166
- safe_method [:flash_messages] => String
166
+ safe_method [:flash_messages] => {:class => String, :method => 'flash_messages', :html_safe => true}
167
167
  # TODO: replace 'flash_messages' with a FlashHash context or a list
168
168
  # of Flash messages.
169
169
 
@@ -171,11 +171,11 @@ module Zena
171
171
  type = opts[:show] || 'both'
172
172
 
173
173
  if (type == 'notice' || type == 'both') && flash[:notice]
174
- notice = "<div class='auto_fade notice' onclick='new Effect.Fade(this)'>#{flash[:notice]}</div>"
174
+ notice = "<div class='auto_fade notice' onclick='new Effect.Fade(this)'>#{::ERB::Util.html_escape(flash[:notice])}</div>"
175
175
  end
176
176
 
177
177
  if (type == 'error' || type == 'both') && flash[:error ]
178
- error = "<div class='error' onclick='new Effect.Fade(this)'>#{flash[:error]}</div>"
178
+ error = "<div class='error' onclick='new Effect.Fade(this)'>#{::ERB::Util.html_escape(flash[:error])}</div>"
179
179
  end
180
180
 
181
181
  if page = opts[:page]
data/lib/zena/use/i18n.rb CHANGED
@@ -14,7 +14,7 @@ module Zena
14
14
  include RubyLess
15
15
 
16
16
  # never returns nil
17
- safe_method [:get, String] => {:class => String, :accept_nil => true}
17
+ safe_method [:get, String] => {:class => String, :accept_nil => true, :html_safe => true}
18
18
 
19
19
  def initialize(node_id, static = nil)
20
20
  @node_id = node_id
@@ -27,6 +27,7 @@ module Zena
27
27
  end
28
28
 
29
29
  def get_without_loading(key, use_global = true)
30
+ # SECURITY: We consider all strings in dictionaries as SAFE.
30
31
  @dict[key] || (use_global && ApplicationController.send(:_, key))
31
32
  end
32
33
 
@@ -205,7 +206,7 @@ module Zena
205
206
  module ViewMethods
206
207
  include RubyLess
207
208
 
208
- safe_method [:lang_links, {:wrap => String, :join => String}] => String
209
+ safe_method [:lang_links, {:wrap => String, :join => String}] => {:class => String, :method => 'lang_links', :html_safe => true}
209
210
 
210
211
  def self.included(base)
211
212
  base.send(:alias_method_chain, :will_paginate, :i18n) if base.respond_to?(:will_paginate)
@@ -349,13 +350,21 @@ module Zena
349
350
  { :class => String,
350
351
  :method => "#{dict}.get",
351
352
  :accept_nil => true,
352
- :pre_processor => Proc.new {|this, str| trans(str)}
353
+ :html_safe => true,
354
+ :pre_processor => Proc.new {|this, str|
355
+ t = erb_escape(trans(str))
356
+ RubyLess::TypedString.new(t.inspect, :html_safe => true, :literal => t, :class => String)
357
+ }
353
358
  }
354
359
  else
355
360
  { :class => String,
356
361
  :method => 'trans',
357
362
  :accept_nil => true,
358
- :pre_processor => Proc.new {|this, str| trans(str)}
363
+ :html_safe => true,
364
+ :pre_processor => Proc.new {|this, str|
365
+ t = erb_escape(trans(str))
366
+ RubyLess::TypedString.new(t.inspect, :html_safe => true, :literal => t, :class => String)
367
+ }
359
368
  }
360
369
  end
361
370
  end
@@ -1,5 +1,7 @@
1
1
  require 'tempfile'
2
2
  require 'digest/sha1'
3
+ # convert PDF to JPG
4
+ # convert -geometry 1600x1600 -density 200x200 -quality 90 test.pdf test.jpg
3
5
  unless defined?(Magick)
4
6
  begin
5
7
  # this works on the deb box
@@ -4,8 +4,12 @@ module Zena
4
4
  DATE_FIELD_REGEXP = %r{\d+[\.-/]\d+[\.-/]\d+}
5
5
  module ViewMethods
6
6
  include RubyLess
7
+ # Pseudo string comming from params.
7
8
  safe_method [:query_parse, String] => {:class => String, :accept_nil => true}
8
- safe_method :query_errors => {:class => String, :nil => true}
9
+ safe_method [:query_parse, Hash] => {:class => String, :accept_nil => true}
10
+ safe_method [:query_parse, String, Hash] => {:class => String, :accept_nil => true}
11
+
12
+ safe_method :query_errors => {:class => String, :nil => true, :html_safe => true}
9
13
 
10
14
  def find_node_by_zip(zip)
11
15
  return nil unless zip
@@ -17,7 +21,7 @@ module Zena
17
21
  end
18
22
 
19
23
  def query(class_name, node_name, pseudo_sql, opts = {})
20
- type = opts[:type] || :find
24
+ type = opts[:type] || (opts[:find] == :count ? :count : :find)
21
25
  @query_errors = nil
22
26
  if klass = VirtualClass[class_name]
23
27
  begin
@@ -28,13 +32,16 @@ module Zena
28
32
  # mixed in and strangely RubyLess cannot access the helpers from 'self'.
29
33
  :rubyless_helper => zafu_helper.helpers
30
34
  )
35
+
36
+ # Node.logger.warn eval(query.to_s(type == :count ? :count : :find), opts[:binding] || binding)
37
+
31
38
  if type == :count
32
39
  return klass.do_find(:count, eval(query.to_s(:count), opts[:binding] || binding))
33
40
  else
34
41
  return klass.do_find(opts[:find] || :all, eval(query.to_s, opts[:binding] || binding))
35
42
  end
36
43
  rescue ::QueryBuilder::Error => err
37
- @query_errors = "<span class='query'>#{pseudo_sql}</span> <span class='error'>#{err}</span>"
44
+ @query_errors = "<span class='query'>#{::ERB::Util.html_escape(pseudo_sql)}</span> <span class='error'>#{::ERB::Util.html_escape(err)}</span>"
38
45
  end
39
46
  end
40
47
  # error
@@ -43,13 +50,15 @@ module Zena
43
50
 
44
51
  # Takes a hash of parameters and builds query arguments for SQLiss
45
52
  # the query ends up like ' AND param_name = "foo" AND param_name > 35'...
46
- def query_parse(params)
53
+ def query_parse(params, opts = {})
54
+ ignore_params = (opts[:ignore] || '').split(',').map(&:strip)
47
55
  params ||= {}
48
56
  res = []
49
57
  if params.kind_of?(String)
50
58
  return params
51
59
  elsif params.kind_of?(Hash)
52
60
  params.each do |k,v|
61
+ next if ignore_params.include?(k.to_s)
53
62
  clause = query_build_clause(k,v)
54
63
  res << clause unless clause.blank?
55
64
  end
@@ -77,7 +86,6 @@ module Zena
77
86
  end
78
87
 
79
88
  if arg =~ DATE_FIELD_REGEXP
80
- # FIXME: Use to_utc and date function in Zena::Db
81
89
  arg = query_parse_dates(arg)
82
90
  first = arg.first
83
91
  end
@@ -107,8 +115,10 @@ module Zena
107
115
 
108
116
  def query_parse_dates(str)
109
117
  str.gsub(DATE_FIELD_REGEXP) do |date_str|
110
- if date = DateTime.parse(date_str) rescue nil
111
- date.strftime("'%Y-%m-%d'")
118
+ # Consider params as coming from the visitor's timezone.
119
+ if time = parse_date(date_str)
120
+ # We now have an UTC field.
121
+ time.strftime("'%Y-%m-%d %H:%M'")
112
122
  else
113
123
  date_str
114
124
  end
@@ -174,7 +184,6 @@ module Zena
174
184
  # Open a list context with a query comming from the url params. Default param name is
175
185
  # "qb"
176
186
  def r_query
177
- return parser_error("Cannot be used in list context (#{node.class_name})") if node.list_context?
178
187
  return parser_error("Missing 'default' query") unless default_psql = @params[:default]
179
188
 
180
189
  count = get_count(default_psql, {:find => @params[:find]})
@@ -184,9 +193,21 @@ module Zena
184
193
  rescue ::QueryBuilder::Error => err
185
194
  return parser_error(err.message)
186
195
  end
187
-
188
- klass = count == :all ? [default_query.main_class] : default_query.main_class
189
-
196
+
197
+ if count == :all
198
+ klass = [default_query.main_class]
199
+ type = :find
200
+ res_nil = true
201
+ elsif count == :first
202
+ klass = default_query.main_class
203
+ type = :find
204
+ res_nil = true
205
+ else
206
+ klass = Number
207
+ type = :count
208
+ res_nil = false
209
+ end
210
+
190
211
  can_be_nil = true
191
212
  if sql = @params[:eval]
192
213
  sql = RubyLess.translate(self, sql)
@@ -205,8 +226,12 @@ module Zena
205
226
  sql = "#{sql} || #{default_psql.inspect}"
206
227
  end
207
228
 
208
- query = DynamicQuery.new(default_query, node, sql, count)
209
- expand_with_finder(:method => query.to_s, :class => klass, :nil => true, :query => query)
229
+ query = DynamicQuery.new(default_query, single_node, sql, count)
230
+ if @blocks.empty? && type == :count
231
+ out "<%= #{query.to_s} %>"
232
+ else
233
+ expand_with_finder(:method => query.to_s, :class => klass, :nil => res_nil, :query => query)
234
+ end
210
235
  end
211
236
 
212
237
  # Pre-processing of the 'find("...")' method.
@@ -292,7 +317,7 @@ module Zena
292
317
  private
293
318
  def single_node
294
319
  node = self.node
295
- while node.list_context?
320
+ while node.list_context? || node.klass <= String || node.klass <= Number
296
321
  node = node.up
297
322
  if !node
298
323
  raise ::QueryBuilder::Error.new("Could not access node context query builder from list #{self.node.class_name}")
@@ -0,0 +1,57 @@
1
+ require 'querybuilder'
2
+
3
+ module Zena
4
+ module Use
5
+ class QueryLink
6
+
7
+ module ModelMethods
8
+ def self.included(base)
9
+ base.send(:include, ::QueryBuilder)
10
+ base.extend ClassMethods
11
+ base.query_compiler = Zena::Use::QueryLink::Compiler
12
+ end
13
+ end
14
+
15
+ module ClassMethods
16
+ # Find a node and propagate visitor
17
+ def do_find(count, query)
18
+ case count
19
+ when :all
20
+ res = find_by_sql(query)
21
+ res.empty? ? nil : res
22
+ when :first
23
+ find_by_sql(query).first
24
+ when :count
25
+ count_by_sql(query)
26
+ else
27
+ nil
28
+ end
29
+ end
30
+ end
31
+
32
+
33
+ class Compiler < ::QueryBuilder::Processor
34
+ attr_reader :node_name
35
+ set_main_table 'links'
36
+ set_main_class 'Link'
37
+ set_default :order, 'comment ASC'
38
+
39
+ # Overwrite this and take car to check for valid fields.
40
+ def process_field(field_name)
41
+ if %w{comment status date name}.include?(field_name)
42
+ field_name = 'comment' if field_name == 'name'
43
+ "#{table}.#{field_name}"
44
+ else
45
+ super # raise an error
46
+ end
47
+ end
48
+
49
+ def map_attr(fld)
50
+ # error
51
+ nil
52
+ end
53
+ end # Compiler
54
+
55
+ end # QueryLink
56
+ end # Use
57
+ end # Zena