radiant-forum-extension 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +155 -0
- data/Rakefile +139 -0
- data/VERSION +1 -0
- data/app/controllers/admin/forums_controller.rb +11 -0
- data/app/controllers/forums_controller.rb +34 -0
- data/app/controllers/posts_controller.rb +238 -0
- data/app/controllers/topics_controller.rb +78 -0
- data/app/models/forum.rb +52 -0
- data/app/models/moderatorship.rb +5 -0
- data/app/models/post.rb +122 -0
- data/app/models/post_attachment.rb +73 -0
- data/app/models/topic.rb +162 -0
- data/app/views/admin/forums/_form.html.haml +25 -0
- data/app/views/admin/forums/edit.html.haml +5 -0
- data/app/views/admin/forums/index.html.haml +76 -0
- data/app/views/admin/forums/moderate.html.haml +2 -0
- data/app/views/admin/forums/new.html.haml +5 -0
- data/app/views/admin/forums/remove.html.haml +22 -0
- data/app/views/admin/pages/_edit_commentability.html.haml +9 -0
- data/app/views/admin/reader_settings/_forum.html.haml +4 -0
- data/app/views/admin/sites/_choose_forum_layout.html.haml +7 -0
- data/app/views/forums/_forum.html.haml +22 -0
- data/app/views/forums/index.html.haml +44 -0
- data/app/views/forums/show.html.haml +38 -0
- data/app/views/forums/show.rss.builder +13 -0
- data/app/views/layouts/feed.rss.builder +5 -0
- data/app/views/monitorships/create.rjs.erb +1 -0
- data/app/views/monitorships/destroy.rjs.erb +1 -0
- data/app/views/monitorships/index.html.erb +35 -0
- data/app/views/pages/_comment.html.haml +10 -0
- data/app/views/pages/_comments.html.haml +15 -0
- data/app/views/posts/_attachment.html.haml +2 -0
- data/app/views/posts/_form.html.haml +6 -0
- data/app/views/posts/_latest.html.haml +17 -0
- data/app/views/posts/_post.html.haml +67 -0
- data/app/views/posts/_post.rss.builder +13 -0
- data/app/views/posts/_reply.html.haml +36 -0
- data/app/views/posts/_search.html.haml +28 -0
- data/app/views/posts/_uploader.html.haml +16 -0
- data/app/views/posts/edit.html.haml +20 -0
- data/app/views/posts/index.html.haml +33 -0
- data/app/views/posts/index.rss.builder +14 -0
- data/app/views/posts/new.html.haml +14 -0
- data/app/views/posts/preview.html.haml +32 -0
- data/app/views/posts/search.html.haml +54 -0
- data/app/views/posts/search.rss.builder +14 -0
- data/app/views/posts/show.html.haml +10 -0
- data/app/views/reader_notifier/post.rhtml +14 -0
- data/app/views/readers/_extra_controls.html.haml +5 -0
- data/app/views/readers/show_with_posts.html.haml +47 -0
- data/app/views/topics/_form.html.haml +26 -0
- data/app/views/topics/_help.html.haml +8 -0
- data/app/views/topics/_locked.html.haml +2 -0
- data/app/views/topics/_topic.html.haml +35 -0
- data/app/views/topics/_topic.rss.builder +9 -0
- data/app/views/topics/comments.html.haml +5 -0
- data/app/views/topics/edit.html.haml +26 -0
- data/app/views/topics/index.html.haml +41 -0
- data/app/views/topics/index.rss.builder +14 -0
- data/app/views/topics/new.html.haml +40 -0
- data/app/views/topics/show.html.haml +45 -0
- data/app/views/topics/show.rss.builder +14 -0
- data/config/routes.rb +16 -0
- data/db/migrate/001_create_forum_tables.rb +64 -0
- data/db/migrate/002_pages_commentable.rb +16 -0
- data/db/migrate/003_extend_sites.rb +13 -0
- data/db/migrate/004_sample_layout.rb +41 -0
- data/db/migrate/005_post_attachments.rb +19 -0
- data/db/migrate/006_user_relations.rb +20 -0
- data/db/migrate/007_reader_ownership.rb +35 -0
- data/db/migrate/008_first_posts.rb +13 -0
- data/db/migrate/20090824111005_import_helpers.rb +15 -0
- data/forum_extension.rb +44 -0
- data/lib/forum_admin_ui.rb +37 -0
- data/lib/forum_helper.rb +109 -0
- data/lib/forum_page.rb +45 -0
- data/lib/forum_reader.rb +9 -0
- data/lib/forum_reader_notifier.rb +9 -0
- data/lib/forum_readers_controller.rb +15 -0
- data/lib/forum_red_cloth3.rb +10 -0
- data/lib/forum_red_cloth4.rb +21 -0
- data/lib/forum_site.rb +12 -0
- data/lib/forum_tags.rb +285 -0
- data/lib/tasks/radiant_forum_extension_tasks.rake +137 -0
- data/pkg/radiant-forum-extension-0.5.0.gem +0 -0
- data/public/images/admin/forum.png +0 -0
- data/public/images/admin/new-forum.png +0 -0
- data/public/images/admin/nominus.png +0 -0
- data/public/images/admin/noremove.png +0 -0
- data/public/images/emoticons/angry.gif +0 -0
- data/public/images/emoticons/bigsmile.gif +0 -0
- data/public/images/emoticons/confused.gif +0 -0
- data/public/images/emoticons/cool.gif +0 -0
- data/public/images/emoticons/cry.gif +0 -0
- data/public/images/emoticons/devil.gif +0 -0
- data/public/images/emoticons/neutral.gif +0 -0
- data/public/images/emoticons/sad.gif +0 -0
- data/public/images/emoticons/shamed.gif +0 -0
- data/public/images/emoticons/shocked.gif +0 -0
- data/public/images/emoticons/smile.gif +0 -0
- data/public/images/emoticons/surprised.gif +0 -0
- data/public/images/emoticons/tongue.gif +0 -0
- data/public/images/emoticons/wink.gif +0 -0
- data/public/images/forum/attachment.png +0 -0
- data/public/images/forum/attachment_link.png +0 -0
- data/public/images/forum/attachment_over.png +0 -0
- data/public/images/forum/chk_off.png +0 -0
- data/public/images/forum/chk_on.png +0 -0
- data/public/images/forum/feed_14.png +0 -0
- data/public/images/forum/feed_28.png +0 -0
- data/public/images/forum/post_14.png +0 -0
- data/public/images/forum/post_14_over.png +0 -0
- data/public/images/forum/rdo_off.png +0 -0
- data/public/images/forum/rdo_on.png +0 -0
- data/public/images/forum/wait_16_grey.gif +0 -0
- data/public/javascripts/platform/forum.js +175 -0
- data/public/javascripts/platform/remotecontent.js +89 -0
- data/public/stylesheets/admin/forum.css +87 -0
- data/public/stylesheets/platform/forum.css +70 -0
- data/radiant-forum-extension.gemspec +215 -0
- data/spec/controllers/admin/forums_controller_spec.rb +54 -0
- data/spec/controllers/forums_controller_spec.rb +42 -0
- data/spec/controllers/posts_controller_spec.rb +310 -0
- data/spec/controllers/topics_controller_spec.rb +133 -0
- data/spec/datasets/forum_layouts_dataset.rb +15 -0
- data/spec/datasets/forum_pages_dataset.rb +11 -0
- data/spec/datasets/forum_readers_dataset.rb +55 -0
- data/spec/datasets/forum_sites_dataset.rb +10 -0
- data/spec/datasets/forums_dataset.rb +28 -0
- data/spec/datasets/posts_dataset.rb +31 -0
- data/spec/datasets/topics_dataset.rb +37 -0
- data/spec/lib/forum_admin_ui_spec.rb +35 -0
- data/spec/lib/forum_page_spec.rb +63 -0
- data/spec/lib/forum_reader_notification_spec.rb +9 -0
- data/spec/lib/forum_reader_spec.rb +13 -0
- data/spec/lib/forum_site_spec.rb +39 -0
- data/spec/models/forum_spec.rb +48 -0
- data/spec/models/post_spec.rb +91 -0
- data/spec/models/topic_spec.rb +105 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +44 -0
- data/vendor/plugins/acts_as_list/README +23 -0
- data/vendor/plugins/acts_as_list/init.rb +3 -0
- data/vendor/plugins/acts_as_list/lib/active_record/acts/list.rb +256 -0
- data/vendor/plugins/acts_as_list/test/list_test.rb +332 -0
- 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
|