Diff
Modified: branches/invitation_throttling/app/controllers/users_controller.rb (2015 => 2016)
--- branches/invitation_throttling/app/controllers/users_controller.rb 2008-11-28 17:40:01 UTC (rev 2015)
+++ branches/invitation_throttling/app/controllers/users_controller.rb 2008-11-28 19:07:01 UTC (rev 2016)
@@ -366,8 +366,25 @@
# For sending invitation emails
def invite
+ sending_allowed_with_reset_timestamp = ActivityLimit.check_limit(current_user, "user_invite", false)
+
respond_to do |format|
- format.html # invite.rhtml
+ if sending_allowed_with_reset_timestamp[0]
+ format.html # invite.rhtml
+ else
+ # limit of invitation for this user is already exceeded
+ error_msg = "You can't send invitations - your limit is reached, "
+ if sending_allowed_with_reset_timestamp[1].nil?
+ error_msg += "it will not be reset. Please contact myExperiment administration for details."
+ elsif sending_allowed_with_reset_timestamp[1] <= 60
+ error_msg += "please try again within a couple of minutes"
+ else
+ error_msg += "it will be reset in " + formatted_timespan(sending_allowed_with_reset_timestamp[1])
+ end
+
+ flash[:error] = error_msg
+ format.html { redirect_to user_path(current_user) }
+ end
end
end
@@ -381,15 +398,21 @@
else
# captcha verified correctly, can proceed
- addr_count, validated_addr_count, valid_addresses, db_user_addresses, err_addresses, overflow_addresses = Invitation.validate_address_list(params[:invitations][:addr_to])
+ addr_count, validated_addr_count, valid_addresses, db_user_addresses, err_addresses = Invitation.validate_address_list(params[:invitations][:addr_to], current_user)
existing_invitation_emails = []
valid_addresses_tokens = {} # a hash for pairs of 'email' => 'token'
+ overflow_addresses = []
# if validation found valid addresses, do the sending
+ # (limit on the number of invitation email is only checked where the actual email will be sent)
if validated_addr_count > 0
if params[:invitations][:as_friendship].nil?
valid_addresses.each { |email_addr|
- valid_addresses_tokens[email_addr] = ""
+ if ActivityLimit.check_limit(current_user, "user_invite")[0]
+ valid_addresses_tokens[email_addr] = ""
+ else
+ overflow_addresses << email_addr
+ end
}
Invitation.send_invitation_emails("invite", base_host, User.find(params[:invitations_user_id]), valid_addresses_tokens, params[:invitations][:msg_text])
elsif params[:invitations][:as_friendship] == "true"
@@ -399,10 +422,14 @@
if PendingInvitation.find_by_email_and_request_type_and_request_for(email_addr, "friendship", params[:invitations_user_id])
existing_invitation_emails << email_addr
else
- token_code = Digest::SHA1.hexdigest( email_addr.reverse + SECRET_WORD )
- valid_addresses_tokens[email_addr] = token_code
- invitation = PendingInvitation.new(:email => email_addr, :request_type => "friendship", :requested_by => params[:invitations_user_id], :request_for => params[:invitations_user_id], :message => params[:invitations][:msg_text], :token => token_code)
- invitation.save
+ if ActivityLimit.check_limit(current_user, "user_invite")[0]
+ token_code = Digest::SHA1.hexdigest( email_addr.reverse + SECRET_WORD )
+ valid_addresses_tokens[email_addr] = token_code
+ invitation = PendingInvitation.new(:email => email_addr, :request_type => "friendship", :requested_by => params[:invitations_user_id], :request_for => params[:invitations_user_id], :message => params[:invitations][:msg_text], :token => token_code)
+ invitation.save
+ else
+ overflow_addresses << email_addr
+ end
end
end
@@ -487,7 +514,17 @@
end
unless overflow_addresses.empty?
- error_msg += "<br/><br/>You can only send invitations to #{INVITATION_EMAIL_LIMIT} unique, valid, non-blank email addresses.<br/>The following addresses were not processed because of maximum allowed amount was exceeded:<br/>" + overflow_addresses.join("<br/>")
+ error_msg += "<br/><br/>You have ran out of quota for sending invitations, "
+ reset_quota_after = ActivityLimit.check_limit(current_user, "user_invite", false)[1]
+ if reset_quota_after.nil?
+ error_msg += "it will not be reset. Please contact myExperiment administration for details."
+ elsif reset_quota_after <= 60
+ error_msg += "please try again within a couple of minutes."
+ else
+ error_msg += "it will be reset in " + formatted_timespan(reset_quota_after) + "."
+ end
+
+ error_msg += "<br/>The following addresses were not processed because maximum allowed amount of invitations was exceeded:<br/>" + overflow_addresses.join("<br/>")
end
error_msg += "</span>"
Modified: branches/invitation_throttling/app/models/invitation.rb (2015 => 2016)
--- branches/invitation_throttling/app/models/invitation.rb 2008-11-28 17:40:01 UTC (rev 2015)
+++ branches/invitation_throttling/app/models/invitation.rb 2008-11-28 19:07:01 UTC (rev 2016)
@@ -12,17 +12,12 @@
# 3) array of valid addresses
# 4) hash of pairs ("existing_db_address" -> user_id)
# 5) array of erroneous addresses
- # 6) array of addresses that didn't fit into the EMAIL_LIMIT boundary
- def self.validate_address_list (emails_csv_string)
+ def self.validate_address_list (emails_csv_string, current_user)
- # check if need to set a limit on the number of emails to be processed
- restrict_email_count = (INVITATION_EMAIL_LIMIT == -1 ? false : true)
-
# calling code relies on the 'err_addresses' variable being initialized to [] at the beginning
err_addresses = []
valid_addresses = []
db_user_addresses = {}
- overflow_addresses = []
addr_cnt = 0
validated_addr_cnt = 0
@@ -44,22 +39,13 @@
if email_addr.downcase.match(/address@hidden,4}$/)
# email_addr is validated if we are here;
- # check if we didn't exceed the INVITATION_EMAIL_LIMIT boundary yet
- if restrict_email_count && addr_cnt <= INVITATION_EMAIL_LIMIT
- # if we didn't,
- # check if it is also present in the DB as registered address of some user -
- # if so, it needs to be treated differentrly
- if( u = User.find(:first, :conditions => ["email = ? OR unconfirmed_email = ?", email_addr, email_addr]) )
- db_user_addresses[email_addr] = u.id
- else
- validated_addr_cnt += 1
- valid_addresses << email_addr
- end
+ # check if it is also present in the DB as registered address of some user -
+ # if so, it needs to be treated differentrly
+ if( u = User.find(:first, :conditions => ["email = ? OR unconfirmed_email = ?", email_addr, email_addr]) )
+ db_user_addresses[email_addr] = u.id
else
- # we did exceed the limit, but the goal is to
- # only allow sending of not more than INVITATION_EMAIL_LIMIT emails / invitations;
- # the ones that are after the INVITATION_EMAIL_LIMIT boundary will not be processed at all
- overflow_addresses << email_addr
+ validated_addr_cnt += 1
+ valid_addresses << email_addr
end
else
err_addresses << email_addr
@@ -69,7 +55,7 @@
}
- return [addr_cnt, validated_addr_cnt, valid_addresses, db_user_addresses, err_addresses, overflow_addresses]
+ return [addr_cnt, validated_addr_cnt, valid_addresses, db_user_addresses, err_addresses]
end
Modified: branches/invitation_throttling/config/environment_private.rb.pre (2015 => 2016)
--- branches/invitation_throttling/config/environment_private.rb.pre 2008-11-28 17:40:01 UTC (rev 2015)
+++ branches/invitation_throttling/config/environment_private.rb.pre 2008-11-28 19:07:01 UTC (rev 2016)
@@ -81,4 +81,40 @@
#
# It is essential to put patterns into the ignore list in a single quotes - this will enable the
# patterns to be treated as regular expressions, not just strings
-BOT_IGNORE_LIST = ['Googlebot', 'Slurp', 'msnbot', 'crawler', 'bot', 'heritrix', 'spider', 'Nutch']
\ No newline at end of file
+BOT_IGNORE_LIST = ['Googlebot', 'Slurp', 'msnbot', 'crawler', 'bot', 'heritrix', 'spider', 'Nutch']
+
+
+# =========== Settings for Activity Limits ===========
+
+# Each limited feature will require a set of 5 settings; meanings of each described below.
+# First part of every setting is the name of the feature being limited.
+# <feature_name>_LIMIT_START_VALUE - the initial maximum allowance for the feature (used when the new limit is created)
+# <feature_name>_LIMIT_MAX_VALUE - absolute maximum allowance for the feature (this can't be exceeded after any promotions);
+# NULL for always increasing allowance
+# <feature_name>_LIMIT_FREQUENCY -- in hours -- the time period over which the allowance is given; for example 5 messages (allowance) for 24 hours (frequency)
+# NULL for non-periodic limits (i.e. limits which won't have their counters reset every <frequency> hours)
+# <feature_name>_LIMIT_PROMOTE_EVERY -- in days -- every <X> days the user will be promoted to the new level,
+# where the allowance per frequency period will be adjusted by <feature_name>_LIMIT_PROMOTE_INCREMENT;
+# NULL to indicate that promotion should never happen
+# <feature_name>_LIMIT_PROMOTE_INCREMENT - should be positive;
+# 0 to indicate that promotion shouldn't expand the allowance (why would this be useful?)
+# NULL to perform a one-time promotion by setting the limit to whatever the value of <feature_name>_LIMIT_MAX_VALUE is;
+# NULL when the <feature_name>_LIMIT_MAX_VALUE is also NULL makes the feature unlimited.
+
+INTERNAL_MESSAGE_LIMIT_START_VALUE = 10
+INTERNAL_MESSAGE_LIMIT_MAX_VALUE = 50
+INTERNAL_MESSAGE_LIMIT_FREQUENCY = 24 # hours
+INTERNAL_MESSAGE_LIMIT_PROMOTE_EVERY = 30 # days
+INTERNAL_MESSAGE_LIMIT_PROMOTE_INCREMENT = 10
+
+USER_INVITE_LIMIT_START_VALUE = 5
+USER_INVITE_LIMIT_MAX_VALUE = 30
+USER_INVITE_LIMIT_FREQUENCY = 24 # hours
+USER_INVITE_LIMIT_PROMOTE_EVERY = 30 # days
+USER_INVITE_LIMIT_PROMOTE_INCREMENT = 5
+
+GROUP_INVITE_LIMIT_START_VALUE = 5
+GROUP_INVITE_LIMIT_MAX_VALUE = 30
+GROUP_INVITE_LIMIT_FREQUENCY = 24 # hours
+GROUP_INVITE_LIMIT_PROMOTE_EVERY = 30 # days
+GROUP_INVITE_LIMIT_PROMOTE_INCREMENT = 5