card 1.96.1 → 1.96.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/config/locales/de.yml +18 -23
  4. data/config/locales/en.yml +16 -21
  5. data/config/locales/es.yml +16 -21
  6. data/lib/card.rb +1 -3
  7. data/lib/card/codename.rb +2 -1
  8. data/lib/card/error.rb +73 -57
  9. data/lib/card/format.rb +2 -2
  10. data/lib/card/format/error.rb +17 -18
  11. data/lib/card/format/nesting/mode.rb +1 -1
  12. data/lib/card/format/permission.rb +8 -10
  13. data/lib/card/format/render.rb +1 -2
  14. data/lib/card/model/save_helper.rb +20 -12
  15. data/lib/card/query.rb +1 -1
  16. data/lib/card/query/card_query.rb +2 -2
  17. data/lib/card/query/card_query/found_by.rb +43 -0
  18. data/lib/card/query/card_query/match_attributes.rb +72 -0
  19. data/lib/card/query/card_query/relational_attributes.rb +19 -0
  20. data/lib/card/view.rb +0 -1
  21. data/lib/card/view/cache_action.rb +1 -10
  22. data/mod/account/set/right/token.rb +1 -3
  23. data/mod/account/set/self/account_links.rb +9 -12
  24. data/mod/basic_formats/set/all/json.rb +19 -54
  25. data/mod/basic_formats/spec/set/all/json_spec.rb +13 -3
  26. data/mod/basic_formats/spec/shared_context/json_shared_context.rb +3 -8
  27. data/mod/basic_types/set/type/json.rb +13 -1
  28. data/mod/bootstrap/set/abstract/bootswatch_theme/html_views.rb +0 -1
  29. data/mod/bootstrap/set/all/bootstrap/helper.rb +7 -0
  30. data/mod/bootstrap/set/self/script_bootstrap.rb +1 -2
  31. data/mod/core/set/all/content.rb +4 -6
  32. data/mod/core/set/all/export.rb +39 -15
  33. data/mod/core/set/all/fetch.rb +9 -0
  34. data/mod/core/set/all/permissions.rb +0 -4
  35. data/mod/core/set/all/phases.rb +1 -1
  36. data/mod/core/set/all/subcards.rb +1 -0
  37. data/mod/core/set/all/trash.rb +6 -2
  38. data/mod/core/set/all/type.rb +1 -1
  39. data/mod/core/set/all/utils.rb +13 -1
  40. data/mod/core/spec/set/all/fetch_spec.rb +17 -4
  41. data/mod/history/set/all/history.rb +34 -57
  42. data/mod/history/set/all/{act_view.rb → history/act_listing.rb} +0 -13
  43. data/mod/history/set/all/history/actions.rb +119 -0
  44. data/mod/history/set/all/history/acts.rb +12 -0
  45. data/mod/history/set/all/history/events.rb +94 -0
  46. data/mod/history/set/all/history/last.rb +97 -0
  47. data/mod/history/set/all/history/revision.rb +54 -0
  48. data/mod/history/set/all/history/selected.rb +64 -0
  49. data/mod/history/set/all/history/views.rb +34 -0
  50. data/mod/history/spec/set/all/history/views_spec.rb +29 -0
  51. data/mod/item/spec/set/all/bar_spec.rb +2 -2
  52. data/mod/machines/file/all_script_machine_output/file.js +66 -30621
  53. data/mod/machines/file/all_style_machine_output/file.css +2 -2
  54. data/mod/machines/file/script_html5shiv_printshiv_machine_output/file.js +1 -1
  55. data/mod/machines/lib/javascript/decko.js.coffee +1 -1
  56. data/mod/pointer/lib/javascript/script_pointer_config.js.coffee +1 -1
  57. data/mod/pointer/set/abstract/01_paging.rb +1 -0
  58. data/mod/pointer/set/abstract/02_pointer/html_views.rb +2 -2
  59. data/mod/pointer/set/abstract/02_pointer/html_views/checkbox_input.haml +2 -2
  60. data/mod/pointer/set/abstract/02_pointer/html_views/filter/filter_items.haml +1 -1
  61. data/mod/pointer/set/abstract/02_pointer/html_views/radio_input.haml +4 -4
  62. data/mod/pointer/set/abstract/02_pointer/options_api.rb +25 -1
  63. data/mod/pointer/set/abstract/02_pointer/other_views.rb +4 -0
  64. data/mod/pointer/spec/set/abstract/pointer/options_api_spec.rb +35 -0
  65. data/mod/search/set/abstract/search.rb +4 -114
  66. data/mod/search/set/abstract/search/views.rb +156 -0
  67. data/mod/search/set/abstract/wql_search.rb +10 -0
  68. data/mod/search/set/self/search.rb +3 -10
  69. data/mod/search/set/type/search_type.rb +5 -1
  70. data/mod/search/template/abstract/03_filter/filter_form.haml +1 -1
  71. data/mod/standard/set/all/error.rb +24 -192
  72. data/mod/standard/set/all/rich_html/content.rb +1 -1
  73. data/mod/standard/set/all/rich_html/error.rb +177 -0
  74. data/mod/standard/{template/all → set/all/rich_html}/error/not_found.haml +0 -0
  75. data/mod/standard/{template/all → set/all/rich_html}/error/server_error.haml +0 -0
  76. data/mod/standard/set/all/rich_html/form.rb +9 -9
  77. data/mod/standard/set/all/rich_html/menu.rb +14 -5
  78. data/mod/standard/spec/content/chunk/include_spec.rb +1 -2
  79. data/mod/standard/spec/set/type/search_type_spec.rb +1 -1
  80. data/mod/utility/set/abstract/bs_badge/bs_badge.haml +1 -1
  81. data/mod/utility/set/abstract/media.rb +3 -1
  82. metadata +20 -16
  83. data/lib/card/query/card_query/attribute_helper.rb +0 -74
  84. data/lib/card/query/card_query/special_attributes.rb +0 -49
  85. data/mod/history/set/all/action_view.rb +0 -52
  86. data/mod/history/set/all/actions.rb +0 -185
  87. data/mod/history/set/all/acts.rb +0 -16
  88. data/mod/history/set/all/content_history.rb +0 -180
  89. data/mod/history/spec/set/all/act_view_spec.rb +0 -16
  90. data/mod/history/spec/set/all/action_view_spec.rb +0 -10
  91. data/mod/history/spec/set/all/history_spec.rb +0 -11
@@ -78,7 +78,7 @@ class Card
78
78
  # @return [Symbol] viewname
79
79
  def configured_view_in_closed_mode view
80
80
  closed_config = Card::Format.closed[view]
81
- return view if closed_config == true || Card::Format.error_code[view]
81
+ return view if closed_config == true
82
82
  closed_config
83
83
  end
84
84
  end
@@ -2,11 +2,9 @@ class Card
2
2
  class Format
3
3
  module Permission
4
4
  def ok_view view, skip_perms=false
5
- return :too_deep if subformats_nested_too_deeply?
5
+ raise Card::Error::UserError, tr(:too_deep) if subformats_nested_too_deeply?
6
6
  approved_view = check_view view, skip_perms
7
7
  handle_view_denial view, approved_view
8
- assign_view_error_status approved_view
9
-
10
8
  approved_view
11
9
  end
12
10
 
@@ -15,12 +13,6 @@ class Card
15
13
  @denied_view = view
16
14
  end
17
15
 
18
- def assign_view_error_status view
19
- return unless focal?
20
- return unless (error_code = Card::Format.error_code[view])
21
- root.error_status = error_code
22
- end
23
-
24
16
  def check_view view, skip_perms
25
17
  case
26
18
  when skip_perms then view
@@ -47,12 +39,17 @@ class Card
47
39
 
48
40
  def permitted_view view
49
41
  if (@denied_task = task_denied_for_view view)
50
- Card::Format.denial[view] || :denial
42
+ deny_view view
51
43
  else
52
44
  view
53
45
  end
54
46
  end
55
47
 
48
+ def deny_view view
49
+ root.error_status = 403 if focal? && voo.root?
50
+ Card::Format.denial[view] || :denial
51
+ end
52
+
56
53
  def task_denied_for_view view
57
54
  perms_required = Card::Format.perms[view] || :read
58
55
  if perms_required.is_a? Proc
@@ -64,6 +61,7 @@ class Card
64
61
 
65
62
  def view_for_unknown _view
66
63
  # note: overridden in HTML
64
+ root.error_status = 404 if focal?
67
65
  focal? ? :not_found : :missing
68
66
  end
69
67
 
@@ -111,8 +111,7 @@ class Card
111
111
 
112
112
  def view_method view
113
113
  unless supports_view? view
114
- voo.unsupported_view = view
115
- view = :unsupported_view
114
+ raise Card::Error::UserError, unsupported_view_error_message(view)
116
115
  end
117
116
  method view_method_name(view)
118
117
  end
@@ -60,8 +60,15 @@ class Card
60
60
  # ensure_card "Under Score", type: :pointer # => changes the type to pointer
61
61
  # # but not the name
62
62
  def ensure_card name_or_args, content_or_args=nil
63
- name = name_or_args.is_a?(Hash) ? name_or_args[:name] : name_or_args
64
- args = standardize_ensure_args name_or_args, content_or_args
63
+ name, args = standardize_ensure_args name_or_args, content_or_args
64
+ ensure_card_simplified name, args
65
+ end
66
+
67
+ # like ensure_card but derives codename from name if no codename is given.
68
+ # The derived codename is all lower case with underscores; "*" and ":" are removed
69
+ def ensure_code_card name_or_args, content_or_args=nil
70
+ name, args = standardize_ensure_args name_or_args, content_or_args
71
+ args[:codename] = name.downcase.tr(" ", "_").tr(":*", "") unless args[:codename]
65
72
  ensure_card_simplified name, args
66
73
  end
67
74
 
@@ -71,8 +78,7 @@ class Card
71
78
  # For example if a card with name "under_score" exists
72
79
  # then `ensure_card "Under Score"` renames it to "Under Score"
73
80
  def ensure_card! name_or_args, content_or_args=nil
74
- name = name_or_args.is_a?(Hash) ? args[:name] : name_or_args
75
- args = standardize_ensure_args name_or_args, content_or_args
81
+ name, args = standardize_ensure_args name_or_args, content_or_args
76
82
  ensure_card_simplified name, add_name(name, args)
77
83
  end
78
84
 
@@ -221,11 +227,13 @@ class Card
221
227
  end
222
228
 
223
229
  def standardize_ensure_args name_or_args, content_or_args
224
- if name_or_args.is_a?(Hash)
225
- name_or_args
226
- else
227
- hashify content_or_args, :content
228
- end
230
+ name = name_or_args.is_a?(Hash) ? name_or_args[:name] : name_or_args
231
+ args = if name_or_args.is_a?(Hash)
232
+ name_or_args
233
+ else
234
+ hashify content_or_args, :content
235
+ end
236
+ [name, args]
229
237
  end
230
238
 
231
239
  def standardize_update_args name_or_args, content_or_args
@@ -244,10 +252,10 @@ class Card
244
252
  end
245
253
 
246
254
  def add_name name, content_or_args
247
- if content_or_args.is_a?(String)
248
- { content: content_or_args, name: name }
249
- else
255
+ if content_or_args.is_a?(Hash)
250
256
  content_or_args.reverse_merge name: name
257
+ else
258
+ { content: content_or_args, name: name }
251
259
  end
252
260
  end
253
261
 
@@ -54,7 +54,7 @@ class Card
54
54
  member_of member
55
55
 
56
56
  found_by not sort match name_match complete
57
- junction_complete extension_type],
57
+ extension_type],
58
58
 
59
59
  plus_relational: %w[plus left_plus right_plus],
60
60
  conjunction: %w[and or all any],
@@ -4,10 +4,10 @@ class Card
4
4
  class CardQuery < AbstractQuery
5
5
  include Clause
6
6
  include Run
7
- include SpecialAttributes
7
+ include MatchAttributes
8
8
  include RelationalAttributes
9
9
  include ReferenceAttributes
10
- include AttributeHelper
10
+ include FoundBy
11
11
  include Interpretation
12
12
  include Normalization
13
13
  include Sorting
@@ -0,0 +1,43 @@
1
+ class Card
2
+ module Query
3
+ class CardQuery
4
+ # nest independent query
5
+ module FoundBy
6
+ def found_by val
7
+ found_by_cards(val).compact.each do |card|
8
+ subquery found_by_subquery(card)
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def found_by_subquery card
15
+ found_by_statement(card).merge(fasten: :direct, context: card.name)
16
+ end
17
+
18
+ def found_by_statement card
19
+ card&.try(:wql_hash) || invalid_found_by_card!(card)
20
+ end
21
+
22
+ def invalid_found_by_card! card
23
+ raise Card::Error::BadQuery, '"found_by" value must be valid Search, ' \
24
+ "but #{card.name} is a #{card.type_name}"
25
+ end
26
+
27
+ def found_by_cards val
28
+ if val.is_a? Hash
29
+ Query.run val
30
+ else
31
+ fetch_found_by_cards val
32
+ end
33
+ end
34
+
35
+ def fetch_found_by_cards val
36
+ Array.wrap(val).map do |v|
37
+ Card.fetch v.to_name.absolute(context), new: {}
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,72 @@
1
+ class Card
2
+ module Query
3
+ class CardQuery
4
+ # Implements the match attributes that match always against content and/or name.
5
+ # Currently that's different from the match operator that can be restricted to
6
+ # names or content.
7
+ # Example: { match: "name or content" } vs. { name: ["match", "a name"] }
8
+ # TODO: unify handling for both using full text indexing
9
+ module MatchAttributes
10
+ # match term anywhere in name or content
11
+ def match val
12
+ val.gsub!(/[^#{Card::Name::OK4KEY_RE}]+/, " ")
13
+ return nil if val.strip.empty?
14
+ val.gsub!("*", '\\\\\\\\*')
15
+ val_list = val.split(/\s+/).map do |v|
16
+ name_or_content_match v
17
+ end
18
+ add_condition and_join(val_list)
19
+ end
20
+
21
+ # match names beginning with term
22
+ def complete val
23
+ add_condition key_like("#{val.to_name.key}%", val =~ /\+/)
24
+ end
25
+
26
+ # match term anywhere in name
27
+ # DEPRECATE - move handling to name: ["match", val]
28
+ def name_match val
29
+ add_condition name_like(val)
30
+ end
31
+
32
+ private
33
+
34
+ def name_or_content_match val
35
+ cxn = connection
36
+ or_join([name_like(val), content_match(val, cxn)])
37
+ end
38
+
39
+ def name_like val
40
+ key_like "%#{val.to_name.key}%"
41
+ end
42
+
43
+ def content_match val, cxn
44
+ field_match "#{table_alias}.db_content", val, cxn
45
+ end
46
+
47
+ def field_match field, val, cxn
48
+ %(#{field} #{cxn.match quote("[[:<:]]#{val}[[:>:]]")})
49
+ end
50
+
51
+ # TODO: move sql to SqlStatement
52
+ def key_like pattern, no_junction=false
53
+ conds = ["#{table_alias}.key LIKE #{quote pattern}"]
54
+ conds << "#{table_alias}.right_id is null" if no_junction
55
+ # FIXME: -- this should really be more nuanced --
56
+ # it includes all descendants after one plus
57
+ conds.join " AND "
58
+ end
59
+
60
+ # TODO: use standard conjunction handling
61
+
62
+ def or_join conditions
63
+ "(#{Array(conditions).join ' OR '})"
64
+ end
65
+
66
+ def and_join conditions
67
+ "(#{Array(conditions).join ' AND '})"
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -70,6 +70,25 @@ class Card
70
70
  def plus val
71
71
  any(left_plus: val, right_plus: val.deep_clone)
72
72
  end
73
+
74
+ private
75
+
76
+ def tie_action action, val
77
+ tie :action, { action => val }, to: :card_id
78
+ end
79
+
80
+ def tie_act action, val
81
+ tie :act, { action => val }, to: :actor_id
82
+ end
83
+
84
+ def junction val, side, field
85
+ tie :card, junction_val(val, side), to: field
86
+ end
87
+
88
+ def junction_val val, side
89
+ part_clause, junction_clause = val.is_a?(Array) ? val : [val, {}]
90
+ clause_to_hash(junction_clause).merge side => part_clause
91
+ end
73
92
  end
74
93
  end
75
94
  end
@@ -14,7 +14,6 @@ class Card
14
14
  extend Cache::ClassMethods
15
15
 
16
16
  attr_reader :format, :parent, :card
17
- attr_accessor :unsupported_view
18
17
 
19
18
  # @return [Symbol]
20
19
  def self.canonicalize view
@@ -64,16 +64,7 @@ class Card
64
64
 
65
65
  # @return [Symbol]
66
66
  def active_cache_action
67
- validate_active_cache_action do
68
- active_cache_ok? ? active_cache_action_from_setting : :stub
69
- end
70
- end
71
-
72
- # catch recursive views and invalid stubs
73
- def validate_active_cache_action
74
- ok_view == :too_deep ? :yield : yield
75
- # FIXME: this allows "too deep" error to be cached inside another view.
76
- # may need a "raise" cache action?
67
+ active_cache_ok? ? active_cache_action_from_setting : :stub
77
68
  end
78
69
 
79
70
  # @return [True/False]
@@ -49,8 +49,6 @@ def term_from_string string
49
49
  return "permanent" if string == "none"
50
50
  re_match = /^(\d+)[\.\s]*(#{DURATIONS})s?$/.match(string)
51
51
  number, unit = re_match.captures if re_match
52
- unless unit
53
- raise Card::Error::BadContent, tr(:exception_bad_expiration, example: '2 days')
54
- end
52
+ raise Card::Open::Error, tr(:exception_bad_expiration, example: '2 days') unless unit
55
53
  number.to_i.send unit
56
54
  end
@@ -1,7 +1,15 @@
1
1
 
2
2
  format :html do
3
+ view :core, cache: :never do
4
+ status_class = Auth.signed_in? ? "logged-in" : "logged-out"
5
+ wrap_with :span, id: "logging", class: status_class do
6
+ item_links.join " "
7
+ end
8
+ end
9
+
3
10
  def item_links _args=nil
4
- %i[my_card invite sign_out sign_up sign_in].map do |link_view|
11
+ # removed invite for now
12
+ %i[my_card sign_out sign_up sign_in].map do |link_view|
5
13
  render link_view
6
14
  end.compact
7
15
  end
@@ -44,21 +52,10 @@ format :html do
44
52
  I18n.t(purpose, scope: "mod.account.set.self.account_links")
45
53
  end
46
54
 
47
- view :raw do
48
- item_links.join " "
49
- end
50
-
51
55
  def nav_link_class type
52
56
  "nav-link #{classy(type)}"
53
57
  end
54
58
 
55
- view :core, cache: :never do
56
- status_class = Auth.signed_in? ? "logged-in" : "logged-out"
57
- wrap_with :span, id: "logging", class: status_class do
58
- render_raw
59
- end
60
- end
61
-
62
59
  def show_signup_link?
63
60
  !Auth.signed_in? && Card.new(type_id: Card::SignupID).ok?(:create)
64
61
  end
@@ -4,14 +4,12 @@ format :json do
4
4
  uniq_nested_cards
5
5
  end
6
6
 
7
- AUTOCOMPLETE_LIMIT = 8 # number of name suggestions for autocomplete text fields
8
-
9
7
  def default_nest_view
10
8
  :atom
11
9
  end
12
10
 
13
11
  def default_item_view
14
- params[:item] || :atom
12
+ params[:item] || :name
15
13
  end
16
14
 
17
15
  def max_depth
@@ -29,30 +27,6 @@ format :json do
29
27
  JSON.send method, raw
30
28
  end
31
29
 
32
- # TODO: design better autocomplete API
33
- # view :name_complete, cache: :never do
34
- # name_search
35
- # end
36
-
37
- view :name_complete, cache: :never do
38
- name_search query_attribute: :junction_complete
39
- end
40
-
41
- view :name_match, cache: :never do
42
- starts_with = name_search query_attribute: :junction_complete
43
- remaining_slots = AUTOCOMPLETE_LIMIT - starts_with.size
44
- return starts_with if remaining_slots.zero?
45
- starts_with + name_search(query_attribute: :name_match,
46
- limit: remaining_slots)
47
- end
48
-
49
- def name_search query_attribute: :complete, limit: AUTOCOMPLETE_LIMIT
50
- card.search limit: limit,
51
- sort: "name",
52
- return: "name",
53
- query_attribute => params[:term]
54
- end
55
-
56
30
  view :status, tags: :unknown_ok, perms: :none, cache: :never do
57
31
  status = card.state
58
32
  hash = { key: card.key,
@@ -68,36 +42,31 @@ format :json do
68
42
  card: _render_atom }
69
43
  end
70
44
 
71
- view :content do
72
- render_page
73
- end
74
-
75
45
  view :core do
76
- { card.name => card.content }
46
+ card.known? ? render_content : nil
47
+ end
48
+ view :content do
49
+ card.content
77
50
  end
78
51
 
79
52
  view :nucleus, cache: :never do
80
- {
81
- id: card.id,
82
- name: card.name,
83
- url: path(format: :json),
84
- html_url: path
85
- }
53
+ h = { id: card.id,
54
+ name: card.name,
55
+ type: card.type_name,
56
+ url: path(format: :json) }
57
+ h[:codename] = card.codename if card.codename
58
+ h
86
59
  end
87
60
 
88
- view :atom, cache: :never do
61
+ # TODO: add simple values for fields
62
+ view :atom, cache: :never, tags: :unknown_ok do
89
63
  h = _render_nucleus
90
- h[:type] = card.type_name
91
- h[:type_url] = path mark: card.type_name, format: :json
92
- h[:atom_url] = path format: :json, view: :atom
93
- h[:nucleus_url] = path format: :json, view: :nucleus
94
- h[:content] = card.db_content unless card.structure
95
- h[:codename] = card.codename if card.codename
64
+ h[:content] = render_content if card.known? && !card.structure
96
65
  h
97
66
  end
98
67
 
99
68
  view :items, cache: :never do
100
- listing item_cards
69
+ listing item_cards, view: :atom
101
70
  end
102
71
 
103
72
  view :links, cache: :never do
@@ -112,14 +81,16 @@ format :json do
112
81
 
113
82
  view :ancestors, cache: :never do
114
83
  card.name.ancestors.map do |name|
115
- nest name
84
+ nest name, view: :nucleus
116
85
  end
117
86
  end
118
87
 
119
88
  view :molecule, cache: :never do
120
89
  _render_atom.merge items: _render_items,
121
90
  links: _render_links,
122
- ancestors: _render_ancestors
91
+ ancestors: _render_ancestors,
92
+ html_url: path,
93
+ type: nest(card.type_card, view: :nucleus)
123
94
 
124
95
  end
125
96
 
@@ -137,12 +108,6 @@ format :json do
137
108
  }
138
109
  end
139
110
 
140
- view :export_item do
141
- item = { name: card.name, type: card.type_name, content: card.content }
142
- item[:codename] = card.codename if card.codename
143
- item
144
- end
145
-
146
111
  view :essentials do
147
112
  if voo.show? :marks
148
113
  render_marks.merge(essentials)