wagn 1.12.11 → 1.12.12
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.
- 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
|