Compare View

switch
from
...
to
 
Commits (15)
controllers/admin/gamification_plugin_badges_controller.rb
1 class GamificationPluginBadgesController < PluginAdminController 1 class GamificationPluginBadgesController < PluginAdminController
2 2
3 def index 3 def index
4 - @gamification_plugin_badges = environment.gamification_plugin_badges 4 + @gamification_plugin_badges = environment.gamification_plugin_badges.group_by(&:owner)
5 end 5 end
6 6
7 def show 7 def show
@@ -17,8 +17,13 @@ class GamificationPluginBadgesController &lt; PluginAdminController @@ -17,8 +17,13 @@ class GamificationPluginBadgesController &lt; PluginAdminController
17 end 17 end
18 18
19 def create 19 def create
  20 + owner_id = params[:gamification_plugin_badge].delete(:owner_id)
20 @gamification_plugin_badge = GamificationPlugin::Badge.new(params[:gamification_plugin_badge]) 21 @gamification_plugin_badge = GamificationPlugin::Badge.new(params[:gamification_plugin_badge])
21 - @gamification_plugin_badge.owner = environment 22 + if owner_id.present?
  23 + @gamification_plugin_badge.owner = environment.organizations.find(owner_id)
  24 + else
  25 + @gamification_plugin_badge.owner = environment
  26 + end
22 27
23 if @gamification_plugin_badge.save 28 if @gamification_plugin_badge.save
24 session[:notice] = _('Badge was successfully created.') 29 session[:notice] = _('Badge was successfully created.')
@@ -31,6 +36,14 @@ class GamificationPluginBadgesController &lt; PluginAdminController @@ -31,6 +36,14 @@ class GamificationPluginBadgesController &lt; PluginAdminController
31 def update 36 def update
32 @gamification_plugin_badge = environment.gamification_plugin_badges.find(params[:id]) 37 @gamification_plugin_badge = environment.gamification_plugin_badges.find(params[:id])
33 38
  39 + # FIXME avoid code duplication
  40 + owner_id = params[:gamification_plugin_badge].delete(:owner_id)
  41 + if owner_id.present?
  42 + @gamification_plugin_badge.owner = environment.organizations.find(owner_id)
  43 + else
  44 + @gamification_plugin_badge.owner = environment
  45 + end
  46 +
34 if @gamification_plugin_badge.update_attributes(params[:gamification_plugin_badge]) 47 if @gamification_plugin_badge.update_attributes(params[:gamification_plugin_badge])
35 session[:notice] = _('Badge was successfully updated.') 48 session[:notice] = _('Badge was successfully updated.')
36 redirect_to :action => :index 49 redirect_to :action => :index
@@ -39,6 +52,10 @@ class GamificationPluginBadgesController &lt; PluginAdminController @@ -39,6 +52,10 @@ class GamificationPluginBadgesController &lt; PluginAdminController
39 end 52 end
40 end 53 end
41 54
  55 + def search_owners
  56 + render :text => prepare_to_token_input(environment.organizations).to_json
  57 + end
  58 +
42 def destroy 59 def destroy
43 @gamification_plugin_badge = environment.gamification_plugin_badges.find(params[:id]) 60 @gamification_plugin_badge = environment.gamification_plugin_badges.find(params[:id])
44 @gamification_plugin_badge.destroy 61 @gamification_plugin_badge.destroy
lib/ext/environment.rb
@@ -2,6 +2,11 @@ require_dependency &#39;environment&#39; @@ -2,6 +2,11 @@ require_dependency &#39;environment&#39;
2 2
3 class Environment 3 class Environment
4 4
5 - has_many :gamification_plugin_badges, :class_name => 'GamificationPlugin::Badge', :foreign_key => 'owner_id', :source => :owner 5 + has_many :gamification_plugin_environment_badges, :class_name => 'GamificationPlugin::Badge', :foreign_key => 'owner_id', :source => :owner
  6 + has_many :gamification_plugin_organization_badges, :through => :organizations
  7 +
  8 + def gamification_plugin_badges
  9 + GamificationPlugin::Badge.from("#{gamification_plugin_organization_badges.union(gamification_plugin_environment_badges).to_sql} as #{GamificationPlugin::Badge.table_name}")
  10 + end
6 11
7 end 12 end
lib/ext/organization.rb 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +require_dependency 'organization'
  2 +
  3 +class Organization
  4 +
  5 + has_many :gamification_plugin_organization_badges, :class_name => 'GamificationPlugin::Badge', :foreign_key => 'owner_id', :source => :owner
  6 +
  7 +end
lib/gamification_plugin/api.rb
@@ -57,7 +57,7 @@ class GamificationPlugin::API &lt; Grape::API @@ -57,7 +57,7 @@ class GamificationPlugin::API &lt; Grape::API
57 get ':id/points_by_profile' do 57 get ':id/points_by_profile' do
58 person = environment.people.visible_for_person(current_person).find_by_id(params[:id]) 58 person = environment.people.visible_for_person(current_person).find_by_id(params[:id])
59 return not_found! if person.blank? 59 return not_found! if person.blank?
60 - {points: person.points_by_type(params[:profile]) } 60 + {points: person.points_by_profile(params[:profile]) }
61 end 61 end
62 62
63 get ':id/points_out_of_profiles' do 63 get ':id/points_out_of_profiles' do
lib/gamification_plugin/dashboard_helper.rb
@@ -12,10 +12,19 @@ module GamificationPlugin::DashboardHelper @@ -12,10 +12,19 @@ module GamificationPlugin::DashboardHelper
12 end 12 end
13 13
14 def score_point_category(point) 14 def score_point_category(point)
15 - point = GamificationPlugin::PointsType.where(name: point.score.category).first 15 + point = GamificationPlugin::PointsType.where(id: point.score.category).first
16 point.nil? ? '' : point.description 16 point.nil? ? '' : point.description
17 end 17 end
18 18
  19 + def score_point_target_link(point, text)
  20 + url = Merit::PointRules.target_url(point)
  21 + url.present? ? link_to(text, url) : text
  22 + end
  23 +
  24 + def score_point_action_class(point)
  25 + point.undo_rule? ? 'undo_action':'do_action'
  26 + end
  27 +
19 def ranking(target, from_date=nil, limit=10) 28 def ranking(target, from_date=nil, limit=10)
20 # FIXME move these queries to profile model 29 # FIXME move these queries to profile model
21 ranking = Profile.select('profiles.*, sum(num_points) as gamification_points, ROW_NUMBER() OVER(order by sum(num_points) DESC) as gamification_position').joins(:sash => {:scores => :score_points}).where(:type => target.class).order('sum(num_points) DESC').group('profiles.id') 30 ranking = Profile.select('profiles.*, sum(num_points) as gamification_points, ROW_NUMBER() OVER(order by sum(num_points) DESC) as gamification_position').joins(:sash => {:scores => :score_points}).where(:type => target.class).order('sum(num_points) DESC').group('profiles.id')
@@ -35,4 +44,15 @@ module GamificationPlugin::DashboardHelper @@ -35,4 +44,15 @@ module GamificationPlugin::DashboardHelper
35 (context_ranking.blank? ? '' : render(:partial => 'gamification/ranking', :locals => {:ranking => context_ranking, :target_ranking => target_ranking, :ranking_class => 'context'})) 44 (context_ranking.blank? ? '' : render(:partial => 'gamification/ranking', :locals => {:ranking => context_ranking, :target_ranking => target_ranking, :ranking_class => 'context'}))
36 end 45 end
37 46
  47 + def badges_title(owner)
  48 + return _('Badges for %s' % owner.name) if owner.kind_of?(Organization)
  49 + _('Badges')
  50 + end
  51 +
  52 + def grouped_badges
  53 + environment.gamification_plugin_badges.all.group_by(&:owner).sort do |a, b|
  54 + a.first.kind_of?(Environment) ? -1 : a.first.name <=> b.first.name
  55 + end
  56 + end
  57 +
38 end 58 end
lib/merit/badge_rules.rb
@@ -14,6 +14,7 @@ module Merit @@ -14,6 +14,7 @@ module Merit
14 action: 'comment#create', 14 action: 'comment#create',
15 default_threshold: 5, 15 default_threshold: 5,
16 to: :author, 16 to: :author,
  17 + target_profile: lambda {|comment| comment.profile },
17 value: lambda { |comment, author| author.present? ? author.comments.count : 0 } 18 value: lambda { |comment, author| author.present? ? author.comments.count : 0 }
18 } 19 }
19 ], 20 ],
@@ -22,6 +23,7 @@ module Merit @@ -22,6 +23,7 @@ module Merit
22 action: 'comment#create', 23 action: 'comment#create',
23 default_threshold: 5, 24 default_threshold: 5,
24 to: lambda {|comment| comment.source.author}, 25 to: lambda {|comment| comment.source.author},
  26 + target_profile: lambda {|comment| comment.profile },
25 value: lambda { |comment, author| author.present? ? Comment.where(source_id: Article.where(author_id: author.id)).count : 0 } 27 value: lambda { |comment, author| author.present? ? Comment.where(source_id: Article.where(author_id: author.id)).count : 0 }
26 } 28 }
27 ], 29 ],
@@ -30,6 +32,7 @@ module Merit @@ -30,6 +32,7 @@ module Merit
30 action: 'article#create', 32 action: 'article#create',
31 default_threshold: 5, 33 default_threshold: 5,
32 to: :author, 34 to: :author,
  35 + target_profile: lambda {|article| article.profile },
33 value: lambda { |article, author| author.present? ? TextArticle.where(author_id: author.id).count : 0 } 36 value: lambda { |article, author| author.present? ? TextArticle.where(author_id: author.id).count : 0 }
34 }, 37 },
35 ], 38 ],
@@ -38,6 +41,7 @@ module Merit @@ -38,6 +41,7 @@ module Merit
38 action: 'vote#create', 41 action: 'vote#create',
39 default_threshold: 5, 42 default_threshold: 5,
40 to: lambda {|vote| vote.voteable.author}, 43 to: lambda {|vote| vote.voteable.author},
  44 + target_profile: lambda {|vote| vote.voteable.profile },
41 value: lambda { |vote, author| vote.voteable ? Vote.for_voteable(vote.voteable).where('vote > 0').count : 0} 45 value: lambda { |vote, author| vote.voteable ? Vote.for_voteable(vote.voteable).where('vote > 0').count : 0}
42 } 46 }
43 ], 47 ],
@@ -46,6 +50,7 @@ module Merit @@ -46,6 +50,7 @@ module Merit
46 action: 'vote#create', 50 action: 'vote#create',
47 default_threshold: 5, 51 default_threshold: 5,
48 to: lambda {|vote| vote.voteable.author}, 52 to: lambda {|vote| vote.voteable.author},
  53 + target_profile: lambda {|vote| vote.voteable.profile },
49 value: lambda { |vote, author| Vote.for_voteable(vote.voteable).where('vote < 0').count } 54 value: lambda { |vote, author| Vote.for_voteable(vote.voteable).where('vote < 0').count }
50 } 55 }
51 ], 56 ],
@@ -54,6 +59,7 @@ module Merit @@ -54,6 +59,7 @@ module Merit
54 action: 'vote#create', 59 action: 'vote#create',
55 default_threshold: 5, 60 default_threshold: 5,
56 to: lambda {|vote| vote.voter}, 61 to: lambda {|vote| vote.voter},
  62 + target_profile: lambda {|vote| vote.voteable.profile },
57 value: lambda { |vote, voter| voter ? Vote.for_voter(voter).count : 0 } 63 value: lambda { |vote, voter| voter ? Vote.for_voter(voter).count : 0 }
58 } 64 }
59 ], 65 ],
@@ -65,6 +71,7 @@ module Merit @@ -65,6 +71,7 @@ module Merit
65 value: lambda { |friendship, person| person.friends.count } 71 value: lambda { |friendship, person| person.friends.count }
66 } 72 }
67 ], 73 ],
  74 + manual: [],
68 75
69 #FIXME review the name of the badges and see a way to make it generic 76 #FIXME review the name of the badges and see a way to make it generic
70 creative: [ 77 creative: [
@@ -72,12 +79,14 @@ module Merit @@ -72,12 +79,14 @@ module Merit
72 action: 'comment#create', 79 action: 'comment#create',
73 default_threshold: 5, 80 default_threshold: 5,
74 to: :author, 81 to: :author,
  82 + target_profile: lambda {|comment| comment.profile },
75 value: lambda { |comment, author| author.present? ? author.comments.count : 0 } 83 value: lambda { |comment, author| author.present? ? author.comments.count : 0 }
76 }, 84 },
77 { 85 {
78 action: 'article#create', 86 action: 'article#create',
79 default_threshold: 5, 87 default_threshold: 5,
80 to: :author, 88 to: :author,
  89 + target_profile: lambda {|article| article.profile },
81 value: lambda { |article, author| author.present? ? author.articles.count : 0 } 90 value: lambda { |article, author| author.present? ? author.articles.count : 0 }
82 }, 91 },
83 ], 92 ],
@@ -86,6 +95,7 @@ module Merit @@ -86,6 +95,7 @@ module Merit
86 action: 'articlefollower#create', 95 action: 'articlefollower#create',
87 default_threshold: 5, 96 default_threshold: 5,
88 to: lambda {|article| article.person }, 97 to: lambda {|article| article.person },
  98 + target_profile: lambda {|article_follower| article_follower.article.profile },
89 model: 'ArticleFollower', 99 model: 'ArticleFollower',
90 value: lambda { |article, person| person.present? ? person.article_followers.count : 0 } 100 value: lambda { |article, person| person.present? ? person.article_followers.count : 0 }
91 } 101 }
@@ -95,12 +105,14 @@ module Merit @@ -95,12 +105,14 @@ module Merit
95 action: 'Vote#create', 105 action: 'Vote#create',
96 default_threshold: 5, 106 default_threshold: 5,
97 to: lambda { |vote| vote.voter }, 107 to: lambda { |vote| vote.voter },
  108 + target_profile: lambda {|vote| vote.voteable.profile },
98 value: lambda { |vote, voter| Vote.for_voter(voter).count } 109 value: lambda { |vote, voter| Vote.for_voter(voter).count }
99 }, 110 },
100 { 111 {
101 action: 'Event#create', 112 action: 'Event#create',
102 default_threshold: 5, 113 default_threshold: 5,
103 to: lambda { |article| article.author }, 114 to: lambda { |article| article.author },
  115 + target_profile: lambda {|article| article.profile },
104 value: lambda { |event, author| author.events.count } 116 value: lambda { |event, author| author.events.count }
105 }, 117 },
106 ], 118 ],
@@ -109,12 +121,14 @@ module Merit @@ -109,12 +121,14 @@ module Merit
109 action: 'vote#create', 121 action: 'vote#create',
110 default_threshold: 5, 122 default_threshold: 5,
111 to: lambda {|vote| vote.voter}, 123 to: lambda {|vote| vote.voter},
  124 + target_profile: lambda {|vote| vote.voteable.profile },
112 value: lambda { |vote, voter| voter ? voter.votes.where('vote > 0').count : 0 } 125 value: lambda { |vote, voter| voter ? voter.votes.where('vote > 0').count : 0 }
113 }, 126 },
114 { 127 {
115 action: 'comment#create', 128 action: 'comment#create',
116 default_threshold: 5, 129 default_threshold: 5,
117 to: :author, 130 to: :author,
  131 + target_profile: lambda {|comment| comment.profile },
118 value: lambda { |comment, author| author.present? ? author.comments.count : 0 } 132 value: lambda { |comment, author| author.present? ? author.comments.count : 0 }
119 } 133 }
120 ], 134 ],
@@ -123,6 +137,7 @@ module Merit @@ -123,6 +137,7 @@ module Merit
123 action: 'articlefollower#create', 137 action: 'articlefollower#create',
124 default_threshold: 5, 138 default_threshold: 5,
125 to: :person, 139 to: :person,
  140 + target_profile: lambda {|article_follower| article_follower.article.profile },
126 model: 'ArticleFollower', 141 model: 'ArticleFollower',
127 value: lambda { |article_follower, person| person.present? ? person.article_followers.count : 0 } 142 value: lambda { |article_follower, person| person.present? ? person.article_followers.count : 0 }
128 }, 143 },
@@ -130,11 +145,28 @@ module Merit @@ -130,11 +145,28 @@ module Merit
130 action: 'comment#create', 145 action: 'comment#create',
131 default_threshold: 5, 146 default_threshold: 5,
132 to: :author, 147 to: :author,
  148 + target_profile: lambda {|comment| comment.profile },
133 value: lambda { |comment, author| author.present? ? author.comments.count : 0 } 149 value: lambda { |comment, author| author.present? ? author.comments.count : 0 }
134 }, 150 },
135 ] 151 ]
136 } 152 }
137 153
  154 + def target_author(source, setting)
  155 + if setting[:to].is_a? Symbol
  156 + source.send(setting[:to])
  157 + else
  158 + setting[:to].call(source) rescue nil
  159 + end
  160 + end
  161 +
  162 + def target_profile(source, setting)
  163 + setting[:target_profile].present? ? setting[:target_profile].call(source) : nil
  164 + end
  165 +
  166 + def check_organization_badge(badge, source, setting)
  167 + !badge.owner.kind_of?(Organization) || badge.owner == target_profile(source, setting)
  168 + end
  169 +
138 def initialize(environment=nil) 170 def initialize(environment=nil)
139 return if environment.nil? 171 return if environment.nil?
140 @environment = environment 172 @environment = environment
@@ -142,7 +174,7 @@ module Merit @@ -142,7 +174,7 @@ module Merit
142 rules = AVAILABLE_RULES 174 rules = AVAILABLE_RULES
143 rules.merge! CONFERENCE_RULES if defined? CONFERENCE_RULES 175 rules.merge! CONFERENCE_RULES if defined? CONFERENCE_RULES
144 176
145 - environment.gamification_plugin_badges.all.each do |badge| 177 + environment.gamification_plugin_badges.each do |badge|
146 next if rules[badge.name.to_sym].nil? 178 next if rules[badge.name.to_sym].nil?
147 rules[badge.name.to_sym].each do |setting| 179 rules[badge.name.to_sym].each do |setting|
148 options = {badge: badge.name, level: badge.level, to: setting[:to]} 180 options = {badge: badge.name, level: badge.level, to: setting[:to]}
@@ -150,18 +182,11 @@ module Merit @@ -150,18 +182,11 @@ module Merit
150 grant_on setting[:action], options do |source| 182 grant_on setting[:action], options do |source|
151 can_be_granted = true 183 can_be_granted = true
152 rules[badge.name.to_sym].each do |s| 184 rules[badge.name.to_sym].each do |s|
153 - if setting[:to].is_a? Symbol  
154 - to = source.send(setting[:to])  
155 - else  
156 - begin  
157 - to = setting[:to].call(source)  
158 - rescue  
159 - to = nil  
160 - end  
161 - end  
162 - # pass source and to for different situations 185 + to = target_author(source, setting)
  186 + # pass source and to for different situations
163 action = (badge.custom_fields || {}).fetch(s[:action], {}) 187 action = (badge.custom_fields || {}).fetch(s[:action], {})
164 can_be_granted &= s[:value].call(source, to) >= action.fetch(:threshold, s[:default_threshold]).to_i 188 can_be_granted &= s[:value].call(source, to) >= action.fetch(:threshold, s[:default_threshold]).to_i
  189 + can_be_granted &= check_organization_badge(badge, source, setting)
165 end 190 end
166 can_be_granted 191 can_be_granted
167 end 192 end
lib/merit/point_rules.rb
@@ -11,6 +11,7 @@ module Merit @@ -11,6 +11,7 @@ module Merit
11 description: _('Comment author'), 11 description: _('Comment author'),
12 default_weight: 40, 12 default_weight: 40,
13 target_profile: lambda {|comment| comment.source.profile }, 13 target_profile: lambda {|comment| comment.source.profile },
  14 + target_url: lambda {|comment| comment.url},
14 }, 15 },
15 comment_article_author: { 16 comment_article_author: {
16 action: 'comment#create', 17 action: 'comment#create',
@@ -20,6 +21,7 @@ module Merit @@ -20,6 +21,7 @@ module Merit
20 description: _('Article author of a comment'), 21 description: _('Article author of a comment'),
21 default_weight: 50, 22 default_weight: 50,
22 target_profile: lambda {|comment| comment.source.profile }, 23 target_profile: lambda {|comment| comment.source.profile },
  24 + target_url: lambda {|comment| comment.url},
23 }, 25 },
24 comment_article: { 26 comment_article: {
25 action: 'comment#create', 27 action: 'comment#create',
@@ -29,6 +31,7 @@ module Merit @@ -29,6 +31,7 @@ module Merit
29 description: _('Source article of a comment'), 31 description: _('Source article of a comment'),
30 default_weight: 50, 32 default_weight: 50,
31 target_profile: lambda {|comment| comment.source.profile }, 33 target_profile: lambda {|comment| comment.source.profile },
  34 + target_url: lambda {|comment| comment.url},
32 }, 35 },
33 comment_community: { 36 comment_community: {
34 action: 'comment#create', 37 action: 'comment#create',
@@ -38,7 +41,8 @@ module Merit @@ -38,7 +41,8 @@ module Merit
38 description: _('Article community of a comment'), 41 description: _('Article community of a comment'),
39 default_weight: 50, 42 default_weight: 50,
40 target_profile: lambda {|comment| comment.source.profile }, 43 target_profile: lambda {|comment| comment.source.profile },
41 - condition: lambda {|comment, profile| comment.profile.community?} 44 + condition: lambda {|comment, profile| comment.profile.community?},
  45 + target_url: lambda {|comment| comment.url},
42 }, 46 },
43 article_author: { 47 article_author: {
44 action: 'article#create', 48 action: 'article#create',
@@ -48,6 +52,7 @@ module Merit @@ -48,6 +52,7 @@ module Merit
48 description: _('Article author'), 52 description: _('Article author'),
49 default_weight: 50, 53 default_weight: 50,
50 target_profile: lambda {|article| article.profile }, 54 target_profile: lambda {|article| article.profile },
  55 + target_url: lambda {|article| article.url},
51 }, 56 },
52 article_community: { 57 article_community: {
53 action: 'article#create', 58 action: 'article#create',
@@ -57,7 +62,8 @@ module Merit @@ -57,7 +62,8 @@ module Merit
57 description: _('Article community'), 62 description: _('Article community'),
58 default_weight: 10, 63 default_weight: 10,
59 target_profile: lambda {|article| article.profile }, 64 target_profile: lambda {|article| article.profile },
60 - condition: lambda {|article, profile| article.profile.present? and article.profile.community? } 65 + condition: lambda {|article, profile| article.profile.present? and article.profile.community? },
  66 + target_url: lambda {|article| article.url},
61 }, 67 },
62 vote_voteable_author: { 68 vote_voteable_author: {
63 action: 'vote#create', 69 action: 'vote#create',
@@ -67,6 +73,7 @@ module Merit @@ -67,6 +73,7 @@ module Merit
67 description: _('Author of a voted content'), 73 description: _('Author of a voted content'),
68 default_weight: 20, 74 default_weight: 20,
69 target_profile: lambda {|vote| vote.voteable.present? ? vote.voteable.profile : nil }, 75 target_profile: lambda {|vote| vote.voteable.present? ? vote.voteable.profile : nil },
  76 + target_url: lambda {|vote| vote.voteable.url},
70 }, 77 },
71 vote_voteable: { 78 vote_voteable: {
72 action: 'vote#create', 79 action: 'vote#create',
@@ -76,6 +83,7 @@ module Merit @@ -76,6 +83,7 @@ module Merit
76 description: _('Voted content'), 83 description: _('Voted content'),
77 default_weight: 30, 84 default_weight: 30,
78 target_profile: lambda {|vote| vote.voteable.present? ? vote.voteable.profile : nil }, 85 target_profile: lambda {|vote| vote.voteable.present? ? vote.voteable.profile : nil },
  86 + target_url: lambda {|vote| vote.voteable.url},
79 }, 87 },
80 vote_voter: { 88 vote_voter: {
81 action: 'vote#create', 89 action: 'vote#create',
@@ -85,6 +93,7 @@ module Merit @@ -85,6 +93,7 @@ module Merit
85 description: _('Voter'), 93 description: _('Voter'),
86 default_weight: 10, 94 default_weight: 10,
87 target_profile: lambda {|vote| vote.voteable.present? ? vote.voteable.profile : nil }, 95 target_profile: lambda {|vote| vote.voteable.present? ? vote.voteable.profile : nil },
  96 + target_url: lambda {|vote| vote.voteable.url},
88 }, 97 },
89 friends: { 98 friends: {
90 action: 'friendship#create', 99 action: 'friendship#create',
@@ -150,6 +159,7 @@ module Merit @@ -150,6 +159,7 @@ module Merit
150 end 159 end
151 160
152 def profile_condition(setting, target, profile) 161 def profile_condition(setting, target, profile)
  162 + return false if target == true
153 profile.nil? || setting[:target_profile].blank? || setting[:target_profile].call(target) == profile 163 profile.nil? || setting[:target_profile].blank? || setting[:target_profile].call(target) == profile
154 end 164 end
155 165
@@ -163,6 +173,15 @@ module Merit @@ -163,6 +173,15 @@ module Merit
163 end 173 end
164 end 174 end
165 175
  176 + def self.target_url(point)
  177 + rule_name = point.point_type.present? ? point.point_type.name : point.score.category
  178 + target_url = AVAILABLE_RULES[rule_name.to_sym][:target_url]
  179 + return nil if target_url.blank? || point.action.blank?
  180 +
  181 + model = BaseTargetFinder.new(Merit::Rule.new, point.action).get_target_from_database
  182 + model.present? ? target_url.call(model) : nil
  183 + end
  184 +
166 def initialize(environment=nil) 185 def initialize(environment=nil)
167 return if environment.nil? 186 return if environment.nil?
168 @environment = environment 187 @environment = environment
lib/merit_ext.rb
@@ -17,6 +17,15 @@ module Merit @@ -17,6 +17,15 @@ module Merit
17 class Score 17 class Score
18 class Point 18 class Point
19 belongs_to :action 19 belongs_to :action
  20 +
  21 + def point_type
  22 + @point_type ||= GamificationPlugin::PointsType.where(id: score.category).first
  23 + end
  24 +
  25 + def undo_rule?
  26 + rule = Merit::PointRules::AVAILABLE_RULES[point_type.name.to_sym]
  27 + rule[:undo_action] == "#{action.target_model}##{action.action_method}"
  28 + end
20 end 29 end
21 end 30 end
22 31
models/gamification_plugin/points_categorization.rb
1 -class GamificationPlugin::PointsCategorization < Noosfero::Plugin::ActiveRecord 1 +class GamificationPlugin::PointsCategorization < ActiveRecord::Base
2 belongs_to :profile 2 belongs_to :profile
3 belongs_to :point_type, class_name: 'GamificationPlugin::PointsType', foreign_key: :point_type_id 3 belongs_to :point_type, class_name: 'GamificationPlugin::PointsType', foreign_key: :point_type_id
4 attr_accessible :profile_id, :profile, :point_type_id, :weight 4 attr_accessible :profile_id, :profile, :point_type_id, :weight
@@ -10,5 +10,5 @@ class GamificationPlugin::PointsCategorization &lt; Noosfero::Plugin::ActiveRecord @@ -10,5 +10,5 @@ class GamificationPlugin::PointsCategorization &lt; Noosfero::Plugin::ActiveRecord
10 scope :for_type, lambda { |p_type| joins(:point_type).where(gamification_plugin_points_types: {name: p_type}) } 10 scope :for_type, lambda { |p_type| joins(:point_type).where(gamification_plugin_points_types: {name: p_type}) }
11 scope :for_profile, lambda { |p_profile| joins(:profile).where(profiles: {identifier: p_profile}) } 11 scope :for_profile, lambda { |p_profile| joins(:profile).where(profiles: {identifier: p_profile}) }
12 12
13 - scope :grouped_profiles, select(:profile_id).group(:profile_id).includes(:profile) 13 + scope :grouped_profiles, -> { select(:profile_id).group(:profile_id).includes(:profile) }
14 end 14 end
models/gamification_plugin/points_type.rb
1 -class GamificationPlugin::PointsType < Noosfero::Plugin::ActiveRecord 1 +class GamificationPlugin::PointsType < ActiveRecord::Base
2 attr_accessible :description, :name 2 attr_accessible :description, :name
3 3
4 validates :name, presence: true, uniqueness: true 4 validates :name, presence: true, uniqueness: true
public/admin.css
1 .gamification-plugin-rank-rules .template-level { 1 .gamification-plugin-rank-rules .template-level {
2 display: none !important; 2 display: none !important;
3 } 3 }
  4 +
  5 +.gamification-plugin-badges .badge-owner-group {
  6 + text-align: right;
  7 + color: gray;
  8 + font-weight: bold;
  9 +}
public/admin.js
@@ -13,6 +13,11 @@ var gamificationPluginAdmin = { @@ -13,6 +13,11 @@ var gamificationPluginAdmin = {
13 var name = jQuery('#gamification-plugin-form-badge-name').find('option:selected').text(); 13 var name = jQuery('#gamification-plugin-form-badge-name').find('option:selected').text();
14 jQuery('.name_'+name).show(); 14 jQuery('.name_'+name).show();
15 jQuery('.name_'+name).find('input').removeAttr('disabled'); 15 jQuery('.name_'+name).find('input').removeAttr('disabled');
  16 + if(jQuery('.name_'+name).children().length>0) {
  17 + jQuery('.action-fields').show();
  18 + } else {
  19 + jQuery('.action-fields').hide();
  20 + }
16 } 21 }
17 22
18 } 23 }
public/style.css
@@ -237,3 +237,7 @@ @@ -237,3 +237,7 @@
237 float: left; 237 float: left;
238 width: 180px; 238 width: 180px;
239 } 239 }
  240 +
  241 +.gamification-dashboard .points .score.undo_action .category {
  242 + text-decoration: line-through;
  243 +}
test/functional/gamification_plugin_badges_controller_test.rb
@@ -25,6 +25,14 @@ class GamificationPluginBadgesControllerTest &lt; ActionController::TestCase @@ -25,6 +25,14 @@ class GamificationPluginBadgesControllerTest &lt; ActionController::TestCase
25 end 25 end
26 end 26 end
27 27
  28 + should "should create gamification_plugin_badge with organization as owner" do
  29 + organization = fast_create(Organization)
  30 + assert_difference('GamificationPlugin::Badge.count') do
  31 + post :create, gamification_plugin_badge: { description: @gamification_plugin_badge.description, level: @gamification_plugin_badge.level, name: @gamification_plugin_badge.name, custom_fields: {threshold: @gamification_plugin_badge.threshold}, owner_id: organization.id }
  32 + assert_equal organization, GamificationPlugin::Badge.last.owner
  33 + end
  34 + end
  35 +
28 should "should show gamification_plugin_badge" do 36 should "should show gamification_plugin_badge" do
29 get :show, id: @gamification_plugin_badge 37 get :show, id: @gamification_plugin_badge
30 assert_response :success 38 assert_response :success
@@ -40,6 +48,23 @@ class GamificationPluginBadgesControllerTest &lt; ActionController::TestCase @@ -40,6 +48,23 @@ class GamificationPluginBadgesControllerTest &lt; ActionController::TestCase
40 assert assigns(:gamification_plugin_badge) 48 assert assigns(:gamification_plugin_badge)
41 end 49 end
42 50
  51 + should "should change badge owner" do
  52 + organization = fast_create(Organization)
  53 + put :update, id: @gamification_plugin_badge, gamification_plugin_badge: { description: @gamification_plugin_badge.description, level: @gamification_plugin_badge.level, name: @gamification_plugin_badge.name, custom_fields: {threshold: @gamification_plugin_badge.threshold}, owner_id: organization.id }
  54 + assert assigns(:gamification_plugin_badge)
  55 + assert_equal organization, @gamification_plugin_badge.reload.owner
  56 + end
  57 +
  58 + should "should keep badge owner when update" do
  59 + organization = fast_create(Organization)
  60 + @gamification_plugin_badge.owner = organization
  61 + @gamification_plugin_badge.save!
  62 +
  63 + put :update, id: @gamification_plugin_badge, gamification_plugin_badge: { description: @gamification_plugin_badge.description, level: @gamification_plugin_badge.level, name: @gamification_plugin_badge.name, custom_fields: {threshold: @gamification_plugin_badge.threshold}, owner_id: organization.id }
  64 + assert assigns(:gamification_plugin_badge)
  65 + assert_equal organization, @gamification_plugin_badge.reload.owner
  66 + end
  67 +
43 should "should destroy gamification_plugin_badge" do 68 should "should destroy gamification_plugin_badge" do
44 assert_difference('GamificationPlugin::Badge.count', -1) do 69 assert_difference('GamificationPlugin::Badge.count', -1) do
45 delete :destroy, id: @gamification_plugin_badge 70 delete :destroy, id: @gamification_plugin_badge
test/functional/gamification_plugin_profile_controller_test.rb
@@ -11,12 +11,13 @@ class GamificationPluginProfileControllerTest &lt; ActionController::TestCase @@ -11,12 +11,13 @@ class GamificationPluginProfileControllerTest &lt; ActionController::TestCase
11 attr_accessor :person, :environment 11 attr_accessor :person, :environment
12 12
13 should 'display points in gamification dashboard' do 13 should 'display points in gamification dashboard' do
14 - person.add_points(20, :category => :comment_author)  
15 - person.add_points(30, :category => :article_author) 14 + create_all_point_rules
  15 + article = create(TextArticle, :profile_id => fast_create(Community).id, :author => person)
  16 + create(Comment, :source => article, :author_id => create_user.person.id)
16 get :dashboard, :profile => person.identifier 17 get :dashboard, :profile => person.identifier
17 - assert_tag :div, :attributes => {:class => 'score article_author positive'}, :child => {:tag => 'span', :attributes => {:class => 'value'}, :content => '30'}  
18 - assert_tag :div, :attributes => {:class => 'score comment_author positive'}, :child => {:tag => 'span', :attributes => {:class => 'value'}, :content => '20'}  
19 - assert_tag :div, :attributes => {:class => 'score total'}, :child => {:tag => 'span', :attributes => {:class => 'value'}, :content => '50'} 18 + assert_tag :div, :attributes => {:class => 'score article_author positive do_action'}, :child => {:tag => 'span', :attributes => {:class => 'value'}, :content => default_point_weight(:article_author).to_s}
  19 + assert_tag :div, :attributes => {:class => 'score comment_article_author positive do_action'}, :child => {:tag => 'span', :attributes => {:class => 'value'}, :content => default_point_weight(:comment_article_author).to_s}
  20 + assert_tag :div, :attributes => {:class => 'score total'}, :child => {:tag => 'span', :attributes => {:class => 'value'}, :content => (default_point_weight(:comment_article_author) + default_point_weight(:article_author)).to_s}
20 end 21 end
21 22
22 should 'display level in gamification dashboard' do 23 should 'display level in gamification dashboard' do
test/test_helper.rb
@@ -15,6 +15,10 @@ def create_all_point_rules @@ -15,6 +15,10 @@ def create_all_point_rules
15 end 15 end
16 end 16 end
17 17
  18 +def default_point_weight(rule_name)
  19 + Merit::PointRules::AVAILABLE_RULES[rule_name][:default_weight]
  20 +end
  21 +
18 def load_point_rule(rule_name, config) 22 def load_point_rule(rule_name, config)
19 rule_config = Merit::PointRules::AVAILABLE_RULES[rule_name.to_sym] 23 rule_config = Merit::PointRules::AVAILABLE_RULES[rule_name.to_sym]
20 raise "Point rule '#{rule_name}' is not available" if rule_config.nil? 24 raise "Point rule '#{rule_name}' is not available" if rule_config.nil?
@@ -22,7 +26,7 @@ def load_point_rule(rule_name, config) @@ -22,7 +26,7 @@ def load_point_rule(rule_name, config)
22 rule_config 26 rule_config
23 end 27 end
24 28
25 -#person_points_debug(person) 29 +#person_points_debug(person)
26 def person_points_debug(person) 30 def person_points_debug(person)
27 person.score_points.map do |sp| 31 person.score_points.map do |sp|
28 puts 'Ponto:' 32 puts 'Ponto:'
test/unit/api_test.rb
@@ -8,6 +8,7 @@ class APITest &lt; ActiveSupport::TestCase @@ -8,6 +8,7 @@ class APITest &lt; ActiveSupport::TestCase
8 environment = Environment.default 8 environment = Environment.default
9 environment.enable_plugin(GamificationPlugin) 9 environment.enable_plugin(GamificationPlugin)
10 GamificationPlugin.gamification_set_rules(@environment) 10 GamificationPlugin.gamification_set_rules(@environment)
  11 + create_all_point_rules
11 end 12 end
12 13
13 should 'get my own badges' do 14 should 'get my own badges' do
@@ -27,14 +28,6 @@ class APITest &lt; ActiveSupport::TestCase @@ -27,14 +28,6 @@ class APITest &lt; ActiveSupport::TestCase
27 assert_not_nil json['percent'] 28 assert_not_nil json['percent']
28 end 29 end
29 30
30 - should 'get my total pontuation' do  
31 - badge = GamificationPlugin::Badge.create!(:owner => environment, :name => 'test_badge')  
32 - person.add_badge(badge.id)  
33 - get "/api/v1/gamification_plugin/my/points?#{params.to_query}"  
34 - json = JSON.parse(last_response.body)  
35 - assert_not_nil json['points']  
36 - end  
37 -  
38 should 'get badges of the public person' do 31 should 'get badges of the public person' do
39 badge = GamificationPlugin::Badge.create!(:owner => environment, :name => 'test_badge') 32 badge = GamificationPlugin::Badge.create!(:owner => environment, :name => 'test_badge')
40 another_person = create(User, :environment => environment).person 33 another_person = create(User, :environment => environment).person
@@ -76,4 +69,95 @@ class APITest &lt; ActiveSupport::TestCase @@ -76,4 +69,95 @@ class APITest &lt; ActiveSupport::TestCase
76 assert_equal 404, last_response.status 69 assert_equal 404, last_response.status
77 end 70 end
78 71
  72 + should 'get amount of environment badges grouped by name' do
  73 + 3.times { GamificationPlugin::Badge.create!(:owner => environment, :name => 'test_badge') }
  74 + get "/api/v1/gamification_plugin/badges"
  75 + json = JSON.parse(last_response.body)
  76 + assert_equal 3, json['test_badge']
  77 + end
  78 +
  79 + should 'get my points' do
  80 + article = create(TextArticle, :profile_id => @person.id, :author => @person)
  81 + create(Comment, :source_id => article.id, :author => fast_create(Person))
  82 +
  83 + get "/api/v1/gamification_plugin/my/points?#{params.to_query}"
  84 + json = JSON.parse(last_response.body)
  85 + assert_equal default_point_weight(:article_author) + default_point_weight(:comment_article_author), json['points']
  86 + end
  87 +
  88 + should 'get my points filtered by type' do
  89 + article = create(TextArticle, :profile_id => @person.id, :author => @person)
  90 + create(Comment, :source_id => article.id, :author => fast_create(Person))
  91 + params[:type] = 'article_author'
  92 +
  93 + get "/api/v1/gamification_plugin/my/points_by_type?#{params.to_query}"
  94 + json = JSON.parse(last_response.body)
  95 + assert_equal default_point_weight(:article_author), json['points']
  96 + end
  97 +
  98 + should 'get my points filtered by profile' do
  99 + community = fast_create(Community)
  100 + create_point_rule_definition('article_author', community)
  101 + create(TextArticle, :profile_id => @person.id, :author => @person)
  102 + create(TextArticle, :profile_id => community.id, :author => @person)
  103 + params[:profile] = community.identifier
  104 +
  105 + get "/api/v1/gamification_plugin/my/points_by_profile?#{params.to_query}"
  106 + json = JSON.parse(last_response.body)
  107 + assert_equal default_point_weight(:article_author), json['points']
  108 + end
  109 +
  110 + should 'get my points excluding points earned in profiles' do
  111 + community = fast_create(Community)
  112 + create_point_rule_definition('article_author', community)
  113 + create(TextArticle, :profile_id => @person.id, :author => @person)
  114 + create(TextArticle, :profile_id => community.id, :author => @person)
  115 +
  116 + get "/api/v1/gamification_plugin/my/points_out_of_profiles?#{params.to_query}"
  117 + json = JSON.parse(last_response.body)
  118 + assert_equal 2*default_point_weight(:article_author), json['points']
  119 + end
  120 +
  121 + should 'get points of a person' do
  122 + article = create(TextArticle, :profile_id => @person.id, :author => @person)
  123 + create(Comment, :source_id => article.id, :author => fast_create(Person))
  124 +
  125 + get "/api/v1/gamification_plugin/people/#{person.id}/points?#{params.to_query}"
  126 + json = JSON.parse(last_response.body)
  127 + assert_equal default_point_weight(:article_author) + default_point_weight(:comment_article_author), json['points']
  128 + end
  129 +
  130 + should 'get points of a person filtered by type' do
  131 + article = create(TextArticle, :profile_id => @person.id, :author => @person)
  132 + create(Comment, :source_id => article.id, :author => fast_create(Person))
  133 + params[:type] = 'article_author'
  134 +
  135 + get "/api/v1/gamification_plugin/people/#{@person.id}/points_by_type?#{params.to_query}"
  136 + json = JSON.parse(last_response.body)
  137 + assert_equal default_point_weight(:article_author), json['points']
  138 + end
  139 +
  140 + should 'get points of a person filtered by profile' do
  141 + community = fast_create(Community)
  142 + create_point_rule_definition('article_author', community)
  143 + create(TextArticle, :profile_id => @person.id, :author => @person)
  144 + create(TextArticle, :profile_id => community.id, :author => @person)
  145 + params[:profile] = community.identifier
  146 +
  147 + get "/api/v1/gamification_plugin/people/#{@person.id}/points_by_profile?#{params.to_query}"
  148 + json = JSON.parse(last_response.body)
  149 + assert_equal default_point_weight(:article_author), json['points']
  150 + end
  151 +
  152 + should 'get points of a person excluding points earned in profiles' do
  153 + community = fast_create(Community)
  154 + create_point_rule_definition('article_author', community)
  155 + create(TextArticle, :profile_id => @person.id, :author => @person)
  156 + create(TextArticle, :profile_id => community.id, :author => @person)
  157 +
  158 + get "/api/v1/gamification_plugin/people/#{@person.id}/points_out_of_profiles?#{params.to_query}"
  159 + json = JSON.parse(last_response.body)
  160 + assert_equal 2*default_point_weight(:article_author), json['points']
  161 + end
  162 +
79 end 163 end
test/unit/article_test.rb
@@ -189,4 +189,24 @@ class ArticleTest &lt; ActiveSupport::TestCase @@ -189,4 +189,24 @@ class ArticleTest &lt; ActiveSupport::TestCase
189 end 189 end
190 end 190 end
191 191
  192 + should "add organization's merit badge to author when create 5 new articles" do
  193 + organization = fast_create(Organization)
  194 + GamificationPlugin::Badge.create!(:owner => organization, :name => 'article_author', :level => 1)
  195 + GamificationPlugin.gamification_set_rules(environment)
  196 +
  197 + 5.times { create(TextArticle, :profile_id => organization.id, :author => person) }
  198 + assert_equal 'article_author', person.badges.first.name
  199 + assert_equal 1, person.badges.first.level
  200 + end
  201 +
  202 + should "do not earn organization's badge when the article is not posted in the organization itself" do
  203 + organization = fast_create(Organization)
  204 + other_organization = fast_create(Organization)
  205 + GamificationPlugin::Badge.create!(:owner => organization, :name => 'article_author', :level => 1)
  206 + GamificationPlugin.gamification_set_rules(environment)
  207 +
  208 + 5.times { create(TextArticle, :profile_id => other_organization.id, :author => person) }
  209 + assert_equal [], person.badges
  210 + end
  211 +
192 end 212 end
test/unit/badge_rules_test.rb 0 → 100644
@@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
  1 +require_relative "../test_helper"
  2 +
  3 +class BadgeRulesTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @environment = Environment.default
  7 + end
  8 +
  9 + attr_accessor :environment
  10 +
  11 + should "define badge rules for environment's badges" do
  12 + badge = GamificationPlugin::Badge.create!(:owner => environment, :name => :comment_author)
  13 + badge_rules = Merit::BadgeRules.new(environment)
  14 + assert_equal [Merit::BadgeRules::AVAILABLE_RULES[badge.name].first[:action]], badge_rules.defined_rules.keys
  15 + end
  16 +
  17 + should "define badge rules for organization's badges" do
  18 + organization = fast_create(Organization)
  19 + badge = GamificationPlugin::Badge.create!(:owner => organization, :name => :comment_author)
  20 + badge_rules = Merit::BadgeRules.new(environment)
  21 + assert_equal [Merit::BadgeRules::AVAILABLE_RULES[badge.name].first[:action]], badge_rules.defined_rules.keys
  22 + end
  23 +
  24 + should 'check organization returns true when badge belongs to the environment' do
  25 + badge = GamificationPlugin::Badge.create!(:owner => environment, :name => :comment_author)
  26 + badge_rules = Merit::BadgeRules.new(environment)
  27 + comment = fast_create(Comment)
  28 + assert badge_rules.check_organization_badge(badge, comment, Merit::BadgeRules::AVAILABLE_RULES[badge.name].first)
  29 + end
  30 +
  31 + should 'check organization returns true when the comment belongs to the organization' do
  32 + organization = fast_create(Organization)
  33 + badge = GamificationPlugin::Badge.create!(:owner => organization, :name => :comment_author)
  34 + badge_rules = Merit::BadgeRules.new(environment)
  35 + article = fast_create(Article,:profile_id => organization.id)
  36 + comment = fast_create(Comment, :source_id => article.id)
  37 + assert badge_rules.check_organization_badge(badge, comment, Merit::BadgeRules::AVAILABLE_RULES[badge.name].first)
  38 + end
  39 +
  40 + should 'check organization returns false when the comment does not belongs to the organization' do
  41 + organization = fast_create(Organization)
  42 + badge = GamificationPlugin::Badge.create!(:owner => organization, :name => :comment_author)
  43 + badge_rules = Merit::BadgeRules.new(environment)
  44 + comment = fast_create(Comment)
  45 + assert !badge_rules.check_organization_badge(badge, comment, Merit::BadgeRules::AVAILABLE_RULES[badge.name].first)
  46 + end
  47 +
  48 +end
test/unit/badge_test.rb
@@ -5,9 +5,10 @@ class BadgeTest &lt; ActiveSupport::TestCase @@ -5,9 +5,10 @@ class BadgeTest &lt; ActiveSupport::TestCase
5 def setup 5 def setup
6 @person = create_user('testuser').person 6 @person = create_user('testuser').person
7 @environment = Environment.default 7 @environment = Environment.default
  8 + @organization = fast_create(Organization)
8 end 9 end
9 10
10 - attr_accessor :person, :environment 11 + attr_accessor :person, :environment, :organization
11 12
12 should 'add badge to person' do 13 should 'add badge to person' do
13 badge = GamificationPlugin::Badge.create!(:owner => environment) 14 badge = GamificationPlugin::Badge.create!(:owner => environment)
@@ -37,4 +38,16 @@ class BadgeTest &lt; ActiveSupport::TestCase @@ -37,4 +38,16 @@ class BadgeTest &lt; ActiveSupport::TestCase
37 assert_equal [badge2], person.badges.notification_pending 38 assert_equal [badge2], person.badges.notification_pending
38 end 39 end
39 40
  41 + should 'add badge to person with organization as the badge owner' do
  42 + badge = GamificationPlugin::Badge.create(:owner => organization)
  43 + person.add_badge(badge.id)
  44 + assert_equal [badge], person.badges
  45 + end
  46 +
  47 + should 'add a manual badge to person' do
  48 + badge = GamificationPlugin::Badge.create!(:name => :manual, :owner => environment)
  49 + person.add_badge(badge.id)
  50 + assert_equal [badge], person.badges
  51 + end
  52 +
40 end 53 end
test/unit/comment_test.rb
@@ -276,5 +276,14 @@ class CommentTest &lt; ActiveSupport::TestCase @@ -276,5 +276,14 @@ class CommentTest &lt; ActiveSupport::TestCase
276 end 276 end
277 end 277 end
278 278
  279 + should "add organization's merit badge to author when create 5 new comments" do
  280 + organization = fast_create(Organization)
  281 + GamificationPlugin::Badge.create!(:owner => organization, :name => 'comment_author')
  282 + GamificationPlugin.gamification_set_rules(environment)
  283 + article.profile = organization
  284 +
  285 + 5.times { create(Comment, :source => article, :author_id => person.id) }
  286 + assert_equal 'comment_author', person.badges.first.name
  287 + end
279 288
280 end 289 end
test/unit/dashboard_helper_test.rb 0 → 100644
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
  1 +require_relative "../test_helper"
  2 +
  3 +class DashboardHelperTest < ActiveSupport::TestCase
  4 +
  5 + include GamificationPlugin::DashboardHelper
  6 +
  7 + should 'return title for global badges' do
  8 + owner = Environment.new
  9 + assert_equal 'Badges', badges_title(owner)
  10 + end
  11 +
  12 + should 'return title for organization badges' do
  13 + owner = Organization.new(:name => 'organization')
  14 + assert_equal 'Badges for organization', badges_title(owner)
  15 + end
  16 +
  17 + should 'return badges grouped by owner' do
  18 + environment = Environment.default
  19 + expects(:environment).at_least_once.returns(environment)
  20 + badge1 = GamificationPlugin::Badge.create!(:owner => fast_create(Organization))
  21 + badge2 = GamificationPlugin::Badge.create!(:owner => environment)
  22 + assert_equal [[badge2.owner, [badge2]], [badge1.owner, [badge1]]], grouped_badges
  23 + end
  24 +
  25 + should 'return category of a score point' do
  26 + point_type = GamificationPlugin::PointsType.create!(name: "point category", description: "point category")
  27 + score = Merit::Score.new
  28 + score.category = point_type.id.to_s
  29 + score.save!
  30 + point = score.score_points.create!
  31 + assert_equal "point category", score_point_category(point)
  32 + end
  33 +
  34 +end
test/unit/gamification_plugin_test.rb
1 -require_relative '../../../../test/test_helper' 1 +require_relative '../test_helper'
2 2
3 class GamificationPluginTest < ActiveSupport::TestCase 3 class GamificationPluginTest < ActiveSupport::TestCase
4 4
test/unit/merit_ext_test.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +require_relative "../test_helper"
  2 +
  3 +class MeritExtTest < ActiveSupport::TestCase
  4 +
  5 + should 'check if the point was originated by an undo action' do
  6 + point = Merit::Score::Point.new
  7 + point_type = GamificationPlugin::PointsType.new(name: :comment_author)
  8 + point.expects(:point_type).returns(point_type)
  9 + action = mock
  10 + action.expects(:target_model).returns('comment')
  11 + action.expects(:action_method).returns('destroy')
  12 + point.expects(:action).at_least_once.returns(action)
  13 + assert point.undo_rule?
  14 + end
  15 +
  16 + should 'check if the point was originated by a do action' do
  17 + point = Merit::Score::Point.new
  18 + point_type = GamificationPlugin::PointsType.new(name: :comment_author)
  19 + point.expects(:point_type).returns(point_type)
  20 + action = mock
  21 + action.expects(:target_model).returns('comment')
  22 + action.expects(:action_method).returns('create')
  23 + point.expects(:action).at_least_once.returns(action)
  24 + assert !point.undo_rule?
  25 + end
  26 +
  27 +end
test/unit/point_rules_test.rb
@@ -19,4 +19,21 @@ class PointRulesTest &lt; ActiveSupport::TestCase @@ -19,4 +19,21 @@ class PointRulesTest &lt; ActiveSupport::TestCase
19 assert point_rules.defined_rules.present? 19 assert point_rules.defined_rules.present?
20 end 20 end
21 21
  22 + should 'return target url for a point related to article creation' do
  23 + person = create_user('testuser').person
  24 + create_point_rule_definition('article_author')
  25 + article = create(TextArticle, :profile_id => person.id, :author => person)
  26 + url = Merit::PointRules.target_url(person.score_points.last)
  27 + assert_equal article.url, url
  28 + end
  29 +
  30 + should 'return target url for a point related to comment creation' do
  31 + person = create_user('testuser').person
  32 + create_point_rule_definition('comment_author')
  33 + article = create(Article, :profile_id => person.id, :author => person)
  34 + comment = create(Comment, :source_id => article.id, :author => person)
  35 + url = Merit::PointRules.target_url(person.score_points.last)
  36 + assert_equal comment.url, url
  37 + end
  38 +
22 end 39 end
views/gamification/dashboard.html.erb
@@ -22,9 +22,9 @@ @@ -22,9 +22,9 @@
22 <div class="scores"> 22 <div class="scores">
23 <h3><%= _('Latest Score Points') %></h3> 23 <h3><%= _('Latest Score Points') %></h3>
24 <% @target.score_points.order('created_at desc').limit(5).each do |point| %> 24 <% @target.score_points.order('created_at desc').limit(5).each do |point| %>
25 - <div class="score <%= point.score.category %> <%= score_point_class(point) %>"> 25 + <div class="score <%= point.point_type.name %> <%= score_point_class(point) %> <%= score_point_action_class(point) %>">
26 <span class="value"><%= point.num_points %></span> 26 <span class="value"><%= point.num_points %></span>
27 - <span class="category"><%= _(score_point_category(point)) %></span> 27 + <span class="category"><%= score_point_target_link point, _(score_point_category(point)) %></span>
28 <span class="date timeago" title="<%= point.created_at %>"><%= point.created_at %></span> 28 <span class="date timeago" title="<%= point.created_at %>"><%= point.created_at %></span>
29 </div> 29 </div>
30 <% end %> 30 <% end %>
@@ -34,24 +34,26 @@ @@ -34,24 +34,26 @@
34 34
35 <% unless environment.gamification_plugin_badges.empty? %> 35 <% unless environment.gamification_plugin_badges.empty? %>
36 <div class="badges"> 36 <div class="badges">
37 - <h3><%= _('Badges') %></h3>  
38 - <ul class="badge-list">  
39 - <% environment.gamification_plugin_badges.group(:name).count.each do |badge_name, amount| %>  
40 - <% person_badge = @target.badges.where(:name => badge_name).last %>  
41 - <% badge = environment.gamification_plugin_badges.where(:name => badge_name).last %>  
42 - <li class="badge <%= badge.name %>">  
43 - <div class="badge" title="<%= badge.description %>">  
44 - <div class="image <%= badge.name %>"></div>  
45 - <ul class="level rating">  
46 - <% 1.upto(badge.level).map do |n|%>  
47 - <span class="star <%= (person_badge && person_badge.level >= n) ? 'earned' : 'not-earned' %>" >★</span>  
48 - <% end %>  
49 - </ul>  
50 - <div class="title"><%= badge.title %></div>  
51 - </div>  
52 - </li> 37 + <% grouped_badges.each do |owner, badges| %>
  38 + <h3><%= badges_title owner %></h3>
  39 + <ul class="badge-list">
  40 + <% badges.group_by(&:name).each do |badge_name, badges_group| %>
  41 + <% badge = badges_group.sort_by(&:level).last %>
  42 + <% person_badge = @target.badges.where(:name => badge.name).last %>
  43 + <li class="badge <%= badge.name %>">
  44 + <div class="badge" title="<%= badge.description %>">
  45 + <div class="image <%= badge.name %>"></div>
  46 + <ul class="level rating">
  47 + <% 1.upto(badge.level).map do |n|%>
  48 + <span class="star <%= (person_badge && person_badge.level >= n) ? 'earned' : 'not-earned' %>" >★</span>
  49 + <% end %>
  50 + </ul>
  51 + <div class="title"><%= badge.title %></div>
  52 + </div>
  53 + </li>
  54 + <% end %>
  55 + </ul>
53 <% end %> 56 <% end %>
54 - </ul>  
55 </div> 57 </div>
56 <% end %> 58 <% end %>
57 </div> 59 </div>
views/gamification_plugin_badges/_form.html.erb
@@ -29,24 +29,31 @@ @@ -29,24 +29,31 @@
29 <%= f.label :level %><br /> 29 <%= f.label :level %><br />
30 <%= f.text_field :level %> 30 <%= f.text_field :level %>
31 </div> 31 </div>
32 - <h4><%= _('Actions') %></h4>  
33 - <%= f.fields_for :custom_fields do |c| %>  
34 - <% rules.each do |name, settings| %>  
35 - <div class='controller-actions <%= "name_#{name}" %>'>  
36 - <% settings.each do |setting| %>  
37 - <%= c.label _(setting[:action]) %>  
38 - <%= c.fields_for setting[:action] do |d| %>  
39 - <% action = (@gamification_plugin_badge.custom_fields || {}).fetch(setting[:action], {}) %>  
40 - <div class="field">  
41 - <%= d.label :threshold %><br />  
42 - <%= d.text_field :threshold, {value: action.fetch('threshold', "")} %>  
43 - </div>  
44 - <br> 32 + <div class="field">
  33 + <%= f.label :profile_owner %><br />
  34 + <% tokenized_owner = @gamification_plugin_badge.owner.present? && @gamification_plugin_badge.owner.kind_of?(Organization) ? prepare_to_token_input([@gamification_plugin_badge.owner]) : nil %>
  35 + <%= token_input_field_tag('gamification_plugin_badge[owner_id]', 'badge-owner', {:action => 'search_owners'}, {:focus => false, :hint_text => _('Choose a profile or leave it blank for a global badge'), :token_limit => 1, :pre_populate => tokenized_owner}) %>
  36 + </div>
  37 + <div class="action-fields">
  38 + <h4><%= _('Actions') %></h4>
  39 + <%= f.fields_for :custom_fields do |c| %>
  40 + <% rules.each do |name, settings| %>
  41 + <div class='controller-actions <%= "name_#{name}" %>'>
  42 + <% settings.select {|s| s[:action].present?}.each do |setting| %>
  43 + <%= c.label _(setting[:action]) %>
  44 + <%= c.fields_for setting[:action] do |d| %>
  45 + <% action = (@gamification_plugin_badge.custom_fields || {}).fetch(setting[:action], {}) %>
  46 + <div class="field">
  47 + <%= d.label :threshold %><br />
  48 + <%= d.text_field :threshold, {value: action.fetch('threshold', "")} %>
  49 + </div>
  50 + <br>
  51 + <% end %>
45 <% end %> 52 <% end %>
46 - <% end %>  
47 - </div> 53 + </div>
  54 + <% end %>
48 <% end %> 55 <% end %>
49 - <% end %> 56 + </div>
50 <div class="actions"> 57 <div class="actions">
51 <%= f.submit %> 58 <%= f.submit %>
52 </div> 59 </div>
views/gamification_plugin_badges/index.html.erb
  1 +<%= stylesheet_link_tag 'plugins/gamification/admin.css' %>
  2 +
1 <h1><%= _('Gamification Settings: Listing Badges') %></h1> 3 <h1><%= _('Gamification Settings: Listing Badges') %></h1>
2 4
3 -<table> 5 +<table class="gamification-plugin-badges">
4 <tr> 6 <tr>
5 <th>Name</th> 7 <th>Name</th>
6 <th>Title</th> 8 <th>Title</th>
@@ -10,7 +12,11 @@ @@ -10,7 +12,11 @@
10 <th></th> 12 <th></th>
11 </tr> 13 </tr>
12 14
13 -<% @gamification_plugin_badges.each do |gamification_plugin_badge| %> 15 +<% @gamification_plugin_badges.each do |owner, badges| %>
  16 + <% if owner.present? %>
  17 + <tr><td class="badge-owner-group" colspan="6"><%= _("Badges for:") %> <%= owner.name %></td></tr>
  18 + <% end %>
  19 + <% badges.each do |gamification_plugin_badge| %>
14 <tr> 20 <tr>
15 <td><%= gamification_plugin_badge.name %></td> 21 <td><%= gamification_plugin_badge.name %></td>
16 <td><%= gamification_plugin_badge.title %></td> 22 <td><%= gamification_plugin_badge.title %></td>
@@ -19,6 +25,7 @@ @@ -19,6 +25,7 @@
19 <td><%= link_to 'Edit', :action => :edit, :id => gamification_plugin_badge.id %></td> 25 <td><%= link_to 'Edit', :action => :edit, :id => gamification_plugin_badge.id %></td>
20 <td><%= button_without_text :delete, _('Remove'), {:action => :destroy, :id => gamification_plugin_badge.id}, :method => :post, :confirm => _('Are you sure?') %></td> 26 <td><%= button_without_text :delete, _('Remove'), {:action => :destroy, :id => gamification_plugin_badge.id}, :method => :post, :confirm => _('Are you sure?') %></td>
21 </tr> 27 </tr>
  28 + <% end %>
22 <% end %> 29 <% end %>
23 </table> 30 </table>
24 31
views/gamification_plugin_badges/show.html.erb
@@ -22,6 +22,13 @@ @@ -22,6 +22,13 @@
22 <%= @gamification_plugin_badge.level %> 22 <%= @gamification_plugin_badge.level %>
23 </p> 23 </p>
24 24
  25 +<% if @gamification_plugin_badge.owner.kind_of?(Organization) %>
  26 +<p>
  27 + <b><%= _('Profile owner:') %></b>
  28 + <%= @gamification_plugin_badge.owner.name %>
  29 + </p>
  30 +<% end %>
  31 +
25 <p> 32 <p>
26 <b>Threshold:</b> 33 <b>Threshold:</b>
27 <% if @gamification_plugin_badge.custom_fields.is_a? Hash %> 34 <% if @gamification_plugin_badge.custom_fields.is_a? Hash %>