Commit 01b9a054c2070f9392b6d57f3a4c5e6d1209a1ef

Authored by Leandro Santos
2 parents 8efc035e d86bcb8f
Exists in staging and in 1 other branch production

Merge branch 'master' into staging

app/api/entities.rb
@@ -295,12 +295,19 @@ module Api @@ -295,12 +295,19 @@ module Api
295 295
296 class Activity < Entity 296 class Activity < Entity
297 root 'activities', 'activity' 297 root 'activities', 'activity'
298 - expose :id, :params, :verb, :created_at, :updated_at, :comments_count, :visible 298 + expose :id, :created_at, :updated_at
299 expose :user, :using => Profile 299 expose :user, :using => Profile
  300 +
300 expose :target do |activity, opts| 301 expose :target do |activity, opts|
301 type_map = {Profile => ::Profile, ArticleBase => ::Article}.find {|h| activity.target.kind_of?(h.last)} 302 type_map = {Profile => ::Profile, ArticleBase => ::Article}.find {|h| activity.target.kind_of?(h.last)}
302 type_map.first.represent(activity.target) unless type_map.nil? 303 type_map.first.represent(activity.target) unless type_map.nil?
303 end 304 end
  305 + expose :params, :if => lambda { |activity, options| activity.kind_of?(ActionTracker::Record)}
  306 + expose :content, :if => lambda { |activity, options| activity.kind_of?(Scrap)}
  307 + expose :verb do |activity, options|
  308 + activity.kind_of?(Scrap) ? 'leave_scrap' : activity.verb
  309 + end
  310 +
304 end 311 end
305 312
306 class Role < Entity 313 class Role < Entity
app/api/helpers.rb
@@ -198,6 +198,28 @@ module Api @@ -198,6 +198,28 @@ module Api
198 present_partial tasks, :with => Entities::Task 198 present_partial tasks, :with => Entities::Task
199 end 199 end
200 200
  201 + ###########################
  202 + # Activities #
  203 + ###########################
  204 + def find_activities(asset, method_or_relation = 'activities')
  205 +
  206 + not_found! if asset.blank? || asset.secret || !asset.visible
  207 + forbidden! if !asset.display_private_info_to?(current_person)
  208 +
  209 + activities = select_filtered_collection_of(asset, method_or_relation, params)
  210 + activities = activities.map(&:activity)
  211 + activities
  212 + end
  213 +
  214 + def present_activities_for_asset(asset, method_or_relation = 'activities')
  215 + tasks = find_activities(asset, method_or_relation)
  216 + present_activities(tasks)
  217 + end
  218 +
  219 + def present_activities(activities)
  220 + present_partial activities, :with => Entities::Activity, :current_person => current_person
  221 + end
  222 +
201 def make_conditions_with_parameter(params = {}) 223 def make_conditions_with_parameter(params = {})
202 parsed_params = parser_params(params) 224 parsed_params = parser_params(params)
203 conditions = {} 225 conditions = {}
@@ -221,7 +243,7 @@ module Api @@ -221,7 +243,7 @@ module Api
221 order = 'RANDOM()' 243 order = 'RANDOM()'
222 else 244 else
223 field_name, direction = params[:order].split(' ') 245 field_name, direction = params[:order].split(' ')
224 - assoc_class = extract_associated_classname(method_or_relation) 246 + assoc_class = extract_associated_classname(object, method_or_relation)
225 if !field_name.blank? and assoc_class 247 if !field_name.blank? and assoc_class
226 if assoc_class.attribute_names.include? field_name 248 if assoc_class.attribute_names.include? field_name
227 if direction.present? and ['ASC','DESC'].include? direction.upcase 249 if direction.present? and ['ASC','DESC'].include? direction.upcase
@@ -234,12 +256,12 @@ module Api @@ -234,12 +256,12 @@ module Api
234 return order 256 return order
235 end 257 end
236 258
237 - def make_timestamp_with_parameters_and_method(params, method_or_relation) 259 + def make_timestamp_with_parameters_and_method(object, method_or_relation, params)
238 timestamp = nil 260 timestamp = nil
239 if params[:timestamp] 261 if params[:timestamp]
240 datetime = DateTime.parse(params[:timestamp]) 262 datetime = DateTime.parse(params[:timestamp])
241 - table_name = extract_associated_tablename(method_or_relation)  
242 - assoc_class = extract_associated_classname(method_or_relation) 263 + table_name = extract_associated_tablename(object, method_or_relation)
  264 + assoc_class = extract_associated_classname(object, method_or_relation)
243 date_atrr = assoc_class.attribute_names.include?('updated_at') ? 'updated_at' : 'created_at' 265 date_atrr = assoc_class.attribute_names.include?('updated_at') ? 'updated_at' : 'created_at'
244 timestamp = "#{table_name}.#{date_atrr} >= '#{datetime}'" 266 timestamp = "#{table_name}.#{date_atrr} >= '#{datetime}'"
245 end 267 end
@@ -279,7 +301,7 @@ module Api @@ -279,7 +301,7 @@ module Api
279 def select_filtered_collection_of(object, method_or_relation, params) 301 def select_filtered_collection_of(object, method_or_relation, params)
280 conditions = make_conditions_with_parameter(params) 302 conditions = make_conditions_with_parameter(params)
281 order = make_order_with_parameters(object,method_or_relation,params) 303 order = make_order_with_parameters(object,method_or_relation,params)
282 - timestamp = make_timestamp_with_parameters_and_method(params, method_or_relation) 304 + timestamp = make_timestamp_with_parameters_and_method(object, method_or_relation, params)
283 305
284 objects = is_a_relation?(method_or_relation) ? method_or_relation : object.send(method_or_relation) 306 objects = is_a_relation?(method_or_relation) ? method_or_relation : object.send(method_or_relation)
285 objects = by_reference(objects, params) 307 objects = by_reference(objects, params)
@@ -450,15 +472,15 @@ module Api @@ -450,15 +472,15 @@ module Api
450 end 472 end
451 private 473 private
452 474
453 - def extract_associated_tablename(method_or_relation)  
454 - extract_associated_classname(method_or_relation).table_name 475 + def extract_associated_tablename(object, method_or_relation)
  476 + extract_associated_classname(object, method_or_relation).table_name
455 end 477 end
456 478
457 - def extract_associated_classname(method_or_relation) 479 + def extract_associated_classname(object, method_or_relation)
458 if is_a_relation?(method_or_relation) 480 if is_a_relation?(method_or_relation)
459 method_or_relation.blank? ? '' : method_or_relation.first.class 481 method_or_relation.blank? ? '' : method_or_relation.first.class
460 else 482 else
461 - method_or_relation.to_s.singularize.camelize.constantize 483 + object.send(method_or_relation).table_name.singularize.camelize.constantize
462 end 484 end
463 end 485 end
464 486
app/api/v1/activities.rb
@@ -5,13 +5,8 @@ module Api @@ -5,13 +5,8 @@ module Api
5 resource :profiles do 5 resource :profiles do
6 6
7 get ':id/activities' do 7 get ':id/activities' do
8 - profile = Profile.find_by id: params[:id]  
9 -  
10 - not_found! if profile.blank? || profile.secret || !profile.visible  
11 - forbidden! if !profile.display_private_info_to?(current_person)  
12 -  
13 - activities = profile.activities.map(&:activity)  
14 - present activities, :with => Entities::Activity, :current_person => current_person 8 + profile = environment.profiles.find_by id: params[:id]
  9 + present_activities_for_asset(profile)
15 end 10 end
16 end 11 end
17 end 12 end
app/api/v1/roles.rb
@@ -5,14 +5,15 @@ module Api @@ -5,14 +5,15 @@ module Api
5 5
6 MAX_PER_PAGE = 50 6 MAX_PER_PAGE = 50
7 7
8 - resource :organizations do  
9 - segment "/:organization_id" do 8 + resource :profiles do
  9 + segment "/:profile_id" do
10 resource :roles do 10 resource :roles do
11 11
12 paginate max_per_page: MAX_PER_PAGE 12 paginate max_per_page: MAX_PER_PAGE
13 get do 13 get do
14 - organization = environment.profiles.find(params[:organization_id])  
15 - roles = Profile::Roles.organization_roles(organization.environment.id, organization.id) 14 + profile = environment.profiles.find(params[:profile_id])
  15 + return forbidden! unless profile.kind_of?(Organization)
  16 + roles = Profile::Roles.organization_roles(profile.environment.id, profile.id)
16 present_partial paginate(roles), with: Entities::Role 17 present_partial paginate(roles), with: Entities::Role
17 end 18 end
18 19
app/api/v1/tasks.rb
@@ -31,8 +31,13 @@ module Api @@ -31,8 +31,13 @@ module Api
31 desc "#{action.capitalize} a task" 31 desc "#{action.capitalize} a task"
32 put ":id/#{action}" do 32 put ":id/#{action}" do
33 task = find_task(current_person, Task.to(current_person), params[:id]) 33 task = find_task(current_person, Task.to(current_person), params[:id])
34 - task.send(action, current_person) if (task.status == Task::Status::ACTIVE)  
35 - present_partial task, :with => Entities::Task 34 + begin
  35 + task.update(params[:task])
  36 + task.send(action, current_person) if (task.status == Task::Status::ACTIVE)
  37 + present_partial task, :with => Entities::Task
  38 + rescue Exception => ex
  39 + render_api_error!(ex.message, 500)
  40 + end
36 end 41 end
37 end 42 end
38 end 43 end
app/models/scrap.rb
@@ -36,6 +36,9 @@ class Scrap &lt; ApplicationRecord @@ -36,6 +36,9 @@ class Scrap &lt; ApplicationRecord
36 36
37 before_validation :strip_all_html_tags 37 before_validation :strip_all_html_tags
38 38
  39 + alias :user :sender
  40 + alias :target :receiver
  41 +
39 def top_root 42 def top_root
40 scrap = self 43 scrap = self
41 scrap = Scrap.find(scrap.scrap_id) while scrap.scrap_id 44 scrap = Scrap.find(scrap.scrap_id) while scrap.scrap_id
plugins/profile_images/lib/profile_images_plugin.rb 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +class ProfileImagesPlugin < Noosfero::Plugin
  2 + def self.plugin_name
  3 + 'ProfileImagesPlugin'
  4 + end
  5 +
  6 + def self.plugin_description
  7 + _('Adds a block that lists all images inside a profile.')
  8 + end
  9 +
  10 + def self.extra_blocks
  11 + {
  12 + ProfileImagesPlugin::ProfileImagesBlock => { type: [Person, Community, Enterprise] }
  13 + }
  14 + end
  15 +
  16 + def self.has_admin_url?
  17 + false
  18 + end
  19 +
  20 + def stylesheet?
  21 + true
  22 + end
  23 +end
plugins/profile_images/lib/profile_images_plugin/profile_images_block.rb 0 → 100644
@@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
  1 +class ProfileImagesPlugin::ProfileImagesBlock < Block
  2 + attr_accessible :limit
  3 + settings_items :limit, type: :integer, default: 6
  4 +
  5 + def view_title
  6 + self.default_title
  7 + end
  8 +
  9 + def images
  10 + images = owner.articles.images
  11 + self.limit.nil? ? images : images.first(self.get_limit)
  12 + end
  13 +
  14 + def extra_option
  15 + { }
  16 + end
  17 +
  18 + def self.description
  19 + _('Display the images inside the context where the block is available.')
  20 + end
  21 +
  22 + def help
  23 + _('This block lists the images inside this profile.')
  24 + end
  25 +
  26 + def default_title
  27 + _('Profile images')
  28 + end
  29 +
  30 + def api_content
  31 + content = []
  32 + images.each do |image|
  33 + content << { title: image.title, view_url: image.view_url, path: image.public_filename(:thumb), id: image.id }
  34 + end
  35 + { images: content }
  36 + end
  37 +
  38 + def display_api_content_by_default?
  39 + false
  40 + end
  41 +
  42 + def timeout
  43 + 4.hours
  44 + end
  45 +
  46 + def self.expire_on
  47 + { profile: [:article] }
  48 + end
  49 +end
plugins/profile_images/public/style.css 0 → 100644
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +.profile-images-block ul {
  2 + padding: 0;
  3 + display: block;
  4 + width: 100%;
  5 +}
  6 +
  7 +.profile-images-block li {
  8 + list-style: none;
  9 +}
  10 +
  11 +.profile-images-block a {
  12 + display: block;
  13 + width: 53px;
  14 + height: 53px;
  15 + float: left;
  16 + padding: 1px;
  17 + border: 1px solid #ccc;
  18 + margin: 3px;
  19 + background-size: cover;
  20 +}
  21 +
  22 +.profile-images-block a:hover {
  23 + border: 1px solid #000;
  24 +}
  25 +
  26 +.profile-images-block a span {
  27 + display: none;
  28 +}
plugins/profile_images/test/test_helper.rb 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +require_relative '../../../test/test_helper'
plugins/profile_images/test/unit/profile_images_block_test.rb 0 → 100644
@@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
  1 +require_relative '../test_helper'
  2 +
  3 +class ProfileImagesBlockTest < ActiveSupport::TestCase
  4 + should 'describe itself' do
  5 + assert_not_equal Block.description, ProfileImagesPlugin::ProfileImagesBlock.description
  6 + end
  7 +
  8 + should 'is editable' do
  9 + block = ProfileImagesPlugin::ProfileImagesBlock.new
  10 + assert block.editable?
  11 + end
  12 +
  13 + should 'return images' do
  14 + # profile
  15 + # |- image1
  16 + # |- file
  17 + # |- folder1/
  18 + # |--- image2
  19 + # |--- folder2/
  20 + # |------ image3
  21 + profile = create(Profile, name: 'Test')
  22 + image1 = create(UploadedFile, uploaded_data: fixture_file_upload('/files/rails.png', 'image/png'), profile: profile)
  23 + file = fast_create(UploadedFile, profile_id: profile.id)
  24 + folder1 = fast_create(Folder, profile_id: profile.id)
  25 + image2 = create(UploadedFile, uploaded_data: fixture_file_upload('/files/rails.png', 'image/png'), profile: profile, parent: folder1)
  26 + folder2 = fast_create(Folder, parent_id: folder1.id)
  27 + image3 = create(UploadedFile, uploaded_data: fixture_file_upload('/files/rails.png', 'image/png'), profile: profile, parent: folder2)
  28 +
  29 + block = ProfileImagesPlugin::ProfileImagesBlock.new
  30 + block.stubs(:owner).returns(profile)
  31 +
  32 + assert_equal [image1, image2, image3].map(&:id), block.images.map(&:id)
  33 + end
  34 +
  35 + should 'return images with limit' do
  36 + # profile
  37 + # |- image1
  38 + # |- image2
  39 + profile = create(Profile, name: 'Test')
  40 + image1 = create(UploadedFile, uploaded_data: fixture_file_upload('/files/rails.png', 'image/png'), profile: profile)
  41 + image2 = create(UploadedFile, uploaded_data: fixture_file_upload('/files/rails.png', 'image/png'), profile: profile)
  42 +
  43 + block = ProfileImagesPlugin::ProfileImagesBlock.new
  44 + block.stubs(:owner).returns(profile)
  45 + block.limit = 1
  46 +
  47 + assert_equal [image1.id], block.images.map(&:id)
  48 + end
  49 +end
  50 +
  51 +require 'boxes_helper'
  52 +
  53 +class ProfileImagesBlockViewTest < ActionView::TestCase
  54 + include BoxesHelper
  55 +
  56 + should 'return images in api_content' do
  57 + # profile
  58 + # |- image1
  59 + # |- image2
  60 + profile = create(Profile, name: 'Test')
  61 + image1 = create(UploadedFile, uploaded_data: fixture_file_upload('/files/rails.png', 'image/png'), profile: profile)
  62 + image2 = create(UploadedFile, uploaded_data: fixture_file_upload('/files/rails.png', 'image/png'), profile: profile)
  63 +
  64 + block = ProfileImagesPlugin::ProfileImagesBlock.new
  65 + block.stubs(:owner).returns(profile)
  66 +
  67 + assert_equal [image1.id, image2.id], block.api_content[:images].map{ |a| a[:id] }
  68 + end
  69 +end
plugins/profile_images/views/blocks/profile_images.html.erb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +<%= block_title(block.view_title, block.subtitle) %>
  2 +
  3 +<div class="profile-images-block">
  4 + <% unless block.images.size == 0 %>
  5 + <ul>
  6 + <% block.images.each do |image| %>
  7 + <li><%= link_to content_tag(:span, image.title), image.view_url, style: "background-image: url(#{image.public_filename(:thumb)})" %></li>
  8 + <% end %>
  9 + </ul>
  10 + <br style="clear: both;" />
  11 + <% else %>
  12 + <div class="profile-images-block-none"><%= c_('None') %></div>
  13 + <% end %>
  14 +</div>
plugins/profile_images/views/box_organizer/profile_images_plugin/_profile_images_block.html.erb 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +<div id="profile_images_block_plugin">
  2 + <%= labelled_form_field(_('Limit'), text_field(:block, :limit, size: 3)) %>
  3 +</div>
plugins/profile_images/views/profile_design 0 → 120000
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +box_organizer/
0 \ No newline at end of file 2 \ No newline at end of file
plugins/recent_activities/lib/ext/action_tracker_model.rb 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +require_dependency 'action_tracker_model'
  2 +
  3 +class ActionTracker::Record
  4 + def label
  5 + case self.target.class.name
  6 + when 'Event'
  7 + 'events'
  8 + when 'Community'
  9 + 'communities'
  10 + when 'Friendship'
  11 + 'people'
  12 + else
  13 + 'posts'
  14 + end
  15 + end
  16 +end
plugins/recent_activities/lib/ext/entities.rb 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +require_dependency 'api/entities'
  2 +
  3 +module Api
  4 + module Entities
  5 + class Activity
  6 + expose :label
  7 + expose :start_date do |activity|
  8 + activity.target.start_date if activity.target.is_a?(Event)
  9 + end
  10 + end
  11 + end
  12 +end
plugins/recent_activities/locales/en.yml 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +"en-US": &en-US
  2 + time:
  3 + formats:
  4 + medium: "%B %d, %Y"
plugins/recent_activities/locales/pt-BR.yml 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +"pt-BR": &pt-BR
  2 + time:
  3 + formats:
  4 + medium: "%d de %B de %Y"
plugins/recent_activities/locales/pt-PT.yml 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +"pt-PT": &pt-PT
  2 + time:
  3 + formats:
  4 + medium: "%d de %B de %Y"
plugins/recent_activities/locales/pt.yml 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +"pt": &pt
  2 + time:
  3 + formats:
  4 + medium: "%d de %B de %Y"
plugins/recent_activities/public/style.css
@@ -18,7 +18,8 @@ @@ -18,7 +18,8 @@
18 color: #ccc; 18 color: #ccc;
19 } 19 }
20 20
21 -.recent-activities-block img { 21 +.recent-activities-block img,
  22 +.recent-activities-block .upimg {
22 padding: 1px; 23 padding: 1px;
23 border: 1px solid #ccc; 24 border: 1px solid #ccc;
24 margin: 3px 3px 0 0; 25 margin: 3px 3px 0 0;
@@ -27,3 +28,45 @@ @@ -27,3 +28,45 @@
27 .recent-activities-block p:first-letter { 28 .recent-activities-block p:first-letter {
28 text-transform: capitalize 29 text-transform: capitalize
29 } 30 }
  31 +
  32 +.recent-activities-block .upimg {
  33 + width: 20px;
  34 + height: 20px;
  35 + display: inline-block;
  36 + background-size: cover;
  37 +}
  38 +
  39 +.recent-activities-block .recent-activity-date,
  40 +.recent-activities-block .recent-activity-date a,
  41 +.recent-activities-block .recent-activity-date a:visited {
  42 + color: #888a85;
  43 +}
  44 +
  45 +.recent-activities-block .recent-activity-label {
  46 + padding: 2px;
  47 + text-transform: capitalize;
  48 + background: #333;
  49 + color: #fff;
  50 + margin-left: 5px;
  51 + display: inline-block;
  52 + font-size: 11px;
  53 +}
  54 +
  55 +.recent-activities-block .recent-activity-event {
  56 + display: table-row;
  57 +}
  58 +
  59 +.recent-activities-block .recent-activity-events img {
  60 + width: 32px;
  61 + height: 32px;
  62 + float: left;
  63 +}
  64 +
  65 +.recent-activities-block .recent-activity-events .lead {
  66 + width: 148px;
  67 + margin-left: 2px;
  68 + margin-top: 2px;
  69 + float: left;
  70 + display: block;
  71 + text-align: justify;
  72 +}
plugins/recent_activities/test/unit/recent_activities_block_test.rb
@@ -68,6 +68,31 @@ class RecentActivitiesBlockViewTest &lt; ActionView::TestCase @@ -68,6 +68,31 @@ class RecentActivitiesBlockViewTest &lt; ActionView::TestCase
68 block = RecentActivitiesPlugin::ActivitiesBlock.new 68 block = RecentActivitiesPlugin::ActivitiesBlock.new
69 block.stubs(:owner).returns(profile) 69 block.stubs(:owner).returns(profile)
70 70
  71 + api_activity = block.api_content['activities'].last
71 assert_equal [a.id], block.api_content['activities'].map{ |a| a[:id] } 72 assert_equal [a.id], block.api_content['activities'].map{ |a| a[:id] }
  73 + assert_not_nil api_activity[:label]
  74 + assert_nil api_activity[:start_date]
  75 + end
  76 +
  77 + should 'return event information in api_content' do
  78 + person = fast_create(Person)
  79 + event = build(Event, { name: 'Event', start_date: DateTime.new(2020, 1, 1) })
  80 + event.profile = person
  81 + event.save!
  82 + activity = create_activity(person, event)
  83 +
  84 + block = RecentActivitiesPlugin::ActivitiesBlock.new
  85 + block.stubs(:owner).returns(person)
  86 +
  87 + api_activity = block.api_content['activities'].last
  88 + assert_not_nil api_activity[:start_date]
  89 + end
  90 +
  91 + protected
  92 +
  93 + def create_activity(person, target)
  94 + activity = ActionTracker::Record.create! verb: :leave_scrap, user: person, target: target
  95 + ProfileActivity.create! profile_id: target.id, activity: activity
  96 + activity.reload
72 end 97 end
73 end 98 end
plugins/recent_activities/test/unit/recent_activities_test.rb 0 → 100644
@@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
  1 +require 'test_helper'
  2 +
  3 +class RecentActivitiesPluginTest < ActiveSupport::TestCase
  4 + def setup
  5 + @environment = Environment.default
  6 + @environment.enable_plugin(RecentActivitiesPlugin)
  7 + end
  8 +
  9 + should 'have label for events' do
  10 + person = fast_create(Person)
  11 + event = build(Event, { name: 'Event', start_date: DateTime.new(2020, 1, 1) })
  12 + event.profile = person
  13 + event.save!
  14 + activity = create_activity(person, event)
  15 + assert_equal 'events', activity.label
  16 + end
  17 +
  18 + should 'have label for communities' do
  19 + person = fast_create(Person)
  20 + community = fast_create(Community)
  21 + activity = create_activity(person, community)
  22 + assert_equal 'communities', activity.label
  23 + end
  24 +
  25 + should 'have label for people' do
  26 + person = fast_create(Person)
  27 + friendship = fast_create(Friendship)
  28 + activity = create_activity(person, friendship)
  29 + assert_equal 'people', activity.label
  30 + end
  31 +
  32 + should 'have label for posts' do
  33 + person = fast_create(Person)
  34 + article = fast_create(Article)
  35 + activity = create_activity(person, article)
  36 + assert_equal 'posts', activity.label
  37 + end
  38 +
  39 + protected
  40 +
  41 + def create_activity(person, target)
  42 + activity = ActionTracker::Record.create! verb: :leave_scrap, user: person, target: target
  43 + ProfileActivity.create! profile_id: target.id, activity: activity
  44 + activity.reload
  45 + end
  46 +end
plugins/recent_activities/views/blocks/activities.html.erb
@@ -4,9 +4,37 @@ @@ -4,9 +4,37 @@
4 <% unless block.activities.size == 0 %> 4 <% unless block.activities.size == 0 %>
5 <ul> 5 <ul>
6 <% block.activities.each do |activity| %> 6 <% block.activities.each do |activity| %>
7 - <li> 7 + <li class="recent-activity-<%= activity.label %>">
  8 + <p class="recent-activity-date">
  9 + <% if activity.label === 'events' %>
  10 + <%=
  11 + _('Event on <b>%s</b> at %s - %s').html_safe %
  12 + [
  13 + l(activity.target.start_date, format: :medium),
  14 + activity.target.start_date.strftime("%H:%M"),
  15 + link_to(activity.user.name, activity.user.url)
  16 + ]
  17 + %>
  18 + <% else %>
  19 + <%=
  20 + _('On <b>%s</b> at %s - %s').html_safe %
  21 + [
  22 + l(activity.created_at, format: :medium),
  23 + activity.created_at.strftime("%H:%M"),
  24 + link_to(activity.user.name, activity.user.url)
  25 + ]
  26 + %>
  27 + <% end %>
  28 + <span class="recent-activity-label"><%= activity.label %></span>
  29 + </p>
  30 +
  31 + <% if activity.label === 'events' %>
  32 + <%= image_tag(activity.params['first_image']) %>
  33 + <p class="lead"><b><%= activity.params['name'] %></b><br /><%= strip_tags(activity.params['lead']) %></p>
  34 + <br style="clear: both;" />
  35 + <% else %>
8 <p><%= describe(activity).html_safe %></p> 36 <p><%= describe(activity).html_safe %></p>
9 - <time datetime="<%= activity.created_at %>"><%= time_ago_in_words(activity.created_at) %></time> 37 + <% end %>
10 </li> 38 </li>
11 <% end %> 39 <% end %>
12 </ul> 40 </ul>
test/api/activities_test.rb
@@ -8,7 +8,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase @@ -8,7 +8,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase
8 end 8 end
9 9
10 should 'get own activities' do 10 should 'get own activities' do
11 - create_activity(person) 11 + create_activity(:target => person)
12 12
13 get "/api/v1/profiles/#{person.id}/activities?#{params.to_query}" 13 get "/api/v1/profiles/#{person.id}/activities?#{params.to_query}"
14 json = JSON.parse(last_response.body) 14 json = JSON.parse(last_response.body)
@@ -19,7 +19,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase @@ -19,7 +19,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase
19 19
20 should 'not get private community activities' do 20 should 'not get private community activities' do
21 community = fast_create(Community, :public_profile => false) 21 community = fast_create(Community, :public_profile => false)
22 - create_activity(community) 22 + create_activity(:target => community)
23 23
24 get "/api/v1/profiles/#{community.id}/activities?#{params.to_query}" 24 get "/api/v1/profiles/#{community.id}/activities?#{params.to_query}"
25 json = JSON.parse(last_response.body) 25 json = JSON.parse(last_response.body)
@@ -40,7 +40,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase @@ -40,7 +40,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase
40 40
41 should 'get community activities for member' do 41 should 'get community activities for member' do
42 community = fast_create(Community) 42 community = fast_create(Community)
43 - create_activity(community) 43 + create_activity(:target => community)
44 community.add_member(person) 44 community.add_member(person)
45 45
46 get "/api/v1/profiles/#{community.id}/activities?#{params.to_query}" 46 get "/api/v1/profiles/#{community.id}/activities?#{params.to_query}"
@@ -50,7 +50,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase @@ -50,7 +50,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase
50 50
51 should 'not get other person activities' do 51 should 'not get other person activities' do
52 other_person = fast_create(Person) 52 other_person = fast_create(Person)
53 - create_activity(other_person) 53 + create_activity(:target => other_person)
54 54
55 get "/api/v1/profiles/#{other_person.id}/activities?#{params.to_query}" 55 get "/api/v1/profiles/#{other_person.id}/activities?#{params.to_query}"
56 json = JSON.parse(last_response.body) 56 json = JSON.parse(last_response.body)
@@ -61,7 +61,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase @@ -61,7 +61,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase
61 should 'get friend activities' do 61 should 'get friend activities' do
62 other_person = fast_create(Person) 62 other_person = fast_create(Person)
63 other_person.add_friend(person) 63 other_person.add_friend(person)
64 - create_activity(other_person) 64 + create_activity(:target => other_person)
65 65
66 get "/api/v1/profiles/#{other_person.id}/activities?#{params.to_query}" 66 get "/api/v1/profiles/#{other_person.id}/activities?#{params.to_query}"
67 json = JSON.parse(last_response.body) 67 json = JSON.parse(last_response.body)
@@ -70,16 +70,123 @@ class ActivitiesTest &lt; ActiveSupport::TestCase @@ -70,16 +70,123 @@ class ActivitiesTest &lt; ActiveSupport::TestCase
70 70
71 should 'get activities for non logged user in a public community' do 71 should 'get activities for non logged user in a public community' do
72 community = fast_create(Community) 72 community = fast_create(Community)
73 - create_activity(community) 73 + create_activity(:target => community)
74 community.add_member(person) 74 community.add_member(person)
75 get "/api/v1/profiles/#{community.id}/activities?#{params.to_query}" 75 get "/api/v1/profiles/#{community.id}/activities?#{params.to_query}"
76 json = JSON.parse(last_response.body) 76 json = JSON.parse(last_response.body)
77 assert_equivalent community.activities.map(&:activity).map(&:id), json["activities"].map{|c| c["id"]} 77 assert_equivalent community.activities.map(&:activity).map(&:id), json["activities"].map{|c| c["id"]}
78 end 78 end
79 79
80 - def create_activity(target)  
81 - activity = ActionTracker::Record.create! :verb => :leave_scrap, :user => person, :target => target  
82 - ProfileActivity.create! profile_id: target.id, activity: activity 80 + should 'not crash api if an scrap activity is in the list' do
  81 + create_activity(:target => person)
  82 + create(Scrap, :sender_id => person.id, :receiver_id => person.id)
  83 +
  84 + assert_nothing_raised NoMethodError do
  85 + get "/api/v1/profiles/#{person.id}/activities?#{params.to_query}"
  86 + end
  87 + end
  88 +
  89 + should 'scrap activity be returned in acitivities list' do
  90 + create_activity(:target => person)
  91 + create(Scrap, :sender_id => person.id, :receiver_id => person.id)
  92 +
  93 + get "/api/v1/profiles/#{person.id}/activities?#{params.to_query}"
  94 + json = JSON.parse(last_response.body)
  95 +
  96 + assert_equivalent person.activities.map(&:activity).map(&:id), json["activities"].map{|c| c["id"]}
  97 + end
  98 +
  99 + should 'always return the activity verb parameter' do
  100 + ActionTracker::Record.destroy_all
  101 + ProfileActivity.destroy_all
  102 + create_activity(:target => person)
  103 + get "/api/v1/profiles/#{person.id}/activities?#{params.to_query}"
  104 + json = JSON.parse(last_response.body)
  105 + assert_equal 'create_article', json["activities"].last['verb']
  106 + end
  107 +
  108 + should 'scrap activity return leave_scrap verb' do
  109 + ActionTracker::Record.destroy_all
  110 + create(TinyMceArticle, :name => 'Tracked Article 1', :profile_id => person.id)
  111 + create(Scrap, :sender_id => person.id, :receiver_id => person.id)
  112 + get "/api/v1/profiles/#{person.id}/activities?#{params.to_query}"
  113 + json = JSON.parse(last_response.body)
  114 + assert_equivalent ['create_article', 'leave_scrap'], json["activities"].map{|a|a['verb']}
  115 + end
  116 +
  117 + should 'the content be returned in scrap activities' do
  118 + ActionTracker::Record.destroy_all
  119 + content = 'some content'
  120 + create(Scrap, :sender_id => person.id, :receiver_id => person.id, :content => content)
  121 + get "/api/v1/profiles/#{person.id}/activities?#{params.to_query}"
  122 + json = JSON.parse(last_response.body)
  123 + assert_equal content, json["activities"].last['content']
  124 + end
  125 +
  126 + should 'not return the content in other kind of activities except scrap' do
  127 + ActionTracker::Record.destroy_all
  128 + create_activity(:target => person)
  129 + get "/api/v1/profiles/#{person.id}/activities?#{params.to_query}"
  130 + json = JSON.parse(last_response.body)
  131 + assert_nil json["activities"].last['content']
  132 + end
  133 +
  134 + should 'list activities with pagination' do
  135 + ActionTracker::Record.destroy_all
  136 + a1 = create_activity(:target => person)
  137 + a2 = create_activity(:target => person)
  138 +
  139 + params[:page] = 1
  140 + params[:per_page] = 1
  141 + get "/api/v1/profiles/#{person.id}/activities?#{params.to_query}"
  142 + json_page_one = JSON.parse(last_response.body)
  143 +
  144 + params[:page] = 2
  145 + params[:per_page] = 1
  146 + get "/api/v1/profiles/#{person.id}/activities?#{params.to_query}"
  147 + json_page_two = JSON.parse(last_response.body)
  148 +
  149 + assert_includes json_page_one["activities"].map { |a| a["id"] }, a2.id
  150 + assert_not_includes json_page_one["activities"].map { |a| a["id"] }, a1.id
  151 +
  152 + assert_includes json_page_two["activities"].map { |a| a["id"] }, a1.id
  153 + assert_not_includes json_page_two["activities"].map { |a| a["id"] }, a2.id
  154 + end
  155 +
  156 + should 'list only 20 elements by page if no limit param is passed' do
  157 + ActionTracker::Record.destroy_all
  158 + 1.upto(25).map do
  159 + create_activity(:target => person)
  160 + end
  161 + get "/api/v1/profiles/#{person.id}/activities?#{params.to_query}"
  162 + json = JSON.parse(last_response.body)
  163 + assert_equal 20, json["activities"].length
  164 + end
  165 +
  166 + should 'list activities with timestamp' do
  167 + ActionTracker::Record.destroy_all
  168 + a1 = create_activity(:target => person)
  169 + a2 = create_activity(:target => person)
  170 + a2.updated_at = Time.zone.now
  171 + a2.save
  172 +
  173 + a1.updated_at = Time.zone.now + 3.hours
  174 + a1.save!
  175 +
  176 +
  177 + params[:timestamp] = Time.zone.now + 1.hours
  178 + get "/api/v1/profiles/#{person.id}/activities?#{params.to_query}"
  179 + json = JSON.parse(last_response.body)
  180 +
  181 + assert_includes json["activities"].map { |a| a["id"] }, a1.id
  182 + assert_not_includes json["activities"].map { |a| a["id"] }, a2.id
  183 + end
  184 +
  185 +
  186 +
  187 + def create_activity(params = {})
  188 + params[:verb] ||= 'create_article'
  189 + ActionTracker::Record.create!(params.merge(:user => person))
83 end 190 end
84 191
85 end 192 end
test/api/roles_test.rb
@@ -16,8 +16,13 @@ class TolesTest &lt; ActiveSupport::TestCase @@ -16,8 +16,13 @@ class TolesTest &lt; ActiveSupport::TestCase
16 role1 = Role.create!(key: 'profile_administrator', name: 'admin', environment: environment) 16 role1 = Role.create!(key: 'profile_administrator', name: 'admin', environment: environment)
17 role2 = Role.new(key: 'profile_moderator', name: 'moderator', environment: environment) 17 role2 = Role.new(key: 'profile_moderator', name: 'moderator', environment: environment)
18 profile.custom_roles << role2 18 profile.custom_roles << role2
19 - get "/api/v1/organizations/#{profile.id}/roles?#{params.to_query}" 19 + get "/api/v1/profiles/#{profile.id}/roles?#{params.to_query}"
20 json = JSON.parse(last_response.body) 20 json = JSON.parse(last_response.body)
21 assert_equivalent [role1.id, role2.id], json['roles'].map {|r| r['id']} 21 assert_equivalent [role1.id, role2.id], json['roles'].map {|r| r['id']}
22 end 22 end
  23 +
  24 + should 'return forbidden status when profile is not an organization' do
  25 + get "/api/v1/profiles/#{person.id}/roles?#{params.to_query}"
  26 + assert_equal 403, last_response.status
  27 + end
23 end 28 end
test/api/task_test.rb
@@ -204,6 +204,24 @@ class TasksTest &lt; ActiveSupport::TestCase @@ -204,6 +204,24 @@ class TasksTest &lt; ActiveSupport::TestCase
204 assert_nil task.reload.closed_by_id 204 assert_nil task.reload.closed_by_id
205 assert_equal Task::Status::ACTIVE, task.status 205 assert_equal Task::Status::ACTIVE, task.status
206 end 206 end
  207 +
  208 + should "person be able to #{action} a task with parameters" do
  209 + person1 = fast_create(Person)
  210 + task = create(Task, :requestor => person1, :target => person)
  211 + params[:task] = {reject_explanation: "reject explanation"}
  212 + put "/api/v1/tasks/#{task.id}/#{action}?#{params.to_query}"
  213 + assert_equal "Task::Status::#{task_actions_state[action]}".constantize, task.reload.status
  214 + assert_equal "reject explanation", task.reload.reject_explanation
  215 + end
  216 +
  217 + should "not update a forbidden parameter when #{action} a task" do
  218 + person1 = fast_create(Person)
  219 + person2 = fast_create(Person)
  220 + task = create(Task, :requestor => person1, :target => person)
  221 + params[:task] = { requestor: {id: person2.id} }
  222 + put "/api/v1/tasks/#{task.id}/#{action}?#{params.to_query}"
  223 + assert_equal 500, last_response.status
  224 + end
207 end 225 end
208 226
209 ################################################# 227 #################################################
test/test_helper.rb
@@ -87,8 +87,8 @@ class ActiveSupport::TestCase @@ -87,8 +87,8 @@ class ActiveSupport::TestCase
87 norm1 = enum1.group_by{|e|e}.values 87 norm1 = enum1.group_by{|e|e}.values
88 norm2 = enum2.group_by{|e|e}.values 88 norm2 = enum2.group_by{|e|e}.values
89 assert_equal norm1.size, norm2.size, "Size mismatch: #{enum1.inspect} vs #{enum2.inspect}" 89 assert_equal norm1.size, norm2.size, "Size mismatch: #{enum1.inspect} vs #{enum2.inspect}"
90 - assert_equal [], norm1 - norm2  
91 - assert_equal [], norm2 - norm1 90 + assert_equal [], norm1 - norm2, "Arrays #{norm1} and #{norm2} are not equivalents"
  91 + assert_equal [], norm2 - norm1, "Arrays #{norm1} and #{norm2} are not equivalents"
92 end 92 end
93 93
94 def assert_mandatory(object, attribute, test_value = 'some random string') 94 def assert_mandatory(object, attribute, test_value = 'some random string')