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
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'querybuilder'
|
|
2
|
+
|
|
3
|
+
module Zena
|
|
4
|
+
module Use
|
|
5
|
+
class QueryComment < QueryBuilder
|
|
6
|
+
attr_reader :uses_node_name, :node_name
|
|
7
|
+
set_main_table 'comments'
|
|
8
|
+
set_main_class 'Comment'
|
|
9
|
+
|
|
10
|
+
# Default sort order
|
|
11
|
+
def default_order_clause
|
|
12
|
+
"created_at ASC"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def default_context_filter
|
|
16
|
+
# should never be called alone
|
|
17
|
+
raise Exception.new("QueryComment should only be called from within QueryNode")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def parse_change_class(rel, is_last)
|
|
21
|
+
case rel
|
|
22
|
+
when 'author'
|
|
23
|
+
add_table('users')
|
|
24
|
+
@where << "#{table('users')}.id = #{field_or_attr('author_id')}"
|
|
25
|
+
# should we move on to Contact ?
|
|
26
|
+
when 'node', 'nodes'
|
|
27
|
+
add_table('discussions')
|
|
28
|
+
add_table('nodes')
|
|
29
|
+
@where << "#{table('discussions')}.id = #{table('comments')}.discussion_id"
|
|
30
|
+
@where << "#{table('nodes')}.id = #{table('discussions')}.node_id"
|
|
31
|
+
return Zena::Use::QueryNode # class change
|
|
32
|
+
else
|
|
33
|
+
return nil
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Same as QueryNode... DRY needed.
|
|
38
|
+
def map_literal(value)
|
|
39
|
+
if value =~ /(.*?)\[(node|visitor|param):(\w+)\](.*)/
|
|
40
|
+
val_start = $1 == '' ? '' : "#{$1.inspect} +"
|
|
41
|
+
val_end = $4 == '' ? '' : "+ #{$4.inspect}"
|
|
42
|
+
case $2
|
|
43
|
+
when 'visitor'
|
|
44
|
+
if $3 == 'user_id'
|
|
45
|
+
value = "visitor.id"
|
|
46
|
+
else
|
|
47
|
+
value = "Node.zafu_attribute(visitor.contact, #{$3.inspect})"
|
|
48
|
+
end
|
|
49
|
+
when 'node'
|
|
50
|
+
@uses_node_name = true
|
|
51
|
+
if $3 == 'user_id'
|
|
52
|
+
value = "#{@node_name}.user_id"
|
|
53
|
+
else
|
|
54
|
+
value = "Node.zafu_attribute(#{@node_name}, #{$3.inspect})"
|
|
55
|
+
end
|
|
56
|
+
when 'param'
|
|
57
|
+
return "\#{Node.connection.quote(#{val_start}params[:#{$3}].to_s#{val_end})}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
if !val_start.blank? || !val_end.blank?
|
|
61
|
+
"\#{Node.connection.quote(#{val_start}#{value}#{val_end})}"
|
|
62
|
+
else
|
|
63
|
+
"\#{#{value}}"
|
|
64
|
+
end
|
|
65
|
+
else
|
|
66
|
+
value = Node.connection.quote(value)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Overwrite this and take car to check for valid fields.
|
|
71
|
+
def map_field(fld, table_name, context = nil)
|
|
72
|
+
if ['status', 'updated_at', 'author_name', 'created_at', 'title', 'text', 'author_id'].include?(fld)
|
|
73
|
+
"#{table_name}.#{fld}"
|
|
74
|
+
else
|
|
75
|
+
# TODO: error, raise / ignore ?
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def map_attr(fld)
|
|
80
|
+
# error
|
|
81
|
+
nil
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Erb finder used by zafu
|
|
85
|
+
def finder(count)
|
|
86
|
+
return 'nil' unless valid?
|
|
87
|
+
case count
|
|
88
|
+
when :count
|
|
89
|
+
"#{node_name}.do_find(:count, #{to_s(:count)}, #{!uses_node_name}, #{main_class})"
|
|
90
|
+
else
|
|
91
|
+
"#{node_name}.do_find(#{count.inspect}, #{self.to_s}, #{!uses_node_name}, #{main_class})"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end # QueryComment
|
|
95
|
+
end # Use
|
|
96
|
+
end # Zena
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
require 'query_builder'
|
|
2
|
+
# Since QueryNode 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 QueryNode is to be
|
|
4
|
+
# compilated into an ERB template.
|
|
5
|
+
|
|
6
|
+
module Zena
|
|
7
|
+
module Use
|
|
8
|
+
class QueryNode < QueryBuilder
|
|
9
|
+
attr_reader :context, :uses_node_name, :node_name
|
|
10
|
+
set_main_table 'nodes'
|
|
11
|
+
set_main_class 'Node'
|
|
12
|
+
@@filter_fields = {'id' => {:key => 'zip'}}
|
|
13
|
+
|
|
14
|
+
load_custom_queries ["#{RAILS_ROOT}/bricks/*/queries"]
|
|
15
|
+
|
|
16
|
+
def self.insert_zero_link(link_class)
|
|
17
|
+
return if link_class.find_by_id(0)
|
|
18
|
+
link_class.connection.execute "INSERT INTO #{link_class.table_name} (id,target_id,source_id,status,comment) VALUES (0,0,0,NULL,NULL)"
|
|
19
|
+
unless link_class.find_by_id(0)
|
|
20
|
+
# the zero id is replaced by auto-increment value
|
|
21
|
+
last_id = link_class.find(:first, :order => 'id DESC').id
|
|
22
|
+
link_class.connection.execute "UPDATE #{link_class.table_name} SET id = 0 WHERE id = #{last_id}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.add_filter_field(key, fld_def)
|
|
27
|
+
@@filter_fields[key] = fld_def
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def initialize(query, opts = {})
|
|
31
|
+
@uses_node_name = false
|
|
32
|
+
@table_name = 'nodes'
|
|
33
|
+
@node_name = opts[:node_name]
|
|
34
|
+
# list of dyna_attributes keys allready in the filter
|
|
35
|
+
@dyn_keys = {}
|
|
36
|
+
super(query, opts)
|
|
37
|
+
# Raw filters are statements prepared that should not be further processed except for table_name replacement.
|
|
38
|
+
parse_raw_filters(opts[:raw_filters])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Build joins and filters from a relation.
|
|
42
|
+
def parse_relation(rel, context)
|
|
43
|
+
# join_relation first so we can overwrite 'class' finders (images) with a relation.
|
|
44
|
+
unless join_relation(rel, context) || context_relation(rel, context)
|
|
45
|
+
@errors << "unknown relation '#{rel}'"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Default sort order
|
|
50
|
+
def default_order_clause
|
|
51
|
+
"position ASC, name ASC"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def default_context_filter
|
|
55
|
+
'self'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def after_parse
|
|
59
|
+
@where.unshift "(\#{#{@node_name}.secure_scope('#{table}')})"
|
|
60
|
+
if @tables.include?('links')
|
|
61
|
+
link_table = table('links')
|
|
62
|
+
@select << "#{link_table}.id AS link_id,#{Zena::Use::Relations::LINK_ATTRIBUTES.map {|l| "#{link_table}.#{l} AS l_#{l}"}.join(',')}"
|
|
63
|
+
elsif @errors_unless_safe_links
|
|
64
|
+
@errors += @errors_unless_safe_links
|
|
65
|
+
end
|
|
66
|
+
@distinct = true if @tables.include?('versions')
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Erb finder used by zafu
|
|
70
|
+
def finder(count)
|
|
71
|
+
return 'nil' unless valid?
|
|
72
|
+
case count
|
|
73
|
+
when :count
|
|
74
|
+
"#{node_name}.do_find(:count, #{self.to_s(:count)}, #{!uses_node_name}, #{main_class})"
|
|
75
|
+
else
|
|
76
|
+
"#{node_name}.do_find(#{count.inspect}, #{self.to_s}, #{!uses_node_name}, #{main_class})"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
# Make sure all alternate queries include "links.id = 0" (dummy link)
|
|
82
|
+
def fix_where_list(where_list)
|
|
83
|
+
return unless @tables.include?('links')
|
|
84
|
+
where_list.each do |f|
|
|
85
|
+
unless f =~ /links\./
|
|
86
|
+
f << " AND links.id = 0"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
true
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Used to resolve 'in' clauses ('in project', 'in parent', etc)
|
|
93
|
+
def context_filter_fields(rel, is_last = false)
|
|
94
|
+
case rel
|
|
95
|
+
when 'self'
|
|
96
|
+
['parent_id', 'id']
|
|
97
|
+
when 'parent'
|
|
98
|
+
is_last ? ['parent_id', 'parent_id'] : ['parent_id', 'id']
|
|
99
|
+
when 'project'
|
|
100
|
+
is_last ? ['project_id', 'project_id'] : ['project_id', 'id']
|
|
101
|
+
when 'section'
|
|
102
|
+
is_last ? ['section_id', 'section_id'] : ['section_id', 'id']
|
|
103
|
+
when 'site', main_table
|
|
104
|
+
:void
|
|
105
|
+
else
|
|
106
|
+
nil
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Relations that can be resolved without a join
|
|
111
|
+
def context_relation(rel, context)
|
|
112
|
+
case rel
|
|
113
|
+
when 'self'
|
|
114
|
+
fields = ['id', 'id']
|
|
115
|
+
when 'parent', 'project', 'section'
|
|
116
|
+
fields = ['id', "#{rel}_id"]
|
|
117
|
+
when 'parents', 'projects', 'sections'
|
|
118
|
+
if @table_counter[main_table] > 0 || @tables.include?('links')
|
|
119
|
+
fields = ['id', "#{rel[0..-2]}_id"]
|
|
120
|
+
end
|
|
121
|
+
when 'root'
|
|
122
|
+
@where << "#{table}.id = #{current_site.root_id}"
|
|
123
|
+
return true
|
|
124
|
+
when 'author', 'traductions', 'versions'
|
|
125
|
+
# TODO: not implemented yet...
|
|
126
|
+
return nil
|
|
127
|
+
when 'visitor'
|
|
128
|
+
@where << "#{table}.id = #{insert_bind("visitor.contact_id")}"
|
|
129
|
+
return true
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
unless fields
|
|
133
|
+
if klass = Node.get_class(rel)
|
|
134
|
+
parse_context(default_context_filter) unless context
|
|
135
|
+
@where << "#{table}.kpath LIKE '#{klass.kpath}%'"
|
|
136
|
+
return true
|
|
137
|
+
else
|
|
138
|
+
# unknown class
|
|
139
|
+
return nil
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
@where << "#{field_or_attr(fields[0])} = #{field_or_attr(fields[1], table(main_table,-1))}"
|
|
144
|
+
true
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def parse_change_class(rel, is_last)
|
|
148
|
+
case rel
|
|
149
|
+
when 'comment', 'comments'
|
|
150
|
+
if is_last
|
|
151
|
+
# no need to load discussions, versions and all the mess
|
|
152
|
+
add_table('comments')
|
|
153
|
+
@where << "#{table('comments')}.discussion_id = #{map_attr('discussion_id')}"
|
|
154
|
+
return Zena::Use::QueryComment # class change
|
|
155
|
+
else
|
|
156
|
+
# parse_context(default_context_filter, true) if is_last
|
|
157
|
+
# after_parse
|
|
158
|
+
add_table('discussions')
|
|
159
|
+
add_table('comments')
|
|
160
|
+
@where << "#{table('discussions')}.node_id = #{table('nodes')}.id"
|
|
161
|
+
@where << "#{table('comments')}.discussion_id = #{table('discussions')}.id"
|
|
162
|
+
after_parse
|
|
163
|
+
return Zena::Use::QueryComment
|
|
164
|
+
end
|
|
165
|
+
else
|
|
166
|
+
return nil
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Filters that need a join
|
|
171
|
+
def join_relation(rel, context, new_table_alias = nil, previous_table_alias = nil)
|
|
172
|
+
new_table_alias ||= table(main_table)
|
|
173
|
+
previous_table_alias ||= table(main_table, -1)
|
|
174
|
+
|
|
175
|
+
if rel == main_table || rel == 'children'
|
|
176
|
+
# dummy clauses
|
|
177
|
+
parse_context(default_context_filter) unless context
|
|
178
|
+
return :void
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
if rel = RelationProxy.find_by_role(rel.singularize)
|
|
182
|
+
# We cannot use a LEFT JOIN here because it will totally mess up if we merge alternate queries
|
|
183
|
+
add_table('links')
|
|
184
|
+
# (= other_side = result) target <-- source (= link_side = caller)
|
|
185
|
+
if context && context != 'self'
|
|
186
|
+
# tagged in project (not equal to 'tagged from nodes in project')
|
|
187
|
+
# remove caller join
|
|
188
|
+
@distinct = true
|
|
189
|
+
@where << "#{field_or_attr('id', new_table_alias)} = #{table('links')}.#{rel.other_side} AND #{table('links')}.relation_id = #{rel[:id]}"
|
|
190
|
+
else
|
|
191
|
+
@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)}"
|
|
192
|
+
end
|
|
193
|
+
else
|
|
194
|
+
nil
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def map_literal(value, env = :sql)
|
|
199
|
+
if value =~ /(.*?)\[(visitor|param|node):(\w+)\](.*)/
|
|
200
|
+
val_start = $1 == '' ? '' : "#{$1.inspect} +"
|
|
201
|
+
val_end = $4 == '' ? '' : "+ #{$4.inspect}"
|
|
202
|
+
case $2
|
|
203
|
+
when 'visitor'
|
|
204
|
+
value = env == :sql ? insert_bind("#{val_start}Node.zafu_attribute(visitor.contact, #{$3.inspect})#{val_end}") : nil
|
|
205
|
+
when 'param'
|
|
206
|
+
value = env == :sql ? insert_bind("#{val_start}params[:#{$3}].to_s#{val_end}") : "params[:#{$3}]"
|
|
207
|
+
when 'node'
|
|
208
|
+
@uses_node_name = true
|
|
209
|
+
value = env == :sql ? insert_bind("#{val_start}#{@node_name}.safe_read(#{$3.inspect}).to_s#{val_end}") : "#{@node_name}.safe_read(#{$3.inspect})"
|
|
210
|
+
end
|
|
211
|
+
else
|
|
212
|
+
value = env == :sql ? quote(value) : nil
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Translate fields used for query/sort/grouping (context parameter) into something useable by SQL. Add the appropriate tables when needed.
|
|
217
|
+
def map_field(field, table_name = table, context = nil)
|
|
218
|
+
return map_literal("[#{field}]") if field =~ /\Aparam:/
|
|
219
|
+
case field[0..1]
|
|
220
|
+
when 'd_'
|
|
221
|
+
# DYNAMIC ATTRIBUTE
|
|
222
|
+
key = field[2..-1]
|
|
223
|
+
key, function = parse_sql_function_in_field(key)
|
|
224
|
+
key = Zena::Db.sql_function(function, dyn_value('versions', key, context))
|
|
225
|
+
when 'c_'
|
|
226
|
+
# CONTENT TABLE
|
|
227
|
+
field = field[2..-1]
|
|
228
|
+
# FIXME: implement #41
|
|
229
|
+
nil
|
|
230
|
+
when 'v_'
|
|
231
|
+
# VERSION
|
|
232
|
+
key = field[2..-1]
|
|
233
|
+
key, function = parse_sql_function_in_field(key)
|
|
234
|
+
if Version.safe_method_type([key]) && Version.column_names.include?(key)
|
|
235
|
+
vtable_name = needs_table('nodes', 'versions', "TABLE1.id = TABLE2.node_id")
|
|
236
|
+
Zena::Db.sql_function(function, "#{vtable_name}.#{key}")
|
|
237
|
+
else
|
|
238
|
+
# bad version attribute
|
|
239
|
+
nil
|
|
240
|
+
end
|
|
241
|
+
when 'l_'
|
|
242
|
+
key, function = parse_sql_function_in_field(field)
|
|
243
|
+
if key == 'l_status' || key == 'l_comment' || key == 'l_date' || (key == 'l_id' && [:order, :group].include?(context))
|
|
244
|
+
@errors_unless_safe_links ||= []
|
|
245
|
+
@errors_unless_safe_links << "cannot use link field '#{key}' in this query" unless (key == 'l_id' && context == :order)
|
|
246
|
+
# ok
|
|
247
|
+
Zena::Db.sql_function(function, "#{table('links')}.#{key[2..-1]}")
|
|
248
|
+
else
|
|
249
|
+
# bad attribute
|
|
250
|
+
nil
|
|
251
|
+
end
|
|
252
|
+
else
|
|
253
|
+
# NODE
|
|
254
|
+
key, function = parse_sql_function_in_field(field)
|
|
255
|
+
if context == :filter
|
|
256
|
+
if map_def = @@filter_fields[key]
|
|
257
|
+
if table_def = map_def[:table]
|
|
258
|
+
table_to_use = needs_table(*table_def)
|
|
259
|
+
else
|
|
260
|
+
table_to_use = table_name
|
|
261
|
+
end
|
|
262
|
+
Zena::Db.sql_function(function, "#{table_to_use}.#{map_def[:key]}")
|
|
263
|
+
elsif (Node.safe_method_type([key]) && Node.column_names.include?(key))
|
|
264
|
+
Zena::Db.sql_function(function, "#{table_name}.#{key}")
|
|
265
|
+
elsif key =~ /^(.*)_ids?$/
|
|
266
|
+
# tag_id = 33 ===> join links as lk, nodes as tt .......
|
|
267
|
+
rel = $1
|
|
268
|
+
|
|
269
|
+
if RelationProxy.find_by_role(rel.singularize)
|
|
270
|
+
add_table('jnode', 'nodes')
|
|
271
|
+
join_relation(rel, nil, table('jnode'), table('nodes'))
|
|
272
|
+
"#{table('jnode')}.zip"
|
|
273
|
+
else
|
|
274
|
+
nil
|
|
275
|
+
end
|
|
276
|
+
else
|
|
277
|
+
nil
|
|
278
|
+
end
|
|
279
|
+
else
|
|
280
|
+
if ['id', 'parent_id','project_id','section_id'].include?(key) || (Node.safe_method_type([key]) && Node.column_names.include?(key))
|
|
281
|
+
Zena::Db.sql_function(function, "#{table_name}.#{key}")
|
|
282
|
+
else
|
|
283
|
+
# bad attribute
|
|
284
|
+
nil
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def valid_field?(table_name, fld)
|
|
291
|
+
# FIXME: security !
|
|
292
|
+
true
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def map_attr(fld, env = :sql)
|
|
296
|
+
case fld
|
|
297
|
+
when 'project_id', 'section_id', 'discussion_id'
|
|
298
|
+
@uses_node_name = true
|
|
299
|
+
insert_bind("#{@node_name}.get_#{fld}")
|
|
300
|
+
when 'id', 'parent_id'
|
|
301
|
+
@uses_node_name = true
|
|
302
|
+
insert_bind("#{@node_name}.#{fld}")
|
|
303
|
+
else
|
|
304
|
+
# type = Node.safe_method_type([fld])
|
|
305
|
+
# type[:method] ?
|
|
306
|
+
|
|
307
|
+
# bad parameter
|
|
308
|
+
@errors << "invalid parameter '#{fld}'"
|
|
309
|
+
"0"
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def parse_paginate_clause(paginate)
|
|
314
|
+
return @offset unless paginate
|
|
315
|
+
if !@limit
|
|
316
|
+
# TODO: raise error ?
|
|
317
|
+
@errors << "invalid paginate clause '#{paginate}' (used without limit)"
|
|
318
|
+
nil
|
|
319
|
+
elsif (fld = map_literal("[param:#{paginate}]", :ruby)) && (page_size = @limit[/ LIMIT (\d+)/,1])
|
|
320
|
+
@page_size = [2,page_size.to_i].max
|
|
321
|
+
" OFFSET #{insert_bind("((#{fld}.to_i > 0 ? #{fld}.to_i : 1)-1)*#{page_size.to_i}")}"
|
|
322
|
+
else
|
|
323
|
+
@errors << "invalid paginate clause '#{paginate}'"
|
|
324
|
+
nil
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# When a field is defined as log_at:year, return [log_at, year].
|
|
329
|
+
def parse_sql_function_in_field(field)
|
|
330
|
+
if field =~ /\A(\w+):(\w+)\Z/
|
|
331
|
+
if ['year'].include?($2)
|
|
332
|
+
[$1,$2]
|
|
333
|
+
else
|
|
334
|
+
[$1]
|
|
335
|
+
end
|
|
336
|
+
else
|
|
337
|
+
[field]
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def parse_raw_filters(filters)
|
|
342
|
+
return unless filters
|
|
343
|
+
filters.each do |f|
|
|
344
|
+
all_ok = true
|
|
345
|
+
filter = f.gsub(/TABLE_NAME(\[(\w+)\]|)/) do
|
|
346
|
+
if $2
|
|
347
|
+
if @table_counter[$2]
|
|
348
|
+
table($2)
|
|
349
|
+
else
|
|
350
|
+
all_ok = false
|
|
351
|
+
@errors << "invalid table_name '#{$2}' in raw filter"
|
|
352
|
+
@errors.uniq!
|
|
353
|
+
end
|
|
354
|
+
else
|
|
355
|
+
table
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
@where << filter if all_ok
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def dyn_value(table_name, key, context)
|
|
363
|
+
@dyn_keys[table_name] ||= {}
|
|
364
|
+
@dyn_keys[table_name][key] ||= begin
|
|
365
|
+
needs_table('nodes', 'versions', "TABLE1.id = TABLE2.node_id")
|
|
366
|
+
dtable = needs_join_table('versions', 'LEFT', 'dyn_attributes', "TABLE1.id = TABLE2.owner_id AND TABLE2.key = #{quote(key)}", "versions=dyn_attributes=#{key}")
|
|
367
|
+
"#{dtable}.value"
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def parse_custom_query_argument(key, value)
|
|
372
|
+
return nil unless value
|
|
373
|
+
super.gsub(/(RELATION_ID|NODE_ATTR)\(([^)]+)\)/) do
|
|
374
|
+
type, value = $1, $2
|
|
375
|
+
if type == 'RELATION_ID'
|
|
376
|
+
role = value
|
|
377
|
+
if rel = RelationProxy.find_by_role(role.singularize)
|
|
378
|
+
rel[:id]
|
|
379
|
+
else
|
|
380
|
+
@errors << "could not find Relation '#{role}' in custom query"
|
|
381
|
+
'-1'
|
|
382
|
+
end
|
|
383
|
+
elsif type == 'NODE_ATTR'
|
|
384
|
+
attribute = value
|
|
385
|
+
if Node.safe_method_type([attribute])
|
|
386
|
+
insert_bind("#{@node_name}.#{attribute}")
|
|
387
|
+
else
|
|
388
|
+
@errors << "cannot read attribute '#{attribute}' in custom query"
|
|
389
|
+
'-1'
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
end.gsub(/NODE_ID/) do
|
|
393
|
+
@uses_node_name = true
|
|
394
|
+
insert_bind("#{@node_name}.id")
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def extract_custom_query(list)
|
|
399
|
+
super.singularize
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def quote(literal)
|
|
403
|
+
Node.connection.quote(literal)
|
|
404
|
+
end
|
|
405
|
+
end # QueryNode
|
|
406
|
+
end # Use
|
|
407
|
+
end # Zena
|
|
408
|
+
|