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 295  
296 296 class Activity < Entity
297 297 root 'activities', 'activity'
298   - expose :id, :params, :verb, :created_at, :updated_at, :comments_count, :visible
  298 + expose :id, :created_at, :updated_at
299 299 expose :user, :using => Profile
  300 +
300 301 expose :target do |activity, opts|
301 302 type_map = {Profile => ::Profile, ArticleBase => ::Article}.find {|h| activity.target.kind_of?(h.last)}
302 303 type_map.first.represent(activity.target) unless type_map.nil?
303 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 311 end
305 312  
306 313 class Role < Entity
... ...
app/api/helpers.rb
... ... @@ -198,6 +198,28 @@ module Api
198 198 present_partial tasks, :with => Entities::Task
199 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 223 def make_conditions_with_parameter(params = {})
202 224 parsed_params = parser_params(params)
203 225 conditions = {}
... ... @@ -221,7 +243,7 @@ module Api
221 243 order = 'RANDOM()'
222 244 else
223 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 247 if !field_name.blank? and assoc_class
226 248 if assoc_class.attribute_names.include? field_name
227 249 if direction.present? and ['ASC','DESC'].include? direction.upcase
... ... @@ -234,12 +256,12 @@ module Api
234 256 return order
235 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 260 timestamp = nil
239 261 if params[:timestamp]
240 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 265 date_atrr = assoc_class.attribute_names.include?('updated_at') ? 'updated_at' : 'created_at'
244 266 timestamp = "#{table_name}.#{date_atrr} >= '#{datetime}'"
245 267 end
... ... @@ -279,7 +301,7 @@ module Api
279 301 def select_filtered_collection_of(object, method_or_relation, params)
280 302 conditions = make_conditions_with_parameter(params)
281 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 306 objects = is_a_relation?(method_or_relation) ? method_or_relation : object.send(method_or_relation)
285 307 objects = by_reference(objects, params)
... ... @@ -450,15 +472,15 @@ module Api
450 472 end
451 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 477 end
456 478  
457   - def extract_associated_classname(method_or_relation)
  479 + def extract_associated_classname(object, method_or_relation)
458 480 if is_a_relation?(method_or_relation)
459 481 method_or_relation.blank? ? '' : method_or_relation.first.class
460 482 else
461   - method_or_relation.to_s.singularize.camelize.constantize
  483 + object.send(method_or_relation).table_name.singularize.camelize.constantize
462 484 end
463 485 end
464 486  
... ...
app/api/v1/activities.rb
... ... @@ -5,13 +5,8 @@ module Api
5 5 resource :profiles do
6 6  
7 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 10 end
16 11 end
17 12 end
... ...
app/api/v1/roles.rb
... ... @@ -5,14 +5,15 @@ module Api
5 5  
6 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 10 resource :roles do
11 11  
12 12 paginate max_per_page: MAX_PER_PAGE
13 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 17 present_partial paginate(roles), with: Entities::Role
17 18 end
18 19  
... ...
app/api/v1/tasks.rb
... ... @@ -31,8 +31,13 @@ module Api
31 31 desc "#{action.capitalize} a task"
32 32 put ":id/#{action}" do
33 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 41 end
37 42 end
38 43 end
... ...
app/models/scrap.rb
... ... @@ -36,6 +36,9 @@ class Scrap &lt; ApplicationRecord
36 36  
37 37 before_validation :strip_all_html_tags
38 38  
  39 + alias :user :sender
  40 + alias :target :receiver
  41 +
39 42 def top_root
40 43 scrap = self
41 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 @@
  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 @@
  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 @@
  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 @@
  1 +require_relative '../../../test/test_helper'
... ...
plugins/profile_images/test/unit/profile_images_block_test.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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 @@
  1 +box_organizer/
0 2 \ No newline at end of file
... ...
plugins/recent_activities/lib/ext/action_tracker_model.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 18 color: #ccc;
19 19 }
20 20  
21   -.recent-activities-block img {
  21 +.recent-activities-block img,
  22 +.recent-activities-block .upimg {
22 23 padding: 1px;
23 24 border: 1px solid #ccc;
24 25 margin: 3px 3px 0 0;
... ... @@ -27,3 +28,45 @@
27 28 .recent-activities-block p:first-letter {
28 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 68 block = RecentActivitiesPlugin::ActivitiesBlock.new
69 69 block.stubs(:owner).returns(profile)
70 70  
  71 + api_activity = block.api_content['activities'].last
71 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 97 end
73 98 end
... ...
plugins/recent_activities/test/unit/recent_activities_test.rb 0 → 100644
... ... @@ -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 4 <% unless block.activities.size == 0 %>
5 5 <ul>
6 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 36 <p><%= describe(activity).html_safe %></p>
9   - <time datetime="<%= activity.created_at %>"><%= time_ago_in_words(activity.created_at) %></time>
  37 + <% end %>
10 38 </li>
11 39 <% end %>
12 40 </ul>
... ...
test/api/activities_test.rb
... ... @@ -8,7 +8,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase
8 8 end
9 9  
10 10 should 'get own activities' do
11   - create_activity(person)
  11 + create_activity(:target => person)
12 12  
13 13 get "/api/v1/profiles/#{person.id}/activities?#{params.to_query}"
14 14 json = JSON.parse(last_response.body)
... ... @@ -19,7 +19,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase
19 19  
20 20 should 'not get private community activities' do
21 21 community = fast_create(Community, :public_profile => false)
22   - create_activity(community)
  22 + create_activity(:target => community)
23 23  
24 24 get "/api/v1/profiles/#{community.id}/activities?#{params.to_query}"
25 25 json = JSON.parse(last_response.body)
... ... @@ -40,7 +40,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase
40 40  
41 41 should 'get community activities for member' do
42 42 community = fast_create(Community)
43   - create_activity(community)
  43 + create_activity(:target => community)
44 44 community.add_member(person)
45 45  
46 46 get "/api/v1/profiles/#{community.id}/activities?#{params.to_query}"
... ... @@ -50,7 +50,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase
50 50  
51 51 should 'not get other person activities' do
52 52 other_person = fast_create(Person)
53   - create_activity(other_person)
  53 + create_activity(:target => other_person)
54 54  
55 55 get "/api/v1/profiles/#{other_person.id}/activities?#{params.to_query}"
56 56 json = JSON.parse(last_response.body)
... ... @@ -61,7 +61,7 @@ class ActivitiesTest &lt; ActiveSupport::TestCase
61 61 should 'get friend activities' do
62 62 other_person = fast_create(Person)
63 63 other_person.add_friend(person)
64   - create_activity(other_person)
  64 + create_activity(:target => other_person)
65 65  
66 66 get "/api/v1/profiles/#{other_person.id}/activities?#{params.to_query}"
67 67 json = JSON.parse(last_response.body)
... ... @@ -70,16 +70,123 @@ class ActivitiesTest &lt; ActiveSupport::TestCase
70 70  
71 71 should 'get activities for non logged user in a public community' do
72 72 community = fast_create(Community)
73   - create_activity(community)
  73 + create_activity(:target => community)
74 74 community.add_member(person)
75 75 get "/api/v1/profiles/#{community.id}/activities?#{params.to_query}"
76 76 json = JSON.parse(last_response.body)
77 77 assert_equivalent community.activities.map(&:activity).map(&:id), json["activities"].map{|c| c["id"]}
78 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 190 end
84 191  
85 192 end
... ...
test/api/roles_test.rb
... ... @@ -16,8 +16,13 @@ class TolesTest &lt; ActiveSupport::TestCase
16 16 role1 = Role.create!(key: 'profile_administrator', name: 'admin', environment: environment)
17 17 role2 = Role.new(key: 'profile_moderator', name: 'moderator', environment: environment)
18 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 20 json = JSON.parse(last_response.body)
21 21 assert_equivalent [role1.id, role2.id], json['roles'].map {|r| r['id']}
22 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 28 end
... ...
test/api/task_test.rb
... ... @@ -204,6 +204,24 @@ class TasksTest &lt; ActiveSupport::TestCase
204 204 assert_nil task.reload.closed_by_id
205 205 assert_equal Task::Status::ACTIVE, task.status
206 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 225 end
208 226  
209 227 #################################################
... ...
test/test_helper.rb
... ... @@ -87,8 +87,8 @@ class ActiveSupport::TestCase
87 87 norm1 = enum1.group_by{|e|e}.values
88 88 norm2 = enum2.group_by{|e|e}.values
89 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 92 end
93 93  
94 94 def assert_mandatory(object, attribute, test_value = 'some random string')
... ...