EliteJournal 1.9.403 → 1.9.480
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/app/controllers/application.rb +6 -4
- data/app/controllers/atom_controller.rb +10 -17
- data/app/controllers/post_controller.rb +67 -36
- data/app/controllers/rss_controller.rb +12 -17
- data/app/controllers/tags_controller.rb +2 -2
- data/app/controllers/users_controller.rb +9 -3
- data/app/helpers/application_helper.rb +13 -23
- data/app/helpers/pagination_helper.rb +487 -0
- data/app/helpers/tags_helper.rb +1 -1
- data/app/models/post.rb +2 -0
- data/app/models/user.rb +4 -4
- data/app/views/account/info.rhtml +4 -0
- data/app/views/atom/feed.rxml +2 -2
- data/app/views/css/edit.rhtml +1 -1
- data/app/views/layouts/application.rhtml +5 -5
- data/app/views/{journal → post}/_comment.rhtml +1 -1
- data/app/views/{journal → post}/_post.rhtml +3 -3
- data/app/views/{journal → post}/_trackback.rhtml +0 -0
- data/app/views/{journal → post}/error.rhtml +0 -0
- data/app/views/post/index.rhtml +5 -0
- data/app/views/post/reply.rhtml +21 -12
- data/app/views/post/replyxml.rxml +2 -2
- data/app/views/{journal → post}/view.rhtml +0 -0
- data/app/views/rss/{index.rxml → feed.rxml} +1 -1
- data/app/views/users/index.rhtml +1 -1
- data/config/environment.rb +22 -9
- data/config/routes.rb +36 -0
- data/db/db-mysql.sql +2 -0
- data/db/db-postgresql.sql +2 -0
- data/db/db-sqlite.sql +2 -0
- data/db/development_structure.sql +67 -65
- data/elitejournal +3 -40
- data/public/dispatch.rb +2 -2
- metadata +12 -22
- data/app/controllers/journal_controller.rb +0 -53
- data/app/helpers/journal_helper.rb +0 -2
- data/app/views/journal/index.rhtml +0 -1
@@ -2,7 +2,7 @@ require_dependency 'user'
|
|
2
2
|
|
3
3
|
class ApplicationController < ActionController::Base
|
4
4
|
before_filter :configure_app, :except => [ :search, :face ]
|
5
|
-
before_filter :collect_info, :except => [ :search, :face ]
|
5
|
+
before_filter :collect_info, :except => [ :search, :face, :feed ]
|
6
6
|
|
7
7
|
private
|
8
8
|
def current_user
|
@@ -25,8 +25,7 @@ class ApplicationController < ActionController::Base
|
|
25
25
|
helper_method :owns_post?
|
26
26
|
|
27
27
|
def user_page?
|
28
|
-
|
29
|
-
((@params['controller'] == 'users') && (@params['action'] != 'index')) || (user_actions.include? @params['action'])
|
28
|
+
@params['user'] ? true : false
|
30
29
|
end
|
31
30
|
helper_method :user_page?
|
32
31
|
|
@@ -40,7 +39,7 @@ class ApplicationController < ActionController::Base
|
|
40
39
|
end
|
41
40
|
|
42
41
|
def redirect_to_main
|
43
|
-
redirect_to :controller => '
|
42
|
+
redirect_to :controller => 'post', :action => 'index'
|
44
43
|
end
|
45
44
|
|
46
45
|
def redirect_to_user
|
@@ -65,6 +64,9 @@ class ApplicationController < ActionController::Base
|
|
65
64
|
|
66
65
|
def configure_app
|
67
66
|
@app_config = YAML::load(File.open(File.dirname(__FILE__) + "/../../config/app.yml"))
|
67
|
+
if @params['user']
|
68
|
+
@user = User.find_by_username(@params['user']) || User.new
|
69
|
+
end
|
68
70
|
end
|
69
71
|
|
70
72
|
def collect_info
|
@@ -5,22 +5,15 @@ class AtomController < ApplicationController
|
|
5
5
|
caches_page :feed
|
6
6
|
|
7
7
|
def feed
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
if @params['user']
|
9
|
+
@title = @user.title
|
10
|
+
@subtitle = @user.subtitle
|
11
|
+
@posts = @user.posts.find_all(nil, 'created_at desc', @app_config['main']['num_posts']) rescue []
|
12
|
+
else
|
13
|
+
@posts = Post.find_all(nil, 'updated_at DESC', @app_config['main']['num_posts'])
|
14
|
+
@title = @app_config['main']['title']
|
15
|
+
@subtitle = @app_config['main']['subtitle']
|
16
|
+
end
|
17
|
+
#cache_page if ActionController::Base.perform_caching
|
11
18
|
end
|
12
|
-
|
13
|
-
# +method_missing+ provides the convenient /atom/username URLs to
|
14
|
-
# access a user's feed. If a non-existent user is passed, it will
|
15
|
-
# just return an empty feed.
|
16
|
-
def method_missing(method)
|
17
|
-
user = User.find_by_username(method.to_s)
|
18
|
-
@title = user.title
|
19
|
-
@subtitle = user.subtitle
|
20
|
-
@posts = user.posts.find_all(nil, 'created_at desc', @app_config['main']['num_posts']) rescue []
|
21
|
-
|
22
|
-
render_action 'feed'
|
23
|
-
cache_page if ActionController::Base.perform_caching
|
24
|
-
end
|
25
|
-
|
26
19
|
end
|
@@ -1,11 +1,52 @@
|
|
1
1
|
require_dependency 'trackback'
|
2
2
|
|
3
3
|
class PostController < ApplicationController
|
4
|
-
before_filter :auth_required, :except => [:reply, :replyxml]
|
4
|
+
before_filter :auth_required, :except => [:index, :reply, :replyxml, :view, :range]
|
5
5
|
cache_sweeper :feed_killer, :only => [:new, :edit, :destroy, :destroyxml]
|
6
|
+
helper :pagination
|
6
7
|
|
7
8
|
def index
|
8
|
-
|
9
|
+
@post_pages, @posts = paginate :posts,
|
10
|
+
:order_by => 'created_at DESC',
|
11
|
+
:per_page => @app_config['main']['num_posts']
|
12
|
+
end
|
13
|
+
|
14
|
+
def view
|
15
|
+
@post = Post.find_by_sql("SELECT p.* FROM posts p, users u WHERE p.slug='#{@params['slug']}' AND u.username='#{@params['user']}' and p.user_id=u.id")[0]
|
16
|
+
redirect_to_main and return if @post.nil?
|
17
|
+
@comments = @post.find_all_in_comments("comment_id=0")
|
18
|
+
end
|
19
|
+
|
20
|
+
# This is used for fancy URL viewing with URLs of the format /year/month/day.
|
21
|
+
def range
|
22
|
+
unless @params['year']
|
23
|
+
@error = 'Invalid date.'
|
24
|
+
render 'post/error'
|
25
|
+
return
|
26
|
+
end
|
27
|
+
if @params['day'] and !@params['month']
|
28
|
+
@error = 'Invalid date.'
|
29
|
+
render 'post/error'
|
30
|
+
return
|
31
|
+
end
|
32
|
+
|
33
|
+
year = @params['year']
|
34
|
+
fmonth = @params['month'] || '01'
|
35
|
+
tmonth = @params['month'] ? fmonth : '12'
|
36
|
+
fday = @params['day'] || '01'
|
37
|
+
tday = @params['day'] ? fday : '31'
|
38
|
+
|
39
|
+
|
40
|
+
tday = @params['day'] ? fday : (Time.mktime(year.to_i, tmonth.to_i + 1, 1) - 1.day).day.to_s rescue fday
|
41
|
+
|
42
|
+
if @params['user']
|
43
|
+
userid = User.find_by_username(@params['user']).id
|
44
|
+
@posts = Post.find_all(["created_at BETWEEN ? AND ? AND user_id=?", "#{year}-#{fmonth}-#{fday} 00:00:00", "#{year}-#{tmonth}-#{tday} 23:59:59", userid], 'created_at DESC') rescue []
|
45
|
+
else
|
46
|
+
@posts = Post.find_all(["created_at BETWEEN ? AND ?", "#{year}-#{fmonth}-#{fday} 00:00:00", "#{year}-#{tmonth}-#{tday} 23:59:59"], 'created_at DESC') rescue []
|
47
|
+
end
|
48
|
+
|
49
|
+
render_action 'index'
|
9
50
|
end
|
10
51
|
|
11
52
|
def new
|
@@ -34,7 +75,7 @@ class PostController < ApplicationController
|
|
34
75
|
return
|
35
76
|
end
|
36
77
|
end
|
37
|
-
|
78
|
+
|
38
79
|
def edit
|
39
80
|
begin
|
40
81
|
@post = current_user.posts.find(@params['id'])
|
@@ -42,7 +83,7 @@ class PostController < ApplicationController
|
|
42
83
|
redirect_to_main
|
43
84
|
return
|
44
85
|
end
|
45
|
-
|
86
|
+
|
46
87
|
if @request.post?
|
47
88
|
@post.attributes = @params['post']
|
48
89
|
if @post.save
|
@@ -51,7 +92,7 @@ class PostController < ApplicationController
|
|
51
92
|
end
|
52
93
|
end
|
53
94
|
end
|
54
|
-
|
95
|
+
|
55
96
|
def postxml
|
56
97
|
@post = current_user.posts.find(@params['id']) rescue Post.new
|
57
98
|
@post.update_attributes @params['post'] if @request.post?
|
@@ -62,17 +103,17 @@ class PostController < ApplicationController
|
|
62
103
|
current_user.posts.find(@params['id']).destroy rescue nil
|
63
104
|
redirect_to_main
|
64
105
|
end
|
65
|
-
|
106
|
+
|
66
107
|
def destroyxml
|
67
108
|
@postid = @params['id']
|
68
109
|
current_user.posts.find(@params['id']).destroy rescue nil
|
69
110
|
end
|
70
|
-
|
111
|
+
|
71
112
|
def destroy_comment
|
72
113
|
Comment.find(@params['id']).destroy rescue nil
|
73
114
|
redirect_to @request.env['HTTP_REFERER']
|
74
115
|
end
|
75
|
-
|
116
|
+
|
76
117
|
def toggle_commenting
|
77
118
|
if (post = current_user.posts.find(@params['id']) rescue nil)
|
78
119
|
post.allows_comment = (not post.allows_comment?)
|
@@ -89,37 +130,27 @@ class PostController < ApplicationController
|
|
89
130
|
end
|
90
131
|
|
91
132
|
def reply
|
92
|
-
|
93
|
-
|
94
|
-
@user
|
95
|
-
|
96
|
-
|
97
|
-
return
|
98
|
-
end
|
99
|
-
|
100
|
-
@comments = @post.find_all_in_comments("comment_id=0")
|
101
|
-
|
102
|
-
if @request.post?
|
103
|
-
@newcomment = @post.comments.create(@params['newcomment'])
|
104
|
-
unless @newcomment.save
|
105
|
-
@error = @newcomment.errors.full_messages
|
106
|
-
render 'journal/error'
|
107
|
-
return
|
108
|
-
end
|
109
|
-
redirect_to :controller => 'journal', :action => 'view', :id => @newcomment.post_id
|
110
|
-
return
|
111
|
-
end
|
133
|
+
@post = Post.find_by_slug(@params['slug'])
|
134
|
+
unless @post.allows_comment?
|
135
|
+
redirect_to :controller => 'post', :action => 'view', :user => @post.user.username, :slug => @post.slug
|
136
|
+
return
|
137
|
+
end
|
112
138
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
if @params['comment_id']
|
117
|
-
@comment = Comment.find(@params['comment_id'])
|
118
|
-
end
|
139
|
+
if @params['id']
|
140
|
+
@comment = Comment.find @params['id']
|
141
|
+
end
|
119
142
|
|
120
|
-
|
121
|
-
|
143
|
+
|
144
|
+
if @request.post?
|
145
|
+
@newcomment = @comment ? @comment.comments.create(@params['newcomment']) : @post.comments.create(@params['newcomment'])
|
146
|
+
if @newcomment.save
|
147
|
+
redirect_to :controller => 'post', :action => 'view', :user => @post.user.username, :slug => @post.slug
|
148
|
+
end
|
122
149
|
end
|
150
|
+
|
151
|
+
# A little hack to pre-load the subject line.
|
152
|
+
# Do this down here so it doesn't get called for the POST.
|
153
|
+
@newcomment = Comment.new('subject' => 'Re: ' + @post.subject)
|
123
154
|
end
|
124
155
|
|
125
156
|
def replyxml
|
@@ -1,22 +1,17 @@
|
|
1
1
|
class RssController < ApplicationController
|
2
2
|
layout nil
|
3
|
-
caches_page :
|
3
|
+
caches_page :feed
|
4
4
|
|
5
|
-
def
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@title = user.title
|
17
|
-
@subtitle = user.subtitle
|
18
|
-
@posts = user.posts.find_all(nil, 'created_at desc', @app_config['main']['num_posts']) rescue []
|
19
|
-
render_action 'index'
|
20
|
-
cache_page if ActionController::Base.perform_caching
|
5
|
+
def feed
|
6
|
+
if @params['user']
|
7
|
+
@title = @user.title
|
8
|
+
@subtitle = @user.subtitle
|
9
|
+
@posts = @user.posts.find_all(nil, 'created_at desc', @app_config['main']['num_posts']) rescue []
|
10
|
+
else
|
11
|
+
@posts = Post.find_all(nil, 'updated_at DESC', @app_config['main']['num_posts'])
|
12
|
+
@title = @app_config['main']['title']
|
13
|
+
@subtitle = @app_config['main']['subtitle']
|
14
|
+
end
|
15
|
+
#cache_page if ActionController::Base.perform_caching
|
21
16
|
end
|
22
17
|
end
|
@@ -6,7 +6,7 @@ class TagsController < ApplicationController
|
|
6
6
|
def show
|
7
7
|
@tag = Tag.find_by_tag(CGI.unescape(@params['tag'])) rescue Tag.new
|
8
8
|
@posts = @tag.posts
|
9
|
-
render '
|
9
|
+
render 'post/index'
|
10
10
|
end
|
11
11
|
|
12
12
|
def add
|
@@ -22,7 +22,7 @@ class TagsController < ApplicationController
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
25
|
-
redirect_to :controller => '
|
25
|
+
redirect_to :controller => 'post', :action => 'view', :id => post.id
|
26
26
|
end
|
27
27
|
|
28
28
|
def addxml
|
@@ -1,4 +1,6 @@
|
|
1
1
|
class UsersController < ApplicationController
|
2
|
+
helper :pagination
|
3
|
+
|
2
4
|
def index
|
3
5
|
@users = User.find_all nil, 'username'
|
4
6
|
end
|
@@ -6,8 +8,12 @@ class UsersController < ApplicationController
|
|
6
8
|
def method_missing(method)
|
7
9
|
@user = User.find_by_username(method.to_s)
|
8
10
|
redirect_to_main and return unless @user
|
9
|
-
|
10
|
-
|
11
|
-
|
11
|
+
|
12
|
+
@post_pages, @posts = paginate :posts,
|
13
|
+
:conditions => ['user_id = ?', @user.id],
|
14
|
+
:order_by => 'created_at DESC',
|
15
|
+
:per_page => @user.posts_per_page
|
16
|
+
|
17
|
+
render 'post/index'
|
12
18
|
end
|
13
19
|
end
|
@@ -1,18 +1,6 @@
|
|
1
1
|
# The methods added to this helper will be available to all templates in the application.
|
2
2
|
module ApplicationHelper
|
3
|
-
def
|
4
|
-
content_tag "a", year, {'href'=> @app_config['main']['app_base'] + year}
|
5
|
-
end
|
6
|
-
|
7
|
-
def link_to_month(year, month, name)
|
8
|
-
content_tag "a", name, {'href'=> @app_config['main']['app_base'] + year + '/' + month}
|
9
|
-
end
|
10
|
-
|
11
|
-
def link_to_day(year, month, day, name)
|
12
|
-
content_tag "a", name, {'href'=> @app_config['main']['app_base'] + year + '/' + month + '/' + day}
|
13
|
-
end
|
14
|
-
|
15
|
-
def sexifytime(t)
|
3
|
+
def sexifytime(t, username=nil)
|
16
4
|
day = case t.day % 10
|
17
5
|
when 0
|
18
6
|
"#{t.day}th"
|
@@ -30,9 +18,10 @@ module ApplicationHelper
|
|
30
18
|
|
31
19
|
|
32
20
|
str = ''
|
33
|
-
str <<
|
34
|
-
str <<
|
35
|
-
str <<
|
21
|
+
str << link_to(day, :controller => 'post', :action => 'range', :user => username, :year => t.strftime('%Y'), :month => t.strftime('%m'), :day => t.strftime('%d'))
|
22
|
+
str << ' ' << link_to(t.strftime('%B'), :controller => 'post', :action => 'range', :user => username, :year => t.strftime('%Y'), :month => t.strftime('%m'), :day => nil)
|
23
|
+
str << ' ' << link_to(t.strftime('%Y'), :controller => 'post', :action => 'range', :user => username, :year => t.strftime('%Y'), :month => nil, :day => nil)
|
24
|
+
str << ', '
|
36
25
|
|
37
26
|
str << case t.hour
|
38
27
|
when 0..2
|
@@ -69,7 +58,7 @@ module ApplicationHelper
|
|
69
58
|
end
|
70
59
|
|
71
60
|
def tag_links(post, limit = nil)
|
72
|
-
tags = post.tags.sort_by {|tag| tag.numitems}.reverse.map { |tag| link_to(tag.tag, :controller => 'tags', :action => 'show', :
|
61
|
+
tags = post.tags.sort_by {|tag| tag.numitems}.reverse.map { |tag| link_to(tag.tag, :controller => 'tags', :action => 'show', :tag => CGI.escape(tag.tag)) }
|
73
62
|
(limit ? tags.first(limit) : tags ).join(', ')
|
74
63
|
end
|
75
64
|
|
@@ -86,17 +75,17 @@ module ApplicationHelper
|
|
86
75
|
end
|
87
76
|
|
88
77
|
str = '<ul class="postlinks">'
|
89
|
-
str << "<li #{disp} id=\"replylink_#{post.id}\">" + link_to('Reply', {:controller => 'post', :action => 'reply', :
|
90
|
-
str << '<li>' + link_to('Add Tags', {:controller => 'post', :action => 'tag', :
|
91
|
-
str << '<li>' + link_to("View / Permalink", {:controller => '
|
92
|
-
str << "<li #{disp} id=\"comment_count_#{post.id}\"> " + link_to("Comments (#{post.comments.length})", :controller => '
|
93
|
-
str << "<li>" + link_to("Trackbacks (#{post.pings.length})", :controller => '
|
78
|
+
str << "<li #{disp} id=\"replylink_#{post.id}\">" + link_to('Reply', {:controller => 'post', :action => 'reply', :user => post.user.username, :slug => post.slug}, 'onclick' => "return showReply('#{post.id}')", 'id' => "post_reply_#{post.id}") + "</li>"
|
79
|
+
str << '<li>' + link_to('Add Tags', {:controller => 'post', :action => 'tag', :user => post.user.username, :slug => post.slug}, 'onclick' => "return showTagger('#{post.id}')", 'id' => "post_tagger_#{post.id}") + '</li>'
|
80
|
+
str << '<li>' + link_to("View / Permalink", {:controller => 'post', :action => 'view', :user => post.user.username, :slug => post.slug}, {'class' => 'middle' }) + '</li>'
|
81
|
+
str << "<li #{disp} id=\"comment_count_#{post.id}\"> " + link_to("Comments (#{post.comments.length})", :controller => 'post', :action => 'view', :user => post.user.username, :slug => post.slug, :anchor => 'comments') + "</li>"
|
82
|
+
str << "<li>" + link_to("Trackbacks (#{post.pings.length})", :controller => 'post', :action => 'view', :user => post.user.username, :slug => post.slug, :anchor => 'trackbacks') + "</li>"
|
94
83
|
str << '</ul>'
|
95
84
|
end
|
96
85
|
|
97
86
|
def render_body(post)
|
98
87
|
unless ['view', 'reply'].include? @params['action']
|
99
|
-
return post.sections.first + (post.sections.length > 1 ? '<p
|
88
|
+
return post.sections.first + (post.sections.length > 1 ? '<p>' + link_to('Read More ...', :controller => 'post', :action => 'view', :user => post.user.username, :slug => post.slug) + '</p>' : '')
|
100
89
|
end
|
101
90
|
post.rendered
|
102
91
|
end
|
@@ -115,3 +104,4 @@ module ApplicationHelper
|
|
115
104
|
user_page? ? @user.subtitle : @app_config['main']['subtitle']
|
116
105
|
end
|
117
106
|
end
|
107
|
+
|
@@ -0,0 +1,487 @@
|
|
1
|
+
# == Pagination Helper
|
2
|
+
#
|
3
|
+
# === Action Pack pagination for Active Record collections
|
4
|
+
#
|
5
|
+
# <em>Sam Stephenson <sstephenson at gmail dot com></em>
|
6
|
+
#
|
7
|
+
# Pagination Helper aids in the process of paging large collections of Active
|
8
|
+
# Record objects. It offers macro-style automatic fetching of your model for
|
9
|
+
# multiple views, or explicit fetching for single actions. And if the magic
|
10
|
+
# isn't flexible enough for your needs, you can create your own paginators
|
11
|
+
# with a minimal amount of code.
|
12
|
+
#
|
13
|
+
# Unlike most helpers, Pagination Helper is available to all of Action Pack:
|
14
|
+
# it helps you query your models with Action Controller and render links in
|
15
|
+
# Action View.
|
16
|
+
#
|
17
|
+
# ----
|
18
|
+
#
|
19
|
+
# === Controller examples
|
20
|
+
#
|
21
|
+
# Pagination Helper can handle as much or as little as you wish. In the
|
22
|
+
# controller, have it automatically query your model for pagination; or,
|
23
|
+
# if you prefer, create Paginator objects yourself.
|
24
|
+
#
|
25
|
+
# ==== Automatic pagination for every action in a controller
|
26
|
+
#
|
27
|
+
# class PersonController < ApplicationController
|
28
|
+
# helper :pagination
|
29
|
+
# model :person
|
30
|
+
#
|
31
|
+
# paginate :people, :order_by => 'last_name, first_name',
|
32
|
+
# :per_page => 20
|
33
|
+
#
|
34
|
+
# # ...
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# Each action in this controller now has access to a <tt>@people</tt> instance
|
38
|
+
# variable, which is an ordered collection of model objects for the current
|
39
|
+
# page (at most 20, sorted by last name and first name), and a
|
40
|
+
# <tt>@person_pages</tt> Paginator instance. The current page is determined by
|
41
|
+
# the <tt>@params['page']</tt> variable.
|
42
|
+
#
|
43
|
+
# ==== Pagination for a single action
|
44
|
+
#
|
45
|
+
# def list
|
46
|
+
# @person_pages, @people =
|
47
|
+
# paginate :people, :order_by => 'last_name, first_name'
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# Like the previous example, but explicitly creates <tt>@person_pages</tt> and
|
51
|
+
# <tt>@people</tt> for a single action, and uses the default of 10 items per
|
52
|
+
# page.
|
53
|
+
#
|
54
|
+
# ==== Custom/"classic" pagination
|
55
|
+
#
|
56
|
+
# def list
|
57
|
+
# @person_pages = Paginator.new self, Person.count, 10, @params['page']
|
58
|
+
# @people = Person.find_all nil, 'last_name, first_name',
|
59
|
+
# @person_pages.current.to_sql
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# Explicitly creates the paginator from the previous example and uses
|
63
|
+
# Paginator#to_sql to retrieve <tt>@people</tt> from the model.
|
64
|
+
#
|
65
|
+
# === View examples
|
66
|
+
#
|
67
|
+
# Paginator Helper includes various methods to help you display pagination
|
68
|
+
# links in your views. (For information on displaying the paginated
|
69
|
+
# collections you retrieve in the controller, see the documentation for
|
70
|
+
# ActionView::Partials#render_collection_of_partials.)
|
71
|
+
#
|
72
|
+
# ==== Using Paginator#basic_html
|
73
|
+
#
|
74
|
+
# Use the +basic_html+ method to get a simple list of pages (always including
|
75
|
+
# the first and last page) with a window around the current page. In your
|
76
|
+
# view, using one of the controller examples above,
|
77
|
+
#
|
78
|
+
# <%= @person_pages.basic_html(self) %>
|
79
|
+
#
|
80
|
+
# will render a list of links. For a list of parameters, see the documentation
|
81
|
+
# for Page#basic_html.
|
82
|
+
#
|
83
|
+
# ==== Using a paginator partial
|
84
|
+
#
|
85
|
+
# If you need more advanced control over pagination links and need to paginate
|
86
|
+
# multiple actions and controllers, consider using a shared paginator partial.
|
87
|
+
# For instance, _why suggests this partial, which you might place in
|
88
|
+
# <tt>app/views/partial/_paginator.rhtml</tt>:
|
89
|
+
#
|
90
|
+
# <div class="counter">
|
91
|
+
# Displaying <%= paginator.current.first_item %>
|
92
|
+
# - <%= paginator.current.last_item %>
|
93
|
+
# of <%= paginator.item_count %>
|
94
|
+
#
|
95
|
+
# <%= link_to(h('< Previous'), paginator.current.previous.to_link) +
|
96
|
+
# " | " if paginator.current.previous %>
|
97
|
+
# <%= paginator.basic_html(self, 4) %>
|
98
|
+
# <%= " | " + link_to(h('Next >'), paginator.current.next.to_link) if
|
99
|
+
# paginator.current.next %>
|
100
|
+
# </div>
|
101
|
+
#
|
102
|
+
# Then in your views, simply call
|
103
|
+
#
|
104
|
+
# <%= render_partial "partial/paginator", @person_pages %>
|
105
|
+
#
|
106
|
+
# wherever you want your pagination links to appear.
|
107
|
+
#
|
108
|
+
# ----
|
109
|
+
#
|
110
|
+
# ==== Thanks
|
111
|
+
#
|
112
|
+
# Thanks to the following people for their contributions to Pagination Helper:
|
113
|
+
# * Marcel Molina Jr (noradio), who provided the idea for and original
|
114
|
+
# implementation of Paginator::Window, as well as endless mental support.
|
115
|
+
# * evl, who pointed out that Page#link_to should take and merge in a hash of
|
116
|
+
# additional parameters.
|
117
|
+
# * why the lucky stiff, who wrote a lovely article on the original Pagination
|
118
|
+
# Helper alongside the first implementation of Paginator#base_html, created
|
119
|
+
# Page#first_item and Page#last_item, and who provided inspiration for
|
120
|
+
# revising Pagination Helper to make it more "Rails-ish."
|
121
|
+
# * xal, who saw the limitations of having only the macro-style paginate
|
122
|
+
# method and suggested the single-action version, and who also suggested the
|
123
|
+
# automatic inclusion into ActionController::Base.
|
124
|
+
# * jbd for feedback and debugging help.
|
125
|
+
# * ##rubyonrails on Freenode and the Rails mailing list.
|
126
|
+
#
|
127
|
+
module PaginationHelper
|
128
|
+
# A hash holding options for controllers using macro-style pagination
|
129
|
+
OPTIONS = Hash.new
|
130
|
+
|
131
|
+
# The default options for pagination
|
132
|
+
DEFAULT_OPTIONS = {
|
133
|
+
:class_name => nil,
|
134
|
+
:per_page => 10,
|
135
|
+
:parameter => 'page',
|
136
|
+
:conditions => nil,
|
137
|
+
:order_by => nil
|
138
|
+
}
|
139
|
+
|
140
|
+
def self.included(base) #:nodoc:
|
141
|
+
super
|
142
|
+
base.extend(ClassMethods)
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.validate_options!(collection_id, options, in_action) #:nodoc:
|
146
|
+
options.merge!(DEFAULT_OPTIONS) {|key, old, new| old}
|
147
|
+
|
148
|
+
valid_options = [:class_name, :per_page,
|
149
|
+
:parameter, :conditions, :order_by]
|
150
|
+
valid_options << :actions unless in_action
|
151
|
+
|
152
|
+
unknown_option_keys = options.keys - valid_options
|
153
|
+
raise ActionController::ActionControllerError,
|
154
|
+
"Unknown options: #{unknown_option_keys.join(', ')}" unless
|
155
|
+
unknown_option_keys.empty?
|
156
|
+
|
157
|
+
options[:singular_name] = Inflector.singularize(collection_id.to_s)
|
158
|
+
options[:class_name] ||= Inflector.camelize(options[:singular_name])
|
159
|
+
end
|
160
|
+
|
161
|
+
# Returns a paginator and a collection of Active Record model instances for
|
162
|
+
# the paginator's current page. This is designed to be used in a single
|
163
|
+
# action; to automatically paginate multiple actions, consider
|
164
|
+
# ClassMethods#paginate.
|
165
|
+
#
|
166
|
+
# +options+ are:
|
167
|
+
# <tt>:class_name</tt>:: the class name to use, if it can't be inferred by
|
168
|
+
# singularizing the collection name.
|
169
|
+
# <tt>:per_page</tt>:: the maximum number of items to include in a
|
170
|
+
# single page. Defaults to 10.
|
171
|
+
# <tt>:parameter</tt>:: the CGI parameter from which the current page is
|
172
|
+
# determined. Defaults to 'page'; i.e., the current
|
173
|
+
# page is specified by <tt>@params['page']</tt>.
|
174
|
+
# <tt>:conditions</tt>:: optional conditions passed to Model.find_all.
|
175
|
+
# <tt>:order_by</tt>:: optional order parameter passed to Model.find_all
|
176
|
+
# and Model.count.
|
177
|
+
def paginate(collection_id, options={})
|
178
|
+
PaginationHelper.validate_options!(collection_id, options, true)
|
179
|
+
paginator_and_collection_for(collection_id, options)
|
180
|
+
end
|
181
|
+
|
182
|
+
# These methods become class methods on any controller which includes
|
183
|
+
# PaginationHelper.
|
184
|
+
module ClassMethods
|
185
|
+
# Creates a +before_filter+ which automatically paginates an Active Record
|
186
|
+
# model for all actions in a controller (or certain actions if specified
|
187
|
+
# with the <tt>:actions</tt> option).
|
188
|
+
#
|
189
|
+
# +options+ are the same as PaginationHelper#paginate, with the addition
|
190
|
+
# of:
|
191
|
+
# <tt>:actions</tt>:: an array of actions for which the pagination is
|
192
|
+
# active. Defaults to +nil+ (i.e., every action).
|
193
|
+
def paginate(collection_id, options={})
|
194
|
+
PaginationHelper.validate_options!(collection_id, options, false)
|
195
|
+
module_eval do
|
196
|
+
before_filter :create_paginators_and_retrieve_collections
|
197
|
+
OPTIONS[self] ||= Hash.new
|
198
|
+
OPTIONS[self][collection_id] = options
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def create_paginators_and_retrieve_collections #:nodoc:
|
204
|
+
PaginationHelper::OPTIONS[self.class].each do |collection_id, options|
|
205
|
+
next unless options[:actions].include? action_name if options[:actions]
|
206
|
+
|
207
|
+
paginator, collection =
|
208
|
+
paginator_and_collection_for(collection_id, options)
|
209
|
+
|
210
|
+
paginator_name = "@#{options[:singular_name]}_pages"
|
211
|
+
self.instance_variable_set(paginator_name, paginator)
|
212
|
+
|
213
|
+
collection_name = "@#{collection_id.to_s}"
|
214
|
+
self.instance_variable_set(collection_name, collection)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Returns the total number of items in the collection to be paginated for
|
219
|
+
# the +model+ and given +conditions+. Override this method to implement a
|
220
|
+
# custom counter.
|
221
|
+
def count_collection_for_pagination(model, conditions)
|
222
|
+
model.count(conditions)
|
223
|
+
end
|
224
|
+
|
225
|
+
# Returns a collection of items for the given +model+ and +conditions+,
|
226
|
+
# ordered by +order_by+, for the current page in the given +paginator+.
|
227
|
+
# Override this method to implement a custom finder.
|
228
|
+
def find_collection_for_pagination(model, conditions, order_by, paginator)
|
229
|
+
model.find_all(conditions, order_by, paginator.current.to_sql)
|
230
|
+
end
|
231
|
+
|
232
|
+
protected :create_paginators_and_retrieve_collections,
|
233
|
+
:count_collection_for_pagination, :find_collection_for_pagination
|
234
|
+
|
235
|
+
def paginator_and_collection_for(collection_id, options) #:nodoc:
|
236
|
+
klass = eval options[:class_name]
|
237
|
+
page = @params[options[:parameter]]
|
238
|
+
count = count_collection_for_pagination(klass, options[:conditions])
|
239
|
+
|
240
|
+
paginator = Paginator.new(self, count,
|
241
|
+
options[:per_page], page, options[:parameter])
|
242
|
+
|
243
|
+
collection = find_collection_for_pagination(klass,
|
244
|
+
options[:conditions], options[:order_by], paginator)
|
245
|
+
|
246
|
+
return paginator, collection
|
247
|
+
end
|
248
|
+
|
249
|
+
private :paginator_and_collection_for
|
250
|
+
|
251
|
+
# A class representing a paginator for an Active Record collection.
|
252
|
+
class Paginator
|
253
|
+
include Enumerable
|
254
|
+
|
255
|
+
# Creates a new Paginator on the given +controller+ for a set of items of
|
256
|
+
# size +item_count+ and having +items_per_page+ items per page. Raises
|
257
|
+
# ArgumentError if items_per_page is out of bounds (i.e., less than or
|
258
|
+
# equal to zero). The page CGI parameter for links defaults to "page" and
|
259
|
+
# can be overridden with +page_parameter+.
|
260
|
+
def initialize(controller, item_count, items_per_page, current_page=1,
|
261
|
+
page_parameter='page')
|
262
|
+
raise ArgumentError, 'must have at least one item per page' if
|
263
|
+
items_per_page <= 0
|
264
|
+
|
265
|
+
@controller = controller
|
266
|
+
@item_count = item_count || 0
|
267
|
+
@items_per_page = items_per_page
|
268
|
+
@page_parameter = page_parameter
|
269
|
+
|
270
|
+
self.current_page = current_page
|
271
|
+
end
|
272
|
+
attr_reader :controller, :item_count, :items_per_page, :page_parameter
|
273
|
+
|
274
|
+
# Sets the current page number of this paginator. If +page+ is a Page
|
275
|
+
# object, its +number+ attribute is used as the value; if the page does
|
276
|
+
# not belong to this Paginator, an ArgumentError is raised.
|
277
|
+
def current_page=(page)
|
278
|
+
if page.is_a? Page
|
279
|
+
raise ArgumentError, 'Page/Paginator mismatch' unless
|
280
|
+
page.paginator == self
|
281
|
+
end
|
282
|
+
page = page.to_i
|
283
|
+
@current_page = has_page_number?(page) ? page : 1
|
284
|
+
end
|
285
|
+
|
286
|
+
# Returns a Page object representing this paginator's current page.
|
287
|
+
def current_page
|
288
|
+
self[@current_page]
|
289
|
+
end
|
290
|
+
alias current :current_page
|
291
|
+
|
292
|
+
# Returns a new Page representing the first page in this paginator.
|
293
|
+
def first_page
|
294
|
+
self[1]
|
295
|
+
end
|
296
|
+
alias first :first_page
|
297
|
+
|
298
|
+
# Returns a new Page representing the last page in this paginator.
|
299
|
+
def last_page
|
300
|
+
self[page_count]
|
301
|
+
end
|
302
|
+
alias last :last_page
|
303
|
+
|
304
|
+
# Returns the number of pages in this paginator.
|
305
|
+
def page_count
|
306
|
+
return 1 if @item_count.zero?
|
307
|
+
(@item_count / @items_per_page.to_f).ceil
|
308
|
+
end
|
309
|
+
alias length :page_count
|
310
|
+
|
311
|
+
# Returns true if this paginator contains the page of index +number+.
|
312
|
+
def has_page_number?(number)
|
313
|
+
return false unless number.is_a? Fixnum
|
314
|
+
number >= 1 and number <= page_count
|
315
|
+
end
|
316
|
+
|
317
|
+
# Returns a new Page representing the page with the given index +number+.
|
318
|
+
def [](number)
|
319
|
+
Page.new(self, number)
|
320
|
+
end
|
321
|
+
|
322
|
+
# Successively yields all the paginator's pages to the given block.
|
323
|
+
def each(&block)
|
324
|
+
page_count.times do |n|
|
325
|
+
yield self[n+1]
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
# Creates a basic HTML link bar for the given +view+. The first and last
|
330
|
+
# pages of the paginator are always shown, along with +window_size+ pages
|
331
|
+
# around the current page. By default, the current page is displayed, but
|
332
|
+
# not linked; to change this behavior, pass true to the
|
333
|
+
# +link_to_current_page+ argument. Specify additional link_to parameters
|
334
|
+
# with +params+.
|
335
|
+
def basic_html(view, window_size=2, link_to_current_page=false, params={})
|
336
|
+
window_pages = current.window(window_size).pages
|
337
|
+
return if window_pages.length <= 1 unless link_to_current_page
|
338
|
+
|
339
|
+
html = ''
|
340
|
+
unless window_pages[0].first?
|
341
|
+
html << view.link_to(first.number, first.to_link(params))
|
342
|
+
html << ' ... ' if window_pages[0].number - first.number > 1
|
343
|
+
html << ' '
|
344
|
+
end
|
345
|
+
|
346
|
+
window_pages.each do |page|
|
347
|
+
if current == page and not link_to_current_page
|
348
|
+
html << page.number.to_s
|
349
|
+
else
|
350
|
+
html << view.link_to(page.number, page.to_link(params))
|
351
|
+
end
|
352
|
+
html << ' '
|
353
|
+
end
|
354
|
+
|
355
|
+
unless window_pages.last.last?
|
356
|
+
html << ' ... ' if last.number - window_pages[-1].number > 1
|
357
|
+
html << view.link_to(last.number, last.to_link(params))
|
358
|
+
end
|
359
|
+
|
360
|
+
html
|
361
|
+
end
|
362
|
+
|
363
|
+
# A class representing a single page in a paginator.
|
364
|
+
class Page
|
365
|
+
include Comparable
|
366
|
+
|
367
|
+
# Creates a new Page for the given +paginator+ with the index +number+.
|
368
|
+
# If +number+ is not in the range of valid page numbers or is not a
|
369
|
+
# number at all, it defaults to 1.
|
370
|
+
def initialize(paginator, number)
|
371
|
+
@paginator = paginator
|
372
|
+
@number = number.to_i
|
373
|
+
@number = 1 unless @paginator.has_page_number? @number
|
374
|
+
end
|
375
|
+
attr_reader :paginator, :number
|
376
|
+
alias to_i :number
|
377
|
+
|
378
|
+
# Compares two Page objects and returns true when they represent the
|
379
|
+
# same page (i.e., their paginators are the same and they have the same
|
380
|
+
# page number).
|
381
|
+
def ==(page)
|
382
|
+
@paginator == page.paginator and
|
383
|
+
@number == page.number
|
384
|
+
end
|
385
|
+
|
386
|
+
# Compares two Page objects and returns -1 if the left-hand page comes
|
387
|
+
# before the right-hand page, 0 if the pages are equal, and 1 if the
|
388
|
+
# left-hand page comes after the right-hand page. Raises ArgumentError
|
389
|
+
# if the pages do not belong to the same Paginator object.
|
390
|
+
def <=>(page)
|
391
|
+
raise ArgumentError unless @paginator == page.paginator
|
392
|
+
@number <=> page.number
|
393
|
+
end
|
394
|
+
|
395
|
+
# Returns the item offset for the first item in this page.
|
396
|
+
def offset
|
397
|
+
@paginator.items_per_page * (@number - 1)
|
398
|
+
end
|
399
|
+
|
400
|
+
# Returns the number of the first item displayed.
|
401
|
+
def first_item
|
402
|
+
offset + 1
|
403
|
+
end
|
404
|
+
|
405
|
+
# Returns the number of the last item displayed.
|
406
|
+
def last_item
|
407
|
+
[@paginator.items_per_page * @number, @paginator.item_count].min
|
408
|
+
end
|
409
|
+
|
410
|
+
# Returns true if this page is the first page in the paginator.
|
411
|
+
def first?
|
412
|
+
self == @paginator.first
|
413
|
+
end
|
414
|
+
|
415
|
+
# Returns true if this page is the last page in the paginator.
|
416
|
+
def last?
|
417
|
+
self == @paginator.last
|
418
|
+
end
|
419
|
+
|
420
|
+
# Returns a new Page object representing the page just before this page,
|
421
|
+
# or nil if this is the first page.
|
422
|
+
def previous
|
423
|
+
if first? then nil else Page.new(@paginator, @number - 1) end
|
424
|
+
end
|
425
|
+
|
426
|
+
# Returns a new Page object representing the page just after this page,
|
427
|
+
# or nil if this is the last page.
|
428
|
+
def next
|
429
|
+
if last? then nil else Page.new(@paginator, @number + 1) end
|
430
|
+
end
|
431
|
+
|
432
|
+
# Returns a new Window object for this page with the specified
|
433
|
+
# +padding+.
|
434
|
+
def window(padding=2)
|
435
|
+
Window.new(self, padding)
|
436
|
+
end
|
437
|
+
|
438
|
+
# Returns a hash appropriate for using with link_to or url_for. Takes an
|
439
|
+
# optional +params+ hash for specifying additional link parameters.
|
440
|
+
def to_link(params={})
|
441
|
+
{:params => {@paginator.page_parameter => @number.to_s}.merge(params)}
|
442
|
+
end
|
443
|
+
|
444
|
+
# Returns the SQL "LIMIT ... OFFSET" clause for this page.
|
445
|
+
def to_sql
|
446
|
+
['? OFFSET ?', @paginator.items_per_page, offset]
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
# A class for representing ranges around a given page.
|
451
|
+
class Window
|
452
|
+
# Creates a new Window object for the given +page+ with the specified
|
453
|
+
# +padding+.
|
454
|
+
def initialize(page, padding=2)
|
455
|
+
@paginator = page.paginator
|
456
|
+
@page = page
|
457
|
+
self.padding = padding
|
458
|
+
end
|
459
|
+
attr_reader :paginator, :page
|
460
|
+
|
461
|
+
# Sets the window's padding (the number of pages on either side of the
|
462
|
+
# window page).
|
463
|
+
def padding=(padding)
|
464
|
+
@padding = padding < 0 ? 0 : padding
|
465
|
+
# Find the beginning and end pages of the window
|
466
|
+
@first = @paginator.has_page_number?(@page.number - @padding) ?
|
467
|
+
@paginator[@page.number - @padding] : @paginator.first
|
468
|
+
@last = @paginator.has_page_number?(@page.number + @padding) ?
|
469
|
+
@paginator[@page.number + @padding] : @paginator.last
|
470
|
+
end
|
471
|
+
attr_reader :padding, :first, :last
|
472
|
+
|
473
|
+
# Returns an array of Page objects in the current window.
|
474
|
+
def pages
|
475
|
+
(@first.number..@last.number).to_a.map {|n| @paginator[n]}
|
476
|
+
end
|
477
|
+
alias to_a :pages
|
478
|
+
end
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
module ActionController #:nodoc:
|
483
|
+
class Base #:nodoc:
|
484
|
+
# Automatically include PaginationHelper.
|
485
|
+
include PaginationHelper
|
486
|
+
end
|
487
|
+
end
|