social_stream-base 0.19.2 → 0.20.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.
- data/app/assets/javascripts/social_stream.follow.js +23 -0
- data/app/assets/javascripts/social_stream.wall.js.erb +3 -3
- data/app/controllers/search_controller.rb +30 -16
- data/app/helpers/location_helper.rb +1 -44
- data/app/helpers/search_helper.rb +7 -7
- data/app/models/activity.rb +0 -21
- data/app/models/activity_action.rb +8 -0
- data/app/models/activity_object.rb +55 -25
- data/app/models/activity_object_audience.rb +8 -0
- data/app/models/actor.rb +9 -0
- data/app/models/comment.rb +1 -3
- data/app/models/group.rb +3 -0
- data/app/models/post.rb +1 -3
- data/app/models/relation.rb +5 -0
- data/app/models/relation/follow.rb +6 -7
- data/app/models/relation/public.rb +5 -0
- data/app/models/relation/single.rb +11 -1
- data/app/views/activities/_new.html.erb +1 -1
- data/app/views/contacts/_link_follow.html.erb +8 -4
- data/app/views/followers/update.js.erb +2 -0
- data/app/views/layouts/_search.html.erb +2 -2
- data/app/views/search/_extended_search.html.erb +17 -17
- data/app/views/search/_form.html.erb +4 -4
- data/app/views/search/_header_search.html.erb +2 -2
- data/app/views/search/index.html.erb +1 -1
- data/app/views/search/index.js.erb +4 -4
- data/db/migrate/20120403175913_create_activity_object_audiences.rb +34 -0
- data/db/migrate/20120411132550_add_visit_count_to_activity_object.rb +9 -0
- data/db/migrate/20120411151413_relation_public_permissions.rb +8 -0
- data/lib/generators/social_stream/base/templates/initializer.rb +3 -0
- data/lib/social_stream-base.rb +2 -1
- data/lib/social_stream/ability/base.rb +14 -8
- data/lib/social_stream/base/dependencies.rb +1 -0
- data/lib/social_stream/base/thinking-sphinx.rb +18 -0
- data/lib/social_stream/base/version.rb +1 -1
- data/lib/social_stream/controllers/objects.rb +6 -0
- data/lib/social_stream/test_helpers/controllers.rb +1 -1
- data/lib/social_stream/views/location.rb +48 -0
- data/lib/tasks/db/populate.rake +1 -1
- data/lib/thinking-sphinx/social_stream.rb +3 -0
- data/spec/controllers/groups_controller_spec.rb +6 -6
- data/spec/controllers/posts_controller_spec.rb +6 -6
- data/spec/controllers/users_controller_spec.rb +4 -4
- data/spec/dummy/config/initializers/social_stream.rb +3 -0
- data/spec/factories/activity.rb +24 -1
- data/spec/factories/post.rb +1 -1
- data/spec/models/activity_authorization_spec.rb +1 -1
- data/spec/models/post_spec.rb +0 -4
- data/spec/models/relation_follow_spec.rb +8 -0
- metadata +73 -64
@@ -0,0 +1,23 @@
|
|
1
|
+
SocialStream.Follow = (function(SS, $, undefined){
|
2
|
+
var initButtons = function(){
|
3
|
+
$(".following-button").mouseenter(function(){
|
4
|
+
$(this).hide();
|
5
|
+
$(this).siblings(".unfollow-button").show();
|
6
|
+
});
|
7
|
+
|
8
|
+
$(".unfollow-button").mouseleave(function(){
|
9
|
+
$(this).hide();
|
10
|
+
$(this).siblings(".following-button").show();
|
11
|
+
});
|
12
|
+
|
13
|
+
$(".unfollow-button").hide();
|
14
|
+
}
|
15
|
+
|
16
|
+
$(function(){
|
17
|
+
SocialStream.Follow.initButtons();
|
18
|
+
});
|
19
|
+
|
20
|
+
return {
|
21
|
+
initButtons: initButtons
|
22
|
+
};
|
23
|
+
})(SocialStream, jQuery);
|
@@ -91,7 +91,7 @@ SocialStream.Wall = (function(SS, $, undefined){
|
|
91
91
|
});
|
92
92
|
$("#masterSubmitButton").val(I18n.t('activity.sending') + '...').attr("disabled", true);
|
93
93
|
|
94
|
-
// Add
|
94
|
+
// Add relation_ids[] parameter from authorization selector
|
95
95
|
$('.liveAdded').remove();
|
96
96
|
|
97
97
|
var currentForm = $(this);
|
@@ -102,7 +102,7 @@ SocialStream.Wall = (function(SS, $, undefined){
|
|
102
102
|
.addClass('liveAdded')
|
103
103
|
.attr('type', 'hidden')
|
104
104
|
.attr('name', currentForm
|
105
|
-
.attr('id').split('_')[1]+'[
|
105
|
+
.attr('id').split('_')[1]+'[relation_ids][]')
|
106
106
|
.val($(this).attr('value')));
|
107
107
|
});
|
108
108
|
return true;
|
@@ -128,7 +128,7 @@ SocialStream.Wall = (function(SS, $, undefined){
|
|
128
128
|
$(this).removeAttr("readonly").val("").blur();
|
129
129
|
});
|
130
130
|
//New comments
|
131
|
-
$('.input_new_comments').each(function() {
|
131
|
+
$('.input_new_comments').each(function() {
|
132
132
|
$(this).removeAttr("readonly").val("");
|
133
133
|
});
|
134
134
|
}
|
@@ -6,20 +6,35 @@ class SearchController < ApplicationController
|
|
6
6
|
RESULTS_SEARCH_PER_PAGE=12
|
7
7
|
MIN_QUERY=2
|
8
8
|
def index
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
@search_result =
|
10
|
+
if params[:q].blank? || too_short_query
|
11
|
+
[]
|
12
|
+
elsif params[:mode].eql? "header_search"
|
13
|
+
search :quick
|
14
|
+
elsif params[:type].present?
|
15
|
+
focus_search
|
16
16
|
else
|
17
|
-
|
18
|
-
@search_result = focus_search
|
19
|
-
else
|
20
|
-
@search_result = search :extended
|
21
|
-
end
|
17
|
+
search :extended
|
22
18
|
end
|
19
|
+
|
20
|
+
respond_to do |format|
|
21
|
+
format.html {
|
22
|
+
if params[:mode] == "header_search"
|
23
|
+
render :partial => "header_search"
|
24
|
+
end
|
25
|
+
}
|
26
|
+
|
27
|
+
format.json {
|
28
|
+
json_obj = (
|
29
|
+
params[:type].present? ?
|
30
|
+
{ params[:type].pluralize => @search_result } :
|
31
|
+
@search_result
|
32
|
+
)
|
33
|
+
|
34
|
+
render :json => json_obj
|
35
|
+
}
|
36
|
+
|
37
|
+
format.js
|
23
38
|
end
|
24
39
|
end
|
25
40
|
|
@@ -43,7 +58,7 @@ class SearchController < ApplicationController
|
|
43
58
|
end
|
44
59
|
|
45
60
|
def focus_search
|
46
|
-
@search_class_sym = params[:
|
61
|
+
@search_class_sym = params[:type].singularize.to_sym unless params[:type].blank?
|
47
62
|
search_class = @search_class_sym.to_s.classify.constantize
|
48
63
|
result = ThinkingSphinx.search(get_search_query, :classes => [search_class])
|
49
64
|
result = authorization_filter result
|
@@ -51,13 +66,13 @@ class SearchController < ApplicationController
|
|
51
66
|
end
|
52
67
|
|
53
68
|
def too_short_query
|
54
|
-
bare_query = strip_tags(params[:
|
69
|
+
bare_query = strip_tags(params[:q]) unless bare_query.html_safe?
|
55
70
|
return bare_query.strip.size < MIN_QUERY
|
56
71
|
end
|
57
72
|
|
58
73
|
def get_search_query
|
59
74
|
search_query = ""
|
60
|
-
param = strip_tags(params[:
|
75
|
+
param = strip_tags(params[:q]) || ""
|
61
76
|
bare_query = param unless bare_query.html_safe?
|
62
77
|
search_query_words = bare_query.strip.split
|
63
78
|
search_query_words.each_index do |i|
|
@@ -70,7 +85,6 @@ class SearchController < ApplicationController
|
|
70
85
|
def authorization_filter results
|
71
86
|
filtered_results = Array.new
|
72
87
|
results.each do |result|
|
73
|
-
puts result
|
74
88
|
if result.is_a? SocialStream::Models::Object
|
75
89
|
filtered_results << result if can? :read, result
|
76
90
|
else
|
@@ -1,46 +1,3 @@
|
|
1
1
|
module LocationHelper
|
2
|
-
|
3
|
-
# Renders the location stack for your view. You can add as many stack levels as you wish.
|
4
|
-
#
|
5
|
-
# Usage:
|
6
|
-
# <%= location(level1,leve2,level3,level4,....) %>
|
7
|
-
#
|
8
|
-
# Output:
|
9
|
-
# base > level1 > level2 > level3 > level 4
|
10
|
-
#
|
11
|
-
# Default configuration:
|
12
|
-
# base => "You are here" ("location.base" on config/locales)
|
13
|
-
# separator => ">" ("location.separator" on config/locales)
|
14
|
-
#
|
15
|
-
# Styles and HTML wrapping:
|
16
|
-
# partial => location/_location.html.erb
|
17
|
-
#
|
18
|
-
# Example:
|
19
|
-
# Render a location with two leves depth:
|
20
|
-
#
|
21
|
-
# <%= location(link_to(leve1.name, level1.url),link_to(leve2.name, level2.url)) %>
|
22
|
-
#
|
23
|
-
def location(*stack)
|
24
|
-
|
25
|
-
location_body = render :partial => "location/location_body", :locals=>{:stack => stack}
|
26
|
-
|
27
|
-
location_div = capture do
|
28
|
-
render :partial => "location/location", :locals=>{:location_body => location_body}
|
29
|
-
end
|
30
|
-
|
31
|
-
case request.format
|
32
|
-
when Mime::JS
|
33
|
-
response = <<-EOJ
|
34
|
-
|
35
|
-
$('#map_location').html("#{ escape_javascript(location_div) }");
|
36
|
-
EOJ
|
37
|
-
|
38
|
-
response.html_safe
|
39
|
-
else
|
40
|
-
content_for(:location) do
|
41
|
-
location_div
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
2
|
+
include SocialStream::Views::Location
|
46
3
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module SearchHelper
|
2
2
|
def too_short_query?
|
3
|
-
return true if params[:
|
4
|
-
bare_query = strip_tags(params[:
|
3
|
+
return true if params[:q].blank?
|
4
|
+
bare_query = strip_tags(params[:q]) unless bare_query.html_safe?
|
5
5
|
return bare_query.strip.size < SearchController::MIN_QUERY
|
6
6
|
end
|
7
7
|
|
@@ -26,15 +26,15 @@ module SearchHelper
|
|
26
26
|
|
27
27
|
def get_search_query_words
|
28
28
|
search_query = ""
|
29
|
-
bare_query = strip_tags(params[:
|
29
|
+
bare_query = strip_tags(params[:q]) unless bare_query.html_safe?
|
30
30
|
return bare_query.strip.split
|
31
31
|
end
|
32
32
|
|
33
33
|
def search_class(type, model_sym)
|
34
34
|
case type
|
35
35
|
when :selected
|
36
|
-
params[:
|
37
|
-
params[:
|
36
|
+
params[:type].present? &&
|
37
|
+
params[:type].eql?(model_sym.to_s) &&
|
38
38
|
'selected' || ''
|
39
39
|
when :disabled
|
40
40
|
search_results?(model_sym) &&
|
@@ -61,8 +61,8 @@ module SearchHelper
|
|
61
61
|
|
62
62
|
link_to_if results,
|
63
63
|
content_tag(:span, t("#{ model_sym }.title.other"), span_options),
|
64
|
-
search_path(:
|
65
|
-
:
|
64
|
+
search_path(:type => model_sym,
|
65
|
+
:q => params[:q]),
|
66
66
|
:remote => true
|
67
67
|
end
|
68
68
|
end
|
data/app/models/activity.rb
CHANGED
@@ -90,8 +90,6 @@ class Activity < ActiveRecord::Base
|
|
90
90
|
order("created_at desc")
|
91
91
|
}
|
92
92
|
|
93
|
-
before_validation :fill_relations
|
94
|
-
|
95
93
|
after_create :increment_like_count
|
96
94
|
after_destroy :decrement_like_count, :delete_notifications
|
97
95
|
|
@@ -325,25 +323,6 @@ class Activity < ActiveRecord::Base
|
|
325
323
|
|
326
324
|
private
|
327
325
|
|
328
|
-
# Before validation callback
|
329
|
-
#
|
330
|
-
# Fill the relations when posting to other subject's wall
|
331
|
-
def fill_relations
|
332
|
-
return if relation_ids.present?
|
333
|
-
|
334
|
-
self.relation_ids =
|
335
|
-
# FIXME: repeated in ActivityObject#_relation_ids
|
336
|
-
if SocialStream.relation_model == :custom
|
337
|
-
if channel.reflexive?
|
338
|
-
receiver.relation_customs.map(&:id)
|
339
|
-
else
|
340
|
-
receiver.relation_customs.allow(channel.author, 'create', 'activity').map(&:id)
|
341
|
-
end
|
342
|
-
else
|
343
|
-
Array.wrap Relation::Public.instance.id
|
344
|
-
end
|
345
|
-
end
|
346
|
-
|
347
326
|
#
|
348
327
|
# Get the email subject for the activity's notification
|
349
328
|
#
|
@@ -19,6 +19,14 @@ class ActivityAction < ActiveRecord::Base
|
|
19
19
|
where(:activity_object_id => ActivityObject.normalize_id(activity_object))
|
20
20
|
}
|
21
21
|
|
22
|
+
scope :authored_or_owned, where(arel_table[:author].eq(true).
|
23
|
+
or(arel_table[:user_author].eq(true)).
|
24
|
+
or(arel_table[:owner].eq(true)))
|
25
|
+
|
26
|
+
scope :authored_or_owned_by, lambda{ |subject|
|
27
|
+
authored_or_owned.sent_by(subject)
|
28
|
+
}
|
29
|
+
|
22
30
|
before_create :follow_by_author_and_owner
|
23
31
|
|
24
32
|
private
|
@@ -9,7 +9,6 @@
|
|
9
9
|
# Objects are added to +config/initializers/social_stream.rb+
|
10
10
|
#
|
11
11
|
class ActivityObject < ActiveRecord::Base
|
12
|
-
attr_writer :_relation_ids
|
13
12
|
attr_reader :_activity_parent_id
|
14
13
|
|
15
14
|
# ActivityObject is a supertype of SocialStream.objects
|
@@ -17,6 +16,9 @@ class ActivityObject < ActiveRecord::Base
|
|
17
16
|
|
18
17
|
acts_as_taggable
|
19
18
|
|
19
|
+
has_many :activity_object_audiences, :dependent => :destroy
|
20
|
+
has_many :relations, :through => :activity_object_audiences
|
21
|
+
|
20
22
|
has_many :activity_object_activities, :dependent => :destroy
|
21
23
|
has_many :activities, :through => :activity_object_activities
|
22
24
|
|
@@ -29,6 +31,11 @@ class ActivityObject < ActiveRecord::Base
|
|
29
31
|
:source => :actor,
|
30
32
|
:conditions => { 'activity_actions.follow' => true }
|
31
33
|
|
34
|
+
# Associations for indexing
|
35
|
+
has_many :author_action,
|
36
|
+
:class_name => "ActivityAction",
|
37
|
+
:conditions => { 'activity_actions.author' => true }
|
38
|
+
|
32
39
|
has_many :activity_object_properties,
|
33
40
|
:dependent => :destroy
|
34
41
|
has_many :object_properties,
|
@@ -42,7 +49,10 @@ class ActivityObject < ActiveRecord::Base
|
|
42
49
|
:through => :activity_object_holders,
|
43
50
|
:source => :activity_object
|
44
51
|
|
52
|
+
before_validation :fill_relation_ids, :if => lambda { |obj| obj.object_type != "Actor" }
|
53
|
+
|
45
54
|
validates_presence_of :object_type
|
55
|
+
validate :allowed_relations, :if => lambda { |obj| obj.object_type != "Actor" }
|
46
56
|
|
47
57
|
# TODO: This is currently defined in lib/social_stream/models/object.rb
|
48
58
|
#
|
@@ -110,6 +120,15 @@ class ActivityObject < ActiveRecord::Base
|
|
110
120
|
class_eval code, __FILE__, __LINE__ - code.lines.count - 2
|
111
121
|
end
|
112
122
|
|
123
|
+
# subject was the author, user author or owner of this {ActivityObject}?
|
124
|
+
def authored_or_owned_by?(subject)
|
125
|
+
return false if subject.blank?
|
126
|
+
|
127
|
+
received_actions.
|
128
|
+
merge(ActivityAction.authored_or_owned_by(subject)).
|
129
|
+
any?
|
130
|
+
end
|
131
|
+
|
113
132
|
# Was the author represented when this {ActivityObject} was created?
|
114
133
|
def represented_author?
|
115
134
|
author_id != user_author_id
|
@@ -146,28 +165,7 @@ class ActivityObject < ActiveRecord::Base
|
|
146
165
|
Activity.new :author => author,
|
147
166
|
:user_author => user_author,
|
148
167
|
:owner => owner,
|
149
|
-
:relation_ids =>
|
150
|
-
end
|
151
|
-
|
152
|
-
def _relation_ids
|
153
|
-
@_relation_ids ||=
|
154
|
-
if author.blank? || owner.blank?
|
155
|
-
nil
|
156
|
-
else
|
157
|
-
# FIXME: repeated in Activity#fill_relations
|
158
|
-
if SocialStream.relation_model == :custom
|
159
|
-
if author == owner
|
160
|
-
owner.relation_customs.map(&:id)
|
161
|
-
else
|
162
|
-
owner.
|
163
|
-
relation_customs.
|
164
|
-
allow(author, 'create', 'activity').
|
165
|
-
map(&:id)
|
166
|
-
end
|
167
|
-
else
|
168
|
-
Array.wrap Relation::Public.instance.id
|
169
|
-
end
|
170
|
-
end
|
168
|
+
:relation_ids => relation_ids
|
171
169
|
end
|
172
170
|
|
173
171
|
def _activity_parent
|
@@ -175,12 +173,44 @@ class ActivityObject < ActiveRecord::Base
|
|
175
173
|
end
|
176
174
|
|
177
175
|
def _activity_parent_id=(id)
|
178
|
-
self.
|
176
|
+
self.relation_ids = Activity.find(id).relation_ids
|
179
177
|
@_activity_parent_id = id
|
180
178
|
end
|
181
179
|
|
182
180
|
private
|
183
181
|
|
182
|
+
def fill_relation_ids
|
183
|
+
return if relation_ids.present? || author.blank? || owner.blank?
|
184
|
+
|
185
|
+
@valid_relations = true
|
186
|
+
|
187
|
+
self.relation_ids =
|
188
|
+
if SocialStream.relation_model == :custom
|
189
|
+
owner.
|
190
|
+
relations.
|
191
|
+
allowing('read', 'activity').
|
192
|
+
map(&:id)
|
193
|
+
else
|
194
|
+
Array.wrap Relation::Public.instance.id
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# validate method
|
199
|
+
#
|
200
|
+
# check relations are included in
|
201
|
+
def allowed_relations
|
202
|
+
return if @valid_relations
|
203
|
+
|
204
|
+
allowed_rels =
|
205
|
+
owner.relations.allowing('read', 'activity') +
|
206
|
+
Relation::Single.allowing('read', 'activity')
|
207
|
+
|
208
|
+
if (relation_ids - allowed_rels.map(&:id)).any?
|
209
|
+
errors.add(:relation_ids, "not allowed")
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
|
184
214
|
def create_post_activity
|
185
215
|
create_activity "post"
|
186
216
|
end
|
@@ -194,7 +224,7 @@ class ActivityObject < ActiveRecord::Base
|
|
194
224
|
:author_id => author_id,
|
195
225
|
:user_author => user_author,
|
196
226
|
:owner => owner,
|
197
|
-
:relation_ids =>
|
227
|
+
:relation_ids => relation_ids,
|
198
228
|
:parent_id => _activity_parent_id
|
199
229
|
|
200
230
|
a.activity_objects << self
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# Every {ActivityObject} ({Post}, {Comment}, etc.) is shared with one or more {Relation Relations}.
|
2
|
+
#
|
3
|
+
# Each {Relation} is equivalent to a set {Actor Actors}, which are the ones that have {Tie Ties}
|
4
|
+
# to that {Relation}, in other words, the contacts that were added to that {Relation}
|
5
|
+
class ActivityObjectAudience < ActiveRecord::Base
|
6
|
+
belongs_to :activity_object
|
7
|
+
belongs_to :relation
|
8
|
+
end
|
data/app/models/actor.rb
CHANGED
@@ -57,6 +57,10 @@ class Actor < ActiveRecord::Base
|
|
57
57
|
has_many :received_ties,
|
58
58
|
:through => :received_contacts,
|
59
59
|
:source => :ties
|
60
|
+
|
61
|
+
has_many :received_relations,
|
62
|
+
:through => :received_ties,
|
63
|
+
:source => :relation
|
60
64
|
|
61
65
|
has_many :senders,
|
62
66
|
:through => :received_contacts,
|
@@ -313,6 +317,11 @@ class Actor < ActiveRecord::Base
|
|
313
317
|
map(&:id)
|
314
318
|
end
|
315
319
|
|
320
|
+
# Does this {Actor} allow subject to perform action on object?
|
321
|
+
def allow?(subject, action, object)
|
322
|
+
ties_to(subject).with_permissions(action, object).any?
|
323
|
+
end
|
324
|
+
|
316
325
|
# The {Channel} of this {Actor} to self (totally close!)
|
317
326
|
def self_channel
|
318
327
|
Channel.find_or_create_by_author_id_and_user_author_id_and_owner_id id, id, id
|