card 1.20.0 → 1.20.1
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 +4 -4
- data/VERSION +1 -1
- data/card.gemspec +1 -1
- data/lib/card.rb +7 -3
- data/lib/card/act_manager/stage_director.rb +22 -10
- data/lib/card/cache/persistent.rb +15 -9
- data/lib/card/format/nest.rb +12 -5
- data/lib/card/format/nest/fetch.rb +2 -1
- data/lib/card/format/render.rb +5 -2
- data/lib/card/set/event.rb +7 -1
- data/lib/card/subcards.rb +3 -3
- data/lib/card/view/cache.rb +1 -1
- data/lib/card/view/fetch.rb +17 -5
- data/lib/card/view/options.rb +52 -26
- data/lib/cardio.rb +2 -1
- data/mod/account/spec/set/right/account_spec.rb +17 -3
- data/mod/admin/set/self/admin.rb +0 -2
- data/mod/admin/spec/set/self/admin_spec.rb +14 -11
- data/mod/bootstrap/set/all/bootstrap/helper.rb +27 -29
- data/mod/carrierwave/set/type/file.rb +1 -1
- data/mod/carrierwave/set/type/image.rb +18 -6
- data/mod/core/set/all/collection.rb +33 -7
- data/mod/core/set/all/fetch.rb +16 -3
- data/mod/core/set/all/permissions.rb +6 -12
- data/mod/core/set/all/trash.rb +3 -1
- data/mod/core/spec/set/all/collection_spec.rb +9 -8
- data/mod/email/set/all/notify.rb +27 -17
- data/mod/email/set/right/follow.rb +49 -36
- data/mod/email/set/type/email_template.rb +25 -69
- data/mod/email/set/type/email_template/email_config.rb +63 -0
- data/mod/email/set/type_plus_right/user/follow.rb +3 -3
- data/mod/machines/lib/stylesheets/style_cards.scss +292 -0
- data/mod/pointer/set/abstract/01_pointer.rb +8 -8
- data/mod/pointer/set/abstract/01_pointer/edit.rb +6 -2
- data/mod/prosemirror_editor/lib/javascript/script_prosemirror.js +12283 -11605
- data/mod/prosemirror_editor/lib/javascript/script_prosemirror_config.js.coffee +1 -1
- data/mod/standard/set/abstract/01_search_params.rb +1 -1
- data/mod/standard/set/abstract/search/paging.rb +4 -4
- data/mod/standard/set/all/comment.rb +67 -47
- data/mod/standard/set/all/links.rb +2 -2
- data/mod/standard/set/all/rich_html/content.rb +1 -1
- data/mod/standard/set/all/rich_html/editing.rb +3 -2
- data/mod/standard/set/all/rich_html/form.rb +21 -12
- data/mod/standard/set/all/rich_html/header.rb +9 -0
- data/mod/standard/set/all/rich_html/menu.rb +16 -12
- data/mod/standard/set/all/rich_html/toolbar.rb +140 -130
- data/mod/standard/set/all/rich_html/wrapper.rb +11 -1
- data/mod/standard/set/rstar/rules_editor.rb +2 -34
- data/mod/standard/set/self/search.rb +1 -1
- data/mod/standard/set/type/set.rb +4 -4
- data/mod/standard/spec/set/type/email_template/email_config_spec.rb +218 -0
- data/mod/standard/spec/set/type/email_template_spec.rb +3 -185
- data/spec/lib/card/cache_spec.rb +0 -1
- data/spec/lib/card/format/render_spec.rb +19 -0
- data/spec/lib/card/stage_director_spec.rb +1 -1
- data/tmpsets/set/mod001-core/all/actify.rb +5 -6
- data/tmpsets/set/mod001-core/all/fetch.rb +14 -12
- data/tmpsets/set/mod001-core/all/name.rb +1 -1
- data/tmpsets/set/mod001-core/all/permissions.rb +12 -22
- data/tmpsets/set/mod001-core/all/tracked_attributes.rb +76 -0
- data/tmpsets/set/mod001-core/all/utils.rb +40 -3
- data/tmpsets/set/mod002-history/all/history.rb +1 -2
- data/tmpsets/set/mod008-solid_cache/abstract/solid_cache.rb +1 -1
- data/tmpsets/set/mod013-carrierwave/abstract/attachment.rb +282 -0
- data/tmpsets/set/mod013-carrierwave/type/file.rb +155 -0
- data/tmpsets/set/mod013-carrierwave/type/image.rb +96 -0
- data/tmpsets/set/mod014-admin/self/admin.rb +113 -0
- data/tmpsets/set/mod014-admin/self/admin_info.rb +110 -0
- data/tmpsets/set/mod014-admin/self/version.rb +15 -0
- data/tmpsets/set/mod015-developer/all/event_viz.rb +59 -0
- data/tmpsets/set/mod015-developer/all/view_viz.rb +30 -0
- data/tmpsets/set/mod015-developer/right/debug.rb +96 -0
- metadata +15 -2
@@ -170,9 +170,23 @@ describe Card::Set::Right::Account do
|
|
170
170
|
end
|
171
171
|
|
172
172
|
describe "#send_change_notice" do
|
173
|
-
|
174
|
-
|
175
|
-
|
173
|
+
subject(:mail) do
|
174
|
+
Card[:follower_notification_email].format.render_mail(
|
175
|
+
context: Card.fetch("A", look_in_trash: true),
|
176
|
+
to: "joe@user.com",
|
177
|
+
follower: Card["Joe User"],
|
178
|
+
followed_set: Card[:all],
|
179
|
+
follow_option: Card[:always]
|
180
|
+
)
|
181
|
+
end
|
182
|
+
|
183
|
+
it "works for deleted card" do
|
184
|
+
delete "A"
|
185
|
+
expect(mail.subject).to eq 'Joe User deleted "A"'
|
186
|
+
end
|
187
|
+
|
188
|
+
it "sends multipart email" do
|
189
|
+
expect(mail.content_type).to include("multipart/alternative")
|
176
190
|
end
|
177
191
|
|
178
192
|
context "denied access" do
|
data/mod/admin/set/self/admin.rb
CHANGED
@@ -8,8 +8,6 @@ event :admin_tasks, :initialize, on: :update do
|
|
8
8
|
case task.to_sym
|
9
9
|
when :clear_cache then Card::Cache.reset_all
|
10
10
|
when :repair_references then Card::Reference.repair_all
|
11
|
-
# when :clear_view_cache then Card::View.reset
|
12
|
-
when :delete_old_revisions then Card::Action.delete_old
|
13
11
|
when :repair_permissions then Card.repair_all_permissions
|
14
12
|
when :clear_solid_cache then Card.clear_solid_cache
|
15
13
|
when :clear_machine_cache then Card.reset_all_machines
|
@@ -23,17 +23,20 @@ describe Card::Set::Self::Admin do
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
26
|
+
# NOTE: I removed this functionality for now, because I don't think we
|
27
|
+
# should have web access to admin functions that can incur actual data loss.
|
28
|
+
|
29
|
+
# it "triggers deleting old revisions (with right params)" do
|
30
|
+
# Card::Auth.as_bot do
|
31
|
+
# a = Card["A"]
|
32
|
+
# a.update_attributes! content: "a new day"
|
33
|
+
# a.update_attributes! content: "another day"
|
34
|
+
# expect(a.actions.count).to eq(3)
|
35
|
+
# Card::Env.params[:task] = :delete_old_revisions
|
36
|
+
# @admin.update_attributes({})
|
37
|
+
# expect(a.actions.count).to eq(1)
|
38
|
+
# end
|
39
|
+
# end
|
37
40
|
|
38
41
|
# it 'is trigger reference repair' do
|
39
42
|
# Card::Auth.as_bot do
|
@@ -1,6 +1,7 @@
|
|
1
1
|
format :html do
|
2
2
|
def glyphicon icon_type, extra_class=""
|
3
|
-
wrap_with :span, "",
|
3
|
+
wrap_with :span, "",
|
4
|
+
"aria-hidden" => true,
|
4
5
|
class: "glyphicon glyphicon-#{icon_type} #{extra_class}"
|
5
6
|
end
|
6
7
|
|
@@ -25,50 +26,47 @@ format :html do
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def dropdown_list items, extra_css_class=nil, active=nil
|
28
|
-
|
29
|
+
wrap_with :ul, class: "dropdown-menu #{extra_css_class}", role: "menu" do
|
29
30
|
case items
|
30
31
|
when Array
|
31
|
-
items.map.with_index
|
32
|
-
"<li #{'class=\'active\'' if index == active}>#{item}</li>" if item
|
33
|
-
end.compact.join "\n"
|
32
|
+
items.map.with_index { |item, i| dropdown_list_item item, i, active }
|
34
33
|
when Hash
|
35
|
-
items.map
|
36
|
-
"<li #{'class=\'active\'' if key == active}>#{item}</li>" if item
|
37
|
-
end.compact.join "\n"
|
34
|
+
items.map { |key, item| dropdown_list_item item, key, active }
|
38
35
|
else
|
39
|
-
items
|
40
|
-
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
36
|
+
[items]
|
37
|
+
end.compact.join "\n"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def dropdown_list_item item, active_test, active
|
42
|
+
return unless item
|
43
|
+
"<li #{'class=\'active\'' if active_test == active}>#{item}</li>"
|
46
44
|
end
|
47
45
|
|
48
46
|
def separator
|
49
47
|
'<li role="separator" class="divider"></li>'
|
50
48
|
end
|
51
49
|
|
52
|
-
def split_button
|
53
|
-
items = yield
|
54
|
-
args[:situation] ||= "primary"
|
55
|
-
|
50
|
+
def split_button main_button, active_item
|
56
51
|
wrap_with :div, class: "btn-group" do
|
57
52
|
[
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
"aria-haspopup" => "true", "aria-expanded" => "false") do
|
62
|
-
<<-HTML
|
63
|
-
<span class="caret"></span>
|
64
|
-
<span class="sr-only">Toggle Dropdown</span>
|
65
|
-
HTML
|
66
|
-
end,
|
67
|
-
dropdown_list(items, nil, args[:active_item])
|
53
|
+
main_button,
|
54
|
+
split_button_toggle,
|
55
|
+
dropdown_list(yield, nil, active_item)
|
68
56
|
]
|
69
57
|
end
|
70
58
|
end
|
71
59
|
|
60
|
+
def split_button_toggle
|
61
|
+
button_tag(situation: "primary",
|
62
|
+
class: "dropdown-toggle",
|
63
|
+
"data-toggle" => "dropdown",
|
64
|
+
"aria-haspopup" => "true",
|
65
|
+
"aria-expanded" => "false") do
|
66
|
+
'<span class="caret"></span><span class="sr-only">Toggle Dropdown</span>'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
72
70
|
def list_group content_or_options=nil, options={}
|
73
71
|
options = content_or_options if block_given?
|
74
72
|
content = block_given? ? yield : content_or_options
|
@@ -93,7 +93,7 @@ format :html do
|
|
93
93
|
""
|
94
94
|
end
|
95
95
|
|
96
|
-
view :preview_editor, tags: :unknown_ok do |args|
|
96
|
+
view :preview_editor, tags: :unknown_ok, cache: :never do |args|
|
97
97
|
cached_upload_card_name = Card::Env.params[:attachment_upload]
|
98
98
|
cached_upload_card_name.gsub!(/\[\w+\]$/, "[action_id_of_cached_upload]")
|
99
99
|
<<-HTML
|
@@ -16,10 +16,14 @@ format do
|
|
16
16
|
|
17
17
|
def source_url
|
18
18
|
return card.raw_content if card.web?
|
19
|
+
selected_version.url
|
20
|
+
end
|
21
|
+
|
22
|
+
def selected_version
|
19
23
|
if voo.size == :original
|
20
|
-
card.image
|
24
|
+
card.image
|
21
25
|
else
|
22
|
-
card.image.versions[voo.size.to_sym]
|
26
|
+
card.image.versions[voo.size.to_sym]
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
@@ -34,12 +38,11 @@ format do
|
|
34
38
|
when voo.size then voo.size.to_sym
|
35
39
|
when main? then :large
|
36
40
|
else :medium
|
37
|
-
|
41
|
+
end
|
38
42
|
voo.size = :original if voo.size == :full
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
42
|
-
|
43
46
|
format :html do
|
44
47
|
include File::HtmlFormat
|
45
48
|
|
@@ -54,7 +57,7 @@ format :html do
|
|
54
57
|
end
|
55
58
|
|
56
59
|
def preview
|
57
|
-
return
|
60
|
+
return if card.new_card? && !card.preliminary_upload?
|
58
61
|
voo.size = :medium
|
59
62
|
wrap_with :div, class: "attachment-preview",
|
60
63
|
id: "#{card.attachment.filename}-preview" do
|
@@ -62,7 +65,7 @@ format :html do
|
|
62
65
|
end
|
63
66
|
end
|
64
67
|
|
65
|
-
def show_action_content_toggle?
|
68
|
+
def show_action_content_toggle? _action, _view_type
|
66
69
|
true
|
67
70
|
end
|
68
71
|
|
@@ -87,6 +90,15 @@ format :html do
|
|
87
90
|
end
|
88
91
|
end
|
89
92
|
|
93
|
+
format :email_html do
|
94
|
+
view :core do
|
95
|
+
url_generator = voo.closest_live_option(:inline_attachment_url)
|
96
|
+
path = selected_version.path
|
97
|
+
return _render_source unless url_generator && ::File.exist?(path)
|
98
|
+
image_tag url_generator.call(path)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
90
102
|
format :css do
|
91
103
|
view :core do
|
92
104
|
render_source
|
@@ -79,6 +79,18 @@ def insert_item index, name
|
|
79
79
|
self.content = new_names.join "\n"
|
80
80
|
end
|
81
81
|
|
82
|
+
def add_id id
|
83
|
+
add_item "~#{id}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def drop_id id
|
87
|
+
drop_item "~#{id}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def insert_id index, id
|
91
|
+
insert_item index, "~#{id}"
|
92
|
+
end
|
93
|
+
|
82
94
|
def extended_item_cards context=nil
|
83
95
|
context = (context ? context.cardname : cardname)
|
84
96
|
args = { limit: "" }
|
@@ -118,10 +130,23 @@ def extended_list context=nil
|
|
118
130
|
# a collection
|
119
131
|
end
|
120
132
|
|
133
|
+
def context_card
|
134
|
+
@context_card || self
|
135
|
+
end
|
136
|
+
|
137
|
+
def with_context card
|
138
|
+
old_context = @context_card
|
139
|
+
@context_card = card
|
140
|
+
result = yield
|
141
|
+
@context_card = old_context
|
142
|
+
result
|
143
|
+
end
|
144
|
+
|
121
145
|
def contextual_content context_card, format_args={}, view_args={}
|
122
|
-
|
123
|
-
|
124
|
-
|
146
|
+
view = view_args.delete(:view) || :core
|
147
|
+
with_context context_card do
|
148
|
+
format(format_args).render view, view_args
|
149
|
+
end
|
125
150
|
end
|
126
151
|
|
127
152
|
def each_chunk opts={}
|
@@ -171,7 +196,7 @@ format do
|
|
171
196
|
end
|
172
197
|
|
173
198
|
def voo_items_view
|
174
|
-
return unless
|
199
|
+
return unless voo && (items = voo.items)
|
175
200
|
items[:view]
|
176
201
|
end
|
177
202
|
|
@@ -215,10 +240,11 @@ format do
|
|
215
240
|
end
|
216
241
|
|
217
242
|
def normalized_edit_fields
|
218
|
-
edit_fields.map do |
|
219
|
-
options
|
243
|
+
edit_fields.map do |name_or_card, options|
|
244
|
+
next [name_or_card, options || {}] if name_or_card.is_a?(Card)
|
245
|
+
options ||= Card.fetch_name name_or_card
|
220
246
|
options = { title: options } if options.is_a?(String)
|
221
|
-
[card.cardname.field(
|
247
|
+
[card.cardname.field(name_or_card), options]
|
222
248
|
end
|
223
249
|
end
|
224
250
|
|
data/mod/core/set/all/fetch.rb
CHANGED
@@ -329,19 +329,32 @@ def cache_class_from_type cache_type
|
|
329
329
|
end
|
330
330
|
|
331
331
|
def register_view_cache_key cache_key
|
332
|
+
view_cache_keys cache_key
|
333
|
+
hard_write_view_cache_keys
|
334
|
+
end
|
335
|
+
|
336
|
+
def view_cache_keys new_key=nil
|
332
337
|
@view_cache_keys ||= []
|
333
|
-
@view_cache_keys <<
|
338
|
+
@view_cache_keys << new_key if new_key
|
339
|
+
append_missing_view_cache_keys
|
334
340
|
@view_cache_keys.uniq!
|
335
|
-
|
341
|
+
end
|
342
|
+
|
343
|
+
def append_missing_view_cache_keys
|
344
|
+
return unless Card.cache.hard
|
345
|
+
@view_cache_keys +=
|
346
|
+
(Card.cache.hard.read_attribute(key, :view_cache_keys) || [])
|
336
347
|
end
|
337
348
|
|
338
349
|
def hard_write_view_cache_keys
|
350
|
+
# puts "WRITE VIEW CACHE KEYS (#{name}): #{view_cache_keys}"
|
339
351
|
return unless Card.cache.hard
|
340
352
|
Card.cache.hard.write_attribute key, :view_cache_keys, @view_cache_keys
|
341
353
|
end
|
342
354
|
|
343
355
|
def expire_views
|
344
|
-
|
356
|
+
# puts "EXPIRE VIEW CACHE (#{name}): #{view_cache_keys}"
|
357
|
+
return unless view_cache_keys.present?
|
345
358
|
Array.wrap(@view_cache_keys).each do |view_cache_key|
|
346
359
|
Card::View.cache.delete view_cache_key
|
347
360
|
end
|
@@ -224,26 +224,20 @@ def add_to_read_rule_update_queue updates
|
|
224
224
|
end
|
225
225
|
|
226
226
|
event :check_permissions, :validate do
|
227
|
-
task =
|
228
|
-
if @action != :delete && comment # will be obviated by new comment handling
|
229
|
-
:comment
|
230
|
-
else
|
231
|
-
@action
|
232
|
-
end
|
233
227
|
track_permission_errors do
|
234
|
-
ok?
|
228
|
+
ok? action_for_permission_check
|
235
229
|
end
|
236
230
|
end
|
237
231
|
|
232
|
+
def action_for_permission_check
|
233
|
+
commenting? ? :comment : @action
|
234
|
+
end
|
235
|
+
|
238
236
|
def track_permission_errors
|
239
237
|
@permission_errors = []
|
240
238
|
result = yield
|
241
|
-
|
242
|
-
@permission_errors.each do |message|
|
243
|
-
errors.add :permission_denied, message
|
244
|
-
end
|
239
|
+
@permission_errors.each { |msg| errors.add :permission_denied, msg }
|
245
240
|
@permission_errors = nil
|
246
|
-
|
247
241
|
result
|
248
242
|
end
|
249
243
|
|
data/mod/core/set/all/trash.rb
CHANGED
@@ -15,6 +15,7 @@ module ClassMethods
|
|
15
15
|
Card.delete_trashed_files
|
16
16
|
Card.where(trash: true).delete_all
|
17
17
|
Card::Action.delete_cardless
|
18
|
+
Card::Change.delete_actionless
|
18
19
|
Card::Reference.unmap_if_referee_missing
|
19
20
|
Card::Reference.delete_if_referer_missing
|
20
21
|
end
|
@@ -94,7 +95,8 @@ event :validate_delete, :validate, on: :delete do
|
|
94
95
|
end
|
95
96
|
end
|
96
97
|
|
97
|
-
event :validate_delete_children, :
|
98
|
+
event :validate_delete_children, after: :validate_delete, on: :delete do
|
99
|
+
return if errors.any?
|
98
100
|
children.each do |child|
|
99
101
|
child.trash = true
|
100
102
|
add_subcard child
|
@@ -54,22 +54,23 @@ describe Card::Set::All::Collection do
|
|
54
54
|
end
|
55
55
|
|
56
56
|
describe "#contextual_content" do
|
57
|
+
let(:context_card) { Card["A"] } # refers to 'Z'
|
57
58
|
it "processes nests relative to context card" do
|
58
|
-
|
59
|
-
c = Card.new(name: "foo", content: "{{_self+B|core}}")
|
59
|
+
c = create "foo", content: "{{_self+B|core}}"
|
60
60
|
expect(c.contextual_content(context_card)).to eq("AlphaBeta")
|
61
61
|
end
|
62
62
|
|
63
63
|
# why the heck is this good? -efm
|
64
64
|
it "returns content even when context card is hard templated" do
|
65
|
-
|
66
|
-
|
67
|
-
Card::Auth.as_bot do
|
68
|
-
Card.create! name: "A+*self+*structure", content: "Banana"
|
69
|
-
end
|
70
|
-
c = Card.new name: "foo", content: "{{_self+B|core}}"
|
65
|
+
create "A+*self+*structure", content: "Banana"
|
66
|
+
c = create "foo", content: "{{_self+B|core}}"
|
71
67
|
expect(c.contextual_content(context_card)).to eq("AlphaBeta")
|
72
68
|
end
|
69
|
+
|
70
|
+
it "it doesn't use chunk list of context card" do
|
71
|
+
c = create "foo", content: "test@email.com", type: "HTML"
|
72
|
+
expect(c.contextual_content(context_card)).not_to have_tag "a"
|
73
|
+
end
|
73
74
|
end
|
74
75
|
|
75
76
|
describe "tabs view" do
|
data/mod/email/set/all/notify.rb
CHANGED
@@ -170,31 +170,41 @@ format do
|
|
170
170
|
#{render_list_of_changes(args)})
|
171
171
|
end
|
172
172
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
173
|
+
def followed_set_card
|
174
|
+
(set_name = voo.closest_live_option(:followed_set)) && Card.fetch(set_name)
|
175
|
+
end
|
176
|
+
|
177
|
+
def follow_option_card
|
178
|
+
(option_name = voo.closest_live_option(:follow_option)) &&
|
179
|
+
Card.fetch(option_name)
|
180
|
+
end
|
181
|
+
|
182
|
+
view :followed, perms: :none, closed: true do
|
183
|
+
if (set_card = followed_set_card) &&
|
184
|
+
(option_card = follow_option_card)
|
178
185
|
option_card.description set_card
|
179
186
|
else
|
180
187
|
"followed card"
|
181
188
|
end
|
182
189
|
end
|
183
190
|
|
184
|
-
view :follower, perms: :none, closed: true do
|
185
|
-
|
191
|
+
view :follower, perms: :none, closed: true do
|
192
|
+
voo.closest_live_option(:follower) || "follower"
|
193
|
+
end
|
194
|
+
|
195
|
+
def live_follow_rule_name
|
196
|
+
return unless (set_card = followed_set_card) &&
|
197
|
+
voo.closest_live_option(:follower)
|
198
|
+
set_card.follow_rule_name voo.closest_live_option(:follower)
|
186
199
|
end
|
187
200
|
|
188
|
-
view :unfollow_url, perms: :none, closed: true do |
|
189
|
-
if
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
card: { subcards: {
|
196
|
-
rule_name => Card[:never].name
|
197
|
-
} }
|
201
|
+
view :unfollow_url, perms: :none, closed: true do |_args|
|
202
|
+
if (rule_name = live_follow_rule_name)
|
203
|
+
target_name = "#{voo.closest_live_option :follower}+#{Card[:follow].name}"
|
204
|
+
update_path = page_path target_name, action: :update,
|
205
|
+
card: { subcards: {
|
206
|
+
rule_name => Card[:never].name
|
207
|
+
} }
|
198
208
|
card_url update_path # absolutize path
|
199
209
|
end
|
200
210
|
end
|