social_stream 0.0.1
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/Gemfile +16 -0
- data/Gemfile.lock +117 -0
- data/LICENSE +20 -0
- data/README.rdoc +63 -0
- data/Rakefile +43 -0
- data/app/controllers/home_controller.rb +3 -0
- data/app/helpers/activities_helper.rb +10 -0
- data/app/models/activity.rb +75 -0
- data/app/models/activity_object.rb +18 -0
- data/app/models/activity_object_activity.rb +4 -0
- data/app/models/activity_verb.rb +14 -0
- data/app/models/actor.rb +31 -0
- data/app/models/permission.rb +4 -0
- data/app/models/relation.rb +73 -0
- data/app/models/relation_permission.rb +4 -0
- data/app/models/tie.rb +207 -0
- data/app/models/user.rb +82 -0
- data/app/views/activities/_activity.html.erb +5 -0
- data/app/views/activities/_activity_options.html.erb +8 -0
- data/app/views/activities/_jquery.html.erb +52 -0
- data/app/views/activities/_new.html.erb +13 -0
- data/app/views/activities/_root_activity.html.erb +44 -0
- data/app/views/activities/_subactivity.html.erb +23 -0
- data/app/views/activities/_subactivity_options.html.erb +7 -0
- data/app/views/devise/registrations/new.html.erb +21 -0
- data/app/views/home/_activities.html.erb +19 -0
- data/app/views/home/_location.html.erb +3 -0
- data/app/views/home/index.html.erb +4 -0
- data/app/views/ties/_pending.html.erb +33 -0
- data/config/locales/en.yml +29 -0
- data/lib/generators/social_stream/USAGE +9 -0
- data/lib/generators/social_stream/install_generator.rb +30 -0
- data/lib/generators/social_stream/templates/initializer.rb +14 -0
- data/lib/generators/social_stream/templates/migration.rb +131 -0
- data/lib/generators/social_stream/templates/seeds.yml +29 -0
- data/lib/social_stream.rb +49 -0
- data/lib/social_stream/models/activity_object.rb +59 -0
- data/lib/social_stream/models/actor.rb +30 -0
- data/lib/social_stream/models/supertype.rb +41 -0
- data/lib/social_stream/rails.rb +13 -0
- data/lib/social_stream/rails/common.rb +34 -0
- data/lib/social_stream/rails/engine.rb +7 -0
- data/lib/social_stream/rails/railtie.rb +7 -0
- data/lib/social_stream/rails/routes.rb +17 -0
- data/lib/social_stream/seed.rb +47 -0
- data/lib/social_stream/version.rb +3 -0
- metadata +144 -0
data/app/models/tie.rb
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
# A link between two actors in a relation.
|
2
|
+
#
|
3
|
+
# The first Actor is the sender of the Tie. The second Actor is the receiver of the Tie.
|
4
|
+
#
|
5
|
+
# == Ties and Activities
|
6
|
+
# Activities are attached to ties.
|
7
|
+
# * The sender actor is the author of the Activity. It is the user that uploads
|
8
|
+
# a resource to the website or the social entity that originates the activity.
|
9
|
+
# * The receiver is the target of the Activity. The wall-profile of an actor is
|
10
|
+
# composed by the resources assigned to the ties in which the actor is the receiver.
|
11
|
+
# * The Relation sets up the mode in which the Activity is shared. It sets the rules,
|
12
|
+
# or permissions, by which actors have access to the Activity.
|
13
|
+
#
|
14
|
+
# == Inverse ties
|
15
|
+
# Relations can have its inverse. When a tie is establised, an inverse tie is establised
|
16
|
+
# as well.
|
17
|
+
#
|
18
|
+
# == Scopes
|
19
|
+
# There are several scopes defined:
|
20
|
+
# * sent_by(actor), ties whose sender is actor
|
21
|
+
# * received_by(actor), ties whose receiver is actor
|
22
|
+
# * sent_or_received_by(actor), the union of the former
|
23
|
+
# * inverse(tie), the inverse of tie
|
24
|
+
#
|
25
|
+
class Tie < ActiveRecord::Base
|
26
|
+
# Avoids loops at create_inverse after save callback
|
27
|
+
attr_accessor :_without_inverse
|
28
|
+
attr_protected :_without_inverse
|
29
|
+
|
30
|
+
# Facilitates relation assigment along with find_relation callback
|
31
|
+
attr_accessor :relation_name
|
32
|
+
|
33
|
+
validates_presence_of :sender_id, :receiver_id, :relation_id
|
34
|
+
|
35
|
+
belongs_to :sender,
|
36
|
+
:class_name => "Actor",
|
37
|
+
:include => SocialStream.actors
|
38
|
+
belongs_to :receiver,
|
39
|
+
:class_name => "Actor",
|
40
|
+
:include => SocialStream.actors
|
41
|
+
belongs_to :relation
|
42
|
+
|
43
|
+
has_many :activities
|
44
|
+
|
45
|
+
scope :sent_by, lambda { |a|
|
46
|
+
where(:sender_id => Actor_id(a))
|
47
|
+
}
|
48
|
+
|
49
|
+
scope :received_by, lambda { |a|
|
50
|
+
where(:receiver_id => Actor_id(a))
|
51
|
+
}
|
52
|
+
|
53
|
+
scope :sent_or_received_by, lambda { |a|
|
54
|
+
where(arel_table[:sender_id].eq(Actor_id(a)).
|
55
|
+
or(arel_table[:receiver_id].eq(Actor_id(a))))
|
56
|
+
|
57
|
+
}
|
58
|
+
|
59
|
+
scope :inverse, lambda { |t|
|
60
|
+
sent_by(t.receiver).
|
61
|
+
received_by(t.sender).
|
62
|
+
where(:relation_id => t.relation.inverse_id)
|
63
|
+
}
|
64
|
+
|
65
|
+
def sender_subject
|
66
|
+
sender.try(:subject)
|
67
|
+
end
|
68
|
+
|
69
|
+
def receiver_subject
|
70
|
+
receiver.try(:subject)
|
71
|
+
end
|
72
|
+
|
73
|
+
before_validation :find_relation
|
74
|
+
|
75
|
+
scope :pending, includes(:relation) & Relation.request
|
76
|
+
|
77
|
+
# The set of ties between sender and receiver
|
78
|
+
#
|
79
|
+
def relation_set(r = :nil)
|
80
|
+
set = self.class.where(:sender_id => sender_id,
|
81
|
+
:receiver_id => receiver_id)
|
82
|
+
|
83
|
+
case r
|
84
|
+
when :nil
|
85
|
+
set
|
86
|
+
when String
|
87
|
+
set.where(:relation_id => relation.mode.find_by_name(r))
|
88
|
+
else
|
89
|
+
set.where(:relation_id => r)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# The tie with relation r inside this relation_set
|
94
|
+
def related(r)
|
95
|
+
relation_set(r).first
|
96
|
+
end
|
97
|
+
|
98
|
+
after_create :complete_weak_set, :create_inverse
|
99
|
+
|
100
|
+
# Access Control
|
101
|
+
|
102
|
+
scope :with_permissions, lambda { |action, object|
|
103
|
+
includes(:relation => :permissions).
|
104
|
+
where('permissions.action' => action).
|
105
|
+
where('permissions.object' => object)
|
106
|
+
}
|
107
|
+
|
108
|
+
scope :parameterized, lambda { |tie|
|
109
|
+
where(tie_conditions(tie).
|
110
|
+
or(weak_set_conditions(tie)).
|
111
|
+
or(group_set_conditions(tie)).
|
112
|
+
or(weak_group_set_conditions(tie)))
|
113
|
+
}
|
114
|
+
|
115
|
+
scope :access_set, lambda { |tie, action, object|
|
116
|
+
with_permissions(action, object).
|
117
|
+
parameterized(tie)
|
118
|
+
}
|
119
|
+
|
120
|
+
|
121
|
+
def permissions(user, action, object)
|
122
|
+
self.class.
|
123
|
+
sent_by(user).
|
124
|
+
access_set(self, action, object)
|
125
|
+
end
|
126
|
+
|
127
|
+
def permission?(user, action, object)
|
128
|
+
permissions(user, action, object).any?
|
129
|
+
end
|
130
|
+
|
131
|
+
class << self
|
132
|
+
def tie_conditions(t)
|
133
|
+
arel_table[:sender_id].eq(t.sender_id).and(
|
134
|
+
arel_table[:receiver_id].eq(t.receiver_id)).and(
|
135
|
+
arel_table[:relation_id].eq(t.relation_id)).and(
|
136
|
+
Permission.arel_table[:parameter].eq('tie'))
|
137
|
+
end
|
138
|
+
|
139
|
+
def weak_set_conditions(t)
|
140
|
+
arel_table[:sender_id].eq(t.sender_id).and(
|
141
|
+
arel_table[:receiver_id].eq(t.receiver_id)).and(
|
142
|
+
arel_table[:relation_id].in(t.relation.stronger_or_equal)).and(
|
143
|
+
Permission.arel_table[:parameter].eq('weak_set'))
|
144
|
+
end
|
145
|
+
|
146
|
+
def group_set_conditions(t)
|
147
|
+
arel_table[:receiver_id].eq(t.receiver_id).and(
|
148
|
+
arel_table[:relation_id].eq(t.relation_id)).and(
|
149
|
+
Permission.arel_table[:parameter].eq('group_set'))
|
150
|
+
end
|
151
|
+
|
152
|
+
def weak_group_set_conditions(t)
|
153
|
+
arel_table[:receiver_id].eq(t.receiver_id).and(
|
154
|
+
arel_table[:relation_id].in(t.relation.stronger_or_equal)).and(
|
155
|
+
Permission.arel_table[:parameter].eq('weak_group_set'))
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
# Before validation callback
|
162
|
+
# Infers relation from its name and the type of the actors
|
163
|
+
def find_relation
|
164
|
+
if relation_name.present?
|
165
|
+
self.relation = Relation.mode(sender_subject.class.to_s,
|
166
|
+
receiver_subject.class.to_s).
|
167
|
+
find_by_name(relation_name)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# After create callback
|
172
|
+
# Creates ties with a weaker relations in the strength hierarchy of this tie
|
173
|
+
def complete_weak_set
|
174
|
+
relation.weaker.each do |r|
|
175
|
+
if relation_set(r).blank?
|
176
|
+
t = relation_set.build :relation => r
|
177
|
+
t._without_inverse = true
|
178
|
+
t.save!
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# After create callback
|
184
|
+
# Creates a the inverse of this tie
|
185
|
+
def create_inverse
|
186
|
+
if !_without_inverse &&
|
187
|
+
relation.inverse.present? &&
|
188
|
+
Tie.inverse(self).blank?
|
189
|
+
t = Tie.inverse(self).build
|
190
|
+
t._without_inverse = true
|
191
|
+
t.save!
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class << self
|
196
|
+
def Actor_id(a)
|
197
|
+
case a
|
198
|
+
when Integer
|
199
|
+
a
|
200
|
+
when Actor
|
201
|
+
a.id
|
202
|
+
else
|
203
|
+
a.actor.id
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
data/app/models/user.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
class User < ActiveRecord::Base
|
2
|
+
# Include default devise modules. Others available are:
|
3
|
+
# :token_authenticatable, :confirmable, :lockable, :timeoutable, :validatable
|
4
|
+
devise :database_authenticatable, :registerable,
|
5
|
+
:recoverable, :rememberable, :trackable
|
6
|
+
|
7
|
+
# Setup accessible (or protected) attributes for your model
|
8
|
+
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
|
9
|
+
|
10
|
+
validates_presence_of :name, :email
|
11
|
+
validates_format_of :email, :with => Devise.email_regexp, :allow_blank => true
|
12
|
+
# TODO: uniqueness of email, which is in actor
|
13
|
+
|
14
|
+
with_options :if => :password_required? do |v|
|
15
|
+
v.validates_presence_of :password
|
16
|
+
v.validates_confirmation_of :password
|
17
|
+
v.validates_length_of :password, :within => Devise.password_length, :allow_blank => true
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
# From devise
|
23
|
+
def password_required?
|
24
|
+
!persisted? || !password.nil? || !password_confirmation.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
%w( email permalink name ).each do |a|
|
29
|
+
eval <<-EOS
|
30
|
+
def find_by_#{ a }(#{ a }) # def find_by_email(email)
|
31
|
+
find :first, # find(:first,
|
32
|
+
:include => :actor, # :include => :actor,
|
33
|
+
:conditions => # :conditions =>
|
34
|
+
{ 'actors.#{ a }' => #{ a } } # { 'actors.email' => email }
|
35
|
+
end # end
|
36
|
+
EOS
|
37
|
+
end
|
38
|
+
|
39
|
+
# Overwrite devise default find method to support login with email,
|
40
|
+
# presence ID and login
|
41
|
+
def find_for_authentication(conditions)
|
42
|
+
if ( login = conditions[:email] ).present?
|
43
|
+
if login =~ /@/
|
44
|
+
find_by_email(login)
|
45
|
+
else
|
46
|
+
find_by_permalink(login)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_or_initialize_with_error_by(attribute, value, error=:invalid)
|
54
|
+
if attribute == :email
|
55
|
+
find_or_initialize_with_error_by_email(value, error)
|
56
|
+
else
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Overwrite devise default method to support finding with actor.email
|
62
|
+
def find_or_initialize_with_error_by_email(value, error)
|
63
|
+
if value.present?
|
64
|
+
record = find_by_email(value)
|
65
|
+
end
|
66
|
+
|
67
|
+
unless record
|
68
|
+
record = new
|
69
|
+
|
70
|
+
if value.present?
|
71
|
+
record.email = value
|
72
|
+
else
|
73
|
+
error = :blank
|
74
|
+
end
|
75
|
+
|
76
|
+
record.errors.add(:email, error)
|
77
|
+
end
|
78
|
+
|
79
|
+
record
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<div class="activity_options">
|
2
|
+
<ul class="activity_options" >
|
3
|
+
<li><div class="post_time_ago"><%= t('time.ago', :time => time_ago_in_words(activity.created_at)) %></div></li>
|
4
|
+
<li><div class="verb_comment"> · <%= link_to t('activity.to_comment'), "#", :class => "to_comment" %> </div></li>
|
5
|
+
<li><div class="verb_like" id="like_<%= dom_id(activity) %>"> · <%= like_activity(activity)%></div></li>
|
6
|
+
<li><div class="verb_delete"> · <%= link_to t('activity.delete'), activity.direct_object , :confirm => t('activity.confirm_delete'), :method => :delete, :remote => true %> </div></li>
|
7
|
+
</ul>
|
8
|
+
</div>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
//javascript for main activities input
|
2
|
+
$("#activities_share_btn").hide();
|
3
|
+
title = "<%= t('activity.input') %>";
|
4
|
+
$("#input_activities").click(function(){
|
5
|
+
if(this.value == title){
|
6
|
+
this.value="";
|
7
|
+
$("#activities_share_btn").show();
|
8
|
+
}
|
9
|
+
});
|
10
|
+
$("#input_activities").blur(function(){
|
11
|
+
if(this.value == ""){
|
12
|
+
this.value= title;
|
13
|
+
$("#activities_share_btn").hide();
|
14
|
+
}
|
15
|
+
});
|
16
|
+
|
17
|
+
//javascript for comments
|
18
|
+
$(".input_new_comments").val("<%= t('comment.input') %>");
|
19
|
+
$(".activities_comment_btn").hide();
|
20
|
+
|
21
|
+
//usa livequery para usar el javascript luego de una accion con AJAX
|
22
|
+
$(".input_new_comments").livequery("click",function(){
|
23
|
+
if(this.value == "<%= t('comment.input') %>"){
|
24
|
+
$(this).val("");
|
25
|
+
}
|
26
|
+
$(".activities_comment_btn").hide();
|
27
|
+
$(this).parent(".new_comment").children(".activities_comment_btn").show();
|
28
|
+
});
|
29
|
+
$(".input_new_comments").livequery("blur",function(){
|
30
|
+
if(this.value == ""){
|
31
|
+
$(".activities_comment_btn").hide();
|
32
|
+
$(this).val("<%= t('comment.input') %>");
|
33
|
+
}
|
34
|
+
});
|
35
|
+
|
36
|
+
$(".input_new_comments").click(function(){
|
37
|
+
if(this.value == "<%= t('comment.input') %>"){
|
38
|
+
$(this).val("");
|
39
|
+
}
|
40
|
+
$(".activities_comment_btn").hide();
|
41
|
+
$(this).parent(".new_comment").children(".activities_comment_btn").show();
|
42
|
+
});
|
43
|
+
|
44
|
+
//javascript for tocomment option
|
45
|
+
$(".to_comment").livequery("click", function(){
|
46
|
+
$(this).parents(".activity_content").find(".input_new_comments").click();
|
47
|
+
return false;
|
48
|
+
});
|
49
|
+
$(".to_comment").livequery("blur", function(){
|
50
|
+
$(this).parents(".activity_content").find(".input_new_comments").blur();
|
51
|
+
return false;
|
52
|
+
});
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<div id="activities_header" class="content_size">
|
2
|
+
<%= form_for Post.new(:text => t('activity.input'),
|
3
|
+
:_activity_tie_id => current_tie.id),
|
4
|
+
:remote => true do |f| %>
|
5
|
+
<%= f.hidden_field :_activity_tie_id %>
|
6
|
+
<%= f.text_field :text, :id => "input_activities", :size => 85 %>
|
7
|
+
<div id="activities_share_btn">
|
8
|
+
<%= image_submit_tag "buttons/btn_security.png" %>
|
9
|
+
<%= image_submit_tag "buttons/btn_share.png" %>
|
10
|
+
</div>
|
11
|
+
<% end -%>
|
12
|
+
</div>
|
13
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<%= div_for root_activity do %>
|
2
|
+
<div class="actor_logo">
|
3
|
+
<%= link_to image_tag(root_activity.sender_subject.logo,
|
4
|
+
:size => "50x50",
|
5
|
+
:alt => root_activity.sender_subject.name),
|
6
|
+
root_activity.sender_subject %>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div class="activity_content">
|
10
|
+
<div class="actor_name">
|
11
|
+
<%= link_to(root_activity.sender_subject.name, root_activity.sender_subject) %>
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<div class="activity_objects">
|
15
|
+
<%= render root_activity.activity_objects %>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<%= render :partial => 'activities/activity_options',
|
19
|
+
:locals => { :activity => root_activity } %>
|
20
|
+
|
21
|
+
<div class="activity_comments" id="comments_<%= dom_id(root_activity) %>">
|
22
|
+
<%= render root_activity.comments %>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<div class="activity_new_comment">
|
26
|
+
<%= form_for Comment.new(:text => t('comment.input'),
|
27
|
+
:_activity_tie_id => current_tie.id,
|
28
|
+
:_activity_parent_id => root_activity.id),
|
29
|
+
:remote => true do |f| %>
|
30
|
+
<%= f.hidden_field :_activity_tie_id %>
|
31
|
+
<%= f.hidden_field :_activity_parent_id %>
|
32
|
+
<%= f.text_field :text, :class =>"input_new_comments" %>
|
33
|
+
<div class="activities_comment_btn">
|
34
|
+
<div class="activities_security"></div>
|
35
|
+
<%= image_submit_tag "buttons/btn_share.png" %>
|
36
|
+
</div>
|
37
|
+
<% end %>
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
<div class="space_comments"></div>
|
41
|
+
<div class="space_activities">
|
42
|
+
<div class="space_sub"></div>
|
43
|
+
</div>
|
44
|
+
<% end %>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<%= div_for subactivity, :class => "subactivity" do %>
|
2
|
+
<div class="actor_logo_subactivity">
|
3
|
+
<%= link_to image_tag(subactivity.sender_subject.logo,
|
4
|
+
:size => "30x30",
|
5
|
+
:alt => subactivity.sender_subject.name),
|
6
|
+
subactivity.sender_subject %>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div class="activity_content">
|
10
|
+
<div class="actor_name-activity_objects">
|
11
|
+
<%= link_to(subactivity.sender_subject.name, subactivity.sender_subject) %>
|
12
|
+
<span class="subactivity_objects">
|
13
|
+
<%= render subactivity.activity_objects %>
|
14
|
+
</span>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<%= render :partial => 'activities/subactivity_options',
|
18
|
+
:locals => { :activity => subactivity } %>
|
19
|
+
|
20
|
+
</div>
|
21
|
+
<% end %>
|
22
|
+
<div class="space_comments">
|
23
|
+
</div>
|