wagn 1.12.11 → 1.12.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/VERSION +1 -1
- data/app/assets/javascripts/wagn.js.coffee +1 -1
- data/app/controllers/admin_controller.rb +1 -1
- data/app/controllers/wagn_controller.rb +6 -6
- data/app/models/user.rb +1 -1
- data/lib/card.rb +1 -3
- data/lib/card/exceptions.rb +18 -6
- data/lib/card/format.rb +1 -1
- data/lib/card/query/card_spec.rb +393 -388
- data/lib/wagn/exceptions.rb +7 -6
- data/lib/wagn/loader.rb +2 -2
- data/mods/core/formats/html_format.rb +1 -1
- data/mods/core/sets/all/fetch.rb +3 -8
- data/mods/core/sets/all/name.rb +10 -10
- data/mods/core/sets/all/utils.rb +1 -1
- data/mods/standard/sets/all/account.rb +1 -1
- data/mods/standard/sets/right/style.rb +1 -1
- data/mods/standard/sets/type/file.rb +1 -1
- data/mods/standard/sets/type/search_type.rb +2 -2
- data/public/assets/{application-7cda003cf1291a165b1eac69ea3027aa.js → application-b0c6081404451e8d3ba4d60c15fcaf40.js} +10 -10
- data/public/assets/application-b0c6081404451e8d3ba4d60c15fcaf40.js.gz +0 -0
- data/public/assets/application.js +10 -10
- data/public/assets/application.js.gz +0 -0
- data/public/assets/html5shiv-printshiv-ad36fc7219f4aeaa275f9a7fe2383413.js.gz +0 -0
- data/public/assets/html5shiv-printshiv.js.gz +0 -0
- data/public/assets/manifest.yml +2 -2
- data/public/assets/tinymce-22b5a139d9ff7df7643afae9ce205508.js.gz +0 -0
- data/public/assets/tinymce.js.gz +0 -0
- data/spec/controllers/account_controller_spec.rb +0 -1
- metadata +4 -4
- data/public/assets/application-7cda003cf1291a165b1eac69ea3027aa.js.gz +0 -0
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NWE5ZWQ5N2VkMDI0NTJiYTgwYjIwMDI5M2M5YWZhZTM4NTEzNjljOQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MDk2OTUxNTBiNDJhNGRmZDliNDAyYmFhMDRjNjYwMTA3ODE0ZTY2YQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NThjODA4OTcwOWM2ZTM5YTJjZDAxZWYyMDYxNGZmMzg4MWJlNzcyMmU3YzFm
|
10
|
+
MGQ3ZTc3NjlkYTkyNTVmYzNiMjAyODlhYTkxNDEzYTRlNzFhMzVlNTY0N2M3
|
11
|
+
MWMwNWRmNTRlMGM0MTU0ZTUwZTJhMmUxZDAxMDg2OTg5MTk5NjI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MzI3NTBlNWI4NzA4Zjg3NGY3NDMxOTJhOTBjZmI1ZGQ0MTc4NTgyZTdlOWVh
|
14
|
+
ZjE4MzYyNTcwODc2NGU3NDAzMzcwMDA1YmQ1YWEzNTE0NjQ4ZTVhNzUwZTM0
|
15
|
+
NjkzN2M2ODE3NDUwZWZkMmYyNTQwMGRiNGUxNmU0NjJkMjgxNzY=
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.12.
|
1
|
+
1.12.12
|
@@ -160,8 +160,8 @@ $(window).ready ->
|
|
160
160
|
if input[1]
|
161
161
|
$(this).notify "Wagn does not yet support multiple files in a single form."
|
162
162
|
return false
|
163
|
-
widget = input.data 'fileupload' #jQuery UI widget
|
164
163
|
|
164
|
+
widget = input.data 'blueimpFileupload' #jQuery UI widget
|
165
165
|
unless widget._isXHRUpload(widget.options) # browsers that can't do ajax uploads use iframe
|
166
166
|
$(this).find('[name=success]').val('_self') # can't do normal redirects.
|
167
167
|
# iframe response not passed back; all responses treated as success. boo
|
@@ -3,7 +3,7 @@ class AdminController < CardController
|
|
3
3
|
before_filter :admin_only, :except=>:setup
|
4
4
|
|
5
5
|
def setup
|
6
|
-
raise
|
6
|
+
raise Card::Oops, "Already setup" unless Account.no_logins?
|
7
7
|
if request.post?
|
8
8
|
Wagn::Env[:recaptcha_on] = false
|
9
9
|
handle do
|
@@ -131,17 +131,17 @@ class WagnController < ActionController::Base
|
|
131
131
|
view = case exception
|
132
132
|
## arguably the view and status should be defined in the error class;
|
133
133
|
## some are redundantly defined in view
|
134
|
-
when
|
134
|
+
when Card::Oops, Card::Query
|
135
|
+
card.errors.add :exception, exception.message
|
136
|
+
# Card::Oops error messages are visible to end users and are generally not treated as bugs.
|
137
|
+
# Probably want to rename accordingly.
|
138
|
+
:errors
|
139
|
+
when Card::PermissionDenied, Wagn::PermissionDenied
|
135
140
|
:denial
|
136
141
|
when Wagn::NotFound, ActiveRecord::RecordNotFound, ActionController::MissingFile
|
137
142
|
:not_found
|
138
143
|
when Wagn::BadAddress
|
139
144
|
:bad_address
|
140
|
-
when Wagn::Oops
|
141
|
-
card.errors.add :exception, exception.message
|
142
|
-
# Wagn:Oops error messages are visible to end users and are generally not treated as bugs.
|
143
|
-
# Probably want to rename accordingly.
|
144
|
-
:errors
|
145
145
|
else #the following indicate a code problem and therefore require full logging
|
146
146
|
Rails.logger.info exception.backtrace*"\n"
|
147
147
|
notify_airbrake exception if Airbrake.configuration.api_key
|
data/app/models/user.rb
CHANGED
@@ -36,7 +36,7 @@ class User < ActiveRecord::Base
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def send_account_info args
|
39
|
-
raise
|
39
|
+
raise Card::Oops, "subject and message required" unless args[:subject] && args[:message]
|
40
40
|
begin
|
41
41
|
if password.blank?
|
42
42
|
generate_password
|
data/lib/card.rb
CHANGED
@@ -5,14 +5,12 @@ class Card < ActiveRecord::Base
|
|
5
5
|
require_dependency 'card/constant'
|
6
6
|
require_dependency 'card/set'
|
7
7
|
require_dependency 'card/format'
|
8
|
+
require_dependency 'card/exceptions'
|
8
9
|
|
9
10
|
extend Card::Set
|
10
11
|
extend Card::Constant
|
11
12
|
extend Wagn::Loader
|
12
13
|
|
13
|
-
require_dependency 'card/exceptions'
|
14
|
-
include Card::Exceptions
|
15
|
-
|
16
14
|
has_many :revisions, :order => :id
|
17
15
|
has_many :references_from, :class_name => :Reference, :foreign_key => :referee_id
|
18
16
|
has_many :references_to, :class_name => :Reference, :foreign_key => :referer_id
|
data/lib/card/exceptions.rb
CHANGED
@@ -1,7 +1,16 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
|
3
|
-
|
4
|
-
class
|
3
|
+
class Card
|
4
|
+
class Error < StandardError #code problem
|
5
|
+
end
|
6
|
+
|
7
|
+
class Oops < Error # wagneer problem
|
8
|
+
end
|
9
|
+
|
10
|
+
class BadQuery < Error
|
11
|
+
end
|
12
|
+
|
13
|
+
class PermissionDenied < Error
|
5
14
|
attr_reader :card
|
6
15
|
|
7
16
|
def initialize card
|
@@ -10,18 +19,21 @@ module Card::Exceptions
|
|
10
19
|
end
|
11
20
|
|
12
21
|
def build_message
|
13
|
-
|
22
|
+
if msg = @card.errors[:permission_denied]
|
23
|
+
"for card #{@card.name}: #{msg}"
|
24
|
+
else
|
25
|
+
super
|
26
|
+
end
|
14
27
|
end
|
15
28
|
end
|
16
29
|
|
17
30
|
class Abort < Exception
|
18
31
|
attr_reader :status
|
32
|
+
|
19
33
|
def initialize status=:failure, msg=''
|
20
34
|
@status = status
|
21
35
|
super msg
|
22
36
|
end
|
23
|
-
|
24
|
-
# ["\n( card action gently canceled )\n"]
|
25
|
-
# end
|
37
|
+
|
26
38
|
end
|
27
39
|
end
|
data/lib/card/format.rb
CHANGED
data/lib/card/query/card_spec.rb
CHANGED
@@ -1,517 +1,522 @@
|
|
1
1
|
|
2
|
-
class Card
|
3
|
-
class
|
2
|
+
class Card
|
3
|
+
class Query
|
4
|
+
class CardSpec < Spec
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
ATTRIBUTES = {
|
7
|
+
:basic => %w{ name type_id content id key updater_id left_id right_id creator_id updater_id codename },
|
8
|
+
:relational => %w{ type part left right editor_of edited_by last_editor_of last_edited_by creator_of created_by member_of member },
|
9
|
+
:plus_relational => %w{ plus left_plus right_plus },
|
10
|
+
:ref_relational => %w{ refer_to referred_to_by link_to linked_to_by include included_by },
|
11
|
+
:conjunction => %w{ and or all any },
|
12
|
+
:special => %w{ found_by not sort match complete extension_type },
|
13
|
+
:ignore => %w{ prepend append view params vars size }
|
14
|
+
}.inject({}) {|h,pair| pair[1].each {|v| h[v.to_sym]=pair[0] }; h }
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
DEFAULT_ORDER_DIRS = { :update => "desc", :relevance => "desc" }
|
17
|
+
CONJUNCTIONS = { :any=>:or, :in=>:or, :or=>:or, :all=>:and, :and=>:and }
|
17
18
|
|
18
|
-
|
19
|
-
|
19
|
+
attr_reader :sql, :query, :rawspec, :selfname
|
20
|
+
attr_accessor :joins
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
class << self
|
23
|
+
def build query
|
24
|
+
cardspec = self.new query
|
25
|
+
cardspec.merge cardspec.rawspec
|
26
|
+
end
|
25
27
|
end
|
26
|
-
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
def initialize query
|
30
|
+
@mods = MODIFIERS.clone
|
31
|
+
@spec, @joins = {}, {}
|
32
|
+
@selfname, @parent = '', nil
|
33
|
+
@sql = SqlStatement.new
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
35
|
+
@query = query.clone
|
36
|
+
@query.merge! @query.delete(:params) if @query[:params]
|
37
|
+
@vars = @query.delete(:vars) || {}
|
38
|
+
@vars.symbolize_keys!
|
39
|
+
@query = clean(@query)
|
40
|
+
@rawspec = @query.deep_clone
|
40
41
|
|
41
|
-
|
42
|
-
|
42
|
+
self
|
43
|
+
end
|
43
44
|
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
46
|
+
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
47
|
+
# QUERY CLEANING - strip strings, absolutize names, interpret contextual parameters
|
48
|
+
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
48
49
|
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
51
|
+
def clean query
|
52
|
+
query = query.symbolize_keys
|
53
|
+
if s = query.delete(:context) then @selfname = s end
|
54
|
+
if p = query.delete(:_parent) then @parent = p end
|
55
|
+
query.each do |key,val|
|
56
|
+
query[key] = clean_val val
|
57
|
+
end
|
58
|
+
query
|
56
59
|
end
|
57
|
-
query
|
58
|
-
end
|
59
60
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
61
|
+
def clean_val val
|
62
|
+
case val
|
63
|
+
when String
|
64
|
+
if val =~ /^\$(\w+)$/
|
65
|
+
val = @vars[$1.to_sym].to_s.strip
|
66
|
+
end
|
67
|
+
absolute_name val
|
68
|
+
when Card::Name ; clean_val val.s
|
69
|
+
when Hash ; clean val
|
70
|
+
when Array ; val.map { |v| clean_val v }
|
71
|
+
when Integer, Float, Symbol ; val
|
72
|
+
else ; raise BadQuery, "unknown WQL value type: #{val.class}"
|
65
73
|
end
|
66
|
-
absolute_name val
|
67
|
-
when Card::Name ; clean_val val.s
|
68
|
-
when Hash ; clean val
|
69
|
-
when Array ; val.map { |v| clean_val v }
|
70
|
-
when Integer, Float, Symbol ; val
|
71
|
-
else ; raise "unknown WQL value type: #{val.class}"
|
72
74
|
end
|
73
|
-
end
|
74
75
|
|
75
|
-
|
76
|
-
|
77
|
-
|
76
|
+
def root
|
77
|
+
@parent ? @parent.root : self
|
78
|
+
end
|
78
79
|
|
79
|
-
|
80
|
-
|
81
|
-
|
80
|
+
def absolute_name name
|
81
|
+
name =~ /\b_/ ? name.to_name.to_absolute(root.selfname) : name
|
82
|
+
end
|
82
83
|
|
83
84
|
|
84
|
-
|
85
|
-
|
86
|
-
|
85
|
+
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
86
|
+
# MERGE - reduce query to basic attributes and SQL subconditions
|
87
|
+
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
87
88
|
|
88
89
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
90
|
+
def merge s
|
91
|
+
s = hashify s
|
92
|
+
translate_to_attributes s
|
93
|
+
ready_to_sqlize s
|
94
|
+
@spec.merge! s
|
95
|
+
self
|
96
|
+
end
|
96
97
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
98
|
+
def hashify s
|
99
|
+
case s
|
100
|
+
when String; { :key => s.to_name.key }
|
101
|
+
when Integer; { :id => s }
|
102
|
+
when Hash; s
|
103
|
+
else; raise BadQueyr, "Invalid cardspec args #{s.inspect}"
|
104
|
+
end
|
103
105
|
end
|
104
|
-
end
|
105
106
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
107
|
+
def translate_to_attributes spec
|
108
|
+
content = nil
|
109
|
+
spec.each do |key,val|
|
110
|
+
if key == :_parent
|
111
|
+
@parent = spec.delete(key)
|
112
|
+
elsif OPERATORS.has_key?(key.to_s) && !ATTRIBUTES[key]
|
113
|
+
spec.delete(key)
|
114
|
+
content = [key,val]
|
115
|
+
elsif MODIFIERS.has_key?(key)
|
116
|
+
next if spec[key].is_a? Hash
|
117
|
+
val = spec.delete key
|
118
|
+
@mods[key] = Array === val ? val : val.to_s
|
119
|
+
end
|
118
120
|
end
|
121
|
+
spec[:content] = content if content
|
119
122
|
end
|
120
|
-
spec[:content] = content if content
|
121
|
-
end
|
122
123
|
|
123
124
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
125
|
+
def ready_to_sqlize spec
|
126
|
+
spec.each do |key,val|
|
127
|
+
keyroot = field_root(key).to_sym
|
128
|
+
if keyroot==:cond # internal SQL cond (already ready)
|
129
|
+
elsif ATTRIBUTES[keyroot] == :basic # sqlize knows how to handle these keys; just process value
|
130
|
+
spec[key] = ValueSpec.new(val, self)
|
131
|
+
else # keys need additional processing
|
132
|
+
val = spec.delete key
|
133
|
+
is_array = Array===val
|
134
|
+
case ATTRIBUTES[keyroot]
|
135
|
+
when :ignore #noop
|
136
|
+
when :relational, :special, :conjunction ; relate is_array, keyroot, val, :send
|
137
|
+
when :ref_relational ; relate is_array, keyroot, val, :refspec
|
138
|
+
when :plus_relational
|
139
|
+
# Arrays can have multiple interpretations for these, so we have to look closer...
|
140
|
+
subcond = is_array && ( Array===val.first || conjunction(val.first) )
|
140
141
|
|
141
|
-
|
142
|
-
|
142
|
+
relate subcond, keyroot, val, :send
|
143
|
+
else ; raise BadQuery, "Invalid attribute #{key}"
|
144
|
+
end
|
143
145
|
end
|
144
146
|
end
|
145
|
-
end
|
146
147
|
|
147
|
-
|
148
|
+
end
|
148
149
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
150
|
+
def relate subcond, key, val, method
|
151
|
+
if subcond
|
152
|
+
conj = conjunction( val.first ) ? conjunction( val.shift ) : :and
|
153
|
+
if conj == current_conjunction # same conjunction as container, no need for subcondition
|
154
|
+
val.each { |v| send method, key, v }
|
155
|
+
else
|
156
|
+
send conj, val.inject({}) { |h,v| h[field key] = v; h } # subcondition
|
157
|
+
end
|
154
158
|
else
|
155
|
-
send
|
159
|
+
send method, key, val
|
156
160
|
end
|
157
|
-
else
|
158
|
-
send method, key, val
|
159
161
|
end
|
160
|
-
end
|
161
162
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
163
|
+
def refspec key, cardspec
|
164
|
+
if cardspec == '_none'
|
165
|
+
key = :link_to_missing
|
166
|
+
cardspec = 'blank'
|
167
|
+
end
|
168
|
+
cardspec = CardSpec.build(:return=>'id', :_parent=>self).merge(cardspec)
|
169
|
+
merge field(:id) => ValueSpec.new(['in',RefSpec.new( key, cardspec )], self)
|
166
170
|
end
|
167
|
-
cardspec = CardSpec.build(:return=>'id', :_parent=>self).merge(cardspec)
|
168
|
-
merge field(:id) => ValueSpec.new(['in',RefSpec.new( key, cardspec )], self)
|
169
|
-
end
|
170
171
|
|
171
172
|
|
172
|
-
|
173
|
-
|
174
|
-
|
173
|
+
def conjunction val
|
174
|
+
if [String, Symbol].member? val.class
|
175
|
+
CONJUNCTIONS[val.to_sym]
|
176
|
+
end
|
175
177
|
end
|
176
|
-
end
|
177
178
|
|
178
179
|
|
179
|
-
|
180
|
-
|
181
|
-
|
180
|
+
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
181
|
+
# ATTRIBUTE METHODS - called during merge
|
182
|
+
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
182
183
|
|
183
184
|
|
184
|
-
|
185
|
+
#~~~~~~ RELATIONAL
|
185
186
|
|
186
|
-
|
187
|
-
|
188
|
-
|
187
|
+
def type val
|
188
|
+
merge field(:type_id) => id_or_subspec(val)
|
189
|
+
end
|
189
190
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
191
|
+
def part val
|
192
|
+
right = Integer===val ? val : val.clone
|
193
|
+
subcondition :left=>val, :right=>right, :conj=>:or
|
194
|
+
end
|
194
195
|
|
195
|
-
|
196
|
-
|
197
|
-
|
196
|
+
def left val
|
197
|
+
merge field(:left_id) => id_or_subspec(val)
|
198
|
+
end
|
198
199
|
|
199
|
-
|
200
|
-
|
201
|
-
|
200
|
+
def right val
|
201
|
+
merge field(:right_id) => id_or_subspec(val)
|
202
|
+
end
|
202
203
|
|
203
|
-
|
204
|
-
|
205
|
-
|
204
|
+
def editor_of val
|
205
|
+
revision_spec :creator_id, :card_id, val
|
206
|
+
end
|
206
207
|
|
207
|
-
|
208
|
-
|
209
|
-
|
208
|
+
def edited_by val
|
209
|
+
revision_spec :card_id, :creator_id, val
|
210
|
+
end
|
210
211
|
|
211
|
-
|
212
|
-
|
213
|
-
|
212
|
+
def last_editor_of val
|
213
|
+
merge field(:id) => subspec(val, :return=>'updater_id')
|
214
|
+
end
|
214
215
|
|
215
|
-
|
216
|
-
|
217
|
-
|
216
|
+
def last_edited_by val
|
217
|
+
merge field(:updater_id) => id_or_subspec(val)
|
218
|
+
end
|
218
219
|
|
219
|
-
|
220
|
-
|
221
|
-
|
220
|
+
def creator_of val
|
221
|
+
merge field(:id)=>subspec(val,:return=>'creator_id')
|
222
|
+
end
|
222
223
|
|
223
|
-
|
224
|
-
|
225
|
-
|
224
|
+
def created_by val
|
225
|
+
merge field(:creator_id) => id_or_subspec(val)
|
226
|
+
end
|
226
227
|
|
227
|
-
|
228
|
-
|
229
|
-
|
228
|
+
def member_of val
|
229
|
+
merge field(:right_plus) => [RolesID, {:refer_to=>val}]
|
230
|
+
end
|
230
231
|
|
231
|
-
|
232
|
-
|
233
|
-
|
232
|
+
def member val
|
233
|
+
merge field(:referred_to_by) => {:left=>val, :right=>RolesID }
|
234
|
+
end
|
234
235
|
|
235
236
|
|
236
|
-
|
237
|
+
#~~~~~~ PLUS RELATIONAL
|
237
238
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
239
|
+
def left_plus(val)
|
240
|
+
part_spec, junc_spec = val.is_a?(Array) ? val : [ val, {} ]
|
241
|
+
merge( field(:id) => subspec(junc_spec, :return=>'right_id', :left =>part_spec))
|
242
|
+
end
|
242
243
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
244
|
+
def right_plus(val)
|
245
|
+
part_spec, junc_spec = val.is_a?(Array) ? val : [ val, {} ]
|
246
|
+
merge( field(:id) => subspec(junc_spec, :return=>'left_id', :right=> part_spec ))
|
247
|
+
end
|
247
248
|
|
248
|
-
|
249
|
-
|
250
|
-
|
249
|
+
def plus(val)
|
250
|
+
subcondition( { :left_plus=>val, :right_plus=>val.deep_clone }, :conj=>:or )
|
251
|
+
end
|
251
252
|
|
252
253
|
|
253
|
-
|
254
|
+
#~~~~~~~ CONJUNCTION
|
254
255
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
256
|
+
def and val
|
257
|
+
subcondition val
|
258
|
+
end
|
259
|
+
alias :all :and
|
259
260
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
261
|
+
def or val
|
262
|
+
subcondition val, :conj=>:or
|
263
|
+
end
|
264
|
+
alias :any :or
|
264
265
|
|
265
|
-
|
266
|
+
#~~~~~~ SPECIAL
|
266
267
|
|
267
268
|
|
268
|
-
|
269
|
+
def found_by val
|
269
270
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
271
|
+
cards = if Hash===val
|
272
|
+
Query.new(val).run
|
273
|
+
else
|
274
|
+
Array.wrap(val).map do |v|
|
275
|
+
Card.fetch absolute_name(val), :new=>{}
|
276
|
+
end
|
275
277
|
end
|
276
|
-
end
|
277
278
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
279
|
+
cards.each do |c|
|
280
|
+
unless c && [SearchTypeID,SetID].include?(c.type_id)
|
281
|
+
raise BadQuery, %{"found_by" value needs to be valid Search card}
|
282
|
+
end
|
283
|
+
found_by_spec = CardSpec.new(c.get_spec).rawspec
|
284
|
+
merge(field(:id) => subspec(found_by_spec))
|
285
|
+
end
|
282
286
|
end
|
283
|
-
end
|
284
287
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
+
def not val
|
289
|
+
merge field(:id) => subspec( val, {:return=>'id'}, negate=true )
|
290
|
+
end
|
288
291
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
292
|
+
def sort val
|
293
|
+
return nil if @parent
|
294
|
+
val[:return] = val[:return] ? safe_sql(val[:return]) : 'content'
|
295
|
+
@mods[:sort] = "t_sort.#{val[:return]}"
|
296
|
+
item = val.delete(:item) || 'left'
|
297
|
+
|
298
|
+
if val[:return] == 'count'
|
299
|
+
cs_args = { :return=>'count', :group=>'sort_join_field' }
|
300
|
+
@mods[:sort] = "coalesce(#{@mods[:sort]},0)"
|
301
|
+
case item
|
302
|
+
when 'referred_to'
|
303
|
+
join_field = 'id'
|
304
|
+
cs = CardSpec.build cs_args.merge( field(:cond)=>SqlCond.new("referer_id in #{CardSpec.build( val.merge(:return=>'id')).to_sql}") )
|
305
|
+
cs.add_join :wr, :card_references, :id, :referee_id
|
306
|
+
else
|
307
|
+
raise BadQuery, "count with item: #{item} not yet implemented"
|
308
|
+
end
|
309
|
+
else
|
310
|
+
join_field = case item
|
311
|
+
when 'left' ; 'left_id'
|
312
|
+
when 'right' ; 'right_id'
|
313
|
+
else ; raise BadQuery, "sort item: #{item} not yet implemented"
|
314
|
+
end
|
315
|
+
cs = CardSpec.build(val)
|
310
316
|
end
|
311
|
-
cs = CardSpec.build(val)
|
312
|
-
end
|
313
317
|
|
314
|
-
|
315
|
-
|
316
|
-
|
318
|
+
cs.sql.fields << "#{cs.table_alias}.#{join_field} as sort_join_field"
|
319
|
+
add_join :sort, cs.to_sql, :id, :sort_join_field, :side=>'LEFT'
|
320
|
+
end
|
317
321
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
+
def match(val)
|
323
|
+
cxn, val = match_prep val
|
324
|
+
val.gsub! /[^#{Card::Name::OK4KEY_RE}]+/, ' '
|
325
|
+
return nil if val.strip.empty?
|
322
326
|
|
323
327
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
328
|
+
cond = begin
|
329
|
+
join_alias = add_revision_join
|
330
|
+
# FIXME: OMFG this is ugly
|
331
|
+
val_list = val.split(/\s+/).map do |v|
|
332
|
+
name_or_content = ["replace(#{self.table_alias}.name,'+',' ')","#{join_alias}.content"].map do |field|
|
333
|
+
%{#{field} #{ cxn.match quote("[[:<:]]#{v}[[:>:]]") }}
|
334
|
+
end
|
335
|
+
"(#{name_or_content.join ' OR '})"
|
330
336
|
end
|
331
|
-
"(#{
|
337
|
+
"(#{val_list.join ' AND '})"
|
332
338
|
end
|
333
|
-
"(#{val_list.join ' AND '})"
|
334
|
-
end
|
335
339
|
|
336
|
-
|
337
|
-
|
340
|
+
merge field(:cond)=>SqlCond.new(cond)
|
341
|
+
end
|
338
342
|
|
339
343
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
+
def complete(val)
|
345
|
+
no_plus_card = (val=~/\+/ ? '' : "and right_id is null") #FIXME -- this should really be more nuanced -- it breaks down after one plus
|
346
|
+
merge field(:cond) => SqlCond.new(" lower(name) LIKE lower(#{quote(val.to_s+'%')}) #{no_plus_card}")
|
347
|
+
end
|
344
348
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
+
def extension_type val
|
350
|
+
# DEPRECATED!!!
|
351
|
+
add_join :usr, :users, :id, :card_id
|
352
|
+
end
|
349
353
|
|
350
354
|
|
351
|
-
|
352
|
-
|
353
|
-
|
355
|
+
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
356
|
+
# ATTRIBUTE METHOD HELPERS - called by attribute methods above
|
357
|
+
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
354
358
|
|
355
359
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
360
|
+
def table_alias
|
361
|
+
case
|
362
|
+
when @mods[:return]=='condition'
|
363
|
+
@parent ? @parent.table_alias : "t"
|
364
|
+
when @parent
|
365
|
+
@parent.table_alias + "x"
|
366
|
+
else
|
367
|
+
"t"
|
368
|
+
end
|
364
369
|
end
|
365
|
-
end
|
366
370
|
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
371
|
+
def add_join(name, table, cardfield, otherfield, opts={})
|
372
|
+
join_alias = "#{table_alias}_#{name}"
|
373
|
+
@joins[join_alias] = "#{opts[:side]} JOIN #{table} AS #{join_alias} ON #{table_alias}.#{cardfield} = #{join_alias}.#{otherfield}"
|
374
|
+
join_alias
|
375
|
+
end
|
372
376
|
|
373
|
-
|
374
|
-
|
375
|
-
|
377
|
+
def add_revision_join
|
378
|
+
add_join(:rev, :card_revisions, :current_revision_id, :id)
|
379
|
+
end
|
376
380
|
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
381
|
+
def field name
|
382
|
+
@fields ||= {}
|
383
|
+
@fields[name] ||= 0
|
384
|
+
@fields[name] += 1
|
385
|
+
"#{ name }:#{ @fields[name] }"
|
386
|
+
end
|
383
387
|
|
384
|
-
|
385
|
-
|
386
|
-
|
388
|
+
def field_root key
|
389
|
+
key.to_s.gsub /\:\d+/, ''
|
390
|
+
end
|
387
391
|
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
392
|
+
def subcondition(val, args={})
|
393
|
+
args = { :return=>:condition, :_parent=>self }.merge(args)
|
394
|
+
cardspec = CardSpec.build( args )
|
395
|
+
merge field(:cond) => cardspec.merge(val)
|
396
|
+
self.joins.merge! cardspec.joins
|
397
|
+
self.sql.relevance_fields += cardspec.sql.relevance_fields
|
398
|
+
end
|
395
399
|
|
396
400
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
+
def revision_spec(field, linkfield, val)
|
402
|
+
card_select = CardSpec.build(:_parent=>self, :return=>'id').merge(val).to_sql
|
403
|
+
add_join :ed, "(select distinct #{field} from card_revisions where #{linkfield} in #{card_select})", :id, field
|
404
|
+
end
|
401
405
|
|
402
406
|
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
407
|
+
def subspec(spec, additions={ :return=>'id'}, negate=false)
|
408
|
+
additions = additions.merge(:_parent=>self)
|
409
|
+
operator = negate ? 'not in' : 'in'
|
410
|
+
ValueSpec.new([operator,CardSpec.build(additions).merge(spec)], self)
|
411
|
+
end
|
408
412
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
413
|
+
def id_or_subspec spec
|
414
|
+
id = case spec
|
415
|
+
when Integer ; spec
|
416
|
+
when String ; Card.fetch_id(spec)
|
417
|
+
end
|
418
|
+
id or subspec spec
|
419
|
+
end
|
416
420
|
|
417
|
-
|
418
|
-
|
419
|
-
|
421
|
+
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
422
|
+
# SQL GENERATION - translate merged hash into complete SQL statement.
|
423
|
+
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
420
424
|
|
421
425
|
|
422
|
-
|
423
|
-
|
426
|
+
def to_sql *args
|
427
|
+
sql.conditions << basic_conditions
|
424
428
|
|
425
|
-
|
429
|
+
return "(" + sql.conditions.last + ")" if @mods[:return]=='condition'
|
426
430
|
|
427
|
-
|
428
|
-
|
429
|
-
|
431
|
+
if pconds = permission_conditions
|
432
|
+
sql.conditions << pconds
|
433
|
+
end
|
430
434
|
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
+
sql.fields.unshift fields_to_sql
|
436
|
+
sql.order = sort_to_sql # has side effects!
|
437
|
+
sql.tables = "cards #{table_alias}"
|
438
|
+
sql.joins += @joins.values
|
435
439
|
|
436
|
-
|
440
|
+
sql.conditions << "#{table_alias}.trash is false"
|
437
441
|
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
442
|
+
unless @parent or @mods[:return]=='count'
|
443
|
+
sql.group = "GROUP BY #{safe_sql(@mods[:group])}" if !@mods[:group].blank?
|
444
|
+
if @mods[:limit].to_i > 0
|
445
|
+
sql.limit = "LIMIT #{ @mods[:limit ].to_i }"
|
446
|
+
sql.offset = "OFFSET #{ @mods[:offset].to_i }" if !@mods[:offset].blank?
|
447
|
+
end
|
443
448
|
end
|
444
|
-
end
|
445
449
|
|
446
|
-
|
447
|
-
|
450
|
+
sql.to_s
|
451
|
+
end
|
448
452
|
|
449
|
-
|
450
|
-
|
451
|
-
|
453
|
+
def basic_conditions
|
454
|
+
@spec.map { |key, val| val.to_sql field_root(key) }.join " #{ current_conjunction } "
|
455
|
+
end
|
452
456
|
|
453
|
-
|
454
|
-
|
455
|
-
|
457
|
+
def current_conjunction
|
458
|
+
@mods[:conj].blank? ? :and : @mods[:conj]
|
459
|
+
end
|
456
460
|
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
461
|
+
def permission_conditions
|
462
|
+
unless Account.always_ok? #or ( Card::Query.root_perms_only && !root? )
|
463
|
+
read_rules = Account.as_card.read_rules
|
464
|
+
read_rule_list = read_rules.nil? ? 1 : read_rules.join(',')
|
465
|
+
"(#{table_alias}.read_rule_id IN (#{ read_rule_list }))"
|
466
|
+
end
|
467
|
+
end
|
464
468
|
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
469
|
+
def fields_to_sql
|
470
|
+
field = @mods[:return]
|
471
|
+
case (field.blank? ? :card : field.to_sym)
|
472
|
+
when :raw; "#{table_alias}.*"
|
473
|
+
when :card; "#{table_alias}.name"
|
474
|
+
when :count; "coalesce(count(*),0) as count"
|
475
|
+
when :content
|
476
|
+
join_alias = add_revision_join
|
477
|
+
"#{join_alias}.content"
|
478
|
+
else
|
479
|
+
ATTRIBUTES[field.to_sym]==:basic ? "#{table_alias}.#{field}" : safe_sql(field)
|
480
|
+
end
|
476
481
|
end
|
477
|
-
end
|
478
482
|
|
479
|
-
|
480
|
-
|
483
|
+
def sort_to_sql
|
484
|
+
#fail "order_key = #{@mods[:sort]}, class = #{order_key.class}"
|
481
485
|
|
482
|
-
|
483
|
-
|
486
|
+
return nil if @parent or @mods[:return]=='count' #FIXME - extend to all root-only clauses
|
487
|
+
order_key ||= @mods[:sort].blank? ? "update" : @mods[:sort]
|
484
488
|
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
489
|
+
order_directives = [order_key].flatten.map do |key|
|
490
|
+
dir = @mods[:dir].blank? ? (DEFAULT_ORDER_DIRS[key.to_sym]||'asc') : safe_sql(@mods[:dir]) #wonky
|
491
|
+
sort_field key, @mods[:sort_as], dir
|
492
|
+
end.join ', '
|
493
|
+
"ORDER BY #{order_directives}"
|
490
494
|
|
491
|
-
|
495
|
+
end
|
492
496
|
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
497
|
+
def sort_field key, as, dir
|
498
|
+
order_field = case key
|
499
|
+
when "id"; "#{table_alias}.id"
|
500
|
+
when "update"; "#{table_alias}.updated_at"
|
501
|
+
when "create"; "#{table_alias}.created_at"
|
502
|
+
when /^(name|alpha)$/; "LOWER( #{table_alias}.key )"
|
503
|
+
when 'content'
|
504
|
+
join_alias = add_revision_join
|
505
|
+
"lower(#{join_alias}.content)"
|
506
|
+
when "relevance"
|
507
|
+
if !sql.relevance_fields.empty?
|
508
|
+
sql.fields << sql.relevance_fields
|
509
|
+
"name_rank desc, content_rank"
|
510
|
+
else
|
511
|
+
"#{table_alias}.updated_at"
|
512
|
+
end
|
506
513
|
else
|
507
|
-
|
514
|
+
safe_sql(key)
|
508
515
|
end
|
509
|
-
|
510
|
-
|
511
|
-
end
|
512
|
-
order_field = "CAST(#{order_field} AS #{cast_type(as)})" if as
|
513
|
-
"#{order_field} #{dir}"
|
516
|
+
order_field = "CAST(#{order_field} AS #{cast_type(as)})" if as
|
517
|
+
"#{order_field} #{dir}"
|
514
518
|
|
519
|
+
end
|
515
520
|
end
|
516
521
|
end
|
517
522
|
end
|