diff --git a/Gemfile b/Gemfile index 800c7638f..11975f0ba 100644 --- a/Gemfile +++ b/Gemfile @@ -6,7 +6,9 @@ group :test do gem "mocha", :require => "mocha/setup" gem "ffaker", :git => "http://github.com/EmmanuelOga/ffaker.git" gem "simplecov", :require => false - gem "testrbl" + gem "pry" + gem "vcr" + gem "webmock" end group :assets do diff --git a/Gemfile.lock b/Gemfile.lock index 09ff60c5d..f56c21611 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -53,6 +53,7 @@ GEM activesupport (3.2.12) i18n (~> 0.6) multi_json (~> 1.0) + addressable (2.3.4) arel (3.0.2) awesome_print (1.1.0) aws-s3 (0.6.3) @@ -73,6 +74,7 @@ GEM capistrano chronic (0.9.1) coderay (1.0.9) + crack (0.3.2) daemons (1.1.9) delayed_job (3.0.5) activesupport (~> 3.0) @@ -178,7 +180,6 @@ GEM rack (~> 1.0) tilt (~> 1.1, != 1.3.0) term-ansicolor (1.1.4) - testrbl (0.2.0) therubyracer (0.11.4) libv8 (~> 3.11.8.12) ref @@ -198,6 +199,10 @@ GEM kgio (~> 2.6) rack raindrops (~> 0.7) + vcr (2.4.0) + webmock (1.11.0) + addressable (>= 2.2.7) + crack (>= 0.3.2) webrobots (0.1.1) whenever (0.8.2) activesupport (>= 2.3.4) @@ -236,8 +241,9 @@ DEPENDENCIES simple_form simplecov term-ansicolor - testrbl therubyracer uglifier (>= 1.0.3) unicorn + vcr + webmock whenever diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 9d524df13..f96bd0ee9 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -408,22 +408,75 @@ Danbooru.Note = { Danbooru.Note.TranslationMode.active = true; $("#original-file-link").click(); - $("#image").one("click", Danbooru.Note.TranslationMode.create_note); - Danbooru.notice('Click on the image to create a note (shortcut is n)'); + $("#image").one("click", function() { $(".note-box").show() }); /* override the 'hide all note boxes' click event */ + $("#image").one("mousedown", Danbooru.Note.TranslationMode.Drag.start).one("mouseup", Danbooru.Note.TranslationMode.Drag.stop); + Danbooru.notice('Click or drag on the image to create a note (shortcut is n)'); }, stop: function() { Danbooru.Note.TranslationMode.active = false; }, - create_note: function(e) { + create_note: function(e,dragged,x,y,w,h) { Danbooru.Note.TranslationMode.active = false; var offset = $("#image").offset(); - Danbooru.Note.new(e.pageX - offset.left, e.pageY - offset.top); + + if(dragged) { + if(w > 9 && h > 9) { /* minimum note size: 10px */ + Danbooru.Note.new(x-offset.left,y-offset.top,w,h); + } + } else { + Danbooru.Note.new(e.pageX - offset.left, e.pageY - offset.top); + } Danbooru.Note.TranslationMode.stop(); $(".note-box").show(); e.stopPropagation(); e.preventDefault(); + }, + + Drag: { + dragging: false, + dragStartX: 0, + dragStartY: 0, + dragDistanceY: 0, + dragDistanceY: 0, + + start: function (e) { + e.preventDefault(); /* don't drag the image */ + $(window).mousemove(Danbooru.Note.TranslationMode.Drag.drag); + Danbooru.Note.TranslationMode.Drag.dragStartX = e.pageX; + Danbooru.Note.TranslationMode.Drag.dragStartY = e.pageY; + }, + + drag: function (e) { + Danbooru.Note.TranslationMode.Drag.dragDistanceX = e.pageX - Danbooru.Note.TranslationMode.Drag.dragStartX; + Danbooru.Note.TranslationMode.Drag.dragDistanceY = e.pageY - Danbooru.Note.TranslationMode.Drag.dragStartY; + + if(Danbooru.Note.TranslationMode.Drag.dragDistanceX > 9 && Danbooru.Note.TranslationMode.Drag.dragDistanceY > 9) { + Danbooru.Note.TranslationMode.Drag.dragging = true; /* must drag at least 10pixels (minimum note size) in both dimensions. */ + } + if(Danbooru.Note.TranslationMode.Drag.dragging) { + var offset = $("#image").offset(); + $('#note-helper').css({ /* preview of the note you are dragging */ + display: 'block', + left: (Danbooru.Note.TranslationMode.Drag.dragStartX - offset.left + 1), + top: (Danbooru.Note.TranslationMode.Drag.dragStartY - offset.top + 1), + width: (Danbooru.Note.TranslationMode.Drag.dragDistanceX - 3), + height: (Danbooru.Note.TranslationMode.Drag.dragDistanceY - 3) + }); + } + }, + + stop: function (e) { + $(window).unbind("mousemove"); + if(Danbooru.Note.TranslationMode.Drag.dragging) { + $('#note-helper').css({display:'none'}); + Danbooru.Note.TranslationMode.create_note(e, true, Danbooru.Note.TranslationMode.Drag.dragStartX, Danbooru.Note.TranslationMode.Drag.dragStartY, Danbooru.Note.TranslationMode.Drag.dragDistanceX-1, Danbooru.Note.TranslationMode.Drag.dragDistanceY-1); + Danbooru.Note.TranslationMode.Drag.dragging = false; /* border of the note is pixel-perfect on the preview border */ + } else { /* no dragging -> create a normal note */ + Danbooru.Note.TranslationMode.create_note(e); + } + } } }, diff --git a/app/assets/javascripts/posts.js b/app/assets/javascripts/posts.js index ded8b825c..5101d9bc9 100644 --- a/app/assets/javascripts/posts.js +++ b/app/assets/javascripts/posts.js @@ -92,7 +92,7 @@ Danbooru.Post.initialize_shortcuts = function() { $(document).bind("keydown.q", function(e) { - $("#tags").trigger("focus"); + $("#tags").trigger("focus").selectEnd(); e.preventDefault(); }); diff --git a/app/assets/stylesheets/specific/artists.css.scss b/app/assets/stylesheets/specific/artists.css.scss index 30cc10e6b..986660d71 100644 --- a/app/assets/stylesheets/specific/artists.css.scss +++ b/app/assets/stylesheets/specific/artists.css.scss @@ -21,6 +21,10 @@ div#c-artists { textarea { height: 5em; } + + .hint { + display: block; + } } div.recent-posts { diff --git a/app/assets/stylesheets/specific/notes.css.scss b/app/assets/stylesheets/specific/notes.css.scss index 8a1225652..a4219db33 100644 --- a/app/assets/stylesheets/specific/notes.css.scss +++ b/app/assets/stylesheets/specific/notes.css.scss @@ -55,6 +55,14 @@ div#note-container { border: 1px solid red; } } + + div#note-helper { + position: absolute; + border: 1px solid red; + opacity: 0.6; + display: none; + background: white; + } } div.note-edit-dialog { diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index fa1d6f463..989beb816 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -5,6 +5,7 @@ class ApplicationController < ActionController::Base before_filter :set_current_user after_filter :reset_current_user before_filter :set_title + before_filter :normalize_search before_filter :set_started_at_session before_filter :api_check layout "default" @@ -100,4 +101,8 @@ protected def set_title @page_title = Danbooru.config.app_name + "/#{params[:controller]}" end + + def normalize_search + params[:search] ||= {} + end end diff --git a/app/controllers/pools_controller.rb b/app/controllers/pools_controller.rb index 6036f3fda..7c3aeb6e5 100644 --- a/app/controllers/pools_controller.rb +++ b/app/controllers/pools_controller.rb @@ -68,6 +68,8 @@ class PoolsController < ApplicationController @pool = Pool.find(params[:id]) @version = PoolVersion.find(params[:version_id]) @pool.revert_to!(@version) - respond_with(@pool, :notice => "Pool reverted") + respond_with(@pool) do |format| + format.js + end end end diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 191ae621f..87df1473c 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -22,13 +22,8 @@ class PostsController < ApplicationController @post = Post.find(params[:id]) @post_flag = PostFlag.new(:post_id => @post.id) @post_appeal = PostAppeal.new(:post_id => @post.id) - - @children_post_set = PostSets::Post.new("parent:#{@post.id} -id:#{@post.id}", 1, 200) - @children_post_set.posts.reverse! - @parent_post_set = PostSets::Post.new("id:#{@post.parent_id} status:any") - @siblings_post_set = PostSets::Post.new("parent:#{@post.parent_id} -id:#{@post.parent_id}", 1, 200) - @siblings_post_set.posts.reverse! - + @parent_post_set = PostSets::PostRelationship.new(@post.parent_id, :include_deleted => @post.is_deleted?) + @children_post_set = PostSets::PostRelationship.new(@post.id, :include_deleted => @post.is_deleted?) respond_with(@post) end diff --git a/app/helpers/posts_helper.rb b/app/helpers/posts_helper.rb index b759efbed..7f09d3af9 100644 --- a/app/helpers/posts_helper.rb +++ b/app/helpers/posts_helper.rb @@ -41,16 +41,17 @@ module PostsHelper end end - def has_parent_message(post, parent_post_set, siblings_post_set) + def has_parent_message(post, parent_post_set) html = "" html << "This post belongs to a " html << link_to("parent", post_path(post.parent_id)) - html << " (deleted)" if parent_post_set.posts.first.is_deleted? + html << " (deleted)" if parent_post_set.parent.first.is_deleted? - if siblings_post_set.posts.count > 1 + sibling_count = parent_post_set.children.count - 1 + if sibling_count > 0 html << " and has " - text = siblings_post_set.posts.count > 2 ? "#{siblings_post_set.posts.count - 1} siblings" : "a sibling" + text = sibling_count == 1 ? "a sibling" : "#{sibling_count} siblings" html << link_to(text, posts_path(:tags => "parent:#{post.parent_id}")) end @@ -65,7 +66,7 @@ module PostsHelper html = "" html << "This post has " - text = children_post_set.posts.count == 1 ? "a child" : "#{children_post_set.posts.count} children" + text = children_post_set.children.count == 1 ? "a child" : "#{children_post_set.children.count} children" html << link_to(text, posts_path(:tags => "parent:#{post.id}")) html << " (#{link_to("learn more", wiki_pages_path(:title => "help:post_relationships"))}) " diff --git a/app/logical/post_sets/post_relationship.rb b/app/logical/post_sets/post_relationship.rb new file mode 100644 index 000000000..2cd085590 --- /dev/null +++ b/app/logical/post_sets/post_relationship.rb @@ -0,0 +1,24 @@ +module PostSets + class PostRelationship < PostSets::Post + attr_reader :parent, :children + + def initialize(parent_id, options = {}) + @parent = ::Post.where("id = ?", parent_id) + @children = ::Post.where("parent_id = ?", parent_id).order("id ASC") + if options[:include_deleted] + super("parent:#{parent_id} status:any") + else + @children = @children.where("is_deleted = ?", false) + super("parent:#{parent_id}") + end + end + + def posts + @parent + @children + end + + def presenter + ::PostSetPresenters::Post.new(self) + end + end +end diff --git a/app/models/pool.rb b/app/models/pool.rb index 7365d272a..d03b235d0 100644 --- a/app/models/pool.rb +++ b/app/models/pool.rb @@ -25,9 +25,9 @@ class Pool < ActiveRecord::Base params = {} if params.blank? if params[:name_matches].present? - params[:name_matches] = params[:name_matches].tr(" ", "_") - params[:name_matches] = "*#{params[:name_matches]}*" unless params[:name_matches] =~ /\*/ - q = q.where("name ilike ? escape E'\\\\'", params[:name_matches].to_escaped_for_sql_like) + name_matches = params[:name_matches].tr(" ", "_") + name_matches = "*#{name_matches}*" unless name_matches =~ /\*/ + q = q.where("name ilike ? escape E'\\\\'", name_matches.to_escaped_for_sql_like) end if params[:description_matches].present? diff --git a/app/models/post.rb b/app/models/post.rb index 377d562e8..6cf017458 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -31,6 +31,7 @@ class Post < ActiveRecord::Base has_many :disapprovals, :class_name => "PostDisapproval", :dependent => :destroy validates_uniqueness_of :md5 validates_presence_of :parent, :if => lambda {|rec| !rec.parent_id.nil?} + validate :post_is_not_its_own_parent attr_accessible :source, :rating, :tag_string, :old_tag_string, :last_noted_at, :parent_id, :as => [:member, :builder, :gold, :platinum, :contributor, :janitor, :moderator, :admin, :default] attr_accessible :is_rating_locked, :is_note_locked, :as => [:builder, :contributor, :janitor, :moderator, :admin] attr_accessible :is_status_locked, :as => [:admin] @@ -798,6 +799,13 @@ class Post < ActiveRecord::Base update_column(:score, 0) end + + def post_is_not_its_own_parent + if parent_id.present? && id == parent_id + errors[:base] << "Post cannot have itself as a parent" + false + end + end end module DeletionMethods diff --git a/app/models/tag.rb b/app/models/tag.rb index 347f4a35a..21797a22b 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -554,7 +554,7 @@ class Tag < ActiveRecord::Base q = q.reorder("name") else - q = q.reorder("post_count desc") + q = q.reorder("id desc") end q diff --git a/app/models/user.rb b/app/models/user.rb index 5df350e43..d84a31b07 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -351,7 +351,7 @@ class User < ActiveRecord::Base def create_mod_action if level_changed? - ModAction.create(:description => "#{name} level changed #{level_string(level_was)} -> #{level_string} by #{CurrentUser.name}") + ModAction.create(:description => %{"#{name}":/users/#{id} level changed #{level_string(level_was)} -> #{level_string}}) end end diff --git a/app/views/artists/_search.html.erb b/app/views/artists/_search.html.erb index 83dc58b57..a7e26f151 100644 --- a/app/views/artists/_search.html.erb +++ b/app/views/artists/_search.html.erb @@ -5,7 +5,7 @@
- <%= text_field "search", "name" %> + <%= text_field "search", "name", :value => params[:search][:name] %> You can search on any name or URL
@@ -15,7 +15,7 @@
- <%= select "search", "sort", [["Date", "date"], ["Name", "name"]] %> + <%= select "search", "sort", [["Date", "date"], ["Name", "name"]], :selected => params[:search][:sort] %>
diff --git a/app/views/layouts/_main_links.html.erb b/app/views/layouts/_main_links.html.erb index bc42a258b..cfb5e3d22 100644 --- a/app/views/layouts/_main_links.html.erb +++ b/app/views/layouts/_main_links.html.erb @@ -7,8 +7,8 @@ <%= nav_link_to("Posts", posts_path) %> <%= nav_link_to("Comments", comments_path(:group_by => "post")) %> <%= nav_link_to("Notes", notes_path(:group_by => "post")) %> - <%= nav_link_to("Artists", artists_path(:search => {:order => "date"})) %> - <%= nav_link_to("Tags", tags_path(:search => {:order => "date"})) %> + <%= nav_link_to("Artists", artists_path) %> + <%= nav_link_to("Tags", tags_path) %> <% if CurrentUser.is_moderator? %> <%= nav_link_to("Aliases", tag_aliases_path) %> <%= nav_link_to("Implications", tag_implications_path) %> diff --git a/app/views/pool_versions/index.html.erb b/app/views/pool_versions/index.html.erb index dafc759b7..4fb00410b 100644 --- a/app/views/pool_versions/index.html.erb +++ b/app/views/pool_versions/index.html.erb @@ -33,7 +33,7 @@ <%= compact_time pool_version.updated_at %> <% if CurrentUser.is_member? %> - <%= link_to "Revert", revert_pool_path(pool_version.pool_id, :version => pool_version.id) %> + <%= link_to "Revert", revert_pool_path(pool_version.pool_id, :version_id => pool_version.id), :method => :put, :remote => true %> <% end %> diff --git a/app/views/pools/_search.html.erb b/app/views/pools/_search.html.erb index f2de6c63d..f0f2f0ccf 100644 --- a/app/views/pools/_search.html.erb +++ b/app/views/pools/_search.html.erb @@ -5,7 +5,7 @@
- <%= text_field "search", "name_matches" %> + <%= text_field "search", "name_matches", :value => params[:search][:name_matches] %>
@@ -14,7 +14,7 @@
- <%= text_field "search", "description_matches" %> + <%= text_field "search", "description_matches", :value => params[:search][:description_matches] %>
@@ -23,7 +23,7 @@