diff --git a/app/logical/upload_limit.rb b/app/logical/upload_limit.rb new file mode 100644 index 000000000..ecc4a4ac9 --- /dev/null +++ b/app/logical/upload_limit.rb @@ -0,0 +1,99 @@ +class UploadLimit + extend Memoist + + INITIAL_POINTS = 1000 + MAXIMUM_POINTS = 10000 + + attr_reader :user + + def initialize(user) + @user = user + end + + def limited? + used_upload_slots >= upload_slots + end + + def used_upload_slots + pending = user.posts.pending + early_deleted = user.posts.deleted.where("created_at >= ?", 3.days.ago) + + pending.or(early_deleted).count + end + + def upload_slots + upload_level + 5 + end + + def upload_level + UploadLimit.points_to_level(user.upload_points) + end + + def approvals_on_current_level + (user.upload_points - UploadLimit.level_to_points(upload_level)) / 10 + end + + def approvals_for_next_level + UploadLimit.points_for_next_level(upload_level) / 10 + end + + def update_limit!(post, incremental: true) + return if user.can_upload_free? + + user.with_lock do + if incremental + user.upload_points += UploadLimit.upload_value(user.upload_points, post.is_deleted) + user.save! + else + user.update!(upload_points: UploadLimit.points_for_user(user)) + end + end + end + + def self.points_for_user(user) + points = INITIAL_POINTS + + uploads = user.posts.where(is_pending: false).order(id: :asc).pluck(:is_deleted) + uploads.each do |is_deleted| + points += upload_value(points, is_deleted) + points = points.clamp(0, MAXIMUM_POINTS) + + #warn "slots: %2d, points: %3d, value: %2d" % [UploadLimit.points_to_level(points) + 5, points, UploadLimit.upload_value(level, is_deleted)] + end + + points + end + + def self.upload_value(current_points, is_deleted) + if is_deleted + level = points_to_level(current_points) + -1 * (points_for_next_level(level) / 3.0).round.to_i + else + 10 + end + end + + def self.points_for_next_level(level) + 100 + 20 * [level - 10, 0].max + end + + def self.points_to_level(points) + level = 0 + + loop do + points -= points_for_next_level(level) + break if points < 0 + level += 1 + end + + level + end + + def self.level_to_points(level) + (1..level).map do |n| + points_for_next_level(n - 1) + end.sum + end + + memoize :used_upload_slots +end diff --git a/app/models/post.rb b/app/models/post.rb index c54ee6501..55d3f1d24 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -1279,6 +1279,9 @@ class Post < ApplicationRecord # XXX This must happen *after* the `is_deleted` flag is set to true (issue #3419). give_favorites_to_parent(options) if options[:move_favorites] + is_automatic = (reason == "Unapproved in three days") + uploader.new_upload_limit.update_limit!(self, incremental: is_automatic) + unless options[:without_mod_action] ModAction.log("deleted post ##{id}, reason: #{reason}", :post_delete) end diff --git a/app/models/post_approval.rb b/app/models/post_approval.rb index 1cb2d4654..df65375f5 100644 --- a/app/models/post_approval.rb +++ b/app/models/post_approval.rb @@ -26,10 +26,13 @@ class PostApproval < ApplicationRecord end def approve_post - ModAction.log("undeleted post ##{post_id}", :post_undelete) if post.is_deleted + is_undeletion = post.is_deleted post.flags.each(&:resolve!) post.update(approver: user, is_flagged: false, is_pending: false, is_deleted: false) + ModAction.log("undeleted post ##{post_id}", :post_undelete) if is_undeletion + + post.uploader.new_upload_limit.update_limit!(post, incremental: !is_undeletion) end def self.search(params) diff --git a/app/models/user.rb b/app/models/user.rb index c0e96e982..5bd0e8986 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -468,6 +468,10 @@ class User < ApplicationRecord (is_moderator? && flag.not_uploaded_by?(id)) || flag.creator_id == id end + def new_upload_limit + @new_upload_limit ||= UploadLimit.new(self) + end + def upload_limit [max_upload_limit - used_upload_slots, 0].max end diff --git a/app/views/users/_statistics.html.erb b/app/views/users/_statistics.html.erb index fae5374ab..5831f5dfb 100644 --- a/app/views/users/_statistics.html.erb +++ b/app/views/users/_statistics.html.erb @@ -66,6 +66,15 @@