This commit is contained in:
albert
2010-08-18 18:42:33 -04:00
parent 23656e3fa9
commit 5610731b35
48 changed files with 664 additions and 716 deletions

View File

@@ -8,7 +8,7 @@ group :test do
gem "faker" gem "faker"
end end
gem "rails", "3.0.0.beta3" gem "rails", "3.0.0.rc"
gem "pg" gem "pg"
gem "memcache-client", :require => "memcache" gem "memcache-client", :require => "memcache"
gem "imagesize", :require => "image_size" gem "imagesize", :require => "image_size"

View File

@@ -2,9 +2,6 @@
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require File.expand_path('../config/application', __FILE__) require File.expand_path('../config/application', __FILE__)
require 'rake' require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
Rails::Application.load_tasks Danbooru::Application.load_tasks

View File

@@ -1,6 +1,7 @@
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
protect_from_forgery protect_from_forgery
before_filter :set_current_user before_filter :set_current_user
after_filter :reset_current_user
before_filter :initialize_cookies before_filter :initialize_cookies
layout "default" layout "default"
@@ -27,24 +28,29 @@ protected
def set_current_user def set_current_user
if session[:user_id] if session[:user_id]
@current_user = User.find_by_id(session[:user_id]) CurrentUser.user = User.find_by_id(session[:user_id])
CurrentUser.ip_addr = request.remote_ip
end end
if @current_user if CurrentUser.user
if @current_user.is_banned? && @current_user.ban && @current_user.ban.expires_at < Time.now if CurrentUser.user.is_banned? && CurrentUser.user.ban && CurrentUser.user.ban.expires_at < Time.now
@current_user.update_attribute(:is_banned, false) CurrentUser.user.unban!
Ban.destroy_all("user_id = #{@current_user.id}")
end end
else else
@current_user = AnonymousUser.new CurrentUser.user = AnonymousUser.new
end end
Time.zone = @current_user.time_zone Time.zone = CurrentUser.user.time_zone
end
def reset_current_user
CurrentUser.user = nil
CurrentUser.ip_addr = nil
end end
%w(member banned privileged contributor janitor moderator admin).each do |level| %w(member banned privileged contributor janitor moderator admin).each do |level|
define_method("#{level}_only") do define_method("#{level}_only") do
if @current_user.__send__("is_#{level}?") if CurrentUser.user.__send__("is_#{level}?")
true true
else else
access_denied() access_denied()
@@ -53,10 +59,10 @@ protected
end end
def initialize_cookies def initialize_cookies
if @current_user.is_anonymous? if CurrentUser.user.is_anonymous?
cookies["blacklisted_tags"] = "" cookies["blacklisted_tags"] = ""
else else
cookies["blacklisted_tags"] = @current_user.blacklisted_tags cookies["blacklisted_tags"] = CurrentUser.user.blacklisted_tags
end end
end end
end end

View File

@@ -1,11 +1,16 @@
class ArtistsController < ApplicationController class ArtistsController < ApplicationController
def new def new
@artist = Artist.new_with_defaults(params)
end end
def edit def edit
@artist = Artist.find(params[:id])
end end
def index def index
order = params[:order] == "date" ? "updated_at DESC" : "name"
limit = params[:limit] || 50
@artists = Artist.paginate(Artist.build_relation())
end end
def show def show

View File

@@ -2,6 +2,7 @@ class CommentsController < ApplicationController
respond_to :html, :xml, :json respond_to :html, :xml, :json
def index def index
@posts = Post.paginate :order => "last_commented_at DESC", :per_page => 8
end end
def update def update

View File

@@ -1,7 +1,15 @@
class FavoritesController < ApplicationController class FavoritesController < ApplicationController
def create def create
@favorite = Favorite.create(
:user_id => @current_user.id,
:post_id => params[:favorite][:post_id]
)
end end
def destroy def destroy
Favorite.destroy(
:user_id => @current_user.id,
:post_id => params[:favorite][:post_id]
)
end end
end end

View File

@@ -1,27 +1,16 @@
module PostsHelper module PostsHelper
def image_dimensions(post, current_user) def resize_image_links(post, user)
if post.is_image? links = []
"(#{post.image_width_for(current_user)}x#{post.image_height_for(current_user)})"
links << %{<a href="#" data-src="#{post.file_url}" data-width="#{post.image_width}" data-height="#{post.image_height}">Original</a>} if post.has_medium? || post.has_large?
links << %{<a href="#" data-src="#{post.medium_file_url}" data-width="#{post.medium_image_width}" data-height="#{post.medium_image_height}">Medium</a>} if post.has_medium?
links << %{<a href="#" data-src="#{post.large_file_url}" data-width="#{post.large_image_width}" data-height="#{post.large_image_height}">Large</a>} if post.has_large?
if links.any?
html = %{<li id="resize-link"><a href="#">Resize</a></li><ul id="resize-links">} + links.map {|x| %{<li>#{x}</li>}}.join("") + %{</ul>}
html.html_safe
else else
"" ""
end end
end end
def image_dimension_menu(post, current_user)
html = ""
file_size = number_to_human_size(post.file_size)
original_dimensions = post.is_image? ? "(#{post.image_width}x#{post.image_height})" : nil
large_dimensions = post.has_large? ? "(#{post.large_image_width}x#{post.large_image_height})" : nil
medium_dimensions = post.has_medium? ? "(#{post.medium_image_width}x#{post.medium_image_height})" : nil
current_dimensions = "(#{post.image_width_for(current_user)}x#{post.image_height_for(current_user)})"
html << %{<menu type="context" data-user-default="<%= current_user.default_image_size %>">}
html << %{<li>#{file_size} #{current_dimensions}</li>}
html << %{<ul>}
html << %{<li id="original">#{file_size} #{original_dimensions}</li>}
html << %{<li id="medium">#{file_size} #{medium_dimensions}</li>} if medium_dimensions
html << %{<li id="large">#{file_size} #{large_dimensions}</li>} if large_dimensions
html << %{</ul>}
html << %{</menu>}
html.html_safe
end
end end

View File

@@ -1,23 +1,60 @@
class Favorite class Favorite
def self.table_name_for(user) attr_accessor :attributes, :errors
"favorites_#{user.id % 10}"
def self.table_name_for(user_id)
"favorites_#{user_id.to_i % 10}"
end end
def self.create(user, post) def self.create(attributes)
ActiveRecord::Base.connection.execute("INSERT INTO #{table_name_for(user)} (user_id, post_id) VALUES (#{user.id}, #{post.id})") user_id = attributes[:user_id]
post_id = attributes[:post_id]
execute_sql("INSERT INTO #{table_name_for(user_id)} (user_id, post_id) VALUES (?, ?)", user_id, post_id)
rescue ActiveRecord::RecordNotUnique
# ignore
end end
def self.destroy(user, post) def self.destroy(conditions)
ActiveRecord::Base.connection.execute("DELETE FROM #{table_name_for(user)} WHERE user_id = #{user.id} AND post_id = #{post.id}") if conditions[:user_id] && conditions[:post_id]
end destroy_for_post_and_user(conditions[:post_id], conditions[:user_id])
elsif conditions[:user_id]
def self.destroy_all_for_post(post) destroy_for_user(conditions[:user_id])
0.upto(9) do |i| elsif conditions[:post_id]
ActiveRecord::Base.connection.execute("DELETE FROM favorites_#{i} WHERE post_id = #{post.id}") destroy_for_post(conditions[:post_id])
end end
end end
def self.destroy_all_for_user(user) def self.exists?(conditions)
ActiveRecord::Base.connection.execute("DELETE FROM #{table_name_for(user)} WHERE user_id = #{user.id}") if conditions[:user_id] && conditions[:post_id]
select_value_sql("SELECT 1 FROM #{table_name_for(conditions[:user_id])} WHERE user_id = ? AND post_id = ?", conditions[:user_id], conditions[:post_id])
elsif conditions[:user_id]
select_value_sql("SELECT 1 FROM #{table_name_for(conditions[:user_id])} WHERE user_id = ?", conditions[:user_id])
elsif conditions[:post_id]
select_value_sql("SELECT 1 FROM #{table_name_for(conditions[:user_id])} WHERE post_id = ?", conditions[:post_id])
else
false
end
end
private
def self.destroy_for_post_and_user(post_id, user_id)
execute_sql("DELETE FROM #{table_name_for(user_id)} WHERE post_id = #{post_id} AND user_id = #{user_id}")
end
def self.destroy_for_post(post)
0.upto(9) do |i|
execute_sql("DELETE FROM favorites_#{i} WHERE post_id = #{post.id}")
end
end
def self.destroy_for_user(user)
execute_sql("DELETE FROM #{table_name_for(user)} WHERE user_id = #{user.id}")
end
def self.select_value_sql(sql, *params)
ActiveRecord::Base.select_value_sql(sql, *params)
end
def self.execute_sql(sql, *params)
ActiveRecord::Base.execute_sql(sql, *params)
end end
end end

View File

@@ -3,7 +3,7 @@ class Artist < ActiveRecord::Base
before_create :initialize_creator before_create :initialize_creator
before_save :normalize_name before_save :normalize_name
after_save :create_version after_save :create_version
after_save :commit_url_string after_save :save_url_string
validates_uniqueness_of :name validates_uniqueness_of :name
validates_presence_of :updater_id, :updater_ip_addr validates_presence_of :updater_id, :updater_ip_addr
belongs_to :updater, :class_name => "User" belongs_to :updater, :class_name => "User"
@@ -36,7 +36,7 @@ class Artist < ActiveRecord::Base
m.extend(ClassMethods) m.extend(ClassMethods)
end end
def commit_url_string def save_url_string
if @url_string if @url_string
artist_urls.clear artist_urls.clear
@@ -82,7 +82,7 @@ class Artist < ActiveRecord::Base
module UpdaterMethods module UpdaterMethods
def updater_name def updater_name
User.find_name(updater_id).tr("_", " ") User.id_to_name(updater_id).tr("_", " ")
end end
end end
@@ -150,12 +150,35 @@ class Artist < ActiveRecord::Base
end end
end end
module FactoryMethods
def new_with_defaults(params)
returning(Artist.new) do |artist|
if params[:name]
artist.name = params[:name]
post = Post.find_by_tags("source:http* #{artist.name}").first
unless post.nil? || post.source.blank?
artist.url_string = post.source
end
end
if params[:other_names]
artist.other_names = params[:other_names]
end
if params[:urls]
artist.url_string = params[:urls]
end
end
end
end
include UrlMethods include UrlMethods
include NameMethods include NameMethods
include GroupMethods include GroupMethods
include UpdaterMethods include UpdaterMethods
extend SearchMethods extend SearchMethods
include VersionMethods include VersionMethods
extend FactoryMethods
def initialize_creator def initialize_creator
if creator.nil? if creator.nil?

View File

@@ -3,6 +3,6 @@ class ArtistVersion < ActiveRecord::Base
belongs_to :artist belongs_to :artist
def updater_name def updater_name
User.find_name(updater_id).tr("_", " ") User.id_to_name(updater_id).tr("_", " ")
end end
end end

View File

@@ -13,7 +13,7 @@ class Comment < ActiveRecord::Base
scope :hidden, lambda {|user| where("score < ?", user.comment_threshold)} scope :hidden, lambda {|user| where("score < ?", user.comment_threshold)}
def creator_name def creator_name
User.find_name(creator_id) User.id_to_name(creator_id)
end end
def validate_creator_is_not_limited def validate_creator_is_not_limited

View File

@@ -18,11 +18,11 @@ class Dmail < ActiveRecord::Base
module AddressMethods module AddressMethods
def to_name def to_name
User.find_pretty_name(to_id) User.id_to_pretty_name(to_id)
end end
def from_name def from_name
User.find_pretty_name(from_id) User.id_to_pretty_name(from_id)
end end
def to_name=(name) def to_name=(name)

View File

@@ -120,7 +120,7 @@ class Job < ActiveRecord::Base
when "mass_tag_edit" when "mass_tag_edit"
start = data["start_tags"] start = data["start_tags"]
result = data["result_tags"] result = data["result_tags"]
user = User.find_name(data["updater_id"]) user = User.id_to_name(data["updater_id"])
"start:#{start} result:#{result} user:#{user}" "start:#{start} result:#{result} user:#{user}"
when "approve_tag_alias" when "approve_tag_alias"

View File

@@ -37,7 +37,7 @@ class Note < ActiveRecord::Base
end end
def creator_name def creator_name
User.find_name(creator_id) User.id_to_name(creator_id)
end end
def update_post def update_post

View File

@@ -1,5 +1,5 @@
class NoteVersion < ActiveRecord::Base class NoteVersion < ActiveRecord::Base
def updater_name def updater_name
User.find_name(updater_id) User.id_to_name(updater_id)
end end
end end

View File

@@ -6,9 +6,14 @@ class Pool < ActiveRecord::Base
belongs_to :creator, :class_name => "User" belongs_to :creator, :class_name => "User"
belongs_to :updater, :class_name => "User" belongs_to :updater, :class_name => "User"
has_many :versions, :class_name => "PoolVersion", :dependent => :destroy has_many :versions, :class_name => "PoolVersion", :dependent => :destroy
before_save :normalize_name
after_save :create_version after_save :create_version
attr_accessible :name, :description, :post_ids, :is_public, :is_active attr_accessible :name, :description, :post_ids, :is_public, :is_active
def self.name_to_id(name)
select_value_sql("SELECT id FROM pools WHERE name = ?", name.downcase)
end
def self.create_anonymous(creator, creator_ip_addr) def self.create_anonymous(creator, creator_ip_addr)
Pool.new do |pool| Pool.new do |pool|
pool.name = "TEMP:#{Time.now.to_f}.#{rand(1_000_000)}" pool.name = "TEMP:#{Time.now.to_f}.#{rand(1_000_000)}"
@@ -21,6 +26,10 @@ class Pool < ActiveRecord::Base
end end
end end
def normalize_name
self.name = name.downcase
end
def revert_to!(version) def revert_to!(version)
self.post_ids = version.post_ids self.post_ids = version.post_ids
save save

View File

@@ -1,9 +1,8 @@
class Post < ActiveRecord::Base class Post < ActiveRecord::Base
attr_accessor :updater_id, :updater_ip_addr, :old_tag_string, :should_create_pool attr_accessor :old_tag_string, :old_parent_id
after_destroy :delete_files after_destroy :delete_files
after_destroy :delete_favorites
after_destroy :update_tag_post_counts
after_save :create_version after_save :create_version
after_save :update_parent_on_save
before_save :merge_old_tags before_save :merge_old_tags
before_save :normalize_tags before_save :normalize_tags
before_save :create_tags before_save :create_tags
@@ -11,6 +10,7 @@ class Post < ActiveRecord::Base
before_save :set_tag_counts before_save :set_tag_counts
belongs_to :updater, :class_name => "User" belongs_to :updater, :class_name => "User"
belongs_to :approver, :class_name => "User" belongs_to :approver, :class_name => "User"
belongs_to :parent, :class_name => "Post"
has_one :unapproval, :dependent => :destroy has_one :unapproval, :dependent => :destroy
has_one :upload, :dependent => :destroy has_one :upload, :dependent => :destroy
has_one :moderation_detail, :class_name => "PostModerationDetail", :dependent => :destroy has_one :moderation_detail, :class_name => "PostModerationDetail", :dependent => :destroy
@@ -18,9 +18,11 @@ class Post < ActiveRecord::Base
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 has_many :comments
validates_presence_of :updater_id, :updater_ip_addr has_many :children, :class_name => "Post", :foreign_key => "parent_id", :order => "posts.id"
validates_uniqueness_of :md5 validates_uniqueness_of :md5
attr_accessible :source, :rating, :tag_string, :old_tag_string, :updater_id, :updater_ip_addr, :last_noted_at validates_presence_of :parent, :if => lambda {|rec| !rec.parent_id.nil?}
validate :validate_parent_does_not_have_a_parent
attr_accessible :source, :rating, :tag_string, :old_tag_string, :last_noted_at
module FileMethods module FileMethods
def delete_files def delete_files
@@ -179,13 +181,13 @@ class Post < ActiveRecord::Base
end end
end end
module ModerationMethods module ApprovalMethods
def unapprove!(reason, current_user, current_ip_addr) def unapprove!(reason)
raise Unapproval::Error.new("You can't unapprove a post more than once") if is_flagged? raise Unapproval::Error.new("You can't unapprove a post more than once") if is_flagged?
unapproval = create_unapproval( unapproval = create_unapproval(
:unapprover_id => current_user.id, :unapprover_id => CurrentUser.user.id,
:unapprover_ip_addr => current_ip_addr, :unapprover_ip_addr => CurrentUser.ip_addr,
:reason => reason :reason => reason
) )
@@ -193,15 +195,13 @@ class Post < ActiveRecord::Base
raise Unapproval::Error.new(unapproval.errors.full_messages.join("; ")) raise Unapproval::Error.new(unapproval.errors.full_messages.join("; "))
end end
update_attribute(:is_flagged, true) toggle!(:is_flagged)
end
def delete!
update_attribute(:is_deleted, true)
end end
def approve! def approve!
update_attributes(:is_deleted => false, :is_pending => false) update_attributes(
:is_pending => false
)
end end
end end
@@ -226,19 +226,17 @@ class Post < ActiveRecord::Base
:source => source, :source => source,
:rating => rating, :rating => rating,
:tag_string => tag_string, :tag_string => tag_string,
:updater_id => updater_id, :updater_id => CurrentUser.user.id,
:updater_ip_addr => updater_ip_addr :updater_ip_addr => CurrentUser.ip_addr
) )
raise PostVersion::Error.new(version.errors.full_messages.join("; ")) if version.errors.any? raise PostVersion::Error.new(version.errors.full_messages.join("; ")) if version.errors.any?
end end
def revert_to!(version, reverter_id, reverter_ip_addr) def revert_to!(version)
self.source = version.source self.source = version.source
self.rating = version.rating self.rating = version.rating
self.tag_string = version.tag_string self.tag_string = version.tag_string
self.updater_id = reverter_id
self.updater_ip_addr = reverter_ip_addr
save! save!
end end
end end
@@ -256,6 +254,14 @@ class Post < ActiveRecord::Base
set_tag_string(tag_array.map {|x| Tag.find_or_create_by_name(x).name}.join(" ")) set_tag_string(tag_array.map {|x| Tag.find_or_create_by_name(x).name}.join(" "))
end end
def increment_tag_post_counts
execute_sql("UPDATE tags SET post_count = post_count + 1 WHERE name IN (?)", tag_array) if tag_array.any?
end
def decrement_tag_post_counts
execute_sql("UPDATE tags SET post_count = post_count - 1 WHERE name IN (?)", tag_array) if tag_array.any?
end
def update_tag_post_counts def update_tag_post_counts
decrement_tags = tag_array_was - tag_array decrement_tags = tag_array_was - tag_array
increment_tags = tag_array - tag_array_was increment_tags = tag_array - tag_array_was
@@ -333,19 +339,44 @@ class Post < ActiveRecord::Base
module FavoriteMethods module FavoriteMethods
def delete_favorites def delete_favorites
Favorite.destroy_all_for_post(self) Favorite.destroy_for_post(self)
end end
def add_favorite(user) def add_favorite(user)
self.fav_string += " fav:#{user.name}" if user.is_a?(ActiveRecord::Base)
user_id = user.id
else
user_id = user
end
return false if fav_string =~ /fav:#{user_id}/
self.fav_string += " fav:#{user_id}"
self.fav_string.strip! self.fav_string.strip!
Favorite.create(user, self)
# in order to avoid rerunning the callbacks, just update through raw sql
execute_sql("UPDATE posts SET fav_string = ? WHERE id = ?", fav_string, id)
Favorite.create(:user_id => user_id, :post_id => id)
end end
def remove_favorite(user) def remove_favorite(user)
self.fav_string.gsub!(/(?:\A| )fav:#{user.name}(?:\Z| )/, " ") if user.is_a?(ActiveRecord::Base)
user_id = user.id
else
user_id = user
end
self.fav_string.gsub!(/(?:\A| )fav:#{user_id}(?:\Z| )/, " ")
self.fav_string.strip! self.fav_string.strip!
Favorite.destroy(user, self)
# in order to avoid rerunning the callbacks, just update through raw sql
execute_sql("UPDATE posts SET fav_string = ? WHERE id = ?", fav_string, id)
Favorite.destroy(:user_id => user_id, :post_id => id)
end
def favorited_user_ids
fav_string.scan(/\d+/)
end end
end end
@@ -435,7 +466,11 @@ class Post < ActiveRecord::Base
q = Tag.parse_query(q) q = Tag.parse_query(q)
end end
relation = where() if q[:status] == "deleted"
relation = RemovedPost.where()
else
relation = where()
end
relation = add_range_relation(q[:post_id], "posts.id", relation) relation = add_range_relation(q[:post_id], "posts.id", relation)
relation = add_range_relation(q[:mpixels], "posts.width * posts.height / 1000000.0", relation) relation = add_range_relation(q[:mpixels], "posts.width * posts.height / 1000000.0", relation)
@@ -458,14 +493,10 @@ class Post < ActiveRecord::Base
relation = relation.where(["posts.md5 IN (?)", q[:md5]]) relation = relation.where(["posts.md5 IN (?)", q[:md5]])
end end
if q[:status] == "deleted" if q[:status] == "pending"
relation = relation.where("posts.is_deleted = TRUE")
elsif q[:status] == "pending"
relation = relation.where("posts.is_pending = TRUE") relation = relation.where("posts.is_pending = TRUE")
elsif q[:status] == "flagged" elsif q[:status] == "flagged"
relation = relation.where("posts.is_flagged = TRUE") relation = relation.where("posts.is_flagged = TRUE")
else
relation = relation.where("posts.is_deleted = FALSE")
end end
if q[:source].is_a?(String) if q[:source].is_a?(String)
@@ -553,31 +584,31 @@ class Post < ActiveRecord::Base
end end
def uploader_id def uploader_id
uploader.id
end
def uploader_name
uploader_string[9..-1] uploader_string[9..-1]
end end
def uploader_name
User.id_to_name(uploader_id)
end
def uploader def uploader
User.find_by_name(uploader_name) User.find(uploader_id)
end end
def uploader=(user) def uploader=(user)
self.uploader_string = "uploader:#{user.name}" self.uploader_string = "uploader:#{user.id}"
end end
end end
module PoolMethods module PoolMethods
def add_pool(pool) def add_pool(pool)
self.pool_string += " pool:#{pool.name}" self.pool_string += " pool:#{pool.id}"
self.pool_string.strip! self.pool_string.strip!
pool.add_post!(self) pool.add_post!(self)
end end
def remove_pool(pool) def remove_pool(pool)
self.pool_string.gsub!(/(?:\A| )pool:#{pool.name}(?:\Z| )/, " ") self.pool_string.gsub!(/(?:\A| )pool:#{pool.id}(?:\Z| )/, " ")
self.pool_string.strip! self.pool_string.strip!
pool.remove_post!(self) pool.remove_post!(self)
end end
@@ -631,9 +662,106 @@ class Post < ActiveRecord::Base
end end
end end
module ParentMethods
# A parent has many children. A child belongs to a parent.
# A parent cannot have a parent.
#
# After deleting a child:
# - Move favorites to parent.
# - Does the parent have any active children?
# - Yes: Done.
# - No: Update parent's has_children flag to false.
#
# After deleting a parent:
# - Move favorites to the first child.
# - Reparent all active children to the first active child.
module ClassMethods
def update_has_children_flag_for(post_id)
has_children = Post.exists?(["parent_id = ?", post_id])
execute_sql("UPDATE posts SET has_children = ? WHERE id = ?", has_children, post_id)
end
def recalculate_has_children_for_all_posts
transaction do
execute_sql("UPDATE posts SET has_children = false WHERE has_children = true")
execute_sql("UPDATE posts SET has_children = true WHERE id IN (SELECT p.parent_id FROM posts p WHERE p.parent_id IS NOT NULL)")
end
end
end
def self.included(m)
m.extend(ClassMethods)
end
def validate_parent_does_not_have_a_parent
return if parent.nil?
if !parent.parent.nil?
errors.add(:parent, "can not have a parent")
end
end
def update_parent_on_destroy
Post.update_has_children_flag_for(parent_id)
Post.update_has_children_flag_for(parent_id_was) if parent_id_was && parent_id != parent_id_was
end
def update_children_on_destroy
if children.size == 0
# do nothing
elsif children.size == 1
children.first.update_attribute(:parent_id, nil)
else
cached_children = children
cached_children[1..-1].each do |child|
child.update_attribute(:parent_id, cached_children[0].id)
end
cached_children[0].update_attribute(:parent_id, nil)
end
end
def update_parent_on_save
if parent_id == parent_id_was
# do nothing
elsif !parent_id_was.nil?
Post.update_has_children_flag_for(parent_id)
Post.update_has_children_flag_for(parent_id_was)
else
Post.update_has_children_flag_for(parent_id)
end
end
def give_favorites_to_parent
return if parent.nil?
favorited_user_ids.each do |user_id|
parent.add_favorite(user_id)
remove_favorite(user_id)
end
end
def delete_favorites
Favorite.destroy_for_post(self)
end
end
module RemovalMethods
def remove!
Post.transaction do
execute_sql("INSERT INTO removed_posts (#{Post.column_names.join(', ')}) SELECT #{Post.column_names.join(', ')} FROM posts WHERE posts.id = #{id}")
give_favorites_to_parent
update_children_on_destroy
delete_favorites
decrement_tag_post_counts
execute_sql("DELETE FROM posts WHERE id = #{id}")
update_parent_on_destroy
end
end
end
include FileMethods include FileMethods
include ImageMethods include ImageMethods
include ModerationMethods include ApprovalMethods
include PresenterMethods include PresenterMethods
include VersionMethods include VersionMethods
include TagMethods include TagMethods
@@ -644,6 +772,8 @@ class Post < ActiveRecord::Base
include VoteMethods include VoteMethods
extend CountMethods extend CountMethods
include CacheMethods include CacheMethods
include ParentMethods
include RemovalMethods
def reload(options = nil) def reload(options = nil)
super super

View File

@@ -217,22 +217,22 @@ class Tag < ActiveRecord::Base
if token =~ /\A(-uploader|uploader|-pool|pool|-fav|fav|sub|md5|-rating|rating|width|height|mpixels|score|filesize|source|id|date|order|status|tagcount|gentags|arttags|chartags|copytags):(.+)\Z/ if token =~ /\A(-uploader|uploader|-pool|pool|-fav|fav|sub|md5|-rating|rating|width|height|mpixels|score|filesize|source|id|date|order|status|tagcount|gentags|arttags|chartags|copytags):(.+)\Z/
case $1 case $1
when "-uploader" when "-uploader"
q[:tags][:exclude] << token[1..-1] q[:tags][:exclude] << "uploader:#{User.name_to_id(token[1..-1])}"
when "uploader" when "uploader"
q[:tags][:related] << token q[:tags][:related] << "uploader:#{User.name_to_id(token)}"
when "-pool" when "-pool"
q[:tags][:exclude] << token[1..-1] q[:tags][:exclude] << "pool:#{Pool.name_to_id(token[1..-1])}"
when "pool" when "pool"
q[:tags][:related] << token q[:tags][:related] << "pool:#{Pool.name_to_id(token)}"
when "-fav" when "-fav"
q[:tags][:exclude] << token[1..-1] q[:tags][:exclude] << "fav:#{User.name_to_id(token[1..-1])}"
when "fav" when "fav"
q[:tags][:related] << token q[:tags][:related] << "fav:#{User.name_to_id(token)}"
when "sub" when "sub"
q[:subscriptions] << $2 q[:subscriptions] << $2

View File

@@ -20,6 +20,7 @@ class User < ActiveRecord::Base
before_create :promote_to_admin_if_first_user before_create :promote_to_admin_if_first_user
before_create :normalize_level before_create :normalize_level
has_many :feedback, :class_name => "UserFeedback", :dependent => :destroy has_many :feedback, :class_name => "UserFeedback", :dependent => :destroy
has_one :ban
belongs_to :inviter, :class_name => "User" belongs_to :inviter, :class_name => "User"
scope :named, lambda {|name| where(["lower(name) = ?", name])} scope :named, lambda {|name| where(["lower(name) = ?", name])}
@@ -30,12 +31,23 @@ class User < ActiveRecord::Base
return false return false
end end
end end
def unban!
update_attribute(:is_banned, false)
ban.destroy
end
end end
module NameMethods module NameMethods
module ClassMethods module ClassMethods
def find_name(user_id) def name_to_id(name)
Cache.get("un:#{user_id}") do Cache.get("uni:#{Cache.sanitize(name)}") do
select_value_sql("SELECT id FROM users WHERE name = ?", name.downcase)
end
end
def id_to_name(user_id)
Cache.get("uin:#{user_id}") do
select_value_sql("SELECT name FROM users WHERE id = ?", user_id) || Danbooru.config.default_guest_name select_value_sql("SELECT name FROM users WHERE id = ?", user_id) || Danbooru.config.default_guest_name
end end
end end
@@ -44,8 +56,8 @@ class User < ActiveRecord::Base
where(["lower(name) = ?", name.downcase]).first where(["lower(name) = ?", name.downcase]).first
end end
def find_pretty_name(user_id) def id_to_pretty_name(user_id)
find_name.tr("_", " ") id_to_name.tr("_", " ")
end end
end end
@@ -58,7 +70,7 @@ class User < ActiveRecord::Base
end end
def update_cache def update_cache
Cache.put("un:#{id}", name) Cache.put("uin:#{id}", name)
end end
end end

View File

@@ -45,7 +45,7 @@ class WikiPage < ActiveRecord::Base
end end
def creator_name def creator_name
User.find_name(user_id).tr("_", " ") User.id_to_name(user_id).tr("_", " ")
end end
def pretty_title def pretty_title

View File

@@ -3,7 +3,7 @@ class WikiPageVersion < ActiveRecord::Base
belongs_to :updater belongs_to :updater
def updater_name def updater_name
User.find_name(updater_id) User.id_to_name(updater_id)
end end
def pretty_title def pretty_title

View File

@@ -10,7 +10,8 @@
<% end %> <% end %>
<%= auto_discovery_link_tag :atom, posts_path(:format => "atom", :tags => params[:tags]) %> <%= auto_discovery_link_tag :atom, posts_path(:format => "atom", :tags => params[:tags]) %>
<%= stylesheet_link_tag "default" %> <%= stylesheet_link_tag "default" %>
<%= javascript_include_tag "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" %> <%#= javascript_include_tag "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" %>
<%= javascript_include_tag "jquery.min.js" %>
<%= javascript_include_tag "rails" %> <%= javascript_include_tag "rails" %>
<%= javascript_include_tag "application" %> <%= javascript_include_tag "application" %>
<%= Danbooru.config.custom_html_header_content %> <%= Danbooru.config.custom_html_header_content %>

View File

@@ -1,2 +1,2 @@
<%= render :partial => "posts/partials/show/notes", :locals => {:post => post, :notes => post.notes.active} %> <%= render :partial => "posts/partials/show/notes", :locals => {:post => post, :notes => post.notes.active} %>
<%= image_tag(post.file_url_for(@current_user), :alt => post.tag_string, :width => post.image_width_for(@current_user), :height => post.image_height_for(@current_user), "data-original-width" => post.image_width, "data-original-height" => post.image_height) %> <%= image_tag(post.file_url_for(@current_user), :alt => post.tag_string, :width => post.image_width_for(@current_user), :height => post.image_height_for(@current_user), :id => "image") %>

View File

@@ -6,7 +6,10 @@
<li>Approver: <%= link_to(post.approver.name, user_path(post.approver_id)) %></li> <li>Approver: <%= link_to(post.approver.name, user_path(post.approver_id)) %></li>
<% end %> <% end %>
<li> <li>
Size: <%= image_dimension_menu(post, @current_user) %> Size: <%= number_to_human_size(post.file_size) %>
<% if post.is_image? %>
(<%= post.image_width %>x<%= post.image_height %>)
<% end %>
</li> </li>
<li><%= link_to "Tag History", post_versions_path(:post_id => post) %></li> <li><%= link_to "Tag History", post_versions_path(:post_id => post) %></li>
<li><%= link_to "Note History", note_versions_path(:post_id => post) %></li> <li><%= link_to "Note History", note_versions_path(:post_id => post) %></li>

View File

@@ -1,7 +1,5 @@
<ul> <ul>
<% if !post.is_deleted? && post.is_image? && post.image_width && post.image_width > 700 %> <%= resize_image_links(post, @current_user) %>
<li><%= link_to "Resize", "#" %></li>
<% end %>
<li><%= link_to "Favorite", "#" %></li> <li><%= link_to "Favorite", "#" %></li>
<li><%= link_to "Unfavorite", "#" %></li> <li><%= link_to "Unfavorite", "#" %></li>
<li><%= link_to "Translate", "#" %></li> <li><%= link_to "Translate", "#" %></li>

View File

@@ -1,33 +1,15 @@
require File.expand_path('../boot', __FILE__) require File.expand_path('../boot', __FILE__)
require 'rails/all' require 'rails/all'
Bundler.require(:default, Rails.env) if defined?(Bundler)
# Auto-require default libraries and those for the current Rails environment.
Bundler.require :default, Rails.env
module Danbooru module Danbooru
class Application < Rails::Application class Application < Rails::Application
# Add additional load paths for your own custom dirs config.autoload_paths += %W(#{config.root}/app/presenters #{config.root}/app/logical)
config.load_paths += %W( #{config.root}/presenters #{config.root}/logical ) config.plugins = [:all]
config.time_zone = 'Eastern Time (US & Canada)'
# Only load the plugins named here, in the order given (default is alphabetical). config.encoding = "utf-8"
# :all can be used as a placeholder for all plugins not explicitly named
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')]
# config.i18n.default_locale = :de
# Configure generators values. Many other options are available, be sure to check the documentation.
# config.generators do |g|
# g.orm :active_record
# g.template_engine :erb
# g.test_framework :test_unit, :fixture => true
# end
config.active_record.schema_format = :sql config.active_record.schema_format = :sql
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters << :password config.filter_parameters << :password
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
end end
end end

View File

@@ -1,17 +1,13 @@
# Use Bundler (preferred) require 'rubygems'
# Set up gems listed in the Gemfile.
gemfile = File.expand_path('../../Gemfile', __FILE__)
begin begin
require File.expand_path('../../.bundle/environment', __FILE__) ENV['BUNDLE_GEMFILE'] = gemfile
rescue LoadError
require 'rubygems'
require 'bundler' require 'bundler'
Bundler.setup Bundler.setup
rescue Bundler::GemNotFound => e
# To use 2.x style vendor/rails and RubyGems STDERR.puts e.message
# STDERR.puts "Try running `bundle install`."
# vendor_rails = File.expand_path('../../vendor/rails', __FILE__) exit!
# if File.exist?(vendor_rails) end if File.exist?(gemfile)
# Dir["#{vendor_rails}/*/lib"].each { |path| $:.unshift(path) }
# end
#
# require 'rubygems'
end

View File

@@ -16,4 +16,7 @@ Danbooru::Application.configure do
# Don't care if the mailer can't send # Don't care if the mailer can't send
config.action_mailer.raise_delivery_errors = false config.action_mailer.raise_delivery_errors = false
# Print deprecation notices to the Rails logger
config.active_support.deprecation = :log
end end

View File

@@ -1,4 +1,4 @@
Danboorus::Application.configure do Danbooru::Application.configure do
# Settings specified here will take precedence over those in config/environment.rb # Settings specified here will take precedence over those in config/environment.rb
# The production environment is meant for finished, "live" apps. # The production environment is meant for finished, "live" apps.
@@ -9,6 +9,15 @@ Danboorus::Application.configure do
config.consider_all_requests_local = false config.consider_all_requests_local = false
config.action_controller.perform_caching = true config.action_controller.perform_caching = true
# Specifies the header that your server uses for sending files
config.action_dispatch.x_sendfile_header = "X-Sendfile"
# For nginx:
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
# If you have no front-end server that supports something like X-Sendfile,
# just comment this out and Rails will serve the files
# See everything in the log (default is :info) # See everything in the log (default is :info)
# config.log_level = :debug # config.log_level = :debug
@@ -30,4 +39,11 @@ Danboorus::Application.configure do
# Enable threaded mode # Enable threaded mode
# config.threadsafe! # config.threadsafe!
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation can not be found)
config.i18n.fallbacks = true
# Send deprecation notices to registered listeners
config.active_support.deprecation = :notify
end end

View File

@@ -14,6 +14,9 @@ Danbooru::Application.configure do
config.consider_all_requests_local = true config.consider_all_requests_local = true
config.action_controller.perform_caching = false config.action_controller.perform_caching = false
# Raise exceptions instead of rendering exception templates
config.action_dispatch.show_exceptions = false
# Disable request forgery protection in test environment # Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = false config.action_controller.allow_forgery_protection = false
@@ -26,4 +29,7 @@ Danbooru::Application.configure do
# This is necessary if your schema can't be completely dumped by the schema dumper, # This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types # like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql # config.active_record.schema_format = :sql
# Print deprecation notices to the stderr
config.active_support.deprecation = :stderr
end end

View File

@@ -1,7 +0,0 @@
# Be sure to restart your server when you modify this file.
# Your secret key for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
ActionController::Base.cookie_verifier_secret = '214c98302eef905ab8bce4a19562e322097c526f28e718160a3c0d617ddc8edab6ae7e22cb5eec8930e215bfb936a7086d6f5b146c0092a9af1884613ce0a260'

View File

@@ -1,31 +1,37 @@
module Danbooru
module Extensions
module ActiveRecord
%w(execute select_value select_values select_all).each do |method_name|
define_method("#{method_name}_sql") do |sql, *params|
connection.__send__(method_name, self.class.sanitize_sql_array([sql, *params]))
end
self.class.__send__(:define_method, "#{method_name}_sql") do |sql, *params|
connection.__send__(method_name, sanitize_sql_array([sql, *params]))
end
end
end
module String
def to_escaped_for_sql_like
return self.gsub(/\\/, '\0\0').gsub(/%/, '\\%').gsub(/_/, '\\_').gsub(/\*/, '%')
end
def to_escaped_js
return self.gsub(/\\/, '\0\0').gsub(/['"]/) {|m| "\\#{m}"}.gsub(/\r\n|\r|\n/, '\\n')
end
end
end
end
class ActiveRecord::Base class ActiveRecord::Base
class << self class << self
public :sanitize_sql_array public :sanitize_sql_array
end end
%w(execute select_value select_values select_all).each do |method_name| include Danbooru::Extensions::ActiveRecord
define_method("#{method_name}_sql") do |sql, *params|
connection.__send__(method_name, self.class.sanitize_sql_array([sql, *params]))
end
self.class.__send__(:define_method, "#{method_name}_sql") do |sql, *params|
connection.__send__(method_name, sanitize_sql_array([sql, *params]))
end
end
end
class NilClass
def id
raise NoMethodError
end
end end
class String class String
def to_escaped_for_sql_like include Danbooru::Extensions::String
return self.gsub(/\\/, '\0\0').gsub(/%/, '\\%').gsub(/_/, '\\_').gsub(/\*/, '%')
end
def to_escaped_js
return self.gsub(/\\/, '\0\0').gsub(/['"]/) {|m| "\\#{m}"}.gsub(/\r\n|\r|\n/, '\\n')
end
end end

View File

@@ -1,3 +1,10 @@
ActiveSupport::Inflector.inflections do |inflect| # Be sure to restart your server when you modify this file.
inflect.uncountable %w( user_feedback )
end # Add new inflection rules using the following format
# (all these examples are active by default):
# ActiveSupport::Inflector.inflections do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end

View File

@@ -1,15 +1,8 @@
# Be sure to restart your server when you modify this file. # Be sure to restart your server when you modify this file.
# Your secret key for verifying cookie session data integrity. Danbooru::Application.config.session_store :cookie_store, :key => '_config_session'
# If you change this key, all old sessions will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
ActionController::Base.session = {
:key => '_danbooru_session',
:secret => '3102c705148af8124298f9e89d45da3d26e47cc4d9a67cb1c8d9c42c008ee253786346efda50331bb14811f1f445c1c9ed2d51597ad2017328de0dd263048d1a'
}
# Use the database for sessions instead of the cookie-based default, # Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information # which shouldn't be used to store highly confidential information
# (create the session table with "rake db:sessions:create") # (create the session table with "rake db:sessions:create")
# ActionController::Base.session_store = :active_record_store # Config::Application.config.session_store :active_record_store

View File

@@ -1,4 +1,4 @@
Danbooru::Application.routes.draw do |map| Danbooru::Application.routes.draw do
namespace :admin do namespace :admin do
resources :users resources :users
end end

View File

@@ -13,7 +13,6 @@ class CreatePosts < ActiveRecord::Migration
t.column :is_rating_locked, :boolean, :null => false, :default => false t.column :is_rating_locked, :boolean, :null => false, :default => false
t.column :is_pending, :boolean, :null => false, :default => false t.column :is_pending, :boolean, :null => false, :default => false
t.column :is_flagged, :boolean, :null => false, :default => false t.column :is_flagged, :boolean, :null => false, :default => false
t.column :is_deleted, :boolean, :null => false, :default => false
# Uploader # Uploader
t.column :uploader_string, :string, :null => false t.column :uploader_string, :string, :null => false
@@ -47,6 +46,10 @@ class CreatePosts < ActiveRecord::Migration
t.column :file_size, :integer, :null => false t.column :file_size, :integer, :null => false
t.column :image_width, :integer, :null => false t.column :image_width, :integer, :null => false
t.column :image_height, :integer, :null => false t.column :image_height, :integer, :null => false
# Parent
t.column :parent_id, :integer
t.column :has_children, :boolean, :null => false, :default => false
end end
add_index :posts, :md5, :unique => true add_index :posts, :md5, :unique => true
@@ -58,6 +61,7 @@ class CreatePosts < ActiveRecord::Migration
add_index :posts, :image_height add_index :posts, :image_height
add_index :posts, :source add_index :posts, :source
add_index :posts, :view_count add_index :posts, :view_count
add_index :posts, :parent_id
execute "CREATE INDEX index_posts_on_mpixels ON posts (((image_width * image_height)::numeric / 1000000.0))" execute "CREATE INDEX index_posts_on_mpixels ON posts (((image_width * image_height)::numeric / 1000000.0))"

View File

@@ -8,6 +8,7 @@ class CreateFavorites < ActiveRecord::Migration
add_index "favorites_#{number}", :post_id add_index "favorites_#{number}", :post_id
add_index "favorites_#{number}", :user_id add_index "favorites_#{number}", :user_id
add_index "favorites_#{number}", [:post_id, :user_id], :unique => true
end end
end end

View File

@@ -1,90 +0,0 @@
module Cache
def incr(key, expiry = 0)
val = Cache.get(key, expiry)
Cache.put(key, val.to_i + 1)
ActiveRecord::Base.logger.debug('MemCache Incr %s' % [key])
end
def get_multi(keys, prefix, expiry = 0)
key_to_sanitized_key_hash = keys.inject({}) do |hash, x|
hash[x] = "#{prefix}:#{Cache.sanitize(x)}"
hash
end
start_time = Time.now
sanitized_key_to_value_hash = MEMCACHE.get_multi(key_to_sanitized_key_hash.values)
elapsed = Time.now - start_time
returning({}) do |result_hash|
key_to_sanitized_key_hash.each do |key, sanitized_key|
if sanitized_key_to_value_hash.has_key?(sanitized_key)
result_hash[key] = sanitized_key_to_value_hash[sanitized_key]
else
result_hash[key] = yield(key)
Cache.put(sanitized_key, result_hash[key], expiry)
end
end
ActiveRecord::Base.logger.debug('MemCache Multi-Get (%0.6f) %s' % [elapsed, keys.join(",")])
end
end
def get(key, expiry = 0)
begin
start_time = Time.now
value = MEMCACHE.get key
elapsed = Time.now - start_time
ActiveRecord::Base.logger.debug('MemCache Get (%0.6f) %s' % [elapsed, key])
if value.nil? and block_given? then
value = yield
MEMCACHE.set key, value, expiry
end
value
rescue MemCache::MemCacheError => err
ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
if block_given? then
value = yield
put key, value, expiry
end
value
end
end
def put(key, value, expiry = 0)
key.gsub!(/\s/, "_")
key = key[0, 200]
begin
start_time = Time.now
MEMCACHE.set key, value, expiry
elapsed = Time.now - start_time
ActiveRecord::Base.logger.debug('MemCache Set (%0.6f) %s' % [elapsed, key])
value
rescue MemCache::MemCacheError => err
ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
nil
end
end
def delete(key, delay = nil)
begin
start_time = Time.now
MEMCACHE.delete key, delay
elapsed = Time.now - start_time
ActiveRecord::Base.logger.debug('MemCache Delete (%0.6f) %s' % [elapsed, key])
nil
rescue MemCache::MemCacheError => err
ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
nil
end
end
def sanitize(key)
key.gsub(/\W/) {|x| "%#{x.ord}"}.slice(0, 240)
end
module_function :get
module_function :get_multi
module_function :incr
module_function :put
module_function :delete
module_function :sanitize
end

View File

@@ -4,67 +4,84 @@ SHELL = /bin/sh
#### Start of system configuration section. #### #### Start of system configuration section. ####
srcdir = . srcdir = .
topdir = /opt/local/lib/ruby/1.8/i686-darwin10 topdir = /Users/ayi/.rvm/rubies/ruby-1.9.2-preview1/include/ruby-1.9.1
hdrdir = $(topdir) hdrdir = /Users/ayi/.rvm/rubies/ruby-1.9.2-preview1/include/ruby-1.9.1
VPATH = $(srcdir):$(topdir):$(hdrdir) arch_hdrdir = /Users/ayi/.rvm/rubies/ruby-1.9.2-preview1/include/ruby-1.9.1/$(arch)
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
prefix = $(DESTDIR)/Users/ayi/.rvm/rubies/ruby-1.9.2-preview1
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
exec_prefix = $(prefix) exec_prefix = $(prefix)
prefix = $(DESTDIR)/opt/local vendorhdrdir = $(rubyhdrdir)/vendor_ruby
sharedstatedir = $(prefix)/com sitehdrdir = $(rubyhdrdir)/site_ruby
mandir = $(DESTDIR)/opt/local/share/man rubyhdrdir = $(includedir)/$(RUBY_BASE_NAME)-$(ruby_version)
psdir = $(docdir) vendordir = $(rubylibprefix)/vendor_ruby
oldincludedir = $(DESTDIR)/usr/include sitedir = $(rubylibprefix)/site_ruby
ridir = $(datarootdir)/$(RI_BASE_NAME)
mandir = $(datarootdir)/man
localedir = $(datarootdir)/locale localedir = $(datarootdir)/locale
bindir = $(exec_prefix)/bin
libexecdir = $(exec_prefix)/libexec
sitedir = $(libdir)/ruby/site_ruby
htmldir = $(docdir)
vendorarchdir = $(vendorlibdir)/$(sitearch)
includedir = $(prefix)/include
infodir = $(datarootdir)/info
vendorlibdir = $(vendordir)/$(ruby_version)
sysconfdir = $(prefix)/etc
libdir = $(exec_prefix)/lib libdir = $(exec_prefix)/lib
sbindir = $(exec_prefix)/sbin psdir = $(docdir)
rubylibdir = $(libdir)/ruby/$(ruby_version)
docdir = $(datarootdir)/doc/$(PACKAGE)
dvidir = $(docdir)
vendordir = $(DESTDIR)/opt/local/lib/ruby/vendor_ruby
datarootdir = $(prefix)/share
pdfdir = $(docdir) pdfdir = $(docdir)
archdir = $(rubylibdir)/$(arch) dvidir = $(docdir)
sitearchdir = $(sitelibdir)/$(sitearch) htmldir = $(docdir)
datadir = $(datarootdir) infodir = $(datarootdir)/info
docdir = $(datarootdir)/doc/$(PACKAGE)
oldincludedir = $(DESTDIR)/usr/include
includedir = $(prefix)/include
localstatedir = $(prefix)/var localstatedir = $(prefix)/var
sharedstatedir = $(prefix)/com
sysconfdir = $(prefix)/etc
datadir = $(datarootdir)
datarootdir = $(prefix)/share
libexecdir = $(exec_prefix)/libexec
sbindir = $(exec_prefix)/sbin
bindir = $(exec_prefix)/bin
rubylibdir = $(rubylibprefix)/$(ruby_version)
archdir = $(rubylibdir)/$(arch)
sitelibdir = $(sitedir)/$(ruby_version) sitelibdir = $(sitedir)/$(ruby_version)
sitearchdir = $(sitelibdir)/$(sitearch)
vendorlibdir = $(vendordir)/$(ruby_version)
vendorarchdir = $(vendorlibdir)/$(sitearch)
CC = g++ CC = g++
LIBRUBY = $(LIBRUBY_SO) CXX = g++
LIBRUBY = $(LIBRUBY_A)
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME) LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
OUTFLAG = -o
COUTFLAG = -o
RUBY_EXTCONF_H = RUBY_EXTCONF_H =
CFLAGS = -fno-common -O2 -fno-exceptions -Wall -arch x86_64 cflags = $(optflags) $(debugflags) $(warnflags)
INCFLAGS = -I. -I. -I/opt/local/lib/ruby/1.8/i686-darwin10 -I. optflags = -O3
debugflags = -g
warnflags = -Wall -Wno-unused-parameter -Wno-parentheses -Wno-missing-field-initializers -Wshorten-64-to-32 -Wpointer-arith -Wwrite-strings
CFLAGS = -fno-common -O2 -fno-exceptions -Wall
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
DEFS = DEFS =
CPPFLAGS = -DHAVE_GD_H -DHAVE_GDIMAGECREATEFROMGIF -DHAVE_GDIMAGEJPEG -DHAVE_JPEG_SET_QUALITY -DHAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8 -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -I/opt/local/include CPPFLAGS = -DHAVE_GD_H -DHAVE_GDIMAGECREATEFROMGIF -DHAVE_GDIMAGEJPEG -DHAVE_JPEG_SET_QUALITY -DHAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8 -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE $(DEFS) $(cppflags)
CXXFLAGS = $(CFLAGS) CXXFLAGS = $(CFLAGS) $(cxxflags)
ldflags = -L. -L/opt/local/lib ldflags = -L.
dldflags = dldflags =
archflag = -arch x86_64 archflag =
DLDFLAGS = $(ldflags) $(dldflags) $(archflag) DLDFLAGS = $(ldflags) $(dldflags) $(archflag)
LDSHARED = $(CC) -dynamic -bundle -undefined suppress -flat_namespace LDSHARED = $(CC) -dynamic -bundle -undefined suppress -flat_namespace
LDSHAREDXX = $(CXX) -dynamic -bundle -undefined suppress -flat_namespace
AR = ar AR = ar
EXEEXT = EXEEXT =
RUBY_BASE_NAME = ruby
RUBY_INSTALL_NAME = ruby RUBY_INSTALL_NAME = ruby
RUBY_SO_NAME = ruby RUBY_SO_NAME = ruby
arch = i686-darwin10 arch = i386-darwin10.4.0
sitearch = i686-darwin10 sitearch = $(arch)
ruby_version = 1.8 ruby_version = 1.9.1
ruby = /opt/local/bin/ruby ruby = /Users/ayi/.rvm/rubies/ruby-1.9.2-preview1/bin/ruby
RUBY = $(ruby) RUBY = $(ruby)
RM = rm -f RM = rm -f
RM_RF = $(RUBY) -run -e rm -- -rf
RMDIRS = $(RUBY) -run -e rmdir -- -p
MAKEDIRS = mkdir -p MAKEDIRS = mkdir -p
INSTALL = /usr/bin/install -c INSTALL = /usr/bin/install -c
INSTALL_PROG = $(INSTALL) -m 0755 INSTALL_PROG = $(INSTALL) -m 0755
@@ -75,18 +92,19 @@ COPY = cp
preload = preload =
libpath = . $(libdir) libpath = . $(libdir) /opt/local/lib
LIBPATH = -L. -L$(libdir) LIBPATH = -L. -L$(libdir) -L/opt/local/lib
DEFFILE = DEFFILE =
CLEANFILES = mkmf.log CLEANFILES = mkmf.log
DISTCLEANFILES = DISTCLEANFILES =
DISTCLEANDIRS =
extout = extout =
extout_prefix = extout_prefix =
target_prefix = target_prefix =
LOCAL_LIBS = LOCAL_LIBS =
LIBS = $(LIBRUBYARG_SHARED) -lpng -ljpeg -lgd -lpthread -ldl -lobjc LIBS = -lpng -ljpeg -lgd -lpthread -ldl -lobjc
SRCS = danbooru_image_resizer.cpp GIFReader.cpp JPEGReader.cpp PNGReader.cpp Resize.cpp RowBuffer.cpp SRCS = danbooru_image_resizer.cpp GIFReader.cpp JPEGReader.cpp PNGReader.cpp Resize.cpp RowBuffer.cpp
OBJS = danbooru_image_resizer.o GIFReader.o JPEGReader.o PNGReader.o Resize.o RowBuffer.o OBJS = danbooru_image_resizer.o GIFReader.o JPEGReader.o PNGReader.o Resize.o RowBuffer.o
TARGET = danbooru_image_resizer TARGET = danbooru_image_resizer
@@ -98,27 +116,38 @@ BINDIR = $(bindir)
RUBYCOMMONDIR = $(sitedir)$(target_prefix) RUBYCOMMONDIR = $(sitedir)$(target_prefix)
RUBYLIBDIR = $(sitelibdir)$(target_prefix) RUBYLIBDIR = $(sitelibdir)$(target_prefix)
RUBYARCHDIR = $(sitearchdir)$(target_prefix) RUBYARCHDIR = $(sitearchdir)$(target_prefix)
HDRDIR = $(rubyhdrdir)/ruby$(target_prefix)
ARCHHDRDIR = $(rubyhdrdir)/$(arch)/ruby$(target_prefix)
TARGET_SO = $(DLLIB) TARGET_SO = $(DLLIB)
CLEANLIBS = $(TARGET).bundle $(TARGET).il? $(TARGET).tds $(TARGET).map CLEANLIBS = $(TARGET).bundle
CLEANOBJS = *.o *.a *.s[ol] *.pdb *.exp *.bak CLEANOBJS = *.o *.bak
all: $(DLLIB) all: $(DLLIB)
static: $(STATIC_LIB) static: $(STATIC_LIB)
.PHONY: all install static install-so install-rb
.PHONY: clean clean-so clean-rb
clean: clean-rb-default::
clean-rb::
clean-so::
clean: clean-so clean-rb-default clean-rb
@-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) @-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES)
distclean: clean distclean-rb-default::
distclean-rb::
distclean-so::
distclean: clean distclean-so distclean-rb-default distclean-rb
@-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log @-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
@-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES) @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
@-$(RMDIRS) $(DISTCLEANDIRS)
realclean: distclean realclean: distclean
install: install-so install-rb install: install-so install-rb
install-so: $(RUBYARCHDIR) install-so: $(RUBYARCHDIR)
install-so: $(RUBYARCHDIR)/$(DLLIB) install-so: $(RUBYARCHDIR)/$(DLLIB)
$(RUBYARCHDIR)/$(DLLIB): $(DLLIB) $(RUBYARCHDIR)/$(DLLIB): $(RUBYARCHDIR) $(DLLIB)
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR) $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
install-rb: pre-install-rb install-rb-default install-rb: pre-install-rb install-rb-default
install-rb-default: pre-install-rb-default install-rb-default: pre-install-rb-default
@@ -134,24 +163,24 @@ site-install-rb: install-rb
.SUFFIXES: .c .m .cc .cxx .cpp .C .o .SUFFIXES: .c .m .cc .cxx .cpp .C .o
.cc.o: .cc.o:
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
.cxx.o: .cxx.o:
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
.cpp.o: .cpp.o:
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
.C.o: .C.o:
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
.c.o: .c.o:
$(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $< $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<
$(DLLIB): $(OBJS) Makefile $(DLLIB): $(OBJS) Makefile
@-$(RM) $@ @-$(RM) $(@)
$(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS) $(LDSHAREDXX) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
$(OBJS): ruby.h defines.h $(OBJS): $(hdrdir)/ruby.h $(hdrdir)/ruby/defines.h $(arch_hdrdir)/ruby/config.h

View File

@@ -1,144 +0,0 @@
#!/usr/bin/env ruby
require 'cgi'
module DText
def parse_inline(str, options = {})
str = str.gsub(/&/, "&amp;")
str.gsub!(/</, "&lt;")
str.gsub!(/>/, "&gt;")
str.gsub!(/\[\[.+?\]\]/m) do |tag|
tag = tag[2..-3]
if tag =~ /^(.+?)\|(.+)$/
tag = $1
name = $2
'<a href="/wiki/show?title=' + CGI.escape(CGI.unescapeHTML(tag.tr(" ", "_").downcase)) + '">' + name + '</a>'
else
'<a href="/wiki/show?title=' + CGI.escape(CGI.unescapeHTML(tag.tr(" ", "_").downcase)) + '">' + tag + '</a>'
end
end
str.gsub!(/\{\{.+?\}\}/m) do |tag|
tag = tag[2..-3]
'<a href="/post/index?tags=' + CGI.escape(CGI.unescapeHTML(tag)) + '">' + tag + '</a>'
end
str.gsub!(/[Pp]ost #(\d+)/, '<a href="/post/show/\1">post #\1</a>')
str.gsub!(/[Ff]orum #(\d+)/, '<a href="/forum/show/\1">forum #\1</a>')
str.gsub!(/[Cc]omment #(\d+)/, '<a href="/comment/show/\1">comment #\1</a>')
str.gsub!(/[Pp]ool #(\d+)/, '<a href="/pool/show/\1">pool #\1</a>')
str.gsub!(/\n/m, "<br>")
str.gsub!(/\[b\](.+?)\[\/b\]/, '<strong>\1</strong>')
str.gsub!(/\[i\](.+?)\[\/i\]/, '<em>\1</em>')
str.gsub!(/\[spoilers?\](.+?)\[\/spoilers?\]/m, '<span class="spoiler">\1</span>')
str.gsub!(/("[^"]+":(http:\/\/|\/)\S+|http:\/\/\S+)/m) do |link|
if link =~ /^"([^"]+)":(.+)$/
text = $1
link = $2
else
text = link
end
if link =~ /([;,.!?\)\]<>])$/
link.chop!
ch = $1
else
ch = ""
end
link.gsub!(/"/, '&quot;')
'<a href="' + link + '">' + text + '</a>' + ch
end
str
end
def parse_list(str, options = {})
html = ""
layout = []
nest = 0
str.split(/\n/).each do |line|
if line =~ /^\s*(\*+) (.+)/
nest = $1.size
content = parse_inline($2)
else
content = parse_inline(line)
end
if nest > layout.size
html += "<ul>"
layout << "ul"
end
while nest < layout.size
elist = layout.pop
if elist
html += "</#{elist}>"
end
end
html += "<li>#{content}</li>"
end
while layout.any?
elist = layout.pop
html += "</#{elist}>"
end
html
end
def parse(str, options = {})
return "" if str.blank?
# Make sure quote tags are surrounded by newlines
unless options[:inline]
str.gsub!(/\s*\[quote\]\s*/m, "\n\n[quote]\n\n")
str.gsub!(/\s*\[\/quote\]\s*/m, "\n\n[/quote]\n\n")
end
str.gsub!(/(?:\r?\n){3,}/, "\n\n")
str.strip!
blocks = str.split(/(?:\r?\n){2}/)
html = blocks.map do |block|
case block
when /^(h[1-6])\.\s*(.+)$/
tag = $1
content = $2
if options[:inline]
"<h6>" + parse_inline(content, options) + "</h6>"
else
"<#{tag}>" + parse_inline(content, options) + "</#{tag}>"
end
when /^\s*\*+ /
parse_list(block, options)
when "[quote]"
if options[:inline]
""
else
'<blockquote>'
end
when "[/quote]"
if options[:inline]
""
else
'</blockquote>'
end
else
'<p>' + parse_inline(block) + "</p>"
end
end
html.join("")
end
module_function :parse_inline
module_function :parse_list
module_function :parse
end

View File

@@ -1,79 +0,0 @@
// Cookie.setup();
$(document).ready(function() {
// $("#hide-upgrade-account-link").click(function() {
// $("#upgrade-account").hide();
// Cookie.put('hide-upgrade-account', '1', 7);
// });
// Comment listing
$(".comment-section form").hide();
$(".comment-section input.expand-comment-response").click(function() {
var post_id = $(this).closest(".comment-section").attr("data-post-id");
$(".comment-section[data-post-id=" + post_id + "] form").show();
$(this).hide();
})
});
var Danbooru = {};
// ContextMenu
Danbooru.ContextMenu = {};
Danbooru.ContextMenu.add_icon = function() {
$("menu[type=context] > li").append('<img src="/images/arrow2_s.png">');
}
Danbooru.ContextMenu.toggle_icon = function(li) {
if (li == null) {
$("menu[type=context] > li > img").attr("src", "/images/arrow2_s.png");
} else {
$(li).find("img").attr("src", function() {
if (this.src.match(/_n/)) {
return "/images/arrow2_s.png";
} else {
return "/images/arrow2_n.png";
}
});
}
}
Danbooru.ContextMenu.setup = function() {
$("menu[type=context] li").hover(
function() {$(this).css({"background-color": "#F6F6F6"})},
function() {$(this).css({"background-color": "#EEE"})}
);
this.add_icon();
$("menu[type=context] > li").click(function(e) {
$(this).parent().find("ul").toggle();
e.stopPropagation();
Danbooru.ContextMenu.toggle_icon(this);
});
$(document).click(function() {
$("menu[type=context] > ul").hide();
Danbooru.ContextMenu.toggle_icon();
});
$("menu[type=context] > ul > li").click(function(element) {
$(this).closest("ul").toggle();
var text = $(this).text()
var menu = $(this).closest("menu");
menu.children("li").text(text);
if (menu.attr("data-update-field-id")) {
$("#" + menu.attr("data-update-field-id")).val(text);
Danbooru.ContextMenu.add_icon();
}
if (menu.attr("data-submit-on-change") == "true") {
menu.closest("form").submit();
}
});
}
$(document).ready(function() {
Danbooru.ContextMenu.setup();
});

View File

@@ -1,129 +0,0 @@
// from http://github.com/rails/jquery-ujs/raw/master/src/rails.js
jQuery(function ($) {
var csrf_token = $('meta[name=csrf-token]').attr('content'),
csrf_param = $('meta[name=csrf-param]').attr('content');
$.fn.extend({
/**
* Triggers a custom event on an element and returns the event result
* this is used to get around not being able to ensure callbacks are placed
* at the end of the chain.
*
* TODO: deprecate with jQuery 1.4.2 release, in favor of subscribing to our
* own events and placing ourselves at the end of the chain.
*/
triggerAndReturn: function (name, data) {
var event = new $.Event(name);
this.trigger(event, data);
return event.result !== false;
},
/**
* Handles execution of remote calls firing overridable events along the way
*/
callRemote: function () {
var el = this,
data = el.is('form') ? el.serializeArray() : [],
method = el.attr('method') || el.attr('data-method') || 'GET',
url = el.attr('action') || el.attr('href');
if (url === undefined) {
throw "No URL specified for remote call (action or href must be present).";
} else {
if (el.triggerAndReturn('ajax:before')) {
$.ajax({
url: url,
data: data,
dataType: 'script',
type: method.toUpperCase(),
beforeSend: function (xhr) {
el.trigger('ajax:loading', xhr);
},
success: function (data, status, xhr) {
el.trigger('ajax:success', [data, status, xhr]);
},
complete: function (xhr) {
el.trigger('ajax:complete', xhr);
},
error: function (xhr, status, error) {
el.trigger('ajax:failure', [xhr, status, error]);
}
});
}
el.trigger('ajax:after');
}
}
});
/**
* confirmation handler
*/
$('a[data-confirm],input[data-confirm]').live('click', function () {
var el = $(this);
if (el.triggerAndReturn('confirm')) {
if (!confirm(el.attr('data-confirm'))) {
return false;
}
}
});
/**
* remote handlers
*/
$('form[data-remote]').live('submit', function (e) {
$(this).callRemote();
e.preventDefault();
});
$('a[data-remote],input[data-remote]').live('click', function (e) {
$(this).callRemote();
e.preventDefault();
});
$('a[data-method]:not([data-remote])').live('click', function (e){
var link = $(this),
href = link.attr('href'),
method = link.attr('data-method'),
form = $('<form method="post" action="'+href+'">'),
metadata_input = '<input name="_method" value="'+method+'" type="hidden" />';
if (csrf_param != null && csrf_token != null) {
metadata_input += '<input name="'+csrf_param+'" value="'+csrf_token+'" type="hidden" />';
}
form.hide()
.append(metadata_input)
.appendTo('body');
e.preventDefault();
form.submit();
});
/**
* disable-with handlers
*/
var disable_with_input_selector = 'input[data-disable-with]';
var disable_with_form_selector = 'form[data-remote]:has(' + disable_with_input_selector + ')';
$(disable_with_form_selector).live('ajax:before', function () {
$(this).find(disable_with_input_selector).each(function () {
var input = $(this);
input.data('enable-with', input.val())
.attr('value', input.attr('data-disable-with'))
.attr('disabled', 'disabled');
});
});
$(disable_with_form_selector).live('ajax:after', function () {
$(this).find(disable_with_input_selector).each(function () {
var input = $(this);
input.removeAttr('disabled')
.val(input.data('enable-with'));
});
});
});

View File

@@ -227,10 +227,14 @@ aside.sidebar > section > h1 {
font-size: 1.5em; font-size: 1.5em;
} }
aside.sidebar > section > ul > li { aside.sidebar > section > ul li {
list-style-type: none; list-style-type: none;
} }
aside.sidebar > section > ul ul li {
margin-left: 1em;
}
/*** Comments ***/ /*** Comments ***/
div.comment-response { div.comment-response {

0
script/compile_javascripts Normal file → Executable file
View File

View File

@@ -1,10 +1,6 @@
#!/usr/bin/env ruby1.9 #!/usr/bin/env ruby
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
ENV_PATH = File.expand_path('../../config/environment', __FILE__) APP_PATH = File.expand_path('../../config/application', __FILE__)
BOOT_PATH = File.expand_path('../../config/boot', __FILE__) require File.expand_path('../../config/boot', __FILE__)
APP_PATH = File.expand_path('../../config/application', __FILE__)
ROOT_PATH = File.expand_path('../..', __FILE__)
require BOOT_PATH
require 'rails/commands' require 'rails/commands'

View File

@@ -2,8 +2,6 @@ Factory.define(:post) do |f|
f.md5 {|x| Time.now.to_f.to_s} f.md5 {|x| Time.now.to_f.to_s}
f.uploader {|x| x.association(:user)} f.uploader {|x| x.association(:user)}
f.uploader_ip_addr "127.0.0.1" f.uploader_ip_addr "127.0.0.1"
f.updater_id {|x| x.uploader_id}
f.updater_ip_addr "127.0.0.1"
f.tag_string "tag1 tag2" f.tag_string "tag1 tag2"
f.tag_count 2 f.tag_count 2
f.tag_count_general 2 f.tag_count_general 2

View File

@@ -1,9 +1,10 @@
ENV["RAILS_ENV"] = "test" ENV["RAILS_ENV"] = "test"
require 'factory_girl' require 'factory_girl'
require 'shoulda' require 'shoulda'
require 'mocha' require 'mocha'
require 'faker' require 'faker'
require File.expand_path(File.dirname(__FILE__) + "/../config/environment") require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help' require 'rails/test_help'
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}

View File

@@ -1,6 +1,142 @@
require File.dirname(__FILE__) + '/../test_helper' require_relative '../test_helper'
class PostTest < ActiveSupport::TestCase class PostTest < ActiveSupport::TestCase
setup do
user = Factory.create(:user)
CurrentUser.user = user
CurrentUser.ip_addr = "127.0.0.1"
end
teardown do
CurrentUser.user = nil
CurrentUser.ip_addr = nil
end
context "Removal:" do
context "Removing a post" do
should "duplicate the post in the archive table and remove it from the base table" do
post = Factory.create(:post)
assert_difference("RemovedPost.count", 1) do
assert_difference("Post.count", -1) do
post.remove!
end
end
removed_post = RemovedPost.last
assert_equal(post.tag_string, removed_post.tag_string)
end
should "decrement the tag counts" do
post = Factory.create(:post, :tag_string => "aaa")
assert_equal(1, Tag.find_by_name("aaa").post_count)
post.remove!
assert_equal(0, Tag.find_by_name("aaa").post_count)
end
end
end
context "Parenting:" do
context "Assignining a parent to a post" do
should "update the has_children flag on the parent" do
p1 = Factory.create(:post)
assert(!p1.has_children?, "Parent should not have any children")
c1 = Factory.create(:post, :parent_id => p1.id)
p1.reload
assert(p1.has_children?, "Parent not updated after child was added")
end
should "update the has_children flag on the old parent" do
p1 = Factory.create(:post)
p2 = Factory.create(:post)
c1 = Factory.create(:post, :parent_id => p1.id)
c1.parent_id = p2.id
c1.save
p1.reload
p2.reload
assert(!p1.has_children?, "Old parent should not have a child")
assert(p2.has_children?, "New parent should have a child")
end
should "validate that the parent exists" do
post = Factory.build(:post, :parent_id => 1_000_000)
post.save
assert(post.errors[:parent].any?, "Parent should be invalid")
end
should "fail if the parent has a parent" do
p1 = Factory.create(:post)
c1 = Factory.create(:post, :parent_id => p1.id)
c2 = Factory.build(:post, :parent_id => c1.id)
c2.save
assert(c2.errors[:parent].any?, "Parent should be invalid")
end
end
context "Destroying a post with a parent" do
should "reassign favorites to the parent" do
p1 = Factory.create(:post)
c1 = Factory.create(:post, :parent_id => p1.id)
user = Factory.create(:user)
c1.add_favorite(user)
c1.remove!
p1.reload
assert(!Favorite.exists?(:post_id => c1.id, :user_id => user.id))
assert(Favorite.exists?(:post_id => p1.id, :user_id => user.id))
end
should "update the parent's has_children flag" do
p1 = Factory.create(:post)
c1 = Factory.create(:post, :parent_id => p1.id)
c1.remove!
p1.reload
assert(!p1.has_children?, "Parent should not have children")
end
end
context "Destroying a post with" do
context "one child" do
should "remove the parent of that child" do
p1 = Factory.create(:post)
c1 = Factory.create(:post, :parent_id => p1.id)
p1.remove!
c1.reload
assert_nil(c1.parent)
end
end
context "two or more children" do
should "reparent all children to the first child" do
p1 = Factory.create(:post)
c1 = Factory.create(:post, :parent_id => p1.id)
c2 = Factory.create(:post, :parent_id => p1.id)
c3 = Factory.create(:post, :parent_id => p1.id)
p1.remove!
c1.reload
c2.reload
c3.reload
assert_nil(c1.parent)
assert_equal(c1.id, c2.parent_id)
assert_equal(c1.id, c3.parent_id)
end
end
end
context "Undestroying a post with a parent" do
should "not preserve the parent's has_children flag" do
p1 = Factory.create(:post)
c1 = Factory.create(:post, :parent_id => p1.id)
c1.remove!
c1 = RemovedPost.last
c1.unremove!
c1 = Post.last
p1.reload
assert_nil(p1.parent_id)
assert(!p1.has_children?, "Parent should not have children")
end
end
end
context "During moderation a post" do context "During moderation a post" do
setup do setup do
@post = Factory.create(:post) @post = Factory.create(:post)
@@ -8,29 +144,29 @@ class PostTest < ActiveSupport::TestCase
end end
should "be unapproved once and only once" do should "be unapproved once and only once" do
@post.unapprove!("bad", @user, "127.0.0.1") @post.unapprove!("bad", @user.id, "127.0.0.1")
assert(@post.is_flagged?, "Post should be flagged.") assert(@post.is_flagged?, "Post should be flagged.")
assert_not_nil(@post.unapproval, "Post should have an unapproval record.") assert_not_nil(@post.unapproval, "Post should have an unapproval record.")
assert_equal("bad", @post.unapproval.reason) assert_equal("bad", @post.unapproval.reason)
assert_raise(Unapproval::Error) {@post.unapprove!("bad", @user, "127.0.0.1")} assert_raise(Unapproval::Error) {@post.unapprove!("bad", @user.id, "127.0.0.1")}
end end
should "not unapprove if no reason is given" do should "not unapprove if no reason is given" do
assert_raise(Unapproval::Error) {@post.unapprove!("", @user, "127.0.0.1")} assert_raise(Unapproval::Error) {@post.unapprove!("", @user.id, "127.0.0.1")}
end end
should "be deleted" do should "be destroyed" do
@post.delete! @post.destroy(1, "127.0.0.1")
assert(@post.is_deleted?, "Post should be deleted.") assert(@post.is_deleted?, "Post should be deleted.")
end end
should "be approved" do should "be approved" do
@post.approve! @post.approve!(1, "127.0.0.1")
assert(!@post.is_pending?, "Post should not be pending.") assert(!@post.is_pending?, "Post should not be pending.")
@deleted_post = Factory.create(:post, :is_deleted => true) @deleted_post = Factory.create(:post, :is_deleted => true)
@deleted_post.approve! @deleted_post.approve!(1, "127.0.0.1")
assert(!@post.is_deleted?, "Post should not be deleted.") assert(!@post.is_deleted?, "Post should not be deleted.")
end end
end end