card-mod-history 0.11.0
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 +7 -0
- data/lib/card/act.rb +137 -0
- data/lib/card/act/act_renderer.rb +217 -0
- data/lib/card/act/act_renderer/absolute_act_renderer.rb +34 -0
- data/lib/card/act/act_renderer/bridge_act_renderer.rb +53 -0
- data/lib/card/act/act_renderer/relative_act_renderer.rb +59 -0
- data/lib/card/action.rb +230 -0
- data/lib/card/action/action_renderer.rb +94 -0
- data/lib/card/action/admin.rb +36 -0
- data/lib/card/action/differ.rb +89 -0
- data/lib/card/change.rb +70 -0
- data/set/all/history.rb +109 -0
- data/set/all/history/act_listing.rb +124 -0
- data/set/all/history/actions.rb +124 -0
- data/set/all/history/acts.rb +8 -0
- data/set/all/history/events.rb +100 -0
- data/set/all/history/last.rb +98 -0
- data/set/all/history/revision.rb +65 -0
- data/set/all/history/selected.rb +64 -0
- data/set/all/history/views.rb +35 -0
- data/set/all/history_bridge.rb +65 -0
- metadata +80 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
def acted_at
|
2
|
+
last_act.acted_at
|
3
|
+
end
|
4
|
+
|
5
|
+
def revised_at
|
6
|
+
(last_action && (act = last_action.act) && act.acted_at) || Time.zone.now
|
7
|
+
end
|
8
|
+
|
9
|
+
def last_change_on field, opts={}
|
10
|
+
action_id = extract_action_id(opts[:before] || opts[:not_after])
|
11
|
+
|
12
|
+
# If there is only one action then there are no entries in the changes table,
|
13
|
+
# so we can't do a sql search but the changes are accessible via the action.
|
14
|
+
if no_last_change? action_id, opts[:before]
|
15
|
+
nil
|
16
|
+
elsif create_action_last_change? action_id
|
17
|
+
create_action&.change field
|
18
|
+
else
|
19
|
+
last_change_from_action_id action_id, field, opts
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def no_last_change? action_id, before
|
24
|
+
before && action_id == create_action.id
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_action_last_change? action_id
|
28
|
+
action_id == create_action&.id || (!action_id && create_action&.sole?)
|
29
|
+
end
|
30
|
+
|
31
|
+
def last_change_from_action_id action_id, field, opts
|
32
|
+
Change.joins(:action).where(
|
33
|
+
last_change_sql_conditions(opts),
|
34
|
+
card_id: id,
|
35
|
+
action_id: action_id,
|
36
|
+
field: Card::Change.field_index(field)
|
37
|
+
).order(:id).last
|
38
|
+
end
|
39
|
+
|
40
|
+
def last_change_sql_conditions opts
|
41
|
+
cond = "card_actions.card_id = :card_id AND field = :field"
|
42
|
+
cond += " AND (draft is not true)" unless opts[:including_drafts]
|
43
|
+
operator = "<" if opts[:before]
|
44
|
+
operator = "<=" if opts[:not_after]
|
45
|
+
cond += " AND card_action_id #{operator} :action_id" if operator
|
46
|
+
cond
|
47
|
+
end
|
48
|
+
|
49
|
+
def last_action_id
|
50
|
+
last_action&.id
|
51
|
+
end
|
52
|
+
|
53
|
+
def last_action
|
54
|
+
actions.where("id IS NOT NULL").last
|
55
|
+
end
|
56
|
+
|
57
|
+
def last_content_action
|
58
|
+
last_change_on(:db_content)&.action
|
59
|
+
end
|
60
|
+
|
61
|
+
def last_content_action_id
|
62
|
+
last_change_on(:db_content)&.card_action_id
|
63
|
+
end
|
64
|
+
|
65
|
+
def last_actor
|
66
|
+
last_act.actor
|
67
|
+
end
|
68
|
+
|
69
|
+
def last_act
|
70
|
+
@last_act ||=
|
71
|
+
if (action = last_action)
|
72
|
+
last_act_on_self = acts.last
|
73
|
+
act_of_last_action = action.act
|
74
|
+
return act_of_last_action unless last_act_on_self
|
75
|
+
return last_act_on_self unless act_of_last_action
|
76
|
+
|
77
|
+
return last_act_on_self if act_of_last_action == last_act_on_self
|
78
|
+
|
79
|
+
if last_act_on_self.acted_at > act_of_last_action.acted_at
|
80
|
+
last_act_on_self
|
81
|
+
else
|
82
|
+
act_of_last_action
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def previous_action action_id
|
88
|
+
return unless action_id
|
89
|
+
|
90
|
+
action_index = actions.find_index { |a| a.id == action_id }
|
91
|
+
all_actions[action_index - 1] if action_index.to_i.nonzero?
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def extract_action_id action_arg
|
97
|
+
action_arg.is_a?(Card::Action) ? action_arg.id : action_arg
|
98
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
def revision action, before_action=false
|
2
|
+
# a "revision" refers to the state of all tracked fields
|
3
|
+
# at the time of a given action
|
4
|
+
action = Card::Action.fetch(action) if action.is_a? Integer
|
5
|
+
return unless action
|
6
|
+
|
7
|
+
if before_action
|
8
|
+
revision_before_action action
|
9
|
+
else
|
10
|
+
revision_attributes action
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def revision_attributes action
|
15
|
+
Card::Change::TRACKED_FIELDS.each_with_object({}) do |field, attr_changes|
|
16
|
+
last_change = action.change(field) || last_change_on(field, not_after: action)
|
17
|
+
attr_changes[field.to_sym] = (last_change ? last_change.value : self[field])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def revision_before_action action
|
22
|
+
if (prev_action = action.previous_action)
|
23
|
+
revision prev_action
|
24
|
+
else
|
25
|
+
{ trash: true }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def rollback_request?
|
30
|
+
history? && actions_to_revert.any?
|
31
|
+
end
|
32
|
+
|
33
|
+
def process_revert_actions revert_actions=nil
|
34
|
+
revert_actions ||= actions_to_revert
|
35
|
+
update_args = { subcards: {} }
|
36
|
+
reverting_to_previous = Env.params["revert_to"] == "previous"
|
37
|
+
revert_actions.each do |action|
|
38
|
+
merge_revert_action! action, update_args, reverting_to_previous
|
39
|
+
end
|
40
|
+
update_args
|
41
|
+
end
|
42
|
+
|
43
|
+
def actions_to_revert
|
44
|
+
if (act_id = Env.params["revert_act"])
|
45
|
+
Act.find(act_id).actions
|
46
|
+
else
|
47
|
+
explicit_actions_to_revert
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def explicit_actions_to_revert
|
52
|
+
Array.wrap(Env.params["revert_actions"]).map do |a_id|
|
53
|
+
Action.fetch(a_id) || nil
|
54
|
+
end.compact
|
55
|
+
end
|
56
|
+
|
57
|
+
def merge_revert_action! action, update_args, reverting_to_previous
|
58
|
+
rev = action.card.revision(action, reverting_to_previous)
|
59
|
+
rev.delete :name unless rev[:name] # handles null name field in compound cards
|
60
|
+
if action.card_id == id
|
61
|
+
update_args.merge! rev
|
62
|
+
else
|
63
|
+
update_args[:subcards][action.card.name] = rev
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# if these aren't in a nested module, the methods just overwrite the base
|
2
|
+
# methods, but we need a distinct module so that super will be able to refer to
|
3
|
+
# the base methods.
|
4
|
+
def content
|
5
|
+
@selected_action_id ? selected_content : super
|
6
|
+
end
|
7
|
+
|
8
|
+
def content= value
|
9
|
+
@selected_content = nil
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def select_action_by_params params
|
14
|
+
action = nth_action(params[:rev]) || action_from_id(params[:rev_id])
|
15
|
+
return unless action
|
16
|
+
|
17
|
+
select_action action.id
|
18
|
+
end
|
19
|
+
|
20
|
+
def select_action action_id
|
21
|
+
run_callbacks :select_action do
|
22
|
+
self.selected_action_id = action_id
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def selected_action_id
|
27
|
+
@selected_action_id || (@current_action&.id) || last_action_id
|
28
|
+
end
|
29
|
+
|
30
|
+
def selected_action_id= action_id
|
31
|
+
@selected_content = nil
|
32
|
+
@selected_action_id = action_id
|
33
|
+
end
|
34
|
+
|
35
|
+
def selected_action
|
36
|
+
selected_action_id && Action.fetch(selected_action_id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def selected_content
|
40
|
+
@selected_content ||= content_at_time_of_selected_action || db_content
|
41
|
+
end
|
42
|
+
|
43
|
+
def content_at_time_of_selected_action
|
44
|
+
lc = last_change_on(:db_content, not_after: @selected_action_id, including_drafts: true)
|
45
|
+
lc&.value
|
46
|
+
end
|
47
|
+
|
48
|
+
def with_selected_action_id action_id
|
49
|
+
current_action_id = @selected_action_id
|
50
|
+
select_action_id action_id
|
51
|
+
result = yield
|
52
|
+
select_action_id current_action_id
|
53
|
+
result
|
54
|
+
end
|
55
|
+
|
56
|
+
def select_action_id action_id
|
57
|
+
run_callbacks :select_action do
|
58
|
+
self.selected_action_id = action_id
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def selected_content_action_id
|
63
|
+
@selected_action_id || new_content_action_id || last_content_action_id
|
64
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# History views
|
2
|
+
|
3
|
+
format :html do
|
4
|
+
view :history, cache: :never do
|
5
|
+
frame do
|
6
|
+
class_up "d0-card-body", "history-slot"
|
7
|
+
acts_layout card.history_acts, :relative, :show
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
view :act, cache: :never do
|
12
|
+
act_listing act_from_context
|
13
|
+
end
|
14
|
+
|
15
|
+
view :act_legend do
|
16
|
+
bs_layout do
|
17
|
+
row md: [12, 12], lg: [7, 5] do
|
18
|
+
col action_legend
|
19
|
+
col content_legend, class: "text-right"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
view :draft_legend do
|
25
|
+
"#{action_icon(:draft)} unsaved draft"
|
26
|
+
end
|
27
|
+
|
28
|
+
view :action_summary do
|
29
|
+
action_content action_from_context, :summary
|
30
|
+
end
|
31
|
+
|
32
|
+
view :action_expanded do
|
33
|
+
action_content action_from_context, :expanded
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
format :html do
|
2
|
+
view :creator_credit,
|
3
|
+
wrap: { div: { class: "text-muted creator-credit" } }, cache: :never do
|
4
|
+
return "" unless card.real?
|
5
|
+
"Created by #{nest card.creator, view: :link} "\
|
6
|
+
"#{time_ago_in_words(card.created_at)} ago"
|
7
|
+
end
|
8
|
+
|
9
|
+
view :updated_by, wrap: { div: { class: "text-muted" } } do
|
10
|
+
return "" unless card.id
|
11
|
+
updaters = Card.search(updater_of: { id: card.id })
|
12
|
+
return "" unless updaters.present?
|
13
|
+
|
14
|
+
updaters = updater_links updaters, others_target: Card.fetch(card, :editors)
|
15
|
+
"Updated by #{updaters}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def updater_links updaters, item_view: :link, max_count: 3, others_target: card
|
19
|
+
return "" unless updaters.present?
|
20
|
+
|
21
|
+
total = updaters.size
|
22
|
+
fetch_count = total > max_count ? max_count - 1 : max_count
|
23
|
+
|
24
|
+
reduced = first_card(fetch_count).map { |c| nest c, view: item_view }
|
25
|
+
if total > max_count
|
26
|
+
reduced << link_to_card(others_target, "#{total - fetch_count} others")
|
27
|
+
end
|
28
|
+
reduced.to_sentence
|
29
|
+
end
|
30
|
+
|
31
|
+
def acts_bridge_layout acts, context=:bridge
|
32
|
+
output [
|
33
|
+
_render_creator_credit,
|
34
|
+
act_link_list(acts, context),
|
35
|
+
act_paging(acts, context)
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
def act_link_list acts, context
|
40
|
+
items = acts_for_accordion(acts, context) do |act, seq|
|
41
|
+
act_link_list_item act, seq, context
|
42
|
+
end
|
43
|
+
bridge_pills items
|
44
|
+
end
|
45
|
+
|
46
|
+
def act_link_list_item act, seq=nil, _context=nil
|
47
|
+
opts = act_listing_opts_from_params(seq)
|
48
|
+
opts[:slot_class] = "revision-#{act.id} history-slot nav-item"
|
49
|
+
act_renderer(:bridge).new(self, act, opts).bridge_link
|
50
|
+
end
|
51
|
+
|
52
|
+
def act_list_group acts, context, &block
|
53
|
+
list_group acts_for_accordion(acts, context, &block), class: "clear-both"
|
54
|
+
end
|
55
|
+
|
56
|
+
view :bridge_act, cache: :never do
|
57
|
+
opts = act_listing_opts_from_params(nil)
|
58
|
+
act = act_from_context
|
59
|
+
ar = act_renderer(:bridge).new(self, act, opts)
|
60
|
+
class_up "action-list", "my-3"
|
61
|
+
wrap_with_overlay title: ar.overlay_title, slot: breadcrumb_data("History") do
|
62
|
+
act_listing(act, opts[:act_seq], :bridge)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: card-mod-history
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.11.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ethan McCutchen
|
8
|
+
- Philipp Kühl
|
9
|
+
- Gerry Gleason
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2020-12-24 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: card
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - '='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.101.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - '='
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: 1.101.0
|
29
|
+
description: ''
|
30
|
+
email:
|
31
|
+
- info@decko.org
|
32
|
+
executables: []
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- lib/card/act.rb
|
37
|
+
- lib/card/act/act_renderer.rb
|
38
|
+
- lib/card/act/act_renderer/absolute_act_renderer.rb
|
39
|
+
- lib/card/act/act_renderer/bridge_act_renderer.rb
|
40
|
+
- lib/card/act/act_renderer/relative_act_renderer.rb
|
41
|
+
- lib/card/action.rb
|
42
|
+
- lib/card/action/action_renderer.rb
|
43
|
+
- lib/card/action/admin.rb
|
44
|
+
- lib/card/action/differ.rb
|
45
|
+
- lib/card/change.rb
|
46
|
+
- set/all/history.rb
|
47
|
+
- set/all/history/act_listing.rb
|
48
|
+
- set/all/history/actions.rb
|
49
|
+
- set/all/history/acts.rb
|
50
|
+
- set/all/history/events.rb
|
51
|
+
- set/all/history/last.rb
|
52
|
+
- set/all/history/revision.rb
|
53
|
+
- set/all/history/selected.rb
|
54
|
+
- set/all/history/views.rb
|
55
|
+
- set/all/history_bridge.rb
|
56
|
+
homepage: http://decko.org
|
57
|
+
licenses:
|
58
|
+
- GPL-3.0
|
59
|
+
metadata:
|
60
|
+
card-mod: history
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '2.5'
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubygems_version: 3.0.3
|
77
|
+
signing_key:
|
78
|
+
specification_version: 4
|
79
|
+
summary: Revision histories in acts, actions, and changes
|
80
|
+
test_files: []
|