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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,7 @@ module Moderator
end
::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
end
respond_with(@posts)
@@ -25,7 +25,7 @@ module Moderator
cookies.permanent[:moderated] = Time.now.to_i
::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
if @posts.empty?

View File

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

View File

@@ -3,10 +3,6 @@ class PostsController < ApplicationController
before_filter :builder_only, :only => [:copy_notes]
before_filter :enable_cors, :only => [:index, :show]
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
if params[:md5].present?

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,7 +25,7 @@ class ApproverPruner
Dmail.create_automated(
:to_id => user.id,
: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

View File

@@ -27,7 +27,7 @@ class BulkRevert
end
def find_post_versions
q = PostVersion.where("true")
q = PostArchive.where("true")
if constraints[:user_name]
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_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
end
@@ -52,7 +52,7 @@ module Moderator
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, 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, Comment.where(creator: users).group(:ip_addr).count)
add_row(sums, Dmail.where(from: users).group(:creator_ip_addr).count)

View File

@@ -13,7 +13,7 @@ module Reports
end
def mock_version(row)
PostVersion.new.tap do |x|
PostArchive.new.tap do |x|
x.id = row["f"][0]["v"]
x.post_id = row["f"][1]["v"]
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
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")
end
end
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!
end
end

View File

@@ -12,6 +12,112 @@ class AmazonBackup < ActiveRecord::Base
first.update_column(:last_id, new_id)
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
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|
ModAction.log("comment ##{rec.id} updated by #{CurrentUser.name}")
end
after_destroy :update_last_commented_at_on_destroy
after_destroy(:if => lambda {|rec| CurrentUser.id != rec.creator_id}) do |rec|
after_update :update_last_commented_at_on_destroy, :if => lambda {|rec| rec.is_deleted? && rec.is_deleted_changed?}
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}")
end
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
end
def page_for(post_id)
(posts.where("id < ?", post_id).count / Danbooru.config.posts_per_page.to_f).ceil
end
def last_page
(response_count / Danbooru.config.posts_per_page.to_f).ceil
end

View File

@@ -71,7 +71,7 @@ class JanitorTrial < ActiveRecord::Base
end
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)
end

View File

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

View File

@@ -1,4 +1,6 @@
class PostArchive < ActiveRecord::Base
extend Memoist
def self.enabled?
Danbooru.config.aws_sqs_archives_url.present?
end
@@ -6,49 +8,285 @@ class PostArchive < ActiveRecord::Base
establish_connection (ENV["ARCHIVE_DATABASE_URL"] || "archive_#{Rails.env}".to_sym) if enabled?
self.table_name = "post_versions"
def self.calculate_version(post_id, updated_at, version_id)
if 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 < ?", post_id, updated_at, version_id).count
else
1 + PostVersion.where("post_id = ? and updated_at < ?", post_id, updated_at).count
end
end
def self.export(version_id = 9096768)
PostVersion.where("id > ?", version_id).find_each do |version|
previous = version.previous
tags = version.tags.scan(/\S+/)
if previous
prev_tags = previous.tags.scan(/\S+/)
added_tags = tags - previous.tags.scan(/\S+/)
removed_tags = previous.tags.scan(/\S+/) - tags
module SearchMethods
def for_user(user_id)
if user_id
where("updater_id = ?", user_id)
else
added_tags = tags
removed_tags = []
where("false")
end
end
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
rating_changed = previous.nil? || version.rating != previous.try(:rating)
parent_changed = previous.nil? || version.parent_id != previous.try(:parent_id)
source_changed = previous.nil? || version.source != previous.try(:source)
create(
post_id: version.post_id,
tags: version.tags,
added_tags: added_tags,
removed_tags: removed_tags,
updater_id: version.updater_id,
updater_ip_addr: version.updater_ip_addr.to_s,
updated_at: version.updated_at,
version: calculate_version(version.post_id, version.updated_at, version.id),
rating: version.rating,
rating_changed: rating_changed,
parent_id: version.parent_id,
parent_changed: parent_changed,
source: version.source,
source_changed: source_changed
)
puts "inserted #{version.id}"
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|
previous = version.previous
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
prev_tags = previous.tags.scan(/\S+/)
added_tags = tags - prev_tags
removed_tags = prev_tags - tags
else
added_tags = tags
removed_tags = []
end
rating_changed = previous.nil? || version.rating != previous.rating
parent_changed = previous.nil? || version.parent_id != previous.parent_id
source_changed = previous.nil? || version.source != previous.source
create(
post_id: version.post_id,
tags: version.tags,
added_tags: added_tags,
removed_tags: removed_tags,
updater_id: version.updater_id,
updater_ip_addr: version.updater_ip_addr.to_s,
updated_at: version.updated_at,
version: version_number,
rating: version.rating,
rating_changed: rating_changed,
parent_id: version.parent_id,
parent_changed: parent_changed,
source: version.source,
source_changed: source_changed
)
puts "inserted #{version.id}"
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
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)
latest_tags = post.tag_array
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_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
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_format_of :name, :with => /\A[^_].*[^_]\Z/, :on => :create, :message => "cannot begin or end with an underscore"
validates_uniqueness_of :name, :case_sensitive => false
validates :name, user_name: true, on: :create
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_inclusion_of :default_image_size, :in => %w(large original)
@@ -153,6 +151,10 @@ class User < ActiveRecord::Base
def id_to_pretty_name(user_id)
id_to_name(user_id).gsub(/([^_])_+(?=[^_])/, "\\1 \\2")
end
def normalize_name(name)
name.to_s.mb_chars.downcase.strip.tr(" ", "_").to_s
end
end
def pretty_name

View File

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

View File

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

View File

@@ -2,7 +2,7 @@ class WikiPageVersion < ActiveRecord::Base
belongs_to :wiki_page
belongs_to :updater, :class_name => "User"
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
module SearchMethods

View File

@@ -95,6 +95,10 @@ class PostPresenter < Presenter
@post = post
end
def tag_set_presenter
@tag_set_presenter ||= TagSetPresenter.new(@post.tag_array)
end
def preview_html
PostPresenter.preview(@post)
end
@@ -170,13 +174,15 @@ class PostPresenter < Presenter
end
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
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
def has_nav_links?(template)

View File

@@ -64,6 +64,17 @@ class TagSetPresenter < Presenter
html.html_safe
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
def general_tags
@general_tags ||= categories.select {|k, v| v == Tag.categories.general}

View File

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

View File

@@ -19,7 +19,7 @@
<% if @dmail.is_automated? %>
<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>
<% end %>
</div>

View File

@@ -60,7 +60,7 @@
<strong>Hidden</strong>: <%= render "post_disapprovals/compact_counts", :disapprovals => post.disapprovals, :post => post %>
</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>
</section>
</div>

View File

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