Commit 01b9a054c2070f9392b6d57f3a4c5e6d1209a1ef
Exists in
staging
and in
1 other branch
Merge branch 'master' into staging
Showing
28 changed files
with
575 additions
and
38 deletions
Show diff stats
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 < ApplicationRecord | @@ -36,6 +36,9 @@ class Scrap < 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 |
@@ -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 |
@@ -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 | +} |
@@ -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
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/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 < ActionView::TestCase | @@ -68,6 +68,31 @@ class RecentActivitiesBlockViewTest < 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 < ActiveSupport::TestCase | @@ -8,7 +8,7 @@ class ActivitiesTest < 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 < ActiveSupport::TestCase | @@ -19,7 +19,7 @@ class ActivitiesTest < 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 < ActiveSupport::TestCase | @@ -40,7 +40,7 @@ class ActivitiesTest < 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 < ActiveSupport::TestCase | @@ -50,7 +50,7 @@ class ActivitiesTest < 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 < ActiveSupport::TestCase | @@ -61,7 +61,7 @@ class ActivitiesTest < 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 < ActiveSupport::TestCase | @@ -70,16 +70,123 @@ class ActivitiesTest < 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 < ActiveSupport::TestCase | @@ -16,8 +16,13 @@ class TolesTest < 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 < ActiveSupport::TestCase | @@ -204,6 +204,24 @@ class TasksTest < 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') |