social_stream 0.2.3 → 0.3.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.
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