card-mod-follow 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|