From 3e2b2e741839bff5ceea2f6e0519a27360d73f0c Mon Sep 17 00:00:00 2001 From: albert Date: Wed, 3 Aug 2011 18:32:57 -0400 Subject: [PATCH 01/11] added new tag batch change model --- app/controllers/moderator/tags_controller.rb | 14 +++++++++ app/logical/moderator/tag_batch_change.rb | 24 ++++++++++++++ app/models/tag.rb | 17 +--------- app/views/moderator/tags/edit.html.erb | 21 +++++++++++++ config/routes.rb | 2 +- test/unit/moderator/tag_batch_change_test.rb | 33 ++++++++++++++++++++ 6 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 app/controllers/moderator/tags_controller.rb create mode 100644 app/logical/moderator/tag_batch_change.rb create mode 100644 app/views/moderator/tags/edit.html.erb create mode 100644 test/unit/moderator/tag_batch_change_test.rb diff --git a/app/controllers/moderator/tags_controller.rb b/app/controllers/moderator/tags_controller.rb new file mode 100644 index 000000000..d579d7651 --- /dev/null +++ b/app/controllers/moderator/tags_controller.rb @@ -0,0 +1,14 @@ +module Moderator + class TagsController < ApplicationController + before_filter :moderator_only + + 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 + end +end diff --git a/app/logical/moderator/tag_batch_change.rb b/app/logical/moderator/tag_batch_change.rb new file mode 100644 index 000000000..835612239 --- /dev/null +++ b/app/logical/moderator/tag_batch_change.rb @@ -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 diff --git a/app/models/tag.rb b/app/models/tag.rb index ee6d65cf6..2df386603 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -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 @@ -367,7 +353,6 @@ class Tag < ActiveRecord::Base include CategoryMethods extend StatisticsMethods include NameMethods - extend UpdateMethods extend ParseMethods extend SuggestionMethods end diff --git a/app/views/moderator/tags/edit.html.erb b/app/views/moderator/tags/edit.html.erb new file mode 100644 index 000000000..0b56da32a --- /dev/null +++ b/app/views/moderator/tags/edit.html.erb @@ -0,0 +1,21 @@ +
+
+

Edit Tags

+ + <%= form_tag(moderator_tag_path, :method => :put) do %> +
+ + <%= text_field :tag, :predicate %> +
+ +
+ + <%= text_field :tag, :consequent %> +
+ +
+ <%= submit_tag %> +
+ <% end %> +
+
diff --git a/config/routes.rb b/config/routes.rb index dda1785a8..f7bc45a8a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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] diff --git a/test/unit/moderator/tag_batch_change_test.rb b/test/unit/moderator/tag_batch_change_test.rb new file mode 100644 index 000000000..c547b6a45 --- /dev/null +++ b/test/unit/moderator/tag_batch_change_test.rb @@ -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 From 09d7bba90a067033b318c43d236a3283708e1760 Mon Sep 17 00:00:00 2001 From: albert Date: Wed, 3 Aug 2011 18:40:37 -0400 Subject: [PATCH 02/11] added moderator/tag --- app/controllers/moderator/tags_controller.rb | 5 ++++ .../moderator/tags_controller_test.rb | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 test/functional/moderator/tags_controller_test.rb diff --git a/app/controllers/moderator/tags_controller.rb b/app/controllers/moderator/tags_controller.rb index d579d7651..43adc8a4e 100644 --- a/app/controllers/moderator/tags_controller.rb +++ b/app/controllers/moderator/tags_controller.rb @@ -1,6 +1,7 @@ module Moderator class TagsController < ApplicationController before_filter :moderator_only + rescue_from TagBatchChange::Error, :with => :error def edit end @@ -10,5 +11,9 @@ module Moderator 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 diff --git a/test/functional/moderator/tags_controller_test.rb b/test/functional/moderator/tags_controller_test.rb new file mode 100644 index 000000000..63a3bfa57 --- /dev/null +++ b/test/functional/moderator/tags_controller_test.rb @@ -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 From e106f70b6dafda52061f7b8d7d1c04ba5561c3cb Mon Sep 17 00:00:00 2001 From: albert Date: Thu, 4 Aug 2011 19:54:13 -0400 Subject: [PATCH 03/11] * Removed memcaching for TagImplication (too many latent race conditions) * Added Post.exact_tag_match to skip normalization/metatag parsing * Added DelayedJob support for tag alias/implication processing --- Gemfile.lock | 25 ++++----- app/controllers/tag_aliases_controller.rb | 1 + .../tag_implications_controller.rb | 1 + app/models/post.rb | 6 +++ app/models/tag_alias.rb | 32 ++++++++---- app/models/tag_implication.rb | 52 +++++++------------ app/views/static/jquery_test.html.erb | 40 -------------- app/views/static/terms_of_service.html.erb | 12 ++--- db/development_structure.sql | 4 ++ .../20100211191709_create_tag_aliases.rb | 2 + .../20100211191716_create_tag_implications.rb | 2 + script/testing/reset_post_1.sh | 4 -- test/factories/tag_alias.rb | 12 +++-- test/factories/tag_implication.rb | 12 +++-- .../functional/tag_aliases_controller_test.rb | 1 + .../tag_implications_controller_test.rb | 1 + test/unit/tag_alias_test.rb | 28 +++++----- test/unit/tag_implication_test.rb | 50 +++++------------- 18 files changed, 123 insertions(+), 162 deletions(-) delete mode 100644 app/views/static/jquery_test.html.erb delete mode 100755 script/testing/reset_post_1.sh diff --git a/Gemfile.lock b/Gemfile.lock index 6858f8b1a..c564232ab 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -46,7 +46,7 @@ GEM activesupport (= 3.1.0.rc1) activesupport (3.1.0.rc1) 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.2) 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,15 +76,15 @@ 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) @@ -109,16 +109,17 @@ 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.12) + hike (~> 1.2) rack (~> 1.0) - tilt (!= 1.3.0, ~> 1.1) + tilt (~> 1.1, != 1.3.0) super_exception_notifier (3.0.13) actionmailer 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) diff --git a/app/controllers/tag_aliases_controller.rb b/app/controllers/tag_aliases_controller.rb index 61f4a03d5..7e9f6df95 100644 --- a/app/controllers/tag_aliases_controller.rb +++ b/app/controllers/tag_aliases_controller.rb @@ -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 diff --git a/app/controllers/tag_implications_controller.rb b/app/controllers/tag_implications_controller.rb index 91ace2f76..36f829baf 100644 --- a/app/controllers/tag_implications_controller.rb +++ b/app/controllers/tag_implications_controller.rb @@ -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 diff --git a/app/models/post.rb b/app/models/post.rb index bac987551..2c9807bcc 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -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 @@ -501,6 +502,11 @@ class Post < ActiveRecord::Base 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) diff --git a/app/models/tag_alias.rb b/app/models/tag_alias.rb index 4eefe90fb..786c87951 100644 --- a/app/models/tag_alias.rb +++ b/app/models/tag_alias.rb @@ -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 diff --git a/app/models/tag_implication.rb b/app/models/tag_implication.rb index 17966d9e8..e0a497f87 100644 --- a/app/models/tag_implication.rb +++ b/app/models/tag_implication.rb @@ -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 diff --git a/app/views/static/jquery_test.html.erb b/app/views/static/jquery_test.html.erb deleted file mode 100644 index 261abb8cc..000000000 --- a/app/views/static/jquery_test.html.erb +++ /dev/null @@ -1,40 +0,0 @@ -
-
- Lorem ipsum -
- -
- Lorem ipsum -
-
- - \ No newline at end of file diff --git a/app/views/static/terms_of_service.html.erb b/app/views/static/terms_of_service.html.erb index f99b6f3de..675c9df42 100644 --- a/app/views/static/terms_of_service.html.erb +++ b/app/views/static/terms_of_service.html.erb @@ -1,6 +1,6 @@
-

Terms of Service

+

Terms of Service

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.

    @@ -13,13 +13,13 @@
-
Post/Comment Limiting
+

Post/Comment Limiting

You cannot upload a post or comment during the first week of signing up.

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.

-
Prohibited Content
+

Prohibited Content

In addition, you may not use the Site to upload any of the following:

  • Non-anime: Photographs of American porn actresses, for example, are prohibited. Photographs of cosplayers, figures, or prominent figures in the industry are acceptable.
  • @@ -34,7 +34,7 @@
-

Copyright Infringement

+

Copyright Infringement

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:

    @@ -45,14 +45,14 @@
-

Privacy Policy

+

Privacy Policy

The Site will not disclose the IP address, email address, password, or DMails of any user except to the staff.

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.

-

Agreement

+

Agreement

By clicking on the "I Agree" link, you have read all the terms and have agreed to them.

<%= link_to("I Agree", params[:url] || "/", :onclick => "Cookie.put('tos', '1')") %> | <%= link_to("Cancel", "/") %>

diff --git a/db/development_structure.sql b/db/development_structure.sql index 91fbfe239..3317975ee 100644 --- a/db/development_structure.sql +++ b/db/development_structure.sql @@ -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 ); diff --git a/db/migrate/20100211191709_create_tag_aliases.rb b/db/migrate/20100211191709_create_tag_aliases.rb index 329fa58b4..7b943c3e4 100644 --- a/db/migrate/20100211191709_create_tag_aliases.rb +++ b/db/migrate/20100211191709_create_tag_aliases.rb @@ -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 diff --git a/db/migrate/20100211191716_create_tag_implications.rb b/db/migrate/20100211191716_create_tag_implications.rb index 8db2a6975..2e2e70ebb 100644 --- a/db/migrate/20100211191716_create_tag_implications.rb +++ b/db/migrate/20100211191716_create_tag_implications.rb @@ -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 diff --git a/script/testing/reset_post_1.sh b/script/testing/reset_post_1.sh deleted file mode 100755 index 176111dbb..000000000 --- a/script/testing/reset_post_1.sh +++ /dev/null @@ -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 diff --git a/test/factories/tag_alias.rb b/test/factories/tag_alias.rb index aebf276b8..058108709 100644 --- a/test/factories/tag_alias.rb +++ b/test/factories/tag_alias.rb @@ -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 diff --git a/test/factories/tag_implication.rb b/test/factories/tag_implication.rb index 86b20a633..205c63a58 100644 --- a/test/factories/tag_implication.rb +++ b/test/factories/tag_implication.rb @@ -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 diff --git a/test/functional/tag_aliases_controller_test.rb b/test/functional/tag_aliases_controller_test.rb index 81977549f..ae7aa6fde 100644 --- a/test/functional/tag_aliases_controller_test.rb +++ b/test/functional/tag_aliases_controller_test.rb @@ -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 diff --git a/test/functional/tag_implications_controller_test.rb b/test/functional/tag_implications_controller_test.rb index 6fb742839..5278cef62 100644 --- a/test/functional/tag_implications_controller_test.rb +++ b/test/functional/tag_implications_controller_test.rb @@ -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 diff --git a/test/unit/tag_alias_test.rb b/test/unit/tag_alias_test.rb index f38d92425..19713df88 100644 --- a/test/unit/tag_alias_test.rb +++ b/test/unit/tag_alias_test.rb @@ -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 diff --git a/test/unit/tag_implication_test.rb b/test/unit/tag_implication_test.rb index 84e10d5c2..2a61969e4 100644 --- a/test/unit/tag_implication_test.rb +++ b/test/unit/tag_implication_test.rb @@ -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) From c078178fc461ba903753745b432ea733869675b4 Mon Sep 17 00:00:00 2001 From: albert Date: Fri, 5 Aug 2011 19:07:28 -0400 Subject: [PATCH 04/11] added whenever (cron replacement), fixed related tag calculation updates (now delayed), tag subscriptions now calculated on cron --- Gemfile | 1 + Gemfile.lock | 5 +++++ app/models/post.rb | 20 ++++++++++++-------- app/models/tag.rb | 4 ++-- config/schedule.rb | 5 +++++ test/unit/post_test.rb | 8 +++++++- test/unit/tag_test.rb | 1 + tmp/test.jpg | Bin 0 -> 28086 bytes 8 files changed, 33 insertions(+), 11 deletions(-) create mode 100644 config/schedule.rb create mode 100644 tmp/test.jpg diff --git a/Gemfile b/Gemfile index a3bd122c4..800b5dc6c 100644 --- a/Gemfile +++ b/Gemfile @@ -20,3 +20,4 @@ gem "mechanize" gem "nokogiri" gem "meta_search", :git => "git://github.com/ernie/meta_search.git" gem "silent-postgres" +gem "whenever", :require => false \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index c564232ab..c7b2c5032 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,6 +16,7 @@ GIT GEM remote: http://gemcutter.org/ specs: + aaronh-chronic (0.3.9) actionmailer (3.1.0.rc1) actionpack (= 3.1.0.rc1) mail (~> 2.3.0) @@ -124,6 +125,9 @@ GEM 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 @@ -146,3 +150,4 @@ DEPENDENCIES simple_form simplecov super_exception_notifier + whenever diff --git a/app/models/post.rb b/app/models/post.rb index 2c9807bcc..78610f88e 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -488,16 +488,20 @@ 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 diff --git a/app/models/tag.rb b/app/models/tag.rb index 2df386603..610715317 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -310,11 +310,11 @@ class Tag < ActiveRecord::Base module RelationMethods def 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 diff --git a/config/schedule.rb b/config/schedule.rb new file mode 100644 index 000000000..53fdc0a39 --- /dev/null +++ b/config/schedule.rb @@ -0,0 +1,5 @@ +set :output, "/var/log/whenever.log" + +every 1.hour do + TagSubscription.process_all +end diff --git a/test/unit/post_test.rb b/test/unit/post_test.rb index 7303b3d31..dfb18646f 100644 --- a/test/unit/post_test.rb +++ b/test/unit/post_test.rb @@ -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") diff --git a/test/unit/tag_test.rb b/test/unit/tag_test.rb index 0fecf7e52..bf6941788 100644 --- a/test/unit/tag_test.rb +++ b/test/unit/tag_test.rb @@ -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 diff --git a/tmp/test.jpg b/tmp/test.jpg new file mode 100644 index 0000000000000000000000000000000000000000..253c508d9c84cee6433f57c5c756f06dc25534a8 GIT binary patch literal 28086 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 PaJbDXdPt*R{r^n>pWW2} literal 0 HcmV?d00001 From 4f0273143b05f868a276a5f8d2a02bb19f8e99d3 Mon Sep 17 00:00:00 2001 From: albert Date: Fri, 5 Aug 2011 19:33:39 -0400 Subject: [PATCH 05/11] bug fixes --- app/logical/post_sets/artist.rb | 2 +- app/models/tag.rb | 5 ++++- app/presenters/post_set_presenters/post.rb | 17 ++++++++++++----- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/app/logical/post_sets/artist.rb b/app/logical/post_sets/artist.rb index cdd822f8f..e3cc9371f 100644 --- a/app/logical/post_sets/artist.rb +++ b/app/logical/post_sets/artist.rb @@ -1,5 +1,5 @@ module PostSets - class Artist < Post + class Artist < PostSets::Post attr_reader :artist def initialize(artist) diff --git a/app/models/tag.rb b/app/models/tag.rb index 610715317..9282780ba 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -309,6 +309,7 @@ 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) update_attributes(:related_tags => RelatedTagCalculator.convert_hash_to_string(counts), :related_tags_updated_at => Time.now) end @@ -331,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 @@ -354,5 +356,6 @@ class Tag < ActiveRecord::Base extend StatisticsMethods include NameMethods extend ParseMethods + include RelationMethods extend SuggestionMethods end diff --git a/app/presenters/post_set_presenters/post.rb b/app/presenters/post_set_presenters/post.rb index 0ce1a4b46..07cd2e9bd 100644 --- a/app/presenters/post_set_presenters/post.rb +++ b/app/presenters/post_set_presenters/post.rb @@ -4,11 +4,18 @@ module PostSetPresenters 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 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 posts From 80683f3f1779a0fad46512e1fe2cdd8b8f478792 Mon Sep 17 00:00:00 2001 From: albert Date: Sat, 6 Aug 2011 15:50:37 -0400 Subject: [PATCH 06/11] updated rails version --- Gemfile | 2 +- Gemfile.lock | 64 ++++++++++++----------- app/controllers/admin/posts_controller.rb | 6 --- app/controllers/admin/users_controller.rb | 7 --- app/views/admin/tags/edit.html.erb | 10 ---- 5 files changed, 34 insertions(+), 55 deletions(-) delete mode 100644 app/controllers/admin/posts_controller.rb delete mode 100644 app/controllers/admin/users_controller.rb delete mode 100644 app/views/admin/tags/edit.html.erb diff --git a/Gemfile b/Gemfile index 800b5dc6c..441eadebb 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,7 @@ group :test do gem "simplecov", :require => false end -gem "rails", "3.1.0.rc1" +gem "rails", "3.1.0.rc4" gem "pg" gem "memcache-client", :require => "memcache" gem "imagesize", :require => "image_size" diff --git a/Gemfile.lock b/Gemfile.lock index c7b2c5032..b094a0a82 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,35 +17,35 @@ GEM remote: http://gemcutter.org/ specs: aaronh-chronic (0.3.9) - actionmailer (3.1.0.rc1) - actionpack (= 3.1.0.rc1) + actionmailer (3.1.0.rc4) + actionpack (= 3.1.0.rc4) mail (~> 2.3.0) - actionpack (3.1.0.rc1) - activemodel (= 3.1.0.rc1) - activesupport (= 3.1.0.rc1) + actionpack (3.1.0.rc4) + activemodel (= 3.1.0.rc4) + activesupport (= 3.1.0.rc4) builder (~> 3.0.0) erubis (~> 2.7.0) - i18n (~> 0.6.0beta1) - rack (~> 1.3.0.beta2) + i18n (~> 0.6) + rack (~> 1.3.0) rack-cache (~> 1.0.1) rack-mount (~> 0.8.1) rack-test (~> 0.6.0) - sprockets (~> 2.0.0.beta.5) + sprockets (~> 2.0.0.beta.10) tzinfo (~> 0.3.27) - activemodel (3.1.0.rc1) - activesupport (= 3.1.0.rc1) + activemodel (3.1.0.rc4) + activesupport (= 3.1.0.rc4) 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) + i18n (~> 0.6) + activerecord (3.1.0.rc4) + activemodel (= 3.1.0.rc4) + activesupport (= 3.1.0.rc4) 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) + activeresource (3.1.0.rc4) + activemodel (= 3.1.0.rc4) + activesupport (= 3.1.0.rc4) + activesupport (3.1.0.rc4) multi_json (~> 1.0) arel (2.1.4) bcrypt-ruby (2.1.4) @@ -55,7 +55,7 @@ GEM activesupport (~> 3.0) daemons erubis (2.7.0) - factory_girl (2.0.2) + factory_girl (2.0.3) haml (3.1.2) hike (1.2.0) i18n (0.6.0) @@ -87,21 +87,23 @@ GEM rack 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.rc4) + actionmailer (= 3.1.0.rc4) + actionpack (= 3.1.0.rc4) + activerecord (= 3.1.0.rc4) + activeresource (= 3.1.0.rc4) + activesupport (= 3.1.0.rc4) 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.rc4) + railties (3.1.0.rc4) + actionpack (= 3.1.0.rc4) + activesupport (= 3.1.0.rc4) 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) @@ -110,7 +112,7 @@ GEM simplecov (0.4.2) simplecov-html (~> 0.4.4) simplecov-html (0.4.5) - sprockets (2.0.0.beta.12) + sprockets (2.0.0.beta.13) hike (~> 1.2) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) @@ -144,7 +146,7 @@ DEPENDENCIES mocha nokogiri pg - rails (= 3.1.0.rc1) + rails (= 3.1.0.rc4) shoulda silent-postgres simple_form diff --git a/app/controllers/admin/posts_controller.rb b/app/controllers/admin/posts_controller.rb deleted file mode 100644 index 70c6c2ff5..000000000 --- a/app/controllers/admin/posts_controller.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Admin - class PostsController - def edit - end - end -end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb deleted file mode 100644 index 58404a3e2..000000000 --- a/app/controllers/admin/users_controller.rb +++ /dev/null @@ -1,7 +0,0 @@ -class Admin::UsersController < ApplicationController - def edit - end - - def update - end -end diff --git a/app/views/admin/tags/edit.html.erb b/app/views/admin/tags/edit.html.erb deleted file mode 100644 index 17f42b5b8..000000000 --- a/app/views/admin/tags/edit.html.erb +++ /dev/null @@ -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" %> - -
-
From c92bdf491e4661c830fbb8204b797588ce9e6e5d Mon Sep 17 00:00:00 2001 From: albert Date: Sat, 6 Aug 2011 16:22:49 -0400 Subject: [PATCH 07/11] updated to rails 3.1.rc5 --- Gemfile | 6 +- Gemfile.lock | 63 ++- config/application.rb | 7 +- config/environments/development.rb | 3 + config/environments/production.rb | 5 +- config/initializers/inflections.rb | 15 +- config/initializers/wrap_parameters.rb | 4 +- public/stylesheets/compiled/default.css | 550 ------------------------ 8 files changed, 57 insertions(+), 596 deletions(-) delete mode 100644 public/stylesheets/compiled/default.css diff --git a/Gemfile b/Gemfile index 441eadebb..ce006c104 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,11 @@ group :test do gem "simplecov", :require => false end -gem "rails", "3.1.0.rc4" +group :assets do + +end + +gem "rails", "3.1.0.rc5" gem "pg" gem "memcache-client", :require => "memcache" gem "imagesize", :require => "image_size" diff --git a/Gemfile.lock b/Gemfile.lock index b094a0a82..352b3e780 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,35 +17,34 @@ GEM remote: http://gemcutter.org/ specs: aaronh-chronic (0.3.9) - actionmailer (3.1.0.rc4) - actionpack (= 3.1.0.rc4) + actionmailer (3.1.0.rc5) + actionpack (= 3.1.0.rc5) mail (~> 2.3.0) - actionpack (3.1.0.rc4) - activemodel (= 3.1.0.rc4) - activesupport (= 3.1.0.rc4) + 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) - rack (~> 1.3.0) - rack-cache (~> 1.0.1) + rack (~> 1.3.1) + rack-cache (~> 1.0.2) rack-mount (~> 0.8.1) rack-test (~> 0.6.0) - sprockets (~> 2.0.0.beta.10) - tzinfo (~> 0.3.27) - activemodel (3.1.0.rc4) - activesupport (= 3.1.0.rc4) + 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) - activerecord (3.1.0.rc4) - activemodel (= 3.1.0.rc4) - activesupport (= 3.1.0.rc4) - arel (~> 2.1.1) - tzinfo (~> 0.3.27) - activeresource (3.1.0.rc4) - activemodel (= 3.1.0.rc4) - activesupport (= 3.1.0.rc4) - activesupport (3.1.0.rc4) + 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.4) bcrypt-ruby (2.1.4) @@ -87,17 +86,17 @@ GEM rack rack-test (0.6.1) rack (>= 1.0) - rails (3.1.0.rc4) - actionmailer (= 3.1.0.rc4) - actionpack (= 3.1.0.rc4) - activerecord (= 3.1.0.rc4) - activeresource (= 3.1.0.rc4) - activesupport (= 3.1.0.rc4) + 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.rc4) - railties (3.1.0.rc4) - actionpack (= 3.1.0.rc4) - activesupport (= 3.1.0.rc4) + 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) @@ -115,7 +114,7 @@ GEM sprockets (2.0.0.beta.13) hike (~> 1.2) rack (~> 1.0) - tilt (~> 1.1, != 1.3.0) + tilt (!= 1.3.0, ~> 1.1) super_exception_notifier (3.0.13) actionmailer rake @@ -146,7 +145,7 @@ DEPENDENCIES mocha nokogiri pg - rails (= 3.1.0.rc4) + rails (= 3.1.0.rc5) shoulda silent-postgres simple_form diff --git a/config/application.rb b/config/application.rb index adbdc8097..3a8640d4a 100644 --- a/config/application.rb +++ b/config/application.rb @@ -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 diff --git a/config/environments/development.rb b/config/environments/development.rb index 1f07c0aa4..384fe56c6 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -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 diff --git a/config/environments/production.rb b/config/environments/production.rb index 3fd4a3812..597b4909f 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -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) diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index e1e6e8322..9e8b0131f 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -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 diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb index c66629f84..16c324357 100644 --- a/config/initializers/wrap_parameters.rb +++ b/config/initializers/wrap_parameters.rb @@ -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] diff --git a/public/stylesheets/compiled/default.css b/public/stylesheets/compiled/default.css deleted file mode 100644 index f65378366..000000000 --- a/public/stylesheets/compiled/default.css +++ /dev/null @@ -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; } From cbf3347efff117f9a685c0c74743fbfee09be561 Mon Sep 17 00:00:00 2001 From: albert Date: Sat, 6 Aug 2011 17:28:20 -0400 Subject: [PATCH 08/11] added news ticker --- app/assets/javascripts/cookie.js | 14 +++++++--- app/assets/stylesheets/application.css.scss | 29 +++++++++++++++++++++ app/views/layouts/default.html.erb | 2 ++ app/views/news/_listing.html.erb | 6 +++++ 4 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 app/views/news/_listing.html.erb diff --git a/app/assets/javascripts/cookie.js b/app/assets/javascripts/cookie.js index 9c82ccce5..31ee60a08 100644 --- a/app/assets/javascripts/cookie.js +++ b/app/assets/javascripts/cookie.js @@ -51,10 +51,18 @@ return; } + if (this.get("hide-news-ticker") == "1") { + $("#news-ticker").hide(); + } else { + $("#close-news-ticker-link").observe("click", function(e) { + $("#news-ticker").hide(); + this.put("hide-news-ticker", "1", 1); + return false; + }); + } + if (this.get("hide-upgrade-account") != "1") { - if ($("upgrade-account")) { - $("upgrade-account").show(); - } + $("#upgrade-account").show(); } } })(); diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 6ff47eb82..129da4351 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -1046,3 +1046,32 @@ div#moderator-dashboard { font-size: 1.5em; } } + + + +/*** 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; + } +} \ No newline at end of file diff --git a/app/views/layouts/default.html.erb b/app/views/layouts/default.html.erb index 1ed3e6d0e..49f683caa 100644 --- a/app/views/layouts/default.html.erb +++ b/app/views/layouts/default.html.erb @@ -22,6 +22,8 @@
+ <%= render "news/listing" %> +

<%= link_to Danbooru.config.app_name, "/" %>