radiant-forum-extension 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. data/README.md +155 -0
  2. data/Rakefile +139 -0
  3. data/VERSION +1 -0
  4. data/app/controllers/admin/forums_controller.rb +11 -0
  5. data/app/controllers/forums_controller.rb +34 -0
  6. data/app/controllers/posts_controller.rb +238 -0
  7. data/app/controllers/topics_controller.rb +78 -0
  8. data/app/models/forum.rb +52 -0
  9. data/app/models/moderatorship.rb +5 -0
  10. data/app/models/post.rb +122 -0
  11. data/app/models/post_attachment.rb +73 -0
  12. data/app/models/topic.rb +162 -0
  13. data/app/views/admin/forums/_form.html.haml +25 -0
  14. data/app/views/admin/forums/edit.html.haml +5 -0
  15. data/app/views/admin/forums/index.html.haml +76 -0
  16. data/app/views/admin/forums/moderate.html.haml +2 -0
  17. data/app/views/admin/forums/new.html.haml +5 -0
  18. data/app/views/admin/forums/remove.html.haml +22 -0
  19. data/app/views/admin/pages/_edit_commentability.html.haml +9 -0
  20. data/app/views/admin/reader_settings/_forum.html.haml +4 -0
  21. data/app/views/admin/sites/_choose_forum_layout.html.haml +7 -0
  22. data/app/views/forums/_forum.html.haml +22 -0
  23. data/app/views/forums/index.html.haml +44 -0
  24. data/app/views/forums/show.html.haml +38 -0
  25. data/app/views/forums/show.rss.builder +13 -0
  26. data/app/views/layouts/feed.rss.builder +5 -0
  27. data/app/views/monitorships/create.rjs.erb +1 -0
  28. data/app/views/monitorships/destroy.rjs.erb +1 -0
  29. data/app/views/monitorships/index.html.erb +35 -0
  30. data/app/views/pages/_comment.html.haml +10 -0
  31. data/app/views/pages/_comments.html.haml +15 -0
  32. data/app/views/posts/_attachment.html.haml +2 -0
  33. data/app/views/posts/_form.html.haml +6 -0
  34. data/app/views/posts/_latest.html.haml +17 -0
  35. data/app/views/posts/_post.html.haml +67 -0
  36. data/app/views/posts/_post.rss.builder +13 -0
  37. data/app/views/posts/_reply.html.haml +36 -0
  38. data/app/views/posts/_search.html.haml +28 -0
  39. data/app/views/posts/_uploader.html.haml +16 -0
  40. data/app/views/posts/edit.html.haml +20 -0
  41. data/app/views/posts/index.html.haml +33 -0
  42. data/app/views/posts/index.rss.builder +14 -0
  43. data/app/views/posts/new.html.haml +14 -0
  44. data/app/views/posts/preview.html.haml +32 -0
  45. data/app/views/posts/search.html.haml +54 -0
  46. data/app/views/posts/search.rss.builder +14 -0
  47. data/app/views/posts/show.html.haml +10 -0
  48. data/app/views/reader_notifier/post.rhtml +14 -0
  49. data/app/views/readers/_extra_controls.html.haml +5 -0
  50. data/app/views/readers/show_with_posts.html.haml +47 -0
  51. data/app/views/topics/_form.html.haml +26 -0
  52. data/app/views/topics/_help.html.haml +8 -0
  53. data/app/views/topics/_locked.html.haml +2 -0
  54. data/app/views/topics/_topic.html.haml +35 -0
  55. data/app/views/topics/_topic.rss.builder +9 -0
  56. data/app/views/topics/comments.html.haml +5 -0
  57. data/app/views/topics/edit.html.haml +26 -0
  58. data/app/views/topics/index.html.haml +41 -0
  59. data/app/views/topics/index.rss.builder +14 -0
  60. data/app/views/topics/new.html.haml +40 -0
  61. data/app/views/topics/show.html.haml +45 -0
  62. data/app/views/topics/show.rss.builder +14 -0
  63. data/config/routes.rb +16 -0
  64. data/db/migrate/001_create_forum_tables.rb +64 -0
  65. data/db/migrate/002_pages_commentable.rb +16 -0
  66. data/db/migrate/003_extend_sites.rb +13 -0
  67. data/db/migrate/004_sample_layout.rb +41 -0
  68. data/db/migrate/005_post_attachments.rb +19 -0
  69. data/db/migrate/006_user_relations.rb +20 -0
  70. data/db/migrate/007_reader_ownership.rb +35 -0
  71. data/db/migrate/008_first_posts.rb +13 -0
  72. data/db/migrate/20090824111005_import_helpers.rb +15 -0
  73. data/forum_extension.rb +44 -0
  74. data/lib/forum_admin_ui.rb +37 -0
  75. data/lib/forum_helper.rb +109 -0
  76. data/lib/forum_page.rb +45 -0
  77. data/lib/forum_reader.rb +9 -0
  78. data/lib/forum_reader_notifier.rb +9 -0
  79. data/lib/forum_readers_controller.rb +15 -0
  80. data/lib/forum_red_cloth3.rb +10 -0
  81. data/lib/forum_red_cloth4.rb +21 -0
  82. data/lib/forum_site.rb +12 -0
  83. data/lib/forum_tags.rb +285 -0
  84. data/lib/tasks/radiant_forum_extension_tasks.rake +137 -0
  85. data/pkg/radiant-forum-extension-0.5.0.gem +0 -0
  86. data/public/images/admin/forum.png +0 -0
  87. data/public/images/admin/new-forum.png +0 -0
  88. data/public/images/admin/nominus.png +0 -0
  89. data/public/images/admin/noremove.png +0 -0
  90. data/public/images/emoticons/angry.gif +0 -0
  91. data/public/images/emoticons/bigsmile.gif +0 -0
  92. data/public/images/emoticons/confused.gif +0 -0
  93. data/public/images/emoticons/cool.gif +0 -0
  94. data/public/images/emoticons/cry.gif +0 -0
  95. data/public/images/emoticons/devil.gif +0 -0
  96. data/public/images/emoticons/neutral.gif +0 -0
  97. data/public/images/emoticons/sad.gif +0 -0
  98. data/public/images/emoticons/shamed.gif +0 -0
  99. data/public/images/emoticons/shocked.gif +0 -0
  100. data/public/images/emoticons/smile.gif +0 -0
  101. data/public/images/emoticons/surprised.gif +0 -0
  102. data/public/images/emoticons/tongue.gif +0 -0
  103. data/public/images/emoticons/wink.gif +0 -0
  104. data/public/images/forum/attachment.png +0 -0
  105. data/public/images/forum/attachment_link.png +0 -0
  106. data/public/images/forum/attachment_over.png +0 -0
  107. data/public/images/forum/chk_off.png +0 -0
  108. data/public/images/forum/chk_on.png +0 -0
  109. data/public/images/forum/feed_14.png +0 -0
  110. data/public/images/forum/feed_28.png +0 -0
  111. data/public/images/forum/post_14.png +0 -0
  112. data/public/images/forum/post_14_over.png +0 -0
  113. data/public/images/forum/rdo_off.png +0 -0
  114. data/public/images/forum/rdo_on.png +0 -0
  115. data/public/images/forum/wait_16_grey.gif +0 -0
  116. data/public/javascripts/platform/forum.js +175 -0
  117. data/public/javascripts/platform/remotecontent.js +89 -0
  118. data/public/stylesheets/admin/forum.css +87 -0
  119. data/public/stylesheets/platform/forum.css +70 -0
  120. data/radiant-forum-extension.gemspec +215 -0
  121. data/spec/controllers/admin/forums_controller_spec.rb +54 -0
  122. data/spec/controllers/forums_controller_spec.rb +42 -0
  123. data/spec/controllers/posts_controller_spec.rb +310 -0
  124. data/spec/controllers/topics_controller_spec.rb +133 -0
  125. data/spec/datasets/forum_layouts_dataset.rb +15 -0
  126. data/spec/datasets/forum_pages_dataset.rb +11 -0
  127. data/spec/datasets/forum_readers_dataset.rb +55 -0
  128. data/spec/datasets/forum_sites_dataset.rb +10 -0
  129. data/spec/datasets/forums_dataset.rb +28 -0
  130. data/spec/datasets/posts_dataset.rb +31 -0
  131. data/spec/datasets/topics_dataset.rb +37 -0
  132. data/spec/lib/forum_admin_ui_spec.rb +35 -0
  133. data/spec/lib/forum_page_spec.rb +63 -0
  134. data/spec/lib/forum_reader_notification_spec.rb +9 -0
  135. data/spec/lib/forum_reader_spec.rb +13 -0
  136. data/spec/lib/forum_site_spec.rb +39 -0
  137. data/spec/models/forum_spec.rb +48 -0
  138. data/spec/models/post_spec.rb +91 -0
  139. data/spec/models/topic_spec.rb +105 -0
  140. data/spec/spec.opts +6 -0
  141. data/spec/spec_helper.rb +44 -0
  142. data/vendor/plugins/acts_as_list/README +23 -0
  143. data/vendor/plugins/acts_as_list/init.rb +3 -0
  144. data/vendor/plugins/acts_as_list/lib/active_record/acts/list.rb +256 -0
  145. data/vendor/plugins/acts_as_list/test/list_test.rb +332 -0
  146. metadata +287 -0
data/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # Forum
2
+
3
+ This is a simple but comprehensive forum implementation that plugs into radiant. It's fairly well-featured, including:
4
+
5
+ * forums, topics, posts etc
6
+ * page comments
7
+ * simple but effective forum search
8
+ * rss feeds of pretty much anything (including search queries and individual authors)
9
+ * textile-formatted messages with html sanitized
10
+ * smilies :(
11
+ * multiple message attachments and helpful image-handling
12
+ * configurable message-editing interval during which authors can make corrections
13
+
14
+ 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 one easy to adapt and extend.
15
+
16
+ The forum was derived long ago from [Beast](http://beast.caboo.se) and the fossilised bones will still be visible here and there. It has been in use on various sites in various versions for three years, but in making it ready for publication I've pretty much refactored the whole thing and probably optimised new bugs into it. Please let me know as soon as you spot one.
17
+
18
+ ## Status
19
+
20
+ Mature and stable, but I'm a bit habitual with it so may have overlooked some rough edges in parts of the interface I don't visit much. It would benefit from wider use.
21
+
22
+ There's a lot going on here and plenty of places for me to hide bugs, but the tests are fairly thorough.
23
+
24
+ ## Latest
25
+
26
+ * Quite a lot of tidying up and trimming of the interface
27
+ * sample javascripts and layout updated
28
+ * Updated for 0.8.1 with gem and extension dependencies, and many small fixes to keep up with the `reader` and `group_forum` extensions.
29
+
30
+ ## Still to do
31
+
32
+ * Moderators among readership
33
+ * Reinstate message preview
34
+ * Reinstate email-monitoring
35
+ * Message-problem button
36
+ * Admin pages that are less crappy
37
+ * Anonymous page-comment option
38
+
39
+ ## Requirements
40
+
41
+ Radiant 0.8.1 (we're using the new config machinery) 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.
42
+
43
+ We also require [will_paginate](http://github.com/mislav/will_paginate/) and [paperclip](http://github.com/thoughtbot/paperclip/) as gems. The latter is for post attachments, and if you're using [paperclipped](http://github.com/kbingman/paperclipped) (which vendors paperclip) then you can probably skip it. Otherwise:
44
+
45
+ sudo rake gems:install
46
+
47
+ should get everything you need.
48
+
49
+ The forum is compatible with multi_site but you have to use [sites](https://github.com/spanner/radiant-sites-extension) instead if you want forums and readers site-scoped.
50
+
51
+ ## Installation
52
+
53
+ The usual:
54
+
55
+ git submodule add git://github.com/spanner/radiant-forum-extension.git vendor/extensions/forum
56
+
57
+ Check the extension loading order in environment.rb (you will need `share_layouts`, `reader` and perhaps `multi_site` to load before this one) and then:
58
+
59
+ rake radiant:extensions:forum:migrate
60
+ rake radiant:extensions:forum:update
61
+
62
+ As well as the basic machinery this should give you:
63
+
64
+ * a basic forum.css that you will want to improve upon,
65
+ * an admin/forum.css that you can probably leave alone,
66
+ * a sample layout that should be enough to let you experiment
67
+
68
+ ## Administration
69
+
70
+ The forum is easy to use and almost entirely separate from your page hierarchy. Forums - which are described as 'discussion categories' most of the time and not emphasised much in the reader-facing pages - are created and edited in the admin interface, and there readers can also (soon) be promoted to moderate selected forums. Everything else is done through the reader interface including the editing and deletion of posts and topics.
71
+
72
+ ## Configuration
73
+
74
+ A few config options can be set:
75
+
76
+ * `forum.layout` should be the name of the layout you want to use for reader-facing forum pages. See below for more detail.
77
+ * `forum.default_forum` is the name of the forum that will be selected by default in the new-message form
78
+ * `forum.editable_period` is the number of minutes for which posts remain editable by their authors once they have been submitted
79
+
80
+ ## Layouts
81
+
82
+ We use `share_layouts` to present your forum pages inside the radiant layout of your choice. Set the config option 'forum.layout' to the name of the layout you want to use (or see below for multi_site instructions). 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:
83
+
84
+ * the main page content (that you get with a bare `<r:content />`) gives you the main list, object or form.
85
+ * 'pagetitle' will be the main page heading (eg the name of the topic): `<r:content part="pagetitle" />`
86
+ * 'breadcrumbs' is what you'd expect: usually something simple like Forum > Category name
87
+ * 'breadhead' is an up-one-level link that you may want to include in the page header
88
+ * 'feed' is an icon link to the RSS feed for whatever you are currently looking at (and works for most things including searches, of which more below)
89
+ * 'mugshot' is a gravatar for the author of this topic or post
90
+ * 'credits' is the authorship summary for this topic or post
91
+
92
+ These parts are only defined where they're relevent, so you can do something like this:
93
+
94
+ <h1 class="pagetitle">
95
+ <r:content part="mugshot" />
96
+ <r:content part="breadhead" />
97
+ <r:content part="pagetitle" />
98
+ <r:content part="feed" />
99
+ </h1>
100
+
101
+ and it will look right for all the forum pages.
102
+
103
+ You probably also want to include this somewhere, as on all reader-service pages:
104
+
105
+ <r:if_reader><r:reader:controls /></r:if_reader>
106
+
107
+ Have a look at the included sample layout for a starting point.
108
+
109
+ ## Multi_site
110
+
111
+ The forum is fine under multi_site. If you use [our version](http://github.com/spanner/radiant-multi-site-extension) then everything will be site-scoped for you. In that case, the forum layout is chosen for each site using a dropdown on the (currently extremely scruffy) site-editing page. Everything else works in just the same way and you should be able to run several forums next to each other without problems.
112
+
113
+ ## Private discussion
114
+
115
+ 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.
116
+
117
+ ## Searching the forum
118
+
119
+ The forum has its own simple search mechanism. It's not very bright but it has two great advantages: you can combine text search with filtering by author and category, and you can bookmark any search query as an RSS feed to be alerted when new posts match that search. There are arguments for integrating this with eg `sphinx_search` but at the moment I quite like keeping them separate: I find that searching for messages is not the same as searching for pages, but usually a different group seeking different results. Let me know if you disagree.
120
+
121
+ To enable the search, you can either link to `/forum/posts/search` or put `<r:forum_search>` somewhere in your forum layout. See the tag documentation for control of the search form presentation.
122
+
123
+ ## Page Comments
124
+
125
+ To enable page comments, all you have to do is put `<r:comments:all />` somewhere in the layout (or in a snippet or on an individual page). Behind the scenes this will create a dedicated forum for page comments and a topic for each page as it is commented upon. The rest of the mechanism is the same, and the comments themselves are just posts.
126
+
127
+ The page cache means we don't want anything personal on the page itself - no edit controls or logout buttons - but there are a couple of ways you can get around this with a remote call.
128
+
129
+ * Put `<r:comments:remote />` on the page instead and a stub will be included suitable for your scripts to grab. A simple example is included in platform/forum.js.
130
+ * `<r:comments:all /><r:comments:link />` is more efficient but less friendly: it will give you a static list of all comments followed by a reply link. Posting a comment clears the page cache, btw.
131
+
132
+ You can also use `<r:comments:each>...</r:comments:each>` and the various r:comment tags to compose page comments however you like. See the tags-available documentation for details.
133
+
134
+ ## Security & Spam
135
+
136
+ The reader extension includes email confirmation and a simple honeytrap that should prevent most bots from registering. The forum includes proper XSS protection via the sanitize gem and the usual CSRF protection is enabled. You ought to be relatively spam-free.
137
+
138
+ ## Scripting the forum interface
139
+
140
+ I've presented this in the simplest way possible, on the assumption that everyone will want to do their own thing with it. An unobtrusive javascript front end (based on mootools but without much fanciness) is included to make things a little bit more friendly: it includes edit-in-place for posts, a bit of code to handle bringing page comments into the page, and some generally-useful routines like notice faders. That's about it, but I hope it will provide a starting point. The 'forum_example' layout that is created on installation should bring in that javascript and enough css for you to see it all working. For local reasons it's all stored under `javascripts/platform/...`
141
+
142
+ ## Smilies
143
+
144
+ I've included a basic set here and a redcloth extension that catches :smile: notation alongside the textile markup. There's a javascript front end in the works for that - click on the smiley, you know - which will also soon appear here.
145
+
146
+ ## Bugs
147
+
148
+ Very likely. [Github issues](http://github.com/spanner/radiant-forum-extension/issues), please, or for little things an email or github message is fine.
149
+
150
+ ## Author & Copyright
151
+
152
+ * William Ross, for spanner. will at spanner.org
153
+ * Originally based on dear old Beast, currently not visible at [http://beast.caboo.se](http://beast.caboo.se)
154
+ * Copyright 2007-9 spanner ltd
155
+ * released under the same terms as Rails and/or Radiant
data/Rakefile ADDED
@@ -0,0 +1,139 @@
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'
12
+ gem.add_dependency 'paperclip'
13
+ gem.add_dependency 'will_paginate'
14
+ end
15
+ rescue LoadError
16
+ puts "Jeweler (or a dependency) not available. This is only required if you plan to package forum as a gem."
17
+ end
18
+
19
+ # In rails 1.2, plugins aren't available in the path until they're loaded.
20
+ # Check to see if the rspec plugin is installed first and require
21
+ # it if it is. If not, use the gem version.
22
+
23
+ # Determine where the RSpec plugin is by loading the boot
24
+ unless defined? RADIANT_ROOT
25
+ ENV["RAILS_ENV"] = "test"
26
+ case
27
+ when ENV["RADIANT_ENV_FILE"]
28
+ require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
29
+ when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
30
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
31
+ else
32
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
33
+ end
34
+ end
35
+
36
+ require 'rake'
37
+ require 'rake/rdoctask'
38
+ require 'rake/testtask'
39
+
40
+ rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
41
+ $LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
42
+ require 'spec/rake/spectask'
43
+ require 'cucumber'
44
+ require 'cucumber/rake/task'
45
+
46
+ # Cleanup the RADIANT_ROOT constant so specs will load the environment
47
+ Object.send(:remove_const, :RADIANT_ROOT)
48
+
49
+ extension_root = File.expand_path(File.dirname(__FILE__))
50
+
51
+ task :default => :spec
52
+ task :stats => "spec:statsetup"
53
+
54
+ desc "Run all specs in spec directory"
55
+ Spec::Rake::SpecTask.new(:spec) do |t|
56
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
57
+ t.spec_files = FileList['spec/**/*_spec.rb']
58
+ end
59
+
60
+ task :features => 'spec:integration'
61
+
62
+ namespace :spec do
63
+ desc "Run all specs in spec directory with RCov"
64
+ Spec::Rake::SpecTask.new(:rcov) do |t|
65
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
66
+ t.spec_files = FileList['spec/**/*_spec.rb']
67
+ t.rcov = true
68
+ t.rcov_opts = ['--exclude', 'spec', '--rails']
69
+ end
70
+
71
+ desc "Print Specdoc for all specs"
72
+ Spec::Rake::SpecTask.new(:doc) do |t|
73
+ t.spec_opts = ["--format", "specdoc", "--dry-run"]
74
+ t.spec_files = FileList['spec/**/*_spec.rb']
75
+ end
76
+
77
+ [:models, :controllers, :views, :helpers].each do |sub|
78
+ desc "Run the specs under spec/#{sub}"
79
+ Spec::Rake::SpecTask.new(sub) do |t|
80
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
81
+ t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
82
+ end
83
+ end
84
+
85
+ desc "Run the Cucumber features"
86
+ Cucumber::Rake::Task.new(:integration) do |t|
87
+ t.fork = true
88
+ t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'pretty')]
89
+ # t.feature_pattern = "#{extension_root}/features/**/*.feature"
90
+ t.profile = "default"
91
+ end
92
+
93
+ # Setup specs for stats
94
+ task :statsetup do
95
+ require 'code_statistics'
96
+ ::STATS_DIRECTORIES << %w(Model\ specs spec/models)
97
+ ::STATS_DIRECTORIES << %w(View\ specs spec/views)
98
+ ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
99
+ ::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
100
+ ::CodeStatistics::TEST_TYPES << "Model specs"
101
+ ::CodeStatistics::TEST_TYPES << "View specs"
102
+ ::CodeStatistics::TEST_TYPES << "Controller specs"
103
+ ::CodeStatistics::TEST_TYPES << "Helper specs"
104
+ ::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
105
+ end
106
+
107
+ namespace :db do
108
+ namespace :fixtures do
109
+ desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
110
+ task :load => :environment do
111
+ require 'active_record/fixtures'
112
+ ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
113
+ (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
114
+ Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ desc 'Generate documentation for the forum extension.'
122
+ Rake::RDocTask.new(:rdoc) do |rdoc|
123
+ rdoc.rdoc_dir = 'rdoc'
124
+ rdoc.title = 'ForumExtension'
125
+ rdoc.options << '--line-numbers' << '--inline-source'
126
+ rdoc.rdoc_files.include('README')
127
+ rdoc.rdoc_files.include('lib/**/*.rb')
128
+ end
129
+
130
+ # For extensions that are in transition
131
+ desc 'Test the forum extension.'
132
+ Rake::TestTask.new(:test) do |t|
133
+ t.libs << 'lib'
134
+ t.pattern = 'test/**/*_test.rb'
135
+ t.verbose = true
136
+ end
137
+
138
+ # Load any custom rakefiles for extension
139
+ Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.6.0
@@ -0,0 +1,11 @@
1
+ class Admin::ForumsController < Admin::ResourceController
2
+
3
+ # # I have no idea where this default is being overridden
4
+ # skip_before_filter :verify_authenticity_token if ENV["RAILS_ENV"] == "test"
5
+
6
+ only_allow_access_to :new, :create, :edit, :update, :remove, :destroy,
7
+ :when => :admin,
8
+ :denied_url => { :controller => 'pages', :action => 'index' },
9
+ :denied_message => 'You must be an administrator to add or change discussion categories.'
10
+
11
+ end
@@ -0,0 +1,34 @@
1
+ class ForumsController < ReaderActionController
2
+ skip_before_filter :require_reader
3
+ before_filter :find_forum, :only => :show
4
+ before_filter :no_changes_here, :except => [:index, :show]
5
+
6
+ radiant_layout { |controller| controller.layout_for(:forum) }
7
+
8
+ def index
9
+ @forums = Forum.visible.paginate(:all, :order => "position", :page => params[:page] || 1, :per_page => params[:per_page])
10
+ end
11
+
12
+ def show
13
+ respond_to do |format|
14
+ format.html {
15
+ @topics = Topic.paginate_by_forum_id(params[:id], :page => params[:page] || 1, :per_page => params[:per_page] || 20, :include => :replied_by, :order => 'sticky desc, replied_at desc')
16
+ }
17
+ format.rss {
18
+ @topics = Topic.paginate_by_forum_id(params[:id], :page => params[:page] || 1, :per_page => params[:per_page] || 20, :include => :replied_by, :order => 'replied_at desc')
19
+ render :layout => 'feed'
20
+ }
21
+ end
22
+ end
23
+
24
+ def no_changes_here
25
+ redirect_to admin_forums_url
26
+ end
27
+
28
+ protected
29
+
30
+ def find_forum
31
+ @forum = Forum.find(params[:id])
32
+ end
33
+
34
+ end
@@ -0,0 +1,238 @@
1
+ class PostsController < ReaderActionController
2
+
3
+ before_filter :set_site_title
4
+ before_filter :require_activated_reader, :except => [:index, :show, :search]
5
+ before_filter :find_topic_or_page, :except => [:index, :search]
6
+ before_filter :require_unlocked_topic_and_page, :only => [:new, :create]
7
+ before_filter :find_post, :only => [:show, :edit, :update]
8
+ before_filter :build_post, :only => [:new]
9
+ before_filter :require_authority, :only => [:edit, :update, :destroy]
10
+
11
+ radiant_layout { |controller| controller.layout_for(:forum) }
12
+
13
+ @@default_query_options = {
14
+ :page => 1,
15
+ :per_page => 20,
16
+ :order => 'posts.created_at desc',
17
+ :include => [:topic, :forum, :reader]
18
+ }
19
+
20
+ def index
21
+ @posts = Post.visible.paginate(:all, @@default_query_options.merge(:page => params[:page], :per_page => params[:per_page]))
22
+ render_page_or_feed
23
+ end
24
+
25
+ def search
26
+ conditions = []
27
+ @reader = Reader.find(params[:reader_id]) unless params[:reader_id].blank?
28
+ @topic = Topic.find(params[:topic_id]) unless params[:topic_id].blank?
29
+ @forum = Forum.find(params[:forum_id]) unless params[:forum_id].blank?
30
+
31
+ conditions << Post.send(:sanitize_sql, ["posts.reader_id = ?", @reader.id]) if @reader
32
+ conditions << Post.send(:sanitize_sql, ["posts.topic_id = ?", @topic.id]) if @topic
33
+ conditions << Post.send(:sanitize_sql, ["posts.forum_id = ?", @forum.id]) if @forum
34
+ conditions << Post.send(:sanitize_sql, ['LOWER(posts.body) LIKE ?', "%#{params[:q]}%"]) unless params[:q].blank?
35
+ @searching = true if conditions.any?
36
+
37
+ @posts = Post.paginate(:all, @@default_query_options.merge(:conditions => @searching ? conditions.collect { |c| "(#{c})" }.join(' AND ') : nil, :page => params[:page], :per_page => params[:per_page]))
38
+
39
+ # for summary of the set, and onward links
40
+ @forums = @posts.collect(&:forum).uniq
41
+ @topics = @posts.collect(&:topic).uniq
42
+ @readers = @posts.collect(&:reader).uniq
43
+
44
+ if @searching
45
+ @title = "Search Results"
46
+ @description = "Posts"
47
+ @description << " matching '#{params[:q]}'" unless params[:q].blank?
48
+ @description << " from #{@reader.name}" if @reader
49
+ if @topic
50
+ @description << " under #{@topic.name}"
51
+ elsif @forum
52
+ @description << " in #{@forum.name}"
53
+ end
54
+ end
55
+
56
+ render_page_or_feed
57
+ end
58
+
59
+ def monitored
60
+ @searching = true
61
+ options = @@query_options.merge(:conditions => ['monitorships.reader_id = ? and monitorships.active = ?', params[:reader_id], true])
62
+ options[:joins] += ' inner join monitorships on monitorships.topic_id = topics.id'
63
+ options[:page] = params[:page] || 1
64
+ @posts = Post.paginate(:all, options)
65
+ @title = "Topics you're watching"
66
+ render_page_or_feed
67
+ end
68
+
69
+ def show
70
+ respond_to do |format|
71
+ format.html { redirect_to_page_or_topic }
72
+ format.js { render :layout => false }
73
+ end
74
+ end
75
+
76
+ # this is typically called by xht to bring a comment form into a page or a reply form into a topic
77
+ # if the reader is not logged in, reader_required should have intervened and caused the return of a login form instead
78
+
79
+ def new
80
+ respond_to do |format|
81
+ format.html { render :template => 'posts/new' } # we specify the template because in theory we could be reverting from a post to create
82
+ format.js { render :partial => 'posts/reply' }
83
+ end
84
+ end
85
+
86
+ def create
87
+ if @topic.new_record?
88
+ # only happens if it's a page comment and the topic has just been built
89
+ # in that case we can let the topic-creation routines do the post work
90
+ @topic.body = params[:post][:body]
91
+ @topic.reader = current_reader
92
+ @topic.save!
93
+ @post = @topic.first_post
94
+ else
95
+ @post = @topic.posts.create!(params[:post])
96
+ end
97
+
98
+ @post.save_attachments(params[:files]) unless @page && !Radiant::Config['forum.comments_have_attachments']
99
+ Radiant::Cache.clear if @page
100
+
101
+ respond_to do |format|
102
+ format.html { redirect_to_page_or_topic }
103
+ format.js { render :action => 'show', :layout => false }
104
+ end
105
+
106
+ rescue ActiveRecord::RecordInvalid
107
+ flash[:error] = 'Problem!'
108
+ respond_to do |format|
109
+ format.html { render :action => 'new' }
110
+ format.js { render :action => 'new', :layout => false }
111
+ end
112
+ end
113
+
114
+ def topic_locked
115
+ respond_to do |format|
116
+ format.html do
117
+ flash[:notice] = 'Topic is locked.'
118
+ redirect_to_page_or_topic
119
+ end
120
+ format.js { render :partial => 'topics/locked' }
121
+ end
122
+ return
123
+ end
124
+
125
+ def edit
126
+ respond_to do |format|
127
+ format.html { render }
128
+ format.js { render :layout => false }
129
+ end
130
+ end
131
+
132
+ def update
133
+ @post.attributes = params[:post]
134
+ @post.save!
135
+ @post.save_attachments(params[:files])
136
+ Radiant::Cache.clear if @post.topic.page
137
+ rescue ActiveRecord::RecordInvalid
138
+ flash[:error] = "Sorry: message can't be empty"
139
+ ensure
140
+ respond_to do |format|
141
+ format.html { redirect_to_page_or_topic }
142
+ format.js { render :partial => 'post', :layout => false }
143
+ format.json { render :json => @post.as_json }
144
+ end
145
+ end
146
+
147
+ def destroy
148
+ if @post.first?
149
+ @post.topic.destroy
150
+ flash[:notice] = "Topic removed"
151
+ respond_to do |format|
152
+ format.html { redirect_to_forum }
153
+ format.js { render :partial => 'post', :layout => false }
154
+ end
155
+ else
156
+ @post.destroy
157
+ flash[:notice] = "Post removed"
158
+ respond_to do |format|
159
+ format.html { redirect_to_page_or_topic }
160
+ format.js { render :partial => 'post', :layout => false }
161
+ end
162
+ end
163
+ end
164
+
165
+ protected
166
+
167
+ def require_authority
168
+ (current_user && current_user.admin?) || @post.editable_by?(current_reader) # includes an editable-interval check
169
+ end
170
+
171
+ def find_topic_or_page
172
+ if params[:page_id]
173
+ @page = Page.find(params[:page_id])
174
+ @topic = @page.find_or_build_topic
175
+ @forum = @topic.forum if @topic
176
+ elsif params[:topic_id]
177
+ @topic = Topic.find(params[:topic_id], :include => :forum)
178
+ @forum = @topic.forum if @topic
179
+ end
180
+ end
181
+
182
+ def require_unlocked_topic_and_page
183
+ return page_locked if @page && @page.locked?
184
+ return topic_locked if @topic.locked?
185
+ true
186
+ end
187
+
188
+ def find_post
189
+ @post = @topic.posts.find(params[:id])
190
+ end
191
+
192
+ def build_post
193
+ @post = Post.new(params[:post])
194
+ @post.topic = @topic if @topic
195
+ @post.reader = current_reader
196
+ end
197
+
198
+ def redirect_to_page_or_topic
199
+ if (@topic.page)
200
+ post_location = @post ? "##{@post.dom_id}" : ""
201
+ redirect_to @topic.page.url + post_location
202
+ else
203
+ post_location = @post ? {:page => @post.topic_page, :anchor => @post.dom_id} : {}
204
+ redirect_to forum_topic_url(@topic.forum, @topic, post_location)
205
+ end
206
+ end
207
+
208
+ def redirect_to_forum
209
+ redirect_to forum_url(@topic.forum)
210
+ end
211
+
212
+ def page_locked
213
+ respond_to do |format|
214
+ format.html {
215
+ flash[:error] = 'This page is not commentable.'
216
+ redirect_to @page.url
217
+ }
218
+ format.js {
219
+ render :template => 'locked', :layout => false
220
+ }
221
+ end
222
+ false
223
+ end
224
+
225
+ def topic_locked
226
+ respond_to do |format|
227
+ format.html {
228
+ flash[:error] = 'This topic is locked.'
229
+ redirect_to_page_or_topic
230
+ }
231
+ format.js {
232
+ render :partial => 'topics/locked'
233
+ }
234
+ end
235
+ false
236
+ end
237
+
238
+ end