Merge branch 'master' into fix-mention-dmails

This commit is contained in:
Albert Yi
2017-02-27 17:00:24 -08:00
committed by GitHub
49 changed files with 579 additions and 175 deletions

View File

@@ -59,6 +59,7 @@ gem 'google-api-client'
gem 'cityhash' gem 'cityhash'
gem 'bigquery', :git => "https://github.com/abronte/BigQuery.git", :ref => "b92b4e0b54574e3fde7ad910f39a67538ed387ad" gem 'bigquery', :git => "https://github.com/abronte/BigQuery.git", :ref => "b92b4e0b54574e3fde7ad910f39a67538ed387ad"
gem 'memcache_mock' gem 'memcache_mock'
gem 'memoist'
# needed for looser jpeg header compat # needed for looser jpeg header compat
gem 'ruby-imagespec', :require => "image_spec", :git => "https://github.com/r888888888/ruby-imagespec.git", :branch => "exif-fixes" gem 'ruby-imagespec', :require => "image_spec", :git => "https://github.com/r888888888/ruby-imagespec.git", :branch => "exif-fixes"

View File

@@ -405,6 +405,7 @@ DEPENDENCIES
mechanize mechanize
memcache-client memcache-client
memcache_mock memcache_mock
memoist
mocha mocha
net-sftp net-sftp
net-ssh net-ssh

View File

@@ -9,6 +9,10 @@ div.list-of-forum-posts {
margin-bottom: 3em; margin-bottom: 3em;
word-wrap: break-word; word-wrap: break-word;
&:target {
background-color: #FFC;
}
div.author { div.author {
width: 12em; width: 12em;
float: left; float: left;

View File

@@ -1,7 +1,6 @@
module Admin module Admin
class UsersController < ApplicationController class UsersController < ApplicationController
before_filter :moderator_only before_filter :moderator_only
rescue_from User::PrivilegeError, :with => :access_denied
def edit def edit
@user = User.find(params[:id]) @user = User.find(params[:id])

View File

@@ -1,7 +1,6 @@
class CommentsController < ApplicationController class CommentsController < ApplicationController
respond_to :html, :xml, :json respond_to :html, :xml, :json
before_filter :member_only, :except => [:index, :search, :show] before_filter :member_only, :except => [:index, :search, :show]
rescue_from ActiveRecord::StatementInvalid, :with => :rescue_exception
skip_before_filter :api_check skip_before_filter :api_check
def index def index

View File

@@ -1,7 +1,6 @@
class DmailsController < ApplicationController class DmailsController < ApplicationController
respond_to :html, :xml, :json respond_to :html, :xml, :json
before_filter :member_only before_filter :member_only
rescue_from User::PrivilegeError, :with => :access_denied
def new def new
if params[:respond_to_id] if params[:respond_to_id]

View File

@@ -1,6 +1,5 @@
class LegacyController < ApplicationController class LegacyController < ApplicationController
before_filter :member_only, :only => [:create_post] before_filter :member_only, :only => [:create_post]
rescue_from PostSets::SearchError, :with => :rescue_exception
def posts def posts
@post_set = PostSets::Post.new(tag_query, params[:page], params[:limit], format: "json") @post_set = PostSets::Post.new(tag_query, params[:page], params[:limit], format: "json")

View File

@@ -1,8 +1,6 @@
module Maintenance module Maintenance
module User module User
class DeletionsController < ApplicationController class DeletionsController < ApplicationController
rescue_from UserDeletion::ValidationError, :with => :rescue_exception
def show def show
end end

View File

@@ -3,7 +3,6 @@ module Moderator
class PostsController < ApplicationController class PostsController < ApplicationController
before_filter :approver_only, :only => [:delete, :undelete, :move_favorites, :ban, :unban, :confirm_delete, :confirm_move_favorites, :confirm_ban] before_filter :approver_only, :only => [:delete, :undelete, :move_favorites, :ban, :unban, :confirm_delete, :confirm_move_favorites, :confirm_ban]
before_filter :admin_only, :only => [:expunge] before_filter :admin_only, :only => [:expunge]
rescue_from ::PostFlag::Error, ::Post::ApprovalError, :with => :rescue_exception
skip_before_filter :api_check skip_before_filter :api_check
def confirm_delete def confirm_delete

View File

@@ -15,7 +15,7 @@ module Moderator
end end
::Post.without_timeout do ::Post.without_timeout do
@posts = ::Post.order("posts.id asc").pending_or_flagged.available_for_moderation(params[:hidden]).tag_match(params[:query]).paginate(params[:page], :limit => per_page) @posts = ::Post.includes(:disapprovals, :uploader).order("posts.id asc").pending_or_flagged.available_for_moderation(params[:hidden]).tag_match(params[:query]).paginate(params[:page], :limit => per_page)
@posts.each # hack to force rails to eager load @posts.each # hack to force rails to eager load
end end
respond_with(@posts) respond_with(@posts)
@@ -25,7 +25,7 @@ module Moderator
cookies.permanent[:moderated] = Time.now.to_i cookies.permanent[:moderated] = Time.now.to_i
::Post.without_timeout do ::Post.without_timeout do
@posts = ::Post.order("posts.id asc").pending_or_flagged.available_for_moderation(false).reorder("random()").limit(RANDOM_COUNT) @posts = ::Post.includes(:disapprovals, :uploader).order("posts.id asc").pending_or_flagged.available_for_moderation(false).reorder("random()").limit(RANDOM_COUNT)
@posts.each # hack to force rails to eager load @posts.each # hack to force rails to eager load
if @posts.empty? if @posts.empty?

View File

@@ -1,9 +1,8 @@
class PostVersionsController < ApplicationController class PostVersionsController < ApplicationController
respond_to :html, :xml, :json respond_to :html, :xml, :json
rescue_from ActiveRecord::StatementInvalid, :with => :rescue_exception
def index def index
@post_versions = PostVersion.search(params[:search]).order("updated_at desc, id desc").paginate(params[:page], :limit => params[:limit], :search_count => params[:search]) @post_versions = PostArchive.search(params[:search]).order("updated_at desc, id desc").paginate(params[:page], :limit => params[:limit], :search_count => params[:search])
respond_with(@post_versions) do |format| respond_with(@post_versions) do |format|
format.xml do format.xml do
render :xml => @post_versions.to_xml(:root => "post-versions") render :xml => @post_versions.to_xml(:root => "post-versions")
@@ -15,7 +14,7 @@ class PostVersionsController < ApplicationController
end end
def undo def undo
@post_version = PostVersion.find(params[:id]) @post_version = PostArchive.find(params[:id])
if @post_version.post.visible? if @post_version.post.visible?
@post_version.undo! @post_version.undo!

View File

@@ -3,10 +3,6 @@ class PostsController < ApplicationController
before_filter :builder_only, :only => [:copy_notes] before_filter :builder_only, :only => [:copy_notes]
before_filter :enable_cors, :only => [:index, :show] before_filter :enable_cors, :only => [:index, :show]
respond_to :html, :xml, :json respond_to :html, :xml, :json
rescue_from PostSets::SearchError, :with => :rescue_exception
rescue_from Post::SearchError, :with => :rescue_exception
rescue_from ActiveRecord::StatementInvalid, :with => :rescue_exception
rescue_from ActiveRecord::RecordNotFound, :with => :rescue_exception
def index def index
if params[:md5].present? if params[:md5].present?

View File

@@ -1,7 +1,6 @@
class UploadsController < ApplicationController class UploadsController < ApplicationController
before_filter :member_only before_filter :member_only
respond_to :html, :xml, :json, :js respond_to :html, :xml, :json, :js
rescue_from Upload::Error, :with => :rescue_exception
def new def new
@upload = Upload.new @upload = Upload.new

View File

@@ -2,7 +2,6 @@ class UserNameChangeRequestsController < ApplicationController
before_filter :member_only, :only => [:index, :show] before_filter :member_only, :only => [:index, :show]
before_filter :gold_only, :only => [:new, :create] before_filter :gold_only, :only => [:new, :create]
before_filter :admin_only, :only => [:approve, :reject] before_filter :admin_only, :only => [:approve, :reject]
rescue_from User::PrivilegeError, :with => :access_denied
respond_to :html, :json, :xml respond_to :html, :json, :xml
def new def new

View File

@@ -1,7 +1,6 @@
class UsersController < ApplicationController class UsersController < ApplicationController
respond_to :html, :xml, :json respond_to :html, :xml, :json
before_filter :member_only, :only => [:edit, :update, :upgrade] before_filter :member_only, :only => [:edit, :update, :upgrade]
rescue_from User::PrivilegeError, :with => :access_denied
skip_before_filter :api_check skip_before_filter :api_check
def new def new

View File

@@ -3,8 +3,6 @@ class WikiPagesController < ApplicationController
before_filter :member_only, :except => [:index, :show, :show_or_new] before_filter :member_only, :except => [:index, :show, :show_or_new]
before_filter :builder_only, :only => [:destroy] before_filter :builder_only, :only => [:destroy]
before_filter :normalize_search_params, :only => [:index] before_filter :normalize_search_params, :only => [:index]
rescue_from ActiveRecord::StatementInvalid, :with => :rescue_exception
rescue_from ActiveRecord::RecordNotFound, :with => :rescue_exception
def new def new
@wiki_page = WikiPage.new(params[:wiki_page]) @wiki_page = WikiPage.new(params[:wiki_page])

View File

@@ -25,7 +25,7 @@ class ApproverPruner
Dmail.create_automated( Dmail.create_automated(
:to_id => user.id, :to_id => user.id,
:title => "Approver inactivity", :title => "Approver inactivity",
:body => "You haven't approved a post in the past three months. In order to make sure the list of active approvers is up-to-date, you have lost your approver privileges. Please reply to this message if you want to be reinstated." :body => "You haven't approved a post in the past three months. In order to make sure the list of active approvers is up-to-date, you have lost your approver privileges."
) )
end end
end end

View File

@@ -27,7 +27,7 @@ class BulkRevert
end end
def find_post_versions def find_post_versions
q = PostVersion.where("true") q = PostArchive.where("true")
if constraints[:user_name] if constraints[:user_name]
constraints[:user_id] = User.find_by_name(constraints[:user_name]).try(:id) constraints[:user_id] = User.find_by_name(constraints[:user_name]).try(:id)

View File

@@ -34,7 +34,7 @@ module Moderator
add_row(sums, Hash[User.where(last_ip_addr: ip_addrs).collect { |user| [user, 1] }]) add_row(sums, Hash[User.where(last_ip_addr: ip_addrs).collect { |user| [user, 1] }])
add_row_id(sums, PoolArchive.where(updater_ip_addr: ip_addrs).group(:updater_id).count) if PoolArchive.enabled? add_row_id(sums, PoolArchive.where(updater_ip_addr: ip_addrs).group(:updater_id).count) if PoolArchive.enabled?
add_row_id(sums, PostVersion.where(updater_ip_addr: ip_addrs).group(:updater_id).count) add_row_id(sums, PostArchive.where(updater_ip_addr: ip_addrs).group(:updater_id).count) if PostArchive.enabled?
sums sums
end end
@@ -52,7 +52,7 @@ module Moderator
add_row(sums, ArtistVersion.where(updater: users).group(:updater_ip_addr).count) add_row(sums, ArtistVersion.where(updater: users).group(:updater_ip_addr).count)
add_row(sums, NoteVersion.where(updater: users).group(:updater_ip_addr).count) add_row(sums, NoteVersion.where(updater: users).group(:updater_ip_addr).count)
add_row(sums, PoolArchive.where(updater_id: users.map(&:id)).group(:updater_ip_addr).count) if PoolArchive.enabled? add_row(sums, PoolArchive.where(updater_id: users.map(&:id)).group(:updater_ip_addr).count) if PoolArchive.enabled?
add_row(sums, PostVersion.where(updater_id: users.map(&:id)).group(:updater_ip_addr).count) add_row(sums, PostArchive.where(updater_id: users.map(&:id)).group(:updater_ip_addr).count) if PostArchive.enabled?
add_row(sums, WikiPageVersion.where(updater: users).group(:updater_ip_addr).count) add_row(sums, WikiPageVersion.where(updater: users).group(:updater_ip_addr).count)
add_row(sums, Comment.where(creator: users).group(:ip_addr).count) add_row(sums, Comment.where(creator: users).group(:ip_addr).count)
add_row(sums, Dmail.where(from: users).group(:creator_ip_addr).count) add_row(sums, Dmail.where(from: users).group(:creator_ip_addr).count)

View File

@@ -13,7 +13,7 @@ module Reports
end end
def mock_version(row) def mock_version(row)
PostVersion.new.tap do |x| PostArchive.new.tap do |x|
x.id = row["f"][0]["v"] x.id = row["f"][0]["v"]
x.post_id = row["f"][1]["v"] x.post_id = row["f"][1]["v"]
x.updated_at = Time.at(row["f"][2]["v"].to_f) x.updated_at = Time.at(row["f"][2]["v"].to_f)

View File

@@ -0,0 +1,10 @@
class UserNameValidator < ActiveModel::EachValidator
def validate_each(rec, attr, value)
name = User.normalize_name(value)
rec.errors[attr] << "already exists" if User.find_by_name(name).present?
rec.errors[attr] << "must be 2 to 100 characters long" if !name.length.between?(2, 100)
rec.errors[attr] << "cannot have whitespace or colons" if name =~ /[[:space:]]|:/
rec.errors[attr] << "cannot begin or end with an underscore" if name =~ /\A_|_\z/
end
end

View File

@@ -15,13 +15,13 @@ class UserRevert
end end
def validate! def validate!
if PostVersion.where(updater_id: user_id).count > THRESHOLD if PostArchive.where(updater_id: user_id).count > THRESHOLD
raise TooManyChangesError.new("This user has too many changes to be reverted") raise TooManyChangesError.new("This user has too many changes to be reverted")
end end
end end
def revert_post_changes def revert_post_changes
PostVersion.where(updater_id: user_id).find_each do |x| PostArchive.where(updater_id: user_id).find_each do |x|
x.undo! x.undo!
end end
end end

View File

@@ -12,6 +12,112 @@ class AmazonBackup < ActiveRecord::Base
first.update_column(:last_id, new_id) first.update_column(:last_id, new_id)
end end
def self.restore_from_glacier(min_id, max_id)
credentials = Aws::Credentials.new(Danbooru.config.aws_access_key_id, Danbooru.config.aws_secret_access_key)
Aws.config.update({
region: "us-east-1",
credentials: credentials
})
client = Aws::S3::Client.new
bucket = Danbooru.config.aws_s3_bucket_name
f = lambda do |key|
begin
client.restore_object(
bucket: bucket,
key: key,
restore_request: {
days: 7,
glacier_job_parameters: {
tier: "Bulk"
}
}
)
rescue Aws::S3::Errors::InternalError
puts " internal error...retrying"
sleep 30
retry
rescue Aws::S3::Errors::InvalidObjectState
puts " already restored #{key}"
rescue Aws::S3::Errors::NoSuchKey
puts " missing #{key}"
file_path = "/var/www/danbooru2/shared/data/#{key}"
if File.exists?(file_path)
base64_md5 = Digest::MD5.base64digest(File.read(file_path))
body = open(file_path, "rb")
client.put_object(bucket: bucket, key: key, body: body, content_md5: base64_md5, acl: "public-read")
puts " uploaded"
end
rescue Aws::S3::Errors::RestoreAlreadyInProgress
puts " already restoring #{key}"
end
end
Post.where("id >= ? and id <= ?", min_id, max_id).find_each do |post|
if post.has_large?
puts "large:#{post.id}"
key = "sample/" + File.basename(post.large_file_path)
f.call(key)
end
if post.has_preview?
puts "preview:#{post.id}"
key = "preview/" + File.basename(post.preview_file_path)
f.call(key)
end
puts "#{post.id}"
key = File.basename(post.file_path)
f.call(key)
end
end
def self.copy_to_standard(min_id, max_id)
credentials = Aws::Credentials.new(Danbooru.config.aws_access_key_id, Danbooru.config.aws_secret_access_key)
Aws.config.update({
region: "us-east-1",
credentials: credentials
})
client = Aws::S3::Client.new
bucket = Danbooru.config.aws_s3_bucket_name
f = lambda do |key|
begin
client.copy_object(bucket: bucket, key: key, acl: "public-read", storage_class: "STANDARD", copy_source: "/#{bucket}/#{key}", metadata_directive: "COPY")
puts " copied #{key}"
rescue Aws::S3::Errors::InternalError
puts " internal error...retrying"
sleep 30
retry
rescue Aws::S3::Errors::InvalidObjectState
puts " invalid state #{key}"
rescue Aws::S3::Errors::NoSuchKey
puts " missing #{key}"
end
end
Post.where("id >= ? and id <= ?", min_id, max_id).find_each do |post|
next unless post.has_preview?
if post.has_preview?
puts "preview:#{post.id}"
key = "preview/" + File.basename(post.preview_file_path)
f.call(key)
end
if post.has_large?
puts "large:#{post.id}"
key = "sample/" + File.basename(post.large_file_path)
f.call(key)
end
puts "#{post.id}"
key = File.basename(post.file_path)
f.call(key)
end
end
def self.execute def self.execute
return false unless Danbooru.config.aws_s3_enabled? return false unless Danbooru.config.aws_s3_enabled?

View File

@@ -14,8 +14,8 @@ class Comment < ActiveRecord::Base
after_update(:if => lambda {|rec| CurrentUser.id != rec.creator_id}) do |rec| after_update(:if => lambda {|rec| CurrentUser.id != rec.creator_id}) do |rec|
ModAction.log("comment ##{rec.id} updated by #{CurrentUser.name}") ModAction.log("comment ##{rec.id} updated by #{CurrentUser.name}")
end end
after_destroy :update_last_commented_at_on_destroy after_update :update_last_commented_at_on_destroy, :if => lambda {|rec| rec.is_deleted? && rec.is_deleted_changed?}
after_destroy(:if => lambda {|rec| CurrentUser.id != rec.creator_id}) do |rec| after_update(:if => lambda {|rec| rec.is_deleted? && rec.is_deleted_changed? && CurrentUser.id != rec.creator_id}) do |rec|
ModAction.log("comment ##{rec.id} deleted by #{CurrentUser.name}") ModAction.log("comment ##{rec.id} deleted by #{CurrentUser.name}")
end end
attr_accessible :body, :post_id, :do_not_bump_post, :is_deleted, :as => [:member, :gold, :platinum, :builder, :janitor, :moderator, :admin] attr_accessible :body, :post_id, :do_not_bump_post, :is_deleted, :as => [:member, :gold, :platinum, :builder, :janitor, :moderator, :admin]

View File

@@ -154,6 +154,10 @@ class ForumTopic < ActiveRecord::Base
self.updater_id = CurrentUser.id self.updater_id = CurrentUser.id
end end
def page_for(post_id)
(posts.where("id < ?", post_id).count / Danbooru.config.posts_per_page.to_f).ceil
end
def last_page def last_page
(response_count / Danbooru.config.posts_per_page.to_f).ceil (response_count / Danbooru.config.posts_per_page.to_f).ceil
end end

View File

@@ -71,7 +71,7 @@ class JanitorTrial < ActiveRecord::Base
end end
def send_dmail def send_dmail
body = "You have been selected as a test janitor. You can now approve pending posts and have access to the moderation interface. You should reacquaint yourself with the [[howto:upload]] guide to make sure you understand the site rules.\n\nOver the next several weeks your approvals will be monitored. If the majority of them are not quality uploads you will fail the trial period and lose your approval privileges. You will also receive a negative user record indicating you previously attempted and failed a test janitor trial.\n\nThere is a minimum quota of 1 approval a month to indicate that you are being active. Remember, the goal isn't to approve as much as possible. It's to filter out borderline-quality art.\n\nIf you have any questions please respond to this message." body = "You have been selected as a test janitor. You can now approve pending posts and have access to the moderation interface. You should reacquaint yourself with the [[howto:upload]] guide to make sure you understand the site rules.\n\nOver the next several weeks your approvals will be monitored. If the majority of them are not quality uploads you will fail the trial period and lose your approval privileges. You will also receive a negative user record indicating you previously attempted and failed a test janitor trial.\n\nThere is a minimum quota of 1 approval a month to indicate that you are being active. Remember, the goal isn't to approve as much as possible. It's to filter out borderline-quality art."
Dmail.create_automated(:title => "Test Janitor Trial Period", :body => body, :to_id => user_id) Dmail.create_automated(:title => "Test Janitor Trial Period", :body => body, :to_id => user_id)
end end

View File

@@ -42,7 +42,6 @@ class Post < ActiveRecord::Base
has_one :pixiv_ugoira_frame_data, :class_name => "PixivUgoiraFrameData", :dependent => :destroy has_one :pixiv_ugoira_frame_data, :class_name => "PixivUgoiraFrameData", :dependent => :destroy
has_many :flags, :class_name => "PostFlag", :dependent => :destroy has_many :flags, :class_name => "PostFlag", :dependent => :destroy
has_many :appeals, :class_name => "PostAppeal", :dependent => :destroy has_many :appeals, :class_name => "PostAppeal", :dependent => :destroy
has_many :versions, lambda {order("post_versions.updated_at ASC, post_versions.id ASC")}, :class_name => "PostVersion", :dependent => :destroy
has_many :votes, :class_name => "PostVote", :dependent => :destroy has_many :votes, :class_name => "PostVote", :dependent => :destroy
has_many :notes, :dependent => :destroy has_many :notes, :dependent => :destroy
has_many :comments, lambda {includes(:creator, :updater).order("comments.id")}, :dependent => :destroy has_many :comments, lambda {includes(:creator, :updater).order("comments.id")}, :dependent => :destroy
@@ -1414,12 +1413,16 @@ class Post < ActiveRecord::Base
end end
module VersionMethods module VersionMethods
def create_version(force = false) def versions
if new_record? || rating_changed? || source_changed? || parent_id_changed? || tag_string_changed? || force if PostArchive.enabled?
if merge_version? PostArchive.where(post_id: id).order("updated_at ASC, id asc")
delete_previous_version else
raise "Archive service not configured"
end
end end
def create_version(force = false)
if new_record? || rating_changed? || source_changed? || parent_id_changed? || tag_string_changed? || force
create_new_version create_new_version
end end
end end
@@ -1431,19 +1434,7 @@ class Post < ActiveRecord::Base
def create_new_version def create_new_version
User.where(id: CurrentUser.id).update_all("post_update_count = post_update_count + 1") User.where(id: CurrentUser.id).update_all("post_update_count = post_update_count + 1")
CurrentUser.reload PostArchive.queue(self) if PostArchive.enabled?
versions.create(
:rating => rating,
:source => source,
:tags => tag_string,
:parent_id => parent_id
)
end
def delete_previous_version
prev = versions.last
prev.destroy
end end
def revert_to(target) def revert_to(target)

View File

@@ -1,4 +1,6 @@
class PostArchive < ActiveRecord::Base class PostArchive < ActiveRecord::Base
extend Memoist
def self.enabled? def self.enabled?
Danbooru.config.aws_sqs_archives_url.present? Danbooru.config.aws_sqs_archives_url.present?
end end
@@ -6,32 +8,95 @@ class PostArchive < ActiveRecord::Base
establish_connection (ENV["ARCHIVE_DATABASE_URL"] || "archive_#{Rails.env}".to_sym) if enabled? establish_connection (ENV["ARCHIVE_DATABASE_URL"] || "archive_#{Rails.env}".to_sym) if enabled?
self.table_name = "post_versions" self.table_name = "post_versions"
def self.calculate_version(post_id, updated_at, version_id) module SearchMethods
if updated_at.to_i == Time.zone.parse("2007-03-14T19:38:12Z").to_i def for_user(user_id)
# Old post versions which didn't have updated_at set correctly if user_id
1 + PostVersion.where("post_id = ? and updated_at = ? and id < ?", post_id, updated_at, version_id).count where("updater_id = ?", user_id)
else else
1 + PostVersion.where("post_id = ? and updated_at < ?", post_id, updated_at).count where("false")
end end
end end
def self.export(version_id = 9096768) def for_user_name(name)
user_id = User.name_to_id(name)
for_user(user_id)
end
def search(params)
q = where("true")
params = {} if params.blank?
if params[:updater_name].present?
q = q.for_user_name(params[:updater_name])
end
if params[:updater_id].present?
q = q.where("updater_id = ?", params[:updater_id].to_i)
end
if params[:post_id].present?
q = q.where("post_id = ?", params[:post_id].to_i)
end
if params[:start_id].present?
q = q.where("id <= ?", params[:start_id].to_i)
end
q
end
end
module ArchiveServiceMethods
extend ActiveSupport::Concern
class_methods do
def sqs_service
SqsService.new(Danbooru.config.aws_sqs_archives_url)
end
def queue(post)
# queue updates to sqs so that if archives goes down for whatever reason it won't
# block post updates
raise "Archive service is not configured" if !enabled?
json = {
"post_id" => post.id,
"rating" => post.rating,
"parent_id" => post.parent_id,
"source" => post.source,
"updater_id" => CurrentUser.id,
"updater_ip_addr" => CurrentUser.ip_addr.to_s,
"updated_at" => post.updated_at.try(:iso8601),
"created_at" => post.created_at.try(:iso8601),
"tags" => post.tag_string
}
msg = "add post version\n#{json.to_json}"
sqs_service.send_message(msg)
end
def export_to_archives(version_id = 4394763)
PostVersion.where("id > ?", version_id).find_each do |version| PostVersion.where("id > ?", version_id).find_each do |version|
previous = version.previous previous = version.previous
tags = version.tags.scan(/\S+/) tags = version.tags.scan(/\S+/)
version_number = if version.updated_at.to_i == Time.zone.parse("2007-03-14T19:38:12Z").to_i
# Old post versions which didn't have updated_at set correctly
1 + PostVersion.where("post_id = ? and updated_at = ? and id < ?", version.post_id, version.updated_at, version.id).count
else
1 + PostVersion.where("post_id = ? and updated_at < ?", version.post_id, version.updated_at).count
end
if previous if previous
prev_tags = previous.tags.scan(/\S+/) prev_tags = previous.tags.scan(/\S+/)
added_tags = tags - previous.tags.scan(/\S+/) added_tags = tags - prev_tags
removed_tags = previous.tags.scan(/\S+/) - tags removed_tags = prev_tags - tags
else else
added_tags = tags added_tags = tags
removed_tags = [] removed_tags = []
end end
rating_changed = previous.nil? || version.rating != previous.try(:rating) rating_changed = previous.nil? || version.rating != previous.rating
parent_changed = previous.nil? || version.parent_id != previous.try(:parent_id) parent_changed = previous.nil? || version.parent_id != previous.parent_id
source_changed = previous.nil? || version.source != previous.try(:source) source_changed = previous.nil? || version.source != previous.source
create( create(
post_id: version.post_id, post_id: version.post_id,
tags: version.tags, tags: version.tags,
@@ -40,7 +105,7 @@ class PostArchive < ActiveRecord::Base
updater_id: version.updater_id, updater_id: version.updater_id,
updater_ip_addr: version.updater_ip_addr.to_s, updater_ip_addr: version.updater_ip_addr.to_s,
updated_at: version.updated_at, updated_at: version.updated_at,
version: calculate_version(version.post_id, version.updated_at, version.id), version: version_number,
rating: version.rating, rating: version.rating,
rating_changed: rating_changed, rating_changed: rating_changed,
parent_id: version.parent_id, parent_id: version.parent_id,
@@ -52,3 +117,176 @@ class PostArchive < ActiveRecord::Base
end end
end end
end end
end
extend SearchMethods
include ArchiveServiceMethods
def tag_array
tags.scan(/\S+/)
end
def presenter
PostVersionPresenter.new(self)
end
def reload
flush_cache
super
end
def post
Post.where(id: post_id).first
end
def previous
PostArchive.where("post_id = ? and version < ?", post_id, version).order("version desc").first
end
def updater
User.find(updater_id)
end
def diff(version = nil)
if post.nil?
latest_tags = tag_array
else
latest_tags = post.tag_array
latest_tags << "rating:#{post.rating}" if post.rating.present?
latest_tags << "parent:#{post.parent_id}" if post.parent_id.present?
latest_tags << "source:#{post.source}" if post.source.present?
end
new_tags = tag_array
new_tags << "rating:#{rating}" if rating.present?
new_tags << "parent:#{parent_id}" if parent_id.present?
new_tags << "source:#{source}" if source.present?
old_tags = version.present? ? version.tag_array : []
if version.present?
old_tags << "rating:#{version.rating}" if version.rating.present?
old_tags << "parent:#{version.parent_id}" if version.parent_id.present?
old_tags << "source:#{version.source}" if version.source.present?
end
added_tags = new_tags - old_tags
removed_tags = old_tags - new_tags
return {
:added_tags => added_tags,
:removed_tags => removed_tags,
:obsolete_added_tags => added_tags - latest_tags,
:obsolete_removed_tags => removed_tags & latest_tags,
:unchanged_tags => new_tags & old_tags,
}
end
def changes
delta = {
:added_tags => added_tags,
:removed_tags => removed_tags
}
latest_tags = post.tag_array
latest_tags << "rating:#{post.rating}" if post.rating.present?
latest_tags << "parent:#{post.parent_id}" if post.parent_id.present?
latest_tags << "source:#{post.source}" if post.source.present?
if parent_changed
delta[:added_tags] << "parent:#{parent_id}"
if previous
delta[:removed_tags] << "parent:#{previous.parent_id}"
end
end
if rating_changed
delta[:added_tags] << "rating:#{rating}"
if previous
delta[:removed_tags] << "rating:#{previous.rating}"
end
end
if source_changed
delta[:added_tags] << "source:#{source}"
if previous
delta[:removed_tags] << "source:#{previous.source}"
end
end
delta[:obsolete_added_tags] = delta[:added_tags] - latest_tags
delta[:obsolete_removed_tags] = delta[:removed_tags] & latest_tags
if previous
delta[:unchanged_tags] = tag_array & previous.tag_array
else
delta[:unchanged_tags] = []
end
delta
end
def added_tags_with_fields
changes[:added_tags].join(" ")
end
def removed_tags_with_fields
changes[:removed_tags].join(" ")
end
def obsolete_added_tags
changes[:obsolete_added_tags].join(" ")
end
def obsolete_removed_tags
changes[:obsolete_removed_tags].join(" ")
end
def unchanged_tags
changes[:unchanged_tags].join(" ")
end
def truncated_source
source.gsub(/^http:\/\//, "").sub(/\/.+/, "")
end
def undo
added = changes[:added_tags_with_fields] - changes[:obsolete_added_tags]
removed = changes[:removed_tags_with_fields] - changes[:obsolete_removed_tags]
added.each do |tag|
if tag =~ /^source:/
post.source = ""
elsif tag =~ /^parent:/
post.parent_id = nil
else
escaped_tag = Regexp.escape(tag)
post.tag_string = post.tag_string.sub(/(?:\A| )#{escaped_tag}(?:\Z| )/, " ").strip
end
end
removed.each do |tag|
if tag =~ /^source:(.+)$/
post.source = $1
else
post.tag_string = "#{post.tag_string} #{tag}".strip
end
end
end
def undo!
undo
post.save!
end
def updater
User.find_by_id(updater_id)
end
def method_attributes
super + [:obsolete_added_tags, :obsolete_removed_tags, :unchanged_tags, :updater_name]
end
memoize :previous, :post, :tag_array, :changes, :added_tags_with_fields, :removed_tags_with_fields, :obsolete_removed_tags, :obsolete_added_tags, :unchanged_tags
end

View File

@@ -61,17 +61,6 @@ class PostVersion < ActiveRecord::Base
super super
end end
def sequence_for_post
versions = PostVersion.where(:post_id => post_id).order("updated_at desc, id desc")
diffs = []
versions.each_index do |i|
if i < versions.size - 1
diffs << versions[i].diff(versions[i + 1])
end
end
return diffs
end
def diff(version) def diff(version)
latest_tags = post.tag_array latest_tags = post.tag_array
latest_tags << "rating:#{post.rating}" if post.rating.present? latest_tags << "rating:#{post.rating}" if post.rating.present?

View File

@@ -54,10 +54,8 @@ class User < ActiveRecord::Base
attr_accessor :password, :old_password attr_accessor :password, :old_password
attr_accessible :dmail_filter_attributes, :enable_privacy_mode, :enable_post_navigation, :new_post_navigation_layout, :password, :old_password, :password_confirmation, :password_hash, :email, :last_logged_in_at, :last_forum_read_at, :has_mail, :receive_email_notifications, :comment_threshold, :always_resize_images, :favorite_tags, :blacklisted_tags, :name, :ip_addr, :time_zone, :default_image_size, :enable_sequential_post_navigation, :per_page, :hide_deleted_posts, :style_usernames, :enable_auto_complete, :custom_style, :show_deleted_children, :disable_categorized_saved_searches, :disable_tagged_filenames, :enable_recent_searches, :as => [:moderator, :janitor, :gold, :platinum, :member, :anonymous, :default, :builder, :admin] attr_accessible :dmail_filter_attributes, :enable_privacy_mode, :enable_post_navigation, :new_post_navigation_layout, :password, :old_password, :password_confirmation, :password_hash, :email, :last_logged_in_at, :last_forum_read_at, :has_mail, :receive_email_notifications, :comment_threshold, :always_resize_images, :favorite_tags, :blacklisted_tags, :name, :ip_addr, :time_zone, :default_image_size, :enable_sequential_post_navigation, :per_page, :hide_deleted_posts, :style_usernames, :enable_auto_complete, :custom_style, :show_deleted_children, :disable_categorized_saved_searches, :disable_tagged_filenames, :enable_recent_searches, :as => [:moderator, :janitor, :gold, :platinum, :member, :anonymous, :default, :builder, :admin]
attr_accessible :level, :as => :admin attr_accessible :level, :as => :admin
validates_length_of :name, :within => 2..100, :on => :create
validates_format_of :name, :with => /\A[^\s:]+\Z/, :on => :create, :message => "cannot have whitespace or colons" validates :name, user_name: true, on: :create
validates_format_of :name, :with => /\A[^_].*[^_]\Z/, :on => :create, :message => "cannot begin or end with an underscore"
validates_uniqueness_of :name, :case_sensitive => false
validates_uniqueness_of :email, :case_sensitive => false, :if => lambda {|rec| rec.email.present? && rec.email_changed? } validates_uniqueness_of :email, :case_sensitive => false, :if => lambda {|rec| rec.email.present? && rec.email_changed? }
validates_length_of :password, :minimum => 5, :if => lambda {|rec| rec.new_record? || rec.password.present?} validates_length_of :password, :minimum => 5, :if => lambda {|rec| rec.new_record? || rec.password.present?}
validates_inclusion_of :default_image_size, :in => %w(large original) validates_inclusion_of :default_image_size, :in => %w(large original)
@@ -153,6 +151,10 @@ class User < ActiveRecord::Base
def id_to_pretty_name(user_id) def id_to_pretty_name(user_id)
id_to_name(user_id).gsub(/([^_])_+(?=[^_])/, "\\1 \\2") id_to_name(user_id).gsub(/([^_])_+(?=[^_])/, "\\1 \\2")
end end
def normalize_name(name)
name.to_s.mb_chars.downcase.strip.tr(" ", "_").to_s
end
end end
def pretty_name def pretty_name

View File

@@ -3,11 +3,8 @@ class UserNameChangeRequest < ActiveRecord::Base
validates_inclusion_of :status, :in => %w(pending approved rejected) validates_inclusion_of :status, :in => %w(pending approved rejected)
belongs_to :user belongs_to :user
belongs_to :approver, :class_name => "User" belongs_to :approver, :class_name => "User"
validate :uniqueness_of_desired_name
validate :not_limited, :on => :create validate :not_limited, :on => :create
validates_length_of :desired_name, :within => 2..100, :on => :create validates :desired_name, user_name: true
validates_format_of :desired_name, :with => /\A[^\s:]+\Z/, :on => :create, :message => "cannot have whitespace or colons"
before_validation :normalize_name
attr_accessible :status, :user_id, :original_name, :desired_name, :change_reason, :rejection_reason, :approver_id attr_accessible :status, :user_id, :original_name, :desired_name, :change_reason, :rejection_reason, :approver_id
def self.pending def self.pending
@@ -40,8 +37,8 @@ class UserNameChangeRequest < ActiveRecord::Base
status == "pending" status == "pending"
end end
def normalize_name def desired_name=(name)
self.desired_name = desired_name.strip.gsub(/ /, "_") super(User.normalize_name(name))
end end
def feedback def feedback
@@ -72,15 +69,6 @@ class UserNameChangeRequest < ActiveRecord::Base
end end
end end
def uniqueness_of_desired_name
if User.find_by_name(desired_name)
errors.add(:desired_name, "already exists")
return false
else
return true
end
end
def hidden_attributes def hidden_attributes
if CurrentUser.is_admin? || user == CurrentUser.user if CurrentUser.is_admin? || user == CurrentUser.user
[] []

View File

@@ -163,6 +163,7 @@ class WikiPage < ActiveRecord::Base
:title => title, :title => title,
:body => body, :body => body,
:is_locked => is_locked, :is_locked => is_locked,
:is_deleted => is_deleted,
:other_names => other_names :other_names => other_names
) )
end end

View File

@@ -2,7 +2,7 @@ class WikiPageVersion < ActiveRecord::Base
belongs_to :wiki_page belongs_to :wiki_page
belongs_to :updater, :class_name => "User" belongs_to :updater, :class_name => "User"
belongs_to :artist belongs_to :artist
attr_accessible :wiki_page_id, :title, :body, :is_locked, :updater_id, :updater_ip_addr, :version, :other_names attr_accessible :wiki_page_id, :title, :body, :is_locked, :is_deleted, :updater_id, :updater_ip_addr, :version, :other_names
delegate :visible?, :to => :wiki_page delegate :visible?, :to => :wiki_page
module SearchMethods module SearchMethods

View File

@@ -95,6 +95,10 @@ class PostPresenter < Presenter
@post = post @post = post
end end
def tag_set_presenter
@tag_set_presenter ||= TagSetPresenter.new(@post.tag_array)
end
def preview_html def preview_html
PostPresenter.preview(@post) PostPresenter.preview(@post)
end end
@@ -170,13 +174,15 @@ class PostPresenter < Presenter
end end
def tag_list_html(template, options = {}) def tag_list_html(template, options = {})
@tag_set_presenter ||= TagSetPresenter.new(@post.tag_array) tag_set_presenter.tag_list_html(template, options.merge(:show_extra_links => CurrentUser.user.is_gold?))
@tag_set_presenter.tag_list_html(template, options.merge(:show_extra_links => CurrentUser.user.is_gold?))
end end
def split_tag_list_html(template, options = {}) def split_tag_list_html(template, options = {})
@tag_set_presenter ||= TagSetPresenter.new(@post.tag_array) tag_set_presenter.split_tag_list_html(template, options.merge(:show_extra_links => CurrentUser.user.is_gold?))
@tag_set_presenter.split_tag_list_html(template, options.merge(:show_extra_links => CurrentUser.user.is_gold?)) end
def inline_tag_list_html(template)
tag_set_presenter.inline_tag_list(template)
end end
def has_nav_links?(template) def has_nav_links?(template)

View File

@@ -64,6 +64,17 @@ class TagSetPresenter < Presenter
html.html_safe html.html_safe
end end
# compact (horizontal) list, as seen in the /comments index.
def inline_tag_list(template)
@tags.map do |tag_name|
<<-EOS
<span class="category-#{Tag.category_for(tag_name)}">
#{template.link_to(tag_name.tr("_", " "), template.posts_path(tags: tag_name))}
</span>
EOS
end.join.html_safe
end
private private
def general_tags def general_tags
@general_tags ||= categories.select {|k, v| v == Tag.categories.general} @general_tags ||= categories.select {|k, v| v == Tag.categories.general}

View File

@@ -24,11 +24,7 @@
</div> </div>
<div class="row list-of-tags"> <div class="row list-of-tags">
<strong>Tags</strong> <strong>Tags</strong>
<% post.tag_array.each do |tag_name| %> <%= post.presenter.inline_tag_list_html(self) %>
<span class="category-<%= Tag.category_for(tag_name) %>">
<%= link_to(tag_name.tr("_", " "), posts_path(:tags => tag_name)) %>
</span>
<% end %>
</div> </div>
</div> </div>

View File

@@ -19,7 +19,7 @@
<% if @dmail.is_automated? %> <% if @dmail.is_automated? %>
<p class="tn"> <p class="tn">
This is an automated message. Post in the forums if you have any questions. This is an automated message. Responses will not be seen. If you have any questions either message a moderator or ask in the forum.
</p> </p>
<% end %> <% end %>
</div> </div>

View File

@@ -60,7 +60,7 @@
<strong>Hidden</strong>: <%= render "post_disapprovals/compact_counts", :disapprovals => post.disapprovals, :post => post %> <strong>Hidden</strong>: <%= render "post_disapprovals/compact_counts", :disapprovals => post.disapprovals, :post => post %>
</li> </li>
<li><strong>Source</strong>: <%= post.source %></li> <li><strong>Source</strong>: <%= post.source %></li>
<li><strong>Tags</strong>: <%= post.tag_string %></li> <li><strong>Tags</strong>: <%= post.presenter.inline_tag_list_html(self) %></li>
</ul> </ul>
</section> </section>
</div> </div>

View File

@@ -1,17 +1,17 @@
<% if disapprovals.count > 0 && (CurrentUser.can_approve_posts? || post.created_at < 3.days.ago) %> <% if CurrentUser.can_approve_posts? || post.created_at < 3.days.ago %>
<% if disapprovals.breaks_rules.count > 0 %> <% if disapprovals.map(&:reason).grep("breaks_rules").count > 0 %>
(breaks rules: <%= disapprovals.breaks_rules.count %>) (breaks rules: <%= disapprovals.map(&:reason).grep("breaks_rules").count %>)
<% end %> <% end %>
<% if disapprovals.poor_quality.count > 0 %> <% if disapprovals.map(&:reason).grep("poor_quality").count > 0 %>
(poor quality: <%= disapprovals.poor_quality.count %>) (poor quality: <%= disapprovals.map(&:reason).grep("poor_quality").count %>)
<% end %> <% end %>
<% if disapprovals.disinterest.count > 0 %> <% if disapprovals.map(&:reason).grep(/disinterest|legacy/).count > 0 %>
(no interest: <%= disapprovals.disinterest.count %>) (no interest: <%= disapprovals.map(&:reason).grep(/disinterest|legacy/).count %>)
<% end %> <% end %>
<% if disapprovals.with_message.any? %> <% if disapprovals.map(&:message).any?(&:present?) %>
(messages: <%= disapprovals.disinterest.with_message.map(&:message).to_sentence %>) (messages: <%= disapprovals.map(&:message).select(&:present?).map { |msg| format_text(msg, ragel: true, inline: true) }.to_sentence.html_safe %>)
<% end %> <% end %>
<% end %> <% end %>

View File

@@ -1,21 +1,21 @@
<% if disapprovals.count > 0 && (CurrentUser.can_approve_posts? || post.created_at < 3.days.ago) %> <% if CurrentUser.can_approve_posts? || post.created_at < 3.days.ago %>
<p> <p>
It has been reviewed by <%= pluralize disapprovals.count, "moderator" %>. It has been reviewed by <%= pluralize disapprovals.length, "moderator" %>.
<% if disapprovals.breaks_rules.count > 0 %> <% if disapprovals.map(&:reason).grep("breaks_rules").count > 0 %>
<%= disapprovals.breaks_rules.count %> believe it breaks the rules. <%= disapprovals.map(&:reason).grep("breaks_rules").count %> believe it breaks the rules.
<% end %> <% end %>
<% if disapprovals.poor_quality.count > 0 %> <% if disapprovals.map(&:reason).grep("poor_quality").count > 0 %>
<%= disapprovals.poor_quality.count %> believe it has poor quality. <%= disapprovals.map(&:reason).grep("poor_quality").count %> believe it has poor quality.
<% end %> <% end %>
<% if disapprovals.disinterest.count > 0 %> <% if disapprovals.map(&:reason).grep(/disinterest|legacy/).count > 0 %>
<%= disapprovals.disinterest.count %> did not like the post enough to approve it. <%= disapprovals.map(&:reason).grep(/disinterest|legacy/).count %> did not like the post enough to approve it.
<% end %> <% end %>
<% if disapprovals.with_message.any? %> <% if disapprovals.map(&:message).any?(&:present?) %>
Messages: <%= disapprovals.with_message.map(&:message).to_sentence %> Messages: <%= disapprovals.map(&:message).select(&:present?).map { |msg| format_text(msg, ragel: true, inline: true) }.to_sentence.html_safe %>.
<% end %> <% end %>
</p> </p>
<% end %> <% end %>

View File

@@ -53,7 +53,7 @@ module Moderator
end end
should "render" do should "render" do
assert_equal(1, PostVersion.count) assert_equal(1, PostArchive.count)
get :show, {}, {:user_id => @admin.id} get :show, {}, {:user_id => @admin.id}
assert_response :success assert_response :success
end end

View File

@@ -144,12 +144,12 @@ class PostsControllerTest < ActionController::TestCase
context "revert action" do context "revert action" do
setup do setup do
@post.stubs(:merge_version?).returns(false) PostArchive.sqs_service.stubs(:merge?).returns(false)
@post.update_attributes(:tag_string => "zzz") @post.update_attributes(:tag_string => "zzz")
end end
should "work" do should "work" do
@version = @post.versions(true).first @version = @post.versions.first
assert_equal("aaaa", @version.tags) assert_equal("aaaa", @version.tags)
post :revert, {:id => @post.id, :version_id => @version.id}, {:user_id => @user.id} post :revert, {:id => @post.id, :version_id => @version.id}, {:user_id => @user.id}
assert_redirected_to post_path(@post) assert_redirected_to post_path(@post)

View File

@@ -0,0 +1,45 @@
module PostArchiveTestHelper
def setup
super
mock_post_archive_service!
start_post_archive_transaction
end
def teardown
super
rollback_post_archive_transaction
end
def mock_post_archive_service!
mock_sqs_service = Class.new do
def send_message(msg)
_, json = msg.split(/\n/)
json = JSON.parse(json)
json.delete("created_at")
json["version"] = 1 + PostArchive.where(post_id: json["post_id"]).count
prev = PostArchive.where(post_id: json["post_id"]).order("id desc").first
if merge?(prev, json)
prev.update_columns(json)
else
PostArchive.create(json)
end
end
def merge?(prev, json)
prev && (prev.updater_id == json["updater_id"]) && (prev.updated_at >= 1.hour.ago)
end
end
PostArchive.stubs(:sqs_service).returns(mock_sqs_service.new)
end
def start_post_archive_transaction
PostArchive.connection.begin_transaction joinable: false
end
def rollback_post_archive_transaction
PostArchive.connection.rollback_transaction
end
end

View File

@@ -11,6 +11,7 @@ end
require File.expand_path('../../config/environment', __FILE__) require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help' require 'rails/test_help'
require 'cache' require 'cache'
require 'helpers/post_archive_test_helper'
Dir[File.expand_path(File.dirname(__FILE__) + "/factories/*.rb")].each {|file| require file} Dir[File.expand_path(File.dirname(__FILE__) + "/factories/*.rb")].each {|file| require file}
@@ -24,6 +25,24 @@ if defined?(MEMCACHE)
Object.send(:remove_const, :MEMCACHE) Object.send(:remove_const, :MEMCACHE)
end end
class ActiveSupport::TestCase
include PostArchiveTestHelper
end
class ActionController::TestCase
include PostArchiveTestHelper
def assert_authentication_passes(action, http_method, role, params, session)
__send__(http_method, action, params, session.merge(:user_id => @users[role].id))
assert_response :success
end
def assert_authentication_fails(action, http_method, role)
__send__(http_method, action, params, session.merge(:user_id => @users[role].id))
assert_redirected_to(new_sessions_path)
end
end
MEMCACHE = MemcacheMock.new MEMCACHE = MemcacheMock.new
Delayed::Worker.delay_jobs = false Delayed::Worker.delay_jobs = false

View File

@@ -107,11 +107,14 @@ class NoteTest < ActiveSupport::TestCase
setup do setup do
@post = FactoryGirl.create(:post, :image_width => 1000, :image_height => 1000) @post = FactoryGirl.create(:post, :image_width => 1000, :image_height => 1000)
@note = FactoryGirl.create(:note, :post => @post) @note = FactoryGirl.create(:note, :post => @post)
@note.stubs(:merge_version?).returns(false)
end end
should "increment the updater's note_update_count" do should "increment the updater's note_update_count" do
assert_difference("CurrentUser.note_update_count", 1) do @user.reload
assert_difference("@user.note_update_count", 1) do
@note.update_attributes(:body => "zzz") @note.update_attributes(:body => "zzz")
@user.reload
end end
end end

View File

@@ -10,7 +10,9 @@ class PostTest < ActiveSupport::TestCase
assert_equal(posts.map(&:id), Post.tag_match(query).pluck(:id)) assert_equal(posts.map(&:id), Post.tag_match(query).pluck(:id))
end end
setup do def setup
super
Timecop.travel(2.weeks.ago) do Timecop.travel(2.weeks.ago) do
@user = FactoryGirl.create(:user) @user = FactoryGirl.create(:user)
end end
@@ -20,7 +22,9 @@ class PostTest < ActiveSupport::TestCase
mock_saved_search_service! mock_saved_search_service!
end end
teardown do def teardown
super
CurrentUser.user = nil CurrentUser.user = nil
CurrentUser.ip_addr = nil CurrentUser.ip_addr = nil
end end
@@ -1055,7 +1059,7 @@ class PostTest < ActiveSupport::TestCase
context "that has been updated" do context "that has been updated" do
should "create a new version if it's the first version" do should "create a new version if it's the first version" do
assert_difference("PostVersion.count", 1) do assert_difference("PostArchive.count", 1) do
post = FactoryGirl.create(:post) post = FactoryGirl.create(:post)
end end
end end
@@ -1063,7 +1067,7 @@ class PostTest < ActiveSupport::TestCase
should "create a new version if it's been over an hour since the last update" do should "create a new version if it's been over an hour since the last update" do
post = FactoryGirl.create(:post) post = FactoryGirl.create(:post)
Timecop.travel(6.hours.from_now) do Timecop.travel(6.hours.from_now) do
assert_difference("PostVersion.count", 1) do assert_difference("PostArchive.count", 1) do
post.update_attributes(:tag_string => "zzz") post.update_attributes(:tag_string => "zzz")
end end
end end
@@ -1071,15 +1075,16 @@ class PostTest < ActiveSupport::TestCase
should "merge with the previous version if the updater is the same user and it's been less than an hour" do should "merge with the previous version if the updater is the same user and it's been less than an hour" do
post = FactoryGirl.create(:post) post = FactoryGirl.create(:post)
assert_difference("PostVersion.count", 0) do assert_difference("PostArchive.count", 0) do
post.update_attributes(:tag_string => "zzz") post.update_attributes(:tag_string => "zzz")
end end
assert_equal("zzz", post.versions.last.tags) assert_equal("zzz", post.versions.last.tags)
end end
should "increment the updater's post_update_count" do should "increment the updater's post_update_count" do
PostArchive.sqs_service.stubs(:merge?).returns(false)
post = FactoryGirl.create(:post, :tag_string => "aaa bbb ccc") post = FactoryGirl.create(:post, :tag_string => "aaa bbb ccc")
post.stubs(:merge_version?).returns(false) CurrentUser.reload
assert_difference("CurrentUser.post_update_count", 1) do assert_difference("CurrentUser.post_update_count", 1) do
post.update_attributes(:tag_string => "zzz") post.update_attributes(:tag_string => "zzz")
@@ -2289,8 +2294,8 @@ class PostTest < ActiveSupport::TestCase
context "a post that has been updated" do context "a post that has been updated" do
setup do setup do
PostArchive.sqs_service.stubs(:merge?).returns(false)
@post = FactoryGirl.create(:post, :rating => "q", :tag_string => "aaa", :source => nil) @post = FactoryGirl.create(:post, :rating => "q", :tag_string => "aaa", :source => nil)
@post.stubs(:merge_version?).returns(false)
@post.update_attributes(:tag_string => "aaa bbb ccc ddd") @post.update_attributes(:tag_string => "aaa bbb ccc ddd")
@post.update_attributes(:tag_string => "bbb xxx yyy", :source => "xyz") @post.update_attributes(:tag_string => "bbb xxx yyy", :source => "xyz")
@post.update_attributes(:tag_string => "bbb mmm yyy", :source => "abc") @post.update_attributes(:tag_string => "bbb mmm yyy", :source => "abc")

View File

@@ -18,28 +18,21 @@ class PostVersionTest < ActiveSupport::TestCase
context "that has multiple versions: " do context "that has multiple versions: " do
setup do setup do
PostArchive.sqs_service.stubs(:merge?).returns(false)
@post = FactoryGirl.create(:post, :tag_string => "1") @post = FactoryGirl.create(:post, :tag_string => "1")
@post.stubs(:merge_version?).returns(false)
@post.stubs(:tag_string_changed?).returns(true)
@post.update_attributes(:tag_string => "1 2") @post.update_attributes(:tag_string => "1 2")
@post.update_attributes(:tag_string => "2 3") @post.update_attributes(:tag_string => "2 3")
end end
context "a version record" do context "a version record" do
setup do setup do
@version = PostVersion.last @version = PostArchive.last
end end
should "know its previous version" do should "know its previous version" do
assert_not_nil(@version.previous) assert_not_nil(@version.previous)
assert_equal("1 2", @version.previous.tags) assert_equal("1 2", @version.previous.tags)
end end
should "know the seuqence of all versions for the post" do
assert_equal(2, @version.sequence_for_post.size)
assert_equal(%w(3), @version.sequence_for_post[0][:added_tags])
assert_equal(%w(2), @version.sequence_for_post[1][:added_tags])
end
end end
end end
@@ -75,9 +68,8 @@ class PostVersionTest < ActiveSupport::TestCase
context "that has been updated" do context "that has been updated" do
setup do setup do
@parent = FactoryGirl.create(:post) PostArchive.sqs_service.stubs(:merge?).returns(false)
@post = FactoryGirl.create(:post, :tag_string => "aaa bbb ccc", :rating => "q", :source => "xyz") @post = FactoryGirl.create(:post, :tag_string => "aaa bbb ccc", :rating => "q", :source => "xyz")
@post.stubs(:merge_version?).returns(false)
@post.update_attributes(:tag_string => "bbb ccc xxx", :source => "") @post.update_attributes(:tag_string => "bbb ccc xxx", :source => "")
end end

View File

@@ -1,7 +1,9 @@
require 'test_helper' require 'test_helper'
class PostVoteTest < ActiveSupport::TestCase class PostVoteTest < ActiveSupport::TestCase
setup do def setup
super
user = FactoryGirl.create(:user) user = FactoryGirl.create(:user)
CurrentUser.user = user CurrentUser.user = user
CurrentUser.ip_addr = "127.0.0.1" CurrentUser.ip_addr = "127.0.0.1"

View File

@@ -186,6 +186,13 @@ class UserTest < ActiveSupport::TestCase
assert_equal(Danbooru.config.default_guest_name, User.id_to_name(-1)) assert_equal(Danbooru.config.default_guest_name, User.id_to_name(-1))
end end
should "not contain whitespace" do
# U+2007: https://en.wikipedia.org/wiki/Figure_space
user = FactoryGirl.build(:user, :name => "foo\u2007bar")
user.save
assert_equal(["Name cannot have whitespace or colons"], user.errors.full_messages)
end
should "not contain a colon" do should "not contain a colon" do
user = FactoryGirl.build(:user, :name => "a:b") user = FactoryGirl.build(:user, :name => "a:b")
user.save user.save