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"
end
gem "rails", "3.0.0.beta3"
gem "rails", "3.0.0.rc"
gem "pg"
gem "memcache-client", :require => "memcache"
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.
require File.expand_path('../config/application', __FILE__)
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
protect_from_forgery
before_filter :set_current_user
after_filter :reset_current_user
before_filter :initialize_cookies
layout "default"
@ -27,24 +28,29 @@ protected
def set_current_user
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
if @current_user
if @current_user.is_banned? && @current_user.ban && @current_user.ban.expires_at < Time.now
@current_user.update_attribute(:is_banned, false)
Ban.destroy_all("user_id = #{@current_user.id}")
if CurrentUser.user
if CurrentUser.user.is_banned? && CurrentUser.user.ban && CurrentUser.user.ban.expires_at < Time.now
CurrentUser.user.unban!
end
else
@current_user = AnonymousUser.new
CurrentUser.user = AnonymousUser.new
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
%w(member banned privileged contributor janitor moderator admin).each do |level|
define_method("#{level}_only") do
if @current_user.__send__("is_#{level}?")
if CurrentUser.user.__send__("is_#{level}?")
true
else
access_denied()
@ -53,10 +59,10 @@ protected
end
def initialize_cookies
if @current_user.is_anonymous?
if CurrentUser.user.is_anonymous?
cookies["blacklisted_tags"] = ""
else
cookies["blacklisted_tags"] = @current_user.blacklisted_tags
cookies["blacklisted_tags"] = CurrentUser.user.blacklisted_tags
end
end
end

View File

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

View File

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

View File

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

View File

@ -1,27 +1,16 @@
module PostsHelper
def image_dimensions(post, current_user)
if post.is_image?
"(#{post.image_width_for(current_user)}x#{post.image_height_for(current_user)})"
def resize_image_links(post, user)
links = []
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
""
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

View File

@ -1,23 +1,60 @@
class Favorite
def self.table_name_for(user)
"favorites_#{user.id % 10}"
attr_accessor :attributes, :errors
def self.table_name_for(user_id)
"favorites_#{user_id.to_i % 10}"
end
def self.create(user, post)
ActiveRecord::Base.connection.execute("INSERT INTO #{table_name_for(user)} (user_id, post_id) VALUES (#{user.id}, #{post.id})")
def self.create(attributes)
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
def self.destroy(user, post)
ActiveRecord::Base.connection.execute("DELETE FROM #{table_name_for(user)} WHERE user_id = #{user.id} AND post_id = #{post.id}")
def self.destroy(conditions)
if conditions[:user_id] && conditions[:post_id]
destroy_for_post_and_user(conditions[:post_id], conditions[:user_id])
elsif conditions[:user_id]
destroy_for_user(conditions[:user_id])
elsif conditions[:post_id]
destroy_for_post(conditions[:post_id])
end
end
def self.exists?(conditions)
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_all_for_post(post)
def self.destroy_for_post(post)
0.upto(9) do |i|
ActiveRecord::Base.connection.execute("DELETE FROM favorites_#{i} WHERE post_id = #{post.id}")
execute_sql("DELETE FROM favorites_#{i} WHERE post_id = #{post.id}")
end
end
def self.destroy_all_for_user(user)
ActiveRecord::Base.connection.execute("DELETE FROM #{table_name_for(user)} WHERE user_id = #{user.id}")
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

View File

@ -3,7 +3,7 @@ class Artist < ActiveRecord::Base
before_create :initialize_creator
before_save :normalize_name
after_save :create_version
after_save :commit_url_string
after_save :save_url_string
validates_uniqueness_of :name
validates_presence_of :updater_id, :updater_ip_addr
belongs_to :updater, :class_name => "User"
@ -36,7 +36,7 @@ class Artist < ActiveRecord::Base
m.extend(ClassMethods)
end
def commit_url_string
def save_url_string
if @url_string
artist_urls.clear
@ -82,7 +82,7 @@ class Artist < ActiveRecord::Base
module UpdaterMethods
def updater_name
User.find_name(updater_id).tr("_", " ")
User.id_to_name(updater_id).tr("_", " ")
end
end
@ -150,12 +150,35 @@ class Artist < ActiveRecord::Base
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 NameMethods
include GroupMethods
include UpdaterMethods
extend SearchMethods
include VersionMethods
extend FactoryMethods
def initialize_creator
if creator.nil?

View File

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

View File

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

View File

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

View File

@ -120,7 +120,7 @@ class Job < ActiveRecord::Base
when "mass_tag_edit"
start = data["start_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}"
when "approve_tag_alias"

View File

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

View File

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

View File

@ -6,9 +6,14 @@ class Pool < ActiveRecord::Base
belongs_to :creator, :class_name => "User"
belongs_to :updater, :class_name => "User"
has_many :versions, :class_name => "PoolVersion", :dependent => :destroy
before_save :normalize_name
after_save :create_version
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)
Pool.new do |pool|
pool.name = "TEMP:#{Time.now.to_f}.#{rand(1_000_000)}"
@ -21,6 +26,10 @@ class Pool < ActiveRecord::Base
end
end
def normalize_name
self.name = name.downcase
end
def revert_to!(version)
self.post_ids = version.post_ids
save

View File

@ -1,9 +1,8 @@
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_favorites
after_destroy :update_tag_post_counts
after_save :create_version
after_save :update_parent_on_save
before_save :merge_old_tags
before_save :normalize_tags
before_save :create_tags
@ -11,6 +10,7 @@ class Post < ActiveRecord::Base
before_save :set_tag_counts
belongs_to :updater, :class_name => "User"
belongs_to :approver, :class_name => "User"
belongs_to :parent, :class_name => "Post"
has_one :unapproval, :dependent => :destroy
has_one :upload, :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 :notes, :dependent => :destroy
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
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
def delete_files
@ -179,13 +181,13 @@ class Post < ActiveRecord::Base
end
end
module ModerationMethods
def unapprove!(reason, current_user, current_ip_addr)
module ApprovalMethods
def unapprove!(reason)
raise Unapproval::Error.new("You can't unapprove a post more than once") if is_flagged?
unapproval = create_unapproval(
:unapprover_id => current_user.id,
:unapprover_ip_addr => current_ip_addr,
:unapprover_id => CurrentUser.user.id,
:unapprover_ip_addr => CurrentUser.ip_addr,
:reason => reason
)
@ -193,15 +195,13 @@ class Post < ActiveRecord::Base
raise Unapproval::Error.new(unapproval.errors.full_messages.join("; "))
end
update_attribute(:is_flagged, true)
toggle!(:is_flagged)
end
def delete!
update_attribute(:is_deleted, true)
end
def approve!
update_attributes(:is_deleted => false, :is_pending => false)
update_attributes(
:is_pending => false
)
end
end
@ -226,19 +226,17 @@ class Post < ActiveRecord::Base
:source => source,
:rating => rating,
:tag_string => tag_string,
:updater_id => updater_id,
:updater_ip_addr => updater_ip_addr
:updater_id => CurrentUser.user.id,
:updater_ip_addr => CurrentUser.ip_addr
)
raise PostVersion::Error.new(version.errors.full_messages.join("; ")) if version.errors.any?
end
def revert_to!(version, reverter_id, reverter_ip_addr)
def revert_to!(version)
self.source = version.source
self.rating = version.rating
self.tag_string = version.tag_string
self.updater_id = reverter_id
self.updater_ip_addr = reverter_ip_addr
save!
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(" "))
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
decrement_tags = tag_array_was - tag_array
increment_tags = tag_array - tag_array_was
@ -333,19 +339,44 @@ class Post < ActiveRecord::Base
module FavoriteMethods
def delete_favorites
Favorite.destroy_all_for_post(self)
Favorite.destroy_for_post(self)
end
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!
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
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!
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
@ -435,7 +466,11 @@ class Post < ActiveRecord::Base
q = Tag.parse_query(q)
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[:mpixels], "posts.width * posts.height / 1000000.0", relation)
@ -458,14 +493,10 @@ class Post < ActiveRecord::Base
relation = relation.where(["posts.md5 IN (?)", q[:md5]])
end
if q[:status] == "deleted"
relation = relation.where("posts.is_deleted = TRUE")
elsif q[:status] == "pending"
if q[:status] == "pending"
relation = relation.where("posts.is_pending = TRUE")
elsif q[:status] == "flagged"
relation = relation.where("posts.is_flagged = TRUE")
else
relation = relation.where("posts.is_deleted = FALSE")
end
if q[:source].is_a?(String)
@ -477,7 +508,7 @@ class Post < ActiveRecord::Base
end
relation = add_tag_string_search_relation(q[:tags], relation)
if q[:rating] == "q"
relation = relation.where("posts.rating = 'q'")
elsif q[:rating] == "s"
@ -553,31 +584,31 @@ class Post < ActiveRecord::Base
end
def uploader_id
uploader.id
end
def uploader_name
uploader_string[9..-1]
end
def uploader_name
User.id_to_name(uploader_id)
end
def uploader
User.find_by_name(uploader_name)
User.find(uploader_id)
end
def uploader=(user)
self.uploader_string = "uploader:#{user.name}"
self.uploader_string = "uploader:#{user.id}"
end
end
module PoolMethods
def add_pool(pool)
self.pool_string += " pool:#{pool.name}"
self.pool_string += " pool:#{pool.id}"
self.pool_string.strip!
pool.add_post!(self)
end
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!
pool.remove_post!(self)
end
@ -631,9 +662,106 @@ class Post < ActiveRecord::Base
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 ImageMethods
include ModerationMethods
include ApprovalMethods
include PresenterMethods
include VersionMethods
include TagMethods
@ -644,6 +772,8 @@ class Post < ActiveRecord::Base
include VoteMethods
extend CountMethods
include CacheMethods
include ParentMethods
include RemovalMethods
def reload(options = nil)
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/
case $1
when "-uploader"
q[:tags][:exclude] << token[1..-1]
q[:tags][:exclude] << "uploader:#{User.name_to_id(token[1..-1])}"
when "uploader"
q[:tags][:related] << token
q[:tags][:related] << "uploader:#{User.name_to_id(token)}"
when "-pool"
q[:tags][:exclude] << token[1..-1]
q[:tags][:exclude] << "pool:#{Pool.name_to_id(token[1..-1])}"
when "pool"
q[:tags][:related] << token
q[:tags][:related] << "pool:#{Pool.name_to_id(token)}"
when "-fav"
q[:tags][:exclude] << token[1..-1]
q[:tags][:exclude] << "fav:#{User.name_to_id(token[1..-1])}"
when "fav"
q[:tags][:related] << token
q[:tags][:related] << "fav:#{User.name_to_id(token)}"
when "sub"
q[:subscriptions] << $2

View File

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

View File

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

View File

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

View File

@ -10,7 +10,8 @@
<% end %>
<%= auto_discovery_link_tag :atom, posts_path(:format => "atom", :tags => params[:tags]) %>
<%= 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 "application" %>
<%= 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} %>
<%= 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>
<% end %>
<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><%= link_to "Tag History", post_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>
<% if !post.is_deleted? && post.is_image? && post.image_width && post.image_width > 700 %>
<li><%= link_to "Resize", "#" %></li>
<% end %>
<%= resize_image_links(post, @current_user) %>
<li><%= link_to "Favorite", "#" %></li>
<li><%= link_to "Unfavorite", "#" %></li>
<li><%= link_to "Translate", "#" %></li>

View File

@ -1,33 +1,15 @@
require File.expand_path('../boot', __FILE__)
require 'rails/all'
# Auto-require default libraries and those for the current Rails environment.
Bundler.require :default, Rails.env
Bundler.require(:default, Rails.env) if defined?(Bundler)
module Danbooru
class Application < Rails::Application
# Add additional load paths for your own custom dirs
config.load_paths += %W( #{config.root}/presenters #{config.root}/logical )
# Only load the plugins named here, in the order given (default is alphabetical).
# :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.autoload_paths += %W(#{config.root}/app/presenters #{config.root}/app/logical)
config.plugins = [:all]
config.time_zone = 'Eastern Time (US & Canada)'
config.encoding = "utf-8"
config.active_record.schema_format = :sql
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters << :password
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
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
require File.expand_path('../../.bundle/environment', __FILE__)
rescue LoadError
require 'rubygems'
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
Bundler.setup
# To use 2.x style vendor/rails and RubyGems
#
# vendor_rails = File.expand_path('../../vendor/rails', __FILE__)
# if File.exist?(vendor_rails)
# Dir["#{vendor_rails}/*/lib"].each { |path| $:.unshift(path) }
# end
#
# require 'rubygems'
end
rescue Bundler::GemNotFound => e
STDERR.puts e.message
STDERR.puts "Try running `bundle install`."
exit!
end if File.exist?(gemfile)

View File

@ -16,4 +16,7 @@ Danbooru::Application.configure do
# Don't care if the mailer can't send
config.action_mailer.raise_delivery_errors = false
# Print deprecation notices to the Rails logger
config.active_support.deprecation = :log
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
# The production environment is meant for finished, "live" apps.
@ -9,6 +9,15 @@ Danboorus::Application.configure do
config.consider_all_requests_local = false
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)
# config.log_level = :debug
@ -30,4 +39,11 @@ Danboorus::Application.configure do
# Enable threaded mode
# 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

View File

@ -14,6 +14,9 @@ Danbooru::Application.configure do
config.consider_all_requests_local = true
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
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,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
# Print deprecation notices to the stderr
config.active_support.deprecation = :stderr
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 << self
public :sanitize_sql_array
end
%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
class NilClass
def id
raise NoMethodError
end
include Danbooru::Extensions::ActiveRecord
end
class 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
include Danbooru::Extensions::String
end

View File

@ -1,3 +1,10 @@
ActiveSupport::Inflector.inflections do |inflect|
inflect.uncountable %w( user_feedback )
end
# Be sure to restart your server when you modify this file.
# 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.
# Your secret key for verifying cookie session data integrity.
# 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'
}
Danbooru::Application.config.session_store :cookie_store, :key => '_config_session'
# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
# (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
resources :users
end

View File

@ -13,7 +13,6 @@ class CreatePosts < ActiveRecord::Migration
t.column :is_rating_locked, :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_deleted, :boolean, :null => false, :default => false
# Uploader
t.column :uploader_string, :string, :null => false
@ -47,6 +46,10 @@ class CreatePosts < ActiveRecord::Migration
t.column :file_size, :integer, :null => false
t.column :image_width, :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
add_index :posts, :md5, :unique => true
@ -58,6 +61,7 @@ class CreatePosts < ActiveRecord::Migration
add_index :posts, :image_height
add_index :posts, :source
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))"

View File

@ -8,6 +8,7 @@ class CreateFavorites < ActiveRecord::Migration
add_index "favorites_#{number}", :post_id
add_index "favorites_#{number}", :user_id
add_index "favorites_#{number}", [:post_id, :user_id], :unique => true
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. ####
srcdir = .
topdir = /opt/local/lib/ruby/1.8/i686-darwin10
hdrdir = $(topdir)
VPATH = $(srcdir):$(topdir):$(hdrdir)
topdir = /Users/ayi/.rvm/rubies/ruby-1.9.2-preview1/include/ruby-1.9.1
hdrdir = /Users/ayi/.rvm/rubies/ruby-1.9.2-preview1/include/ruby-1.9.1
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)
prefix = $(DESTDIR)/opt/local
sharedstatedir = $(prefix)/com
mandir = $(DESTDIR)/opt/local/share/man
psdir = $(docdir)
oldincludedir = $(DESTDIR)/usr/include
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
sitehdrdir = $(rubyhdrdir)/site_ruby
rubyhdrdir = $(includedir)/$(RUBY_BASE_NAME)-$(ruby_version)
vendordir = $(rubylibprefix)/vendor_ruby
sitedir = $(rubylibprefix)/site_ruby
ridir = $(datarootdir)/$(RI_BASE_NAME)
mandir = $(datarootdir)/man
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
sbindir = $(exec_prefix)/sbin
rubylibdir = $(libdir)/ruby/$(ruby_version)
docdir = $(datarootdir)/doc/$(PACKAGE)
dvidir = $(docdir)
vendordir = $(DESTDIR)/opt/local/lib/ruby/vendor_ruby
datarootdir = $(prefix)/share
psdir = $(docdir)
pdfdir = $(docdir)
archdir = $(rubylibdir)/$(arch)
sitearchdir = $(sitelibdir)/$(sitearch)
datadir = $(datarootdir)
dvidir = $(docdir)
htmldir = $(docdir)
infodir = $(datarootdir)/info
docdir = $(datarootdir)/doc/$(PACKAGE)
oldincludedir = $(DESTDIR)/usr/include
includedir = $(prefix)/include
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)
sitearchdir = $(sitelibdir)/$(sitearch)
vendorlibdir = $(vendordir)/$(ruby_version)
vendorarchdir = $(vendorlibdir)/$(sitearch)
CC = g++
LIBRUBY = $(LIBRUBY_SO)
CXX = g++
LIBRUBY = $(LIBRUBY_A)
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
OUTFLAG = -o
COUTFLAG = -o
RUBY_EXTCONF_H =
CFLAGS = -fno-common -O2 -fno-exceptions -Wall -arch x86_64
INCFLAGS = -I. -I. -I/opt/local/lib/ruby/1.8/i686-darwin10 -I.
cflags = $(optflags) $(debugflags) $(warnflags)
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 =
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
CXXFLAGS = $(CFLAGS)
ldflags = -L. -L/opt/local/lib
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)
ldflags = -L.
dldflags =
archflag = -arch x86_64
archflag =
DLDFLAGS = $(ldflags) $(dldflags) $(archflag)
LDSHARED = $(CC) -dynamic -bundle -undefined suppress -flat_namespace
LDSHAREDXX = $(CXX) -dynamic -bundle -undefined suppress -flat_namespace
AR = ar
EXEEXT =
RUBY_BASE_NAME = ruby
RUBY_INSTALL_NAME = ruby
RUBY_SO_NAME = ruby
arch = i686-darwin10
sitearch = i686-darwin10
ruby_version = 1.8
ruby = /opt/local/bin/ruby
arch = i386-darwin10.4.0
sitearch = $(arch)
ruby_version = 1.9.1
ruby = /Users/ayi/.rvm/rubies/ruby-1.9.2-preview1/bin/ruby
RUBY = $(ruby)
RM = rm -f
RM_RF = $(RUBY) -run -e rm -- -rf
RMDIRS = $(RUBY) -run -e rmdir -- -p
MAKEDIRS = mkdir -p
INSTALL = /usr/bin/install -c
INSTALL_PROG = $(INSTALL) -m 0755
@ -75,18 +92,19 @@ COPY = cp
preload =
libpath = . $(libdir)
LIBPATH = -L. -L$(libdir)
libpath = . $(libdir) /opt/local/lib
LIBPATH = -L. -L$(libdir) -L/opt/local/lib
DEFFILE =
CLEANFILES = mkmf.log
DISTCLEANFILES =
DISTCLEANDIRS =
extout =
extout_prefix =
target_prefix =
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
OBJS = danbooru_image_resizer.o GIFReader.o JPEGReader.o PNGReader.o Resize.o RowBuffer.o
TARGET = danbooru_image_resizer
@ -98,27 +116,38 @@ BINDIR = $(bindir)
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
HDRDIR = $(rubyhdrdir)/ruby$(target_prefix)
ARCHHDRDIR = $(rubyhdrdir)/$(arch)/ruby$(target_prefix)
TARGET_SO = $(DLLIB)
CLEANLIBS = $(TARGET).bundle $(TARGET).il? $(TARGET).tds $(TARGET).map
CLEANOBJS = *.o *.a *.s[ol] *.pdb *.exp *.bak
CLEANLIBS = $(TARGET).bundle
CLEANOBJS = *.o *.bak
all: $(DLLIB)
static: $(STATIC_LIB)
all: $(DLLIB)
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)
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) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
@-$(RMDIRS) $(DISTCLEANDIRS)
realclean: distclean
realclean: distclean
install: install-so install-rb
install-so: $(RUBYARCHDIR)
install-so: $(RUBYARCHDIR)/$(DLLIB)
$(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
$(RUBYARCHDIR)/$(DLLIB): $(RUBYARCHDIR) $(DLLIB)
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
install-rb: pre-install-rb 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
.cc.o:
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
.cxx.o:
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
.cpp.o:
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
.C.o:
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
.c.o:
$(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $<
$(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<
$(DLLIB): $(OBJS) Makefile
@-$(RM) $@
$(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
@-$(RM) $(@)
$(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;
}
aside.sidebar > section > ul > li {
aside.sidebar > section > ul li {
list-style-type: none;
}
aside.sidebar > section > ul ul li {
margin-left: 1em;
}
/*** Comments ***/
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.
ENV_PATH = File.expand_path('../../config/environment', __FILE__)
BOOT_PATH = File.expand_path('../../config/boot', __FILE__)
APP_PATH = File.expand_path('../../config/application', __FILE__)
ROOT_PATH = File.expand_path('../..', __FILE__)
require BOOT_PATH
APP_PATH = File.expand_path('../../config/application', __FILE__)
require File.expand_path('../../config/boot', __FILE__)
require 'rails/commands'

View File

@ -2,8 +2,6 @@ Factory.define(:post) do |f|
f.md5 {|x| Time.now.to_f.to_s}
f.uploader {|x| x.association(:user)}
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_count 2
f.tag_count_general 2

View File

@ -1,9 +1,10 @@
ENV["RAILS_ENV"] = "test"
require 'factory_girl'
require 'shoulda'
require 'mocha'
require 'faker'
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
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
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
setup do
@post = Factory.create(:post)
@ -8,29 +144,29 @@ class PostTest < ActiveSupport::TestCase
end
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_not_nil(@post.unapproval, "Post should have an unapproval record.")
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
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
should "be deleted" do
@post.delete!
should "be destroyed" do
@post.destroy(1, "127.0.0.1")
assert(@post.is_deleted?, "Post should be deleted.")
end
should "be approved" do
@post.approve!
@post.approve!(1, "127.0.0.1")
assert(!@post.is_pending?, "Post should not be pending.")
@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.")
end
end