card-mod-follow 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/README.md +134 -0
- data/lib/card/follow_option.rb +33 -0
- data/lib/card/follower_stash.rb +88 -0
- data/set/abstract/follow_option.rb +56 -0
- data/set/all/follow.rb +33 -0
- data/set/all/follow/follow_link.rb +61 -0
- data/set/all/follow/follow_link_views.rb +29 -0
- data/set/all/follow/followed_by.rb +70 -0
- data/set/all/follow/follower_ids.rb +121 -0
- data/set/all/follow/start_follow_link.rb +11 -0
- data/set/all/follow/stop_follow_link.rb +12 -0
- data/set/all/notify.rb +81 -0
- data/set/all/notify/base_views.rb +127 -0
- data/set/all/notify/html_views.rb +17 -0
- data/set/right/account.rb +15 -0
- data/set/right/follow.rb +118 -0
- data/set/right/follow/follow_status.haml +9 -0
- data/set/right/follow_fields.rb +3 -0
- data/set/right/followers.rb +23 -0
- data/set/right/following.rb +50 -0
- data/set/self/always.rb +13 -0
- data/set/self/created.rb +19 -0
- data/set/self/edited.rb +20 -0
- data/set/self/follow.rb +1 -0
- data/set/self/follow_defaults.rb +88 -0
- data/set/self/follow_fields.rb +1 -0
- data/set/self/never.rb +13 -0
- data/set/type/cardtype.rb +21 -0
- data/set/type/notification_template.rb +33 -0
- data/set/type/set.rb +53 -0
- data/set/type/user.rb +7 -0
- data/set/type_plus_right/user/follow.rb +77 -0
- data/set/type_plus_right/user/follow/follow_editor.haml +8 -0
- data/set/type_plus_right/user/follow/follow_editor_helper.rb +121 -0
- metadata +108 -0
data/set/right/follow.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
# The Right::Follow set configures follow preferences (`[Set]+[User]+:follow`)
|
2
|
+
# While the user follow dashboard ([User]+:follow`) is also in this Set, its
|
3
|
+
# customizations are handled in TypePlusRight::User::Follow
|
4
|
+
|
5
|
+
event :cache_expired_for_new_preference, :integrate, when: :is_preference? do
|
6
|
+
Card.follow_caches_expired
|
7
|
+
end
|
8
|
+
|
9
|
+
def option_cards
|
10
|
+
Card::FollowOption.cards.compact
|
11
|
+
end
|
12
|
+
|
13
|
+
def options_rule_card
|
14
|
+
Card.new(
|
15
|
+
name: "follow_options_card",
|
16
|
+
type_code: :pointer,
|
17
|
+
content: option_cards.map { |oc| "[[#{oc.name}]]" }.join("\n")
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_follow_item? condition
|
22
|
+
new_card? || !include_item?(condition)
|
23
|
+
end
|
24
|
+
|
25
|
+
def ok_to_update
|
26
|
+
permit :update
|
27
|
+
end
|
28
|
+
|
29
|
+
def ok_to_create
|
30
|
+
permit :create
|
31
|
+
end
|
32
|
+
|
33
|
+
def ok_to_delete
|
34
|
+
permit :delete
|
35
|
+
end
|
36
|
+
|
37
|
+
def permit action, verb=nil
|
38
|
+
if %i[create delete update].include?(action) && allowed_to_change_follow_status?
|
39
|
+
true
|
40
|
+
else
|
41
|
+
super action, verb
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def allowed_to_change_follow_status?
|
46
|
+
Auth.signed_in? &&
|
47
|
+
((user = rule_user) && Auth.current_id == user.id) || Auth.always_ok?
|
48
|
+
end
|
49
|
+
|
50
|
+
format :html do
|
51
|
+
# shows a follow item link for each of the current follow options
|
52
|
+
view :follow_status, cache: :never do
|
53
|
+
wrap { haml :follow_status }
|
54
|
+
end
|
55
|
+
|
56
|
+
# interface to view/alter a specific rule option
|
57
|
+
view :follow_item, cache: :never do
|
58
|
+
follow_item Env.params[:condition]
|
59
|
+
end
|
60
|
+
|
61
|
+
def follow_item condition, button=true
|
62
|
+
condition ||= "*always"
|
63
|
+
wrap do
|
64
|
+
card_form action: :update, success: { view: :follow_item } do
|
65
|
+
[
|
66
|
+
follow_item_hidden_tags(condition),
|
67
|
+
(follow_item_button(condition) if button),
|
68
|
+
follow_item_link(condition)
|
69
|
+
].compact
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def rule_form_args
|
75
|
+
super.merge "data-update-foreign-slot": ".card-slot.follow_section-view"
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def follow_item_hidden_tags condition
|
81
|
+
condkey = card.add_follow_item?(condition) ? :add_item : :drop_item
|
82
|
+
hidden_tags condition: condition, condkey => condition
|
83
|
+
end
|
84
|
+
|
85
|
+
def follow_item_button condition
|
86
|
+
action = card.add_follow_item?(condition) ? :add : :delete
|
87
|
+
button_tag type: :submit, "aria-label": "Left Align",
|
88
|
+
class: "btn-sm btn-item #{follow_item_button_class action}" do
|
89
|
+
follow_item_icon action
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def follow_item_button_class action
|
94
|
+
action == :add ? "btn-item-add" : "btn-item-delete btn-primary"
|
95
|
+
end
|
96
|
+
|
97
|
+
def follow_item_icon action
|
98
|
+
icon_tag(action == :add ? :add : :check)
|
99
|
+
end
|
100
|
+
|
101
|
+
def follow_item_link condition
|
102
|
+
link_to_card follow_item_link_target, follow_item_link_text(condition)
|
103
|
+
end
|
104
|
+
|
105
|
+
def follow_item_link_target
|
106
|
+
set = card.rule_set
|
107
|
+
setname = set.name
|
108
|
+
set.tag.codename == :self ? setname.left : setname.field("by name")
|
109
|
+
end
|
110
|
+
|
111
|
+
def follow_item_link_text condition
|
112
|
+
if (option_card = Card.fetch condition)
|
113
|
+
option_card.description card.rule_set
|
114
|
+
else
|
115
|
+
card.rule_set.follow_label
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
%h4
|
2
|
+
Get notified about changes
|
3
|
+
%ul.delete-list.list-group
|
4
|
+
- card.item_names.each do |option|
|
5
|
+
%li.list-group-item
|
6
|
+
= follow_item(option == "*never" ? "*always" : option)
|
7
|
+
= link_to_view :rule_form, "more options",
|
8
|
+
class: "btn update-follow-link slotter",
|
9
|
+
"data-card_key": card.set_prototype.key
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
# X+*followers provides a list of all users following X.
|
4
|
+
|
5
|
+
include_set Abstract::Pointer
|
6
|
+
|
7
|
+
format :html do
|
8
|
+
view :core, cache: :never do
|
9
|
+
super()
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def content
|
14
|
+
left ? item_names.to_pointer_content : ""
|
15
|
+
end
|
16
|
+
|
17
|
+
def item_names _args={}
|
18
|
+
left ? left.follow_set_card.prototype.follower_names : []
|
19
|
+
end
|
20
|
+
|
21
|
+
def virtual?
|
22
|
+
new?
|
23
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
def virtual?
|
2
|
+
new?
|
3
|
+
end
|
4
|
+
|
5
|
+
format :html do
|
6
|
+
view :core do
|
7
|
+
if card.left && Auth.signed_in?
|
8
|
+
render_rule_editor
|
9
|
+
else
|
10
|
+
nest Card.fetch(card.name.left, :followers), view: :titled, items: { view: :link }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
view :status do
|
15
|
+
if (rcard = current_follow_rule_card)
|
16
|
+
rcard.item_cards.map do |item|
|
17
|
+
%(<div class="alert alert-success" role="alert">
|
18
|
+
<strong>#{rcard.rule_set.follow_label}</strong>: #{item.title}
|
19
|
+
</div>)
|
20
|
+
end.join
|
21
|
+
else
|
22
|
+
"No following preference"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
view :one_line_content do
|
27
|
+
""
|
28
|
+
end
|
29
|
+
|
30
|
+
view :rule_editor, cache: :never do
|
31
|
+
rule_context = Card.fetch preference_name, new: { type_id: PointerID }
|
32
|
+
wrap_with :div, class: "edit-rule" do
|
33
|
+
follow_context = current_follow_rule_card || rule_context
|
34
|
+
subformat(follow_context).rule_form :open, rule_context, :modal
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def preference_name
|
39
|
+
set_name = card.left.follow_set_card.name
|
40
|
+
Card::Name[set_name, Auth.current.name, :follow]
|
41
|
+
end
|
42
|
+
|
43
|
+
def edit_rule_success
|
44
|
+
{ view: "status", id: card.name.url_key }
|
45
|
+
end
|
46
|
+
|
47
|
+
def current_follow_rule_card
|
48
|
+
card.left.preference :follow
|
49
|
+
end
|
50
|
+
end
|
data/set/self/always.rb
ADDED
data/set/self/created.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
include_set Abstract::FollowOption
|
2
|
+
|
3
|
+
restrictive_follow_opts position: 1
|
4
|
+
|
5
|
+
follower_candidate_ids do |card|
|
6
|
+
[card.creator_id]
|
7
|
+
end
|
8
|
+
|
9
|
+
def title
|
10
|
+
"Following content you created"
|
11
|
+
end
|
12
|
+
|
13
|
+
def label
|
14
|
+
"follow if I created"
|
15
|
+
end
|
16
|
+
|
17
|
+
def description set_card
|
18
|
+
"#{set_card.follow_label} I created"
|
19
|
+
end
|
data/set/self/edited.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
include_set Abstract::FollowOption
|
2
|
+
|
3
|
+
restrictive_follow_opts position: 2
|
4
|
+
|
5
|
+
follower_candidate_ids do |card|
|
6
|
+
# FIXME? - could optimize by not using cards table...
|
7
|
+
card.id ? Card.search(editor_of: card.id, return: :id) : []
|
8
|
+
end
|
9
|
+
|
10
|
+
def title
|
11
|
+
"Following content you edited"
|
12
|
+
end
|
13
|
+
|
14
|
+
def label
|
15
|
+
"follow if I edited"
|
16
|
+
end
|
17
|
+
|
18
|
+
def description set_card
|
19
|
+
"#{set_card.follow_label} I edited"
|
20
|
+
end
|
data/set/self/follow.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
setting_opts group: :other, position: 7, rule_type_editable: false, preference: true
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# DEPRECATED
|
2
|
+
#
|
3
|
+
# Despite its name (*follow defaults)card does not influence defaults for *follow rules.
|
4
|
+
# What it does is provide a mechanism (with interface) for updating all users so that
|
5
|
+
# they follow the items that are its content.
|
6
|
+
#
|
7
|
+
# PLAN:
|
8
|
+
# - actual defaults should be handled as much as possible with something like
|
9
|
+
# the *defaults rule
|
10
|
+
# - on the *admin page, we can have a link so sharks can update all the pristine cards
|
11
|
+
# to use whatever the actual defaults representation is (see previous point)
|
12
|
+
# - if you truly want to override existing follow rules, that may be monkey territory?
|
13
|
+
# - we will delete "*follow defaults" after the above are completed
|
14
|
+
|
15
|
+
event :update_follow_rules, :finalize, on: :save, when: :update_all_users do
|
16
|
+
Auth.as_bot do
|
17
|
+
Card.search(type: "user").each do |user|
|
18
|
+
follow_defaults.each do |set_card, option|
|
19
|
+
follow_rule = Card.fetch(set_card.follow_rule_name(user.name), new: {})
|
20
|
+
next unless follow_rule
|
21
|
+
|
22
|
+
follow_rule.drop_item "*never"
|
23
|
+
follow_rule.drop_item "*always"
|
24
|
+
follow_rule.add_item option
|
25
|
+
follow_rule.save!
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
Card.follow_caches_expired
|
30
|
+
end
|
31
|
+
|
32
|
+
def follow_defaults
|
33
|
+
item_names.map do |item|
|
34
|
+
if (set_card = Card.fetch item.to_name.left)&.type_code == :set
|
35
|
+
[set_card, follow_option(item)]
|
36
|
+
end
|
37
|
+
end.compact
|
38
|
+
end
|
39
|
+
|
40
|
+
def follow_option item
|
41
|
+
option_card =
|
42
|
+
Card.fetch(item.to_name.right) || Card[item.to_name.right.to_sym]
|
43
|
+
option_card.follow_option? ? option_card.name : "*always"
|
44
|
+
end
|
45
|
+
|
46
|
+
format :html do
|
47
|
+
view :edit, perms: :update, unknown: true do
|
48
|
+
frame_and_form :update, hidden: { success: "_self",
|
49
|
+
card: { update_all_users: false } } do
|
50
|
+
[
|
51
|
+
_render_content_formgroups,
|
52
|
+
_render_confirm_update_all,
|
53
|
+
_render_edit_buttons
|
54
|
+
]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
view :edit_buttons do
|
59
|
+
button_formgroup do
|
60
|
+
[submit_and_update_button, simple_submit_button, cancel_to_edit_button]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def submit_and_update_button
|
65
|
+
submit_button text: "Submit and update all users",
|
66
|
+
disable_with: "Updating", class: "follow-updater"
|
67
|
+
end
|
68
|
+
|
69
|
+
def simple_submit_button
|
70
|
+
button_tag "Submit", class: "follow"
|
71
|
+
end
|
72
|
+
|
73
|
+
def cancel_to_edit_button
|
74
|
+
cancel_button href: path(view: :edit, id: card.id)
|
75
|
+
end
|
76
|
+
|
77
|
+
view :confirm_update_all do
|
78
|
+
wrap do
|
79
|
+
alert "info" do
|
80
|
+
%(
|
81
|
+
<h1>Are you sure you want to change the default follow rules?</h1>
|
82
|
+
<p>You may choose to update all existing users.
|
83
|
+
This may take a while. </p>
|
84
|
+
)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
setting_opts group: :other, position: 5, rule_type_editable: false
|
data/set/self/never.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
def follow_label
|
2
|
+
follow_set_card.follow_label
|
3
|
+
end
|
4
|
+
|
5
|
+
def followed_by? user_id=nil
|
6
|
+
follow_set_card.all_members_followed_by? user_id
|
7
|
+
end
|
8
|
+
|
9
|
+
def follow_set_card
|
10
|
+
Card.fetch name, :type
|
11
|
+
end
|
12
|
+
|
13
|
+
def list_direct_followers?
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
format :html do
|
18
|
+
def related_by_type_items
|
19
|
+
super.unshift ["#{card.name} cards", [card, :type, :by_name], mark: :absolute]
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
card_reader :contextual_class
|
2
|
+
card_reader :disappear
|
3
|
+
card_reader :message
|
4
|
+
|
5
|
+
def deliver context
|
6
|
+
success.flash alert_message(context)
|
7
|
+
end
|
8
|
+
|
9
|
+
def alert_message context
|
10
|
+
mcard = message.present? ? message_card : self
|
11
|
+
format(:html).alert_message context, mcard
|
12
|
+
end
|
13
|
+
|
14
|
+
format :html do
|
15
|
+
def alert_message context, message_card
|
16
|
+
mformat = subformat message_card
|
17
|
+
alert card.alert_class, true, card.disappear? do
|
18
|
+
mformat.contextual_content context, view: alert_view(mformat)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def alert_view format
|
23
|
+
format.respond_to?(:notify) ? format.notify : :core
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def disappear?
|
28
|
+
disappear.present? ? disappear_card.checked? : true
|
29
|
+
end
|
30
|
+
|
31
|
+
def alert_class
|
32
|
+
contextual_class.present? ? contextual_class_card.item_name : :success
|
33
|
+
end
|
data/set/type/set.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
event :cache_expired_for_new_set, :store, on: :create do
|
2
|
+
Card.follow_caches_expired
|
3
|
+
end
|
4
|
+
|
5
|
+
def list_direct_followers?
|
6
|
+
true
|
7
|
+
end
|
8
|
+
|
9
|
+
def follow_label
|
10
|
+
if (klass = subclass_for_set)
|
11
|
+
klass.short_label name.left_name
|
12
|
+
else
|
13
|
+
""
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def follow_rule_name user=nil
|
18
|
+
Card::Name[[name, user, :follow].compact]
|
19
|
+
end
|
20
|
+
|
21
|
+
def followed_by? user_id=nil
|
22
|
+
all_members_followed_by? user_id
|
23
|
+
end
|
24
|
+
|
25
|
+
def follow_set_card
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def all_members_followed?
|
30
|
+
all_members_followed_by? Auth.current_id
|
31
|
+
end
|
32
|
+
|
33
|
+
def all_members_followed_by? user_id=nil
|
34
|
+
return false unless prototype.followed_by?(user_id)
|
35
|
+
|
36
|
+
directly_followed_by?(user_id) || broader_set_followed_by?(user_id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def broader_set_followed_by? user_id
|
40
|
+
broader_sets.find do |set_name|
|
41
|
+
Card.fetch(set_name)&.directly_followed_by? user_id
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def directly_followed?
|
46
|
+
directly_followed_by? Auth.current_id
|
47
|
+
end
|
48
|
+
|
49
|
+
def directly_followed_by? user_id=nil
|
50
|
+
return true if user_id && follow_rule?(user_id)
|
51
|
+
|
52
|
+
follow_rule?
|
53
|
+
end
|