major refactoring of post sets and pagination, incomplete
This commit is contained in:
@@ -4,7 +4,7 @@ class PostsController < ApplicationController
|
|||||||
respond_to :html, :xml, :json
|
respond_to :html, :xml, :json
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@post_set = PostSets::Post.new(params[:tags], :page => params[:page], :before_id => params[:before_id])
|
@post_set = PostSets::Post.new(params[:tags], params)
|
||||||
respond_with(@post_set)
|
respond_with(@post_set)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
78
app/helpers/pagination_helper.rb
Normal file
78
app/helpers/pagination_helper.rb
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
module PaginationHelper
|
||||||
|
def smart_paginator(set, &block)
|
||||||
|
if set.page && set.page > 1000
|
||||||
|
sequential_paginator(set)
|
||||||
|
else
|
||||||
|
numbered_paginator(set, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def sequential_paginator(set)
|
||||||
|
html = "<menu>"
|
||||||
|
|
||||||
|
unless set.first_page?
|
||||||
|
html << '<li>' + link_to("« Previous", params.merge(:after_id => set.first_id)) + '</li>'
|
||||||
|
end
|
||||||
|
|
||||||
|
unless set.last_page?
|
||||||
|
html << '<li>' + link_to("Next »", params.merge(:before_id => set.last_id)) + '</li>'
|
||||||
|
end
|
||||||
|
|
||||||
|
html << "</menu>"
|
||||||
|
html.html_safe
|
||||||
|
end
|
||||||
|
|
||||||
|
def numbered_paginator(set, &block)
|
||||||
|
html = "<menu>"
|
||||||
|
window = 3
|
||||||
|
if set.total_pages <= (window * 2) + 5
|
||||||
|
1.upto(set.total_pages) do |page|
|
||||||
|
html << numbered_paginator_item(page, set.current_page, &block)
|
||||||
|
end
|
||||||
|
elsif set.current_page <= window + 2
|
||||||
|
1.upto(set.current_page + window) do |page|
|
||||||
|
html << numbered_paginator_item(page, set.current_page, &block)
|
||||||
|
end
|
||||||
|
html << numbered_paginator_item("...", set.current_page, &block)
|
||||||
|
html << numbered_paginator_final_item(set.total_pages, set.current_page, &block)
|
||||||
|
|
||||||
|
elsif set.current_page >= set.total_pages - (window + 1)
|
||||||
|
html << numbered_paginator_item(1, set.current_page, &block)
|
||||||
|
html << numbered_paginator_item("...", set.current_page, &block)
|
||||||
|
(set.current_page - window).upto(set.total_pages) do |page|
|
||||||
|
html << numbered_paginator_item(page, set.current_page, &block)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
html << numbered_paginator_item(1, set.current_page, &block)
|
||||||
|
html << numbered_paginator_item("...", set.current_page, &block)
|
||||||
|
(set.current_page - window).upto(set.current_page + window) do |page|
|
||||||
|
html << numbered_paginator_item(page, set.current_page, &block)
|
||||||
|
end
|
||||||
|
html << numbered_paginator_item("...", set.current_page, &block)
|
||||||
|
html << numbered_paginator_final_item(set.total_pages, set.current_page, &block)
|
||||||
|
end
|
||||||
|
html << "</menu>"
|
||||||
|
html.html_safe
|
||||||
|
end
|
||||||
|
|
||||||
|
def numbered_paginator_final_item(total_pages, current_page, &block)
|
||||||
|
if total_pages <= 1000
|
||||||
|
numbered_paginator_item(total_pages, current_page, &block)
|
||||||
|
else
|
||||||
|
""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def numbered_paginator_item(page, current_page, &block)
|
||||||
|
html = "<li>"
|
||||||
|
if page == "..."
|
||||||
|
html << "..."
|
||||||
|
elsif page == current_page
|
||||||
|
html << page.to_s
|
||||||
|
else
|
||||||
|
html << capture(page, &block)
|
||||||
|
end
|
||||||
|
html << "</li>"
|
||||||
|
html.html_safe
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -5,6 +5,20 @@ class Favorite
|
|||||||
"favorites_#{user_id.to_i % 10}"
|
"favorites_#{user_id.to_i % 10}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.sql_order_clause(post_ids, posts_table_alias = "posts")
|
||||||
|
if post_ids.empty?
|
||||||
|
return "#{posts_table_alias}.id desc"
|
||||||
|
end
|
||||||
|
|
||||||
|
conditions = []
|
||||||
|
|
||||||
|
post_ids.each_with_index do |post_id, n|
|
||||||
|
conditions << "when #{post_id} then #{n}"
|
||||||
|
end
|
||||||
|
|
||||||
|
"case #{posts_table_alias}.id " + conditions.join(" ") + " end"
|
||||||
|
end
|
||||||
|
|
||||||
def self.create(attributes)
|
def self.create(attributes)
|
||||||
user_id = attributes[:user_id]
|
user_id = attributes[:user_id]
|
||||||
post_id = attributes[:post_id]
|
post_id = attributes[:post_id]
|
||||||
@@ -26,6 +40,19 @@ class Favorite
|
|||||||
destroy_for_post(conditions[:post_id])
|
destroy_for_post(conditions[:post_id])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.find_post_ids(user_id, options)
|
||||||
|
limit = options[:limit] || 1 || Danbooru.config.posts_per_page
|
||||||
|
if options[:before_id]
|
||||||
|
select_values_sql("SELECT post_id FROM #{table_name_for(user_id)} WHERE id < ? ORDER BY id DESC LIMIT ?", options[:before_id], limit)
|
||||||
|
elsif options[:after_id]
|
||||||
|
select_values_sql("SELECT post_id FROM #{table_name_for(user_id)} WHERE id > ? ORDER BY id ASC LIMIT ?", options[:after_id], limit).reverse
|
||||||
|
elsif options[:offset]
|
||||||
|
select_values_sql("SELECT post_id FROM #{table_name_for(user_id)} ORDER BY id DESC LIMIT ? OFFSET ?", limit, options[:offset])
|
||||||
|
else
|
||||||
|
select_values_sql("SELECT post_id FROM #{table_name_for(user_id)} ORDER BY id DESC LIMIT ?", limit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.exists?(conditions)
|
def self.exists?(conditions)
|
||||||
if conditions[:user_id] && conditions[:post_id]
|
if conditions[:user_id] && conditions[:post_id]
|
||||||
@@ -39,26 +66,30 @@ class Favorite
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
def self.destroy_for_post_and_user(post_id, user_id)
|
||||||
def self.destroy_for_post_and_user(post_id, user_id)
|
execute_sql("DELETE FROM #{table_name_for(user_id)} WHERE post_id = #{post_id} AND user_id = #{user_id}")
|
||||||
execute_sql("DELETE FROM #{table_name_for(user_id)} WHERE post_id = #{post_id} AND user_id = #{user_id}")
|
end
|
||||||
end
|
|
||||||
|
def self.destroy_for_post(post)
|
||||||
def self.destroy_for_post(post)
|
0.upto(9) do |i|
|
||||||
0.upto(9) do |i|
|
execute_sql("DELETE FROM favorites_#{i} WHERE post_id = #{post.id}")
|
||||||
execute_sql("DELETE FROM favorites_#{i} WHERE post_id = #{post.id}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.destroy_for_user(user)
|
|
||||||
execute_sql("DELETE FROM #{table_name_for(user)} WHERE user_id = #{user.id}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.select_value_sql(sql, *params)
|
|
||||||
ActiveRecord::Base.select_value_sql(sql, *params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.execute_sql(sql, *params)
|
|
||||||
ActiveRecord::Base.execute_sql(sql, *params)
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.destroy_for_user(user)
|
||||||
|
execute_sql("DELETE FROM #{table_name_for(user)} WHERE user_id = #{user.id}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.select_value_sql(sql, *params)
|
||||||
|
ActiveRecord::Base.select_value_sql(sql, *params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.select_values_sql(sql, *params)
|
||||||
|
ActiveRecord::Base.select_values_sql(sql, *params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.execute_sql(sql, *params)
|
||||||
|
ActiveRecord::Base.execute_sql(sql, *params)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,45 +1,78 @@
|
|||||||
|
# A PostSet represents a paginated slice of posts. It is used in conjunction
|
||||||
|
# with the helpers to render the paginator.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
#
|
||||||
|
# @post_set = PostSets::Base.new(params)
|
||||||
|
# @post_set.extend(PostSets::Sequential)
|
||||||
|
# @post_set.extend(PostSets::Post)
|
||||||
|
|
||||||
module PostSets
|
module PostSets
|
||||||
class Base
|
class Base
|
||||||
attr_accessor :page, :before_id, :count, :posts
|
attr_reader :params, :posts
|
||||||
|
delegate :to_xml, :to_json, :to => :posts
|
||||||
def initialize(options = {})
|
|
||||||
@page = options[:page] ? options[:page].to_i : 1
|
def initialize(params)
|
||||||
@before_id = options[:before_id]
|
@params = params
|
||||||
load_posts
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_wiki?
|
# Should a return a paginated array of posts. This means it should have
|
||||||
false
|
# at most <limit> elements.
|
||||||
end
|
def posts
|
||||||
|
|
||||||
def use_sequential_paginator?
|
|
||||||
!use_numbered_paginator?
|
|
||||||
end
|
|
||||||
|
|
||||||
def use_numbered_paginator?
|
|
||||||
before_id.nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
def load_posts
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_xml
|
# Does this post set have a valid wiki page representation?
|
||||||
posts.to_xml
|
def has_wiki?
|
||||||
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_json
|
# Should return an array of strings representing the tags.
|
||||||
posts.to_json
|
def tags
|
||||||
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Given an ActiveRelation object, perform the necessary pagination to
|
||||||
|
# extract at most <limit> elements. Should return an array.
|
||||||
|
def slice(relation)
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
# For cases where we're not relying on the default pagination
|
||||||
|
# implementation (for example, if the ids are cached in a string)
|
||||||
|
# then pass in the offset/before_id/after_id parameters here.
|
||||||
|
def pagination_options
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
# This method should throw an exception if for whatever reason the query
|
||||||
|
# is invalid or forbidden.
|
||||||
|
def validate
|
||||||
|
end
|
||||||
|
|
||||||
|
# Clear out any memoized instance variables.
|
||||||
|
def reload
|
||||||
|
@posts = nil
|
||||||
|
@presenter = nil
|
||||||
|
@tag_string = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def tag_string
|
||||||
|
@tag_string ||= tags.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_first_page?
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_last_page?
|
||||||
|
posts.size == 0
|
||||||
|
end
|
||||||
|
|
||||||
def presenter
|
def presenter
|
||||||
@presenter ||= PostSetPresenter.new(self)
|
@presenter ||= PostSetPresenter.new(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def offset
|
|
||||||
((page < 1) ? 0 : (page - 1)) * count
|
|
||||||
end
|
|
||||||
|
|
||||||
def limit
|
def limit
|
||||||
Danbooru.config.posts_per_page
|
Danbooru.config.posts_per_page
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,18 +1,29 @@
|
|||||||
module PostSets
|
module PostSets
|
||||||
class Favorite < Base
|
module Favorite
|
||||||
attr_accessor :user
|
def user
|
||||||
|
@user ||= User.find(params[:id])
|
||||||
def initialize(user)
|
|
||||||
@user = user
|
|
||||||
super()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def tags
|
def tags
|
||||||
"fav:#{user.name}"
|
@tags ||= ["fav:#{user.name}"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_wiki?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def reload
|
||||||
|
super
|
||||||
|
@user = nil
|
||||||
|
@count = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def count
|
||||||
|
@count ||= Favorite.count(user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_posts
|
def posts
|
||||||
@posts = user.favorite_posts(:before_id => before_id)
|
@posts ||= user.favorites(pagination_options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
35
app/logical/post_sets/numbered.rb
Normal file
35
app/logical/post_sets/numbered.rb
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
module PostSets
|
||||||
|
module Numbered
|
||||||
|
attr_reader :page
|
||||||
|
|
||||||
|
def initialize(params)
|
||||||
|
super
|
||||||
|
@page = options[:page] ? options[:page].to_i : 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def total_pages
|
||||||
|
@total_pages ||= (count / limit.to_f).ceil.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def reload
|
||||||
|
super
|
||||||
|
@total_pages = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def slice(relation)
|
||||||
|
relation.offset(offset).all
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_options
|
||||||
|
{:offset => offset}
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_first_page?
|
||||||
|
offset == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def offset
|
||||||
|
((page < 1) ? 0 : (page - 1)) * limit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,33 +1,30 @@
|
|||||||
|
# This only works with the numbered paginator because of the way
|
||||||
|
# the association is stored.
|
||||||
module PostSets
|
module PostSets
|
||||||
class Pool < Base
|
module Pool
|
||||||
attr_reader :pool
|
def pool
|
||||||
|
@pool ||= Pool.find(params[:id])
|
||||||
def initialize(pool, options = {})
|
|
||||||
@pool = pool
|
|
||||||
@count = pool.post_id_array.size
|
|
||||||
super(options)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def tags
|
def tags
|
||||||
"pool:#{pool.name}"
|
["pool:#{pool.name}"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_wiki?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def count
|
||||||
|
pool.post_count
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_posts
|
def posts
|
||||||
@posts = pool.posts(:limit => limit, :offset => offset).order("posts.id")
|
@posts ||= pool.posts(pagination_options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def sorted_posts
|
def reload
|
||||||
sort_posts(@posts)
|
super
|
||||||
end
|
@pool = nil
|
||||||
|
|
||||||
private
|
|
||||||
def sort_posts(posts)
|
|
||||||
posts_by_id = posts.inject({}) do |hash, post|
|
|
||||||
hash[post.id] = post
|
|
||||||
hash
|
|
||||||
end
|
|
||||||
|
|
||||||
@pool.post_id_array.map {|x| posts_by_id[x]}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,78 +1,56 @@
|
|||||||
module PostSets
|
module PostSets
|
||||||
class Post < Base
|
module Post
|
||||||
class Error < Exception ; end
|
class Error < Exception ; end
|
||||||
|
|
||||||
attr_accessor :tags, :errors, :count
|
attr_accessor :tags, :count, :wiki_page, :artist, :suggestions
|
||||||
attr_accessor :wiki_page, :artist, :suggestions
|
|
||||||
|
def tags
|
||||||
def initialize(tags, options = {})
|
@tags ||= Tag.normalize(params[:tags])
|
||||||
super(options)
|
end
|
||||||
@tags = Tag.normalize(tags)
|
|
||||||
@errors = []
|
def count
|
||||||
load_associations
|
@count ||= ::Post.fast_count(tag_string)
|
||||||
load_suggestions
|
end
|
||||||
validate
|
|
||||||
|
def posts
|
||||||
|
@posts ||= slice(::Post.tag_match(tags).limit(limit))
|
||||||
|
end
|
||||||
|
|
||||||
|
def reload
|
||||||
|
super
|
||||||
|
@tags = nil
|
||||||
|
@tag_string = nil
|
||||||
|
@count = nil
|
||||||
|
@wiki_page = nil
|
||||||
|
@artist = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def wiki_page
|
||||||
|
@wiki_page ||= ::WikiPage.titled(tag_string).first
|
||||||
|
end
|
||||||
|
|
||||||
|
def artist
|
||||||
|
@artist ||= ::Artist.find_by_name(tag_string)
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_wiki?
|
def has_wiki?
|
||||||
is_single_tag?
|
is_single_tag?
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_errors?
|
|
||||||
errors.any?
|
|
||||||
end
|
|
||||||
|
|
||||||
def offset
|
|
||||||
x = (page - 1) * limit
|
|
||||||
if x < 0
|
|
||||||
x = 0
|
|
||||||
end
|
|
||||||
x
|
|
||||||
end
|
|
||||||
|
|
||||||
def is_single_tag?
|
def is_single_tag?
|
||||||
tag_array.size == 1
|
tag_array.size == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
def date_tag
|
|
||||||
tag_array.grep(/date:/).first
|
|
||||||
end
|
|
||||||
|
|
||||||
def load_associations
|
|
||||||
if is_single_tag?
|
|
||||||
@wiki_page = ::WikiPage.titled(tags).first
|
|
||||||
@artist = ::Artist.find_by_name(tags)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def load_posts
|
|
||||||
@count = ::Post.fast_count(tags)
|
|
||||||
@posts = ::Post.tag_match(tags).before_id(before_id).all(:order => "posts.id desc", :limit => limit, :offset => offset)
|
|
||||||
end
|
|
||||||
|
|
||||||
def load_suggestions
|
|
||||||
if count < limit && is_single_tag?
|
|
||||||
@suggestions = Tag.find_suggestions(tags)
|
|
||||||
else
|
|
||||||
@suggestions = []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag_array
|
def tag_array
|
||||||
@tag_array ||= Tag.scan_query(tags)
|
@tag_array ||= Tag.scan_query(tags)
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag
|
|
||||||
tag_array.first
|
|
||||||
end
|
|
||||||
|
|
||||||
def validate
|
def validate
|
||||||
|
super
|
||||||
validate_page
|
validate_page
|
||||||
validate_query_count
|
validate_query_count
|
||||||
rescue Error => x
|
|
||||||
@errors << x.to_s
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_page
|
def validate_page
|
||||||
if page > 1_000
|
if page > 1_000
|
||||||
raise Error.new("You cannot explicitly specify the page after page 1000")
|
raise Error.new("You cannot explicitly specify the page after page 1000")
|
||||||
|
|||||||
29
app/logical/post_sets/sequential.rb
Normal file
29
app/logical/post_sets/sequential.rb
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
module PostSets
|
||||||
|
module Sequential
|
||||||
|
attr_reader :before_id, :after_id
|
||||||
|
|
||||||
|
def initialize(params)
|
||||||
|
super
|
||||||
|
@before_id = params[:before_id]
|
||||||
|
@after_id = params[:after_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
def slice(relation)
|
||||||
|
if before_id
|
||||||
|
relation.where("id < ?", before_id).all
|
||||||
|
elsif after_id
|
||||||
|
relation.where("id > ?", after_id).order("id asc").all.reverse
|
||||||
|
else
|
||||||
|
relation.all
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_options
|
||||||
|
{:before_id => before_id, :after_id => after_id}
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_first_page?
|
||||||
|
before_id.nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,34 +1,29 @@
|
|||||||
module PostSets
|
module PostSets
|
||||||
class WikiPage < Base
|
module WikiPage
|
||||||
attr_reader :tag_name
|
def wiki_page
|
||||||
|
@wiki_page ||= begin
|
||||||
def initialize(tag_name)
|
if params[:id]
|
||||||
@tag_name = tag_name
|
::WikiPage.find(params[:id])
|
||||||
super()
|
elsif params[:tags]
|
||||||
|
::WikiPage.titled(params[:tags]).first
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_posts
|
def has_wiki?
|
||||||
@posts = ::Post.tag_match(tag_name).all(:order => "posts.id desc", :limit => limit, :offset => offset)
|
true
|
||||||
end
|
|
||||||
|
|
||||||
def limit
|
|
||||||
8
|
|
||||||
end
|
|
||||||
|
|
||||||
def offset
|
|
||||||
0
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def tags
|
def tags
|
||||||
[@tag_name]
|
@tags ||= Tag.normalize(wiki_page.title)
|
||||||
end
|
end
|
||||||
|
|
||||||
def use_sequential_paginator?
|
def posts
|
||||||
false
|
@posts ||= slice(::Post.tag_match(tag_string))
|
||||||
end
|
end
|
||||||
|
|
||||||
def use_numbered_paginator?
|
def count
|
||||||
false
|
@count ||= ::Post.fast_count(tag_string)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -72,9 +72,9 @@ class Pool < ActiveRecord::Base
|
|||||||
|
|
||||||
def posts(options = {})
|
def posts(options = {})
|
||||||
offset = options[:offset] || 0
|
offset = options[:offset] || 0
|
||||||
limit = options[:limit] || 20
|
limit = options[:limit] || Danbooru.config.posts_per_page
|
||||||
ids = post_id_array[offset, limit]
|
ids = post_id_array[offset, limit]
|
||||||
Post.where(["id IN (?)", ids])
|
Post.where(["id IN (?)", ids]).order(Favorite.sql_order_clause(ids))
|
||||||
end
|
end
|
||||||
|
|
||||||
def post_id_array
|
def post_id_array
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
class Post < ActiveRecord::Base
|
class Post < ActiveRecord::Base
|
||||||
class ApprovalError < Exception ; end
|
class ApprovalError < Exception ; end
|
||||||
class DisapprovalError < Exception ; end
|
class DisapprovalError < Exception ; end
|
||||||
|
class SearchError < Exception ; end
|
||||||
|
|
||||||
attr_accessor :old_tag_string, :old_parent_id
|
attr_accessor :old_tag_string, :old_parent_id
|
||||||
after_destroy :delete_files
|
after_destroy :delete_files
|
||||||
@@ -39,6 +40,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 :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 :hidden_from_moderation, lambda {where(["id IN (SELECT pd.post_id FROM post_disapprovals pd WHERE pd.user_id = ?)", CurrentUser.id])}
|
||||||
scope :before_id, lambda {|id| id.present? ? where(["posts.id < ?", id]) : where("TRUE")}
|
scope :before_id, lambda {|id| id.present? ? where(["posts.id < ?", id]) : where("TRUE")}
|
||||||
|
scope :after_id, lambda {|id| id.present? ? where("posts.id > ?", id) : where("true")}
|
||||||
scope :tag_match, lambda {|query| Post.tag_match_helper(query)}
|
scope :tag_match, lambda {|query| Post.tag_match_helper(query)}
|
||||||
search_methods :tag_match
|
search_methods :tag_match
|
||||||
|
|
||||||
@@ -374,36 +376,33 @@ class Post < ActiveRecord::Base
|
|||||||
Favorite.destroy_for_post(self)
|
Favorite.destroy_for_post(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_favorite(user)
|
def favorited_by?(user_id)
|
||||||
if user.is_a?(ActiveRecord::Base)
|
fav_string =~ /(?:\A| )fav:#{user_id}(?:\Z| )/
|
||||||
user_id = user.id
|
end
|
||||||
else
|
|
||||||
user_id = user
|
def add_favorite(user_id)
|
||||||
|
if user_id.is_a?(ActiveRecord::Base)
|
||||||
|
user_id = user_id.id
|
||||||
|
end
|
||||||
|
|
||||||
|
if favorited_by?(user_id)
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return false if fav_string =~ /(?:\A| )fav:#{user_id}(?:\Z| )/
|
|
||||||
self.fav_string += " fav:#{user_id}"
|
self.fav_string += " fav:#{user_id}"
|
||||||
self.fav_string.strip!
|
self.fav_string.strip!
|
||||||
|
update_attribute(:fav_string, fav_string)
|
||||||
# in order to avoid rerunning the callbacks, just update through raw sql
|
|
||||||
execute_sql("UPDATE posts SET fav_string = ? WHERE id = ?", fav_string, id)
|
|
||||||
|
|
||||||
Favorite.create(:user_id => user_id, :post_id => id)
|
Favorite.create(:user_id => user_id, :post_id => id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_favorite(user)
|
def remove_favorite(user_id)
|
||||||
if user.is_a?(ActiveRecord::Base)
|
if user_id.is_a?(ActiveRecord::Base)
|
||||||
user_id = user.id
|
user_id = user_id.id
|
||||||
else
|
|
||||||
user_id = user
|
|
||||||
end
|
end
|
||||||
|
|
||||||
self.fav_string.gsub!(/(?:\A| )fav:#{user_id}(?:\Z| )/, " ")
|
self.fav_string.gsub!(/(?:\A| )fav:#{user_id}(?:\Z| )/, " ")
|
||||||
self.fav_string.strip!
|
self.fav_string.strip!
|
||||||
|
update_attribute(:fav_string, fav_string)
|
||||||
# in order to avoid rerunning the callbacks, just update through raw sql
|
|
||||||
execute_sql("UPDATE posts SET fav_string = ? WHERE id = ?", fav_string, id)
|
|
||||||
|
|
||||||
Favorite.destroy(:user_id => user_id, :post_id => id)
|
Favorite.destroy(:user_id => user_id, :post_id => id)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -413,9 +412,9 @@ class Post < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
module SearchMethods
|
module SearchMethods
|
||||||
class SearchError < Exception ; end
|
|
||||||
|
|
||||||
def add_range_relation(arr, field, relation)
|
def add_range_relation(arr, field, relation)
|
||||||
|
return relation if arr.nil?
|
||||||
|
|
||||||
case arr[0]
|
case arr[0]
|
||||||
when :eq
|
when :eq
|
||||||
relation.where(["#{field} = ?", arr[1]])
|
relation.where(["#{field} = ?", arr[1]])
|
||||||
@@ -513,10 +512,10 @@ class Post < ActiveRecord::Base
|
|||||||
relation = add_range_relation(q[:character_tag_count], "posts.tag_count_character", relation)
|
relation = add_range_relation(q[:character_tag_count], "posts.tag_count_character", relation)
|
||||||
relation = add_range_relation(q[:tag_count], "posts.tag_count", relation)
|
relation = add_range_relation(q[:tag_count], "posts.tag_count", relation)
|
||||||
|
|
||||||
if q[:md5].any?
|
if q[:md5]
|
||||||
relation = relation.where(["posts.md5 IN (?)", q[:md5]])
|
relation = relation.where(["posts.md5 IN (?)", q[:md5]])
|
||||||
end
|
end
|
||||||
|
|
||||||
if q[:status] == "pending"
|
if q[:status] == "pending"
|
||||||
relation = relation.where("posts.is_pending = TRUE")
|
relation = relation.where("posts.is_pending = TRUE")
|
||||||
elsif q[:status] == "flagged"
|
elsif q[:status] == "flagged"
|
||||||
@@ -525,11 +524,11 @@ class Post < ActiveRecord::Base
|
|||||||
relation = relation.where("posts.is_deleted = TRUE")
|
relation = relation.where("posts.is_deleted = TRUE")
|
||||||
end
|
end
|
||||||
|
|
||||||
if q[:source].is_a?(String)
|
if q[:source]
|
||||||
relation = relation.where(["posts.source LIKE ? ESCAPE E'\\\\'", q[:source]])
|
relation = relation.where(["posts.source LIKE ? ESCAPE E'\\\\'", q[:source]])
|
||||||
end
|
end
|
||||||
|
|
||||||
if q[:subscriptions].any?
|
if q[:subscriptions]
|
||||||
relation = add_tag_subscription_relation(q[:subscriptions], relation)
|
relation = add_tag_subscription_relation(q[:subscriptions], relation)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ class Tag < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def parse_query(query, options = {})
|
def parse_query(query, options = {})
|
||||||
q = Hash.new {|h, k| h[k] = []}
|
q = {}
|
||||||
q[:tags] = {
|
q[:tags] = {
|
||||||
:related => [],
|
:related => [],
|
||||||
:include => [],
|
:include => [],
|
||||||
@@ -237,6 +237,7 @@ class Tag < ActiveRecord::Base
|
|||||||
q[:tags][:related] << "fav:#{User.name_to_id($2)}"
|
q[:tags][:related] << "fav:#{User.name_to_id($2)}"
|
||||||
|
|
||||||
when "sub"
|
when "sub"
|
||||||
|
q[:subscriptions] ||= []
|
||||||
q[:subscriptions] << $2
|
q[:subscriptions] << $2
|
||||||
|
|
||||||
when "md5"
|
when "md5"
|
||||||
|
|||||||
@@ -117,16 +117,13 @@ class User < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
module FavoriteMethods
|
module FavoriteMethods
|
||||||
def favorite_posts(options = {})
|
def favorites(options = {})
|
||||||
favorites_table = Favorite.table_name_for(id)
|
post_ids = Favorite.find_post_ids(id, options)
|
||||||
if options[:before_id]
|
if post_ids.any?
|
||||||
before_id_sql_fragment = ["favorites.id < ?", options[:before_id]]
|
Post.where("id in (?)", post_ids).order(Favorite.sql_order_clause(post_ids))
|
||||||
else
|
else
|
||||||
before_id_sql_fragment = "TRUE"
|
Post.where("false")
|
||||||
end
|
end
|
||||||
limit = options[:limit] || 20
|
|
||||||
|
|
||||||
Post.joins("JOIN #{favorites_table} AS favorites ON favorites.post_id = posts.id").where("favorites.user_id = ?", id).where(before_id_sql_fragment).order("favorite_id DESC").limit(limit).select("posts.*, favorites.id AS favorite_id")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
58
app/presenters/paginators/numbered.rb
Normal file
58
app/presenters/paginators/numbered.rb
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
module Paginators
|
||||||
|
class Numbered
|
||||||
|
attr_reader :template, :source
|
||||||
|
delegate :url, :total_pages, :current_page, :to => :source
|
||||||
|
|
||||||
|
def initialize(template, source)
|
||||||
|
@template = template
|
||||||
|
@source = source
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_html
|
||||||
|
html = "<menu>"
|
||||||
|
window = 3
|
||||||
|
if total_pages <= (window * 2) + 5
|
||||||
|
1.upto(total_pages) do |page|
|
||||||
|
html << pagination_item(page, current_page)
|
||||||
|
end
|
||||||
|
elsif current_page <= window + 2
|
||||||
|
1.upto(current_page + window) do |page|
|
||||||
|
html << pagination_item(page, current_page)
|
||||||
|
end
|
||||||
|
html << pagination_item("...", current_page)
|
||||||
|
html << pagination_item(total_pages, current_page)
|
||||||
|
|
||||||
|
elsif current_page >= total_pages - (window + 1)
|
||||||
|
html << pagination_item(1, current_page)
|
||||||
|
html << pagination_item("...", current_page)
|
||||||
|
(current_page - window).upto(total_pages) do |page|
|
||||||
|
html << pagination_item(page, current_page)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
html << pagination_item(1, current_page)
|
||||||
|
html << pagination_item("...", current_page)
|
||||||
|
(current_page - window).upto(current_page + window) do |page|
|
||||||
|
html << pagination_item(page, current_page)
|
||||||
|
end
|
||||||
|
html << pagination_item("...", current_page)
|
||||||
|
html << pagination_item(total_pages, current_page)
|
||||||
|
end
|
||||||
|
html << "</menu>"
|
||||||
|
html.html_safe
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
def pagination_item(page, current_page)
|
||||||
|
html = "<li>"
|
||||||
|
if page == "..."
|
||||||
|
html << "..."
|
||||||
|
elsif page == current_page
|
||||||
|
html << page.to_s
|
||||||
|
else
|
||||||
|
html << template.link_to(page, url(template, :page => page))
|
||||||
|
end
|
||||||
|
html << "</li>"
|
||||||
|
html.html_safe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -15,6 +15,7 @@ module Paginators
|
|||||||
[1, post_set.page].max
|
[1, post_set.page].max
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# TODO: this is not compatible with paginating favorites
|
||||||
def sequential_link(template)
|
def sequential_link(template)
|
||||||
template.posts_path(:tags => template.params[:tags], before_id => post_set.posts[-1].id, :page => nil)
|
template.posts_path(:tags => template.params[:tags], before_id => post_set.posts[-1].id, :page => nil)
|
||||||
end
|
end
|
||||||
|
|||||||
29
app/presenters/paginators/sequential.rb
Normal file
29
app/presenters/paginators/sequential.rb
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
module Paginators
|
||||||
|
class Sequential
|
||||||
|
attr_reader :template, :source
|
||||||
|
delegate :url, :to => :source
|
||||||
|
|
||||||
|
def initialize(template, source)
|
||||||
|
@template = template
|
||||||
|
@source = source
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_html
|
||||||
|
html = "<menu>"
|
||||||
|
html << '<li>' + template.link_to("« Previous", prev_url) + '</li>'
|
||||||
|
if next_url
|
||||||
|
html << '<li>' + template.link_to("Next »", next_url) + '</li>'
|
||||||
|
end
|
||||||
|
html << "</menu>"
|
||||||
|
html.html_safe
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_url
|
||||||
|
template.request.env["HTTP_REFERER"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_url
|
||||||
|
@next_url ||= url(template)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,11 +1,5 @@
|
|||||||
<div id="c-posts">
|
<div id="c-posts">
|
||||||
<div id="a-index">
|
<div id="a-index">
|
||||||
<% if @post_set.suggestions.any? %>
|
|
||||||
<div class="notice">
|
|
||||||
Maybe you meant: <%= @post_set.suggestions.map {|x| link_to(x, posts_path(:tags => x), :class => "tag-type-#{Tag.type_name(x)}" )}.to_sentence(:last_word_connector => ", or ", :two_words_connector => " or ") %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<aside id="sidebar">
|
<aside id="sidebar">
|
||||||
<section id="search-box">
|
<section id="search-box">
|
||||||
<h1>Search</h1>
|
<h1>Search</h1>
|
||||||
|
|||||||
@@ -5,5 +5,7 @@
|
|||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
<div class="paginator">
|
<div class="paginator">
|
||||||
<%= post_set.presenter.pagination_html(self) %>
|
<%= smart_paginator(post_set) do |page| %>
|
||||||
|
<%= link_to(page, posts_path(:page => page, :tags => params[:tags])) %>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<div id="c-uploads">
|
<div id="c-uploads">
|
||||||
<div id="a-new">
|
<div id="a-new">
|
||||||
|
<h1>Upload Post</h1>
|
||||||
|
|
||||||
<div id="upload-guide-notice">
|
<div id="upload-guide-notice">
|
||||||
<p>Before uploading, please read the <%= link_to "how to upload guide", wiki_pages_path(:title => "howto:upload") %>.</p>
|
<p>Before uploading, please read the <%= link_to "how to upload guide", wiki_pages_path(:title => "howto:upload") %>.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ class CreatePools < ActiveRecord::Migration
|
|||||||
t.column :description, :text
|
t.column :description, :text
|
||||||
t.column :is_active, :boolean, :null => false, :default => true
|
t.column :is_active, :boolean, :null => false, :default => true
|
||||||
t.column :post_ids, :text, :null => false, :default => ""
|
t.column :post_ids, :text, :null => false, :default => ""
|
||||||
|
t.column :post_count, :integer, :null => false, :default => 0
|
||||||
t.timestamps
|
t.timestamps
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user