radiant-forum-extension 1.2.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +37 -56
- data/VERSION +1 -1
- data/app/controllers/forum_base_controller.rb +78 -0
- data/app/controllers/forums_controller.rb +5 -34
- data/app/controllers/posts_controller.rb +61 -153
- data/app/controllers/topics_controller.rb +6 -63
- data/app/helpers/forum_helper.rb +24 -48
- data/app/models/forum.rb +5 -18
- data/app/models/post.rb +53 -55
- data/app/models/post_attachment.rb +9 -6
- data/app/models/topic.rb +20 -113
- data/app/views/admin/reader_configuration/_edit_forum.html.haml +4 -2
- data/app/views/admin/reader_configuration/_forum.html.haml +6 -2
- data/app/views/forums/_forum.html.haml +14 -20
- data/app/views/forums/_latest.html.haml +12 -0
- data/app/views/forums/_standard_parts.html.haml +49 -0
- data/app/views/forums/index.html.haml +16 -24
- data/app/views/forums/show.html.haml +18 -28
- data/app/views/pages/_comment.html.haml +1 -1
- data/app/views/posts/_attachments.html.haml +13 -0
- data/app/views/posts/_confirm_delete.html.haml +10 -0
- data/app/views/posts/_context.html.haml +16 -0
- data/app/views/posts/_edit_links.html.haml +11 -0
- data/app/views/posts/_form.html.haml +40 -2
- data/app/views/posts/_latest.html.haml +10 -17
- data/app/views/posts/_new_attachment.html.haml +2 -0
- data/app/views/posts/_post.html.haml +25 -60
- data/app/views/posts/{_search.html.haml → _search_form.html.haml} +1 -1
- data/app/views/posts/_uploader.html.haml +2 -2
- data/app/views/posts/edit.html.haml +26 -13
- data/app/views/posts/index.html.haml +19 -27
- data/app/views/posts/new.html.haml +27 -7
- data/app/views/posts/remove.html.haml +27 -0
- data/app/views/posts/show.html.haml +20 -7
- data/app/views/readers/_forum_messages.html.haml +20 -0
- data/app/views/readers/_messages_summary.html.haml +3 -0
- data/app/views/topics/_context.html.haml +12 -0
- data/app/views/topics/_latest.html.haml +14 -16
- data/app/views/topics/_locked.html.haml +1 -1
- data/app/views/topics/_replies.html.haml +6 -0
- data/app/views/topics/_reply.html.haml +23 -0
- data/app/views/topics/_topic.html.haml +14 -43
- data/app/views/topics/_voices.html.haml +5 -0
- data/app/views/topics/index.html.haml +11 -24
- data/app/views/topics/show.html.haml +25 -38
- data/config/initializers/radiant_config.rb +5 -1
- data/config/locales/en.yml +73 -27
- data/config/routes.rb +3 -4
- data/db/migrate/20101222160900_page_posts.rb +21 -0
- data/db/migrate/20101222163605_no_comment_forum.rb +10 -0
- data/db/migrate/20110105103827_topic_merely_associative.rb +23 -0
- data/db/migrate/20110111080550_detach_observer.rb +11 -0
- data/db/migrate/20110127113852_import_attachments.rb +9 -0
- data/forum_extension.rb +12 -13
- data/lib/commentable_model.rb +98 -0
- data/lib/forum_page.rb +2 -22
- data/lib/forum_reader_sessions_controller.rb +14 -0
- data/lib/forum_readers_controller.rb +2 -9
- data/lib/forum_red_cloth3.rb +23 -4
- data/lib/forum_red_cloth4.rb +23 -4
- data/lib/forum_tags.rb +298 -194
- data/lib/sanitize/config/forum.rb +47 -0
- data/public/images/furniture/blank.png +0 -0
- data/public/images/furniture/emoticons.png +0 -0
- data/public/javascripts/forum.js +349 -93
- data/public/javascripts/jquery.tools.min.js +195 -0
- data/public/punymce/blank.htm +1 -0
- data/public/punymce/css/content.css +4 -0
- data/public/punymce/css/editor.css +58 -0
- data/public/punymce/i18n/sv.js +28 -0
- data/public/punymce/img/icons.gif +0 -0
- data/public/punymce/img/icons_uncompressed.png +0 -0
- data/public/punymce/plugins/bbcode.js +1 -0
- data/public/punymce/plugins/bbcode_src.js +50 -0
- data/public/punymce/plugins/editsource/css/editor.css +3 -0
- data/public/punymce/plugins/editsource/editsource.js +1 -0
- data/public/punymce/plugins/editsource/editsource_src.js +81 -0
- data/public/punymce/plugins/editsource/img/icons.gif +0 -0
- data/public/punymce/plugins/emoticons/css/content.css +13 -0
- data/public/punymce/plugins/emoticons/css/editor.css +17 -0
- data/public/punymce/plugins/emoticons/emoticons.js +1 -0
- data/public/punymce/plugins/emoticons/emoticons_src.js +303 -0
- data/public/punymce/plugins/emoticons/img/emoticons.gif +0 -0
- data/public/punymce/plugins/emoticons/img/emoticons.png +0 -0
- data/public/punymce/plugins/emoticons/img/trans.gif +0 -0
- data/public/punymce/plugins/entities.js +1 -0
- data/public/punymce/plugins/entities_src.js +37 -0
- data/public/punymce/plugins/forceblocks.js +1 -0
- data/public/punymce/plugins/forceblocks_src.js +465 -0
- data/public/punymce/plugins/forcenl.js +1 -0
- data/public/punymce/plugins/forcenl_src.js +26 -0
- data/public/punymce/plugins/image/css/editor.css +1 -0
- data/public/punymce/plugins/image/image.js +1 -0
- data/public/punymce/plugins/image/image_src.js +30 -0
- data/public/punymce/plugins/image/img/icons.gif +0 -0
- data/public/punymce/plugins/link/css/editor.css +2 -0
- data/public/punymce/plugins/link/img/icons.gif +0 -0
- data/public/punymce/plugins/link/link.js +1 -0
- data/public/punymce/plugins/link/link_src.js +36 -0
- data/public/punymce/plugins/paste.js +1 -0
- data/public/punymce/plugins/paste_src.js +169 -0
- data/public/punymce/plugins/protect.js +1 -0
- data/public/punymce/plugins/protect_src.js +30 -0
- data/public/punymce/plugins/safari2x.js +1 -0
- data/public/punymce/plugins/safari2x_src.js +284 -0
- data/public/punymce/plugins/tabfocus.js +1 -0
- data/public/punymce/plugins/tabfocus_src.js +45 -0
- data/public/punymce/plugins/textcolor/css/editor.css +7 -0
- data/public/punymce/plugins/textcolor/img/icons.gif +0 -0
- data/public/punymce/plugins/textcolor/textcolor.js +1 -0
- data/public/punymce/plugins/textcolor/textcolor_src.js +73 -0
- data/public/punymce/puny_mce.js +1 -0
- data/public/punymce/puny_mce_full.js +1 -0
- data/public/punymce/puny_mce_src.js +1460 -0
- data/public/stylesheets/sass/forum.sass +175 -169
- data/radiant-forum-extension.gemspec +81 -19
- data/spec/controllers/admin/forums_controller_spec.rb +2 -2
- data/spec/controllers/forums_controller_spec.rb +3 -6
- data/spec/controllers/posts_controller_spec.rb +76 -59
- data/spec/controllers/topics_controller_spec.rb +4 -95
- data/spec/datasets/forum_readers_dataset.rb +1 -0
- data/spec/datasets/forums_dataset.rb +91 -10
- data/spec/lib/commentable_model_spec.rb +88 -0
- data/spec/lib/forum_page_spec.rb +2 -34
- data/spec/lib/forum_site_spec.rb +2 -3
- data/spec/lib/forum_tags_spec.rb +35 -0
- data/spec/models/forum_spec.rb +31 -20
- data/spec/models/post_spec.rb +40 -39
- data/spec/models/topic_spec.rb +29 -71
- data/spec/spec_helper.rb +10 -1
- metadata +84 -22
- data/app/views/posts/_reply.html.haml +0 -35
- data/app/views/posts/_upload.html.haml +0 -2
- data/app/views/posts/preview.html.haml +0 -32
- data/app/views/posts/search.html.haml +0 -63
- data/app/views/posts/search.rss.builder +0 -14
- data/app/views/topics/_form.html.haml +0 -30
- data/app/views/topics/_help.html.haml +0 -8
- data/app/views/topics/comments.html.haml +0 -6
- data/app/views/topics/edit.html.haml +0 -26
- data/app/views/topics/new.html.haml +0 -56
- data/spec/datasets/forum_pages_dataset.rb +0 -11
- data/spec/datasets/posts_dataset.rb +0 -31
- data/spec/datasets/topics_dataset.rb +0 -37
@@ -0,0 +1,98 @@
|
|
1
|
+
module CommentableModel # for inclusion into ActiveRecord::Base
|
2
|
+
def self.included(base)
|
3
|
+
base.extend ClassMethods
|
4
|
+
end
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def has_comments?
|
8
|
+
false
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_comments
|
12
|
+
return if has_comments?
|
13
|
+
has_many :posts, :order => 'posts.created_at ASC', :dependent => :destroy
|
14
|
+
class_eval {
|
15
|
+
extend CommentableModel::CommentableClassMethods
|
16
|
+
include CommentableModel::CommentableInstanceMethods
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module CommentableClassMethods
|
22
|
+
def has_comments?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module CommentableInstanceMethods
|
28
|
+
def has_posts?
|
29
|
+
posts.any?
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_replies?
|
33
|
+
posts.count > 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def replies
|
37
|
+
posts.except(posts.first) # double query but therefore still chainable and paginatable
|
38
|
+
end
|
39
|
+
|
40
|
+
def reply_count
|
41
|
+
posts.count - 1
|
42
|
+
end
|
43
|
+
|
44
|
+
def voices
|
45
|
+
posts.distinct_readers.map(&:reader)
|
46
|
+
end
|
47
|
+
|
48
|
+
def voice_count
|
49
|
+
posts.distinct_readers.count #actually counting posts, but the total is the same
|
50
|
+
end
|
51
|
+
|
52
|
+
def other_voices
|
53
|
+
voices - [posts.first.reader]
|
54
|
+
end
|
55
|
+
|
56
|
+
def other_voice_count
|
57
|
+
voice_count - 1
|
58
|
+
end
|
59
|
+
|
60
|
+
def paged?
|
61
|
+
Radiant::Config['forum.paginate_posts?'] && posts.count > posts_per_page
|
62
|
+
end
|
63
|
+
|
64
|
+
def posts_per_page
|
65
|
+
(Radiant::Config['forum.posts_per_page'] || 25).to_i
|
66
|
+
end
|
67
|
+
|
68
|
+
def last_page
|
69
|
+
(posts.count.to_f/posts_per_page.to_f).ceil.to_i
|
70
|
+
end
|
71
|
+
|
72
|
+
def page_for(post)
|
73
|
+
return nil unless post.holder == self
|
74
|
+
return 1 unless paged?
|
75
|
+
position = posts.index(post) || posts.count + 1 # new post not always present in cached posts collection
|
76
|
+
(position.to_f/posts_per_page.to_f).ceil.to_i
|
77
|
+
end
|
78
|
+
|
79
|
+
def notice_destruction_of(post)
|
80
|
+
if self.respond_to?(:replied_at) && self.replied_at == post.created_at
|
81
|
+
set_last_post(self.posts.except(post).last)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def notice_creation_of(post)
|
86
|
+
set_last_post(post)
|
87
|
+
end
|
88
|
+
|
89
|
+
def set_last_post(post=nil)
|
90
|
+
if post
|
91
|
+
self.replied_by = post.reader if self.respond_to? :replied_by
|
92
|
+
self.replied_at = post.created_at if self.respond_to? :replied_at
|
93
|
+
self.save if self.changed?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
data/lib/forum_page.rb
CHANGED
@@ -2,33 +2,13 @@ module ForumPage
|
|
2
2
|
|
3
3
|
def self.included(base)
|
4
4
|
base.class_eval {
|
5
|
-
|
5
|
+
has_comments
|
6
6
|
include InstanceMethods
|
7
7
|
}
|
8
8
|
end
|
9
9
|
|
10
10
|
module InstanceMethods
|
11
11
|
|
12
|
-
def find_or_build_topic
|
13
|
-
if self.topic
|
14
|
-
self.topic
|
15
|
-
elsif self.still_commentable?
|
16
|
-
self.build_topic(:name => title, :forum => Forum.find_or_create_comments_forum, :reader => Reader.find_or_create_for_user(created_by)) # posts_controller will do the right thing with a new topic
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def posts
|
21
|
-
self.topic ? self.topic.posts : []
|
22
|
-
end
|
23
|
-
|
24
|
-
def has_posts?
|
25
|
-
self.topic && self.topic.has_posts?
|
26
|
-
end
|
27
|
-
|
28
|
-
# def cache?
|
29
|
-
# !has_posts?
|
30
|
-
# end
|
31
|
-
|
32
12
|
def still_commentable?
|
33
13
|
commentable? && !comments_closed? && (!commentable_period || Time.now - self.created_at < commentable_period)
|
34
14
|
end
|
@@ -40,6 +20,6 @@ module ForumPage
|
|
40
20
|
def locked?
|
41
21
|
!still_commentable?
|
42
22
|
end
|
43
|
-
|
23
|
+
|
44
24
|
end
|
45
25
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ForumReaderSessionsController
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
def default_loggedin_url_with_forum
|
6
|
+
Rails.logger.warn ">>> default_loggedin_url is going to be #{topics_url}"
|
7
|
+
topics_url
|
8
|
+
end
|
9
|
+
alias_method_chain :default_loggedin_url, :forum
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -1,16 +1,9 @@
|
|
1
1
|
module ForumReadersController
|
2
|
-
|
3
2
|
def self.included(base)
|
4
3
|
base.class_eval do
|
5
4
|
helper :forum
|
6
|
-
|
5
|
+
add_show_partial 'readers/forum_messages'
|
6
|
+
add_index_partial 'readers/messages_summary'
|
7
7
|
end
|
8
8
|
end
|
9
|
-
|
10
|
-
def show_with_forum
|
11
|
-
@reader = Reader.find(params[:id])
|
12
|
-
@posts = Post.paginate_by_reader_id(@reader.id, :page => params[:page], :include => :topic, :order => 'posts.created_at desc') if @reader
|
13
|
-
render :template => 'readers/show_with_posts'
|
14
|
-
end
|
15
|
-
|
16
9
|
end
|
data/lib/forum_red_cloth3.rb
CHANGED
@@ -1,10 +1,29 @@
|
|
1
1
|
module ForumRedCloth3
|
2
2
|
|
3
3
|
def smilies(text)
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
emoticons = {
|
5
|
+
':)' => 'happy',
|
6
|
+
':|' => 'neutral',
|
7
|
+
':(' => 'sad',
|
8
|
+
':D' => 'grin',
|
9
|
+
':O' => 'surprised',
|
10
|
+
';)' => 'wink',
|
11
|
+
'}:)' => 'devil',
|
12
|
+
':P' => 'tongue',
|
13
|
+
':[' => 'mad',
|
14
|
+
'8|' => 'shocked',
|
15
|
+
':@' => 'lol',
|
16
|
+
'B]' => 'cool'
|
17
|
+
}
|
18
|
+
|
19
|
+
# old syntax carried over from vanilla
|
20
|
+
text.gsub!(/\:(angry|smile|bigsmile|confused|cool|cry|devil|neutral|sad|shamed|shocked|surprised|tongue|wink)\:/) do |w|
|
21
|
+
%{<img src="/images/emoticons/#{$1}.gif" alt="(#{$1})" title="#{$1}" class="smiley" />}
|
7
22
|
end
|
8
|
-
end
|
9
23
|
|
24
|
+
# these are generally put in by the punymce toolbar, so we use their nasty but effective combination of blank image with sprite background
|
25
|
+
text.gsub!(/(\}\:\)|\:\)|\:\||\:\(|\:D|\:O|\;\)|\:P|\:\@|8\||\:\[|B\])/) do |w|
|
26
|
+
%{<img src="/images/furniture/blank.png" class="emoticon #{emoticons[w]}" />};
|
27
|
+
end
|
28
|
+
end
|
10
29
|
end
|
data/lib/forum_red_cloth4.rb
CHANGED
@@ -12,10 +12,29 @@ module ForumRedCloth4
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def smilies(text)
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
emoticons = {
|
16
|
+
':)' => 'happy',
|
17
|
+
':|' => 'neutral',
|
18
|
+
':(' => 'sad',
|
19
|
+
':D' => 'grin',
|
20
|
+
':O' => 'surprised',
|
21
|
+
';)' => 'wink',
|
22
|
+
'}:)' => 'devil',
|
23
|
+
':P' => 'tongue',
|
24
|
+
':[' => 'mad',
|
25
|
+
'8|' => 'shocked',
|
26
|
+
':@' => 'lol',
|
27
|
+
'B]' => 'cool'
|
28
|
+
}
|
29
|
+
|
30
|
+
# old syntax carried over from vanilla
|
31
|
+
text.gsub!(/\:(angry|smile|bigsmile|confused|cool|cry|devil|neutral|sad|shamed|shocked|surprised|tongue|wink)\:/) do |w|
|
32
|
+
%{<img src="/images/emoticons/#{$1}.gif" alt="(#{$1})" title="#{$1}" class="smiley" />}
|
18
33
|
end
|
19
|
-
end
|
20
34
|
|
35
|
+
# these are generally put in by the punymce toolbar, so we use their nasty but effective combination of blank image with sprite background
|
36
|
+
text.gsub!(/(\}\:\)|\:\)|\:\||\:\(|\:D|\:O|\;\)|\:P|\:\@|8\||\:\[|B\])/) do |w|
|
37
|
+
%{<img src="/images/furniture/blank.png" class="emoticon #{emoticons[w]}" />};
|
38
|
+
end
|
39
|
+
end
|
21
40
|
end
|
data/lib/forum_tags.rb
CHANGED
@@ -1,284 +1,388 @@
|
|
1
1
|
module ForumTags
|
2
2
|
include Radiant::Taggable
|
3
|
-
include ActionView::Helpers::
|
4
|
-
include
|
5
|
-
include
|
3
|
+
include ActionView::Helpers::UrlHelper
|
4
|
+
include ActionController::UrlWriter
|
5
|
+
include I18n
|
6
6
|
|
7
7
|
class TagError < StandardError; end
|
8
8
|
|
9
|
-
tag '
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
tag 'forum' do |tag|
|
10
|
+
tag.expand
|
11
|
+
end
|
12
|
+
|
13
|
+
tag 'forum:topics' do |tag|
|
14
|
+
tag.expand
|
15
|
+
end
|
16
|
+
|
17
|
+
desc %{
|
18
|
+
Renders a standard list of recent topics.
|
19
|
+
Pass a 'limit' parameter to set the length of the list: default is 10.
|
20
|
+
|
21
|
+
<pre><code>
|
22
|
+
<r:forum:topics:latest limit="5" />
|
23
|
+
# is the same as:
|
24
|
+
<ul>
|
25
|
+
<r:forum:topics:each limit="5">
|
26
|
+
<li><r:forum:topic:link /><br /><r:forum:topic:context /></li>
|
27
|
+
</r:forum:topics:each>
|
28
|
+
</ul>
|
29
|
+
</code></pre>
|
30
|
+
}
|
31
|
+
tag 'forum:topics:latest' do |tag|
|
32
|
+
limit = (tag.attr['limit'] || 10).to_i
|
33
|
+
output = "<ul>"
|
34
|
+
Topic.latest(limit).each do |topic|
|
35
|
+
tag.locals.topic = topic
|
36
|
+
tag.locals.post = topic.posts.last
|
37
|
+
output << tag.render('forum:topic:summary', tag.attr.dup)
|
14
38
|
end
|
39
|
+
output << "</ul>"
|
40
|
+
output
|
15
41
|
end
|
16
42
|
|
17
43
|
desc %{
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
44
|
+
Loops over the most recently-updated forum topics.
|
45
|
+
Supply a `limit` attribute to set the number of topics shown. The default is 10.
|
46
|
+
Within the loop you can use all the usual r:forum:topic and r:forum:post tags.
|
47
|
+
The post tags will refer to the latest reply (or to the first post if there are no replies).
|
22
48
|
}
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
"#{tag.locals.comments.size} comments"
|
49
|
+
tag 'forum:topics:each' do |tag|
|
50
|
+
output = []
|
51
|
+
limit = (tag.attr['limit'] || 10).to_i
|
52
|
+
Topic.latest(limit).each do |topic|
|
53
|
+
tag.locals.topic = topic
|
54
|
+
tag.locals.post = topic.posts.last
|
55
|
+
output << tag.expand
|
31
56
|
end
|
57
|
+
output
|
58
|
+
end
|
59
|
+
|
60
|
+
tag 'forum:topic' do |tag|
|
61
|
+
tag.locals.topic = Topic.find( tag.attr['id'] ) unless tag.attr['id'].blank?
|
62
|
+
raise TagError, "can't have forum:topic without a topic" unless tag.locals.topic
|
63
|
+
tag.expand
|
32
64
|
end
|
33
65
|
|
34
66
|
desc %{
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
<pre><code
|
67
|
+
Renders a standard, minimal topic list item consisting of link and explanation.
|
68
|
+
This is the shorthand used by forum:topics:latest but it can also be used in other settings.
|
69
|
+
|
70
|
+
<pre><code>
|
71
|
+
<r:forum:topic:summary />
|
72
|
+
# is the same as:
|
73
|
+
<li><r:forum:topic:link /><br /><r:forum:topic:context /></li>
|
74
|
+
</code></pre>
|
39
75
|
}
|
40
|
-
tag '
|
41
|
-
|
42
|
-
tag.expand if page.posts.any?
|
76
|
+
tag 'forum:topic:summary' do |tag|
|
77
|
+
"<li>#{tag.render('forum:topic:link')}<br />#{tag.render('forum:topic:context')}</li>"
|
43
78
|
end
|
44
79
|
|
45
80
|
desc %{
|
46
|
-
|
47
|
-
|
48
|
-
*Usage:*
|
49
|
-
<pre><code><r:unless_comments>...</r:unless_comments></code></pre>
|
81
|
+
Renders the url of the current topic.
|
50
82
|
}
|
51
|
-
tag '
|
52
|
-
|
53
|
-
tag.expand if page.posts.any?
|
83
|
+
tag 'forum:topic:url' do |tag|
|
84
|
+
forum_topic_path(tag.locals.topic.forum, tag.locals.topic)
|
54
85
|
end
|
55
|
-
|
86
|
+
|
56
87
|
desc %{
|
57
|
-
|
58
|
-
|
59
|
-
*Usage:*
|
60
|
-
<pre><code><r:comments:each>
|
61
|
-
<h2><r:comment:reader:name /></h2>
|
62
|
-
<p class="date"><r:comment:date /></p>
|
63
|
-
<r:comment:body_html />
|
64
|
-
</r:comments:each>
|
65
|
-
<r:comment_link />
|
66
|
-
</code></pre>
|
88
|
+
Renders a link to the current topic using its name as the text.
|
67
89
|
}
|
68
|
-
tag '
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
90
|
+
tag 'forum:topic:link' do |tag|
|
91
|
+
options = tag.attr.dup
|
92
|
+
anchor = options['anchor'] ? "##{options.delete('anchor')}" : ''
|
93
|
+
attributes = options.inject('') { |s, (k, v)| s << %{#{k.downcase}="#{v}" } }.strip
|
94
|
+
attributes = " #{attributes}" unless attributes.empty?
|
95
|
+
text = tag.double? ? tag.expand : tag.render('forum:topic:name')
|
96
|
+
%{<a href="#{tag.render('forum:topic:url')}#{anchor}"#{attributes}>#{text}</a>}
|
97
|
+
end
|
98
|
+
tag 'link' do |tag|
|
75
99
|
end
|
76
100
|
|
77
101
|
desc %{
|
78
|
-
|
79
|
-
|
80
|
-
*Usage:*
|
81
|
-
<pre><code><r:comments:all /></code></pre>
|
102
|
+
Renders a standard gravatar block (as found in the forum pages) for the reader who
|
103
|
+
started the current topic.
|
82
104
|
}
|
83
|
-
tag '
|
84
|
-
|
85
|
-
results = ""
|
86
|
-
results << %{<div class="page_comments">}
|
87
|
-
results << "<h2>Comments</h2>"
|
88
|
-
results << %{<div id="forum">
|
89
|
-
}
|
90
|
-
if posts.empty?
|
91
|
-
results << "<p>None yet.</p>"
|
92
|
-
else
|
93
|
-
posts.each do |post|
|
94
|
-
tag.locals.comment = post
|
95
|
-
results << tag.render('comment')
|
96
|
-
end
|
97
|
-
end
|
98
|
-
results << %{#{tag.render('comment_link', 'class' => 'inline inviting')}}
|
99
|
-
results << "</div></div>"
|
100
|
-
results
|
105
|
+
tag 'forum:topic:gravatar' do |tag|
|
106
|
+
%{<div class="speaker">#{standard_gravatar_for(tag.locals.topic.reader)}</div>}
|
101
107
|
end
|
102
108
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
109
|
+
desc %{
|
110
|
+
Renders the name of the reader who started the current topic.
|
111
|
+
}
|
112
|
+
tag 'forum:topic:author' do |tag|
|
113
|
+
tag.locals.topic.reader.name
|
114
|
+
end
|
115
|
+
|
116
|
+
desc %{
|
117
|
+
Renders the name of the current topic.
|
118
|
+
}
|
119
|
+
tag 'forum:topic:name' do |tag|
|
120
|
+
tag.locals.topic.name
|
121
|
+
end
|
122
|
+
|
123
|
+
desc %{
|
124
|
+
Renders the (sanitized and textilized) body of the first post in the current topic.
|
125
|
+
}
|
126
|
+
tag 'forum:topic:body' do |tag|
|
127
|
+
tag.locals.topic.posts.first.body_html
|
128
|
+
end
|
129
|
+
|
130
|
+
desc %{
|
131
|
+
Renders the author and date context line for the current topic.
|
132
|
+
}
|
133
|
+
tag 'forum:topic:context' do |tag|
|
134
|
+
output = []
|
135
|
+
output << I18n.t('started_by')
|
136
|
+
output << %{<a href="#{reader_path(tag.locals.topic.reader)}">#{tag.render('forum:topic:author')}</a>}
|
137
|
+
output << tag.render('forum:topic:date')
|
138
|
+
output.join(' ')
|
117
139
|
end
|
118
140
|
|
119
|
-
|
120
|
-
|
141
|
+
desc %{
|
142
|
+
Renders the creation date of the current topic in a friendly, colloquial form.
|
143
|
+
}
|
144
|
+
tag 'forum:topic:date' do |tag|
|
145
|
+
I18n.l tag.locals.topic.created_at, :format => :standard
|
146
|
+
end
|
147
|
+
|
148
|
+
tag 'forum:posts' do |tag|
|
121
149
|
tag.expand
|
122
150
|
end
|
123
151
|
|
124
152
|
desc %{
|
125
|
-
|
153
|
+
Loops over the posts most recently added. In effect this is very similar to calling
|
154
|
+
r:topics:each, but there are some differences:
|
155
|
+
|
156
|
+
* page comments and any other non-topic posts are included
|
157
|
+
* here r:post tags always refer to the current post.
|
158
|
+
Within the topics:each loop they always refer to the last reply to that topic.
|
159
|
+
* tag.locals.page is set if the foreground post is a page comment, so you can use
|
160
|
+
all the usual radius tags for that page.
|
161
|
+
|
162
|
+
Supply a `limit` attribute to set the number of posts shown. The default is 10.
|
126
163
|
}
|
127
|
-
tag '
|
128
|
-
|
164
|
+
tag 'forum:posts:each' do |tag|
|
165
|
+
results = []
|
166
|
+
limit = (tag.attr['limit'] || 10).to_i
|
167
|
+
Post.latest(limit).each do |post|
|
168
|
+
tag.locals.post = post
|
169
|
+
tag.locals.topic = post.topic
|
170
|
+
tag.locals.page = post.page
|
171
|
+
results << tag.expand
|
172
|
+
end
|
173
|
+
results
|
174
|
+
end
|
175
|
+
|
176
|
+
desc %{
|
177
|
+
This tag is generally used in double form or as a silent prefix, where it will just expand:
|
178
|
+
|
179
|
+
<pre><code>
|
180
|
+
<r:forum:post><r:gravatar /> <r:link /></r:forum:post>
|
181
|
+
# or just
|
182
|
+
<r:forum:post:link />
|
183
|
+
</code></pre>
|
184
|
+
|
185
|
+
But if used in single form it will return a standard, minimal post list item
|
186
|
+
consisting of link and explanation:
|
187
|
+
|
188
|
+
<pre><code>
|
189
|
+
<r:forum:post />
|
190
|
+
# is the same as:
|
191
|
+
<li><r:forum:post:link /><br /><r:forum:post:context /></li>
|
192
|
+
</code></pre>
|
193
|
+
|
194
|
+
Note that the text of the link will be the name of the topic or page to which this post is attached,
|
195
|
+
and that within a topics:each loop any r:forum:post tags will show the last post to the topic.
|
196
|
+
}
|
197
|
+
tag 'forum:post' do |tag|
|
198
|
+
tag.locals.post = Post.find(tag.attr['id']) unless tag.attr['id'].blank?
|
199
|
+
raise TagError, "can't have forum:post without a post" unless tag.locals.post
|
200
|
+
tag.expand if tag.locals.post
|
129
201
|
end
|
130
202
|
|
131
203
|
desc %{
|
132
|
-
|
204
|
+
Renders a url (with pagination and anchor) for the current post. Within a topics:each loop this
|
205
|
+
is a way to link to the last post.
|
133
206
|
}
|
134
|
-
tag '
|
135
|
-
tag.locals.
|
207
|
+
tag 'forum:post:url' do |tag|
|
208
|
+
paginated_post_path(tag.locals.post)
|
136
209
|
end
|
137
210
|
|
138
211
|
desc %{
|
139
|
-
|
212
|
+
Renders a title that can be used over the post: this will be the name of its page or topic.
|
140
213
|
}
|
141
|
-
tag '
|
142
|
-
tag.locals.
|
214
|
+
tag 'forum:post:name' do |tag|
|
215
|
+
tag.locals.post.holder.title
|
143
216
|
end
|
144
217
|
|
145
218
|
desc %{
|
146
|
-
The
|
219
|
+
Renders a link to the current post. The link text will be the page or topic title and within that
|
220
|
+
the destination of the link will be the page and anchor for this post.
|
147
221
|
}
|
148
|
-
tag '
|
149
|
-
|
222
|
+
tag 'forum:post:link' do |tag|
|
223
|
+
link_to tag.render('forum:post:name'), tag.render('forum:post:url')
|
150
224
|
end
|
151
225
|
|
152
226
|
desc %{
|
153
|
-
|
227
|
+
Renders a standard gravatar block (as in the forum pages) for the author of this post.
|
154
228
|
}
|
155
|
-
tag '
|
156
|
-
|
229
|
+
tag 'forum:post:gravatar' do |tag|
|
230
|
+
%{<div class="speaker">#{standard_gravatar_for(tag.locals.post.reader)}</div>}
|
157
231
|
end
|
158
232
|
|
159
233
|
desc %{
|
160
|
-
|
234
|
+
Renders the name of the author of this post.
|
161
235
|
}
|
162
|
-
tag '
|
163
|
-
tag.locals.
|
236
|
+
tag 'forum:post:author' do |tag|
|
237
|
+
tag.locals.post.reader.name
|
164
238
|
end
|
165
239
|
|
166
240
|
desc %{
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
241
|
+
Renders the (sanitized and textilized) body of the current post.
|
242
|
+
}
|
243
|
+
tag 'forum:post:body' do |tag|
|
244
|
+
tag.locals.post.body_html
|
245
|
+
end
|
172
246
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
</r:if_comments>
|
178
|
-
<r:unless_comments>
|
179
|
-
<r:comment_link class="how_exciting">Be the first to add a comment!</r:comment_link>
|
180
|
-
</r:unless_comments>
|
181
|
-
</code></pre>
|
247
|
+
desc %{
|
248
|
+
Renders a description line for the current post, which is usually something like
|
249
|
+
'comment added by', 'new reply from' or 'new topic begun by' followed by the author's
|
250
|
+
name and the colloquial form of the creation date.
|
182
251
|
}
|
252
|
+
tag 'forum:post:context' do |tag|
|
253
|
+
output = []
|
254
|
+
post = tag.locals.post
|
255
|
+
if post.page
|
256
|
+
output << I18n.t('new_comment_from')
|
257
|
+
elsif post.first?
|
258
|
+
output << I18n.t('new_reply_from')
|
259
|
+
else
|
260
|
+
output << I18n.t('new_topic_from')
|
261
|
+
end
|
262
|
+
output << %{<a href="#{reader_path(tag.locals.post.reader)}">#{tag.render('forum:post:author')}</a>}
|
263
|
+
output << tag.render('forum:post:date')
|
264
|
+
output.join(' ')
|
265
|
+
end
|
266
|
+
|
267
|
+
desc %{
|
268
|
+
Renders the creation date of the current post
|
269
|
+
}
|
270
|
+
tag 'forum:post:date' do |tag|
|
271
|
+
I18n.l tag.locals.post.created_at, :format => :standard
|
272
|
+
end
|
273
|
+
|
274
|
+
# page comments are just a special case of posts that have a page but not topic
|
275
|
+
# there is the difference that we generally want to display the whole set
|
276
|
+
# and the added complication that they should be paginated.
|
277
|
+
# but we only need to define some more collection and summary tags and set the post collection appropriately
|
278
|
+
|
279
|
+
desc %{
|
280
|
+
The address for add-a-comment links
|
281
|
+
}
|
282
|
+
tag 'comment_url' do |tag|
|
283
|
+
new_page_post_path(tag.locals.page)
|
284
|
+
end
|
285
|
+
|
183
286
|
tag 'comment_link' do |tag|
|
184
|
-
raise TagError, "can't have `r:comment_link' without a page." unless tag.locals.page
|
185
287
|
options = tag.attr.dup
|
186
|
-
options['class'] ||= 'newmessage'
|
187
288
|
attributes = options.inject('') { |s, (k, v)| s << %{#{k.to_s.downcase}="#{v}" } }.strip
|
188
289
|
attributes = " #{attributes}" unless attributes.empty?
|
189
|
-
text = tag.double? ? tag.expand : "Add a comment"
|
290
|
+
text = tag.double? ? tag.expand : I18n.t("Add a comment")
|
190
291
|
%{<a href="#{tag.render('comment_url')}"#{attributes}>#{text}</a>}
|
191
292
|
end
|
192
293
|
|
193
294
|
desc %{
|
194
|
-
|
295
|
+
Anything between if_comments tags is displayed only - dramatic pause - if there are comments.
|
195
296
|
}
|
196
|
-
tag '
|
197
|
-
raise TagError, "can't have
|
198
|
-
|
297
|
+
tag 'if_comments' do |tag|
|
298
|
+
raise TagError, "can't have if_comments without a page" unless page = tag.locals.page
|
299
|
+
tag.expand if page.posts.any?
|
199
300
|
end
|
200
301
|
|
201
302
|
desc %{
|
202
|
-
|
303
|
+
Anything between unless_comments tags is displayed only if there are no comments.
|
304
|
+
|
305
|
+
*Usage:*
|
306
|
+
<pre><code><r:unless_comments>...</r:unless_comments></code></pre>
|
203
307
|
}
|
204
|
-
tag '
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
308
|
+
tag 'unless_comments' do |tag|
|
309
|
+
raise TagError, "can't have unless_comments without a page" unless page = tag.locals.page
|
310
|
+
tag.expand unless page.posts.any?
|
311
|
+
end
|
312
|
+
|
313
|
+
tag 'comments' do |tag|
|
314
|
+
raise TagError, "can't have comments without a page" unless page = tag.locals.page
|
315
|
+
if page.commentable?
|
316
|
+
output = []
|
317
|
+
tag.locals.posts = tag.locals.paginated_list = page.posts.paginate
|
318
|
+
tag.expand
|
210
319
|
end
|
211
|
-
results << %{</ul>}
|
212
|
-
results
|
213
320
|
end
|
214
321
|
|
215
322
|
desc %{
|
216
|
-
|
217
|
-
|
218
|
-
Takes options with_title (set to false to omit the usual heading), by_forum (set to true to show a discussion category dropdown) and by_reader (set to true to show a message-from dropdown) and label (set to the title you would like to display over the main search field).
|
323
|
+
Renders string (internationalised) like "1 comment", "27 comments" or "no comments yet".
|
219
324
|
}
|
220
|
-
tag '
|
221
|
-
|
222
|
-
compact = true unless tag.attr['by_forum'] == 'true' || tag.attr['by_reader'] == 'true'
|
223
|
-
q_label = tag.attr['label']
|
224
|
-
q_label = "Look for this text" if q_label.blank? && !compact
|
225
|
-
results << %{<form class="friendly" action="#{search_posts_url}">}
|
226
|
-
results << %{<h2>Forum Search</h2>} unless tag.attr['with_title'] == 'false'
|
227
|
-
results << %{<p>}
|
228
|
-
results << %{<label for="q">#{q_label}</label><br />} unless q_label.blank?
|
229
|
-
results << %{#{text_field_tag("q", params[:q], :class => 'standard')}}
|
230
|
-
results << %{#{submit_tag "search", :class => 'button'}} if compact
|
231
|
-
results << %{</p>}
|
232
|
-
unless compact
|
233
|
-
results << %{<p><label for="reader_id">From this person</label><br /><select name="reader_id"><option value="">anyone</option>#{options_from_collection_for_select(Reader.all, "id", "name")}</select></p>} if tag.attr['by_reader'] == 'true'
|
234
|
-
results << %{<p><label for="forum_id">In this discussion category</label><br /><select name="forum_id"><option value="">anywhere</option>#{options_from_collection_for_select(Forum.visible, "id", "name")}</select></p>} if tag.attr['by_forum'] == 'true'
|
235
|
-
results << %{<p class="buttons">#{submit_tag "search", :class => 'button'}</p>}
|
236
|
-
end
|
237
|
-
results << %{</form>}
|
238
|
-
results
|
325
|
+
tag 'comments:summary' do |tag|
|
326
|
+
I18n.t("comment_count", :count => tag.locals.posts.total_entries)
|
239
327
|
end
|
240
328
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
329
|
+
desc %{
|
330
|
+
Loops over the (paginated) comment set in ascending order of date. Within the loop you can
|
331
|
+
use the r:comment shorthand or any r:forum:post:* tags. Note that r:forum:topic tags won't
|
332
|
+
work: there is no topic to show.
|
333
|
+
}
|
334
|
+
tag 'comments:each' do |tag|
|
245
335
|
results = []
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
results << %{<a href="#{forum_topic_path(topic.forum, topic)}">#{topic.name}</a> }
|
250
|
-
results << %{<span class="credit">}
|
251
|
-
if topic.page
|
252
|
-
results << " commented upon by "
|
253
|
-
elsif post.first?
|
254
|
-
results << " started by "
|
255
|
-
else
|
256
|
-
results << " replied to by "
|
336
|
+
tag.locals.posts.each do |post|
|
337
|
+
tag.locals.post = post
|
338
|
+
results << tag.expand
|
257
339
|
end
|
258
|
-
results <<
|
259
|
-
results << %{</span>}
|
340
|
+
results << tag.render('pagination', tag.attr.dup)
|
260
341
|
results
|
261
342
|
end
|
262
343
|
|
344
|
+
desc %{
|
345
|
+
A useful shortcut: To enable page commenting, all you have to do is put this in your layout:
|
263
346
|
|
347
|
+
<pre><code><r:comments:all /></code></pre>
|
348
|
+
|
349
|
+
It will display a (paginated) list of page comments followed by an 'add comment' link that you
|
350
|
+
can hook into using the supplied forum javascript or your own equivalent.
|
351
|
+
}
|
352
|
+
tag 'comments:all' do |tag|
|
353
|
+
posts = tag.locals.posts
|
354
|
+
results = ""
|
355
|
+
results << %{<div class="page_comments">}
|
356
|
+
results << %{<p class="context">#{tag.render('comments:summary')}</p>}
|
357
|
+
posts.each do |post|
|
358
|
+
tag.locals.post = post
|
359
|
+
results << tag.render('comment')
|
360
|
+
end
|
361
|
+
results << %{<p class="add_comment">#{tag.render('comment_link', 'class' => 'remote')}</p>}
|
362
|
+
results << tag.render('pagination', tag.attr.dup)
|
363
|
+
results << "</div>"
|
364
|
+
results
|
365
|
+
end
|
264
366
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
367
|
+
desc %{
|
368
|
+
A useful shortcut that renders an entire post - in much the same way as a post would appear
|
369
|
+
in the forum - so that it can be displayed as a comment on the page.
|
370
|
+
}
|
371
|
+
tag 'comment' do |tag|
|
372
|
+
raise TagError, "can't have r:comment without a post" unless post = tag.locals.post
|
373
|
+
if tag.double?
|
374
|
+
tag.locals.reader = post.reader
|
375
|
+
tag.expand
|
376
|
+
else
|
377
|
+
output = %{<div class="post"><div class="post_wrapper">}
|
378
|
+
output << tag.render("forum:post:gravatar")
|
379
|
+
output << %{<div class="post_header">}
|
380
|
+
output << %{<h2>#{tag.render("forum:post:reader")}</h2>}
|
381
|
+
output << %{<p class="context">#{tag.render("forum:post:context")}</p>}
|
382
|
+
output << %{</div>}
|
383
|
+
output << %{<div class="post_body">}
|
384
|
+
output << tag.render("forum:post:body")
|
385
|
+
output
|
282
386
|
end
|
283
387
|
end
|
284
388
|
|