social_stream 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. data/.gitignore +1 -0
  2. data/README.rdoc +2 -3
  3. data/app/controllers/likes_controller.rb +2 -2
  4. data/app/controllers/messages_controller.rb +3 -0
  5. data/app/controllers/representations_controller.rb +8 -0
  6. data/app/controllers/ties_controller.rb +1 -1
  7. data/app/helpers/activities_helper.rb +2 -2
  8. data/app/helpers/ties_helper.rb +19 -16
  9. data/app/models/activity.rb +42 -7
  10. data/app/models/activity_object.rb +1 -0
  11. data/app/models/activity_verb.rb +5 -1
  12. data/app/models/actor.rb +113 -68
  13. data/app/models/group.rb +15 -1
  14. data/app/models/message.rb +2 -0
  15. data/app/models/permission.rb +20 -41
  16. data/app/models/profile.rb +1 -1
  17. data/app/models/relation.rb +34 -26
  18. data/app/models/representation.rb +35 -0
  19. data/app/models/tie.rb +47 -100
  20. data/app/models/user.rb +12 -17
  21. data/app/views/activities/_activities.html.erb +2 -2
  22. data/app/views/activities/_new.html.erb +6 -3
  23. data/app/views/activities/_options.html.erb +1 -1
  24. data/app/views/comments/_new.html.erb +2 -2
  25. data/app/views/frontpage/index.html.erb +0 -2
  26. data/app/views/groups/_group.html.erb +3 -3
  27. data/app/views/groups/_index.html.erb +3 -4
  28. data/app/views/groups/_middle_show.html.erb +7 -4
  29. data/app/views/groups/_new.html.erb +25 -0
  30. data/app/views/groups/_right_show.html.erb +4 -2
  31. data/app/views/groups/new.html.erb +1 -0
  32. data/app/views/groups/show.html.erb +1 -1
  33. data/app/views/home/_groups.html.erb +7 -5
  34. data/app/views/home/_location.html.erb +1 -1
  35. data/app/views/home/_options.html.erb +2 -2
  36. data/app/views/home/_right.html.erb +2 -2
  37. data/app/views/home/index.html.erb +6 -2
  38. data/app/views/layouts/_footer.html.erb +1 -1
  39. data/app/views/layouts/_header.erb +4 -1
  40. data/app/views/layouts/_representation.html.erb +20 -0
  41. data/app/views/{private_messages → messages}/_form.html.erb +4 -4
  42. data/app/views/{private_messages → messages}/_index.html.erb +1 -1
  43. data/app/views/{private_messages → messages}/_location.html.erb +1 -1
  44. data/app/views/messages/_message.html.erb +17 -0
  45. data/app/views/messages/_messages.html.erb +2 -0
  46. data/app/views/messages/edit.html.erb +6 -0
  47. data/app/views/{private_messages → messages}/index.html.erb +0 -0
  48. data/app/views/{private_messages → messages}/index.js.erb +0 -0
  49. data/app/views/{private_messages → messages}/new.html.erb +2 -2
  50. data/app/views/messages/show.html.erb +21 -0
  51. data/app/views/subjects/_contacts.html.erb +20 -0
  52. data/app/views/ties/_new.html.erb +6 -6
  53. data/app/views/ties/_pendings.html.erb +3 -3
  54. data/app/views/ties/_suggestions.html.erb +3 -3
  55. data/app/views/ties/_tie.html.erb +1 -1
  56. data/app/views/ties/create.js.erb +4 -5
  57. data/app/views/ties/new.js.erb +1 -1
  58. data/app/views/users/_groups.html.erb +5 -5
  59. data/app/views/users/_index.html.erb +3 -1
  60. data/app/views/users/_options.html.erb +1 -1
  61. data/app/views/users/_right_show.html.erb +3 -3
  62. data/app/views/users/show.html.erb +5 -3
  63. data/config/locales/en.yml +13 -19
  64. data/config/routes.rb +11 -4
  65. data/lib/generators/social_stream/install_generator.rb +2 -10
  66. data/lib/generators/social_stream/templates/initializer.rb +1 -1
  67. data/lib/generators/social_stream/templates/migration.rb +19 -24
  68. data/lib/generators/social_stream/templates/public/javascripts/jquery.js +6883 -0
  69. data/lib/generators/social_stream/templates/public/javascripts/rails.js +146 -0
  70. data/lib/generators/social_stream/templates/public/javascripts/ui.dropdownchecklist.js +3 -13
  71. data/lib/generators/social_stream/templates/public/stylesheets/header.css +5 -1
  72. data/lib/generators/social_stream/templates/public/{images → stylesheets/images}/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  73. data/lib/generators/social_stream/templates/public/{images → stylesheets/images}/ui-bg_flat_75_ffffff_40x100.png +0 -0
  74. data/lib/generators/social_stream/templates/public/{images → stylesheets/images}/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  75. data/lib/generators/social_stream/templates/public/{images → stylesheets/images}/ui-bg_glass_65_ffffff_1x400.png +0 -0
  76. data/lib/generators/social_stream/templates/public/{images → stylesheets/images}/ui-bg_glass_75_dadada_1x400.png +0 -0
  77. data/lib/generators/social_stream/templates/public/{images → stylesheets/images}/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  78. data/lib/generators/social_stream/templates/public/{images → stylesheets/images}/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  79. data/lib/generators/social_stream/templates/public/{images → stylesheets/images}/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  80. data/lib/generators/social_stream/templates/public/{images → stylesheets/images}/ui-icons_222222_256x240.png +0 -0
  81. data/lib/generators/social_stream/templates/public/{images → stylesheets/images}/ui-icons_2e83ff_256x240.png +0 -0
  82. data/lib/generators/social_stream/templates/public/{images → stylesheets/images}/ui-icons_454545_256x240.png +0 -0
  83. data/lib/generators/social_stream/templates/public/{images → stylesheets/images}/ui-icons_888888_256x240.png +0 -0
  84. data/lib/generators/social_stream/templates/public/{images → stylesheets/images}/ui-icons_cd0a0a_256x240.png +0 -0
  85. data/lib/generators/social_stream/templates/relations.yml +42 -0
  86. data/lib/social_stream/ability.rb +7 -6
  87. data/lib/social_stream/controllers/helpers.rb +35 -0
  88. data/lib/social_stream/models/{activity_object.rb → object.rb} +1 -1
  89. data/lib/social_stream/models/{actor.rb → subject.rb} +10 -30
  90. data/lib/social_stream/models/supertype.rb +5 -2
  91. data/lib/social_stream/rails.rb +25 -6
  92. data/lib/social_stream/relations.rb +46 -0
  93. data/lib/social_stream/version.rb +1 -1
  94. data/lib/social_stream.rb +13 -13
  95. data/lib/tasks/db/populate.rake +23 -29
  96. data/spec/controllers/groups_controller_spec.rb +2 -6
  97. data/spec/controllers/ties_controller_spec.rb +19 -0
  98. data/spec/controllers/users_controller_spec.rb +0 -2
  99. data/spec/dummy/config/application.rb +0 -1
  100. data/spec/dummy/config/initializers/social_stream.rb +14 -2
  101. data/spec/dummy/config/relations.yml +42 -0
  102. data/spec/dummy/db/schema.rb +9 -0
  103. data/spec/dummy/db/seeds.rb +0 -1
  104. data/spec/factories/activity.rb +2 -2
  105. data/spec/factories/actor.rb +2 -1
  106. data/spec/factories/group.rb +1 -0
  107. data/spec/factories/post.rb +1 -1
  108. data/spec/factories/tie.rb +16 -22
  109. data/spec/models/activity_spec.rb +27 -24
  110. data/spec/models/actor_spec.rb +3 -0
  111. data/spec/models/representation_spec.rb +16 -0
  112. data/spec/models/tie_spec.rb +49 -57
  113. data/spec/models/user_spec.rb +22 -0
  114. data/spec/support/db.rb +1 -1
  115. metadata +47 -45
  116. data/Gemfile.lock +0 -171
  117. data/app/controllers/private_messages_controller.rb +0 -3
  118. data/app/models/private_message.rb +0 -6
  119. data/app/views/groups/_follow.html.erb +0 -9
  120. data/app/views/groups/_followers.html.erb +0 -16
  121. data/app/views/home/_contacts.html.erb +0 -17
  122. data/app/views/private_messages/_messages.html.erb +0 -2
  123. data/app/views/private_messages/_private_message.html.erb +0 -17
  124. data/app/views/private_messages/edit.html.erb +0 -6
  125. data/app/views/private_messages/show.html.erb +0 -21
  126. data/app/views/ties/_pending.html.erb +0 -21
  127. data/app/views/users/_contacts.html.erb +0 -19
  128. data/init.rb +0 -3
  129. data/lib/generators/social_stream/templates/seeds.yml +0 -64
  130. data/lib/social_stream/rails/common.rb +0 -36
  131. data/lib/social_stream/rails/engine.rb +0 -9
  132. data/lib/social_stream/rails/railtie.rb +0 -9
  133. data/lib/social_stream/seed.rb +0 -49
  134. data/spec/dummy/db/seeds/social_stream.yml +0 -64
  135. data/spec/models/user_space.rb +0 -10
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  .bundle
2
2
  pkg/*gem
3
3
  **.tmp_*
4
+ Gemfile.lock
data/README.rdoc CHANGED
@@ -43,14 +43,13 @@ This will generate the following:
43
43
  * A jquery:install generation for jQuery support
44
44
  * A devise:install generation for authentication support
45
45
  * An initializer file with configuration for Social Stream.
46
- * A database seeds file for defining custom Social Stream relations, along with an entry in db/seeds.rb to load it. You can define your own relations at <tt>db/seeds/social_stream.yml</tt>
46
+ * A configuration file for defining custom Social Stream relations. You can define your application default relations at <tt>config/relations.yml</tt>
47
47
  * A new application layout
48
48
  * A migration providing the database schema
49
49
 
50
- Do not forget to migrate and seed your database
50
+ Do not forget to migrate your database
51
51
 
52
52
  rake db:migrate
53
- rake db:seed
54
53
 
55
54
  == Actors and Activity Objects
56
55
 
@@ -17,7 +17,7 @@ class LikesController < ApplicationController
17
17
  end
18
18
 
19
19
  def destroy
20
- if (@like = activity!.liked_by(current_user).first)
20
+ if (@like = activity!.liked_by(current_subject).first)
21
21
  @like.destroy
22
22
  end
23
23
 
@@ -37,7 +37,7 @@ class LikesController < ApplicationController
37
37
  end
38
38
 
39
39
  def tie
40
- @tie ||= current_user.sent_ties(:receiver => activity!.receiver,
40
+ @tie ||= current_subject.sent_ties(:receiver => activity!.receiver,
41
41
  :relation => activity!.relation).first
42
42
  end
43
43
 
@@ -0,0 +1,3 @@
1
+ class MessagesController < InheritedResources::Base
2
+ respond_to :html, :xml, :js
3
+ end
@@ -0,0 +1,8 @@
1
+ # Changes context for users to represent other subjects
2
+ class RepresentationsController < ApplicationController
3
+ def create
4
+ self.current_subject = Representation.new(params[:representation]).subject
5
+
6
+ redirect_to(request.referer || home_path)
7
+ end
8
+ end
@@ -4,7 +4,7 @@ class TiesController < InheritedResources::Base
4
4
  before_filter :authenticate_user!, :only => :suggestion
5
5
 
6
6
  def suggestion
7
- @tie = current_user.suggestion
7
+ @tie = current_subject.suggestion
8
8
  render :layout => false
9
9
  end
10
10
  end
@@ -1,11 +1,11 @@
1
1
  module ActivitiesHelper
2
2
 
3
- # Link to 'like' or 'unlike' depending on the like status of the activity to current_user
3
+ # Link to 'like' or 'unlike' depending on the like status of the activity to current_subject
4
4
  #
5
5
  # @param [Activity]
6
6
  # @return [String]
7
7
  def link_like(activity)
8
- if (activity.liked_by?(current_user))
8
+ if (activity.liked_by?(current_subject))
9
9
  link_to t('activity.unlike'), activity_like_path(activity), :method => :delete, :remote => true
10
10
  else
11
11
  link_to t('activity.like'), activity_like_path(activity), :method => :post, :remote => true
@@ -3,26 +3,29 @@ module TiesHelper
3
3
  "N contacts in common"
4
4
  end
5
5
 
6
- def tie_link(tie)
7
- # FIXME: tie name
8
- if tie.relation.granted
9
- # There is granted relation, so another user must confirmate it
10
- # We need to render ties#new with a message
11
- link_to t("#{ tie.relation.name }.new"),
12
- new_tie_path("tie[sender_id]" => tie.sender.id,
13
- "tie[receiver_id]" => tie.receiver.id,
14
- "tie[relation_name]" => tie.relation.name),
15
- :title => t("#{ tie.relation.name }.confirm_new",
16
- :name => tie.receiver_subject.name),
17
- :remote => true
18
-
6
+ # Show current ties from current user to actor, if they exist, or provide a link
7
+ # to create a new tie to actor
8
+ def ties_to(a)
9
+ if user_signed_in?
10
+ if current_subject.ties_to(a).present?
11
+ current_subject.ties_to(a).first.relation_name
12
+ else
13
+ new_tie_link(current_subject.sent_ties.build :receiver_id => Actor.normalize_id(a))
14
+ end
19
15
  else
20
- # Tie can be established at once.
21
- render :partial => 'ties/form',
22
- :locals => { :tie => tie }
16
+ link_to t("contact.new.link"), new_user_session_path
23
17
  end
24
18
  end
25
19
 
20
+ def new_tie_link(tie)
21
+ link_to t("contact.new.link"),
22
+ new_tie_path("tie[sender_id]" => tie.sender.id,
23
+ "tie[receiver_id]" => tie.receiver.id),
24
+ :title => t("contact.new.title",
25
+ :name => tie.receiver_subject.name),
26
+ :remote => true
27
+ end
28
+
26
29
  def link_follow_state
27
30
  link_to("Unfollow", home_path)
28
31
  end
@@ -17,19 +17,16 @@ class Activity < ActiveRecord::Base
17
17
 
18
18
  has_one :tie,
19
19
  :through => :tie_activities,
20
- :conditions => { 'tie_activities.original' => true },
21
- :include => [ :sender ]
20
+ :conditions => { 'tie_activities.original' => true }
22
21
 
23
- delegate :sender, :receiver, :relation,
24
- :sender_subject, :receiver_subject,
25
- :to => :tie
22
+ delegate :relation, :to => :tie
26
23
 
27
24
  has_many :activity_object_activities,
28
25
  :dependent => :destroy
29
26
  has_many :activity_objects,
30
27
  :through => :activity_object_activities
31
28
 
32
- scope :wall, lambda { |ties|
29
+ scope :home_wall, lambda { |ties|
33
30
  select("DISTINCT activities.*").
34
31
  roots.
35
32
  joins(:tie_activities).
@@ -37,6 +34,15 @@ class Activity < ActiveRecord::Base
37
34
  order("created_at desc")
38
35
  }
39
36
 
37
+ scope :profile_wall, lambda { |ties|
38
+ select("DISTINCT activities.*").
39
+ roots.
40
+ joins(:tie_activities).
41
+ where('tie_activities.tie_id' => ties).
42
+ where('tie_activities.original' => true).
43
+ order("created_at desc")
44
+ }
45
+
40
46
  # After an activity is created, it is associated to ties
41
47
  attr_accessor :_tie
42
48
  after_create :assign_to_ties
@@ -51,6 +57,34 @@ class Activity < ActiveRecord::Base
51
57
  self.activity_verb = ActivityVerb[name]
52
58
  end
53
59
 
60
+ # The author of the activity is the receiver of the tie
61
+ #
62
+ # This method provides the actor. Use sender_subject for the subject (user, group, etc..)
63
+ def sender
64
+ tie.receiver
65
+ end
66
+
67
+ # The author of the activity is the receiver of the tie
68
+ #
69
+ # This method provides the subject (user, group, etc...). Use sender for the actor.
70
+ def sender_subject
71
+ tie.receiver_subject
72
+ end
73
+
74
+ # The wall where the activity is shown belongs to the sender of the tie
75
+ #
76
+ # This method provides the actor. Use sender_subject for the subject (user, group, etc..)
77
+ def receiver
78
+ tie.sender
79
+ end
80
+
81
+ # The wall where the activity is shown belongs to the sender of the tie
82
+ #
83
+ # This method provides the subject (user, group, etc...). Use sender for the actor.
84
+ def receiver_subject
85
+ tie.sender_subject
86
+ end
87
+
54
88
  # The comments about this activity
55
89
  def comments
56
90
  children.includes(:activity_objects).where('activity_objects.object_type' => "Comment")
@@ -62,7 +96,7 @@ class Activity < ActiveRecord::Base
62
96
  end
63
97
 
64
98
  def liked_by(user) #:nodoc:
65
- likes.joins(:ties).where('tie_activities.original' => true) & Tie.sent_by(user)
99
+ likes.joins(:ties).where('tie_activities.original' => true) & Tie.received_by(user)
66
100
  end
67
101
 
68
102
  # Does user like this activity?
@@ -77,6 +111,7 @@ class Activity < ActiveRecord::Base
77
111
 
78
112
  private
79
113
 
114
+ # Assign to ties of followers
80
115
  def assign_to_ties
81
116
  original = tie_activities.create!(:tie => _tie)
82
117
  _tie.activity_receivers.each do |t|
@@ -1,4 +1,5 @@
1
1
  class ActivityObject < ActiveRecord::Base
2
+ @subtypes_name = :object
2
3
  include SocialStream::Models::Supertype
3
4
 
4
5
  has_many :activity_object_activities, :dependent => :destroy
@@ -12,7 +12,11 @@ class ActivityVerb < ActiveRecord::Base
12
12
 
13
13
  class << self
14
14
  def [] name
15
- verb_name(name).first
15
+ if Available.include?(name)
16
+ find_or_create_by_name name
17
+ else
18
+ raise "ActivityVerb not available: #{ name }"
19
+ end
16
20
  end
17
21
  end
18
22
  end
data/app/models/actor.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # An actor is a social entity. This includes individuals, but also groups, departments, organizations even nations or states. Actors are linked by ties.
2
2
  class Actor < ActiveRecord::Base
3
+ @subtypes_name = :subject
3
4
  include SocialStream::Models::Supertype
4
5
 
5
6
  validates_presence_of :name, :subject_type
@@ -12,24 +13,46 @@ class Actor < ActiveRecord::Base
12
13
  :profile => '94x94' },
13
14
  :default_url => "/images/:attachment/:style/:subtype_class.png"
14
15
 
16
+ has_one :profile, :dependent => :destroy
17
+
15
18
  has_many :sent_ties,
16
19
  :class_name => "Tie",
17
20
  :foreign_key => 'sender_id',
18
21
  :dependent => :destroy
19
22
 
20
- has_many :senders,
21
- :through => :received_ties,
22
- :uniq => true
23
-
24
23
  has_many :received_ties,
25
24
  :class_name => "Tie",
26
25
  :foreign_key => 'receiver_id',
27
26
  :dependent => :destroy
28
27
 
28
+ has_many :senders,
29
+ :through => :received_ties,
30
+ :uniq => true
31
+
29
32
  has_many :receivers,
30
33
  :through => :sent_ties,
31
34
  :uniq => true
32
35
 
36
+ after_create :initialize_ties
37
+
38
+ after_create :create_profile
39
+
40
+ class << self
41
+ # Get actor's id from an object, if possible
42
+ def normalize_id(a)
43
+ case a
44
+ when Integer
45
+ a
46
+ when Array
47
+ a.map{ |e| normalize_id(e) }
48
+ when Actor
49
+ a.id
50
+ else
51
+ a.actor.id
52
+ end
53
+ end
54
+ end
55
+
33
56
  # The subject instance for this actor
34
57
  def subject
35
58
  subtype_instance ||
@@ -41,68 +64,71 @@ class Actor < ActiveRecord::Base
41
64
  Tie.sent_or_received_by(self)
42
65
  end
43
66
 
44
- # All the subject actors of class subject_type that send at least one tie
45
- # to this actor
67
+ # Relations defined and managed by this actor
68
+ def relations
69
+ Relation.includes(:ties) & Tie.sent_by(self)
70
+ end
71
+
72
+ # A given relation defined and managed by this actor
73
+ def relation(name)
74
+ relations.find_by_name(name)
75
+ end
76
+
77
+ # All the actors this one has relation with
46
78
  #
47
- # Options::
79
+ # Options:
80
+ # * subject_type: Filter by the class of the subjects.
81
+ # * direction: senders or receivers
48
82
  # * relations: Restrict the relations of considered ties
49
- # * include_self: False by default, don't include this actor as subject even they
50
- # have ties with themselves.
51
- def sender_subjects(subject_type, options = {})
52
- # FIXME: DRY!
53
- subject_class = subject_type.to_s.classify.constantize
54
-
55
- cs = subject_class.
56
- select("DISTINCT #{ subject_class.quoted_table_name }.*").
57
- with_sent_ties &
58
- Tie.received_by(self)
83
+ # * include_self: False by default, don't include this actor as subject even they have ties with themselves.
84
+ #
85
+ def actors(options = {})
86
+ subject_types = Array(options[:subject_type] || self.class.subtypes)
87
+ subject_classes = subject_types.map{ |s| s.to_s.classify }
88
+
89
+ as = Actor.select("DISTINCT actors.*").
90
+ where('actors.subject_type' => subject_classes).
91
+ includes(subject_types)
92
+
93
+
94
+ case options[:direction]
95
+ when :senders
96
+ as = as.joins(:sent_ties) & Tie.received_by(self)
97
+ when :receivers
98
+ as = as.joins(:received_ties) & Tie.sent_by(self)
99
+ else
100
+ raise "actors in both directions is not supported yet"
101
+ end
59
102
 
60
103
  if options[:include_self].blank?
61
- cs = cs.where("#{ self.class.quoted_table_name }.id != ?", self.id)
104
+ as = as.where("actors.id != ?", self.id)
62
105
  end
63
106
 
64
107
  if options[:relations].present?
65
- cs &=
66
- Tie.related_by(Tie.Relation(options[:relations], :mode => [ subject_class, self.subject.class ]))
108
+ as &= Tie.related_by(options[:relations])
67
109
  end
68
110
 
69
- cs
111
+ as
70
112
  end
71
113
 
72
- # All the subject actors of class subject_type that receive at least one tie
73
- # from this actor
114
+ # All the subject actors that send or receive at least one tie to this actor
74
115
  #
75
- # Options::
76
- # * relations: Restrict the relations of considered ties
77
- # * include_self: False by default, don't include this actor as subject even they
78
- # have ties with themselves.
79
- def receiver_subjects(subject_type, options = {})
80
- # FIXME: DRY!
81
- subject_class = subject_type.to_s.classify.constantize
82
-
83
- cs = subject_class.
84
- select("DISTINCT #{ subject_class.quoted_table_name }.*").
85
- with_received_ties &
86
- Tie.sent_by(self)
87
-
88
- if options[:include_self].blank?
89
- cs = cs.where("#{ self.class.quoted_table_name }.id != ?", self.id)
90
- end
116
+ # When passing a block, it will be evaluated for the actors query, allowing to add
117
+ # options before the mapping to subjects
118
+ #
119
+ # See actors for options
120
+ def subjects(options = {})
121
+ as = actors(options)
91
122
 
92
- if options[:relations].present?
93
- cs &=
94
- Tie.related_by(Tie.Relation(options[:relations], :mode => [ subject.class, subject_class ]))
123
+ if block_given?
124
+ as = yield(as)
95
125
  end
96
126
 
97
- cs
127
+ as.map(&:subject)
98
128
  end
99
129
 
100
130
  # This is an scaffold for a recomendations engine
101
131
  #
102
- SuggestedRelations = {
103
- 'User' => 'friend_request',
104
- 'Group' => 'follower'
105
- }
106
132
 
107
133
  # Make n suggestions
108
134
  # TODO: make more
@@ -118,16 +144,17 @@ class Actor < ActiveRecord::Base
118
144
  #
119
145
  # @return [Tie]
120
146
  def suggestion(options = {})
121
- candidates_types = options[:type].present? ?
122
- Array(options[:type].to_s.classify) :
123
- SuggestedRelations.keys
147
+ candidates_types =
148
+ options[:type].present? ?
149
+ Array(options[:type]) :
150
+ self.class.subtypes
124
151
 
125
- candidates_classes = candidates_types.map(&:constantize)
152
+ candidates_classes = candidates_types.map{ |t| t.to_s.classify.constantize }
126
153
 
127
154
  # Candidates are all the instance of "type" minus all the subjects
128
155
  # that are receiving any tie from this actor
129
156
  candidates = candidates_classes.inject([]) do |cs, klass|
130
- cs += klass.all - receiver_subjects(klass)
157
+ cs += klass.all - subjects(:subject_type => klass, :direction => :receivers)
131
158
  cs -= Array(subject) if subject.is_a?(klass)
132
159
  cs
133
160
  end
@@ -136,38 +163,56 @@ class Actor < ActiveRecord::Base
136
163
 
137
164
  return nil unless candidate.present?
138
165
 
139
- sent_ties.build :receiver_id => candidate.actor.id,
140
- :relation => Relation.mode(subject_type, candidate.class).find_by_name(SuggestedRelations[candidate.class.to_s])
166
+ # Building ties with sent_ties catches them and excludes them from pending ties.
167
+ # An useful side effect for excluding this ones from pending, but can be weird!
168
+ # Maybe we must use:
169
+ # Tie.sent_by(self).build :receiver_id => candidate.actor.id
170
+ sent_ties.build :receiver_id => candidate.actor.id
141
171
  end
142
172
 
143
- # All the ties this actor has with subject that support activities
144
- def active_ties_to(subject)
145
- sent_ties.received_by(subject).allowed(self, 'create', 'activity')
173
+ # Set of ties sent by this actor received by a
174
+ def ties_to(a)
175
+ sent_ties.received_by(a)
176
+ end
177
+
178
+ # All the ties this actor has with subject that support permission
179
+ def sent_ties_allowing(subject, action, objective)
180
+ return [] if subject.blank?
181
+
182
+ sent_ties.allowing(subject, action, objective)
146
183
  end
147
184
 
148
185
  def pending_ties
149
- #TODO: optimize by SQL
150
186
  @pending_ties ||=
151
- received_ties.pending.
152
- select{ |t| ! receivers.include?(t.sender) }.
153
- map{ |u| Tie.new :sender_id => u.receiver_id,
154
- :receiver_id => u.sender_id,
155
- :relation_id => u.relation.granted_id
156
- }
187
+ received_ties.where('ties.sender_id NOT IN (?)', sent_ties.map(&:receiver_id).uniq).map(&:sender_id).uniq.
188
+ map{ |i| Tie.new :sender => self,
189
+ :receiver_id => i }
157
190
  end
158
191
 
159
192
  # The set of activities in the wall of this actor, includes all the activities
160
193
  # from the ties the actor has access to
161
194
  #
162
- def wall
163
- Activity.wall Tie.allowed(self, 'read', 'activity')
195
+ def home_wall
196
+ Activity.home_wall ties
164
197
  end
165
198
 
166
199
  # The set of activities in the wall profile of this actor, includes the activities
167
200
  # from the ties of this actor that can be read by user
168
201
  #
169
- def wall_profile(user)
170
- Activity.wall ties.allowed(user, 'read', 'activity')
202
+ def profile_wall(user)
203
+ # FIXME: show public activities
204
+ return [] if user.blank?
205
+
206
+ Activity.profile_wall ties.allowing(user, 'read', 'activity')
207
+ end
208
+
209
+ private
210
+
211
+ def initialize_ties
212
+ ::SocialStream::Relations.create(subject_type).each do |r|
213
+ sent_ties.create! :receiver => self,
214
+ :relation => r
215
+ end
171
216
  end
172
217
  end
173
218
 
data/app/models/group.rb CHANGED
@@ -1,5 +1,19 @@
1
1
  class Group < ActiveRecord::Base
2
+ attr_accessor :_founder
3
+
2
4
  def followers
3
- sender_subjects(:user, :relations => 'follower')
5
+ subjects(:subject_type => :user, :direction => :senders)
6
+ end
7
+
8
+ after_create :create_founder
9
+
10
+ private
11
+
12
+ def create_founder
13
+ founder =
14
+ Actor.find_by_permalink(_founder) || raise("Cannot create group without founder")
15
+
16
+ sent_ties.create! :receiver => founder,
17
+ :relation => relations.sort.first
4
18
  end
5
19
  end
@@ -0,0 +1,2 @@
1
+ class Message < ActiveRecord::Base
2
+ end
@@ -1,25 +1,26 @@
1
1
  # SocialStream provides a sophisticated and powerful system of permissions based on the relations
2
2
  # and ties of the social network.
3
3
  #
4
- # Permissions are composed by action, objective and parameterized. Action and objective are classical
4
+ # Permissions are composed by action, objective and function. Action and objective are classical
5
5
  # in content management systems, e.g. "create" "activity", "update" "tie", "read" "post"
6
6
  #
7
- # parameterized is a novel feature. It supports applying the permission to certain set of ties.
8
- # This set of ties changes with the formation of ties in your website.
7
+ # function is a novel feature. It supports applying the permission to certain set of ties.
8
+ # This set of ties changes along with the formation of ties in your website.
9
9
  #
10
- # Permissions is assigned to relations, and through relations, to ties.
11
- # When a sender establishes a tie with a receiver, she grants to the receiver the permissions assigned
10
+ # Permissions are assigned to relations, and through relations, to ties.
11
+ # When a sender establishes a tie with a receiver, she is granting to the receiver the permissions assigned
12
12
  # to relation of the tie she has just established. For example, when Alice establishes a "friend" tie
13
13
  # to Bob, she is granting him the permissions associated with "friend" relation.
14
14
  #
15
- # One of this permissions can be "read" "activity" "inverse_group_set". This way, Bob will have access
16
- # to read the activities attached to ties inside the "inverse_group_set", which are the ties from all
17
- # the "friends" of Alice to Alice herself.
15
+ # One of this permissions can be "read" "activity" "star_set". This way, Bob will have access
16
+ # to read the activities attached to ties inside the "star_set", which are the ties from Alice to her "friends"
18
17
  #
19
18
  class Permission < ActiveRecord::Base
20
19
  has_many :relation_permissions, :dependent => :destroy
21
20
  has_many :relations, :through => :relation_permissions
22
21
 
22
+ scope :represent, where(:action => 'represent')
23
+
23
24
  # The SQL and ARel conditions for permission queries
24
25
  ParameterConditions = {
25
26
  :table => {
@@ -27,16 +28,10 @@ class Permission < ActiveRecord::Base
27
28
  "ties_as.sender_id = ties.sender_id AND ties_as.receiver_id = ties.receiver_id AND ties_as.relation_id = ties.relation_id",
28
29
  'weak_set' =>
29
30
  "ties_as.sender_id = ties.sender_id AND ties_as.receiver_id = ties.receiver_id AND relations.lft BETWEEN relations_as.lft AND relations_as.rgt",
30
- 'inverse_weak_set' =>
31
- "ties_as.sender_id = ties.receiver_id AND ties_as.receiver_id = ties.sender_id AND relations.inverse_id = relations_inverse.id AND relations_inverse.lft BETWEEN relations_as.lft AND relations_as.rgt",
32
- 'group_set' =>
33
- "ties_as.receiver_id = ties.receiver_id AND ties_as.relation_id = ties.relation_id",
34
- 'inverse_group_set' =>
35
- "ties_as.sender_id = ties.receiver_id AND ties_as.relation_id = relations.inverse_id",
36
- 'weak_group_set' =>
37
- "ties_as.receiver_id = ties.receiver_id AND relations.lft BETWEEN relations_as.lft AND relations_as.rgt",
38
- 'inverse_weak_group_set' =>
39
- "ties_as.sender_id = ties.receiver_id AND relations.inverse_id = relations_inverse.id AND relations_inverse.lft BETWEEN relations_as.lft AND relations_as.rgt"
31
+ 'star_set' =>
32
+ "ties_as.sender_id = ties.sender_id AND ties_as.relation_id = ties.relation_id",
33
+ 'weak_star_set' =>
34
+ "ties_as.sender_id = ties.sender_id AND relations.lft BETWEEN relations_as.lft AND relations_as.rgt"
40
35
  },
41
36
  :arel => {
42
37
  'tie' => lambda { |as, t|
@@ -51,31 +46,15 @@ class Permission < ActiveRecord::Base
51
46
  as[:receiver_id].eq(t.receiver_id)).and(
52
47
  as[:relation_id].in(t.relation.stronger_or_equal.map(&:id)))
53
48
  },
54
- 'inverse_weak_set' => lambda { |as, t|
55
- # Senders and receivers interchanged, with a stronger or equal relation of the inverse
56
- as[:sender_id].eq(t.receiver_id).and(
57
- as[:receiver_id].eq(t.sender_id)).and(
58
- as[:relation_id].in(Array(t.relation.inverse.try(:stronger_or_equal)).map(&:id)))
59
- },
60
- 'group_set' => lambda { |as, t|
49
+ 'star_set' => lambda { |as, t|
61
50
  # The same receiver and relation
62
- as[:receiver_id].eq(t.receiver_id).and(
51
+ as[:sender_id].eq(t.sender_id).and(
63
52
  as[:relation_id].eq(t.relation_id))
64
53
  },
65
- 'inverse_group_set' => lambda { |as, t|
66
- # Senders to the common receiver in the same relation
67
- as[:sender_id].eq(t.receiver_id).and(
68
- as[:relation_id].eq(t.relation.inverse_id))
69
- },
70
- 'weak_group_set' => lambda { |as, t|
54
+ 'weak_star_set' => lambda { |as, t|
71
55
  # The same receiver with stronger or equal relations
72
- as[:receiver_id].eq(t.receiver_id).and(
56
+ as[:sender_id].eq(t.sender_id).and(
73
57
  as[:relation_id].in(t.relation.stronger_or_equal.map(&:id)))
74
- },
75
- 'inverse_weak_group_set' => lambda { |as, t|
76
- # Senders to the common receiver with stronger or equal relations
77
- as[:sender_id].eq(t.receiver_id).and(
78
- as[:relation_id].in(Array(t.relation.inverse.try(:stronger_or_equal)).map(&:id)))
79
58
  }
80
59
  }
81
60
  }
@@ -84,10 +63,10 @@ class Permission < ActiveRecord::Base
84
63
  def parameter_conditions(tie = nil)
85
64
  if tie.present?
86
65
  ParameterConditions[:arel].inject([]) { |conditions, h|
87
- # Add the condition 'permissions.parameter = key'
66
+ # Add the condition 'permissions.function = key'
88
67
  # to all arel ParameterConditions
89
68
  conditions <<
90
- h.last.call(Tie.arel_table, tie).and(arel_table[:parameter].eq(h.first))
69
+ h.last.call(Tie.arel_table, tie).and(arel_table[:function].eq(h.first))
91
70
  }.inject(nil){ |result, pc|
92
71
  # Join all ParameterConditions with OR
93
72
  result.nil? ? pc : result.or(pc)
@@ -95,7 +74,7 @@ class Permission < ActiveRecord::Base
95
74
  else
96
75
  ParameterConditions[:table].inject([]){ |result, pc|
97
76
  result <<
98
- sanitize_sql([ "#{ pc.last } AND permissions.parameter = ?", pc.first ])
77
+ sanitize_sql([ "#{ pc.last } AND permissions.function = ?", pc.first ])
99
78
  }.join(" OR ")
100
79
  end
101
80
  end
@@ -1,3 +1,3 @@
1
1
  class Profile < ActiveRecord::Base
2
- belongs_to :user
2
+ belongs_to :actor
3
3
  end