radiant-forum-extension 2.1.6 → 3.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. data/README.md +20 -16
  2. data/Rakefile +0 -16
  3. data/app/controllers/forum_base_controller.rb +32 -10
  4. data/app/controllers/posts_controller.rb +37 -14
  5. data/app/helpers/forum_helper.rb +23 -16
  6. data/app/models/forum.rb +11 -7
  7. data/app/models/post.rb +53 -14
  8. data/app/models/post_attachment.rb +1 -1
  9. data/app/models/topic.rb +21 -8
  10. data/app/views/admin/dashboard/_forum_dashboard.html.haml +51 -0
  11. data/app/views/admin/forums/_form.html.haml +12 -0
  12. data/app/views/admin/forums/edit.html.haml +1 -1
  13. data/app/views/admin/forums/index.html.haml +16 -20
  14. data/app/views/admin/posts/_form.html.haml +2 -2
  15. data/app/views/admin/posts/edit.html.haml +1 -1
  16. data/app/views/admin/posts/index.html.haml +5 -5
  17. data/app/views/admin/reader_configuration/_edit_forum.html.haml +6 -0
  18. data/app/views/admin/reader_configuration/_forum.html.haml +5 -1
  19. data/app/views/admin/topics/_form.html.haml +3 -5
  20. data/app/views/admin/topics/edit.html.haml +1 -1
  21. data/app/views/admin/topics/index.html.haml +5 -5
  22. data/app/views/forums/_forum.html.haml +6 -5
  23. data/app/views/forums/_latest.html.haml +14 -12
  24. data/app/views/forums/_statistics.html.haml +4 -4
  25. data/app/views/forums/index.html.haml +8 -10
  26. data/app/views/forums/index.rss.builder +2 -2
  27. data/app/views/forums/show.html.haml +12 -15
  28. data/app/views/pages/_add_comment.html.haml +9 -8
  29. data/app/views/pages/_comments.html.haml +3 -3
  30. data/app/views/posts/_attachments.html.haml +1 -1
  31. data/app/views/posts/_confirm_delete.html.haml +7 -2
  32. data/app/views/posts/_context.html.haml +5 -5
  33. data/app/views/posts/_edit_links.html.haml +4 -4
  34. data/app/views/posts/_form.html.haml +15 -12
  35. data/app/views/posts/_ineditable.html.haml +7 -0
  36. data/app/views/posts/_latest.html.haml +3 -8
  37. data/app/views/posts/_minimal.html.haml +7 -0
  38. data/app/views/posts/_post.html.haml +20 -17
  39. data/app/views/posts/_post.rss.builder +2 -2
  40. data/app/views/posts/_search_form.html.haml +13 -13
  41. data/app/views/posts/_search_results.html.haml +15 -0
  42. data/app/views/posts/_search_summary.html.haml +13 -0
  43. data/app/views/posts/_uploader.html.haml +1 -1
  44. data/app/views/posts/edit.html.haml +22 -21
  45. data/app/views/posts/index.html.haml +12 -19
  46. data/app/views/posts/index.rss.builder +6 -6
  47. data/app/views/posts/new.html.haml +19 -22
  48. data/app/views/posts/remove.html.haml +15 -15
  49. data/app/views/posts/show.html.haml +16 -10
  50. data/app/views/readers/_forum_messages.html.haml +9 -6
  51. data/app/views/readers/_messages_summary.html.haml +1 -1
  52. data/app/views/shared/_standard_forum_parts.html.haml +41 -0
  53. data/app/views/topics/_busiest.html.haml +9 -8
  54. data/app/views/topics/_context.html.haml +11 -8
  55. data/app/views/topics/_latest.html.haml +2 -2
  56. data/app/views/topics/_locked.html.haml +1 -1
  57. data/app/views/topics/_minimal.html.haml +2 -2
  58. data/app/views/topics/_replies.html.haml +4 -3
  59. data/app/views/topics/_reply.html.haml +13 -8
  60. data/app/views/topics/_topic.html.haml +5 -9
  61. data/app/views/topics/_topic.rss.builder +1 -1
  62. data/app/views/topics/index.html.haml +14 -10
  63. data/app/views/topics/index.rss.builder +2 -2
  64. data/app/views/topics/show.html.haml +20 -19
  65. data/app/views/topics/show.rss.builder +3 -3
  66. data/config/initializers/radiant_config.rb +4 -1
  67. data/config/locales/en.yml +202 -178
  68. data/db/migrate/002_pages_commentable.rb +1 -2
  69. data/db/migrate/004_sample_layout.rb +38 -15
  70. data/db/migrate/20110111080550_detach_observer.rb +4 -4
  71. data/db/migrate/20110613112823_search_text.rb +9 -0
  72. data/db/migrate/20110613130230_tidy_up.rb +27 -0
  73. data/db/migrate/20110630083446_page_replied_at.rb +11 -0
  74. data/forum_extension.rb +13 -11
  75. data/lib/commentable_model.rb +13 -9
  76. data/lib/forum_admin_ui.rb +6 -6
  77. data/lib/forum_page.rb +5 -3
  78. data/lib/forum_reader.rb +7 -1
  79. data/lib/forum_reader_sessions_controller.rb +2 -3
  80. data/lib/forum_tags.rb +126 -11
  81. data/lib/radiant-forum-extension.rb +8 -0
  82. data/public/cleditor/images/buttons.gif +0 -0
  83. data/public/cleditor/images/icons/1.gif +0 -0
  84. data/public/cleditor/images/icons/10.gif +0 -0
  85. data/public/cleditor/images/icons/11.gif +0 -0
  86. data/public/cleditor/images/icons/12.gif +0 -0
  87. data/public/cleditor/images/icons/2.gif +0 -0
  88. data/public/cleditor/images/icons/3.gif +0 -0
  89. data/public/cleditor/images/icons/4.gif +0 -0
  90. data/public/cleditor/images/icons/5.gif +0 -0
  91. data/public/cleditor/images/icons/6.gif +0 -0
  92. data/public/cleditor/images/icons/7.gif +0 -0
  93. data/public/cleditor/images/icons/8.gif +0 -0
  94. data/public/cleditor/images/icons/9.gif +0 -0
  95. data/public/cleditor/images/icons/icons.gif +0 -0
  96. data/public/cleditor/images/toolbar.gif +0 -0
  97. data/public/cleditor/jquery.cleditor.css +24 -0
  98. data/public/cleditor/jquery.cleditor.icon.js +65 -0
  99. data/public/cleditor/jquery.cleditor.js +1132 -0
  100. data/public/cleditor/jquery.cleditor.xhtml.js +230 -0
  101. data/public/javascripts/forum.js +146 -107
  102. data/public/stylesheets/sass/forum.sass +94 -58
  103. data/radiant-forum-extension.gemspec +25 -276
  104. data/spec/controllers/forums_controller_spec.rb +0 -7
  105. data/spec/controllers/posts_controller_spec.rb +20 -37
  106. data/spec/controllers/topics_controller_spec.rb +1 -7
  107. data/spec/datasets/forums_dataset.rb +38 -40
  108. data/spec/lib/commentable_model_spec.rb +26 -38
  109. data/spec/lib/forum_reader_spec.rb +0 -4
  110. data/spec/models/forum_spec.rb +46 -16
  111. data/spec/models/post_spec.rb +10 -6
  112. data/spec/models/topic_spec.rb +26 -17
  113. data/spec/spec_helper.rb +1 -0
  114. metadata +66 -99
  115. data/VERSION +0 -1
  116. data/app/views/forums/_standard_parts.html.haml +0 -59
  117. data/lib/sanitize/config/forum.rb +0 -49
  118. data/public/javascripts/gallery.js +0 -275
  119. data/public/punymce/blank.htm +0 -1
  120. data/public/punymce/css/content.css +0 -4
  121. data/public/punymce/css/editor.css +0 -58
  122. data/public/punymce/i18n/sv.js +0 -28
  123. data/public/punymce/img/icons.gif +0 -0
  124. data/public/punymce/img/icons_uncompressed.png +0 -0
  125. data/public/punymce/plugins/bbcode.js +0 -1
  126. data/public/punymce/plugins/bbcode_src.js +0 -50
  127. data/public/punymce/plugins/editsource/css/editor.css +0 -3
  128. data/public/punymce/plugins/editsource/editsource.js +0 -1
  129. data/public/punymce/plugins/editsource/editsource_src.js +0 -81
  130. data/public/punymce/plugins/editsource/img/icons.gif +0 -0
  131. data/public/punymce/plugins/emoticons/css/content.css +0 -13
  132. data/public/punymce/plugins/emoticons/css/editor.css +0 -17
  133. data/public/punymce/plugins/emoticons/emoticons.js +0 -1
  134. data/public/punymce/plugins/emoticons/emoticons_src.js +0 -303
  135. data/public/punymce/plugins/emoticons/img/emoticons.gif +0 -0
  136. data/public/punymce/plugins/emoticons/img/emoticons.png +0 -0
  137. data/public/punymce/plugins/emoticons/img/trans.gif +0 -0
  138. data/public/punymce/plugins/entities.js +0 -1
  139. data/public/punymce/plugins/entities_src.js +0 -37
  140. data/public/punymce/plugins/forceblocks.js +0 -1
  141. data/public/punymce/plugins/forceblocks_src.js +0 -465
  142. data/public/punymce/plugins/forcenl.js +0 -1
  143. data/public/punymce/plugins/forcenl_src.js +0 -26
  144. data/public/punymce/plugins/image/css/editor.css +0 -1
  145. data/public/punymce/plugins/image/image.js +0 -1
  146. data/public/punymce/plugins/image/image_src.js +0 -30
  147. data/public/punymce/plugins/image/img/icons.gif +0 -0
  148. data/public/punymce/plugins/link/css/editor.css +0 -2
  149. data/public/punymce/plugins/link/img/icons.gif +0 -0
  150. data/public/punymce/plugins/link/link.js +0 -1
  151. data/public/punymce/plugins/link/link_src.js +0 -36
  152. data/public/punymce/plugins/paste.js +0 -1
  153. data/public/punymce/plugins/paste_src.js +0 -169
  154. data/public/punymce/plugins/protect.js +0 -1
  155. data/public/punymce/plugins/protect_src.js +0 -30
  156. data/public/punymce/plugins/safari2x.js +0 -1
  157. data/public/punymce/plugins/safari2x_src.js +0 -284
  158. data/public/punymce/plugins/tabfocus.js +0 -1
  159. data/public/punymce/plugins/tabfocus_src.js +0 -45
  160. data/public/punymce/plugins/textcolor/css/editor.css +0 -7
  161. data/public/punymce/plugins/textcolor/img/icons.gif +0 -0
  162. data/public/punymce/plugins/textcolor/textcolor.js +0 -1
  163. data/public/punymce/plugins/textcolor/textcolor_src.js +0 -73
  164. data/public/punymce/puny_mce.js +0 -1
  165. data/public/punymce/puny_mce_full.js +0 -1
  166. data/public/punymce/puny_mce_src.js +0 -1460
data/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  This is a tidy but comprehensive forum implementation that plugs into radiant and supports both discussion forums and page comments.
4
4
 
5
- This new (as I write) version 2 is a complete rewrite with a lot of internal clarification in preparation for rails 3. It's in late beta at the moment: bug fixes, documentation and packaging for public release.
5
+ This new (as I write) version 2 is a complete rewrite with a lot of internal clarification in preparation for rails 3. It's only a release candidate at this stage: I'm working on bug fixes, documentation and packaging for public release. There is also a developing demo and documentation site that you can use as a starting point. Code is at [https://github.com/spanner/radiant-forum-demo](https://github.com/spanner/radiant-forum-demo) and the site will be public soon.
6
6
 
7
- The forum is designed to be pick-and-mixed with other radiant extensions, so it's very focused on being an excellent forum and doesn't do anything else. I hope you will find it's very good at what it does do:
7
+ The forum is designed to be pick-and-mixed with other radiant extensions, so it's focused on being a good forum and doesn't do anything else. I hope you will find it's good at what it does do:
8
8
 
9
9
  * forums, topics, posts etc
10
10
  * page comments
@@ -17,14 +17,20 @@ The forum is designed to be pick-and-mixed with other radiant extensions, so it'
17
17
 
18
18
  I've tried to keep it simple and tidy both inside and out. This is helped by the fact that all the user-management is in the reader extension, so you should find this is easy to adapt and extend. The admin interface on the other hand is pretty basic: we expect most administration to happen through the public pages but there does need to be at least a dashboard to give a better overview. That will come soon.
19
19
 
20
- As far as I know this is the best forum software available for rails. I look forward to finding out that it's not.
20
+ As far as I know this is the only decent forum software available for rails. If I can't find a better alternative, I will spin this off as a separate rails 3 engine that works alongside radiant but does not require it.
21
+
22
+ ## Demo and documentation
23
+
24
+ You can find a standard radiant forum installation at [https://github.com/spanner/radiant-forum-demo](https://github.com/spanner/radiant-forum-demo). It serves many purposes: integration test target, reference platform, documentation and demo site and starting point for larger projects. There is some way to go on the documentation front but it should already serve as a useful starting point.
21
25
 
22
26
  ## Status
23
27
 
24
- This code has been surviving in the world for four or five years (and more, since some it originally came from Beast), but this version is about 75% new and you know what that means: new bugs. Github issues are always welcome.
28
+ This code has been surviving in the world for five or six years (and more, since some it originally came from Beast), but this version is about 75% new and you know what that means: new bugs. Github issues are always welcome.
25
29
 
26
30
  ## Latest
27
31
 
32
+ * Tidied up and made compatible with radiant 1
33
+ * Group-visibility folded in
28
34
  * Inline editing with editable-for-interval setting
29
35
  * Punymce-based wysiwig editing.
30
36
  * Page comments data structure simplified. Migration required.
@@ -34,7 +40,7 @@ This code has been surviving in the world for four or five years (and more, sinc
34
40
  * Basic admin interface as well as readerland administration
35
41
  * Editable interval for messages
36
42
 
37
- This version is not wholly multi-site compatible at the moment, since it relies heavily on configuration items that are not yet site-scoped. They will be soon.
43
+ This version is not multi-site compatible. It relies heavily on configuration items that are not yet site-scoped. That's next on the list after radiant 1 comes out.
38
44
 
39
45
  ## Still to do
40
46
 
@@ -46,13 +52,9 @@ This version is not wholly multi-site compatible at the moment, since it relies
46
52
 
47
53
  Radiant 0.9.2 (we're using the new configuration interface) with the [reader](http://github.com/spanner/radiant-reader-extension) extension. Reader has a few requirements of its own so it's best to install that one first, make sure it's testing clean and then install the forum.
48
54
 
49
- We also require [will_paginate](http://github.com/mislav/will_paginate/) and [paperclip](http://github.com/thoughtbot/paperclip/) as gems but you will already have those.
50
-
51
55
  sudo rake gems:install
52
56
 
53
57
  should get everything you need.
54
-
55
- The forum is compatible with multi_site but you should use [sites](https://github.com/spanner/radiant-sites-extension) instead if you want forums and readers site-scoped.
56
58
 
57
59
  ## Administration
58
60
 
@@ -62,6 +64,10 @@ The forum is easy to use and almost entirely separate from your page hierarchy.
62
64
 
63
65
  There is now a configuration panel for the forum and you shouldn't have to do any console-tinkering.
64
66
 
67
+ ## Changing forum text
68
+
69
+ Every string used in the public-facing forum (and reader) pages comes from the locale file. They are labelled functionally rather than literally, with keys like `forum_extension.no_search_results`. To change the wording, either edit or override the locale.
70
+
65
71
  ## Layouts
66
72
 
67
73
  Forum pages have their own controllers but they are presented inside your normal radiant layouts. Use the configuration interface to choose the layout you want to use. Your layout can work in the usual way: all we do is define page parts for you to include with `<r:content part="something" />`. These are the parts available:
@@ -93,13 +99,11 @@ Have a look at the included sample layout for a starting point.
93
99
 
94
100
  ## Speed and caching
95
101
 
96
- This version of the forum is designed to work from behind radiant's main cache. There is no context-specific material in any of the pages or page parts visible to a normal browsing user and the comment and reply forms are normally retrieved by ajax calls after the pages have loaded. For that you need to use the provided (jquery-based) forum.js, or write your own equivalent (or for a more robust but less helpful forum, skip the ajax and let people click through to the comment or reply form).
97
-
98
- The intention is to make this behaviour configurable but at the moment I'm finding the forum pages too slow if uncached. Most of the wait comes from the shared_layouts/radius template machinery but there are some slow queries too. With optimisation a live forum should be viable.
102
+ Forum and reader views are not cached, by default, but commented pages are. It is possible to cache the forum if you omit any dynamic content (such as the reply form), but at some cost to ease of use.
99
103
 
100
104
  ## Private discussion
101
105
 
102
- If you install the [reader_group](http://github.com/spanner/radiant-reader_group-extension) and [group_forum](http://github.com/spanner/radiant-group_forum-extension) extensions then your forums can be made visible only to designated groups, as with pages and other groupable items.
106
+ Group functionality has recently been folded into the reader and forum extensions, so you don't need to install a whole tree of tiny changes any more. The interface hasn't quite settled down, but the functionality is solid: associate a forum with a group, and all of its contents are visible only to members of that group.
103
107
 
104
108
  ## Searching the forum
105
109
 
@@ -128,15 +132,15 @@ The forum comes with some jquery-based scripting to handle inline administration
128
132
 
129
133
  ## Smilies
130
134
 
131
- Included here is a redcloth extension to handle emoticons of the type inserted by the punymce toolbar we're using. Since we have control of both ends I've customised the set to make accidental emoticons less likely. There will soon be an option to disable them completely.
135
+ Included here is a redcloth extension to handle emoticons of the type inserted by the punymce toolbar we're using. I've customised the set to make accidental emoticons less likely. There will soon be an option to disable them completely.
132
136
 
133
137
  ## Bugs
134
138
 
135
- In the short term, quite likely. [Github issues](http://github.com/spanner/radiant-forum-extension/issues), please, or for little things an email or github message is fine.
139
+ In the short term, very likely. [Github issues](http://github.com/spanner/radiant-forum-extension/issues), please, or for little things an email or github message is fine.
136
140
 
137
141
  ## Author & Copyright
138
142
 
139
143
  * William Ross, for spanner. will at spanner.org
140
- * Originally based on dear old Beast, currently not visible at [http://beast.caboo.se](http://beast.caboo.se)
144
+ * Originally based on dear old Beast
141
145
  * Copyright 2007-11 spanner ltd
142
146
  * released under the same terms as Rails and/or Radiant
data/Rakefile CHANGED
@@ -1,19 +1,3 @@
1
- begin
2
- require 'jeweler'
3
- Jeweler::Tasks.new do |gem|
4
- gem.name = "radiant-forum-extension"
5
- gem.summary = %Q{Forum and Comment Extension for Radiant CMS}
6
- gem.description = %Q{Nice clean forums and page comments for inclusion in your radiant site. Derived long ago from beast. Requires the reader extension and share_layouts.}
7
- gem.email = "will@spanner.org"
8
- gem.homepage = "http://github.com/spanner/radiant-forum-extension"
9
- gem.authors = ["spanner"]
10
- gem.add_dependency "radiant", ">= 0.9.0"
11
- gem.add_dependency 'radiant-reader-extension', "~> 1.3.0"
12
- end
13
- rescue LoadError
14
- puts "Jeweler (or a dependency) not available. This is only required if you plan to package forum as a gem."
15
- end
16
-
17
1
  # In rails 1.2, plugins aren't available in the path until they're loaded.
18
2
  # Check to see if the rspec plugin is installed first and require
19
3
  # it if it is. If not, use the gem version.
@@ -4,41 +4,64 @@ class ForumBaseController < ReaderActionController
4
4
  radiant_layout { |c| Radiant::Config['forum.layout'] || Radiant::Config['reader.layout'] }
5
5
  before_filter :require_login_unless_public
6
6
  before_filter :establish_context
7
+ before_filter :require_visibility_to_reader
7
8
  helper :forum, :reader
8
9
 
9
10
  protected
10
11
 
12
+ def set_cache_header
13
+ if Radiant.config['forum.cached?']
14
+ expires_in (Radiant.config['forum.cache_duration'] || 60).to_i.minutes, :public => true
15
+ else
16
+ expires_now
17
+ end
18
+ end
19
+
11
20
  def require_login_unless_public
12
21
  return false unless Radiant::Config['forum.public?'] || require_reader && require_activated_reader
13
22
  end
14
23
 
24
+ def require_visibility_to_reader
25
+ if @page && !@page.visible_to?(current_reader)
26
+ flash[:error] = t("page_not_public")
27
+ redirect_to reader_permission_denied_url
28
+ return false
29
+ end
30
+
31
+ if @forum && !@forum.visible_to?(current_reader)
32
+ flash[:error] = "forum_not_public"
33
+ redirect_to reader_permission_denied_url
34
+ return false
35
+ end
36
+ end
37
+
15
38
  def establish_context
16
39
  @reader = Reader.find(params[:reader_id]) unless params[:reader_id].blank?
17
40
  @topic = Topic.visible_to(current_reader).find(params[:topic_id]) unless params[:topic_id].blank?
18
41
  @forum = Forum.visible_to(current_reader).find(params[:forum_id]) unless params[:forum_id].blank?
19
42
  @page = Page.visible_to(current_reader).find(params[:page_id]) unless params[:page_id].blank?
20
43
  end
21
-
44
+
22
45
  def redirect_to_post
23
46
  if (@post.page)
24
47
  redirect_to "#{@post.page.url}?#{WillPaginate::ViewHelpers.pagination_options[:param_name]}=#{@post.page_when_paginated}##{@post.dom_id}"
25
48
  elsif @post.first?
26
- redirect_to forum_topic_path(@post.topic.forum, @post.topic)
49
+ redirect_to topic_path(@post.topic)
27
50
  else
28
51
  post_location = {WillPaginate::ViewHelpers.pagination_options[:param_name] => @post.page_when_paginated, :anchor => @post.dom_id}
29
- redirect_to forum_topic_url(@post.topic.forum, @post.topic, post_location)
52
+ redirect_to topic_path(@post.topic, post_location)
30
53
  end
31
54
  end
32
55
 
33
56
  def redirect_to_topic
34
- redirect_to forum_topic_path(@forum, @topic)
57
+ redirect_to topic_path(@topic)
35
58
  end
36
59
 
37
60
  def redirect_to_page_or_topic
38
61
  if @page
39
62
  redirect_to @page.url
40
63
  elsif @topic
41
- redirect_to forum_topic_url(@topic.forum, @topic)
64
+ redirect_to topic_path(@topic)
42
65
  end
43
66
  end
44
67
 
@@ -51,15 +74,14 @@ protected
51
74
  def render_page_or_feed(template_name = action_name)
52
75
  respond_to do |format|
53
76
  format.html {
54
- expires_now
77
+ set_cache_header
55
78
  render :action => template_name
56
79
  }
57
80
  format.rss {
58
- expires_now
59
- render :action => template_name, :layout => 'feed'
81
+ render :action => template_name, :layout => 'feed'
60
82
  }
61
83
  format.js {
62
- render :action => template_name, :layout => false
84
+ render :action => template_name, :layout => false
63
85
  }
64
86
  end
65
87
  end
@@ -72,7 +94,7 @@ protected
72
94
  respond_to do |format|
73
95
  format.html {
74
96
  expires_now
75
- flash[:error] = t('topic_locked')
97
+ flash[:error] = t('forum_extension.topic_locked')
76
98
  redirect_to_page_or_topic
77
99
  }
78
100
  format.js { render :partial => 'topics/locked' }
@@ -9,12 +9,16 @@ class PostsController < ForumBaseController
9
9
  def index
10
10
  @term = params[:q]
11
11
  posts = Post.visible_to(current_reader)
12
- posts = posts.containing(@term) unless @term.blank?
12
+ posts = posts.matching(@term) unless @term.blank?
13
13
  posts = posts.from_reader(@reader) if @reader
14
14
  posts = posts.in_forum(@forum) if @forum
15
15
  posts = posts.in_topic(@topic) if @topic
16
16
  @posts = posts.paginate(pagination_parameters)
17
- render_page_or_feed
17
+ respond_to do |format|
18
+ format.html
19
+ format.js { render :partial => 'search_results' }
20
+ format.rss { render :layout => 'feed' }
21
+ end
18
22
  end
19
23
 
20
24
  def show
@@ -26,15 +30,24 @@ class PostsController < ForumBaseController
26
30
 
27
31
  def new
28
32
  unless @post.topic || @post.page
29
- @forum ||= Forum.find_by_name(Radiant::Config['forum.default_forum'])
30
- @post.topic = @forum.topics.new
33
+ if @forum ||= Forum.find_by_name(Radiant::Config['forum.default_forum'])
34
+ @post.topic = @forum.topics.new
35
+ else
36
+ @post.topic = Topic.new
37
+ end
31
38
  end
32
39
  respond_to do |format|
33
- format.html { expires_now }
40
+ format.html {
41
+ @inline = false
42
+ render
43
+ }
34
44
  format.js {
45
+ @inline = true
35
46
  if @post.page
47
+ # page comment form is usually brought in by xhr so that page itself can be cached
36
48
  render :partial => 'pages/add_comment', :layout => false
37
49
  else
50
+ # this is a much less common case, but you can cache topic pages if you really want to
38
51
  render :partial => 'topics/reply', :layout => false
39
52
  end
40
53
  }
@@ -49,33 +62,43 @@ class PostsController < ForumBaseController
49
62
  format.js { render :partial => 'post' }
50
63
  end
51
64
  rescue ActiveRecord::RecordInvalid
52
- flash[:error] = t("validation_failure")
65
+ flash[:error] = t("forum_extension.validation_failure")
53
66
  respond_to do |format|
54
67
  format.html { render :action => 'new' }
55
- format.js { render :template => 'posts/new', :layout => false }
68
+ format.js { render :partial => 'form' }
56
69
  end
57
70
  end
58
71
 
59
72
  def edit
60
73
  respond_to do |format|
61
- format.html { expires_now }
62
- format.js { render :template => 'posts/edit', :layout => false }
74
+ format.html {
75
+ @inline = false
76
+ render
77
+ }
78
+ format.js {
79
+ @inline = true
80
+ if current_reader.is_moderator?(@post) || @post.editable_by?(current_reader)
81
+ render :partial => 'form'
82
+ else
83
+ render :partial => 'ineditable'
84
+ end
85
+ }
63
86
  end
64
87
  end
65
88
 
66
89
  def update
67
90
  @post.attributes = params[:post]
68
91
  @post.save!
69
- Radiant::Cache.clear if @post.page
92
+ Radiant::Cache.clear if @post.page || (@post.topic && Radiant.config['forum.cached?'])
70
93
  respond_to do |format|
71
94
  format.html { redirect_to_post }
72
95
  format.js { render :partial => 'post' }
73
96
  end
74
97
  rescue ActiveRecord::RecordInvalid
75
- flash[:error] = t("validation_failure")
98
+ flash[:error] = t("forum_extension.validation_failure")
76
99
  respond_to do |format|
77
100
  format.html { render :action => 'edit' }
78
- format.js { render :template => 'posts/edit', :layout => false }
101
+ format.js { render :partial => 'form' }
79
102
  end
80
103
  end
81
104
 
@@ -89,13 +112,13 @@ class PostsController < ForumBaseController
89
112
  def destroy
90
113
  if @post.first?
91
114
  @post.topic.destroy
92
- flash[:notice] = t("topic_removed")
115
+ flash[:notice] = t("forum_extension.topic_removed")
93
116
  redirect_to_forum
94
117
  else
95
118
  @post.destroy
96
119
  respond_to do |format|
97
120
  format.html {
98
- flash[:notice] = t("post_removed")
121
+ flash[:notice] = t("forum_extension.post_removed")
99
122
  redirect_to_page_or_topic
100
123
  }
101
124
  format.js { render :partial => 'post' }
@@ -1,19 +1,15 @@
1
- require 'sanitize'
2
- require "sanitize/config/forum"
3
-
4
1
  module ForumHelper
2
+ include ReaderHelper
3
+ mattr_accessor :forums_found
5
4
 
6
- def feed_link(url)
7
- link_to image_tag('/images/furniture/feed_14.png', :alt => t('rss_feed')), url, :class => "rssfeed"
5
+ def using_forums?
6
+ @forums_found = Forum.count > 1 unless defined? @forums_found
7
+ @forums_found
8
8
  end
9
-
10
- def clean_textilize(text) # adding smilies to the default reader method
11
- if text.blank?
12
- ""
13
- else
14
- textilized = RedCloth.new(text, [ :hard_breaks ])
15
- textilized.hard_breaks = true if textilized.respond_to?("hard_breaks=")
16
- Sanitize.clean(textilized.to_html(:textile, :smilies), Sanitize::Config::FORUM)
9
+
10
+ def feed_link(url)
11
+ if Radiant.config['forum.offer_rss?']
12
+ link_to image_tag('/images/furniture/feed_14.png', :alt => t('forum_extension.rss_feed')), url, :class => "rssfeed"
17
13
  end
18
14
  end
19
15
 
@@ -28,16 +24,27 @@ module ForumHelper
28
24
  end
29
25
  end
30
26
 
27
+ def link_to_forum(forum, options={})
28
+ title = options.delete(:title) || forum.title
29
+ link_to title, forum_url(forum), options
30
+ end
31
+
32
+ def link_to_topic(topic, options={})
33
+ title = options.delete(:title) || topic.title
34
+ link_to title, topic_url(topic), options
35
+ end
36
+
31
37
  def link_to_post(post, options={})
32
- link_to post.holder.title, paginated_post_url(post), options
38
+ title = options.delete(:title) || post.holder.title
39
+ link_to title, paginated_post_url(post), options
33
40
  end
34
41
 
35
42
  def edit_link(post)
36
- link_to t('edit'), edit_topic_post_url(post.topic, post), :class => 'edit_post', :id => "edit_post_#{post.id}", :title => t("edit_post")
43
+ link_to t('forum_extension.edit'), edit_topic_post_url(post.topic, post), :class => 'edit_post', :id => "edit_post_#{post.id}", :title => t("forum_extension.edit_post")
37
44
  end
38
45
 
39
46
  def remove_link(post)
40
- link_to t('delete'), topic_post_url(post.topic, post), :method => 'delete', :class => 'delete_post', :id => "delete_post_#{post.id}", :title => t("remove_post"), :confirm => t('really_remove_post')
47
+ link_to t('forum_extension.delete'), topic_post_url(post.topic, post), :method => 'delete', :class => 'delete_post', :id => "delete_post_#{post.id}", :title => t("remove_post"), :confirm => t('forum_extension.really_remove_post')
41
48
  end
42
49
 
43
50
  def friendly_date(datetime)
data/app/models/forum.rb CHANGED
@@ -1,22 +1,26 @@
1
1
  class Forum < ActiveRecord::Base
2
2
  has_site if respond_to? :has_site
3
+ has_groups
4
+
3
5
  has_many :topics, :dependent => :destroy
4
6
 
5
7
  default_scope :order => 'name ASC'
6
8
  named_scope :imported, :conditions => "old_id IS NOT NULL"
7
9
  validates_presence_of :name
8
10
 
9
- # other extensions can attach chains here to limit access
10
- def self.visible_to(reader)
11
- self.scoped
12
- end
13
-
14
11
  def dom_id
15
12
  "forum_#{self.id}"
16
13
  end
17
14
 
18
- def visible_to?(reader=nil)
19
- return true if reader || Radiant::Config['forum.public?']
15
+ # chains the visible_to? method created during the has_groups call.
16
+ def visible_to_with_configuration?(reader=nil)
17
+ Rails.logger.warn "Forum#visible_to_with_configuration?"
18
+ return true if (reader || visible_by_default?) && visible_to_without_configuration?(reader)
19
+ end
20
+ alias_method_chain :visible_to?, :configuration
21
+
22
+ def visible_by_default?
23
+ !!Radiant.config['forum.public?']
20
24
  end
21
25
 
22
26
  end
data/app/models/post.rb CHANGED
@@ -11,11 +11,11 @@ class Post < ActiveRecord::Base
11
11
  accepts_nested_attributes_for :attachments, :allow_destroy => true
12
12
  validates_presence_of :reader, :body
13
13
 
14
+ before_save :update_search_text
14
15
  after_create :notify_holder_of_creation
15
16
  after_destroy :notify_holder_of_destruction
16
-
17
+
17
18
  default_scope :order => "posts.created_at DESC"
18
-
19
19
  named_scope :comments, :conditions => "page_id IS NOT NULL"
20
20
  named_scope :non_comments, :conditions => "page_id IS NULL"
21
21
  named_scope :imported, :conditions => "old_id IS NOT NULL"
@@ -32,18 +32,26 @@ class Post < ActiveRecord::Base
32
32
  self.length # replacing a SQL shortcut that omits the distinct clause
33
33
  end
34
34
  end
35
- named_scope :containing, lambda { |term|
36
- {
37
- :conditions => ["posts.body LIKE :term OR topics.name LIKE :term", {:term => "%#{term}%"}],
38
- :joins => "LEFT OUTER JOIN topics on posts.topic_id = topics.id"
35
+ # adapted from the usual scope defined in has_groups, since here visibility is set at the forum level
36
+ named_scope :visible_to, lambda { |reader|
37
+ conditions = "topics.id IS NULL OR forums.id IS NULL OR pp.group_id IS NULL"
38
+ if reader && reader.group_ids.any?
39
+ ids = reader.group_ids
40
+ conditions = ["#{conditions} OR pp.group_id IN(#{ids.map{"?"}.join(',')})", *ids]
41
+ end
42
+ {
43
+ :joins => "INNER JOIN topics on posts.topic_id = topics.id LEFT OUTER JOIN forums on topics.forum_id = forums.id LEFT OUTER JOIN permissions as pp on pp.permitted_id = forums.id AND pp.permitted_type = 'Forum'",
44
+ :conditions => conditions,
45
+ :group => column_names.map { |n| self.table_name + '.' + n }.join(','),
46
+ :readonly => false
47
+ }
48
+ }
49
+ named_scope :matching, lambda { |term|
50
+ {
51
+ :conditions => ["posts.search_text LIKE ?", "%#{stopped(term)}%"]
39
52
  }
40
53
  }
41
54
 
42
- # other extensions can attach chains here to limit access
43
- def self.visible_to(reader)
44
- self.scoped
45
- end
46
-
47
55
  def self.in_forum(forum)
48
56
  in_topics(forum.topics)
49
57
  end
@@ -51,11 +59,19 @@ class Post < ActiveRecord::Base
51
59
  def holder
52
60
  page || topic
53
61
  end
62
+
63
+ def title
64
+ holder.title if holder
65
+ end
54
66
 
55
67
  def comment?
56
68
  !!page
57
69
  end
58
70
 
71
+ def reply?
72
+ !comment? && !first?
73
+ end
74
+
59
75
  def page_when_paginated
60
76
  holder.page_for(self)
61
77
  end
@@ -94,13 +110,21 @@ class Post < ActiveRecord::Base
94
110
 
95
111
  def editable_by?(reader=nil)
96
112
  return false unless reader
97
- still_editable? && reader && (reader.id == reader_id)
113
+ still_editable? && reader.id == reader_id
98
114
  end
99
115
 
100
116
  def visible_to?(reader=nil)
101
- return true if reader || Radiant::Config['forum.public?']
117
+ if topic && !topic.visible_to?(reader)
118
+ false
119
+ elsif page && !page.visible_to?(reader)
120
+ false
121
+ elsif !reader && !Radiant::Config['forum.public?']
122
+ false
123
+ else
124
+ true
125
+ end
102
126
  end
103
-
127
+
104
128
  # so that page comments can be rendered from a radius tag
105
129
  def body_html
106
130
  if body
@@ -131,4 +155,19 @@ class Post < ActiveRecord::Base
131
155
  "post_#{self.id}"
132
156
  end
133
157
 
158
+ private
159
+
160
+ # this is rather crude, but it's database-agnostic, doesn't require
161
+ # any external indexing, and works well enough for most purposes.
162
+ # for a more satisfactory search, tell sphinx to index the search_text field.
163
+
164
+ def update_search_text
165
+ self.search_text = self.class.stopped("#{self.title} #{self.body}")
166
+ end
167
+
168
+ def self.stopped(text="")
169
+ stops = I18n.t('forum_extension.stopwords').split.join('|')
170
+ text.downcase.gsub(/\b(#{stops})\b/, '').gsub(/(\W+)/, ' ')
171
+ end
172
+
134
173
  end
@@ -40,7 +40,7 @@ class PostAttachment < ActiveRecord::Base
40
40
  :path => ":rails_root/:class/:id/:basename:no_original_style.:extension" # attachments can only be accessed through the PostAttachments controller, in case file security is required
41
41
 
42
42
  attr_protected :file_file_name, :file_content_type, :file_file_size
43
- validates_attachment_presence :file, :message => t('error.no_file')
43
+ validates_attachment_presence :file, :message => t('forum_extension.error.no_file')
44
44
  validates_attachment_content_type :file, :content_type => Radiant::Config["forum.attachment.content_types"].split(', ') if Radiant::Config.table_exists? && !Radiant::Config["forum.attachment.content_types"].blank?
45
45
  validates_attachment_size :file, :less_than => Radiant::Config["forum.attachment.max_size"].to_i.megabytes if Radiant::Config.table_exists? && Radiant::Config["forum.attachment.max_size"]
46
46
 
data/app/models/topic.rb CHANGED
@@ -1,9 +1,7 @@
1
1
  class Topic < ActiveRecord::Base
2
2
  has_site if respond_to? :has_site
3
3
  has_comments
4
-
5
4
  belongs_to :forum
6
- belongs_to :replied_by, :class_name => 'Reader'
7
5
 
8
6
  validates_presence_of :name
9
7
  validates_uniqueness_of :old_id, :allow_nil => true
@@ -14,18 +12,33 @@ class Topic < ActiveRecord::Base
14
12
  named_scope :latest, lambda { |count|
15
13
  { :order => 'replied_at DESC', :limit => count }
16
14
  }
17
-
18
- # other extensions can attach chains here to limit access
19
- def self.visible_to(reader)
20
- self.scoped
21
- end
15
+ # adapted from the usual scope defined in has_groups, since here visibility is set at the forum level
16
+ named_scope :visible_to, lambda { |reader|
17
+ conditions = "forums.id IS NULL OR pp.group_id IS NULL"
18
+ if reader && reader.group_ids.any?
19
+ ids = reader.group_ids
20
+ conditions = ["#{conditions} OR pp.group_id IN(#{ids.map{"?"}.join(',')})", *ids]
21
+ end
22
+ {
23
+ :joins => "LEFT OUTER JOIN forums on topics.forum_id = forums.id LEFT OUTER JOIN permissions as pp on pp.permitted_id = forums.id AND pp.permitted_type = 'Forum'",
24
+ :conditions => conditions,
25
+ :group => column_names.map { |n| self.table_name + '.' + n }.join(','),
26
+ :readonly => false
27
+ }
28
+ }
22
29
 
23
30
  def dom_id
24
31
  "topic_#{self.id}"
25
32
  end
26
33
 
27
34
  def visible_to?(reader=nil)
28
- return true if reader || Radiant::Config['forum.public?']
35
+ if forum && !forum.visible_to?(reader)
36
+ false
37
+ elsif !reader && !Radiant::Config['forum.public?']
38
+ false
39
+ else
40
+ true
41
+ end
29
42
  end
30
43
 
31
44
  def reader
@@ -0,0 +1,51 @@
1
+ - include_stylesheet 'admin/forum_dashboard'
2
+
3
+ #recent_comments.dashboard_module
4
+ .header
5
+ %h2
6
+ = t('forum_extension.pages_with_latest_comments')
7
+ .blockcontent
8
+ - latest = Page.last_commented(6)
9
+ - if latest.any?
10
+ - latest.each do |page|
11
+ .page
12
+ %h3
13
+ = link_to("#{icon} #{node_title}", edit_admin_page_url(page), :title => page.url, :class => 'page')
14
+ = link_to("&rarr;", page.path, :class => 'public')
15
+ %p.minor
16
+ =t('forum_extension.comment_count_from', :count => page.posts.count)
17
+ = link_to topic.replied_by.name, reader_url(topic.replied_by)
18
+ = friendly_date(topic.replied_at)
19
+ - else
20
+ %p.minor
21
+ =t('dashboard_extension.no_show')
22
+
23
+ #recent_topics.dashboard_module
24
+ .header
25
+ %h2
26
+ = t('forum_extension.latest_forum_activity')
27
+ .blockcontent
28
+ - latest = Topic.latest(4)
29
+ - if latest.any?
30
+ - latest.each do |topic|
31
+ - post = topic.posts.last
32
+ %h3
33
+ = link_to_post(post, :class => 'topic')
34
+ %span.context
35
+ - if topic.has_replies?
36
+ = t('forum_extension.new_reply_from')
37
+ = link_to topic.replied_by.name, reader_url(topic.replied_by)
38
+ = friendly_date(topic.replied_at)
39
+ - else
40
+ = t('forum_extension.started_by')
41
+ = link_to topic.reader.name, reader_url(topic.reader)
42
+ = friendly_date(topic.created_at)
43
+ %p.excerpt
44
+ = truncate_words(scrub_html(post.body), 16)
45
+ %p.minor
46
+ =t('forum_extension.administer_by_browsing')
47
+ - else
48
+ %p.minor
49
+ =t('dashboard_extension.no_show')
50
+
51
+