zena 0.16.3 → 0.16.4
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.
- data/.gitignore +1 -0
- data/History.txt +11 -0
- data/app/controllers/nodes_controller.rb +1 -1
- data/app/controllers/versions_controller.rb +1 -1
- data/app/controllers/virtual_classes_controller.rb +29 -2
- data/app/models/cached_page.rb +3 -3
- data/app/models/document.rb +1 -20
- data/app/models/iformat.rb +4 -4
- data/app/models/image.rb +1 -1
- data/app/models/image_content.rb +5 -5
- data/app/models/node.rb +25 -0
- data/app/models/text_document_version.rb +11 -3
- data/app/models/virtual_class.rb +50 -0
- data/app/views/{nodes → sites}/404.html +0 -0
- data/app/views/templates/edit_tabs/_image.rhtml +1 -1
- data/app/views/versions/_list.rhtml +3 -1
- data/app/views/virtual_classes/_li.erb +2 -2
- data/app/views/virtual_classes/index.erb +9 -1
- data/bricks/tags/patch/node.rb +1 -1
- data/config/bricks.yml +1 -1
- data/config/gems.yml +4 -2
- data/db/migrate/047_change_default_link_id_to_zero.rb +1 -1
- data/db/migrate/20090825201159_insert_zero_link.rb +1 -1
- data/lib/bricks.rb +2 -2
- data/lib/bricks/requirements_validation.rb +13 -8
- data/lib/gettext_strings.rb +1 -0
- data/lib/tasks/zena.rake +15 -6
- data/lib/zena.rb +9 -3
- data/lib/{base_additions.rb → zena/base_additions.rb} +0 -0
- data/lib/{core_ext → zena/core_ext}/date_time.rb +0 -0
- data/lib/{core_ext → zena/core_ext}/dir.rb +0 -0
- data/lib/zena/core_ext/file_utils.rb +13 -0
- data/lib/{core_ext → zena/core_ext}/fixnum.rb +0 -0
- data/lib/{core_ext → zena/core_ext}/string.rb +0 -0
- data/lib/zena/deploy.rb +1 -1
- data/lib/zena/deploy/start.html +1 -1
- data/lib/zena/deploy/template.rb +5 -1
- data/lib/{fix_rails_layouts.rb → zena/fix_rails_layouts.rb} +0 -0
- data/lib/zena/info.rb +2 -2
- data/lib/zena/routes.rb +1 -1
- data/lib/zena/use/fixtures.rb +1 -1
- data/lib/zena/use/html_tags.rb +3 -3
- data/lib/zena/use/image_builder.rb +329 -0
- data/lib/zena/use/query_comment.rb +96 -0
- data/lib/zena/use/query_node.rb +408 -0
- data/lib/zena/use/query_node_finders.rb +91 -0
- data/lib/zena/use/rendering.rb +2 -2
- data/lib/{webdav_adapter.rb → zena/webdav_adapter.rb} +0 -0
- data/locale/de/LC_MESSAGES/zena.mo +0 -0
- data/locale/de/zena.po +1456 -0
- data/locale/en/LC_MESSAGES/zena.mo +0 -0
- data/locale/en/zena.po +626 -1197
- data/locale/fr/LC_MESSAGES/zena.mo +0 -0
- data/locale/fr/zena.po +895 -1221
- data/locale/zena.pot +527 -1027
- data/public/calendar/lang/calendar-de-utf8.js +128 -0
- data/public/calendar/menuarrow.gif +0 -0
- data/public/stylesheets/admin.css +2 -1
- data/test/custom_queries/complex.host.yml +1 -1
- data/test/fixtures/files/vclasses.yml +12 -0
- data/test/functional/virtual_classes_controller_test.rb +9 -0
- data/test/helpers/{node_query → query_node}/basic.yml +0 -0
- data/test/helpers/{node_query → query_node}/comments.yml +0 -0
- data/test/helpers/{node_query → query_node}/complex.yml +0 -0
- data/test/helpers/{node_query → query_node}/filters.yml +0 -0
- data/test/helpers/{node_query → query_node}/relations.yml +0 -0
- data/test/helpers/{node_query_test.rb → query_node_test.rb} +1 -2
- data/test/integration/navigation_test.rb +2 -0
- data/test/test_helper.rb +1 -1
- data/test/test_zena.rb +1 -1
- data/test/unit/core_ext_test.rb +3 -3
- data/test/unit/iformat_test.rb +3 -3
- data/test/unit/image_builder_test.rb +34 -34
- data/test/unit/node_test.rb +15 -1
- data/test/unit/virtual_class_test.rb +106 -0
- data/test/unit/zena/use/upload_test.rb +3 -3
- data/vendor/plugins/gettext_i18n_rails/tasks/gettext_rails_i18n.rake +3 -3
- data/zena.gemspec +36 -29
- data/zena_console.rb +33 -0
- metadata +30 -23
- data/lib/comment_query.rb +0 -92
- data/lib/image_builder.rb +0 -325
- data/lib/zena/use/node_query_finders.rb +0 -494
- data/vendor/bricks/20070122-172926.txt +0 -282
data/lib/comment_query.rb
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
require 'querybuilder'
|
|
2
|
-
|
|
3
|
-
class CommentQuery < QueryBuilder
|
|
4
|
-
attr_reader :uses_node_name, :node_name
|
|
5
|
-
set_main_table 'comments'
|
|
6
|
-
set_main_class 'Comment'
|
|
7
|
-
|
|
8
|
-
# Default sort order
|
|
9
|
-
def default_order_clause
|
|
10
|
-
"created_at ASC"
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def default_context_filter
|
|
14
|
-
# should never be called alone
|
|
15
|
-
raise Exception.new("CommentQuery should only be called from within NodeQuery")
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def parse_change_class(rel, is_last)
|
|
19
|
-
case rel
|
|
20
|
-
when 'author'
|
|
21
|
-
add_table('users')
|
|
22
|
-
@where << "#{table('users')}.id = #{field_or_attr('author_id')}"
|
|
23
|
-
# should we move on to Contact ?
|
|
24
|
-
when 'node', 'nodes'
|
|
25
|
-
add_table('discussions')
|
|
26
|
-
add_table('nodes')
|
|
27
|
-
@where << "#{table('discussions')}.id = #{table('comments')}.discussion_id"
|
|
28
|
-
@where << "#{table('nodes')}.id = #{table('discussions')}.node_id"
|
|
29
|
-
return NodeQuery # class change
|
|
30
|
-
else
|
|
31
|
-
return nil
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Same as NodeQuery... DRY needed.
|
|
36
|
-
def map_literal(value)
|
|
37
|
-
if value =~ /(.*?)\[(node|visitor|param):(\w+)\](.*)/
|
|
38
|
-
val_start = $1 == '' ? '' : "#{$1.inspect} +"
|
|
39
|
-
val_end = $4 == '' ? '' : "+ #{$4.inspect}"
|
|
40
|
-
case $2
|
|
41
|
-
when 'visitor'
|
|
42
|
-
if $3 == 'user_id'
|
|
43
|
-
value = "visitor.id"
|
|
44
|
-
else
|
|
45
|
-
value = "Node.zafu_attribute(visitor.contact, #{$3.inspect})"
|
|
46
|
-
end
|
|
47
|
-
when 'node'
|
|
48
|
-
@uses_node_name = true
|
|
49
|
-
if $3 == 'user_id'
|
|
50
|
-
value = "#{@node_name}.user_id"
|
|
51
|
-
else
|
|
52
|
-
value = "Node.zafu_attribute(#{@node_name}, #{$3.inspect})"
|
|
53
|
-
end
|
|
54
|
-
when 'param'
|
|
55
|
-
return "\#{Node.connection.quote(#{val_start}params[:#{$3}].to_s#{val_end})}"
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
if !val_start.blank? || !val_end.blank?
|
|
59
|
-
"\#{Node.connection.quote(#{val_start}#{value}#{val_end})}"
|
|
60
|
-
else
|
|
61
|
-
"\#{#{value}}"
|
|
62
|
-
end
|
|
63
|
-
else
|
|
64
|
-
value = Node.connection.quote(value)
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Overwrite this and take car to check for valid fields.
|
|
69
|
-
def map_field(fld, table_name, context = nil)
|
|
70
|
-
if ['status', 'updated_at', 'author_name', 'created_at', 'title', 'text', 'author_id'].include?(fld)
|
|
71
|
-
"#{table_name}.#{fld}"
|
|
72
|
-
else
|
|
73
|
-
# TODO: error, raise / ignore ?
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def map_attr(fld)
|
|
78
|
-
# error
|
|
79
|
-
nil
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# Erb finder used by zafu
|
|
83
|
-
def finder(count)
|
|
84
|
-
return 'nil' unless valid?
|
|
85
|
-
case count
|
|
86
|
-
when :count
|
|
87
|
-
"#{node_name}.do_find(:count, #{to_s(:count)}, #{!uses_node_name}, #{main_class})"
|
|
88
|
-
else
|
|
89
|
-
"#{node_name}.do_find(#{count.inspect}, #{self.to_s}, #{!uses_node_name}, #{main_class})"
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
data/lib/image_builder.rb
DELETED
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
require 'tempfile'
|
|
2
|
-
require 'digest/sha1'
|
|
3
|
-
unless defined?(Magick)
|
|
4
|
-
begin
|
|
5
|
-
# this works on the deb box
|
|
6
|
-
require 'RMagick'
|
|
7
|
-
rescue LoadError
|
|
8
|
-
begin
|
|
9
|
-
# this works on my Mac
|
|
10
|
-
require 'rmagick'
|
|
11
|
-
rescue LoadError
|
|
12
|
-
puts "ImageMagick not found. Using dummy."
|
|
13
|
-
require 'ftools'
|
|
14
|
-
# Create a dummy magick module
|
|
15
|
-
module Magick
|
|
16
|
-
CenterGravity = OverCompositeOp = MaxRGB = NorthGravity = SouthGravity = nil
|
|
17
|
-
class << self
|
|
18
|
-
end
|
|
19
|
-
class ZenaDummy
|
|
20
|
-
def initialize(*a)
|
|
21
|
-
end
|
|
22
|
-
def method_missing(meth, *args)
|
|
23
|
-
# do nothing
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
class Image < ZenaDummy
|
|
27
|
-
end
|
|
28
|
-
class ImageList < ZenaDummy
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
class ImageBuilder
|
|
36
|
-
DEFAULT_FORMATS = {
|
|
37
|
-
'tiny' => { :name=>'tiny', :size=>:force, :width=>16, :height=>16 , :gravity=>Magick::CenterGravity },
|
|
38
|
-
'mini' => { :name=>'mini', :size=>:force, :width=>32, :height=>32 , :gravity=>Magick::CenterGravity },
|
|
39
|
-
'square' => { :name=>'square', :size=>:limit, :width=>180, :height=>180, :gravity=>Magick::CenterGravity },
|
|
40
|
-
'med' => { :name=>'med', :size=>:limit, :width=>280, :height=>186, :gravity=>Magick::CenterGravity },
|
|
41
|
-
'top' => { :name=>'top', :size=>:force, :width=>280, :height=>186, :gravity => Magick::NorthGravity },
|
|
42
|
-
'low' => { :name=>'low', :size=>:force, :width=>280, :height=>186, :gravity => Magick::SouthGravity },
|
|
43
|
-
'side' => { :name=>'side', :size=>:force, :width=>220, :height=>500, :gravity=>Magick::CenterGravity },
|
|
44
|
-
'std' => { :name=>'std', :size=>:limit, :width=>600, :height=>400, :gravity=>Magick::CenterGravity },
|
|
45
|
-
'pv' => { :name=>'pv', :size=>:force, :width=>70, :height=>70 , :gravity=>Magick::CenterGravity },
|
|
46
|
-
'edit' => { :name=>'edit', :size=>:limit, :width=>400, :height=>400, :gravity=>Magick::CenterGravity },
|
|
47
|
-
'full' => { :name=>'full', :size=>:keep , :gravity=>Magick::CenterGravity },
|
|
48
|
-
nil => { :name=>'full', :size=>:keep , :gravity=>Magick::CenterGravity },
|
|
49
|
-
}.freeze
|
|
50
|
-
# 'sepia'=> { :size=>:limit, :width=>280, :ratio=>2/3.0, :post=>Proc.new {|img| img.sepiatone(Magick::MaxRGB * 0.8)}},
|
|
51
|
-
|
|
52
|
-
class << self
|
|
53
|
-
def image_content_type?(content_type)
|
|
54
|
-
content_type =~ /image/
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def dummy?
|
|
58
|
-
Magick.const_defined?(:ZenaDummy)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def hash_id(format)
|
|
62
|
-
Digest::SHA1.hexdigest("#{format[:name]}#{format[:size]}#{format[:width]}#{format[:height]}#{format[:gravity]}")[0..9].to_i(16)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
DEFAULT_FORMATS.each do |k, v|
|
|
66
|
-
v[:hash_id] = ImageBuilder.hash_id(v)
|
|
67
|
-
v.freeze
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def initialize(h)
|
|
72
|
-
params = {:height=>nil, :width=>nil, :path=>nil, :file=>nil, :actions=>[]}.merge(h)
|
|
73
|
-
|
|
74
|
-
params.each do |k,v|
|
|
75
|
-
case k
|
|
76
|
-
when :height
|
|
77
|
-
@height = v if v
|
|
78
|
-
when :width
|
|
79
|
-
@width = v if v
|
|
80
|
-
when :path
|
|
81
|
-
@path = v if v
|
|
82
|
-
when :file
|
|
83
|
-
@file = v if v
|
|
84
|
-
when :actions
|
|
85
|
-
if v.kind_of?(Array)
|
|
86
|
-
@actions = v
|
|
87
|
-
else
|
|
88
|
-
raise StandardError, "Bad actions format"
|
|
89
|
-
end
|
|
90
|
-
else
|
|
91
|
-
raise StandardError, "Bad parameter (#{k})"
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
unless @width && @height || dummy?
|
|
95
|
-
if @img = build_image_from_file_or_path
|
|
96
|
-
@width = @img.columns
|
|
97
|
-
@height = @img.rows
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def dummy?
|
|
103
|
-
ImageBuilder.dummy? || (!@path && !@img && !@file)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def read
|
|
107
|
-
return nil if dummy?
|
|
108
|
-
render_img
|
|
109
|
-
@img.to_blob
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def write(path)
|
|
113
|
-
return false if dummy?
|
|
114
|
-
render_img
|
|
115
|
-
@img.write(path)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def rows
|
|
119
|
-
return nil unless @height || !dummy?
|
|
120
|
-
(@height ||= render_img.rows).round
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def columns
|
|
124
|
-
return nil unless @width || !dummy?
|
|
125
|
-
(@width ||= render_img.columns).round
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
alias height rows
|
|
129
|
-
alias width columns
|
|
130
|
-
|
|
131
|
-
def resize!(s)
|
|
132
|
-
# we do not zoom pixels
|
|
133
|
-
return unless s < 1.0
|
|
134
|
-
@img = nil # reset current rendered image
|
|
135
|
-
@width *= s
|
|
136
|
-
@height *= s
|
|
137
|
-
@actions << Proc.new {|img| img.resize!(s) }
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def crop!(x,y,w,h)
|
|
141
|
-
@img = nil # reset current rendered image
|
|
142
|
-
@width = [@width -x, w].min
|
|
143
|
-
@height = [@height-y, h].min
|
|
144
|
-
@actions << Proc.new {|img| img.crop!(x,y,[@img.columns-x, w].min,[@img.rows-y, h].min, true) }
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def format=(fmt)
|
|
148
|
-
return unless !dummy? && Magick.formats[fmt.upcase] =~ /w/
|
|
149
|
-
@actions << Proc.new {|img| img.format = fmt.upcase; img }
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def format
|
|
153
|
-
render_img.format
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def exif
|
|
157
|
-
@exif ||= ExifData.new(render_img.get_exif_by_entry)
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
def max_filesize=(size)
|
|
161
|
-
@actions << Proc.new {|img| do_limit!(size) }
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def do_limit!(size)
|
|
165
|
-
return @img if @filesize <= size
|
|
166
|
-
|
|
167
|
-
# Check real size
|
|
168
|
-
tmp_path = Tempfile.new('tmp_img').path
|
|
169
|
-
@img.write('jpeg:' + tmp_path)
|
|
170
|
-
|
|
171
|
-
return @img if File.stat(tmp_path).size <= size
|
|
172
|
-
|
|
173
|
-
# Change type to JPG and quality to 80
|
|
174
|
-
if (@img.format == 'JPG' || @img.format == 'JPEG') && @img.quality > 80
|
|
175
|
-
@img.write('jpeg:' + tmp_path) { self.quality = 80 }
|
|
176
|
-
else
|
|
177
|
-
@img.format = 'JPG'
|
|
178
|
-
@img.write('jpeg:' + tmp_path) { self.quality = 80 }
|
|
179
|
-
end
|
|
180
|
-
ratio = File.stat(tmp_path).size.to_f / size
|
|
181
|
-
|
|
182
|
-
return @img = Magick::ImageList.new(tmp_path) if ratio <= 1.0
|
|
183
|
-
|
|
184
|
-
# Not enough ? Resize.
|
|
185
|
-
ratio = 1.0 / Math.sqrt(ratio)
|
|
186
|
-
@width *= ratio
|
|
187
|
-
@height *= ratio
|
|
188
|
-
@img.resize!(ratio)
|
|
189
|
-
@img
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def crop_min!(w,h,gravity=Magick::CenterGravity)
|
|
193
|
-
@img = nil # reset current rendered image
|
|
194
|
-
@width = [@width ,w].min
|
|
195
|
-
@height = [@height,h].min
|
|
196
|
-
@actions << Proc.new {|img| img.crop!(gravity,[@img.columns,w].min,[@img.rows,h].min, true) }
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
def set_background!(opacity,w,h)
|
|
200
|
-
@img = nil # reset current rendered image
|
|
201
|
-
@width = [@width ,w].max
|
|
202
|
-
@height = [@height,h].max
|
|
203
|
-
@actions << Proc.new do |img|
|
|
204
|
-
bg = Magick::Image.new(w,h)
|
|
205
|
-
bg.opacity = opacity
|
|
206
|
-
bg.format = img.format
|
|
207
|
-
img = bg.composite(img, Magick::CenterGravity, Magick::OverCompositeOp)
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
# Transform into another format. If nil : do nothing.
|
|
212
|
-
def transform!(tformat=nil)
|
|
213
|
-
return self unless tformat
|
|
214
|
-
@img = nil
|
|
215
|
-
format = { :size=>:limit, :gravity=>Magick::CenterGravity }.merge(tformat)
|
|
216
|
-
@pre, @post = format[:pre], format[:post]
|
|
217
|
-
|
|
218
|
-
if format[:size] == :keep
|
|
219
|
-
h,w = @height, @width
|
|
220
|
-
else
|
|
221
|
-
h,w = format[:height], format[:width]
|
|
222
|
-
end
|
|
223
|
-
if format[:scale]
|
|
224
|
-
if h || w
|
|
225
|
-
# scale is a pre-zoom before crop
|
|
226
|
-
scale = format[:scale]
|
|
227
|
-
else
|
|
228
|
-
# we resize to scale
|
|
229
|
-
h,w = @height*format[:scale], @width*format[:scale]
|
|
230
|
-
# but we do not zoom
|
|
231
|
-
scale = 1.0
|
|
232
|
-
# ignore ':size' format if not height nor width was given
|
|
233
|
-
format[:size] = :force
|
|
234
|
-
end
|
|
235
|
-
else
|
|
236
|
-
scale = 1.0
|
|
237
|
-
end
|
|
238
|
-
if format[:ratio] && h && !w
|
|
239
|
-
w = h / format[:ratio]
|
|
240
|
-
elsif format[:ratio] && w && !h
|
|
241
|
-
h = w * format[:ratio]
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
pw,ph = @width, @height
|
|
245
|
-
if [w,h,pw,ph].include?(nil) || [w,h,pw,ph].min <= 0
|
|
246
|
-
# image size or thumb size is null (no image processing tool used, no idea on image size)
|
|
247
|
-
if format[:size] == :keep
|
|
248
|
-
@height, @width = nil, nil
|
|
249
|
-
else
|
|
250
|
-
@height, @width = h, w
|
|
251
|
-
end
|
|
252
|
-
return self
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
case format[:size]
|
|
256
|
-
when :force
|
|
257
|
-
crop_scale = [w.to_f/pw, h.to_f/ph].max
|
|
258
|
-
if crop_scale > 1.0
|
|
259
|
-
# we do not zoom. Fill with transparent background.
|
|
260
|
-
crop_min!(w,h,format[:gravity])
|
|
261
|
-
set_background!(Magick::MaxRGB, w, h)
|
|
262
|
-
else
|
|
263
|
-
resize!(crop_scale * scale)
|
|
264
|
-
crop_min!(w, h,format[:gravity])
|
|
265
|
-
end
|
|
266
|
-
when :force_no_crop
|
|
267
|
-
crop_scale = [w.to_f/pw, h.to_f/ph].min
|
|
268
|
-
resize!(crop_scale * scale)
|
|
269
|
-
crop_min!(w, h,format[:gravity])
|
|
270
|
-
set_background!(Magick::MaxRGB, w, h)
|
|
271
|
-
when :limit
|
|
272
|
-
crop_scale = [w.to_f/pw, h.to_f/ph].min
|
|
273
|
-
resize!(crop_scale * scale)
|
|
274
|
-
crop_min!(w, h,format[:gravity])
|
|
275
|
-
when :keep
|
|
276
|
-
end
|
|
277
|
-
self
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
def render_img
|
|
281
|
-
raise IOError, 'MagickDummy cannot render image' if ImageBuilder.dummy?
|
|
282
|
-
unless @img
|
|
283
|
-
unless @img = build_image_from_file_or_path
|
|
284
|
-
raise IOError, 'Cannot render image without path or file'
|
|
285
|
-
end
|
|
286
|
-
if @pre
|
|
287
|
-
@pre = [@pre].flatten
|
|
288
|
-
@pre.each do |a|
|
|
289
|
-
@img = a.call(@img)
|
|
290
|
-
end
|
|
291
|
-
end
|
|
292
|
-
|
|
293
|
-
if @actions
|
|
294
|
-
@actions.each do |a|
|
|
295
|
-
@img = a.call(@img)
|
|
296
|
-
end
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
if @post
|
|
300
|
-
@post = [@post].flatten
|
|
301
|
-
@post.each do |a|
|
|
302
|
-
@img = a.call(@img)
|
|
303
|
-
end
|
|
304
|
-
end
|
|
305
|
-
end
|
|
306
|
-
@img
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
def build_image_from_file_or_path
|
|
310
|
-
if @file || @path
|
|
311
|
-
if @file.kind_of?(StringIO)
|
|
312
|
-
img = Magick::ImageList.new
|
|
313
|
-
@file.rewind
|
|
314
|
-
img.from_blob(@file.read)
|
|
315
|
-
@file.rewind
|
|
316
|
-
@filesize = @file.size
|
|
317
|
-
img
|
|
318
|
-
else
|
|
319
|
-
img = Magick::ImageList.new(@file ? @file.path : @path)
|
|
320
|
-
@filesize = img.filesize
|
|
321
|
-
img
|
|
322
|
-
end
|
|
323
|
-
end
|
|
324
|
-
end
|
|
325
|
-
end
|
|
@@ -1,494 +0,0 @@
|
|
|
1
|
-
require 'query_builder'
|
|
2
|
-
# Since NodeQuery uses a special scope (secure_scope) that needs to be evaluated _in the query string_, you cannot
|
|
3
|
-
# use query.sql. You have to use: eval query.to_s. This is not an issue since normal useage for NodeQuery is to be
|
|
4
|
-
# compilated into an ERB template.
|
|
5
|
-
class NodeQuery < QueryBuilder
|
|
6
|
-
attr_reader :context, :uses_node_name, :node_name
|
|
7
|
-
set_main_table 'nodes'
|
|
8
|
-
set_main_class 'Node'
|
|
9
|
-
@@filter_fields = {'id' => {:key => 'zip'}}
|
|
10
|
-
|
|
11
|
-
load_custom_queries ["#{RAILS_ROOT}/bricks/*/queries"]
|
|
12
|
-
|
|
13
|
-
def self.insert_zero_link(link_class)
|
|
14
|
-
return if link_class.find_by_id(0)
|
|
15
|
-
link_class.connection.execute "INSERT INTO #{link_class.table_name} (id,target_id,source_id,status,comment) VALUES (0,0,0,NULL,NULL)"
|
|
16
|
-
unless link_class.find_by_id(0)
|
|
17
|
-
# the zero id is replaced by auto-increment value
|
|
18
|
-
last_id = link_class.find(:first, :order => 'id DESC').id
|
|
19
|
-
link_class.connection.execute "UPDATE #{link_class.table_name} SET id = 0 WHERE id = #{last_id}"
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def self.add_filter_field(key, fld_def)
|
|
24
|
-
@@filter_fields[key] = fld_def
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def initialize(query, opts = {})
|
|
28
|
-
@uses_node_name = false
|
|
29
|
-
@table_name = 'nodes'
|
|
30
|
-
@node_name = opts[:node_name]
|
|
31
|
-
# list of dyna_attributes keys allready in the filter
|
|
32
|
-
@dyn_keys = {}
|
|
33
|
-
super(query, opts)
|
|
34
|
-
# Raw filters are statements prepared that should not be further processed except for table_name replacement.
|
|
35
|
-
parse_raw_filters(opts[:raw_filters])
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Build joins and filters from a relation.
|
|
39
|
-
def parse_relation(rel, context)
|
|
40
|
-
# join_relation first so we can overwrite 'class' finders (images) with a relation.
|
|
41
|
-
unless join_relation(rel, context) || context_relation(rel, context)
|
|
42
|
-
@errors << "unknown relation '#{rel}'"
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Default sort order
|
|
47
|
-
def default_order_clause
|
|
48
|
-
"position ASC, name ASC"
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def default_context_filter
|
|
52
|
-
'self'
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def after_parse
|
|
56
|
-
@where.unshift "(\#{#{@node_name}.secure_scope('#{table}')})"
|
|
57
|
-
if @tables.include?('links')
|
|
58
|
-
link_table = table('links')
|
|
59
|
-
@select << "#{link_table}.id AS link_id,#{Zena::Use::Relations::LINK_ATTRIBUTES.map {|l| "#{link_table}.#{l} AS l_#{l}"}.join(',')}"
|
|
60
|
-
elsif @errors_unless_safe_links
|
|
61
|
-
@errors += @errors_unless_safe_links
|
|
62
|
-
end
|
|
63
|
-
@distinct = true if @tables.include?('versions')
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Erb finder used by zafu
|
|
67
|
-
def finder(count)
|
|
68
|
-
return 'nil' unless valid?
|
|
69
|
-
case count
|
|
70
|
-
when :count
|
|
71
|
-
"#{node_name}.do_find(:count, #{self.to_s(:count)}, #{!uses_node_name}, #{main_class})"
|
|
72
|
-
else
|
|
73
|
-
"#{node_name}.do_find(#{count.inspect}, #{self.to_s}, #{!uses_node_name}, #{main_class})"
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
private
|
|
78
|
-
# Make sure all alternate queries include "links.id = 0" (dummy link)
|
|
79
|
-
def fix_where_list(where_list)
|
|
80
|
-
return unless @tables.include?('links')
|
|
81
|
-
where_list.each do |f|
|
|
82
|
-
unless f =~ /links\./
|
|
83
|
-
f << " AND links.id = 0"
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
true
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Used to resolve 'in' clauses ('in project', 'in parent', etc)
|
|
90
|
-
def context_filter_fields(rel, is_last = false)
|
|
91
|
-
case rel
|
|
92
|
-
when 'self'
|
|
93
|
-
['parent_id', 'id']
|
|
94
|
-
when 'parent'
|
|
95
|
-
is_last ? ['parent_id', 'parent_id'] : ['parent_id', 'id']
|
|
96
|
-
when 'project'
|
|
97
|
-
is_last ? ['project_id', 'project_id'] : ['project_id', 'id']
|
|
98
|
-
when 'section'
|
|
99
|
-
is_last ? ['section_id', 'section_id'] : ['section_id', 'id']
|
|
100
|
-
when 'site', main_table
|
|
101
|
-
:void
|
|
102
|
-
else
|
|
103
|
-
nil
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# Relations that can be resolved without a join
|
|
108
|
-
def context_relation(rel, context)
|
|
109
|
-
case rel
|
|
110
|
-
when 'self'
|
|
111
|
-
fields = ['id', 'id']
|
|
112
|
-
when 'parent', 'project', 'section'
|
|
113
|
-
fields = ['id', "#{rel}_id"]
|
|
114
|
-
when 'parents', 'projects', 'sections'
|
|
115
|
-
if @table_counter[main_table] > 0 || @tables.include?('links')
|
|
116
|
-
fields = ['id', "#{rel[0..-2]}_id"]
|
|
117
|
-
end
|
|
118
|
-
when 'root'
|
|
119
|
-
@where << "#{table}.id = #{current_site.root_id}"
|
|
120
|
-
return true
|
|
121
|
-
when 'author', 'traductions', 'versions'
|
|
122
|
-
# TODO: not implemented yet...
|
|
123
|
-
return nil
|
|
124
|
-
when 'visitor'
|
|
125
|
-
@where << "#{table}.id = #{insert_bind("visitor.contact_id")}"
|
|
126
|
-
return true
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
unless fields
|
|
130
|
-
if klass = Node.get_class(rel)
|
|
131
|
-
parse_context(default_context_filter) unless context
|
|
132
|
-
@where << "#{table}.kpath LIKE '#{klass.kpath}%'"
|
|
133
|
-
return true
|
|
134
|
-
else
|
|
135
|
-
# unknown class
|
|
136
|
-
return nil
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
@where << "#{field_or_attr(fields[0])} = #{field_or_attr(fields[1], table(main_table,-1))}"
|
|
141
|
-
true
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def parse_change_class(rel, is_last)
|
|
145
|
-
case rel
|
|
146
|
-
when 'comment', 'comments'
|
|
147
|
-
if is_last
|
|
148
|
-
# no need to load discussions, versions and all the mess
|
|
149
|
-
add_table('comments')
|
|
150
|
-
@where << "#{table('comments')}.discussion_id = #{map_attr('discussion_id')}"
|
|
151
|
-
return CommentQuery # class change
|
|
152
|
-
else
|
|
153
|
-
# parse_context(default_context_filter, true) if is_last
|
|
154
|
-
# after_parse
|
|
155
|
-
add_table('discussions')
|
|
156
|
-
add_table('comments')
|
|
157
|
-
@where << "#{table('discussions')}.node_id = #{table('nodes')}.id"
|
|
158
|
-
@where << "#{table('comments')}.discussion_id = #{table('discussions')}.id"
|
|
159
|
-
after_parse
|
|
160
|
-
return CommentQuery
|
|
161
|
-
end
|
|
162
|
-
else
|
|
163
|
-
return nil
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
# Filters that need a join
|
|
168
|
-
def join_relation(rel, context, new_table_alias = nil, previous_table_alias = nil)
|
|
169
|
-
new_table_alias ||= table(main_table)
|
|
170
|
-
previous_table_alias ||= table(main_table, -1)
|
|
171
|
-
|
|
172
|
-
if rel == main_table || rel == 'children'
|
|
173
|
-
# dummy clauses
|
|
174
|
-
parse_context(default_context_filter) unless context
|
|
175
|
-
return :void
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
if rel = RelationProxy.find_by_role(rel.singularize)
|
|
179
|
-
# We cannot use a LEFT JOIN here because it will totally mess up if we merge alternate queries
|
|
180
|
-
add_table('links')
|
|
181
|
-
# (= other_side = result) target <-- source (= link_side = caller)
|
|
182
|
-
if context && context != 'self'
|
|
183
|
-
# tagged in project (not equal to 'tagged from nodes in project')
|
|
184
|
-
# remove caller join
|
|
185
|
-
@distinct = true
|
|
186
|
-
@where << "#{field_or_attr('id', new_table_alias)} = #{table('links')}.#{rel.other_side} AND #{table('links')}.relation_id = #{rel[:id]}"
|
|
187
|
-
else
|
|
188
|
-
@where << "#{field_or_attr('id', new_table_alias)} = #{table('links')}.#{rel.other_side} AND #{table('links')}.relation_id = #{rel[:id]} AND #{table('links')}.#{rel.link_side} = #{field_or_attr('id', previous_table_alias)}"
|
|
189
|
-
end
|
|
190
|
-
else
|
|
191
|
-
nil
|
|
192
|
-
end
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
def map_literal(value, env = :sql)
|
|
196
|
-
if value =~ /(.*?)\[(visitor|param|node):(\w+)\](.*)/
|
|
197
|
-
val_start = $1 == '' ? '' : "#{$1.inspect} +"
|
|
198
|
-
val_end = $4 == '' ? '' : "+ #{$4.inspect}"
|
|
199
|
-
case $2
|
|
200
|
-
when 'visitor'
|
|
201
|
-
value = env == :sql ? insert_bind("#{val_start}Node.zafu_attribute(visitor.contact, #{$3.inspect})#{val_end}") : nil
|
|
202
|
-
when 'param'
|
|
203
|
-
value = env == :sql ? insert_bind("#{val_start}params[:#{$3}].to_s#{val_end}") : "params[:#{$3}]"
|
|
204
|
-
when 'node'
|
|
205
|
-
@uses_node_name = true
|
|
206
|
-
value = env == :sql ? insert_bind("#{val_start}#{@node_name}.safe_read(#{$3.inspect}).to_s#{val_end}") : "#{@node_name}.safe_read(#{$3.inspect})"
|
|
207
|
-
end
|
|
208
|
-
else
|
|
209
|
-
value = env == :sql ? quote(value) : nil
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
# Translate fields used for query/sort/grouping (context parameter) into something useable by SQL. Add the appropriate tables when needed.
|
|
214
|
-
def map_field(field, table_name = table, context = nil)
|
|
215
|
-
return map_literal("[#{field}]") if field =~ /\Aparam:/
|
|
216
|
-
case field[0..1]
|
|
217
|
-
when 'd_'
|
|
218
|
-
# DYNAMIC ATTRIBUTE
|
|
219
|
-
key = field[2..-1]
|
|
220
|
-
key, function = parse_sql_function_in_field(key)
|
|
221
|
-
key = Zena::Db.sql_function(function, dyn_value('versions', key, context))
|
|
222
|
-
when 'c_'
|
|
223
|
-
# CONTENT TABLE
|
|
224
|
-
field = field[2..-1]
|
|
225
|
-
# FIXME: implement #41
|
|
226
|
-
nil
|
|
227
|
-
when 'v_'
|
|
228
|
-
# VERSION
|
|
229
|
-
key = field[2..-1]
|
|
230
|
-
key, function = parse_sql_function_in_field(key)
|
|
231
|
-
if Version.safe_method_type([key]) && Version.column_names.include?(key)
|
|
232
|
-
vtable_name = needs_table('nodes', 'versions', "TABLE1.id = TABLE2.node_id")
|
|
233
|
-
Zena::Db.sql_function(function, "#{vtable_name}.#{key}")
|
|
234
|
-
else
|
|
235
|
-
# bad version attribute
|
|
236
|
-
nil
|
|
237
|
-
end
|
|
238
|
-
when 'l_'
|
|
239
|
-
key, function = parse_sql_function_in_field(field)
|
|
240
|
-
if key == 'l_status' || key == 'l_comment' || key == 'l_date' || (key == 'l_id' && [:order, :group].include?(context))
|
|
241
|
-
@errors_unless_safe_links ||= []
|
|
242
|
-
@errors_unless_safe_links << "cannot use link field '#{key}' in this query" unless (key == 'l_id' && context == :order)
|
|
243
|
-
# ok
|
|
244
|
-
Zena::Db.sql_function(function, "#{table('links')}.#{key[2..-1]}")
|
|
245
|
-
else
|
|
246
|
-
# bad attribute
|
|
247
|
-
nil
|
|
248
|
-
end
|
|
249
|
-
else
|
|
250
|
-
# NODE
|
|
251
|
-
key, function = parse_sql_function_in_field(field)
|
|
252
|
-
if context == :filter
|
|
253
|
-
if map_def = @@filter_fields[key]
|
|
254
|
-
if table_def = map_def[:table]
|
|
255
|
-
table_to_use = needs_table(*table_def)
|
|
256
|
-
else
|
|
257
|
-
table_to_use = table_name
|
|
258
|
-
end
|
|
259
|
-
Zena::Db.sql_function(function, "#{table_to_use}.#{map_def[:key]}")
|
|
260
|
-
elsif (Node.safe_method_type([key]) && Node.column_names.include?(key))
|
|
261
|
-
Zena::Db.sql_function(function, "#{table_name}.#{key}")
|
|
262
|
-
elsif key =~ /^(.*)_ids?$/
|
|
263
|
-
# tag_id = 33 ===> join links as lk, nodes as tt .......
|
|
264
|
-
rel = $1
|
|
265
|
-
|
|
266
|
-
if RelationProxy.find_by_role(rel.singularize)
|
|
267
|
-
add_table('jnode', 'nodes')
|
|
268
|
-
join_relation(rel, nil, table('jnode'), table('nodes'))
|
|
269
|
-
"#{table('jnode')}.zip"
|
|
270
|
-
else
|
|
271
|
-
nil
|
|
272
|
-
end
|
|
273
|
-
else
|
|
274
|
-
nil
|
|
275
|
-
end
|
|
276
|
-
else
|
|
277
|
-
if ['id', 'parent_id','project_id','section_id'].include?(key) || (Node.safe_method_type([key]) && Node.column_names.include?(key))
|
|
278
|
-
Zena::Db.sql_function(function, "#{table_name}.#{key}")
|
|
279
|
-
else
|
|
280
|
-
# bad attribute
|
|
281
|
-
nil
|
|
282
|
-
end
|
|
283
|
-
end
|
|
284
|
-
end
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
def valid_field?(table_name, fld)
|
|
288
|
-
# FIXME: security !
|
|
289
|
-
true
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
def map_attr(fld, env = :sql)
|
|
293
|
-
case fld
|
|
294
|
-
when 'project_id', 'section_id', 'discussion_id'
|
|
295
|
-
@uses_node_name = true
|
|
296
|
-
insert_bind("#{@node_name}.get_#{fld}")
|
|
297
|
-
when 'id', 'parent_id'
|
|
298
|
-
@uses_node_name = true
|
|
299
|
-
insert_bind("#{@node_name}.#{fld}")
|
|
300
|
-
else
|
|
301
|
-
# type = Node.safe_method_type([fld])
|
|
302
|
-
# type[:method] ?
|
|
303
|
-
|
|
304
|
-
# bad parameter
|
|
305
|
-
@errors << "invalid parameter '#{fld}'"
|
|
306
|
-
"0"
|
|
307
|
-
end
|
|
308
|
-
end
|
|
309
|
-
|
|
310
|
-
def parse_paginate_clause(paginate)
|
|
311
|
-
return @offset unless paginate
|
|
312
|
-
if !@limit
|
|
313
|
-
# TODO: raise error ?
|
|
314
|
-
@errors << "invalid paginate clause '#{paginate}' (used without limit)"
|
|
315
|
-
nil
|
|
316
|
-
elsif (fld = map_literal("[param:#{paginate}]", :ruby)) && (page_size = @limit[/ LIMIT (\d+)/,1])
|
|
317
|
-
@page_size = [2,page_size.to_i].max
|
|
318
|
-
" OFFSET #{insert_bind("((#{fld}.to_i > 0 ? #{fld}.to_i : 1)-1)*#{page_size.to_i}")}"
|
|
319
|
-
else
|
|
320
|
-
@errors << "invalid paginate clause '#{paginate}'"
|
|
321
|
-
nil
|
|
322
|
-
end
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
# When a field is defined as log_at:year, return [log_at, year].
|
|
326
|
-
def parse_sql_function_in_field(field)
|
|
327
|
-
if field =~ /\A(\w+):(\w+)\Z/
|
|
328
|
-
if ['year'].include?($2)
|
|
329
|
-
[$1,$2]
|
|
330
|
-
else
|
|
331
|
-
[$1]
|
|
332
|
-
end
|
|
333
|
-
else
|
|
334
|
-
[field]
|
|
335
|
-
end
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
def parse_raw_filters(filters)
|
|
339
|
-
return unless filters
|
|
340
|
-
filters.each do |f|
|
|
341
|
-
all_ok = true
|
|
342
|
-
filter = f.gsub(/TABLE_NAME(\[(\w+)\]|)/) do
|
|
343
|
-
if $2
|
|
344
|
-
if @table_counter[$2]
|
|
345
|
-
table($2)
|
|
346
|
-
else
|
|
347
|
-
all_ok = false
|
|
348
|
-
@errors << "invalid table_name '#{$2}' in raw filter"
|
|
349
|
-
@errors.uniq!
|
|
350
|
-
end
|
|
351
|
-
else
|
|
352
|
-
table
|
|
353
|
-
end
|
|
354
|
-
end
|
|
355
|
-
@where << filter if all_ok
|
|
356
|
-
end
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
def dyn_value(table_name, key, context)
|
|
360
|
-
@dyn_keys[table_name] ||= {}
|
|
361
|
-
@dyn_keys[table_name][key] ||= begin
|
|
362
|
-
needs_table('nodes', 'versions', "TABLE1.id = TABLE2.node_id")
|
|
363
|
-
dtable = needs_join_table('versions', 'LEFT', 'dyn_attributes', "TABLE1.id = TABLE2.owner_id AND TABLE2.key = #{quote(key)}", "versions=dyn_attributes=#{key}")
|
|
364
|
-
"#{dtable}.value"
|
|
365
|
-
end
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
def parse_custom_query_argument(key, value)
|
|
369
|
-
return nil unless value
|
|
370
|
-
super.gsub(/(RELATION_ID|NODE_ATTR)\(([^)]+)\)/) do
|
|
371
|
-
type, value = $1, $2
|
|
372
|
-
if type == 'RELATION_ID'
|
|
373
|
-
role = value
|
|
374
|
-
if rel = RelationProxy.find_by_role(role.singularize)
|
|
375
|
-
rel[:id]
|
|
376
|
-
else
|
|
377
|
-
@errors << "could not find Relation '#{role}' in custom query"
|
|
378
|
-
'-1'
|
|
379
|
-
end
|
|
380
|
-
elsif type == 'NODE_ATTR'
|
|
381
|
-
attribute = value
|
|
382
|
-
if Node.safe_method_type([attribute])
|
|
383
|
-
insert_bind("#{@node_name}.#{attribute}")
|
|
384
|
-
else
|
|
385
|
-
@errors << "cannot read attribute '#{attribute}' in custom query"
|
|
386
|
-
'-1'
|
|
387
|
-
end
|
|
388
|
-
end
|
|
389
|
-
end.gsub(/NODE_ID/) do
|
|
390
|
-
@uses_node_name = true
|
|
391
|
-
insert_bind("#{@node_name}.id")
|
|
392
|
-
end
|
|
393
|
-
end
|
|
394
|
-
|
|
395
|
-
def extract_custom_query(list)
|
|
396
|
-
super.singularize
|
|
397
|
-
end
|
|
398
|
-
|
|
399
|
-
def quote(literal)
|
|
400
|
-
Node.connection.quote(literal)
|
|
401
|
-
end
|
|
402
|
-
end
|
|
403
|
-
|
|
404
|
-
module Zena
|
|
405
|
-
module Use
|
|
406
|
-
module NodeQueryFinders
|
|
407
|
-
module AddUseNodeQueryMethod
|
|
408
|
-
def self.included(base)
|
|
409
|
-
# create class methods
|
|
410
|
-
base.extend AddUseNodeQueryMethodImpl
|
|
411
|
-
end
|
|
412
|
-
end
|
|
413
|
-
|
|
414
|
-
module AddUseNodeQueryMethodImpl
|
|
415
|
-
def use_node_query
|
|
416
|
-
class_eval do
|
|
417
|
-
include Zena::Use::NodeQueryFinders::InstanceMethods
|
|
418
|
-
class << self
|
|
419
|
-
include Zena::Use::NodeQueryFinders::ClassMethods
|
|
420
|
-
end
|
|
421
|
-
end
|
|
422
|
-
end
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
module ClassMethods
|
|
426
|
-
|
|
427
|
-
# Return an sql query string that will be used by 'do_find':
|
|
428
|
-
# build_find(:all, PSEUDO_SQL, node_name) => "SELECT * FROM nodes WHERE nodes.parent_id = #{@node[:id]} AND ..."
|
|
429
|
-
# PSEUDO_SQL: what to find in pseudo sql (See NodeQuery for details).
|
|
430
|
-
# node_name: contextual variable name
|
|
431
|
-
#
|
|
432
|
-
# Pseudo sql syntax:
|
|
433
|
-
#
|
|
434
|
-
# '[CLASS|VCLASS|RELATION] [in [site|section|project|parent]|] [where CLAUSE|] [from SUB_QUERY|] or [PSEUDO_SQL]'
|
|
435
|
-
#
|
|
436
|
-
# with :
|
|
437
|
-
# * CLASS: a native class ('images', 'documents', 'pages', 'projects', ...)
|
|
438
|
-
# * VCLASS: a virtual class created by the user ('posts', 'houses', ...)
|
|
439
|
-
# * RELATION: a relation defined by the user ('icon_for', 'news', 'calendar', ...)
|
|
440
|
-
# * CLAUSE: field = value ('log_at:year = 2005'). You can use parameters, visitor data in clause: 'log_at:year = [param:year]', 'd_assigned = [visitor:name]'. You can only use 'and' in clauses. 'or' is not supported. You can use version and/or dynamic attributes : 'v_comment = super', 'd_priority = low'.
|
|
441
|
-
#
|
|
442
|
-
# Examples: 'todos in section where d_priority = high and d_assigned = [visitor:name]'
|
|
443
|
-
#def build_find(count, pseudo_sql, node_name, raw_filters = nil, ignore_warnings = false, ref_date = nil)
|
|
444
|
-
def build_find(count, pseudo_sql, opts = {})
|
|
445
|
-
if count == :first
|
|
446
|
-
opts[:limit] = 1
|
|
447
|
-
end
|
|
448
|
-
NodeQuery.new(pseudo_sql, opts.merge(:custom_query_group => visitor.site.host))
|
|
449
|
-
end
|
|
450
|
-
end # ClassMethods
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
module InstanceMethods
|
|
454
|
-
|
|
455
|
-
# Find a node and propagate visitor
|
|
456
|
-
def do_find(count, query, ignore_source = false, klass = Node)
|
|
457
|
-
return nil if query.empty?
|
|
458
|
-
return nil if (new_record? && !ignore_source) # do not run query (might contain nil id)
|
|
459
|
-
|
|
460
|
-
case count
|
|
461
|
-
when :all
|
|
462
|
-
res = klass.find_by_sql(query)
|
|
463
|
-
secure_result(Node,res)
|
|
464
|
-
when :first
|
|
465
|
-
res = klass.find_by_sql(query).first
|
|
466
|
-
secure_result(Node,res)
|
|
467
|
-
when :count
|
|
468
|
-
klass.count_by_sql(query)
|
|
469
|
-
else
|
|
470
|
-
nil
|
|
471
|
-
end
|
|
472
|
-
end
|
|
473
|
-
|
|
474
|
-
# Find related nodes.
|
|
475
|
-
# See Node#build_find for details on the options available.
|
|
476
|
-
# TODO: replace with rubyless translate ? Is this thing really used anyway ?
|
|
477
|
-
def find(count, rel, opts = {})
|
|
478
|
-
rel = [rel] if rel.kind_of?(String)
|
|
479
|
-
|
|
480
|
-
if !opts[:skip_rubyless] && rel.size == 1 && type = RubyLess::SafeClass.safe_method_type_for(self.class, [rel.first])
|
|
481
|
-
self.send(type[:method])
|
|
482
|
-
else
|
|
483
|
-
query = Node.build_find(count, rel, :node_name => 'self')
|
|
484
|
-
if query.valid?
|
|
485
|
-
do_find(count, eval(query.to_s))
|
|
486
|
-
else
|
|
487
|
-
nil
|
|
488
|
-
end
|
|
489
|
-
end
|
|
490
|
-
end
|
|
491
|
-
end # InstanceMethods
|
|
492
|
-
end # NodeQueryFinders
|
|
493
|
-
end # Use
|
|
494
|
-
end # Zena
|