socializer 0.1.13

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 (291) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +11 -0
  3. data/.gitignore +29 -0
  4. data/.rubocop.yml +42 -0
  5. data/.travis.yml +13 -0
  6. data/CONTRIBUTING.md +46 -0
  7. data/Gemfile +31 -0
  8. data/README.md +91 -0
  9. data/Rakefile +38 -0
  10. data/app/assets/images/socializer/.gitkeep +0 -0
  11. data/app/assets/images/socializer/auth_buttons/facebook_128.png +0 -0
  12. data/app/assets/images/socializer/auth_buttons/facebook_256.png +0 -0
  13. data/app/assets/images/socializer/auth_buttons/facebook_32.png +0 -0
  14. data/app/assets/images/socializer/auth_buttons/facebook_64.png +0 -0
  15. data/app/assets/images/socializer/auth_buttons/google_128.png +0 -0
  16. data/app/assets/images/socializer/auth_buttons/google_256.png +0 -0
  17. data/app/assets/images/socializer/auth_buttons/google_32.png +0 -0
  18. data/app/assets/images/socializer/auth_buttons/google_64.png +0 -0
  19. data/app/assets/images/socializer/auth_buttons/linkedin_128.png +0 -0
  20. data/app/assets/images/socializer/auth_buttons/linkedin_256.png +0 -0
  21. data/app/assets/images/socializer/auth_buttons/linkedin_32.png +0 -0
  22. data/app/assets/images/socializer/auth_buttons/linkedin_64.png +0 -0
  23. data/app/assets/images/socializer/auth_buttons/twitter_128.png +0 -0
  24. data/app/assets/images/socializer/auth_buttons/twitter_256.png +0 -0
  25. data/app/assets/images/socializer/auth_buttons/twitter_32.png +0 -0
  26. data/app/assets/images/socializer/auth_buttons/twitter_64.png +0 -0
  27. data/app/assets/images/socializer/auth_buttons/yahoo_128.png +0 -0
  28. data/app/assets/images/socializer/auth_buttons/yahoo_256.png +0 -0
  29. data/app/assets/images/socializer/auth_buttons/yahoo_32.png +0 -0
  30. data/app/assets/images/socializer/auth_buttons/yahoo_64.png +0 -0
  31. data/app/assets/images/socializer/close.png +0 -0
  32. data/app/assets/images/socializer/icons/Bell.png +0 -0
  33. data/app/assets/images/socializer/icons/Edit.png +0 -0
  34. data/app/assets/images/socializer/icons/Home.png +0 -0
  35. data/app/assets/images/socializer/icons/Power.png +0 -0
  36. data/app/assets/images/socializer/icons/Safe.png +0 -0
  37. data/app/assets/images/socializer/icons/User.png +0 -0
  38. data/app/assets/images/socializer/icons/Users.png +0 -0
  39. data/app/assets/images/socializer/icons/Web.png +0 -0
  40. data/app/assets/images/socializer/loading.gif +0 -0
  41. data/app/assets/images/socializer/next.png +0 -0
  42. data/app/assets/images/socializer/pause.png +0 -0
  43. data/app/assets/images/socializer/play.png +0 -0
  44. data/app/assets/images/socializer/previous.png +0 -0
  45. data/app/assets/javascripts/socializer/activities.js.coffee +47 -0
  46. data/app/assets/javascripts/socializer/application.js +24 -0
  47. data/app/assets/javascripts/socializer/circles.js.coffee +33 -0
  48. data/app/assets/javascripts/socializer/jquery.qtip.min.js +7 -0
  49. data/app/assets/javascripts/socializer/jquery.tokeninput.js +862 -0
  50. data/app/assets/javascripts/socializer/notes.js.coffee +38 -0
  51. data/app/assets/javascripts/socializer/people.js.coffee +7 -0
  52. data/app/assets/javascripts/socializer/shares.js.coffee +8 -0
  53. data/app/assets/stylesheets/socializer/activities.css +84 -0
  54. data/app/assets/stylesheets/socializer/application.css +409 -0
  55. data/app/assets/stylesheets/socializer/authentications.css +4 -0
  56. data/app/assets/stylesheets/socializer/circles.css +74 -0
  57. data/app/assets/stylesheets/socializer/comments.css +4 -0
  58. data/app/assets/stylesheets/socializer/groups.css +61 -0
  59. data/app/assets/stylesheets/socializer/jquery.qtip.min.css +2 -0
  60. data/app/assets/stylesheets/socializer/likes.css +4 -0
  61. data/app/assets/stylesheets/socializer/memberships.css +4 -0
  62. data/app/assets/stylesheets/socializer/notes.css +21 -0
  63. data/app/assets/stylesheets/socializer/notifications.css +46 -0
  64. data/app/assets/stylesheets/socializer/pages.css +4 -0
  65. data/app/assets/stylesheets/socializer/persons.css +25 -0
  66. data/app/assets/stylesheets/socializer/sessions.css +4 -0
  67. data/app/assets/stylesheets/socializer/shares.css.scss +3 -0
  68. data/app/assets/stylesheets/socializer/ties.css +4 -0
  69. data/app/assets/stylesheets/socializer/token-input.css +183 -0
  70. data/app/controllers/socializer/activities_controller.rb +84 -0
  71. data/app/controllers/socializer/application_controller.rb +37 -0
  72. data/app/controllers/socializer/audiences_controller.rb +30 -0
  73. data/app/controllers/socializer/authentications_controller.rb +13 -0
  74. data/app/controllers/socializer/circles_controller.rb +54 -0
  75. data/app/controllers/socializer/comments_controller.rb +37 -0
  76. data/app/controllers/socializer/groups_controller.rb +70 -0
  77. data/app/controllers/socializer/likes_controller.rb +50 -0
  78. data/app/controllers/socializer/memberships_controller.rb +46 -0
  79. data/app/controllers/socializer/notes_controller.rb +43 -0
  80. data/app/controllers/socializer/notifications_controller.rb +29 -0
  81. data/app/controllers/socializer/pages_controller.rb +7 -0
  82. data/app/controllers/socializer/people_controller.rb +37 -0
  83. data/app/controllers/socializer/person_addresses_controller.rb +27 -0
  84. data/app/controllers/socializer/person_contributions_controller.rb +27 -0
  85. data/app/controllers/socializer/person_educations_controller.rb +27 -0
  86. data/app/controllers/socializer/person_employments_controller.rb +27 -0
  87. data/app/controllers/socializer/person_links_controller.rb +27 -0
  88. data/app/controllers/socializer/person_phones_controller.rb +27 -0
  89. data/app/controllers/socializer/person_places_controller.rb +27 -0
  90. data/app/controllers/socializer/person_profiles_controller.rb +27 -0
  91. data/app/controllers/socializer/sessions_controller.rb +37 -0
  92. data/app/controllers/socializer/shares_controller.rb +54 -0
  93. data/app/controllers/socializer/ties_controller.rb +16 -0
  94. data/app/helpers/socializer/application_helper.rb +22 -0
  95. data/app/models/concerns/socializer/object_type_base.rb +58 -0
  96. data/app/models/socializer/activity.rb +162 -0
  97. data/app/models/socializer/activity_field.rb +10 -0
  98. data/app/models/socializer/activity_object.rb +118 -0
  99. data/app/models/socializer/audience.rb +19 -0
  100. data/app/models/socializer/authentication.rb +20 -0
  101. data/app/models/socializer/circle.rb +32 -0
  102. data/app/models/socializer/comment.rb +13 -0
  103. data/app/models/socializer/group.rb +100 -0
  104. data/app/models/socializer/group_category.rb +11 -0
  105. data/app/models/socializer/group_link.rb +12 -0
  106. data/app/models/socializer/identity.rb +9 -0
  107. data/app/models/socializer/membership.rb +24 -0
  108. data/app/models/socializer/note.rb +13 -0
  109. data/app/models/socializer/notification.rb +16 -0
  110. data/app/models/socializer/person.rb +129 -0
  111. data/app/models/socializer/person_address.rb +12 -0
  112. data/app/models/socializer/person_contribution.rb +12 -0
  113. data/app/models/socializer/person_education.rb +8 -0
  114. data/app/models/socializer/person_employment.rb +8 -0
  115. data/app/models/socializer/person_link.rb +12 -0
  116. data/app/models/socializer/person_phone.rb +16 -0
  117. data/app/models/socializer/person_place.rb +11 -0
  118. data/app/models/socializer/person_profile.rb +12 -0
  119. data/app/models/socializer/tie.rb +12 -0
  120. data/app/models/socializer/verb.rb +9 -0
  121. data/app/views/layouts/socializer/_header.html.erb +11 -0
  122. data/app/views/layouts/socializer/application.html.erb +60 -0
  123. data/app/views/socializer/activities/_activity.html.erb +23 -0
  124. data/app/views/socializer/activities/_activity_bottom.html.erb +55 -0
  125. data/app/views/socializer/activities/_activity_content.html.erb +19 -0
  126. data/app/views/socializer/activities/_activity_top.html.erb +22 -0
  127. data/app/views/socializer/activities/_stream_card.html.erb +6 -0
  128. data/app/views/socializer/activities/_stream_list.html.erb +16 -0
  129. data/app/views/socializer/activities/audience.html.erb +19 -0
  130. data/app/views/socializer/activities/destroy.js.erb +3 -0
  131. data/app/views/socializer/activities/edit.html.erb +0 -0
  132. data/app/views/socializer/activities/index.html.erb +15 -0
  133. data/app/views/socializer/authentications/_form.html.erb +0 -0
  134. data/app/views/socializer/authentications/_menu.html.erb +5 -0
  135. data/app/views/socializer/authentications/index.html.erb +8 -0
  136. data/app/views/socializer/authentications/new.html.erb +8 -0
  137. data/app/views/socializer/circles/_card.html.erb +7 -0
  138. data/app/views/socializer/circles/_circles.html.erb +6 -0
  139. data/app/views/socializer/circles/_form.html.erb +6 -0
  140. data/app/views/socializer/circles/_menu.html.erb +7 -0
  141. data/app/views/socializer/circles/contact_of.html.erb +7 -0
  142. data/app/views/socializer/circles/contacts.html.erb +7 -0
  143. data/app/views/socializer/circles/edit.html.erb +1 -0
  144. data/app/views/socializer/circles/find_people.html.erb +7 -0
  145. data/app/views/socializer/circles/index.html.erb +6 -0
  146. data/app/views/socializer/circles/new.html.erb +1 -0
  147. data/app/views/socializer/circles/show.html.erb +10 -0
  148. data/app/views/socializer/comments/_form.html.erb +8 -0
  149. data/app/views/socializer/comments/edit.html.erb +1 -0
  150. data/app/views/socializer/comments/new.html.erb +1 -0
  151. data/app/views/socializer/groups/_card.html.erb +6 -0
  152. data/app/views/socializer/groups/_form.html.erb +6 -0
  153. data/app/views/socializer/groups/_menu.html.erb +10 -0
  154. data/app/views/socializer/groups/edit.html.erb +1 -0
  155. data/app/views/socializer/groups/index.html.erb +0 -0
  156. data/app/views/socializer/groups/joinable.html.erb +16 -0
  157. data/app/views/socializer/groups/memberships.html.erb +19 -0
  158. data/app/views/socializer/groups/new.html.erb +1 -0
  159. data/app/views/socializer/groups/ownerships.html.erb +20 -0
  160. data/app/views/socializer/groups/pending_invites.html.erb +20 -0
  161. data/app/views/socializer/groups/public.html.erb +16 -0
  162. data/app/views/socializer/groups/restricted.html.erb +16 -0
  163. data/app/views/socializer/groups/show.html.erb +29 -0
  164. data/app/views/socializer/likes/create.js.erb +2 -0
  165. data/app/views/socializer/likes/destroy.js.erb +2 -0
  166. data/app/views/socializer/likes/index.html.erb +5 -0
  167. data/app/views/socializer/notes/_form.html.erb +7 -0
  168. data/app/views/socializer/notes/create.js.erb +10 -0
  169. data/app/views/socializer/notes/destroy.js.erb +3 -0
  170. data/app/views/socializer/notes/edit.html.erb +1 -0
  171. data/app/views/socializer/notes/new.html.erb +2 -0
  172. data/app/views/socializer/notifications/index.html.erb +35 -0
  173. data/app/views/socializer/pages/index.html.erb +2 -0
  174. data/app/views/socializer/people/_card.html.erb +15 -0
  175. data/app/views/socializer/people/_form.html.erb +30 -0
  176. data/app/views/socializer/people/_login.html.erb +14 -0
  177. data/app/views/socializer/people/_menu.html.erb +9 -0
  178. data/app/views/socializer/people/edit.html.erb +2 -0
  179. data/app/views/socializer/people/index.html.erb +5 -0
  180. data/app/views/socializer/people/likes.html.erb +6 -0
  181. data/app/views/socializer/people/message.html.erb +5 -0
  182. data/app/views/socializer/people/show.html.erb +122 -0
  183. data/app/views/socializer/person_addresses/_form.html.erb +12 -0
  184. data/app/views/socializer/person_contributions/_form.html.erb +7 -0
  185. data/app/views/socializer/person_educations/_form.html.erb +10 -0
  186. data/app/views/socializer/person_employments/_form.html.erb +10 -0
  187. data/app/views/socializer/person_links/_form.html.erb +6 -0
  188. data/app/views/socializer/person_phones/_form.html.erb +6 -0
  189. data/app/views/socializer/person_places/_form.html.erb +6 -0
  190. data/app/views/socializer/person_profiles/_form.html.erb +6 -0
  191. data/app/views/socializer/shares/_form.html.erb +19 -0
  192. data/app/views/socializer/shares/new.html.erb +1 -0
  193. data/config.ru +4 -0
  194. data/config/cucumber.yml +8 -0
  195. data/config/initializers/inflections.rb +3 -0
  196. data/config/locales/en.yml +108 -0
  197. data/config/locales/fr.yml +329 -0
  198. data/config/rails_best_practices.yml +41 -0
  199. data/config/routes.rb +71 -0
  200. data/db/migrate/20110804175740_create_socializer_activity_objects.rb +14 -0
  201. data/db/migrate/20110804180557_create_socializer_people.rb +31 -0
  202. data/db/migrate/20110804184415_create_socializer_notes.rb +12 -0
  203. data/db/migrate/20110804192211_create_socializer_authentications.rb +12 -0
  204. data/db/migrate/20110805085417_create_socializer_circles.rb +14 -0
  205. data/db/migrate/20110805090617_create_socializer_activities.rb +17 -0
  206. data/db/migrate/20110805092635_create_socializer_audiences.rb +14 -0
  207. data/db/migrate/20110805094846_create_socializer_comments.rb +12 -0
  208. data/db/migrate/20110805121454_create_socializer_groups.rb +19 -0
  209. data/db/migrate/20110805121543_create_socializer_memberships.rb +14 -0
  210. data/db/migrate/20110805125606_create_socializer_ties.rb +13 -0
  211. data/db/migrate/20111207234435_create_socializer_identities.rb +12 -0
  212. data/db/migrate/20130524000429_create_socializer_verbs.rb +10 -0
  213. data/db/migrate/20130528143831_create_socializer_activity_fields.rb +10 -0
  214. data/db/migrate/20140128235250_create_socializer_notifications.rb +11 -0
  215. data/db/migrate/20140131065531_create_socializer_person_employments.rb +15 -0
  216. data/db/migrate/20140131065733_create_socializer_person_educations.rb +15 -0
  217. data/db/migrate/20140131065836_create_socializer_person_places.rb +11 -0
  218. data/db/migrate/20140131070051_create_socializer_person_contributions.rb +12 -0
  219. data/db/migrate/20140131070232_create_socializer_person_links.rb +11 -0
  220. data/db/migrate/20140131070259_create_socializer_person_profiles.rb +11 -0
  221. data/db/migrate/20140131070417_create_socializer_person_phones.rb +12 -0
  222. data/db/migrate/20140131070611_create_socializer_person_addresses.rb +17 -0
  223. data/db/migrate/20140131070839_create_socializer_group_links.rb +11 -0
  224. data/db/migrate/20140131070951_create_socializer_group_categories.rb +10 -0
  225. data/features/support/capybara.rb +14 -0
  226. data/features/support/env.rb +57 -0
  227. data/features/support/factory_girl.rb +1 -0
  228. data/lib/socializer.rb +4 -0
  229. data/lib/socializer/engine.rb +27 -0
  230. data/lib/socializer/version.rb +3 -0
  231. data/lib/tasks/cucumber.rake +65 -0
  232. data/lib/tasks/socializer_tasks.rake +4 -0
  233. data/script/cucumber +10 -0
  234. data/socializer.gemspec +49 -0
  235. data/spec/controllers/socializer/likes_controller_spec.rb +29 -0
  236. data/spec/controllers/socializer/shares_controller_spec.rb +22 -0
  237. data/spec/factories/socializer_activities.rb +11 -0
  238. data/spec/factories/socializer_activity_fields.rb +8 -0
  239. data/spec/factories/socializer_activity_objects.rb +7 -0
  240. data/spec/factories/socializer_audiences.rb +9 -0
  241. data/spec/factories/socializer_authentications.rb +6 -0
  242. data/spec/factories/socializer_circles.rb +9 -0
  243. data/spec/factories/socializer_comments.rb +6 -0
  244. data/spec/factories/socializer_group_categories.rb +8 -0
  245. data/spec/factories/socializer_group_links.rb +9 -0
  246. data/spec/factories/socializer_groups.rb +9 -0
  247. data/spec/factories/socializer_identities.rb +10 -0
  248. data/spec/factories/socializer_memberships.rb +6 -0
  249. data/spec/factories/socializer_notes.rb +7 -0
  250. data/spec/factories/socializer_notifications.rb +10 -0
  251. data/spec/factories/socializer_people.rb +7 -0
  252. data/spec/factories/socializer_person_addresses.rb +8 -0
  253. data/spec/factories/socializer_person_contributions.rb +9 -0
  254. data/spec/factories/socializer_person_educations.rb +7 -0
  255. data/spec/factories/socializer_person_employments.rb +7 -0
  256. data/spec/factories/socializer_person_links.rb +9 -0
  257. data/spec/factories/socializer_person_phones.rb +9 -0
  258. data/spec/factories/socializer_person_places.rb +8 -0
  259. data/spec/factories/socializer_person_profiles.rb +9 -0
  260. data/spec/factories/socializer_ties.rb +6 -0
  261. data/spec/factories/socializer_verbs.rb +7 -0
  262. data/spec/inflector_spec.rb +19 -0
  263. data/spec/models/socializer/activity_field_spec.rb +25 -0
  264. data/spec/models/socializer/activity_object_spec.rb +118 -0
  265. data/spec/models/socializer/activity_spec.rb +16 -0
  266. data/spec/models/socializer/audience_spec.rb +32 -0
  267. data/spec/models/socializer/authentication_spec.rb +29 -0
  268. data/spec/models/socializer/circle_spec.rb +53 -0
  269. data/spec/models/socializer/comment_spec.rb +19 -0
  270. data/spec/models/socializer/group_category_spec.rb +23 -0
  271. data/spec/models/socializer/group_link_spec.rb +25 -0
  272. data/spec/models/socializer/group_spec.rb +218 -0
  273. data/spec/models/socializer/identity_spec.rb +32 -0
  274. data/spec/models/socializer/membership_spec.rb +68 -0
  275. data/spec/models/socializer/note_spec.rb +19 -0
  276. data/spec/models/socializer/notification_spec.rb +28 -0
  277. data/spec/models/socializer/person_address_spec.rb +27 -0
  278. data/spec/models/socializer/person_contribution_spec.rb +27 -0
  279. data/spec/models/socializer/person_education_spec.rb +25 -0
  280. data/spec/models/socializer/person_employment_spec.rb +25 -0
  281. data/spec/models/socializer/person_link_spec.rb +26 -0
  282. data/spec/models/socializer/person_phone_spec.rb +28 -0
  283. data/spec/models/socializer/person_place_spec.rb +25 -0
  284. data/spec/models/socializer/person_profile_spec.rb +26 -0
  285. data/spec/models/socializer/person_spec.rb +105 -0
  286. data/spec/models/socializer/tie_spec.rb +24 -0
  287. data/spec/models/socializer/verb_spec.rb +24 -0
  288. data/spec/spec_helper.rb +84 -0
  289. data/vendor/assets/javascripts/moment/langs.js +5841 -0
  290. data/vendor/assets/javascripts/moment/moment.js +2400 -0
  291. metadata +754 -0
@@ -0,0 +1,162 @@
1
+ module Socializer
2
+ class Activity < ActiveRecord::Base
3
+ include Socializer::ObjectTypeBase
4
+
5
+ default_scope { order(created_at: :desc) }
6
+
7
+ attr_accessible :verb, :circles, :actor_id, :activity_object_id, :target_id
8
+
9
+ belongs_to :parent, class_name: 'Activity', foreign_key: 'target_id'
10
+ belongs_to :activitable_actor, class_name: 'ActivityObject', foreign_key: 'actor_id'
11
+ belongs_to :activitable_object, class_name: 'ActivityObject', foreign_key: 'activity_object_id'
12
+ belongs_to :activitable_target, class_name: 'ActivityObject', foreign_key: 'target_id'
13
+ belongs_to :verb, inverse_of: :activities
14
+
15
+ has_one :activity_field, inverse_of: :activity
16
+ has_many :audiences # , dependent: :destroy
17
+ has_many :activity_objects, through: :audiences
18
+ has_many :children, class_name: 'Activity', foreign_key: 'target_id', dependent: :destroy
19
+ has_many :notifications
20
+
21
+ validates :verb, presence: true
22
+
23
+ def comments
24
+ @comments ||= children.joins(:activitable_object).where { activitable_object.activitable_type.eq('Socializer::Comment') }
25
+ end
26
+
27
+ def actor
28
+ @actor ||= activitable_actor.activitable
29
+ end
30
+
31
+ def object
32
+ @object ||= activitable_object.activitable
33
+ end
34
+
35
+ def target
36
+ @target ||= activitable_target.activitable
37
+ end
38
+
39
+ # Selects the activites that either the person made, that is public from a person in
40
+ # one of his circle, or that is shared to one of the circles he is part of.
41
+ #
42
+ # * <tt>options[:provider]</tt> - <tt>nil</tt>, <tt>activities</tt>, <tt>people</tt>, <tt>circles</tt>, <tt>groups</tt>
43
+ # * <tt>options[:actor_id]</tt> - unique identifier of the previously typed provider
44
+ # * <tt>options[:viewer_id]</tt> - who wants to see the activity stream
45
+ #
46
+ # Activity.stream(provider: nil, actor_id: current_user.id, viewer_id: current_user.id)
47
+ def self.stream(options = {})
48
+ options.assert_valid_keys(:provider, :actor_id, :viewer_id)
49
+
50
+ provider = options.delete(:provider)
51
+ actor_uid = options.delete(:actor_id)
52
+ viewer_id = options.delete(:viewer_id)
53
+
54
+ viewer_id = Person.find(viewer_id).guid
55
+
56
+ query = build_query(viewer_id)
57
+
58
+ case provider
59
+ when nil
60
+ # this is your dashboard. display everything about people in circles and yourself.
61
+ query.uniq
62
+ when 'activities'
63
+ # we only want to display a single activity. make sure the viewer is allowed to do so.
64
+ activity_id = actor_uid
65
+ query.where { id.eq(activity_id) }.uniq
66
+ when 'people'
67
+ # this is a user profile. display everything about him that you are allowed to see
68
+ person_id = Person.find(actor_uid).guid
69
+ query.where { actor_id.eq(person_id) }.uniq
70
+ when 'circles'
71
+ # FIXME: Should display notes even if circle has no members and the owner is viewing it.
72
+ # Notes still don't show after adding people to the circles.
73
+
74
+ circles_sql = Circle.select { id }.where { (id.eq actor_uid) & (author_id.eq viewer_id) }
75
+ followed_sql = Tie.select { contact_id }.where { circle_id.in(circles_sql) }
76
+
77
+ query.where { actor_id.in(followed_sql) }.uniq
78
+ when 'groups'
79
+ # this is a group. display everything that was posted to this group as audience
80
+ group_id = Group.find(actor_uid).guid
81
+ # query.where(audiences: {activity_object_id: group_id}).uniq
82
+ query.where { audiences.activity_object_id.eq(group_id) }.uniq
83
+ else
84
+ fail 'Unknown stream provider.'
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def self.build_query(viewer_id)
91
+ fail 'viewer_id cannot be nil.' if viewer_id.nil?
92
+
93
+ # for an activity to be interesting, it must correspond to one of these verbs
94
+ verbs_of_interest = %w(post share)
95
+ verbs_of_interest = Verb.where { name.in(verbs_of_interest) }
96
+
97
+ # privacy_levels
98
+ privacy_public = Audience.privacy_level.find_value(:public).value
99
+ privacy_circles = Audience.privacy_level.find_value(:circles).value
100
+ privacy_limited = Audience.privacy_level.find_value(:limited).value
101
+
102
+ query = joins { audiences }.where { verb_id.in(verbs_of_interest) }.where { target_id.eq(nil) }
103
+
104
+ query.where { (audiences.privacy_level == privacy_public) |
105
+ ((audiences.privacy_level == privacy_circles) & `#{viewer_id}`.in(my { build_circles_subquery })) |
106
+ ((audiences.privacy_level == privacy_limited) & (
107
+ `#{viewer_id}`.in(my { build_limited_circle_subquery(viewer_id) }) |
108
+ audiences.activity_object_id.in(my { build_limited_group_subquery(viewer_id) }) |
109
+ audiences.activity_object_id.in(my { build_limited_viewer_subquery(viewer_id) })
110
+ )) |
111
+ (actor_id == viewer_id) }
112
+ end
113
+
114
+ # Audience : CIRCLES
115
+ # Ensure the audience is CIRCLES and then make sure that the viewer is in those circles
116
+ def self.build_circles_subquery
117
+ # TODO: Convert this to squeel
118
+
119
+ # Retrieve the author's unique identifier
120
+ subquery = 'SELECT socializer_activity_objects.id ' \
121
+ 'FROM socializer_activity_objects ' \
122
+ 'INNER JOIN socializer_people ' \
123
+ 'ON socializer_activity_objects.activitable_id = socializer_people.id ' \
124
+ 'WHERE socializer_people.id = socializer_activities.actor_id'
125
+
126
+ Circle.select { id }.where { author_id.in(`#{subquery}`) }
127
+ end
128
+
129
+ # Audience : LIMITED
130
+ # Ensure that the audience is LIMITED and then make sure that the viewer is either
131
+ # part of a circle that is the target audience, or that the viewer is part of
132
+ # a group that is the target audience, or that the viewer is the target audience.
133
+ def self.build_limited_circle_subquery(viewer_id)
134
+ # TODO: Convert this to squeel
135
+
136
+ # Retrieve the circle's unique identifier related to the audience (when the audience
137
+ # is not a circle, this query will simply return nothing)
138
+ limited_circle_id_sql = 'SELECT socializer_circles.id ' \
139
+ 'FROM socializer_circles ' \
140
+ 'INNER JOIN socializer_activity_objects ' \
141
+ 'ON socializer_circles.id = socializer_activity_objects.activitable_id ' \
142
+ "AND socializer_activity_objects.activitable_type = 'Socializer::Circle' " \
143
+ 'WHERE socializer_activity_objects.id = socializer_audiences.activity_object_id '
144
+
145
+ # Retrieve all the contacts (people) that are part of those circles
146
+ Tie.select { contact_id }.where { circle_id.in(`#{limited_circle_id_sql}`) }
147
+
148
+ # Ensure that the audience is LIMITED and then make sure that the viewer is either
149
+ # part of a circle that is the target audience, or that the viewer is part of
150
+ # a group that is the target audience, or that the viewer is the target audience.
151
+ # limited_sql = Audience.with_privacy_level(:limited).where{(`"#{viewer_id}"`.in(actor_circles_sql)) | (activity_object_id.in(limited_groups_sql)) | (activity_object_id.eq(viewer_id))}
152
+ end
153
+
154
+ def self.build_limited_group_subquery(viewer_id)
155
+ ActivityObject.select { id }.joins { activitable(Group).memberships }.where { socializer_memberships.member_id.eq(viewer_id) }
156
+ end
157
+
158
+ def self.build_limited_viewer_subquery(viewer_id)
159
+ "( #{viewer_id} )"
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,10 @@
1
+ module Socializer
2
+ class ActivityField < ActiveRecord::Base
3
+ attr_accessible :content, :activity
4
+
5
+ belongs_to :activity, inverse_of: :activity_field
6
+
7
+ validates :content, presence: true
8
+ validates :activity, presence: true
9
+ end
10
+ end
@@ -0,0 +1,118 @@
1
+ module Socializer
2
+ class ActivityObject < ActiveRecord::Base
3
+ attr_accessor :scope, :object_ids
4
+ attr_accessible :scope, :object_ids, :activitable_id, :activitable_type, :like_count, :unread_notifications_count
5
+
6
+ belongs_to :activitable, polymorphic: true
7
+
8
+ has_many :notifications
9
+ has_many :audiences # , dependent: :destroy
10
+ # FIXME: This shouldn't need source. See if it is fixed in Rails 4 RC2 or Final
11
+ has_many :activities, through: :audiences, source: :activity
12
+
13
+ has_many :actor_activities, class_name: 'Activity', foreign_key: 'actor_id', dependent: :destroy
14
+ has_many :object_activities, class_name: 'Activity', foreign_key: 'activity_object_id', dependent: :destroy
15
+ has_many :target_activities, class_name: 'Activity', foreign_key: 'target_id', dependent: :destroy
16
+
17
+ # when the embedded object is an actor (person/group)
18
+ # it can be the owner of certain objects.
19
+ has_many :notes, foreign_key: 'author_id'
20
+ has_many :comments, foreign_key: 'author_id'
21
+ has_many :groups, foreign_key: 'author_id'
22
+ has_many :circles, foreign_key: 'author_id'
23
+
24
+ has_many :ties, foreign_key: 'contact_id'
25
+ has_many :memberships, -> { where active: true }, foreign_key: 'member_id'
26
+
27
+ # define a class macro for setting comparaison with activitable_type
28
+ def self.attribute_type_of(*args)
29
+ args.each do |type|
30
+ define_method("#{type}?") { activitable_type == "Socializer::#{type.capitalize}" }
31
+ end
32
+ end
33
+
34
+ attribute_type_of :note, :activity, :comment, :person, :group, :circle
35
+
36
+ # REFACTOR: DRY this up. Reduce database calls
37
+ # TODO: Rename this method to liked_by
38
+ def likes
39
+ people = []
40
+ query = Activity.joins { verb }.where { activity_object_id.eq(my { id }) }
41
+
42
+ activities_likes = query.where { verb.name.eq('like') }
43
+ activities_likes.each do |activity|
44
+ people.push activity.actor
45
+ end
46
+
47
+ activities_unlikes = query.where { verb.name.eq('unlike') }
48
+ activities_unlikes.each do |activity|
49
+ people.delete_at people.index(activity.actor)
50
+ end
51
+
52
+ people
53
+ end
54
+
55
+ def like!(person)
56
+ activity = Activity.create! do |a|
57
+ a.actor_id = person.activity_object.id
58
+ a.activity_object_id = id
59
+ a.verb = Verb.find_or_create_by(name: 'like')
60
+ end
61
+
62
+ Audience.create!(privacy_level: :public, activity_id: activity.id)
63
+
64
+ increment_like_count
65
+ end
66
+
67
+ def unlike!(person)
68
+ activity = Activity.create! do |a|
69
+ a.actor_id = person.activity_object.id
70
+ a.activity_object_id = id
71
+ a.verb = Verb.find_or_create_by(name: 'unlike')
72
+ end
73
+
74
+ Audience.create!(privacy_level: :public, activity_id: activity.id)
75
+
76
+ decrement_like_count
77
+ end
78
+
79
+ def share!
80
+ public = Socializer::Audience.privacy_level.find_value(:public).value.to_s
81
+ circles = Socializer::Audience.privacy_level.find_value(:circles).value.to_s
82
+
83
+ activity = Activity.new do |a|
84
+ a.actor_id = actor_id
85
+ a.activity_object_id = id
86
+ a.verb = Verb.find_or_create_by(name: 'share')
87
+ end
88
+
89
+ # REFACTOR: remove duplication
90
+ if scope == public || scope == circles
91
+ activity.audiences.build(privacy_level: scope)
92
+ else
93
+ object_ids.each do |object_id|
94
+ activity.audiences.build do |a|
95
+ a.privacy_level = :limited
96
+ a.activity_object_id = object_id
97
+ end
98
+ end
99
+ end
100
+
101
+ activity.save!
102
+ end
103
+
104
+ def increment_unread_notifications_count
105
+ increment!(:unread_notifications_count)
106
+ end
107
+
108
+ private
109
+
110
+ def increment_like_count
111
+ increment!(:like_count)
112
+ end
113
+
114
+ def decrement_like_count
115
+ decrement!(:like_count)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,19 @@
1
+ module Socializer
2
+ class Audience < ActiveRecord::Base
3
+ extend Enumerize
4
+
5
+ enumerize :privacy_level, in: { public: 1, circles: 2, limited: 3 }, default: :public, predicates: true, scope: true
6
+
7
+ attr_accessible :activity_id, :privacy_level
8
+
9
+ belongs_to :activity
10
+ belongs_to :activity_object
11
+
12
+ validates :activity_id, presence: true, uniqueness: { scope: :activity_object_id }
13
+ validates :privacy_level, presence: true
14
+
15
+ def object
16
+ @object ||= activity_object.activitable
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module Socializer
2
+ class Authentication < ActiveRecord::Base
3
+ attr_accessible :provider, :uid, :image_url
4
+
5
+ belongs_to :person
6
+
7
+ before_destroy :make_sure_its_not_the_last_one
8
+
9
+ private
10
+
11
+ def make_sure_its_not_the_last_one
12
+ if person.authentications.count == 1
13
+ # FIXME: authentication - This is not be a good user experience.
14
+ # If the user clicks 'unbind' on their last authentication they will get an error.
15
+ # A flash notice should be set and the user should be able to continue on.
16
+ fail 'Cannot delete the last authentication available.'
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,32 @@
1
+ module Socializer
2
+ class Circle < ActiveRecord::Base
3
+ include Socializer::ObjectTypeBase
4
+
5
+ attr_accessible :name, :content
6
+
7
+ belongs_to :activity_author, class_name: 'ActivityObject', foreign_key: 'author_id'
8
+
9
+ has_many :ties
10
+ has_many :activity_contacts, through: :ties
11
+
12
+ validates :name, presence: true, uniqueness: { scope: :author_id }
13
+
14
+ def author
15
+ @author ||= activity_author.activitable
16
+ end
17
+
18
+ def contacts
19
+ @contacts ||= activity_contacts.map { |ec| ec.activitable }
20
+ end
21
+
22
+ def add_contact(contact_id)
23
+ @tie = ties.build(contact_id: contact_id)
24
+ @tie.save
25
+ end
26
+
27
+ def remove_contact(contact_id)
28
+ @tie = ties.find_by(contact_id: contact_id)
29
+ @tie.destroy
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ module Socializer
2
+ class Comment < ActiveRecord::Base
3
+ include Socializer::ObjectTypeBase
4
+
5
+ attr_accessible :content
6
+
7
+ belongs_to :activity_author, class_name: 'ActivityObject', foreign_key: 'author_id'
8
+
9
+ def author
10
+ @author ||= activity_author.activitable
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,100 @@
1
+ module Socializer
2
+ class Group < ActiveRecord::Base
3
+ extend Enumerize
4
+ include Socializer::ObjectTypeBase
5
+
6
+ enumerize :privacy_level, in: { public: 1, restricted: 2, private: 3 }, default: :public, predicates: true, scope: true
7
+
8
+ attr_accessible :name, :privacy_level, :author_id
9
+
10
+ # Relationships
11
+ belongs_to :activity_author, class_name: 'ActivityObject', foreign_key: 'author_id'
12
+
13
+ has_many :memberships
14
+ has_many :activity_members, -> { where(socializer_memberships: { active: true }) }, through: :memberships
15
+ has_many :links, class_name: 'GroupLink', foreign_key: 'group_id', dependent: :destroy
16
+ has_many :categories, class_name: 'GroupCategory', foreign_key: 'group_id', dependent: :destroy
17
+
18
+ # Validations
19
+ validates :author_id, presence: true
20
+ validates :name, presence: true, uniqueness: { scope: :author_id }
21
+ validates :privacy_level, presence: true
22
+
23
+ # Callbacks
24
+ after_create :add_author_to_members
25
+ before_destroy :deny_delete_if_members
26
+
27
+ # Named Scopes
28
+
29
+ # Class Methods
30
+ def self.public
31
+ Group.with_privacy_level(:public)
32
+ end
33
+
34
+ def self.restricted
35
+ Group.with_privacy_level(:restricted)
36
+ end
37
+
38
+ def self.private
39
+ Group.with_privacy_level(:private)
40
+ end
41
+
42
+ def self.joinable
43
+ Group.with_privacy_level(:public, :restricted)
44
+ end
45
+
46
+ # Instance Methods
47
+ def author
48
+ @author ||= activity_author.activitable
49
+ end
50
+
51
+ def members
52
+ @members ||= activity_members.map { |em| em.activitable }
53
+ end
54
+
55
+ def join(person)
56
+ @membership = person.memberships.build(group_id: id)
57
+
58
+ if privacy_level.public?
59
+ @membership.active = true
60
+ elsif privacy_level.restricted?
61
+ @membership.active = false
62
+ else
63
+ fail 'Cannot self-join a private group, you need to be invited'
64
+ end
65
+
66
+ @membership.save
67
+ end
68
+
69
+ def invite(person)
70
+ @membership = person.memberships.build(group_id: id)
71
+
72
+ @membership.active = false
73
+ @membership.save
74
+ end
75
+
76
+ def leave(person)
77
+ @membership = person.memberships.find_by(group_id: id)
78
+ @membership.destroy
79
+ end
80
+
81
+ def member?(person)
82
+ person.memberships.find_by(group_id: id).present?
83
+ end
84
+
85
+ private
86
+
87
+ def add_author_to_members
88
+ @membership = author.memberships.build(group_id: id)
89
+
90
+ @membership.active = true
91
+ @membership.save
92
+ end
93
+
94
+ def deny_delete_if_members
95
+ if memberships.count > 0
96
+ fail 'Cannot delete a group that has members in it.'
97
+ end
98
+ end
99
+ end
100
+ end