card 1.101.3 → 1.101.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/config/initializers/02_patches/active_record.rb +1 -1
- data/config/locales/en.yml +155 -378
- data/db/migrate_core_cards/20150202143810_import_bootstrap_layout.rb +1 -1
- data/lib/card.rb +15 -2
- data/lib/card/auth.rb +5 -2
- data/lib/card/auth/current.rb +39 -100
- data/lib/card/auth/proxy.rb +36 -16
- data/lib/card/auth/token.rb +6 -0
- data/lib/card/cache/all.rb +83 -0
- data/lib/card/cache/card_class.rb +41 -0
- data/lib/card/cache/persistent.rb +3 -34
- data/lib/card/cache/persistent_class.rb +28 -0
- data/lib/card/codename.rb +1 -1
- data/lib/card/content.rb +16 -2
- data/lib/card/content/all.rb +59 -0
- data/lib/card/director/act_direction.rb +4 -0
- data/lib/card/director/all.rb +61 -0
- data/lib/card/director/card_class.rb +18 -0
- data/lib/card/director/phases.rb +0 -1
- data/lib/card/dirty.rb +13 -3
- data/lib/card/env/success.rb +14 -14
- data/lib/card/env/success/target.rb +9 -11
- data/lib/card/error.rb +1 -1
- data/lib/card/fetch/all.rb +32 -0
- data/lib/card/fetch/card_class.rb +147 -0
- data/lib/card/format.rb +1 -1
- data/lib/card/format/error.rb +3 -3
- data/lib/card/format/nest.rb +1 -1
- data/lib/card/format/nest/fetch.rb +1 -1
- data/lib/card/lexicon.rb +2 -2
- data/lib/card/name/all.rb +8 -0
- data/lib/card/name/all/descendants.rb +6 -3
- data/lib/card/name/card_class.rb +26 -0
- data/lib/card/reference/all.rb +131 -0
- data/lib/card/rule/all.rb +75 -0
- data/lib/card/set/event/all.rb +95 -0
- data/lib/card/set/event/skip_and_trigger.rb +89 -0
- data/lib/card/set/pattern/all.rb +63 -0
- data/lib/card/subcards/all.rb +103 -0
- data/lib/cardio/migration/import.rb +1 -1
- data/lib/cardio/utils.rb +5 -3
- data/mod/admin/set/self/admin_info.rb +3 -5
- data/mod/admin/set/self/trash.rb +2 -2
- data/mod/core/set/all/autoname.rb +17 -0
- data/mod/core/set/all/codename.rb +2 -2
- data/mod/core/set/all/content.rb +52 -97
- data/mod/core/set/all/name_events.rb +69 -58
- data/mod/core/set/all/reference_events.rb +67 -0
- data/mod/core/set/all/states.rb +2 -2
- data/mod/core/set/all/subcards.rb +0 -100
- data/mod/core/set/all/trash.rb +11 -13
- data/mod/core/set/all/type.rb +7 -9
- data/mod/core/set/all/utils.rb +3 -0
- data/mod/core/set/type/cardtype.rb +3 -3
- data/mod/core/set_pattern/06_rule.rb +1 -1
- data/mod/core/spec/set/all/{rules2_spec.rb → clean_me_spec.rb} +0 -0
- data/mod/core/spec/set/all/name_events_spec.rb +204 -0
- metadata +30 -37
- data/lib/card/mod_inflector.rb +0 -16
- data/lib/card/name/all/class_methods.rb +0 -28
- data/mod/core/set/all/actify.rb +0 -68
- data/mod/core/set/all/cache.rb +0 -109
- data/mod/core/set/all/event_conditions.rb +0 -172
- data/mod/core/set/all/fetch.rb +0 -122
- data/mod/core/set/all/fetch_helper.rb +0 -35
- data/mod/core/set/all/i18n.rb +0 -9
- data/mod/core/set/all/pattern.rb +0 -54
- data/mod/core/set/all/references.rb +0 -191
- data/mod/core/set/all/rename.rb +0 -33
- data/mod/core/set/all/rules.rb +0 -81
- data/mod/core/spec/set/all/actify_spec.rb +0 -58
- data/mod/core/spec/set/all/content_spec.rb +0 -15
- data/mod/core/spec/set/all/event_conditions_spec.rb +0 -217
- data/mod/core/spec/set/all/fetch_helper_spec.rb +0 -65
- data/mod/core/spec/set/all/fetch_spec.rb +0 -338
- data/mod/core/spec/set/all/i18n_spec.rb +0 -17
- data/mod/core/spec/set/all/pattern_spec.rb +0 -101
- data/mod/core/spec/set/all/permissions/reader_rules_spec.rb +0 -166
- data/mod/core/spec/set/all/references_spec.rb +0 -62
- data/mod/core/spec/set/all/rename_spec.rb +0 -189
- data/mod/core/spec/set/all/rules_spec.rb +0 -100
- data/mod/core/spec/set/all/subcards_spec.rb +0 -102
@@ -0,0 +1,147 @@
|
|
1
|
+
class Card
|
2
|
+
class Fetch
|
3
|
+
# = Card#fetch
|
4
|
+
#
|
5
|
+
# A multipurpose retrieval operator that integrates caching, database lookups,
|
6
|
+
# and "virtual" card construction
|
7
|
+
module CardClass
|
8
|
+
# Look for cards in
|
9
|
+
# * cache
|
10
|
+
# * database
|
11
|
+
# * virtual cards
|
12
|
+
#
|
13
|
+
# @param args [Integer, String, Card::Name, Symbol, Array]
|
14
|
+
# one or more of the three unique identifiers
|
15
|
+
# 1. a numeric id (Integer)
|
16
|
+
# 2. a name/key (String or Card::Name)
|
17
|
+
# 3. a codename (Symbol)
|
18
|
+
# If you pass more then one mark or an array of marks they get joined with a '+'.
|
19
|
+
# The final argument can be a hash to set the following options
|
20
|
+
# :skip_virtual Real cards only
|
21
|
+
# :skip_modules Don't load Set modules
|
22
|
+
# :look_in_trash Return trashed card objects
|
23
|
+
# :local_only Use only local cache for lookup and storing
|
24
|
+
# new: { opts for Card#new } Return a new card when not found
|
25
|
+
# @return [Card]
|
26
|
+
def fetch *args
|
27
|
+
Card::Fetch.new(*args)&.retrieve_or_new
|
28
|
+
rescue ActiveModel::RangeError => _e
|
29
|
+
return Card.new name: "card id out of range: #{f.mark}"
|
30
|
+
end
|
31
|
+
|
32
|
+
# fetch only real (no virtual) cards
|
33
|
+
#
|
34
|
+
# @param mark - see #fetch
|
35
|
+
# @return [Card]
|
36
|
+
def [] *mark
|
37
|
+
fetch(*mark, skip_virtual: true)
|
38
|
+
end
|
39
|
+
|
40
|
+
# fetch real cards without set modules loaded. Should only be used for
|
41
|
+
# simple attributes
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# quick_fetch "A", :self, :structure
|
45
|
+
#
|
46
|
+
# @param mark - see #fetch
|
47
|
+
# @return [Card]
|
48
|
+
def quick_fetch *mark
|
49
|
+
fetch(*mark, skip_virtual: true, skip_modules: true)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Card]
|
53
|
+
def fetch_from_cast cast
|
54
|
+
fetch_args = cast[:id] ? [cast[:id].to_i] : [cast[:name], { new: cast }]
|
55
|
+
fetch(*fetch_args)
|
56
|
+
end
|
57
|
+
|
58
|
+
#----------------------------------------------------------------------
|
59
|
+
# ATTRIBUTE FETCHING
|
60
|
+
# The following methods optimize fetching of specific attributes
|
61
|
+
|
62
|
+
def id cardish
|
63
|
+
case cardish
|
64
|
+
when Integer then cardish
|
65
|
+
when Card then cardish.id
|
66
|
+
when Symbol then Card::Codename.id cardish
|
67
|
+
else fetch_id cardish
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# @param mark_parts - see #fetch
|
72
|
+
# @return [Integer]
|
73
|
+
def fetch_id *mark_parts
|
74
|
+
mark = Card::Fetch.new(*mark_parts)&.mark
|
75
|
+
mark.is_a?(Integer) ? mark : quick_fetch(mark.to_s)&.id
|
76
|
+
end
|
77
|
+
|
78
|
+
# @param mark - see #fetch
|
79
|
+
# @return [Card::Name]
|
80
|
+
def fetch_name *mark, &block
|
81
|
+
if (card = quick_fetch(*mark))
|
82
|
+
card.name
|
83
|
+
elsif block_given?
|
84
|
+
yield.to_name
|
85
|
+
end
|
86
|
+
rescue => error
|
87
|
+
rescue_fetch_name error, &block
|
88
|
+
end
|
89
|
+
|
90
|
+
# @param mark - see #fetch
|
91
|
+
# @return [Integer]
|
92
|
+
def fetch_type_id *mark
|
93
|
+
fetch(*mark, skip_modules: true)&.type_id
|
94
|
+
end
|
95
|
+
|
96
|
+
# a fetch method to support the needs of the card controller.
|
97
|
+
# should be in Decko?
|
98
|
+
def controller_fetch args
|
99
|
+
card_opts = controller_fetch_opts args
|
100
|
+
if args[:action] == "create"
|
101
|
+
# FIXME: we currently need a "new" card to catch duplicates
|
102
|
+
# (otherwise save will just act like a normal update)
|
103
|
+
# We may need a "#create" instance method to handle this checking?
|
104
|
+
Card.new card_opts
|
105
|
+
else
|
106
|
+
standard_controller_fetch args, card_opts
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def standard_controller_fetch args, card_opts
|
113
|
+
mark = args[:mark] || card_opts[:name]
|
114
|
+
card = Card.fetch mark, skip_modules: true,
|
115
|
+
look_in_trash: args[:look_in_trash],
|
116
|
+
new: card_opts
|
117
|
+
card.assign_attributes card_opts if args[:assign] && card&.real?
|
118
|
+
card&.include_set_modules
|
119
|
+
card
|
120
|
+
end
|
121
|
+
|
122
|
+
def controller_fetch_opts args
|
123
|
+
opts = Env.hash args[:card]
|
124
|
+
opts[:type] ||= args[:type] if args[:type]
|
125
|
+
# for /new/:type shortcut. we should handle in routing and deprecate this
|
126
|
+
opts[:name] ||= Name.url_key_to_standard args[:mark]
|
127
|
+
opts
|
128
|
+
end
|
129
|
+
|
130
|
+
def rescue_fetch_name error, &block
|
131
|
+
if rescued_fetch_name_to_name? error, &block
|
132
|
+
yield.to_name
|
133
|
+
elsif error.is_a? ActiveModel::RangeError
|
134
|
+
nil
|
135
|
+
else
|
136
|
+
raise error
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def rescued_fetch_name_to_name? error
|
141
|
+
return unless block_given?
|
142
|
+
|
143
|
+
error.is_a?(ActiveModel::RangeError) || error.is_a?(Error::CodenameNotFound)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
data/lib/card/format.rb
CHANGED
data/lib/card/format/error.rb
CHANGED
@@ -29,7 +29,7 @@ class Card
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def monitor_depth
|
32
|
-
raise Card::Error::UserError,
|
32
|
+
raise Card::Error::UserError, t(:format_too_deep) if depth >= Card.config.max_depth
|
33
33
|
yield
|
34
34
|
end
|
35
35
|
|
@@ -42,7 +42,7 @@ class Card
|
|
42
42
|
if card&.name.present?
|
43
43
|
safe_name
|
44
44
|
else
|
45
|
-
Cardio.
|
45
|
+
Cardio.t :lib_no_cardname
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -71,7 +71,7 @@ class Card
|
|
71
71
|
if exception.is_a? Card::Error::UserError
|
72
72
|
exception.message
|
73
73
|
else
|
74
|
-
|
74
|
+
t :lib_error_rendering, cardname: error_cardname(exception), view: view
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
data/lib/card/format/nest.rb
CHANGED
data/lib/card/lexicon.rb
CHANGED
@@ -36,12 +36,12 @@ class Card
|
|
36
36
|
|
37
37
|
def update card
|
38
38
|
add card
|
39
|
-
cache.delete cache_key(card.
|
39
|
+
cache.delete cache_key(card.lex_before_act)
|
40
40
|
end
|
41
41
|
|
42
42
|
# def delete card
|
43
43
|
# cache.delete card.id.to_s
|
44
|
-
# cache.delete cache_key(card.
|
44
|
+
# cache.delete cache_key(card.lex_before_act)
|
45
45
|
# end
|
46
46
|
|
47
47
|
def lex_to_name lex
|
data/lib/card/name/all.rb
CHANGED
@@ -35,6 +35,10 @@ class Card
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
+
def lex
|
39
|
+
simple? ? name : [left_id, right_id]
|
40
|
+
end
|
41
|
+
|
38
42
|
def autoname name
|
39
43
|
if Card.exists?(name) || Director.include?(name)
|
40
44
|
autoname name.next
|
@@ -64,6 +68,10 @@ class Card
|
|
64
68
|
@key
|
65
69
|
end
|
66
70
|
|
71
|
+
def field tag, opts={}
|
72
|
+
Card.fetch name.field(tag), opts
|
73
|
+
end
|
74
|
+
|
67
75
|
private
|
68
76
|
|
69
77
|
def assign_side_ids
|
@@ -19,9 +19,12 @@ class Card
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def each_child
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
return unless id
|
23
|
+
|
24
|
+
Card.where(
|
25
|
+
"(left_id = #{id} or right_id = #{id}) and trash is false"
|
26
|
+
).each do |child|
|
27
|
+
yield child
|
25
28
|
end
|
26
29
|
end
|
27
30
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Card
|
2
|
+
class Name
|
3
|
+
# Name-related Card class methods
|
4
|
+
module CardClass
|
5
|
+
def rename! oldname, newname
|
6
|
+
Card[oldname].update! name: newname, update_referers: true
|
7
|
+
end
|
8
|
+
|
9
|
+
def uniquify_name name, rename=:new
|
10
|
+
return name unless Card.exists? name
|
11
|
+
uniq_name = generate_alternative_name name
|
12
|
+
return uniq_name unless rename == :old
|
13
|
+
rename!(name, uniq_name)
|
14
|
+
name
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def generate_alternative_name name
|
20
|
+
uniq_name = "#{name} 1"
|
21
|
+
uniq_name.next! while Card.exists?(uniq_name)
|
22
|
+
uniq_name
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
class Card
|
2
|
+
class Reference < ApplicationRecord
|
3
|
+
module All
|
4
|
+
# frozen_string_literal: true
|
5
|
+
|
6
|
+
# Cards can refer to other cards in their content, eg via links and nests.
|
7
|
+
# The card that refers is the "referer", the card that is referred to is
|
8
|
+
# the "referee". The reference itself has its own class (Card::Reference),
|
9
|
+
# which handles id-based reference tracking.
|
10
|
+
|
11
|
+
PARTIAL_REF_CODE = "P".freeze
|
12
|
+
|
13
|
+
# cards that refer to self
|
14
|
+
def referers
|
15
|
+
referer_cards_from_references references_in
|
16
|
+
end
|
17
|
+
|
18
|
+
# cards that include self
|
19
|
+
def nesters
|
20
|
+
referer_cards_from_references references_in.where(ref_type: "I")
|
21
|
+
end
|
22
|
+
|
23
|
+
def referer_cards_from_references references
|
24
|
+
references.map(&:referer_id).uniq.map(&Card.method(:fetch)).compact
|
25
|
+
end
|
26
|
+
|
27
|
+
# cards that self refers to
|
28
|
+
def referees
|
29
|
+
referees_from_references references_out
|
30
|
+
end
|
31
|
+
|
32
|
+
# cards that self includes
|
33
|
+
def nestees
|
34
|
+
referees_from_references references_out.where(ref_type: "I")
|
35
|
+
end
|
36
|
+
|
37
|
+
def referees_from_references references
|
38
|
+
references.map(&:referee_key).uniq.map { |key| Card.fetch key, new: {} }
|
39
|
+
end
|
40
|
+
|
41
|
+
# cards that refer to self by name
|
42
|
+
# (finds cards not yet linked by id)
|
43
|
+
def name_referers
|
44
|
+
Card.joins(:references_out).where card_references: { referee_key: key }
|
45
|
+
end
|
46
|
+
|
47
|
+
# delete old references from this card's content, create new ones
|
48
|
+
def update_references_out
|
49
|
+
delete_references_out
|
50
|
+
create_references_out
|
51
|
+
end
|
52
|
+
|
53
|
+
# interpret references from this card's content and
|
54
|
+
# insert entries in reference table
|
55
|
+
def create_references_out
|
56
|
+
ref_hash = {}
|
57
|
+
each_reference_out do |referee_name, ref_type|
|
58
|
+
interpret_reference ref_hash, referee_name, ref_type
|
59
|
+
end
|
60
|
+
return if ref_hash.empty?
|
61
|
+
Reference.mass_insert reference_values_array(ref_hash)
|
62
|
+
end
|
63
|
+
|
64
|
+
# delete references from this card
|
65
|
+
def delete_references_out
|
66
|
+
raise "id required to delete references" if id.nil?
|
67
|
+
Reference.where(referer_id: id).delete_all
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def with_normalized_referee referee_name
|
73
|
+
return unless referee_name # eg commented nest has no referee_name
|
74
|
+
referee_name = referee_name.to_name
|
75
|
+
referee_key = referee_name.key
|
76
|
+
return if referee_key == key # don't create self reference
|
77
|
+
yield referee_name, referee_key, Lexicon.id(referee_name)
|
78
|
+
end
|
79
|
+
|
80
|
+
# interpretation phase helps to prevent duplicate references
|
81
|
+
# results in hash like:
|
82
|
+
# { referee1_key: [referee1_id, referee1_type2],
|
83
|
+
# referee2_key...
|
84
|
+
# }
|
85
|
+
def interpret_reference ref_hash, raw_referee_name, ref_type
|
86
|
+
with_normalized_referee raw_referee_name do |name, key, id|
|
87
|
+
ref_hash[key] ||= [id]
|
88
|
+
ref_hash[key] << ref_type
|
89
|
+
interpret_partial_references ref_hash, name unless id
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# translate interpreted reference hash into values array,
|
94
|
+
# removing duplicate and unnecessary ref_types
|
95
|
+
def reference_values_array ref_hash
|
96
|
+
[].tap do |values|
|
97
|
+
ref_hash.each do |referee_key, hash_val|
|
98
|
+
referee_id = hash_val.shift || "null"
|
99
|
+
each_reference_type hash_val.uniq do |ref_type|
|
100
|
+
values << [id, referee_id, "'#{referee_key}'", "'#{ref_type}'"]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def each_reference_type ref_types
|
107
|
+
ref_types.delete PARTIAL_REF_CODE if ref_types.size > 1
|
108
|
+
# partial references are not necessary if there are explicit references
|
109
|
+
ref_types.each { |ref_type| yield ref_type }
|
110
|
+
end
|
111
|
+
|
112
|
+
# invokes the given block for each reference in content with
|
113
|
+
# the reference name and reference type
|
114
|
+
def each_reference_out
|
115
|
+
content_object.find_chunks(:Reference).each do |chunk|
|
116
|
+
yield chunk.referee_name, chunk.reference_code
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Partial references are needed to track references to virtual cards.
|
121
|
+
# For example a link to virual card [[A+*self]] won't have a referee_id,
|
122
|
+
# but when A's name is changed we have to find and update that link.
|
123
|
+
def interpret_partial_references ref_hash, referee_name
|
124
|
+
return if referee_name.simple?
|
125
|
+
[referee_name.left, referee_name.right].each do |sidename|
|
126
|
+
interpret_reference ref_hash, sidename, PARTIAL_REF_CODE
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
class Card
|
2
|
+
module Rule
|
3
|
+
# rule-related Card instance methods
|
4
|
+
module All
|
5
|
+
def rule setting_code
|
6
|
+
rule_card(setting_code, skip_modules: true)&.db_content
|
7
|
+
end
|
8
|
+
|
9
|
+
def rule_card setting_code, options={}
|
10
|
+
Card.fetch rule_card_id(setting_code), options
|
11
|
+
end
|
12
|
+
|
13
|
+
def rule_card_id setting_code
|
14
|
+
rule_id_lookup Card::Rule.rule_cache, setting_code
|
15
|
+
end
|
16
|
+
|
17
|
+
def preference setting_code, user=nil
|
18
|
+
preference_card(setting_code, user, skip_modules: true)&.db_content
|
19
|
+
end
|
20
|
+
|
21
|
+
def preference_card setting_code, user=nil, options={}
|
22
|
+
Card.fetch preference_card_id(setting_code, user), options
|
23
|
+
end
|
24
|
+
|
25
|
+
def preference_card_id setting_code, user=nil
|
26
|
+
return unless (user_id = preference_user_id user)
|
27
|
+
rule_id_lookup Card::Rule.preference_cache,
|
28
|
+
"#{setting_code}+#{user_id}",
|
29
|
+
"#{setting_code}+#{AllID}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def rule?
|
33
|
+
standard_rule? || preference?
|
34
|
+
end
|
35
|
+
|
36
|
+
def standard_rule?
|
37
|
+
(Card.fetch_type_id(name.right) == SettingID) &&
|
38
|
+
(Card.fetch_type_id(name.left) == SetID)
|
39
|
+
end
|
40
|
+
|
41
|
+
def preference?
|
42
|
+
name.parts.length > 2 &&
|
43
|
+
(Card.fetch_type_id(name.right) == SettingID) &&
|
44
|
+
(Card.fetch_type_id(name[0..-3]) == SetID) &&
|
45
|
+
valid_preferer?
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def valid_preferer?
|
51
|
+
preferer = self[-2, skip_modules: true]
|
52
|
+
(preferer.type_id == UserID) || (preferer.codename == :all)
|
53
|
+
end
|
54
|
+
|
55
|
+
def preference_user_id user
|
56
|
+
case user
|
57
|
+
when Integer then user
|
58
|
+
when Card then user.id
|
59
|
+
when nil then Auth.current_id
|
60
|
+
else
|
61
|
+
raise Card::ServerError, "invalid preference user"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def rule_id_lookup lookup_hash, cache_suffix, fallback_suffix=nil
|
66
|
+
rule_set_keys.each do |rule_set_key|
|
67
|
+
rule_id = lookup_hash["#{rule_set_key}+#{cache_suffix}"]
|
68
|
+
rule_id ||= fallback_suffix && lookup_hash["#{rule_set_key}+#{fallback_suffix}"]
|
69
|
+
return rule_id if rule_id
|
70
|
+
end
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|