Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Robin Liao
2011-08-13 00:50:50 -07:00
72 changed files with 876 additions and 889 deletions

View File

@@ -8,7 +8,11 @@ group :test do
gem "simplecov", :require => false
end
gem "rails", "3.1.0.rc1"
group :assets do
end
gem "rails", "3.1.0.rc5"
gem "pg"
gem "memcache-client", :require => "memcache"
gem "imagesize", :require => "image_size"
@@ -20,3 +24,4 @@ gem "mechanize"
gem "nokogiri"
gem "meta_search", :git => "git://github.com/ernie/meta_search.git"
gem "silent-postgres"
gem "whenever", :require => false

View File

@@ -16,37 +16,37 @@ GIT
GEM
remote: http://gemcutter.org/
specs:
actionmailer (3.1.0.rc1)
actionpack (= 3.1.0.rc1)
aaronh-chronic (0.3.9)
actionmailer (3.1.0.rc5)
actionpack (= 3.1.0.rc5)
mail (~> 2.3.0)
actionpack (3.1.0.rc1)
activemodel (= 3.1.0.rc1)
activesupport (= 3.1.0.rc1)
actionpack (3.1.0.rc5)
activemodel (= 3.1.0.rc5)
activesupport (= 3.1.0.rc5)
builder (~> 3.0.0)
erubis (~> 2.7.0)
i18n (~> 0.6.0beta1)
rack (~> 1.3.0.beta2)
rack-cache (~> 1.0.1)
i18n (~> 0.6)
rack (~> 1.3.1)
rack-cache (~> 1.0.2)
rack-mount (~> 0.8.1)
rack-test (~> 0.6.0)
sprockets (~> 2.0.0.beta.5)
tzinfo (~> 0.3.27)
activemodel (3.1.0.rc1)
activesupport (= 3.1.0.rc1)
sprockets (~> 2.0.0.beta.12)
activemodel (3.1.0.rc5)
activesupport (= 3.1.0.rc5)
bcrypt-ruby (~> 2.1.4)
builder (~> 3.0.0)
i18n (~> 0.6.0beta1)
activerecord (3.1.0.rc1)
activemodel (= 3.1.0.rc1)
activesupport (= 3.1.0.rc1)
arel (~> 2.1.1)
tzinfo (~> 0.3.27)
activeresource (3.1.0.rc1)
activemodel (= 3.1.0.rc1)
activesupport (= 3.1.0.rc1)
activesupport (3.1.0.rc1)
i18n (~> 0.6)
activerecord (3.1.0.rc5)
activemodel (= 3.1.0.rc5)
activesupport (= 3.1.0.rc5)
arel (~> 2.1.4)
tzinfo (~> 0.3.29)
activeresource (3.1.0.rc5)
activemodel (= 3.1.0.rc5)
activesupport (= 3.1.0.rc5)
activesupport (3.1.0.rc5)
multi_json (~> 1.0)
arel (2.1.3)
arel (2.1.4)
bcrypt-ruby (2.1.4)
builder (3.0.0)
daemons (1.1.4)
@@ -54,9 +54,9 @@ GEM
activesupport (~> 3.0)
daemons
erubis (2.7.0)
factory_girl (1.3.3)
factory_girl (2.0.3)
haml (3.1.2)
hike (1.1.0)
hike (1.2.0)
i18n (0.6.0)
imagesize (0.1.1)
mail (2.3.0)
@@ -64,10 +64,10 @@ GEM
mime-types (~> 1.16)
treetop (~> 1.4.8)
mechanize (2.0.1)
net-http-digest_auth (~> 1.1, >= 1.1.1)
net-http-digest_auth (>= 1.1.1, ~> 1.1)
net-http-persistent (~> 1.8)
nokogiri (~> 1.4)
webrobots (~> 0.0, >= 0.0.9)
webrobots (>= 0.0.9, ~> 0.0)
memcache-client (1.8.5)
mime-types (1.16)
mocha (0.9.12)
@@ -76,31 +76,33 @@ GEM
net-http-persistent (1.8)
nokogiri (1.5.0)
pg (0.11.0)
polyglot (0.3.1)
rack (1.3.1)
polyglot (0.3.2)
rack (1.3.2)
rack-cache (1.0.2)
rack (>= 0.4)
rack-mount (0.8.1)
rack (>= 1.0.0)
rack-ssl (1.3.2)
rack
rack-test (0.6.0)
rack-test (0.6.1)
rack (>= 1.0)
rails (3.1.0.rc1)
actionmailer (= 3.1.0.rc1)
actionpack (= 3.1.0.rc1)
activerecord (= 3.1.0.rc1)
activeresource (= 3.1.0.rc1)
activesupport (= 3.1.0.rc1)
rails (3.1.0.rc5)
actionmailer (= 3.1.0.rc5)
actionpack (= 3.1.0.rc5)
activerecord (= 3.1.0.rc5)
activeresource (= 3.1.0.rc5)
activesupport (= 3.1.0.rc5)
bundler (~> 1.0)
railties (= 3.1.0.rc1)
railties (3.1.0.rc1)
actionpack (= 3.1.0.rc1)
activesupport (= 3.1.0.rc1)
railties (= 3.1.0.rc5)
railties (3.1.0.rc5)
actionpack (= 3.1.0.rc5)
activesupport (= 3.1.0.rc5)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.6)
rake (0.9.2)
rdoc (3.9.1)
shoulda (2.11.3)
silent-postgres (0.0.8)
simple_form (1.4.2)
@@ -109,8 +111,8 @@ GEM
simplecov (0.4.2)
simplecov-html (~> 0.4.4)
simplecov-html (0.4.5)
sprockets (2.0.0.beta.10)
hike (~> 1.0)
sprockets (2.0.0.beta.13)
hike (~> 1.2)
rack (~> 1.0)
tilt (!= 1.3.0, ~> 1.1)
super_exception_notifier (3.0.13)
@@ -118,11 +120,15 @@ GEM
rake
thor (0.14.6)
tilt (1.3.2)
treetop (1.4.9)
treetop (1.4.10)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.29)
webrobots (0.0.10)
nokogiri (>= 1.4.4)
whenever (0.6.8)
aaronh-chronic (>= 0.3.9)
activesupport (>= 2.3.4)
PLATFORMS
ruby
@@ -139,9 +145,10 @@ DEPENDENCIES
mocha
nokogiri
pg
rails (= 3.1.0.rc1)
rails (= 3.1.0.rc5)
shoulda
silent-postgres
simple_form
simplecov
super_exception_notifier
whenever

View File

@@ -0,0 +1,118 @@
(function() {
Danbooru.Blacklist = {};
Danbooru.Blacklist.blacklists = [];
Danbooru.Blacklist.parse_entries = function() {
var entries = (Danbooru.meta("blacklisted-tags") || "").replace(/(rating:[qes])\w+/, "$1").split(/,/);
$.each(entries, function(i, tags) {
var blacklist = {
"tags": tags,
"require": [],
"exclude": [],
"disabled": false,
"hits": 0
};
var matches = tags.match(/\S+/g) || [];
$.each(matches, function(i, tag) {
if (tag.charAt(0) === '-') {
blacklist.exclude.push(tag.slice(1));
} else {
blacklist.require.push(tag);
}
})
Danbooru.Blacklist.blacklists.push(blacklist);
});
}
Danbooru.Blacklist.toggle = function(e) {
$(".blacklisted").each(function(i, element) {
var $element = $(element);
if ($element.hasClass("blacklisted-active")) {
$element.removeClass("blacklisted-active");
} else {
$element.addClass("blacklisted-active");
}
});
}
Danbooru.Blacklist.update_sidebar = function() {
$.each(this.blacklists, function(i, blacklist) {
if (blacklist.hits === 0) {
return;
}
var item = $("<li/>");
var link = $("<a/>");
var count = $("<span/>");
link.html(blacklist.tags);
link.click(Danbooru.Blacklist.toggle);
count.html(blacklist.hits);
item.append(link);
item.append(" ");
item.append(count);
$("#blacklist-list").append(item);
});
$("#blacklist-box").show();
}
Danbooru.Blacklist.apply = function() {
$.each(this.blacklists, function(i, blacklist) {
blacklist.hits = 0;
});
var count = 0
$.each(this.posts(), function(i, post) {
$.each(Danbooru.Blacklist.blacklists, function(i, blacklist) {
if (Danbooru.Blacklist.post_match(post, blacklist)) {
Danbooru.Blacklist.post_hide(post);
blacklist.hits += 1;
count += 1;
}
});
});
return count;
}
Danbooru.Blacklist.posts = function() {
return $("article.post-preview");
}
Danbooru.Blacklist.post_match = function(post, blacklist) {
var $post = $(post);
var tags = $post.data("tags").match(/\S+/g) || [];
tags.push("rating:" + $post.data("rating"));
tags.push("uploader:" + $post.data("uploader"));
if (blacklist.require.length > 0 || blacklist.exclude.length > 0) {
if (blacklist.require.length === 0 || Danbooru.is_subset(tags, blacklist.require)) {
if (blacklist.exclude.length === 0 || (!Danbooru.is_subset(tags, blacklist.exclude))) {
return true;
}
}
}
return false;
}
Danbooru.Blacklist.post_hide = function(post) {
var $post = $(post);
$post.addClass("blacklisted").addClass("blacklisted-active");
}
Danbooru.Blacklist.initialize = function() {
Danbooru.Blacklist.parse_entries();
if (Danbooru.Blacklist.apply() > 0) {
Danbooru.Blacklist.update_sidebar();
} else {
$("#blacklist-box").hide();
}
}
})();
$(document).ready(function() {
Danbooru.Blacklist.initialize();
});

View File

@@ -51,15 +51,23 @@
return;
}
if (this.get("hide-news-ticker") == "1") {
$("#news-ticker").hide();
} else {
$("#close-news-ticker-link").click(function(e) {
$("#news-ticker").hide();
Danbooru.Cookie.put("hide-news-ticker", "1", 1);
return false;
});
}
if (this.get("hide-upgrade-account") != "1") {
if ($("upgrade-account")) {
$("upgrade-account").show();
}
$("#upgrade-account").show();
}
}
})();
$(document).ready(function() {
$(function() {
Danbooru.Cookie.initialize();
});

View File

@@ -18,4 +18,16 @@
Danbooru.ajax_stop = function(target) {
$(target).next("img.wait").remove();
}
Danbooru.is_subset = function(array, subarray) {
var all = true;
$.each(subarray, function(i, val) {
if ($.inArray(val, array) === -1) {
all = false;
}
});
return all;
}
})();

View File

@@ -334,6 +334,7 @@ div#page {
width: 75%;
float: left;
margin-left: 2em;
overflow: hidden;
}
}
@@ -626,6 +627,10 @@ article.post-preview {
float: left;
}
article.post-preview.blacklisted-active {
display: none;
}
div#c-posts {
div.notice {
font-size: 0.8em;
@@ -650,6 +655,23 @@ div#c-posts {
aside#sidebar > section > ul ul li {
margin-left: 1em;
}
aside#sidebar > section#blacklist-box ul {
margin-left: 1em;
li {
list-style-type: disc;
}
a {
color: $link_color;
cursor: pointer;
}
span {
color: #AAA;
}
}
h1 {
font-size: $h3_size;
@@ -693,6 +715,9 @@ div#c-posts {
}
}
div#c-explore-posts {
}
/*** Post Histories ***/
div.post_histories {
@@ -1046,3 +1071,40 @@ div#moderator-dashboard {
font-size: 1.5em;
}
}
/*** page footer ***/
footer#page-footer {
clear: both;
margin: 1em;
text-align: center;
padding-top: 1em;
border-top: 2px solid #CCC;
}
/*** news ticker ***/
div#news-ticker {
padding: 5px;
background: #EEE;
border-bottom: 2px solid #666;
overflow: hidden;
font-size: 0.8em;
ul {
float: left;
margin: 0;
padding: 0;
}
li {
list-style-type: none;
float: left;
margin: 0 2em 0 0;
padding: 0;
}
a#close-news-ticker-link {
float: right;
}
}

View File

@@ -1,6 +0,0 @@
module Admin
class PostsController
def edit
end
end
end

View File

@@ -1,7 +0,0 @@
class Admin::UsersController < ApplicationController
def edit
end
def update
end
end

View File

@@ -0,0 +1,11 @@
module Explore
class PostsController < ApplicationController
respond_to :html, :xml, :json
def popular
@post_set = PostSets::Popular.new(params[:date], params[:scale])
@posts = @post_set.posts
respond_with(@posts)
end
end
end

View File

@@ -0,0 +1,19 @@
module Moderator
class TagsController < ApplicationController
before_filter :moderator_only
rescue_from TagBatchChange::Error, :with => :error
def edit
end
def update
tag_batch_change = TagBatchChange.new(params[:tag][:predicate], params[:tag][:consequent])
tag_batch_change.execute
redirect_to edit_moderator_tag_path, :notice => "Posts updated"
end
def error
redirect_to edit_moderator_tag_path, :notice => "Error"
end
end
end

View File

@@ -15,6 +15,7 @@ class TagAliasesController < ApplicationController
def create
@tag_alias = TagAlias.create(params[:tag_alias])
@tag_alias.delay.process!
respond_with(@tag_alias, :location => tag_aliases_path(:search => {:id_eq => @tag_alias.id}))
end

View File

@@ -15,6 +15,7 @@ class TagImplicationsController < ApplicationController
def create
@tag_implication = TagImplication.create(params[:tag_implication])
@tag_implication.delay.process!
respond_with(@tag_implication, :location => tag_implications_path(:search => {:id_eq => @tag_implication.id}))
end

View File

@@ -0,0 +1,24 @@
module Moderator
class TagBatchChange
class Error < Exception ; end
attr_reader :predicate, :consequent
def initialize(predicate, consequent)
@predicate = predicate
@consequent = consequent
end
def execute
raise Error.new("Predicate is missing") if predicate.blank?
normalized_predicate = TagAlias.to_aliased(::Tag.scan_tags(predicate))
normalized_consequent = TagAlias.to_aliased(::Tag.scan_tags(consequent))
::Post.tag_match(predicate).each do |post|
tags = (post.tag_array - normalized_predicate + normalized_consequent).join(" ")
post.update_attributes(:tag_string => tags)
end
end
end
end

View File

@@ -4,7 +4,7 @@ class PixivProxy
end
def self.get(url)
if url =~ /\/(\d+)(_m)?\.(jpg|jpeg|png|gif)/i
if url =~ /\/(\d+)(_m|_p\d+)?\.(jpg|jpeg|png|gif)/i
url = "http://www.pixiv.net/member_illust.php?mode=medium&illust_id=#{$1}"
get_single(url)
elsif url =~ /member_illust\.php/ && url =~ /illust_id=/

View File

@@ -1,9 +1,9 @@
module PostSets
class Artist < Post
class Artist < PostSets::Post
attr_reader :artist
def initialize(artist)
super(:tags => artist.name)
super(artist.name)
@artist = artist
end

View File

@@ -13,6 +13,7 @@ module PostSets
end
def artist
nil
end
def is_single_tag?

View File

@@ -0,0 +1,48 @@
module PostSets
class Popular < Base
attr_reader :date, :scale
def initialize(date, scale)
@date = date.blank? ? Date.today : date.to_date
@scale = scale
end
def posts
::Post.where("created_at between ? and ?", min_date, max_date + 1).order("score desc").limit(limit)
end
def limit
25
end
def min_date
case scale
when "week"
date.beginning_of_week
when "month"
date.beginning_of_month
else
date
end
end
def max_date
case scale
when "week"
date.end_of_week
when "month"
date.end_of_month
else
date
end
end
def presenter
::PostSetPresenters::Popular.new(self)
end
end
end

View File

@@ -13,6 +13,7 @@ class Comment < ActiveRecord::Base
scope :body_matches, lambda {|query| where("body_index @@ plainto_tsquery(?)", query).order("comments.id DESC")}
scope :hidden, lambda {|user| where("score < ?", user.comment_threshold)}
scope :post_tag_match, lambda {|query| joins(:post).where("posts.tag_index @@ to_tsquery('danbooru', ?)", query)}
scope :for_user, lambda {|user_id| where("creator_id = ?", user_id)}
search_methods :body_matches, :post_tag_match

View File

@@ -1,4 +1,4 @@
class Favorite < ActiveRecord::Base
belongs_to :post
scope :for_user, lambda {|user_id| where("user_id = ?", user_id)}
scope :for_user, lambda {|user_id| where("user_id = #{user_id}")}
end

View File

@@ -8,6 +8,7 @@ class ForumPost < ActiveRecord::Base
validates_presence_of :body, :creator_id
validate :validate_topic_is_unlocked
scope :body_matches, lambda {|body| where(["text_index @@ plainto_tsquery(?)", body])}
scope :for_user, lambda {|user_id| where("creator_id = ?", user_id)}
search_methods :body_matches
def self.new_reply(params)

View File

@@ -1,6 +1,7 @@
class NoteVersion < ActiveRecord::Base
before_validation :initialize_updater
belongs_to :updater, :class_name => "User"
scope :for_user, lambda {|user_id| where("updater_id = ?", user_id)}
def initialize_updater
self.updater_id = CurrentUser.id

View File

@@ -5,6 +5,7 @@ class PoolVersion < ActiveRecord::Base
belongs_to :pool
belongs_to :updater, :class_name => "User"
before_validation :initialize_updater
scope :for_user, lambda {|user_id| where("updater_id = ?", user_id)}
def initialize_updater
self.updater_id = CurrentUser.id

View File

@@ -41,6 +41,7 @@ class Post < ActiveRecord::Base
scope :available_for_moderation, lambda {where(["id NOT IN (SELECT pd.post_id FROM post_disapprovals pd WHERE pd.user_id = ?)", CurrentUser.id])}
scope :hidden_from_moderation, lambda {where(["id IN (SELECT pd.post_id FROM post_disapprovals pd WHERE pd.user_id = ?)", CurrentUser.id])}
scope :tag_match, lambda {|query| Post.tag_match_helper(query)}
scope :exact_tag_match, lambda {|query| Post.exact_tag_match_helper(query)}
scope :positive, where("score > 1")
scope :negative, where("score < -1")
search_methods :tag_match
@@ -487,20 +488,29 @@ class Post < ActiveRecord::Base
def add_tag_subscription_relation(subscriptions, relation)
subscriptions.each do |subscription|
subscription =~ /^(.+?):(.+)$/
user_name = $1 || subscription
subscription_name = $2
user = User.find_by_name(user_name)
if user
if subscription =~ /^(.+?):(.+)$/
user_name = $1
subscription_name = $2
user = User.find_by_name(user_name)
return relation if user.nil?
post_ids = TagSubscription.find_post_ids(user.id, subscription_name)
relation = relation.where(["posts.id IN (?)", post_ids])
else
user = User.find_by_name(subscription)
return relation if user.nil?
post_ids = TagSubscription.find_post_ids(user.id)
end
post_ids = [0] if post_ids.empty?
relation = relation.where(["posts.id IN (?)", post_ids])
end
relation
end
def exact_tag_match_helper(q)
arel = Post.scoped
add_tag_string_search_relation({:related => [q].flatten, :include => [], :exclude => []}, arel)
end
def tag_match_helper(q)
unless q.is_a?(Hash)
@@ -576,6 +586,10 @@ class Post < ActiveRecord::Base
relation = relation.where("posts.rating <> 'e'")
end
if q[:order] == "rank"
relation = relation.where("p.score > 0 and p.created_at >= ?", 0, 3.days.ago)
end
case q[:order]
when "id", "id_asc"
relation = relation.order("posts.id")
@@ -609,6 +623,9 @@ class Post < ActiveRecord::Base
when "filesize_asc"
relation = relation.order("posts.file_size")
when "rank"
sql << " ORDER BY log(3, p.score) + (extract(epoch from p.created_at) - extract(epoch from timestamp '2005-05-24')) / 45000 DESC"
else
relation = relation.order("posts.id DESC")
end

View File

@@ -2,6 +2,7 @@ class PostVersion < ActiveRecord::Base
belongs_to :post
belongs_to :updater, :class_name => "User"
before_validation :initialize_updater
scope :for_user, lambda {|user_id| where("updater_id = ?", user_id)}
def self.create_from_post(post)
if post.created_at == post.updated_at

View File

@@ -106,21 +106,7 @@ class Tag < ActiveRecord::Base
m.extend(ClassMethods)
end
end
module UpdateMethods
def mass_edit(start_tags, result_tags, updater_id, updater_ip_addr)
updater = User.find(updater_id)
Post.tag_match(start_tags).each do |p|
start = TagAlias.to_aliased(scan_tags(start_tags))
result = TagAlias.to_aliased(scan_tags(result_tags))
tags = (p.tag_array - start + result).join(" ")
CurrentUser.scoped(updater, updater_ip_addr) do
p.update_attributes(:tag_string => tags)
end
end
end
end
module ParseMethods
def normalize(query)
query.to_s.downcase.strip
@@ -323,12 +309,13 @@ class Tag < ActiveRecord::Base
module RelationMethods
def update_related
return unless should_update_related?
counts = RelatedTagCalculator.calculate_from_sample(Danbooru.config.post_sample_size, name)
self.related_tags = RelatedTagCalculator.convert_hash_to_string(counts)
update_attributes(:related_tags => RelatedTagCalculator.convert_hash_to_string(counts), :related_tags_updated_at => Time.now)
end
def update_related_if_outdated
updated_related if should_update_related?
delay.update_related if should_update_related?
end
def related_cache_expiry
@@ -345,7 +332,8 @@ class Tag < ActiveRecord::Base
end
def related_tag_array
related_tags.split(/ /).in_groups_of(2)
update_related_if_outdated
related_tags.to_s.split(/ /).in_groups_of(2)
end
end
@@ -367,7 +355,7 @@ class Tag < ActiveRecord::Base
include CategoryMethods
extend StatisticsMethods
include NameMethods
extend UpdateMethods
extend ParseMethods
include RelationMethods
extend SuggestionMethods
end

View File

@@ -1,11 +1,7 @@
class TagAlias < ActiveRecord::Base
attr_accessor :creator_ip_addr
after_save :update_posts
after_save :clear_cache
after_save :clear_remote_cache
before_save :clear_all_cache
after_save :update_cache
after_destroy :clear_cache
after_destroy :clear_remote_cache
after_destroy :clear_all_cache
before_validation :initialize_creator, :on => :create
validates_presence_of :creator_id
validates_uniqueness_of :antecedent_name
@@ -25,8 +21,17 @@ class TagAlias < ActiveRecord::Base
alias_hash.values.flatten.uniq
end
def process!
update_column(:status, "processing")
update_posts
update_column(:status, "active")
rescue Exception => e
update_column(:status, "error: #{e}")
end
def initialize_creator
self.creator_id = CurrentUser.user.id
self.creator_ip_addr = CurrentUser.ip_addr
end
def antecedent_tag
@@ -44,6 +49,11 @@ class TagAlias < ActiveRecord::Base
false
end
end
def clear_all_cache
clear_cache
clear_remote_cache
end
def clear_cache
Cache.delete("ta:#{Cache.sanitize(antecedent_name)}")
@@ -60,13 +70,15 @@ class TagAlias < ActiveRecord::Base
end
def update_posts
Post.tag_match(antecedent_name).find_each do |post|
Post.exact_tag_match(antecedent_name).find_each do |post|
escaped_antecedent_name = Regexp.escape(antecedent_name)
fixed_tags = post.tag_string.sub(/(?:\A| )#{escaped_antecedent_name}(?:\Z| )/, " #{consequent_name} ").strip
post.update_attributes(
:tag_string => fixed_tags
)
CurrentUser.scoped(creator, creator_ip_addr) do
post.update_attributes(
:tag_string => fixed_tags
)
end
end
end
end

View File

@@ -1,43 +1,19 @@
class TagImplication < ActiveRecord::Base
before_save :clear_cache
before_save :update_descendant_names
after_save :update_descendant_names_for_parent
after_save :update_cache
after_save :update_posts
after_destroy :clear_cache
after_destroy :clear_remote_cache
belongs_to :creator, :class_name => "User"
before_validation :initialize_creator, :on => :create
validates_presence_of :creator_id
validates_uniqueness_of :antecedent_name, :scope => :consequent_name
validate :absence_of_circular_relation
module CacheMethods
def clear_cache
Cache.delete("ti:#{Cache.sanitize(antecedent_name)}")
@descendants = nil
end
def clear_remote_cache
Danbooru.config.other_server_hosts.each do |server|
Net::HTTP.delete(URI.parse("http://#{server}/tag_implications/#{id}/cache"))
end
end
def update_cache
descendant_names_array
true
end
end
module DescendantMethods
extend ActiveSupport::Concern
module ClassMethods
# assumes names are normalized
def with_descendants(names)
names + Cache.get_multi(names.flatten, "ti") do |name|
([name] + where(["antecedent_name = ?", name]).all.map {|x| x.descendant_names_array}).flatten
end.values.flatten.uniq
(names + where("antecedent_name in (?)", names).map(&:descendant_names_array)).flatten.uniq
end
end
@@ -55,9 +31,7 @@ class TagImplication < ActiveRecord::Base
end
def descendant_names_array
Cache.get("ti:#{Cache.sanitize(antecedent_name)}") do
descendant_names.split(/ /)
end
descendant_names.split(/ /)
end
def update_descendant_names
@@ -93,12 +67,20 @@ class TagImplication < ActiveRecord::Base
end
end
include CacheMethods
include DescendantMethods
include ParentMethods
def initialize_creator
self.creator_id = CurrentUser.user.id
self.creator_ip_addr = CurrentUser.ip_addr
end
def process!
update_column(:status, "processing")
update_posts
update_column(:status, "active")
rescue Exception => e
update_column(:status, "error: #{e}")
end
def absence_of_circular_relation
@@ -110,12 +92,14 @@ class TagImplication < ActiveRecord::Base
end
def update_posts
Post.tag_match(antecedent_name).find_each do |post|
Post.exact_tag_match(antecedent_name).find_each do |post|
escaped_antecedent_name = Regexp.escape(antecedent_name)
fixed_tags = post.tag_string.sub(/(?:\A| )#{escaped_antecedent_name}(?:\Z| )/, " #{antecedent_name} #{descendant_names} ").strip
post.update_attributes(
:tag_string => fixed_tags
)
CurrentUser.scoped(creator, creator_ip_addr) do
post.update_attributes(
:tag_string => fixed_tags
)
end
end
end

View File

@@ -9,6 +9,7 @@ class UserFeedback < ActiveRecord::Base
scope :positive, where("category = ?", "positive")
scope :neutral, where("category = ?", "neutral")
scope :negative, where("category = ?", "negative")
scope :for_user, lambda {|user_id| where("user_id = ?", user_id)}
def initialize_creator
self.creator_id = CurrentUser.id

View File

@@ -1,6 +1,7 @@
class WikiPageVersion < ActiveRecord::Base
belongs_to :wiki_page
belongs_to :updater, :class_name => "User"
scope :for_user, lambda {|user_id| where("updater_id = ?", user_id)}
def updater_name
User.id_to_name(updater_id)

View File

@@ -0,0 +1,21 @@
module PostSetPresenters
class Base
def posts
raise NotImplementedError
end
def post_previews_html(template)
html = ""
if posts.empty?
return template.render(:partial => "post_sets/blank")
end
posts.each do |post|
html << PostPresenter.preview(post)
end
html.html_safe
end
end
end

View File

@@ -1,7 +1,7 @@
module PostSetPresenters
class Favorite
class Favorite < Base
attr_accessor :favorite_set, :tag_set_presenter
delegate :favorites, :posts, :to => :favorite_set
delegate :favorites, :to => :favorite_set
def initialize(favorite_set)
@favorite_set = favorite_set
@@ -15,19 +15,9 @@ module PostSetPresenters
def tag_list_html(template)
tag_set_presenter.tag_list_html(template)
end
def post_previews_html(template)
html = ""
if favorites.empty?
return template.render(:partial => "post_sets/blank")
end
favorites.each do |favorite|
html << PostPresenter.preview(favorite.post)
end
html.html_safe
def posts
favorites.map(&:post)
end
end
end

View File

@@ -1,12 +1,13 @@
module PostSetPresenters
class Pool
attr_reader :tag_set_presenter, :pool_set
class Pool < Base
attr_reader :tag_set_presenter, :post_set
delegate :posts, :to => :post_set
def initialize(pool_set)
@pool_set = pool_set
def initialize(post_set)
@post_set = post_set
@tag_set_presenter = TagSetPresenter.new(
RelatedTagCalculator.calculate_from_sample_to_array(
pool_set.tag_string
post_set.tag_string
).map {|x| x[0]}
)
end
@@ -14,19 +15,5 @@ module PostSetPresenters
def tag_list_html(template)
tag_set_presenter.tag_list_html(template)
end
def post_previews_html(template)
html = ""
if pool_set.posts.empty?
return template.render(:partial => "post_sets/blank")
end
pool_set.posts.each do |post|
html << PostPresenter.preview(post)
end
html.html_safe
end
end
end

View File

@@ -0,0 +1,34 @@
module PostSetPresenters
class Popular < Base
attr_accessor :post_set, :tag_set_presenter
delegate :posts, :date, :to => :post_set
def initialize(post_set)
@post_set = post_set
end
def prev_day
date - 1
end
def next_day
date + 1
end
def prev_week
date - 7
end
def next_week
date + 7
end
def prev_month
1.month.ago(date)
end
def next_month
1.month.since(date)
end
end
end

View File

@@ -1,36 +1,26 @@
module PostSetPresenters
class Post
class Post < Base
attr_accessor :post_set, :tag_set_presenter
delegate :posts, :to => :post_set
def initialize(post_set)
@post_set = post_set
@tag_set_presenter = TagSetPresenter.new(
RelatedTagCalculator.calculate_from_sample_to_array(
post_set.tag_string
).map {|x| x[0]}
)
@tag_set_presenter = TagSetPresenter.new(related_tags)
end
def posts
post_set.posts
def related_tags
if post_set.is_single_tag?
tag = Tag.find_by_name(post_set.tag_string)
if tag
return tag.related_tag_array.map(&:first)
end
end
RelatedTagCalculator.calculate_from_sample_to_array(post_set.tag_string).map(&:first)
end
def tag_list_html(template)
tag_set_presenter.tag_list_html(template)
end
def post_previews_html(template)
html = ""
if posts.empty?
return template.render(:partial => "post_sets/blank")
end
posts.each do |post|
html << PostPresenter.preview(post)
end
html.html_safe
end
end
end

View File

@@ -0,0 +1,107 @@
class UserPresenter
attr_reader :user
def initialize(user)
@user = user
end
def level
user.level_string
end
def ban_reason
if user.is_banned?
"#{user.ban.reason}; expires #{user.ban.expires_at}"
else
nil
end
end
def tag_subscriptions(template)
user.subscriptions.map do |subscription|
template.link_to(subscription.name, template.tag_subscription_path(subscription))
end.join("; ")
end
def upload_limit
deleted_count = Post.for_user(user.id).deleted.count
pending_count = Post.for_user(user.id).pending.count
approved_count = Post.where("is_flagged = false and is_pending = false and user_id = ?", user.id).count
if user.base_upload_limit
limit = user.base_upload_limit - pending_count
string = "base:#{user.base_upload_limit} - pending:#{pending_count}"
else
limit = 10 + (approved_count / 10) - (deleted_count / 4) - pending_count
string = "base:10 + approved:(#{approved_count} / 10) - deleted:(#{deleted_count}) / 4 - pending:#{pending_count}"
end
if limit > 20
limit = 20
string += " = capped:20"
elsif limit < 0
limit = 0
string += " = capped:0"
else
string += " = #{limit}"
end
return string
end
def uploads(template)
template.link_to(Post.for_user(user.id).count, template.posts_path(:tags => "uploader:#{user.name}"))
end
def deleted_uploads(template)
template.link_to(Post.for_user(user.id).deleted.count, template.posts_path(:tags => "status:deleted uploader:#{user.name}"))
end
def favorites(template)
template.link_to(Favorite.for_user(user.id).count, template.favorites_path(:user_id => user.id))
end
def comments(template)
template.link_to(Comment.for_user(user.id).count, template.comments_path(:search => {:creator_id_eq => user.id}))
end
def post_versions(template)
template.link_to(PostVersion.for_user(user.id).count, template.post_versions_path(:search => {:updater_id_eq => user.id}))
end
def note_versions(template)
template.link_to(NoteVersion.for_user(user.id).count, template.note_versions_path(:search => {:updater_id_eq => user.id}))
end
def wiki_page_versions(template)
template.link_to(WikiPageVersion.for_user(user.id).count, template.wiki_page_versions_path(:search => {:updater_id_eq => user.id}))
end
def forum_posts(template)
template.link_to(ForumPost.for_user(user.id).count, template.forum_posts_path(:search => {:creator_id_eq => user.id}))
end
def pool_versions(template)
template.link_to(PoolVersion.for_user(user.id).count, template.pool_versions_path(:search => {:updater_id_eq => user.id}))
end
def inviter(template)
if user.inviter_id
template.link_to(user.inviter.name, template.user_path(user.inviter_id))
else
nil
end
end
def approvals(template)
template.link_to(Post.where("approver_id = ?", user.id).count, template.posts_path(:tags => "approver:#{user.name}"))
end
def feedbacks(template)
positive = UserFeedback.for_user(user.id).positive.count
neutral = UserFeedback.for_user(user.id).neutral.count
negative = UserFeedback.for_user(user.id).negative.count
template.link_to("positive:#{positive} neutral:#{neutral} negative:#{negative}", user_feedbacks_path(:search => {:user_id_rq => user.id}))
end
end

View File

@@ -1,10 +0,0 @@
<% form_tag({:action => "mass_edit"}, :onsubmit => "return confirm('Are you sure you wish to perform this tag edit?')") do %>
<%= text_field_tag "start", params[:source], :size => 60 %>
<%= text_field_tag "result", params[:name], :size => 60 %>
<%= button_to_function "Preview", "$('preview').innerHTML = 'Loading...'; new Ajax.Updater('preview', '#{url_for(:controller=>"tag",:action=>"edit_preview")}', {method: 'get', parameters: 'tags=' + $F('start')})" %><%= submit_tag "Save" %>
<% end %>
<%= render :partial => "footer" %>
<div id="preview">
</div>

View File

@@ -0,0 +1,19 @@
<aside id="sidebar">
<ul>
<li>
<%= link_to "&laquo;".html_safe, popular_explore_posts_path(:date => post_set.presenter.prev_day, :scale => "day") %>
<%= link_to "&raquo;".html_safe, popular_explore_posts_path(:date => post_set.presenter.next_day, :scale => "day") %>
<%= link_to "Day", popular_explore_posts_path(:date => post_set.presenter.date, :scale => "day") %>
</li>
<li>
<%= link_to "&laquo;".html_safe, popular_explore_posts_path(:date => post_set.presenter.prev_week, :scale => "week") %>
<%= link_to "&raquo;".html_safe, popular_explore_posts_path(:date => post_set.presenter.next_week, :scale => "week") %>
<%= link_to "Week", popular_explore_posts_path(:date => post_set.presenter.date, :scale => "week") %>
</li>
<li>
<%= link_to "&laquo;".html_safe, popular_explore_posts_path(:date => post_set.presenter.prev_month, :scale => "month") %>
<%= link_to "&raquo;".html_safe, popular_explore_posts_path(:date => post_set.presenter.next_month, :scale => "month") %>
<%= link_to "Month", popular_explore_posts_path(:date => post_set.presenter.date, :scale => "month") %>
</li>
</ul>
</aside>

View File

@@ -0,0 +1,14 @@
<div id="c-explore-posts">
<div id="a-index">
<h1>Explore: <%= @post_set.min_date %> &ndash; <%= @post_set.max_date %></h1>
<%= render "date_explore", :post_set => @post_set %>
<section id="content">
<%= @post_set.presenter.post_previews_html(self) %>
</section>
</div>
</div>
<%= render "posts/partials/common/secondary_links" %>

View File

@@ -22,6 +22,8 @@
</head>
<body>
<header id="top">
<%= render "news/listing" %>
<h1><%= link_to Danbooru.config.app_name, "/" %></h1>
<nav>
<menu class="main">
@@ -64,5 +66,7 @@
<footer>
<%= yield :page_footer_content %>
</footer>
<%= render "static/footer" %>
</body>
</html>

View File

@@ -0,0 +1,21 @@
<div id="c-moderator-tags">
<div id="a-edit">
<h1>Edit Tags</h1>
<%= form_tag(moderator_tag_path, :method => :put) do %>
<div class="input">
<label>Predicate</label>
<%= text_field :tag, :predicate %>
</div>
<div class="input">
<label>Consequent</label>
<%= text_field :tag, :consequent %>
</div>
<div class="input">
<%= submit_tag %>
</div>
<% end %>
</div>
</div>

View File

@@ -0,0 +1,6 @@
<div id="news-ticker">
<ul>
</ul>
<a href="#" id="close-news-ticker-link">close</a>
</div>

View File

@@ -5,12 +5,12 @@
<%= render :partial => "posts/partials/index/mode_menu" %>
<%= render :partial => "posts/partials/index/blacklist" %>
<section id="tag-box">
<h1>Tags</h1>
<%= @post_set.presenter.tag_list_html(self) %>
</section>
<%= render :partial => "posts/partials/index/blacklist" %>
</aside>
<section id="content">

View File

@@ -2,8 +2,8 @@
<menu>
<li><%= link_to "Listing", posts_path %></li>
<li><%= link_to "Upload", new_upload_path %></li>
<li><%= link_to "Popular", explore_post_popular_path %></li>
<li><%= link_to "Hot", explore_post_hot_path %></li>
<li><%= link_to "Popular", popular_explore_posts_path %></li>
<li><%= link_to "Hot", posts_path(:tags => "order:rank") %></li>
<% unless CurrentUser.is_anonymous? %>
<li><%= link_to "Favorites", favorites_path %></li>
<li><%= link_to "Subscriptions", posts_tag_subscription_path(CurrentUser.id) %></li>

View File

@@ -1,6 +1,5 @@
<section id="blacklist-box">
<h1>Blacklisted</h1>
<%= link_to "Hidden", "#" %>
<ul>
<ul id="blacklist-list">
</ul>
</section>

View File

@@ -6,12 +6,12 @@
<li><%= link_to "Flag", new_post_flag_path(:post_id => post.id), :id => "flag" %></li>
<li><%= link_to "Appeal", new_post_appeal_path(:post_id => post.id), :id => "appeal" %></li>
<% if CurrentUser.is_janitor? %>
<li><%= link_to "Approve", moderation_post_approval_path(:post_id => post.id), :remote => true, :method => :post, :id => "approve" %></li>
<li><%= link_to "Disapprove", moderation_post_approval_path(:post_id => post.id), :remote => true, :method => :destroy, :id => "disapprove" %></li>
<li><%= link_to "Approve", moderator_post_approval_path(:post_id => post.id), :remote => true, :method => :post, :id => "approve" %></li>
<li><%= link_to "Disapprove", moderator_post_disapproval_path(:post_id => post.id), :remote => true, :method => :post, :id => "disapprove" %></li>
<% end %>
<% if CurrentUser.is_moderator? %>
<li><%= link_to "Undelete", moderation_post_deletion_path(:post_id => post.id), :remote => true, :method => :destroy, :id => "undelete" %></li>
<li><%= link_to "Delete", moderation_post_deletion_path(:post_id => post.id), :remote => true, :method => :post, :id => "delete" %></li>
<li><%= link_to "Undelete", undelete_moderator_post_post_path(:post_id => post.id), :remote => true, :method => :post, :id => "undelete" %></li>
<li><%= link_to "Delete", delete_moderator_post_post_path(:post_id => post.id), :remote => true, :method => :post, :id => "delete" %></li>
<% end %>
<li><%= link_to "Pool", "#", :id => "pool" %></li>
</ul>

View File

@@ -0,0 +1,3 @@
<footer id="page-footer">
Running Danbooru v<%= Danbooru.config.version %>. <%= mail_to Danbooru.config.contact_email, "Contact", :encode => :hex %>.
</footer>

View File

@@ -1,40 +0,0 @@
<div id="jquery-test">
<div class="note" id="lots-of-text-1">
Lorem ipsum
</div>
<div class="note" id="lots-of-text-2" style="left: 400px;">
Lorem ipsum
</div>
</div>
<script type="text/javascript">
var body = $("#lots-of-text-2");
body.css({height: "auto", minWidth: 140});
var w = body[0].offsetWidth;
var h = body[0].offsetHeight;
var lo = null;
var hi = null;
var x = null;
var last = null;
if (body[0].scrollWidth <= body[0].clientWidth) {
lo = 20, hi = w
do {
x = (lo+hi)/2
body.css({minWidth: x});
if (body[0].offsetHeight > h) {
lo = x;
} else {
hi = x;
}
} while ((hi - lo) > 4);
if (body[0].offsetHeight > h) {
body.css({minWidth: hi});
}
}
</script>

View File

@@ -1,6 +1,6 @@
<div style="width: 40em; margin: 5em auto; overflow: scroll;">
<div class="section">
<h4>Terms of Service</h4>
<h1>Terms of Service</h1>
<p>By accessing the "<%= Danbooru.config.app_name %>" website ("Site") you agree to the following terms of service. If you do not agree to these terms, then please do not access the Site.</p>
<ul>
@@ -13,13 +13,13 @@
</ul>
<div class="section">
<h6>Post/Comment Limiting</h6>
<h1>Post/Comment Limiting</h1>
<p>You cannot upload a post or comment during the first week of signing up.</p>
<p>After the initial period, you can post up to one comment an hour and a variable number of posts based on how many of your previous uploads were approved or deleted.</p>
</div>
<div class="section">
<h6>Prohibited Content</h6>
<h1>Prohibited Content</h1>
<p>In addition, you may not use the Site to upload any of the following:</p>
<ul>
<li>Non-anime: Photographs of American porn actresses, for example, are prohibited. Photographs of cosplayers, figures, or prominent figures in the industry are acceptable.</li>
@@ -34,7 +34,7 @@
</div>
<div class="section">
<h4>Copyright Infringement</h4>
<h1>Copyright Infringement</h1>
<p>If you believe a post infringes upon your copyright, please send an email to the <%= mail_to Danbooru.config.contact_email, "webmaster", :encode => "hex" %> with the following pieces of information:</p>
<ul>
@@ -45,14 +45,14 @@
</div>
<div class="section">
<h4>Privacy Policy</h4>
<h1>Privacy Policy</h1>
<p>The Site will not disclose the IP address, email address, password, or DMails of any user except to the staff.</p>
<p>The Site is allowed to make public everything else, including but not limited to: uploaded posts, favorited posts, comments, forum posts, wiki edits, and note edits.</p>
</div>
<div>
<h4>Agreement</h4>
<h1>Agreement</h1>
<p>By clicking on the "I Agree" link, you have read all the terms and have agreed to them.</p>
<p><%= link_to("I Agree", params[:url] || "/", :onclick => "Cookie.put('tos', '1')") %> | <%= link_to("Cancel", "/") %></p>
</div>

View File

@@ -2,9 +2,10 @@ require File.expand_path('../boot', __FILE__)
require 'rails/all'
# If you have a Gemfile, require the gems listed there, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(:default, Rails.env) if defined?(Bundler)
# If you have a Gemfile, require the default gems, the ones in the
# current environment and also include :assets gems if in development
# or test environments.
Bundler.require *Rails.groups(:assets) if defined?(Bundler)
module Danbooru
class Application < Rails::Application

View File

@@ -12,7 +12,7 @@ module Danbooru
# Contact email address of the admin.
def contact_email
"webmaster#{server_host}"
"webmaster@#{server_host}"
end
# Stripped of any special characters.

View File

@@ -21,4 +21,7 @@ Danbooru::Application.configure do
# Only use best-standards-support built into browsers
config.action_dispatch.best_standards_support = :builtin
# Do not compress assets
config.assets.compress = false
end

View File

@@ -11,9 +11,8 @@ Danbooru::Application.configure do
# Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_assets = false
# Compress both stylesheets and JavaScripts
config.assets.js_compressor = :uglifier
config.assets.css_compressor = :scss
# Compress JavaScripts and CSS
config.assets.compress = true
# Specifies the header that your server uses for sending files
# (comment out if your front-end server doesn't support this)

View File

@@ -1,5 +1,10 @@
ActiveSupport::Inflector.inflections do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
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,7 +1,7 @@
# Be sure to restart your server when you modify this file.
#
# This file contains the settings for ActionController::ParametersWrapper
# which will be enabled by default in the upcoming version of Ruby on Rails.
# This file contains settings for ActionController::ParamsWrapper which
# is enabled by default.
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
ActionController::Base.wrap_parameters format: [:json]

View File

@@ -10,7 +10,7 @@ Danbooru::Application.routes.draw do
end
end
resources :invitations, :only => [:new, :create, :index]
resource :tag
resource :tag, :only => [:edit, :update]
namespace :post do
resource :dashboard, :only => [:show]
resource :approval, :only => [:create]
@@ -125,9 +125,11 @@ Danbooru::Application.routes.draw do
resources :wiki_page_versions, :only => [:index, :show]
namespace :explore do
namespace :post do
resource :popular, :only => [:show]
resource :hot, :only => [:show]
resources :posts, :only => [:popular, :hot] do
collection do
get :popular
get :hot
end
end
end

5
config/schedule.rb Normal file
View File

@@ -0,0 +1,5 @@
set :output, "/var/log/whenever.log"
every 1.hour do
TagSubscription.process_all
end

View File

@@ -2250,7 +2250,9 @@ CREATE TABLE tag_aliases (
antecedent_name character varying(255) NOT NULL,
consequent_name character varying(255) NOT NULL,
creator_id integer NOT NULL,
creator_ip_addr inet NOT NULL,
forum_topic_id integer,
status character varying(255) DEFAULT 'pending'::character varying NOT NULL,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
@@ -2285,7 +2287,9 @@ CREATE TABLE tag_implications (
consequent_name character varying(255) NOT NULL,
descendant_names text NOT NULL,
creator_id integer NOT NULL,
creator_ip_addr inet NOT NULL,
forum_topic_id integer,
status character varying(255) DEFAULT 'pending'::character varying NOT NULL,
created_at timestamp without time zone,
updated_at timestamp without time zone
);

View File

@@ -4,7 +4,9 @@ class CreateTagAliases < ActiveRecord::Migration
t.column :antecedent_name, :string, :null => false
t.column :consequent_name, :string, :null => false
t.column :creator_id, :integer, :null => false
t.column :creator_ip_addr, :inet, :null => false
t.column :forum_topic_id, :integer
t.column :status, :text, :null => false, :default => "pending"
t.timestamps
end

View File

@@ -5,7 +5,9 @@ class CreateTagImplications < ActiveRecord::Migration
t.column :consequent_name, :string, :null => false
t.column :descendant_names, :text, :null => false
t.column :creator_id, :integer, :null => false
t.column :creator_ip_addr, :inet, :null => false
t.column :forum_topic_id, :integer
t.column :status, :text, :null => false, :default => "pending"
t.timestamps
end

View File

@@ -1,550 +0,0 @@
.blacklisted {
display: none !important; }
body, div, h1, h2, h3, h4, h5, h6, p, ul, li, dd, dt, header, aside {
font-family: Verdana, Geneva, sans-serif;
line-height: 1.25em; }
body {
font-size: 87.5%; }
h1, h2, h3, h4 {
font-family: Tahoma;
line-height: 1em; }
body {
padding: 1em 2em;
margin: 0; }
article, section {
display: block; }
a:link {
color: #006ffa;
text-decoration: none; }
a:visited {
color: #006ffa;
text-decoration: none; }
a:hover {
color: #9093ff;
text-decoration: none; }
a:active {
color: #006ffa;
text-decoration: none; }
abbr[title=required] {
display: none; }
blockquote {
margin: 0 0 1em 0;
padding: 1em;
border: 1px solid #666;
background: #EEE; }
code {
font-family: monospace;
font-size: 1.2em; }
dd {
margin-bottom: 1em; }
dt {
font-weight: bold; }
h1 {
font-size: 2.1818em; }
h2 {
font-size: 1.9091em; }
h3 {
font-size: 1.6364em; }
h4 {
font-size: 1.4545em; }
h5 {
font-size: 1.2727em; }
h6 {
font-size: 1.090em; }
header {
margin: 0 0 1em 0;
padding: 0;
display: block; }
img {
border: none;
vertical-align: middle; }
input[type=text], input[type=password], input[type=url], textarea, button {
/* border: 1px solid #AAA;*/
font-size: 1em;
/* -moz-border-radius: 4px;*/
/* -webkit-border-radius: 4px;*/ }
input[type=submit] {
padding: 1px 4px;
/* border: 1px solid #AAA;*/
/* background-color: #EEE;*/
/* -moz-border-radius: 4px;*/
/* -webkit-border-radius: 4px;*/
font-size: 1em;
cursor: pointer; }
input:focus, textarea:focus {
/* background-color: #FFD;*/ }
input[type=submit]:hover {
/* background-color: #F6F6F6;*/ }
menu {
margin: 0;
padding: 0; }
menu ul {
margin: 0;
padding: 0; }
menu li {
margin: 0 1em 0 0;
padding: 0;
list-style-type: none;
display: inline; }
p {
margin-bottom: 1em; }
section {
display: block; }
span.error {
display: block;
font-weight: bold;
color: red; }
span.link {
color: #006ffa;
cursor: pointer; }
span.wait {
color: #CCC; }
span.ui-icon {
float: left;
margin-right: 0.25em; }
table tfoot {
margin-top: 2em; }
table.striped tbody tr:hover {
background-color: #FFE; }
table.striped tr.even {
background-color: #EEE; }
div.error-messages {
margin: 1em 0;
padding: 1em; }
div.error-messages span.ui-icon {
float: left;
margin-right: 0.5em; }
div.error-messages h1 {
font-size: 1em;
color: #A00; }
div#search {
margin-bottom: 1em; }
div#notice {
margin: 1em 0;
padding: 1em; }
div#notice span.ui-icon {
float: left;
margin-right: 0.5em; }
div#page aside#sidebar {
width: 20%;
float: left; }
div#page aside#sidebar h1 {
font-size: 1.2em; }
div#page aside#sidebar ul {
list-style-type: none; }
div#page aside#sidebar > section {
margin-bottom: 1em; }
div#page section#content {
width: 75%;
float: left;
margin-left: 2em; }
div.clearfix {
clear: both; }
/*** Paginator ***/
div.paginator {
display: block;
padding: 2em 0 1em 0;
font-size: 1em;
text-align: center;
font-weight: bold;
clear: both; }
div.paginator a {
margin: 0 3px;
padding: 2px 6px;
font-weight: normal;
border: 1px solid #EAEAEA; }
div.paginator a.arrow {
border: none; }
div.paginator a.arrow:hover {
background: white;
color: #9093FF; }
div.paginator a.current {
border: 1px solid #AAA; }
div.paginator a:hover {
background: #3c3cdc;
color: white; }
div.paginator span {
margin: 0 3px;
padding: 2px 6px; }
/*** Header ***/
body > header > h1 {
font-size: 3em;
font-family: Tahoma, Helvetica, sans-serif; }
/*** Dialog Boxes ***/
div.ui-dialog ul {
margin-left: 1em;
margin-bottom: 1em; }
/*** Simple Form ***/
form.simple_form {
margin: 0 0 1em 0; }
form.simple_form div.input.boolean label {
display: inline; }
form.simple_form div.input {
margin-bottom: 1em; }
form.simple_form div.input input[type=text], form.simple_form div.input input[type=file] {
width: 20em; }
form.simple_form div.input textarea {
width: 30em; }
form.simple_form div.input label {
display: block;
cursor: pointer;
font-weight: bold; }
form.simple_form div.input span.hint {
color: #666;
font-style: italic;
display: block; }
form.simple_form div.input fieldset {
border: none;
display: inline;
margin: 0;
padding: 0; }
form.simple_form div.input fieldset label {
font-weight: normal;
width: auto;
margin-right: 2em; }
/*** DText Preview ***/
div.dtext {
width: 30em; }
div.dtext ul {
margin-left: 1em; }
/*** Pools Posts ***/
div#c-pools-posts div#a-new form {
margin-bottom: 1em; }
div#c-pools-posts div#a-new li {
margin-left: 1em;
cursor: pointer; }
/*** Pools ***/
div#c-pools div#a-edit p {
width: 30em; }
div#c-pools div#a-edit ul.ui-sortable {
list-style-type: none; }
div#c-pools div#a-edit ul.ui-sortable span {
margin: 0;
float: right;
cursor: pointer; }
div#c-pools div#a-edit ul.ui-sortable li {
padding: 0.5em; }
div#c-pools div#a-edit ul.ui-sortable li.ui-state-default {
margin-bottom: 20px;
width: 180px;
background: none; }
div#c-pools div#a-edit ul.ui-sortable li.ui-state-placeholder {
margin-bottom: 20px;
width: 180px;
height: 150px;
background: none; }
/*** Comments ***/
div.comments-for-post div.list-of-comments article {
margin-bottom: 2em; }
div.comments-for-post div.list-of-comments article div.author {
width: 20%;
float: left; }
div.comments-for-post div.list-of-comments article div.content {
margin-left: 2em;
width: 30em;
float: left; }
div.comments-for-post div.comment-preview {
margin-bottom: 2em; }
div.dtext-preview {
border: 2px dashed #AAA;
padding: 1em;
margin: 1em;
width: 30em; }
div#c-comments div#a-index div.header span.info {
margin-right: 1.5em; }
div#c-comments div#a-index div.header strong, div#c-comments div#a-index div.header time {
margin-right: 0.3em; }
div#c-comments div#a-index div.header time {
font-weight: bold; }
div#c-comments div#a-index div.header div.list-of-tags a {
margin-right: 0.5em; }
div#c-comments div#a-index div.header div.notices {
margin: 1em 0; }
div#c-comments div#a-index div.preview {
float: left;
width: 180px; }
div#c-comments div#a-index div.post {
margin-bottom: 4em; }
div#c-comments div#a-index div.post div.comments-for-post {
float: left;
width: 55em; }
/*** Favorites ***/
div#c-favorites section#content > h1 {
display: none; }
/*** Posts ***/
article.post-preview {
margin-right: 4em;
margin-bottom: 4em;
float: left; }
div#c-posts div.notice {
font-size: 80%;
padding: 1em;
margin-bottom: 1em; }
div#c-posts div.notice ul {
margin-left: 1em; }
div#c-posts aside#sidebar > section#pool-sidebar span.ui-icon {
color: #666; }
div#c-posts aside#sidebar > section > ul li {
list-style-type: none; }
div#c-posts aside#sidebar > section > ul ul li {
margin-left: 1em; }
div#c-posts section#tag-and-wiki-box {
padding: 0; }
div#c-posts section#tag-and-wiki-box menu li {
display: inline-block; }
div#c-posts section#tag-and-wiki-box menu li.active a {
color: #000; }
div#c-posts section#tag-and-wiki-box div#tag-box h2 {
display: none; }
div#c-posts section#tag-and-wiki-box div#tag-box li {
list-style-type: none; }
div#c-posts section#tag-and-wiki-box div#wiki-box h2 {
display: none; }
div#c-posts section#content > h1 {
display: none; }
div#c-posts section#content section > h2 {
display: none; }
div#c-posts section#content menu#post-sections {
margin-bottom: 1em; }
div#c-posts section#content menu#post-sections li {
font-size: 1.1em;
font-weight: bold; }
div#c-posts section#content menu#post-sections li.active a {
color: black; }
div#c-posts section#content section#edit fieldset label {
display: inline; }
/*** Post Histories ***/
div.post_histories div.index div.post {
margin-bottom: 2em; }
div.post_histories div.index div.post div.preview {
width: 20%;
float: left; }
div.post_histories div.index div.post div.history {
width: 70%;
float: left; }
div.post_histories div.index div.post div.history table {
width: 100%; }
div.post_histories div.index div.post div.history ins {
color: green;
text-decoration: none; }
div.post_histories div.index div.post div.history del {
color: red;
text-decoration: line-through; }
/*** Post Unapprovals ***/
div#unapprove-dialog p {
margin-bottom: 1em; }
/*** Sessions ***/
div#sessions div#new section {
width: 30em;
float: left; }
div#sessions div#new aside {
width: 20em;
float: left; }
div#sessions div#new aside li {
display: list-item;
margin-bottom: 0.5em;
list-style-type: square;
margin-left: 1em; }
div#sessions div#new h2 {
margin-bottom: 5px; }
/*** Artists ***/
div#c-artists span.new-artist {
font-weight: bold;
color: #A00; }
div#c-artists div#a-show {
max-width: 60em; }
div#c-artists div#a-show p.legend {
margin-bottom: 2em;
font-size: 0.8em;
font-style: italic; }
div#c-artists div#a-edit textarea, div#c-artists div#a-new textarea {
height: 5em; }
/*** Users ***/
div.users div.new {
max-width: 60em; }
div.users div.new p {
margin-bottom: 1em; }
div.users div.new li {
margin-left: 1em; }
div.users div.new div#account-comparison li {
font-size: 0.9em;
line-height: 1.5em; }
div.users div.new div#account-comparison section {
width: 18em;
float: left;
padding: 1em; }
div.users div.new footer.nav-links {
font-size: 1.4545em;
font-weight: bold;
text-align: center;
display: block; }
div.users div.new div#p2 ul {
margin-bottom: 2em; }
/*** Uploads ***/
div#c-uploads div#a-new div#upload-guide-notice {
margin-bottom: 2em; }
div#c-uploads div#a-new fieldset.ratings label {
display: inline; }
/*** Forum ***/
div.list-of-forum-posts article {
margin-bottom: 3em; }
div.list-of-forum-posts article div.author {
width: 20%;
float: left; }
div.list-of-forum-posts article div.content {
margin-left: 2em;
width: 30em;
float: left; }
div.list-of-forum-posts article div.content menu {
margin-top: 0.5em; }
div#c-forum-topics div.single-forum-post {
width: 60em; }
div#c-forum-topics div#a-show h1#forum-topic-title {
font-size: 2.1818em; }
div#c-forum-topics span.info {
color: #AAA; }
div#c-forum-topics div#form-content {
float: left;
width: 450px;
padding-right: 1em; }
div#c-forum-topics div#form-aside {
float: left;
width: 400px; }
div#c-forum-topics div#form-aside div#preview > p {
margin-top: 0.5em;
padding-top: 0.5em;
border-top: 1px solid #AAA; }
/*** Wiki Pages ***/
div#c-wiki-pages span.version {
color: #AAA; }
div#c-wiki-pages div#form-content {
float: left;
width: 30em;
padding-right: 1em; }
div#c-wiki-pages div#form-aside {
float: left;
width: 20em; }
div#c-wiki-pages div#form-aside div#preview > p {
margin-top: 0.5em;
padding-top: 0.5em;
border-top: 1px solid #AAA; }
/*** Post Moderation ***/
div#c-post-moderation article {
margin-bottom: 4em; }
div#c-post-moderation aside {
float: left;
width: 520px; }
div#c-post-moderation section {
float: left;
width: 300px; }
/*** Note Container ***/
div#note-container {
position: absolute; }
div#note-container div.note-body {
background: #FFE;
border: 1px solid black;
max-width: 300px;
min-width: 140px;
min-height: 10px;
position: absolute;
padding: 5px;
cursor: pointer;
overflow: auto; }
div#note-container div.note-body p.tn {
font-size: 0.8em;
color: gray; }
div#note-container div.note-box {
position: absolute;
border: 1px solid black;
width: 150px;
height: 150px;
cursor: move;
background: #FFE; }
div#note-container div.note-box div.note-corner {
background: black;
width: 7px;
height: 7px;
position: absolute;
bottom: 0;
right: 0;
cursor: se-resize; }
div#note-container div.unsaved {
background: #FFF;
border: 1px solid red; }
div#note-container div.unsaved div.note-corner {
background: red; }
div#jquery-test div.note {
background: #FFE;
border: 1px solid black;
max-width: 300px;
min-width: 140px;
min-height: 10px;
position: absolute;
padding: 5px;
cursor: pointer;
overflow: auto; }

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env bash
psql -c "UPDATE posts SET is_flagged = false, is_pending = true, approver_id = null WHERE id = 1" danbooru2
psql -c "DELETE FROM unapprovals" danbooru2

View File

@@ -1,4 +1,10 @@
Factory.define(:tag_alias) do |f|
f.antecedent_name "aaa"
f.consequent_name "bbb"
FactoryGirl.define do
factory :tag_alias do
antecedent_name "aaa"
consequent_name "bbb"
after_create do |tag_alias|
tag_alias.process!
end
end
end

View File

@@ -1,4 +1,10 @@
Factory.define(:tag_implication) do |f|
f.antecedent_name "aaa"
f.consequent_name "bbb"
FactoryGirl.define do
factory :tag_implication do
antecedent_name "aaa"
consequent_name "bbb"
after_create do |tag_implication|
tag_implication.process!
end
end
end

View File

@@ -0,0 +1,29 @@
require 'test_helper'
module Moderator
class TagsControllerTest < ActionController::TestCase
context "The tags controller" do
setup do
@user = Factory.create(:moderator_user)
CurrentUser.user = @user
CurrentUser.ip_addr = "127.0.0.1"
@post = Factory.create(:post)
end
should "render the edit action" do
get :edit, {}, {:user_id => @user.id}
assert_response :success
end
should "execute the update action" do
post :update, {:tag => {:predicate => "aaa", :consequent => "bbb"}}, {:user_id => @user.id}
assert_redirected_to edit_moderator_tag_path
end
should "fail gracefully if the update action fails" do
post :update, {:tag => {:predicate => "", :consequent => "bbb"}}, {:user_id => @user.id}
assert_redirected_to edit_moderator_tag_path
end
end
end
end

View File

@@ -7,6 +7,7 @@ class TagAliasesControllerTest < ActionController::TestCase
CurrentUser.user = @user
CurrentUser.ip_addr = "127.0.0.1"
MEMCACHE.flush_all
Delayed::Worker.delay_jobs = false
end
teardown do

View File

@@ -7,6 +7,7 @@ class TagImplicationsControllerTest < ActionController::TestCase
CurrentUser.user = @user
CurrentUser.ip_addr = "127.0.0.1"
MEMCACHE.flush_all
Delayed::Worker.delay_jobs = false
end
teardown do

View File

@@ -0,0 +1,33 @@
require "test_helper"
module Moderator
class TagBatchChangeTest < ActiveSupport::TestCase
context "a tag batch change" do
setup do
@user = Factory.create(:moderator_user)
CurrentUser.user = @user
CurrentUser.ip_addr = "127.0.0.1"
@post = Factory.create(:post, :tag_string => "aaa")
end
teardown do
CurrentUser.user = nil
CurrentUser.ip_addr = nil
end
should "execute" do
tag_batch_change = TagBatchChange.new("aaa", "bbb")
tag_batch_change.execute
@post.reload
assert_equal("bbb", @post.tag_string)
end
should "raise an error if there is no predicate" do
tag_batch_change = TagBatchChange.new("", "bbb")
assert_raises(TagBatchChange::Error) do
tag_batch_change.execute
end
end
end
end
end

View File

@@ -525,7 +525,13 @@ class PostTest < ActiveSupport::TestCase
assert_equal(post2.id, relation.first.id)
end
should "return posts for a tag subscription search"
should "return posts for a tag subscription search" do
post1 = Factory.create(:post, :tag_string => "aaa")
sub = Factory.create(:tag_subscription, :tag_query => "aaa", :name => "zzz")
TagSubscription.process_all
relation = Post.tag_match("sub:#{CurrentUser.name}")
assert_equal(1, relation.count)
end
should "return posts for a particular rating" do
post1 = Factory.create(:post, :rating => "s")

View File

@@ -1,4 +1,4 @@
require_relative '../test_helper'
require 'test_helper'
class TagAliasTest < ActiveSupport::TestCase
context "A tag alias" do
@@ -15,14 +15,14 @@ class TagAliasTest < ActiveSupport::TestCase
end
should "populate the creator information" do
ta = Factory.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "bbb")
ta = FactoryGirl.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "bbb")
assert_equal(CurrentUser.user.id, ta.creator_id)
end
should "convert a tag to its normalized version" do
tag1 = Factory.create(:tag, :name => "aaa")
tag2 = Factory.create(:tag, :name => "bbb")
ta = Factory.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "bbb")
ta = FactoryGirl.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "bbb")
normalized_tags = TagAlias.to_aliased(["aaa", "ccc"])
assert_equal(["bbb", "ccc"], normalized_tags.sort)
end
@@ -30,7 +30,7 @@ class TagAliasTest < ActiveSupport::TestCase
should "update the cache" do
tag1 = Factory.create(:tag, :name => "aaa")
tag2 = Factory.create(:tag, :name => "bbb")
ta = Factory.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "bbb")
ta = FactoryGirl.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "bbb")
assert_equal("bbb", MEMCACHE.get("ta:aaa"))
ta.destroy
assert_nil(MEMCACHE.get("ta:aaa"))
@@ -42,7 +42,7 @@ class TagAliasTest < ActiveSupport::TestCase
post2 = Factory.create(:post, :tag_string => "ccc ddd")
assert_equal("aaa bbb", post1.tag_string)
assert_equal("ccc ddd", post2.tag_string)
ta = Factory.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "ccc")
ta = FactoryGirl.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "ccc")
post1.reload
post2.reload
assert_equal("ccc bbb", post1.tag_string)
@@ -50,7 +50,7 @@ class TagAliasTest < ActiveSupport::TestCase
end
should "not validate for transitive relations" do
ta1 = Factory.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "bbb")
ta1 = FactoryGirl.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "bbb")
assert_difference("TagAlias.count", 0) do
ta3 = Factory.build(:tag_alias, :antecedent_name => "bbb", :consequent_name => "ddd")
ta3.save
@@ -60,15 +60,15 @@ class TagAliasTest < ActiveSupport::TestCase
end
should "record the alias's creator in the tag history" do
user = Factory.create(:user)
p1 = nil
CurrentUser.scoped(user, "127.0.0.1") do
p1 = Factory.create(:post, :tag_string => "aaa bbb ccc")
uploader = Factory.create(:user)
post = nil
CurrentUser.scoped(uploader, "127.0.0.1") do
post = Factory.create(:post, :tag_string => "aaa bbb ccc")
end
ta1 = Factory.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "xxx")
p1.reload
assert_not_equal(ta1.creator_id, p1.uploader_id)
assert_equal(ta1.creator_id, p1.versions.last.updater_id)
tag_alias = FactoryGirl.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "xxx")
post.reload
assert_not_equal(tag_alias.creator_id, post.uploader_id)
assert_equal(tag_alias.creator_id, post.versions.last.updater_id)
end
end
end

View File

@@ -16,12 +16,12 @@ class TagImplicationTest < ActiveSupport::TestCase
end
should "populate the creator information" do
ti = Factory.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
ti = FactoryGirl.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
assert_equal(CurrentUser.user.id, ti.creator_id)
end
should "not validate when a circular relation is created" do
ti1 = Factory.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
ti1 = FactoryGirl.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
ti2 = Factory.build(:tag_implication, :antecedent_name => "bbb", :consequent_name => "aaa")
ti2.save
assert(ti2.errors.any?, "Tag implication should not have validated.")
@@ -29,34 +29,18 @@ class TagImplicationTest < ActiveSupport::TestCase
end
should "not allow for duplicates" do
ti1 = Factory.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
ti1 = FactoryGirl.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
ti2 = Factory.build(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
ti2.save
assert(ti2.errors.any?, "Tag implication should not have validated.")
assert_equal("Antecedent name has already been taken", ti2.errors.full_messages.join(""))
end
should "clear the cache upon saving" do
ti1 = Factory.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
assert_equal(["bbb"], ti1.descendant_names_array)
assert_equal(["bbb"], MEMCACHE.get("ti:aaa"))
ti1.update_attributes(
:consequent_name => "ccc"
)
assert_equal(["ccc"], MEMCACHE.get("ti:aaa"))
end
should "clear the cache upon destruction" do
ti1 = Factory.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
ti1.destroy
assert_nil(MEMCACHE.get("ti:aaa"))
end
should "calculate all its descendants" do
ti1 = Factory.create(:tag_implication, :antecedent_name => "bbb", :consequent_name => "ccc")
ti1 = FactoryGirl.create(:tag_implication, :antecedent_name => "bbb", :consequent_name => "ccc")
assert_equal("ccc", ti1.descendant_names)
assert_equal(["ccc"], ti1.descendant_names_array)
ti2 = Factory.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
ti2 = FactoryGirl.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
assert_equal("bbb ccc", ti2.descendant_names)
assert_equal(["bbb", "ccc"], ti2.descendant_names_array)
ti1.reload
@@ -64,15 +48,9 @@ class TagImplicationTest < ActiveSupport::TestCase
assert_equal(["ccc"], ti1.descendant_names_array)
end
should "cache its descendants" do
ti1 = Factory.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
assert_equal(["bbb"], ti1.descendant_names_array)
assert_equal(["bbb"], MEMCACHE.get("ti:aaa"))
end
should "update its descendants on save" do
ti1 = Factory.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
ti2 = Factory.create(:tag_implication, :antecedent_name => "ccc", :consequent_name => "ddd")
ti1 = FactoryGirl.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
ti2 = FactoryGirl.create(:tag_implication, :antecedent_name => "ccc", :consequent_name => "ddd")
ti2.update_attributes(
:antecedent_name => "bbb"
)
@@ -83,10 +61,10 @@ class TagImplicationTest < ActiveSupport::TestCase
end
should "update the decendants for its parent on save" do
ti1 = Factory.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
ti2 = Factory.create(:tag_implication, :antecedent_name => "bbb", :consequent_name => "ccc")
ti3 = Factory.create(:tag_implication, :antecedent_name => "ccc", :consequent_name => "ddd")
ti4 = Factory.create(:tag_implication, :antecedent_name => "ccc", :consequent_name => "eee")
ti1 = FactoryGirl.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
ti2 = FactoryGirl.create(:tag_implication, :antecedent_name => "bbb", :consequent_name => "ccc")
ti3 = FactoryGirl.create(:tag_implication, :antecedent_name => "ccc", :consequent_name => "ddd")
ti4 = FactoryGirl.create(:tag_implication, :antecedent_name => "ccc", :consequent_name => "eee")
ti1.reload
ti2.reload
ti3.reload
@@ -99,8 +77,8 @@ class TagImplicationTest < ActiveSupport::TestCase
should "update any affected post upon save" do
p1 = Factory.create(:post, :tag_string => "aaa bbb ccc")
ti1 = Factory.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "xxx")
ti2 = Factory.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "yyy")
ti1 = FactoryGirl.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "xxx")
ti2 = FactoryGirl.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "yyy")
p1.reload
assert_equal("aaa yyy xxx bbb ccc", p1.tag_string)
end
@@ -111,7 +89,7 @@ class TagImplicationTest < ActiveSupport::TestCase
CurrentUser.scoped(user, "127.0.0.1") do
p1 = Factory.create(:post, :tag_string => "aaa bbb ccc")
end
ti1 = Factory.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "xxx")
ti1 = FactoryGirl.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "xxx")
p1.reload
assert_not_equal(ti1.creator_id, p1.uploader_id)
assert_equal(ti1.creator_id, p1.versions.last.updater_id)

View File

@@ -6,6 +6,7 @@ class TagTest < ActiveSupport::TestCase
CurrentUser.user = user
CurrentUser.ip_addr = "127.0.0.1"
MEMCACHE.flush_all
Delayed::Worker.delay_jobs = false
end
teardown do

BIN
tmp/test.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB