Modified: branches/invitation_throttling/app/controllers/application.rb (2010 => 2011)
--- branches/invitation_throttling/app/controllers/application.rb 2008-11-27 16:39:43 UTC (rev 2010)
+++ branches/invitation_throttling/app/controllers/application.rb 2008-11-27 19:03:12 UTC (rev 2011)
@@ -39,6 +39,73 @@
end
+ #
+ def check_activity_limit(contributor, limit_feature, update_counter=true)
+ if (limit = ActivityLimit.find(:first, :conditions => ["contributor_type = ? AND contributor_id = ? AND limit_feature = ?", contributor.class.name, contributor.id, limit_feature]))
+ # limit exists - check its validity
+ if (limit.limit_frequency && limit.reset_after && Time.now > limit.reset_after)
+ # "reset_after" / "limit_frequency" are not NULL - so the limit is periodic; now it's the time to
+ # reset the counter to zero - no matter what its value of was before
+ #
+ # before this check if the contributor also needs to be "promoted" to the next level -
+ # e.g. in the first month of membership on myExperiment one can send 10 messages daily,
+ # but after that can send 15 (because the user becomes more "trusted")
+ if (limit.promote_after && Time.now > limit.promote_after)
+ # TODO: do the user promotion to the next level
+ end
+ limit.current_count = 0
+ limit.reset_after = Time.now + limit.limit_frequency.hours
+ end
+ else
+ # limit doesn't exist yet - create it, then proceed to validation and saving
+ time_now = Time.now
+ limit = ActivityLimit.new(:contributor_type => contributor.class.name, :contributor_id => contributor.id,
+ :limit_feature => limit_feature,
+ :limit_max => eval("#{limit_feature.upcase}_LIMIT"),
+ :limit_frequency => eval("#{limit_feature.upcase}_LIMIT_FREQUENCY"),
+ :reset_after => time_now + eval("#{limit_feature.upcase}_LIMIT_FREQUENCY").hours,
+ :promote_after => time_now + eval("#{limit_feature.upcase}_LIMIT_PROMOTE_EVERY").days,
+ :current_count => 0)
+ end
+
+
+ # the action takes place - increment the counter and carry out
+ # the actual check on the current counter value
+ current_count_before_update = limit.current_count
+ limit.current_count += 1 if update_counter
+ limit.save
+
+ if limit.limit_max
+ # allow the action if the "current_count" has not exceeded the "limit_max" value
+ return [(current_count_before_update < limit.limit_max), (limit.reset_after - Time.now)]
+ else
+ # NULL in "limit_max" means unlimited allowance
+ return [true, (limit.reset_after - Time.now)]
+ end
+ end
+
+
+ def formatted_timespan(time_period)
+ # Takes a period of time in seconds and returns it in human-readable form (down to minutes)
+ # from (http://www.postal-code.com/binarycode/category/devruby/)
+ out_str = ""
+
+ interval_array = [ [:weeks, 604800], [:days, 86400], [:hours, 3600], [:minutes, 60] ]
+ interval_array.each do |sub|
+ if time_period >= sub[1]
+ time_val, time_period = time_period.divmod(sub[1])
+
+ time_val == 1 ? name = sub[0].to_s.singularize : name = sub[0].to_s
+
+ ( sub[0] != :minutes ? out_str += ", " : out_str += " and " ) if out_str != ''
+ out_str += time_val.to_s + " #{name}"
+ end
+ end
+
+ return out_str
+ end
+
+
# this method is only intended to check if entry
# in "viewings" or "downloads" table needs to be
# created for current access - and this is *only*
Modified: branches/invitation_throttling/app/controllers/messages_controller.rb (2010 => 2011)
--- branches/invitation_throttling/app/controllers/messages_controller.rb 2008-11-27 16:39:43 UTC (rev 2010)
+++ branches/invitation_throttling/app/controllers/messages_controller.rb 2008-11-27 19:03:12 UTC (rev 2011)
@@ -81,7 +81,11 @@
flash[:error] = "You cannot send a message to yourself"
format.html { redirect_to new_message_url }
end
- else
+ elsif (allowed_plus_timespan = check_activity_limit(current_user, "internal_message", false))[0]
+ # the user is allowed to send messages - limit not yet reached; show the new message screen
+ # (but the counter is not updated just yet - the user might not send the message after all,
+ # so this is a mere validation - which saves user from typing the message in and learning that
+ # it can't be set because of the limit, which is expired)
if params[:reply_id]
@message = Message.new(:to => @reply.from,
:reply_id => @reply.id,
@@ -90,41 +94,59 @@
else
@message = Message.new
end
+ else
+ # no more messages can be sent because of the activity limit
+ respond_to do |format|
+ error_msg = "You have reached the message limit, "
+ if allowed_plus_timespan[1] <= 60
+ error_msg += "please try again within a couple of minutes"
+ else
+ error_msg += "it will be reset in " + formatted_timespan(allowed_plus_timespan[1])
+ end
+
+ flash[:error] = error_msg
+ format.html { redirect_to messages_path }
+ end
end
end
# POST /messages
def create
- @message = Message.new(params[:message])
- @message.from ||= current_user.id
+ # check if sending is allowed and increment the message counter
+ sending_allowed = check_activity_limit(current_user, "internal_message")[0]
- # set initial datetimes
- @message.read_at = nil
-
- # test for spoofing of "from" field
- unless @message.from.to_i == current_user.id.to_i
- errors = true
- @message.errors.add :from, "must be logged on"
- end
-
- # test for existance of reply_id
- if @message.reply_id
- begin
- reply = Message.find(@message.reply_id)
+ if sending_allowed
+ @message = Message.new(params[:message])
+ @message.from ||= current_user.id
- # test that user is replying to a message that was actually received by them
- unless reply.to.to_i == current_user.id.to_i
+ # set initial datetimes
+ @message.read_at = nil
+
+ # test for spoofing of "from" field
+ unless @message.from.to_i == current_user.id.to_i
+ errors = true
+ @message.errors.add :from, "must be logged on"
+ end
+
+ # test for existance of reply_id
+ if @message.reply_id
+ begin
+ reply = Message.find(@message.reply_id)
+
+ # test that user is replying to a message that was actually received by them
+ unless reply.to.to_i == current_user.id.to_i
+ errors = true
+ @message.errors.add :reply_id, "not addressed to sender"
+ end
+ rescue ActiveRecord::RecordNotFound
errors = true
- @message.errors.add :reply_id, "not addressed to sender"
+ @message.errors.add :reply_id, "not found"
end
- rescue ActiveRecord::RecordNotFound
- errors = true
- @message.errors.add :reply_id, "not found"
end
end
respond_to do |format|
- if !errors and @message.save
+ if sending_allowed && !errors && @message.save
begin
Notifier.deliver_new_message(@message, base_host) if @message.u_to.send_notifications?
@@ -137,6 +159,9 @@
flash[:notice] = 'Message was successfully sent.'
format.html { redirect_to messages_url }
+ elsif !sending_allowed
+ # when redirecting, the check will be carried out again, and full error message displayed to the user
+ format.html { redirect_to new_message_url }
else
format.html { render :action ="" "new" }
end